It works ... jesus FUCKING christ, I just had been feeding the address bus shift...
[romdev] / fw / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <avr/io.h>
5 #include <avr/interrupt.h>
6 #include <util/delay.h>
7 #include "serial.h"
8
9 /* pin assignments
10  * B[0,1]: D0,D1
11  * D[2,7]: D2-D7
12  * B2: SPI ~SS/shiftreg ~OE
13  * B3: SPI MOSI
14  * B4: SPI MISO (unused)
15  * B5: SPI SCK
16  * C0: programming mode PGM
17  * C1: RAM ~CS
18  * C2: RAM ~WE
19  * C3: RAM ~OE
20  * C5: ~SYSRST
21  */
22 #define PB_SS           0x04
23 #define PB_MOSI         0x08
24 #define PB_MISO         0x10
25 #define PB_SCK          0x20
26 #define PC_PGM          0x01
27 #define PC_CS           0x02
28 #define PC_WE           0x04
29 #define PC_OE           0x08
30 #define PC_SYSRST       0x20
31
32 static void proc_cmd(char *input);
33 static void start_prog(void);
34 static void end_prog(void);
35 static void write_ram(uint16_t addr, uint8_t val);
36 static uint8_t read_ram(uint16_t addr);
37 static void dbg_setup_read(uint16_t addr);
38 static void set_data_bus(unsigned char val);
39 static void release_data_bus(void);
40 static void set_addr_bus(uint16_t addr);
41 static void sys_reset(void);
42 static inline void iodelay(void);
43
44 static int echo;
45 static int progmode;
46 static uint16_t addr;
47
48 static char input[128];
49 static unsigned char inp_cidx;
50
51
52 int main(void)
53 {
54         /* SPI (SS/MOSI/SCK) are outputs */
55         DDRB = PB_SS | PB_MOSI | PB_SCK;
56         PORTB = PB_SS;  /* drive SS high when not in programming mode */
57         DDRC = 0xff;    /* control signals are all outputs */
58         PORTC = PC_CS | PC_WE | PC_OE | PC_SYSRST;
59         DDRD = 0;       /* tri-state the data bus by default (high 6 bits, low 2 are in DDRB) */
60         PORTD = 0;
61
62         /* disable all pullups */
63         MCUCR |= 1 << PUD;
64
65         /* enable SPI (used to talk to the address bus shift registers)
66          * SPI master mode, clock / 16, CPOL=0/CPHA=0 because shiftreg clocks on the
67          * rising edge, and MSB first
68          */
69         SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
70
71         /* init the serial port we use to talk to the host */
72         init_serial(38400);
73         sei();
74
75         for(;;) {
76                 if(have_input()) {
77                         int c = getchar();
78                         if(echo) {
79                                 putchar(c);
80                         }
81
82                         if(c == '\r' || c == '\n') {
83                                 input[inp_cidx] = 0;
84                                 proc_cmd(input);
85                                 inp_cidx = 0;
86                         } else if(inp_cidx < sizeof input - 1) {
87                                 input[inp_cidx++] = c;
88                         }
89                 }
90         }
91         return 0;
92 }
93
94 static void proc_cmd(char *input)
95 {
96         char *endp;
97         int data;
98
99         switch(input[0]) {
100         case 'e':
101                 echo = input[1] == '1' ? 1 : 0;
102                 printf("OK echo %s\n", echo ? "on" : "off");
103                 break;
104
105         case 'p':
106                 if(progmode) {
107                         puts("ERR already in programming mode");
108                 } else {
109                         start_prog();
110                         puts("OK programming mode");
111                 }
112                 break;
113
114         case 'b':
115                 puts("OK booting");
116                 if(!progmode) {
117                         sys_reset();
118                 } else {
119                         end_prog();
120                 }
121                 break;
122
123         case 'a':
124                 addr = strtol(input + 1, &endp, 0);
125                 printf("OK address: %x\n", (unsigned int)addr);
126                 break;
127
128         case 'w':
129                 if(!progmode) {
130                         puts("ERR not in programming mode");
131                         break;
132                 }
133                 data = strtol(input + 1, &endp, 0);
134                 write_ram(addr++, data);
135                 puts("OK");
136                 break;
137
138         case 'r':
139                 if(!progmode) {
140                         puts("ERR not in programming mode");
141                         break;
142                 }
143                 data = read_ram(addr++);
144                 printf("OK %d\n", (int)data);
145                 break;
146
147         case 'R':
148                 if(!progmode) {
149                         puts("ERR not in programming mode");
150                         break;
151                 }
152                 dbg_setup_read(addr);
153                 printf("OK set up read from %x\n", (unsigned int)addr);
154                 break;
155
156         case 'A':
157                 if(!progmode) {
158                         puts("ERR not in programming mode");
159                         break;
160                 }
161                 addr = strtol(input + 1, &endp, 0);
162                 set_addr_bus(addr);
163                 printf("OK DBG address %x\n", (unsigned int)addr);
164                 break;
165
166         case '?':
167                 puts("OK command help");
168                 puts(" e 0|1: turn echo on/off");
169                 puts(" p: enter programming mode");
170                 puts(" b: exit programming mode and boot up");
171                 puts(" a <addr>: set address counter (0-ffff)");
172                 puts(" w <data>: store data byte and increment address (0-255)");
173                 puts(" r: read data byte and increment address");
174                 puts(" ?: print command help");
175                 printf("Currently in %s mode\n", progmode ? "programming" : "boot");
176                 break;
177
178         default:
179                 printf("ERR unknown command: '%c'\n", input[0]);
180         }
181 }
182
183 static void start_prog(void)
184 {
185         /* hold reset low, and take over the RAM */
186         PORTC &= ~PC_SYSRST;
187         PORTC |= PC_PGM;
188
189         progmode = 1;
190 }
191
192 static void end_prog(void)
193 {
194         /* make extra sure we don't drive the data bus */
195         release_data_bus();
196
197         /* PGM low and return reset and SS high */
198         PORTB |= PB_SS;
199         PORTC &= ~PC_PGM;
200         PORTC |= PC_SYSRST | PC_CS | PC_OE;
201
202         progmode = 0;
203 }
204
205 static void write_ram(uint16_t addr, uint8_t val)
206 {
207         set_addr_bus(addr);
208         set_data_bus(val);
209         PORTC |= PC_OE;
210         PORTC &= ~(PC_WE | PC_CS);
211         iodelay();
212         PORTC |= PC_CS;
213         PORTC |= PC_WE;
214         release_data_bus();
215 }
216
217 static uint8_t read_ram(uint16_t addr)
218 {
219         uint8_t val;
220         set_addr_bus(addr);
221         PORTC |= PC_WE;
222         PORTC &= ~(PC_CS | PC_OE);
223         iodelay();
224         val = (PIND & 0xfc) | (PINB & 3);
225         PORTC |= PC_CS | PC_OE;
226         return val;
227 }
228
229 static void dbg_setup_read(uint16_t addr)
230 {
231         set_addr_bus(addr);
232         PORTC |= PC_WE;
233         PORTC &= ~(PC_CS | PC_OE);
234 }
235
236
237 static void set_data_bus(unsigned char val)
238 {
239         /* drive the data bus */
240         DDRD = 0xff;
241         DDRB |= 3;
242         PORTB = (PORTB & 0xfc) | (val & 3);
243         PORTD = val;
244 }
245
246 static void release_data_bus(void)
247 {
248         DDRD = 0;
249         DDRB &= 0xfc;
250
251         PORTD = 0;
252         PORTB &= 0xfc;
253 }
254
255 static void set_addr_bus(uint16_t addr)
256 {
257         PORTB &= ~PB_SS;
258         SPDR = (uint8_t)(addr >> 8);
259         while(!(SPSR & (1 << SPIF)));
260         SPDR = (uint8_t)addr;
261         while(!(SPSR & (1 << SPIF)));
262
263         PORTB |= PB_SS;
264         iodelay();
265         PORTB &= ~PB_SS;        /* SS must return low because the shiftreg OE is tied to it */
266 }
267
268 static void sys_reset(void)
269 {
270         PORTC &= ~PC_SYSRST;
271         _delay_ms(500);
272         PORTC |= PC_SYSRST;
273 }
274
275 static inline void iodelay(void)
276 {
277         asm volatile("nop");
278         asm volatile("nop");
279         asm volatile("nop");
280 }