hexed: add jump cmd, refactor transfer
[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
77static void do_exit(const char *msg)
76ba9df5 78{
79 /* switch TL back to high */
8689c962 80 outb(0xe0, PORT_CONTROL);
81
82 if (msg)
83 printf("%s", msg);
76ba9df5 84 exit(1);
85}
86
8689c962 87static void inthandler(int u)
76ba9df5 88{
8689c962 89 do_exit("\n");
90}
76ba9df5 91
8689c962 92static 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");
76ba9df5 102 }
8689c962 103}
76ba9df5 104
8689c962 105static 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");
76ba9df5 115 }
8689c962 116}
117
118static 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
137static 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
150static void send_cmd(unsigned int cmd)
151{
152 send_byte(CMD_PREFIX);
153 send_byte(cmd);
154}
155
156static void usage(const char *argv0)
157{
158 fprintf(stderr, "usage:\n%s <cmd> [args]\n"
159 "\tsend <file> <addr> [size]\n"
272bd2ec 160 "\trecv <file> <addr> <size>\n"
161 "\tjump <addr>\n", argv0);
8689c962 162 exit(1);
163}
164
165static unsigned int atoi_or_die(const char *a)
166{
167 char *p = NULL;
168 unsigned int i;
76ba9df5 169
8689c962 170 i = strtoul(a, &p, 0);
76ba9df5 171 if (p == NULL || *p != 0) {
8689c962 172 fprintf(stderr, "atoi: can't convert: %s\n", a);
173 exit(1);
76ba9df5 174 }
175
8689c962 176 return i;
177}
178
179int 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);
76ba9df5 190 if (data == NULL) {
8689c962 191 fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
76ba9df5 192 return 1;
193 }
194
8689c962 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 }
272bd2ec 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 }
8689c962 246 else
247 usage(argv[0]);
248
249 ret = ioperm(PORT_DATA, 3, 1);
76ba9df5 250 if (ret != 0) {
251 perror("ioperm");
252 return 1;
253 }
254
255 signal(SIGINT, inthandler);
256
8689c962 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 */
76ba9df5 260
8689c962 261 if (inb(PORT_STATUS) & 0x40)
262 printf("waiting for TH low..\n");
263 while (inb(PORT_STATUS) & 0x40)
272bd2ec 264 usleep(100000);
8689c962 265
266 outb(0xe0, PORT_CONTROL);
76ba9df5 267
8689c962 268 if (strcmp(argv[1], "send") == 0)
76ba9df5 269 {
8689c962 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
76ba9df5 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
8689c962 286 send_byte(data[i]);
76ba9df5 287 }
76ba9df5 288 }
8689c962 289 else if (strcmp(argv[1], "recv") == 0)
76ba9df5 290 {
8689c962 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 */
76ba9df5 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
8689c962 308 data[i] = recv_byte();
76ba9df5 309 }
8689c962 310
311 fwrite(data, 1, size, file);
76ba9df5 312 }
272bd2ec 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 }
76ba9df5 326
8689c962 327 /* switch TL back to high, disable outputs */
328 outb(0xe0, PORT_CONTROL);
329
76ba9df5 330 return 0;
331}
332