Skip to content

Chip-8 on the COSMAC VIP: Generating Random Numbers

This is part of a series of posts analysing the Chip-8 interpreter on the RCA COSMAC VIP computer. These posts may be useful if you are building a Chip-8 interpreter on another platform or if you have an interest in the operation of the COSMAC VIP. For other posts in the series refer to the index or instruction index.

INSTRUCTION GROUP: CXNN
Load VX with a random number from 0 to 255 ANDed with NN

This instruction is essential for games or other applications where you need to generate apparently random behaviour.

This routine relies on a random number seed. Although this value is never explicitly initialised, because it uses register R9 it will be at zero when the interpreter is run. The interrupt routine in the COSMAC VIP’s operating system increments this value 60 times every second (see this earlier post for more detail).

The random number seed is then modified further whenever a random number is generated. The flowchart shows how this works:

A flowchart describing the function of the COSMAC VIP Chip-8 interpreter's random number generator.
A flowchart describing the function of the COSMAC VIP Chip-8 interpreter’s random number generator

Here’s the code for this routine:

Labels
Address (hex)

Code (hex)
Assembly

Comments

CXNN:
01D9

19
INC 9

Increment random number seed (R9). This value is incremented 60 times a second by the interrupt routine, but this may not have run since the last random number was generated, so it is also incremented here.

01DA

89
GLO 9

Get the low-order byte of the random number seed.

01DB

AE
PLO E

Save this in RE.0.

01DC

93
GHI 3

Get the high order byte of the interpreter programme counter (this will be 0x01 in the usual COSMAC VIP configuration).

01DD

BE
PHI E

Put this in RE.1. RE now points to a “random” byte of interpreter code in that page.

01DE

99
GHI 9

Get the high order byte of the random number seed.

01DF

EE
SEX E

Use RE for register indirect addressing.

01E0

F4
ADD

Add value of random byte from interpreter code to the current high-order byte of the random number seed.

01E1

56
STR 6

Store this in VX.

01E2

76
SHRC

Shift the result one bit to the right. This will effectively divide the full result of the addition by 2 as it takes into account the carry bit generated by the addition.

01E3

E6
SEX 6

Use VX pointer for register indirect addressing.

01E4

F4
ADD

Add current value in VX to shifted value in accumulator.

01E5

B9
PHI 9

Save this as the new high-order byte of the random number seed.

01E6

56
STR 6

Put this value in VX.

01E7

45
LDA 5

Get second byte of Chip-8 instruction and advance programme counter.

01E8

F2
AND

Use this to mask the random number in VX.

01E9

56
STR 6

Put final value in VX.

01EA

D4
SEP 4

Return to the fetch and decode routine.

Execution time for this instruction is 36 machine cycles (163.44 microseconds).

There are a couple of things to note about this routine. Firstly, as it uses the code in the interpreter as a base source of numbers, the distribution of numbers will not be even. That’s not a massive issue – you probably wouldn’t want to use this random number generator as a source of numbers for any scientific modelling, but it’s adequate for games.

Secondly, the use of a mask gives this routine a lot of flexibility. The obvious use of the mask is to restrict numbers to a particular range. For example, if you wanted to generate only numbers between zero and 15, you could use CX0F (binary: 00001111). However, you can also do things like only generating even numbers (CXFE – binary: 11111110). Then you would just add 0x01 to the result to only generate odd numbers. You could also only generate multiples of a higher value. For example, multiples of 16 (CXF0 – binary 11110000).

The programmer of a contemporary interpreter should provide the correct behaviour for this instruction, including the masking. The method used for generating the numbers can either follow the algorithm of the original interpreter, or use the facility for random number generation that is provided by whatever language you are using for the interpreter. The former will result in behaviour that is more faithful to the original interpreter, but the latter will almost certainly result in a better distribution of numbers.

Published inProgrammingRetro Computing

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *