Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrea Odetti 2021-11-13 09:29:41 +00:00
commit 8db8423634
21 changed files with 391 additions and 284 deletions

View file

@ -657,6 +657,10 @@
RelativePath=".\source\AY8910.h"
>
</File>
<File
RelativePath=".\source\Card.cpp"
>
</File>
<File
RelativePath=".\source\Card.h"
>

View file

@ -144,6 +144,7 @@
<ItemGroup>
<ClCompile Include="source\6821.cpp" />
<ClCompile Include="source\AY8910.cpp" />
<ClCompile Include="source\Card.cpp" />
<ClCompile Include="source\CardManager.cpp" />
<ClCompile Include="source\CmdLine.cpp" />
<ClCompile Include="source\Configuration\About.cpp" />

View file

@ -238,6 +238,9 @@
<ClCompile Include="source\SNESMAX.cpp">
<Filter>Source Files\Emulator</Filter>
</ClCompile>
<ClCompile Include="source\Card.cpp">
<Filter>Source Files\Emulator</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\CommonVICE\6510core.h">

Binary file not shown.

View file

@ -26,7 +26,7 @@
;
; Modified by Tom Charlesworth:
; . Updated so it can be assembled by ACME 0.96.4
; . Updated so it can be assembled by ACME 0.97
; . Fixed so that ProDOS entrypoint is $c70a (26 Dev 2007) (Bug #12723)
; . Modified to support Apple Oasis' entrypoint: $c761 (8 Sept 2012) (Feature #5557)
; . Added support for SmartPort entrypoint (20 Oct 2012)
@ -34,10 +34,11 @@
; . GH#370 (Robert Hoem, 27 Oct 2016):
; . Added a check against open-apple during boot to route boot to slot 6
; . This happens after the first two blocks are loaded from the HD.
; . GH#319: smartport return address wrong when crossing page
; . GH#319: SmartPort return address wrong when crossing page
; . GH#996: Make code slot-independent (so HDD controller card can go into any slot)
; . Moved the 512-byte block read into AppleWin's HDD emulation (to mirror the block write command)
; TODO:
; . Make code relocatable (so HDD controller card can go into any slot)
; . Remove support for Entrypoint_C746 (old AppleWin) & Entrypoint_C761 (Apple Oasis)
; . Remove support for Entrypoint_Cs46 (old AppleWin) & Entrypoint_Cs61 (Apple Oasis)
; - provide a utility to convert these to use Entrypoint_ProDOS
; . Check SmartPort: Is it OK to trash Y and $42,..,$47 ?
;
@ -47,31 +48,38 @@
!sl "hddrvr.labels"
; constants
hd_execute = $c0f0
hd_error = $c0f1
hd_command = $c0f2
hd_unitnum = $c0f3
hd_memblock = $c0f4
hd_diskblock = $c0f6
hd_nextbyte = $c0f8
hd_execute = $c080
hd_status = $c081 ; b7=busy, b0=error
hd_command = $c082
hd_unitnum = $c083
hd_memblock = $c084
hd_diskblock = $c086
;hd_nextbyte = $c088 ; legacy read-only port (still supported by AppleWin)
; Notes on accesses to I/O registers:
; . ROR ABS16,X and ROL ABS16,X - only used for $C081+s*$10 STATUS register:
; 6502: double read (old data), write (old data), write (new data). The writes are harmless as writes to STATUS are ignored.
; 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.
command = $42
unitnum = $43
memblock = $44
diskblock = $46
slot6 = $c600
slot6 = $C600
OS = $0801
BUTTON0 = $C061
; The Autoboot rom will call this.
; This is also the entry point for such things as IN#7 and PR#7
;; code
*= $c700 ; org $c700
;======================================
!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)
; The Autoboot rom will call this.
; This is also the entry point for such things as IN#7 and PR#7
start
; Autoboot and ProDOS look at the following few opcodes to detect block devices
@ -83,44 +91,23 @@ start
lda #$3C
bne Bootstrap
Entrypoint_ProDOS ; $c70a - ProDOS entrypoint
Entrypoint_ProDOS ; $Cn0A - ProDOS entrypoint
sec
bcs Entrypoint
Entrypoint_SmartPort ; $c70d - SmartPort entrypoint
Entrypoint_SmartPort ; $Cn0D - SmartPort entrypoint
clc
Entrypoint ; $c70e - entrypoint?
bcs cmdproc
bcc SmartPort
;;
Entrypoint ; $Cn0E - entrypoint?
bcs GetSlotInX ; C=1: GetSlotInX -> cmdproc
Bootstrap
; Lets check to see if there's an image ready
lda #$00
sta hd_command
; Slot 7, disk 1
lda #$70 ; Slot# << 4
sta hd_unitnum
lda hd_execute
; error capturing code. Applewin is picky
; about code assigning data to registers and
; memory. The safest method is via I/O port
ror hd_error ; Post: C=0 or 1
bcc hdboot
; no image ready, boot diskette image instead
BootSlot6
jmp slot6
; C=0: fall through to SmartPort...
;======================================
; TODO: Is it OK to trash Y and $42,..,$47 ?
; Pre: C=0
SmartPort
; Pre: C=0, X = Slot# << 4
SmartPort ; SmartPort -> GetSlotInX -> cmdproc
pla
sta $46
adc #3 ; Pre: C=0, Post: C=0 or 1
@ -136,24 +123,7 @@ SmartPort
lda ($46),y ; cmd
sta $42
iny
bne SmartPort2
;======================================
; 8 unused bytes
*= $c746 ; org $c746
Entrypoint_C746 ; Old f/w 'cmdproc' entrypoint
; Keep this for any DOSMaster HDD images created with old AppleWin HDD f/w.
; DOSMaster hardcodes the entrypoint addr into its bootstrapping code:
; - So DOSMaster images are tied to the HDD's controller's f/w
sec
bcs Entrypoint
;======================================
; Pre: Y=2
SmartPort2
lda ($46),y ; param_l
sta $45
iny
@ -164,31 +134,76 @@ SmartPort2
lda ($45),y ; unit
sta $43
iny
lda ($45),y ; memblock_l
sta $44
iny
bne SmartPort3
lda ($45),y ; memblock_h
pha
iny
lda ($45),y ; diskblock_l
pha
iny
bne SmartPort2
;======================================
; 1 unused byte
*= $c761 ; org $c761
; 2 unused bytes
Entrypoint_C761 ; Apple Oasis HDD controller entrypoint
@checkCs46
*= $0046 ; org $0046
!warn "Cs46 padding = ", * - @checkCs46
Entrypoint_Cs46 ; Old f/w 'cmdproc' entrypoint
; Keep this for any DOSMaster HDD images created with old AppleWin HDD f/w.
; DOSMaster hardcodes the entrypoint addr into its bootstrapping code:
; - So DOSMaster images are tied to the HDD's controller's f/w
sec
bcs Entrypoint ; or directly to GetSlotInX
;======================================
Bootstrap
; Lets check to see if there's an image ready
; Slot n, disk 1
clc
bcc GetSlotInX ; Post: X = Slot# << 4
Bootstrap2
lda #$00
sta hd_unitnum,x ; b7=0 => disk 1
sta hd_command,x
lda hd_execute,x
ror hd_status,x ; Post: C=0 or 1
bcc hdboot
; no image ready, boot diskette image instead
BootSlot6
jmp slot6
;======================================
; 2 unused bytes
@checkCs61
*= $0061 ; org $0061
!warn "Cs61 padding = ", * - @checkCs61
Entrypoint_Cs61 ; Apple Oasis HDD controller entrypoint
; Keep this for any DOSMaster HDD images created with Apple Oasis HDD f/w.
; DOSMaster hardcodes the entrypoint addr into its bootstrapping code:
; - So DOSMaster images are tied to the HDD's controller's f/w
sec
bcs Entrypoint
bcs Entrypoint ; or directly to GetSlotInX
;======================================
; image ready. Lets boot from it.
; we want to load block 1 from s7,d1 to $800 then jump there
; we want to load block 1 from disk 1 to $800 then jump there
; Pre: X = Slot# << 4
; C = 0
hdboot
lda #$70 ; Slot# << 4
sta unitnum
lda #$0
sta unitnum ; b7=0 => disk 1
sta memblock
sta diskblock
sta diskblock+1
@ -196,101 +211,109 @@ hdboot
sta memblock+1
lda #$1
sta command
jsr cmdproc
bne cmdproc
hdboot2
bcs BootSlot6
goload
bit BUTTON0 ; button 0 pressed?
bit BUTTON0 ; button 0 pressed?
bmi BootSlot6
; X=device
ldx #$70 ; Slot# << 4
; Pre: X = Slot# << 4
jmp OS
; entry point for ProDOS' block driver
; simple really. Copy the command from $42..$47
; to our I/O ports then execute command
cmdproc
clc
lda $42
sta hd_command
lda $43
sta hd_unitnum
lda $44
sta hd_memblock
lda $45
sta hd_memblock+1
lda $46
sta hd_diskblock
lda $47
sta hd_diskblock+1
lda hd_execute
; check for error
pha
lda command
cmp #1
bne skipSread
jsr sread
skipSread
ror hd_error ; Post: C=0 or 1
pla
rts
; if there's no error, then lets read the block into memory
; because Applewin is picky about memory management, here's what I did:
; on read, hd_nextbyte = buffer[0], therefore we'll read that byte 256 times (in which
; the emulated code increments the buffer by 1 on each read) to (memblock),y
; increment memblock+1 and read the second 256 bytes via hd_nextbyte.
;
; if I could figure out how to consistently get applewin to update it's memory regions all
; this code can be moved into the emulation code (although, this is how I'd build the hardware
; anyway...)
sread
tya
pha
ldy #0
loop1
lda hd_nextbyte
sta (memblock),y
iny
bne loop1
inc memblock+1
ldy #0
loop2
lda hd_nextbyte
sta (memblock),y
iny
bne loop2
dec memblock+1 ; restore memblock+1 ($45) to original value (for Epyx's California Games)
pla
tay
rts
;======================================
SmartPort3
lda ($45),y ; memblock_h
pha
iny
lda ($45),y ; diskblock_l
pha
iny
SmartPort2
lda ($45),y ; diskblock_h
sta $47
pla
sta $46
pla
sta $45
iny
bne cmdproc
sec
; fall through...
;======================================
; 18 unused bytes
; Pre:
; C=0 => via Bootstrap
; C=1 => via Entrypoint / SmartPort2
; Post:
; X = Slot# << 4
GetSlotInX
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
tax ; X=$n0
plp ; + restore int status
bcc Bootstrap2
; otherwise fall through for Entrypoint / SmartPort...
;--------------------------------------
; entry point for ProDOS' block driver
; simple really. Copy the command from $42..$47
; to our I/O ports then execute command
; Pre:
; C=0 => via Bootstrap (hdboot)
; C=1 => via GetSlotInX (eg. Entrypoint / SmartPort2)
; X = Slot# << 4
; Post:
; C = hd_status.b0
; A = result of hd_execute
; X = Slot# << 4
cmdproc
php
lda command
sta hd_command,x
lda unitnum
sta hd_unitnum,x
lda memblock
sta hd_memblock,x
lda memblock+1
sta hd_memblock+1,x
lda diskblock
sta hd_diskblock,x
lda diskblock+1
sta hd_diskblock+1,x
lda hd_execute,x ; A = result of hd_execute (NB. instantaneous 512 byte r/w!)
- rol hd_status,x ; b7=busy doing DMA?
bcs -
plp ; restore C from start of cmdproc
bcs done
ror hd_status,x ; Post: C=0 or 1
lda #0
beq hdboot2
done
ror hd_status,x ; Post: C=0 or 1
rts
;======================================
; 33 unused bytes
!zone data
@ -309,7 +332,10 @@ SmartPort3
; datablock. This starts near the end of the firmware (at offset $FC)
;; data
*= $c7fc ; org $c7fc
@checkCsFC
*= $00FC ; org $00FC
!warn "CsFC padding = ", * - @checkCsFC
!word $7fff ; how many blocks are on the device.
!byte $D7 ; specifics about the device (number of drives, read/write/format capability, etc)
!byte <Entrypoint_ProDOS ; entry point offset for ProDOS (must be $0a)

Binary file not shown.

54
source/Card.cpp Normal file
View file

@ -0,0 +1,54 @@
/*
AppleWin : An Apple //e emulator for Windows
Copyright (C) 2021, 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
*/
#include "StdAfx.h"
#include "Card.h"
#include "Tfe/tfe.h"
#include "Mockingboard.h"
#include "ParallelPrinter.h"
#include "z80emu.h"
void DummyCard::InitializeIO(LPBYTE pCxRomPeripheral)
{
switch (QueryType())
{
case CT_GenericPrinter:
PrintLoadRom(pCxRomPeripheral, m_slot);
break;
case CT_Uthernet:
tfe_InitializeIO(pCxRomPeripheral, m_slot);
break;
case CT_GenericClock:
break; // nothing to do
case CT_MockingboardC:
case CT_Phasor:
// only in slot 4
if (m_slot == SLOT4)
{
MB_InitializeIO(pCxRomPeripheral, SLOT4, SLOT5);
}
break;
case CT_Z80:
Z80_InitializeIO(pCxRomPeripheral, m_slot);
break;
}
}

View file

@ -31,10 +31,10 @@ enum SLOTS { SLOT0=0, SLOT1, SLOT2, SLOT3, SLOT4, SLOT5, SLOT6, SLOT7, NUM_SLOTS
class Card
{
public:
Card(void) : m_type(CT_Empty), m_slot(SLOT0) {}
Card(SS_CARDTYPE type, UINT slot) : m_type(type), m_slot(slot) {}
virtual ~Card(void) {}
virtual void InitializeIO(LPBYTE pCxRomPeripheral) = 0;
virtual void Init(void) = 0;
virtual void Reset(const bool powerCycle) = 0;
SS_CARDTYPE QueryType(void) { return m_type; }
@ -51,11 +51,12 @@ private:
class EmptyCard : public Card
{
public:
EmptyCard(void) {}
EmptyCard(UINT slot) : Card(CT_Empty, slot) {}
virtual ~EmptyCard(void) {}
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
virtual void InitializeIO(LPBYTE pCxRomPeripheral) {}
virtual void Init(void) {}
virtual void Reset(const bool powerCycle) {}
};
//
@ -66,6 +67,7 @@ public:
DummyCard(SS_CARDTYPE type, UINT slot) : Card(type, slot) {}
virtual ~DummyCard(void) {}
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void Init(void) {}
virtual void Reset(const bool powerCycle) {}
};

View file

@ -48,7 +48,7 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
switch (type)
{
case CT_Empty:
m_slot[slot] = new EmptyCard;
m_slot[slot] = new EmptyCard(slot);
break;
case CT_Disk2:
m_slot[slot] = new Disk2InterfaceCard(slot);
@ -153,7 +153,7 @@ void CardManager::InsertAuxInternal(SS_CARDTYPE type)
switch (type)
{
case CT_Empty:
m_aux = new EmptyCard;
m_aux = new EmptyCard(SLOT_AUX);
break;
case CT_80Col:
m_aux = new DummyCard(type, SLOT_AUX);
@ -191,3 +191,14 @@ void CardManager::RemoveAux(void)
{
InsertAux(CT_Empty);
}
void CardManager::InitializeIO(LPBYTE pCxRomPeripheral)
{
for (UINT i = 0; i < NUM_SLOTS; ++i)
{
if (m_slot[i])
{
m_slot[i]->InitializeIO(pCxRomPeripheral);
}
}
}

View file

@ -57,6 +57,8 @@ public:
class CSuperSerialCard* GetSSC(void) { return m_pSSC; }
bool IsSSCInstalled(void) { return m_pSSC != NULL; }
void InitializeIO(LPBYTE pCxRomPeripheral);
private:
void InsertInternal(UINT slot, SS_CARDTYPE type);
void InsertAuxInternal(SS_CARDTYPE type);

View file

@ -178,11 +178,17 @@ void Disk2InterfaceCard::LoadLastDiskImage(const int drive)
char pathname[MAX_PATH];
std::string& regSection = RegGetConfigSlotSection(m_slot);
if (RegLoadString(regSection.c_str(), regKey.c_str(), TRUE, pathname, MAX_PATH, TEXT("")))
if (RegLoadString(regSection.c_str(), regKey.c_str(), TRUE, pathname, MAX_PATH, TEXT("")) && (pathname[0] != 0))
{
m_saveDiskImage = false;
InsertDisk(drive, pathname, IMAGE_USE_FILES_WRITE_PROTECT_STATUS, IMAGE_DONT_CREATE);
ImageError_e error = InsertDisk(drive, pathname, IMAGE_USE_FILES_WRITE_PROTECT_STATUS, IMAGE_DONT_CREATE);
m_saveDiskImage = true;
if (error != eIMAGE_ERROR_NONE)
{
NotifyInvalidImage(drive, pathname, error);
EjectDisk(drive);
}
}
}

View file

@ -130,7 +130,7 @@ public:
virtual void Init(void) {};
virtual void Reset(const bool powerCycle);
void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
void Destroy(void); // no, doesn't "destroy" the disk image. DiskIIManagerShutdown()
void Boot(void);

View file

@ -14,7 +14,7 @@ public:
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
static BYTE __stdcall IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles);

View file

@ -42,54 +42,64 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../resource/resource.h"
/*
Memory map:
Memory map (for slot 7):
C0F0 (r) EXECUTE AND RETURN STATUS
C0F1 (r) STATUS (or ERROR)
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
C0F8 (r) NEXT BYTE (legacy read-only port - still supported)
Firmware notes:
. ROR ABS16,X and ROL ABS16,X - only used for $C081+s*$10 STATUS register:
6502: double read (old data), write (old data), write (new data). The writes are harmless as writes to STATUS are ignored.
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.
*/
/*
Hard drive emulation in Applewin.
Hard drive emulation in AppleWin.
Concept
To emulate a 32mb hard drive connected to an Apple IIe via Applewin.
To emulate a 32mb hard drive connected to an Apple IIe via AppleWin.
Designed to work with Autoboot Rom and Prodos.
Overview
1. Hard drive image file
The hard drive image file (.HDV) will be formatted into blocks of 512
bytes, in a linear fashion. The internal formatting and meaning of each
block to be decided by the Apple's operating system (ProDos). To create
an empty .HDV file, just create a 0 byte file (I prefer the debug method).
block to be decided by the Apple's operating system (ProDOS). To create
an empty .HDV file, just create a 0 byte file.
2. Emulation code
There are 4 commands Prodos will send to a block device.
There are 4 commands ProDOS will send to a block device.
Listed below are each command and how it's handled:
1. STATUS
In the emulation's case, returns only a DEVICE OK (0) or DEVICE I/O ERROR (8).
DEVICE I/O ERROR only returned if no HDV file is selected.
In the emulation's case, returns only a DEVICE OK (0), DEVICE I/O ERROR ($27) or DEVICE NOT CONNECTED ($28)
DEVICE NOT CONNECTED only returned if no HDV file is selected.
2. READ
Loads requested block into a 512 byte buffer by attempting to seek to
location in HDV file.
If seek fails, returns a DEVICE I/O ERROR. Resets hd_buf_ptr used by HD_NEXTBYTE
If seek fails, returns a DEVICE I/O ERROR. Resets m_buf_ptr used by legacy HD_NEXTBYTE
Copies requested block from a 512 byte buffer to the Apple's memory.
Sets STATUS.busy=1 until the DMA operation completes.
Returns a DEVICE OK if read was successful, or a DEVICE I/O ERROR otherwise.
3. WRITE
Copies requested block from the Apple's memory to a 512 byte buffer
then attempts to seek to requested block.
If the seek fails (usually because the seek is beyond the EOF for the
HDV file), the Emulation will attempt to "grow" the HDV file to accomodate.
Once the file can accomodate, or if the seek did not fail, the buffer is
HDV file), the emulation will attempt to "grow" the HDV file to accommodate.
Once the file can accommodate, or if the seek did not fail, the buffer is
written to the HDV file. NOTE: A2PC will grow *AND* shrink the HDV file.
Sets STATUS.busy=1 until the DMA operation completes.
I didn't see the point in shrinking the file as this behaviour would require
patching prodos (to detect DELETE FILE calls).
@ -99,9 +109,9 @@ Overview
3. Bugs
The only thing I've noticed is that Copy II+ 7.1 seems to crash or stall
occasionally when trying to calculate how many free block are available
occasionally when trying to calculate how many free blocks are available
when running a catalog. This might be due to the great number of blocks
available. Also, DDD pro will not optimise the disk correctally (it's
available. Also, DDD pro will not optimise the disk correctly (it's
doing a disk defragment of some sort, and when it requests a block outside
the range of the image file, it starts getting I/O errors), so don't
bother. Any program that preforms a read before write to an "unwritten"
@ -125,6 +135,9 @@ HarddiskInterfaceCard::HarddiskInterfaceCard(UINT slot) :
// . ProDOS will write to Command before switching drives
m_command = 0;
// 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
// if created by user in Config->Disk, then MemInitializeIO() won't be called
@ -149,7 +162,7 @@ void HarddiskInterfaceCard::Reset(const bool powerCycle)
//===========================================================================
void HarddiskInterfaceCard::InitializeIO(const LPBYTE pCxRomPeripheral)
void HarddiskInterfaceCard::InitializeIO(LPBYTE pCxRomPeripheral)
{
const DWORD HARDDISK_FW_SIZE = APPLE_SLOT_SIZE;
@ -190,7 +203,20 @@ void HarddiskInterfaceCard::CleanupDrive(const int iDrive)
void HarddiskInterfaceCard::NotifyInvalidImage(TCHAR* pszImageFilename)
{
// TC: TO DO
// TC: TO DO - see Disk2InterfaceCard::NotifyInvalidImage()
char szBuffer[MAX_PATH + 128];
StringCbPrintf(
szBuffer,
MAX_PATH + 128,
TEXT("Unable to open the file %s."),
pszImageFilename);
GetFrame().FrameMessageBox(
szBuffer,
g_pAppTitle.c_str(),
MB_ICONEXCLAMATION | MB_SETFOREGROUND);
}
//===========================================================================
@ -206,11 +232,17 @@ void HarddiskInterfaceCard::LoadLastDiskImage(const int drive)
char pathname[MAX_PATH];
std::string& regSection = RegGetConfigSlotSection(m_slot);
if (RegLoadString(regSection.c_str(), regKey.c_str(), TRUE, pathname, MAX_PATH, TEXT("")))
if (RegLoadString(regSection.c_str(), regKey.c_str(), TRUE, pathname, MAX_PATH, TEXT("")) && (pathname[0] != 0))
{
m_saveDiskImage = false;
Insert(drive, pathname);
bool res = Insert(drive, pathname);
m_saveDiskImage = true;
if (!res)
{
NotifyInvalidImage(pathname);
CleanupDrive(drive);
}
}
}
@ -309,10 +341,10 @@ void HarddiskInterfaceCard::Destroy(void)
//===========================================================================
// Pre: pathname likely to include path (but can also just be filename)
BOOL HarddiskInterfaceCard::Insert(const int iDrive, const std::string& pathname)
bool HarddiskInterfaceCard::Insert(const int iDrive, const std::string& pathname)
{
if (pathname.empty())
return FALSE;
return false;
if (m_hardDiskDrive[iDrive].m_imageloaded)
Unplug(iDrive);
@ -429,8 +461,8 @@ bool HarddiskInterfaceCard::IsDriveUnplugged(const int iDrive)
//===========================================================================
#define DEVICE_OK 0x00
#define DEVICE_UNKNOWN_ERROR 0x28
#define DEVICE_IO_ERROR 0x27
#define DEVICE_NOT_CONNECTED 0x28 // No device detected/connected
BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
{
@ -438,6 +470,9 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
HarddiskInterfaceCard* pCard = (HarddiskInterfaceCard*)MemGetSlotParameters(slot);
HardDiskDrive* pHDD = &(pCard->m_hardDiskDrive[pCard->m_unitNum >> 7]); // bit7 = drive select
CpuCalcCycles(nExecutedCycles);
const UINT CYCLES_FOR_DMA_RW_BLOCK = HD_BLOCK_SIZE;
BYTE r = DEVICE_OK;
pHDD->m_status_next = DISK_STATUS_READ;
@ -466,7 +501,31 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
{
pHDD->m_error = 0;
r = 0;
pCard->m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK;
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)
const UINT PAGE_SIZE = 256;
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];
// 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
memcpy(page + (dstAddr & 0xff), pSrc, size);
pSrc += size;
dstAddr += size;
remaining -= size;
}
}
else
{
@ -501,7 +560,7 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
}
}
memmove(pHDD->m_buf, mem+pHDD->m_memblock, HD_BLOCK_SIZE);
memcpy(pHDD->m_buf, mem + pHDD->m_memblock, HD_BLOCK_SIZE);
if (bRes)
bRes = ImageWriteBlock(pHDD->m_imagehandle, pHDD->m_diskblock, pHDD->m_buf);
@ -510,6 +569,7 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
{
pHDD->m_error = 0;
r = 0;
pCard->m_notBusyCycle = g_nCumulativeCycles + (UINT64)CYCLES_FOR_DMA_RW_BLOCK;
}
else
{
@ -527,16 +587,19 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
{
pHDD->m_status_next = DISK_STATUS_OFF;
pHDD->m_error = 1;
r = DEVICE_UNKNOWN_ERROR;
r = DEVICE_NOT_CONNECTED; // GH#452
}
break;
case 0x1: // m_error
pHDD->m_status_next = DISK_STATUS_OFF; // TODO: FIXME: ??? YELLOW ??? WARNING
if (pHDD->m_error)
{
_ASSERT(pHDD->m_error & 1);
pHDD->m_error |= 1; // Firmware requires that b0=1 for an 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;
break;
@ -558,7 +621,7 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
case 0x7:
r = (BYTE)(pHDD->m_diskblock & 0xFF00 >> 8);
break;
case 0x8:
case 0x8: // Legacy: continue to support this I/O port for old HDD firmware
r = pHDD->m_buf[pHDD->m_buf_ptr];
if (pHDD->m_buf_ptr < sizeof(pHDD->m_buf)-1)
pHDD->m_buf_ptr++;
@ -649,8 +712,10 @@ bool HarddiskInterfaceCard::ImageSwap(void)
//===========================================================================
// Unit version history:
// 2: Updated $Csnn firmware to fix GH#319
static const UINT kUNIT_VERSION = 2;
// 2: Updated $C7nn firmware to fix GH#319
// 3: Updated $Csnn firmware to fix GH#996 (now slot-independent code)
// Added: Not Busy Cycle
static const UINT kUNIT_VERSION = 3;
#define SS_YAML_VALUE_CARD_HDD "Generic HDD"
@ -667,6 +732,7 @@ static const UINT kUNIT_VERSION = 2;
#define SS_YAML_KEY_STATUS_PREV "Status Prev"
#define SS_YAML_KEY_BUF_PTR "Buffer Offset"
#define SS_YAML_KEY_BUF "Buffer"
#define SS_YAML_KEY_NOT_BUSY_CYCLE "Not Busy Cycle"
std::string HarddiskInterfaceCard::GetSnapshotCardName(void)
{
@ -700,6 +766,7 @@ void HarddiskInterfaceCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
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.SaveHexUint8(SS_YAML_KEY_COMMAND, m_command);
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_NOT_BUSY_CYCLE, m_notBusyCycle);
SaveSnapshotHDDUnit(yamlSaveHelper, HARDDISK_1);
SaveSnapshotHDDUnit(yamlSaveHelper, HARDDISK_2);
@ -777,12 +844,15 @@ bool HarddiskInterfaceCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT sl
if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
if (version == 1 && (regs.pc >> 8) == (0xC0|slot))
if (version <= 2 && (regs.pc >> 8) == (0xC0|slot))
throw std::string("HDD card: 6502 is running old HDD firmware");
m_unitNum = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_UNIT); // b7=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<NUM_HARDDISKS; i++)
{

View file

@ -88,13 +88,13 @@ public:
virtual void Init(void) {}
virtual void Reset(const bool powerCycle);
void InitializeIO(const LPBYTE pCxRomPeripheral);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
void Destroy(void);
const std::string& GetFullName(const int iDrive);
const std::string& HarddiskGetFullPathName(const int iDrive);
void GetFilenameAndPathForSaveState(std::string& filename, std::string& path);
bool Select(const int iDrive);
BOOL Insert(const int iDrive, const std::string& pathname);
bool Insert(const int iDrive, const std::string& pathname);
void Unplug(const int iDrive);
bool IsDriveUnplugged(const int iDrive);
void LoadLastDiskImage(const int iDrive);
@ -125,6 +125,7 @@ private:
BYTE m_unitNum; // b7=unit
BYTE m_command;
UINT64 m_notBusyCycle;
bool m_saveDiskImage; // Save the DiskImage name to Registry

View file

@ -50,7 +50,7 @@ LanguageCardUnit::~LanguageCardUnit(void)
SetMemMainLanguageCard(NULL);
}
void LanguageCardUnit::InitializeIO(void)
void LanguageCardUnit::InitializeIO(LPBYTE pCxRomPeripheral)
{
RegisterIoHandler(kSlot0, &LanguageCardUnit::IO, &LanguageCardUnit::IO, NULL, NULL, this, NULL);
}
@ -276,7 +276,7 @@ UINT Saturn128K::GetActiveBank(void)
return m_uSaturnActiveBank;
}
void Saturn128K::InitializeIO(void)
void Saturn128K::InitializeIO(LPBYTE pCxRomPeripheral)
{
RegisterIoHandler(kSlot0, &Saturn128K::IO, &Saturn128K::IO, NULL, NULL, this, NULL);
}

View file

@ -15,7 +15,7 @@ public:
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
virtual void InitializeIO(void);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void SetMemorySize(UINT banks) {} // No-op for //e and slot-0 16K LC
virtual UINT GetActiveBank(void) { return 0; } // Always 0 as only 1x 16K bank
virtual void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper) { _ASSERT(0); } // Not used for //e
@ -71,7 +71,7 @@ public:
Saturn128K(UINT banks);
virtual ~Saturn128K(void);
virtual void InitializeIO(void);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void SetMemorySize(UINT banks);
virtual UINT GetActiveBank(void);
virtual void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);

View file

@ -1720,84 +1720,11 @@ void MemInitializeIO(void)
InitIoHandlers();
if (g_pLanguageCard)
g_pLanguageCard->InitializeIO();
g_pLanguageCard->InitializeIO(NULL);
else
RegisterIoHandler(LanguageCardUnit::kSlot0, IO_Null, IO_Null, NULL, NULL, NULL, NULL);
if (GetCardMgr().QuerySlot(SLOT1) == CT_GenericPrinter)
PrintLoadRom(pCxRomPeripheral, SLOT1); // $C100 : Parallel printer f/w
if (GetCardMgr().QuerySlot(SLOT2) == CT_SSC)
dynamic_cast<CSuperSerialCard&>(GetCardMgr().GetRef(SLOT2)).InitializeIO(pCxRomPeripheral); // $C200 : SSC
if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet)
{
// Slot 3 contains the Uthernet card (which can coexist with an 80-col+Ram card in AUX slot)
// . Uthernet card has no ROM and only IO mapped at $C0Bx
tfe_InitializeIO(pCxRomPeripheral, SLOT3);
}
else if (GetCardMgr().QuerySlot(SLOT3) == CT_FourPlay)
{
dynamic_cast<FourPlayCard&>(GetCardMgr().GetRef(SLOT3)).InitializeIO(pCxRomPeripheral);
}
else if (GetCardMgr().QuerySlot(SLOT3) == CT_SNESMAX)
{
dynamic_cast<SNESMAXCard&>(GetCardMgr().GetRef(SLOT3)).InitializeIO(pCxRomPeripheral);
}
// Apple//e: Auxiliary slot contains Extended 80 Column card or RamWorksIII card
if (GetCardMgr().QuerySlot(SLOT4) == CT_MouseInterface)
{
dynamic_cast<CMouseInterface&>(GetCardMgr().GetRef(SLOT4)).InitializeIO(pCxRomPeripheral); // $C400 : Mouse f/w
}
else if (GetCardMgr().QuerySlot(SLOT4) == CT_MockingboardC || GetCardMgr().QuerySlot(SLOT4) == CT_Phasor)
{
MB_InitializeIO(pCxRomPeripheral, SLOT4, SLOT5);
}
else if (GetCardMgr().QuerySlot(SLOT4) == CT_Z80)
{
Z80_InitializeIO(pCxRomPeripheral, SLOT4); // $C400 : Z80 card
}
// else if (GetCardMgr().QuerySlot(SLOT4) == CT_GenericClock)
// {
// LoadRom_Clock_Generic(pCxRomPeripheral, SLOT4);
// }
else if (GetCardMgr().QuerySlot(SLOT4) == CT_FourPlay)
{
dynamic_cast<FourPlayCard&>(GetCardMgr().GetRef(SLOT4)).InitializeIO(pCxRomPeripheral);
}
else if (GetCardMgr().QuerySlot(SLOT4) == CT_SNESMAX)
{
dynamic_cast<SNESMAXCard&>(GetCardMgr().GetRef(SLOT4)).InitializeIO(pCxRomPeripheral);
}
if (GetCardMgr().QuerySlot(SLOT5) == CT_Z80)
{
Z80_InitializeIO(pCxRomPeripheral, SLOT5); // $C500 : Z80 card
}
else if (GetCardMgr().QuerySlot(SLOT5) == CT_SAM)
{
dynamic_cast<SAMCard&>(GetCardMgr().GetRef(SLOT5)).InitializeIO(pCxRomPeripheral);
}
else if (GetCardMgr().QuerySlot(SLOT5) == CT_FourPlay)
{
dynamic_cast<FourPlayCard&>(GetCardMgr().GetRef(SLOT5)).InitializeIO(pCxRomPeripheral);
}
else if (GetCardMgr().QuerySlot(SLOT5) == CT_SNESMAX)
{
dynamic_cast<SNESMAXCard&>(GetCardMgr().GetRef(SLOT5)).InitializeIO(pCxRomPeripheral);
}
else if (GetCardMgr().QuerySlot(SLOT5) == CT_Disk2)
{
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT5)).InitializeIO(pCxRomPeripheral); // $C500 : Disk][ card
}
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6)).InitializeIO(pCxRomPeripheral); // $C600 : Disk][ card
if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD)
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(SLOT7)).InitializeIO(pCxRomPeripheral);
GetCardMgr().InitializeIO(pCxRomPeripheral);
}
// Called by:

View file

@ -14,7 +14,7 @@ public:
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
// void Uninitialize();
void Reset();
UINT GetSlot(void) { return m_slot; }

View file

@ -14,7 +14,7 @@ public:
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles);

View file

@ -18,10 +18,10 @@ public:
}
virtual ~SNESMAXCard(void) {}
virtual void Init(void) {};
virtual void Reset(const bool powerCycle) {};
virtual void Init(void) {}
virtual void Reset(const bool powerCycle) {}
void InitializeIO(LPBYTE pCxRomPeripheral);
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
static BYTE __stdcall IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles);
static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles);