This repository has been archived by the owner on Oct 12, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
exdos_wd.c
333 lines (284 loc) · 8.69 KB
/
exdos_wd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/* Xep128: Minimalistic Enterprise-128 emulator with focus on "exotic" hardware
Copyright (C)2015,2016 LGB (Gábor Lénárt) <[email protected]>
http://xep128.lgb.hu/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "xep128.h"
#ifdef CONFIG_EXDOS_SUPPORT
#include "exdos_wd.h"
#include "configuration.h"
#include <unistd.h>
#include <sys/types.h>
// Set the value to DEBUGPRINT to always print to the stdout as well,
// and DEBUG to use only the debug file (if it's requested at all).
// For production release, DEBUG is the normal behaviour.
#define DEBUGEXDOS DEBUG
static FILE *disk_fp = NULL;
static int disk_fd = -1;
static Uint8 disk_buffer[512];
char wd_img_path[PATH_MAX + 1];
int wd_max_tracks, wd_max_sectors, wd_image_size;
Uint8 wd_sector, wd_track;
static Uint8 wd_status, wd_data, wd_command, wd_interrupt, wd_DRQ;
static int driveSel, diskSide, diskInserted, readOnly;
static int diskChanged = 0x40; // 0x40 -> disk is NOT changed
static int buffer_pos = 0, buffer_size = 1;
#define WDINT_ON 0x3E
#define WDINT_OFF 0x3C
#define WDDRQ 2
// bit mask to set in WD status in case of seek error [spin-up-completed, seek-error, CRC]
#define SEEK_ERROR (32 | 16 | 8)
// bit mask to set WD status in case of seek OK [spin-up-completed]
#define SEEK_OK 32
static const int disk_formats[][2] = {
{40, 8},
{40, 9},
{80, 8},
{80, 9},
{80, 15},
{80, 18},
{80, 21},
{82, 21},
{80, 36},
{-1, -1}
};
static int doCRC (Uint8 *buf, int nBytes, int n) // CRC16 hmmm Just copied from ep128emu :) - thanks!
{
int nBits = nBytes << 3;
int bitCnt = 0;
int bitBuf = 0;
int bufP = 0;
while (nBits--) {
if (bitCnt == 0) {
bitBuf = buf[bufP++];
bitCnt = 8;
}
if ((bitBuf ^ (n >> 8)) & 0x80)
n = (n << 1) ^ 0x1021;
else
n = (n << 1);
n = n & 0xFFFF;
bitBuf = (bitBuf << 1) & 0xFF;
bitCnt--;
}
return n;
}
static int guess_geometry ( void )
{
int a;
off_t disk_size = lseek(disk_fd, 0, SEEK_END);
for (a = 0; disk_formats[a][0] != -1; a++) {
wd_max_tracks = disk_formats[a][0];
wd_max_sectors = disk_formats[a][1];
if ((wd_max_tracks * wd_max_sectors) << 10 == disk_size) {
wd_image_size = disk_size;
DEBUGPRINT("WD: disk size is %d bytes, %d tracks, %d sectors." NL, wd_image_size, wd_max_tracks, wd_max_sectors);
return 0;
}
}
return 1;
}
void wd_detach_disk_image ( void )
{
if (disk_fp)
fclose(disk_fp);
if (disk_fd > -1)
close(disk_fd);
disk_fp = NULL;
disk_fd = -1;
*wd_img_path = 0;
readOnly = 0;
diskInserted = 1; // no disk inserted by default (1 means NOT)
}
int wd_attach_disk_image ( const char *fn )
{
wd_detach_disk_image();
if (!strcasecmp(fn, "none")) {
DEBUGPRINT("WD: no disk image was requested." NL);
return 0;
}
disk_fp = open_emu_file(fn, "rb", wd_img_path); // first, try the read-only mode
if (!disk_fp) {
ERROR_WINDOW("No EXDOS image found with name '%s'.", fn);
return 1;
} else {
// R/O was ok, try R/W
FILE *fp2 = fopen(wd_img_path, "r+b");
if (fp2) {
DEBUGPRINT("WD: disk image opened in R/W mode: %s" NL, wd_img_path);
fclose(disk_fp);
disk_fp = fp2;
} else {
readOnly = 1;
DEBUGPRINT("WD: disk image opened in R/O mode only: %s" NL, wd_img_path);
}
}
disk_fd = fileno(disk_fp);
if (guess_geometry()) {
ERROR_WINDOW("Cannot figure the EXDOS disk image geometry out, invalid/not supported image size?");
wd_detach_disk_image();
return 1;
}
diskInserted = 0; // disk OK, use inserted status
return 1;
}
void wd_exdos_reset ( void )
{
if (!disk_fp)
wd_detach_disk_image();
readOnly = 0;
wd_track = 0;
wd_sector = 0;
wd_status = 4; // track 0 flag is on at initialization
wd_data = 0;
wd_command = 0xD0; // fake last command as force interrupt
wd_interrupt = WDINT_OFF; // interrupt output is OFF
wd_DRQ = 0; // no DRQ (data request)
driveSel = (disk_fp != NULL); // drive is selected by default if there is disk image!
diskSide = 0;
diskInserted = (disk_fp == NULL) ? 1 : 0; // 0 means inserted disk, 1 means = not inserted
DEBUG("WD: reset" NL);
}
static int read_sector ( void )
{
off_t ofs;
int ret;
buffer_pos = 0;
buffer_size = 512;
if (!driveSel) {
// ugly hack! to avoid delay on boot trying to read B:, we provide here an empty sector ... :-/
DEBUGEXDOS("WD: read sector refused: driveSel=0" NL);
memset(disk_buffer, 0, 512); // fake empty
return 0;
}
if (wd_sector < 1 || wd_sector > wd_max_sectors) {
DEBUGEXDOS("WD: read sector refused: invalid sector number %d (valid = %d...%d)" NL, wd_sector, 1, wd_max_sectors);
return 1;
}
if (wd_track < 0 || wd_track >= wd_max_tracks) {
DEBUGEXDOS("WD: read sector refused: invalid track number %d (valid = %d...%d)" NL, wd_track, 0, wd_max_tracks - 1);
return 1;
}
ofs = (wd_track * wd_max_sectors * 2 + wd_sector - 1 + wd_max_sectors * diskSide) << 9;
DEBUGEXDOS("WD: Seeking to: offset %d (track=%d, sector=%d, side=%d)" NL, (int)ofs, wd_track, wd_sector, diskSide);
if (lseek(disk_fd, ofs, SEEK_SET) != ofs) {
ERROR_WINDOW("WD/EXDOS disk image seek error: %s", ERRSTR());
return 1;
}
ret = read(disk_fd, disk_buffer, 512);
DEBUGEXDOS("WD: read() for 512 bytes data to read, retval=%d" NL, ret);
switch (ret) {
case 0:
ERROR_WINDOW("WD/EXDOS disk image read error: NO DATA READ");
return 1;
case 512:
break;
default:
ERROR_WINDOW("WD/EXDOS disk image read error: %s", ERRSTR());
return 1;
}
return 0;
}
Uint8 wd_read_status ( void )
{
wd_interrupt = WDINT_OFF; // on reading WD status, interrupt is reset!
return 128 | wd_status | wd_DRQ; // motor is always on, the given wdStatus bits, DRQ handled separately (as we need it for exdos status too!)
}
Uint8 wd_read_data ( void )
{
if (wd_DRQ) {
wd_data = disk_buffer[buffer_pos++];
if (buffer_pos >= buffer_size)
wd_DRQ = 0; // end of data, switch DRQ off!
}
return wd_data;
}
Uint8 wd_read_exdos_status ( void )
{
return wd_interrupt | (wd_DRQ << 6) | diskInserted | diskChanged;
}
void wd_send_command ( Uint8 value )
{
wd_command = value;
wd_DRQ = 0; // reset DRQ
wd_interrupt = WDINT_OFF; // reset INTERRUPT
DEBUGEXDOS("WD: command received: 0x%02X driveSel=%d distInserted=%d hasImage=%d" NL, value, driveSel, diskInserted, disk_fp != NULL);
switch (value >> 4) {
case 0: // restore (type I), seeks to track zero
if (driveSel) {
wd_status = 4 | SEEK_OK; // 4->set track0
wd_track = 0;
} else
wd_status = SEEK_ERROR | 4; // set track0 flag (4) is needed here not to be mapped A: as B: by EXDOS, or such?!
wd_interrupt = WDINT_ON;
break;
case 1: // seek (type I)
if (wd_data < wd_max_tracks && driveSel) {
wd_track = wd_data;
wd_status = SEEK_OK;
} else
wd_status = SEEK_ERROR;
wd_interrupt = WDINT_ON;
if (!wd_track) wd_status |= 4;
break;
case 8: // read sector, single (type II)
if (read_sector())
wd_status = 16; // record not found
else {
wd_status = 0;
wd_DRQ = WDDRQ;
}
wd_interrupt = WDINT_ON;
break;
case 12: // read address (type III)
if (driveSel) {
int i;
disk_buffer[0] = wd_track;
wd_sector = wd_track; // why?! it seems WD puts track number into sector register. Sounds odd ...
disk_buffer[1] = diskSide;
disk_buffer[2] = 1; // first sector!
disk_buffer[3] = 2; // sector size???
i = doCRC(disk_buffer, 4, 0xB230);
disk_buffer[4] = i >> 8; // CRC1
disk_buffer[5] = i & 0xFF; // CRC2
wd_DRQ = WDDRQ;
buffer_size = 6;
buffer_pos = 0;
wd_status = 0;
} else
wd_status = 16; // record not found
wd_interrupt = WDINT_ON;
break;
case 13: // force interrupt (type IV)
if (value & 15) wd_interrupt = WDINT_ON;
wd_status = (wd_track == 0) ? 4 : 0;
break;
default:
DEBUGEXDOS("WD: unknown command: 0x%02X" NL, value);
wd_status = 4 | 8 | 16 | 64; // unimplemented command results in large set of problems reported :)
wd_interrupt = WDINT_ON;
break;
}
}
void wd_write_data ( Uint8 value )
{
wd_data = value;
}
void wd_set_exdos_control ( Uint8 value )
{
driveSel = (disk_fp != NULL) && ((value & 15) == 1);
diskSide = (value >> 4) & 1;
diskInserted = driveSel ? 0 : 1;
}
#else
#warning "EXDOS/WD support is not compiled in / not ready"
#endif