Blog

The Einsteins Machine Code Monitor System

Coding
January 24, 2026

The Einsteins Machine Code Monitor System

Introduction

This article like many others I will be doing, is a recreation, tidy up, etc of articles originally

published in old User Group Magazines. As the quality of these old magazines is quite

poor, I decided to take it upon myself to recreate those articles and present them in an

improved format, with pictorial enhancements and where possible improved content.

This article was originally written by C.P. Wallis and Published in Einstein Monthly News

Volume 2 Issue 4.

The Article

Did you know that MOS has a powerful debugger? The monitor is the section of MOS

which can be controlled from the keyboard and it contains commands which allow

individual instructions in a program to be examined as they are processed by the 8 0 chip -

-although reading the DOS/MOS manual is unlikely to produce any enlightenment until you

know what you need.

There are two ways to debug a program: you can insert write statements by trial and error

until you find the variable which fails to change as it should. Or you can step through the

program to the stage at which the error appears and then follow the register changes

which occur. The second method requires a program called a monitor (or various other

names). There are Public Domain (e.g. Z E ) and commercial programs which provide

sophisticated control over the progress of the run if you can master all the commands. The

MOS monitor is simpler to use, but its main advantage is that it is in ROM and so there is

only minimal code exposed if your program goes off the rails. As well as debugging new

programs, the monitor is very useful for finding the correct location for modifications or why

a perfectly legitimate instruction causes Syntax Error.

Preparation

There is no doubt that the quickest way to learn machine code is to step through a short

program. You will need a list of Z80 opcodes in numerical order, together with their

operands (that is the bytes which follow each opcode). The list should show the number of

bytes and type of operand - data, address, port, offset etc. Omit the codes DD and FD

(using registers IX and IY) until you understand the other codes. You will probably have to

write out the list yourself, but it is worth checking as many assembler and disassembler

manuals as you can find. Assembly language textbooks tend to copy the Zilog manual

which gives the codes in binary and needs a lot of work to sort out.

For example the assembly language instruction CALL is represented by code CD and is

followed by two bytes specifying the address of a subroutine to be called (remember that

the HIGH byte is SECOND). So your list will contain…

CODE OPCODE BYTES TYPE

CD CALL 2 Address

Making a list like this takes you half-way to learning machine code. It will also become

obvious that there are various ways t o condense the list into a reasonably small space.

The opcodes are always given a s hexadecimal numbers but don't let this worry you.

There i s no arithmetic needed for opcodes, so think of them as names. The only time

arithmetic is needed i s to convert relative jumps to addresses and MOS provides the A

instruction to do this for you.

Starting the Program

Getting started is the most difficult part and the DOS/MOS manual gives no clues. The

simplest procedure is to use the LOAD command and then go into MOS. If you have a

short well-behaved program TEST.COM, proceed as follows: Remember to type

<ENTER> after each line

PROMPT TYPE IN COMMENTS

0: LOAD TEST.COM Reads program into memory

0: MOS Enters

MOS> G 0100 0100 Set up monitor

This will only work if the program

(1) makes no use of the system stack and

(2) does not expect a command tail.

The command tail is anything which you would type after TEST when the program is

started normally. Look for it in the buffer at address 080H.

There are many ways of starting the monitor and most of them work most of the time but

you are always liable to run into obscure errors on occasion which will waste a lot of time.

After experimenting for more than three years, I have found that the simplest way to be

sure that the monitor is set up correctly is the following sequence (assuming that you

would normally start the program by typing TEST Q.DAT):

0: LOAD TEST.COM Begin as above

0: MOS

> G 0 0100 Dont confuse G0 with GO

0: GO Q.DAT Load command tail

The monitor is now set up at the start of the program with the registers displayed to prove

it. This procedure should be used even if there is no command tail, because it ensures that

there is a valid stack. The DOS/MOS Manual has a good description of the effect of these

instructions which is much easier to follow when you know what needs to be done. The

second address in the G instruction can be in the middle of the program when you have

located a suitable position.

An alternative procedure if you want to skip straight to the middle of the program is to

modify the .com filly placing an FF code at the required start address. This can be done

with a disc editor or from MOS using LOAD and SAVE (remember to note the number of

blocks). The program is run with a normal call and it will stop in MOS when it reaches the

FF code. Change the FF back to the original value with the M instruction and you are

ready to start

Running the Program

The MOS E instruction is the core of the monitor. When you have set up the program, type

T 0100 010F

to display the first sixteen bytes of code. Suppose the value at 0100H is 31 (LD SP,): this

loads the stack pointer and is followed by the address (2 bytes) to which the SP register

should point on completion of the instruction. The whole instruction occupies 3 bytes and

therefore the next opcode is at 0100H + 3 = 0103H. So type

E 0103

The Z80 registers are displayed, showing that the program counter (PC) is now at 0103H.

To check the SP register type

Z2

The next opcode is determined (the code display should still be on the screen) and the

whole process i s repeated. This leads to single-stepping through the program. When you

get more expert, it will be possible to jump further ahead than one instruction: whatever

