rev |
line source |
nuclear@3
|
1 /*
|
nuclear@5
|
2 Megadrive USB development cartridge prototype
|
nuclear@3
|
3 Copyright (C) 2017 John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@3
|
4
|
nuclear@3
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@3
|
6 it under the terms of the GNU General Public License as published by
|
nuclear@3
|
7 the Free Software Foundation, either version 3 of the License, or
|
nuclear@3
|
8 (at your option) any later version.
|
nuclear@3
|
9
|
nuclear@3
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@3
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@3
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@3
|
13 GNU General Public License for more details.
|
nuclear@3
|
14
|
nuclear@3
|
15 You should have received a copy of the GNU General Public License
|
nuclear@3
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@3
|
17 */
|
nuclear@2
|
18 #define F_CPU XTAL
|
nuclear@2
|
19
|
nuclear@0
|
20 #include <stdio.h>
|
nuclear@1
|
21 #include <stdlib.h>
|
nuclear@0
|
22 #include <string.h>
|
nuclear@0
|
23 #include <ctype.h>
|
nuclear@2
|
24 #include <stdint.h>
|
nuclear@0
|
25 #include <avr/io.h>
|
nuclear@0
|
26 #include <avr/interrupt.h>
|
nuclear@2
|
27 #include <util/delay.h>
|
nuclear@0
|
28 #include "serial.h"
|
nuclear@0
|
29
|
nuclear@1
|
30 /* pin assignments:
|
nuclear@5
|
31 * A[0,7] A0 - A7
|
nuclear@5
|
32 * C[0,7] A8 - A15
|
nuclear@5
|
33 * D2 A16
|
nuclear@5
|
34 * B[0, 7] data
|
nuclear@5
|
35 * D4 ~WE
|
nuclear@5
|
36 * D5 ~OE
|
nuclear@5
|
37 * D7 ~SYS_RESET
|
nuclear@1
|
38 */
|
nuclear@1
|
39
|
nuclear@5
|
40 #define A16_BIT 0x04
|
nuclear@5
|
41 #define WE_BIT 0x10
|
nuclear@5
|
42 #define OE_BIT 0x20
|
nuclear@5
|
43 #define RST_BIT 0x80
|
nuclear@1
|
44
|
nuclear@5
|
45 #define A16_SHIFT 2
|
nuclear@1
|
46
|
nuclear@5
|
47 #define MEM_SIZE (128L * 1024L)
|
nuclear@5
|
48
|
nuclear@5
|
49 #define VER_STR "0.1"
|
nuclear@5
|
50
|
nuclear@5
|
51 void proc_cmd(char *input);
|
nuclear@5
|
52 void run_mode(void);
|
nuclear@5
|
53 void prog_mode(void);
|
nuclear@5
|
54 void sram_write(uint32_t addr, unsigned char val);
|
nuclear@5
|
55 unsigned char sram_read(uint32_t addr);
|
nuclear@0
|
56
|
nuclear@0
|
57 #define MAX_INPUT_SIZE 128
|
nuclear@0
|
58 static char input[MAX_INPUT_SIZE];
|
nuclear@0
|
59 static unsigned char inp_cidx;
|
nuclear@0
|
60
|
nuclear@5
|
61 uint32_t addr;
|
nuclear@5
|
62 unsigned char running;
|
nuclear@5
|
63 int echo = 0;
|
nuclear@1
|
64
|
nuclear@0
|
65 int main(void)
|
nuclear@0
|
66 {
|
nuclear@6
|
67 init_serial(115200);
|
nuclear@5
|
68 run_mode();
|
nuclear@5
|
69
|
nuclear@0
|
70 sei();
|
nuclear@0
|
71
|
nuclear@0
|
72 for(;;) {
|
nuclear@2
|
73 if(have_input()) {
|
nuclear@0
|
74 int c = getchar();
|
nuclear@5
|
75 if(echo) {
|
nuclear@5
|
76 putchar(c);
|
nuclear@5
|
77 }
|
nuclear@0
|
78
|
nuclear@0
|
79 if(c == '\r' || c == '\n') {
|
nuclear@0
|
80 input[inp_cidx] = 0;
|
nuclear@1
|
81 proc_cmd(input);
|
nuclear@0
|
82 inp_cidx = 0;
|
nuclear@0
|
83 } else if(inp_cidx < sizeof input - 1) {
|
nuclear@0
|
84 input[inp_cidx++] = c;
|
nuclear@0
|
85 }
|
nuclear@0
|
86 }
|
nuclear@0
|
87 }
|
nuclear@0
|
88 return 0;
|
nuclear@0
|
89 }
|
nuclear@0
|
90
|
nuclear@5
|
91 void proc_cmd(char *input)
|
nuclear@0
|
92 {
|
nuclear@5
|
93 char *endp;
|
nuclear@5
|
94 int data;
|
nuclear@5
|
95
|
nuclear@5
|
96 switch(input[0]) {
|
nuclear@5
|
97 case 'e':
|
nuclear@5
|
98 echo = input[1] == '1' ? 1 : 0;
|
nuclear@5
|
99 printf("OK echo %s\n", echo ? "on" : "off");
|
nuclear@5
|
100 break;
|
nuclear@5
|
101
|
nuclear@5
|
102 case 'p':
|
nuclear@5
|
103 prog_mode();
|
nuclear@6
|
104 puts("OK programming mode");
|
nuclear@5
|
105 break;
|
nuclear@5
|
106
|
nuclear@5
|
107 case 'b':
|
nuclear@5
|
108 run_mode();
|
nuclear@6
|
109 puts("OK run mode");
|
nuclear@5
|
110 break;
|
nuclear@5
|
111
|
nuclear@5
|
112 case 'a':
|
nuclear@5
|
113 addr = strtol(input + 1, &endp, 0);
|
nuclear@5
|
114 printf("OK address: %lx\n", (unsigned long)addr);
|
nuclear@5
|
115 break;
|
nuclear@5
|
116
|
nuclear@5
|
117 case 'w':
|
nuclear@5
|
118 if(running) {
|
nuclear@6
|
119 puts("ERR running");
|
nuclear@5
|
120 break;
|
nuclear@5
|
121 }
|
nuclear@5
|
122 if(addr >= MEM_SIZE) {
|
nuclear@6
|
123 puts("ERR overflow");
|
nuclear@5
|
124 break;
|
nuclear@5
|
125 }
|
nuclear@5
|
126
|
nuclear@5
|
127 data = strtol(input + 1, &endp, 0);
|
nuclear@5
|
128 sram_write(addr++, data);
|
nuclear@6
|
129 puts("OK");
|
nuclear@5
|
130 break;
|
nuclear@5
|
131
|
nuclear@5
|
132 case 'r':
|
nuclear@5
|
133 if(running) {
|
nuclear@6
|
134 puts("ERR running");
|
nuclear@5
|
135 break;
|
nuclear@5
|
136 }
|
nuclear@5
|
137 if(addr >= MEM_SIZE) {
|
nuclear@6
|
138 puts("ERR overflow");
|
nuclear@5
|
139 break;
|
nuclear@5
|
140 }
|
nuclear@5
|
141
|
nuclear@5
|
142 data = sram_read(addr++);
|
nuclear@5
|
143 printf("OK %d\n", (int)data);
|
nuclear@5
|
144 break;
|
nuclear@5
|
145
|
nuclear@5
|
146 default:
|
nuclear@6
|
147 puts("ERR unknown command");
|
nuclear@5
|
148 break;
|
nuclear@5
|
149 }
|
nuclear@0
|
150 }
|
nuclear@1
|
151
|
nuclear@5
|
152 void run_mode(void)
|
nuclear@1
|
153 {
|
nuclear@5
|
154 /* tri-state everything and release the reset line */
|
nuclear@6
|
155 /* do not tri-state WE and keep it high */
|
nuclear@5
|
156 DDRA = 0;
|
nuclear@5
|
157 PORTA = 0;
|
nuclear@5
|
158 DDRB = 0;
|
nuclear@5
|
159 PORTB = 0;
|
nuclear@5
|
160 DDRC = 0;
|
nuclear@5
|
161 PORTC = 0;
|
nuclear@6
|
162 DDRD = RST_BIT | WE_BIT;
|
nuclear@6
|
163 PORTD = RST_BIT | WE_BIT;
|
nuclear@2
|
164
|
nuclear@5
|
165 running = 1;
|
nuclear@1
|
166 }
|
nuclear@1
|
167
|
nuclear@5
|
168 void prog_mode(void)
|
nuclear@5
|
169 {
|
nuclear@5
|
170 /* hold the reset line and take control of the bus */
|
nuclear@6
|
171 PORTD = WE_BIT; /* keep WE high to avoid writing random bytes */
|
nuclear@5
|
172 DDRD = 0xff;
|
nuclear@5
|
173 DDRA = 0xff;
|
nuclear@5
|
174 DDRB = 0xff;
|
nuclear@5
|
175 DDRC = 0xff;
|
nuclear@5
|
176
|
nuclear@5
|
177 running = 0;
|
nuclear@5
|
178 }
|
nuclear@5
|
179
|
nuclear@5
|
180 void set_address(uint32_t addr)
|
nuclear@5
|
181 {
|
nuclear@5
|
182 PORTA = addr & 0xff;
|
nuclear@5
|
183 PORTC = (addr >> 8) & 0xff;
|
nuclear@5
|
184 PORTD = (PORTD & ~A16_BIT) | ((addr >> (16 - A16_SHIFT)) & A16_BIT);
|
nuclear@5
|
185 }
|
nuclear@5
|
186
|
nuclear@5
|
187 void sram_write(uint32_t addr, unsigned char val)
|
nuclear@5
|
188 {
|
nuclear@5
|
189 set_address(addr);
|
nuclear@5
|
190
|
nuclear@5
|
191 /* no need for DDRB change, we drive the bus by default in programming mode */
|
nuclear@5
|
192 PORTB = val; /* set data */
|
nuclear@5
|
193
|
nuclear@5
|
194 /* pulse WE */
|
nuclear@5
|
195 PORTD &= ~WE_BIT;
|
nuclear@5
|
196 /* WE should be low for at least 9ns, which is way faster than we can toggle it anyway */
|
nuclear@5
|
197 PORTD |= WE_BIT;
|
nuclear@5
|
198 }
|
nuclear@5
|
199
|
nuclear@5
|
200 unsigned char sram_read(uint32_t addr)
|
nuclear@1
|
201 {
|
nuclear@1
|
202 unsigned char val;
|
nuclear@1
|
203
|
nuclear@5
|
204 set_address(addr);
|
nuclear@1
|
205
|
nuclear@5
|
206 PORTB = 0; /* make sure we won't read the previously written value */
|
nuclear@1
|
207
|
nuclear@5
|
208 DDRB = 0; /* release the data bus */
|
nuclear@5
|
209 PORTD &= ~OE_BIT; /* assert OE (output enable) */
|
nuclear@5
|
210 _delay_us(0.1);
|
nuclear@5
|
211 val = PINB; /* read the data */
|
nuclear@5
|
212 PORTD |= OE_BIT; /* deassert OE */
|
nuclear@5
|
213 DDRB = 0xff; /* take back the bus */
|
nuclear@1
|
214
|
nuclear@1
|
215 return val;
|
nuclear@1
|
216 }
|