Programming for the one and only universal graphics mode.
---------------------------------------------------------
If you need to write a graphics routine that has a reasonable resolution and
which is nearly always present, there is just one choice: mode 12h or the well
known 640 x 480 x 16. This mode is the highest resolution mode which is always
available in all VGA cards.
800 x 600 is better but it either needs a VESA driver installed or the user
must himself figure out how to switch the machine to that mode. Not an easy
task for the majority of "experienced Windows users" (isn't this a paradox?).
Mode 12h is treated as a worst case by many Superior Operating Systems. But
for most purposes it is just fine. It's fast, reasonably easy to use and it is
omni present.
That's why I decided to port my textmode windows to this graphics mode.
The application.
----------------
I built a simple AD converter that measures voltages and converts them into
digits. The ADC fits on a COM port and is completely controlled from software.
The idea was to have different reference voltages, sample rates, scaling
factors, a bar graph display and a 4 digit LED-style read-out.
And in the bottom window there is a "recorder" that plots pixels in real-time.
If all parts have been explained I might post the full package (the sources,
the schematics and such) so that everyone can build one for your own.
How to switch to Mode 12h?
--------------------------
Going to mode 12h is easy. Just use the BIOS interrupt 10h as follows:
mov ax, 012
int 010
and you're in. Remember, I use A86 syntax, so all numbers starting with a
nought are considered hexadecimal.
Plotting in a graphics screen.
------------------------------
Now that we're in Mode 012, we should also try to fill that clear black
rectangle. But first we should define a way of remembering WHERE to put our
cute little dots.
For all my plotting, I use the following structure:
-------------------------------- Window Information Block ------
Infoblk1 STRUC
Win_X dw ? ; top-left window position, X and ...
Win_Y dw ? ; ... Y
Win_wid dw ? ; window width and ...
Win_hgt dw ? ; ... height
CurrX dw ? ; within window, current X-coordinate, ...
CurrY dw ? ; ... and Y
DeltaX dw ?
DeltaY dw ?
Indent dw ? ; Indentation for characters in PIXELS!
Multiply dw ? ; screenwidth handler
Watte01 dw ? ;
BoxCol db ? ; border colour
TxtCol db ? ; text colour
BckCol db ? ; background colour
MenuCol db ? ; menu text colour
ENDS
-------------------------------- Window Information Block ------
It will be clear after looking into this list, that each InfoBlock describes a
window, a rectangular portion of the screen, which is treated as a unity.
Each window is defined by the topleft (x,y) coordinates and the window width
and height. Knowing these four words, the window is defined and fixed on
screen. If the window is to be moved, just adjust the topleft (x,y) position.
Since it is handy to know where in this window we are plotting, I defined two
more X and Y values: "CurrX" and "CurrY". When a request to (un)plot is made,
it will start on these coordinates.
For line drawing and such there are the "DeltaX" and "DeltaY" variables. The
former is for horizontal lines, the latter for vertical lines.
Now that we have our fancy window, where we can plot and draw lines, we also
need some text to see what it's all supposed to be about. The text is plotted
at the CurrX and CurrY postions. Each character is PLOTTED there, so tokens
can be put at ANY location on screen, not just on byte boundaries.
For nice and easy allignments, I defined the variable "Indent" which defines
how many pixels from the left or right margin must remain blank.
Since this software should be as easy to adapt to other resolutions as
possible, there is a need for a "Multiply" variable. This is filled with the
offset address of a dedicated screen multiplier routine.
In Mode 012 there are 640 pixels on a line. That's 80 bytes. So in order to
calculate the pixel address you need to use the following formula:
PixAddr = CurrY * 80 + CurrX / 8
So we need a set of damned fast Mul_80 routines. If needed you can make some
of them and at init-time find out the CPU and hardware and assign a suitable
routine and fill it in in the Window definition structures.
The "Watte01" field is just a filler. Reserved by me.
Since the Mode 012 has 16 colours to spare we should also use them. Therefore
I set up space for 4 colours: Box-, Text-, Background- and Menu-colours.
Each printing routine will make sure the right colour is set.
It will be clear that each window is very flexible to use. If the position is
wrong, just change a few numbers. Also if the colours are not optimal.
And by having several windows assigned to the same area on screen, you can
easily build special effects:
fullscrn dw 0, 0,640,480, 0, 0, 0, 0, 4, mul_80, 0
db 12, 14, 3, 15 ; main screen window
FullScrn just describes the complete screen. It is used for some very general
printing an plotting tasks. It starts at topleft (0,0) and is 640 wide and 480
high.
ParWin2 dw 5, 30,630,150, 8, 9, 0, 0, 4, mul_80, 0
db 10, 11, 3, 11 ; Parameter window
This is a window which is a subwindow of the Full Screen for storing data and
parameters.
PlotWin dw 5,195,630,260, 0, 0, 0, 0, 4, mul_80, 0
db 9, 15, 3, 7 ; Virtual plotting window
This is the Virtual Plotting Window. It has some text, plus the actual
plotting window:
PlotWin2 dw 6,196,628,256, 0, 0, 0, 0, 4, mul_80, 0
db 9, 15, 3, 7 ; Actual plotting window
This is the place where the pixels live. It starts one pixel down/right of the
virtual window and also ends one pixel short of it.
The reason for making this "dummy" window structure was that this way there is
no need for an elaborate checking of extreme ends of the window while erasing
pixels. On the extremes of the "Virtual Plotting Window" there are the pixels
that make up a nice coloured box. It looks not nice when these lines are
erased. And the easiest way to prevent this was by defining two separate
windows: one for constructing the box and one for the actual work.
The 4 digit LED-style read-out is also controlled by four different windows.
Each digit has its own window definition:
------------ Digit Space ------------------------------- Start ---
DigSpac1 dw 16, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0
db 9, 11, 14, 3 ; Digital display, digit 1, MSD
DigSpac2 dw 56, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0
db 9, 11, 14, 3 ; Digital display, digit 2
DigSpac3 dw 96, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0
db 9, 11, 12, 3 ; Digital display. digit 3
DigSpac4 dw 136, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0
db 9, 11, 12, 3 ; Digital display, digit 4, LSD
MSD = Most Significant Digit LSD = Least Significant Digit
------------ Digit Space -------------------------------- End ----
This way it is convenient to allign the digits on screen. As with normal LED-
style digits, the seven segments of them are drawn piece by piece. And erased
if necessary.
As you will know from voltmeters, the MSD is the least likely to change in
time and the LSD is most likely to be different between any two samples. So in
a way it is necessary to control erasing of just one digit without massive
software overheads. Therefore I again chose to use a separate window for each
digit. It makes erasing the digit easier and independent of the other three.
Something else to observe is, that the two or three digits behind the decimal
point have another colour from those before it. This way the user can easily
see the approximate magnitude of the number without having to search for a
decimal point. This is accomplished easily by having different BckCols in the
LSD windows.
This all costs a few bytes extra, but it saves a lot of coding.
How to quickly load a segment register.
---------------------------------------
Segment registers cannot be loaded with immediate data. So you normally put a
register on the stack and use that to transfer the constant to the actual
segment register. This is not necessary. It can be done much easier like
below:
VGA_base dw 0A000 ; for ease of loading segment registers
And the corresponding code:
mov es, [VGA_base]
The detour via the stack or via AX takes more cycles and bytes.
Defining what to print.
-----------------------
In a graphics screen there are an awful lot of places where to store our
text. So we need a way to define where to put which tokens. For this I use the
following construct:
-------------- Topic ----------------------------------- Start ---
Topic MACRO ; start of printing message
dw #1, #2
db #3, #4
#EM
TopicEnd MACRO ; topics stop here
dw 0F000
#EM
Topic 180, 9, 'Start : '
ParaStrt db 'Manual ', 0
Topic 9, 28, 'Power : '
ParaPowr db 'OFF', 0
Topic 360, 55, 'Group : '
ParaGrup db '16 ', 0
TopicEnd
-------------- Topic ------------------------------------ End ----
The Topic Macro puts the first two arguments (the new values for CurrX and
CurrY) in the first two WORD positions of the definition table. The actual
text is then put in the BYTE positions. In most cases there will be no #4
argument, but A86 doesn't care about that.
Each "to-print" table is shut down by an EndTopic Macro. It defines a new
CurrX of -4096. That clearly is out of range, so this is end of table.
In normal operation, small negative values of CurrX and CurrY are accepted and
taken care of, although it can be dangerous to use this feature.
Multiplying by 80.
------------------
On all CPU's form the 486, the MUL instruction is single cycle, so it'll be
damn fast. For all older CPU's, the following code could mean some significant
speed increases:
-------------------- Multiply ------------------------ Start ----
mul_80: push bx ; PixAddr in Mode 012
shl ax, 4
mov bx, ax ; bx = 16 x SCR_Y
shl ax, 2 ; ax = 64 x SCR_Y
add ax, bx ; ax = 80 x SCR_Y
pop bx
ret
-------------------- Multiply ------------------------- End -----
This routine is used over and over again, so a few microseconds more or less
will make a big difference.
Where to leave our pixels?
--------------------------
Suppose you need to plot pixel (3,0). That's an easy one. It will fit in the
very first byte of the VGA memory array. It's segment is 0A000 and it's offset
is plain 0.
But not the full byte, since that would produce a line. No, we need to access
bit 4 of byte 0.
Yes, the first pixel is bit 7 of byte 0 and the 8th pixel is bit 0 of byte 0.
Or, in index-language, CurrX = 0 addresses bit 7, and so on.
So we need to invert the screenposition into a bitposition. We'll come to that
later. Suppose, by some sheer magic, we succeeded in making that conversion,
we still need to tell the VGA which bit is involved. That's done by means of
the following routine:
--------------------- SetMask ------------------------ Start -------
SetMask: push dx ; ah = mask
mov dx, 03CE
mov al, 8
out dx, ax ; set bit mask
pop dx
ret
--------------------- SetMask ------------------------- End --------
This is an optimized routine. The VGA is a 16 bit card, so we can use 16 bit
I/O instructions for adjacent I/O ports. The construct:
mov al, 8
out dx, ax ; set bit mask
is identical to:
mov al, 8
out dx, al
inc dx
mov al, ah
out dx, al
Anyway, the plottingmask is defined to be as loaded in the AH register. We can
put any value in AH, not just one pixel, but also "no pixels" and "all
pixels".
Defining colour in Mode 012.
----------------------------
Colours to use during plotting are defined in a comparable fashion:
--------------------- Set Colour --------------------- Start -------
SetColr: push dx ; ah = colour
mov dx, 03C4
mov al, 2
out dx, ax ; select page register and colour
pop dx
ret
--------------------- Set Colour ---------------------- End --------
In Mode 013 you just can load a bytevalue colour into a memory location and
that's it. So that's an ultrafast resolution, but at the price of resolution.
In Mode 012 we define colour with a series of I/O instructions. If a colour
got set, it remains active until canceled by another SetColr call. Try to
remember this when all on a sudden all kinds of fancy colours start to appear
on screen....
Where to put the pixel?
-----------------------
I have presented the formula some paragrpahs before this one. Basically we
work with virtual coordinates and must translate these to real coordinates
before trying to calculate an address. This is done by:
------------------ VGA memory address ---------------- Start -------
VGaddr: ; calculate address in VGA memory
mov es, [VGA_base] ; quickly load segment register
mov ax, [di.CurrY] ; ax = current Y
add ax, [di.Win_Y] ; adjust for window offset
call [di.Multiply] ; multiply by bytes per row
mov bx, [di.CurrX] ; bx = current X
add bx, [di.Win_X] ; adjust for window offset
shr bx, 3 ; divide by 8
add bx, ax ; bx = index address into video segment
ret
------------------ VGA memory address ----------------- End --------
It's all fairly straightforward.
How do we plot pixels in Mode 012?
----------------------------------
This is a silly process. We cannot access all the 4 colour planes at once, so
we have used SetColr to define which colourplanes are to be affected. This all
is rather complicated. You may either believe me on my word, or consult a 1200
page reference....
Now that we're ready to plot pixels, we do so by the following code:
------------------ VgaPlot -------------------- Start --------------
VgaPlot: mov al, [es:bx] ; Do the actual plotting
mov al, [ToPlot]
mov [es:bx], al
ret
------------------ VgaPlot --------------------- End ---------------
The first line is a read command. It notifies the VGA controller about the
address of the pixelbyte. The resulting data from the read is of no concern.
We immediately replace it with the value of "ToPlot". For plotting there is a
value of "FF" in this byte and for erasing there is a "00" in it.
After this comes the actual plotting function. The write to the specified
address sets the pixels as defined by AL and SetMask.
Adding it all up gives the following code to really plot a pixel:
-------- PlotPix ------------------------------- Start -----------
PlotPix: push ax, bx, cx, es ; plot a point on screen
call VGaddr
mov cx, [di.CurrX] ; calculate plottingmask
add cx, [di.Win_X]
and cx, 0111xB ; cl = position in byte
mov ah, 080
shr ah, cl ; now move the high bit backwards...
call SetMask ; use it to set mask
call VgaPlot ; and do the plotting
pop es, cx, bx, ax
ret
-------- PlotPix -------------------------------- End ------------
That's it to plot a pixel: just a few calls to some procedures we defined
earlier on. The msjority of this procedure is comprised of the way to find the
actual bit-position in the VGA memory byte. Remember, to plot pixel 0 we need
bit 7!
Therefore we load CX with the current X value, correct this for the current
window position and isolate the lower 3 bits. These indicate the position of
the pixel in screenmemory.
mov cx, [di.CurrX] ; calculate plottingmask
add cx, [di.Win_X]
and cx, 0111xB ; cl = position in byte
At this point, CL contains the n-th bit in this byte. So I load AH with the
binary pattern 10000000 and shift it right until the corresponding bit
position is reached:
mov ah, 080
shr ah, cl ; now move the high bit backwards...
I don't know if there are batches of Intel CPU's that have a problem with the
SHR instruction is CL equals zero, but I have not yet noticed any.
Lines: series of pixels.
------------------------
There are three kinds of lines: horizontal, vertical and sloped ones. Vertical
lines are plotted pixel by pixel since all of them end up in different bytes
of VGA memory. Sloped lines are best taken care of by a Bresenham-style line
drawing algorithm (although the digital differential analyser is better).
Horizontal lines are a different kind of line. In these, several adjacent
pixels are plotted. And adjacent pixels mainly are in the same VGA memory
byte. Therefore I made two horizontal line drawers. The one for short lines
(less than 17 pixels) just plots the pixels one by one.
The other algorithm, for lines of 17 pixels or more, tries to fill VGA memory
with as much byte writes as possible.
Taking care of longer horizontal lines.
---------------------------------------
Suppose our line is composed as follows:
First 1 2 3 ... K Last ; byte in video memory
......## ######## ######## ###...### ###..... ; # = pixel to be set
So our line starts at pixel 6 (i.e. bit 1) of VGA memory byte "First". Next it
lasts for N pixels and the last pixel to plot is pixel 2 (or bit 5).
We need some variables to calculate how to proceed with this in the shortest
possible time. This needs some calculations, so for short lines the math
overhead is more work than the actual plotting will take up.
First 1 2 3 ... K Last ; byte in video memory
......## ######## ######## ###...### ###..... ; # = pixel to be set
We first need to know the E-value which describes the number of pixels to plot
in the very first byte. The E-value is calculated as follows:
E-val = 8 - ((CurrX + Win_X) AND 7)
Now we know the number of pixels to plot in the very first VGA memory
location. It would however come in handy if we would know with which plotting
mask this would correspond. That's why we use it to derive the E-mask:
E-mask = FF shr ((8 - E-val) AND 7)
Next we need to know how many pixels there need to be plotted in the last
memory location. L-value and L-mask are determined as follows:
L-val = (Total - E-val) AND 7
L-mask = 080 sar L-val
With the SAR we shift signbits to the right until the number of pixels
corresponds with the number of bits in the mask.
The last parameter we need to know is the actual speeding-up part: the full
bytes that can be plotted. The octet-part of the routine. We do this as
follows:
K-val = (T - E-val - L-val)/8
Now it also becomes clear why I kept the E-val and L-val parameters. They're
just needed for getting the right value for K-val.
There is, however one exceptional situation. Suppose the line we need to plot
is 26 pixels long, starting at pixel 6. This would produce the values:
E-val = 2 E-mask = 00000011
L-val = (26 - 2) AND 7 = 24 AND 7 = 0 L-mask = 00000000
K-val = (26 - 2 - 0)/8 = 3
So, if the line ends on a byte boundary, we may NOT try to plot of
pixels past it (in a plotting loop that starts with CX = 0).
What the H_line procedure does is no more than what I decribed above. Here
comes the source:
-------- H_Line -------------------------------- Start -----------
L0: mov cx, [di.DeltaX] ; do a short line
L1: call PlotPix ; by just repeating a single pixel-
inc [di.CurrX] ; plot and update of CurrX
loop L1 ; until done
pop es, cx, bx, ax
ret
H_Line: push ax, bx, cx, es ; optimized horizontal line drawing
cmp [di.DeltaX], 17 ; too few pixels for a bulk draw?
jb L0
mov cx, [di.CurrX] ; do a long line
add cx, [di.Win_X] ; first get the E-value as described
and cx, 0111xB ; above
mov bx, 8
sub bx, cx
mov [E_val], bx ; pixels to plot in leftmost byte
mov al, 0FF ; now compose the mask to use there
shr al, cl
mov [E_mask], al ; and store it in memory
mov cx, [di.DeltaX] ; CX = length of line
sub cx, [E_val] ; compensate for first-byte pixels
mov ax, cx
and ax, 0111xB ; this many pixels in rigthmost byte
mov [L_val], ax ; and store it in memory
sub cx, ax ; CX = number of pixels inbetween
shr cx, 3 ; divide by 8 pixels per byte
mov [K_val], cx ; number of "full" bytes to plot
clr al ; AL := 0
mov cx, [L_val] ; prepare to compose L-mask
cmp cx, 0 ; any bits in "last byte"
IF ne mov al, bit 7 ; if any bits, setup AH register
dec cx ; compensate for pixel 0, ...
sar al, cl ; ... compose plotting mask and ...
mov [L_mask], al ; ... store it into memory.
; that's it. Let's plot!
call VGaddr ; load BX with address of byte in
; VGA memory
mov ah, [E_mask]
call SetMask ; set plotting mask and ...
call VgaPlot ; ... plot leftmost part
inc bx ; get adjacent address
mov cx, [K_val] ; prepare for bulk-filling
jcxz >L4 ; if nothing to do, jump out
mov ah, 0FF ; else set ALL PIXELS mask
call SetMask
L3: call VgaPlot ; plot middle part
inc bx
loop L3 ; until done
L4: mov ah, [L_mask]
call SetMask
call VgaPlot ; plot remaining pixels
mov ax, [di.DeltaX]
add [di.CurrX], ax ; make sure CurrX is updated
pop es, cx, bx, ax ; and git outa'here
ret
-------- H_Line --------------------------------- End ------------
The preparations are the bulk of the work, but after that is done, the line is
plotted with the lowest amount of I/O overhead.
Vertical lines.
---------------
Vertical lines are simply plot by repeatedly calling PlotPix. It's so simple
that neither need nor want to elaborate on it:
-------- VertLin ------------------------------- Start -----------
VertLin: push cx ; draw a vertical line
mov cx, [di.DeltaY]
L0: call PlotPix
inc [di.CurrY] ; adjust Y coordinate
loop L0 ; but not X value!
pop cx
ret
-------- VertLin -------------------------------- End ------------
What to do with linedrawing functions?
--------------------------------------
Now that we can draw lines, we can also draw boxes and window borders. This
all looks very professional and the overview of a program is enhanced
considerably. Try to figure out how to make the box-drawers by yourself.
Plotting text.
--------------
Now that we have windows that can be put at any plotting position, we also
need to be able to position text at any position. It doesn't look nice if
different windows force text to default to byte boundaries. And with the
experience we got from the H_line function, we are able to make a character
plotter that puts text on screen at ANY position.
I use a 9 x 16 character set. The nineth bit is just always blank, but it
enhances readability considerably. The pixels in the bitmap are all 8 bits
wide and 16 pixels tall.
In exceptional cases, the bitmaps can be plotted at byte boundaries. In 85+ %
of the time this will not be the case. Therefore I do the following:
- do some positioning math first
- repeat 16 times:
- load the byte of the bitmap in AH
- shift AX to the right the correct number of pixels
- plot the AH part
- if plotting on a byte boundary, we're done, else
- repeat 16 times:
- load the byte of the bitmap in AH
- shift AX to the right the correct number of pixels
- plot the AL part
Let's just have a look:
-------- PutChar ------------------------------- Start -----------
L0: add [di.CurrY], 16 ; process 'LF'
L1: pop es, si, cx, bx
ret
L2: mov bx, [di.Indent] ; process 'CR'
mov [di.CurrX], bx
jmp L1
PutChar: push bx, cx, si, es ; print char in al at (x,y)
cmp al, lf
je L0
cmp al, cr
je L2
mov bx, [di.CurrX]
add bx, CHR_WID
cmp bx, [di.Win_wid] ; still safe to print character?
jbe >L3 ; if so, skip over this part
mov bx, [di.Indent]
mov [di.CurrX], bx ; mimick 'CR'
add [di.CurrY], 16 ; mimick 'LF'
L3: mov cx, [di.CurrX]
add cx, [di.Win_X]
and cx, 0111xB
mov [C_val], cl ; store shiftcount for masks
mov bx, 0FF00
shr bx, cl ; setup plotting mask and ...
mov [P_mask], bx ; ... store it
clr ah ; ax = ASCII code
mov si, ax ; make address of pixels in bitmap
shl si, 4
add si, offset bitmap
call VGaddr ; bx = -> in video memory
mov ax, [P_mask] ; only the AH part is used ...
call SetMask ; ... here.
mov cx, 16 ; 16 pixel lines per token
L4: push cx ; we're in the loop now
mov ah, [si] ; AH = pixelpattern
clr al ; AL = empty
mov cl, [C_val] ; get shiftcount
shr ax, cl ; distribute pixelBYTE across a WORD
mov cl, [es:bx] ; dummy read, CL is expendable
mov [es:bx], ah ; actual plotting of this half
add bx, 80 ; point to next pixelbyte address
inc si ; next pixeldata address
pop cx
loop L4 ; and loop back
sub bx, 16 * 80 - 1 ; back to original position
mov ax, [P_mask]
cmp al, 0 ; if nothing to do, ...
je >L6 ; ... skip this chapter
mov ah, al ; else repeat the lot for the right-
call SetMask ; most pixels....
mov cx, 16
sub si, cx ; correct SI
L5: push cx
mov ah, [si]
clr al
mov cl, [C_val]
shr ax, cl
mov cl, [es:bx]
mov [es:bx], al
add bx, 80
inc si
pop cx
loop L5
L6: add [di.CurrX], CHR_WID ; adjust CurrX value before ...
jmp L1 ; ... getting a hike
-------- PutChar -------------------------------- End ------------
So far for plotting text. This routine will dump any character in any place of
the graphics screen. But it needs a CurrX and a CurrY value to know where to
plot things. This is both an advantage and a disadvantage. The advantage is
that we can plot ANYWHERE we like. The disadvantage is that we need to
elaborately specify CurrX and CurrY before the text is where we would like to
have it.
That's why I made the constrcut with the Topic and TopicEnd macro's, as
described above.
Here comes the code for printing a table on screen. We spent a lot of time on
the preparations, and this is the stage where it is going to pay off. Look how
much code we need for printing neat sets of tokens and characters on screen.
-------- Print --------------------------------- Start -----------
print: mov ah, [di.TxtCol] ; print a table of text
call SetColr
L0: lodsw ; get Xpos
cmp ax, 0F000 ; end of table?
je ret ; exit, if so
mov [di.CurrX], ax
lodsw ; get Ypos
mov [di.CurrY], ax
L1: lodsb ; get text
cmp al, 0
je L0
call putchar ; and print it
jmp L1 ; until this line is done
-------- Print ---------------------------------- End ------------
Wit this approach, and starting from a working (empty) framework of routines,
you can design the userinterface of your software within the hour. And it will
look just fine.
The actual code is then the only thing you need to worry about.....
Having such routines, which have been tested and found reliable, you make the
user interface easily and are able to concentrate on the actual coding the
maximum amount of time. If the screen needs another layout (since you couldn't
realize the function you considered), just change a few entries in the table.
Many times just the X or Y values need some adjustment for better lining up,
or for regrouping. No need to worry about the order of the plotting. Just make
sure that the correct window is selected (for the colours) and that the table
is terminated by a TopicEnd.
Conclusion.
-----------
So far my elaboration on the VGA mode 12h. Again, I would rather use 800 x 600
but that mode is not standardised. VGA 12h is standard on all VGA cards, so
it's the best we can universally get and for many applications it is more than
enough.
Please try to make the BoxDrawing function. I will submit the "solution" to
the next issue.
For future issues I will start working on an explanation about mouse-usage.
This little rodent is nice to control many applications. If the screen is well
layed out, you just don't need the keyboard for data entry. Just drag the
mouse along the screen and poke him in the eye.
Here comes the bitmapdata for the character generator:
------- BitMap --------------------------------- Start -----------
bitmap:
.NOLIST
db 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; ^0
db 000, 000, 07E, 081, 0A5, 081, 081, 0BD, 099, 081, 081,
07E, 000, 000, 000, 000 ; ^A
db 000, 000, 07E, 0FF, 0DB, 0FF, 0FF, 0C3, 0E7, 0FF, 0FF,
07E, 000, 000, 000, 000 ; ^B
db 000, 000, 000, 000, 06C, 0FE, 0FE, 0FE, 0FE, 07C, 038,
010, 000, 000, 000, 000 ; ^C
db 000, 000, 000, 000, 010, 038, 07C, 0FE, 07C, 038, 010,
000, 000, 000, 000, 000 ; ^D
db 000, 000, 000, 018, 03C, 03C, 0E7, 0E7, 0E7, 018, 018,
03C, 000, 000, 000, 000 ; ^E
db 000, 000, 000, 018, 03C, 07E, 0FF, 0FF, 07E, 018, 018,
03C, 000, 000, 000, 000 ; ^F
db 000, 000, 000, 000, 000, 000, 018, 03C, 03C, 018, 000,
000, 000, 000, 000, 000 ; ^G
db 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0E7, 0C3, 0C3, 0E7, 0FF,
0FF, 0FF, 0FF, 0FF, 0FF ; ^H
db 000, 000, 000, 000, 000, 03C, 066, 042, 042, 066, 03C,
000, 000, 000, 000, 000 ; ^I
db 0FF, 0FF, 0FF, 0FF, 0FF, 0C3, 099, 0BD, 0BD, 099, 0C3,
0FF, 0FF, 0FF, 0FF, 0FF ; ^J
db 000, 000, 01E, 00E, 01A, 032, 078, 0CC, 0CC, 0CC, 0CC,
078, 000, 000, 000, 000 ; ^K
db 000, 000, 03C, 066, 066, 066, 066, 03C, 018, 07E, 018,
018, 000, 000, 000, 000 ; ^L
db 000, 000, 03F, 033, 03F, 030, 030, 030, 030, 070, 0F0,
0E0, 000, 000, 000, 000 ; ^M
db 000, 000, 07F, 063, 07F, 063, 063, 063, 063, 067, 0E7,
0E6, 0C0, 000, 000, 000 ; ^N
db 000, 000, 000, 018, 018, 0DB, 03C, 0E7, 03C, 0DB, 018,
018, 000, 000, 000, 000 ; ^O
db 000, 080, 0C0, 0E0, 0F0, 0F8, 0FE, 0F8, 0F0, 0E0, 0C0,
080, 000, 000, 000, 000 ; ^P
db 000, 002, 006, 00E, 01E, 03E, 0FE, 03E, 01E, 00E, 006,
002, 000, 000, 000, 000 ; ^Q
db 000, 000, 018, 03C, 07E, 018, 018, 018, 07E, 03C, 018,
000, 000, 000, 000, 000 ; ^R
db 000, 000, 066, 066, 066, 066, 066, 066, 066, 000, 066,
066, 000, 000, 000, 000 ; ^S
db 000, 000, 07F, 0DB, 0DB, 0DB, 07B, 01B, 01B, 01B, 01B,
01B, 000, 000, 000, 000 ; ^T
db 000, 07C, 0C6, 060, 038, 06C, 0C6, 0C6, 06C, 038, 00C,
0C6, 07C, 000, 000, 000 ; ^U
db 000, 000, 000, 000, 000, 000, 000, 000, 0FE, 0FE, 0FE,
0FE, 000, 000, 000, 000 ; ^V
db 000, 000, 018, 03C, 07E, 018, 018, 018, 07E, 03C, 018,
07E, 000, 000, 000, 000 ; ^W
db 000, 000, 018, 03C, 07E, 018, 018, 018, 018, 018, 018,
018, 000, 000, 000, 000 ; ^X
db 000, 000, 018, 018, 018, 018, 018, 018, 018, 07E, 03C,
018, 000, 000, 000, 000 ; ^Y
db 000, 000, 000, 000, 000, 018, 00C, 0FE, 00C, 018, 000,
000, 000, 000, 000, 000 ; ^Z
db 000, 000, 000, 000, 000, 030, 060, 0FE, 060, 030, 000,
000, 000, 000, 000, 000 ; ^[
db 000, 000, 000, 000, 000, 000, 0C0, 0C0, 0C0, 0FE, 000,
000, 000, 000, 000, 000 ; ^\
db 000, 000, 000, 000, 000, 028, 06C, 0FE, 06C, 028, 000,
000, 000, 000, 000, 000 ; ^]
db 000, 000, 000, 000, 010, 038, 038, 07C, 07C, 0FE, 0FE,
000, 000, 000, 000, 000 ; ^^
db 000, 000, 000, 000, 0FE, 0FE, 07C, 07C, 038, 038, 010,
000, 000, 000, 000, 000 ; ^_
db 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; < >
db 000, 000, 018, 03C, 03C, 03C, 018, 018, 018, 000, 018,
018, 000, 000, 000, 000 ; !
db 000, 066, 066, 066, 024, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; "
db 000, 000, 000, 06C, 06C, 0FE, 06C, 06C, 06C, 0FE, 06C,
06C, 000, 000, 000, 000 ; #
db 018, 018, 07C, 0C6, 0C2, 0C0, 07C, 006, 006, 086, 0C6,
07C, 018, 018, 000, 000 ; $
db 000, 000, 000, 000, 0C2, 0C6, 00C, 018, 030, 060, 0C6,
086, 000, 000, 000, 000 ; %
db 000, 000, 038, 06C, 06C, 038, 076, 0DC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ; &
db 000, 030, 030, 030, 060, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; '
db 000, 000, 00C, 018, 030, 030, 030, 030, 030, 030, 018,
00C, 000, 000, 000, 000 ; (
db 000, 000, 030, 018, 00C, 00C, 00C, 00C, 00C, 00C, 018,
030, 000, 000, 000, 000 ; )
db 000, 000, 000, 000, 000, 066, 03C, 0FF, 03C, 066, 000,
000, 000, 000, 000, 000 ; *
db 000, 000, 000, 000, 000, 018, 018, 07E, 018, 018, 000,
000, 000, 000, 000, 000 ; +
db 000, 000, 000, 000, 000, 000, 000, 000, 000, 018, 018,
018, 030, 000, 000, 000 ; ,
db 000, 000, 000, 000, 000, 000, 0FE, 0FE, 000, 000, 000,
000, 000, 000, 000, 000 ; -
db 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 018,
018, 000, 000, 000, 000 ; .
db 000, 000, 000, 000, 002, 006, 00C, 018, 030, 060, 0C0,
080, 000, 000, 000, 000 ; /
db 000, 000, 038, 06C, 0C6, 0C6, 0D6, 0D6, 0C6, 0C6, 06C,
038, 000, 000, 000, 000 ; 0
db 000, 000, 018, 038, 078, 018, 018, 018, 018, 018, 018,
07E, 000, 000, 000, 000 ; 1
db 000, 000, 07C, 0C6, 006, 00C, 018, 030, 060, 0C0, 0C6,
0FE, 000, 000, 000, 000 ; 2
db 000, 000, 07C, 0C6, 006, 006, 03C, 006, 006, 006, 0C6,
07C, 000, 000, 000, 000 ; 3
db 000, 000, 00C, 01C, 03C, 06C, 0CC, 0FE, 00C, 00C, 00C,
01E, 000, 000, 000, 000 ; 4
db 000, 000, 0FE, 0C0, 0C0, 0C0, 0FC, 006, 006, 006, 0C6,
07C, 000, 000, 000, 000 ; 5
db 000, 000, 038, 060, 0C0, 0C0, 0FC, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ; 6
db 000, 000, 0FE, 0C6, 006, 006, 00C, 018, 030, 030, 030,
030, 000, 000, 000, 000 ; 7
db 000, 000, 07C, 0C6, 0C6, 0C6, 07C, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ; 8
db 000, 000, 07C, 0C6, 0C6, 0C6, 07E, 006, 006, 006, 00C,
078, 000, 000, 000, 000 ; 9
db 000, 000, 000, 000, 000, 018, 018, 000, 000, 000, 018,
018, 000, 000, 000, 000 ; :
db 000, 000, 000, 000, 018, 018, 000, 000, 000, 018, 018,
030, 000, 000, 000, 000 ; ;
db 000, 000, 000, 006, 00C, 018, 030, 060, 030, 018, 00C,
006, 000, 000, 000, 000 ; <
db 000, 000, 000, 000, 000, 07E, 000, 000, 07E, 000, 000,
000, 000, 000, 000, 000 ; =
db 000, 000, 000, 060, 030, 018, 00C, 006, 00C, 018, 030,
060, 000, 000, 000, 000 ; >
db 000, 000, 07C, 0C6, 0C6, 00C, 018, 018, 018, 000, 018,
018, 000, 000, 000, 000 ; ?
db 000, 000, 000, 07C, 0C6, 0C6, 0DE, 0DE, 0DE, 0DC, 0C0,
07C, 000, 000, 000, 000 ; @
db 000, 000, 010, 038, 06C, 0C6, 0C6, 0FE, 0C6, 0C6, 0C6,
0C6, 000, 000, 000, 000 ; A
db 000, 000, 0FC, 066, 066, 066, 07C, 066, 066, 066, 066,
0FC, 000, 000, 000, 000 ; B
db 000, 000, 03C, 066, 0C2, 0C0, 0C0, 0C0, 0C0, 0C2, 066,
03C, 000, 000, 000, 000 ; C
db 000, 000, 0F8, 06C, 066, 066, 066, 066, 066, 066, 06C,
0F8, 000, 000, 000, 000 ; D
db 000, 000, 0FE, 066, 062, 068, 078, 068, 060, 062, 066,
0FE, 000, 000, 000, 000 ; E
db 000, 000, 0FE, 066, 062, 068, 078, 068, 060, 060, 060,
0F0, 000, 000, 000, 000 ; F
db 000, 000, 03C, 066, 0C2, 0C0, 0C0, 0DE, 0C6, 0C6, 066,
03A, 000, 000, 000, 000 ; G
db 000, 000, 0C6, 0C6, 0C6, 0C6, 0FE, 0C6, 0C6, 0C6, 0C6,
0C6, 000, 000, 000, 000 ; H
db 000, 000, 03C, 018, 018, 018, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ; I
db 000, 000, 01E, 00C, 00C, 00C, 00C, 00C, 0CC, 0CC, 0CC,
078, 000, 000, 000, 000 ; J
db 000, 000, 0E6, 066, 066, 06C, 078, 078, 06C, 066, 066,
0E6, 000, 000, 000, 000 ; K
db 000, 000, 0F0, 060, 060, 060, 060, 060, 060, 062, 066,
0FE, 000, 000, 000, 000 ; L
db 000, 000, 0C6, 0EE, 0FE, 0FE, 0D6, 0C6, 0C6, 0C6, 0C6,
0C6, 000, 000, 000, 000 ; M
db 000, 000, 0C6, 0E6, 0F6, 0FE, 0DE, 0CE, 0C6, 0C6, 0C6,
0C6, 000, 000, 000, 000 ; N
db 000, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ; O
db 000, 000, 0FC, 066, 066, 066, 07C, 060, 060, 060, 060,
0F0, 000, 000, 000, 000 ; P
db 000, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0D6, 0DE,
07C, 00C, 00E, 000, 000 ; Q
db 000, 000, 0FC, 066, 066, 066, 07C, 06C, 066, 066, 066,
0E6, 000, 000, 000, 000 ; R
db 000, 000, 07C, 0C6, 0C6, 060, 038, 00C, 006, 0C6, 0C6,
07C, 000, 000, 000, 000 ; S
db 000, 000, 07E, 07E, 05A, 018, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ; T
db 000, 000, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ; U
db 000, 000, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 06C, 038,
010, 000, 000, 000, 000 ; V
db 000, 000, 0C6, 0C6, 0C6, 0C6, 0D6, 0D6, 0D6, 0FE, 0EE,
06C, 000, 000, 000, 000 ; W
db 000, 000, 0C6, 0C6, 06C, 07C, 038, 038, 07C, 06C, 0C6,
0C6, 000, 000, 000, 000 ; X
db 000, 000, 066, 066, 066, 066, 03C, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ; Y
db 000, 000, 0FE, 0C6, 086, 00C, 018, 030, 060, 0C2, 0C6,
0FE, 000, 000, 000, 000 ; Z
db 000, 000, 03C, 030, 030, 030, 030, 030, 030, 030, 030,
03C, 000, 000, 000, 000 ; [
db 000, 000, 000, 080, 0C0, 0E0, 070, 038, 01C, 00E, 006,
002, 000, 000, 000, 000 ; \
db 000, 000, 03C, 00C, 00C, 00C, 00C, 00C, 00C, 00C, 00C,
03C, 000, 000, 000, 000 ; ]
db 010, 038, 06C, 0C6, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; ^
db 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
000, 0FF, 0FF, 000, 000 ; _
db 030, 030, 018, 000, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; `
db 000, 000, 000, 000, 000, 078, 00C, 07C, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ; a
db 000, 000, 0E0, 060, 060, 078, 06C, 066, 066, 066, 066,
07C, 000, 000, 000, 000 ; b
db 000, 000, 000, 000, 000, 07C, 0C6, 0C0, 0C0, 0C0, 0C6,
07C, 000, 000, 000, 000 ; c
db 000, 000, 01C, 00C, 00C, 03C, 06C, 0CC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ; d
db 000, 000, 000, 000, 000, 07C, 0C6, 0FE, 0C0, 0C0, 0C6,
07C, 000, 000, 000, 000 ; e
db 000, 000, 038, 06C, 064, 060, 0F0, 060, 060, 060, 060,
0F0, 000, 000, 000, 000 ; f
db 000, 000, 000, 000, 000, 076, 0CC, 0CC, 0CC, 0CC, 0CC,
07C, 00C, 0CC, 078, 000 ; g
db 000, 000, 0E0, 060, 060, 06C, 076, 066, 066, 066, 066,
0E6, 000, 000, 000, 000 ; h
db 000, 000, 018, 018, 000, 038, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ; i
db 000, 000, 006, 006, 000, 00E, 006, 006, 006, 006, 006,
006, 066, 066, 03C, 000 ; j
db 000, 000, 0E0, 060, 060, 066, 06C, 078, 078, 06C, 066,
0E6, 000, 000, 000, 000 ; k
db 000, 000, 038, 018, 018, 018, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ; l
db 000, 000, 000, 000, 000, 0EC, 0FE, 0D6, 0D6, 0D6, 0D6,
0C6, 000, 000, 000, 000 ; m
db 000, 000, 000, 000, 000, 0DC, 066, 066, 066, 066, 066,
066, 000, 000, 000, 000 ; n
db 000, 000, 000, 000, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ; o
db 000, 000, 000, 000, 000, 0DC, 066, 066, 066, 066, 066,
07C, 060, 060, 0F0, 000 ; p
db 000, 000, 000, 000, 000, 076, 0CC, 0CC, 0CC, 0CC, 0CC,
07C, 00C, 00C, 01E, 000 ; q
db 000, 000, 000, 000, 000, 0DC, 076, 066, 060, 060, 060,
0F0, 000, 000, 000, 000 ; r
db 000, 000, 000, 000, 000, 07C, 0C6, 060, 038, 00C, 0C6,
07C, 000, 000, 000, 000 ; s
db 000, 000, 010, 030, 030, 0FC, 030, 030, 030, 030, 036,
01C, 000, 000, 000, 000 ; t
db 000, 000, 000, 000, 000, 0CC, 0CC, 0CC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ; u
db 000, 000, 000, 000, 000, 066, 066, 066, 066, 066, 03C,
018, 000, 000, 000, 000 ; v
db 000, 000, 000, 000, 000, 0C6, 0C6, 0D6, 0D6, 0D6, 0FE,
06C, 000, 000, 000, 000 ; w
db 000, 000, 000, 000, 000, 0C6, 06C, 038, 038, 038, 06C,
0C6, 000, 000, 000, 000 ; x
db 000, 000, 000, 000, 000, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
07E, 006, 00C, 0F8, 000 ; y
db 000, 000, 000, 000, 000, 0FE, 0CC, 018, 030, 060, 0C6,
0FE, 000, 000, 000, 000 ; z
db 000, 000, 00E, 018, 018, 018, 070, 018, 018, 018, 018,
00E, 000, 000, 000, 000 ; {
db 000, 000, 018, 018, 018, 018, 000, 018, 018, 018, 018,
018, 000, 000, 000, 000 ; |
db 000, 000, 070, 018, 018, 018, 00E, 018, 018, 018, 018,
070, 000, 000, 000, 000 ; }
db 000, 000, 076, 0DC, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ; ~
db 000, 000, 000, 000, 010, 038, 06C, 0C6, 0C6, 0C6, 0FE,
000, 000, 000, 000, 000 ; DEL
db 000, 000, 03C, 066, 0C2, 0C0, 0C0, 0C0, 0C2, 066, 03C,
00C, 006, 07C, 000, 000 ;
db 000, 000, 0CC, 000, 000, 0CC, 0CC, 0CC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 00C, 018, 030, 000, 07C, 0C6, 0FE, 0C0, 0C0, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 010, 038, 06C, 000, 078, 00C, 07C, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 000, 0CC, 000, 000, 078, 00C, 07C, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 060, 030, 018, 000, 078, 00C, 07C, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 038, 06C, 038, 000, 078, 00C, 07C, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 03C, 066, 060, 060, 066, 03C, 00C,
006, 03C, 000, 000, 000 ;
db 000, 010, 038, 06C, 000, 07C, 0C6, 0FE, 0C0, 0C0, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 000, 0C6, 000, 000, 07C, 0C6, 0FE, 0C0, 0C0, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 060, 030, 018, 000, 07C, 0C6, 0FE, 0C0, 0C0, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 000, 066, 000, 000, 038, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ;
db 000, 018, 03C, 066, 000, 038, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ;
db 000, 060, 030, 018, 000, 038, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ;
db 000, 0C6, 000, 010, 038, 06C, 0C6, 0C6, 0FE, 0C6, 0C6,
0C6, 000, 000, 000, 000 ;
db 038, 06C, 038, 000, 038, 06C, 0C6, 0C6, 0FE, 0C6, 0C6,
0C6, 000, 000, 000, 000 ;
db 018, 030, 060, 000, 0FE, 066, 060, 07C, 060, 060, 066,
0FE, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 0CC, 076, 036, 07E, 0D8, 0D8,
06E, 000, 000, 000, 000 ;
db 000, 000, 03E, 06C, 0CC, 0CC, 0FE, 0CC, 0CC, 0CC, 0CC,
0CE, 000, 000, 000, 000 ;
db 000, 010, 038, 06C, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 000, 0C6, 000, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 060, 030, 018, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 030, 078, 0CC, 000, 0CC, 0CC, 0CC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 060, 030, 018, 000, 0CC, 0CC, 0CC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 000, 0C6, 000, 000, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
07E, 006, 00C, 078, 000 ;
db 000, 0C6, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 0C6, 000, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 018, 018, 03C, 066, 060, 060, 060, 066, 03C, 018,
018, 000, 000, 000, 000 ;
db 000, 038, 06C, 064, 060, 0F0, 060, 060, 060, 060, 0E6,
0FC, 000, 000, 000, 000 ;
db 000, 000, 066, 066, 03C, 018, 07E, 018, 07E, 018, 018,
018, 000, 000, 000, 000 ;
db 000, 0F8, 0CC, 0CC, 0F8, 0C4, 0CC, 0DE, 0CC, 0CC, 0CC,
0C6, 000, 000, 000, 000 ;
db 000, 00E, 01B, 018, 018, 018, 07E, 018, 018, 018, 018,
018, 0D8, 070, 000, 000 ;
db 000, 018, 030, 060, 000, 078, 00C, 07C, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 00C, 018, 030, 000, 038, 018, 018, 018, 018, 018,
03C, 000, 000, 000, 000 ;
db 000, 018, 030, 060, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 018, 030, 060, 000, 0CC, 0CC, 0CC, 0CC, 0CC, 0CC,
076, 000, 000, 000, 000 ;
db 000, 000, 076, 0DC, 000, 0DC, 066, 066, 066, 066, 066,
066, 000, 000, 000, 000 ;
db 076, 0DC, 000, 0C6, 0E6, 0F6, 0FE, 0DE, 0CE, 0C6, 0C6,
0C6, 000, 000, 000, 000 ;
db 000, 03C, 06C, 06C, 03E, 000, 07E, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 038, 06C, 06C, 038, 000, 07C, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 030, 030, 000, 030, 030, 060, 0C0, 0C6, 0C6,
07C, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 0FE, 0C0, 0C0, 0C0, 0C0,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 0FE, 006, 006, 006, 006,
000, 000, 000, 000, 000 ;
db 000, 0C0, 0C0, 0C2, 0C6, 0CC, 018, 030, 060, 0DC, 086,
00C, 018, 03E, 000, 000 ;
db 000, 0C0, 0C0, 0C2, 0C6, 0CC, 018, 030, 066, 0CE, 09E,
03E, 006, 006, 000, 000 ;
db 000, 000, 018, 018, 000, 018, 018, 018, 03C, 03C, 03C,
018, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 036, 06C, 0D8, 06C, 036, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 0D8, 06C, 036, 06C, 0D8, 000,
000, 000, 000, 000, 000 ;
db 011, 044, 011, 044, 011, 044, 011, 044, 011, 044, 011,
044, 011, 044, 011, 044 ;
db 055, 0AA, 055, 0AA, 055, 0AA, 055, 0AA, 055, 0AA, 055,
0AA, 055, 0AA, 055, 0AA ;
db 0DD, 077, 0DD, 077, 0DD, 077, 0DD, 077, 0DD, 077, 0DD,
077, 0DD, 077, 0DD, 077 ;
db 018, 018, 018, 018, 018, 018, 018, 018, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 018, 018, 0F8, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 0F8, 018, 0F8, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 036, 036, 036, 036, 036, 036, 036, 0F6, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 000, 000, 000, 000, 000, 000, 000, 0FE, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 000, 000, 000, 000, 000, 0F8, 018, 0F8, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 036, 036, 036, 036, 036, 0F6, 006, 0F6, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 036, 036, 036, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 000, 000, 000, 000, 000, 0FE, 006, 0F6, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 0F6, 006, 0FE, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 036, 036, 036, 036, 036, 036, 036, 0FE, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 018, 018, 018, 018, 018, 0F8, 018, 0F8, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 000, 0F8, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 018, 018, 01F, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 018, 018, 018, 018, 018, 018, 018, 0FF, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 000, 0FF, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 018, 018, 01F, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 000, 000, 000, 000, 000, 000, 000, 0FF, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 018, 018, 018, 018, 018, 018, 018, 0FF, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 01F, 018, 01F, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 036, 036, 036, 036, 036, 036, 036, 037, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 037, 030, 03F, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 03F, 030, 037, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 0F7, 000, 0FF, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 0FF, 000, 0F7, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 037, 030, 037, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 000, 000, 000, 000, 000, 0FF, 000, 0FF, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 036, 036, 036, 036, 036, 0F7, 000, 0F7, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 018, 018, 018, 018, 018, 0FF, 000, 0FF, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 036, 036, 036, 036, 036, 036, 036, 0FF, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 0FF, 000, 0FF, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 000, 000, 000, 000, 000, 000, 000, 0FF, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 036, 036, 03F, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 018, 018, 018, 018, 018, 01F, 018, 01F, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 01F, 018, 01F, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 000, 000, 000, 000, 000, 000, 000, 03F, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 036, 036, 036, 036, 036, 036, 036, 0FF, 036, 036, 036,
036, 036, 036, 036, 036 ;
db 018, 018, 018, 018, 018, 0FF, 018, 0FF, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 018, 018, 0F8, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 000, 01F, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0FF,
0FF, 0FF, 0FF, 0FF, 0FF ;
db 000, 000, 000, 000, 000, 000, 000, 0FF, 0FF, 0FF, 0FF,
0FF, 0FF, 0FF, 0FF, 0FF ;
db 0F0, 0F0, 0F0, 0F0, 0F0, 0F0, 0F0, 0F0, 0F0, 0F0, 0F0,
0F0, 0F0, 0F0, 0F0, 0F0 ;
db 00F, 00F, 00F, 00F, 00F, 00F, 00F, 00F, 00F, 00F, 00F,
00F, 00F, 00F, 00F, 00F ;
db 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 0FF, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 076, 0DC, 0D8, 0D8, 0D8, 0DC,
076, 000, 000, 000, 000 ;
db 000, 000, 078, 0CC, 0CC, 0CC, 0D8, 0CC, 0C6, 0C6, 0C6,
0CC, 000, 000, 000, 000 ;
db 000, 000, 0FE, 0C6, 0C6, 0C0, 0C0, 0C0, 0C0, 0C0, 0C0,
0C0, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 0FE, 06C, 06C, 06C, 06C, 06C, 06C,
06C, 000, 000, 000, 000 ;
db 000, 000, 000, 0FE, 0C6, 060, 030, 018, 030, 060, 0C6,
0FE, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 07E, 0D8, 0D8, 0D8, 0D8, 0D8,
070, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 066, 066, 066, 066, 066, 07C, 060,
060, 0C0, 000, 000, 000 ;
db 000, 000, 000, 000, 076, 0DC, 018, 018, 018, 018, 018,
018, 000, 000, 000, 000 ;
db 000, 000, 000, 07E, 018, 03C, 066, 066, 066, 03C, 018,
07E, 000, 000, 000, 000 ;
db 000, 000, 000, 038, 06C, 0C6, 0C6, 0FE, 0C6, 0C6, 06C,
038, 000, 000, 000, 000 ;
db 000, 000, 038, 06C, 0C6, 0C6, 0C6, 06C, 06C, 06C, 06C,
0EE, 000, 000, 000, 000 ;
db 000, 000, 01E, 030, 018, 00C, 03E, 066, 066, 066, 066,
03C, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 07E, 0DB, 0DB, 0DB, 07E, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 003, 006, 07E, 0DB, 0DB, 0F3, 07E, 060,
0C0, 000, 000, 000, 000 ;
db 000, 000, 01C, 030, 060, 060, 07C, 060, 060, 060, 030,
01C, 000, 000, 000, 000 ;
db 000, 000, 000, 07C, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6, 0C6,
0C6, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 0FE, 000, 000, 0FE, 000, 000, 0FE,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 018, 018, 07E, 018, 018, 000, 000,
0FF, 000, 000, 000, 000 ;
db 000, 000, 000, 030, 018, 00C, 006, 00C, 018, 030, 000,
07E, 000, 000, 000, 000 ;
db 000, 000, 000, 00C, 018, 030, 060, 030, 018, 00C, 000,
07E, 000, 000, 000, 000 ;
db 000, 000, 00E, 01B, 01B, 018, 018, 018, 018, 018, 018,
018, 018, 018, 018, 018 ;
db 018, 018, 018, 018, 018, 018, 018, 018, 0D8, 0D8, 0D8,
070, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 018, 018, 000, 07E, 000, 018, 018,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 076, 0DC, 000, 076, 0DC, 000,
000, 000, 000, 000, 000 ;
db 000, 038, 06C, 06C, 038, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 000, 018, 018, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 000, 000, 018, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 00F, 00C, 00C, 00C, 00C, 00C, 0EC, 06C, 06C, 03C,
01C, 000, 000, 000, 000 ;
db 000, 0D8, 06C, 06C, 06C, 06C, 06C, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 070, 0D8, 030, 060, 0C8, 0F8, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 07C, 07C, 07C, 07C, 07C, 07C, 07C,
000, 000, 000, 000, 000 ;
db 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000 ;
.LIST
------- BitMap ---------------------------------- End ------------