2263a6d232cba6683606965725a4077fda5d14b8
[megadrive.git] / hexed / pc_transfer.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/io.h>
6 #include <signal.h>
7 #include <sys/time.h>
8
9 #include "transfer.h"
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
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
40  */
41
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
51 static void do_exit(const char *msg)
52 {
53         /* switch TL back to high */
54         outb(0xe0, PORT_CONTROL);
55
56         if (msg)
57                 printf("%s", msg);
58         exit(1);
59 }
60
61 static void inthandler(int u)
62 {
63         do_exit("\n");
64 }
65
66 static 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");
76         }
77 }
78
79 static 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");
89         }
90 }
91
92 static 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
111 static 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
124 static void send_cmd(unsigned int cmd)
125 {
126         send_byte(CMD_PREFIX);
127         send_byte(cmd);
128 }
129
130 static 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
138 static unsigned int atoi_or_die(const char *a)
139 {
140         char *p = NULL;
141         unsigned int i;
142
143         i = strtoul(a, &p, 0);
144         if (p == NULL || *p != 0) {
145                 fprintf(stderr, "atoi: can't convert: %s\n", a);
146                 exit(1);
147         }
148
149         return i;
150 }
151
152 int 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);
163         if (data == NULL) {
164                 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
165                 return 1;
166         }
167
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);
216         if (ret != 0) {
217                 perror("ioperm");
218                 return 1;
219         }
220
221         signal(SIGINT, inthandler);
222
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 */
226
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);
233
234         if (strcmp(argv[1], "send") == 0)
235         {
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
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
253                         send_byte(data[i]);
254                 }
255         }
256         else if (strcmp(argv[1], "recv") == 0)
257         {
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 */
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
275                         data[i] = recv_byte();
276                 }
277
278                 fwrite(data, 1, size, file);
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
284         /* switch TL back to high, disable outputs */
285         outb(0xe0, PORT_CONTROL);
286
287         return 0;
288 }
289