|
The RCX is a programmable, microcontroller-based brick that can simultaneously operate three motors, three sensors, and an infrared serial communications interface. The brick is one of approximately 727 pieces of LEGO® set 9719 Robotics Invention System.
This document is a description of the internals of the RCX, and is organized into the following sections:
Please note that this not an official LEGO® document or web site. LEGO® is a trademark of the LEGO Group, which does not sponsor, authorize, or endorse this document.
In addition, note that this document describes RCX internals as the author understands them. While every effort has been made to ensure that the contents of this document are accurate, the author does not guarantee that any portion of this document is correct. The author cannot be held responsible for any consequences of the use or misuse of the information contained in this document.
This document is currently a work in-progress.
Copyright © 1998, 1999 Kekoa Proudfoot. All
rights reserved.
|
The base system for using the RCX consists of the RCX itself, an infrared transceiver, and a PC. Additional components, such as motors, sensors, and other building elements, combine with the base system to allow the creation of functional autonomous robotic devices.
At the core of the RCX is a Hitachi H8 microcontroller with 32K of external
RAM. The microcontroller is used to control three motors, three sensors,
and an infrared serial communications port. An on-chip, 16K ROM contains a
driver that is run when the RCX is first powered up. The on-chip driver is
extended by downloading 16K of firmware to the RCX. Both the driver and
firmware accept and execute commands from the PC through the IR
communications port. Additionally, user programs are downloaded to the RCX
as byte code and are stored in a 6K region of memory. When instructed to
do so, the firmware interprets and executes the byte code of these
programs.
|
RCX
Pictures of a disassembled RCX. To remove the circuit board, open the RCX, remove the four screws, remove the IR shield, then separate the front cover from the circuit board. Release the two battery contacts from the battery side, then slide the circuit board free. Do this at your own risk; if you're not careful there's a chance you'll break something.
Patrick Gili reports that you can also twist the circuit board free without first removing the battery contacts.
I've found that the negative battery contact is somewhat easier to release than the positive one. By removing the negative contact first, then sliding the circuit board free, I am able to remove the positive contact from the circuit board side by inserting a small screwdriver in the slot between the contact and the battery case and gently prying the contact outward and upward. I'm not sure if this technique will work for others; it might be possible only because I have already disassembled my RCX several times.
The parts, from left to right, starting with the top row: circuit board, front cover; IR shield, screws, battery contacts; battery case, battery cover. Two removable plastic ribs are sitting on the battery case. (1x image) | |
The top of the circuit board. Two IR LEDs and an IR receiver are on the left, an LCD is in the center, and several capacitors and an adapter socket are on the right. The LCD is covering an LCD controller and a speaker; above and below the LCD are the contacts for the four rubber keys. Twelve clamps rise from the circuit board to mate with the input and output ports on the cover. (1x image, 2x image, part numbers) | |
The bottom of the circuit board. The large square chip is the microcontroller, the chip below that is a RAM. The large chip in the middle is a bank of flip flops, the small chip above that is a bank of NAND gates. Peter Phillips sent word that the three identical chips on the left half of the board are indeed motor controllers. (1x image, 2x image, part numbers) |
IR Transceiver
It is also possible to disassemble the IR transceiver. Simply remove the four screws and lift off the back cover, making sure to move the battery spring out of the way.
IR transceiver parts. From left to right, starting with the top row: front cover, back cover; circuit board, screws. The front cover doubles as an IR filter. (1x image) | |
The top of the circuit board in the IR transceiver. A switch and a 6.7 ohm resistor are on the left, the two chips in the middle are banks of NAND gates, and the grey circle in the lower right is a voltage regulator. The white rectangle below the chip in the upper middle is a green LED, and hanging off the right side of the chip are the IR receiver and two identical IR LEDs. The DB-9 serial connector is on the opposite side of the circuit board. (1x image, 2x image) |
Cables
The cable to connect the IR transceiver to your PC is a null modem cable that contains six wires, of which only five are used. With the larger row on top, the holes of each connector are numbered from right to left starting with the top row. Pins inserted into these holes are joined by the cable to pins inserted into the holes of the opposite connector as follows:
Pin | To | Name | Description |
---|---|---|---|
2 | 3 | RD | Receive Data |
3 | 2 | TD | Transmit Data |
5 | 5 | SG | Signal Ground |
7 | 8 | RTS | Ready To Send |
8 | 7 | CTS | Clear To Send |
Pin 4 connects to pin 4 but is unused; pins 1, 6, and 9 have no connection.
The RTS/CTS signals are not used for flow control. Instead, they are used by the PC to check whether or not the transceiver is connected. The transceiver wires CTS and RTS together; the PC checks for the transceiver by asserting and deasserting RTS. If it sees that CTS tracks RTS, then it assumes that the device sitting on the serial port is the transceiver.
In case you're new to this sort of thing, to attach the IR transceiver to a
Mac, you can use the usual combination of a DIN-8 to DB-25 serial cable
coupled to a DB-25 to DB-9 adapter. If you try this, you might not be able
to get the RTS and CTS signals to function properly; this shouldn't be a
problem if you're writing or using custom software.
|
Encoding
The bit encoding and basic packet structure of the serial protocol was originally described by Dave Baum in a message posted to the lego-robotics mailing list. This section summarizes and expands on that description.
My notes as of Nov 10 1998, which roughly outline some of the details of the serial protocol, the byte code interpreter, and other parts of the rcx:
Basic packet format Immediately after packet header is opcode byte Remainder is data for opcode PC sends query opcode RCX reply opcode is always ~query opcode RCX completely ignores messages that have invalid packet checksums Messages sent by PC seem to alternate between having 0x08 set and not set This 0x08 bit is a sequence bit only in one special case RCX never executes same exact opcode twice in a row Second and beyond are dropped But the same reply that was sent the first time is sent again Toggle 0x08 bit to make sure same opcode twice in a row is accepted Sometimes packets start with aa ff 00 instead of 55 ff 00 Sometimes packets start with stranger things too Strange packet headers are most likely due to receiver bias and timing errors Special packet data 01 02 03 04 ff fe fd fc sent when RCX is not listening Tower echos commands sent by PC Sometimes an ff appears a few seconds after a message is received Chris Osborn explained that ff is caused by tower powering down Confirmed this observation Friederich Prinz notes 55 ff 00 is not strictly necessary when sending to RCX He uses ff 00 instead I verified that this agrees with the ROM pseudocode Any string of 55s, ffs, and 00s (or the empty string) works as a header |
Byte Code Interpreter Tasks and subroutines 8 subroutines per program 10 tasks per program Mindstorms software does not seem to use subroutines, only tasks My Commands seem to be inlined regardless of how many times they are used But subroutines are there to use if you want to make use of them yourself Memory map The memory map contains addresses, stored as big endian shorts. The layout is as follows: index description 00-07 prog 0 subs 0-7 start 08-15 prog 1 subs 0-7 start 16-23 prog 2 subs 0-7 start 24-31 prog 3 subs 0-7 start 32-39 prog 4 subs 0-7 start 40-49 prog 0 tasks 0-9 start 50-59 prog 1 tasks 0-9 start 60-69 prog 2 tasks 0-9 start 70-79 prog 3 tasks 0-9 start 80-89 prog 4 tasks 0-9 start 90 datalog start 91 datalog next 92 first free 93 last valid The addresses increase monotonically, so the end of the space allocated for one item can usually be found by looking at the next address in the map. The first address is ceba and the last address is e6b9. Therefore the total size of user memory is exactly 6K. The memory map says something about how user memory is managed inside the rcx. Whenever an item is added, later items are moved up in memory to make room for the new item. Whenever an item is deleted, later items are moved down in memory to consolidate free space. An empty subroutine takes up one byte of space, while an empty task takes up zero bytes of space. When a start download or set datalog command is sent, it might fail with an error code related to amount of memory. Memory map was used to confirm this. Sources Many commands take parameters as source and argument Sources are like addressing modes, many of which are unconventional: 0 variable <0..31> 1 timers <0,1,2,3> 2 immediate 3 motor state <0,1,2> <0x07=power 0x08=fwd 0x40=off 0x80=on 0x00=float> 4 random <return value is in 0 to argument, inclusive> 5 reserved 6 reserved 7 reserved 8 current program number 9 sensors <0,1,2> 10 sensor type <0,1,2> 11 sensor mode <0,1,2> 12 raw sensor value <0,1,2> 13 boolean sensor value <0,1,2> 14 minutes on clock/watch <0> 15 message <0> Sources 5, 6, and 7 are never valid. Do not use them. They are reserved for Cybermaster. Loops The byte code interpreter seems to include a stack of loop counters The maximum loop counter stack depth is four Set loop counter pushes stack down and sets the top value Decrement loop counter and branch decrements counter on top of stack Decrement before test When top counter is less than zero, stack is popped and branch is taken Otherwise branch is not taken Decrement loop counter and branch only branches forward But you can use a second branch to go backwards Sounds Sounds seem to be buffered, with buffer size around eight Sounds are lost when buffer is full Sounds seem to be asynchronous, meaning their calls seem to return immediately Programs Programs use the same opcodes as sent over serial cable PC opcodes which query state in RCX seem to be treated as noops in programs All other PC to RCX opcodes seem to work, which is surprising Unrecognized opcodes cause the program (task?) to halt Program (task?) also halts when current address is not in valid memory range Valid memory range specified in memory map Still need to describe how to compose and download a program |
|
The table in this section gives a brief overview of the opcodes that make up the RCX byte code and is an index into the RCX Opcode Reference, where more information about the opcodes can be found. An alphabetical opcode index is also available.
In the table below, the valid contexts of each opcode are listed under the Modes heading. A P indicates a request sent from the PC to the RCX, an R indicates a reply from the RCX to the PC, and a C indicates a command that may be used in byte code.
Opcode | Modes | Name | Encoding | Notes | ||
---|---|---|---|---|---|---|
10/18 | P | Alive | void | [details] | ||
12/1a | P | Get value | byte source byte argument | [details] | ||
13/1b | P | C | Set motor power | byte motors byte source byte argument | [details] | |
14/1c | P | C | Set variable | byte index byte source short argument | [details] | |
15/1d | P | Get versions | byte key[5] | [details] | ||
16/1e | R | Set motor direction | void | [details] | ||
17/xx | C | Call subroutine | byte subroutine | [details] | ||
20/28 | P | Get memory map | void | [details] | ||
21/29 | P | C | Set motor on/off | byte code | [details] | |
22/2a | P | C | Set time | byte hours byte minutes | [details] | |
23/2b | P | C | Play tone | short frequency byte duration | [details] | |
24/2c | P | C | Add to variable | byte index byte source short argument | [details] | |
25/2d | P | Start task download | byte unknown short task short length | [details] | ||
26/2e | R | Clear sensor value | void | [details] | ||
27/xx | C | Branch always near | byte offset | [details] | ||
30/38 | P | Get battery power | void | [details] | ||
31/39 | P | C | Set transmitter range | byte range | [details] | |
32/3a | P | C | Set sensor type | byte sensor byte type | [details] | |
33/3b | P | C | Set display | byte source short argument | [details] | |
34/3c | P | C | Subtract from variable | byte index byte source short argument | [details] | |
35/3d | P | Start subroutine download | byte unknown short subroutine short length | [details] | ||
36/3e | R | Delete subroutine | void | [details] | ||
37/xx | C | Decrement loop counter near | byte offset | [details] | ||
40/48 | P | C | Delete all tasks | void | [details] | |
42/4a | P | C | Set sensor mode | byte sensor byte code | [details] | |
43/xx | C | Wait | byte source short argument | [details] | ||
44/4c | P | C | Divide variable | byte index byte source short argument | [details] | |
45/4d | P | Transfer data | short index short length byte data[length] byte checksum | [details] | ||
46/4e | R | Set power down delay | void | [details] | ||
50/58 | P | C | Stop all tasks | void | [details] | |
51/59 | P | C | Play sound | byte sound | [details] | |
52/5a | P | C | Set datalog size | short size | [details] | |
52/5a | R | Unlock firmware | byte data[25] | [details] | ||
53/5b | R | Upload datalog | dlrec data[length] | [details] | ||
54/5c | P | C | Multiply variable | byte index byte source short argument | [details] | |
56/5e | R | Clear timer | void | [details] | ||
60/68 | P | C | Power off | void | [details] | |
61/69 | P | C | Delete task | byte task | [details] | |
62/6a | P | C | Datalog next | byte source byte argument | [details] | |
63/6b | R | Or variable | void | [details] | ||
64/6c | P | C | Sign variable | byte index byte source short argument | [details] | |
65/6d | P | C | Delete firmware | byte key[5] | [details] | |
66/6e | R | Set program number | void | [details] | ||
70/78 | P | C | Delete all subroutines | void | [details] | |
71/79 | P | C | Start task | byte task | [details] | |
72/xx | C | Branch always far | byte offset byte extension | [details] | ||
73/7b | R | And variable | void | [details] | ||
74/7c | P | C | Absolute value | byte index byte source short argument | [details] | |
75/7d | P | Start firmware download | short address short checksum byte unknown | [details] | ||
76/7e | R | Stop task | void | [details] | ||
81/89 | P | C | Stop task | byte task | [details] | |
82/8a | R | Start firmware download | byte errorcode | [details] | ||
82/xx | C | Set loop counter | byte source byte argument | [details] | ||
83/8b | R | Absolute value | void | [details] | ||
84/8c | P | C | And variable | byte index byte source byte argument | [details] | |
85/xx | C | Test and branch near | byte opsrc1 byte src2 short arg1 byte arg2 byte offset | [details] | ||
86/8e | R | Start task | void | [details] | ||
87/8f | R | Delete all subroutines | void | [details] | ||
90/xx | C | Clear message | void | [details] | ||
91/99 | P | C | Set program number | byte program | [details] | |
92/9a |   | R | Delete firmware | void | [details] | |
92/xx | C | Decrement loop counter far | short offset | [details] | ||
93/9b | R | Sign variable | void | [details] | ||
94/9c | P | C | Or variable | byte index byte source byte argument | [details] | |
95/9d | R | Datalog next | byte errorcode | [details] | ||
95/xx | C | Test and branch far | byte opsrc1 byte src2 short arg1 byte arg2 short offset | [details] | ||
96/9e | R | Delete task | void | [details] | ||
97/9f | R | Power off | void | [details] | ||
a1/a9 | P | C | Clear timer | byte timer | [details] | |
a3/ab | R | Multiply variable | void | [details] | ||
a4/ac | P | Upload datalog | short first short count | [details] | ||
a5/ad | P | Unlock firmware | byte key[5] | [details] | ||
a5/ad | R | Set datalog size | byte errorcode | [details] | ||
a6/ae | R | Play sound | void | [details] | ||
a7/af | R | Stop all tasks | void | [details] | ||
b1/b9 | P | C | Set power down delay | byte minutes | [details] | |
b2/ba | R | Transfer data | byte errorcode | [details] | ||
b2/xx | C | Send message | byte source byte argument | [details] | ||
b3/bb | R | Divide variable | void | [details] | ||
b5/bd | R | Set sensor mode | void | [details] | ||
b7/bf | R | Delete all tasks | void | [details] | ||
c1/c9 | P | C | Delete subroutine | byte subroutine | [details] | |
c2/ca | R | Start subroutine download | byte errorcode | [details] | ||
c3/cb | R | Subtract from variable | void | [details] | ||
c4/cc | R | Set display | void | [details] | ||
c5/cd | R | Set sensor type | void | [details] | ||
c6/ce | R | Set transmitter range | void | [details] | ||
c7/cf | R | Get battery power | short millivolts | [details] | ||
d1/d9 | P | C | Clear sensor value | byte sensor | [details] | |
d2/da | R | Start task download | byte errorcode | [details] | ||
d3/db | R | Add to variable | void | [details] | ||
d4/dc | R | Play tone | void | [details] | ||
d5/dd | R | Set time | void | [details] | ||
d6/de | R | Set motor on/off | void | [details] | ||
d7/df | R | Get memory map | short map[94] | [details] | ||
e1/e9 | P | C | Set motor direction | byte code | [details] | |
e2/ea | R | Get versions | short rom[2] short firmware[2] | [details] | ||
e3/eb | R | Set variable | void | [details] | ||
e4/ec | R | Set motor power | void | [details] | ||
e5/ed | R | Get value | short value | [details] | ||
e7/ef | R | Alive | void | [details] | ||
f7/xx | P | C | Set message | byte message | [details] |
|
A complete image of the ROM on board the microcontroller was obtained on Oct 1 1998. This section contains my notes on the ROM image, as of Apr 25 1999.
For now, you can find instructions for obtaining a ROM image here.
On Oct 29 1998 I released some original replacement firmware. If you're interested in this, the code is called first.s, and the S-record data is called first.srec. The firmware does nothing in particular except respond to the power key and light up the display. After loading first.srec (either by using the OCX or by doing the rename thing), you'll need to turn on your RCX with the batteries removed before restoring the original firmware. Use first.srec at your own risk.
On Dec 15 1998 I started working on a separate ROM reference document and a very small ROM interface library that allows you to easily create programs that call ROM functions from C compiled using GCC. The interface library was necessary to test the reference document; however, I expect that it will make an excellent base point for building up source for a non-Lego version of the 0309 firmware, should anyone want to try that.
High level notes As expected, the ROM contains low-level routines for driving the RCX It controls the on/off/stall signals sent to the motor drivers It manages pulse width modulation for the motors It manages the A/D conversion for the inputs It manages talking to the serial port It manages the speaker It checks messages at a low level Checks opcodes to make sure they have the right number of bytes Checks checksum too Handles opcode 45 specially ROM calls first address of firmware, firmware never returns Once firmware is started, it calls ROM to do things The init_timer function at 3b9a sets up an OCIA handler OCIA handler called every 1/1000th sec This is the main asynchronous ROM routine ROM communicates with firmware using two methods Two data structures passed to init_timer are updated by ROM ROM functions communicate data between ROM and firmware Data at pointers passed to init_serial also updated The ROM can be completely overridden, if you like Just don't call init_timer and the H8 is all yours You will probably still want to use some H8 routines, however The LCD routines especially The init routine is at 03ae Clears some memory, specifically ee5e-f000 Copies default interrupt handler 046a into RAM interrupt vector table Calls main at 0580 The main routine is at 0580 If main ever returns, ROM loops forever ROM interrupt handlers call addresses that are stored in RAM This allows all interrupt handlers to be overridden Prudence on LEGO's part, perhaps But it also allows for some serious hacking (!) By overriding a handlers you can drive the H8 at a very low level This is probably more important than you think it is The default interrupt handler is 046a Default handler does nothing but return All handlers are initialized to this on reset Real interrupt handlers are set after reset IRQ0 set to 1ab8 at 1ac0 IRQ1 set to 294a at 2968 TEI set to 2a84 at 31d4 TXI set to 2a9c at 31dc RXI set to 2c10 at 31e4 ERI set to 30a4 at 31ec OCIA set to 36ba at 3c1c A/D set to 3b74 at 3c24 And that's all of them as far as the ROM is concerned Vector at fd90 is 03ae (init) if firmware loaded, 046a (default) otherwise Other notes Paul Haas was right about messages being grouped and parsed by lower 3 bits Tramm Nelson used to be reverse engineering the ROM from the firmware side He apparently also had gcc working, at least somewhat He also mentioned something about a web page, but gave no URL Matt Cross has also been doing interesting work with the firmware He mentioned switch on opcode in firmware Paul Haas had a few interesting comments what Matt had to say Other people offered advice on how to get a few tools set up Kenneth Dyke was the first person to describe how to compile gnu binutils Craig Trader was the first to describe how to use the disassembler I figured out how to use the assembler on my own Then I disassembled my code and edited the output bytes in emacs (!) Thanks to Matt Cross and Mark Salter I have gotten binutils to work H8300 binutils for IRIX are busted, I now run binutils under Solaris Technique is to assemble a .o, then link, as described by Mark Salter But Matt adds that you must convert branch target addresses to labels This fixes a sign extension problem with branch target addresses Allen Martin, Jon Andersson, and Bob Wind have all posted interesting info Allen came up with a method similar to Matt's for recompiling firmware He added the idea of testing reassembly by inserting nops Jon posted some rough notes from looking at the ROM disassembly Bob Wind posted a description of cc00 firmware data structure I came up with a similar list independently, which is included below Matt Cross found out some interesting things about the 070c ROM routine Matt explained it is a state machine My conclusion is that it drives the RCX when firmware is not loaded Markus Noga has been doing a lot of interesting work with GCC He is also looking into interesting ways to bypass/rewrite ROM routines A bit lower level than I imagined going An interesting way to extend things I know I'm forgetting somebody here Vadim mentioned I had used 0x80 instead of 0x08 Fixed this Also fixed mention of lsb when I meant lower 3 bits |
Notes on addresses: ROM vectors/data: 0000 - short array [37], interrupt vectors 004a - short array [8], math function vectors 005a - short array [83], unused 0100 - short array [24], default RAM interrupt vectors, copied to @fd90[24] For the list of functions below, be sure to also check the lists at the bottom. The two lists are no longer in sync. ROM code: 0130 - @@74 handler, r6 = r6 * r5 (16b multiply) 014a - @@76 handler, r6 = r6 % r5 (16b modulo, unsigned) 0156 - @@82 handler, r6 = r6 / r5 (16b divide, signed) 0188 - @@80 handler, r6 = r6 % r5 (16b modulo, signed) 01be - @@78 handler, r6 = r6 / r5 (16b divide, unsigned) 01fe - @@84 handler, r5r6 = r5r6 * r3r4 (32b multiply) 026e - @@88 routine, r5r6 = r5r6 / r3r4 (32b divide, signed) 0306 - @@86 handler, r5r6 = r5r6 / r3r4 (32b divide, unsigned) 03ae - init - see notes, below 03ca - init memory - clear [ee53,f000), copy RAM interrupt vectors into place 042a - copy memory - copy [r0,r1) to [r2,r2+r1-r0) 0436 - clear memory - clear [r0,r1) 0442 - init control registers - see notes, below 046a - default interrupt handler - does nothing but return 046c - NMI dispatch - all of these dispatches jsr to an address stored in RAM 0478 - IRQ0 dispatch 0484 - IRQ1 dispatch 0490 - IRQ2 dispatch 049c - IC1A dispatch 04a8 - IC1B dispatch 04b4 - IC1C dispatch 04c0 - IC1D dispatch 04cc - OC1A dispatch 04d8 - OC1B dispatch 04e4 - FOV1 dispatch 04f0 - CMI0A dispatch 04fc - CMI0B dispatch 0508 - OVI0 dispatch 0514 - CMI1A dispatch 0520 - CMI1B dispatch 052c - OVI1 dispatch 0538 - ERI dispatch 0544 - RXI dispatch 0550 - TXI dispatch 055c - TEI dispatch 0568 - A/D dispatch 0574 - WOVF dispatch 0580 - void rom_main (void) 0688 - void rom_init_handlers (r6=dataptr) 070c - void rom_update (r6=dataptr) 0d18 - void rom_shutdown_handlers (r6=unuseddataptr) 0d3c - void rom_power_off (void) 0d44 - void rom_init_program (r6=unuseddataptr) 0d8c<- void rom_program_update? (r6=dataptr) 1446.- void rom_program_stop? (r6=unuseddataptr) 148a - void do_nothing (void) 1498 = void init_sensors (void) [@827e, in first handler init] 14c0 = void read_sensor (r6=1000+sensorindex, sp0=sensorstruct *type) [@831a] 1946 = void set_sensor_active (r6=1000+sensorindex) [@82be] 19c4 = void set_sensor_passive (r6=1000+sensorindex) [@82cc] 1a1e - void do_nothing (void), never called 1a22 = void shutdown_sensors (void) [@83ba] 1a4a = void do_nothing [init_motors] (void) [@8430] 1a4e = void control_motor (r6=2000+motorindex, sp0=mode, sp1=power) 1ab0 - void do_nothing (void), never called 1ab4 = void do_nothing [shutdown_motors] (void) [@85e0] 1ab8 - IRQ0 handler - does nothing except rts 1aba = void init_buttons_and_lcd (void) [@8612] 1b32 = void play_view_button_sound (r6=301e) [@8698] 1b62 = void set_lcd_segment (short code) 1e4a = void clear_lcd_segment (short code) 1fb6 = void read_buttons (short code=0x3000, short *ptr) 1ff2>= void set_lcd_number (short code, short value, short pointcode) 27ac = void clear_display (void) 27c8 = void refresh_display (void) 27f0 - void do_nothing (void) 27f4 = void shutdown_buttons_and_lcd (void) [@8d00] 283c - void write_lcd_outputs (r6=short *lcdaddr) 294a - IRQ1 handler 2964 = void init_power (void) [for fourth handler] 299a = void play_system_sound (short code, short sound) 29f2 = void get_power_status (short code, short *ptr) 29f2 = void get_on_off_key_state (short code=0x4000, short *ptr) 29f2 = void get_battery_voltage (short code=0x4001, short *ptr) 2a32 - void set_on_off_key_output_low (short code=0x4002), never called 2a48 - void set_on_off_key_output_high (short code=0x4002), never called 2a5e - void do_nothing (void) returns 0, never called 2a62 = void shutdown_power (void), turns power off 2a84 - TEI handler 2a9c - TXI handler 2c10 - RXI handler 30a4 - ERI handler 30d0 = void init_serial (r6=cc00+4, sp0=cc00+6, sp1=1, sp2=1) [@8f62] 3250 = void set_range_long (short code=0x1770) 3266 = void set_range_short (short code=0x1770) 327c = play_sound_or_set_data_pointer (short code, short param0, short param1) 339a = void reset_internal_minute_timer (r6=0x1774) // code is ignored? 33b0 = void receive_data (void *data, byte maxlen, byte *length) [@92b2] 3426 = void check_for_data (byte *valid, byte **nextbyte) 343e = byte send_data (short code, byte opcode, byte *data, short len) 3636 = void shutdown_serial (void) 3692 = void init_port_6_bit_3 (void), nobody knows what port 6 bit 3 does 36a6 = void do_nothing (void) 36aa = void shutdown_port_6_bit_3 (void), nobody knows what port 6 bit 3 does 36ba - OCIA handler 3b74 - A/D handler 3b9a = void init_timer (r6=timerdataptr (40b), sp0=dispatchdataptr (6b)) 3ccc = void get_sound_playing_flag (short code=0x700c, byte *ptr) ? 3ce6 - void control_motor_2 (r6=code=7001+motorindex, sp0=motorcode) [see 1a4e] 3de0 = void control_output (short code, short sp0, short sp1, short sp2) 3e9e - void clear_sensor_and_timer_data (short code, byte param) 3ed0 - void do_nothing (void) [never called] 3ed4 = void shutdown_timer (void) ROM data: 3f12 - byte array [26], "Do you byte, when I knock?" 3f2c - byte array [25], "Just a bit off the block!" 3f45 - byte, unused, 0 3f46 - byte array [8], sound 0 data // sound data has 3 sections 3f4e - byte array [14], sound 1 data // no indication of sections here though 3f5c - byte array [32], sound 2 data 3f7c - byte array [32], sound 3 data 3f9c - byte array [6], sound 4, 6 data 3fa2 - byte array [16], sound 5 data 3fc2 - byte array [8], motor pwm waveforms 3fca - byte array [54], unused, all ff The codes passed in r6 to some ROM routines seem to be organized Why are they even there? Maybe as a sanity check? Maybe the numbers relate to the function or handler? High nibbles are category/handler id Low nibbles are function index This is looking to be more and more likely C prototypes are used for functions whose interface I understand completely Parameters for these are as follows First parameter in r6 Second parameter at offset sp+0 before call Third parameter at offset sp+2 before call etc. Not sure about return values Some functions return 0 in r6, and it's not clear if they're void or not For 14c0, sensorstruct is defined in 828c function description, below Firmware code: Each handler has three functions - an init, a run, and a stop function Main loop state stored in r4l 0=init, 1=stop, 3=run, 4=sleep, 1f=battery low The six handlers are: 1 sensors 2 motors 3 buttons and display 4 power and on/off button 5 interpreter 6 turns on/off bit 3 of p6ddr at init/stop 8000 - firmware init and main loop 823e - first handler init (sensors) 828c - first handler main (sensors) 83b6 - first handler stop (sensors) 83c6 - second handler init (motors) 8440 - second handler main (motors) 85dc - second handler stop (motors) 85ec - third handler init (buttons/display) 863c - third handler main (buttons/display) 8cf4 - third handler stop (buttons/display) 8d0c - fourth handler init (power/on-off) 8d74 - fourth handler main (power/on-off) 8f2a - fourth handler stop (power/on-off) 8f3a - fifth handler init (interpreter) 90d2 - fifth handler main (interpreter) bc2c - fifth handler stop (interpreter) bc76 - modify memory map (r6=op,sp0=index,sp1=len,sp2=ptr), r6=retval (0=fail) bdc0 - sixth handler init (port 6 bit 3) (what is this for?) bdde - sixth handler main (port 6 bit 3) (what is this for?) be00 - sixth handler stop (port 6 bit 3) (what is this for?) Firmware data - the struct (or global memory area) starts at cc00: cc00 - firmware data, (r+00 indicates offset from register r = cc00) r+00 - byte, dispatch state, first handler, sensors? r+01 - byte, dispatch state, second handler, motors r+02 - byte, dispatch state, third handler, display? buttons? r+03 - byte, dispatch state, fourth handler, power r+04 - byte, dispatch state, fifth handler, interpreter r+05 - byte, dispatch state, sixth handler r+06 R+00 - word, serial receive reset counter, also start of ROM data struct r+08 R+02 - word array[4], timer value, in 1/10ths sec r+10 R+0a - word, minutes on clock/watch r+12 R+0c - word, minutes to power off r+14 R+0e - word array[10], per task wake up delay, in 1/100ths sec r+28 R+22 - word array[3], motor handler wake up counters, in ms r+2e R+28 - 3B?, unused? r+31 - byte, program changed flag r+32 - byte, view button down flag? r+33 - byte, power on flag, firmware turns RCX off if this goes to zero r+34 - byte, can run flag (there is code to run) r+35 - byte, run button state (0=stopped, 1=running) r+36 - byte, ready to sleep flag (1=go to sleep) r+37 - byte, firmware delete flag, 0=starting, 1=delete, 2=running r+38 - byte array[3], sensor type r+3b - byte array[3], sensor mode r+3e - word array[3], sensor raw values r+44 - word array[3], sensor values r+4a - byte array[3], boolean sensor values r+4d - byte array[3], real motor state (0x80=on,0x40=off,0x08=fwd,0x07=power) r+50 - byte array[3], temp motor state (r+4d bits + 0x30=activate) r+53 - byte array[3], motor state (only for returning by query?) r+56 - byte, transmitter in use flag (0=not in use, 1=in use) - affects display r+57 - byte, transmitter range (0=short, 1=long) r+58 - word, data transfer address r+5a - word, battery power, in millivolts, initially set to 9V at 8d22 r+5c - byte, current display (0=watch,1-3=sensors,4-6=motors) r+5d - byte, displayed program number r+5e - word, power down delay r+60 - byte, sensor ready flag (0 if sensor mode/type changed, 1 when ready) r+61 - byte, amount of datalog filled, in fourths, 4=full? or does 5=full? r+62 - byte, temp motor counter (units unknown) r+63 - byte, show upload status bar r+64 - byte, battery low, normally 0, 1 if battery power <= 189c (6300 mV) cc65 - byte, unused padding? cc66 - word, handler pointer 1, set to 828c by 8000 cc68 - word, handler pointer 2, set to 8440 by 8000 cc6a - word, handler pointer 3, set to 863c by 8000 cc6c - word, handler pointer 4, set to 8d74 by 8000 cc6e - word, handler pointer 5, set to 90d2 by 8000 cc70 - word, handler pointer 6, set to bdde by 8000 cc72 - byte, sensor ready again counter cc73 - byte?, padding? cc74 - byte array[3], motor direction flags (0x08=fwd) cc77 - byte array[3], motors stopped flag (1=stopped/floating,0=running) cc7a - byte, button down state (0x1=run 0x2=view 0x4=prgm) cc7b - byte, last button state (0x1=run 0x2=view 0x4=prgm) cc7c - word, walking figure state cc7e - byte, ? set at 8a8e cc7f - byte, time decimal point blink state? cc80 - byte, third handler every-other flag cc81 - byte, datalog blink state cc82 - byte, upload status bar count cc83 - byte, unused padding? cc84 - word, last data transfer address (used for blinking display) cc86 - word?, unused padding? cc88 - word, sum of raw battery voltage samples (sum 0x20 samples for average) cc8a - byte, count of raw battery voltage samples cc8b - byte, on/off button state machine, a=up, b=down, c=up2, d=down2 cc8c - byte, on/off button debounce counter cc8d - byte, unused padding? cc8e - byte array[10], program 0 in subroutine flag, bit index is sub cc98 - byte array[10], program 1 in subroutine flag, bit index is sub cca2 - byte array[10], program 2 in subroutine flag, bit index is sub ccac - byte array[10], program 3 in subroutine flag, bit index is sub ccb6 - byte array[10], program 4 in subroutine flag, bit index is sub ccc0 - byte, current task ccc1 - byte, last received opcode from serial connection ccc2 - byte, transfer in progress ccc3 - byte, running flag (run button says to run code) ccc4 - word, program number ccc6 - word array[32], variables 0-31 cd06 - byte, last message received cd07 - byte, unused padding? cd08 - word, random number state cd0a - byte, reply valid (0=not valid, 1=send) cd0b - byte, reply length cd0c - byte, reply opcode (cd0c is really byte array[16]) cd0d - byte array[15], remainder of reply cd1c - byte, transfer data next expected index cd1d - byte, transfer data status, 0x80=set if task, 0x7f=task/sub number cd1e - word, last d2 data cd20 - byte, send message after parsing flag (0=no send, 5=send) cd21 - byte, unused padding? cd22 - word array[8], program 0 subroutine start addrs (raw memory addr) cd32 - word array[8], program 1 subroutine start addrs cd42 - word array[8], program 2 subroutine start addrs cd52 - word array[8], program 3 subroutine start addrs cd62 - word array[8], program 4 subroutine start addrs cd72 - word array[10], program 0 task start addrs (raw memory addr) cd86 - word array[10], program 1 task start addrs (initially cee2) cd9a - word array[10], program 2 task start addrs cdae - word array[10], program 3 task start addrs cdc2 - word array[10], program 4 task start addrs cdd6 - word, datalog start cdd8 - word, datalog next cdda - word, first free address cddc - word, last valid address cdde - word array[10], current program program counters (raw memory addr) cdf2 - word array[10], program 0 subroutine return addrs (raw memory addr) ce06 - word array[10], program 1 subroutine return addrs (raw memory addr) ce1a - word array[10], program 2 subroutine return addrs (raw memory addr) ce2e - word array[10], program 3 subroutine return addrs (raw memory addr) ce42 - word array[10], program 4 subroutine return addrs (raw memory addr) ce56 - byte array[10], program 0 task status (0=invalid,1=valid,2=running) ce60 - byte array[10], program 1 task status (3=waiting) ce6a - byte array[10], program 2 task status ce74 - byte array[10], program 3 task status ce7e - byte array[10], program 4 task status ce88 - byte array[10][4], task loop counters, 4 per task ceb0 - byte array[10], loop counter depth ceba - byte array[6144], program data e6ba - blank space? ROM data: (okay to use when firmware loaded) before ef00: zero when firmware loaded, because firmware zeros cc00-efff ee5e - word, rom main loop state, 0=restart, 8=off, d=run, 13=run firmware ee60 - word, data transfer address ee62 - word, pointer to firmware start ee64 - dispatch data, plus padding, ee64 is sensor and serial run flag ee74 R+00 - word, serial receive reset counter, also start of ROM timer data? ee76 R+02 - word array[4], timer value ee7e R+0a - word, minutes on clock/watch ee80 R+0c - word, minutes to power off ee82 R+0e - word array[10], per task wakeup delay, in 1/100ths sec ee96 R+22 - word array[3], motor counters? ee9c - 24B, unused padding so ROM timer data is 64B? eeb4 - byte, rom ready to sleep eeb5 - byte, unused padding? eeb6 - byte array[64], send packet data eef6 - byte array[10], receive packet data ef06 - byte, rom update funtion state ef07 - byte, rom on/off key state ef08 - word, last data transfer address ef0a - word, transfer data index ef0c - byte, last received opcode ef0d - byte, send packet length ef0e - byte, on/off key debounce ef0f - byte, unused? ef10 - byte, set to 0 in 0d44, also start of rom program data ef11 - byte, ? ef12 - byte, set to 0 in 0d44 ef13 - byte, set to 0 in 0d44 ef14 - 6B, ? ef1a - byte, set to a in 0d44 ef1b - byte, set to 80 in 0d44 ef1c - 6B, ? ef22 - byte, set to 0 in 0d44 ef23 - byte, set to 0 in 0d44 ef24 - 6B, ? ef2a - byte, set to 0 in 0d44 and 1446 ef2b - byte, set to 0 in 1446 ef2c - byte, set to 0 in 0d44 ef2d - byte, set to 0 in 0d44 and 1446 ef2e - word, set to ffff at 0d3c ef30 - byte, ? ef32 - byte array [3], angle previous state ef35 - byte array [3], edge/pulse inited flag ef38 - byte array [3], edge/pulse debounce counter ef3b - byte array [3], pulse edge counter, also end of rom program data? More ROM data: (volatile when ROM functions in use) ef3e - byte array[15], set to 0 at 1b1c // output for display?, contains ef43 ef43 - byte array[10], output to display in 1b62, part of array[15] at ef3e) ef4e - byte, download/upload counter for display in 1b62 (0..5) ef4f - byte, datalog count for display in 1b62 (0..4) ef50 - byte, use packet header flag ef51 - byte, use complements flag ef52 - byte array[64], outgoing packet data (for short packets) ef92 - byte, outgoing packet data index ef93 - byte, tranmitting flag, 4f=not tranmitting, 13=transmitting ef94 - word, outgoing packet type (1775=short, 1776=long) ef96 - word, outgoing packet length remaining ef98 - word, outgoing packet data pointer (for long packets) ef9a - byte, outgoing packet send state ef9b - byte, outgoing packet opcode and checksum (for long packets) ef9c - byte, outgoing packet data complement state ef9d - byte array[16], receive packet data efad - byte, unused? efae - word, incoming data pointer efb0 - byte, receive data index efb1 - byte, unpack data index efb2 - byte, receive state, 2=ready 3=regular 4=done 5=opfivetail 6=opfivehead efb3 - byte, receive expect complement efb4 - word, data pointer, =cc06 for firmware efb6 - byte, valid incoming data flag efb8 - word, data pointer, serial handler run flag, =ee64 ROM, =cc04 firmware efba - byte, last received byte (ff if last was complement or invalid) efbc - word, transfer data sequence number, set to ffff for new transfers efbe - byte, receive checksum efbf - byte, receive data checksum (for opcode 45) efc0 - byte, receive length remaining (excludes complements) efc1 - byte, current receive byte efc2 - word, receive data length remaining (for opcode 45) efc4 - word, saved incoming data pointer (for resent transfer data packets) efc6 - word, data pointer, set by 3b9a, =ee74 for ROM, =cc06 for firmware efc8 - word, data pointer, dispatch array, =ee64 for ROM, =cc00 for firmware efca - byte, motor output bits, unmodulated efcb - byte, motor 0 pwm waveform, rotates right efcc - byte, motor 1 pwm waveform, rotates right efcd - byte, motor 2 pwm waveform, rotates right efce - byte, motor output bits, modulated by motor waveforms efcf - byte, millisecond counter 0, counts to 30 ms, divides timer to 1/100 sec efd0 - byte, millisecond counter 1, divides timer to 1/10 sec efd1 - byte, unused? efd2 - word, millisecond counter 2, divides timer to 60 sec efd4 - byte, millisecond counter 3, divides timer to 130 ms efd5 - byte, sensor output on port 6, used after a/d conversion efd6 - byte, set to 1 by 3b9a, tail index for sound queues efd7 - byte, set to 1 by 3b9a, head index for sound queues efd8 - byte array[10], sound data period byte queue 0=pause,1=sound,>1=tone efe2 - byte array[10], sound data count byte queue efec - byte array[10], sound data control byte queue eff6 - byte, sound duration remaining counter, in 1/100 sec eff8 - word, pointer to sound data, pitch period byte effa - word, pointer to sound data, duration byte, in 1/100 sec effc - word, pointer to sound data, control byte (related to octave) effe - byte, sound data pointers in use flag (see eff8, effa, effc) efff - byte, sound playing flag f000 - byte, motor control word, ROM sets to @efca or @efce Note: Motors are memory mapped to the ranges [f000,fb7f] and [ff80,ff87]. Writes to addresses in these ranges affect the motors. There is also off-chip memory backing these addresses, so it's possible to use this memory with the caveat that writes will change the motor state. One possible use is to store code in this memory, since that code will typically be read-only once written. Note that the range [ff80,ff87] is accessible using shorter instructions that use 8-bit absolute addressing. ROM data in on-chip RAM: fd80 - byte, same as ffb0 (p1ddr), set to ff by 3b9a fd81 - byte, same as ffb1 (p2ddr), set to ff by 3b9a fd82 - byte, same as ffb4 (p3ddr), but unused? set to 0 by 3b9a fd83 - byte, same as ffb5 (p4ddr) fd84 - byte, same as ffb8 (p5ddr), set to 0 by 3b9a fd85 - byte, same as ffb9 (p6ddr), set=0 by 3b9a, 0x08 set/clr by 6th handlers fd86 - byte, same as ffbe (p7pin) fd87 - byte, unused? fd88 - word, firmware checksum, set to 0 after firmware deleted fd8a - word, firmware checksum complement, set to 0 after firmware deleted fd8c - word, firmware entry point, himem version, maybe just new version? fd8e - (2B?), unused? fd90 - word, like a reset vector fd92 - word, NMI interrupt vector fd94 - word, IRQ0 interrupt vector fd96 - word, IRQ1 interrupt vector fd98 - word, IRQ2 interrupt vector fd9a - word, ICIA interrupt vector fd9c - word, ICIB interrupt vector fd9e - word, ICIC interrupt vector fda0 - word, ICID interrupt vector fda2 - word, OCIA interrupt vector, set to 36ba by 3b9a fda4 - word, OCIB interrupt vector fda6 - word, FOVI interrupt vector fda8 - word, CMI0A interrupt vector fdaa - word, CMI0B interrupt vector fdac - word, OVI0 interrupt vector fdae - word, CMI1A interrupt vector fdb0 - word, CMI1B interrupt vector fdb2 - word, OVI1 interrupt vector fdb4 - word, ERI interrupt vector, set to 30a3 at 31ec fdb6 - word, RXI interrupt vector, set to 2c10 at 31e4 fdb8 - word, TXI interrupt vector, set to 2aac at 31dc fdba - word, TEI interrupt vector, set to 2a84 at 31d4 fdbc - word, A/D interrupt vector, set to 3b74 by 3b9a fdbe - word, WOVF interrupt vector Control registers: ff90 - byte, timer interrupt enable register ff91 - byte, timer control/status register, free-running timer ff92 - word, free-running counter ff94 - word, output compare register A ff96 - byte, timer control register, free-running timer ff97 - byte, timer output compare control register ffb0 - byte, port 1 data direction register, set to ff by 3b9a ffb1 - byte, port 2 data direction register, set to ff by 3b9a ffb5 - byte, port 4 data direction register ffb7 - byte, port 4 data register, 0x01 bit set = xmit power ffb8 - byte, port 5 data direction register ffb9 - byte, port 6 data direction register, 0x40 bit toggled by 6th handlers ffba - byte, port 5 data register ffbb - byte, port 6 data register, 0x40 bit set to output by 6th r4 handler ffbe - byte, port 7 input register ffc2 - byte, wait state control register ffc3 - byte, serial/timer control register, bit 0 set to 1 by 3b9a ffc4 - byte, system control ffc6 - byte, IRQ sense control register ffc7 - byte, IRQ enable register ffc8 - byte, timer 0 control register, set to 0b by 3b9a ffc9 - byte, timer 0 control/status register, set to 03 by 3b9a ffcc - byte, timer 0 counter ffd0 - byte, timer 1 control register ffd1 - byte, timer 1 control/status register ffd2 - byte, time constant register A ffd8 - byte, serial mode register, set to 30 at 311e ffd9 - byte, serial bit rate register ffda - byte, serial control register ffdb - byte, serial transmit data register ffdc - byte, serial status register ffdd - byte, serial receive data register ffe6 - word, a/d register d ffe8 - byte, a/d control/status register ffe9 - byte, a/d control register ... all of these are defined in the H8 specs Interesting firmware notes: 094d - ROM version number - part of a mov.b #0x3,r6l instruction in ROM! 9c90 - 85/95 handler - a hack, has 5 bytes, offset read from program memory! a064 - what's up with 3rd byte of start task download being stored into cc8e? Pages of interest: cc00 - volatile data - firmware/rom data cd00 - data ce00 - data ee00 - nothing ef00 - volatile data - rom data f000 - data f100 - data ... fe00 - data ff00 - volatile data - stack, control registers Stack pointer is set here: 03ae: set to ff00 (in init) 03b6: set to ff7e |
Notes on routines: All ROM routines with "short code=0xnnnn" do nothing if code is not 0xnnnn 03ae - init sets stack to ff00 calls 0442 (init control registers) sets stack to ff7e calls 03ca (init memory) calls 0580 (main) 03ca - init memory clear [ee53,f000) set RAM interrupt vectors to defaults by copying [100,130) to [fd90,fdc0) 042a - copy memory (r0=start, r1=end, r2=dest) copy [start,end) to [dest,dest+start-end) 0436 - clear memory (r0=start, r1=end) clear [start,end) 0442 - init control registers ffb8: set 0x4 bit - p5ddr - set 0x4 pin to output ffba: clr 0x4 bit - p5dr - output 0x4 ffb0: set to ff - p1ddr - set all pins to output ffb1: set to ff - p2ddr - set all pins to output fd80: set to ff (fd80 is copy of ffb0) fd81: set to ff (fd81 is copy of ffb1) 8000: set to 102 return 1 046a - default handler does nothing but return 0580 - rom_main (void) make room for 1 word on stack byte flag (sp+0) if (timer 0 counter (ffcc) != 0) rom main loop state (ee5e) = 0 else rom main loop state (ee5e) = 8 rom ready to sleep (eeb4) = 1 minutes to power off (ee80) = 6 call ROM 3b9a (r6=ee74, sp0=ee64) [init timer] call ROM 0688 (r6=ee5e) [rom init handlers] call ROM 0d44 (r6=ee5e) [rom init program data] while (1) switch (rom main loop state (ee5e)) case 13: // STATE 13: run firmware call ROM 148a (r6=ee5e) [do nothing] if (task 0 wakeup (ee82[0]) < 1) // check run firmware timer call ROM 0d18 (r6=ee5e) [rom shutdown handlers] call ROM 3ed4 (void) [shutdown timer] call firmware entry point (ee62) case 8: // STATE 8: wait for power off call ROM 148a (r6=ee5e) [do nothing] call ROM 070c (r6=ee5e) [update] call ROM 3ccc (r6=700c, sp0=@flag) [get sound playing flag] if (!flag && rom ready to sleep (eeb4) == 1) call ROM 0d18 (r6=ee5e) [rom shutdown handlers] call ROM 3ed4 (void) [shutdown timer] call ROM 0d3c (void) [rom power off] rom main loop state (ee5e) = 0 case 0: // STATE 0: restart minutes to power off (ee80) = f call ROM 3b9a (r6=ee74, sp0=ee64) [init timer] call ROM 0688 (r6=ee5e) [rom init handlers] call ROM 0d44 (r6=ee5e) [rom init program data] rom main loop state (ee5e) = d default: // STATE d: run call ROM 070c (r6=ee5e) [update] if (@ee64 & 0x80) // check if sensor/serial handler run flag set call ROM 0d8c (r6=ee5e) [program update] 0688 - rom_init_handlers (r6=dataptr) call ROM 1498 (void) [init sensors] call ROM 1a4a (void) [init motors] call ROM 1aba (void) [init buttons] call ROM 2964 (void) [init power] // note duplicate use of ee64 sensor/serial run flag call ROM 30d0 (r6=ee64, sp0=ee74, 1, 1) [init serial] call ROM 3692 (void) [init port 6 bit 3] task 0 wakeup (ee82[0]) = 3c // run firmware holdoff timer for (index = 0; index < 40; index++) send packet data[index] = 0 for (index = 0; index < 10; index++) receive packet data[index] (eef6[index]) = 0 call ROM 27ac (void) [clear display] call ROM 27c8 (void) [refresh display] rom update function state (ef06) = 0 // start rom on/off key state (ef07) = 32 070c - rom_update (r6=dataptr) make space for 10 bytes on stack word buttonstate (sp+0) word param (sp+2) word index (sp+4) word status (sp+6) char validmsg (sp+8) char length (sp+9) call ROM 3426 (r6=&validmsg (sp+8), sp0=&ee60) switch (rom update function state (ef06)) case 0: // STATE 0: start for (addr = 8000; addr < f000; addr += 2) save = word at addr; // save value at addr value = a55a word at addr = value // store a55a to addr if (value != a55a) // if value != a55a, never true addr = fffd // addr = fffd, break else // else word at addr = save // store original value to addr if (addr == ffff) // never true call ROM 1ff2 (r6=3001, sp0=ffff, sp1=3002) [display -1] else call ROM 1b62 (r6=3006) [display standing figure] rom update function state (ef06) = 7 // beep twice case 7: // STATE 7: beep twice rom update function state (ef06) = 2 // wait for message call ROM 299a (r6=4004, sp0=1) [beep twice] case 1: // STATE 1: check firmware short offset = 0 byte validstr = 0 short sum = 0 for (addr = 8000; addr < cc00; addr++) sum += byte at addr if (byte at addr == 44 (ascii D) && !validstr) validstr = offset = 1 if (offset != 0) if (byte at @3f11[offset] != byte at addr) validstr = 0 if (offset == 1b) offset = 0 if (sum == firmware checksum (fd88) && ~sum == @fd8a && validstr) rom update function state (ef06) = 3 // send unlock ok message else call ROM 299a (r6=4004, sp0=4) [low buzz] rom update function state (ef06) = 2 // wait for message last received opcode (ef0c) = ff case 3: // STATE 3: send unlock ok message if (call ROM 343e (r6=1776, sp0=@eef6, sp1=3f2c, sp2=19) != 4c) task 0 wakeup (ee82[0]) = 1e // run firmware holdoff timer firmware entry point (ee62) = firmware entry point himem (fd8c) rom main loop state (ee5e) = 13 // run firmware rom update function state (ef06) = 8 // no more updates case 2: // STATE 2: wait for message if (validmsg (sp+8)) rom update function state (ef06) = 4 // process message minutes to power off (ee80) = f case 4: // STATE 4: process message call ROM 33b0 (r6=eef6, sp0=10, sp1=&length (sp+9)) if (last received opcode (ef0c) != receive packet data[0] (eef6)) opcode = receive packet data[0] (eef6) param (sp+2) = unaligned little endian short at @eef7 last received opcode (ef0c) = opcode opcode &= f7 switch (opcode) case 10: // alive rom update function state (ef06) = 5 // send reply send packet data[0] (eeb6) = ~last received opcode (ef0c) send packet length (ef0d) = 1 case 15: // get versions if (receive packet data[1..5] (eef7[5]) == {1,3,5,7,b}) rom update function state (ef06) = 5 // send reply send packet data[0] (eeb6) = ~last received opcode (ef0c) send packet data[1..8] (eeb7[8]) = {0,3,0,1,0,0,0,0} send packet length (ef0d) = 9 else rom update function state (ef06) = 2 // wait for message case 65: // delete firmware if (receive packet data[1..5] (eef7[5]) == {1,3,5,7,b}) rom update function state (ef06) = 5 // send reply send packet data[0] (eeb6) = ~last received opcode (ef0c) send packet length (ef0d) = 1 else rom update function state (ef06) = 2 // wait for message case 75: // start firmware download firmware entry point himem (fd8c) = param (sp+2) firmware checksum (fd88) = little endian short at @eef9 firmware checksum complement (fd8a) = ~firmware cksum (fd88) call ROM 327c (r6=1771, sp0=8000, sp1=0) [set data pointer] set [8000,cc00) to zero transfer data index (ef0a) = 1 rom update function state (ef06) = 5 // send reply send packet data[1] (eeb7) = 0 send packet data[0] (eeb6) = ~last received opcode (ef0c) send packet length (ef0d) = 2 case 45: // transfer data index (sp+4) = param (sp+2) if (index (sp+4) != 0) // increment of transfer data index here looks like a bug // if RCX receives message but reply is lost, PC will // not know whether or not to increment its index if (index (sp+4) == transfer data index (ef0a) ++) // is checksum valid? if (recv pkt data[3] (eef9) == recv pkt data[4] (eefa)) send packet data[1] (eeb7) = 0 // okay else send packet data[1] (eeb7) = 3 // block cksum fail rom update function state (ef06) = 5 // send reply send packet data[0] (eeb6) = ~last recd opcode (ef0c) send packet length (ef0d) = 2 else rom update function state (ef06) = 2 // wait for msg else // apparently, if index 0 fails, you cannot retry if (recv pkt data[3] (eef9) == recv pkt data[4] (eefa)) for (addr = 8000, sum = 0; addr < cc00, addr++) sum += byte at addr if (sum == firmware checksum (fd88) && ~sum == @fd8a) send packet data[1] (eeb7) = 0 // okay call ROM 299a (4004, 5) [fast upward tones] else send packet data[1] (eeb7) = 4 // cksum fail else send packet data[1] = 3 // block cksum fail call ROM 327c (1771, 0, 0) // clear incoming data pointer rom update function state (ef06) = 5 // send reply send packet data[0] (eeb6) = ~last recd opcode (ef0c) send packet length (ef0d) = 2 case a5: // unlock firmware if (receive packet data[1..5] (eef7[5]) == {4c,45,47,4f,ae}) rom update function state (ef06) = 1 // check firmware else rom update function state (ef06) = 2 // wait for message default: rom update function state (ef06) = 2 // wait for message else rom update function state (ef06) = 5 // resend reply case 5: // STATE 5: send reply if (call ROM 343e (r6=1775, 0, eeb6, @ef0d) != 4c) rom update function state (ef06) = 2 // wait for message case 6: // STATE 6: send reply with firmware check, never reached if (call ROM 343e (r6=1775, 0, eeb6, @ef0d) != 4c) rom update function state (ef06) = 1 // check firmware call ROM 27c8 (void) [refresh display] if (data transfer address (ee60) != last data transfer address (ef08)) last data transfer address (ef08) = data transfer address (ee60) call ROM 1b62 (r6=301c) [short range light] task 1 wakeup (ee82[1]) = 64 // clear display holdoff timer if (last data transfer address (ef08) >= 8000) status (sp+6) = (last data transfer address (ef08) + 8000) / a call ROM 1ff2 (r6=3001, sp0=status, sp1=3002) [show transfer status] else if (task 1 wakeup (ee82[1]) == 0) // check clear display holdoff timer task 1 wakeup (ee82[1]) = 64 // clear display holdoff timer call ROM 27ac (void) [clear display] call ROM 29f2 (r6=4000, sp0=&buttonstate) [get on/off key state] switch (rom on/off key state (ef07)) case 32: // wait for press if (buttonstate == 0) // on/off pressed on/off key debounce (ef0e) = 0 else if (on/off key debounce (ef0e) ++ > 2) rom on/off key state (ef07) = 33 case 33: // go to sleep if (buttonstate == 0) // on/off pressed call ROM 299a (r6=4003, sp0=0) [blip] rom ready to sleep (eeb4) = 0 rom main loop state (ee5e) = 8 // wait for power off rom on/off key state (ef07) = 34 on/off key debounce (ef0e) = 0 case 34: // wait for release if (buttonstate == 0) // on/off pressed on/off key debounce (ef0e) = 0 else if (on/off key debounce (ef0e) ++ > 14) rom ready to sleep (eeb4) = 1 rom on/off key state (ef07) = 35 case 35: // do nothing if (minutes to power off (ee80) == 0) minutes to power off (ee80) = 100 call ROM 299a (r6=4003, sp0=0) [blip] rom ready to sleep (eeb4) = 1 rom main loop state (ee5e) = 8 // wait for power off 0d18 - rom_shutdown_handlers call ROM 1a22 (void) [shutdown sensors] call ROM 1ab4 (void) [shutdown motors] call ROM 27ac (void) [clear display] call ROM 27c8 (void) [refresh display] call ROM 27f4 (void) [shutdown buttons] call ROM 3636 (void) [shutdown serial] call ROM 36aa (void) [shutdown port 6 bit 3] 0d3c - rom_power_off (void) call ROM 2a62 (void) [shutdown power] 0d44 - rom_init_program_data (r6=unuseddataptr) @ef2e = ffff @ef2c = 0 @ef10 = 0 @ef2b = 0 @ef12 = 0 @ef13 = 0 @ef1a = 3 @ef1b = 80 @ef22 = 0 @ef23 = 0 @ef2a = 0 @ef2d = 0 0d8c - rom_program_update (r6=dataptr) 1446 - rom_program_stop (r6=unuseddataptr) @ef2b = 0 @ef2d = 0 @ef2a = 0 call ROM 1a4e (r6=2000, sp0=3, sp1=7) [motor 0 stop full power] call ROM 1a4e (r6=2002, sp0=3, sp1=7) [motor 2 stop full power] call ROM 19c4 (r6=1002) [set sensor 2 passive] 1498 - init sensor pins (void) set bit 2 of p6ddr (fd85, ffb9) // port 6 bit 2 is output set bit 1 of p6ddr (fd85, ffb9) // port 6 bit 1 is output set bit 0 of p6ddr (fd85, ffb9) // port 6 bit 0 is output // remaining sensor pins are a/d analog inputs 14c0 - void read_sensor (r6=1000+index, sp0=sensorstruct *sensor) // sensorstruct described in 828c, below // always returns 0, probably void // save r0-r6 on stack, add 10 to sp... long offset (sp+0) long scale (sp+4) word slope (sp+8) word delta (sp+a) word temp (sp+c) word degf (sp+e) switch (r6) case 1000: raw = a/d register c (word @ffe4) >> 6; case 1001: raw = a/d register b (word @ffe2) >> 6; case 1002: raw = a/d register a (word @ffe0) >> 6; slope = sensor->mode & 1f if (slope == 0) if (sensor->boolean == 1) if (raw > 232) boolean = 0 else if (raw < 1cc) boolean = 1 else if (raw > 3ff - slope) boolean = 0 else if (raw < slope) boolean = 1 else delta = sensor->raw - raw; if (delta < 0) if (-delta > slope) boolean = 0 else if (delta > slope) boolean = 1 sensor->raw = raw switch (sensor->mode & 0e) case 40: // edge count if (edge/pulse inited flag[index] (byte @ef35[index]) == 0) edge/pulse inited flag[index] (byte @ef35[index]) = 1 sensor->boolean = boolean else if (--edge/pulse debounce count[index] (byte @ef38[index]) == 0) edge/pulse debounce count[index] (byte @ef38[index]) = 1 if (sensor->boolean != boolean) sensor->value++ sensor->boolean = boolean // need 300 ms between transitions?! edge/pulse debounce count[index] (byte @ef38[index]) = 64 case 60: // pulse count if (edge/pulse inited flag[index] (byte @ef35[index]) == 0) edge/pulse inited flag[index] (byte @ef35[index]) = 1 sensor->boolean = boolean else if (--edge/pulse debounce count[index] (byte @ef38[index]) == 0) edge/pulse debounce count[index] (byte @ef38[index]) = 1 if (sensor->boolean != boolean) sensor->boolean = boolean edge/pulse debounce count[index] (byte @ef38[index]) = 64 if (++pulse edge count[index] (byte @ef3b[index]) == 2) pulse edge count[index] (byte @ef3b[index]) = 0 sensor->value++ case 80: // percentage if (raw < 187) // bug? this allows percent=101, should be raw < 189 sensor->value = 64 else sensor->value = (3ff - raw) * 19 / 9c sensor->boolean = boolean case a0: // temperature in degrees C case c0: // temperature in degrees F if (raw >= 3a0 || raw < 122) sensor->value = 7fff else if (raw < 154) scale = fffffefb offset = 00023730 else if (raw < 186) scale = ffffff3e offset = 0001e078 else if (raw < 1de) scale = ffffff70 offset = 0001944c else if (raw < 208) scale = ffffff85 offset = 00016da0 else if (raw < 316) scale = ffffff95 offset = 00014cd0 else if (raw < 376) scale = ffffff7a offset = 0001a068 else scale = ffffff51 offset = 00022d08 temp = (raw * scale + offset) / 100 if (sensor->mode & 0e == 0a) // temperature in degrees C sensor->value = temp else // temperature in degrees F sensor->value = degf = (temp * 12) / 0a + 140 sensor->boolean = boolean case 20: // boolean sensor->value = boolean sensor->boolean = boolean case 00: // raw sensor->value = raw sensor->boolean = boolean case e0: // angle sensor->boolean = boolean if (raw < 1bf) state = 0 else if (raw < 2bf) state = 1 else if (raw < 3bf) state = 2 else state = 3 if (angle previous state[index] (byte @ef32[index]) == 0) if (state == 1) sensor->value++ else if (state == 2) sensor->value-- else if (angle previous state[index] (byte @ef32[index]) == 1) if (state == 3) sensor->value++ else if (state == 0) sensor->value-- else if (angle previous state[index] (byte @ef32[index]) == 2) if (state == 0) sensor->value++ else if (state == 3) sensor->value-- else if (angle previous state[index] (byte @ef32[index]) == 3) if (state == 2) sensor->value++ else if (state == 1) sensor->value-- angle previous state[index] (byte @ef32[index]) = state endswitch 1946 - void set_sensor_active (r6=1000+sensorindex) switch (r6) case 1000: call ROM 3de0 (r6=700a, sp0=4) // set output to sensor case 1001: call ROM 3de0 (r6=700a, sp0=2) // set output to sensor case 1002: call ROM 3de0 (r6=700a, sp0=1) // set output to sensor 19c4 - void set_sensor_passive (r6=1000+sensorindex) switch (r6) case 1000: call ROM 3e9e (r6=700a, sp0=4) // clear output to sensor case 1001: call ROM 3e9e (r6=700a, sp0=2) // clear output to sensor case 1002: call ROM 3e9e (r6=700a, sp0=1) // clear output to sensor endswitch 1a22 - stop sensor pins clear bit 2 of p6ddr (fd85, ffb9) // port 6 bit 2 is input clear bit 1 of p6ddr (fd85, ffb9) // port 6 bit 1 is input clear bit 0 of p6ddr (fd85, ffb9) // port 6 bit 0 is input 1a4a - void do_nothing (void) 1a4e - void control_motor (short code=2000+motorindex, byte mode, byte power) // code: 2000+motorindex // modes: 1=forward, 2=backward, 3=stop, 4=float // power: 0..7 struct motorstruct (sp+0) byte power (sp+0) byte mode (sp+1) endstruct motor.power = power motor.mode = mode if (code == 2000) call ROM 3ce6 (r6=7001, sp0=&motorstruct) else if (code == 2001) call ROM 3ce6 (r6=7002, sp0=&motorstruct) else if (code == 2002) call ROM 3ce6 (r6=7003, sp0=&motorstruct) 1ab0 - void do_nothing (void) 1ab4 - void do_nothing (void) 1ab8 - void irq0_handler (void) returns without doing anything 1aba - initialize buttons and IRQ0 // called in third handler init (buttons/display) set IRQ0 interrupt vector (fd94) to 1ab8 clear bit 7 of p7pin (fd86, ffbe) // is this allowed? port 7 bit 7 is input clear bit 6 of p7pin (fd86, ffbe) // is this allowed? port 7 bit 6 is input clear bit 2 of p4ddr (fd83, ffb5) // port 4 bit 2 is input set bit 0 of IRQ sense control register (ffc6) // IRQ0 is edge sensed set bit 0 of IRQ enable register (ffc7) // IRQ0 is enabled set bit 5 of p6ddr (fd85, ffb9) // port 6 bit 5 is output set bit 6 of p6ddr (fd85, ffb9) // port 6 bit 6 is output set bit 5 of p6dr (ffbb) // port 6 bit 5 is high set bit 6 of p6dr (ffbb) // port 6 bit 6 is high clear @ef3e[15] clear @ef4e clear @ef4f 1b32 - void play_view_button_sound (short code=301e) if (code == 301e) call ROM 3de0 (r6=700b, sp0=1, sp1=0, sp2=7) // play sound 1, sp2 ignored 1b62 - void set_lcd_segment (short code) switch (code) case 3006: @ef43 &= f0 // standing figure @ef43 |= 06 case 3007: @ef43 &= f0 // walking figure @ef43 |= 0b case 3008: @ef49 |= 01 // sensor 0 view selected case 3009: @ef49 |= 02 // sensor 0 active case 300a: @ef48 |= 10 // sensor 1 view selected case 300b: @ef48 |= 01 // sensor 1 active case 300c: @ef47 |= 10 // sensor 2 view selected case 300d: @ef45 |= 10 // sensor 2 active case 300e: @ef4a |= 04 // motor 0 view selected case 300f: @ef46 |= 40 // motor 0 backward arrow case 3010: @ef46 |= 04 // motor 0 forward arrow case 3011: @ef43 |= 40 // motor 1 view selected case 3012: @ef44 |= 04 // motor 1 backward arrow case 3013: @ef47 |= 04 // motor 1 forward arrow case 3014: @ef44 |= 40 // motor 2 view selected case 3015: @ef47 |= 40 // motor 2 backward arrow case 3016: @ef45 |= 40 // motor 2 forward arrow case 3018: switch (@ef4f++) // datalog indicator case 0: @ef45 |= 1 case 1: @ef45 |= 4 case 2: @ef45 |= 8 case 3: @ef45 |= 2 case 4: @ef45 &= f0 @ef4f = 0 endswitch case 3019: switch (@ef4e++) // download in progress case 0: @ef49 &= ~10 // leftmost dot only @ef4b &= ~11 @ef4a &= ~01 @ef4a |= 10 case 1: @ef4a |= 1 // add dots to right case 2: @ef4b |= 10 case 3: @ef4b |= 1 case 4: @ef49 |= 10 case 5: @ef49 &= ~10 // all off @ef4b &= ~11 @ef4a &= ~11 @ef4e = 0 endswitch case 301a: switch (@ef4e++) // upload in progress case 0: @ef49 |= 10 // all on @ef4b |= 11 @ef4a |= 11 case 1: @ef4a &= ~10 // remove dots from left case 2: @ef4a &= ~01 case 3: @ef4b &= ~10 case 4: @ef4b &= ~01 case 5: @ef49 &= ~10 @ef4e = 0 endswitch case 301b: @ef47 |= 1 // battery low indicator case 301c: @ef49 |= 4 // short range light @ef49 &= ~8 case 301d: @ef49 |= c // long range light case 3020: for (i = 0; i < a; i++) @ef43[i] = ff // test all, why < a? endswitch 1e4a - void clear_lcd_segment (short code) switch (code) case 3006: @ef43 &= f9 // standing figure case 3007: @ef43 &= f4 // walking figure case 3008: @ef49 &= ~01 // sensor 0 view selected case 3009: @ef49 &= ~02 // sensor 0 active case 300a: @ef48 &= ~10 // sensor 1 view selected case 300b: @ef48 &= ~01 // sensor 1 active case 300c: @ef47 &= ~10 // sensor 2 view selected case 300d: @ef45 &= ~10 // sensor 2 active case 300e: @ef4a &= ~04 // motor 0 view selected case 300f: @ef46 &= ~40 // motor 0 backward arrow case 3010: @ef46 &= ~04 // motor 0 forward arrow case 3011: @ef43 &= ~40 // motor 1 view selected case 3012: @ef44 &= ~04 // motor 1 backward arrow case 3013: @ef47 &= ~04 // motor 1 forward arrow case 3014: @ef44 &= ~40 // motor 2 view selected case 3015: @ef47 &= ~40 // motor 2 backward arrow case 3016: @ef45 &= ~40 // motor 2 forward arrow case 3018: @ef45 &= f0 // datalog indicator @ef4f = 0 case 3019: case 301a: // data transfer in progress? (dots on top?) @ef49 &= ~10 @ef4b &= ~11 @ef4a &= ~11 @ef4e = 0 case 301b: @ef47 &= ~01 // battery low indicator case 301c: case 301d: // short and long range lights @ef49 &= ~0c endswitch 1fb6 - void read_buttons (short code=0x3000, short *ptr) *ptr = 0 if (bit 6 of p7pin (ffbe) is set) *ptr |= 0x02 if (bit 7 of p7pin (ffbe) is set) *ptr |= 0x04 if (bit 2 of p4dr (ffb7) is set) *ptr |= 0x01 1ff2 - void set_lcd_number (short code, short value, short pointcode) // this is tentative, I have not yet gone through the long code in detail if (code == 3001) set number on display to signed value, no leading zeros if (code == 3017) set program number on display to value, (use pointcode=0) if (code == 301f) set number on display to unsigned value, use leading zeros if (pointcode == 3002) no decimal point if (pointcode == 3003) show decimal point between 10s and 1s if (pointcode == 3004) show decimal point between 100s and 10s if (pointcode == 3005) show decimal point between 1000s and 100s ? 27ac - void clear_display (void) for (i = 0; i < a; i++) @ef43[i] = 0 27c8 = void refresh_display (void) // this function takes about 1.58 ms to run @ef3e[5] = { 7c, c8, 80, e0, 80 } call ROM 283c (r6=ef3e) 27f4 - void reset_buttons_and_irq0 (void) clear bit 7 of port 7 input register (fd86, ffbe) [is this even valid?] clear bit 6 of port 7 input register (fd86, ffbe) [is this even valid?] set bit 2 of port 4 to input set IRQ0 to level sensed set IRQ0 to disabled set bit 5 of port 6 to input set bit 6 of port 6 to input 283c - void write_lcd_outputs (short *lcdaddr=ef3e) set bit 6 of port 6 to output (fd85, ffb9 |= 40) set bit 5 of port 6 to output (fd85, ffb9 |= 20) set bit 5 of port 6 to high (ffbb |= 20) set bit 6 of port 6 to high (ffbb |= 40) set bit 6 of port 6 to low (ffbb &= ~40) for (i = 0; i < 3; i++) // no-op for delay for (i = 0; i < e; i++) value = lcdaddr[i] set bit 5 of port 6 to low (ffbb &= ~20) set bit 6 of port 6 to output (fd85, ffb9 |= 40) for (j = 0; j < 8; j++) set bit 5 of port 6 to low (ffbb &= ~20) for (k = 0; k < 3; k++) // no-op for delay if (value & 80) set bit 6 of port 6 to high (ffbb |= 40) else set bit 6 of port 6 to low (ffbb &= ~40) value <<= 1 for (k = 0; k < 3; k++) // no-op for delay set bit 5 of port 6 to high (ffbb |= 20) for (k = 0; k < 3; k++) // no-op for delay set bit 5 of port 6 to high (ffbb |= 20) set bit 5 of port 6 to low (ffbb &= ~20) set bit 6 of port 6 to input (fd85, ffb9 &= ~40) for (j = 0; j < 3; j++) // no-op for delay set bit 5 of port 6 to high (ffbb |= 20) for (j = 0; j < 3; j++) // no-op for delay set bit 5 of port 6 to low (ffbb &= ~20) for (i = 0; i < 3; i++) // no-op for delay set bit 6 of port 6 to output (fd85, ffb9 |= 40) set bit 6 of port 6 to low (ffbb &= ~40) for (i = 0; i < 3; i++) // no-op for delay set bit 5 of port 6 to high (ffbb |= 20) for (i = 0; i < 3; i++) // no-op for delay set bit 6 of port 6 to high (ffbb |= 40) 294a - IRQ1 handler set bit 1 of port 4 to input (fd83, ffb5 &= ~2) set IRQ1 to level sensed (ffc6 &= ~2) set IRQ1 to disabled (ffc7 &= ~2) 2964 - void init_stuff_irq1 (void) set RAM IRQ1 (fd96) to IRQ1 handler (2964) set bit 1 of port 4 to input (fd83, ffb5 &= ~2) set IRQ1 to level sensed (ffc6 &= ~2) set IRQ1 to disabled (ffc7 &= ~2) set 0x4 of port 5 to output low (fd84, ffb8 |= 4) (ffba &= ~4) set sleep mode to software standby, recovery time of 131,072 (ffc4 |= c0) return 0 299a - void play_system_sound (short code, short sound) if (code == 4003) call ROM 3de0 (r6=700d, sp0=1, sp1=data, sp2=0) // unqueued else if (code == 4004) call ROM 3de0 (r6=700b, sp0=1, sp1=data, sp2=0) // queued 29f2 - void get_power_status (short code, short *ptr) 29f2 - void get_on_off_key_state (short code=0x4000, short *ptr) 29f2 - void get_battery_voltage (short code=0x4001, short *ptr) if (code is 0x4000) set *ptr to 0 if on/off key is depressed, 1 otherwise if (code is 0x4001) set *ptr to raw A/D value from battery if (code invalid) set *ptr to 0 return 0 2a62 - void power_off (void) set 0x2 of port 4 to input set IRQ1 to edge sensed set IRQ1 to enabled set 0x4 of port 5 to high sleep set 0x4 of port 5 to low 2a84 - TEI handler // stop transmitting serial control register (ffda) &= 5b [disable transmit, TEI, TXI] transmitting flag (ef93) = 4f // not transmitting serial status register (ffdc) &= 7b [clear tx data reg empty, tx end flags] 2a9c - TXI handler if (outgoing packet length remaining (ef96) != 0) if (outgoing packet type (ef94) == 1775) // short message serial transmit data register (ffdb) = @ef52[@ef92++] else if (outgoing packet type (ef94) == 1776) // long message if (outgoing packet send state (ef9a) != 0) // send header, opcode, or opcode complement switch (outgoing packet send state (ef9a) --) case 5: serial transmit data register (ffdb) = 55 case 4: serial transmit data register (ffdb) = ff case 3: serial transmit data register (ffdb) = 00 case 2: serial transmit data register (ffdb) = ~@ef9b case 1: serial transmit data register (ffdb) = @ef9b @ef9b = ~@ef9b endswitch else if (has complements flag (ef51) == 1) // send packet data, checksum, and complements if (outgoing packet length remaining (ef96) > 2) if (outgoing packet data complement state (ef9c) != 0) serial transmit data register (ffdb) = ~@ef98[@ef92++] else serial transmit data register (ffdb) = @ef98[@ef92] @ef9b += @ef98[@ef92] outgoing packet data complement state (ef9c) ^= 1 else if (outgoing packet length remaining (ef96) > 1) serial transmit data register (ffdb) = @ef9b else serial transmit data register (ffdb) = ~@ef9b else // no packet data, send only checksum and complement if (outgoing packet length remaining (ef96) > 1) serial transmit data register (ffdb) = @ef98[@ef92] @ef9b += @ef98[@ef92++] else serial transmit data register (ffdb) = @ef9b outgoing packet length remaining (ef96) -- serial status register (ffdc) &= ~80 // clear tx data register empty else serial control register (ffda) &= ~80 // disable TXI 2c10 - RXI handler if (transmitting flag (ef93) == 4f) // 4f=not tranmitting if (serial receive reset counter (cc06/efb4->[0]) == 0) // counter is zero, so reset receive state expecting complement flag (efb3) = 0 receive state (efb2) = 2 last received byte (efba) = ff serial receive reset counter (cc06/efb4->[0]) = 1e current receive byte (efc1) = serial receive data register (ffdd) if (receive state (efb2) == 2) // STATE 2: receive message header and opcode if (has packet header flag (ef50) == 1) if (current receive byte (efc1) == 00 or ff or 55) expecting complement flag (efb3) = 0 last received byte (efba) = current receive byte (efc1) else if (@ef51 == 1 && expecting complement (efb3) == 1) if (current receive byte (efc1) == ~last received byte (efba)) if (receive length remaining (efc0) == 0) receive state (efb2) = 4 else if (last received byte (efba) & f7 == 45) receive state (efb2) = 6 receive data checksum (efbf) = 0 receive length remaining (efc0) = 4 else receive state (efb2) = 3 expecting complement (efb3) = 0 else unpack index (efb1) = 0 receive data index (efb0) = 1 @ef9d = current receive byte (efc1) receive length remaining (efc0) = current receive byte (efc1) & 07 if (receive length remaining (efc0) > 5) receive length remaining (efc0) -= 6 last received byte (efba) = current receive byte (efc1) receive checksum (efbe) = current receive byte (efc1) if (@ef51 == 0) if (receive length remaining (efc0) == 0) receive state (efb2) = 4 else if (current receive byte (efc1) & f7 == 45) receive state (efb2) = 6 receive data checksum (efbf) = 0 receive length remaining (efc0) = 4 else receive state (efb2) = 3 else expecting complement (efb3) = 1 else if (receive state (efb2) == 3) // STATE 3: receive message body if (@ef51 == 1 && expecting complement (efb3) == 1) // check complement byte if (current receive byte (efc1) == ~last received byte (efba)) // complement passes, check if done if (-- receive length remaining (efc0) == 0) receive state (efb2) = 4 else // complement fails, start over receive state (efb2) = 2 last received byte (efba) = ff expecting complement (efb3) = 0 else // receive primary byte @ef9d[receive data index (efb0)] = current receive byte (efc1) receive data index (efb0) = (receive data index (efb0) + 1) % 10 last received byte (efba) = current receive byte (efc1) receive checksum (efbe) += last received byte (efba) if (@ef51 == 0) if (-- receive length remaining (efc0) == 0) receive state (efb2) = 4 else expecting complement (efb3) = 1 else if (receive state (efb2) == 6) // STATE 6: receive op 45 body if (@ef51 == 1 && expecting complement (efb3) == 1) if (current receive byte (efc1) == ~last received byte (efba)) if (receive length remaining (efc0) == 0) if (receive data length remaining (efc2) -- == 0) receive state (efb2) = 5 expecting complement (efb3) = 0 else if (receive length remaining (efc0) != 0) if (-- receive length remaining (efc0) == 0) if (transfer sequence number (efbc) == (@ef9f << 8) + @ef9e) // index same as last, use saved data pointer incoming data pointer (efae) = saved data pointer (efc4) else // index different from last, save index and data pointer transfer sequence number (efbc) = (@ef9f << 8) + @ef9e saved data pointer (efc4) = incoming data pointer (efae) receive data length remaining (efc2) = (current receive byte (efc1) << 8) receive data length remaining (efc2) += last received byte (efba) if (receive length remaining (efc0) >= 2) @ef9d[receive index (efb0)] = current receive byte (efc1) receive index (efb0) = (receive index (efb0) + 1) % 10 else if (incoming data pointer (efae) != 0) byte at (incoming data pointer (efae)++) = current receive byte (efc1) receive data checksum (efbf) += current receive byte (efc1) last received byte (efba) = current receive byte (efc1) receive checksum (efbe) += last received byte (efba) if (@ef51 == 0) if (receive length remaining (efc0) == 0) if (receive data length remaining (efc2) -- == 0) receive state (efb2) = 5 else expecting complement (efb3) = 1 else if (receive state (efb2) == 5) // STATE 5: op 45 data checksum if (@ef51 == 1 && expecting complement (efb3) == 1) if (current receive byte (efc1) == ~last received byte (efba)) receive state (efb2) = 4 expecting complement (efb3) = 0 else @ef9d[receive data index (efb0)] = current receive byte (efc1) receive data index (efb0) = (receive data index (efb0) + 1) % 10 @ef9d[receive data index (efb0)] = receive data checksum (efbf) receive data index (efb0) = (receive data index (efb0) + 1) % 10 last received byte (efba) = current receive byte (efc1) receive checksum (efbe) += last received byte (efba) if (@ef51 = 0) receive state (efb2) = 4 else expecting complement (efb3) = 1 else if (receive state (efb2) == 4) // STATE 4: receive checksum if (@ef51 == 1 && expecting complement (efb3) == 1) if (current receive byte (efc1) == ~last received byte (efba)) if (receive checksum (efbe) == last received byte (efba)) receive state (efb2) = 2 valid incoming data flag (efb6) = 1 serial handler run flag (byte at efb8) |= 80 last received byte (efba) = ff else receive state (efb2) = 2 last received byte (efba) = ff expecting complement (efb3) = 0 else @ef9d[receive data index (efb0)] = current receive byte (efc1) receive data index (efb0) = (receive data index (efb0) + 1) % 10 last received byte (efba) = current receive byte (efc1) if (@ef51 == 0) if (receive checksum (efbe) == current receive byte (efc1)) receive state (efb2) = 2 valid incoming data flag (efb6) = 1 serial handler run flag (byte at efb8) |= 80 last received byte (efba) = ff else expecting complement (efb3) = 1 else // STATE *: error, restart receive state (efb2) = 2 last received byte (efba) = ff expecting complement (efb3) = 0 else // transmitting, restart unpack data index (efb1) = 0 receive data index (efb0) = 0 @ffdc &= ~40 30a4 - ERI handler if (receive state (efb2) != 5 or 6) receive state (efb2) = 2 serial receive reset counter (cc06/efb4->[0]) = 1e serial status register (ffdc) &= c7 [clr parity, framing, overrun err flags] 30d0 - void init_serial (r6=cc00+4, sp0=cc00+6, byte sp1=1, byte sp2=1) has packet header flag (ef50) = sp1 has complements flag (ef51) = sp2 expecting complement (efb3) = 0 serial handler run flag addr (efb8) = serial handler run flag addr (r6) valid incoming data flag (efb6) = 0 last received byte (efba) = ff transfer sequence number (efbc) = ffff receive state (efb2) = 2 @efb4 = cc06 pointer (sp0) serial control register (ffda) = 0 serial mode register (ffd8) = 30 // parenb | parodd // calculate clock select and value for bit rate register // unsigned char divider = 0; // clock select // int bitrateconstant = 1000; // value for bit rate register // while (bitrateconstant >= 255) { // bitrateconstant = 16000000 / ((1 << (divider * 2 + 4)) * 2400); // bitrateconstant = (bitrateconstant + 1) / 2 - 1; // divider++; // } // divider--; // this computes clock select = 0, value for bit rate register = cf // verified this with a run-time check serial mode register (ffd8) |= 0 bit rate register (ffd9) = cf serial status register (ffdc) &= 84 // clear various error flags set port 4 bit 0 to output (fd83, ffb5) // timer 1: 16 Mhz div 8, toggle every 26 cycles or 13 us, yields 38.5kHz set timer 1 control register (ffd0) = 9 // compare-match A clear, div 2,8 set timer 1 control/status register (ffd1) = 13 // toggle on compare-match A serial/timer control register (ffc3) = serial/timer control register (ffc3) time constant register A (ffd2) = 1a set port 4 bit 0 high (ffb7) set TEI handler to 2a84 set TXI handler to 2a9c set RXI handler to 2c10 set ERI handler to 30a4 clear outgoing packet data (ef52[64]) to zero outgoing packet data index (3f92) = 0 outgoing packet length remaining (ef96) = 0 transmitting flag (ef93) = 4f clear receive packet data (ef9d[16]) to zero receive data index (efb0) = 0 unpack data index (efb1) = 0 incoming data pointer (efae) = 0 serial control register (ffda) |= 50 // enable receive and receive interrupt 3250 - void set_range_long (short code=0x1770) clears bit 0 of ffb7, p4dr 3266 - void set_range_short (short code=0x1770) sets bit 0 of ffb7, p4dr 327c - play_tone_or_set_data_pointer (r6=code, sp0=param0, sp1=param1) if (code == 1771) // set incoming data pointer?: param0 = pointer incoming data pointer (efae) = param0 transder sequence number (efbc) = ffff else if (code == 1773) // play tone: param0 = frequency in Hz, param1 = duration in 1/100th sec if (param0 < 1f || param0 > 4e20) // freq < 31 || freq > 20000 // play tone, frequency out of range, set sp0=0 to pause call ROM 3de0 (r6=700b, sp0=0, sp1=param1, sp2=7) else // play tone, note how to convert freq to pitch period and control if (param0 < 7b) // 123 div = 400 ctl = 6 // div 1024 internal clock else if (param0 < 1ea) // 490 div = 100 ctl = 7 // div 256 internal clock else if (param0 < 3d4) // 980 div = 40 ctl = 4 // div 64 internal clock else if (param0 < f52) // 3922 div = 20 ctl = 5 // div 32 internal clock else div = 8 ctl = 2 // div 8 internal clock call ROM 3de0 (r6=700b, sp0=(7a1200/div)/param0, sp1=param1, sp2=ctl) else if (code == 1772) // play system sound: param1 = sound index call ROM 3de0 (r6=700b, sp0=1, sp1=param1, sp2=7) 339a - void reset_internal_minute_timer (short code) // called with code=0 or code=1774, code is ignored call ROM 3e9e (r6=700e) // reset internal minute timer 33b0 - receive_data (r6=&data, sp0=maxlen, sp1=&length) index = 0 while (index < maxlen && unpack index (efb1) != receive data index (efb0)) data[index++] = receive packet data[unpack data index++] (ef9d[efb1++]) if (unpack data index (efb1) >= 10) unpack data index (efb1) = 0 clear receive data is ready flag (efb6) = 0 *length += index 3426 - void check_for_data (byte *valid, byte **nextbyte) *valid = (receive data is ready (efb6) == 1) *nextbyte = incoming data pointer (efae) 343e = byte send_bytes (short code, byte opcode, byte *data, short len) if (transmitting flag (ef93) == 4f) if (code == 1775) // short message, uses addr, len if (has packet header flag (ef50) == 1) outgoing packet data[0] (ef52[0]) = 55 outgoing packet data[1] (ef52[1]) = ff outgoing packet data[2] (ef52[2]) = 00 outgoing packet data index (ef92) = 3 outgoing packet length remaining (ef96) = 3 else outgoing packet data index (ef92) = 0 outgoing packet length remaining (ef96) = 1 // why send extra byte? if (has complements flag (ef51) == 1) // with complements cksum = 0 for (index = 0; index < len; index++) cksum += addr[index] outgoing packet data[@ef92++] (ef52[@ef92++]) = addr[index] outgoing packet data[@ef92++] (ef52[@ef92++]) = ~addr[index] outgoing packet data (ef52[@ef92++]) = cksum outgoing packet data (ef52[@ef92++]) = ~cksum outgoing packet length remaining (ef96) += len * 2 + 2 else cksum = 0 // no complements for (index = 0; index < len; index++) cksum += addr[index] outgoing packet data (ef52[@ef92++]) = addr[index] outgoing packet data (ef52[@ef92++]) = cksum outgoing packet length remaining (ef96) += len + 1 outgoing packet type (ef94) = 1775 outgoing packet data index (ef92) = 0 transmitting flag (ef93) = 13 serial status register (ffdc) &= 7b // clear TDRE, TE flags serial control register (ffda) |= a4 // enable transmit, TEI, TXI retval = 0 else if (code == 1776) // long message, uses opcode, addr, len if (has header flag (ef50) == 1) outgoing packet send state (ef9a) = 5 else outgoing packet send state (ef9a) = 0 outgoing packet length remaining (ef96) = (@ef51 == 1) ? @ef9a + len * 2 + 2 : @ef9a + len + 1 outgoing packet data pointer (ef98) = addr outgoing packet type (ef94) = 1776 outgoing packet complement state (ef9c) = 0 outgoing packet opcode and checksum (ef9b) = opcode outgoing packet data index (ef92) = 0 tranmitting flag (ef93) = 13 serial status register (ffdc) &= 7b // clear TDRE, TE flags serial control register (ffda) |= a4 // enable transmit, TEI, TXI retval = 0 else retval = 4c return retval 3636 - void shutdown_serial (void) set bit 0 of port 4 to output (fd83, ffb5 |= 1) set bit 0 of port 4 to low (ffb7) set bit 7 of port 6 to output (fd85, ffb9) set bit 7 of port 6 to low (ffbb) clear timer 1 control register clear timer 1 control/status serial/timer control register (ffc3) &= ff serial control register (ffda) &= 5b [disable TEI, TXI, disable transmit] serial control register (ffda) &= af [disable RXI, disable receive] set bit 0 of port 5 to output (fd84, ffb8) set bit 0 of port 5 to low (ffba) set bit 1 of port 5 to output (fd84, ffb8) set bit 1 of port 5 to low (ffba) 3692 - void set_port_6_bit_3_output_high (void) set bit 3 of p6ddr and shadow fd85 - port 6 bit 3 is output set bit 3 of p6dr - port 6 is high 36a6 - void do_nothing (void) does nothing, returns 0 36aa - void set_port_6_bit_3_input (void) clear bit 3 of p6ddr and shadow fd85 - port 6 bit 3 is input 36ba - OCIA handler // executed every 1/1000 sec clear output compare flag (bit 3 of ff91) @f000 = modulated motor output (efce) if (millisecond counter 0 (efcf) % 3 == 0) set port 6 bits 0, 1, 2 to low (ffbb &= ~07) // motor control state modulated motor output (efce) = 0 if (motor 0 waveform (efcb) & 1) modulated motor output (efce) |= unmodulated motor output (efca) & c0 motor 0 waveform (efcb) >>= 1 motor 0 waveform (efcb) |= 80 else motor 0 waveform (efcb) >>= 1 if (motor 1 waveform (efcc) & 1) modulated motor output (efce) |= unmodulated motor output (efca) & 0c motor 1 waveform (efcc) >= 1 motor 1 waveform (efcc) |= 80 else motor 1 waveform (efcc) >>= 1 if (motor 2 waveform (efcd) & 1) modulated motor output (efce) |= unmodulated motor output (efca) & 03 motor 2 waveform (efcd) >= 1 motor 2 waveform (efcd) |= 80 else motor 2 waveform (efcd) >>= 1 if (serial receive reset counter (cc06/efc6->[0]) != 0) serial receive reset counter (cc06/efc6->[0]) -- // activate motor callbacks if (motor 0 timer (cc28/efc6->[22]) != 0) motor 0 timer (cc28/efc6->[22]) -- if (motor 0 timer (cc28/efc6->[22]) == 1) set second handler to run (cc01/efc8->[1] |= 80) if (motor 1 timer (cc2a/efc6->[24]) != 0) motor 1 timer (cc2a/efc6->[24]) -- if (motor 1 timer (cc2a/efc6->[24]) == 1) set second handler to run (cc01/efc8->[1] |= 80) if (motor 2 timer (cc2c/efc6->[26]) != 0) motor 2 timer (cc2c/efc6->[26]) -- if (@cc2c/@efc6->[26] == 1) set second handler to run (cc01/efc8->[1] |= 80) if (millisecond counter 0 (efcf) % 0a == 0) // executed every 1/100 sec if (sound duration remaining (eff6) == 0) clear bits 0, 1 of timer 0 control register (ffc8) [stop timer] clear bit 0 of serial/timer control register (ffc3) if (sound data pointers in use flag (effe) == 1) if (byte at (++ pointer to sound pitch period data (eff8)) == 1) sound data pointers in use flag (effe) = 0 else @effa++ @effc++ time constant register A (ffca) = byte at @eff8 sound duration remaining (eff6) = byte at @effa serial/timer control register (ffc3) |= byte at @effc & 1 timer 0 control register (ffc8) |= byte at @effc >> 1 if (sound data pointers in use flag (effe) == 0) if (sound tail index (efd6) != sound head index (efd7) + 1 % 0a) // sound list not empty index = sound head index (efd7) + 1 % 0a sound head index (efd7) = index if (@efd8[index] == 0) // silence? sound duration remaining (eff6) = @efe2[index] else if (@efd8[index] == 1) // system sound sound data pointers in use flag (effe) = 1 switch (@efe2[index]) case 0: @eff8 = 3f46 // sound 0, blip @effa = 3f4a @effc = 3f4c case 1: @eff8 = 3f4e // sound 1, beep beep @effa = 3f54 @effc = 3f58 case 2: @eff8 = 3f5c // sound 2, downward tones @effa = 3f68 @effc = 3f72 case 3: @eff8 = 3f7c // sound 3, upward tones @effa = 3f88 @effc = 3f92 case 4: @eff8 = 3f9c // sound 4, low buzz @effa = 3f9e @effc = 3fa0 case 5: @eff8 = 3fa2 // sound 5, fast upward tones @effa = 3fae @effc = 3fb8 default: @eff8 = 3f9c // sound 6, low buzz @effa = 3f9e @effc = 3fa0 endswitch time constant register A (ffca) = byte at @eff8 sound duration remaining (eff6) = byte at @effa serial/timer control register (ffc3) |= byte at @effc & 1 timer 0 control register (ffc8) |= byte at @effc >> 1 else // tone sound duration remaining (eff6) = @efe2[index] time constant register A (ffca) = byte at @efd8[index] serial/timer control register (ffc3) |= @efec[index] & 1 timer 0 control register (ffc8) |= @efec[index] >> 1 else // sound queue empty sounds playing flag (efff) = 0 clear bits 0, 1 of timer 0 control register (ffc8) [stop timer] clear bit 0 of serial/timer control register (ffc3) else // sound duration remaining (eff6) != 0 sound duration remaining (eff6) -- if (millisecond counter 3 (efd4) ++ == 0c) // 120 ms passed, off by 1, so really 130 ms millisecond counter 3 (efd4) = 0 set third handler dispatch run flag (cc02/efc8->[2] |= 80) set fourth handler dispatch run flag (cc03/efc8->[3] |= 80) if (millisecond counter 2 (efd2) ++ == 1770) // one minute passed, off by 1, so really one minute plus 10 ms! if (minutes to power off (cc12/efc6->[0c]) != 0) minutes to power off (cc12/efc6->[0c]) -- if (++minutes on clock (cc10/efc6->[0a]) > 59f) // 5a0 min/day minutes on clock (cc10/efc6->[0a]) = 0 millisecond counter 2 (efd2) = 0 if (++ millisecond counter 1 (efd0) == 64) // executed every 1/10th second firmware timer 0 (cc08[0]/efc6->[2]:[0]) = (firmware timer 0 + 1) & 7fff firmware timer 1 (cc08[1]/efc6->[2]:[1]) = (firmware timer 1 + 1) & 7fff firmware timer 2 (cc08[2]/efc6->[2]:[2]) = (firmware timer 2 + 1) & 7fff firmware timer 3 (cc08[3]/efc6->[2]:[3]) = (firmware timer 3 + 1) & 7fff index = millisecond counter 0 (efcf) % 0a if (per task wakeup delay [index] (cc14[index]/efc6->[0e]:[index]) != 0) per task wakeup delay [index] (cc14[index]/efc6->[0e]:[index]) -- if (millisecond counter 0 (efcf) % 3 == 0) start a/d converstion, enable a/d interrupt (ffe8 |= 60) if (millisecond counter 0 (efcf) ++ > 1e) // efcf > 30 millisecond counter 0 (efcf) = 1 3b74 - A/D handler a/d status register (ffe8) &= 9f [stop a/d, disable a/d interrupt] a/d status register (ffe8) &= ~80 [clear a/d end flag] port 6 data register (ffbb) |= sensor output (efd5) set first handler run flag (cc00/efc8->[0] |= 80) 3b9a - void init_milliseconds (r6=firmwaredata6, sp0=firmwaredata) clear p1ddr shadow (fd80) clear p2ddr shadow (fd81) clear p3ddr shadow (fd82) clear p4ddr shadow (fd83) clear p5ddr shadow (fd84) clear p6ddr shadow (fd85) clear p7pin shadow (fd86) unmodulated motor output (efca) = 0 millisecond counter 0 (efcf) = 1 millisecond counter 3 (efd4) = 1 millisecond counter 1 (efd0) = 0 millisecond counter 2 (efd2) = 0 sensor output (efd5) = 0 @efc6 = firmwaredata6 @efc8 = firmwaredata @efd6 = 1 @efd7 = 0 clear queue efd8[] clear queue efe2[] clear queue efec[] sound data pointers in use flag (effe) = 0 sounds playing flag (efff) = 0 @fda2 = 36ba // OCIA handler @fdbc = 3b74 // A/D handler @ffc8 = 0b // timer 0 clear count on compare-match A, use div 256/1024 clock @ffc9 = 03 // timer 0 toggle output on compare-match A @ffc3 |= 01 // timer uses div 256 clock @fd80 = @ffb0 = @fd80 | ff // port 1 all pins output @fd81 = @ffb1 = @fd81 | ff // port 2 all pins output @fd80 = @ffb0 = @fd80 | ff // port 1 all pins output @fd81 = @ffb1 = @fd81 | ff // port 2 all pins output @fd80 = @ffb0 = @fd80 | ff // port 1 all pins output @fd81 = @ffb1 = @fd81 | ff // port 2 all pins output @f000 = unmodulated motor output (efca) // effectively, @f000 = 0 @ffe9 &= ~80 // disable ADTRG' (no external A/D trigger) @ffe8 = 13 // A/D scan mode, AN0-AN3, slower conversion time, more states @ffc2 &= ~20 // do not divide clock by two @ff96 &= fc // free-running timer, use div 2 clock @ff96 |= 02 // free-running timer, use div 32 clock @ff91 = 1 // free-running timer is cleared by compare-match A @ff97 &= ~10 // CPU can access OCRA @ff94 = 01f4 // output compare reg A (01f4 = 500 ticks, 1 ms w/ div 32 clk) @ff92 = 0000 // clear free running timer @ff91 &= ~08 // clear output compare A (by reading TCSR, clearing 08 bit) @ff90 |= 08 // enable output compare interrupt A enable clear interrupt mask bit (andc 7f,ccr) port 6 data register (ffbb) |= sensor output (efd5) // sensor output 3ccc = void get_sound_playing_flag (short code=0x700c, byte *ptr) byte at ptr = sound playing flag (efff) 3ce6 - void control_motor_2 (short code, motorstruct *motor) struct motorstruct (sp+0) byte power (sp+0) byte mode (sp+1) endstruct if (code == 7001) switch (motor->mode) case 1: @efca &= ~40, @efca |= 80 (set bit 7 clr bit 6) // forward case 2: @efca &= ~80, @efca |= 40 (clr bit 7 set bit 6) // backward case 3: @efca &= ~00, @efca |= c0 (set bit 7 set bit 6) // stop case 4: @efca &= ~c0, @efca |= 00 (clr bit 7 clr bit 6) // float endswitch motor 0 waveform (efcb) = motor pwm waveforms (3fc2) [motor->mode & 7] else if (code == 7002) switch (motor->mode) case 1: @efca &= ~04, @efca |= 08 (set bit 3 clr bit 2) // forward case 2: @efca &= ~08, @efca |= 04 (clr bit 3 set bit 2) // backward case 3: @efca &= ~00, @efca |= 0c (set bit 3 set bit 2) // stop case 4: @efca &= ~0c, @efca |= 00 (clr bit 3 clr bit 2) // float endswitch motor 1 waveform (efcc) = motor pwm waveforms (3fc2) [motor->mode & 7] else if (code == 7003) switch (motor->mode) case 1: @efca &= ~01, @efca |= 02 (set bit 1 clr bit 0) // forward case 2: @efca &= ~02, @efca |= 01 (clr bit 1 set bit 0) // backward case 3: @efca &= ~00, @efca |= 03 (set bit 1 set bit 0) // stop case 4: @efca &= ~03, @efca |= 00 (clr bit 1 clr bit 0) // float endswitch motor 2 waveform (efcd) = motor pwm waveforms (3fc2) [motor->mode & 7] 3de0 - void control_output (short code, short param0, byte param1, byte param2) if (code == 700a) // set sensor output sensor output (efd5) |= param0 (as byte) else if (code == 700b) // queued sound if ((tail index (efd6) + 1) % 10 != head index (efd7)) @efd8[tail index (efd6)] = param0 // 0=pause, 1=sound, >1=tone/pitch @efe2[tail index (efd6)] = param1 // time (pause/pitch) or sound @efec[tail index (efd6)] = param2 // control byte (tone/pitch) tail index (efd6) = (tail index (efd6) + 1) % 10 sound playing flag (efff) = 1 else if (code == 700d) // unqueued sound, flushes sound queue // bug: sound data pointers in use flag (effe) not set to 0 // result is that sound queue is not completely flushed sound duration remaining (eff6) = 0 @efd8[(head index (efd7) + 1) % 10] = param0 @efe2[(head index (efd7) + 1) % 10] = param1 // no control byte, apparently tail index (efd6) = (head index (efd7) + 2) % 10 sound playing flag (efff) = 1 3e9e - void clear_sensor_and_timer_data (short code, byte param) if (code == 700a) sensor output (efd5) &= ~param // clear sensor output bits else if (code == 700e) millisecond counter 3 (efd2) = 0 // reset 60 sec counter 3ed0 - void do_nothing (void) 3ed4 - void shutdown_milliseconds (void) set port 6 bit 4 to input (fd85, ffb9) clear motor output bits (efca) clear timer 0 control register (ffc8) clear timer 0 control/status register (ffc9) clear bit 0 of timer status/control register (ffc3) stop a/d conversion, disable a/d interrupt (ffe8 &= 9f) set a/d to single mode, an0 only (ffe8 &= ec) @f000 = unmodulated motor output (efca) @f000 = unmodulated motor output (efca) @f000 = unmodulated motor output (efca) disable OCIA (clear bit 3 of ff90) 8000 - firmware main make room for 1 word on stack byte code (sp+0) clear ram cc00-f000 init jump table (cc66,cc68,cc6a,cc6c,cc6e,cc70) with handler addresses set RAM reset vector (value at fd90) to ROM reset vector (value at 0000) set delete firmware flag (cc37) to 0 set firmware run state (r4l) to 0 while (1) if (firmware run state (r4l) == 0) // RUN STATE 0: init call ROM 3b9a (r6=&romdata=cc06, sp0=&handlerstatusbytes=cc00) call handler init routines (823e,83c6,85ec,8d0c,8f3a,bdc0) set delete firmware flag (cc37) to 2 set firmware run state (r4l) to 3 else if (firmware run state (r4l) == 3) // RUN STATE 3: run find highest priority handler that is ready to run if found call handler from dispatch table (word array[6] at cc66) else set fifth handler run flag (0x80 bit of cc04) if (power on flag (cc33) == 0 || delete firmware flag (cc37) == 1) set firmware run state (r4l) to 1 if (battery low flag (cc64) == 1) set firmware run state (r4l) to 1f else if (firmware run state (r4l) == 4) // RUN STATE 4: sleep call fourth handler stop routine (8f2a) - GO TO SLEEP set firmware run state (r4l) to 0 else if (firmware run state (r4l) == 1) // RUN STATE 1: stop if (delete firmware flag (cc37) == 1) // delete firmware call ROM 3ed4 (void?) call handler stop routines, skip fourth (83b6,85dc,8cf4,bc2c,be00) // setting these prevents firmware from being unlocked again firmware checksum (fd88) = 0 firmware checksum complement (fd8a) = 0 // setting ffcc to non-zero required to keep rcx on after reset // or maybe not if there is a way to make ee80 non-zero ...? set timer 0 counter (ffcc) to 1 call RAM reset vector (at fd90, typically ROM reset vector 03ae) // if that call returns - normally it doesn't - then go to sleep set firmware run state (r4l) to 4 else // power down? call handler stop routines, skip fourth (83b6,85dc,8cf4,bc2c,be00) call ROM 3ccc (r6=700c, sp0=&code) // get sound queue length? call fourth handler (8d74) if (code == 0 && ready to sleep (cc36) == 1) call ROM 3ed4 (void?) set firmware run state (r4l) to 4 else if (firmware run state (r4l) == 1f) // RUN STATE 1F: battery low call ROM 3e9e (r6=700a, sp=7) zero all motor state (byte array[3] at cc4d, bytes array[3] at cc50) call second handler (8440) (motor handler, stop motors) if (power on flag (cc33) == 1) call ROM 3de0 (r6=700b, sp0=1, sp1=1, sp2=0) call fourth handler (8d74) set firmware run state (r4l) to 1 else // RUN STATE INVALID set firmware run state (r4l) to 0 endwhile 823e - first handler init clear raw sensor values (cc3e[]) clear sensor values (cc44[]) clear boolean sensor flags (cc4a[]) set sensor ready flag (cc60) to 1 set first handler dispatch state (cc00) to 7f call ROM 1498 (void) - init sensor pins 828c - first handler struct sensorstruct (sp+0) byte type (sp+0) byte mode (sp+1) word raw (sp+2) word value (sp+4) byte boolean (sp+6) endstruct for (i = 0; i < 3; i++) if (sensor type[i] (cc38[i]) == 3 or 4) call ROM 1946 (r6=1000+i) else call ROM 19c4 (r6=1000+i) type = sensor type[i] (cc38[i]) mode = sensor mode[i] (cc3b[i]) raw = raw sensor value[i] (cc3e[i]) value = sensor value[i] (cc44[i]) boolean = boolean sensor flag[i] (cc4a[i]) if (call ROM 14c0 (r6=1000+i, sp0=&sensorstruct) == 0) raw sensor value[i] (cc3e[i]) = raw sensor value[i] (cc44[i]) = value boolean sensor flag[i] (cc4a[i]) = boolean if (sensor ready flag (cc60) == 0) if (++ sensor ready again counter (cc72) > 1) sensor ready flag (cc60) = 1 else sensor ready again counter (cc72) = 0 if (temp motor counter != 0) temp motor counter (cc62)-- if (temp motor counter == 0) temp motor state (cc50[]) = 4f // temporary motor state = off set 0x80 bit of second handler dispatch state (cc01) clear 0x80 bit of first handler dispatch state (cc00) 83b6 - first handler cleanup call ROM 1a22 (void) 83c6 - second handler init set second handler dispatch state to 1 for (i = 0; i < 3; i++) set motor state[i] (cc53[i]) to (i<<4) | 4f set temp motor state[i] (cc50[i]) = motor state[i] (cc4d[i]) = 4f set motor direction[i] (cc74[i]) = motor state[i] (cc53[i]) & 8 set motor stopped flag (cc77[i]) = 1 set @cc28[i] = 0 call ROM 1a4a (void) // does nothing 8440 - second handler for (i = 0; i < 3; i++) if (temporary motor state[i] (cc50[i]) & 30) motor state[i] (cc53[i]) = (i<<4)|(temp motor state[i] (cc50[i]) & cf) else motor state[i] (cc53[i]) = (i<<4)|(motor state[i] (cc4d[i]) & cf) if (motor state[i] (cc53[i]) & 80) if (motor state[i] (cc53[i]) & 08) if (motor direction[i] (cc74[i]) || motor stopped flag (cc77[i])) call ROM 1a4e (r6=2000+i, 1, motor state[i] (cc53[i]) & 7) motor stopped flag (cc77[i]) = 0 motor direction[i] (cc74[i]) |= 08 else motor direction[i] (cc74[i]) |= 08 call ROM 1a4e (r6=2000+i, 3, 7) @cc28[i] = 64 else if (!motor direction[i] (cc74[i]) || motor stopped flag (cc77[i])) call ROM 1a4e (r6=2000+i, 2, motor state[i] (cc53[i]) & 7) motor stopped flag (cc77[i]) = 0 motor direction[i] (cc74[i]) &= ~08 else motor direction[i] (cc74[i]) &= ~08 call ROM 1a4e (r6=2000+i, 3, 7) @cc28[i] = 64 else if (motor state[i] (cc53[i]) & 40) call ROM 1a4e (r6=2000+i, 3, 7) motor stopped flag (cc77) = 1 else call ROM 1a4e (r6=2000+i, 4, 7) motor stopped flag (cc77) = 1 clear 0x80 bit of second handler dispatch state (cc01) 85dc - second handler cleanup call ROM 1ab4 (void) // does nothing 85ec - third handler init clear every-other flag (cc80) clear datalog blink state (cc81) clear button down state (cc7a) clear upload status bar count (cc82) set current display (cc5c) to 0 set third handler dispatch state to 1 call ROM 1aba (void) - init third handler stuff clear can run flag (cc34) clear view button down flag (cc32) clear program changed flag (cc31) clear run button state (cc35) clear show upload status bar (cc63) clear battery low flag (cc64) 863c - third handler short buttonstate (sp+0) every-other flag (cc80) ^= 01 call ROM 1fb6 (r6=3000,sp0=&buttonstate) // read buttons if (buttonstate & 2) // view button if (!(last button state (cc7b) & 2)) set 0x02 bit of button down state (cc7a) view button down flag (cc32) = 1 current display (cc5c) ++ if (current display (cc5c) > 6) current display (cc5c) = 0 call ROM 1b32 (r6=301e) // refresh display? copy power down delay (cc5e) to minutes to power off (cc12) else clear 0x02 bit of button down state (cc7a) view button down flag (cc32) = 0 if (buttonstate & 1) // run button if (!(last button state (cc7b) & 1)) set 0x02 bit of button down state (cc7a) call ROM 1b32 (r6=301e) // refresh display? copy power down delay (cc5e) to minutes to power off (cc12) if (last button state (cc7b) & 2) if (current display (cc5c) == 4, 5, or 6) temp motor state cc50[cc5c - 4] = ff // drive motor forward set 0x80 bit of second handler dispatch state (cc01) else run button state (cc35) ^= 01 // toggle run state else if (last button state (cc7b) & 1) clear 0x01 bit of button down state (cc7a) set temp motor state[] (cc50[]) to 47 set 0x80 bit of second handler dispatch state (cc01) if (buttonstate & 4) // prgm button if (!(last button state (cc7b) & 4)) set 0x04 bit of button down state (cc7a) call ROM 1b32 (r6=301e) // refresh display? copy power down delay (cc5e) to minutes to power off (cc12) if (last button state (cc7b) & 4) if (current display (cc5c) == 4, 5, or 6) temp motor state cc50[cc5c - 4] = f7 // drive motor backward set 0x80 bit of second handler dispatch state (cc01) else program changed flag (cc31) = 1 displayed program number (cc5d) ++ if (displayed program number (cc5d) > 5) displayed program number (cc5d) = 0 else if (last button state (cc7b) & 4) clear 0x04 bit of button down state (cc7a) set temp motor state[] (cc50[]) to 47 set 0x80 bit of second handler dispatch state (cc01) if (every-other flag (cc80) == 1) call ROM 1e4a datalog blink state (cc81) ^= 01 count = 0 switch (datalog quarters filled (cc61) + blink state (cc81) - 1) // blink case 1: count = 1 case 2: count = 2 case 3: count = 3 case 4: count = 4 case 5: count = 4 endswitch while (count--) call ROM 1b62 (r6=3018) // datalog indicator if (show upload status bar (cc63) == 1) upload status bar count (cc82) ++ if (upload status bar count (cc82) < 0a) call ROM 1b62 (r6=301a) // upload status bar else call ROM 1e4a (r6=301a) // upload status bar show upload status bar (cc63) = 0 upload status bar count (cc82) = 0 if (can run flag (cc34) == 1) walking figure state (cc7c) ^= 01 if (walking figure state (cc7c) != 0) call ROM 1b62 (r6=3007) // walking position else call ROM 1b62 (r6=3006) // standing position else call ROM 1b62 (r6=3006) // standing position if (battery power (cc5a) > 1a2c (6700 mV)) call ROM 1e4a (r6=301b) // clear battery low indicator else if (battery power (cc5a) > 189c (6300 mV)) call ROM 1b62 (r6=301b) // set battery low indicator else battery low (cc64) = 1 call ROM 27ac call ROM 1b62 (r6=301b) // set battery low indicator call ROM 27c8 call ROM 339a (r6=0) // reset internal minute timer minutes to power off (cc12) = 1 if (transmitter in use (cc56) == 1) if (transmitter range (cc57) == 0) // short range? call 1b62 (r6=301c) // set short range light else call 1b62 (r6=301d) // set long range light transmitter in use (cc56) = 0 else call 1e4a (r6=301c) // clear short range light call 1e4a (r6=301d) // clear long range light if (data transfer address (cc58) != last data transfer address (cc84)) last data transfer address (cc84) = data transfer address (cc58) call 1b62 (r6=3019) // set transfer in progress else if (show upload status bar (cc63) == 0) call ROM 1e4a (r6=3019) // clear upload status bar call ROM 1e4a (r6=3008) // clear lots of stuff (order has been changed) call ROM 1e4a (r6=3009) call ROM 1e4a (r6=300a) call ROM 1e4a (r6=300b) call ROM 1e4a (r6=300d) call ROM 1e4a (r6=300c) call ROM 1e4a (r6=300e) call ROM 1e4a (r6=300f) call ROM 1e4a (r6=3010) call ROM 1e4a (r6=3011) call ROM 1e4a (r6=3012) call ROM 1e4a (r6=3013) call ROM 1e4a (r6=3014) call ROM 1e4a (r6=3015) call ROM 1e4a (r6=3016) if (motor 0 is on (0x80 bit of cc53 set)) if (motor 0 state is forward (0x04 bit of cc53 set)) call ROM 1b62 (r6=3010) // motor 0 forward arrow else call ROM 1b62 (r6=300f) // motor 0 backward arrow if (motor 1 is on (0x80 bit of cc54 set)) if (motor 1 state is forward (0x04 bit of cc54 set)) call ROM 1b62 (r6=3013) // motor 1 forward arrow else call ROM 1b62 (r6=3012) // motor 1 backward arrow if (motor 2 is on (0x80 bit of cc55 set)) if (motor 2 state is forward (0x04 bit of cc55 set)) call ROM 1b62 (r6=3016) // motor 2 forward arrow else call ROM 1b62 (r6=3015) // motor 2 backward arrow if (sensor 0 boolean true (cc4a != 0)) call ROM 1b62 (r6=3009) if (sensor 1 boolean true (cc4b != 0)) call ROM 1b62 (r6=300b) if (sensor 2 boolean true (cc4c != 0)) call ROM 1b62 (r6=300d) // show program number! call ROM 1ff2 (r6=3017, sp0=displayed program number (cc5d) + 1, sp1=0) switch (current display (cc5c)) case 0: // display time if (++@cc7e > 2) @cc7e = 0 @cc7f ^= 01 value = (minutes on clock (cc10) / 60) * 100 value += (minutes on clock (cc10) % 60) if (@cc7f == 0) call ROM 1ff2 (r6=301f, sp0=value, sp1=3004) else call ROM 1ff2 (r6=301f, sp0=value, sp1=3002) case 1: // display sensor 0 call ROM 1b62 (r6=3008) // sensor 0 view selected if (sensor mode (cc3b) == a0 or c0) // temperature call ROM 1ff2 (r6=3001, sp0=sensor value (cc44), sp1=3003) else call ROM 1ff2 (r6=3001, sp0=sensor value (cc44), sp1=3002) case 2: // display sensor 1 call ROM 1b62 (r6=300a) // sensor 1 view selected if (sensor mode (cc3c) == a0 or c0) // temperature call ROM 1ff2 (r6=3001, sp0=sensor value (cc46), sp1=3003) else call ROM 1ff2 (r6=3001, sp0=sensor value (cc46), sp1=3002) case 3: // display sensor 2 call ROM 1b62 (r6=300c) // sensor 2 view selected if (sensor mode (cc3d) == a0 or c0) // temperature call ROM 1ff2 (r6=3001, sp0=sensor value (cc48), sp1=3003) else call ROM 1ff2 (r6=3001, sp0=sensor value (cc48), sp1=3002) case 4: // display motor 0 call ROM 1b62 (r6=300e) // motor 0 view selected if (motor 0 is on (0x80 bit of cc53 set)) speed = (motor 0 state (cc53) & 7) + 1 else speed = 0 call ROM 1ff2 (r6=3001, sp0=speed, sp1=3002) case 5: // display motor 1 call ROM 1b62 (r6=3011) // motor 1 view selected if (motor 1 is on (0x80 bit of cc54 set)) speed = (motor 1 state (cc54) & 7) + 1 else speed = 0 call ROM 1ff2 (r6=3001, sp0=speed, sp1=3002) case 6: // display motor 2 call ROM 1b62 (r6=3014) // motor 2 view selected if (motor 1 is on (0x80 bit of cc55 set)) speed = (motor 1 state (cc55) & 7) + 1 else speed = 0 call ROM 1ff2 (r6=3001, sp0=speed, sp1=3002) endswitch current display (cc5c) = 0 // where does this get reset? call ROM 27c8 // refresh display? clear 0x80 bit of third handler (cc02) 8d0c - fourth handler init call ROM 2964 - init_stuff_irq1 set power on flag (cc33) = 1 set battery power (cc5a) to 2328 (9000 mV) if (firmware status (cc37) == 0) set power down delay (cc5e) to 0f (15 minutes) copy power down delay (cc5e) to power down clock (cc12) set sum of raw battery voltage samples (cc88) to 0 set count of raw battery voltage samples (cc8a) to 0 set on/off button state (cc8b) to a set on/off button debouncer (cc8c) to 0 set ready to sleep (cc36) to 0 call ROM 299a (r6=0x4004, sp0=1) // beep twice for power on return value returned by 2964 (always 00) 8d74 - fourth handler push r1-r5 on stack make room for 2 shorts on stack short buttonstate (sp+0) short rawvoltage (sp+2) call ROM 29f2 (r6=0x4000, sp0=&buttonstate) // get on/off key state switch (on/off button state (cc8b)) case a: // TURNING ON - wait for release? if (buttonstate == 0) on/off button debouncer (cc8c) = 0 else // debounce for 2 130 ms ticks if (on/off button debouncer (cc8c) ++ > 2) on/off button state (cc8b) = b case b: // ON - wait for press if (buttonstate == 0) call ROM 299a (r6=4003, sp0=0) // beep once for power off power on flag (cc33) = 0 on/off button state (cc8b) = c on/off button debouncer (cc8c) = 0 case c: // TURNING OFF - wait for release? if (buttonstate == 0) on/off button debouncer (cc8c) = 0 else // debounce for 40 firmware loop interations if (on/off button debouncer (cc8c) ++ > 0x28) ready to sleep (cc36) = 1 on/off button state (cc8b) = d case d: do nothing endswitch if (power down delay (cc5e) != 0 && power down time remaining (cc12) == 0) power down time remaining (cc12) = 0x64 (100 minutes) call ROM 299a (r6=4003, sp0=0) // beep once for power off power on flag (cc33) = 0 ready to sleep (cc36) = 1 call ROM 29f2 (r6=0x4001, sp0=&rawvoltage) // get raw battery voltage sum of raw battery voltage samples (cc88) += rawvoltage if (++ count of raw battery voltage samples (cc8a) == 20) long temp0 = sum of raw battery voltage samples (cc88) * #abd4 long temp1 = count of raw battery voltage samples (cc8a) * #0618 battery voltage (cc5a) = temp1 / temp0 sum of raw battery voltage samples (cc88) = 0 count of raw battery voltage samples (cc8a) = 0 clear fourth handler dispatch execute bit (bit 0x80 of cc03) long temp0 = rawvoltage * #abd4 long temp1 = #00000618 if (temp0 / temp1 < 0000189c (6300 mV)) call ROM 3266 (r6=1770) // set range short temp motor state (cc50[]) = 30 // temporary motor float temp motor counter (cc62) = 14 // time? = 20 set second handler dispatch execute bit (bit 0x80 of cc01) else if (transmitter range (cc57) == 1) call ROM 3250 (r6=1770) // set range long 8f2a - fourth handler cleanup call ROM 2a62 (void) - power_off 8f3a - fifth handler init set fifth handler dispatch state (cc04) to 1 call ROM 30d0 (r6=cc00+4, sp0=cc00+6, sp1=1, sp2=1) - init ROM pkt handler? set minutes on clock/watch (cc10) to 0 set current task (ccc0) to 0 set transfer in progress (cc02) to 0 set run button running flag (ccc3) to 0 set random number state (cd08) to -1 if (transmitter range (cc57) == 1) call ROM 3260 (r6=1770) // set range long clear 0x80 bit of fifth handler dispatch state (cc04) if (firmware delete flag (cc37) == 0) set transmitter range (cc57) = 0 call ROM 3266 (r6=1770) // set range short set current program number (ccc4) = 0 set displayed program number (cc5d) to 0 set @cc61 to 0 set last message received (cd06) to 0 clear variables (ccc6[]) clear sensor types (cc38[]) and sensor modes (cc3b[]) clear in subroutine flags (cc8e[][]) clear task status flags (ce56[][]) set all current program program counters (cdde[]) to cee2 clear loop counter depths (ceb0[]) set subroutine memory map entries (word @cd22[0x28]) to ceba+offset set first 40 bytes of program data (ceba[]) to f6, return from subroutine set task memory map entries (word @cd72[0x32]) to cee2 set datalog next (cdd8) to cee2 set first free (cdda) and last valid (cddc) to cee5 set datalog dlrec at cee2 to {ff,0001} set last valid address (cddc) to e6b9 90d2 - fifth firmware handler push r0-r5 onto stack make room for 2a more bytes on stack word sp00 (sp+00) // temp0 [for processing a byte code command] word sp02 (sp+02) // used by test and branch for source 2 word sp04 (sp+04) // transfer address / memmap task start addr word sp06 (sp+06) // temp1 [for ops 33, 52] word sp08 (sp+08) // transfer length / memmap adjust value word sp0a (sp+0a) // temp2 [for op a4] word sp0c (sp+0c) // temp3 [for op a4] word sp0e (sp+0e) // transfer index [for op 45] byte nextlsb (sp+10) byte runflag (sp+11) = 0 byte lenbits (sp+13) byte sendreply (sp+14) = 0 byte valid (sp+16) byte returnval (sp+17) = 0 byte length (sp+18) // dummy variable byte data[16] ([sp+1a to sp+2a)) store firmwaredataptr in r1 if (program changed flag (cc31 byte) == 1) program changed flag (cc31 byte) = 0 clear can run flag (cc34 flag) stop all tasks (ce56 flags) stop all motors (cc4d flags) set update motor state flag (cc01 flag) reset all program counters (copy cd72[prog] counters to cdde counters) if (run button state (cc35 byte) != is running flag (ccc3 byte)) set is running flag (ccc3 byte) = run button state (cc35 byte) if (can run flag clear (cc34 flag clear)) if (current program, task 0 valid (ce56[prog][0] != 0)) reset task 0 loop counter (ceb0[0] = 0) start task 0 (ce56[prog][0] = 2) copy program counter for task 0 (cdde[0] = cd72[prog][0]) set can run flag (cc34 = 1) else clear can run flag (cc34 = 0) stop all tasks (ce56 flags) stop all motors (cc4d flags) set update motor state flag (cc01 flag) set program number (ccc4) to displayed program number (cc5d) call ROM 3426 (r6=&valid, sp0=#cc58) (cc58 = ptr to transfer data addr) if (valid) // process a request from pc reset power down delay counter (copy cc5e to cc12) call ROM 33b0 (r6=&data, sp0=10, sp1=&length) temp = data[0] with 0x08 bit clear if (temp is a valid request) if (temp is f7 or temp is d2) set runflag = 1 else if (data[0] != last message received (ccc1 byte)) set last message received (ccc1 byte) to data[0] set reply opcode (cd0c byte) to complement of data[0] clear 0x08 bit of data[0] set reply length (cd0b byte) to 1 set runflag = 1 set sendreply = 1 else set sendreply = 1 if (data[0] != 20 && data[0] != a4) set temp = old reply flag (cd0a byte) else clear 0x08 bit of data[0] set runflag = 1 else // process a byte code command if (sensor ready flag (cc60) == 1) increment task index (ccc0 byte) if (task index (ccc0 byte) == 0a) task index (ccc0 byte) = 0 if (task is not invalid (ce56[prog][task] != 0)) if (task is not stopped (ce56[prog][task] != 1)) if (task is paused (ce56[prog][task] == 3)) if (task wakeup delay (cc14[task] == 0)) set task to running (ce56[prog][task] = 2) if (task is running (ce56[prog][task] == 2) if (task pc (cdde[task]) < task end (cd72[prog][task+1])) data[0] = value at task pc (cdde[task]) increment task pc (cdde[task]) lenbits = data[0] & 7 if (lenbits > 5) lenbits -= 6 for (i = 1; i < lenbits + 1; i++) set data[i] = value at task pc (cdde[task]) increment task pc (cdde[task]) set runflag = 1 set sendreply = 0 else stop task (ce56[prog][task] = 1) clear can run flag (cc34 flag) set can run flag (cc34) if another task can run if (runflag) clear reply valid flag (cd0a flag) clear runflag clear nextlsb nextlsb ^= (random state (cd08 word) & 0x0002) nextlsb ^= (random state (cd08 word) & 0x0010) nextlsb ^= (random state (cd08 word) & 0x0040) nextlsb ^= (random state (cd08 word) & 0x0200) random state (cd08 word) = (random state (cd08 word) << 1) | nextlsb // switch on opcode ... execute opcode if (sendreply != 0) if (reply valid flag (cd0a flag) set) set send message (cd20 byte) to 5 if (send message flag (cd20 byte) == 5) call ROM 343e (r6=#1775, sp0=0, sp1=mesgptr (#cd0c), sp2=len (@cd0b)) if (return value of ROM call != 4c) set send message flag (cd20 byte) to 1 set 0x80 bit of fifth firmware handler dispatch state (cc04) put returnval in r6 restore registers and stack 9700 - d2 opcode handler // data is the short following the opcode of this message if (data & 1f8) temp motor counter (cc62) = 65 if (data & 0x0008) set motor 0 forward full power temporarily (cc50 = ff) else if (data & 0x0040) set motor 0 reverse full power temporarily (cc50 = f7) else set motor 0 off full power (cc50 = 4f) if (data & 0x0010) set motor 1 forward full power temporarily (cc51 = ff) else if (data & 0x0080) set motor 1 reverse full power temporarily (cc51 = 7f) else set motor 1 off full power (cc51 = 4f) if (data & 0x0020) set motor 2 forward full power temporarily (cc52 = ff) else if (data & 0x0100) set motor 2 reverse full power temporarily (cc52 = 7f) else set motor 2 off full power (cc52 = 4f) set motor handler activate bit (bit 0x80 of cc01) if (data != last d2 data (cd1e)) last d2 data (cd1e) = data switch (data) case 0x0001: last message (cd06) = 1 case 0x0002: last message (cd06) = 2 case 0x0004: last message (cd06) = 3 case 0x0200: stop all tasks, current program (ccc4) = 0, run program case 0x0400: stop all tasks, current program (ccc4) = 1, run program case 0x0800: stop all tasks, current program (ccc4) = 2, run program case 0x1000: stop all tasks, current program (ccc4) = 3, run program case 0x2000: stop all tasks, current program (ccc4) = 4, run program case 0x4000: stop all tasks case 0x8000: play two high pitch beeps endswitch bc76 - byte modify_memory_map (byte op, byte index, short len, short *addr) if op=1, set size for task number index to len if op=2, set size for sub number index to len if op=3, set size for datalog to len store the starting address in the location pointed to by addr shuffle other memory map entries as necessary returns 00 on success, 40 if not enough space operation is undefined if op is not 1, 2, or 3 bcd0 - init sixth handler call ROM 3692 (void) - set_port_6_bit_3_output_high set sixth handler dispatch state (cc05) to 1 bdde - sixth handler call ROM 36a6 - does nothing clear 0x80 bit of sixth handler dispatch state (cc05) be00 - sixth handler, cleanup call ROM 36aa - set_p6_bit_3_input |
Hardware notes: port 1 bit * - address bus LSB port 2 bit * - address bus MSB port 3 bit * - data bus ffb7: port 4 bit 0 - transmitter range (0=long, 1=short) port 4 bit 1 - on/off button input (also irq1 input) port 4 bit 2 - run button input (also irq0 input) ffba: port 5 bit 0 - transmit data (set once, to output, low) port 5 bit 1 - receive data (set once, to output, low) port 5 bit 2 - external ram power save enable ffbb: port 6 bit 0 - sensor 2 output port 6 bit 1 - sensor 1 output port 6 bit 2 - sensor 0 output port 6 bit 3 - output for sixth handler, set to 1 on init port 6 bit 4 - timer output 0, speaker port 6 bit 5 - input/output for lcd port 6 bit 6 - input/output for lcd port 6 bit 7 - timer output 1, infrared carrier ffbe: port 7 bit 0 - analog input 0, sensor 2 port 7 bit 1 - analog input 1, sensor 1 port 7 bit 2 - analog input 2, sensor 0 port 7 bit 3 - analog input 3, battery voltage port 7 bit 6 - view button input port 7 bit 7 - prgm button input A/D is used in scan mode, channel 3 (AN0-AN3), slower conversion time IRQ0 is generated by run button, but interrupt is ignored IRQ1 is generated by on/off button free-running timer used for 1 ms clock timer 0 used for sounds timer 1 used for infrared carrier Motors are memory mapped Address ranges to control motors are f000-fb7f and ff80-ff87 Of the fxxx addresses, only accesses in these ranges make it off-chip Any write to fxxx off-chip sets external motor control registers Setting 40=fwd, 80=rev, 00=stop, c0=float for motor 0 Setting 04=fwd, 08=rev, 00=stop, 0c=float for motor 1 Setting 01=fwd, 02=rev, 00=stop, 03=float for motor 2 Addresses in range ff80-ff87 accessible using shorter instructions Lego does not take advantage of this though (they control motors via f000) The memory backing f000-fb7f,ff80-ff87 works fine with one caveat The caveat is that writes affect the motor state Mark Riley was the first to bring this to my attention Ralph Hempel first suggested that port 5 bit 2 puts RAM into low power mode He discovered the problem debugging power_off with a stack in off-chip RAM Verified function of port 5 bit 2 by not powering down RAM during power off |
|
I've written a few tools for the RCX. They are described in detail on a separate page. In particular, you will find a program to send messages to the RCX and a program to download a firmware file to the RCX.
The tools are distributed as C source code. They are intended for Unix
machines. In particular, they are known to compile and run under
Linux, Solaris, and IRIX.
|
The official LEGO® MindstormsTM web page is http://www.legomindstorms.com.
If you are interested in purchasing LEGO® Mindstorms products, you might try searching for lego mindstorms at Amazon.
More RCX information, and details about how to join the lego-robotics mailing list, are available at http://www.crynwr.com/lego-robotics/. Many thanks to Russell Nelson, who coordinated the reverse engineering effort with this web site and mailing list.
A Perl script for sending messages to the RCX is at http://hamjudo.com/rcx/. One of the tools I use is a C program with similar functionality, and I must say that I've grown accustomed to the speed of typing raw hex at the RCX from a command line. Links to a number of other tools for communicating with the RCX can be found at http://www.crynwr.com/lego-robotics/.
A document describing SPIRIT.OCX, an ActiveX control which can be used to manipulate the RCX, is available at http://www.holdren.com/scott/legos/. Also, instructions for wrapping the OCX with C++ code are at this location.
Dave Baum is the author and maintainer of a simple RCX compiler and a few other utilities. You can find more information at http://www.enteract.com/~dbaum/lego/nqc/. If these tools were available for my platform, I'd almost certainly use them.
Finally, I gave a talk on reverse engineering the RCX on Oct 7 1998 for the EE380 seminar at Stanford. While I don't describe a lot of reverse engineering methodology in this document, I did describe good amount of it in my talk. My slides are available online.
Unrelated links for search engines: Unofficial Quake 3 Map Specs, Quake Sky Effect, Wavy Images in Quake, Mr. Alligator, Mister Alligator, Tyvek Wallets, MVPL, Dino plus Sheep equals Dinosheep, Dino, Sheep, Dinosheep.
Copyright © 1998, 1999 Kekoa Proudfoot. All rights reserved. | kekoa@graphics.stanford.edu |