This is an OCR scan of the CEGMON monitor manual. Parts of the
original manual which are not applicable to the micro UK101 (such as the
screen and keyboard handling) are omitted.
CEGMON's machine-code monitor has been designed specifically to simplify the
development of short machine-code routines, especially those intended to link to
BASIC. While it is not as comprehensive as OSI's Extended Monitor (ExMon), it can
be co-resident with both BASIC and Assembler, and is immediately available
without loading tape - a great advantage for educational users. We expect it to be
most useful for developing short routines of up to thirty or so lines, and for testing
and debugging larger routines being developed with the Assembler.
This section does assume a basic knowledge of the principles of machine-code
programming for the 6502 (the monitor will provide the practice!). Three useful
reference books are:
Programming the 6502, Rodnay Zaks (Sybex) - good and mostly complete
introduction to the 6502, but earlier editions had a few important inaccurac~es.
6502 Assembly Language Programming Lance A. Leventhal (Osborne/McGraw-
Hill) -solid, full of examples of programming for internal routines and I/O through
PIAs, VIAs, ACIAs etc., in assembly mnemonics. Beware the unusual page-
6502 Software Design, Leo J. Scanlon (Blacksburg) - many practical examples,
mostly based around the A1M65 system, but still useful.
On start-up via 'M', the monitor's prompt - a '>' - appears after the screen is
cleared. At this point the monitor is in its address/command mode, and normally
expecting input from the keyboard. The commands available are:
I jump to data mode, leaving current address unchanged.
'do nothing' - loop back to get address.
L sets load flag - calls for input from the BASIC load vector at $FFEB.
S save machine code.
M do memory block move.
T do tabular dump/display of memory contents.
Z set a breakpoint.
R restart from a breakpoint.
U jump to user routine.
If none of these are given as a reply, the system expects four hex digits to make up an
address (see Error-handhng later). On completion of the address (the 'current
address'), the system prints a '/', then the contents of that address as a hex pair, and a
space. The system is then in the data mode loop, and the following commands are
return to address mode.
I re-open current address, to correct a mis-type.
G start execution at the current address.
enter text entry loop.
increment current address.
LF (line feed) - increment current address, do CR/LF, ___
display new current address and contents on next line.
CR (carriage return) - as for LF, but do CR only;
display by overwriting on same line.
(up-arrow, SHIFT-N) - as for IF, but decrement current address.
Otherwise the system expects both digits of a hex pair, stores the complete byte at
the current address, and loops back to the start of the data mode for a new
command or value.
I jump to data mode
On start-up the current address is set to $0000; thereafter it is not changed on a
restart, such as on an error recovery.
The machine-code load flag at $FB is set; the system then restarts at the beginning of
the data mode loop. It then expects input from the ACIA - either from tape or from
an RS-232 serial interface - in the old SYNMON format of'.' to define address.'/' to
define data, data transmitted in the form of hex pairs separated by a CR, and
concluded by '.', an address and 'G' for an auto-start. The only difference from
SYNMON's load is that the former contents of the current address are displayed on
the current line as well as the address and its new contents. The load loop can run at
up to 4800 baud on a 1 MHz machine.
Note that normal error-checking is disabled during load - see Error-handling
below - and that the system will not accept input from the keyboard until the load
flag is cleared, either in your program, or by a re-entry into the monitor's command-
mode. As with loading tapes for BASIC or Assembler, loading can be halted by
hitting the SPACt bar; the monitor then restarts in its command mode, with normal
Syntax: .Saaaa,bbbb>cccc where aaaa is the start address of the code to be saved,
bbbb is the last address inclusive of the code, and cccc is the restart address - either
to the beginning of the routine for autostart, or to the monitor for further work (see
List of locations, routines and subroutines later). Code is saved from aaaa to bbbb
inclusive - aaaa thru bbbb; the routine automatically provides the ',' and '>'
prompts. It then waits until the RETURN key is pressed on the keyboard-to give you
time to start your recorder - and then prints out the code in the SYNMON load
format. (It will start after any key is pressed; but RETURN is advised, since it will also
output ten nulls to the tape before the actual 'save' starts). The 'start' and 'go'
addresses and hex codes are displayed on the screen; the CR which separates each
data byte, however, is output direct to the ACIA. As a result, this routine is not fully
vectored for user-defined output - it can only be used through the ACIA, to
cassette port or RS-232 interface.
On completion of the save, the BASIC save flag is cleared, and the system restarts
in the command mode.
M memory block move
Syntax: .Maaaa,bbbb>cccc where aaaa is the start of the code to be moved, bbbb
is the end inclusive - aaaa thru bbbb - and cccc is the new start location. The
routine does not erase the code at the previous locations, though it may over-write
it if the new locations overlap the old. If the new start is between the old start and
end addresses, it will over-write the remaining code before it has been copied - if
you need to do this kind of move, copy to a 'safe' area first, and then copy back to
the new area.
T tabular display
Syntax: .Taaaa,bbbb where aaaa is the start address of the code to be displayed,
and bbbb is the last address inclusive -aaaa thru bbbb. The ',' prompt is supplied by
the routine. The contents of the memory are displayed as a table of eight-byte
blocks (sixteen-byte blocks - C2), each block preceded by the address of the first
byte of the block (on Cis, the address is printed between each line of the table). If
you want to display more than a screenful, it's advisable to slow the print speed
down by placing a delay value in $0206 before calling the T routine; to send the
display out to a printer, set the OUTVEC 'save' flag at $0205 to $FF before you start.
On completion, the system restarts in the command/address mode, displaying
the '>' prompt.
Z zero - set a breakpoint
Syntax: .Zaaaa where aaaa is the address at which the breakpoint isto be inserted.
Z sets up at $01C0 (051's IRQ/BRK address) the pointer to CEGMON's breakpoint
handler; saves the current contents of the chosen address in BRKVAL; and replaces
it with a BRK opcode ($00). Note that a breakpoint cannot be set at any ROM
address! - see Using breakpoints later.
The routine then exits back to the command mode, displaying the'>' prompt.
R restart from breakpoint
Collects its start address and the contents of the registers, processor status and stack
pointer from the break-table, and restarts the program at thataddre~~ss~bjexecuting
an RTI instruction - see Using breakpoints later. Inappropriate use of R will usually
cause a system hang~up or crash - it should only be used to restart from a break-
U jump to user routine
Causes the system to 'jump-indirect' to a routine whose start address is held in
$0233-34 - the low byte of the address in $6233, the high byte in $0234. Useful for
calls to regularly-used locations like the Assembler restart, or where the 'current
address' should be left unchanged.
exit to command/address mode
When in the data mode, '~' must be typed before calling for any of the command
mode's commands or for a new address. If it is forgotten, the command mode's
command letters will be treated as errors; while the intended new 'address' will be
treated as two hex pairs, the second overwriting the first at the unchanged current
I re-open current address
Leave current address unchanged, to place a new value at the current address -
used if the value just typed was incorrect.
Sets all registers to $00, and starts execution at the current address. Usually used with
the syntax .aaaaG - but make sure that the'.' command precedes the aaaa address!
start text mode
The text mode expects ASCII text rather than hex digits. Control characters such as
the 'window'-clear and cursor controls, and also graphics characters, can also be
typed direct into memory. Where the text is to be printed to screen later via the
BASIC output vector (OUTVEC), errors niay be corrected by RUBOUT, but both it and
the character it 'deletes' will be stored in memory; otherwise no editing is possible
without exiting back to the data mode. Each new character is stored directly and the
current address is incremented.
A second '"will exit back to the data mode on the same line; a ','wilj be printed
after it, for clarification. but the current address will not be further incremented.
The text-entry mode can also be exited by typing a CR (carriage return); this returns
to the data mode, but printing on the next line, displaying the updated current
address and its contents.
increment current address
Used to space succeeding entries into memory. If more than one ',' is typed, the
current address will be incremented accordingly, and the contents of the 'skipped'
addresses will be left unchanged.
[F line-feed - increment current address, display on next line
This is the same as on OSI's ExMon - the current address is incremented, a CR/LF
(carriage-return/line-feed) is issued, and the new current address and its contents
are displayed on the next line.
CR carriage-return - increment current address, display on current
This differs from OSI's ExMon, where CR is used to exit to the command mode. Its
use here is mainly to allow fast tape load without scrolling; it is identical to LF, except
that a carriage- return only (without line-feed) is issued. See also the use of CR in the
(text) mode above.
up-arrow (SHIFT-N) - decrement current address, dis play on next line
This is the same as in ExMon. The routine is identical to tF, except that the current
address is decremented rather than incremented.
Breakpoints are a useful part of the debugging toolkit for machine-code work. They
force the program execution to halt, rather like the STOP command in BASIC. In
CEGMON's case, the halt then presents the system registers for view and alteration
In the 6502 processor, the breakpoint is forced when the processor executes a
BRK instruction, opcode $00; so breakpoints are set by overwriting an existing
instruction with a BRK opcode. CEGMON does this with the Z command: a $00 is
stored at the chosen address, and the current contents are saved, to be restored by
the breakpoint handler when the breakpoint is hit. There are two restrictions on
setting breakpoints: first, that the BRK instruction must replace an instruction byte,
since if it is placed in the data or address bytes of an instruction, it will simply be
treated as part of that data or address; and second, that since the breakpoint is set by
replacing the existing instruction byte with BRK, breakpoints cannot beset at ROM
addresses. Only one breakpoint can be set at a time, and is automatically cleared by
the breakpoint handler after hitting the breakpoint.
To test a program by using breakpoints, set a breakpoint at a likely location, and
start the program running with a .aaaaG or U command. If nothing different
happens, or if the program hangs up, either the breakpoint was never reached, or
was set incorrectly and interpreted as data or address. When the program hits the
breakpoint, a check is made that this is a BRK and not an IRQ interrupt (which is
ignored); the registers and corrected program counter (see Zaks, p.111, 235;
Leventhal, 14-2, 3) are saved in a table; the previous opcode is restored at the break-
point, replacing the BRK opcode; and the routine then jumps to the monitor data-
mode loop, pointing to the beginning of the break-table. What you will see on the
screen is a CR/LF done, followed by
where aa is the contents of the A register at the time the breakpoint was reached.
$00E0 is the beginning of the break-table - the same as ExMon's. You can then
use the data mode loop to examine and/or modify the registers and program
counter. They are stored as follows:
A register - accumulator
E1 X register
E2 Y register
E3 P register - processor status flags, in hexadecimal form
E4 K register - stack pointer
E5 PCL - low byte of program coLinter
E6 PCH - high byte of program counter
The address shown by E5 and E6 should be the same as the breakpoint address that
you set; if not; you have a loose BRK in your program somewhere!
the data mode, you can change these values; you can also exit as usual
the command mode to set another breakpoint - or reset this one - with the Z
command. When you've finished, and want to restart, type .R (don't forget the'.'!).
This collects the registers' values and the program counter from the break table, and
restarts execution. If you've only looked at the break table, without changing any
values, the program will simply carry on where it left off, as if nothing had
happened, and will do so until it finds another breakpoint or reaches its own
One minor problem does occur when testing programs with the Assembler still in
memory, such as after an A3 assembly. The Assembler uses BRK as 'return to
command-mode' statement; setting a breakpoint via CEGMON's Z command will
over-write the Assembler's own jump and cause it to 'return' to CEGMON when
you restart it after testing your routine. If you are working with the Assembler, note
down the contents of $n1Co-01C2 before setting any breakpoint with Z, and restore
them before restarting the Assembler.
CEGMON's error handling in the machine-code monitor is similar to that in OSI's
ExMon. In the command mode, only the first letter after the '>' prompt or a
command may be a command letter; thereafter, only hex digits are allowed until a
complete four-digit hex address is built up. The same applies within the commands
themselves - only complete four-digit addresses are allowed, as the syntax given
for each command shows. (Note that the system supplies any ',' or '>' prompts).
Within the data mode, excluding its text input mode, the same applies: the first
character in each time round the loop after a previous instruction or value may be
an instruction, such as ',' or LF; thereafter, the system expects hex digits to make up
hex pairs. A '.' or'/' may be typed at any time, to exit back to the command or data
modes (but note that this will leave a part-complete address or data-byte only partly
rotated into place, and almost certainly incorrect). Al' other characters are invalid
With in the text entry mode, no characters are invalid; the only restricted characters
are and CR, which exit back to the main data mode loop.
During text entry from the keyboard, invalid characters will be t'iInte(l, but
immediately followed by an '?' and CR/LF, and the restarte>' prompt. You are then
hack in the command mode. The current address. however, is unchanged, as can be
seen if you then re-enter the data mode by typing a'/'. Control characters like CTRL-
Z - to clear the screen - are recognised, but are decoded as errors on completion:
ctR~Z clears the screen, but a '?' and the '>' prompt are then printed.
During a tape load, this error checking is disabled The inevitable 'glitch' charac-
ters that precede the start of each record on cassette would halt the load before
anything had been loaded if this was not done. This does leave the load open to
errors. However, if a digit is invalid it is simply ignored; the following CR bumps up
the current-address counter as normal, and only the contents of that address are
affected. If a CR is lost, the addresses will be out of step with the data Normal error-
checking is resumed only when the load-flag at $FB is cleared. As mentioned earlier,
this is done automatically by the system at the entry to the command mode, and also
if the spAct bar is hit (luring load; it is also cleared by a BREAK reset. If your program
auto-starts without entering the monitor's command mode, you will need to cle,(r
the flag by storing a null ($00) in it.
On short programs a load can be checked simply with the T tabular display; on
larger programs a checksum loader should probably be bootstrapped in, as on OSI's
ExMon and Assembler - although we have found OSI's own checksum loader to be
less reliable than a straight load! The digit-by-digit load format is surprisingly
reliable with a reasonable tape and tape recorder: when testing CEGMON we used
it to save the Assembler and the CEGMON source code - more than 20K in all -
and re-loaded it at 4800 baud into a C2, with no detectable errors.
Except in the machine-code monitor, all input and output in CEGMON is through
ASIC's vectors at $FFEB-FFF9. Since these all call their routines via JMP-indirect
~lls through further vectors stored in a table in page 2, from $0218-0221, any special
~put or ouput routines for printers or special programming purposes can be
nked directly to BASIC or elsewhere by changing the vectors in the table in page 2.
he program examples show two routines - a program to skip BASIC's masking of
ontrol and graphic characters on input, and a TRACE routine called by BASIC, via
5 cTR~C check vector, between the execution of each BASIC statement.
In the machine-code monitor, neither load, save nor keyboard input are
vectored, simply to maintain compatibility with OSI's original system. All output to
~e screen, however, does go through the BASIC output vector, and can be sent to a
printer or whatever simply being setting the BASIC 'save' flag at $0205 to 01.
he input and output vectors are as follows:
Page $FF vector through normally points to locations/contents in decimal
IVEC $FFEB$ 0218-19 INPUT $FB46 536 - 70 537 - 251
OUTVEC $FFEE$ 021A-1B OUTPUT $FF9B 538 - 155 539 - 255
CCVEC $FFF1$ 021C-1D CTRLC $FB94 540 - 148 541 - 251
LDVEC $FFF4$ 021E-1F SETLOD $FE70 542 - 112 543 - 254
SVVEC $FFF7$ 0220-21 SETSAV $FE7B 544 - 123 545 - 254
Compatibility and conflicts
OSl's system software, as represented by its BASIC, Assembler, ExMon and
SYNMON, was clearly not designed as a system at all. The Assembler and BASIC
cannot be co-resident, and ExMon's disassembler crashes BASIC by overwriting the
tail-end of BASIC's all-important memory scan subroutine (from $00BC to $00D3).
BASIC's keyboard buffer starts at $0013, the Assembler's at $0080. And so on. In
designing CEGMON, one of our main concerns was to build a monitor that could
co-exist with all of these conflicting requirements and still contain the kind of
features we wanted.
Apart from the difficulties over BASIC's terminal width and character-counter,
the other major problem is with the use of zero-page stores. Apart from SYNMON's
five locations at the top end - $Fn-$FF -only one pair is reasonably 'safe', and even
that is used as a temporary store by the Assembler. The $E4-E5 pair is thus used in
four ways by CEGMON: as a temporary pointer for the edit cursor, but only during
each call to the keyboard; as a temporary store for the 'go' address of the machine-
code save, but only during the actual save; as the store for the 'new start-of-block'
address during a memory block move; and as part of the break-table, for the stack-
pointer and PCL contents. This is another reason why the keyboard is called direct
by CEGMON's machine-code monitor. The only time when the conflict may be
important is when you are both testing and saving a routine with the Assembler still
in memory, such as after an A3 assembly to memory. The $F9-FA pair is. also used by
CEGMON as a temporary store for addresses during screen-clear, tabular display,
save and block move.
The other important point is that CEGMON uses some of the old 'free RAM' starting
at $0222: up to $022E for the screen handler's store-locations and subroutines, and
to $0232 for the editor's stores. The monitor U command jump vectors through the
$0233-0234 pair, so the 'free' RAM under CEGMON starts at $0235, 565~~. Programs
written to start at $0222 can still be run on CEGMON, though only with the editor
disabled and the old screen-handler called instead of the new.The edit flag at $0204,
516~~ must contain 0, and CTRL-E must not be typed at any time, or the edit-cursor will
be written at random into memory, probably causing a program crash. The old
screen handler may be called by changing the output vector: POKE 538,149 enables
output through the old screen handler at $BF2D on 051 machines, while POKE 538,
155 enables output through the new routine. (When returning to the new handler,
it's a good idea to home the cursor with CHR$(12) or one of the screen-clear calls).
Note that a BREAK reset will not only reset the vector to point to the new screen
handler, but also over-write the RAM area up to $0234-so you may need to disable
BREAK as well. No problem will arise with either the Assembler or LxMon, since the
cassette versions of both of these start higher up in memory.
Under the OS-65D disc operating system, the entire RAM from $0200 is used; 65D
has its own I/O routines, and ignores those normally used by BASIC-in-ROM.
CEGMON contains a bootstrap to boot up 65D, but from then on 65D is self-
contained, and CEGMON's special features like the editor and screen handler are
ignored. Patches for 65D, to enable it to use CEGMON's editor and screen-handler,
will be made available shortly.
Locations, routines and subroutines
The following is a list of various useful points within CEGMON. As can be seen, the
locations of the SYNMON equivalents have in general been retained; but note that
in most cases the way in which they work will be somewhat different - output to
display in the machine code monitor goes via the screen-handler rather than direct
to screen memory, for example. One other important point is that, within the
machine-code monitor, the Y register is always reset to zero, and most of its routines
assume this to be the case - beware of this if you use these routines in your own
For break-table locations - OOEO-E6 - and their functions, see p.8.
BJABK OOE4 part of break-table, but also used as a pair to store 'go' address in during
BTABCL OOE5 save; new block start address in move; and edit cursor location during each
call to keyboard.
BRKVAL 00E7 store for opcode moved by Z when setting a breakpoint.
LOTO 00F9 store for 'to' addresses in save, move~ and tabular display - see NOTEND.
STORE OOFC store for current data during data mode and most other routines.
LOFROM 00FF store 'current address' for most routines - the 'from' address in save, move
HIFROM 00FF and tabular display.
DOBRK O1CO location for IRQ/BRK jump; set to 'IMP $FA4F' by Z.
CURDIS 0200 cursor displacement on current line.
OLDCHR 0201 stores current character~ during SCREEN; exits containing char 'beneath'
NEWCHR 0202 park for new char for SCREEN.
LDFLAG 0203 BASIC load flag: 00 - no load; FF - load from ACIA.
EDELAG 0204 EDITOR flag: 00 - disable edit cursor; FF enable edit cursor.
SVFLG 0205 BASIC save flag: 00 - skip save; 01 - enable save~to ACIA.
SDELAY 0206 print-delay value for SCREEN; delay is delay-value times approx. 400
machine-cycles (i.e. times 400 micro-seconds at 1 MHz).
COUNTR 0214 auto-repeat counter for GETKEY.
SCRTCH 0215 returns from GETKEY with final ASCII value of key.
LSTCHR 0216 pre-shift value of last key left here by GETKEY to test auto-repeat.
CCFLAG 0212 BASIC cTRL-C flag: 00 - enables cJRL-C break; Ol disables ctRL-C break.
DISP 022F edit-cursor displacement from start of editor's current line.
CURCHR 0230 store for char 'beneath' edit cursor.
CURSLO 0231 contain start of edit cursor's current line on screen.
USERLO 0233 contain location of start of user routine called by machine-code monitor's
USERHI 0234 U command.
Main entry points
RESET FF00 start of BREAK/RESET routine.
NEWMON FF00 'reset' entry to m/c monitor - reset stack, vectors/pointers, clear decimal
mode. Recommended re-entry point for auto-load m/c tapes.
MENTRY FEOC non-reset entry to m/c monitor - clear screen, zero 'current address'.
MSTART F97F entry to command/address mode.
DATALN FA2F entry to data-mode loop - prints 'current address' and its contents.
SAVEMC FA7F start of m/c save routine.
DISK FCOO entry to disc bootstrap (at F700 on C2).
INPUT FB46 BASIC input routine - get a char from keyboard or ACIA.
OUTPUT FF9B General output routine, to SCREEN and ACIA.
OLDSCR FF95 As for output, but screen-handling done by $BF2D rather than SCREEN.
TENULL FFAC outputs ten nulls to ACIA.
CTRLC F094 BASIC's cIR~C check - called between execution of each BASIC statement.
SETLOD FF70 sets BASIC load flag, clears save flag; (fecrements load flag to set it.
SETSAV FF7B sets BASIC save flag.
TAPIN FB57 collects char from ACIA; exits via EDITOR if SPACE hit.
TAPOUT FCB1 output to tape (BF15 on C2).
RSACIA FCA6 initialise ACIA (BF22 on C2).
SCNCLR FF59 clear entire screen; exits with X and Y registers zero.
SCREEN F836 new screen handler.
ENDCHK FBCF checks if top or base of screen overshot - if Y--0, carry clear if top overshot,
if Y-2, carry set if base overshot.
SCOUT FF8C print char at cursor location.
CURHOM FFD1 resets TEXT line pointer to TOP; do STX $0200 to reset cursor at TOP.
EDITOR FABD entry to screen editor - see main text, p.11.
GETKEY FDOO wait till key pressed, return with ASCII value in A register.
KEYWRT FCBE write-to-keyboard invert for Cl (F7BE on C2).
KYREAD FCCF read-A-from-keyboard invert for Cl (F7CF on C2).
KEY2XR FCC6 read-X-from keyboard invert for Cl (F7C6 on C2).
KDELAY FCDF approx. 6500 cycle delay; exits with X and Y registers zero (F7DF on C2).
DELAY2 FCE1 approx. (400 >< Y-register) cycles delay (FZEI on C2).
TRIQAD FFBD collect three addresses: first stored in (FE) pair, second in fF9), third in (E4(.
TWOQAD F9A6 collect two addresses: first stored in (FE) pair, second in (F9).
GETQDE F9BS collect address. store in (FE). Note: call GETNEW first!
GETPRC F9BE collect hex pair for data byte, store in FC. Note: call GETNEW first!
GETNEW FE8D get new char; print it to display before returning.
GETCHR FFE9 get char from keyboard or ACIA.
MCACIA FL80 get char from ACIA, strip off any top bit before returning.
ASCHEX FE93 strip ASCII digit to hex; set to 8016 if not hex.
ROLSTR FEDA roll new nibble into (FE) if X-2, or into FC if X=0.
ADVTOD FEAC print address in (FE), space, value in FC to display.
QDDATD~ FF86 print address in (FE) to display.
PRDATD FEBD print data byte in FC to display.
HEXOUT FECA strip byte in A register to lower nibble; print nibble as ASCII hex to display.
PRBYTE FEFO print data at 'current address' pointed to by (FE) to display. Assumes Y--0!
CRLF FBF5 print carriage~return/line-feed to display.
SPCOUT FBF6 print ASCII space to display.
BUMP FFF9 increment 'current address' at (FE).
NOTEND EBEB compare (FE) with (FY); carry clear if (FE) is less.
SWAP FDE4 memory block move. Expects start address in (FE), end address in (F9), new
start of block in (E4); assumes Y=0.