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