diff --git a/firmware/HDD-SmartPort/HDC-SmartPort.a65 b/firmware/HDD-SmartPort/HDC-SmartPort.a65 index d8d0f5a6..c9167159 100644 --- a/firmware/HDD-SmartPort/HDC-SmartPort.a65 +++ b/firmware/HDD-SmartPort/HDC-SmartPort.a65 @@ -243,7 +243,7 @@ Cn_BLK php ldy #0 - lda BLK_Cmd,y - sta IO_BlkCmdFifo,x ; BLK_Cmd's are $0x + sta IO_BlkCmdFifo,x ; BLK_Cmd's are $0x (NB. not range-checked unlike SP cmds) iny ; BLK_UnitNum's are DSSS0000 : if SSS does NOT match physical slot... ; ...hardware should handle the remap by adding +2 to the REAL dev_ID cpy #6 ; that way, a BLK controller can support 4 devices (see $CnFE byte below) @@ -276,7 +276,9 @@ Cn_BLK_Done lsr ; Post: C=0 or 1 Cn_SP lda (Ptr1),y ; CMD byte cmp #SP_Cmd_format - bcs Cn_SP_E_BADCALL ; only STATUS, READBLOCK & WRITEBLOCK supported + beq + + bcs Cn_SP_E_BADCALL ; only STATUS, READBLOCK, WRITEBLOCK & FORMAT supported ++ ; C=1 if FORMAT cmd [*1] ldx Slot_n0 @@ -290,18 +292,28 @@ Cn_SP lda (Ptr1),y ; CMD byte lda (Ptr1),y sta Ptr1+1 - pla + pla sta Ptr1 ; Now Ptr1 = Param list - ldy #0 ; Param Cnt + ldy #0 ; Param Idx (in bytes) lda (Ptr1),y - cmp #3 ; STATUS, READBLOCK & WRITEBLOCK = all 3 parameters - sec +!if 1 { ; (This parameter-checking code could be removed to free up some space) + bcs @format ; NB. C-flag untouched from above [*1] + cmp #3 ; STATUS, READBLOCK & WRITEBLOCK: all have 3 parameters + beq + bne Cn_SP_E_BADCNT +@format cmp #1 ; FORMAT: has 1 parameter + bne Cn_SP_E_BADCNT ++ +} -- iny ; UnitNum (B), MemPtr (L/H), DiskBlock (L/M/H) -> IO (R,W) - lda (Ptr1),y ; UnitNum (B), MemPtr (L/H), StatusCode (B) -> IO (S) + ; Parameter-1: Parameter-2: Parameter-3: + ; UnitNum (B), MemPtr (L/H), StatusCode (B) -> IO (STATUS) + ; UnitNum (B), MemPtr (L/H), DiskBlock (L/M/H) -> IO (READ,WRITE) + ; UnitNum (B) -> IO (FORMAT) +- iny ; Always copy 6 more bytes to the FIFO regardless of the command + lda (Ptr1),y sta IO_SPCmdFifo,x ; UnitNum Range is 0 (smartport controller itself), 1,2,3..... cpy #6 ; blockNums are 24-bit for SP calls bcc - @@ -364,7 +376,8 @@ Cn_SP_E_BADCNT ldx #E_BADCNT ; $C7 = Removable, Interruptable, #Volumes=1, Supports write/read/status ; $D7 = Removable, Interruptable, #Volumes=2, Supports write/read/status ; $F7 = Removable, Interruptable, #Volumes=4, Supports write/read/status -; $BF = Removable, Interruptable, #Volumes=4, Supports format/write/read/status (KEGS / IIGS) +; $FF = Removable, Interruptable, #Volumes=4, Supports format/write/read/status +; $BF = Removable, , #Volumes=4, Supports format/write/read/status (KEGS / IIGS) ; ; $CnFF = offset to BLK entry point ;-------------------------------------- @@ -372,8 +385,8 @@ Cn_SP_E_BADCNT ldx #E_BADCNT *= $00FB ; org $00FB !warn "CsFB padding = ", * - @checkCsFB ;-------------------------------------- - !byte $00 ; Smart port ID Type byte + !byte $00 ; SmartPort ID Type byte !word $0000 ; how many blocks are on the device. Zero means use status call - !byte $F7 ; specifics about the device (number of drives, read/write/format capability, etc) + !byte $FF ; specifics about the device (number of drives, read/write/format capability, etc) !byte bWriteProtected; + return Err; } diff --git a/source/Harddisk.cpp b/source/Harddisk.cpp index 5bfe40cc..520194db 100644 --- a/source/Harddisk.cpp +++ b/source/Harddisk.cpp @@ -82,16 +82,16 @@ Memory map SmartPort device (IO addr + s*$10): ; C088 (r) NEXT BYTE (legacy read-only port - still supported) C089 (r) LOW BYTE OF DISK IMAGE SIZE IN BLOCKS C08A (r) HIGH BYTE OF DISK IMAGE SIZE IN BLOCKS - C089 (w) a 6-deep FIFO to write: command, unitNum, memPtr(2), blockNum(2) - C08A (w) a 7-deep FIFO to write: command, unitNum, memPtr(2), blockNum(3); first byte gets OR'd with $80 (ie. to indicate it's an SP command) + C089 (w) a 6-deep FIFO to write: command, unitNum, memPtr(2), blockNum(2); for BLK driver + C08A (w) a 7-deep FIFO to write: command, unitNum, memPtr(2), blockNum(3); for SP: first byte gets OR'd with $80 (ie. to indicate it's an SP command) */ /* Hard drive emulation in AppleWin - for the HDDRVR v1 & v2 firmware Concept - To emulate a 32mb hard drive connected to an Apple IIe via AppleWin. - Designed to work with Autoboot Rom and Prodos. + To emulate a 32MiB hard drive connected to an Apple IIe via AppleWin. + Designed to work with Autoboot ROM and ProDOS. Overview 1. Hard drive image file @@ -130,8 +130,8 @@ Overview patching prodos (to detect DELETE FILE calls). 4. FORMAT - Ignored. This would be used for low level formatting of the device - (as in the case of a tape or SCSI drive, perhaps). + This is used for low level formatting of the device. (GH#88) + (Also in the case of a tape or SCSI drive, perhaps.) 3. Bugs The only thing I've noticed is that Copy II+ 7.1 seems to crash or stall @@ -409,6 +409,12 @@ bool HarddiskInterfaceCard::Insert(const int iDrive, const std::string& pathname if (m_hardDiskDrive[iDrive].m_imageloaded) Unplug(iDrive); + const DWORD dwAttributes = GetFileAttributes(pathname.c_str()); + if (dwAttributes == INVALID_FILE_ATTRIBUTES) + m_hardDiskDrive[iDrive].m_bWriteProtected = false; // File doesn't exist - so ImageOpen() below will fail + else + m_hardDiskDrive[iDrive].m_bWriteProtected = (dwAttributes & FILE_ATTRIBUTE_READONLY) ? true : false; + // Check if image is being used by the other HDD, and unplug it in order to be swapped { const std::string & pszOtherPathname = HarddiskGetFullPathName(!iDrive); @@ -517,11 +523,12 @@ void HarddiskInterfaceCard::Unplug(const int iDrive) #if 0 // Enable HDD command logging #define LOG_DISK(format, ...) LOG(format, __VA_ARGS__) +#define DEBUG_SKIP_BUSY_STATUS 1 #else #define LOG_DISK(...) +#define DEBUG_SKIP_BUSY_STATUS 0 #endif -#define DEBUG_SKIP_BUSY_STATUS 0 // ProDOS BLK & SmartPort commands // @@ -553,6 +560,7 @@ const UINT SP_Cmd_busyStatus = SP_Cmd_base + 0x3F; // AppleWin vendor-specific c #define BADCTL 0x21 #define DEVICE_IO_ERROR 0x27 #define DEVICE_NOT_CONNECTED 0x28 // No device detected/connected +#define NOWRITE 0x2B // Disk write protected #define ERRORCODE_MASK 0x3F // limit to just 6 bits (as 'error' byte uses b0 & b7 for other status) #define STATUS_OK 0x00 @@ -655,6 +663,7 @@ BYTE HarddiskInterfaceCard::CmdExecute(HardDiskDrive* pHDD) } const UINT CYCLES_FOR_DMA_RW_BLOCK = HD_BLOCK_SIZE; + const UINT CYCLES_FOR_FORMATTING_1_BLOCK = 100; // Arbitrary const UINT PAGE_SIZE = 256; pHDD->m_error = DEVICE_OK; @@ -671,7 +680,7 @@ BYTE HarddiskInterfaceCard::CmdExecute(HardDiskDrive* pHDD) break; case SP_Cmd_status: pHDD->m_error = SmartPortCmdStatus(pHDD); - LOG_DISK("ST: %02X\n", pHDD->m_error); + LOG_DISK("ST: %02X (statusCode: %02X)\n", pHDD->m_error, m_statusCode); break; case BLK_Cmd_Read: case SP_Cmd_readblock: @@ -744,83 +753,119 @@ BYTE HarddiskInterfaceCard::CmdExecute(HardDiskDrive* pHDD) case BLK_Cmd_Write: case SP_Cmd_writeblock: { - LOG_DISK("WR: %08X (from addr: %04X)\n", pHDD->m_diskblock, pHDD->m_memblock); - pHDD->m_status_next = DISK_STATUS_WRITE; // or DISK_STATUS_PROT if we ever enable write-protect on HDD - bool bRes = true; - const bool bAppendBlocks = (pHDD->m_diskblock * HD_BLOCK_SIZE) >= ImageGetImageSize(pHDD->m_imagehandle); - bool breakpointHit = false; - - if (bAppendBlocks) + LOG_DISK("WR: %08X (from addr: %04X) %s\n", pHDD->m_diskblock, pHDD->m_memblock, pHDD->m_bWriteProtected ? "write-protected" : ""); + if (pHDD->m_bWriteProtected) { - memset(pHDD->m_buf, 0, HD_BLOCK_SIZE); - - // Inefficient (especially for gzip/zip files!) - UINT uBlock = ImageGetImageSize(pHDD->m_imagehandle) / HD_BLOCK_SIZE; - while (uBlock < pHDD->m_diskblock) - { - bRes = ImageWriteBlock(pHDD->m_imagehandle, uBlock++, pHDD->m_buf); - _ASSERT(bRes); - if (!bRes) - break; - } - } - - // Trap and error on any accesses that overlap with I/O memory (GH#1007) - if ((pHDD->m_memblock < APPLE_IO_BEGIN && ((pHDD->m_memblock + HD_BLOCK_SIZE - 1) >= APPLE_IO_BEGIN)) // 1) Starts before I/O, but ends in I/O memory - || ((pHDD->m_memblock >> 12) == (APPLE_IO_BEGIN >> 12))) // 2) Starts in I/O memory - { - WORD dstAddr = ((pHDD->m_memblock >> 12) == (APPLE_IO_BEGIN >> 12)) ? pHDD->m_memblock : APPLE_IO_BEGIN; - - if (g_nAppMode == MODE_STEPPING) - DebuggerBreakOnDmaToOrFromIoMemory(dstAddr, false); - //else // Show MessageBox? - - bRes = false; + pHDD->m_status_next = DISK_STATUS_PROT; + pHDD->m_error = NOWRITE; } else { - // NB. Do the writes in units of PAGE_SIZE so that DMA breakpoints are consistent with reads - WORD srcAddr = pHDD->m_memblock; - UINT remaining = HD_BLOCK_SIZE; - BYTE* pDst = pHDD->m_buf; + pHDD->m_status_next = DISK_STATUS_WRITE; + bool bRes = true; + const bool bAppendBlocks = (pHDD->m_diskblock * HD_BLOCK_SIZE) >= ImageGetImageSize(pHDD->m_imagehandle); + bool breakpointHit = false; - while (remaining) + if (bAppendBlocks) { - UINT size = PAGE_SIZE - (srcAddr & 0xff); - if (size > remaining) size = remaining; // clip the last memcpy for the unaligned case + memset(pHDD->m_buf, 0, HD_BLOCK_SIZE); + + // Inefficient (especially for gzip/zip files!) + UINT uBlock = ImageGetImageSize(pHDD->m_imagehandle) / HD_BLOCK_SIZE; + while (uBlock < pHDD->m_diskblock) + { + bRes = ImageWriteBlock(pHDD->m_imagehandle, uBlock++, pHDD->m_buf); + _ASSERT(bRes); + if (!bRes) + break; + } + } + + // Trap and error on any accesses that overlap with I/O memory (GH#1007) + if ((pHDD->m_memblock < APPLE_IO_BEGIN && ((pHDD->m_memblock + HD_BLOCK_SIZE - 1) >= APPLE_IO_BEGIN)) // 1) Starts before I/O, but ends in I/O memory + || ((pHDD->m_memblock >> 12) == (APPLE_IO_BEGIN >> 12))) // 2) Starts in I/O memory + { + WORD dstAddr = ((pHDD->m_memblock >> 12) == (APPLE_IO_BEGIN >> 12)) ? pHDD->m_memblock : APPLE_IO_BEGIN; if (g_nAppMode == MODE_STEPPING) - breakpointHit = DebuggerCheckMemBreakpoints(srcAddr, size, false); + DebuggerBreakOnDmaToOrFromIoMemory(dstAddr, false); + //else // Show MessageBox? - memcpy(pDst, mem + srcAddr, size); - pDst += size; - srcAddr = (srcAddr + size) & (MEMORY_LENGTH - 1); // wraps at 64KiB boundary - - remaining -= size; + bRes = false; } - } + else + { + // NB. Do the writes in units of PAGE_SIZE so that DMA breakpoints are consistent with reads + WORD srcAddr = pHDD->m_memblock; + UINT remaining = HD_BLOCK_SIZE; + BYTE* pDst = pHDD->m_buf; - if (bRes) - bRes = ImageWriteBlock(pHDD->m_imagehandle, pHDD->m_diskblock, pHDD->m_buf); + while (remaining) + { + UINT size = PAGE_SIZE - (srcAddr & 0xff); + if (size > remaining) size = remaining; // clip the last memcpy for the unaligned case - if (bRes) - { - pHDD->m_error = DEVICE_OK; + if (g_nAppMode == MODE_STEPPING) + breakpointHit = DebuggerCheckMemBreakpoints(srcAddr, size, false); - if (!breakpointHit) - m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK; + memcpy(pDst, mem + srcAddr, size); + pDst += size; + srcAddr = (srcAddr + size) & (MEMORY_LENGTH - 1); // wraps at 64KiB boundary + + remaining -= size; + } + } + + if (bRes) + bRes = ImageWriteBlock(pHDD->m_imagehandle, pHDD->m_diskblock, pHDD->m_buf); + + if (bRes) + { + pHDD->m_error = DEVICE_OK; + + if (!breakpointHit) + m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK; #if DEBUG_SKIP_BUSY_STATUS - m_notBusyCycle = 0; + m_notBusyCycle = 0; #endif - } - else - { - pHDD->m_error = DEVICE_IO_ERROR; - } + } + else + { + pHDD->m_error = DEVICE_IO_ERROR; + } + } // if (pHDD->m_bWriteProtected) } break; case BLK_Cmd_Format: - pHDD->m_error = DEVICE_IO_ERROR; // Not supported + case SP_Cmd_format: + LOG_DISK("FORMAT: write-protected=%d\n", pHDD->m_bWriteProtected); + if (pHDD->m_bWriteProtected) + { + pHDD->m_error = NOWRITE; + } + else + { + const UINT numBlocks = GetImageSizeInBlocks(pHDD->m_imagehandle); + memset(pHDD->m_buf, 0, HD_BLOCK_SIZE); + bool res = false; + m_notBusyCycle = g_nCumulativeCycles; + + for (UINT block = 0; block < numBlocks; block++) + { + // Inefficient (especially for gzip/zip files!) + res = ImageWriteBlock(pHDD->m_imagehandle, block, pHDD->m_buf); + _ASSERT(res); + if (!res) + break; + + m_notBusyCycle += (UINT64)CYCLES_FOR_FORMATTING_1_BLOCK; +#if DEBUG_SKIP_BUSY_STATUS + m_notBusyCycle = 0; +#endif + } + + pHDD->m_error = res ? DEVICE_OK : DEVICE_IO_ERROR; + } break; default: _ASSERT(0); @@ -1070,7 +1115,8 @@ BYTE HarddiskInterfaceCard::SmartPortCmdStatus(HardDiskDrive* pHDD) // general status: // . b7=block device, b6=write allowed, b5=read allowed, b4=device online or disk in drive, // . b3=format allowed, b2=media write protected (block devices only), b1=device currently interrupting (//c only), b0=device currently open (char device only) - const BYTE generalStatus = isImageLoaded ? 0xF0 : 0xE0; // Loaded: b#11110000: bwrl---- / Not loaded: b#11100000: bwr----- + BYTE generalStatus = isImageLoaded ? 0xF8 : 0xE8; // Loaded: b#11111000: bwrlf--- / Not loaded: b#11101000: bwr-f--- + if (pHDD->m_bWriteProtected) generalStatus |= (1 << 2); mem[statusListAddr++] = generalStatus; const UINT imageSizeInBlocks = isImageLoaded ? GetImageSizeInBlocks(pHDD->m_imagehandle) : 0;