fix license text, add gitignore
[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", argv0);
161         exit(1);
162 }
163
164 static unsigned int atoi_or_die(const char *a)
165 {
166         char *p = NULL;
167         unsigned int i;
168
169         i = strtoul(a, &p, 0);
170         if (p == NULL || *p != 0) {
171                 fprintf(stderr, "atoi: can't convert: %s\n", a);
172                 exit(1);
173         }
174
175         return i;
176 }
177
178 int main(int argc, char *argv[])
179 {
180         unsigned int addr = 0, size = 0, i = 0;
181         int ret;
182         unsigned char *data;
183         FILE *file = NULL;
184
185         if (argc < 2)
186                 usage(argv[0]);
187
188         data = malloc(0x1000000);
189         if (data == NULL) {
190                 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
191                 return 1;
192         }
193
194         /* parse args, read files.. */
195         if (strcmp(argv[1], "send") == 0)
196         {
197                 if (argc != 4 && argc != 5)
198                         usage(argv[0]);
199
200                 file = fopen(argv[2], "rb");
201                 if (file == NULL) {
202                         fprintf(stderr, "can't open file: %s\n", argv[2]);
203                         return 1;
204                 }
205
206                 addr = atoi_or_die(argv[3]);
207                 if (argv[4] == NULL) {
208                         fseek(file, 0, SEEK_END);
209                         size = ftell(file);
210                         fseek(file, 0, SEEK_SET);
211                 }
212                 else
213                         size = atoi_or_die(argv[4]);
214
215                 ret = fread(data, 1, size, file);
216                 if (ret != size) {
217                         fprintf(stderr, "fread returned %d/%d\n", ret, size);
218                         perror(NULL);
219                         return 1;
220                 }
221         }
222         else if (strcmp(argv[1], "recv") == 0)
223         {
224                 if (argc != 5)
225                         usage(argv[0]);
226
227                 file = fopen(argv[2], "wb");
228                 if (file == NULL) {
229                         fprintf(stderr, "can't open file: %s\n", argv[2]);
230                         return 1;
231                 }
232
233                 addr = atoi_or_die(argv[3]);
234                 size = atoi_or_die(argv[4]);
235
236                 memset(data, 0, size);
237         }
238         else
239                 usage(argv[0]);
240
241         ret = ioperm(PORT_DATA, 3, 1);
242         if (ret != 0) {
243                 perror("ioperm");
244                 return 1;
245         }
246
247         signal(SIGINT, inthandler);
248
249         printf("regs: %02x %02x %02x\n",
250                 inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
251         outb(0xe8, PORT_CONTROL);       /* TR low - request for transfer */
252
253         if (inb(PORT_STATUS) & 0x40)
254                 printf("waiting for TH low..\n");
255         while (inb(PORT_STATUS) & 0x40)
256                 sleep(1);
257
258         outb(0xe0, PORT_CONTROL);
259
260         if (strcmp(argv[1], "send") == 0)
261         {
262                 printf("send %06x %06x\n", addr, size);
263                 send_cmd(CMD_MD_SEND);
264                 send_byte((addr >> 16) & 0xff);
265                 send_byte((addr >>  8) & 0xff);
266                 send_byte((addr >>  0) & 0xff);
267                 send_byte((size >> 16) & 0xff);
268                 send_byte((size >>  8) & 0xff);
269                 send_byte((size >>  0) & 0xff);
270
271                 for (i = 0; i < size; i++)
272                 {
273                         if ((i & 0xff) == 0) {
274                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
275                                 printf("%06x/%06x", i, size);
276                                 fflush(stdout);
277                         }
278
279                         send_byte(data[i]);
280                 }
281         }
282         else if (strcmp(argv[1], "recv") == 0)
283         {
284                 send_cmd(CMD_MD_RECV);
285                 send_byte((addr >> 16) & 0xff);
286                 send_byte((addr >>  8) & 0xff);
287                 send_byte((addr >>  0) & 0xff);
288                 send_byte((size >> 16) & 0xff);
289                 send_byte((size >>  8) & 0xff);
290                 send_byte((size >>  0) & 0xff);
291                 outb(0xe0, PORT_CONTROL);       /* TL high, recv mode */
292
293                 for (i = 0; i < size; i++)
294                 {
295                         if ((i & 0xff) == 0) {
296                                 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
297                                 printf("%06x/%06x", i, size);
298                                 fflush(stdout);
299                         }
300
301                         data[i] = recv_byte();
302                 }
303
304                 fwrite(data, 1, size, file);
305         }
306         printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
307         printf("%06x/%06x\n", i, size);
308         fclose(file);
309
310         /* switch TL back to high, disable outputs */
311         outb(0xe0, PORT_CONTROL);
312
313         return 0;
314 }
315