nuclear@3: /* nuclear@5: Megadrive USB development cartridge prototype nuclear@3: Copyright (C) 2017 John Tsiombikas nuclear@3: nuclear@3: This program is free software: you can redistribute it and/or modify nuclear@3: it under the terms of the GNU General Public License as published by nuclear@3: the Free Software Foundation, either version 3 of the License, or nuclear@3: (at your option) any later version. nuclear@3: nuclear@3: This program is distributed in the hope that it will be useful, nuclear@3: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@3: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@3: GNU General Public License for more details. nuclear@3: nuclear@3: You should have received a copy of the GNU General Public License nuclear@3: along with this program. If not, see . nuclear@3: */ nuclear@2: #define F_CPU XTAL nuclear@2: nuclear@0: #include nuclear@1: #include nuclear@0: #include nuclear@0: #include nuclear@2: #include nuclear@0: #include nuclear@0: #include nuclear@2: #include nuclear@0: #include "serial.h" nuclear@0: nuclear@1: /* pin assignments: nuclear@5: * A[0,7] A0 - A7 nuclear@5: * C[0,7] A8 - A15 nuclear@5: * D2 A16 nuclear@5: * B[0, 7] data nuclear@5: * D4 ~WE nuclear@5: * D5 ~OE nuclear@5: * D7 ~SYS_RESET nuclear@1: */ nuclear@1: nuclear@5: #define A16_BIT 0x04 nuclear@5: #define WE_BIT 0x10 nuclear@5: #define OE_BIT 0x20 nuclear@5: #define RST_BIT 0x80 nuclear@1: nuclear@5: #define A16_SHIFT 2 nuclear@1: nuclear@5: #define MEM_SIZE (128L * 1024L) nuclear@5: nuclear@5: #define VER_STR "0.1" nuclear@5: nuclear@5: void proc_cmd(char *input); nuclear@5: void run_mode(void); nuclear@5: void prog_mode(void); nuclear@5: void sram_write(uint32_t addr, unsigned char val); nuclear@5: unsigned char sram_read(uint32_t addr); nuclear@0: nuclear@0: #define MAX_INPUT_SIZE 128 nuclear@0: static char input[MAX_INPUT_SIZE]; nuclear@0: static unsigned char inp_cidx; nuclear@0: nuclear@5: uint32_t addr; nuclear@5: unsigned char running; nuclear@5: int echo = 0; nuclear@1: nuclear@0: int main(void) nuclear@0: { nuclear@6: init_serial(115200); nuclear@5: run_mode(); nuclear@5: nuclear@0: sei(); nuclear@0: nuclear@0: for(;;) { nuclear@2: if(have_input()) { nuclear@0: int c = getchar(); nuclear@5: if(echo) { nuclear@5: putchar(c); nuclear@5: } nuclear@0: nuclear@0: if(c == '\r' || c == '\n') { nuclear@0: input[inp_cidx] = 0; nuclear@1: proc_cmd(input); nuclear@0: inp_cidx = 0; nuclear@0: } else if(inp_cidx < sizeof input - 1) { nuclear@0: input[inp_cidx++] = c; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@5: void proc_cmd(char *input) nuclear@0: { nuclear@5: char *endp; nuclear@5: int data; nuclear@5: nuclear@5: switch(input[0]) { nuclear@5: case 'e': nuclear@5: echo = input[1] == '1' ? 1 : 0; nuclear@5: printf("OK echo %s\n", echo ? "on" : "off"); nuclear@5: break; nuclear@5: nuclear@5: case 'p': nuclear@5: prog_mode(); nuclear@6: puts("OK programming mode"); nuclear@5: break; nuclear@5: nuclear@5: case 'b': nuclear@5: run_mode(); nuclear@6: puts("OK run mode"); nuclear@5: break; nuclear@5: nuclear@5: case 'a': nuclear@5: addr = strtol(input + 1, &endp, 0); nuclear@5: printf("OK address: %lx\n", (unsigned long)addr); nuclear@5: break; nuclear@5: nuclear@5: case 'w': nuclear@5: if(running) { nuclear@6: puts("ERR running"); nuclear@5: break; nuclear@5: } nuclear@5: if(addr >= MEM_SIZE) { nuclear@6: puts("ERR overflow"); nuclear@5: break; nuclear@5: } nuclear@5: nuclear@5: data = strtol(input + 1, &endp, 0); nuclear@5: sram_write(addr++, data); nuclear@6: puts("OK"); nuclear@5: break; nuclear@5: nuclear@5: case 'r': nuclear@5: if(running) { nuclear@6: puts("ERR running"); nuclear@5: break; nuclear@5: } nuclear@5: if(addr >= MEM_SIZE) { nuclear@6: puts("ERR overflow"); nuclear@5: break; nuclear@5: } nuclear@5: nuclear@5: data = sram_read(addr++); nuclear@5: printf("OK %d\n", (int)data); nuclear@5: break; nuclear@5: nuclear@5: default: nuclear@6: puts("ERR unknown command"); nuclear@5: break; nuclear@5: } nuclear@0: } nuclear@1: nuclear@5: void run_mode(void) nuclear@1: { nuclear@5: /* tri-state everything and release the reset line */ nuclear@6: /* do not tri-state WE and keep it high */ nuclear@5: DDRA = 0; nuclear@5: PORTA = 0; nuclear@5: DDRB = 0; nuclear@5: PORTB = 0; nuclear@5: DDRC = 0; nuclear@5: PORTC = 0; nuclear@6: DDRD = RST_BIT | WE_BIT; nuclear@6: PORTD = RST_BIT | WE_BIT; nuclear@2: nuclear@5: running = 1; nuclear@1: } nuclear@1: nuclear@5: void prog_mode(void) nuclear@5: { nuclear@5: /* hold the reset line and take control of the bus */ nuclear@6: PORTD = WE_BIT; /* keep WE high to avoid writing random bytes */ nuclear@5: DDRD = 0xff; nuclear@5: DDRA = 0xff; nuclear@5: DDRB = 0xff; nuclear@5: DDRC = 0xff; nuclear@5: nuclear@5: running = 0; nuclear@5: } nuclear@5: nuclear@5: void set_address(uint32_t addr) nuclear@5: { nuclear@5: PORTA = addr & 0xff; nuclear@5: PORTC = (addr >> 8) & 0xff; nuclear@5: PORTD = (PORTD & ~A16_BIT) | ((addr >> (16 - A16_SHIFT)) & A16_BIT); nuclear@5: } nuclear@5: nuclear@5: void sram_write(uint32_t addr, unsigned char val) nuclear@5: { nuclear@5: set_address(addr); nuclear@5: nuclear@5: /* no need for DDRB change, we drive the bus by default in programming mode */ nuclear@5: PORTB = val; /* set data */ nuclear@7: _delay_us(0.1); nuclear@5: nuclear@5: /* pulse WE */ nuclear@5: PORTD &= ~WE_BIT; nuclear@7: _delay_us(0.1); nuclear@5: /* WE should be low for at least 9ns, which is way faster than we can toggle it anyway */ nuclear@5: PORTD |= WE_BIT; nuclear@5: } nuclear@5: nuclear@5: unsigned char sram_read(uint32_t addr) nuclear@1: { nuclear@1: unsigned char val; nuclear@1: nuclear@5: set_address(addr); nuclear@1: nuclear@5: PORTB = 0; /* make sure we won't read the previously written value */ nuclear@1: nuclear@5: DDRB = 0; /* release the data bus */ nuclear@5: PORTD &= ~OE_BIT; /* assert OE (output enable) */ nuclear@7: _delay_us(1); nuclear@5: val = PINB; /* read the data */ nuclear@5: PORTD |= OE_BIT; /* deassert OE */ nuclear@5: DDRB = 0xff; /* take back the bus */ nuclear@1: nuclear@1: return val; nuclear@1: }