hexed: add simple io commands
[megadrive.git] / hexed / pc_transfer.c
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  */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/io.h>
32 #include <signal.h>
33 #include <sys/time.h>
34
35 #include "transfer.h"
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
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
66  */
67
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
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
81 static void do_exit(const char *msg, const char *where)
82 {
83         /* switch TL back to high */
84         outb(0xe0, PORT_CONTROL);
85
86         if (where)
87                 fprintf(stderr, "%s: ", where);
88         if (msg)
89                 fprintf(stderr, "%s", msg);
90         exit(1);
91 }
92
93 static void inthandler(int u)
94 {
95         do_exit("\n", NULL);
96 }
97
98 static void wait_th_low(const char *where)
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)
107                         do_exit("timeout waiting TH low\n", where);
108         }
109 }
110
111 static void wait_th_high(const char *where)
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)
120                         do_exit("timeout waiting TH high\n", where);
121         }
122 }
123
124 static 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
132 static void input_to_output(void)
133 {
134         wait_th_low("input_to_output");
135         outb(0xc0, PORT_CONTROL);       /* TL high, out mode */
136 }
137
138 static unsigned int recv_byte(void)
139 {
140         unsigned int byte;
141
142         outb(0xe2, PORT_CONTROL);       /* TL low */
143
144         wait_th_low("recv_byte");
145
146         byte = inb(PORT_DATA) & 0x0f;
147
148         outb(0xe0, PORT_CONTROL);       /* TL high */
149
150         wait_th_high("recv_byte");
151
152         byte |= inb(PORT_DATA) << 4;
153
154         return byte;
155 }
156
157 static void recv_bytes(unsigned char *b, size_t count)
158 {
159         while (count-- > 0)
160                 *b++ = recv_byte();
161 }
162
163 static void send_byte(unsigned int byte)
164 {
165         wait_th_low("recv_bytes");
166
167         outb(byte & 0x0f, PORT_DATA);
168         outb(0xc2, PORT_CONTROL);       /* TL low */
169
170         wait_th_high("recv_bytes");
171
172         outb((byte >> 4) & 0x0f, PORT_DATA);
173         outb(0xc0, PORT_CONTROL);       /* TL high */
174 }
175
176 static void send_bytes(unsigned char *b, size_t count)
177 {
178         while (count-- > 0)
179                 send_byte(*b++);
180 }
181
182 static void send_cmd(unsigned int cmd)
183 {
184         send_byte(CMD_PREFIX);
185         send_byte(cmd);
186 }
187
188 static void usage(const char *argv0)
189 {
190         fprintf(stderr, "usage:\n%s <cmd> [args]\n"
191                 "\tsend <file> <addr> [size]\n"
192                 "\trecv <file> <addr> <size>\n"
193                 "\tjump <addr>\n", argv0);
194         exit(1);
195 }
196
197 static unsigned int atoi_or_die(const char *a)
198 {
199         char *p = NULL;
200         unsigned int i;
201
202         i = strtoul(a, &p, 0);
203         if (p == NULL || *p != 0) {
204                 fprintf(stderr, "atoi: can't convert: %s\n", a);
205                 exit(1);
206         }
207
208         return i;
209 }
210
211 int main(int argc, char *argv[])
212 {
213         unsigned int addr = 0, size = 0;
214         unsigned int count = 0, i = 0;
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);
223         if (data == NULL) {
224                 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
225                 return 1;
226         }
227
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         }
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         }
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         }
325         else
326                 usage(argv[0]);
327
328         ret = ioperm(PORT_DATA, 3, 1);
329         if (ret != 0) {
330                 perror("ioperm");
331                 return 1;
332         }
333
334         signal(SIGINT, inthandler);
335
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 */
339
340         if (inb(PORT_STATUS) & 0x40)
341                 printf("waiting for TH low..\n");
342         while (inb(PORT_STATUS) & 0x40)
343                 usleep(100000);
344
345         outb(0xe0, PORT_CONTROL);
346
347         if (strcmp(argv[1], "send") == 0)
348         {
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
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
365                         send_byte(data[i]);
366                 }
367         }
368         else if (strcmp(argv[1], "recv") == 0)
369         {
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);
377                 output_to_input();
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
387                         data[i] = recv_byte();
388                 }
389
390                 fwrite(data, 1, size, file);
391         }
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         }
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         }
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         }
457
458         /* switch TL back to high, disable outputs */
459         outb(0xe0, PORT_CONTROL);
460
461         return 0;
462 }
463