VOGONS

Common searches


First post, by Peter Swinkels

User metadata
Rank Member
Rank
Member

According to HelpPC interrupt vector 1Fh points at the extended ASCII characters' fonts. The code below is meant to invert the character bitmaps:

DEFINT A-Z
CONST CHARACTERBITMAPS = &H1F

DECLARE FUNCTION GetOffset& (InterruptNr)
DECLARE FUNCTION GetSegment& (InterruptNr)
DECLARE SUB DisplayExtendedCharacters ()
DECLARE SUB InvertExtendedCharacters ()

SCREEN 1
CLS

InvertExtendedCharacters
DisplayExtendedCharacters

SUB DisplayExtendedCharacters
FOR Character = 128 TO 255
PRINT CHR$(Character);
NEXT Character
END SUB

FUNCTION GetOffset& (InterruptNr)
DEF SEG = &H0

VectorOffset = InterruptNr * &H4

Offset& = PEEK(VectorOffset + &H1) * &H100&
GetOffset& = Offset& OR PEEK(VectorOffset)
END FUNCTION

FUNCTION GetSegment& (InterruptNr)
DEF SEG = &H0

VectorOffset = InterruptNr * &H4

Segment& = PEEK(VectorOffset + &H3) * &H100&
GetSegment& = Segment& OR PEEK(VectorOffset + &H2)
END FUNCTION

SUB InvertExtendedCharacters
Segment& = GetSegment&(CHARACTERBITMAPS)
Offset& = GetOffset&(CHARACTERBITMAPS)

DEF SEG = Segment&
FOR Index& = 0 TO 16384
POKE Offset& + Index&, PEEK(Offset& + Index&) XOR &HFF
NEXT Index&
END SUB

It doesn't appear to be doing anything except possibly crashing... Anyone know where I went wrong?

Last edited by Peter Swinkels on 2021-09-25, 18:14. Edited 1 time in total.

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 1 of 16, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

The character patterns are stored in ROM (read-only memory), so you can't write to it. You will have to copy the patterns (with your modifications) into writeable memory (RAM) and then point the INT 1Fh vector there. It should work nicely to have the writeable memory in the resident portion of a TSR. 😉

Reply 2 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

I got a little further, but it still doesn't quite work:

DEFINT A-Z

TYPE RegTypeX
ax AS INTEGER
bx AS INTEGER
cx AS INTEGER
dx AS INTEGER
bp AS INTEGER
si AS INTEGER
di AS INTEGER
flags AS INTEGER
ds AS INTEGER
es AS INTEGER
END TYPE

CONST EXTENDEDCHARACTERS = &H1F

DECLARE SUB DisplayExtendedCharacters ()
DECLARE SUB INTERRUPTX (intnum AS INTEGER, inreg AS RegTypeX, outreg AS RegTypeX)

DIM Registers AS RegTypeX
SCREEN 1
CLS

Buffer$ = STRING$(&H0, 16384)

Registers.ax = &H2500 OR EXTENDEDCHARACTERS
Registers.dx = SADD(Buffer$)
Registers.ds = VARSEG(Buffer$)
INTERRUPTX &H21, Registers, Registers

DisplayExtendedCharacters

SUB DisplayExtendedCharacters
FOR Character = 128 TO 255
PRINT CHR$(Character);
NEXT Character
END SUB

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 4 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

@ripsaw8080: I forgot to mention that blanking the characters was my intent, which should happen with my code even though the buffer is too large. I also forgot to mention I get scrambled characters instead of blanks when displaying them.

1024 bytes eh? I calculated 16 x 8 x 128 assuming the characters used 16 scanlines each and forgot that the 8 columns were represented by bits instead of bytes. Duh.

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 5 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Got it:

DEFINT A-Z

TYPE RegTypeX
ax AS INTEGER
bx AS INTEGER
cx AS INTEGER
dx AS INTEGER
bp AS INTEGER
si AS INTEGER
di AS INTEGER
flags AS INTEGER
ds AS INTEGER
es AS INTEGER
END TYPE

