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