/* * UAE - The Un*x Amiga Emulator * * Hardfile emulation * * Copyright 1995 Bernd Schmidt * 2002 Toni Wilen (scsi emulation, 64-bit support) */ #include "sysconfig.h" #include "sysdeps.h" #include "td-sdl/thread.h" #include "options.h" #include "memory.h" #include "newcpu.h" #include "custom.h" #include "disk.h" #include "autoconf.h" #include "traps.h" #include "filesys.h" #include "execlib.h" #include "native2amiga.h" #include "gui.h" #include "uae.h" #include "execio.h" #include "zfile.h" #undef DEBUGME #define hf_log #define hf_log2 #define scsi_log #define hf_log3 //#define DEBUGME #ifdef DEBUGME #undef hf_log #define hf_log write_log #undef hf_log2 #define hf_log2 write_log #undef hf_log3 #define hf_log3 write_log #undef scsi_log #define scsi_log write_log #endif #define MAX_ASYNC_REQUESTS 50 #define ASYNC_REQUEST_NONE 0 #define ASYNC_REQUEST_TEMP 1 #define ASYNC_REQUEST_CHANGEINT 10 struct hardfileprivdata { volatile uaecptr d_request[MAX_ASYNC_REQUESTS]; volatile int d_request_type[MAX_ASYNC_REQUESTS]; volatile uae_u32 d_request_data[MAX_ASYNC_REQUESTS]; smp_comm_pipe requests; int thread_running; uae_thread_id thread_id; uae_sem_t sync_sem; uaecptr base; int changenum; uaecptr changeint; }; STATIC_INLINE uae_u32 gl (uae_u8 *p) { return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0); } static uae_sem_t change_sem = 0; static struct hardfileprivdata hardfpd[MAX_FILESYSTEM_UNITS]; static uae_u32 nscmd_cmd; static void wl (uae_u8 *p, int v) { p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v; } static void ww (uae_u8 *p, int v) { p[0] = v >> 8; p[1] = v; } static int rl (uae_u8 *p) { return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]); } static void getchs2 (struct hardfiledata *hfd, int *cyl, int *cylsec, int *head, int *tracksec) { unsigned int total = (unsigned int)(hfd->virtsize / 1024); int heads; int sectors = 63; /* do we have RDB values? */ if (hfd->cylinders) { *cyl = hfd->cylinders; *tracksec = hfd->sectors; *head = hfd->heads; *cylsec = hfd->sectors * hfd->heads; return; } /* what about HDF settings? */ if (hfd->surfaces && hfd->secspertrack) { *head = hfd->surfaces; *tracksec = hfd->secspertrack; *cylsec = (*head) * (*tracksec); *cyl = (unsigned int)(hfd->virtsize / hfd->blocksize) / ((*tracksec) * (*head)); return; } /* no, lets guess something.. */ if (total <= 504 * 1024) heads = 16; else if (total <= 1008 * 1024) heads = 32; else if (total <= 2016 * 1024) heads = 64; else if (total <= 4032 * 1024) heads = 128; else heads = 255; *cyl = (unsigned int)(hfd->virtsize / hfd->blocksize) / (sectors * heads); *cylsec = sectors * heads; *tracksec = sectors; *head = heads; } static void getchsx (struct hardfiledata *hfd, int *cyl, int *cylsec, int *head, int *tracksec) { getchs2 (hfd, cyl, cylsec, head, tracksec); hf_log (_T("CHS: %08X-%08X %d %d %d %d %d\n"), (uae_u32)(hfd->virtsize >> 32),(uae_u32)hfd->virtsize, *cyl, *cylsec, *head, *tracksec); } static void getchsgeometry2 (uae_u64 size, int *pcyl, int *phead, int *psectorspertrack, int mode) { int sptt[4]; int i, spt, head, cyl; uae_u64 total = (unsigned int)(size / 512); if (mode == 1) { // old-style head=1, spt=32 always mode head = 1; spt = 32; cyl = total / (head * spt); } else { sptt[0] = 63; sptt[1] = 127; sptt[2] = 255; sptt[3] = -1; for (i = 0; sptt[i] >= 0; i++) { spt = sptt[i]; for (head = 4; head <= 16;head++) { cyl = total / (head * spt); if (size <= 512 * 1024 * 1024) { if (cyl <= 1023) break; } else { if (cyl < 16383) break; if (cyl < 32767 && head >= 5) break; if (cyl <= 65535) break; } } if (head <= 16) break; } } *pcyl = cyl; *phead = head; *psectorspertrack = spt; } void getchsgeometry (uae_u64 size, int *pcyl, int *phead, int *psectorspertrack) { getchsgeometry2 (size, pcyl, phead, psectorspertrack, 0); } void getchsgeometry_hdf (struct hardfiledata *hfd, uae_u64 size, int *pcyl, int *phead, int *psectorspertrack) { uae_u8 block[512]; int i; if (size <= 512 * 1024 * 1024) { *phead = 1; *psectorspertrack = 32; } memset (block, 0, sizeof block); if (hfd) { hdf_read (hfd, block, 0, 512); if (block[0] == 'D' && block[1] == 'O' && block[2] == 'S') { int mode; for (mode = 0; mode < 2; mode++) { uae_u32 rootblock; uae_u32 chk = 0; getchsgeometry2 (size, pcyl, phead, psectorspertrack, mode); rootblock = (2 + ((*pcyl) * (*phead) * (*psectorspertrack) - 1)) / 2; memset (block, 0, sizeof block); hdf_read (hfd, block, (uae_u64)rootblock * 512, 512); for (i = 0; i < 512; i += 4) chk += (block[i] << 24) | (block[i + 1] << 16) | (block[i + 2] << 8) | (block[i + 3] << 0); if (!chk && block[0] == 0 && block[1] == 0 && block[2] == 0 && block[3] == 2 && block[4] == 0 && block[5] == 0 && block[6] == 0 && block[7] == 0 && block[8] == 0 && block[9] == 0 && block[10] == 0 && block[11] == 0 && block[508] == 0 && block[509] == 0 && block[510] == 0 && block[511] == 1) { return; } } } } getchsgeometry2 (size, pcyl, phead, psectorspertrack, 2); } void getchspgeometry (uae_u64 total, int *pcyl, int *phead, int *psectorspertrack, bool idegeometry) { uae_u64 blocks = total / 512; if (blocks > 16515072) { /* >8G, CHS=16383/16/63 */ *pcyl = 16383; *phead = 16; *psectorspertrack = 63; return; } if (idegeometry) { *phead = 16; *psectorspertrack = 63; *pcyl = blocks / ((*psectorspertrack) * (*phead)); return; } getchsgeometry (total, pcyl, phead, psectorspertrack); } static void getchshd (struct hardfiledata *hfd, int *pcyl, int *phead, int *psectorspertrack) { getchspgeometry (hfd->virtsize, pcyl, phead, psectorspertrack, false); } static void pl (uae_u8 *p, int off, uae_u32 v) { p += off * 4; p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v >> 0; } static void rdb_crc (uae_u8 *p) { uae_u32 sum; int i, blocksize; sum =0; blocksize = rl (p + 1 * 4); for (i = 0; i < blocksize; i++) sum += rl (p + i * 4); sum = -sum; pl (p, 2, sum); } static void create_virtual_rdb (struct hardfiledata *hfd, uae_u32 dostype, int bootpri, const TCHAR *filesys) { uae_u8 *rdb, *part, *denv; int cyl = hfd->heads * hfd->secspertrack; int cyls = 262144 / (cyl * 512); int size = cyl * cyls * 512; rdb = xcalloc (uae_u8, size); hfd->virtual_rdb = rdb; hfd->virtual_size = size; part = rdb + 512; pl(rdb, 0, 0x5244534b); pl(rdb, 1, 64); pl(rdb, 2, 0); // chksum pl(rdb, 3, 0); // hostid pl(rdb, 4, 512); // blockbytes pl(rdb, 5, 0); // flags pl(rdb, 6, -1); // badblock pl(rdb, 7, 1); // part pl(rdb, 8, -1); // fs pl(rdb, 9, -1); // driveinit pl(rdb, 10, -1); // reserved pl(rdb, 11, -1); // reserved pl(rdb, 12, -1); // reserved pl(rdb, 13, -1); // reserved pl(rdb, 14, -1); // reserved pl(rdb, 15, -1); // reserved pl(rdb, 16, hfd->nrcyls); pl(rdb, 17, hfd->secspertrack); pl(rdb, 18, hfd->heads); pl(rdb, 19, 0); // interleave pl(rdb, 20, 0); // park pl(rdb, 21, -1); // res pl(rdb, 22, -1); // res pl(rdb, 23, -1); // res pl(rdb, 24, 0); // writeprecomp pl(rdb, 25, 0); // reducedwrite pl(rdb, 26, 0); // steprate pl(rdb, 27, -1); // res pl(rdb, 28, -1); // res pl(rdb, 29, -1); // res pl(rdb, 30, -1); // res pl(rdb, 31, -1); // res pl(rdb, 32, 0); // rdbblockslo pl(rdb, 33, cyl * cyls); // rdbblockshi pl(rdb, 34, cyls); // locyl pl(rdb, 35, hfd->nrcyls + cyls); // hicyl pl(rdb, 36, cyl); // cylblocks pl(rdb, 37, 0); // autopark pl(rdb, 38, 2); // highrdskblock pl(rdb, 39, -1); // res ua_copy ((char*)rdb + 40 * 4, -1, hfd->vendor_id); ua_copy ((char*)rdb + 42 * 4, -1, hfd->product_id); ua_copy ((char*)rdb + 46 * 4, -1, _T("UAE")); rdb_crc (rdb); pl(part, 0, 0x50415254); pl(part, 1, 64); pl(part, 2, 0); pl(part, 3, 0); pl(part, 4, -1); pl(part, 5, 1); // bootable pl(part, 6, -1); pl(part, 7, -1); pl(part, 8, 0); // devflags part[9 * 4] = _tcslen (hfd->device_name); ua_copy ((char*)part + 9 * 4 + 1, -1, hfd->device_name); denv = part + 128; pl(denv, 0, 80); pl(denv, 1, 512 / 4); pl(denv, 2, 0); // secorg pl(denv, 3, hfd->heads); pl(denv, 4, hfd->blocksize / 512); pl(denv, 5, hfd->secspertrack); pl(denv, 6, hfd->reservedblocks); pl(denv, 7, 0); // prealloc pl(denv, 8, 0); // interleave pl(denv, 9, cyls); // lowcyl pl(denv, 10, hfd->nrcyls + cyls - 1); pl(denv, 11, 50); pl(denv, 12, 0); pl(denv, 13, 0x00ffffff); pl(denv, 14, 0x7ffffffe); pl(denv, 15, bootpri); pl(denv, 16, dostype); rdb_crc (part); hfd->virtsize += size; } void hdf_hd_close (struct hd_hardfiledata *hfd) { if (!hfd) return; hdf_close (&hfd->hfd); xfree (hfd->path); } int hdf_hd_open (struct hd_hardfiledata *hfd, const TCHAR *path, int blocksize, int readonly, const TCHAR *devname, int cyls, int sectors, int surfaces, int reserved, int bootpri, const TCHAR *filesys, int pcyls, int pheads, int psecs) { memset (hfd, 0, sizeof (struct hd_hardfiledata)); hfd->bootpri = bootpri; hfd->hfd.blocksize = blocksize; hfd->hfd.readonly = readonly; if (!hdf_open (&hfd->hfd, path)) return 0; hfd->path = my_strdup(path); hfd->hfd.cylinders = cyls; hfd->hfd.heads = surfaces; hfd->hfd.reservedblocks = reserved; hfd->hfd.secspertrack = sectors; if (devname) _tcscpy (hfd->hfd.device_name, devname); if (pcyls && pheads && psecs) { hfd->cyls = pcyls; hfd->heads = pheads; hfd->secspertrack = psecs; } else if (cyls && surfaces && sectors) { hfd->cyls = cyls; hfd->heads = surfaces; hfd->secspertrack = sectors; } else { getchshd (&hfd->hfd, &hfd->cyls, &hfd->heads, &hfd->secspertrack); } hfd->cyls_def = hfd->cyls; hfd->secspertrack_def = hfd->secspertrack; hfd->heads_def = hfd->heads; if (hfd->hfd.heads && hfd->hfd.secspertrack) { uae_u8 buf[512] = { 0 }; hdf_read (&hfd->hfd, buf, 0, 512); if (buf[0] != 0 && memcmp (buf, _T("RDSK"), 4)) { hfd->hfd.nrcyls = (hfd->hfd.virtsize / blocksize) / (sectors * surfaces); create_virtual_rdb (&hfd->hfd, rl (buf), hfd->bootpri, filesys); while (hfd->hfd.nrcyls * surfaces * sectors > hfd->cyls_def * hfd->secspertrack_def * hfd->heads_def) { hfd->cyls_def++; } } } hfd->size = hfd->hfd.virtsize; return 1; } static int hdf_write2 (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len); static int hdf_read2 (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len); static void hdf_init_cache (struct hardfiledata *hfd) { } static void hdf_flush_cache (struct hardfiledata *hdf) { } static int hdf_cache_read (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { return hdf_read2 (hfd, buffer, offset, len); } static int hdf_cache_write (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { return hdf_write2 (hfd, buffer, offset, len); } int hdf_open (struct hardfiledata *hfd, const TCHAR *pname) { hfd->adide = 0; hfd->byteswap = 0; if (!hdf_open_target (hfd, pname)) return 0; return 1; } void hdf_close (struct hardfiledata *hfd) { hdf_close_target (hfd); } static int hdf_read2 (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { return hdf_read_target (hfd, buffer, offset, len); } static int hdf_write2 (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { return hdf_write_target (hfd, buffer, offset, len); } static void adide_decode (void *v, int len) { int i; uae_u8 *buffer = (uae_u8*)v; for (i = 0; i < len; i += 2) { uae_u8 *b = buffer + i; uae_u16 w = (b[0] << 8) | (b[1] << 0); uae_u16 o = 0; if (w & 0x8000) o |= 0x0001; if (w & 0x0001) o |= 0x0002; if (w & 0x4000) o |= 0x0004; if (w & 0x0002) o |= 0x0008; if (w & 0x2000) o |= 0x0010; if (w & 0x0004) o |= 0x0020; if (w & 0x1000) o |= 0x0040; if (w & 0x0008) o |= 0x0080; if (w & 0x0800) o |= 0x0100; if (w & 0x0010) o |= 0x0200; if (w & 0x0400) o |= 0x0400; if (w & 0x0020) o |= 0x0800; if (w & 0x0200) o |= 0x1000; if (w & 0x0040) o |= 0x2000; if (w & 0x0100) o |= 0x4000; if (w & 0x0080) o |= 0x8000; b[0] = o >> 8; b[1] = o >> 0; } } static void adide_encode (void *v, int len) { int i; uae_u8 *buffer = (uae_u8*)v; for (i = 0; i < len; i += 2) { uae_u8 *b = buffer + i; uae_u16 w = (b[0] << 8) | (b[1] << 0); uae_u16 o = 0; if (w & 0x0001) o |= 0x8000; if (w & 0x0002) o |= 0x0001; if (w & 0x0004) o |= 0x4000; if (w & 0x0008) o |= 0x0002; if (w & 0x0010) o |= 0x2000; if (w & 0x0020) o |= 0x0004; if (w & 0x0040) o |= 0x1000; if (w & 0x0080) o |= 0x0008; if (w & 0x0100) o |= 0x0800; if (w & 0x0200) o |= 0x0010; if (w & 0x0400) o |= 0x0400; if (w & 0x0800) o |= 0x0020; if (w & 0x1000) o |= 0x0200; if (w & 0x2000) o |= 0x0040; if (w & 0x4000) o |= 0x0100; if (w & 0x8000) o |= 0x0080; b[0] = o >> 8; b[1] = o >> 0; } } static void hdf_byteswap (void *v, int len) { int i; uae_u8 *b = (uae_u8*)v; for (i = 0; i < len; i += 2) { uae_u8 tmp = b[i]; b[i] = b[i + 1]; b[i + 1] = tmp; } } int hdf_read_rdb (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { int v; v = hdf_read (hfd, buffer, offset, len); if (v > 0 && offset < 16 * 512 && !hfd->byteswap && !hfd->adide) { uae_u8 *buf = (uae_u8*)buffer; bool changed = false; if (buf[0] == 0x39 && buf[1] == 0x10 && buf[2] == 0xd3 && buf[3] == 0x12) { // AdIDE encoded "CPRM" hfd->adide = 1; changed = true; write_log (_T("HDF: adide scrambling detected\n")); } else if (!memcmp (buf, "DRKS", 4)) { hfd->byteswap = 1; changed = true; write_log (_T("HDF: byteswapped RDB detected\n")); } if (changed) v = hdf_read (hfd, buffer, offset, len); } return v; } int hdf_read (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { int v; if (!hfd->adide) { v = hdf_cache_read (hfd, buffer, offset, len); } else { offset += 512; v = hdf_cache_read (hfd, buffer, offset, len); adide_decode (buffer, len); } if (hfd->byteswap) hdf_byteswap (buffer, len); return v; } int hdf_write (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { int v; if (hfd->byteswap) hdf_byteswap (buffer, len); if (!hfd->adide) { v = hdf_cache_write (hfd, buffer, offset, len); } else { offset += 512; adide_encode (buffer, len); v = hdf_cache_write (hfd, buffer, offset, len); adide_decode (buffer, len); } if (hfd->byteswap) hdf_byteswap (buffer, len); return v; } static uae_u64 cmd_readx (struct hardfiledata *hfd, uae_u8 *dataptr, uae_u64 offset, uae_u64 len) { gui_flicker_led (LED_HD, hfd->unitnum, 1); hf_log3 (_T("cmd_read: %p %04x-%08x (%d) %08x (%d)\n"), dataptr, (uae_u32)(offset >> 32), (uae_u32)offset, (uae_u32)(offset / hfd->blocksize), (uae_u32)len, (uae_u32)(len / hfd->blocksize)); return hdf_read (hfd, dataptr, offset, len); } static uae_u64 cmd_read (struct hardfiledata *hfd, uaecptr dataptr, uae_u64 offset, uae_u64 len) { addrbank *bank_data = &get_mem_bank (dataptr); if (!len || !bank_data || !bank_data->check (dataptr, len)) return 0; return cmd_readx (hfd, bank_data->xlateaddr (dataptr), offset, len); } static uae_u64 cmd_writex (struct hardfiledata *hfd, uae_u8 *dataptr, uae_u64 offset, uae_u64 len) { gui_flicker_led (LED_HD, hfd->unitnum, 2); hf_log3 (_T("cmd_write: %p %04x-%08x (%d) %08x (%d)\n"), dataptr, (uae_u32)(offset >> 32), (uae_u32)offset, (uae_u32)(offset / hfd->blocksize), (uae_u32)len, (uae_u32)(len / hfd->blocksize)); return hdf_write (hfd, dataptr, offset, len); } static uae_u64 cmd_write (struct hardfiledata *hfd, uaecptr dataptr, uae_u64 offset, uae_u64 len) { addrbank *bank_data = &get_mem_bank (dataptr); if (!len || !bank_data || !bank_data->check (dataptr, len)) return 0; return cmd_writex (hfd, bank_data->xlateaddr (dataptr), offset, len); } static int checkbounds (struct hardfiledata *hfd, uae_u64 offset, uae_u64 len) { if (offset >= hfd->virtsize) return 0; if (offset + len > hfd->virtsize) return 0; return 1; } static int nodisk (struct hardfiledata *hfd) { if (hfd->drive_empty) return 1; return 0; } void hardfile_do_disk_change (struct uaedev_config_info *uci, int insert) { int fsid = uci->configoffset; int j; int newstate = insert ? 0 : 1; struct hardfiledata *hfd; hfd = get_hardfile_data (fsid); if (!hfd) return; uae_sem_wait (&change_sem); hardfpd[fsid].changenum++; write_log (_T("uaehf.device:%d media status=%d changenum=%d\n"), fsid, insert, hardfpd[fsid].changenum); hfd->drive_empty = newstate; j = 0; while (j < MAX_ASYNC_REQUESTS) { if (hardfpd[fsid].d_request_type[j] == ASYNC_REQUEST_CHANGEINT) { uae_Cause (hardfpd[fsid].d_request_data[j]); } j++; } if (hardfpd[fsid].changeint) uae_Cause (hardfpd[fsid].changeint); uae_sem_post (&change_sem); } static int add_async_request (struct hardfileprivdata *hfpd, uaecptr request, int type, uae_u32 data) { int i; i = 0; while (i < MAX_ASYNC_REQUESTS) { if (hfpd->d_request[i] == request) { hfpd->d_request_type[i] = type; hfpd->d_request_data[i] = data; hf_log (_T("old async request %p (%d) added\n"), request, type); return 0; } i++; } i = 0; while (i < MAX_ASYNC_REQUESTS) { if (hfpd->d_request[i] == 0) { hfpd->d_request[i] = request; hfpd->d_request_type[i] = type; hfpd->d_request_data[i] = data; hf_log (_T("async request %p (%d) added (total=%d)\n"), request, type, i); return 0; } i++; } hf_log (_T("async request overflow %p!\n"), request); return -1; } static int release_async_request (struct hardfileprivdata *hfpd, uaecptr request) { int i = 0; while (i < MAX_ASYNC_REQUESTS) { if (hfpd->d_request[i] == request) { int type = hfpd->d_request_type[i]; hfpd->d_request[i] = 0; hfpd->d_request_data[i] = 0; hfpd->d_request_type[i] = 0; hf_log (_T("async request %p removed\n"), request); return type; } i++; } hf_log (_T("tried to remove non-existing request %p\n"), request); return -1; } static void abort_async (struct hardfileprivdata *hfpd, uaecptr request, int errcode, int type) { int i; hf_log (_T("aborting async request %p\n"), request); i = 0; while (i < MAX_ASYNC_REQUESTS) { if (hfpd->d_request[i] == request && hfpd->d_request_type[i] == ASYNC_REQUEST_TEMP) { /* ASYNC_REQUEST_TEMP = request is processing */ sleep_millis (1); i = 0; continue; } i++; } i = release_async_request (hfpd, request); if (i >= 0) hf_log (_T("asyncronous request=%08X aborted, error=%d\n"), request, errcode); } static void *hardfile_thread (void *devs); static int start_thread (TrapContext *context, int unit) { struct hardfileprivdata *hfpd = &hardfpd[unit]; if (hfpd->thread_running) return 1; memset (hfpd, 0, sizeof (struct hardfileprivdata)); hfpd->base = m68k_areg(regs, 6); init_comm_pipe (&hfpd->requests, 100, 1); uae_sem_init (&hfpd->sync_sem, 0, 0); uae_start_thread (_T("hardfile"), hardfile_thread, hfpd, &(hfpd->thread_id)); uae_sem_wait (&hfpd->sync_sem); return hfpd->thread_running; } static int mangleunit (int unit) { if (unit <= 99) return unit; if (unit == 100) return 8; if (unit == 110) return 9; return -1; } static uae_u32 REGPARAM2 hardfile_open (TrapContext *context) { uaecptr ioreq = m68k_areg(regs, 1); /* IOReq */ int unit = mangleunit(m68k_dreg (regs, 0)); struct hardfileprivdata *hfpd = &hardfpd[unit]; int err = IOERR_OPENFAIL; int size = get_word (ioreq + 0x12); /* boot device port size == 0!? KS 1.x size = 12??? */ if (size >= IOSTDREQ_SIZE || size == 0 || kickstart_version == 0xffff || kickstart_version < 39) { /* Check unit number */ if (unit >= 0) { struct hardfiledata *hfd = get_hardfile_data (unit); if (hfd && hfd->handle_valid && start_thread (context, unit)) { put_word (hfpd->base + 32, get_word (hfpd->base + 32) + 1); put_long (ioreq + 24, unit); /* io_Unit */ put_byte (ioreq + 31, 0); /* io_Error */ put_byte (ioreq + 8, 7); /* ln_type = NT_REPLYMSG */ hf_log (_T("hardfile_open, unit %d (%d), OK\n"), unit, m68k_dreg (regs, 0)); return 0; } } if (unit < 1000 || is_hardfile(unit) == FILESYS_VIRTUAL) err = 50; /* HFERR_NoBoard */ } else { err = IOERR_BADLENGTH; } hf_log (_T("hardfile_open, unit %d (%d), ERR=%d\n"), unit, m68k_dreg (regs, 0), err); put_long (ioreq + 20, (uae_u32)err); put_byte (ioreq + 31, (uae_u8)err); return (uae_u32)err; } static uae_u32 REGPARAM2 hardfile_close (TrapContext *context) { uaecptr request = m68k_areg(regs, 1); /* IOReq */ int unit = mangleunit (get_long (request + 24)); struct hardfileprivdata *hfpd = &hardfpd[unit]; if (!hfpd) return 0; put_word (hfpd->base + 32, get_word (hfpd->base + 32) - 1); if (get_word(hfpd->base + 32) == 0) write_comm_pipe_u32 (&hfpd->requests, 0, 1); return 0; } static uae_u32 REGPARAM2 hardfile_expunge (TrapContext *context) { return 0; /* Simply ignore this one... */ } static void outofbounds (int cmd, uae_u64 offset, uae_u64 len, uae_u64 max) { write_log (_T("UAEHF: cmd %d: out of bounds, %08X-%08X + %08X-%08X > %08X-%08X\n"), cmd, (uae_u32)(offset >> 32),(uae_u32)offset,(uae_u32)(len >> 32),(uae_u32)len, (uae_u32)(max >> 32),(uae_u32)max); } static void unaligned (int cmd, uae_u64 offset, uae_u64 len, int blocksize) { write_log (_T("UAEHF: cmd %d: unaligned access, %08X-%08X, %08X-%08X, %08X\n"), cmd, (uae_u32)(offset >> 32),(uae_u32)offset,(uae_u32)(len >> 32),(uae_u32)len, blocksize); } static uae_u32 hardfile_do_io (struct hardfiledata *hfd, struct hardfileprivdata *hfpd, uaecptr request) { uae_u32 dataptr, offset, actual = 0, cmd; uae_u64 offset64; int unit = get_long (request + 24); uae_u32 error = 0, len; int async = 0; int bmask = hfd->blocksize - 1; cmd = get_word (request + 28); /* io_Command */ dataptr = get_long (request + 40); switch (cmd) { case CMD_READ: if (nodisk (hfd)) goto no_disk; offset = get_long (request + 44); len = get_long (request + 36); /* io_Length */ if (offset & bmask) { unaligned (cmd, offset, len, hfd->blocksize); goto bad_command; } if (len & bmask) { unaligned (cmd, offset, len, hfd->blocksize); goto bad_len; } if (len + offset > hfd->virtsize) { outofbounds (cmd, offset, len, hfd->virtsize); goto bad_len; } actual = (uae_u32)cmd_read (hfd, dataptr, offset, len); break; case TD_READ64: case NSCMD_TD_READ64: if (nodisk (hfd)) goto no_disk; offset64 = get_long (request + 44) | ((uae_u64)get_long (request + 32) << 32); len = get_long (request + 36); /* io_Length */ if (offset64 & bmask) { unaligned (cmd, offset64, len, hfd->blocksize); goto bad_command; } if (len & bmask) { unaligned (cmd, offset64, len, hfd->blocksize); goto bad_len; } if (len + offset64 > hfd->virtsize) { outofbounds (cmd, offset64, len, hfd->virtsize); goto bad_len; } actual = (uae_u32)cmd_read (hfd, dataptr, offset64, len); break; case CMD_WRITE: case CMD_FORMAT: /* Format */ if (nodisk (hfd)) goto no_disk; if (hfd->readonly || hfd->dangerous) { error = 28; /* write protect */ } else { offset = get_long (request + 44); len = get_long (request + 36); /* io_Length */ if (offset & bmask) { unaligned (cmd, offset, len, hfd->blocksize); goto bad_command; } if (len & bmask) { unaligned (cmd, offset, len, hfd->blocksize); goto bad_len; } if (len + offset > hfd->virtsize) { outofbounds (cmd, offset, len, hfd->virtsize); goto bad_len; } actual = (uae_u32)cmd_write (hfd, dataptr, offset, len); } break; case TD_WRITE64: case TD_FORMAT64: case NSCMD_TD_WRITE64: case NSCMD_TD_FORMAT64: if (nodisk (hfd)) goto no_disk; if (hfd->readonly || hfd->dangerous) { error = 28; /* write protect */ } else { offset64 = get_long (request + 44) | ((uae_u64)get_long (request + 32) << 32); len = get_long (request + 36); /* io_Length */ if (offset64 & bmask) { unaligned (cmd, offset64, len, hfd->blocksize); goto bad_command; } if (len & bmask) { unaligned (cmd, offset64, len, hfd->blocksize); goto bad_len; } if (len + offset64 > hfd->virtsize) { outofbounds (cmd, offset64, len, hfd->virtsize); goto bad_len; } actual = (uae_u32)cmd_write (hfd, dataptr, offset64, len); } break; bad_command: error = IOERR_BADADDRESS; break; bad_len: error = IOERR_BADLENGTH; no_disk: error = 29; /* no disk */ break; case NSCMD_DEVICEQUERY: put_long (dataptr + 0, 0); put_long (dataptr + 4, 16); /* size */ put_word (dataptr + 8, NSDEVTYPE_TRACKDISK); put_word (dataptr + 10, 0); put_long (dataptr + 12, nscmd_cmd); actual = 16; break; case CMD_GETDRIVETYPE: actual = DRIVE_NEWSTYLE; break; case CMD_GETNUMTRACKS: { int cyl, cylsec, head, tracksec; getchsx (hfd, &cyl, &cylsec, &head, &tracksec); actual = cyl * head; break; } case CMD_GETGEOMETRY: { int cyl, cylsec, head, tracksec; uae_u64 size; getchsx (hfd, &cyl, &cylsec, &head, &tracksec); put_long (dataptr + 0, hfd->blocksize); size = hfd->virtsize / hfd->blocksize; if (size > 0x00ffffffff) size = 0xffffffff; put_long (dataptr + 4, (uae_u32)size); put_long (dataptr + 8, cyl); put_long (dataptr + 12, cylsec); put_long (dataptr + 16, head); put_long (dataptr + 20, tracksec); put_long (dataptr + 24, 0); /* bufmemtype */ put_byte (dataptr + 28, 0); /* type = DG_DIRECT_ACCESS */ put_byte (dataptr + 29, 0); /* flags */ } break; case CMD_PROTSTATUS: if (hfd->readonly || hfd->dangerous) actual = -1; else actual = 0; break; case CMD_CHANGESTATE: actual = hfd->drive_empty ? 1 :0; break; /* Some commands that just do nothing and return zero */ case CMD_UPDATE: case CMD_CLEAR: case CMD_MOTOR: case CMD_SEEK: case TD_SEEK64: case NSCMD_TD_SEEK64: break; case CMD_REMOVE: hfpd->changeint = get_long (request + 40); break; case CMD_CHANGENUM: actual = hfpd->changenum; break; case CMD_ADDCHANGEINT: error = add_async_request (hfpd, request, ASYNC_REQUEST_CHANGEINT, get_long (request + 40)); if (!error) async = 1; break; case CMD_REMCHANGEINT: release_async_request (hfpd, request); break; case HD_SCSICMD: /* SCSI */ error = IOERR_NOCMD; write_log (_T("UAEHF: HD_SCSICMD tried on regular HDF, unit %d\n"), unit); break; default: /* Command not understood. */ error = IOERR_NOCMD; break; } put_long (request + 32, actual); put_byte (request + 31, error); hf_log2 (_T("hf: unit=%d, request=%p, cmd=%d offset=%u len=%d, actual=%d error%=%d\n"), unit, request, get_word(request + 28), get_long (request + 44), get_long (request + 36), actual, error); return async; } static uae_u32 REGPARAM2 hardfile_abortio (TrapContext *context) { uae_u32 request = m68k_areg(regs, 1); int unit = mangleunit (get_long (request + 24)); struct hardfiledata *hfd = get_hardfile_data (unit); struct hardfileprivdata *hfpd = &hardfpd[unit]; hf_log2 (_T("uaehf.device abortio ")); start_thread(context, unit); if (!hfd || !hfpd || !hfpd->thread_running) { put_byte (request + 31, 32); hf_log2 (_T("error\n")); return get_byte (request + 31); } put_byte (request + 31, -2); hf_log2 (_T("unit=%d, request=%08X\n"), unit, request); abort_async (hfpd, request, -2, 0); return 0; } static int hardfile_can_quick (uae_u32 command) { switch (command) { case CMD_REMCHANGEINT: return -1; case CMD_RESET: case CMD_STOP: case CMD_START: case CMD_CHANGESTATE: case CMD_PROTSTATUS: case CMD_MOTOR: case CMD_GETDRIVETYPE: case CMD_GETGEOMETRY: case CMD_GETNUMTRACKS: case NSCMD_DEVICEQUERY: return 1; } return 0; } static int hardfile_canquick (struct hardfiledata *hfd, uaecptr request) { uae_u32 command = get_word (request + 28); return hardfile_can_quick (command); } static uae_u32 REGPARAM2 hardfile_beginio (TrapContext *context) { uae_u32 request = m68k_areg(regs, 1); uae_u8 flags = get_byte (request + 30); int cmd = get_word (request + 28); int unit = mangleunit (get_long (request + 24)); struct hardfiledata *hfd = get_hardfile_data (unit); struct hardfileprivdata *hfpd = &hardfpd[unit]; int canquick; put_byte (request + 8, NT_MESSAGE); start_thread(context, unit); if (!hfd || !hfpd || !hfpd->thread_running) { put_byte (request + 31, 32); return get_byte (request + 31); } put_byte (request + 31, 0); canquick = hardfile_canquick (hfd, request); if (((flags & 1) && canquick) || (canquick < 0)) { hf_log (_T("hf quickio unit=%d request=%p cmd=%d\n"), unit, request, cmd); if (hardfile_do_io (hfd, hfpd, request)) hf_log2 (_T("uaehf.device cmd %d bug with IO_QUICK\n"), cmd); if (!(flags & 1)) uae_ReplyMsg (request); return get_byte (request + 31); } else { hf_log2 (_T("hf asyncio unit=%d request=%p cmd=%d\n"), unit, request, cmd); add_async_request (hfpd, request, ASYNC_REQUEST_TEMP, 0); put_byte (request + 30, get_byte (request + 30) & ~1); write_comm_pipe_u32 (&hfpd->requests, request, 1); return 0; } } static void *hardfile_thread (void *devs) { struct hardfileprivdata *hfpd = (struct hardfileprivdata *)devs; uae_set_thread_priority (NULL, 1); hfpd->thread_running = 1; uae_sem_post (&hfpd->sync_sem); for (;;) { uaecptr request = (uaecptr)read_comm_pipe_u32_blocking (&hfpd->requests); uae_sem_wait (&change_sem); if (!request) { // dbg_rem_thread(hfpd->thread_id); hfpd->thread_running = 0; uae_sem_post (&hfpd->sync_sem); uae_sem_post (&change_sem); return 0; } else if (hardfile_do_io (get_hardfile_data (hfpd - &hardfpd[0]), hfpd, request) == 0) { put_byte (request + 30, get_byte (request + 30) & ~1); release_async_request (hfpd, request); uae_ReplyMsg (request); } else { hf_log2 (_T("async request %08X\n"), request); } uae_sem_post (&change_sem); } // dbg_rem_thread(hfpd->thread_id); } void hardfile_reset (void) { int i, j; struct hardfileprivdata *hfpd; for (i = 0; i < MAX_FILESYSTEM_UNITS; i++) { hfpd = &hardfpd[i]; if (hfpd->base && valid_address(hfpd->base, 36) && get_word(hfpd->base + 32) > 0) { for (j = 0; j < MAX_ASYNC_REQUESTS; j++) { uaecptr request; if ((request = hfpd->d_request[j])) abort_async (hfpd, request, 0, 0); } } if(hfpd->thread_running) { uae_sem_wait (&change_sem); write_comm_pipe_u32(&hfpd->requests, 0, 1); uae_sem_post (&change_sem); uae_sem_wait (&hfpd->sync_sem); uae_sem_destroy (&hfpd->sync_sem); destroy_comm_pipe (&hfpd->requests); } memset (hfpd, 0, sizeof (struct hardfileprivdata)); } } void hardfile_install (void) { uae_u32 functable, datatable; uae_u32 initcode, openfunc, closefunc, expungefunc; uae_u32 beginiofunc, abortiofunc; if(change_sem != 0) uae_sem_destroy(&change_sem); change_sem = 0; uae_sem_init (&change_sem, 0, 1); ROM_hardfile_resname = ds (_T("uaehf.device")); ROM_hardfile_resid = ds (_T("UAE hardfile.device 0.2")); nscmd_cmd = here (); dw (NSCMD_DEVICEQUERY); dw (CMD_RESET); dw (CMD_READ); dw (CMD_WRITE); dw (CMD_UPDATE); dw (CMD_CLEAR); dw (CMD_START); dw (CMD_STOP); dw (CMD_FLUSH); dw (CMD_MOTOR); dw (CMD_SEEK); dw (CMD_FORMAT); dw (CMD_REMOVE); dw (CMD_CHANGENUM); dw (CMD_CHANGESTATE); dw (CMD_PROTSTATUS); dw (CMD_GETDRIVETYPE); dw (CMD_GETGEOMETRY); dw (CMD_ADDCHANGEINT); dw (CMD_REMCHANGEINT); dw (HD_SCSICMD); dw (NSCMD_TD_READ64); dw (NSCMD_TD_WRITE64); dw (NSCMD_TD_SEEK64); dw (NSCMD_TD_FORMAT64); dw (0); /* initcode */ initcode = filesys_initcode; /* Open */ openfunc = here (); calltrap (deftrap (hardfile_open)); dw (RTS); /* Close */ closefunc = here (); calltrap (deftrap (hardfile_close)); dw (RTS); /* Expunge */ expungefunc = here (); calltrap (deftrap (hardfile_expunge)); dw (RTS); /* BeginIO */ beginiofunc = here (); calltrap (deftrap (hardfile_beginio)); dw (RTS); /* AbortIO */ abortiofunc = here (); calltrap (deftrap (hardfile_abortio)); dw (RTS); /* FuncTable */ functable = here (); dl (openfunc); /* Open */ dl (closefunc); /* Close */ dl (expungefunc); /* Expunge */ dl (EXPANSION_nullfunc); /* Null */ dl (beginiofunc); /* BeginIO */ dl (abortiofunc); /* AbortIO */ dl (0xFFFFFFFFul); /* end of table */ /* DataTable */ datatable = here (); dw (0xE000); /* INITBYTE */ dw (0x0008); /* LN_TYPE */ dw (0x0300); /* NT_DEVICE */ dw (0xC000); /* INITLONG */ dw (0x000A); /* LN_NAME */ dl (ROM_hardfile_resname); dw (0xE000); /* INITBYTE */ dw (0x000E); /* LIB_FLAGS */ dw (0x0600); /* LIBF_SUMUSED | LIBF_CHANGED */ dw (0xD000); /* INITWORD */ dw (0x0014); /* LIB_VERSION */ dw (0x0004); /* 0.4 */ dw (0xD000); dw (0x0016); /* LIB_REVISION */ dw (0x0000); dw (0xC000); dw (0x0018); /* LIB_IDSTRING */ dl (ROM_hardfile_resid); dw (0x0000); /* end of table */ ROM_hardfile_init = here (); dl (0x00000100); /* ??? */ dl (functable); dl (datatable); dl (initcode); }