9e2681a3f70d09ada1c39d04b6927f04e3261f81
[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 static void do_exit(const char *msg)
78 {
79         /* switch TL back to high */
80         outb(0xe0, PORT_CONTROL);
81
82         if (msg)
83                 printf("%s", msg);
84         exit(1);
85 }
86
87 static void inthandler(int u)
88 {
89         do_exit("\n");
90 }
91
92 static void wait_th_low(void)
93 {
94         struct timeval start, now;
95
96         gettimeofday(&start, NULL);
97
98         while (inb(PORT_STATUS) & 0x40) {
99                 gettimeofday(&now, NULL);
100                 if (timediff(now, start) > ACK_TIMEOUT)
101                         do_exit("timeout waiting TH low\n");
102         }
103 }
104
105 static void wait_th_high(void)
106 {
107         struct timeval start, now;
108
109         gettimeofday(&start, NULL);
110
111         while (!(inb(PORT_STATUS) & 0x40)) {
112                 gettimeofday(&now, NULL);
113                 if (timediff(now, start) > ACK_TIMEOUT)
114                         do_exit("timeout waiting TH high\n");
115         }
116 }
117
118 static unsigned int recv_byte(void)
119 {
120         unsigned int byte;
121
122         outb(0xe2, PORT_CONTROL);       /* TL low */
123
124         wait_th_low();
125
126         byte = inb(PORT_DATA) & 0x0f;
127
128         outb(0xe0, PORT_CONTROL);       /* TL high */
129
130         wait_th_high();
131
132         byte |= inb(PORT_DATA) << 4;
133
134         return byte;
135 }
136
137 static void send_byte(unsigned int byte)
138 {
139         wait_th_low();
140
141         outb(byte & 0x0f, PORT_DATA);
142         outb(0xc2, PORT_CONTROL);       /* TL low */
143
144         wait_th_high();
145
146         outb((byte >> 4) & 0x0f, PORT_DATA);
147         outb(0xc0, PORT_CONTROL);       /* TL high */
148 }
149
150 static void send_cmd(unsigned int cmd)
151 {
152         send_byte(CMD_PREFIX);
153         send_byte(cmd);
154 }
155
156 static void usage(const char *argv0)
157 {
158         fprintf(stderr, "usage:\n%s <cmd> [args]\n"
159                 "\tsend <file> <addr> [size]\n"
160                 "\trecv <file> <addr> <size>\n"
161                 "\tjump <addr>\n", argv0);
162         exit(1);
163 }
164
165 static unsigned int atoi_or_die(const char *a)
166 {
167         char *p = NULL;
168         unsigned int i;
169
170         i = strtoul(a, &p, 0);
171         if (p == NULL || *p != 0) {
172                 fprintf(stderr, "atoi: can't convert: %s\n", a);
173                 exit(1);
174         }
175
176         return i;
177 }
178
179 int main(int argc, char *argv[])
180 {
181         unsigned int addr = 0, size = 0, i = 0;
182         int ret;
183         unsigned char *data;
184         FILE *file = NULL;
185
186         if (argc < 2)
187                 usage(argv[0]);
188
189         data = malloc(0x1000000);
190         if (data == NULL) {
191                 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
192                 return 1;
193         }
194
195         /* parse args, read files.. */
196         if (strcmp(argv[1], "send") == 0)
197         {
198                 if (argc != 4 && argc != 5)
199                         usage(argv[0]);
200
201                 file = fopen(argv[2], "rb");
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                 if (argv[4] == NULL) {
209                         fseek(file, 0, SEEK_END);
210                         size = ftell(file);
211                         fseek(file, 0, SEEK_SET);
212                 }
213                 else
214                         size = atoi_or_die(argv[4]);
215
216                 ret = fread(data, 1, size, file);
217                 if (ret != size) {
218                         fprintf(stderr, "fread returned %d/%d\n", ret, size);
219                         perror(NULL);
220                         return 1;
221                 }
222         }
223         else if (strcmp(argv[1], "recv") == 0)
224         {
225                 if (argc != 5)
226                         usage(argv[0]);
227
228                 file = fopen(argv[2], "wb");
229                 if (file == NULL) {
230                         fprintf(stderr, "can't open file: %s\n", argv[2]);
231                         return 1;
232                 }
233
234                 addr = atoi_or_die(argv[3]);
235                 size = atoi_or_die(argv[4]);
236
237                 memset(data, 0, size);
238         }
239         else if (strcmp(argv[1], "jump") == 0)
240         {
241                 if (argc != 3)
242                         usage(argv[0]);
243
244                 addr = atoi_or_die(argv[2]);
245         }
246         else
247                 usage(argv[0]);
248
249         ret = ioperm(PORT_DATA, 3, 1);
250         if (ret != 0) {
251                 perror("ioperm");
252                 return 1;
253         }
254
255         signal(SIGINT, inthandler);
256
257         printf("regs: %02x %02x %02x\n",
258                 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
259         outb(0xe8, PORT_CONTROL);       /* TR low - request for transfer */
260
261         if (inb(PORT_STATUS) & 0x40)
262                 printf("waiting for TH low..\n");
263         while (inb(PORT_STATUS) & 0x40)
264                 usleep(100000);
265
266         outb(0xe0, PORT_CONTROL);
267
268         if (strcmp(argv[1], "send") == 0)
269         {
270                 send_cmd(CMD_MD_SEND);
271                 send_byte((addr >> 16) & 0xff);
272                 send_byte((addr >>  8) & 0xff);
273                 send_byte((addr >>  0) & 0xff);
274                 send_byte((size >> 16) & 0xff);
275                 send_byte((size >>  8) & 0xff);
276                 send_byte((size >>  0) & 0xff);
277
278                 for (i = 0; i < size; i++)
279                 {
280                         if ((i & 0xff) == 0) {
281                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
282                                 printf("%06x/%06x", i, size);
283                                 fflush(stdout);
284                         }
285
286                         send_byte(data[i]);
287                 }
288         }
289         else if (strcmp(argv[1], "recv") == 0)
290         {
291                 send_cmd(CMD_MD_RECV);
292                 send_byte((addr >> 16) & 0xff);
293                 send_byte((addr >>  8) & 0xff);
294                 send_byte((addr >>  0) & 0xff);
295                 send_byte((size >> 16) & 0xff);
296                 send_byte((size >>  8) & 0xff);
297                 send_byte((size >>  0) & 0xff);
298                 outb(0xe0, PORT_CONTROL);       /* TL high, recv mode */
299
300                 for (i = 0; i < size; i++)
301                 {
302                         if ((i & 0xff) == 0) {
303                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
304                                 printf("%06x/%06x", i, size);
305                                 fflush(stdout);
306                         }
307
308                         data[i] = recv_byte();
309                 }
310
311                 fwrite(data, 1, size, file);
312         }
313         else if (strcmp(argv[1], "jump") == 0)
314         {
315                 send_cmd(CMD_JUMP);
316                 send_byte((addr >> 16) & 0xff);
317                 send_byte((addr >>  8) & 0xff);
318                 send_byte((addr >>  0) & 0xff);
319         }
320
321         if (file != NULL) {
322                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
323                 printf("%06x/%06x\n", i, size);
324                 fclose(file);
325         }
326
327         /* switch TL back to high, disable outputs */
328         outb(0xe0, PORT_CONTROL);
329
330         return 0;
331 }
332