Inside the Einstein: The MOS, a TatungBytes course: the MOS 1.2 start up screen on a rainbow sunburst
← Back to Modules
module
7

One Byte to Call the MOS

Introduction

You have written RST 8 / DEFB 158 so often it is a reflex. But think about what it has to achieve. Your program runs with RAM switched in, so the MOS's code is not visible at all (S4). Yet two bytes later a character is on the screen and your program carries on. This section follows those two bytes.

Why RST

RST 8 is a call to address 0008h that takes one byte instead of the three a CALL needs. Even with the call's number in the byte after it, a MOS call is two bytes against a CALL's three, so a program that makes a hundred calls saves a hundred bytes. On a machine with 8K of ROM and programs measured in kilobytes, that mattered.

The price is that there are only eight RST addresses, and they are fixed. So the MOS uses just one of them for all its calls and passes the call's number in the byte that follows. The processor knows nothing about that byte; it is the MOS's job to read it and to make sure your program does not try to execute it.

Step 1: the jump in RAM

Your RST 8 pushes the address of the next byte - the number - and jumps to 0008h. RAM is in, so 0008h is the copy start-up put there (S6):

        jp 0fc22h               ; RAM 0008h

and that jumps to the code start-up copied into high RAM.

Step 2: FC22h - read the number, switch the ROM in

In the source this code sits in the ROM at 105Ah, under its original name; start-up copied it to FC22h, where it runs:

ZCALM1:
        ld (0fb79h),hl          ;105a (runs at fc22h)
        pop hl                  ;105d (runs at fc25h)
        push af                 ;105e (runs at fc26h)
        ld a,(hl)               ;105f (runs at fc27h)
        inc hl                  ;1060 (runs at fc28h)
        ex (sp),hl              ;1061 (runs at fc29h)
        ld (0fb7bh),sp          ;1062 (runs at fc2ah)
        push hl                 ;1066 (runs at fc2eh)
        or a                    ;1067 (runs at fc2fh)
        ld hl,0fb00h            ;1068 (runs at fc30h)
        sbc hl,sp               ;106b (runs at fc33h)
        pop hl                  ;106d (runs at fc35h)
        jr c,l1073h             ;106e (runs at fc36h)
        ld sp,0fcffh            ;1070 (runs at fc38h)
l1073h:
        out (024h),a            ;1073 (runs at fc3bh)
        call FMC1               ;1075 (runs at fc3dh)

Read it in pieces:

  • Save HL at FB79h - the MOS needs HL for a moment, and your program must get it back.
  • POP HL takes the return address: the address of the number. LD A,(HL) reads the number. INC HL and EX (SP),HL put back a return address one byte further on, so that when the call finishes your program resumes after the number. Your A is kept safe on the stack.
  • Save SP at FB7Bh, so the way out can restore it exactly.
  • Check the stack. SBC HL,SP with HL = FB00h compares your stack pointer with FB00h. If your stack is below FB00h (in the bottom 32K, say), it is about to vanish when the ROM is switched in, so the code moves SP to the MOS's own stack at FCFFh first. Most programs' stacks are higher, and the move is skipped.
  • OUT (24h),A - the ROM is in. This code is in the top half, so it is still there.
  • CALL FMC1 - into the ROM.

Step 3: FMC1 - which routine?

FMC1:
        push hl                 ;0fca
        bit 7,a                 ;0fcb
        res 7,a                 ;0fcd
        jr z,FMUSR              ;0fcf
        ld hl,(0fb3ah)          ;0fd1
        add a,a                 ;0fd4
        add a,l                 ;0fd5
        ld l,a                  ;0fd6
        jr nc,l0fdah            ;0fd7
        inc h                   ;0fd9
l0fdah:
        ld a,(hl)               ;0fda
        inc hl                  ;0fdb
        ld h,(hl)               ;0fdc
        ld l,a                  ;0fdd
        pop af                  ;0fde
        push hl                 ;0fdf
        ld hl,(0fb79h)          ;0fe0
        ret                     ;0fe3

The number's top bit is tested and removed. With it set - every call you have used, 128 and above - the number minus 128 is doubled and added to the address held at FB3Ah, which is 0282h, the start of a table of routine addresses. The word found there is the routine. POP AF gives back your A; PUSH HL and RET jump to the routine with HL restored. For 158 the word is at 02BEh and holds 0F1Ah: OUTC, the routine that prints. S8 is about that table.

Notice the trick at the end: PUSH HL / RET is a jump to an address held in a register, made without losing HL's value, because HL is reloaded in between.

Step 4: the way back

When OUTC returns, it returns into the RAM code just after CALL FMC1:

        out (024h),a            ;1078 (runs at fc40h)
        ld sp,(0fb7bh)          ;107a (runs at fc42h)
        ret                     ;107e (runs at fc46h)

RAM back in; your stack pointer back; return to the byte after the number.

Calls below 128

A number with its top bit clear takes the other branch at JR Z,FMUSR:

FMUSR:
        ld hl,(0fb3ch)          ;0fe4
        jp (hl)                 ;0fe7

No table: just a jump to the address held at FB3Ch. As start-up leaves it, that is 007Dh - INITM, from S6. So RST 8 / DEFB 1 does not return at all: it restarts the MOS, and the start-up screen is drawn again.

When the MOS calls itself

The MOS makes RST 8 calls too, with the ROM already in. Then 0008h is the ROM's own byte, and there it says:

l0008h:
        jp FXMON                ;0008
...
FXMON:
        ld (0fb79h),hl          ;0fc2
        pop hl                  ;0fc5
        push af                 ;0fc6
        ld a,(hl)               ;0fc7
        inc hl                  ;0fc8
        ex (sp),hl              ;0fc9
FMC1:

The same steps without the switching, falling straight into FMC1. Two ways in, one lookup: the RAM copy at 0008h and the ROM's 0008h are the same call reached from either side of the switch.

The code

        ORG 256

        LD A,'A'
        RST 8
        DEFB 158        ; OUTC: table entry 30, at 02BEh
        RST 8
        DEFB 181        ; KSCAN: table entry 53, at 02ECh
        RET

Build and run

Press Run.

What you should see

>A
 A   BC   DE   HL   PC  SZ-H-PNC
00 00FF 07FF FB46 0FA5 01011100

The A from call 158. Then call 181 looked for a key, found none, and returned 0 in A - the register display's first column. Two calls, two different routines, one route in.

Summary

RST 8 is a one-byte call. The number after it is read through the return address, which is then stepped past it. With RAM in, the call reaches a jump start-up left at RAM 0008h, then code in high RAM that switches the ROM in and calls FMC1; with the ROM in, the ROM's own 0008h leads to the same place. FMC1 finds the routine in a table whose address is held at FB3Ah, and the RAM code switches back and returns past the number.

Next

S8 opens the table: 85 addresses, and the reason the > prompt uses them too.

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.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.