diff --git a/AppleWin-VS2022.vcxproj b/AppleWin-VS2022.vcxproj
index 36ab81bf..a853a5c8 100644
--- a/AppleWin-VS2022.vcxproj
+++ b/AppleWin-VS2022.vcxproj
@@ -90,6 +90,7 @@
+
diff --git a/AppleWin-VS2022.vcxproj.filters b/AppleWin-VS2022.vcxproj.filters
index 2639c71b..ec1d8cbc 100644
--- a/AppleWin-VS2022.vcxproj.filters
+++ b/AppleWin-VS2022.vcxproj.filters
@@ -618,6 +618,9 @@
Source Files\Emulator
+
+ Source Files\Emulator
+
diff --git a/firmware/HDD-SmartPort/HDC-SmartPort.a65 b/firmware/HDD-SmartPort/HDC-SmartPort.a65
new file mode 100644
index 00000000..d8d0f5a6
--- /dev/null
+++ b/firmware/HDD-SmartPort/HDC-SmartPort.a65
@@ -0,0 +1,379 @@
+;--------------------------------------
+;AppleWin : An Apple //e emulator for Windows
+;
+;Copyright (C) 1994-1996, Michael O'Brien
+;Copyright (C) 1999-2001, Oliver Schmidt
+;Copyright (C) 2002-2005, Tom Charlesworth
+;Copyright (C) 2006-2024, Tom Charlesworth, Michael Pohoreski
+;
+;AppleWin is free software; you can redistribute it and/or modify
+;it under the terms of the GNU General Public License as published by
+;the Free Software Foundation; either version 2 of the License, or
+;(at your option) any later version.
+;
+;AppleWin is distributed in the hope that it will be useful,
+;but WITHOUT ANY WARRANTY; without even the implied warranty of
+;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;GNU General Public License for more details.
+;
+;You should have received a copy of the GNU General Public License
+;along with AppleWin; if not, write to the Free Software
+;Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+;
+
+; Description: SmartPort firmware for Hard Disk Controller card
+;
+; Author: Copyright (c) 2024, Rémy Gibert
+; . derived from works: Copyright (c) 2005, Robert Hoem
+;
+
+;--------------------------------------
+!cpu 6502 ; Compatible with all Apple2's
+!to "HDC-SmartPort.bin", plain
+!sl "HDC-SmartPort.labels"
+;--------------------------------------
+ !src "IO_Apple.inc"
+ !src "IO_Applewin.inc"
+ !src "MON.inc"
+ !src "ProDOS.inc"
+;--------------------------------------
+; SP commands
+;--------------------------------------
+SP_Cmd_status = $00
+SP_Cmd_status_STATUS = $00
+SP_Cmd_status_GETDCB = $01
+SP_Cmd_status_GETNL = $02
+SP_Cmd_status_GETDIB = $03
+SP_Cmd_readblock = $01
+SP_Cmd_writeblock = $02
+SP_Cmd_format = $03
+SP_Cmd_control = $04
+SP_Cmd_init = $05
+SP_Cmd_open = $06
+SP_Cmd_close = $07
+SP_Cmd_read = $08
+SP_Cmd_write = $09
+
+SP_Cmd_extended = $40
+;--------------------------------------
+; ZP locations
+;--------------------------------------
+Ptr1 = $3C
+
+BLK_Cmd = $42
+BLK_Cmd_Status = $0
+BLK_Cmd_Read = $1
+BLK_Cmd_Write = $2
+BLK_Cmd_Format = $3
+
+BLK_UnitNum = $43
+BLK_MemPtr = $44
+BLK_BlockNum = $46
+;--------------------------------------
+; Screen holes (scratch space)
+;--------------------------------------
+Slot_n0 = $478
+
+; = $4F8
+; = $578
+; = $5F8
+
+; = $678
+; = $6F8
+; = $778
+;Slot_C8 = $7F8 ; IRQ protocol for handling $C800 space properly
+
+; Screen holes,Cn indexed
+
+; = $478-$C0
+; = $4F8-$C0
+; = $578-$C0
+; = $5F8-$C0
+
+; = $678-$C0
+; = $6F8-$C0
+; = $778-$C0
+; = $7F8-$C0
+
+;--------------------------------------
+BootBlocklA3 = $800
+BootBlocklA2 = $801
+;--------------------------------------
+; The Autoboot rom will call this.
+; This is also the entry point for such things as IN#7 and PR#7
+; Autoboot and ProDOS look at the following few opcodes to detect block devices
+; NB. $Cn07 should be $00 for a SmartPort Interface, but changing this means that it won't autoboot on ][, ][+ and unenhanced IIe.
+; . ref: http://www.1000bit.it/support/manuali/apple/technotes/udsk/tn.udsk.2.html
+;--------------------------------------
+
+!zone code
+
+* = $0000 ; org $0000 - position-independent code, so doesn't matter (but the other fixed org positions need to be on the same page)
+
+
+Cn_start lda #$20
+ lda #$00
+ cmp #$03 ; will set C=0
+
+ lda #$00 ; SP mode
+; lda #$3C ; BLK mode
+ bcc Cn_Bootstrap
+
+Cn_Entry_BLK sec ; $Cn0A - BLK entrypoint
+ bcs Cn_Entry
+
+Cn_Entry_SP clc ; $Cn0D - SmartPort entrypoint
+
+Cn_Entry php
+ sei ; disable ints, in case an int handler races our $0000/RTS and stack accesses!
+;--------------------------------------
+; NB. need RAM that's guaranteed to be both read & writeable:
+; . can't use $0200-$BFFF, due to eg. RAMRD=0/RAMWRT=1 combination
+; . can't use LC as ROM might be enabled.
+; So use ZP (specifically $0000) as whatever the state of ALTZP, both read & write will be to the same physical memory location.
+;--------------------------------------
+ lda $00 ; save $00
+ ldx #$60 ; opcode RTS
+ stx $00
+ jsr $0000 ; RTS immediately (NB. can't use $FF58, since LC RAM may be switched in)
+ sta $00 ; restore $00
+
+ tsx
+ lda $0100,x ; $Cn
+ asl
+ asl
+ asl
+ asl
+ sta Slot_n0
+
+ plp ; + restore int status
+ bcs Cn_BLK ; C=1: ProDOS mode .....
+;--------------------------------------
+; C=0: fall through to SmartPort...
+;--------------------------------------
+ lda Ptr1 ; X = Stack Ptr from TSX above
+ pha
+
+ lda Ptr1+1
+ pha
+
+ lda $102,x ; jsr LO byte
+ sta Ptr1
+; clc ; from bcs above
+ adc #3
+ sta $102,x ; jsr LO byte
+
+ lda $103,x ; jsr HI byte
+ sta Ptr1+1
+ adc #0
+ sta $103,x ; jsr HI byte
+
+ ldy #1 ; CMD byte
+ bne Cn_SP ; Always
+;--------------------------------------
+Cn_Sloop jmp MON_SLOOP
+;--------------------------------------
+Cn_Bootstrap bit IO_IIE_OPENAPPLE
+ bmi Cn_Sloop
+
+ jsr MON_RTS ; ROM supposed to be enabled at BOOT time or PR#7
+
+ tsx
+ lda $0100,x ; $Cn
+ asl
+ asl
+ asl
+ asl
+ sta Slot_n0
+
+; Lets check to see if there's an image ready
+; Slot n, disk 1
+
+ tax ; A=%0SSS0000
+ sta IO_Unitnum,x ; b7=0 (and b6:4=SSS) => disk 1
+
+ lda #BLK_Cmd_Status
+ sta IO_Command,x
+
+ lda IO_Execute,x ; Post: A=STATUS (BLK_Cmd_Status completes immediately, no need to check STATUS.b7 busy bit)
+ ; on error A = 0eeeeee1, where eeeeee = Standard ProDOS error codes (see ProDOS.inc)
+
+ lsr ; Post: C=0 or 1
+ bcs Cn_Sloop ; no image ready, boot Slot n-1 image instead
+;--------------------------------------
+; image ready. Lets boot from it.
+; we want to load block 1 from disk 1 to $800 then jump there
+; Pre: X = Slot# << 4
+;--------------------------------------
+ lda #$1
+ sta BLK_Cmd
+ stx BLK_UnitNum ; DSSS0000, where D=0 (ie. disk 1)
+ lda #$0
+ sta BLK_MemPtr
+ sta BLK_BlockNum
+ sta BLK_BlockNum+1
+ lda #$8
+ sta BLK_MemPtr+1
+
+; clc ; C = 0 from bcs Cn_Sloop
+
+;--------------------------------------
+; entry point for BLK driver
+; simple really. Copy the command from BLK_Cmd..BLK_BlockNum+1
+; to our I/O ports then execute command
+
+; Pre:
+; X = Slot# << 4
+; C = 0 from Cn_Bootstrap
+; C = 1 from Cn_Entry_BLK
+; Post:
+; C = IO_Status b0
+; A = result of IO_Execute
+; Read or write command
+; X = Slot# << 4
+; Y = command
+; Status command
+; X = low byte of disk size
+; Y = high byte of disk size
+;--------------------------------------
+Cn_BLK php
+
+ ldx Slot_n0
+
+ ldy #0
+
+- lda BLK_Cmd,y
+ sta IO_BlkCmdFifo,x ; BLK_Cmd's are $0x
+ 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)
+ bcc -
+
+- lda IO_Execute,x ; A = result of IO_Execute (NB. instantaneous 512 byte r/w!)
+ bmi - ; b7=busy doing DMA?
+
+ plp ; restore C
+ bcs Cn_BLK_Done ; from Cn_Entry_BLK
+
+ lsr ; Post: C=0 or 1
+ bcs Cn_Sloop ; Boot Failed....
+
+ jmp BootBlocklA2 ; Success, run $801 BOOT code for Apple ][ (not /// at $800)
+
+Cn_BLK_Done lsr ; Post: C=0 or 1
+
+ ldy BLK_Cmd ; Was it status ?
+ bne + ; no, go home
+
+ pha ; Save result
+ ldy IO_Imgsizehi,x ; Get high byte of size
+ lda IO_Imgsizelo,x ; Get low byte of size
+ tax ; Transfer into X
+ pla ; Get back status call result
+
++ rts
+;--------------------------------------
+Cn_SP lda (Ptr1),y ; CMD byte
+
+ cmp #SP_Cmd_format
+ bcs Cn_SP_E_BADCALL ; only STATUS, READBLOCK & WRITEBLOCK supported
+
+ ldx Slot_n0
+
+ sta IO_SPCmdFifo,x ; Smartport calls = $8x (OR'd with $80 by h/w), Statuscode written at IO_StatusCode
+
+ iny ; param LO
+ lda (Ptr1),y
+ pha
+
+ iny ; param HI
+ lda (Ptr1),y
+
+ sta Ptr1+1
+ pla
+ sta Ptr1 ; Now Ptr1 = Param list
+
+ ldy #0 ; Param Cnt
+
+ lda (Ptr1),y
+ cmp #3 ; STATUS, READBLOCK & WRITEBLOCK = all 3 parameters
+ sec
+ 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)
+ sta IO_SPCmdFifo,x ; UnitNum Range is 0 (smartport controller itself), 1,2,3.....
+ cpy #6 ; blockNums are 24-bit for SP calls
+ bcc -
+
+- lda IO_Execute,x ; A = result of IO_Execute (NB. instantaneous 512 byte r/w!)
+ bmi - ; b7=busy doing DMA?
+
+ lsr ; get C=1 & A = ERROR CODE ... or C=0 & A = 0
+
+ tax ; Save Error Code
+
+ !byte $2C ; BIT ABS
+
+Cn_SP_E_BADCALL ldx #E_BADCALL
+
+ !byte $2C ; BIT ABS
+
+Cn_SP_E_BADCNT ldx #E_BADCNT
+
+ pla
+ sta Ptr1+1
+
+ pla
+ sta Ptr1
+
+ txa ; Restore Error Code
+
+ ldx #0
+ ldy #2 ; exit returning YX = $0200 bytes transferred if success
+
+ rts ; or C=1 & A=ERROR CODE
+;--------------------------------------
+
+!zone data
+
+;--------------------------------------
+; datablock. This starts near the end of the firmware (at offset $CnFB)
+;
+; $CnFB :
+; 7 : 1 = Support Extended commands
+; 6 : [Reserved]
+; 5 : [Reserved]
+; 4 : [Reserved]
+; 3 : [Reserved]
+; 2 : [Reserved]
+; 1 : 1 = Supports SCSI devices (Helps ProDOS build SP device list)
+; 0 : 1 = Supports RAMDisk Card
+;
+; $CnFC/D : size in blocks (BLK) if $0000 Software must issue a BLK Status Call to get size in blocks
+;
+; $CnFE = status bits (BAP p7-14)
+; 7 = medium is removable
+; 6 = device is interruptable
+; 5-4 = number of volumes (0..3 means 1..4)
+; 3 = device supports Format call
+; 2 = device can be written to
+; 1 = device can be read from (must be 1)
+; 0 = device status can be read (must be 1)
+;
+; $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)
+;
+; $CnFF = offset to BLK entry point
+;--------------------------------------
+@checkCsFB
+*= $00FB ; org $00FB
+!warn "CsFB padding = ", * - @checkCsFB
+;--------------------------------------
+ !byte $00 ; Smart port 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
+ else if (lpCmdLine[3] == 'h' && (lpCmdLine[4] >= '1' || lpCmdLine[4] <= '8')) // -s[1..7]h[1|2|...|8]
{
- const UINT drive = lpCmdLine[4] == '1' ? HARDDISK_1 : HARDDISK_2;
+ const UINT drive = lpCmdLine[4] - '1';
+ bool badDrive = drive >= NUM_HARDDISKS;
- if (slot != SLOT5 && slot != SLOT7)
+ if (badDrive || (slot != SLOT5 && slot != SLOT7))
{
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);
}
@@ -641,6 +657,10 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
{
g_cmdLine.useHdcFirmwareV1 = true;
}
+ else if (strcmp(lpCmdLine, "-hdc-firmware-v2") == 0)
+ {
+ g_cmdLine.useHdcFirmwareV2 = true;
+ }
else // unsupported
{
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);
diff --git a/source/CmdLine.h b/source/CmdLine.h
index b8ac0a26..0ef04902 100644
--- a/source/CmdLine.h
+++ b/source/CmdLine.h
@@ -14,9 +14,11 @@ struct CmdLine
SlotInfo()
{
isDiskII13 = false;
+ useHdcFirmwareMode = HdcDefault;
}
bool isDiskII13;
+ HdcMode useHdcFirmwareMode;
};
CmdLine()
@@ -35,6 +37,7 @@ struct CmdLine
supportExtraMBCardTypes = false;
noDisk2StepperDefer = false;
useHdcFirmwareV1 = false;
+ useHdcFirmwareV2 = false;
szSnapshotName = NULL;
szScreenshotFilename = NULL;
uHarddiskNumBlocks = 0;
@@ -80,6 +83,7 @@ struct CmdLine
bool supportExtraMBCardTypes;
bool noDisk2StepperDefer; // debug
bool useHdcFirmwareV1; // debug
+ bool useHdcFirmwareV2;
SS_CARDTYPE slotInsert[NUM_SLOTS];
SlotInfo slotInfo[NUM_SLOTS];
LPCSTR szImageName_drive[NUM_SLOTS][NUM_DRIVES];
diff --git a/source/Common.h b/source/Common.h
index e986d76e..1e3b6fba 100644
--- a/source/Common.h
+++ b/source/Common.h
@@ -121,8 +121,7 @@ enum AppMode_e
#define REGVALUE_GAME_IO_TYPE "Game I/O type"
#define REGVALUE_LAST_DISK_1 "Last Disk Image 1"
#define REGVALUE_LAST_DISK_2 "Last Disk Image 2"
-#define REGVALUE_LAST_HARDDISK_1 "Last Harddisk Image 1"
-#define REGVALUE_LAST_HARDDISK_2 "Last Harddisk Image 2"
+#define REGVALUE_LAST_HARDDISK_ "Last Harddisk Image "
// Preferences
#define REG_PREFS "Preferences"
diff --git a/source/Core.h b/source/Core.h
index c3fb14ea..a5bc6be9 100644
--- a/source/Core.h
+++ b/source/Core.h
@@ -9,6 +9,7 @@ void LogFileTimeUntilFirstKeyReadReset(void);
void LogFileTimeUntilFirstKeyRead(void);
extern const UINT16* GetOldAppleWinVersion(void);
+extern UINT16 g_AppleWinVersion[4];
extern std::string g_VERSIONSTRING; // Constructed in WinMain()
void SetAppleWinVersion(UINT16 major, UINT16 minor, UINT16 fix, UINT16 fix_minor);
diff --git a/source/Harddisk.cpp b/source/Harddisk.cpp
index f26a0e2b..fbe3f8f4 100644
--- a/source/Harddisk.cpp
+++ b/source/Harddisk.cpp
@@ -42,19 +42,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../resource/resource.h"
/*
-Memory map (for slot 7):
+Memory map ProDOS BLK device (IO addr + s*$10):
+. "hddrvr" v1 and v2 firmware
- C0F0 (r) EXECUTE AND RETURN STATUS
- C0F1 (r) STATUS (or ERROR): b7=busy, b0=error
- C0F2 (r/w) COMMAND
- C0F3 (r/w) UNIT NUMBER
- C0F4 (r/w) LOW BYTE OF MEMORY BUFFER
- C0F5 (r/w) HIGH BYTE OF MEMORY BUFFER
- C0F6 (r/w) LOW BYTE OF BLOCK NUMBER
- C0F7 (r/w) HIGH BYTE OF BLOCK NUMBER
- C0F8 (r) NEXT BYTE (legacy read-only port - still supported)
- C0F9 (r) LOW BYTE OF DISK IMAGE SIZE IN BLOCKS
- C0FA (r) HIGHT BYTE OF DISK IMAGE SIZE IN BLOCKS
+ C080 (r) EXECUTE AND RETURN STATUS
+ C081 (r) STATUS (or ERROR): b7=busy, b0=error
+ C082 (r/w) COMMAND
+ C083 (r/w) UNIT NUMBER
+ C084 (r/w) LOW BYTE OF MEMORY BUFFER
+ C085 (r/w) HIGH BYTE OF MEMORY BUFFER
+ C086 (r/w) LOW BYTE OF BLOCK NUMBER
+ C087 (r/w) HIGH BYTE OF BLOCK NUMBER
+ 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
Firmware notes:
. ROR ABS16,X and ROL ABS16,X - only used for $C081+s*$10 STATUS register:
@@ -62,10 +63,31 @@ Firmware notes:
65C02: double read (old data), write (new data). The write is harmless as writes to STATUS are ignored.
. STA ABS16,X does a false-read. This is harmless for writable I/O registers, since the false-read has no side effect.
+---
+
+Memory map SmartPort device (IO addr + s*$10):
+. "hdc-smartport" firmware
+. I/O basically compatible with older "hddrvr" firmware
+
+ C080 (r) EXECUTE AND RETURN STATUS; subsequent reads just return STATUS (need to write COMMAND again for EXECUTE)
+ C081 (r) STATUS : b7=busy, b0=error
+ C082 (w) COMMAND : BLK = $00 status, $01 read, $02 write. SP = $80 status, $81 read, $82 write,
+ C083 (w) UNIT NUMBER : BLK = DSSS0000 if SSS != n from CnXX, add 2 to D (4 drives support). SP = $00,$01.....
+ C084 (w) LOW BYTE OF MEMORY BUFFER
+ C085 (w) HIGH BYTE OF MEMORY BUFFER
+ C086 (w) STATUS CODE : write SP status code $00(device status), $03(device info block)
+ C086 (w) LOW BYTE OF BLOCK NUMBER : BLK = 16 bit value. SP = 24 bit value
+ C087 (w) MIDDLE BYTE OF BLOCK NUMBER
+ C088 (w) HIGH BYTE OF BLOCK NUMBER (SP only)
+; 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)
*/
/*
-Hard drive emulation in AppleWin.
+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.
@@ -131,33 +153,47 @@ Overview
HarddiskInterfaceCard::HarddiskInterfaceCard(UINT slot) :
- Card(CT_GenericHDD, slot), m_userNumBlocks(0), m_useHdcFirmwareV1(false)
+ Card(CT_GenericHDD, slot), m_userNumBlocks(0), m_useHdcFirmwareV1(false), m_useHdcFirmwareV2(false), m_useHdcFirmwareMode(HdcDefault)
{
if (m_slot != SLOT5 && m_slot != SLOT7) // fixme
ThrowErrorInvalidSlot();
- m_unitNum = HARDDISK_1 << 7; // b7=unit
+ m_unitNum = (HARDDISK_1 << 7) | (m_slot << 4); // b7=unit, b6:4=slot
// The HDD interface has a single Command register for both drives:
// . ProDOS will write to Command before switching drives
m_command = 0;
+ m_fifoIdx = 0;
+
+ // SmartPort Status cmd's Status code
+ m_statusCode = 0;
+
+ // SmartPort Controller is always loaded
+ m_smartPortController.m_imageloaded = true;
+
// Interface busy doing DMA for r/w when current cycle is earlier than this cycle
m_notBusyCycle = 0;
m_saveDiskImage = true; // Save the DiskImage name to Registry
+
+ m_saveStateFirmwareV1 = false;
+ m_saveStateFirmwareV2 = false;
+ m_saveStateFirmwareValid = false;
}
HarddiskInterfaceCard::~HarddiskInterfaceCard(void)
{
- CleanupDriveInternal(HARDDISK_1);
- CleanupDriveInternal(HARDDISK_2);
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ CleanupDriveInternal(i);
}
void HarddiskInterfaceCard::Reset(const bool powerCycle)
{
- m_hardDiskDrive[HARDDISK_1].m_error = 0;
- m_hardDiskDrive[HARDDISK_2].m_error = 0;
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ m_hardDiskDrive[i].m_error = 0;
+
+ m_fifoIdx = 0;
}
//===========================================================================
@@ -166,12 +202,43 @@ void HarddiskInterfaceCard::InitializeIO(LPBYTE pCxRomPeripheral)
{
const DWORD HARDDISK_FW_SIZE = APPLE_SLOT_SIZE;
- const WORD id = m_useHdcFirmwareV1 ? IDR_HDDRVR_FW : IDR_HDDRVR_V2_FW;
- BYTE* pData = GetFrame().GetResource(id, "FIRMWARE", HARDDISK_FW_SIZE);
- if (pData == NULL)
- return;
+ WORD id = IsEnhancedIIE() ? IDR_HDC_SMARTPORT_FW : IDR_HDDRVR_V2_FW;
- memcpy(pCxRomPeripheral + m_slot * APPLE_SLOT_SIZE, pData, HARDDISK_FW_SIZE);
+ // Use any cmd line override
+ if (m_useHdcFirmwareV1 || m_saveStateFirmwareV1) id = IDR_HDDRVR_FW;
+ else if (m_useHdcFirmwareV2 || m_saveStateFirmwareV2) id = IDR_HDDRVR_V2_FW;
+ else if (m_useHdcFirmwareMode == HdcSmartPort) id = IDR_HDC_SMARTPORT_FW;
+
+ m_saveStateFirmwareV1 = false;
+ m_saveStateFirmwareV2 = false;
+
+ bool allowFirmwareMods = false;
+ BYTE* pData = NULL;
+ if (!m_saveStateFirmwareValid)
+ {
+ pData = GetFrame().GetResource(id, "FIRMWARE", HARDDISK_FW_SIZE);
+ if (pData == NULL)
+ return;
+ allowFirmwareMods = true;
+ }
+ else
+ {
+ m_saveStateFirmwareValid = false;
+ pData = m_saveStateFirmware;
+ }
+
+ BYTE* pFirmwareBase = pCxRomPeripheral + m_slot * APPLE_SLOT_SIZE;
+ memcpy(pFirmwareBase, pData, HARDDISK_FW_SIZE);
+
+ if (allowFirmwareMods)
+ {
+ if (m_useHdcFirmwareMode == HdcBlockMode2Devices || m_useHdcFirmwareMode == HdcBlockMode4Devices)
+ {
+ pFirmwareBase[0x07] = 0x3C; // Block mode (not SmartPort)
+ const BYTE numDrives = (m_useHdcFirmwareMode == HdcBlockMode2Devices) ? 1 : 3;
+ pFirmwareBase[0xFE] = (pFirmwareBase[0xFE] & 0xCF) | (numDrives << 4); // 2 or 4 drives
+ }
+ }
RegisterIoHandler(m_slot, IORead, IOWrite, NULL, NULL, this, NULL);
}
@@ -218,12 +285,9 @@ void HarddiskInterfaceCard::NotifyInvalidImage(const std::string & szImageFilena
void HarddiskInterfaceCard::LoadLastDiskImage(const int drive)
{
- _ASSERT(drive == HARDDISK_1 || drive == HARDDISK_2);
-
- const std::string regKey = (drive == HARDDISK_1)
- ? REGVALUE_LAST_HARDDISK_1
- : REGVALUE_LAST_HARDDISK_2;
+ _ASSERT(drive >= HARDDISK_1 && drive < NUM_HARDDISKS);
+ const std::string regKey = std::string(REGVALUE_LAST_HARDDISK_) + (char)('1' + drive);
char pathname[MAX_PATH];
std::string regSection = RegGetConfigSlotSection(m_slot);
@@ -245,7 +309,7 @@ void HarddiskInterfaceCard::LoadLastDiskImage(const int drive)
void HarddiskInterfaceCard::SaveLastDiskImage(const int drive)
{
- _ASSERT(drive == HARDDISK_1 || drive == HARDDISK_2);
+ _ASSERT(drive >= HARDDISK_1 && drive < NUM_HARDDISKS);
if (!m_saveDiskImage)
return;
@@ -253,10 +317,7 @@ void HarddiskInterfaceCard::SaveLastDiskImage(const int drive)
std::string regSection = RegGetConfigSlotSection(m_slot);
RegSaveValue(regSection.c_str(), REGVALUE_CARD_TYPE, TRUE, CT_GenericHDD);
- const std::string regKey = (drive == HARDDISK_1)
- ? REGVALUE_LAST_HARDDISK_1
- : REGVALUE_LAST_HARDDISK_2;
-
+ const std::string regKey = std::string(REGVALUE_LAST_HARDDISK_) + (char)('1' + drive);
const std::string& pathName = HarddiskGetFullPathName(drive);
RegSaveString(regSection.c_str(), regKey.c_str(), TRUE, pathName);
@@ -298,7 +359,7 @@ void HarddiskInterfaceCard::GetFilenameAndPathForSaveState(std::string& filename
filename = "";
path = "";
- for (UINT i=HARDDISK_1; i<=HARDDISK_2; i++)
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
{
if (!m_hardDiskDrive[i].m_imageloaded)
continue;
@@ -322,11 +383,11 @@ void HarddiskInterfaceCard::GetFilenameAndPathForSaveState(std::string& filename
void HarddiskInterfaceCard::Destroy(void)
{
- m_saveDiskImage = false;
- CleanupDrive(HARDDISK_1);
-
- m_saveDiskImage = false;
- CleanupDrive(HARDDISK_2);
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ {
+ m_saveDiskImage = false;
+ CleanupDrive(i);
+ }
m_saveDiskImage = true;
}
@@ -446,211 +507,79 @@ void HarddiskInterfaceCard::Unplug(const int iDrive)
}
}
-bool HarddiskInterfaceCard::IsDriveUnplugged(const int iDrive)
-{
- return m_hardDiskDrive[iDrive].m_imageloaded == false;
-}
-
//===========================================================================
+#if 0 // Enable HDD command logging
+#define LOG_DISK(format, ...) LOG(format, __VA_ARGS__)
+#else
+#define LOG_DISK(...)
+#endif
+
+// ProDOS BLK & SmartPort commands
+//
+const UINT BLK_Cmd_Status = 0x00;
+const UINT BLK_Cmd_Read = 0x01;
+const UINT BLK_Cmd_Write = 0x02;
+const UINT BLK_Cmd_Format = 0x03;
+//
+const UINT SP_Cmd_base = 0x80;
+const UINT SP_Cmd_extended = 0x40;
+const UINT SP_Cmd_status = SP_Cmd_base + 0x00;
+const UINT SP_Cmd_status_STATUS = 0x00;
+const UINT SP_Cmd_status_GETDCB = 0x01;
+const UINT SP_Cmd_status_GETNL = 0x02;
+const UINT SP_Cmd_status_GETDIB = 0x03;
+const UINT SP_Cmd_readblock = SP_Cmd_base + 0x01;
+const UINT SP_Cmd_writeblock = SP_Cmd_base + 0x02;
+const UINT SP_Cmd_format = SP_Cmd_base + 0x03;
+const UINT SP_Cmd_control = SP_Cmd_base + 0x04;
+const UINT SP_Cmd_init = SP_Cmd_base + 0x05;
+const UINT SP_Cmd_open = SP_Cmd_base + 0x06;
+const UINT SP_Cmd_close = SP_Cmd_base + 0x07;
+const UINT SP_Cmd_read = SP_Cmd_base + 0x08;
+const UINT SP_Cmd_write = SP_Cmd_base + 0x09;
+const UINT SP_Cmd_busyStatus = SP_Cmd_base + 0x3F; // AppleWin vendor-specific command
+
#define DEVICE_OK 0x00
+#define BUSERR 0x06
+#define BADCTL 0x21
#define DEVICE_IO_ERROR 0x27
#define DEVICE_NOT_CONNECTED 0x28 // No device detected/connected
+#define ERRORCODE_MASK 0x3F // limit to just 6 bits (as 'error' byte uses b0 & b7 for other status)
+
+#define STATUS_OK 0x00
+#define STATUS_ERROR 0x01
+#define STATUS_BUSY 0x80
+#define SET_STATUS_ERROR(err) (((err)<<1)|STATUS_ERROR)
BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
{
const UINT slot = ((addr & 0xff) >> 4) - 8;
HarddiskInterfaceCard* pCard = (HarddiskInterfaceCard*)MemGetSlotParameters(slot);
- HardDiskDrive* pHDD = &(pCard->m_hardDiskDrive[pCard->m_unitNum >> 7]); // bit7 = drive select
+
+ HardDiskDrive* pHDD = NULL;
+ const BYTE addrIdx = addr & 0xF;
+ if (addrIdx != 0x2 && addrIdx != 0x3) // GetUnit() depends on m_command & m_unitNum
+ {
+ pHDD = pCard->GetUnit();
+ if (pHDD == NULL)
+ return SET_STATUS_ERROR(DEVICE_NOT_CONNECTED);
+ }
CpuCalcCycles(nExecutedCycles);
- const UINT CYCLES_FOR_DMA_RW_BLOCK = HD_BLOCK_SIZE;
- const UINT PAGE_SIZE = 256;
- BYTE r = DEVICE_OK;
- pHDD->m_status_next = DISK_STATUS_READ;
+ BYTE r = STATUS_OK;
- switch (addr & 0xF)
+ switch (addrIdx)
{
- case 0x0:
- if (pHDD->m_imageloaded)
- {
- // based on loaded data block request, load block into memory
- // returns status
- switch (pCard->m_command)
- {
- default:
- case 0x00: //status
- if (ImageGetImageSize(pHDD->m_imagehandle) == 0)
- {
- pHDD->m_error = 1;
- r = DEVICE_IO_ERROR;
- }
- break;
- case 0x01: //read
- if ((pHDD->m_diskblock * HD_BLOCK_SIZE) < ImageGetImageSize(pHDD->m_imagehandle))
- {
- bool breakpointHit = false;
-
- bool bRes = ImageReadBlock(pHDD->m_imagehandle, pHDD->m_diskblock, pHDD->m_buf);
- if (bRes)
- {
- pHDD->m_buf_ptr = 0;
-
- // Apple II's MMU could be setup so that read & write memory is different,
- // so can't use 'mem' (like we can for HDD block writes)
- WORD dstAddr = pHDD->m_memblock;
- UINT remaining = HD_BLOCK_SIZE;
- BYTE* pSrc = pHDD->m_buf;
-
- while (remaining)
- {
- memdirty[dstAddr >> 8] = 0xFF;
- LPBYTE page = memwrite[dstAddr >> 8];
- if (!page) // I/O space or ROM
- {
- if (g_nAppMode == MODE_STEPPING)
- DebuggerBreakOnDmaToOrFromIoMemory(dstAddr, true); // GH#1007
- //else // Show MessageBox?
-
- bRes = false;
- break;
- }
-
- // handle both page-aligned & non-page aligned destinations
- UINT size = PAGE_SIZE - (dstAddr & 0xff);
- if (size > remaining) size = remaining; // clip the last memcpy for the unaligned case
-
- if (g_nAppMode == MODE_STEPPING)
- breakpointHit = DebuggerCheckMemBreakpoints(dstAddr, size, true); // GH#1103
-
- memcpy(page + (dstAddr & 0xff), pSrc, size);
- pSrc += size;
- dstAddr = (dstAddr + size) & (MEMORY_LENGTH-1); // wraps at 64KiB boundary
-
- remaining -= size;
- }
- }
-
- if (bRes)
- {
- pHDD->m_error = 0;
- r = 0;
-
- if (!breakpointHit)
- pCard->m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK;
- }
- else
- {
- pHDD->m_error = 1;
- r = DEVICE_IO_ERROR;
- }
- }
- else
- {
- pHDD->m_error = 1;
- r = DEVICE_IO_ERROR;
- }
- break;
- case 0x02: //write
- {
- 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)
- {
- 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;
- }
- 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;
-
- while (remaining)
- {
- UINT size = PAGE_SIZE - (srcAddr & 0xff);
- if (size > remaining) size = remaining; // clip the last memcpy for the unaligned case
-
- if (g_nAppMode == MODE_STEPPING)
- breakpointHit = DebuggerCheckMemBreakpoints(srcAddr, size, false);
-
- 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 = 0;
- r = 0;
-
- if (!breakpointHit)
- pCard->m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK;
- }
- else
- {
- pHDD->m_error = 1;
- r = DEVICE_IO_ERROR;
- }
- }
- break;
- case 0x03: //format
- pHDD->m_status_next = DISK_STATUS_WRITE; // or DISK_STATUS_PROT if we ever enable write-protect on HDD
- break;
- }
- }
- else
- {
- pHDD->m_status_next = DISK_STATUS_OFF;
- pHDD->m_error = 1;
- r = DEVICE_NOT_CONNECTED; // GH#452
- }
+ case 0x0: // EXECUTE & RETURN STATUS
+ r = pCard->CmdExecute(pHDD);
+ pCard->m_command = (pCard->m_command & SP_Cmd_base) ? SP_Cmd_busyStatus : BLK_Cmd_Status; // Subsequent reads from IO addr 0x0 just executes 'Status' cmd
+ _ASSERT(pCard->m_fifoIdx == 0);
+ pCard->m_fifoIdx = 0;
break;
- case 0x1: // m_error
- if (pHDD->m_error & 0x7f)
- pHDD->m_error = 1; // Firmware requires that b0=1 for an error
- else
- pHDD->m_error = 0;
-
- if (g_nCumulativeCycles <= pCard->m_notBusyCycle)
- pHDD->m_error |= 0x80; // Firmware requires that b7=1 for busy (eg. busy doing r/w DMA operation)
- else
- pHDD->m_status_next = DISK_STATUS_OFF; // TODO: FIXME: ??? YELLOW ??? WARNING
-
- r = pHDD->m_error;
+ case 0x1: // STATUS
+ r = pCard->CmdStatus(pHDD);
break;
case 0x2:
r = pCard->m_command;
@@ -670,20 +599,20 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
case 0x7:
r = (BYTE)((pHDD->m_diskblock & 0xFF00) >> 8);
break;
- case 0x8: // Legacy: continue to support this I/O port for old HDD firmware
+ case 0x8: // Legacy: continue to support this I/O port for old HDC firmware
r = pHDD->m_buf[pHDD->m_buf_ptr];
if (pHDD->m_buf_ptr < sizeof(pHDD->m_buf)-1)
pHDD->m_buf_ptr++;
break;
case 0x9:
if (pHDD->m_imageloaded)
- r = (BYTE)(pCard->GetImageSizeInBlocks(pHDD->m_imagehandle) & 0x00ff);
+ r = (BYTE)(pCard->GetImageSizeInBlocks(pHDD->m_imagehandle, true) & 0x00ff);
else
r = 0;
break;
case 0xa:
if (pHDD->m_imageloaded)
- r = (BYTE)((pCard->GetImageSizeInBlocks(pHDD->m_imagehandle) & 0xff00) >> 8);
+ r = (BYTE)((pCard->GetImageSizeInBlocks(pHDD->m_imagehandle, true) & 0xff00) >> 8);
else
r = 0;
break;
@@ -692,7 +621,217 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
r = IO_Null(pc, addr, bWrite, d, nExecutedCycles);
}
- pCard->UpdateLightStatus(pHDD);
+ if (pHDD)
+ pCard->UpdateLightStatus(pHDD);
+
+ return r;
+}
+
+BYTE HarddiskInterfaceCard::CmdExecute(HardDiskDrive* pHDD)
+{
+ LOG_DISK("slot-%d, HDD-%d(%02X): Cmd=%02X ", m_slot, (m_command & SP_Cmd_base) ? m_unitNum : GetProDOSBlockDeviceUnit(), m_unitNum, m_command);
+
+ if (!pHDD->m_imageloaded && m_command != BLK_Cmd_Status && m_command != SP_Cmd_status)
+ {
+ pHDD->m_status_next = DISK_STATUS_OFF;
+ pHDD->m_error = DEVICE_NOT_CONNECTED; // GH#452
+ return CmdStatus(pHDD);
+ }
+
+ if ((m_command == SP_Cmd_readblock || m_command == SP_Cmd_writeblock) && pHDD->m_diskblock > kHarddiskMaxNumBlocks)
+ {
+ pHDD->m_status_next = DISK_STATUS_OFF;
+ pHDD->m_error = DEVICE_IO_ERROR;
+ return CmdStatus(pHDD);
+ }
+
+ const UINT CYCLES_FOR_DMA_RW_BLOCK = HD_BLOCK_SIZE;
+ const UINT PAGE_SIZE = 256;
+
+ pHDD->m_error = DEVICE_OK;
+
+ switch (m_command)
+ {
+ case BLK_Cmd_Status:
+ if (ImageGetImageSize(pHDD->m_imagehandle) == 0)
+ pHDD->m_error = DEVICE_IO_ERROR;
+ LOG_DISK("ST-BLK: %02X\n", pHDD->m_error);
+ break;
+ case SP_Cmd_busyStatus:
+ LOG_DISK("ST-BSY: %02X\n", pHDD->m_error);
+ break;
+ case SP_Cmd_status:
+ pHDD->m_error = SmartPortCmdStatus(pHDD);
+ LOG_DISK("ST: %02X\n", pHDD->m_error);
+ break;
+ case BLK_Cmd_Read:
+ case SP_Cmd_readblock:
+ LOG_DISK("RD: %08X\n", pHDD->m_diskblock);
+ pHDD->m_status_next = DISK_STATUS_READ;
+ if ((pHDD->m_diskblock * HD_BLOCK_SIZE) < ImageGetImageSize(pHDD->m_imagehandle))
+ {
+ bool breakpointHit = false;
+
+ bool bRes = ImageReadBlock(pHDD->m_imagehandle, pHDD->m_diskblock, pHDD->m_buf);
+ if (bRes)
+ {
+ pHDD->m_buf_ptr = 0;
+
+ // Apple II's MMU could be setup so that read & write memory is different,
+ // so can't use 'mem' (like we can for HDD block writes)
+ WORD dstAddr = pHDD->m_memblock;
+ UINT remaining = HD_BLOCK_SIZE;
+ BYTE* pSrc = pHDD->m_buf;
+
+ while (remaining)
+ {
+ memdirty[dstAddr >> 8] = 0xFF;
+ LPBYTE page = memwrite[dstAddr >> 8];
+ if (!page) // I/O space or ROM
+ {
+ if (g_nAppMode == MODE_STEPPING)
+ DebuggerBreakOnDmaToOrFromIoMemory(dstAddr, true); // GH#1007
+ //else // Show MessageBox?
+
+ bRes = false;
+ break;
+ }
+
+ // handle both page-aligned & non-page aligned destinations
+ UINT size = PAGE_SIZE - (dstAddr & 0xff);
+ if (size > remaining) size = remaining; // clip the last memcpy for the unaligned case
+
+ if (g_nAppMode == MODE_STEPPING)
+ breakpointHit = DebuggerCheckMemBreakpoints(dstAddr, size, true); // GH#1103
+
+ memcpy(page + (dstAddr & 0xff), pSrc, size);
+ pSrc += size;
+ dstAddr = (dstAddr + size) & (MEMORY_LENGTH - 1); // wraps at 64KiB boundary
+
+ remaining -= size;
+ }
+ }
+
+ if (bRes)
+ {
+ pHDD->m_error = DEVICE_OK;
+
+ if (!breakpointHit)
+ m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK;
+ }
+ else
+ {
+ pHDD->m_error = DEVICE_IO_ERROR;
+ }
+ }
+ else
+ {
+ pHDD->m_error = DEVICE_IO_ERROR;
+ }
+ break;
+ case BLK_Cmd_Write:
+ case SP_Cmd_writeblock:
+ {
+ LOG_DISK("WR: %08X\n", pHDD->m_diskblock);
+ 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)
+ {
+ 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;
+ }
+ 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;
+
+ while (remaining)
+ {
+ UINT size = PAGE_SIZE - (srcAddr & 0xff);
+ if (size > remaining) size = remaining; // clip the last memcpy for the unaligned case
+
+ if (g_nAppMode == MODE_STEPPING)
+ breakpointHit = DebuggerCheckMemBreakpoints(srcAddr, size, false);
+
+ 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;
+ }
+ else
+ {
+ pHDD->m_error = DEVICE_IO_ERROR;
+ }
+ }
+ break;
+ case BLK_Cmd_Format:
+ pHDD->m_error = DEVICE_IO_ERROR; // Not supported
+ break;
+ default:
+ _ASSERT(0);
+ pHDD->m_error = DEVICE_IO_ERROR;
+ break;
+ }
+
+ return CmdStatus(pHDD);
+}
+
+BYTE HarddiskInterfaceCard::CmdStatus(HardDiskDrive* pHDD)
+{
+ BYTE r = 0;
+
+ if (pHDD->m_error)
+ r = STATUS_ERROR; // Firmware requires that b0=1 for an error
+
+ if (g_nCumulativeCycles <= m_notBusyCycle)
+ r |= STATUS_BUSY; // Firmware requires that b7=1 for busy (eg. busy doing r/w DMA operation)
+ else
+ pHDD->m_status_next = DISK_STATUS_OFF; // TODO: FIXME: ??? YELLOW ??? WARNING
+
+ // Firmware requires that error code is [b6..1]
+ _ASSERT(pHDD->m_error <= ERRORCODE_MASK);
+ r |= (pHDD->m_error & ERRORCODE_MASK) << 1;
+
return r;
}
@@ -702,20 +841,40 @@ BYTE __stdcall HarddiskInterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, B
{
const UINT slot = ((addr & 0xff) >> 4) - 8;
HarddiskInterfaceCard* pCard = (HarddiskInterfaceCard*)MemGetSlotParameters(slot);
- HardDiskDrive* pHDD = &(pCard->m_hardDiskDrive[pCard->m_unitNum >> 7]); // bit7 = drive select
- BYTE r = DEVICE_OK;
+ HardDiskDrive* pHDD = NULL;
+ BYTE addrIdx = addr & 0xF;
+ if (addrIdx != 0x2 && addrIdx != 0x3 // GetUnit() depends on m_command & m_unitNum
+ && !((addrIdx == 0x9 || addrIdx == 0xA) && pCard->m_fifoIdx < 2))
+ {
+ pHDD = pCard->GetUnit();
+ if (pHDD == NULL)
+ {
+ // Ensure that fifoIdx returns to 0, even if unitNum is out of range
+ // EG. ProDOS 8 v2.5.0: Bitsy Bye: TAB through S7 volumes
+ const UINT fifoSize = (addrIdx == 0x9) ? 6 : 7;
+ pCard->m_fifoIdx = (pCard->m_fifoIdx + 1) % fifoSize;
+ return SET_STATUS_ERROR(DEVICE_NOT_CONNECTED);
+ }
+ }
- switch (addr & 0xF)
+ if (addrIdx == 0x9 || addrIdx == 0xA) // BLK or SP cmd FIFO
+ {
+ if (addrIdx == 0xA && pCard->m_fifoIdx == 0)
+ d |= SP_Cmd_base;
+
+ const UINT fifoSize = (addrIdx == 0x9) ? 6 : 7;
+ addrIdx = 0x2 + pCard->m_fifoIdx;
+ pCard->m_fifoIdx = (pCard->m_fifoIdx + 1) % fifoSize;
+ }
+
+ switch (addrIdx)
{
case 0x0: // r/o: status
case 0x1: // r/o: execute
- case 0x8: // r/o: legacy next-data port
- case 0x9: // r/o: low byte of image size
- case 0xa: // r/o: high byte of image size
- // Writing to these 5 read-only registers is a no-op.
+ // Writing to these read-only registers is a no-op.
// NB. Don't change m_status_next, as UpdateLightStatus() has a huge performance cost!
- // Firmware has a busy-wait loop doing "rol hd_status,x"
+ // Some HDC's firmware has a busy-wait loop doing "rol hd_status,x"
// - this RMW opcode does an IORead() then an IOWrite(), and the loop iterates ~100 times!
break;
case 0x2:
@@ -734,27 +893,203 @@ BYTE __stdcall HarddiskInterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, B
pHDD->m_memblock = (pHDD->m_memblock & 0x00FF) | (d << 8);
break;
case 0x6:
- pHDD->m_diskblock = (pHDD->m_diskblock & 0xFF00) | d;
+ if (pCard->m_command != SP_Cmd_status)
+ pHDD->m_diskblock = (pHDD->m_diskblock & 0xFFFF00) | d;
+ else
+ pCard->m_statusCode = d;
break;
case 0x7:
- pHDD->m_diskblock = (pHDD->m_diskblock & 0x00FF) | (d << 8);
+ pHDD->m_diskblock = (pHDD->m_diskblock & 0xFF00FF) | (d << 8);
+ break;
+ case 0x8:
+ if (pCard->m_command & SP_Cmd_base)
+ pHDD->m_diskblock = (pHDD->m_diskblock & 0x00FFFF) | (d << 16);
break;
default:
pHDD->m_status_next = DISK_STATUS_OFF;
- r = IO_Null(pc, addr, bWrite, d, nExecutedCycles);
}
- pCard->UpdateLightStatus(pHDD);
+ if (pHDD)
+ {
+ if ((pCard->m_command & SP_Cmd_base) == 0)
+ pHDD->m_diskblock &= 0x00FFFF; // BLK cmds are only 16-bit
+
+ pCard->UpdateLightStatus(pHDD);
+ }
+
+ return 0;
+}
+
+//===========================================================================
+
+BYTE HarddiskInterfaceCard::GetNumConnectedDevices(void)
+{
+ // Scan backwards to find the index of the last attached HDD
+ int numDevices = NUM_HARDDISKS - 1;
+
+ for (; numDevices >= 0; numDevices--)
+ {
+ if (m_hardDiskDrive[numDevices].m_imageloaded)
+ break;
+ }
+
+ return numDevices + 1;
+}
+
+BYTE HarddiskInterfaceCard::GetProDOSBlockDeviceUnit(void)
+{
+ const BYTE slotFromUnitNum = (m_unitNum >> 4) & 7;
+ const BYTE offset = (slotFromUnitNum == m_slot) ? 0 : 2;
+ return offset + (m_unitNum >> 7); // bit7 = drive select
+}
+
+HardDiskDrive* HarddiskInterfaceCard::GetUnit(void)
+{
+ const bool isSmartPortCmd = m_command & SP_Cmd_base;
+
+ if (!isSmartPortCmd)
+ return &m_hardDiskDrive[GetProDOSBlockDeviceUnit()];
+
+ if (m_unitNum > kMaxSmartPortUnits)
+ return NULL;
+
+ if (m_unitNum > GetNumConnectedDevices())
+ return NULL;
+
+ if (m_unitNum == 0)
+ return &m_smartPortController;
+
+ return &m_hardDiskDrive[m_unitNum - 1];
+}
+
+void HarddiskInterfaceCard::SetIdString(WORD addr, const char* str)
+{
+ BYTE& idStrLen = mem[addr]; // ID string length
+ idStrLen = 0;
+
+ WORD idStrAddr = addr + 1;
+ for (UINT i = 0; i < 16; i++)
+ mem[idStrAddr + i] = ' '; // ID string padded with ASCII spaces
+
+ if (str == NULL)
+ return;
+
+ while (*str && idStrLen < 16)
+ {
+ idStrLen++;
+ mem[idStrAddr++] = *str++;
+ }
+}
+
+BYTE HarddiskInterfaceCard::SmartPortCmdStatus(HardDiskDrive* pHDD)
+{
+ // Make Firmware version: eg. 1.30.18.0 => 130.18
+ UINT fwVerMajorCheck = g_AppleWinVersion[0] * 100 + g_AppleWinVersion[1];
+ _ASSERT(fwVerMajorCheck < 256);
+ if (fwVerMajorCheck >= 256) fwVerMajorCheck = 255;
+ UINT fwVerMinorCheck = g_AppleWinVersion[2];
+ _ASSERT(fwVerMinorCheck < 256);
+ if (fwVerMinorCheck >= 256) fwVerMinorCheck = 255;
+ const BYTE fwVerMajor = fwVerMajorCheck;
+ const BYTE fwVerMinor = fwVerMinorCheck;
+
+ //
+
+ WORD statusListAddr = pHDD->m_memblock;
+ BYTE r = DEVICE_OK;
+
+ if (m_unitNum == 0) // Unit-0: SmartPort Controller
+ {
+ BYTE numDevices = GetNumConnectedDevices();
+
+ switch (m_statusCode)
+ {
+ case SP_Cmd_status_STATUS:
+ case SP_Cmd_status_GETDIB:
+ {
+ // SmartPort driver status (8 bytes)
+ mem[statusListAddr++] = numDevices;
+ for (UINT i = 0; i < 7; i++)
+ mem[statusListAddr++] = 0; // reserved
+ if (m_statusCode == SP_Cmd_status_STATUS)
+ break;
+ // Device Information Block (DIB)
+ std::string idStr = "AppleWin SP";
+ SetIdString(statusListAddr, idStr.c_str());
+ statusListAddr += 17;
+ mem[statusListAddr++] = 0x00; // device type (0x00: Apple II memory expansion card)
+ mem[statusListAddr++] = 0x00; // device subtype (0x00: Apple II memory expansion card)
+ mem[statusListAddr++] = fwVerMajor; // f/w version (major)
+ mem[statusListAddr++] = fwVerMinor; // f/w version (minor)
+ break;
+ }
+ case SP_Cmd_status_GETDCB:
+ case SP_Cmd_status_GETNL:
+ default:
+ pHDD->m_error = 1;
+ r = BADCTL;
+ break;
+ }
+ }
+ else // Unit > 0: SmartPort Devices
+ {
+ switch (m_statusCode)
+ {
+ case SP_Cmd_status_STATUS:
+ case SP_Cmd_status_GETDIB:
+ {
+ // Device status (4 bytes)
+ const bool isImageLoaded = m_hardDiskDrive[m_unitNum - 1].m_imageloaded;
+
+ // 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-----
+ mem[statusListAddr++] = generalStatus;
+
+ const UINT imageSizeInBlocks = isImageLoaded ? GetImageSizeInBlocks(pHDD->m_imagehandle) : 0;
+ mem[statusListAddr++] = imageSizeInBlocks & 0xff; // num blocks (lo)
+ mem[statusListAddr++] = (imageSizeInBlocks >> 8) & 0xff; // num blocks (med)
+ mem[statusListAddr++] = (imageSizeInBlocks >> 16) & 0xff; // num blocks (hi)
+
+ if (m_statusCode == SP_Cmd_status_STATUS)
+ break;
+
+ // Device Information Block (DIB)
+ std::string idStr = "AppleWin SP D#"; // + "01".."99" (device number in decimal)
+ idStr += (char)('0' + m_unitNum / 10);
+ idStr += (char)('0' + m_unitNum % 10);
+ SetIdString(statusListAddr, idStr.c_str());
+ statusListAddr += 17;
+ mem[statusListAddr++] = 0x02; // device type (0x02: Hard disk)
+ mem[statusListAddr++] = 0x20; // device subtype (0x20: Hard disk)
+ mem[statusListAddr++] = fwVerMajor; // f/w version (major)
+ mem[statusListAddr++] = fwVerMinor; // f/w version (minor)
+ break;
+ }
+ case SP_Cmd_status_GETDCB:
+ case SP_Cmd_status_GETNL:
+ default:
+ pHDD->m_error = 1;
+ r = BADCTL;
+ break;
+ }
+ }
+
return r;
}
-UINT HarddiskInterfaceCard::GetImageSizeInBlocks(ImageInfo* const pImageInfo)
+//===========================================================================
+
+UINT HarddiskInterfaceCard::GetImageSizeInBlocks(ImageInfo* const pImageInfo, const bool is16bit/*=false*/)
{
if (m_userNumBlocks != 0)
return m_userNumBlocks;
UINT numberOfBlocks = (pImageInfo ? pImageInfo->uImageSize : 0) / HD_BLOCK_SIZE;
if (numberOfBlocks > kHarddiskMaxNumBlocks)
numberOfBlocks = kHarddiskMaxNumBlocks;
+ if (is16bit && numberOfBlocks > 0xffff)
+ numberOfBlocks = 0xffff;
return numberOfBlocks;
}
@@ -771,7 +1106,8 @@ void HarddiskInterfaceCard::UpdateLightStatus(HardDiskDrive* pHDD)
void HarddiskInterfaceCard::GetLightStatus(Disk_Status_e *pDisk1Status)
{
- HardDiskDrive* pHDD = &m_hardDiskDrive[m_unitNum >> 7]; // bit7 = drive select
+ const BYTE unit = (m_command & SP_Cmd_base) ? m_unitNum : GetProDOSBlockDeviceUnit();
+ HardDiskDrive* pHDD = &m_hardDiskDrive[unit];
*pDisk1Status = pHDD->m_status_prev;
}
@@ -796,7 +1132,9 @@ bool HarddiskInterfaceCard::ImageSwap(void)
// 3: Updated $Csnn firmware to fix GH#996 (now slot-independent code)
// Added: Not Busy Cycle
// 4: Updated $Csnn firmware to fix GH#1264
-static const UINT kUNIT_VERSION = 4;
+// 5: Added: SP Status Code, FIFO Index & 256-byte firmware
+// Units are 1-based (up to v4 they were 0-based)
+static const UINT kUNIT_VERSION = 5;
#define SS_YAML_VALUE_CARD_HDD "Generic HDD"
@@ -814,6 +1152,9 @@ static const UINT kUNIT_VERSION = 4;
#define SS_YAML_KEY_BUF_PTR "Buffer Offset"
#define SS_YAML_KEY_BUF "Buffer"
#define SS_YAML_KEY_NOT_BUSY_CYCLE "Not Busy Cycle"
+#define SS_YAML_KEY_SP_STATUS_CODE "SP Status Code"
+#define SS_YAML_KEY_FIFO_INDEX "FIFO Index"
+#define SS_YAML_KEY_FIRMWARE "Firmware"
const std::string& HarddiskInterfaceCard::GetSnapshotCardName(void)
{
@@ -821,9 +1162,11 @@ const std::string& HarddiskInterfaceCard::GetSnapshotCardName(void)
return name;
}
-void HarddiskInterfaceCard::SaveSnapshotHDDUnit(YamlSaveHelper& yamlSaveHelper, UINT unit)
+void HarddiskInterfaceCard::SaveSnapshotHDDUnit(YamlSaveHelper& yamlSaveHelper, const UINT unit)
{
- YamlSaveHelper::Label label(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_HDDUNIT, unit);
+ const UINT baseUnitNum = 1; // Unit0 is the SP Controller, so SP units start from 1
+
+ YamlSaveHelper::Label label(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_HDDUNIT, baseUnitNum + unit);
yamlSaveHelper.SaveString(SS_YAML_KEY_FILENAME, m_hardDiskDrive[unit].m_fullname);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_ERROR, m_hardDiskDrive[unit].m_error);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_MEMBLOCK, m_hardDiskDrive[unit].m_memblock);
@@ -845,19 +1188,32 @@ void HarddiskInterfaceCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
- yamlSaveHelper.Save("%s: %d # b7=unit\n", SS_YAML_KEY_CURRENT_UNIT, m_unitNum);
+ yamlSaveHelper.Save("%s: %d # b7=unit for ProDOS BLK device\n", SS_YAML_KEY_CURRENT_UNIT, m_unitNum);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_COMMAND, m_command);
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_NOT_BUSY_CYCLE, m_notBusyCycle);
+ yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SP_STATUS_CODE, m_statusCode);
+ yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_FIFO_INDEX, m_fifoIdx);
- SaveSnapshotHDDUnit(yamlSaveHelper, HARDDISK_1);
- SaveSnapshotHDDUnit(yamlSaveHelper, HARDDISK_2);
+ // New label
+ {
+ YamlSaveHelper::Label buffer(yamlSaveHelper, "%s:\n", SS_YAML_KEY_FIRMWARE);
+ yamlSaveHelper.SaveMemory(mem + APPLE_IO_BEGIN + m_slot * APPLE_SLOT_SIZE, APPLE_SLOT_SIZE);
+ }
+
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ {
+ if (m_hardDiskDrive[i].m_imageloaded)
+ SaveSnapshotHDDUnit(yamlSaveHelper, i);
+ }
}
-bool HarddiskInterfaceCard::LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper, UINT unit)
+bool HarddiskInterfaceCard::LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper, const UINT unit, const UINT version)
{
- std::string hddUnitName = std::string(SS_YAML_KEY_HDDUNIT) + (unit == HARDDISK_1 ? std::string("0") : std::string("1"));
+ const UINT baseUnitNum = (version >= 5) ? 1 : 0;
+
+ std::string hddUnitName = std::string(SS_YAML_KEY_HDDUNIT) + (char)('0' + baseUnitNum + unit);
if (!yamlLoadHelper.GetSubMap(hddUnitName))
- throw std::runtime_error("Card: Expected key: " + hddUnitName);
+ return false; // No HDD plugged in for this unit#
m_hardDiskDrive[unit].m_fullname.clear();
m_hardDiskDrive[unit].m_imagename.clear();
@@ -879,13 +1235,13 @@ bool HarddiskInterfaceCard::LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper,
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_BUF))
throw std::runtime_error(hddUnitName + ": Missing: " + SS_YAML_KEY_BUF);
yamlLoadHelper.LoadMemory(m_hardDiskDrive[unit].m_buf, HD_BLOCK_SIZE);
-
yamlLoadHelper.PopMap();
+
yamlLoadHelper.PopMap();
//
- bool bResSelectImage = false;
+ bool userSelectedImageFolder = false;
if (!filename.empty())
{
@@ -893,7 +1249,7 @@ bool HarddiskInterfaceCard::LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper,
if (dwAttributes == INVALID_FILE_ATTRIBUTES)
{
// Get user to browse for file
- bResSelectImage = SelectImage(unit, filename.c_str());
+ userSelectedImageFolder = SelectImage(unit, filename.c_str());
dwAttributes = GetFileAttributes(filename.c_str());
}
@@ -904,7 +1260,7 @@ bool HarddiskInterfaceCard::LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper,
if (!Insert(unit, filename.c_str()))
bImageError = true;
- // HD_Insert() sets up:
+ // Insert() sets up:
// . m_imagename
// . m_fullname
// . m_imageloaded
@@ -916,7 +1272,7 @@ bool HarddiskInterfaceCard::LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper,
}
}
- return bResSelectImage;
+ return userSelectedImageFolder;
}
bool HarddiskInterfaceCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
@@ -925,25 +1281,46 @@ bool HarddiskInterfaceCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT ve
ThrowErrorInvalidVersion(version);
if (version <= 2 && (regs.pc >> 8) == (0xC0|m_slot))
- throw std::runtime_error("HDD card: 6502 is running old HDD firmware");
+ throw std::runtime_error("HDC card: 6502 is running old HDD firmware");
- m_unitNum = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_UNIT); // b7=unit
+ m_unitNum = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_UNIT);
m_command = yamlLoadHelper.LoadUint(SS_YAML_KEY_COMMAND);
if (version >= 3)
m_notBusyCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_NOT_BUSY_CYCLE);
- // Unplug all HDDs first in case HDD-2 is to be plugged in as HDD-1
- for (UINT i=0; i= 5)
+ {
+ m_statusCode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SP_STATUS_CODE);
+ m_fifoIdx = yamlLoadHelper.LoadUint(SS_YAML_KEY_FIFO_INDEX);
+
+ //
+
+ if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_FIRMWARE))
+ throw std::runtime_error(std::string("HDC") + ": Missing: " + SS_YAML_KEY_FIRMWARE);
+ yamlLoadHelper.LoadMemory(m_saveStateFirmware, APPLE_SLOT_SIZE);
+ yamlLoadHelper.PopMap();
+ m_saveStateFirmwareValid = true;
+ }
+
+ // Unplug all HDDs first in case eg. HDD-2 is to be plugged in as HDD-1
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
{
Unplug(i);
m_hardDiskDrive[i].clear();
}
- bool bResSelectImage1 = LoadSnapshotHDDUnit(yamlLoadHelper, HARDDISK_1);
- bool bResSelectImage2 = LoadSnapshotHDDUnit(yamlLoadHelper, HARDDISK_2);
+ bool userSelectedImageFolder = false;
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ userSelectedImageFolder |= LoadSnapshotHDDUnit(yamlLoadHelper, i, version);
- if (!bResSelectImage1 && !bResSelectImage2)
+ if (!userSelectedImageFolder)
RegSaveString(TEXT(REG_PREFS), TEXT(REGVALUE_PREF_HDV_START_DIR), 1, Snapshot_GetPath());
GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_DISK_STATUS);
diff --git a/source/Harddisk.h b/source/Harddisk.h
index 4733c5c0..4c6cb300 100644
--- a/source/Harddisk.h
+++ b/source/Harddisk.h
@@ -26,15 +26,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "Card.h"
#include "DiskImage.h"
#include "DiskImageHelper.h"
+#include "MemoryDefs.h" // APPLE_SLOT_SIZE
enum HardDrive_e
{
HARDDISK_1 = 0,
HARDDISK_2,
- NUM_HARDDISKS
+ NUM_BLK_HARDDISKS = 2,
+ NUM_HARDDISKS = 8
};
-const UINT kHarddiskMaxNumBlocks = 0xffff; // Maximum number of blocks we can report.
+enum HdcMode
+{
+ HdcDefault, HdcSmartPort, HdcBlockMode2Devices, HdcBlockMode4Devices
+};
+
+// For SP read/write block cmds, a 24-bit blockNum => 8GiB capacity
+// . but eg. in CImageBase::ReadBlock() only 32-bit byte positions are supported (ie. 4GiB capacity)
+const UINT kHarddiskMaxNumBlocks = 0x007FFFFF; // Maximum number of blocks we can report.
+const UINT kMaxSmartPortUnits = NUM_HARDDISKS;
class HardDiskDrive
{
@@ -98,10 +108,11 @@ public:
bool Select(const int iDrive);
bool Insert(const int iDrive, const std::string& pathname);
void Unplug(const int iDrive);
- bool IsDriveUnplugged(const int iDrive);
void LoadLastDiskImage(const int iDrive);
void SetUserNumBlocks(UINT numBlocks) { m_userNumBlocks = numBlocks; }
void UseHdcFirmwareV1(void) { m_useHdcFirmwareV1 = true; }
+ void UseHdcFirmwareV2(void) { m_useHdcFirmwareV2 = true; }
+ void SetHdcFirmwareMode(HdcMode hdcMode) { m_useHdcFirmwareMode = hdcMode; }
void GetLightStatus(Disk_Status_e* pDisk1Status);
bool ImageSwap(void);
@@ -121,19 +132,36 @@ private:
const std::string& DiskGetBaseName(const int iDrive);
bool SelectImage(const int drive, LPCSTR pszFilename);
void UpdateLightStatus(HardDiskDrive* pHDD);
- UINT GetImageSizeInBlocks(ImageInfo* const pImageInfo);
- void SaveSnapshotHDDUnit(YamlSaveHelper& yamlSaveHelper, UINT unit);
- bool LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper, UINT unit);
+ BYTE GetNumConnectedDevices(void);
+ BYTE GetProDOSBlockDeviceUnit(void);
+ HardDiskDrive* GetUnit(void);
+ BYTE CmdExecute(HardDiskDrive* pHDD);
+ BYTE CmdStatus(HardDiskDrive* pHDD);
+ void SetIdString(WORD addr, const char* str);
+ BYTE SmartPortCmdStatus(HardDiskDrive* pHDD);
+ UINT GetImageSizeInBlocks(ImageInfo* const pImageInfo, const bool is16bit = false);
+ void SaveSnapshotHDDUnit(YamlSaveHelper& yamlSaveHelper, const UINT unit);
+ bool LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper, const UINT unit, const UINT version);
//
BYTE m_unitNum; // b7=unit
BYTE m_command;
+ BYTE m_fifoIdx;
+ BYTE m_statusCode;
UINT64 m_notBusyCycle;
UINT m_userNumBlocks;
bool m_useHdcFirmwareV1;
+ bool m_useHdcFirmwareV2;
+ HdcMode m_useHdcFirmwareMode;
bool m_saveDiskImage; // Save the DiskImage name to Registry
HardDiskDrive m_hardDiskDrive[NUM_HARDDISKS];
+ HardDiskDrive m_smartPortController; // unit-0 is the SmartPort controller
+
+ bool m_saveStateFirmwareV1;
+ bool m_saveStateFirmwareV2;
+ BYTE m_saveStateFirmware[APPLE_SLOT_SIZE];
+ bool m_saveStateFirmwareValid;
};
diff --git a/source/Memory.h b/source/Memory.h
index 8ed81b6d..a3616148 100644
--- a/source/Memory.h
+++ b/source/Memory.h
@@ -2,6 +2,7 @@
#include "Common.h"
#include "Card.h"
+#include "MemoryDefs.h"
// Memory Flag
#define MF_80STORE 0x00000001
@@ -22,23 +23,6 @@
#define MF_LANGCARD_MASK (MF_WRITERAM|MF_HIGHRAM|MF_BANK2)
-enum
-{
- // Note: All are in bytes!
- TEXT_PAGE1_BEGIN = 0x0400,
-
- APPLE_SLOT_SIZE = 0x0100, // 1 page = $Cx00 .. $CxFF (slot 1 .. 7)
- APPLE_IO_BEGIN = 0xC000,
- APPLE_SLOT_BEGIN = 0xC100, // each slot has 1 page reserved for it
- APPLE_SLOT_END = 0xC7FF, //
-
- FIRMWARE_EXPANSION_SIZE = 0x0800, // 8 pages = $C800 .. $CFFF
- FIRMWARE_EXPANSION_BEGIN = 0xC800, // [C800,CFFF)
- FIRMWARE_EXPANSION_END = 0xCFFF, //
-
- MEMORY_LENGTH = 0x10000
-};
-
enum MemoryInitPattern_e
{
MIP_ZERO
diff --git a/source/MemoryDefs.h b/source/MemoryDefs.h
new file mode 100644
index 00000000..10963b9f
--- /dev/null
+++ b/source/MemoryDefs.h
@@ -0,0 +1,18 @@
+#pragma once
+
+enum
+{
+ // Note: All are in bytes!
+ TEXT_PAGE1_BEGIN = 0x0400,
+
+ APPLE_SLOT_SIZE = 0x0100, // 1 page = $Cx00 .. $CxFF (slot 1 .. 7)
+ APPLE_IO_BEGIN = 0xC000,
+ APPLE_SLOT_BEGIN = 0xC100, // each slot has 1 page reserved for it
+ APPLE_SLOT_END = 0xC7FF, //
+
+ FIRMWARE_EXPANSION_SIZE = 0x0800, // 8 pages = $C800 .. $CFFF
+ FIRMWARE_EXPANSION_BEGIN = 0xC800, // [C800,CFFF)
+ FIRMWARE_EXPANSION_END = 0xCFFF, //
+
+ MEMORY_LENGTH = 0x10000
+};
diff --git a/source/ParallelPrinter.h b/source/ParallelPrinter.h
index 8f5eafce..6fe3be2b 100644
--- a/source/ParallelPrinter.h
+++ b/source/ParallelPrinter.h
@@ -1,7 +1,6 @@
#pragma once
#include "Card.h"
-#include "Memory.h"
class ParallelPrinterCard : public Card
{
diff --git a/source/Utilities.cpp b/source/Utilities.cpp
index b1df421c..04904caa 100644
--- a/source/Utilities.cpp
+++ b/source/Utilities.cpp
@@ -283,10 +283,13 @@ void LoadConfiguration(bool loadImages)
GetCurrentDirectory(sizeof(szFilename), szFilename);
SetCurrentImageDir(szFilename);
- if (loadImages && GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD)
+ for (UINT slot = SLOT1; slot <= SLOT7; slot++)
{
- dynamic_cast(GetCardMgr().GetRef(SLOT7)).LoadLastDiskImage(HARDDISK_1);
- dynamic_cast(GetCardMgr().GetRef(SLOT7)).LoadLastDiskImage(HARDDISK_2);
+ if (loadImages && GetCardMgr().QuerySlot(slot) == CT_GenericHDD)
+ {
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ dynamic_cast(GetCardMgr().GetRef(slot)).LoadLastDiskImage(i);
+ }
}
//
@@ -429,29 +432,33 @@ void InsertHardDisks(const UINT slot, LPCSTR szImageName_harddisk[NUM_HARDDISKS]
{
_ASSERT(slot == 5 || slot == 7);
- if (!szImageName_harddisk[HARDDISK_1] && !szImageName_harddisk[HARDDISK_2])
+ // If no HDDs then just return (and don't insert an HDC into this slot)
+ bool res = true;
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ res &= szImageName_harddisk[i] == NULL;
+ if (res)
return;
if (GetCardMgr().QuerySlot(slot) != CT_GenericHDD)
GetCardMgr().Insert(slot, CT_GenericHDD); // Enable the Harddisk controller card
- bool bRes = true;
-
- if (szImageName_harddisk[HARDDISK_1])
+ res = true;
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
{
- bRes = DoHardDiskInsert(slot, HARDDISK_1, szImageName_harddisk[HARDDISK_1]);
- LogFileOutput("Init: DoHardDiskInsert(HDD1), res=%d\n", bRes);
- GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_DISK_STATUS); // harddisk activity LED
- bBoot = true;
+ if (szImageName_harddisk[i])
+ {
+ res &= DoHardDiskInsert(slot, i, szImageName_harddisk[i]);
+ LogFileOutput("Init: DoHardDiskInsert(HDD-%d), res=%d\n", i, res);
+
+ if (i == HARDDISK_1)
+ {
+ GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_DISK_STATUS); // harddisk activity LED
+ bBoot = true;
+ }
+ }
}
- if (szImageName_harddisk[HARDDISK_2])
- {
- bRes &= DoHardDiskInsert(slot, HARDDISK_2, szImageName_harddisk[HARDDISK_2]);
- LogFileOutput("Init: DoHardDiskInsert(HDD2), res=%d\n", bRes);
- }
-
- if (!bRes)
+ if (!res)
GetFrame().FrameMessageBox("Failed to insert harddisk(s) - see log file", "Warning", MB_ICONASTERISK | MB_OK);
}
diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp
index ddbd22a3..224bbbe4 100644
--- a/source/Windows/AppleWin.cpp
+++ b/source/Windows/AppleWin.cpp
@@ -784,19 +784,19 @@ static void RepeatInitialization(void)
if (g_cmdLine.slotInsert[SLOT5] != CT_Empty)
{
- if (GetCardMgr().QuerySlot(SLOT5) != CT_Disk2) // Ignore if already got Disk2 in slot 5
+ if (GetCardMgr().QuerySlot(SLOT5) != g_cmdLine.slotInsert[SLOT5]) // Ignore if already got this card type in slot 5
GetCardMgr().Insert(SLOT5, g_cmdLine.slotInsert[SLOT5]);
}
if (g_cmdLine.slotInsert[SLOT6] == CT_Disk2) // For now just support Disk2 in slot 6
{
- if (GetCardMgr().QuerySlot(SLOT6) != CT_Disk2) // Ignore if already got Disk2 in slot 6
+ if (GetCardMgr().QuerySlot(SLOT6) != g_cmdLine.slotInsert[SLOT6]) // Ignore if already got this card type in slot 6
GetCardMgr().Insert(SLOT6, g_cmdLine.slotInsert[SLOT6]);
}
if (g_cmdLine.slotInsert[SLOT7] != CT_Empty)
{
- if (GetCardMgr().QuerySlot(SLOT7) != CT_GenericHDD) // Ignore if already got HDC in slot 7
+ if (GetCardMgr().QuerySlot(SLOT7) != g_cmdLine.slotInsert[SLOT7]) // Ignore if already got this card type in slot 7
GetCardMgr().Insert(SLOT7, g_cmdLine.slotInsert[SLOT7]);
}
@@ -809,6 +809,9 @@ static void RepeatInitialization(void)
dynamic_cast(GetCardMgr().GetRef(i)).SetUserNumBlocks(g_cmdLine.uHarddiskNumBlocks);
if (g_cmdLine.useHdcFirmwareV1)
dynamic_cast(GetCardMgr().GetRef(i)).UseHdcFirmwareV1();
+ if (g_cmdLine.useHdcFirmwareV2)
+ dynamic_cast(GetCardMgr().GetRef(i)).UseHdcFirmwareV2();
+ dynamic_cast(GetCardMgr().GetRef(i)).SetHdcFirmwareMode(g_cmdLine.slotInfo[i].useHdcFirmwareMode);
}
}
@@ -855,10 +858,12 @@ static void RepeatInitialization(void)
g_cmdLine.szImageName_drive[SLOT6][DRIVE_1] = g_cmdLine.szImageName_drive[SLOT6][DRIVE_2] = NULL; // Don't insert on a restart
InsertHardDisks(SLOT5, g_cmdLine.szImageName_harddisk[SLOT5], temp);
- g_cmdLine.szImageName_harddisk[SLOT5][HARDDISK_1] = g_cmdLine.szImageName_harddisk[SLOT5][HARDDISK_2] = NULL; // Don't insert on a restart
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ g_cmdLine.szImageName_harddisk[SLOT5][i] = NULL; // Don't insert on a restart
InsertHardDisks(SLOT7, g_cmdLine.szImageName_harddisk[SLOT7], g_cmdLine.bBoot);
- g_cmdLine.szImageName_harddisk[SLOT7][HARDDISK_1] = g_cmdLine.szImageName_harddisk[SLOT7][HARDDISK_2] = NULL; // Don't insert on a restart
+ for (UINT i = 0; i < NUM_HARDDISKS; i++)
+ g_cmdLine.szImageName_harddisk[SLOT7][i] = NULL; // Don't insert on a restart
if (g_cmdLine.bSlotEmpty[SLOT7])
{