|
The Memotech MTX Series |
|
80 Column Card CP/M Firmware
Introduction
The 80 column card in the Memotech FDX and
SDX disk systems is controlled by the CP/M BIOS using the
BOOTCRT.MAC driver module in the
SDX/FDX Boot ROM.
Andy Key
has an excellent
page on his
website which describes the structure of the CP/M boot ROM
and provides the source files to facilitate building various
implementations of the ROM. The original Memotech source files
include a minimal set of comments, but the functions of the code
are not particularly well described.
Bill Brendling has been analysing the
BOOTCRT module as part of his work to create an 80 Column card
add-on to work in tandem with
CFX for Memotech
owners who do not have access to SDX or FDX disk drive hardware.
"There are a few gaps and ambiguities in
the existing documentation, so I have been reverse engineering
the CP/M driver and attempting to produce a more complete
definition."
BOOTCRT.MAC Annotated with additional comments by Bill
BOOTCRT.MAC Annotated with additional comments by Bill
MTX 80 Column CP/M Driver Description by Bill (Text below)
CP/M Driver for MTX 80
Column Card
W
Brendling
22 Jan 2017
In
order to develop a new 80 column display card for the MTX which
is compatible with the existing one, it is necessary to know
what the existing one does. While this has been documented by
Memotech there are some gaps and ambiguities. Therefore this
documentation has been developed by reverse engineering the CP/M
driver source code “BOOTCTR.MAC” downloaded from Andy Key’s
website. In the process of developing this documentation, I have
produced a more heavily annotated version of this source.
Typically a printable
character will be displayed upon the screen, using the current
printing attributes, and the cursor advanced one space. This may
be affected by the write mask (Esc “W”). If this is set, the
character may be updated, without changing the existing
on-screen attributes, or the attributes may be updated without
changing the displayed character.
If the cursor goes beyond
the bottom of the screen, either the display will scroll (scroll
mode) or the cursor will return to the top of the screen (page
mode). There is nothing in the driver to pause and wait for a
key press at the end of a page. This functionality must be at a
higher level.
Assuming that a character
is output, the glyph displayed depends upon the font selected,
and whether or not the graphics mode bit is set in the attribute
byte.
If the graphics mode bit
is clear, then the following table gives the glyph displayed:
Character
Code
|
Standard Font
|
Alternate
Font
|
Special
Graphics Font
|
0x20 - 0x3F
|
Standard numerals
|
Alternate
numerals
|
Standard numerals
|
0x40 - 0x5F
|
Standard upper
case
|
Alternate upper
case
|
Special graphics
(upper)
|
0x60 - 0x7F
|
Standard lower
case
|
Alternate lower
case
|
Special graphics
(lower)
|
0x80 - 0x9F
|
Special graphics
(lower)
|
Special graphics
(lower)
|
Special graphics
(upper)
|
0xA0 - 0xBF
|
Alternate
numerals
|
Alternate
numerals
|
Standard numerals
|
0xC0 - 0xDF
|
Alternate upper
case
|
Alternate upper
case
|
Special graphics
(upper)
|
0xE0 - 0xFF
|
Alternate lower
case
|
Alternate lower
case
|
Special graphics
(lower)
|
Each of the entries in
the table above should be taken to include the symbol glyphs
adjacent to the numerals or letters.
It should be noted that
character code 0x7F is printable. It does not delete the
character under the cursor.
If the graphics mode
attribute is set, then the corresponding glyph from the graphics
PROM is displayed:
Character
Code
|
Standard Font
|
Alternate
Font
|
Special
Graphics Font
|
0x20 - 0x3F
|
Graphic 0x20 -
0x3F
|
Graphic 0xA0 -
0xBF
|
Graphic 0x20 -
0x3F
|
0x40 - 0x5F
|
Graphic 0x40 -
0x5F
|
Graphic 0xC0 -
0xDF
|
Graphic 0x00 -
0x1F
|
0x60 - 0x7F
|
Graphic 0x60 -
0x7F
|
Graphic 0xE0 -
0xFF
|
Graphic 0x80 -
0x9F
|
0x80 - 0x9F
|
Graphic 0x80 -
0x9F
|
Graphic 0x80 -
0x9F
|
Graphic 0x00 -
0x1F
|
0xA0 - 0xBF
|
Graphic 0xA0 -
0xBF
|
Graphic 0xA0 -
0xBF
|
Graphic 0x20 -
0x3F
|
0xC0 - 0xDF
|
Graphic 0xC0 -
0xDF
|
Graphic 0xC0 -
0xDF
|
Graphic 0x00 -
0x1F
|
0xE0 - 0xFF
|
Graphic 0xE0 -
0xFF
|
Graphic 0xE0 -
0xFF
|
Graphic 0x80 -
0x9F
|
Control Codes (0x00 –
0x1F)
Control codes may be
followed by one or more data bytes update the display in various
ways, as detailed in the following table. Any screen updates are
affected by the settings of the write mask, as for the printable
characters.
Control Code
|
Data Bytes
|
Action
|
^@ (0x00)
|
|
Does nothing
|
^A (0x01)
|
m, n
|
Plot a point:
x = m – 32, y = n
– 32
If x < 0, x >
159, y < 0 or y > 95 do nothing.
If existing
character cell containing point does not have graphics
mode attribute bit set, zero the character, and set the
graphics mode attribute bit.
If the 3 lsb of
the non-printing attributes byte are zero (Black
foreground), then un-plot the selected point (clear the
corresponding bit in the character cell) and retain the
existing attributes for the character cell (except
setting graphics mode if not previously set).
Otherwise, plot
the selected point (set the corresponding bit in the
character cell) and update the attributes of the
character cell, using the non-printing attributes byte,
with the graphics mode bit set.
|
^B (0x02)
|
m1, n1, m2, n2
|
Draws a line. See
section below on the line drawing algorithm.
|
^C (0x03)
|
m, n
|
Sets cursor
position: x = m – 32, y = n - 32
If 0 <= x <= 79
sets horizontal position, otherwise unchanged.
If 0 <= y <= 23
sets vertical position, otherwise unchanged.
|
^D (0x04)
|
m
|
Set background
colour. Sets bits 3-5 of both printing and non-printing
attributes to 3 lsb of m.
|
^E (0x05)
|
|
Erase to end of
line. Fill from cursor position to end of line with
space character and non-printing attribute.
|
^F (0x06)
|
m
|
Set attributes.
Set both the printing and non-printing attribute bytes
to the value of m.
|
^G (0x07)
|
|
Sounds the Bell
|
^H (0x08)
|
|
Moves the cursor
back one space. If at the beginning of a line, moves
back to the end of the previous line. If at top of
screen, does nothing. Does not erase the character.
|
^I (0x09)
|
|
Tab. Moves the
cursor forward to the next multiple of 8 columns. If in
the last 7 columns of a line moves to the start of the
next line.
If in last 7 characters of screen, either scrolls a
line, or moves to top of screen, depending upon scroll /
page mode.
|
^J (0x0A)
|
|
Cursor down.
Moves the cursor down one line. If on last line of
screen, either scrolls a line, or moves to top of
screen, depending upon scroll / page mode.
|
^K (0x0B)
|
|
Move cursor up
one line. If already at top of screen, do nothing.
|
^L (0x0C)
|
|
Fill entire video
RAM with space character and non-printing attribute
(depending upon write mask). Sets top of screen to top
of video RAM.
|
^M (0x0D)
|
|
Carriage return.
Sets cursor position to beginning of line.
|
^N (0x0E)
|
|
Blink on. Sets
the blink bit (bit 6) in the printing attributes byte
only.
|
^O (0x0F)
|
|
Blink off.
Clears the blink bit (bit 6) in the printing
attributes byte only.
|
^P (0x10)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x0. Black foreground.
|
^Q (0x11)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x1. Red foreground
(colour) or underline (mono).
|
^R (0x12)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x2. Green foreground
(colour).
|
^S (0x13)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x3. Yellow foreground
(colour) or underline (mono).
|
^T (0x14)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x4. Blue foreground
(colour) or bright (mono).
|
^U (0x15)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x5. Magenta foreground
(colour) or bright & underline (mono).
|
^V (0x16)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x6. Cyan foreground
(colour) or bright (mono).
|
^W (0x17)
|
|
Sets the 3 lsb of
the printing attributes byte to 0x7. White foreground
(colour) or bright & underline (mono).
|
^X (0x18)
|
|
Initialise
display:
Turns on scroll
mode.
Sets both
printing and non-printing attribute bytes to 0x02 (green
foreground on black background).
Turns on the
cursor.
Enables both
character and attribute writes.
Performs a
carriage return and line feed.
Selects the
standard font.
|
^Y (0x19)
|
|
Cursor forward.
If in the last column of a line moves to the start of
the next line.
If in last character of screen, either scrolls a
line, or moves to top of screen, depending upon scroll /
page mode.
|
^Z (0x1A)
|
|
Home cursor.
Moves cursor position to first character of top row.
|
^[ (0x1B)
|
|
Start of an
escape sequence. See next section.
|
^\ (0x1C)
|
|
Sets scroll mode.
If cursor flows off the bottom of the screen, then all
text is moved up one line, bottom line is cleared (using
space character and non-printing attribute, depending
upon write mask), and cursor is positioned on the new
bottom line. Moving text up is performed by updating
video RAM address of top of screen, and is therefore not
affected by write mask.
|
^] (0x1D)
|
|
Sets page mode.
If cursor flows off bottom of screen it re-appears at
the top of the screen. None of the screen is cleared.
There is no code in the driver to wait for a key press.
|
^^ (0x1E)
|
|
Turn on display
of the flashing cursor.
|
^_ (0x1F)
|
|
Turn off display
of the flashing cursor.
|
Escape Sequences
Escape sequences are
similar to control codes. They consist of the ESC character
(0x1B), followed by the command character and then possibly one
or more data bytes.
In the Memotech
implementation, if the character following the ESC is in the
range 0x00 – 0x1F then the sequence is terminated and does
nothing. Otherwise the 5 lsb of the command character are used
to select the command to be executed. Therefore the sequence
(ESC, 0x01) does nothing while each of the sequences (ESC, “!”),
(ESC, “a”), (ESC, 0x81), (ESC, 0xA1), (ESC, 0xC1) and (ESC,
0xE1) all do the same as (ESC, “A”). In the table below, the
standard upper case command character is listed, but the
descriptions are applicable to all the other equivalents.
Command
Character
|
Data Bytes
|
Action
|
“@” (0x40)
|
|
Does nothing.
|
“A” (0x41)
|
|
Selects alternate
font (see section on printable characters)
|
“B” (0x42)
|
m
|
If m = “0” (0x30)
then zeros both printing and non-printing attribute
bytes. Otherwise sets bit (m-1)&0x07 in both attribute
bytes, leaving any other bit unchanged. Intended that
“1” - “8” should be used to select the bit to set, but
any character other than “0” results in a bit being set.
Note that m = 0x00 will result in bit 7 being set, not
bit 0.
|
“C” (0x43)
|
|
Sets scroll mode.
If cursor flows off the bottom of the screen, then all
text is moved up one line, bottom line is cleared (using
space character and non-printing attribute, depending
upon write mask), and cursor is positioned on the new
bottom line. Moving text up is performed by updating
video RAM address of top of screen, and is therefore not
affected by write mask.
|
“D” (0x44)
|
|
Sets page mode.
If cursor flows off bottom of screen it re-appears at
the top of the screen. None of the screen is cleared.
There is no code in the driver to wait for a key press.
|
“E” (0x45)
|
|
Turn on display
of the flashing cursor.
|
“F” (0x46)
|
|
Turn off display
of the flashing cursor.
|
“G” (0x47)
|
|
Selects special
graphics font (see section on printable characters)
|
“H” (0x48)
|
|
Does nothing
|
“I” (0x49)
|
|
Moves all the
text on the line containing the cursor and below down
one line. The bottom line is lost. The line containing
the cursor is cleared (using space character and
non-printing attribute). The cursor retains its previous
position (on the now blank line).
|
“J” (0x4A)
|
|
Moves all text in
lines below the cursor up one line. The new bottom line
is cleared (using space character and non-printing
attribute). The cursor retains its previous position.
|
“K” (0x4B)
|
|
Does nothing.
|
“L” (0x4C)
|
|
Does nothing.
|
“M” (0x4D)
|
|
Does nothing.
|
“N” (0x4E)
|
m
|
If m = “0” (0x30)
then zeros the non-printing attribute byte. Otherwise
sets bit (m-1)&0x07 in the attribute byte, leaving any
other bit unchanged. Intended that “1” - “8” should be
used to select the bit to set, but any character other
than “0” results in a bit being set. Note that m = 0x00
will result in bit 7 being set, not bit 0.
|
“O” (0x4F)
|
|
Does nothing.
|
“P” (0x50)
|
m
|
If m = “0” (0x30)
then zeros the printing attribute byte. Otherwise sets
bit (m-1)&0x07 in the attribute byte, leaving any other
bit unchanged. Intended that “1” - “8” should be used to
select the bit to set, but any character other than “0”
results in a bit being set. Note that m = 0x00 will
result in bit 7 being set, not bit 0.
|
“Q” (0x51)
|
|
Does nothing.
|
“R” (0x52)
|
|
Reads character
and attribute at the current cursor position into two
bytes of Z80 RAM within the driver. No visible effect.
|
“S” (0x53)
|
|
Selects standard
font (see section on printable characters)
|
“T” (0x54)
|
m
|
Sets the printing
attribute byte to the value of the data byte (m).
|
“U” (0x55)
|
m
|
Sets the
non-printing attribute byte to the value of the data
byte (m).
|
“V” (0x56)
|
|
Sets the both
attribute bytes to the value of the data byte (m).
|
“W” (0x57)
|
|
Sets the write
mask:
If m = “0” (0x30)
enable both character and attribute writes.
If m = “1” (0x31)
disable attribute writes.
If m = “2” (0x32)
disable character writes.
Any other value
of m has no effect.
|
“X” (0x58)
|
m
|
Simulate the
effect of the control code given by m & 0x1F.
|
“Y” (0x59)
|
|
Does nothing.
|
“Z” (0x5A)
|
|
Does nothing.
|
“[“ (0x5B)
|
|
Does nothing.
|
“\” (0x5C)
|
|
Does nothing.
|
“]” (0x5D)
|
|
Does nothing.
|
“^” (0x5E)
|
|
Does nothing.
|
“_” (0x5F)
|
|
Does nothing.
|
Line Drawing Algorithm
The end points of the
line to be drawn are defined in a square with coordinates in the
range 0 – 255. Only a rectangle to the top left, with x in 0 –
159 and y in 0 – 95 is visible. So it is possible for these to
be off the screen either to the right or below (or both). Even
if the end points are off the screen, it is possible for some of
the line to be visible and drawn, if it passes through the
visible area.
See the description of
Ctrl+A for the details of plotting each point on the line.
The algorithm used to
draw the line is as follows (all divisions are unsigned integer
division):
If m1 >= 32 then x1 = m1
– 32, else x1 = m1 + 224
If n1 >= 32 then y1 = n1
– 32, else y1 = n1 + 224
If m2 >= 32 then x2 = m2
– 32, else x2 = m2 + 224
If n2 >= 32 then y2 = n2
– 32, else y2 = n2 + 224
If ( ( x2 < x1 ) or ( (
x2 = x1 ) and ( y2 < y1 ) ) then
Swap x1 and
x2
Swap y1 and
y2
Endif
Push (0, 0) - Marker
Loop
If ( ( x2 =
x1 ) and ( y2 = y1 ) ) then
Plot (x1, y1)
Pop (x1, y1)
If ( (x1, y1) = (0,0) ) quit
Pop (x2, y2)
Else
Push (x2, y2)
If ( x1 != x2 ) then
x3 = ( x1 + x2 ) / 2 + 1
x2 = ( x1 + x2 ) / 2
Else
x3 = x2
Endif
if ( y2 > y1 ) then
y3 = ( y1 + y2 ) / 2 + 1
y2 = ( y1 + y2 ) / 2
Else if ( y2 < y1 ) then
y3 = ( y1 + y2 ) / 2
y2 = ( y1 + y2 ) / 2 + 1
Else
y3 = y2
Endif
Push (x3, y3)
Endif
End loop
|