← Back to Modules
module
45

Appendix XII - Writing Your Programs on the Einstein with Zen

Everything in this course is built on a modern computer and sent to the Einstein. In 1984 there was no modern computer: you wrote, assembled and ran your program on the Einstein itself. One tool for the job was Zen, an editor, assembler and debugger in one small program, and it still works.

This appendix is optional, and nothing in the course depends on it. It shows how to build the course's programs with Zen instead of the VS Code extension, and what to change in them so that Zen accepts them. How to drive Zen itself - typing, listing, the debugger - is covered in our guide Assembly Language with Zen, and the Zen disc is on the Downloads page.

What is different

The instructions are the same: LD, JP, RST 8 and the rest are the processor's, and Zen writes them into exactly the same bytes as the course's assembler. Everything around them is where the two disagree, just as Appendix II describes for assemblers in general.

In the courseIn Zen
ORG 256 at the topORG 256 then LOAD 8000H
nothing at the endEND on the last line
0FFh, 4000h0FFH, 4000H - the H must be a capital
00011000b18H - Zen has no binary numbers
DEFM "SCORE"DEFB "SCORE"
3800h+9*32+12392CH - work it out yourself (see below)
DEFS 64 is 64 zerosDEFS 64 is 64 bytes of whatever was there
include 'vdp.asm'read the files in one after another
the extension builds and runsA to assemble, WC to save, run from the 0: prompt

Labels need no change: Zen wants the colon, allows lower case and long names, and, like the course's assembler, loop and LOOP are different labels. Double quotes, JP $ and comments after a semicolon all work as you know them.

The two lines at the top

Every program in the course starts with ORG 256, because every Einstein program runs from address 256, which is 100H. But while you work in Zen, Zen itself is sitting at 100H. So Zen needs to be told two things: where the program will run, and where to put it for now.

        ORG 256         ; where the program will run
        LOAD 8000H      ; where Zen puts it while you work

ORG works exactly as it always has. LOAD 8000H tells Zen to store the machine code at 8000H instead, out of its own way. The code is built to run at 100H and kept at 8000H until you save it.

And at the very end, one more line:

        END

Without it, Zen reads past your last line and stops with EOF.

S3's program in Zen

Here is the program from S3, ready for Zen:

        ORG 256         ; Start of the program in memory
        LOAD 8000H

        LD A,'H'        ; Load the letter 'H' into register A
        RST 8           ; Call a system routine
        DEFB 158        ; ZOUTC - print the character in A

        LD A,'E'
        RST 8
        DEFB 158        ; ZOUTC

        LD A,'L'
        RST 8
        DEFB 158        ; ZOUTC

        LD A,'L'
        RST 8
        DEFB 158        ; ZOUTC

        LD A,'O'
        RST 8
        DEFB 158        ; ZOUTC

PROGEND:
        RET             ; Return to the prompt
        END

Type it in with Zen's E command, or bring it across from your computer as described below. Then:

  1. A to assemble. At OPTION> press ENTER. If a line is wrong, Zen names the problem and shows the line; if all is well you are back at ZEN> with nothing to report.
  2. S lists the labels with their addresses. It asks OPTION> too: type V to see the list on the screen.
  3. WC saves the program. It asks for START>, which is 8000H, then STOP>, the last byte, then NAME>: HELLO.
  4. B leaves Zen, and HELLO at the 0: prompt runs your program. It prints HELLO, and its RET brings you back to the prompt.

Where to stop

STOP> is the address of the program's last byte, at 8000H onwards. The PROGEND: label above is there to find it. S shows its address as the program will run, from 100H onwards; add 7F00H to that and you have the address where it is stored. Here S shows PROGEND0114, and 0114H + 7F00H is 8014H, so:

WC
START>8000H
STOP>8014H
NAME>HELLO

Put PROGEND: in front of the last line that holds code or data you want saved. If that line is more than one byte long, STOP> has to reach its last byte, so add a few; going a little past the end does no harm, because Zen rounds every file up to a whole number of 256-byte blocks anyway.

Write PROGEND in capitals. S lists only labels written in capitals. The course's own lower-case labels work perfectly in Zen, but they do not appear in the list.

Bringing source from your computer

