Ben's Notebook

Noteworthy topics, mostly technical

RCX Deep Dive

Part 3: Notes on ROM and firmware

RCX Internals famously includes a C-like pseudocode version of the ROM and firmware version 0309 with extensive comments. This page aims to build on that work by providing pseudocode for firmware versions that, to my knowledge, don't already have publicly available decompilations. This will be helpful to see when features were added and bugs were fixed.

Most of the work here comes from my own automated disassemblies of the firmware files hosted at pbrick.info. Since the code blocks on this page are very large, I have put them into collapsible containers.

Built-in Programs

First let's look at the simple programs that are built into the ROM, the ones that are available to run without any firmware on the brick. There are 5 built-in programs, though some sources like pbrick.info incorrectly claim there are 3.

The ROM's main loop repeatedly calls the function rom_program_update() at address $0d8c whenever the brick is in its normal running state. Each of the five programs is implemented as a simple state machine.

View ROM program pseudocode
RAM variables used by ROM programs:

ef10 - byte, program number (0-4  -->  programs 1-5)
ef11 - byte, apparently unused
ef12 - sensorstruct, sensor 0 (port 1)
    ef12 - byte, type
    ef13 - byte, mode
    ef14 - word, raw
    ef16 - word, value
    ef18 - byte, boolean
    ef19 - byte, unused
ef1a - sensorstruct, sensor 1 (port 2)
    ef1a - byte, type
    ef1b - byte, mode
    ef1c - word, raw
    ef1e - word, value
    ef20 - byte, boolean
    ef21 - byte, unused
ef22 - sensorstruct, sensor 2 (port 3)
    ef22 - byte, type
    ef23 - byte, mode
    ef24 - word, raw
    ef26 - word, value
    ef28 - byte, boolean
    ef29 - byte, unused
ef2a - byte, motor state (low bit = dir, high bit = stop)
    motor 0 (port A): bits 0x30
    motor 1 (port B): bits 0xc0
    motor 2 (port C): bits 0x03
ef2b - byte, walking figure state
ef2c - byte, button state (run: bit 0, prgm: bit 2)
ef2d - byte, program state
ef2e - word, random number
ef30 - byte, loop counter



