#include #include #include #include "v9t9_common.h" #include "v9t9.h" #include "roms.h" #include "cru.h" #include "command.h" #include "memory.h" #include "dsr.h" char realdiskfilename[OS_NAMESIZE] = "disk.bin"; u8 realdiskdsr[8192]; char diskname[3][OS_NAMESIZE] = { "disk0001.dsk", "disk0002.dsk", "disk0003.dsk" }; char *diskimagepath = NULL; #define _L LOG_REALDISK | LOG_INFO #define _LS LOG_REALDISK | LOG_INFO | LOG_USER #define DSKbuffersize (256*18) /* maximum track size */ #if BEWORKS_FS OSFileType osDiskImageType = { 0666, "x/v9t9-disk-image" }; #elif POSIX_FS OSFileType osDiskImageType = { 0666 }; #elif WIN32_FS OSFileType osDiskImageType = 0; #elif MAC_FS OSFileType osDiskImageType = 'DI99'; #endif typedef struct DSKInfo { u8 num; /* current drive #1-3 */ u8 hold; /* holding for data? */ u8 lastbyte; /* last byte written to WDDATA */ u8 buffer[DSKbuffersize]; /* buffered I/O */ u32 buflen; /* internal use only, max len */ u32 bufpos; /* buffer is only a window */ u32 written; /* how much written? */ OSRef handle; /* file descriptor for disk */ u8 density; /* density, 9 or 18 */ u8 tracksside; /* # tracks per side */ // u8 trackoffset; /* last seeked track */ u32 tracksize; /* max # bytes in a size, set by opendisk */ u32 trackbyteoffset; /* offset into track, bytes */ u8 command; /* command being executed */ u8 track; /* current track */ u8 sector; /* current sector */ u8 side; /* current side */ u8 status; /* current status */ u8 formatsaving; /* are we saving data in a format? */ u8 postponed; /* waiting for input */ } DSKInfo; DSKInfo DSK; #define FDC_seekhome ((~0xf5)&0xff) #define FDC_seek ((~0xe1)&0xff) #define FDC_stepin ((~0xa5)&0xff) #define FDC_readsector ((~0x77)&0xff) #define FDC_writesector ((~0x57)&0xff) #define FDC_readIDmarker ((~0x3f)&0xff) #define FDC_interrupt ((~0x2f)&0xff) #define FDC_formattrack ((~0x0b)&0xff) #define fdc_READY 0x80 #define fdc_WRITEPORT 0x40 #define fdc_BADRECORD 0x10 #define fdc_CRCERR 0x08 #define fdc_LOSTDATA 0x04 #define fdc_TRACK0 0x04 #define fdc_BUSY 0x01 #define fdc_WTDATA 0x5FFE #define fdc_RDDATA 0x5FF6 struct fdc_secrec { u8 idmark; u8 track, side, sector; u8 seclen; /* 128<= DSK.buflen) DSK.formatsaving = 0; if (DSK.handle) { OSError err; OSSize ret = DSK.bufpos; err = OS_Write(DSK.handle, (void *) DSK.buffer, &ret); if (err != OS_NOERR) DSK.status |= fdc_BADRECORD; else if (ret != DSK.bufpos) DSK.status |= fdc_LOSTDATA; } DSK.bufpos = 0; } static u32 FDCgetfilesize(void) { OSSize sz; if (DSK.handle) { OS_GetSize(DSK.handle, &sz); return sz; } else return 0; } static void FDCcheckdisk(void) { if (DSK.handle) DSK.status |= fdc_READY; } /* Returns 0 for error. */ static int FDCseektotrack(void) { if (DSK.handle) { u32 offs; if (DSK.side) offs = DSK.tracksside + (~(DSK.track - DSK.tracksside)); else offs = DSK.track; offs *= DSK.tracksize; if (offs >= FDCgetfilesize()) return 0; logger(_L|L_1, "seeking to Tr%d Sd%d = byte %d of file\n", DSK.track, DSK.side, offs); if (!OS_Seek(DSK.handle, OSSeekAbs, offs) == OS_NOERR) { logger(_LS | LOG_ERROR, "failed to seek!\n"); return 0; } return 1; } else DSK.trackbyteoffset = 0; return 0; } static void FDCclosedisk(void) { // if (!(features&FE_realdisk)) // return; logger(_L|0, "FDCclosedisk\n"); if (DSK.postponed) FDCflushbuffer(); if (DSK.handle) { OS_Close(DSK.handle); DSK.handle = 0; } } static void FDCopendisk(void) { OSSpec spec; OSError err; // struct stat st; // if (!(features&FE_realdisk)) // return; logger(_L|0, "FDCopendisk\n"); if (DSK.handle) FDCclosedisk(); err = OS_FindFileInPath(diskname[DSK.num - 1], diskimagepath, &spec); logger(_L|0, "disk image = %s\n", OS_SpecToString1(&spec)); if (err != OS_NOERR || (err = OS_Open(&spec, OSReadWrite, &DSK.handle)) != OS_NOERR) { logger(_L|LOG_ERROR, "DOAD server: could not open disk at DSK%d, creating a new one (%s)\n", DSK.num, OS_SpecToString1(&spec)); FDCclosedisk(); if ((err = OS_Create(&spec, &osDiskImageType)) != OS_NOERR || (err = OS_Open(&spec, OSReadWrite, &DSK.handle)) != OS_NOERR) { logger(_LS|LOG_ERROR, "DOAD server: could not create disk (%s)\n", OS_GetErrText(err)); DSK.handle = 0; return; } DSK.density = 9; } else { OSSize len = 20; OS_Read(DSK.handle, (void *) DSK.buffer, &len); if (len != 20) { OS_Close(DSK.handle); logger(_LS|LOG_ERROR, "DOAD server: disk image '%s' is short (%d bytes)\n", OS_SpecToString1(&spec), len); DSK.handle = 0; return; } DSK.tracksside = DSK.buffer[17]; DSK.density = DSK.buffer[12]; } DSK.tracksize = DSK.density * 256l; logger(_L|0, "Opened disk %d, #tracks=%d tracksize=%d sectors=%d\n", DSK.num, DSK.tracksside, DSK.tracksize, DSK.density); logger(_L|L_1, "DSK.handle=%d\n", DSK.handle); } static void FDCseekhome(void) { logger(_L|0, "FDC seek home\n"); DSK.track = 0; DSK.status |= fdc_TRACK0; FDCcheckdisk(); FDCseektotrack(); } static void FDCseek(void) { logger(_L|0, "FDC seek, T%d s%d\n", DSK.track, DSK.side); DSK.track = DSK.lastbyte; if (!DSK.track) DSK.status |= fdc_TRACK0; if (DSK.track >= 80) DSK.status |= fdc_BADRECORD; FDCcheckdisk(); FDCseektotrack(); } static void FDCstepin(void) { logger(_L|0, "FDC step in, T%d s%d\n", DSK.track, DSK.side); DSK.track++; if (DSK.track >= 80) DSK.status |= fdc_BADRECORD; FDCcheckdisk(); FDCseektotrack(); } static void FDCskipseek(int skip) { DSK.trackbyteoffset += skip; OS_Seek(DSK.handle, OSSeekRel, skip); } static int FDCfindIDmarker(void) { if (FDCseektotrack() == 0) { logger(_L|L_1, "FDCfindIDmarker failed\n"); DSK.status |= fdc_BADRECORD; return 0; } else { logger(_L|L_1, "FDCfindIDmarker succeeded, skipping to sector %d\n", DSK.sector); FDCskipseek(DSK.sector * 256); return 1; } } static void FDCreadIDmarker(void) { struct fdc_secrec *fsr = (struct fdc_secrec *) DSK.buffer; fsr->idmark = 0xfe; fsr->track = DSK.track; fsr->sector = DSK.sector; fsr->seclen = 2; fsr->side = DSK.side; fsr->datamark = 0xfb; DSK.buflen = sizeof(*fsr); DSK.bufpos = 1; logger(_L|L_1, "ID marker: track=%d sector=%d side=%d\n", fsr->track, fsr->sector, fsr->side); FDCcheckdisk(); } static void FDCreadsector(void) { logger(_L|0, "FDC read sector, T%d S%d s%d\n", DSK.track, DSK.sector, DSK.side); if (DSK.handle) { if (FDCfindIDmarker()) { OSSize ret = 256; OSError err; err = OS_Read(DSK.handle, (void *) DSK.buffer, &ret); if (err != OS_NOERR || ret < 256) { DSK.status |= fdc_CRCERR; DSK.status |= fdc_BADRECORD; DSK.buflen = 0; } else { int x; DSK.buflen = ret; if (log_level(LOG_REALDISK) >= 2) { logger(_L|L_2, "Apparent filename = %8.8s\n", FLAT_MEMORY_PTR(md_video, memory_read_word(0x8356) - memory_read_word(0x8354) + 1)); logger(_L|L_2, "Sector contents:\n"); for (x = 0; x < ret; x++) { int y; logger(_L|L_2, "%04X=", x); for (y = 0; y < 16; y++) logger(_L|L_2, "%02X ", DSK.buffer[x + y]); logger(_L|L_2, " "); for (y = 0; y < 16; y++) logger(_L|L_2, "%c", isprint(DSK.buffer[x + y]) ? DSK.buffer[x + y] : '.'); logger(_L|L_2, "\n"); x += 16; } } } DSK.bufpos = 0; return; } } else { logger(_L|0, "FDCreadsector failed, DSK.handle is 0\n"); } DSK.status |= fdc_BADRECORD; DSK.bufpos = 0; DSK.buflen = 0; } static void FDCwritesector(void) { logger(_L|0, "FDC write sector, T%d S%d s%d\n", DSK.track, DSK.sector, DSK.side); if (FDCfindIDmarker() == 0) { DSK.status |= fdc_BADRECORD; DSK.buflen = 0; } else { DSK.buflen = 256; DSK.postponed = 1; } DSK.written = DSK.bufpos = 0; FDCcheckdisk(); } static void FDCformattrack(void) { logger(_L|0, "FDC format track, #%d\n", DSK.track); DSK.formatsaving = 0; DSK.buflen = DSK.tracksize; } static void FDCguaranteetrackspace(void) { u32 size = (DSK.track + 1) * DSK.tracksize; if (size > FDCgetfilesize()) { if (OS_SetSize(DSK.handle, size) != OS_NOERR) { DSK.status |= fdc_CRCERR | fdc_BADRECORD; } } } static void FDCinterrupt(void) { logger(_L|0, "FDC interrupt\n"); if (DSK.postponed) { FDCflushbuffer(); DSK.postponed = 0; } DSK.status |= fdc_READY; } static void FDCholdoff(void) { logger(_L|0, "FDC hold off\n"); if (DSK.postponed) { FDCflushbuffer(); DSK.postponed = 0; } DSK.status |= fdc_READY; } ////////////////// static s8 FDCreadbyte(u32 addr) { u8 ret; switch ((addr - 0x5ff0) >> 1) { case 0: /* R_RDSTAT */ ret = DSK.status ^ 0x80; logger(_L|L_1, "FDC read status >%04X = >%02X\n", addr, (u8) ret); break; case 1: /* R_RTADDR */ ret = DSK.track; logger(_L|L_1, "FDC read track addr >%04X = >%02X\n", addr, (u8) ret); break; case 2: /* R_RSADDR */ ret = DSK.sector; logger(_L|L_1, "FDC read sector addr >%04X = >%02X\n", addr, (u8) ret); break; case 3: /* R_RDDATA */ if (DSK.hold && (DSK.command == FDC_readsector || DSK.command == FDC_readIDmarker)) { ret = DSK.buffer[DSK.bufpos]; DSK.bufpos++; if (DSK.bufpos >= DSK.buflen) { DSK.bufpos = 0; } } logger(_L|L_2, "FDC read data >%02X\n", addr, (u8) ret); break; case 4: /* R_WTCMD */ case 5: /* R_WTADDR */ case 6: /* R_WSADDR */ case 7: /* R_WTDATA */ ret = 0x00; logger(_L|L_1, "FDC read write xxx >%04X = >%02X\n", addr, (u8) ret); break; } return ret; } static void FDCwritebyte(u32 addr, u8 val) { switch ((addr - 0x5ff0) >> 1) { case 0: /* W_RDSTAT */ case 1: /* W_RTADDR */ case 2: /* W_RSADDR */ case 3: /* W_RDDATA */ logger(_L|L_1, "FDC write read xxx >%04X, >%02X\n", addr, val); break; case 4: /* W_WTCMD */ DSK.bufpos = DSK.status = DSK.postponed = 0; DSK.command = val; switch (DSK.command) { case FDC_seekhome: FDCseekhome(); break; case FDC_seek: FDCseek(); break; case FDC_stepin: FDCstepin(); break; case FDC_readsector: FDCreadsector(); break; case FDC_writesector: FDCwritesector(); break; case FDC_readIDmarker: FDCreadIDmarker(); break; case FDC_interrupt: FDCinterrupt(); break; case FDC_formattrack: FDCformattrack(); break; default: logger(_L|0, "unknown FDC command >%02X\n", val); } break; case 5: /* W_WTADDR */ DSK.track = val; logger(_L|0, "FDC write track addr >%04X, >%02X\n", addr, val); break; case 6: /* W_WSADDR */ DSK.sector = val; logger(_L|0, "FDC write sector addr >%04X, >%02X\n", addr, val); break; case 7: /* W_WTDATA */ if (DSK.hold == 0) DSK.lastbyte = val; else { if (DSK.command == FDC_writesector) { DSK.buffer[DSK.bufpos++] = val; if (DSK.bufpos >= DSK.buflen) FDCflushbuffer(); } else if (DSK.command == FDC_formattrack) { FDCguaranteetrackspace(); } } } } static void bwdrom_realdisk(const mrstruct *mr, u32 addr, s8 val) { if (addr >= 0x5ff0) FDCwritebyte(addr, ~val); } static s8 brdrom_realdisk(const mrstruct *mr, u32 addr) { if (addr >= 0x5ff0) return ~FDCreadbyte(addr); else return BYTE(mr->areamemory, addr - 0x5C00); } extern vmModule realDiskDSR; mrstruct dsr_rom_realdisk_handler = { realdiskdsr, realdiskdsr, NULL, /* ROM */ NULL, NULL, NULL, NULL }; // this is only used on the last area of the DSR ROM block */ mrstruct dsr_rom_realdisk_io_handler = { realdiskdsr + 0x1C00, NULL, NULL, NULL, brdrom_realdisk, NULL, bwdrom_realdisk }; static u32 cruwRealDiskROM(u32 addr, u32 data, u32 num) { if (data) { logger(_L|0, "real disk DSR on\n"); report_status(STATUS_DISK_ACCESS, 1, true); // stateflag|=ST_DEBUG; dsr_set_active(&realDiskDSR); SET_AREA_HANDLER(0x4000, 0x1c00, &dsr_rom_realdisk_handler); SET_AREA_HANDLER(0x5c00, 0x400, &dsr_rom_realdisk_io_handler); logger(_L|L_1, "DSR Read >4000 = >%2X\n", memory_read_byte(0x4000)); } else { logger(_L|0, "real disk DSR off\n"); report_status(STATUS_DISK_ACCESS, 1, false); // stateflag&=~ST_DEBUG; dsr_set_active(NULL); SET_AREA_HANDLER(0x4000, 0x2000, &zero_memory_handler); } return 0; } static u32 cruwRealDiskHold(u32 addr, u32 data, u32 num) { logger(_L|0, "CRU hold off\n"); DSK.hold = data; if (DSK.hold == 0) { FDCholdoff(); } return 0; } static u32 cruwRealDiskHeads(u32 addr, u32 data, u32 num) { logger(_L|0, "CRU heads\n"); return 0; } static u32 cruwRealDiskSel(u32 addr, u32 data, u32 num) { u8 newnum = (addr - 0x1106) >> 1; logger(_L|0, "CRU disk select, #%d\n", newnum); if (data) { logger(_L|0, "opening (DSK.num=%d)\n", DSK.num); if (newnum != DSK.num) { FDCclosedisk(); DSK.num = newnum; } if (!DSK.handle) { FDCopendisk(); } } else if (newnum == DSK.num) { logger(_L|0, "closing\n"); FDCclosedisk(); DSK.num = 0; } return 0; } static u32 cruwRealDiskSide(u32 addr, u32 data, u32 num) { DSK.side = data; FDCseektotrack(); return 0; } static u32 crurRealDiskPoll(u32 addr, u32 data, u32 num) { return 0; } /*************************************/ static DECL_SYMBOL_ACTION(realdisk_disk_change) { int dsknum = sym->name[9] - '0'; if (task == caa_READ) return 0; if (!dsr_is_real_disk(dsknum)) logger(_L|L_0, "DOAD server: DSK%d (%s) is inaccessible\n" "when emulated (FIAD) disk DSR is loaded\n", dsknum, diskname[dsknum - 1]); if (DSK.num == dsknum) { logger(_L|LOG_WARN, "Changing disk %d while it's being accessed!\n", DSK.num); FDCclosedisk(); } return 1; } /************************************/ static vmResult realdisk_getcrubase(u32 * base) { *base = 0x1100; return vmOk; } static vmResult realdisk_filehandler(u32 code) { /* shouldn't ever get here */ return vmOk; } static vmResult realdisk_detect(void) { return vmOk; } static vmResult realdisk_init(void) { command_symbol_table *realdiskcommands = command_symbol_table_new("TI Disk DSR Options", "These commands control the TI 'real' disk-on-a-disk (DOAD) emulation", command_symbol_new("DiskImagePath", "Set directory list to search for DOAD disk images", c_STATIC, NULL /* action */ , RET_FIRST_ARG, command_arg_new_string ("path", "list of directories " "separated by one of these characters: '" OS_ENVSEPLIST "'", NULL /* action */ , -1, &diskimagepath, NULL /* next */ ) , command_symbol_new("DiskImage1|DSK1Image", "DOAD image in drive 1", c_STATIC, realdisk_disk_change, RET_FIRST_ARG, command_arg_new_string ("file", "name of DOAD image", NULL /* action */ , ARG_STR(diskname[0]), NULL /* next */ ) , command_symbol_new("DiskImage2|DSK2Image", "DOAD image in drive 2", c_STATIC, realdisk_disk_change, RET_FIRST_ARG, command_arg_new_string ("file", "name of DOAD image", NULL /* action */ , ARG_STR(diskname[1]), NULL /* next */ ) , command_symbol_new("DiskImage3|DSK3Image", "DOAD image in drive 3", c_STATIC, realdisk_disk_change, RET_FIRST_ARG, command_arg_new_string ("file", "name of DOAD image", NULL /* action */ , ARG_STR(diskname[2]), NULL /* next */ ) , command_symbol_new("DiskDSRFileName", "Name of DSR ROM image which fits in the CPU address space >4000...>5FFF", c_STATIC, NULL /* action */ , RET_FIRST_ARG, command_arg_new_string ("file", "name of binary image", NULL /* action */ , ARG_STR(realdiskfilename), NULL /* next */ ) , NULL /* next */ ))))), NULL /* sub */ , NULL /* next */ ); command_symbol_table_add_subtable(universe, realdiskcommands); // features |= FE_realdisk; return vmOk; } static vmResult realdisk_enable(void) { // if (!(features & FE_realdisk)) // return vmOk; logger(_L|0, "setting up TI real-disk DSR ROM\n"); if (0 == loaddsr(romspath, systemromspath, realdiskfilename, "TI disk DSR ROM", realdiskdsr)) return vmNotAvailable; if (cruadddevice(CRU_WRITE, 0x1100, 1, cruwRealDiskROM) && cruadddevice(CRU_WRITE, 0x1104, 1, cruwRealDiskHold) && cruadddevice(CRU_WRITE, 0x1106, 1, cruwRealDiskHeads) && cruadddevice(CRU_WRITE, 0x1108, 1, cruwRealDiskSel) && cruadddevice(CRU_WRITE, 0x110A, 1, cruwRealDiskSel) && cruadddevice(CRU_WRITE, 0x110C, 1, cruwRealDiskSel) && cruadddevice(CRU_WRITE, 0x110E, 1, cruwRealDiskSide) && cruadddevice(CRU_READ, 0x1102, 1, crurRealDiskPoll) && cruadddevice(CRU_READ, 0x1104, 1, crurRealDiskPoll) && cruadddevice(CRU_READ, 0x1106, 1, crurRealDiskPoll)) { // features |= FE_realdisk; return vmOk; } else return vmNotAvailable; } static vmResult realdisk_disable(void) { // if (!(features & FE_realdisk)) // return vmOk; crudeldevice(CRU_WRITE, 0x1100, 1, cruwRealDiskROM); crudeldevice(CRU_WRITE, 0x1104, 1, cruwRealDiskHold); crudeldevice(CRU_WRITE, 0x1106, 1, cruwRealDiskHeads); crudeldevice(CRU_WRITE, 0x1108, 1, cruwRealDiskSel); crudeldevice(CRU_WRITE, 0x110A, 1, cruwRealDiskSel); crudeldevice(CRU_WRITE, 0x110C, 1, cruwRealDiskSel); crudeldevice(CRU_WRITE, 0x110E, 1, cruwRealDiskSide); crudeldevice(CRU_READ, 0x1102, 1, crurRealDiskPoll); crudeldevice(CRU_READ, 0x1104, 1, crurRealDiskPoll); crudeldevice(CRU_READ, 0x1106, 1, crurRealDiskPoll); return vmOk; } static vmResult realdisk_restart(void) { return vmOk; } static vmResult realdisk_restop(void) { return vmOk; } static vmResult realdisk_term(void) { return vmOk; } static vmDSRModule realDiskModule = { 1, realdisk_getcrubase, realdisk_filehandler }; vmModule realDiskDSR = { 3, "TI real-disk DSR", "dsrRealDisk", vmTypeDSR, vmFlagsNone, realdisk_detect, realdisk_init, realdisk_term, realdisk_enable, realdisk_disable, realdisk_restart, realdisk_restop, {(vmGenericModule *) & realDiskModule} };