hexed: new pc control code (send/recv only for now)
[megadrive.git] / hexed / pc_transfer.c
CommitLineData
76ba9df5 1#include <stdio.h>
2#include <stdlib.h>
8689c962 3#include <string.h>
76ba9df5 4#include <unistd.h>
5#include <sys/io.h>
6#include <signal.h>
8689c962 7#include <sys/time.h>
8
9#include "transfer.h"
76ba9df5 10
11/*
12 * PC:
13 * BASE+0 ~ data
14 * BASE+1 ~ status: BAOSEI??
15 * /BUSY, ACK, PE, SLCT, ERROR, IRQ
16 * BASE+2 ~ control: ??MISIAS
17 * bidirrectMODE, IRQ_EN, /SLCT_IN, INIT, /AUTO_FD_XT, /STROBE
18 *
19 * SEGA
20 * ?HRL3210
21 * TH, TR, TL, D3, D2, D1, D0
22 *
23 * SEGA PC
24 * 1 D0 <-> 2 D0
25 * 2 D1 <-> 3 D1
26 * 3 D2 <-> 4 D2
27 * 4 D3 <-> 5 D3
28 * 5 +5V
29 * 6 TL <-- 14 /AUTO_FD_XT
30 * 7 TH --> 10 ACK
31 * 8 GND --- 21 GND
32 * 9 TR <-- 17 /SLCT_IN
8689c962 33 *
34 * start: TH low/high, TL high
35 *
36 * TH low - lower nibble: MD ready to recv | MD sent to PC
37 * TL low - lower niblle: sent to MD | ready to recv from MD
38 * TH high - upper nibble: MD ready to recv | MD sent to PC
39 * TL high - upper nibble: sent | ready to recv from MD
76ba9df5 40 */
41
8689c962 42#define ACK_TIMEOUT 2000000
43
44#define PORT_DATA 888
45#define PORT_STATUS 889
46#define PORT_CONTROL 890
47
48#define timediff(now, start) \
49 ((now.tv_sec - start.tv_sec) * 1000000 + now.tv_usec - start.tv_usec)
50
51static void do_exit(const char *msg)
76ba9df5 52{
53 /* switch TL back to high */
8689c962 54 outb(0xe0, PORT_CONTROL);
55
56 if (msg)
57 printf("%s", msg);
76ba9df5 58 exit(1);
59}
60
8689c962 61static void inthandler(int u)
76ba9df5 62{
8689c962 63 do_exit("\n");
64}
76ba9df5 65
8689c962 66static void wait_th_low(void)
67{
68 struct timeval start, now;
69
70 gettimeofday(&start, NULL);
71
72 while (inb(PORT_STATUS) & 0x40) {
73 gettimeofday(&now, NULL);
74 if (timediff(now, start) > ACK_TIMEOUT)
75 do_exit("timeout waiting TH low\n");
76ba9df5 76 }
8689c962 77}
76ba9df5 78
8689c962 79static void wait_th_high(void)
80{
81 struct timeval start, now;
82
83 gettimeofday(&start, NULL);
84
85 while (!(inb(PORT_STATUS) & 0x40)) {
86 gettimeofday(&now, NULL);
87 if (timediff(now, start) > ACK_TIMEOUT)
88 do_exit("timeout waiting TH high\n");
76ba9df5 89 }
8689c962 90}
91
92static unsigned int recv_byte(void)
93{
94 unsigned int byte;
95
96 outb(0xe2, PORT_CONTROL); /* TL low */
97
98 wait_th_low();
99
100 byte = inb(PORT_DATA) & 0x0f;
101
102 outb(0xe0, PORT_CONTROL); /* TL high */
103
104 wait_th_high();
105
106 byte |= inb(PORT_DATA) << 4;
107
108 return byte;
109}
110
111static void send_byte(unsigned int byte)
112{
113 wait_th_low();
114
115 outb(byte & 0x0f, PORT_DATA);
116 outb(0xc2, PORT_CONTROL); /* TL low */
117
118 wait_th_high();
119
120 outb((byte >> 4) & 0x0f, PORT_DATA);
121 outb(0xc0, PORT_CONTROL); /* TL high */
122}
123
124static void send_cmd(unsigned int cmd)
125{
126 send_byte(CMD_PREFIX);
127 send_byte(cmd);
128}
129
130static void usage(const char *argv0)
131{
132 fprintf(stderr, "usage:\n%s <cmd> [args]\n"
133 "\tsend <file> <addr> [size]\n"
134 "\trecv <file> <addr> <size>\n", argv0);
135 exit(1);
136}
137
138static unsigned int atoi_or_die(const char *a)
139{
140 char *p = NULL;
141 unsigned int i;
76ba9df5 142
8689c962 143 i = strtoul(a, &p, 0);
76ba9df5 144 if (p == NULL || *p != 0) {
8689c962 145 fprintf(stderr, "atoi: can't convert: %s\n", a);
146 exit(1);
76ba9df5 147 }
148
8689c962 149 return i;
150}
151
152int main(int argc, char *argv[])
153{
154 unsigned int addr = 0, size = 0, i = 0;
155 int ret;
156 unsigned char *data;
157 FILE *file = NULL;
158
159 if (argc < 2)
160 usage(argv[0]);
161
162 data = malloc(0x1000000);
76ba9df5 163 if (data == NULL) {
8689c962 164 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
76ba9df5 165 return 1;
166 }
167
8689c962 168 /* parse args, read files.. */
169 if (strcmp(argv[1], "send") == 0)
170 {
171 if (argc != 4 && argc != 5)
172 usage(argv[0]);
173
174 file = fopen(argv[2], "rb");
175 if (file == NULL) {
176 fprintf(stderr, "can't open file: %s\n", argv[2]);
177 return 1;
178 }
179
180 addr = atoi_or_die(argv[3]);
181 if (argv[4] == NULL) {
182 fseek(file, 0, SEEK_END);
183 size = ftell(file);
184 fseek(file, 0, SEEK_SET);
185 }
186 else
187 size = atoi_or_die(argv[4]);
188
189 ret = fread(data, 1, size, file);
190 if (ret != size) {
191 fprintf(stderr, "fread returned %d/%d\n", ret, size);
192 perror(NULL);
193 return 1;
194 }
195 }
196 else if (strcmp(argv[1], "recv") == 0)
197 {
198 if (argc != 5)
199 usage(argv[0]);
200
201 file = fopen(argv[2], "wb");
202 if (file == NULL) {
203 fprintf(stderr, "can't open file: %s\n", argv[2]);
204 return 1;
205 }
206
207 addr = atoi_or_die(argv[3]);
208 size = atoi_or_die(argv[4]);
209
210 memset(data, 0, size);
211 }
212 else
213 usage(argv[0]);
214
215 ret = ioperm(PORT_DATA, 3, 1);
76ba9df5 216 if (ret != 0) {
217 perror("ioperm");
218 return 1;
219 }
220
221 signal(SIGINT, inthandler);
222
8689c962 223 printf("regs: %02x %02x %02x\n",
224 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
225 outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */
76ba9df5 226
8689c962 227 if (inb(PORT_STATUS) & 0x40)
228 printf("waiting for TH low..\n");
229 while (inb(PORT_STATUS) & 0x40)
230 sleep(1);
231
232 outb(0xe0, PORT_CONTROL);
76ba9df5 233
8689c962 234 if (strcmp(argv[1], "send") == 0)
76ba9df5 235 {
8689c962 236 printf("send %06x %06x\n", addr, size);
237 send_cmd(CMD_MD_SEND);
238 send_byte((addr >> 16) & 0xff);
239 send_byte((addr >> 8) & 0xff);
240 send_byte((addr >> 0) & 0xff);
241 send_byte((size >> 16) & 0xff);
242 send_byte((size >> 8) & 0xff);
243 send_byte((size >> 0) & 0xff);
244
76ba9df5 245 for (i = 0; i < size; i++)
246 {
247 if ((i & 0xff) == 0) {
248 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
249 printf("%06x/%06x", i, size);
250 fflush(stdout);
251 }
252
8689c962 253 send_byte(data[i]);
76ba9df5 254 }
76ba9df5 255 }
8689c962 256 else if (strcmp(argv[1], "recv") == 0)
76ba9df5 257 {
8689c962 258 send_cmd(CMD_MD_RECV);
259 send_byte((addr >> 16) & 0xff);
260 send_byte((addr >> 8) & 0xff);
261 send_byte((addr >> 0) & 0xff);
262 send_byte((size >> 16) & 0xff);
263 send_byte((size >> 8) & 0xff);
264 send_byte((size >> 0) & 0xff);
265 outb(0xe0, PORT_CONTROL); /* TL high, recv mode */
76ba9df5 266
267 for (i = 0; i < size; i++)
268 {
269 if ((i & 0xff) == 0) {
270 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
271 printf("%06x/%06x", i, size);
272 fflush(stdout);
273 }
274
8689c962 275 data[i] = recv_byte();
76ba9df5 276 }
8689c962 277
278 fwrite(data, 1, size, file);
76ba9df5 279 }
280 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
281 printf("%06x/%06x\n", i, size);
282 fclose(file);
283
8689c962 284 /* switch TL back to high, disable outputs */
285 outb(0xe0, PORT_CONTROL);
286
76ba9df5 287 return 0;
288}
289