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