hexed: add simple io commands
[megadrive.git] / hexed / pc_transfer.c
CommitLineData
f28eaaad 1/*
2 * Copyright (c) 2011, GraÅžvydas Ignotas
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the organization nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
76ba9df5 27#include <stdio.h>
28#include <stdlib.h>
8689c962 29#include <string.h>
76ba9df5 30#include <unistd.h>
31#include <sys/io.h>
32#include <signal.h>
8689c962 33#include <sys/time.h>
34
35#include "transfer.h"
76ba9df5 36
37/*
38 * PC:
39 * BASE+0 ~ data
40 * BASE+1 ~ status: BAOSEI??
41 * /BUSY, ACK, PE, SLCT, ERROR, IRQ
42 * BASE+2 ~ control: ??MISIAS
43 * bidirrectMODE, IRQ_EN, /SLCT_IN, INIT, /AUTO_FD_XT, /STROBE
44 *
45 * SEGA
46 * ?HRL3210
47 * TH, TR, TL, D3, D2, D1, D0
48 *
49 * SEGA PC
50 * 1 D0 <-> 2 D0
51 * 2 D1 <-> 3 D1
52 * 3 D2 <-> 4 D2
53 * 4 D3 <-> 5 D3
54 * 5 +5V
55 * 6 TL <-- 14 /AUTO_FD_XT
56 * 7 TH --> 10 ACK
57 * 8 GND --- 21 GND
58 * 9 TR <-- 17 /SLCT_IN
8689c962 59 *
60 * start: TH low/high, TL high
61 *
62 * TH low - lower nibble: MD ready to recv | MD sent to PC
63 * TL low - lower niblle: sent to MD | ready to recv from MD
64 * TH high - upper nibble: MD ready to recv | MD sent to PC
65 * TL high - upper nibble: sent | ready to recv from MD
76ba9df5 66 */
67
8689c962 68#define ACK_TIMEOUT 2000000
69
70#define PORT_DATA 888
71#define PORT_STATUS 889
72#define PORT_CONTROL 890
73
74#define timediff(now, start) \
75 ((now.tv_sec - start.tv_sec) * 1000000 + now.tv_usec - start.tv_usec)
76
93c5aa8a 77#define PBE2(p) ((*(p) << 8) | *(p+1))
78#define PBE3(p) ((*(p) << 16) | (*(p + 1) << 8) | *(p + 2))
79#define PBE4(p) ((*(p) << 24) | (*(p + 1) << 16) | (*(p + 2) << 8) | *(p + 3))
80
81static void do_exit(const char *msg, const char *where)
76ba9df5 82{
83 /* switch TL back to high */
8689c962 84 outb(0xe0, PORT_CONTROL);
85
93c5aa8a 86 if (where)
87 fprintf(stderr, "%s: ", where);
8689c962 88 if (msg)
93c5aa8a 89 fprintf(stderr, "%s", msg);
76ba9df5 90 exit(1);
91}
92
8689c962 93static void inthandler(int u)
76ba9df5 94{
93c5aa8a 95 do_exit("\n", NULL);
8689c962 96}
76ba9df5 97
93c5aa8a 98static void wait_th_low(const char *where)
8689c962 99{
100 struct timeval start, now;
101
102 gettimeofday(&start, NULL);
103
104 while (inb(PORT_STATUS) & 0x40) {
105 gettimeofday(&now, NULL);
106 if (timediff(now, start) > ACK_TIMEOUT)
93c5aa8a 107 do_exit("timeout waiting TH low\n", where);
76ba9df5 108 }
8689c962 109}
76ba9df5 110
93c5aa8a 111static void wait_th_high(const char *where)
8689c962 112{
113 struct timeval start, now;
114
115 gettimeofday(&start, NULL);
116
117 while (!(inb(PORT_STATUS) & 0x40)) {
118 gettimeofday(&now, NULL);
119 if (timediff(now, start) > ACK_TIMEOUT)
93c5aa8a 120 do_exit("timeout waiting TH high\n", where);
76ba9df5 121 }
8689c962 122}
123
93c5aa8a 124static void output_to_input(void)
125{
126 /* TL high, recv mode; also give time
127 * MD to see TL before we lower it in recv_byte */
128 outb(0xe0, PORT_CONTROL);
129 usleep(4*10); /* must be at least 12+8+8 M68k cycles, 28/7.67M */
130}
131
132static void input_to_output(void)
133{
134 wait_th_low("input_to_output");
135 outb(0xc0, PORT_CONTROL); /* TL high, out mode */
136}
137
8689c962 138static unsigned int recv_byte(void)
139{
140 unsigned int byte;
141
142 outb(0xe2, PORT_CONTROL); /* TL low */
143
93c5aa8a 144 wait_th_low("recv_byte");
8689c962 145
146 byte = inb(PORT_DATA) & 0x0f;
147
148 outb(0xe0, PORT_CONTROL); /* TL high */
149
93c5aa8a 150 wait_th_high("recv_byte");
8689c962 151
152 byte |= inb(PORT_DATA) << 4;
153
154 return byte;
155}
156
93c5aa8a 157static void recv_bytes(unsigned char *b, size_t count)
158{
159 while (count-- > 0)
160 *b++ = recv_byte();
161}
162
8689c962 163static void send_byte(unsigned int byte)
164{
93c5aa8a 165 wait_th_low("recv_bytes");
8689c962 166
167 outb(byte & 0x0f, PORT_DATA);
168 outb(0xc2, PORT_CONTROL); /* TL low */
169
93c5aa8a 170 wait_th_high("recv_bytes");
8689c962 171
172 outb((byte >> 4) & 0x0f, PORT_DATA);
173 outb(0xc0, PORT_CONTROL); /* TL high */
174}
175
93c5aa8a 176static void send_bytes(unsigned char *b, size_t count)
177{
178 while (count-- > 0)
179 send_byte(*b++);
180}
181
8689c962 182static void send_cmd(unsigned int cmd)
183{
184 send_byte(CMD_PREFIX);
185 send_byte(cmd);
186}
187
188static void usage(const char *argv0)
189{
190 fprintf(stderr, "usage:\n%s <cmd> [args]\n"
191 "\tsend <file> <addr> [size]\n"
272bd2ec 192 "\trecv <file> <addr> <size>\n"
193 "\tjump <addr>\n", argv0);
8689c962 194 exit(1);
195}
196
197static unsigned int atoi_or_die(const char *a)
198{
199 char *p = NULL;
200 unsigned int i;
76ba9df5 201
8689c962 202 i = strtoul(a, &p, 0);
76ba9df5 203 if (p == NULL || *p != 0) {
8689c962 204 fprintf(stderr, "atoi: can't convert: %s\n", a);
205 exit(1);
76ba9df5 206 }
207
8689c962 208 return i;
209}
210
211int main(int argc, char *argv[])
212{
93c5aa8a 213 unsigned int addr = 0, size = 0;
214 unsigned int count = 0, i = 0;
8689c962 215 int ret;
216 unsigned char *data;
217 FILE *file = NULL;
218
219 if (argc < 2)
220 usage(argv[0]);
221
222 data = malloc(0x1000000);
76ba9df5 223 if (data == NULL) {
8689c962 224 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
76ba9df5 225 return 1;
226 }
227
8689c962 228 /* parse args, read files.. */
229 if (strcmp(argv[1], "send") == 0)
230 {
231 if (argc != 4 && argc != 5)
232 usage(argv[0]);
233
234 file = fopen(argv[2], "rb");
235 if (file == NULL) {
236 fprintf(stderr, "can't open file: %s\n", argv[2]);
237 return 1;
238 }
239
240 addr = atoi_or_die(argv[3]);
241 if (argv[4] == NULL) {
242 fseek(file, 0, SEEK_END);
243 size = ftell(file);
244 fseek(file, 0, SEEK_SET);
245 }
246 else
247 size = atoi_or_die(argv[4]);
248
249 ret = fread(data, 1, size, file);
250 if (ret != size) {
251 fprintf(stderr, "fread returned %d/%d\n", ret, size);
252 perror(NULL);
253 return 1;
254 }
255 }
256 else if (strcmp(argv[1], "recv") == 0)
257 {
258 if (argc != 5)
259 usage(argv[0]);
260
261 file = fopen(argv[2], "wb");
262 if (file == NULL) {
263 fprintf(stderr, "can't open file: %s\n", argv[2]);
264 return 1;
265 }
266
267 addr = atoi_or_die(argv[3]);
268 size = atoi_or_die(argv[4]);
269
270 memset(data, 0, size);
271 }
272bd2ec 272 else if (strcmp(argv[1], "jump") == 0)
273 {
274 if (argc != 3)
275 usage(argv[0]);
276
277 addr = atoi_or_die(argv[2]);
278 }
93c5aa8a 279 else if (strcmp(argv[1], "io") == 0)
280 {
281 unsigned int cmd = 0, value, iosize;
282 unsigned char *p = data;
283
284 for (i = 2; i < argc; ) {
285 if (argv[i][0] == 'r')
286 cmd = IOSEQ_R8;
287 else if (argv[i][0] == 'w')
288 cmd = IOSEQ_W8;
289 else
290 usage(argv[0]);
291
292 iosize = atoi_or_die(&argv[i][1]);
293 if (iosize == 32)
294 cmd += 2;
295 else if (iosize == 16)
296 cmd += 1;
297 else if (iosize != 8)
298 usage(argv[0]);
299 *p++ = cmd;
300 i++;
301
302 addr = atoi_or_die(argv[i]);
303 *p++ = addr >> 16;
304 *p++ = addr >> 8;
305 *p++ = addr >> 0;
306 i++;
307
308 if (cmd == IOSEQ_W8 || cmd == IOSEQ_W16 || cmd == IOSEQ_W32) {
309 value = atoi_or_die(argv[i]);
310 switch (iosize) {
311 case 32:
312 *p++ = value >> 24;
313 *p++ = value >> 16;
314 case 16:
315 *p++ = value >> 8;
316 case 8:
317 *p++ = value >> 0;
318 }
319 i++;
320 }
321
322 count++;
323 }
324 }
8689c962 325 else
326 usage(argv[0]);
327
328 ret = ioperm(PORT_DATA, 3, 1);
76ba9df5 329 if (ret != 0) {
330 perror("ioperm");
331 return 1;
332 }
333
334 signal(SIGINT, inthandler);
335
8689c962 336 printf("regs: %02x %02x %02x\n",
337 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
338 outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */
76ba9df5 339
8689c962 340 if (inb(PORT_STATUS) & 0x40)
341 printf("waiting for TH low..\n");
342 while (inb(PORT_STATUS) & 0x40)
272bd2ec 343 usleep(100000);
8689c962 344
345 outb(0xe0, PORT_CONTROL);
76ba9df5 346
8689c962 347 if (strcmp(argv[1], "send") == 0)
76ba9df5 348 {
8689c962 349 send_cmd(CMD_MD_SEND);
350 send_byte((addr >> 16) & 0xff);
351 send_byte((addr >> 8) & 0xff);
352 send_byte((addr >> 0) & 0xff);
353 send_byte((size >> 16) & 0xff);
354 send_byte((size >> 8) & 0xff);
355 send_byte((size >> 0) & 0xff);
356
76ba9df5 357 for (i = 0; i < size; i++)
358 {
359 if ((i & 0xff) == 0) {
360 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
361 printf("%06x/%06x", i, size);
362 fflush(stdout);
363 }
364
8689c962 365 send_byte(data[i]);
76ba9df5 366 }
76ba9df5 367 }
8689c962 368 else if (strcmp(argv[1], "recv") == 0)
76ba9df5 369 {
8689c962 370 send_cmd(CMD_MD_RECV);
371 send_byte((addr >> 16) & 0xff);
372 send_byte((addr >> 8) & 0xff);
373 send_byte((addr >> 0) & 0xff);
374 send_byte((size >> 16) & 0xff);
375 send_byte((size >> 8) & 0xff);
376 send_byte((size >> 0) & 0xff);
93c5aa8a 377 output_to_input();
76ba9df5 378
379 for (i = 0; i < size; i++)
380 {
381 if ((i & 0xff) == 0) {
382 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
383 printf("%06x/%06x", i, size);
384 fflush(stdout);
385 }
386
8689c962 387 data[i] = recv_byte();
76ba9df5 388 }
8689c962 389
390 fwrite(data, 1, size, file);
76ba9df5 391 }
272bd2ec 392 else if (strcmp(argv[1], "jump") == 0)
393 {
394 send_cmd(CMD_JUMP);
395 send_byte((addr >> 16) & 0xff);
396 send_byte((addr >> 8) & 0xff);
397 send_byte((addr >> 0) & 0xff);
398 }
93c5aa8a 399 else if (strcmp(argv[1], "io") == 0)
400 {
401 unsigned char *p = data;
402 unsigned char rdata[4];
403 send_cmd(CMD_IOSEQ);
404 send_byte((count >> 8) & 0xff);
405 send_byte((count >> 0) & 0xff);
406
407 for (; count > 0; count--) {
408 input_to_output();
409 send_bytes(p, 4); /* cmd + addr */
410
411 switch (p[0]) {
412 case IOSEQ_R8:
413 output_to_input();
414 recv_bytes(rdata, 1);
415 printf("r8 %06x %02x\n", PBE3(p + 1), rdata[0]);
416 p += 4;
417 break;
418 case IOSEQ_R16:
419 output_to_input();
420 recv_bytes(rdata, 2);
421 printf("r16 %06x %04x\n", PBE3(p + 1), PBE2(rdata));
422 p += 4;
423 break;
424 case IOSEQ_R32:
425 output_to_input();
426 recv_bytes(rdata, 4);
427 printf("r32 %06x %08x\n", PBE3(p + 1), PBE4(rdata));
428 p += 4;
429 break;
430 case IOSEQ_W8:
431 send_bytes(&p[4], 1);
432 printf("w8 %06x %02x\n", PBE3(p + 1), p[4]);
433 p += 5;
434 break;
435 case IOSEQ_W16:
436 send_bytes(&p[4], 2);
437 printf("w16 %06x %04x\n", PBE3(p + 1), PBE2(p + 4));
438 p += 6;
439 break;
440 case IOSEQ_W32:
441 send_bytes(&p[4], 4);
442 printf("w32 %06x %08x\n", PBE3(p + 1), PBE4(p + 4));
443 p += 8;
444 break;
445 default:
446 do_exit("error in ioseq data\n", NULL);
447 break;
448 }
449 }
450 }
272bd2ec 451
452 if (file != NULL) {
453 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
454 printf("%06x/%06x\n", i, size);
455 fclose(file);
456 }
76ba9df5 457
8689c962 458 /* switch TL back to high, disable outputs */
459 outb(0xe0, PORT_CONTROL);
460
76ba9df5 461 return 0;
462}
463