CONST EXTENDEDCHARACTERS = &H1F

DECLARE SUB DisplayExtendedCharacters ()
DECLARE SUB INTERRUPTX (intnum AS INTEGER, inreg AS RegTypeX, outreg AS RegTypeX)

DIM Registers AS RegTypeX
SCREEN 1
CLS

Buffer$ = STRING$(1024, 255)

Registers.ax = &H2500 OR EXTENDEDCHARACTERS
Registers.dx = SADD(Buffer$)
Registers.ds = VARSEG(Buffer$)
INTERRUPTX &H21, Registers, Registers

DisplayExtendedCharacters

SUB DisplayExtendedCharacters
FOR Character = 128 TO 255
PRINT CHR$(Character);
NEXT Character
END SUB

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 6 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Alright, I made a program that can retrieve and set the extended character set (see below). It can invert the font data and works fine for EGA/VGA. It also works for CGA except when trying to invert the characters. Anyone know why? The numeric parameter for GetFonts/SetFonts is a Boolean flag indicating whether CGA is used. (0 = CGA, and any other value = EGA/VGA)

DEFINT A-Z

TYPE RegTypeX
ax AS INTEGER
bx AS INTEGER
cx AS INTEGER
dx AS INTEGER
bp AS INTEGER
si AS INTEGER
di AS INTEGER
flags AS INTEGER
ds AS INTEGER
es AS INTEGER
END TYPE

CONST CGA = &H1F
CONST EGAVGA = &H43
CONST SIZECGA = &H400
CONST SIZEEGAVGA = &H1000

DECLARE FUNCTION GetFonts$ (IsCGA)
DECLARE FUNCTION InvertFonts$ (Fonts$)
DECLARE SUB DisplayExtendedCharacters ()
DECLARE SUB INTERRUPTX (intnum AS INTEGER, inreg AS RegTypeX, outreg AS RegTypeX)
DECLARE SUB SetFonts (IsCGA, Fonts$)

SCREEN 12
CLS

Fonts$ = GetFonts$(0)
Fonts$ = InvertFonts$(Fonts$)
SetFonts 0, Fonts$

DisplayExtendedCharacters

SUB DisplayExtendedCharacters
FOR Character = 128 TO 255
PRINT CHR$(Character);
NEXT Character
END SUB

FUNCTION GetFonts$ (IsCGA)
DIM Registers AS RegTypeX

IF IsCGA THEN
Registers.ax = &H3500 OR CGA
Size = SIZECGA
ELSE
Registers.ax = &H3500 OR EGAVGA
Size = SIZEEGAVGA
END IF

INTERRUPTX &H21, Registers, Registers

DEF SEG = Registers.es
Fonts$ = ""
FOR Position = Registers.bx TO Registers.bx + Size
Fonts$ = Fonts$ + CHR$(PEEK(Position))
NEXT Position

Show last 26 lines
GetFonts$ = Fonts$
END FUNCTION

FUNCTION InvertFonts$ (Fonts$)
Inverted$ = Fonts$
FOR Position = 1 TO LEN(Fonts$)
MID$(Inverted$, Position, 1) = CHR$(&HFF XOR ASC(MID$(Inverted$, Position, 1)))
NEXT Position

InvertFonts$ = Inverted$
END FUNCTION

SUB SetFonts (IsCGA, Fonts$)
DIM Registers AS RegTypeX

IF IsCGA THEN
Registers.ax = &H2500 OR CGA
ELSE
Registers.ax = &H2500 OR EGAVGA
END IF

Registers.dx = SADD(Fonts$)
Registers.ds = VARSEG(Fonts$)
INTERRUPTX &H21, Registers, Registers
END SUB

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 7 of 16, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

CGA and Hercules cards do have a character generator ROM, but it is only visible to the card and not to the CPU, and in text mode fonts can only be modified by replacing the ROM chip. The Hercules Plus adds definable fonts, but that card is not emulated by DOSBox.