// called when the ROM first boots up
$0d44 - rom_init_program()
{
    
random number$ef2e: word
= 0xffff
button state$ef2c: byte
= 0
program number$ef10: byte
= 0
walking figure state$ef2b: byte
= 0
sensor 0 type$ef12: byte
= 0 // touch sensor
sensor 0 mode$ef13: byte
= 0 // raw
sensor 1 type$ef1a: byte
= 3 // light sensor
sensor 1 mode$ef1b: byte
= 0x80 // percent
sensor 2 type$ef22: byte
= 0 // touch sensor
sensor 2 mode$ef23: byte
= 0 // raw
motor state$ef2a: byte
= 0
program state$ef2d: byte
= 0 return } // called whenever ROM main loop state = 0x0d $0d8c - rom_program_update(r6=dataptr) { make room for 4 bytes on stack if (
task 0 wakeup delay$ee82: word
== 0) buttons = read_buttons() // ROM routine $1fb6 if (buttons & 1) // run button pressed? if (bit 0 of
button state$ef2c: byte
is clear) set bit 0 of
button state$ef2c: byte
play sound 1 (double beep)
minutes to power off$ee80: word
= 15
task 0 wakeup delay$ee82: word
= 0x14 // 0.20 seconds if (
walking figure state$ef2b: byte
== 0x01) rom_program_stop() else
walking figure state$ef2b: byte
= 0x01 else clear bit 0 of
button state$ef2c: byte
if (buttons & 4) // prgm button pressed? if (bit 2 of
button state$ef2c: byte
is clear) set bit 2 of
button state$ef2c: byte
program number$ef10: byte
= (
program number$ef10: byte
+ 1) % 5 rom_program_stop() play sound 1 (double beep)
minutes to power off$ee80: word
= 15
task 0 wakeup delay$ee82: word
= 0x14 // 0.20 seconds else clear bit 2 of
button state$ef2c: byte
// PRNG using a modified xorshift algorithm bit = 0 if (
random number$ef2e: word
& 0x0002) bit ^= 1 if (
random number$ef2e: word
& 0x0010) bit ^= 1 if (
random number$ef2e: word
& 0x0040) bit ^= 1 if (
random number$ef2e: word
& 0x2000) bit ^= 1
random number$ef2e: word
= (2 *
random number$ef2e: word
) | bit // Store sensor data in the structs defined above for (sensor = 0, sensor >= 3, ++sensor) read_sensor(0x1000+sensor, 0xef12+8*sensor) if (
walking figure state$ef2b: byte
== 0x01) set LCD walking figure segments else set LCD standing figure segments if (get_battery_voltage() <= 0x00df) set LCD low battery indicator else clear LCD low battery indicator clear all LCD port active indicators if (
motor state$ef2a: byte
& 0x20) if (
motor state$ef2a: byte
& 0x10) set LCD motor 0 backward else set LCD motor 0 forward if (
motor state$ef2a: byte
& 0x08) if (
motor state$ef2a: byte
& 0x04) set LCD motor 1 backward else set LCD motor 1 forward if (
motor state$ef2a: byte
& 0x02) if (
motor state$ef2a: byte
& 0x01) set LCD motor 2 backward else set LCD motor 2 forward if (
sensor 0 boolean$ef18: byte
!= 0) set LCD sensor 0 active if (
sensor 1 boolean$ef20: byte
!= 0) set LCD sensor 1 active if (
sensor 2 boolean$ef28: byte
!= 0) set LCD sensor 2 active display program number on LCD refresh_display() // full refresh - takes 1.5 ms // This almost looks like a bug - // program only updates when figure is walking? if (
walking figure state$ef2b: byte
== 0) goto end // built-in program 1 at $0fe6 if (
program number$ef10: byte
== 0) if (
program state$ef2d: byte
== 0) play sound 1 (double beep) ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 1) motor 0 forward power 7 motor 2 forward power 7
motor state$ef2a: byte
= 0x22 goto end // built-in program 2 at $1050 if (
program number$ef10: byte
== 1) if (
program state$ef2d: byte
== 0) motor 0 forward power 7 motor 2 forward power 7
motor state$ef2a: byte
|= 0x22 ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 1) if (
sensor 0 boolean$ef18: byte
== 0) motor 0 forward power 7 set bit 5 of
motor state$ef2a: byte
else motor 0 brake clear bit 5 of
motor state$ef2a: byte
if (
sensor 2 boolean$ef28: byte
== 0) motor 2 forward power 7 set bit 1 of
motor state$ef2a: byte
else motor 2 brake clear bit 1 of
motor state$ef2a: byte
goto end // built-in program 3 at $112e else if (
program number$ef10: byte
== 2) if (
program state$ef2d: byte
== 0) motor 0 forward power 7 motor 2 forward power 7
motor state$ef2a: byte
|= 0x22 ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 1) set sensor 1 active if (
sensor 1 value$ef1e: word
> 0x0032) motor 0 forward power 7 motor 2 forward power 7
motor state$ef2a: byte
|= 0x22 if (
sensor 1 value$ef1e: word
< 0x0028) motor 0 brake motor 2 brake
motor state$ef2a: byte
&= ~0x22 goto end // built-in program 4 at $120c else if (
program number$ef10: byte
== 3) if (
program state$ef2d: byte
== 0)
loop counter$ef30: byte
= 5 ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 1) if (
loop counter$ef30: byte
& 1) motor 0 forward power 7 motor 2 forward power 7
motor state$ef2a: byte
= 0x22 else motor 0 reverse power 7 motor 2 reverse power 7
motor state$ef2a: byte
= 0x33
task 2 wakeup delay$ee86: word
= (
random number$ef2e: word
% 0x012c) // max 3.00 seconds ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 2) if (
task 2 wakeup delay$ee86: word
== 0) if (
loop counter$ef30: byte
& 1) motor 0 reverse power 7
motor state$ef2a: byte
= 0x32 else motor 0 forward power 7
motor state$ef2a: byte
= 0x23
task 2 wakeup delay$ee86: word
= (
random number$ef2e: word
% 0x012c) // max 3.00 seconds ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 3) if (
task 2 wakeup delay$ee86: word
== 0) if (--
loop counter$ef30: byte
== 0) rom_program_stop() else
program state$ef2d: byte
= 1 goto end // built-in program 5 at $1344 else if (
program number$ef10: byte
== 4) if (
program state$ef2d: byte
== 0) motor 0 forward power 7 motor 2 forward power 7
motor state$ef2a: byte
= 0x22 ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 1) if (
sensor 0 boolean$ef18: byte
== 1) motor 0 reverse power 7 motor 2 reverse power 7
motor state$ef2a: byte
= 0x33
task 2 wakeup delay$ee86: word
= 0x0064 // 1.00 second ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 2) if (
task 2 wakeup delay$ee86: word
== 0) motor 0 forward power 7
motor state$ef2a: byte
= 0x23
task 2 wakeup delay$ee86: word
= 0x0032 // 0.50 seconds ++
program state$ef2d: byte
else if (
program state$ef2d: byte
== 3) if (
task 2 wakeup delay$ee86: word
== 0)
program state$ef2d: byte
= 0 goto end // $142a end: clear bit 7 of
dispatch data$ee64: byte
clear r6 return } $1446 - rom_program_stop() {
walking figure state$ef2b: byte
= 0
program state$ef2d: byte
= 0
motor state$ef2a: byte
= 0 motor 0 brake motor 2 brake // bug? only sensor 1 was ever set active, but sensor 2 is turned off instead set sensor 2 passive return }

Back to top of code


Official Firmware

TODO