You can keep writing in VS Code and use Zen only to assemble. Zen reads a source file with RS, and Zen's source files are ordinary text files named NAME.SRC, with a name of up to eight capital letters or digits.

Two things matter when you save the file on your computer:

  • The name ends .SRC. GAME.SRC is read with RS and the name GAME.
  • Lines end with CR LF, the Einstein's convention, not the LF alone that a Mac or Linux editor uses by default. A file with LF endings reads in as one enormous line and assembly stops at once with EOF. In VS Code, click LF in the status bar at the bottom of the window and choose CRLF before saving.

Put the file on a disc image with the Einstein Disc Tools, put that disc in drive 1, and in Zen:

K
RS
NAME>1:GAME

K clears whatever text was in memory first, because RS adds what it reads to the end of the text already there.

Changing course source for Zen

Work down this list with any program from the course. Most of the changes show up as errors when you assemble, so Zen will point you at them. Two do not, and they are marked.

1. Add LOAD and END

Add LOAD 8000H after ORG 256, and END on the last line.

2. Hexadecimal takes a capital H

4000h becomes 4000H. A small h gives OPERAND. A hex number must still start with a digit: 0FFH, not FFH, which Zen would take for a label.

3. Binary numbers become hexadecimal

Zen has none, and 00011000b gives OPERAND. Split the eight bits into two groups of four and write each group as one hex digit:

BinaryGroupsHex
00011000b0001 100018H
01111110b0111 11107EH
11011011b1101 10110DBH
GroupDigitGroupDigitGroupDigitGroupDigit
0000001004100081100C
0001101015100191101D
00102011061010A1110E
00113011171011B1111F

You lose the picture that the ones and noughts drew. Keep the binary as a comment beside each line if you want it: DEFB 18H ; 00011000.

4. DEFM becomes DEFB

DEFM gives HUH?. DEFB takes text in quotes, so nothing else changes.

5. Sums are worked strictly left to right - no error warns you

Zen does no multiplication before addition. 3800h+9*32+12 is worked as 3800H plus 9, times 32, plus 12, which is 012CH: a completely different address from the 392CH you meant, and your text lands nowhere near where you put it. Work any sum containing * or / out yourself and write the answer: 9 × 32 = 288 = 120H, and 3800H + 120H + 0CH = 392CH. Sums of + and - alone are safe, because order does not matter to them.

6. DEFS does not clear - no error warns you

In the course, DEFS 64 gives 64 zero bytes. In Zen it reserves the 64 bytes and leaves in them whatever memory already held. A program that relies on a buffer starting at zero will misbehave. The simplest cure is to clear memory before you assemble, with Zen's F (Fill) command:

F
START>8000H
STOP>9FFFH
DATA>0

Better still, have the program clear its own buffers when it starts. Then it does not matter which assembler built it.

7. Include files are read in, in order

Zen has no include. The course's main.asm from S30 ends with its three include lines, so read the files in the same order they are included, and RS joins them into one text. Delete the include lines, put END at the end of the last file only, and then:

K
RS
NAME>1:MAIN
RS
NAME>1:VDP
RS
NAME>1:SCREENS
RS
NAME>1:SPRITES

Assemble the whole thing as one program, which is exactly what include made of it anyway: one ORG and one LOAD at the start, one END at the finish.

How big a program fits

Zen keeps your source text in memory from 2400H upwards, and LOAD 8000H puts the code at 8000H, so the text has about 23K before the two meet. The final game of this course, comments and all, is about 16K of text and fits with room to spare. H (Howbig) shows where your text ends: if the second number it prints is getting close to 8000, it is time to shorten your comments.

Testing without leaving Zen

For a quick test, Zen can run code where it sits, stop it at a breakpoint and show the registers, without saving anything. The code has to be built to run where it is stored, so use the same address for both, say ORG 9000H and LOAD 9000H, then G9000H. The Zen guide shows how. For the finished program, go back to ORG 256 and LOAD 8000H.

Summary

  • ORG 256, then LOAD 8000H, at the top; END at the bottom.
  • A assembles, WC from 8000H saves a program, and it runs from the 0: prompt like any other.
  • Capital H for hex, no binary, DEFB for DEFM, and read files in turn instead of include.
  • Two changes give no error: work out any sum with * or / yourself, and never assume DEFS space is zero.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.