An 8x8 font for characters 00h-7Fh is stored in system ROM at F000h:FA6Eh in 100% compatible BIOSes, and in CGA and Hercules that table will always be used for drawing the lower half of the character set in graphics modes, but the table pointed at by INT 1Fh is used for the upper half of the character set (128-255). So, for CGA and Hercules, only the upper half of the characters can be modified and only in graphics modes.

Reply 8 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

No, I am not trying to change the ROM. I copy the ROM and modify the pointer at vector 1Fh to point to the copy. Besides this page: http://helppc.netcore2k.net/interrupt/int-1f states: "can be used to change character fonts in CGA graphics mode". Just copying appears to work, it's the inverting that doesn't appear to be working properly.

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 9 of 16, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

The INT 1Fh table should be 1024 bytes for CGA, EGA, and VGA. INT 43h is the pointer that doesn't exist for CGA, which is for the lower half of the character set that is also 1024 bytes in size. When you put both halves of the set together is it 256 x 8 = 2048 bytes, and in EGA/VGA the tables are sequential in the video ROM, but for CGA the lower half is at the specific address I mentioned before.

BTW, AFAIK, SCREEN 12 does not work in CGA.

Reply 10 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

@ripsaw8080:
Oops, I posted code meant for VGA.

Change this:

SCREEN 12
CLS

Fonts$ = GetFonts$(0)
Fonts$ = InvertFonts$(Fonts$)
SetFonts 0, Fonts$

to:

SCREEN 1
CLS

Fonts$ = GetFonts$(-1)
Fonts$ = InvertFonts$(Fonts$)
SetFonts -1, Fonts$

I am using a 1024 byte buffer for CGA - and I intend to only change characters 128-255.

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 11 of 16, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

Well, rather than debugging your program for you, let me assure you that DOSBox will use the INT 1Fh vector to draw characters 128-255 in graphics mode even for the CGA machine type. 😉

Reply 12 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

@ripsaw8080: first of all, thank you for replying to my posts so far. I don't expect you to debug my program for me if you don't want to, however I was hoping someone (be it you or anyone else) would be willing to try to debug my code. I should have mentioned I had tried to debug my code but got stuck. I am not one (at least I think so) to immediately run to a forum at the first error trying to get someone else to fix my problems for me. I usually only make posts like these when I am really at a loss.

In short:
Noone has to debug my stuff for me at all, unless they really feel like it. I don't expect or want that. I only ask for help when I feel I am truly stuck.

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 13 of 16, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

Well, I had looked at your code, but nothing struck me as being incorrect except the SCREEN 12 for CGA, so I figured the problem would be something less than obvious that requires debugging.

Anyway, I pasted your code with the modifications for CGA into a .BAS file and ran it in QB 4.5 (with /l command line switch to load QB.QLB for the INTERRUPTX function), and this is a capture of the screen in DOSBox 0.74-3 (looks the same in SVN) with the machine=cga setting:

qb_000.png
Filename
qb_000.png
File size
1.89 KiB
Views
167 views
File license
Fair use/fair dealing exception

It seems to work as expected, at least to my understanding of what you expected...

Reply 14 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

@ripsaw8080:
I checked again. It works, that is the first time around. (try running it multiple times) After that the characters get corrupted. I have no idea why. Do you (or anyone else) has any idea why?

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels

Reply 15 of 16, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

Likely because you don't set the INT 1Fh vector to point back to the characters in ROM before your program ends, so on subsequent runs you copy from where your font variable was located on the previous run, which won't necessarily be the exact same address for each run.

Of course you should always set the vector back to ROM when your program returns to DOS, because the memory for your "custom" font has been returned to the system as free memory.

Reply 16 of 16, by Peter Swinkels

User metadata
Rank Member
Rank
Member

@ripsaw8080:
D'oh! You're right of course, silly me. I fixed it so it now restores the original vector. And guess what? It works! Thank you!

Do not read if you don't like attention seeking self-advertisements!

Did you read it anyway? Well, you can find all sorts of stuff I made using various programming languages over here:
https://github.com/peterswinkels