address is specified after E, the program will run on until it encounters an opcode at this

address, which is called a breakpoint.

The E instruction works by changing the opcode at the address typed to FF, which calls

MOS, so if you have not calculated the address correctly, the program will either run on to

the end or more likely get lost because you have altered an address instead of an opcode.

When this occurs, it is necessary to start again, but it is possible to jump directly to the last

breakpoint encountered. For example if the last breakpoint examined was at 010FH you

would start up the program again using the following in place of the G instruction above.

G 0 010F

Jumps

The first codes you must learn to recognise are the jump codes, because the next address

may be in another part of the program. Unconditional jumps (C3) are easy - the next

address follows directly after the opcode. Relative jumps (18) need more attention

because you have to calculate the address. For example if the program is

0108 18 02

010A 3E 01

010C B7

the value 2 at 0109H is the relative jump, but it must be added to the NEXT address, that

is 010A + 2 = 010C, s o control passes from 0108H to 010CH.

Conditional jumps require some care. It is necessary to check the flags given in the last

register display (ZO if it has scrolled off). For example code 20 (JR NZ,) will continue to the

next instruction if the Z flag is 1, but will do a relative jump if the Z flag is 0.

Calls

A call instruction (CD) goes to the address specified in the next two bytes in the same way

as a jump instruction (C3). However when a return (C9) instruction is subsequently

encountered, control NORMALLY returns to the opcode following the CD instruction, by

fetching the address from the top of the stack It is not difficult to program alterations to the

stack so the less well-behaved programs may return to another location. For this reason

when you reach a return instruction it is always safer to check the new address by typing

Z2, and then displaying the contents of memory at the address in SP using the T

instruction. This shows the contents of the stack, and the program jumps to the address in

the first two bytes.

Loops

Tracing a loop can become very tedious even when there is a fairly low loop count, and

the risk of mistyping rises rapidly with the count. If there is a single exit condition, skipping

out of the loop is easy. With multiple exit points it may still be possible to work out a

common meeting location for all exits, but if one exit is a conditional return and the other is

a jump, it may be necessary to try each in turn and restart if the guess is wrong. Loops are

the most difficult code to trace but in practice the constraints of assembly language

programming frequently lead to short loops with long blocks of code placed in subroutines

and long loops are not very common.

Conclusion

Single-stepping through code is necessary to follow register changes, but it is far too slow

for working through a whole program. Faster progress can be made by counting through

the displayed code to find the opcodes and setting a breakpoint at the next instruction

which could produce a jump. Further improvements in speed will always n e e d some

degree of guesswork: for example skipping subroutine calls will make good progress

through the code, but you will eventually come to a routine which does not return. In the

best programs this would indicate that the program had detected a fatal error condition,

but there are many other possible causes which can only b e identified by tracing the

subroutine.

Always keep a note of addresses and important values when tracing, so that if you make a

mistake (which is only too easy) you c a n readily return to the same position by restarting.

Programs selected for tracing should preferably have been written in assembly language.

Compiled programs usually generate highly convoluted code, particularly if an intermediate

code is produced. This is a list of machine code subroutines with a very short loop

selecting each entry point. I find with this type of program (and in fact any file-using

program) it is best to start by using E 05 repeatedly. This traps the DOS calls and allows

you to follow what is being done to the files at each stage and provides a useful outline of

the program. Of course you need to read the return address from the stack and set a

breakpoint at it in order to get to the next DOS call. Some programs have a sign on

message output by single character DOS calls and you may have to work up through

several subroutines to find the loop exit point.

It has only been possible to mention a few of the many code variations, with the aim of

showing newcomers how to get started and providing some new ideas for regular users,

but in this type of programming there is no substitute for experience.

Lee Bendall

I’ve worked in the finance sector since the late 1980s, but my passion for computing began much earlier. I purchased my first home computer in 1982, and from that moment I knew technology was what I truly wanted to pursue. So, when the opportunity arose in the 1990s to transition my career into the Financial IT sector, I seized it with both hands. Throughout my later career, I had the privilege of working on leading-edge IT projects and was eventually invited to become a member of the British Computer Society, earning Chartered Information Technology Professional (CITP) status—a charter I’m proud to still hold today. Despite my professional focus, I never lost my enthusiasm for the home computing scene. Over the years, I’ve owned nearly every major machine released in the UK. It was no surprise, then, that when MAME introduced support for vintage computers (initially through MESS), I jumped right in. Now retired, my love for 1980s and 1990s computers remains as strong as ever. When the opportunity arose to purchase an Einstein TC-01, I couldn’t resist. The seller—Adam Groome—shared a deep passion for keeping the Einstein alive, and his enthusiasm quickly drew me into the community. Today, I’m involved in several Einstein-related projects, including work on the Speculator, TK-02, and Silicon Disk hardware. In addition to these, I founded the Einstein Document Preservation Project, which focuses on high-resolution rescanning, restoration, and enhancement of all Einstein documentation. I’m also engaged in a large-scale initiative to support and preserve the Einstein’s software library.