├── README.md ├── examples └── MapleMaster │ └── MapleMaster.ino ├── library.properties ├── src ├── libmaple.c ├── libmaple.h ├── libmaple16.S ├── libmaple22.S └── libmaplecommon.S └── util ├── astroboy.txt ├── maple.py ├── pulsedtr.py ├── vmu_dump.py └── vmu_flash.py /README.md: -------------------------------------------------------------------------------- 1 | # libmaple 2 | 3 | Taken from https://bitbucket.org/nfd/arduino-maple and converted into an Arduino library. 4 | 5 | ##Wiring 6 | 7 | * Red wire: PORTB0 (Pin 8 on Duemilanove/Uno; Pin 53 on Mega) 8 | * White wire: PORTB1 (Pin 9 on Duemilanove/Uno; Pin 52 on Mega) 9 | * Blue wire: +5V 10 | * Brown/Black wire: GND 11 | * Green wire: Unconnected or GND (connected to GND inside the controller) 12 | * GND (unshielded): GND 13 | 14 | ##Usage 15 | 16 | After uploading to your Arduino, run one of the Python scripts in the `util` folder. The Python scripts require the [pySerial](https://github.com/pyserial/pyserial) library (run `pip install pyserial` to install it). You may need to edit the `maple.py` file and change the serial port name to the correct serial port for your Arduino. 17 | 18 | ##Issues 19 | 20 | According to the original commit comments, the code probably doesn't work on Arduino Megas. 21 | -------------------------------------------------------------------------------- /examples/MapleMaster/MapleMaster.ino: -------------------------------------------------------------------------------- 1 | #include "libmaple.h" 2 | 3 | void setup() { 4 | Serial.begin(57600); 5 | maple_init(); 6 | } 7 | 8 | void loop() { 9 | read_maple_packet_from_serial(); 10 | maple_debug(0); 11 | 12 | if(maple_packet_dest_is_maple()) { 13 | maple_transact(); 14 | maple_debug(1); 15 | send_maple_packet_to_serial(); 16 | } else { 17 | // Debug 18 | Serial.print(1); 19 | //uart_putchar(1); 20 | } 21 | maple_debug(1); 22 | } 23 | 24 | bool read_maple_packet_from_serial() 25 | { 26 | // First byte: #bytes in packet (including header and checksum) 27 | if (Serial.available() > 0) { 28 | packet.data_len = Serial.read(); 29 | if(packet.data_len > 0) { 30 | unsigned char *data = &(packet.header[0]); 31 | int i; 32 | for(i = 0; i < packet.data_len; i++) { 33 | *data = Serial.read(); 34 | data++; 35 | } 36 | return true; 37 | } else { 38 | 39 | } 40 | } 41 | 42 | return false; 43 | } 44 | 45 | void send_maple_packet_to_serial() 46 | { 47 | Serial.write((packet.data_len_rx & 0xff00) >> 8); 48 | Serial.write(packet.data_len_rx & 0xff); 49 | if(packet.data_len_rx) { 50 | //int i; 51 | uint8_t *data = &(packet.header[0]); 52 | Serial.write(data, packet.data_len_rx); 53 | //for (i = 0; i < packet.data_len_rx; i++) { 54 | // uart_putchar(data[i]); 55 | //} 56 | } 57 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=libmaple 2 | version=1.0 3 | author=Scott Rising 4 | maintainer=Scott Rising 5 | sentence=A library to interface with the Maple Bus on a Dreamcast system. 6 | paragraph=This library enables communication across the Dreamcast's Maple Bus. 7 | category=Other 8 | url=http://example.com/ 9 | architectures=avr 10 | -------------------------------------------------------------------------------- /src/libmaple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libmaple.h" 3 | 4 | void maple_init() 5 | { 6 | // Initialise serial port 7 | //uart_init(); 8 | 9 | // Maple bus data pins as output -- NB needs corresponding changes in libMaple.S 10 | DDRB = 0xff; 11 | 12 | //puts("Hi there \n"); 13 | } 14 | 15 | bool maple_test_transact() 16 | { 17 | return true; 18 | } 19 | 20 | bool maple_transact() 21 | { 22 | unsigned char *rx_buf_end; 23 | //unsigned char *rx_buf_ptr; 24 | 25 | // debug 26 | /* 27 | packet.header[0] = 0; // Number of additional words in frame 28 | packet.header[1] = 0; // Sender address = Dreamcast 29 | packet.header[2] = 1; //(1 << 5); // Recipient address = main peripheral on port 0 30 | packet.header[3] = 1; // Command = request device information 31 | packet.data[0] = compute_checksum(0); 32 | packet.data_len = 5; 33 | */ 34 | 35 | /*packet.header[0] = (192 + 4) / 4; // Number of additional words in frame 36 | packet.header[1] = 0; // Sender address = Dreamcast 37 | packet.header[2] = 1; 38 | packet.header[3] = 12; // block write 39 | packet.data[0] = 0x4; // LCD 40 | packet.data[1] = 0; // partition 41 | packet.data[2] = 0; // phase 42 | packet.data[3] = 0; // block number 43 | packet.data[4] = 0xff; 44 | packet.data[5] = 0x0f; 45 | packet.data[6] = 0xff; 46 | packet.data[192 + 4] = compute_checksum(192 + 4); 47 | packet.data_len = 4 + 192 + 4 + 1; 48 | */ 49 | 50 | maple_tx_raw(&(packet.header[0]), packet.data_len); 51 | rx_buf_end = maple_rx_raw(&(packet.header[0])); 52 | 53 | packet.data_len_rx = (rx_buf_end - (&(packet.header[0]))); 54 | 55 | return true; 56 | 57 | // debug 58 | /*Serial.print("All done \n"); 59 | Serial.println((int)(rx_buf_end - (&(packet.header[0]))), HEX); 60 | rx_buf_ptr = (&(packet.header[0])); 61 | while(rx_buf_ptr <= rx_buf_end) { 62 | Serial.println(*rx_buf_ptr, HEX); 63 | rx_buf_ptr ++; 64 | } 65 | */ 66 | } 67 | 68 | unsigned char maple_compute_checksum(unsigned char data_bytes) 69 | { 70 | unsigned char *ptr = &(packet.header[0]); 71 | int count; 72 | unsigned char checksum = 0; 73 | 74 | for(count = 0; count < data_bytes + 4; count ++) { 75 | checksum ^= *ptr; 76 | ptr++; 77 | } 78 | return checksum; 79 | } 80 | 81 | bool maple_packet_dest_is_maple(void) 82 | { 83 | return packet.data_len > 0; 84 | } 85 | 86 | // These functions are for interacting with the serial console - stub them off for now. 87 | //bool maple_read_packet(void) { return true; } 88 | //void maple_send_packet(void) { } 89 | /* 90 | // Read packet to send from the controller. 91 | bool maple_read_packet(void) 92 | { 93 | // First byte: #bytes in packet (including header and checksum) 94 | packet.data_len = uart_getchar(); 95 | if(packet.data_len > 0) { 96 | unsigned char *data = &(packet.header[0]); 97 | int i; 98 | for(i = 0; i < packet.data_len; i++) { 99 | *data = uart_getchar(); 100 | data ++; 101 | } 102 | return true; 103 | } else { 104 | return false; 105 | } 106 | } 107 | 108 | void maple_send_packet(void) 109 | { 110 | uart_putchar((packet.data_len_rx & 0xff00) >> 8); 111 | uart_putchar(packet.data_len_rx & 0xff); 112 | if(packet.data_len_rx) { 113 | int i; 114 | uint8_t *data = &(packet.header[0]); 115 | for (i = 0; i < packet.data_len_rx; i++) { 116 | uart_putchar(data[i]); 117 | } 118 | } 119 | } 120 | */ -------------------------------------------------------------------------------- /src/libmaple.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBMAPLE_h 2 | #define LIBMAPLE_h 3 | #include "Arduino.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | #define ON 1 10 | #define OFF 0 11 | 12 | struct maplepacket { 13 | unsigned char data_len; /* Bytes: header, data, and checksum */ 14 | unsigned short data_len_rx; /* For Rx -- didn't realise we could get up to 512 bytes */ 15 | 16 | unsigned char header[4]; 17 | unsigned char data[600]; /* Our maximum packet size */ 18 | } packet; 19 | 20 | void maple_debug(int on); 21 | void maple_tx_raw(unsigned char *buf, unsigned char length); 22 | unsigned char *maple_rx_raw(unsigned char *buf); 23 | void maple_timer_test(); 24 | void maple_init(); 25 | bool maple_transact(); 26 | unsigned char maple_compute_checksum(unsigned char data_bytes); 27 | bool maple_read_packet(void); 28 | bool maple_packet_dest_is_maple(void); 29 | void maple_send_packet(void); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif -------------------------------------------------------------------------------- /src/libmaple16.S: -------------------------------------------------------------------------------- 1 | ; Functions for Maple bus support on devices with 16-bit program counters (328) 2 | #if defined(__AVR_ATmega328P__) 3 | #include 4 | 5 | ; reg usage faq: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage 6 | ; essentially we can play with regs 18 to 27 and 30 and 31. 7 | 8 | .global maple_debug 9 | .global maple_tx_raw 10 | .global maple_rx_raw 11 | .global maple_timer_test 12 | .global TIMER2_OVF_vect 13 | 14 | #define intsave r31 15 | #define intwork r20 16 | #define tmp1 r18 17 | #define tmp2 r19 18 | #define rport2 r21 19 | #define rport r30 20 | 21 | ; Maple1 is on bit 0 of PORTMAPLE -- mask of 1: 22 | #define bitmaple1 0 23 | #define maskmaple1 1 24 | ; maple5 is on bit 1 -- mask of 10: 25 | #define bitmaple5 1 26 | #define maskmaple5 2 27 | 28 | #define PORTMAPLE PORTB 29 | #define PINMAPLE PINB 30 | #define DDRMAPLE DDRB 31 | 32 | .macro maple_high pin 33 | SBI _SFR_IO_ADDR(PORTMAPLE), \pin 34 | .endm 35 | 36 | .macro maple_low pin 37 | CBI _SFR_IO_ADDR(PORTMAPLE), \pin 38 | .endm 39 | 40 | .macro debug_pin_high 41 | SBI _SFR_IO_ADDR(PORTD), 7 42 | .endm 43 | 44 | .macro debug_pin_low 45 | CBI _SFR_IO_ADDR(PORTD), 7 46 | .endm 47 | 48 | .macro debug_pin_flip 49 | IN tmp1, _SFR_IO_ADDR(PORTD) 50 | ANDI tmp1, 0x80 51 | BREQ 1f 52 | debug_pin_low 53 | RJMP 2f 54 | 1: debug_pin_high 55 | 2: 56 | .endm 57 | 58 | .macro delayquarter 59 | nop 60 | nop 61 | nop 62 | nop 63 | .endm 64 | 65 | ; Delay half a uS. CALL takes 4 cycles, RET takes 4 cycles, 66 | ; 8 cycles is half a microsecond at 16MHz, voila. 67 | delayhalf: 68 | ret 69 | 70 | maple_tx_start: 71 | maple_high bitmaple1 72 | maple_high bitmaple5 73 | delayquarter 74 | ; Start of sequence: pin 1 goes low, pin 5 goes high. 75 | maple_low bitmaple1 76 | maple_high bitmaple5 77 | delayquarter 78 | ; Toggle pin 5 four times: 1 79 | maple_low bitmaple5 80 | delayquarter 81 | maple_high bitmaple5 82 | delayquarter 83 | ; 2 84 | maple_low bitmaple5 85 | delayquarter 86 | maple_high bitmaple5 87 | delayquarter 88 | ; 3 89 | maple_low bitmaple5 90 | delayquarter 91 | maple_high bitmaple5 92 | delayquarter 93 | ; 4 94 | maple_low bitmaple5 95 | delayquarter 96 | maple_high bitmaple5 97 | delayquarter 98 | ; End of sequence: pin 1 goes high, pin 5 goes low. 99 | maple_high bitmaple1 100 | delayquarter 101 | maple_low bitmaple5 102 | delayquarter 103 | ret 104 | 105 | maple_tx_end: 106 | ; Start of sequence: pin 1 is high, pin 5 is low. 107 | maple_high bitmaple1 108 | maple_low bitmaple5 109 | delayquarter 110 | ; Pin 5 goes high for a short time. 111 | maple_high bitmaple5 112 | delayquarter 113 | ; Pin 5 goes low 114 | maple_low bitmaple5 115 | delayquarter 116 | ; Pin 1 goes low for 2 cycles 117 | maple_low bitmaple1 118 | RCALL delayhalf 119 | ; Pin 1 goes high for 2 cycles 120 | maple_high bitmaple1 121 | RCALL delayhalf 122 | ; Pin 1 goes low for 2 cycles 123 | maple_low bitmaple1 124 | RCALL delayhalf 125 | ; Pin 1 goes high for 2 cycles. 126 | maple_high bitmaple1 127 | RCALL delayhalf 128 | ; End of sequence: pin 5 goes high 129 | maple_high bitmaple5 130 | ret 131 | 132 | 133 | maple_tx_phase1: 134 | BST r18, 7 ; Read the bit 135 | BRTC 1f ; Is it set? 136 | maple_high bitmaple5 ; Yes, put the bit on the wire 137 | 1: 138 | maple_low bitmaple1 ; New data is ready 139 | delayquarter ; Wait for other end to notice 140 | maple_high bitmaple5 ; Phase complete 141 | ret 142 | 143 | maple_tx_phase2: 144 | BST r18, 7 ; Read the bit 145 | BRTC 1f ; Is it set? 146 | maple_high bitmaple1 ; Set it 147 | 1: 148 | maple_low bitmaple5 ; Notify 149 | delayquarter ; Wait for other end to notice 150 | maple_high bitmaple1 ; Phase complete 151 | ret 152 | 153 | ; r22: length 154 | ; r23: unused 155 | ; r24: low order bit of buffer pointer 156 | ; r25: high order bit of buffer pointer 157 | maple_tx_data: 158 | MOVW r26, r24 ; X register <- the buffer 159 | 160 | _maple_tx_next_byte: 161 | LD r18, X+ ; Next byte of data into r18 162 | 163 | ; put the byte on the bus one bit at a time. 164 | RCALL maple_tx_phase1 ; bit 7 165 | LSL r18 166 | RCALL maple_tx_phase2 ; bit 6 167 | LSL r18 168 | RCALL maple_tx_phase1 ; bit 5 169 | LSL r18 170 | RCALL maple_tx_phase2 ; bit 4 171 | LSL r18 172 | RCALL maple_tx_phase1 ; bit 3 173 | LSL r18 174 | RCALL maple_tx_phase2 ; bit 2 175 | LSL r18 176 | RCALL maple_tx_phase1 ; bit 1 177 | LSL r18 178 | RCALL maple_tx_phase2 ; bit 0 179 | 180 | DEC r22 ; Are we done yet? 181 | BRNE _maple_tx_next_byte ; No, get the next character 182 | 183 | ret 184 | 185 | ; r22: length (1 byte) 186 | ; r23: unused 187 | ; r24: low order bit of buffer pointer 188 | ; r25: high order bit of buffer pointer 189 | 190 | ; Returns pointer to final character stored in r25:r24 191 | maple_tx_raw: 192 | ; Set pins to output for transmission 193 | SBI _SFR_IO_ADDR(DDRMAPLE), bitmaple1 194 | SBI _SFR_IO_ADDR(DDRMAPLE), bitmaple5 195 | 196 | 197 | RCALL maple_tx_start 198 | RCALL maple_tx_data 199 | RCALL maple_tx_end 200 | 201 | ; Tx over: set pins to input and enable pull-up resistors. 202 | SBI _SFR_IO_ADDR(PORTMAPLE), bitmaple1 203 | SBI _SFR_IO_ADDR(PORTMAPLE), bitmaple5 204 | CBI _SFR_IO_ADDR(DDRMAPLE), bitmaple1 205 | CBI _SFR_IO_ADDR(DDRMAPLE), bitmaple5 206 | 207 | ret 208 | 209 | ; Watchdog timer 210 | .macro timer_init 211 | CLI 212 | 213 | STS TIMSK2, r1 ; Normal mode (no PWM) 214 | 215 | STS TCCR2A, r1 ; Normal mode (no PWM) 216 | 217 | LDI r18, 1 << CS22 | 1 << CS21 | 1 << CS20 218 | STS TCCR2B, r18 ; Timer increments every 1024 cycles 219 | ; and overflows every 1024 * 256 cycles, 220 | ; or about 61 times a second at 16MHz 221 | 222 | STS TCNT2, r1 ; Timer count 223 | 224 | LDI r18, 1 ; TIFR2[0] -> 1 to clear 225 | STS TIFR2, r18 ; overflow irq flag 226 | 227 | SEI 228 | .endm 229 | 230 | .macro timer_on 231 | LDI r18, 1 << TOIE2 232 | STS TIMSK2, r18 233 | .endm 234 | 235 | .macro timer_off 236 | STS TIMSK2, r1 237 | .endm 238 | 239 | .macro timer_reset 240 | STS TCNT2, r1 ; Count register -> 0 241 | .endm 242 | 243 | ; Watchdog timer interrupt routine 244 | ; When this fires, it returns to r25:r24, effectively doing 245 | ; a longjmp(). 246 | TIMER2_OVF_vect: 247 | IN intsave, _SFR_IO_ADDR(SREG) ; Save registers; 248 | 249 | POP intwork ; Remove the old return address 250 | POP intwork ; Both words 251 | PUSH r24 ; Replace it with the new one 252 | PUSH r25 ; Both words 253 | STS TIMSK2, r1 ; Disable the timer 254 | 255 | OUT _SFR_IO_ADDR(SREG), intsave ; Restore registers 256 | reti 257 | 258 | ; Reading 259 | .macro _rx_read clockbit 260 | ; Version 1: 261 | ; Worst-case scenario here: the port is set halfway through the IN -- total 262 | ; cost is most of the IN, the BST, 2 cycles for the BRTS, plus the next IN, 263 | ; for a total of 5 cycles, which is sometimes out of tolerance. 264 | ;1: IN rport, _SFR_IO_ADDR(PINMAPLE) ; 1 265 | ; BST rport, \clockbit ; 1 266 | ; BRTS 1b ; 1 (if fall through) 267 | 268 | ; Version 2: 269 | ; This works a little better because by the time we detect the falling edge 270 | ; we already have the value. 271 | 1: IN rport, _SFR_IO_ADDR(PINMAPLE) 272 | SBIC _SFR_IO_ADDR(PINMAPLE), \clockbit 273 | RJMP 1b 274 | .endm 275 | 276 | .macro _rx_store reg srcbit destbit 277 | BST \reg, \srcbit ; 1 278 | BLD r18, \destbit ; 1 279 | ;1: IN \reg, _SFR_IO_ADDR(PINMAPLE) 280 | ; BST \reg, \srcbit 281 | ; BRTC 1b 282 | .endm 283 | 284 | maple_rx_raw: 285 | MOVW r26, r24 ; X register <- the buffer 286 | 287 | ; Set up our bailout routine: r25:24 point to an address to "reti" to. 288 | ldi r25, hi8(pm(_maple_rx_raw_watchdog)) 289 | ldi r24, lo8(pm(_maple_rx_raw_watchdog)) 290 | 291 | LDS r18, TIMSK0 ; disable Arduino interrupts 292 | PUSH r18 ; to re-enable later 293 | STS TIMSK0, r1 294 | 295 | timer_init ; Set up our watchdog timer 296 | 297 | ; Tell the device it can start sending data. (this is from the tx lead-out) 298 | ; maple_high bitmaple5 299 | 300 | ; Wait for a start sequence 301 | timer_on ; rx will start soon 302 | 303 | 1: IN rport2, _SFR_IO_ADDR(PINMAPLE) 304 | BST rport2, bitmaple1 ; maple1 305 | BRTC 1b ; must be high initially 306 | 307 | 2: IN rport2, _SFR_IO_ADDR(PINMAPLE) 308 | BST rport2, bitmaple1 309 | BRTS 2b ; must be low to continue 310 | 311 | 3: IN rport2, _SFR_IO_ADDR(PINMAPLE) 312 | BST rport2, bitmaple1 ; maple1 313 | BRTC 3b ; must be high now 314 | 315 | _rx_read bitmaple1 316 | _rx_loop: 317 | _rx_store rport bitmaple5 7 318 | _rx_read bitmaple5 319 | _rx_store rport bitmaple1 6 320 | _rx_read bitmaple1 321 | _rx_store rport bitmaple5 5 322 | _rx_read bitmaple5 323 | _rx_store rport bitmaple1 4 324 | _rx_read bitmaple1 325 | timer_reset ; pat the watchdog 326 | _rx_store rport bitmaple5 3 327 | _rx_read bitmaple5 328 | _rx_store rport bitmaple1 2 329 | _rx_read bitmaple1 330 | _rx_store rport bitmaple5 1 331 | _rx_read bitmaple5 332 | _rx_store rport bitmaple1 0 ; 2 333 | ST X+, r18 334 | _rx_read bitmaple1 335 | RJMP _rx_loop 336 | 337 | _maple_rx_raw_watchdog: ; Watchdog bailed us out. We're done! 338 | ; debug_pin_high 339 | POP r18 ; 340 | STS TIMSK0, r18 ; re-enable Arduino interrupts 341 | 342 | MOVW r24, r26 ; end of buffer <- X register 343 | 344 | ret 345 | 346 | maple_timer_test: 347 | ; Set up our bailout routine: r25:24 point to an address to "reti" to. 348 | ldi r25, hi8(pm(maple_timer_test_watchdog)) 349 | ldi r24, lo8(pm(maple_timer_test_watchdog)) 350 | 351 | ; Set up the timer 352 | timer_init 353 | timer_on 354 | 355 | 1: 356 | nop 357 | rjmp 1b 358 | 359 | maple_timer_test_watchdog: 360 | ; Timer got us! 361 | timer_off 362 | 363 | ret; 364 | 365 | ; Turn the led on if r24 (first int parameter) is 1, off if r24 is 0. 366 | maple_debug: 367 | IN r18, _SFR_IO_ADDR(PORTB) 368 | bst r24, 0 ; first parameter: on or off? 369 | bld r18, 7 ; bit 5 on duemilanove, bit 7 on the mega 370 | out _SFR_IO_ADDR(PORTB), r18 ; on port B 371 | ret 372 | 373 | #endif -------------------------------------------------------------------------------- /src/libmaple22.S: -------------------------------------------------------------------------------- 1 | ; Functions for Maple bus support on devices with 22-bit program counters (Mega) 2 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 3 | #include 4 | 5 | ; reg usage faq: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage 6 | ; essentially we can play with regs 18 to 27 and 30 and 31. 7 | 8 | .global maple_debug 9 | .global maple_tx_raw 10 | .global maple_rx_raw 11 | .global maple_timer_test 12 | .global TIMER2_OVF_vect 13 | 14 | #define intsave r31 15 | #define intwork r20 16 | #define tmp1 r18 17 | #define tmp2 r19 18 | #define rport2 r21 19 | #define rport r30 20 | 21 | ; Maple1 is on bit 0 of PORTMAPLE -- mask of 1: 22 | #define bitmaple1 0 23 | #define maskmaple1 1 24 | ; maple5 is on bit 1 -- mask of 10: 25 | #define bitmaple5 1 26 | #define maskmaple5 2 27 | 28 | #define PORTMAPLE PORTB 29 | #define PINMAPLE PINB 30 | #define DDRMAPLE DDRB 31 | 32 | .macro maple_high pin 33 | SBI _SFR_IO_ADDR(PORTMAPLE), \pin 34 | .endm 35 | 36 | .macro maple_low pin 37 | CBI _SFR_IO_ADDR(PORTMAPLE), \pin 38 | .endm 39 | 40 | .macro debug_pin_high 41 | SBI _SFR_IO_ADDR(PORTD), 7 42 | .endm 43 | 44 | .macro debug_pin_low 45 | CBI _SFR_IO_ADDR(PORTD), 7 46 | .endm 47 | 48 | .macro debug_pin_flip 49 | IN tmp1, _SFR_IO_ADDR(PORTD) 50 | ANDI tmp1, 0x80 51 | BREQ 1f 52 | debug_pin_low 53 | RJMP 2f 54 | 1: debug_pin_high 55 | 2: 56 | .endm 57 | 58 | .macro delayquarter 59 | nop 60 | nop 61 | nop 62 | nop 63 | .endm 64 | 65 | .macro delayhalf 66 | nop 67 | nop 68 | nop 69 | nop 70 | nop 71 | nop 72 | nop 73 | nop 74 | .endm 75 | 76 | maple_tx_start: 77 | maple_high bitmaple1 78 | maple_high bitmaple5 79 | delayquarter 80 | ; Start of sequence: pin 1 goes low, pin 5 goes high. 81 | maple_low bitmaple1 82 | maple_high bitmaple5 83 | delayquarter 84 | ; Toggle pin 5 four times: 1 85 | maple_low bitmaple5 86 | delayquarter 87 | maple_high bitmaple5 88 | delayquarter 89 | ; 2 90 | maple_low bitmaple5 91 | delayquarter 92 | maple_high bitmaple5 93 | delayquarter 94 | ; 3 95 | maple_low bitmaple5 96 | delayquarter 97 | maple_high bitmaple5 98 | delayquarter 99 | ; 4 100 | maple_low bitmaple5 101 | delayquarter 102 | maple_high bitmaple5 103 | delayquarter 104 | ; End of sequence: pin 1 goes high, pin 5 goes low. 105 | maple_high bitmaple1 106 | delayquarter 107 | maple_low bitmaple5 108 | delayquarter 109 | ret 110 | 111 | maple_tx_end: 112 | ; Start of sequence: pin 1 is high, pin 5 is low. 113 | maple_high bitmaple1 114 | maple_low bitmaple5 115 | delayquarter 116 | ; Pin 5 goes high for a short time. 117 | maple_high bitmaple5 118 | delayquarter 119 | ; Pin 5 goes low 120 | maple_low bitmaple5 121 | delayquarter 122 | ; Pin 1 goes low for 2 cycles 123 | maple_low bitmaple1 124 | delayhalf 125 | ; Pin 1 goes high for 2 cycles 126 | maple_high bitmaple1 127 | delayhalf 128 | ; Pin 1 goes low for 2 cycles 129 | maple_low bitmaple1 130 | delayhalf 131 | ; Pin 1 goes high for 2 cycles. 132 | maple_high bitmaple1 133 | delayhalf 134 | ; End of sequence: pin 5 goes high 135 | maple_high bitmaple5 136 | ret 137 | 138 | 139 | maple_tx_phase1: 140 | BST r18, 7 ; Read the bit 141 | BRTC 1f ; Is it set? 142 | maple_high bitmaple5 ; Yes, put the bit on the wire 143 | 1: 144 | maple_low bitmaple1 ; New data is ready 145 | delayquarter ; Wait for other end to notice 146 | maple_high bitmaple5 ; Phase complete 147 | ret 148 | 149 | maple_tx_phase2: 150 | BST r18, 7 ; Read the bit 151 | BRTC 1f ; Is it set? 152 | maple_high bitmaple1 ; Set it 153 | 1: 154 | maple_low bitmaple5 ; Notify 155 | delayquarter ; Wait for other end to notice 156 | maple_high bitmaple1 ; Phase complete 157 | ret 158 | 159 | ; r22: length 160 | ; r23: unused 161 | ; r24: low order bit of buffer pointer 162 | ; r25: high order bit of buffer pointer 163 | maple_tx_data: 164 | MOVW r26, r24 ; X register <- the buffer 165 | 166 | _maple_tx_next_byte: 167 | LD r18, X+ ; Next byte of data into r18 168 | 169 | ; put the byte on the bus one bit at a time. 170 | RCALL maple_tx_phase1 ; bit 7 171 | LSL r18 172 | RCALL maple_tx_phase2 ; bit 6 173 | LSL r18 174 | RCALL maple_tx_phase1 ; bit 5 175 | LSL r18 176 | RCALL maple_tx_phase2 ; bit 4 177 | LSL r18 178 | RCALL maple_tx_phase1 ; bit 3 179 | LSL r18 180 | RCALL maple_tx_phase2 ; bit 2 181 | LSL r18 182 | RCALL maple_tx_phase1 ; bit 1 183 | LSL r18 184 | RCALL maple_tx_phase2 ; bit 0 185 | 186 | DEC r22 ; Are we done yet? 187 | BRNE _maple_tx_next_byte ; No, get the next character 188 | 189 | ret 190 | 191 | ; r22: length (1 byte) 192 | ; r23: unused 193 | ; r24: low order bit of buffer pointer 194 | ; r25: high order bit of buffer pointer 195 | 196 | ; Returns pointer to final character stored in r25:r24 197 | maple_tx_raw: 198 | ; Set pins to output for transmission 199 | SBI _SFR_IO_ADDR(DDRMAPLE), bitmaple1 200 | SBI _SFR_IO_ADDR(DDRMAPLE), bitmaple5 201 | 202 | 203 | RCALL maple_tx_start 204 | RCALL maple_tx_data 205 | RCALL maple_tx_end 206 | 207 | ; Tx over: set pins to input and enable pull-up resistors. 208 | SBI _SFR_IO_ADDR(PORTMAPLE), bitmaple1 209 | SBI _SFR_IO_ADDR(PORTMAPLE), bitmaple5 210 | CBI _SFR_IO_ADDR(DDRMAPLE), bitmaple1 211 | CBI _SFR_IO_ADDR(DDRMAPLE), bitmaple5 212 | 213 | ret 214 | 215 | ; Watchdog timer 216 | .macro timer_init 217 | CLI 218 | 219 | STS TIMSK2, r1 ; Normal mode (no PWM) 220 | 221 | STS TCCR2A, r1 ; Normal mode (no PWM) 222 | 223 | LDI r18, 1 << CS22 | 1 << CS21 | 1 << CS20 224 | STS TCCR2B, r18 ; Timer increments every 1024 cycles 225 | ; and overflows every 1024 * 256 cycles, 226 | ; or about 61 times a second at 16MHz 227 | 228 | STS TCNT2, r1 ; Timer count 229 | 230 | LDI r18, 1 ; TIFR2[0] -> 1 to clear 231 | STS TIFR2, r18 ; overflow irq flag 232 | 233 | SEI 234 | .endm 235 | 236 | .macro timer_on 237 | LDI r18, 1 << TOIE2 238 | STS TIMSK2, r18 239 | .endm 240 | 241 | .macro timer_off 242 | STS TIMSK2, r1 243 | .endm 244 | 245 | .macro timer_reset 246 | STS TCNT2, r1 ; Count register -> 0 247 | .endm 248 | 249 | ; Reading 250 | .macro _rx_read clockbit 251 | ; Version 1: 252 | ; Worst-case scenario here: the port is set halfway through the IN -- total 253 | ; cost is most of the IN, the BST, 2 cycles for the BRTS, plus the next IN, 254 | ; for a total of 5 cycles, which is sometimes out of tolerance. 255 | 1: IN rport, _SFR_IO_ADDR(PINMAPLE) ; 1 256 | BST rport, \clockbit ; 1 257 | BRTS 1b ; 1 (if fall through) 258 | 259 | ; Version 2: 260 | ; This works a little better because by the time we detect the falling edge 261 | ; we already have the value. 262 | ;1: IN rport, _SFR_IO_ADDR(PINMAPLE) 263 | ; SBIC _SFR_IO_ADDR(PINMAPLE), \clockbit 264 | ; RJMP 1b 265 | .endm 266 | 267 | .macro _rx_store reg srcbit destbit 268 | BST \reg, \srcbit ; 1 269 | BLD r18, \destbit ; 1 270 | ;1: IN \reg, _SFR_IO_ADDR(PINMAPLE) 271 | ; BST \reg, \srcbit 272 | ; BRTC 1b 273 | .endm 274 | 275 | maple_rx_raw: 276 | MOVW r26, r24 ; X register <- the buffer 277 | 278 | ; Set up our bailout routine: r25:24 point to an address to "reti" to. 279 | ldi r25, hh8(pm(_maple_rx_raw_watchdog)) 280 | ldi r24, hi8(pm(_maple_rx_raw_watchdog)) 281 | ldi r23, lo8(pm(_maple_rx_raw_watchdog)) 282 | 283 | LDS r18, TIMSK0 ; disable Arduino interrupts 284 | PUSH r18 ; to re-enable later 285 | STS TIMSK0, r1 286 | 287 | timer_init ; Set up our watchdog timer 288 | 289 | ; Tell the device it can start sending data. (this is from the tx lead-out) 290 | ; maple_high bitmaple5 291 | 292 | ; Wait for a start sequence 293 | timer_on ; rx will start soon 294 | 295 | 1: IN rport2, _SFR_IO_ADDR(PINMAPLE) 296 | BST rport2, bitmaple1 ; maple1 297 | BRTC 1b ; must be high initially 298 | 299 | 2: IN rport2, _SFR_IO_ADDR(PINMAPLE) 300 | BST rport2, bitmaple1 301 | BRTS 2b ; must be low to continue 302 | 303 | 3: IN rport2, _SFR_IO_ADDR(PINMAPLE) 304 | BST rport2, bitmaple1 ; maple1 305 | BRTC 3b ; must be high now 306 | 307 | _rx_read bitmaple1 308 | _rx_loop: 309 | _rx_store rport bitmaple5 7 310 | _rx_read bitmaple5 311 | _rx_store rport bitmaple1 6 312 | _rx_read bitmaple1 313 | _rx_store rport bitmaple5 5 314 | _rx_read bitmaple5 315 | _rx_store rport bitmaple1 4 316 | _rx_read bitmaple1 317 | timer_reset ; pat the watchdog 318 | _rx_store rport bitmaple5 3 319 | _rx_read bitmaple5 320 | _rx_store rport bitmaple1 2 321 | _rx_read bitmaple1 322 | _rx_store rport bitmaple5 1 323 | _rx_read bitmaple5 324 | _rx_store rport bitmaple1 0 ; 2 325 | ST X+, r18 326 | _rx_read bitmaple1 327 | RJMP _rx_loop 328 | 329 | _maple_rx_raw_watchdog: ; Watchdog bailed us out. We're done! 330 | ; debug_pin_high 331 | POP r18 ; 332 | STS TIMSK0, r18 ; re-enable Arduino interrupts 333 | 334 | MOVW r24, r26 ; end of buffer <- X register 335 | 336 | ret 337 | 338 | ; Watchdog timer interrupt routine 339 | ; When this fires, it returns to r25:r24, effectively doing 340 | ; a longjmp(). 341 | TIMER2_OVF_vect: 342 | IN intsave, _SFR_IO_ADDR(SREG) ; Save registers; 343 | 344 | POP intwork ; Remove the old return address 345 | POP intwork ; all three words 346 | POP intwork ; 347 | PUSH r23 ; Store the new return address 348 | PUSH r24 ; all three words 349 | PUSH r25 ; 350 | STS TIMSK2, r1 ; Disable the timer 351 | 352 | OUT _SFR_IO_ADDR(SREG), intsave ; Restore registers 353 | reti 354 | 355 | maple_timer_test: 356 | ; Set up our bailout routine: r25:24 point to an address to "reti" to. 357 | ; broken for 22 bit pc 358 | ldi r25, hh8(pm(maple_timer_test_watchdog)) 359 | ldi r24, hi8(pm(maple_timer_test_watchdog)) 360 | ldi r23, lo8(pm(maple_timer_test_watchdog)) 361 | 362 | ; Set up the timer 363 | timer_init 364 | timer_on 365 | 366 | 1: 367 | nop 368 | rjmp 1b 369 | 370 | maple_timer_test_watchdog: 371 | ; Timer got us! 372 | timer_off 373 | 374 | ret; 375 | 376 | ; Turn the led on if r24 (first int parameter) is 1, off if r24 is 0. 377 | maple_debug: 378 | IN r18, _SFR_IO_ADDR(PORTB) 379 | bst r24, 0 ; first parameter: on or off? 380 | bld r18, 7 ; bit 5 on duemilanove, bit 7 on the mega 381 | out _SFR_IO_ADDR(PORTB), r18 ; on port B 382 | ret 383 | 384 | #endif -------------------------------------------------------------------------------- /src/libmaplecommon.S: -------------------------------------------------------------------------------- 1 | ; Common functions for 16- and 22-bit program counter devices. 2 | -------------------------------------------------------------------------------- /util/astroboy.txt: -------------------------------------------------------------------------------- 1 | xxxxxxxxxxx x 2 | xxxxxxxxxxxxxx x 3 | xxxxxxxxxxxxxxxxx x 4 | xxxxxxxxxxxxxxxxxxxx 5 | xxxxxxxxxxxxxxxxxxxxxxx 6 | xxxxxxxxxxxxxxxxxxxxxxxxx 7 | xxxxxxxxxxxxxxxxxxxxxxxxxxx 8 | xxxxxxxxxxxxxxxxxxxxxxxxxxx 9 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10 | xxxx xxxxxxx xxxxxxxxxxxxxxxxx 11 | xx xxxxxx xxxxxxxxxxxxxxxx 12 | xx xx xxxx xxxxxxxxxxxxxxxx 13 | xx x x xx xxx xxxxxxxxx xxxx 14 | xx x x x x xxxxxxxxx xxx 15 | xx x x x x xxxxxxxx xxxxx 16 | xx x x xxxxxxxxx xxxx 17 | x xx x xxxxxxx xxx 18 | x xxxxx xxxx xxxxxxx xxx 19 | x xxxxx xxxxx xxxxxxxxxxxx 20 | x x xxx x xxxx xxxxxxxxxxxxx 21 | x x xx x xx xxxxxxxxxxxxxxxxxxxx 22 | x x xx x x xx x xxxxxxxxxxxxxxxxxx 23 | x x xxxx x xxxx xxxxxxxxxxxxxxxxx 24 | x x xxx x xxx xxxxxxxxxxxxxxxx 25 | x x xxx x xxx xxxxxxxxxxxxxxxxxx 26 | x x x xxx xxxxxxxxxxxxxxx 27 | x x x x xxxxxxxxxxxxxx 28 | x x x x x xxxx xx 29 | x xxx x x xx xx x 30 | x xxx x x 31 | x xx xxx x 32 | x x x x x 33 | -------------------------------------------------------------------------------- /util/maple.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import struct 5 | import select 6 | import serial 7 | import time 8 | 9 | #PORT = '/dev/ttyUSB0' # Linux 10 | PORT = 'COM5' # Windows 11 | #PORT = '/dev/tty.usbserial-A700ekGi' # OS X (or similar) 12 | #PORT = '/dev/tty.usbmodemfa131' 13 | SPEED = 57600 14 | 15 | # Device function codes 16 | FN_CONTROLLER = 1 17 | FN_MEMORY_CARD = 2 18 | FN_LCD = 0x4 19 | FN_CLOCK = 0x8 20 | FN_MICROPHONE = 0x10 21 | FN_AR_GUN = 0x20 22 | FN_KEYBOARD = 0x40 23 | FN_LIGHT_GUN = 0x80 24 | FN_PURU_PURU = 0x100 25 | FN_MOUSE = 0x200 26 | 27 | FN_CODE_MAP = {FN_CONTROLLER: 'CONTROLLER', FN_MEMORY_CARD: "MEMORY_CARD", 28 | FN_LCD: "LCD", FN_CLOCK: "CLOCK", FN_MICROPHONE: "MICROPHONE", 29 | FN_AR_GUN: "AR_GUN", FN_KEYBOARD: "KEYBOARD", FN_LIGHT_GUN: "LIGHT_GUN", 30 | FN_PURU_PURU: "PURU_PURU", FN_MOUSE: "MOUSE"} 31 | 32 | # Device commands 33 | CMD_INFO = 0x01 34 | CMD_INFO_EXT = 0x02 35 | CMD_RESET = 0x03 36 | CMD_SHUTDOWN = 0x04 37 | CMD_INFO_RESP = 0x05 38 | CMD_INFO_EXT_RESP = 0x06 39 | CMD_ACK_RESP = 0x07 40 | CMD_XFER_RESP = 0x08 41 | CMD_GET_COND = 0x09 42 | CMD_GET_MEMINFO = 0x0A 43 | CMD_READ = 0x0B 44 | CMD_WRITE = 0x0C 45 | CMD_SET_COND = 0x0E 46 | CMD_NO_RESP = 0xFF 47 | CMD_UNSUP_FN_RESP = 0xFE 48 | CMD_UNKNOWN_RESP = 0xFD 49 | CMD_RESEND_RESP = 0xFC 50 | CMD_FILE_ERR_RESP = 0xFB 51 | 52 | 53 | # Hardcoded recipient addresses. 54 | # Controller, main peripheral, port A 55 | ADDRESS_CONTROLLER = 1 << 5 56 | # Controller, first sub-peripheral, port A 57 | ADDRESS_PERIPH1 = 1 58 | # Dreamcast, magic value, port A 59 | ADDRESS_DC = 0 60 | 61 | def log(txt): 62 | print txt 63 | 64 | def debug_hex(packet): 65 | display = ['%02x' % (ord(item)) for item in packet] 66 | return ''.join(display) 67 | 68 | def debug_txt(packet): 69 | display = [c if ord(c) >= ord(' ') and ord(c) <= ord('z') else '.' for c in packet] 70 | return ''.join(display) 71 | 72 | def decode_func_codes(code): 73 | names = [] 74 | for candidate in sorted(FN_CODE_MAP.keys()): 75 | if code & candidate: 76 | names.append(FN_CODE_MAP[candidate]) 77 | 78 | return names 79 | 80 | def swapwords(s): 81 | swapped = [] 82 | while s: 83 | swapped.append(s[:4][-1::-1]) 84 | s = s[4:] 85 | return ''.join(swapped) 86 | 87 | def print_header(data): 88 | words = ord(data[0]) 89 | sender = ord(data[1]) 90 | recipient = ord(data[2]) 91 | command = ord(data[3]) 92 | print "Command %x sender %x recipient %x length %x" % (command, recipient, sender, words) 93 | 94 | def get_command(data): 95 | if data: 96 | return ord(data[3]) 97 | else: 98 | return None 99 | 100 | BUTTONS = ["C", "B", "A", "START", "UP", "DOWN", "LEFT", "RIGHT", 101 | "Z", "Y", "X", "D", "UP2", "DOWN2", "LEFT2", "RIGHT2"] 102 | def print_controller_info(data): 103 | print_header(data) 104 | data = data[4:] # Header 105 | data = data[4:] # Func 106 | data = data[:-1] # CRC 107 | data = swapwords(data) 108 | buttons = struct.unpack("HH", info_bytes[108:112]) 193 | print "Device information:" 194 | print "raw:", debug_hex(info_bytes), len(info_bytes) 195 | print "Functions :", ', '.join(decode_func_codes(func)) 196 | print "Periph 1 :", hex(func_data_0) 197 | print "Periph 2 :", hex(func_data_1) 198 | print "Periph 3 :", hex(func_data_2) 199 | #print "Area :", ord(area_code) 200 | #print "Direction? :", ord(connector_dir) 201 | print "Name :", debug_txt(swapwords(product_name)) 202 | print "License :", debug_txt(swapwords(product_license)) 203 | # These are in tenths of a milliwatt, according to the patent: 204 | print "Power :", standby_power 205 | print "Power max :", max_power 206 | return True 207 | 208 | def getCond(self, address, function): 209 | data = struct.pack("H", num_bytes)[0] 298 | return self.handle.read(num_bytes) 299 | else: 300 | return None 301 | 302 | def test(): 303 | if len(sys.argv) == 2: 304 | image_fn = sys.argv[1] 305 | else: 306 | image_fn = 'astroboy.txt' 307 | image = load_image(image_fn) 308 | 309 | bus = MapleProxy() 310 | 311 | # Nothing will work before you do a deviceInfo on the controller. 312 | # I guess this forces the controller to enumerate its devices. 313 | found_controller = bus.deviceInfo(ADDRESS_CONTROLLER) 314 | if not found_controller: 315 | print "Couldn't find controller." 316 | return 317 | 318 | found_vmu = bus.deviceInfo(ADDRESS_PERIPH1) 319 | if found_vmu: 320 | bus.writeLCD(ADDRESS_PERIPH1, image) 321 | 322 | print "Play with the controller. Hit ctrl-c when done." 323 | while 1: 324 | print_controller_info(bus.readController(ADDRESS_CONTROLLER)) 325 | time.sleep(1) 326 | 327 | if __name__ == '__main__': 328 | test() 329 | 330 | -------------------------------------------------------------------------------- /util/pulsedtr.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import time 3 | import sys 4 | 5 | if len(sys.argv) != 2: 6 | print "Please specify a port, e.g. %s /dev/ttyUSB0" % sys.argv[0] 7 | sys.exit(-1) 8 | 9 | ser = serial.Serial(sys.argv[1]) 10 | ser.setDTR(1) 11 | time.sleep(0.5) 12 | ser.setDTR(0) 13 | ser.close() 14 | 15 | -------------------------------------------------------------------------------- /util/vmu_dump.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import maple 4 | 5 | NUM_BLOCKS = 256 6 | 7 | def read_vmu(handle): 8 | bus = maple.MapleProxy() 9 | 10 | # Quick bus enumeration 11 | bus.deviceInfo(maple.ADDRESS_CONTROLLER) 12 | bus.deviceInfo(maple.ADDRESS_PERIPH1) 13 | 14 | bus.getCond(maple.ADDRESS_CONTROLLER, maple.FN_CONTROLLER) 15 | bus.getCond(maple.ADDRESS_PERIPH1, maple.FN_CLOCK) 16 | bus.getMemInfo(maple.ADDRESS_PERIPH1) 17 | 18 | for block_num in range(NUM_BLOCKS): 19 | sys.stdout.write('.') 20 | sys.stdout.flush() 21 | data = bus.readFlash(maple.ADDRESS_PERIPH1, 0, 0) 22 | handle.write(data) 23 | 24 | if __name__ == '__main__': 25 | handle = open(sys.argv[1], 'wb') 26 | read_vmu(handle) 27 | handle.close() 28 | sys.exit(0) 29 | 30 | 31 | -------------------------------------------------------------------------------- /util/vmu_flash.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import maple 4 | 5 | DUMP_SIZE = 128 * 1024 6 | WRITE_SIZE = 128 7 | BLOCK_SIZE = 512 8 | 9 | def write_vmu(handle): 10 | bus = maple.MapleProxy() 11 | 12 | # Quick bus enumeration 13 | bus.deviceInfo(maple.ADDRESS_CONTROLLER) 14 | bus.deviceInfo(maple.ADDRESS_PERIPH1) 15 | 16 | bus.getMemInfo(maple.ADDRESS_PERIPH1) 17 | 18 | print "Writing 256 blocks..." 19 | for block_num in range(DUMP_SIZE / BLOCK_SIZE): 20 | sys.stdout.write('.') 21 | sys.stdout.flush() 22 | orig_data = bus.readFlash(maple.ADDRESS_PERIPH1, block_num, 0) 23 | target_data = handle.read(BLOCK_SIZE) 24 | if orig_data != target_data: 25 | for phase_num in range(BLOCK_SIZE / WRITE_SIZE): 26 | #print block_num, phase_num 27 | data = target_data[phase_num * WRITE_SIZE : (phase_num + 1) * WRITE_SIZE] 28 | bus.writeFlash(maple.ADDRESS_PERIPH1, block_num, phase_num, data) 29 | new_data = bus.readFlash(maple.ADDRESS_PERIPH1, block_num, 0) 30 | if new_data != target_data: 31 | print "Problem: data mis-match" 32 | print maple.debug_hex(target_data) 33 | print maple.debug_hex(new_data) 34 | return 35 | 36 | print 37 | # Do a dummy read of one block to turn off the transferring-data icon 38 | bus.readFlash(maple.ADDRESS_PERIPH1, 0, 0) 39 | 40 | def read_vmu(): 41 | bus = maple.MapleProxy() 42 | 43 | # Quick bus enumeration 44 | bus.deviceInfo(maple.ADDRESS_CONTROLLER) 45 | bus.deviceInfo(maple.ADDRESS_PERIPH1) 46 | 47 | bus.getCond(maple.ADDRESS_CONTROLLER, maple.FN_CONTROLLER) 48 | bus.getCond(maple.ADDRESS_PERIPH1, maple.FN_CLOCK) 49 | bus.getMemInfo(maple.ADDRESS_PERIPH1) 50 | 51 | print bus.readFlash(maple.ADDRESS_PERIPH1, 0, 0) 52 | 53 | 54 | def open_vmu_dump(fn): 55 | # Filename must be 128k and openable 56 | try: 57 | file_size = os.path.getsize(fn) 58 | except os.error: 59 | print >>sys.stderr, "Can't access %s" % (fn) 60 | return None 61 | 62 | if file_size != DUMP_SIZE: 63 | print >>sys.stderr, "%s should be %dk" % (DUMP_SIZE / 1024) 64 | return None 65 | 66 | return open(fn, 'rb') 67 | 68 | def usage(): 69 | print "Usage: %s filename" % (sys.argv[0]) 70 | 71 | if __name__ == '__main__': 72 | #read_vmu() 73 | #sys.exit(0) 74 | 75 | if len(sys.argv) != 2: 76 | usage() 77 | sys.exit(1) 78 | 79 | vmu_dump = sys.argv[1] 80 | handle = open_vmu_dump(sys.argv[1]) 81 | if handle is None: 82 | sys.exit(1) 83 | 84 | write_vmu(handle) 85 | 86 | handle.close() 87 | 88 | print "%s written" % (vmu_dump) 89 | 90 | 91 | --------------------------------------------------------------------------------