Discussion forums
Put your questions, comments and suggestions here:
Current: RoboCommunity: Robopanda Hacks and mods
Old: Unofficial Robosapien Hacks and Mods Forum :: ROBOSAPIEN forum :: Future WowWee robotics :: RoboPanda :: Robopanda hacks
Additional information
Cartridges Contents
Dumped by Nocturnal:
Partially decoded cartridge contents available in emu_logs tarball
Downloads
SPI Read Traces
- Waveform based:
- Content: startup of training mode - 8 seconds
- Raw data file: wave_raw.bin
- Data offset: 0x1400
- format: 32 bits little-endian
- 31:16 timestamp in 12.5ns intervals
- 15:4 command id (for fifo overrun detection)
- 3 spi_cs
- 2 spi_clk
- 1 spi_si
- 0 spi_so
- Decoder: wave_decode.py
- Decoded output (wave + spi): wave_full.txt.bzip2
- Decoded output (spi only): wave_spi.txt.bzip2
- Hardware SPI decoder based:
- training
- Content: startup and operations in training mode - 352 seconds
- Raw data file: training.bin
- Data offset: 0x1400
- format: see spi_decode.py :))
- Decoder: spi_decode.py
- Decoded output: training.txt.bzip2
- Data filler: fill_data.py
- Decoded output with cartridge data filled in: training_data.txt.bzip2
- Emulator output: training.log
- Note: hardware sniffer does not records data bytes, only address and byte count, so fill_data.py
fills log with data from cartridge image
- standup
- Content: startup of training mode in face down position
- Decoded output with cartridge data filled in: standup_data.txt.bzip2
- Emulator output: standup.log
Bytecode emulator
- Emulator: emu.py
- Input files:
- Output: training.log.bzip2
- Note 1: Only control flow commands decoded for now, so for
conditional branches emulator looks at spi log for deciding flag
value (it calculates right branch address and compares it with log)
- Note 2: For SPI read, play and moving commands there are no
enought knowledge for calculating read address, so it gets looked
up in spi log too.
Cartridge MetaData
There are some essential values for decoding bytecodes:
- 0002 - address of index block (0x2000)
- index - magic (not readed)
- index + 2 - pointer to cartridge version (cpu: 8923 spi: 13246)
- index + 4 - pointer to mover scripts (cpu: 6477 spi: E8EE)
- index + 6 - offset for absolute jumps (0x000C)
- index + 8 - start address (cpu: 63CB spi: E796)
- index + A - something (01AA)
Regions of cartridge:
- 0005 to @PEND - pointers to audio chunks. Audio player reads elements of this table by index (2 + i*3) on playing start
- free space
- index to index + A - index table
- index + C to first bytecode chunk(207C in BC) - unknown table
- first bytecode chunk(207C in BC) - pointer to mover scripts - 4- CPU bytecodes
- pointer to mover scripts - 2 - Number of items in mover scripts table
- pointer to mover scripts - pointer to mover scripts + 2 * number of mover scripts - table of mover scripts entry points
- pointer to mover scripts + 2 * number of mover scripts - version - mover scripts
- version - XXX - some table
- free space
- first audio chunk - last audio chunk - audio chunks
- free space
cpu -> spi address conversion: spi_address = cpu_address * 2 + index
Known CPU Bytecodes
Programming model
CPU has stack, local registers and global memory.
Most opcodes work with values on stack (pop args, do op, push result)
Ops with local registers optimized for small values (0-15, 0-63) and mostly used for loops
Global mamory can be accessed as 16 and 32 bit values(pop/push @XXX and pop2/push2 @XXX)
There are read_spi bytecode for reading data from cartrigde
Codes by first byte
|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
0 |
push #XXXX |
|
|
|
|
|
|
|
|
|
|
|
|
cmp #XX |
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
push @XX |
push2 @XX |
|
|
pop @XX |
pop2 @XX |
|
|
|
misc commands |
|
|
|
push/pop $XX |
misc commands |
|
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
|
|
|
|
sensors |
|
|
|
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
|
|
|
|
cmp $X,#Y |
|
|
|
|
|
|
|
|
|
|
A |
|
|
|
|
|
|
|
|
set $XX,#YY |
|
|
|
|
B |
spi_read |
C |
call XXXX |
D |
E |
rjump XXXX |
rjump_e8 XXXX |
F |
rjump_e0 XXXX |
rjump XXXX,#YYYY |
Length
Most bytecodes has lenght of 2 bytes, and some 4 or 6 bytes
Most 4 byte bytecodes get second word when arg is all-ones:
- call - when arg in first word is 0x1FFF
- push #XXXX - when arg is 0x1FF
- read_spi #XXX - when arg is 0xFFF
- ...
Call bytecodes has second or third word with number of args and locals.
In traces this word gets readed twice - before and after call (first for args and locals push, second for locals pop).
Descriptions
For always up-to-date bytecode information see emu.py sources.
Bytecode decoding method
Decoding of CPU bytecodes done using cartridge emulator by writing small test images consisting of
3 sequences of code:
- stack and locals setup by push #XXXX and [push #XXXX, pop $XX] sequences
- code under test
- stack and registers dump by [read_spi #0, pop @40] and [push $xx, read_spi #0, pop @40]
and by looking at addresses of spi_reads we can get stack and registers state after tested code.
pop @40 codes used for droping result of spi_read from stack (true drop opcode is not known now)
Example of such code can be seen in test.cfg in tools tarball.
Known Mover Bytecodes
- Control FXXX
- F000 - stop mover
- F002 - blocking wait for current movement completion
- F003 XXXX - blocking wait for current movement completion but no more than X * 10 ms
- F004 - wait something
- F005 XXXX - delay for X * 10 ms
- F006 XXXX - call
- F00A - return, last return stops the mover
- F00D - wait something
- Action
- XYYZ - move motor X to position Y with speed Z???
Audio data encoding
Audio data chunk format:
- int32 - length
- int8 - codec (07 or 09)
- int8 - something (always 128)
- 32 or 48 byte chunks of data
- int16 - something (always FFFF)
- int16 - checksum?
All files coded with 07 codec, except for 2 songs in black cartridge
Audio player reads data chunk every 16ms for all codesc.
Different codecs was tried and next codecs worked:
code | initial read | next reads | bitrate |
8004 | 254+128 | 20/16ms | 10k |
8005 | 254+132 | 24/16ms | 12k |
8006 | 254+136 | 28/16ms | 14k |
8007 | 254+140 | 32/16ms | 16k |
8008 | 254+148 | 40/16ms | 20k |
8009 | 254+156 | 48/16ms | 24k |
sound for codecs 4-7 and 8-9 is very different.
4-7 is white noise-like
8-9 is R2D2-like :))
traces of test cartridges with different codecs: test_codecs.tar.gz
Wav files corresponding to some audiochunks
There is a lot of activity with audio on RoboCommunity forum.
Test cartridge image
I have done hardware emulator for cartridge and tested it with standart cartridge images - it works.
So I tried to make some cartridge :)) For now it only contains one song, required metadata and 3 commands code for starting audioplay.
For testing assumptions about index position, this cartridge has index at 0x1000 instead of 0x2000 in standart cartridges.
- image file: test.bin
- trace: test_data.txt
- emulator log: test.log in emu_logs tarball
- Note: there are problems with timestamps - because of low activity on spi bus
timestamp counter in sniffer rollovers multiple times between accesses
Hardware
For SPI sniffing I use FPGA based board ebayed 3 years ago :))
It has Xilinx XCV600E, config proms, RS232 level-shifter, dc-dc
convertors and a lot of unsoldered pins connected to fpga :))
For physical emulation of cartridge I used chunk of old PCI card with termoglued connector housings :))
for fitting into socket PCB tickness must be trimmed in half
For this project it has been upgraded with
- 32M DRAM (8Mx32) - SODIMM for old notebooks
- SD Reader - SD socket soldered off from usb cardreader and connected directly to fpga pins
- VGA interface - 3 resistors and VGA socket connected to fpga pins
As development software used Xilinx ISE WebPack.
For SD Reader used core from OpenCores
For high level tasks small CPU was developed :)) It's not
so optimized as existing cores, but it does exactly what i need :))
And having a relatively big FPGA (600k gates) greatly simplifies such
a task :))
This was one of reasons for looking at traces from bytecode standpoint
- it's really not so hard to develop and implement your own instruction
set.
HDL sources available in hwemu tarball. In current state it uses 30% of Xilinx XCV600E.
Design contains:
- DRAM controller
- VGA controller (1024x768 b/w)
- Programmable system controller
- SD Reader based on OpenCores SPIMaster project
- Hardware SPI sniffer
- SPI Rom emulator
32M of DRAM divided in 2 regions: sniffer memory (24M) and cartridge memory (8M)
Sniffer writes 64bit for each multibyte spi access, with spi usage of robopanda it gives ~20minutes of sniffing
traces and cartridge images read/written to SD card at fixed offsets (no filesystem)
Pictures
|
Robopanda with brain probe attached. I'm not sure it's exactly brain... :)) |
|
Cartridge with sniffer attachment |
|
Cartridge insides |
|
"Development" board |
|
emulator cartridge :)) |
|
|
PCB thickness adjusted |
|
Yet another probe to little panda :)) |
|