Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/famicom-dumper.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2020-09-05 12:11:08 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2020-09-05 12:11:08 +0300
commitb610166a96619746fe3cf26cd0201d5c800776e8 (patch)
tree3f3d18f5997b851c76ab874bb2dba09eb83a409c
parentf6b4e69cbb8b8ffccf5a8da83de15f52f313cf60 (diff)
Using hardware timer for timeouts
-rw-r--r--dumper.c1593
-rw-r--r--famicom-dumper.hex325
2 files changed, 960 insertions, 958 deletions
diff --git a/dumper.c b/dumper.c
index 1d2a85e..dbcd7d1 100644
--- a/dumper.c
+++ b/dumper.c
@@ -1,796 +1,797 @@
-/* Famicom Dumper/Programmer
- *
- * Copyright notice for this file:
- * Copyright (C) 2020 Cluster
- * http://clusterrr.com
- * clusterrr@clusterrr.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include "defines.h"
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include <avr/wdt.h>
-#include <util/delay.h>
-#include <inttypes.h>
-#include "usart.h"
-#include "comm.h"
-#include "dumper.h"
-
-#define LED_RED_ON PORTB |= (1<<7)
-#define LED_RED_OFF PORTB &= ~(1<<7)
-#define LED_GREEN_ON PORTB |= (1<<6)
-#define LED_GREEN_OFF PORTB &= ~(1<<6)
-#define ROMSEL_HI PORTF |= (1<<1)
-#define ROMSEL_LOW PORTF &= ~(1<<1)
-#define PHI2_HI PORTF |= (1<<0)
-#define PHI2_LOW PORTF &= ~(1<<0)
-#define MODE_READ { PORTD = 0xFF; DDRD = 0; }
-#define MODE_WRITE DDRD = 0xFF
-#define PRG_READ PORTF |= (1<<7)
-#define PRG_WRITE PORTF &= ~(1<<7)
-#define CHR_READ_HI PORTF |= (1<<5)
-#define CHR_READ_LOW PORTF &= ~(1<<5)
-#define CHR_WRITE_HI PORTF |= (1<<2)
-#define CHR_WRITE_LOW PORTF &= ~(1<<2)
-
-static void (*jump_to_bootloader)(void) = (void*)0xF800;
-
-ISR(USART0_RX_vect)
-{
- unsigned char b;
- while (UCSR0A & (1<<RXC0))
- {
- b = UDR0;
- comm_proceed(b);
- }
-}
-
-static void set_address(unsigned int address)
-{
- unsigned char l = address & 0xFF;
- unsigned char h = address>>8;
-
- PORTA = l;
- PORTC = h;
-
- // PPU /A13
- if ((address >> 13) & 1)
- PORTF &= ~(1<<4);
- else
- PORTF |= 1<<4;
-}
-
-static void set_romsel(unsigned int address)
-{
- if (address & 0x8000)
- {
- ROMSEL_LOW;
- } else {
- ROMSEL_HI;
- }
-}
-
-static unsigned char read_prg_byte(unsigned int address)
-{
- PHI2_LOW;
- ROMSEL_HI;
- MODE_READ;
- PRG_READ;
- set_address(address);
- PHI2_HI;
- set_romsel(address); // set /ROMSEL low if need
- //_delay_us(1);
- uint8_t result = PIND;
- ROMSEL_HI;
- return result;
-}
-
-static unsigned char read_chr_byte(unsigned int address)
-{
- PHI2_LOW;
- ROMSEL_HI;
- MODE_READ;
- set_address(address);
- CHR_READ_LOW;
- _delay_us(1);
- uint8_t result = PIND;
- CHR_READ_HI;
- PHI2_HI;
- return result;
-}
-
-static unsigned char read_coolboy_byte(unsigned int address)
-{
- PHI2_LOW;
- ROMSEL_HI;
- MODE_READ;
- PRG_READ;
- set_address(address);
- PHI2_HI;
- ROMSEL_LOW;
- COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
- COOLBOY_PORT &= ~(1<<COOLBOY_RD_PIN);
- //_delay_us(1);
- uint8_t result = PIND;
- ROMSEL_HI;
- COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
- return result;
-}
-
-static void read_prg_send(unsigned int address, unsigned int len)
-{
- LED_GREEN_ON;
- comm_start(COMMAND_PRG_READ_RESULT, len);
- while (len > 0)
- {
- comm_send_byte(read_prg_byte(address));
- len--;
- address++;
- }
- set_address(0);
- LED_GREEN_OFF;
-}
-
-static void read_chr_send(unsigned int address, unsigned int len)
-{
- LED_GREEN_ON;
- comm_start(COMMAND_CHR_READ_RESULT, len);
- while (len > 0)
- {
- comm_send_byte(read_chr_byte(address));
- len--;
- address++;
- }
- set_address(0);
- LED_GREEN_OFF;
-}
-
-static uint16_t crc16_update(uint16_t crc, uint8_t a)
-{
- int i;
- crc ^= a;
- for (i = 0; i < 8; ++i)
- {
- if (crc & 1)
- crc = (crc >> 1) ^ 0xA001;
- else
- crc = (crc >> 1);
- }
- return crc;
-}
-
-static void read_prg_crc_send(unsigned int address, unsigned int len)
-{
- LED_GREEN_ON;
- uint16_t crc = 0;
- while (len > 0)
- {
- crc = crc16_update(crc, read_prg_byte(address));
- len--;
- address++;
- }
- set_address(0);
- comm_start(COMMAND_PRG_READ_RESULT, 2);
- comm_send_byte(crc & 0xFF);
- comm_send_byte((crc >> 8) & 0xFF);
- LED_GREEN_OFF;
-}
-
-static void read_chr_crc_send(unsigned int address, unsigned int len)
-{
- LED_GREEN_ON;
- uint16_t crc = 0;
- while (len > 0)
- {
- crc = crc16_update(crc, read_chr_byte(address));
- len--;
- address++;
- }
- set_address(0);
- comm_start(COMMAND_CHR_READ_RESULT, 2);
- comm_send_byte(crc & 0xFF);
- comm_send_byte((crc >> 8) & 0xFF);
- LED_GREEN_OFF;
-}
-
-static void read_coolboy_send(unsigned int address, unsigned int len)
-{
- LED_GREEN_ON;
- COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
- COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
- COOLBOY_DDR |= 1<<COOLBOY_RD_PIN;
- COOLBOY_DDR |= 1<<COOLBOY_WR_PIN;
- comm_start(COMMAND_PRG_READ_RESULT, len);
- while (len > 0)
- {
- comm_send_byte(read_coolboy_byte(address));
- len--;
- address++;
- }
- set_address(0);
- ROMSEL_HI;
- COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
- COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
-
- COOLBOY_DDR &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
- COOLBOY_PORT &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
- LED_GREEN_OFF;
-}
-
-static void write_prg_byte(unsigned int address, uint8_t data)
-{
- PHI2_LOW;
- ROMSEL_HI;
- MODE_WRITE;
- PRG_WRITE;
- PORTD = data;
- set_address(address); // PHI2 low, ROMSEL always HIGH
- //_delay_us(1);
-
- PHI2_HI;
- set_romsel(address); // ROMSEL is low if need, PHI2 high
-
- //_delay_us(1); // WRITING
-
- // PHI2 low, ROMSEL high
- PHI2_LOW;
- ROMSEL_HI;
-
- // Back to read mode
- //_delay_us(1);
- PRG_READ;
- MODE_READ;
- set_address(0);
-
- // Set phi2 to high state to keep cartridge unreseted
- PHI2_HI;
-}
-
-static void write_chr_byte(unsigned int address, uint8_t data)
-{
- PHI2_LOW;
- ROMSEL_HI;
- MODE_WRITE;
- PORTD = data;
- set_address(address); // PHI2 low, ROMSEL always HIGH
- CHR_WRITE_LOW;
-
- _delay_us(1); // WRITING
-
- CHR_WRITE_HI;
-
- MODE_READ;
- set_address(0);
- PHI2_HI;
-}
-
-static void write_prg(unsigned int address, unsigned int len, uint8_t* data)
-{
- LED_RED_ON;
- while (len > 0)
- {
- write_prg_byte(address, *data);
- address++;
- len--;
- data++;
- }
- LED_RED_OFF;
-}
-
-static void write_chr(unsigned int address, unsigned int len, uint8_t* data)
-{
- LED_RED_ON;
- while (len > 0)
- {
- write_chr_byte(address, *data);
- address++;
- len--;
- data++;
- }
- LED_RED_OFF;
-}
-
-static void write_prg_flash_command(unsigned int address, uint8_t data)
-{
- write_prg_byte(address | 0x8000, data);
-}
-
-static void write_coolboy_flash_command(unsigned int address, uint8_t data)
-{
- COOLBOY_DDR |= 1<<COOLBOY_RD_PIN;
- COOLBOY_DDR |= 1<<COOLBOY_WR_PIN;
- COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
- COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
- ROMSEL_HI;
- PRG_READ;
- set_address(address);
- MODE_WRITE;
- PORTD = data;
- PHI2_HI;
- ROMSEL_LOW;
- //_delay_us(1);
- COOLBOY_PORT &= ~(1<<COOLBOY_WR_PIN);
- //_delay_us(1);
- COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
- set_address(0);
- ROMSEL_HI;
- MODE_READ;
-}
-
-static void erase_coolboy_sector()
-{
- LED_RED_ON;
- write_coolboy_flash_command(0x0000, 0xF0);
- write_coolboy_flash_command(0x0AAA, 0xAA);
- write_coolboy_flash_command(0x0555, 0x55);
- write_coolboy_flash_command(0x0AAA, 0x80);
- write_coolboy_flash_command(0x0AAA, 0xAA);
- write_coolboy_flash_command(0x0555, 0x55);
- write_coolboy_flash_command(0x0000, 0x30);
-
- long int timeout = 0;
- uint8_t res;
- int16_t last_res = -1;
- while (1)
- {
- timeout++;
- if (timeout >= 1000000)
- {
- // timeout
- comm_start(COMMAND_FLASH_ERASE_TIMEOUT, 0);
- break;
- }
- res = read_coolboy_byte(0x8000);
- if ((last_res == -1) || ((res ^ (last_res & 0xFF)) & ((1 << 6) | (1 << 2))))
- {
- // in progress
- last_res = res;
- continue;
- }
- // done
- if (res == 0xFF)
- {
- // ok
- comm_start(COMMAND_PRG_WRITE_DONE, 0);
- break;
- } else {
- // error
- comm_start(COMMAND_FLASH_ERASE_ERROR, 1);
- comm_send_byte(res);
- break;
- }
- }
- LED_RED_OFF;
-}
-
-static void erase_flash_sector()
-{
- LED_RED_ON;
- write_prg_flash_command(0x0000, 0xF0);
- write_prg_flash_command(0x0AAA, 0xAA);
- write_prg_flash_command(0x0555, 0x55);
- write_prg_flash_command(0x0AAA, 0x80);
- write_prg_flash_command(0x0AAA, 0xAA);
- write_prg_flash_command(0x0555, 0x55);
- write_prg_flash_command(0x0000, 0x30);
-
- long int timeout = 0;
- uint8_t res;
- int16_t last_res = -1;
- while (1)
- {
- timeout++;
- if (timeout >= 1000000)
- {
- // timeout
- comm_start(COMMAND_FLASH_ERASE_TIMEOUT, 0);
- break;
- }
- res = read_prg_byte(0x8000);
- if ((last_res == -1) || ((res != (last_res & 0xFF))))
- {
- // in progress
- last_res = res;
- continue;
- }
- // done
- if (res == 0xFF)
- {
- // ok
- comm_start(COMMAND_PRG_WRITE_DONE, 0);
- break;
- } else {
- // error
- comm_start(COMMAND_FLASH_ERASE_ERROR, 1);
- comm_send_byte(res);
- break;
- }
- }
- LED_RED_OFF;
-}
-
-static void write_coolboy(unsigned int address, unsigned int len, uint8_t* data)
-{
- LED_RED_ON;
- while (len > 0)
- {
- uint8_t count = 0;
- uint8_t* d = data;
- unsigned int a = address;
- unsigned int address_base = a & 0xFFE0;
- while ((len > 0) && ((a & 0xFFE0) == address_base))
- {
- if (*d != 0xFF)
- count++;
- a++;
- len--;
- d++;
- }
-
- if (count)
- {
- write_coolboy_flash_command(0x0000, 0xF0);
- write_coolboy_flash_command(0x0AAA, 0xAA);
- write_coolboy_flash_command(0x0555, 0x55);
- write_coolboy_flash_command(0x0000, 0x25);
- write_coolboy_flash_command(0x0000, count-1);
-
- while (count > 0)
- {
- if (*data != 0xFF)
- {
- write_coolboy_flash_command(address, *data);
- count--;
- }
- address++;
- data++;
- }
-
- write_coolboy_flash_command(0x0000, 0x29);
-
- long int timeout = 0;
- // waiting for result
- while (1)
- {
- timeout++;
- if (timeout >= 100000)
- {
- // timeout
- comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 0);
- LED_RED_OFF;
- return;
- }
- uint8_t read_1 = read_coolboy_byte((address-1) | 0x8000);
- uint8_t read_2 = read_coolboy_byte((address-1) | 0x8000);
- uint8_t read_3 = read_coolboy_byte((address-1) | 0x8000);
- if (((read_1 ^ read_2) & (1 << 6)) && ((read_2 ^ read_3) & (1 << 6)))
- {
- if (read_1 & (1 << 1))
- {
- comm_start(COMMAND_FLASH_WRITE_ERROR, 3);
- comm_send_byte(read_1);
- comm_send_byte(read_2);
- comm_send_byte(read_3);
- LED_RED_OFF;
- return;
- } else if (read_1 & (1 << 5)) {
- comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 3);
- comm_send_byte(read_1);
- comm_send_byte(read_2);
- comm_send_byte(read_3);
- LED_RED_OFF;
- return;
- }
- } else {
- read_1 = read_coolboy_byte((address-1) | 0x8000);
- read_2 = read_coolboy_byte((address-1) | 0x8000);
- if (read_1 == read_2 && read_2 == *(data-1))
- break; // ok
- }
- }
- }
-
- address = a;
- data = d;
- }
- comm_start(COMMAND_PRG_WRITE_DONE, 0);
- LED_RED_OFF;
-}
-
-static void write_flash(unsigned int address, unsigned int len, uint8_t* data)
-{
- LED_RED_ON;
- while (len > 0)
- {
- uint8_t count = 0;
- uint8_t* d = data;
- unsigned int a = address;
- unsigned int address_base = a & 0xFFE0;
- while ((len > 0) && ((a & 0xFFE0) == address_base))
- {
- if (*d != 0xFF)
- count++;
- a++;
- len--;
- d++;
- }
-
- if (count)
- {
- write_prg_flash_command(0x0000, 0xF0);
- write_prg_flash_command(0x0AAA, 0xAA);
- write_prg_flash_command(0x0555, 0x55);
- write_prg_flash_command(0x0000, 0x25);
- write_prg_flash_command(0x0000, count-1);
-
- while (count > 0)
- {
- if (*data != 0xFF)
- {
- write_prg_flash_command(address, *data);
- count--;
- }
- address++;
- data++;
- }
-
- write_prg_flash_command(0x0000, 0x29);
-
- long int timeout = 0;
- // waiting for result
- while (1)
- {
- timeout++;
- if (timeout >= 100000)
- {
- // timeout
- comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 0);
- LED_RED_OFF;
- return;
- }
- uint8_t read_1 = read_prg_byte((address-1) | 0x8000);
- uint8_t read_2 = read_prg_byte((address-1) | 0x8000);
- uint8_t read_3 = read_prg_byte((address-1) | 0x8000);
- if (((read_1 ^ read_2) & (1 << 6)) && ((read_2 ^ read_3) & (1 << 6)))
- {
- if (read_1 & (1 << 1))
- {
- comm_start(COMMAND_FLASH_WRITE_ERROR, 3);
- comm_send_byte(read_1);
- comm_send_byte(read_2);
- comm_send_byte(read_3);
- LED_RED_OFF;
- return;
- } else if (read_1 & (1 << 5)) {
- comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 3);
- comm_send_byte(read_1);
- comm_send_byte(read_2);
- comm_send_byte(read_3);
- LED_RED_OFF;
- return;
- }
- } else {
- read_1 = read_prg_byte((address-1) | 0x8000);
- read_2 = read_prg_byte((address-1) | 0x8000);
- if (read_1 == read_2 && read_2 == *(data-1))
- break; // ok
- }
- }
- }
-
- address = a;
- data = d;
- }
- comm_start(COMMAND_PRG_WRITE_DONE, 0);
- LED_RED_OFF;
-}
-
-void get_mirroring()
-{
- comm_start(COMMAND_MIRRORING_RESULT, 4);
- LED_GREEN_ON;
- set_address(0);
- _delay_us(1);
- comm_send_byte((PINE >> 2) & 1);
- set_address(1<<10);
- _delay_us(1);
- comm_send_byte((PINE >> 2) & 1);
- set_address(1<<11);
- _delay_us(1);
- comm_send_byte((PINE >> 2) & 1);
- set_address((1<<10) | (1<<11));
- _delay_us(1);
- comm_send_byte((PINE >> 2) & 1);
- set_address(0);
-}
-
-static void init_ports()
-{
- DDRB |= (1 << 6) | (1 << 7); // LEDS
- DDRF = 0b10110111; // CPU R/W, IRQ, PPU /RD, PPU /A13, CIRAM /CE, PPU /WR, /ROMSEL, PHI2
- PORTF = 0b11111111; // CPU R/W, IRQ, PPU /RD, PPU /A13, CIRAM /CE, PPU /WR, /ROMSEL, PHI2
- DDRE &= ~(1<<2); // CIRAM A10
- PORTE |= 1<<2; // CIRAM A10
- MODE_READ;
- set_address(0);
- DDRA = 0xFF; // Address low
- DDRC = 0xFF; // Address high
-}
-
-static void reset_phi2()
-{
- LED_RED_ON;
- LED_GREEN_ON;
- PHI2_LOW;
- ROMSEL_HI;
- _delay_ms(100);
- PHI2_HI;
- LED_RED_OFF;
- LED_GREEN_OFF;
-}
-
-int main (void)
-{
- sei();
- USART_init();
- init_ports();
- COOLBOY_DDR &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
- COOLBOY_PORT &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
-
- LED_RED_OFF;
- LED_GREEN_OFF;
-
- comm_init();
- comm_start(COMMAND_PRG_STARTED, 0);
-
- uint16_t address;
- uint16_t length;
-
- unsigned long int t = 0;
- char led_down = 0;
- int led_bright = 0;
-
- while (1)
- {
- TCCR1A |= (1<<COM1C1) | (1<<COM1B1) | (1<<WGM10);
- TCCR1B |= (1<<CS10);
- if (t++ >= 10000)
- {
- if (!led_down)
- {
- led_bright++;
- if (led_bright >= 110) led_down = 1;
- } else {
- led_bright--;
- if (!led_bright) led_down = 0;
- }
- if (led_bright >= 100) OCR1B = led_bright - 100;
- if (led_down)
- {
- int led_bright2 = 110-led_bright;
- if (led_bright2 <= 20)
- {
- if (led_bright2 > 10) led_bright2 = 20 - led_bright2;
- OCR1C = led_bright2*2;
- }
- }
- t = 0;
- }
-
- if (comm_recv_done)
- {
- comm_recv_done = 0;
- t = led_down = led_bright = 0;
- TCCR1A = OCR1B = OCR1C = 0;
-
- switch (comm_recv_command)
- {
- case COMMAND_PRG_INIT:
- comm_start(COMMAND_PRG_STARTED, 5);
- comm_send_byte(PROTOCOL_VERSION);
- comm_send_byte(SEND_BUFFER & 0xFF);
- comm_send_byte((SEND_BUFFER >> 8) & 0xFF);
- comm_send_byte(RECV_BUFFER & 0xFF);
- comm_send_byte((RECV_BUFFER >> 8) & 0xFF);
- break;
-
- case COMMAND_PRG_READ_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- read_prg_send(address, length);
- break;
-
- case COMMAND_PRG_CRC_READ_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- read_prg_crc_send(address, length);
- break;
-
- case COMMAND_PRG_WRITE_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- write_prg(address, length, (uint8_t*)&recv_buffer[4]);
- comm_start(COMMAND_PRG_WRITE_DONE, 0);
- break;
-
- case COMMAND_COOLBOY_READ_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- read_coolboy_send(address, length);
- break;
-
- case COMMAND_RESET:
- reset_phi2();
- comm_start(COMMAND_RESET_ACK, 0);
- break;
-
- case COMMAND_COOLBOY_ERASE_SECTOR_REQUEST:
- erase_coolboy_sector();
- break;
-
- case COMMAND_COOLBOY_WRITE_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- write_coolboy(address, length, (uint8_t*)&recv_buffer[4]);
- break;
-
- case COMMAND_FLASH_ERASE_SECTOR_REQUEST:
- erase_flash_sector();
- break;
-
- case COMMAND_FLASH_WRITE_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- write_flash(address, length, (uint8_t*)&recv_buffer[4]);
- break;
-
- case COMMAND_CHR_INIT:
- comm_start(COMMAND_CHR_STARTED, 0);
- break;
-
- case COMMAND_CHR_READ_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- read_chr_send(address, length);
- break;
-
- case COMMAND_CHR_CRC_READ_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- read_chr_crc_send(address, length);
- break;
-
- case COMMAND_CHR_WRITE_REQUEST:
- address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
- length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
- write_chr(address, length, (uint8_t*)&recv_buffer[4]);
- comm_start(COMMAND_CHR_WRITE_DONE, 0);
- break;
-
- case COMMAND_MIRRORING_REQUEST:
- get_mirroring();
- break;
-
- case COMMAND_BOOTLOADER:
- cli();
- MCUCSR = 0;
- jump_to_bootloader();
- }
- }
- }
-}
+/* Famicom Dumper/Programmer
+ *
+ * Copyright notice for this file:
+ * Copyright (C) 2020 Cluster
+ * http://clusterrr.com
+ * clusterrr@clusterrr.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "defines.h"
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/wdt.h>
+#include <util/delay.h>
+#include <inttypes.h>
+#include "usart.h"
+#include "comm.h"
+#include "dumper.h"
+
+#define LED_RED_ON PORTB |= (1<<7)
+#define LED_RED_OFF PORTB &= ~(1<<7)
+#define LED_GREEN_ON PORTB |= (1<<6)
+#define LED_GREEN_OFF PORTB &= ~(1<<6)
+#define ROMSEL_HI PORTF |= (1<<1)
+#define ROMSEL_LOW PORTF &= ~(1<<1)
+#define PHI2_HI PORTF |= (1<<0)
+#define PHI2_LOW PORTF &= ~(1<<0)
+#define MODE_READ { PORTD = 0xFF; DDRD = 0; }
+#define MODE_WRITE DDRD = 0xFF
+#define PRG_READ PORTF |= (1<<7)
+#define PRG_WRITE PORTF &= ~(1<<7)
+#define CHR_READ_HI PORTF |= (1<<5)
+#define CHR_READ_LOW PORTF &= ~(1<<5)
+#define CHR_WRITE_HI PORTF |= (1<<2)
+#define CHR_WRITE_LOW PORTF &= ~(1<<2)
+
+static void (*jump_to_bootloader)(void) = (void*)0xF800;
+
+ISR(USART0_RX_vect)
+{
+ unsigned char b;
+ while (UCSR0A & (1<<RXC0))
+ {
+ b = UDR0;
+ comm_proceed(b);
+ }
+}
+
+static void set_address(unsigned int address)
+{
+ unsigned char l = address & 0xFF;
+ unsigned char h = address>>8;
+
+ PORTA = l;
+ PORTC = h;
+
+ // PPU /A13
+ if ((address >> 13) & 1)
+ PORTF &= ~(1<<4);
+ else
+ PORTF |= 1<<4;
+}
+
+static void set_romsel(unsigned int address)
+{
+ if (address & 0x8000)
+ {
+ ROMSEL_LOW;
+ } else {
+ ROMSEL_HI;
+ }
+}
+
+static unsigned char read_prg_byte(unsigned int address)
+{
+ PHI2_LOW;
+ ROMSEL_HI;
+ MODE_READ;
+ PRG_READ;
+ set_address(address);
+ PHI2_HI;
+ set_romsel(address); // set /ROMSEL low if need
+ //_delay_us(1);
+ uint8_t result = PIND;
+ ROMSEL_HI;
+ return result;
+}
+
+static unsigned char read_chr_byte(unsigned int address)
+{
+ PHI2_LOW;
+ ROMSEL_HI;
+ MODE_READ;
+ set_address(address);
+ CHR_READ_LOW;
+ _delay_us(1);
+ uint8_t result = PIND;
+ CHR_READ_HI;
+ PHI2_HI;
+ return result;
+}
+
+static unsigned char read_coolboy_byte(unsigned int address)
+{
+ PHI2_LOW;
+ ROMSEL_HI;
+ MODE_READ;
+ PRG_READ;
+ set_address(address);
+ PHI2_HI;
+ ROMSEL_LOW;
+ COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
+ COOLBOY_PORT &= ~(1<<COOLBOY_RD_PIN);
+ //_delay_us(1);
+ uint8_t result = PIND;
+ ROMSEL_HI;
+ COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
+ return result;
+}
+
+static void read_prg_send(unsigned int address, unsigned int len)
+{
+ LED_GREEN_ON;
+ comm_start(COMMAND_PRG_READ_RESULT, len);
+ while (len > 0)
+ {
+ comm_send_byte(read_prg_byte(address));
+ len--;
+ address++;
+ }
+ set_address(0);
+ LED_GREEN_OFF;
+}
+
+static void read_chr_send(unsigned int address, unsigned int len)
+{
+ LED_GREEN_ON;
+ comm_start(COMMAND_CHR_READ_RESULT, len);
+ while (len > 0)
+ {
+ comm_send_byte(read_chr_byte(address));
+ len--;
+ address++;
+ }
+ set_address(0);
+ LED_GREEN_OFF;
+}
+
+static uint16_t crc16_update(uint16_t crc, uint8_t a)
+{
+ int i;
+ crc ^= a;
+ for (i = 0; i < 8; ++i)
+ {
+ if (crc & 1)
+ crc = (crc >> 1) ^ 0xA001;
+ else
+ crc = (crc >> 1);
+ }
+ return crc;
+}
+
+static void read_prg_crc_send(unsigned int address, unsigned int len)
+{
+ LED_GREEN_ON;
+ uint16_t crc = 0;
+ while (len > 0)
+ {
+ crc = crc16_update(crc, read_prg_byte(address));
+ len--;
+ address++;
+ }
+ set_address(0);
+ comm_start(COMMAND_PRG_READ_RESULT, 2);
+ comm_send_byte(crc & 0xFF);
+ comm_send_byte((crc >> 8) & 0xFF);
+ LED_GREEN_OFF;
+}
+
+static void read_chr_crc_send(unsigned int address, unsigned int len)
+{
+ LED_GREEN_ON;
+ uint16_t crc = 0;
+ while (len > 0)
+ {
+ crc = crc16_update(crc, read_chr_byte(address));
+ len--;
+ address++;
+ }
+ set_address(0);
+ comm_start(COMMAND_CHR_READ_RESULT, 2);
+ comm_send_byte(crc & 0xFF);
+ comm_send_byte((crc >> 8) & 0xFF);
+ LED_GREEN_OFF;
+}
+
+static void read_coolboy_send(unsigned int address, unsigned int len)
+{
+ LED_GREEN_ON;
+ COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
+ COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
+ COOLBOY_DDR |= 1<<COOLBOY_RD_PIN;
+ COOLBOY_DDR |= 1<<COOLBOY_WR_PIN;
+ comm_start(COMMAND_PRG_READ_RESULT, len);
+ while (len > 0)
+ {
+ comm_send_byte(read_coolboy_byte(address));
+ len--;
+ address++;
+ }
+ set_address(0);
+ ROMSEL_HI;
+ COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
+ COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
+
+ COOLBOY_DDR &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
+ COOLBOY_PORT &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
+ LED_GREEN_OFF;
+}
+
+static void write_prg_byte(unsigned int address, uint8_t data)
+{
+ PHI2_LOW;
+ ROMSEL_HI;
+ MODE_WRITE;
+ PRG_WRITE;
+ PORTD = data;
+ set_address(address); // PHI2 low, ROMSEL always HIGH
+ //_delay_us(1);
+
+ PHI2_HI;
+ set_romsel(address); // ROMSEL is low if need, PHI2 high
+
+ //_delay_us(1); // WRITING
+
+ // PHI2 low, ROMSEL high
+ PHI2_LOW;
+ ROMSEL_HI;
+
+ // Back to read mode
+ //_delay_us(1);
+ PRG_READ;
+ MODE_READ;
+ set_address(0);
+
+ // Set phi2 to high state to keep cartridge unreseted
+ PHI2_HI;
+}
+
+static void write_chr_byte(unsigned int address, uint8_t data)
+{
+ PHI2_LOW;
+ ROMSEL_HI;
+ MODE_WRITE;
+ PORTD = data;
+ set_address(address); // PHI2 low, ROMSEL always HIGH
+ CHR_WRITE_LOW;
+
+ _delay_us(1); // WRITING
+
+ CHR_WRITE_HI;
+
+ MODE_READ;
+ set_address(0);
+ PHI2_HI;
+}
+
+static void write_prg(unsigned int address, unsigned int len, uint8_t* data)
+{
+ LED_RED_ON;
+ while (len > 0)
+ {
+ write_prg_byte(address, *data);
+ address++;
+ len--;
+ data++;
+ }
+ LED_RED_OFF;
+}
+
+static void write_chr(unsigned int address, unsigned int len, uint8_t* data)
+{
+ LED_RED_ON;
+ while (len > 0)
+ {
+ write_chr_byte(address, *data);
+ address++;
+ len--;
+ data++;
+ }
+ LED_RED_OFF;
+}
+
+static void write_prg_flash_command(unsigned int address, uint8_t data)
+{
+ write_prg_byte(address | 0x8000, data);
+}
+
+static void write_coolboy_flash_command(unsigned int address, uint8_t data)
+{
+ COOLBOY_DDR |= 1<<COOLBOY_RD_PIN;
+ COOLBOY_DDR |= 1<<COOLBOY_WR_PIN;
+ COOLBOY_PORT |= 1<<COOLBOY_RD_PIN;
+ COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
+ ROMSEL_HI;
+ PRG_READ;
+ set_address(address);
+ MODE_WRITE;
+ PORTD = data;
+ PHI2_HI;
+ ROMSEL_LOW;
+ //_delay_us(1);
+ COOLBOY_PORT &= ~(1<<COOLBOY_WR_PIN);
+ //_delay_us(1);
+ COOLBOY_PORT |= 1<<COOLBOY_WR_PIN;
+ set_address(0);
+ ROMSEL_HI;
+ MODE_READ;
+}
+
+static void erase_coolboy_sector()
+{
+ LED_RED_ON;
+ write_coolboy_flash_command(0x0000, 0xF0);
+ write_coolboy_flash_command(0x0AAA, 0xAA);
+ write_coolboy_flash_command(0x0555, 0x55);
+ write_coolboy_flash_command(0x0AAA, 0x80);
+ write_coolboy_flash_command(0x0AAA, 0xAA);
+ write_coolboy_flash_command(0x0555, 0x55);
+ write_coolboy_flash_command(0x0000, 0x30);
+
+ uint8_t res;
+ int16_t last_res = -1;
+ TCNT1 = 0;
+ // waiting for result
+ while (1)
+ {
+ if (TCNT1 >= 23437) // 3 seconds
+ {
+ // timeout
+ comm_start(COMMAND_FLASH_ERASE_TIMEOUT, 0);
+ break;
+ }
+ res = read_coolboy_byte(0x8000);
+ if ((last_res == -1) || ((res ^ (last_res & 0xFF)) & ((1 << 6) | (1 << 2))))
+ {
+ // in progress
+ last_res = res;
+ continue;
+ }
+ // done
+ if (res == 0xFF)
+ {
+ // ok
+ comm_start(COMMAND_PRG_WRITE_DONE, 0);
+ break;
+ } else {
+ // error
+ comm_start(COMMAND_FLASH_ERASE_ERROR, 1);
+ comm_send_byte(res);
+ break;
+ }
+ }
+ LED_RED_OFF;
+}
+
+static void erase_flash_sector()
+{
+ LED_RED_ON;
+ write_prg_flash_command(0x0000, 0xF0);
+ write_prg_flash_command(0x0AAA, 0xAA);
+ write_prg_flash_command(0x0555, 0x55);
+ write_prg_flash_command(0x0AAA, 0x80);
+ write_prg_flash_command(0x0AAA, 0xAA);
+ write_prg_flash_command(0x0555, 0x55);
+ write_prg_flash_command(0x0000, 0x30);
+
+ uint8_t res;
+ int16_t last_res = -1;
+ TCNT1 = 0;
+ // waiting for result
+ while (1)
+ {
+ if (TCNT1 >= 23437) // 3 seconds
+ {
+ // timeout
+ comm_start(COMMAND_FLASH_ERASE_TIMEOUT, 0);
+ break;
+ }
+ res = read_prg_byte(0x8000);
+ if ((last_res == -1) || ((res != (last_res & 0xFF))))
+ {
+ // in progress
+ last_res = res;
+ continue;
+ }
+ // done
+ if (res == 0xFF)
+ {
+ // ok
+ comm_start(COMMAND_PRG_WRITE_DONE, 0);
+ break;
+ } else {
+ // error
+ comm_start(COMMAND_FLASH_ERASE_ERROR, 1);
+ comm_send_byte(res);
+ break;
+ }
+ }
+ LED_RED_OFF;
+}
+
+static void write_coolboy(unsigned int address, unsigned int len, uint8_t* data)
+{
+ LED_RED_ON;
+ while (len > 0)
+ {
+ uint8_t count = 0;
+ uint8_t* d = data;
+ unsigned int a = address;
+ unsigned int address_base = a & 0xFFE0;
+ while ((len > 0) && ((a & 0xFFE0) == address_base))
+ {
+ if (*d != 0xFF)
+ count++;
+ a++;
+ len--;
+ d++;
+ }
+
+ if (count)
+ {
+ write_coolboy_flash_command(0x0000, 0xF0);
+ write_coolboy_flash_command(0x0AAA, 0xAA);
+ write_coolboy_flash_command(0x0555, 0x55);
+ write_coolboy_flash_command(0x0000, 0x25);
+ write_coolboy_flash_command(0x0000, count-1);
+
+ while (count > 0)
+ {
+ if (*data != 0xFF)
+ {
+ write_coolboy_flash_command(address, *data);
+ count--;
+ }
+ address++;
+ data++;
+ }
+
+ write_coolboy_flash_command(0x0000, 0x29);
+
+ TCNT1 = 0;
+ // waiting for result
+ while (1)
+ {
+ if (TCNT1 >= 7812) // 1 second
+ {
+ // timeout
+ comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 0);
+ LED_RED_OFF;
+ return;
+ }
+ uint8_t read_1 = read_coolboy_byte((address-1) | 0x8000);
+ uint8_t read_2 = read_coolboy_byte((address-1) | 0x8000);
+ uint8_t read_3 = read_coolboy_byte((address-1) | 0x8000);
+ if (((read_1 ^ read_2) & (1 << 6)) && ((read_2 ^ read_3) & (1 << 6)))
+ {
+ if (read_1 & (1 << 1))
+ {
+ comm_start(COMMAND_FLASH_WRITE_ERROR, 3);
+ comm_send_byte(read_1);
+ comm_send_byte(read_2);
+ comm_send_byte(read_3);
+ LED_RED_OFF;
+ return;
+ } else if (read_1 & (1 << 5)) {
+ comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 3);
+ comm_send_byte(read_1);
+ comm_send_byte(read_2);
+ comm_send_byte(read_3);
+ LED_RED_OFF;
+ return;
+ }
+ } else {
+ read_1 = read_coolboy_byte((address-1) | 0x8000);
+ read_2 = read_coolboy_byte((address-1) | 0x8000);
+ if (read_1 == read_2 && read_2 == *(data-1))
+ break; // ok
+ }
+ }
+ }
+
+ address = a;
+ data = d;
+ }
+ comm_start(COMMAND_PRG_WRITE_DONE, 0);
+ LED_RED_OFF;
+}
+
+static void write_flash(unsigned int address, unsigned int len, uint8_t* data)
+{
+ LED_RED_ON;
+ while (len > 0)
+ {
+ uint8_t count = 0;
+ uint8_t* d = data;
+ unsigned int a = address;
+ unsigned int address_base = a & 0xFFE0;
+ while ((len > 0) && ((a & 0xFFE0) == address_base))
+ {
+ if (*d != 0xFF)
+ count++;
+ a++;
+ len--;
+ d++;
+ }
+
+ if (count)
+ {
+ write_prg_flash_command(0x0000, 0xF0);
+ write_prg_flash_command(0x0AAA, 0xAA);
+ write_prg_flash_command(0x0555, 0x55);
+ write_prg_flash_command(0x0000, 0x25);
+ write_prg_flash_command(0x0000, count-1);
+
+ while (count > 0)
+ {
+ if (*data != 0xFF)
+ {
+ write_prg_flash_command(address, *data);
+ count--;
+ }
+ address++;
+ data++;
+ }
+
+ write_prg_flash_command(0x0000, 0x29);
+
+ TCNT1 = 0;
+ // waiting for result
+ while (1)
+ {
+ if (TCNT1 >= 7812) // 1 second
+ {
+ // timeout
+ comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 0);
+ LED_RED_OFF;
+ return;
+ }
+ uint8_t read_1 = read_prg_byte((address-1) | 0x8000);
+ uint8_t read_2 = read_prg_byte((address-1) | 0x8000);
+ uint8_t read_3 = read_prg_byte((address-1) | 0x8000);
+ if (((read_1 ^ read_2) & (1 << 6)) && ((read_2 ^ read_3) & (1 << 6)))
+ {
+ if (read_1 & (1 << 1))
+ {
+ comm_start(COMMAND_FLASH_WRITE_ERROR, 3);
+ comm_send_byte(read_1);
+ comm_send_byte(read_2);
+ comm_send_byte(read_3);
+ LED_RED_OFF;
+ return;
+ } else if (read_1 & (1 << 5)) {
+ comm_start(COMMAND_FLASH_WRITE_TIMEOUT, 3);
+ comm_send_byte(read_1);
+ comm_send_byte(read_2);
+ comm_send_byte(read_3);
+ LED_RED_OFF;
+ return;
+ }
+ } else {
+ read_1 = read_prg_byte((address-1) | 0x8000);
+ read_2 = read_prg_byte((address-1) | 0x8000);
+ if (read_1 == read_2 && read_2 == *(data-1))
+ break; // ok
+ }
+ }
+ }
+
+ address = a;
+ data = d;
+ }
+ comm_start(COMMAND_PRG_WRITE_DONE, 0);
+ LED_RED_OFF;
+}
+
+void get_mirroring()
+{
+ comm_start(COMMAND_MIRRORING_RESULT, 4);
+ LED_GREEN_ON;
+ set_address(0);
+ _delay_us(1);
+ comm_send_byte((PINE >> 2) & 1);
+ set_address(1<<10);
+ _delay_us(1);
+ comm_send_byte((PINE >> 2) & 1);
+ set_address(1<<11);
+ _delay_us(1);
+ comm_send_byte((PINE >> 2) & 1);
+ set_address((1<<10) | (1<<11));
+ _delay_us(1);
+ comm_send_byte((PINE >> 2) & 1);
+ set_address(0);
+}
+
+static void init_ports()
+{
+ DDRB |= (1 << 6) | (1 << 7); // LEDS
+ DDRF = 0b10110111; // CPU R/W, IRQ, PPU /RD, PPU /A13, CIRAM /CE, PPU /WR, /ROMSEL, PHI2
+ PORTF = 0b11111111; // CPU R/W, IRQ, PPU /RD, PPU /A13, CIRAM /CE, PPU /WR, /ROMSEL, PHI2
+ DDRE &= ~(1<<2); // CIRAM A10
+ PORTE |= 1<<2; // CIRAM A10
+ MODE_READ;
+ set_address(0);
+ DDRA = 0xFF; // Address low
+ DDRC = 0xFF; // Address high
+}
+
+static void reset_phi2()
+{
+ LED_RED_ON;
+ LED_GREEN_ON;
+ PHI2_LOW;
+ ROMSEL_HI;
+ _delay_ms(100);
+ PHI2_HI;
+ LED_RED_OFF;
+ LED_GREEN_OFF;
+}
+
+int main (void)
+{
+ sei();
+ USART_init();
+ init_ports();
+ COOLBOY_DDR &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
+ COOLBOY_PORT &= ~((1<<COOLBOY_RD_PIN) | (1<<COOLBOY_RD_PIN));
+
+ LED_RED_OFF;
+ LED_GREEN_OFF;
+
+ comm_init();
+ comm_start(COMMAND_PRG_STARTED, 0);
+
+ uint16_t address;
+ uint16_t length;
+
+ unsigned long int t = 0;
+ char led_down = 0;
+ int led_bright = 0;
+
+ while (1)
+ {
+ // PWM for leds
+ TCCR1A |= (1<<COM1C1) | (1<<COM1B1) | (1<<WGM10);
+ TCCR1B = 1<<CS10; // no prescaler
+ if (t++ >= 10000)
+ {
+ if (!led_down)
+ {
+ led_bright++;
+ if (led_bright >= 110) led_down = 1;
+ } else {
+ led_bright--;
+ if (!led_bright) led_down = 0;
+ }
+ if (led_bright >= 100) OCR1B = led_bright - 100;
+ if (led_down)
+ {
+ int led_bright2 = 110-led_bright;
+ if (led_bright2 <= 20)
+ {
+ if (led_bright2 > 10) led_bright2 = 20 - led_bright2;
+ OCR1C = led_bright2*2;
+ }
+ }
+ t = 0;
+ }
+
+ if (comm_recv_done)
+ {
+ comm_recv_done = 0;
+ t = led_down = led_bright = 0;
+ // Just timer without PWM for timeouts
+ TCCR1A = OCR1B = OCR1C = 0;
+ TCCR1B = (1<<CS12) | (1<<CS10); // /1024 prescaler
+
+ switch (comm_recv_command)
+ {
+ case COMMAND_PRG_INIT:
+ comm_start(COMMAND_PRG_STARTED, 5);
+ comm_send_byte(PROTOCOL_VERSION);
+ comm_send_byte(SEND_BUFFER & 0xFF);
+ comm_send_byte((SEND_BUFFER >> 8) & 0xFF);
+ comm_send_byte(RECV_BUFFER & 0xFF);
+ comm_send_byte((RECV_BUFFER >> 8) & 0xFF);
+ break;
+
+ case COMMAND_PRG_READ_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ read_prg_send(address, length);
+ break;
+
+ case COMMAND_PRG_CRC_READ_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ read_prg_crc_send(address, length);
+ break;
+
+ case COMMAND_PRG_WRITE_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ write_prg(address, length, (uint8_t*)&recv_buffer[4]);
+ comm_start(COMMAND_PRG_WRITE_DONE, 0);
+ break;
+
+ case COMMAND_COOLBOY_READ_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ read_coolboy_send(address, length);
+ break;
+
+ case COMMAND_RESET:
+ reset_phi2();
+ comm_start(COMMAND_RESET_ACK, 0);
+ break;
+
+ case COMMAND_COOLBOY_ERASE_SECTOR_REQUEST:
+ erase_coolboy_sector();
+ break;
+
+ case COMMAND_COOLBOY_WRITE_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ write_coolboy(address, length, (uint8_t*)&recv_buffer[4]);
+ break;
+
+ case COMMAND_FLASH_ERASE_SECTOR_REQUEST:
+ erase_flash_sector();
+ break;
+
+ case COMMAND_FLASH_WRITE_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ write_flash(address, length, (uint8_t*)&recv_buffer[4]);
+ break;
+
+ case COMMAND_CHR_INIT:
+ comm_start(COMMAND_CHR_STARTED, 0);
+ break;
+
+ case COMMAND_CHR_READ_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ read_chr_send(address, length);
+ break;
+
+ case COMMAND_CHR_CRC_READ_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ read_chr_crc_send(address, length);
+ break;
+
+ case COMMAND_CHR_WRITE_REQUEST:
+ address = recv_buffer[0] | ((uint16_t)recv_buffer[1]<<8);
+ length = recv_buffer[2] | ((uint16_t)recv_buffer[3]<<8);
+ write_chr(address, length, (uint8_t*)&recv_buffer[4]);
+ comm_start(COMMAND_CHR_WRITE_DONE, 0);
+ break;
+
+ case COMMAND_MIRRORING_REQUEST:
+ get_mirroring();
+ break;
+
+ case COMMAND_BOOTLOADER:
+ cli();
+ MCUCSR = 0;
+ jump_to_bootloader();
+ }
+ }
+ }
+}
diff --git a/famicom-dumper.hex b/famicom-dumper.hex
index efa4dcb..5edafd2 100644
--- a/famicom-dumper.hex
+++ b/famicom-dumper.hex
@@ -8,7 +8,7 @@
:100070000C9458000C9458000C9458000C945800A0
:100080000C9458000C9458000C94580011241FBE76
:10009000CFEFD0E1DEBFCDBF29E0A0E0B1E001C0ED
-:1000A0001D92A531B207E1F70E94F8020C942008D6
+:1000A0001D92A531B207E1F70E94F8020C942708CF
:1000B0000C9400008BBB95BB95FD06C0809162003F
:1000C0008061809362000895809162008F7E8093AA
:1000D00062000895CF93DF93C2E6D0E028812E7F9F
@@ -98,165 +98,166 @@
:100610008093620012981A9A82BB11BA1BBA15BA5B
:10062000909162009061909362008ABB84BBBC98F9
:10063000C498C798C6980E94EF0260E070E080E01E
-:100640000E941D026EE6C62ED12C22242A948FB55C
-:1006500089628FBD8EB581608EBD20E030E0E0E024
-:1006600041E050E060E070E080910B01811138C002
-:100670008FB589628FBD8EB581608EBD403187E2B6
-:1006800058076105710508F450C0E11153C0C90154
-:1006900001968E3691050CF455C0E1E09C0124567C
-:1006A000310939BD28BD9C01C601821B930B8531E0
-:1006B00091050CF059C08B3091052CF044E150E0CD
-:1006C000481B590BCA01880F991F9093790080939A
-:1006D000780040E050E0BA0180910B01882341F29C
-:1006E00010920B01109279001092780019BC18BC7E
-:1006F0001FBC80910C01833109F4A4C2C8F185327A
-:1007000009F4C2C208F457C0873209F40AC408F4D5
-:10071000FCC2883209F4B1C38E3F09F098CFF89437
-:1007200014BEE0E0F8EF099592CF4F5F5F4F6F4F37
-:100730007F4F9ACF2150310989F4E0E040E050E04A
-:10074000BA0192CF8436910524F02356310939BD80
-:1007500028BD9C0140E050E0BA0186CF2436310527
-:100760000CF4A2CFC9019ACF40E050E0BA017CCF8F
-:10077000893009F4F7C008F064C0863009F453C129
-:10078000873009F41DC1853009F061CF65E070E064
-:1007900080E00E941D0282E00E94450280E00E94EB
-:1007A000450280E80E94450280E00E94450288E000
-:1007B0000E9445024CCF833209F401C208F039C1CE
-:1007C000823209F044CF00910D0180910E0110E0BA
-:1007D000182B30910F01398380911001432F50E085
-:1007E000582B5A834983C69AC49AC39ABC9ABB9A17
-:1007F00069817A8188E00E941D0289819A81892B12
-:1008000071F0C8010E948A000E944502E981FA81C4
-:100810003197FA83E9830F5F1F4FEF2B91F71BBAD4
-:1008200015BA8091620080618093620080916200BD
-:10083000826080936200C49AC39ABC98C498C69898
-:1008400006CF8D30C9F18131A1F18B3009F0FFCE97
-:1008500000910D0180910E0110E0182BF0910F0115
-:10086000F983809110012F2F30E0382B3A832983B0
-:10087000C69A69817A818CE00E941D0249815A8161
-:10088000452B71F0C8010E946A000E9445028981CF
-:100890009A8101979A8389830F5F1F4F892B91F764
-:1008A0001BBA15BA80916200806180936200C6987D
-:1008B000CECE0E947A01CBCE00910D0180910E0127
-:1008C00010E0182B30910F01398380911001432FD4
-:1008D00050E0582B5A834983C79A452BE1F181E1B7
-:1008E000E82E81E0F82EF70191917F01809162005E
-:1008F0008E7F8093620080916200826080936200AC
-:1009000021BA92BBC8010E945A00809162008B7F7D
-:1009100080936200F2E0FA95F1F700C080916200E6
-:1009200084608093620022BA11BA1BBA15BA809112
-:100930006200806180936200809162008160809398
-:1009400062000F5F1F4F29813A81215031093A839C
-:100950002983232B41F6C79860E070E08EE00E9467
-:100960001D0275CE00910D0180910E0110E0182B33
-:1009700030910F01398380911001432F50E0582BA3
-:100980005A834983C79A452BA1F0B1E1EB2EB1E020
-:10099000FB2EF70161917F01C8010E94D7000F5F14
-:1009A0001F4F29813A81215031093A832983232B12
-:1009B00081F7C79860E070E08AE00E941D0247CE90
-:1009C00000910D0180910E0110E0182B50910F0144
-:1009D000598380911001E52FF0E0F82BFA83E98329
-:1009E000C69A69817A8188E00E941D0229813A8134
-:1009F000232B09F455CFC8010E9420010E94450213
-:100A000049815A81415051095A8349830F5F1F4FD1
-:100A1000452B89F71BBA15BA8091620080618093DB
-:100A20006200C69814CE60E070E081E00E941D0272
-:100A30000ECE90910D01998380910E01E92FF0E087
-:100A4000F82BFA83E98300910F018091100110E0E7
-:100A5000182BC79A0115110509F4AAC0F1E16F2EF0
-:100A6000F1E07F2E29813A81207E2301F301E98084
-:100A7000FA80B12C06C0C701807E2817390709F01B
-:100A800045C081918F3F09F0B3944FEFE41AF40A07
-:100A9000015011093F0179F7BB2009F489C060EFCB
-:100AA00080E090E00E94AC006AEA8AEA9AE00E9444
-:100AB000AC0065E585E595E00E94AC0065E280E06C
-:100AC00090E00E94AC00BA946B2D80E090E00E9410
-:100AD000AC00F20161912F016F3F89F089819A8109
-:100AE0000E94AC0089819A810196BB20A1F09A8373
-:100AF0008983BA94F20161912F016F3F79F72981BF
-:100B00003A812F5F3F4F3A832983E3CFB110C7CF9C
-:100B1000FA82E982A7CF69E280E090E00E94AC000F
-:100B200029813A8130683C832B83EFE98E2EE6E8F9
-:100B30009E2EAA24A394B12CA201415051095A839C
-:100B400049830BC041FC6BC245FC59C251E0851A78
-:100B50009108A108B10809F44BC28B819C810E94C5
-:100B60008A00482E8B819C810E948A00582E8B819E
-:100B70009C810E948A00382E842D852586FF04C022
-:100B8000852D832586FDDECF8B819C810E948A0086
-:100B9000582E8B819C810E948A005812D7CFE98100
-:100BA000FA8180815812D2CF0115110509F0B0CF1A
-:100BB00060E070E08AE00E941D02C79848CDC79AA5
-:100BC00060EF80E090E00E94AC006AEA8AEA9AE076
-:100BD0000E94AC0065E585E595E00E94AC0060E808
-:100BE0008AEA9AE00E94AC006AEA8AEA9AE00E94E5
-:100BF000AC0065E585E595E00E94AC0060E380E02F
-:100C000090E00E94AC0080E090E80E948A00898316
-:100C1000AFE38A2EA2E49A2EAFE0AA2EB12C51E0C7
-:100C2000851A9108A108B10809F403C280E090E890
-:100C30000E948A008B8349814827447409F4BCC10F
-:100C40008983EDCFC79AC69A809162008E7F809388
-:100C5000620080916200826080936200FFEF20E773
-:100C600032E0F15020403040E1F700C000008091B8
-:100C70006200816080936200C798C69860E070E06F
-:100C800084E10E941D02E3CCC79A60EF80E090E807
-:100C90000E94D7006AEA8AEA9AE80E94D70065E5CE
-:100CA00085E595E80E94D70060E88AEA9AE80E9404
-:100CB000D7006AEA8AEA9AE80E94D70065E585E5E6
-:100CC00095E80E94D70060E380E090E80E94D7009A
-:100CD00080E090E80E94200189837FE3872E72E400
-:100CE000972E7FE0A72EB12C21E0821A9108A1084F
-:100CF000B10809F49EC180E090E80E942001F981CA
-:100D00008F1709F467C18983EFCFA0900D018091FF
-:100D10000E01B12CB82A00910F018091100110E052
-:100D2000182BC79A0115110509F4A0C061E1662EC0
-:100D300061E0762E9501207E2301F3017501912C4F
-:100D400006C0C701807E2817390709F043C081918A
-:100D50008F3F09F093944FEFE41AF40A0150110900
-:100D60003F0179F7992009F481C060EF80E090E8B5
-:100D70000E94D7006AEA8AEA9AE80E94D70065E5ED
-:100D800085E595E80E94D70065E280E090E80E9442
-:100D9000D7009A94692D80E090E80E94D700F20174
-:100DA00061912F016F3F91F0950130683C832B8357
-:100DB000C9010E94D7003FEFA31AB30A992071F02E
-:100DC0009A94F20161912F016F3F71F73FEFA31ADF
-:100DD000B30AE5CF9110C9CF5701ACCF69E280E0EB
-:100DE00090E80E94D7005FE9852E56E8952EAA2448
-:100DF000A394B12CA201415051095A8349830BC0DD
-:100E000041FC0DC145FCFBC051E0851A9108A108C9
-:100E1000B10809F4EDC08B819C810E942001482E0D
-:100E20008B819C810E942001582E8B819C810E9485
-:100E30002001382E842D852586FF04C0852D83252D
-:100E400086FDDECF8B819C810E942001582E8B81F4
-:100E50009C810E9420015812D7CFE981FA818081BC
-:100E60005812D2CF0115110509F0B6CF60E070E03D
-:100E70008AE00E941D02C798EACBE0900D018091A4
-:100E80000E01F12CF82A00910F018091100110E061
-:100E9000182BC69A0115110509F4C5C01A821982CA
-:100EA000C7010E946A00E981FA81E827FA83E98391
-:100EB00088E090E00CC029813A813695279541E081
-:100EC000242740EA34273A832983019759F0F9818E
-:100ED000F0FDF1CFE981FA81F695E795FA83E98390
-:100EE0000197A9F701501109FFEFEF1AFF0A011549
-:100EF0001105B1F619812A8129831BBA15BA80918F
-:100F0000620080618093620062E070E08CE00E9489
-:100F10001D02812F0E94450289810E944502C698C8
-:100F200096CBE0900D0180910E01F12CF82A0091F2
-:100F30000F018091100110E0182BC69A01151105C0
-:100F400009F474C01A821982C7010E942001E98144
-:100F5000FA81E827FA83E98388E090E00CC02981D0
-:100F60003A813695279541E0242740EA34273A8391
-:100F70002983019759F0F981F0FDF1CFE981FA81D8
-:100F8000F695E795FA83E9830197A9F701501109CE
-:100F9000FFEFEF1AFF0A01151105B1F619812A8139
-:100FA00029831BBA15BA8091620080618093620028
-:100FB00062E070E088E0ABCF8B818F3F09F4F8CD21
-:100FC00061E070E08BE20E941D028B810E9445026D
-:100FD000C7983DCB39813F3F09F4EACD61E070E02D
-:100FE0008BE20E941D0289810E944502C7982FCB87
-:100FF00060E070E08AE20E941D02C79828CB63E09F
-:1010000070E08AE20E941D02842D0E944502852D17
-:101010000E944502832D0E944502C79818CB63E0C9
-:1010200070E089E2EFCF198210E067CF198210E0FB
-:10103000B8CF60E070E08CE20E941D02C79807CB39
-:04104000F894FFCF52
+:100640000E941D02772473940EE6C02ED12C84E103
+:10065000482E512C66246A948FB589628FBD21E0A3
+:100660002EBD20E030E0E0E041E050E060E070E0EE
+:1006700080910B01811135C08FB589628FBD7EBC21
+:10068000403187E258076105710508F451C0E11156
+:1006900054C0C90101968E3691050CF456C0E1E0B4
+:1006A0009C012456310939BD28BD9C01C601821B1D
+:1006B000930B853191050CF05AC08B30910524F0D5
+:1006C000A201481B590BCA01880F991F909379000A
+:1006D0008093780040E050E0BA0180910B018823BC
+:1006E00059F210920B01109279001092780019BC07
+:1006F00018BC1FBC54E05EBD80910C01833109F42D
+:10070000BCC2C8F1853209F4DAC208F457C0873296
+:1007100009F43AC408F42DC3883209F4E1C38E3FCA
+:1007200009F09ACFF89414BEE0E0F8EF099594CF61
+:100730004F5F5F4F6F4F7F4F9BCF2150310989F43F
+:10074000E0E040E050E0BA0193CF8436910524F018
+:100750002356310939BD28BD9C0140E050E0BA0163
+:1007600087CF243631050CF4A1CFC90199CF40E0E1
+:1007700050E0BA017DCF893009F4F6C008F064C0BA
+:10078000863009F452C1873009F41BC1853009F065
+:1007900063CF65E070E080E00E941D0282E00E946D
+:1007A000450280E00E94450280E80E94450280E008
+:1007B0000E94450288E00E9445024ECF833209F430
+:1007C00010C208F038C1823209F046CF00910D0105
+:1007D00080910E0110E0182B30910F013983809128
+:1007E0001001432F50E0582B5A834983C69AC49A6C
+:1007F000C39ABC9ABB9A69817A8188E00E941D02E3
+:1008000089819A81892B71F0C8010E948A000E9417
+:100810004502E981FA813197FA83E9830F5F1F4F1F
+:10082000EF2B91F71BBA15BA80916200806180931B
+:10083000620080916200826080936200C49AC39AD1
+:10084000BC98C498C69808CF8D30C9F18131A1F108
+:100850008B3009F001CF00910D0180910E0110E065
+:10086000182B30910F01398380911001432F50E0F4
+:10087000582B5A834983C69A69817A818CE00E94F9
+:100880001D0289819A81892B71F0C8010E946A003A
+:100890000E944502E981FA813197FA83E9830F5F6B
+:1008A0001F4FEF2B91F71BBA15BA80916200806140
+:1008B00080936200C698D0CE0E947A01CDCEE0909F
+:1008C0000D0180910E01F12CF82A50910F015983EE
+:1008D00080911001E52FF0E0F82BFA83E983C79AA5
+:1008E000EF2BD9F101E111E0F80191918F01809195
+:1008F00062008E7F809362008091620082608093AC
+:10090000620061BA92BBC7010E945A0080916200E6
+:100910008B7F80936200F2E0FA95F1F700C080913E
+:10092000620084608093620062BA11BA1BBA15BA81
+:10093000809162008061809362008091620081609A
+:10094000809362002FEFE21AF20A49815A814150E6
+:1009500051095A834983452B39F6C79860E070E006
+:100960008EE00E941D0278CEE0900D0180910E0174
+:10097000F12CF82A30910F01398380911001432F17
+:1009800050E0582B5A834983C79A452B99F001E1CF
+:1009900011E0F80161918F01C7010E94D700FFEFBC
+:1009A000EF1AFF0A29813A81215031093A832983BC
+:1009B000232B79F7C79860E070E08AE00E941D025F
+:1009C0004BCE00910D0180910E0110E0182B80910B
+:1009D0000F01898380911001F981EF2FF0E0F82B4E
+:1009E000FA83E983C69A69817A8188E00E941D02B0
+:1009F00029813A81232B09F456CFC8010E94200196
+:100A00000E94450249815A81415051095A834983C4
+:100A10000F5F1F4F452B89F71BBA15BA80916200F3
+:100A2000806180936200C69817CE60E070E081E03C
+:100A30000E941D0211CE20910D01298380910E018B
+:100A4000422F50E0582B5A83498300910F01809127
+:100A5000100110E0182BC79A0115110509F4B3C055
+:100A6000E1E1AE2EE1E0BE2E29813A81207E4501F2
+:100A7000F501E980FA80312C06C0C701807E281775
+:100A8000390709F045C081918F3F09F033948FEF0A
+:100A9000E81AF80A015011095F0179F7332009F4C7
+:100AA00092C060EF80E090E00E94AC006AEA8AEABF
+:100AB0009AE00E94AC0065E585E595E00E94AC00F7
+:100AC00065E280E090E00E94AC003A94632D80E003
+:100AD00090E00E94AC00F40161914F016F3F89F0FA
+:100AE00089819A810E94AC0089819A810196332084
+:100AF000A1F09A8389833A94F40161914F016F3F89
+:100B000079F729813A812F5F3F4F3A832983E3CFD9
+:100B10003110C7CFFA82E982A7CF69E280E090E086
+:100B20000E94AC001DBC1CBC29813A8130683C830A
+:100B30002B83A401415051095A8349838CB59DB53B
+:100B400082319A4708F046C08B819C810E948A00BE
+:100B5000882E8B819C810E948A00982E8B819C819B
+:100B60000E948A00382E882D892586FF18C0892D7D
+:100B7000832586FF14C081FC60C285FEDFCF63E061
+:100B800070E08AE20E941D02882D0E944502892D94
+:100B90000E944502832D0E944502C7985DCD8B813E
+:100BA0009C810E948A00982E8B819C810E948A00E1
+:100BB0009812C4CFE981FA8180819812BFCF0115C4
+:100BC000110509F0A7CF60E070E08AE00E941D02E5
+:100BD000C79842CD60E070E08AE20E941D02C7988B
+:100BE0003BCDC79A60EF80E090E00E94AC006AEADB
+:100BF0008AEA9AE00E94AC0065E585E595E00E94EE
+:100C0000AC0060E88AEA9AE00E94AC006AEA8AEAEC
+:100C10009AE00E94AC0065E585E595E00E94AC0095
+:100C200060E380E090E00E94AC001DBC1CBC8CB571
+:100C30009DB582319A47D0F480E090E80E948A0006
+:100C400089838CB59DB582319A4780F480E090E825
+:100C50000E948A008B8359815827547409F4DFC19C
+:100C600089838CB59DB582319A4780F360E070E04E
+:100C70008CE20E941D02C798EFCCC79AC69A809159
+:100C800062008E7F80936200809162008260809318
+:100C90006200FFEF20E732E0F15020403040E1F702
+:100CA00000C0000080916200816080936200C7985C
+:100CB000C69860E070E084E10E941D02CDCCC79A26
+:100CC00060EF80E090E80E94D7006AEA8AEA9AE83A
+:100CD0000E94D70065E585E595E80E94D70060E8A9
+:100CE0008AEA9AE80E94D7006AEA8AEA9AE80E94A9
+:100CF000D70065E585E595E80E94D70060E380E0D0
+:100D000090E80E94D7001DBC1CBC8CB59DB58231FB
+:100D10009A4708F0ABCF80E090E80E9420018983D9
+:100D20008CB59DB582319A4708F0A0CF80E090E85D
+:100D30000E9420012981821771F089838CB59DB5AD
+:100D400082319A4708F092CF80E090E80E9420011B
+:100D500029818213F2CF49814F3F09F434CF61E0FA
+:100D600070E08BE20E941D0289810E944502C798B3
+:100D700073CC80900D0180910E01912C982A509196
+:100D80000F01598380911001E52FF0E0F82BFC83CF
+:100D9000EB83C79AEF2B09F49CC071E1A72E71E099
+:100DA000B72E9401207E1501F5018401F12C06C0B7
+:100DB000C801807E2817390709F04AC081918F3F0A
+:100DC00009F0F3940F5F1F4F4B815C814150510933
+:100DD0005C834B835F01452B59F7FF2009F479C0F1
+:100DE00060EF80E090E80E94D7006AEA8AEA9AE819
+:100DF0000E94D70065E585E595E80E94D70065E289
+:100E000080E090E80E94D700FA94F9826F2D80E08C
+:100E100090E80E94D700F10161911F016F3FA1F09E
+:100E200074016894F7F8C7010E94D700FFEF8F1A8A
+:100E30009F0A2981222381F0598151505983F10160
+:100E400061911F016F3F61F73FEF831A930AE3CF70
+:100E5000F110C6CF4801A5CF69E280E090E80E947A
+:100E6000D7001DBC1CBCA101415051095A834983C4
+:100E70008CB59DB582319A4708F0ACCEC7010E946F
+:100E80002001882EC7010E942001982EC7010E94D0
+:100E90002001382E882D892586FF09C0892D8325BC
+:100EA00086FF05C081FCC9C085FEE2CF68CEC701C0
+:100EB0000E942001982EC7010E9420019812D8CFCD
+:100EC000E981FA8180819812D3CF2B813C81232B39
+:100ED00009F660E070E08AE00E941D02C798BCCB72
+:100EE000E0900D0180910E01F12CF82A00910F0184
+:100EF0008091100110E0182BC69A0115110509F414
+:100F0000A3C01A821982C7010E946A0029813A810E
+:100F100028273A83298388E090E00CC049815A81D0
+:100F200056954795E1E04E27E0EA5E275A834983CC
+:100F3000019759F0398130FDF1CF29813A813695F9
+:100F400027953A8329830197A9F7015011093FEFAB
+:100F5000E31AF30A01151105B1F619814A81498393
+:100F60001BBA15BA8091620080618093620062E0D2
+:100F700070E08CE00E941D02812F0E944502898151
+:100F80000E944502C69868CBE0900D0180910E0149
+:100F9000F12CF82A00910F018091100110E0182B1C
+:100FA000C69A0115110509F44CC01A821982C701AD
+:100FB0000E942001E981FA81E827FA83E98388E029
+:100FC00090E00CC029813A813695279541E024278D
+:100FD00040EA34273A832983019759F0F981F0FDDB
+:100FE000F1CFE981FA81F695E795FA83E9830197D4
+:100FF000A9F701501109FFEFEF1AFF0A01151105BA
+:10100000B1F619812A8129831BBA15BA8091620031
+:1010100080618093620062E070E088E0ABCFFB818A
+:10102000FF3F09F4D0CD61E070E08BE20E941D0229
+:101030008B810E944502C7980FCB63E070E089E284
+:10104000A1CD198210E0E0CF198210E089CFF89489
+:02105000FFCFD0
:00000001FF