The machine language is made up mostly of 2 byte instructions (though, unlike Chip 8, there are some 1 byte instructions) that give access to all the features of the machine.
Programming is mid way between assembly programming and high level programming, The basic coding style is linear and procedural with simple instructions like move, load, add and jump, like any 8 bit assembly language.. However, there are some instructions which perform quite complex instructions.
Hexadecimal | Decimal | Function |
R0-R8 | R0-R8 | General purpose registers - can have any function. |
R9 | R9 | Select a sprite for graphics commands (values 0-7) |
RA | R10 | Select a keypad for key scanning (1 = Left Keypad, 0 = Right Keypad) |
RB | R11 | Carry, return value for Key scan |
RC | R12 | Select a direction when moving a sprite in a different direction to the one currently specified |
RD | R13 | Counts down at 60Hz when non-zero, Beeper sounds when non-zero |
RE-RF | R14-R15 | Counts down at 60Hz when non-zero |
Also there is a 12 bit index register known as ix, which can be used to access memory. All Registers have an undefined value when the program starts.
That 0.5k of RAM is used as follows :-
Memory | Description |
0800-087F | Sprite Graphics Storage : Contains binary representations of the game graphics. There are 8 sprites in total (see "Graphics Commands") |
0880-08AC | User memory for programs |
08AC-08BF | 1802 Stack Space : Also used for subroutine calls in the machine language. The boundary is approximately at $08AC but depends very much on the individual program. About 8 or 9 bytes are required for 1802 Machine Code. |
08C0-08CF | The 16 variables, normally called RA-RF |
08D0-08F7 | Sprite Information : 8 x 5 byte records (see "Graphics Commands") |
08F8-08FF | User memory for programs |
0900-09FF | Video Memory |
All the machine code instructions that access memory using the registers R0-RF automatically use page $08 ; to access memory elsewhere you must use the IX register, which can address any location in the machine's address space.
Opcode | Mnemonic | Description |
0aaa | call aaa | 1802 Machine code call to address aaa |
0066 | call $066 | Turns on the video circuitry. This is actually done in the boot sequence. |
02F2 | call $02f2 | If IX contains $ann Fills memory locations $a00 to $ann (inclusive) with zero. |
1aaa | jmp aaa | Jump to address aaa |
2aaa | jsr aaa | Call subroutine at address aaa. The maximum depth of subroutine call is defined by the 1802 Stack, which grows down into user memory from $0880 onwards |
3raa | jnz Rr,aa | Short Jump (same page) to aa if Register r is not zero |
4raa | jz Rr,aa | Short Jump (same page) to aa if Register r is zero |
5rnn | skne Rr,#nn | Skip following instruction (2 bytes) if Register r is not equal to constant nn |
6rnn | ldi Rr,#nn | Load constant nn into register r |
70aa | djnz R0,aa | Decrement Register 0, short jump to aa if non-zero |
7rnn | adi Rr,#nn | Add constant nn to Register r (not if r = 0) |
8xy0 | mov Rx,Ry | Register X := Register Y, Register B (carry) undefined |
8xy1 | or Rx,Ry | Register X := Register X or Register Y, Register B (carry) undefined |
8xy2 | and Rx,Ry | Register X := Register X and Register Y, Register B (carry) undefined |
8xy3 | xor Rx,Ry | Register X := Register X xor Register Y, Register B (carry) undefined |
8xy4 | add Rx,Ry | Register X := Register X + Register Y, Register B (carry) set if carry out |
8xy5 | sub Rx,Ry | Register X := Register X - Register Y, Register B (carry) set if no borrow |
8xy6 | - | Register X := Register Y shr 1, Register B (carry) is lost bit |
8xy7 | rsb Rx,Ry | Register X := Register Y - Register X, Register B (carry) set if no borrow |
8xyE | - | Register X := Register Y shl 1, Register B (carry) is lost bit |
9xy0 | skne Rx,Ry | Skip following instruction (2 bytes) if Register x not equal to Register Y |
9xy1 | - | Register X := Register Y (?) |
9xy2 | ldr Rx,Ry | Register X := Memory Location in Page 8[Register Y] |
9xy4 | str Rx,Ry | Memory Location in Page 8[Register Y] := Register X |
9xy8 | bcd Rx,Ry | BCD Convert Register X into Page 8, addresses Register Y+0 -> Register Y+2. The most significant bit is in Register X is unchanged, Y points to the last byte of the converted number. |
Annn | ldx #nnn | Load IX with constant nnn |
Bcnn | sti c,#nn | Store constant nn at memory location IX (12 bit), then add c to IX |
C0 | ret | Return from subroutine |
Crnn | ran Rr,#nn | Calculate and random number, and with constant nn, store in Register R |
Dkaa | key n,aa
key RB,aa |
Check if key number K is pressed. If so, store key in RB and do a short jump to location aa. If k is $0F then the key to be checked is in register RB. The keypad is selected using register RA, 1 = Left Keypad, 0 = Right Keypad |
E0 | spclear | Clear Sprite Graphic Buffer* |
E1 | spmove | Move Sprite* |
E2 | spmovec | Move Sprite in direction given register RC* |
E4 | spxor | XOR Graphic Data at IX into Sprite Graphic Buffer* |
E8aa | spdraw aa | XOR Draw Sprite, short jump to aa if a collision occurs.* |
Fraa | - | Does a 1802 code call to memory location $02aa |
FrA6 | ldm Rr | Register Rr := Memory[Index] |
FrA9 | stm Rr | Memory[Index] := Register Rr |
FrAC | ldma Rr | Register Rr := Memory[Index], then increment Index |
FrAF | stma Rr | Memory[Index] := Register Rr, then Increment Index |
FrB3 | slsb Rr | Set Least significant byte of Index to Register Rr, bits 8..11 are unaffected |
FrB6 | afor Rr | And Register Rr with 15, then or into least significant byte of the Index Register (used for looking up number graphics !) |
Address (GR0) | Description | Addresses (others) |
$800-$80F | 16 byte buffer arranged as 2 bytes per row, 8 rows. This contains the sprite graphic data, orientated for the current horizontal position. The "shifting" required to accurately position the bits is done as part of the sprite move command, not as part of the drawing. | $810..$81F
$820..$82F etc. |
$8D0 | Absolute position on screen. This is the address of the left, or right half of the shifted 8 x 8 pixels (because it is shifted it takes 16 pixels as they are orientated so as to be easily copied onto screen). The address is determined by the high bit of the direction byte. | $8D1
$8D2 etc. |
$8D8 | The lower 4 bits specify the height of the sprite from 0..7. The upper bit (7) reverses the drawing byte pairs in $800,1 $802,3 and so on, and also changes the effective start address calculated from the byte stored in $800 | $8D9
$8DA etc. |
$8E0 | Current direction is stored in the lower 4 bits. This can be 8 (up) 4 (left) 6 (right) 2 (down) 0 (stationary). | $8E1
$8E2 etc. |
$8E8 | Value of 1 - Screen Position calculated on every horizontal move. I have no idea what the function of this byte is. | $8E9
$8EA etc. |
Drawing is done slightly differently. First you select your sprite object, this is done by putting a value from 0 to 7 in R9. Then you can blank the graphics buffer, with the command $E0. Having done that, you can set the coarse position into $8D0-$8D7 (this will be accurate vertically, and rounded down to 8 pixels horizontally), the height in $8D8-$8DF, and an initial direction in $8E0-$8E7.
Having done this, you can copy a graphic picture in using command $E4. You are now ready to work with the graphics. You can either move the sprite using $E1 and $E2, or draw it. However, if you "move" a sprite you have to erase the old one first : unlike sprite systems it does not do it for you.
Here are the sprite commands. For all instructions, R9 contains the
sprite number :-
Instruction | Description |
$E0 | Clear the sprite buffer ; zeroes $8x0 - $8xF. |
$E1 | Move the sprite in its currently selected direction. For Directions, see the above table |
$E2 | Move the sprite in the direction provided in RC. For directions, see above. This overrides the current direction stored in $8E0-7 but does not change it. |
$E4 | Using the sprite data pointed to by the index register, exclusive-or it into the sprite buffer. This only works correctly before the sprite is moved. The Index register points to the byte after the sprite data afterwards. The number of bytes to exclusive-or is taken from the height bits. |
$E8aa | Xor plot the sprite at its current position ; repeating the operation restores the screen to its original state. If there is a collision (e.g. a pixel on the screen changes from 1->0), then a short jump takes place. |