23 #include <sys/types.h>
43 static int enable_mode_transient_callback = -1;
46 #define D64_SIZE 174848
47 #define D71_SIZE 349696
48 #define D65_SIZE 2785280
50 #define IS_RO(p) (!!((p) & D81ACCESS_RO))
51 #define IS_RW(p) (!((p) & D81ACCESS_RO))
52 #define HAS_DISK(p) (((p)&& 0xFF) != D81ACCESS_EMPTY)
53 #define IS_AUTOCLOSE(p) (!!((p) & D81ACCESS_AUTOCLOSE))
59 if (enable_mode_transient_callback != -1)
60 FATAL(
"d81access_init(): trying to re-run d81access_init()?!");
61 enable_mode_transient_callback = 1;
62 for (
int i = 0; i < 8; i++) {
74 return d81[which].mode;
80 if (d81[which].
fd >= 0) {
83 DEBUGPRINT(
"D81: previous file descriptor (%d) closed because of auto-close policy" NL, d81[which].
fd);
85 DEBUGPRINT(
"D81: previous file descriptor (%d) is NOT closed, because marked as non-autoclose!" NL, d81[which].
fd);
89 closedir(d81[which].
dir);
91 d81[which].dir = NULL;
94 d81[which].start_at = 0;
95 if (enable_mode_transient_callback)
102 for (
int i = 0; i < 8; i++)
107 static void d81access_close_internal (
int which )
109 enable_mode_transient_callback = 0;
111 enable_mode_transient_callback = 1;
115 static void d81access_attach_fd_internal (
int which,
int fd, off_t offset,
int mode )
118 FATAL(
"d81access_attach_fd_internal() tries to attach invalid fd");
119 d81access_close_internal(which);
122 d81[which].mode =
mode;
133 DEBUGPRINT(
"D81: using empty access (no disk in drive) by request" NL);
136 d81[which].start_at = offset;
147 int check_mode =
mode & 0xFF;
149 FATAL(
"d81access_attach_fd() mode low bits must have D81ACCESS_IMG or D81ACCESS_EMPTY");
150 d81access_attach_fd_internal(which,
fd, offset,
mode);
158 void d81access_attach_cb (
int which, off_t offset, d81access_rd_cb_t rd_callback, d81access_wr_cb_t wr_callback )
160 d81access_close_internal(which);
164 d81[which].read_cb = rd_callback;
165 d81[which].write_cb = wr_callback;
166 d81[which].start_at = offset;
167 DEBUGPRINT(
"D81: attaching D81 via provided callbacks, read=%p, write=%p" NL, rd_callback, wr_callback);
176 DEBUGPRINT(
"D81: attach file request with empty file name, not using FS based disk attachment." NL);
185 DIR *
dir = opendir(
fn);
188 d81access_close_internal(which);
189 d81[which].dir =
dir;
192 DEBUGPRINT(
"D81: file system object \"%s\" opened as a directory." NL,
fn);
195 }
else if (errno != ENOTDIR && errno != ENOENT) {
196 ERROR_WINDOW(
"D81: cannot open directory %s for virtual D81 mode: %s",
fn, strerror(errno));
203 DEBUGPRINT(
"D81: could not open file system object \"%s\" as a directory.",
fn);
205 FATAL(
"d81access_attach_fsobj(): insane mode argument, no DIR,IMG,PRG given");
209 char fnbuf[PATH_MAX + 1];
214 ERROR_WINDOW(
"D81: image/program file was specified (%s) but it cannot be opened: %s",
fn, strerror(errno));
219 ERROR_WINDOW(
"D81: Cannot query the size of external D81 image/program file %s ERROR: %s",
fn, strerror(errno));
227 d81[which].prg_size =
size;
228 d81[which].prg_blk_size =
size / 254;
229 d81[which].prg_blk_last_size =
size % 254;
231 d81[which].prg_blk_size++;
233 d81[which].prg_blk_last_size = 254;
238 ERROR_WINDOW(
"Specified size for D81 seems to be a program file (too small for real D81), but PRG mode virtual disk feature was not requested");
275 ERROR_WINDOW(
"Cannot guess the type of object (from its size) wanted to use for floppy emulation");
280 static int file_io_op (
const int which,
const int is_write,
const int image_offset,
Uint8 *buffer,
const int size )
282 off_t offset = d81[which].start_at + (off_t)image_offset;
283 if (lseek(d81[which].
fd, offset, SEEK_SET) != offset)
284 FATAL(
"D81: SEEK: seek host-OS failure: %s", strerror(errno));
288 FATAL(
"D81: %s: host-OS error: %s", is_write ?
"WRITE" :
"READ", strerror(errno));
293 static int read_fake64 (
const int which,
Uint8 *buffer,
int d81_offset,
int number_of_logical_sectors )
295 for (; number_of_logical_sectors; number_of_logical_sectors--, d81_offset += 0x100, buffer += 0x100) {
296 int track = d81_offset / 0x2800 + 1;
297 int sector = (d81_offset % 0x2800) >> 8;
299 memset(buffer, 0, 0x100);
300 DEBUGPRINT(
"D81: FAKE64: D81 track 18 tried to be read, which would be the D64 dir/sys track. Ignoring!" NL);
305 if (!track || track > 35) {
306 memset(buffer, 0, 0x100);
307 DEBUGPRINT(
"D81: FAKE64: invalid track for D64 %d" NL, track);
312 int d64_max_sectors, d64_track_ofs;
314 d64_track_ofs = 21 * 256 * (track - 1) + 0x00000;
315 d64_max_sectors = 21;
316 }
else if (track <= 24) {
317 d64_track_ofs = 19 * 256 * (track - 18) + 0x16500;
318 d64_max_sectors = 19;
319 }
else if (track <= 30) {
320 d64_track_ofs = 18 * 256 * (track - 25) + 0x1EA00;
321 d64_max_sectors = 18;
323 d64_track_ofs = 17 * 256 * (track - 31) + 0x25600;
324 d64_max_sectors = 17;
326 if (sector >= d64_max_sectors) {
327 memset(buffer, 0, 0x100);
328 DEBUGPRINT(
"D81: FAKE64: invalid sector for D64 %d on track %d" NL, sector, track);
335 sector_to_read = sector - 2;
339 sector_to_read = sector;
340 if (track == 18 || sector_to_read != sector)
341 DEBUGPRINT(
"D81: FAKE64: translated to D64 track:sector %d:%d from the orginal requested %d:%d" NL, track, sector_to_read, track == 18 ? 40 : track, sector);
342 if (file_io_op(which, 0, d64_track_ofs + sector_to_read * 256, buffer, 0x100) != 0x100) {
353 memcpy(buffer + 4, buffer + 0x90, 16);
356 buffer[0x16] = buffer[0xA2];
357 buffer[0x17] = buffer[0xA3];
363 memset(buffer + 0x1D, 0, 0x100 - 0x1D);
364 }
else if (sector == 1 || sector == 2) {
366 Uint8 obuffer[0x100];
367 memcpy(obuffer, buffer, 0x100);
368 memset(buffer, 0, 0x100);
372 for (
int tf0 = 0; tf0 < 35; tf0++) {
373 Uint8 *obam = obuffer + 4 + (4 * tf0);
374 Uint8 *nbam = buffer + 0x10 + (6 * tf0);
376 memcpy(nbam, obam, 4);
384 buffer[4] = obuffer[0xA2];
385 buffer[5] = obuffer[0xA3];
388 }
else if (buffer[0] == 18) {
390 if (buffer[1] > 0 && buffer[1] < 19)
399 static int read_prg (
const int which,
Uint8 *buffer,
int d81_offset,
int number_of_logical_sectors )
401 static const Uint8 vdsk_head_sect[] = {
404 'X',
'E',
'M',
'U',
' ',
'V',
'R',
'-',
'D',
'I',
'S',
'K',
' ',
'R',
'/',
'O',
408 0x33, 0x44, 0xA0, 0xA0
410 static const Uint8 vdsk_file_name[16] = {
411 'F',
'I',
'L',
'E', 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0
414 memset(buffer, 0, 512);
417 for (; number_of_logical_sectors; number_of_logical_sectors--, d81_offset += 0x100, buffer += 0x100) {
418 DEBUGPRINT(
"D81VIRTUAL: reading sub-sector @ $%X" NL, d81_offset);
419 if (d81_offset == 0x61800) {
420 memcpy(buffer, vdsk_head_sect,
sizeof vdsk_head_sect);
421 }
else if (d81_offset == 0x61900 || d81_offset == 0x61A00) {
422 if (d81_offset == 0x61900) {
429 buffer[4] = vdsk_head_sect[0x16];
430 buffer[5] = vdsk_head_sect[0x17];
432 }
else if (d81_offset == 0x61B00) {
436 memcpy(buffer + 5, vdsk_file_name, 16);
437 buffer[0x1E] = d81[which].prg_blk_size & 0xFF;
438 buffer[0x1F] = d81[which].prg_blk_size >> 8;
439 memcpy(buffer + 0x22, buffer + 2, 0x20 - 2);
445 int block = d81_offset >> 8;
449 reqsize = d81[which].prg_blk_last_size;
453 buffer[0] = ((
block + 1) / 40) + 1;
454 buffer[1] = (
block + 1) % 40;
456 ret = file_io_op(which, 0,
block * 254, buffer + 2, reqsize);
457 DEBUGPRINT(
"D81VIRTUAL: ... data block, block number %d, next_track = $%02X next_sector = $%02X" NL,
block, buffer[0], buffer[1]);
459 if (host_seek_to(NULL,
block * 254,
"reading[PRG81VIRT@HOST]", d81_is_prg + 512, d81fd) < 0)
463 DEBUGPRINT(
"D81VIRTUAL: ... reading result: expected %d retval %d" NL, reqsize, ret);
477 static int check_io_req_params (
const int which,
const Uint8 side,
const Uint8 track,
const Uint8 sector,
const int sector_size,
int *io_size_p )
479 if (
XEMU_UNLIKELY(sector_size != 0x100 && sector_size != 0x200))
480 FATAL(
"D81ACCESS: check_io_req_params(): invalid sector size %d", sector_size);
482 DEBUGPRINT(
"D81ACCESS: warning, trying file op on sector-0 within track %d" NL, track);
491 offset = (((
const int)track << 7) + (sector - 1)) * 512;
496 offset = 40 * (track - 0) * 256 + (sector - 1) * 512 + side * 20 * 256;
505 if (
XEMU_UNLIKELY(num_of_sides != -1 && side >= num_of_sides)) {
506 DEBUGPRINT(
"D81ACCESS: trying to access non-existing side (%d)" NL, side);
510 DEBUGPRINT(
"D81ACCESS: trying to access non-existing track (%d)" NL, track);
514 DEBUGPRINT(
"D81ACCESS: trying to access non-exisiting sector (%d) on track %d" NL, sector, track);
520 DEBUGPRINT(
"D81ACCESS: trying to R/W beyond the end of disk image (offset %d, size %d)" NL, offset, d81[which].
image_size);
524 const int io_size = d81[which].image_size - offset;
526 DEBUGPRINT(
"D81ACCESS: partial R/W with %d bytes" NL, io_size);
527 if (io_size != 256) {
529 ERROR_WINDOW(
"Partial R/W on D81 access which is not 256 byte in length but %d (at offset %d, image size is %d bytes)!", io_size, offset, d81[which].
image_size);
532 *io_size_p = io_size;
534 *io_size_p = sector_size;
542 const int offset = check_io_req_params(which, side, track, sector, sector_size, &io_size);
545 switch (d81[which].
mode & 0xFF) {
550 return read_fake64(which, buffer, offset, sector_size >> 8);
554 memset(buffer, 0xFF, sector_size);
555 return file_io_op(which, 0, offset, buffer, io_size) == io_size ? 0 : -1;
558 return read_prg(which, buffer, offset, sector_size >> 8);
560 FATAL(
"D81ACCESS: DIR access method is not yet implemented in Xemu, sorry :-(");
562 FATAL(
"D81: D81ACCESS_CALLBACKS is not implemented!");
565 FATAL(
"D81ACCESS: d81access_read_sect(): invalid value for 'd81[%d].mode & 0xFF' = %d", which, d81[which].
mode & 0xFF);
567 FATAL(
"D81ACCESS: d81access_read_sect() unhandled case" NL);
575 const int offset = check_io_req_params(which, side, track, sector, sector_size, &io_size);
580 switch (d81[which].
mode & 0xFF) {
584 return file_io_op(which, 1, offset, buffer, io_size) == io_size ? 0 : -1;
589 FATAL(
"D81: D81ACCESS_CALLBACKS is not implemented!");
592 FATAL(
"D81ACCESS: d81access_write_sect(): invalid d81[%d].mode & 0xFF", which);
594 FATAL(
"D81ACCESS: d81access_write_sect() unhandled case" NL);
608 static const char diskname_default[] =
"XEMU-NAMELESS";
610 diskname = diskname_default;
611 static const Uint8 mods_at_61800[] = {
613 0x28,0x03,0x44,0x00,0x44,0x45,0x4d,0x4f,0x45,0x4d,0x50,0x54,0x59,0xa0,0xa0,0xa0,
615 0x28,0x03,0x44,0x00,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,
616 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0x30,0x30,0xa0,0x33,0x44,0xa0,0xa0
618 static const Uint8 mods_at_61900[] = {
619 0x28,0x02,0x44,0xbb,0x30,0x30,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
620 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
621 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
622 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
623 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
624 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
625 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
626 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
627 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
628 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
629 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
630 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
631 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
632 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
633 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
634 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x24,0xf0,0xff,0xff,0xff,0xff,
635 0x00,0xff,0x44,0xbb,0x30,0x30,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
636 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
637 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
638 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
639 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
640 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
641 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
642 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
643 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
644 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
645 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
646 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
647 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
648 0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,
649 0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,
650 0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0xff,0xff,
654 memcpy(img + 0x61800, mods_at_61800,
sizeof mods_at_61800);
655 memcpy(img + 0x61900, mods_at_61900,
sizeof mods_at_61900);
659 if (diskname[0] ==
'@' || diskname[0] ==
'#')
664 namelen = strlen(diskname);
665 if (namelen > 4 && !strcasecmp(diskname + namelen - 4,
".D81"))
668 namelen = strlen(diskname);
671 }
else if (!namelen) {
672 diskname = diskname_default;
673 namelen = strlen(diskname_default);
675 unsigned int diskid = namelen + ((namelen + 1) << 8);
676 DEBUGPRINT(
"D81ACCESS: creating memory image of a new D81 disk \"");
677 for (
unsigned int i = 0; i < namelen; i++) {
679 diskid += (
unsigned int)c + (i << 5);
680 if (c >=
'a' && c <=
'z')
682 else if (c < 32 || c >= 0x7F)
684 img[0x61804 + i] = c;
687 diskid = (diskid ^ (diskid >> 5));
688 for (
unsigned int i = 0; i < 2; i++, diskid /= 36) {
689 const Uint8 c = diskid % 36;
690 img[0x61816 + i] = c < 26 ? c +
'A' : c - 26 +
'0';
692 DEBUGPRINT(
"\",\"%c%c\"" NL, img[0x61816], img[0x61817]);
703 char fullpath[PATH_MAX + 1];
704 const int fd =
xemu_open_file(
fn, O_WRONLY | O_CREAT | O_TRUNC | (!do_overwrite ? O_EXCL : 0), NULL, fullpath);
706 const int ret = (errno == EEXIST) ? -2 : -1;
708 ERROR_WINDOW(
"%s [D81-CREATE]\n%s\n%s", cry,
fn, strerror(errno));
719 ERROR_WINDOW(
"%s [D81-WRITE]\n%s\n%s", cry, fullpath, strerror(errno));
723 DEBUGPRINT(
"D81ACCESS: new disk image file \"%s\" has been successfully created (overwrite policy %d)." NL, fullpath, do_overwrite);