Contents of the CGA.TXT file
Partial requirements for CGA, i.e. 640 x 200 and 320 x 200.
The main difference for CGA is the total absurdity of two video buffers, one
for even rows and one for odd. The other important difference is that CGA
starts at B8000 hex while EGA-VGA starts at A0000 hex.
You will, I know, make backups of the files you are going to amend.
If you make the following alterations to COMPLOT.SEQ, etc, then the graphics
should run in CGA 640 x 200. There is more to do for 320 x 200 x 4 colours.
It is quite a contract. Much easier, but more expensive, to buy an EGA card
and wave CGA a none-too-fond farewell.
While we are on that topic, I, in common with many others, think EGA was a
mistake. Not in the class of CGA, which is a disaster, but a mistake, never-
theless. If you are looking at video cards for serious reasons - by which I
mean technical drawing and routines for display and for others to use - VGA
is worth considering. In 640 x 480 and 800 x 600, and assumably for higher
resolutions, pixels are square. It really does simplify life when you can
type 20 20 100 100 LINE and produce a line at 45 degrees, or draw a
square by drawing a box with equal sides, or draw a circle by drawing an
ellipse with equal radii.
: c320 4 319 199 set-screen ; \ 320 x 200 x 4 colors
: c640 4 639 199 set-screen ; \ 640 x 200
code palet \ set palette for 320 x 200
pop bx mov bh, # 1 mov ax, # b00 int 10 next end-code
Change CLR-SCR to:
code clr-scr \ wipe the graphics screen
mov ax, # b000 mov es, ax \ start of 1st video buffer
xor ax, ax mov di, ax \ zero them
mov cx, # 2000 \ no. iters - 2000H = 8192D
rep stosw \ stuff zeros everywhere
Note that BKGRD does strange things in CGA: sets the border in 80 x 25, the
the background in 320 x 200 and the foreground in 640 x 200. IBM's doing, not
Change LOAD-Y-OFFSET to:
: load-y-offset 64 0 do \ does 2 rows per iteration 64H = 100D
b800 i 5 * + i 4 * yoffset + ! \ even rows - 1st buffer
ba00 i 5 * + i 4 * 2+ yoffset + ! \ odd rows - 2nd buffer
create color-tab 0 c, 40 c, 80 c, b0 c, 0 c, 10 c, 20 c, 30,
0 c, 4 c, 8 c, 0b c, 0 c, 1 c, 2 c, 3 c,
0 - 3 COLOR selects 320 x 200 colour.
Change PLOT to:
code plot \ X, Y coords
mov ah, # 0f int 10 \ get mode to al
cmp al, # 6
0= if \ 640 x 200
* pop bx shl bx, # 1 \ Y coord * 2 for table lookup
* mov es, yoffset [bx] \ offset into video
pop bx shl bx, # 1 \ X coord * 2 for table lookup
mov bx, xoffset [bx] \ 2-to-the-bit and X byte
mov ah, bh \ 2-to-the-bit to ah
xor bh, bh \ X byte in bx
or 0 es: [bx], ah \ set the pixel
else \ 320 x 200
/ pop bx shl bx, # 1
/ mov es, yoffset [bx]
pop ax \ X coord
mov bx, # 3 and bx, ax \ "bits" of X for COLOR-TAB lookup
shr ax, # 1 shr ax, # 1 \ X byte
shl bx, # 1 shl bx, # 1 \ * 4 for COLOR-TAB lookup
mov ah, color-tab [bx] \ bit pattern to ah
mov bl, al \ X byte to bx
or 0 es: [bx], ah \ plot it
Shorter but no faster by doing the two lines marked * before reading the mode
and deleting the two lines marked /.
A pixel eraser - XPLOT - by substituting XOR for OR, twice.
That is straightforward. From here on, there are many things to do, including
a major decison to make.
From LINE.SEQ delete LINEON and LINEOFF and all calls to them in the drawing
subroutines. Delete every occurrence of MOV AL, # 8 and OUT DX, AX. Their
purpose is latching, and writing to, the bit planes in EGA-VGA and they are
irrelevant to CGA. In the subroutine HLINE, delete PUSH IP, MOV IP, DX and
POP IP. Change each occurrence of MOV AH, .... to MOV AL, .... Change each
MOVSB to STOSB.
In VLINE we cannot go to the next row simply by adding 80 bytes to the
current byte; we have to leap like idiots between buffers. From an even row
to the next row we jump 2000H forward and from an odd row we jump 2000H - 50H
Furthermore, we need to know whether the line starts on an odd or an even
row. If the line starts on an even Y, we jump forwards and backwards; if on
an odd Y, backwards and forwards. I would take the test for, and call to,
VLINE out of LINE and erase VLINE - but - your choice.
Now, and I am holding my breath, LINE should run in 640 x 200. Not really
going short of oxygen. I typed in the alterations, checked them thoroughly,
and nearly fell off my chair when LINE ran first up. Given that it's done,
you can save yourself the trouble. The amended COMPLOT.SEQ and LINE.SEQ are
combined in CPLINE.SEQ. Note that CPLINE.SEQ does only 640 x 200 so you still
have to do 320 x 200 x 4.
Incidentally, if my calculations are correct, horizontal lines in 640 x 200
run at about 6,500,000 pixels / sec.
You can tidy up the subroutines DY>, DX>+ and DX>- because the DX register
is free. Keep INCFAC in a register in DY> and, with a bit of juggling, get
rid of the SHR DI, # 1 SHL DI, # 1 in the other two.
ARC in SINARC.SEQ should now be ok because it calls SUB-LINE.
If you have any interest in the mirror plotting routines in 4PLOT.SEQ, delete
GRAFON and GRAFOFF and calls to them in 4PLOT. Delete MOV AL, # 8 and
OUT DX, AX. 4LINE should run as is.
ELLIPSE.SEQ needs the same kind of treatment. From SUB-ELL delete the three
lines: mov al, # 8
mov dx, # 3ce
out dx, ax
and the three subsequent occurrences of OUT DX, AX.
Delete from ELLIPSE the calls to GRAFON and GRAFOFF and the line
MOV DX, # 3CE preceding CALL GRAFOFF - third last line. ELLIPSE should run.
However, the problem with the subroutine VLINE crops up again with CUT and
PASTE. If you want to use the curve shifters in SHAPE.SEQ, you cannot avoid
it. Restrict CUT to starting on even rows and finishing on odd rows for this
reason. Do the first row and jump forward decimal 8192 MINUS the length of
the cut in bytes. Then jump back 8112 (= 8192-80) PLUS the length of the cut
in bytes. Same thing for PASTE. Something like:
Number of rows divided by 2 in AL, number of bytes in cut in AH, first byte
first line in IP.
mov bx, # 8192 \ for forward index into odd-row buffer
sub bx, cx \ cx contains the width in bytes of your cut
mov dx, # 8112 \ for backward index into even-row buffer
add dx, cx
repnz movsb \ do first line
mov cl, ah \ reset no. iters counter
add ip, bx \ reset ip for 2nd row & jmp to odd buffer
repnz movsb \ do 2nd line
mov cl, ah
sub ip, dx \ reset for next 2 rows by jmp'g back to
dec al \ even buffer
It will do two rows at a time.
TURTLE.SEQ and SHAPE.SEQ should be ok. DECIMALS.SEQ has nothing to do with
graphics and will run on anything.
And the major decision is whether to alter the subroutines to select between
640 x 200 and 320 x 200 or to write separate subroutines for the two modes.
I would go for the separate routines because they would be easier to main-
tain. Also, they will be faster than choosing between modes while drawing.
Consider the routine PLOT. It would be clumsy to have separate plotters for
640 x 200 and 320 x 200 but they would be much faster because they would
dispense with the test and branch. In practice, the speed of PLOT is not
important because you don't have much call to plot single pixels: although it
is useful for locating a point on the screen.
I have tried only the alterations to COMPLOT and LINE.