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 on interrupts for more detail).
The random number seed is then modified further whenever a random number is generated. The flowchart shows how this works:
Here’s the code for this routine:
|Address (hex)||Code (hex)||Labels||Assembly||Comments|
|01D9||19||CXNN:||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)|
|01DD||BE||PHI E||Put this in RE.1. RE now points to a random byte of interpreter code in page 0x01|
|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 experiment, but it’s perfectly 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.