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