RCA Studio 2 Psuedo Machine Language

Last updated 22nd August 2000
Written by Paul Robson (autismuk@classicgaming.com)

Introduction

If you are familiar with the Chip-8 programming language, originally produced for the RCA Cosmac VIP, and also used in the ETI Dream 6800, Hewlett Packard Calculators, IBM Compatibles, Gameboys, MSX and various other systems the feel of the machine language will come to you quickly. But be careful ; some of it is completely different, and some of it is subtly different, so it is easy to make mistakes.

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.

Variables

There are 16 1 byte variables normally known as r0..r15, or alternatively r0-rf. Many of these have special functions, but they can, with the exceptions of RD-RF be used as general purpose registers too.
 
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.

Memory Map

The memory map is identical to that of the RCA Studio 2. ROM is between $000 and $7FF, with the game code beginning at $400. RAM is between $800 and $9FF, and is exactly duplicated between $C00 and $DFF.

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.

Instruction Set

This is the instruction set of the psuedo machine language.  The graphic instructions marked with an asterisk are explained in more detail in the graphics section. The mnemonics are proposed only.
 
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 !)

Graphics Commands

The graphics subsystem is completely different to that of the Chip-8 system. It works around the concept of graphic objects ; a bit like sprites. There are 8 of these , and they are all a maximum of 8 x 8 pixels. These are stored in areas of page $8xx as follows :-
 
 
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.


Back to index