mirror of
https://github.com/dschmenk/Appalm.git
synced 2024-12-14 21:29:07 +00:00
1803 lines
61 KiB
C
Executable File
1803 lines
61 KiB
C
Executable File
#include <PalmOS.h>
|
|
#include "Apple2.h"
|
|
extern UInt16 grMode;
|
|
|
|
UInt16 memIOU, vidIOU, kbdIOU, spkrIOU, btnIOU[3], pdlIOU[2], pdlResetIOU[2];
|
|
UInt8 kbdBuffer[16];
|
|
UInt8 kbdHead, kbdCount;
|
|
UInt8 *AppleMemory;
|
|
UInt8 *AuxMemory;
|
|
UInt8 *AppleROM;
|
|
UInt8 *BootROM;
|
|
READBYTE *ReadFunction;
|
|
WRITEBYTE *WriteFunction;
|
|
UInt8 auxDirty = 0;
|
|
|
|
void showDiskName(UInt16, Char *);
|
|
void showDriveState(UInt16, Boolean);
|
|
UInt8 RIOM(UInt16);
|
|
void WIOM(UInt16, UInt8);
|
|
UInt8 RC3M(UInt16);
|
|
void clrVidFuncs(UInt16);
|
|
void toggleVideoSoftSwitch(UInt8);
|
|
|
|
/*
|
|
* Function prototypes for sector nibblizing
|
|
*/
|
|
static void init_GCR_table(void);
|
|
static UInt8 gcr_read_nibble(void);
|
|
static void gcr_write_nibble( UInt8 );
|
|
static void decode62( UInt8* );
|
|
static void encode62( UInt8* );
|
|
static void FM_encode( UInt8 );
|
|
static UInt8 FM_decode(void);
|
|
static void write_sync( int );
|
|
static int read_address_field( int*, int*, int* );
|
|
static void write_address_field( int, int, int );
|
|
static int read_data_field(void);
|
|
static void write_data_field(void);
|
|
int NibblesToSectors(UInt8 *nibbles, UInt8 *sectors, int volume, int track);
|
|
void SectorsToNibbles(UInt8 *sectors, UInt8 *nibbles, int volume, int track);
|
|
|
|
void initMemory(void)
|
|
{
|
|
vidIOU = SS_TEXT;
|
|
memIOU =
|
|
kbdIOU =
|
|
spkrIOU =
|
|
btnIOU[0] =
|
|
btnIOU[1] =
|
|
btnIOU[2] =
|
|
pdlIOU[0] =
|
|
pdlIOU[1] = 0;
|
|
pdlResetIOU[0] =
|
|
pdlResetIOU[1] = 0x80;
|
|
kbdHead =
|
|
kbdCount = 0;
|
|
MemSet(AppleMemory, 65536, 0);
|
|
MemSet(AuxMemory, 16384+2048, 0);
|
|
MemSet(ReadFunction, 1024, 0);
|
|
MemSet(WriteFunction, 1024, 0);
|
|
loadROM();
|
|
MemMove(AppleMemory + 0xC100, AppleROM, 0x3F00);
|
|
setMemFuncs();
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* *
|
|
* ROM Access *
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
/* Apple ROM Paging */
|
|
|
|
void READONLY(UInt16, UInt8);
|
|
UInt8 RRMP(UInt16);
|
|
void awrap_ROMReadWrite(void)
|
|
{
|
|
asm(".global RRMP");
|
|
asm(".global READONLY");
|
|
asm("RRMP:");
|
|
asm("clr.l %d0");
|
|
asm("READONLY:");
|
|
asm("rts");
|
|
}
|
|
RDMEM_CWRAP(REXP, address)
|
|
{
|
|
if (!SS_ISSET(memIOU, SS_SLOTCXROM) && address == 0xCFFF)
|
|
{
|
|
MemMove(AppleMemory + 0xCE00, BootROM, 256);
|
|
SS_RESET(memIOU, SS_EXPNROM);
|
|
return (0x00);
|
|
}
|
|
else
|
|
{
|
|
return (AppleMemory[address]);
|
|
}
|
|
}
|
|
RDMEM_CWRAP(RC3M, address)
|
|
{
|
|
if (!SS_ISSET(memIOU, SS_SLOTC3ROM))
|
|
{
|
|
SS_SET(memIOU, SS_EXPNROM);
|
|
MemMove(AppleMemory + 0xC800, AppleROM + 0x700, 2048);
|
|
}
|
|
return (AppleMemory[address]);
|
|
}
|
|
void loadROM(void)
|
|
{
|
|
DmOpenRef romDB;
|
|
LocalID romID;
|
|
MemHandle romRec;
|
|
UInt8 *roms;
|
|
UInt32 romStart;
|
|
UInt16 count;
|
|
|
|
romID = DmFindDatabase(0, "apple2e.rom");
|
|
romDB = DmOpenDatabase(0, romID, dmModeReadOnly);
|
|
romRec = DmQueryRecord(romDB, 0);
|
|
roms = (UInt8 *) MemHandleLock(romRec);
|
|
romStart = 0x004100;
|
|
AppleROM = MemPtrNew(0x3F00);
|
|
MemMove(AppleROM, roms + romStart, 0x3F00);
|
|
MemHandleUnlock(romRec);
|
|
DmCloseDatabase(romDB);
|
|
romID = DmFindDatabase(0, "slot6.rom");
|
|
romDB = DmOpenDatabase(0, romID, dmModeReadOnly);
|
|
romRec = DmQueryRecord(romDB, 0);
|
|
roms = (UInt8 *) MemHandleLock(romRec);
|
|
BootROM = MemPtrNew(256);
|
|
MemMove(BootROM, roms, 256);
|
|
MemHandleUnlock(romRec);
|
|
DmCloseDatabase(romDB);
|
|
BootROM[0x004C] = 0xA9;
|
|
BootROM[0x004D] = 0x00;
|
|
BootROM[0x004E] = 0xEA;
|
|
}
|
|
void unloadROM(void)
|
|
{
|
|
MemPtrFree(AppleROM);
|
|
MemPtrFree(BootROM);
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* *
|
|
* Soft switches *
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
void AuxWriteBnk1(UInt16, UInt8);
|
|
void AuxWriteBnk2(UInt16, UInt8);
|
|
void AuxWrite(UInt16, UInt8);
|
|
void AuxMapWriteBnk1(UInt16, UInt8);
|
|
void AuxMapWriteBnk2(UInt16, UInt8);
|
|
void AuxMapWrite(UInt16, UInt8);
|
|
UInt8 AuxReadBnk1(UInt16);
|
|
UInt8 AuxReadBnk2(UInt16);
|
|
UInt8 AuxRead(UInt16);
|
|
UInt8 RomRead(UInt16);
|
|
void awrap_AuxMemReadWrite(void)
|
|
{
|
|
asm(".global AuxWriteBnk1");
|
|
asm(".global AuxWriteBnk2");
|
|
asm(".global AuxWrite");
|
|
asm(".global AuxMapWriteBnk1");
|
|
asm(".global AuxMapWriteBnk2");
|
|
asm(".global AuxMapWrite");
|
|
asm(".global AuxReadBnk1");
|
|
asm(".global AuxReadBnk2");
|
|
asm(".global AuxRead");
|
|
asm(".global RomRead");
|
|
asm("AuxWriteBnk1:");
|
|
asm("move.l AuxMemory@END.w(%a5), %a6");
|
|
asm("sub.l #0xD000, %a6");
|
|
asm("move.b %d0, (%a6, %d6.l)");
|
|
asm("rts");
|
|
asm("AuxWriteBnk2:");
|
|
asm("AuxWrite:");
|
|
asm("move.l AuxMemory@END.w(%a5), %a6");
|
|
asm("sub.l #0xC000, %a6");
|
|
asm("move.b %d0, (%a6, %d6.l)");
|
|
asm("rts");
|
|
asm("AuxMapWriteBnk1:");
|
|
asm("move.l AuxMemory@END.w(%a5), %a6");
|
|
asm("sub.l #0xD000, %a6");
|
|
asm("move.b %d0, (%a6, %d6.l)");
|
|
asm("move.b %d0, (%a2, %d6.l)");
|
|
asm("rts");
|
|
asm("AuxMapWriteBnk2:");
|
|
asm("AuxMapWrite:");
|
|
asm("move.l AuxMemory@END.w(%a5), %a6");
|
|
asm("sub.l #0xC000, %a6");
|
|
asm("move.b %d0, (%a6, %d6.l)");
|
|
asm("move.b %d0, (%a2, %d6.l)");
|
|
asm("rts");
|
|
asm("AuxReadBnk1:");
|
|
asm("move.l AuxMemory@END.w(%a5), %a6");
|
|
asm("sub.l #0xD000, %a6");
|
|
asm("clr.l %d0");
|
|
asm("move.b (%a6, %d6.l), %d0");
|
|
asm("rts");
|
|
asm("AuxReadBnk2:");
|
|
asm("AuxRead:");
|
|
asm("move.l AuxMemory@END.w(%a5), %a6");
|
|
asm("sub.l #0xC000, %a6");
|
|
asm("clr.l %d0");
|
|
asm("move.b (%a6, %d6.l), %d0");
|
|
asm("rts");
|
|
asm("RomRead:");
|
|
asm("move.l AppleROM@END.w(%a5), %a6");
|
|
asm("sub.l #0xC100, %a6");
|
|
asm("clr.l %d0");
|
|
asm("move.b (%a6, %d6.l), %d0");
|
|
asm("rts");
|
|
}
|
|
void WTXTM(UInt16, UInt8);
|
|
void WATXTM(UInt16, UInt8);
|
|
UInt8 RATXTM(UInt16 address);
|
|
void writeMemorySoftSwitch(UInt8 io)
|
|
{
|
|
UInt16 page;
|
|
UInt8 *tempPage;
|
|
|
|
switch (io)
|
|
{
|
|
case IO_OFFSET(CLR80STORE):
|
|
if (SS_ISSET(vidIOU, SS_80STORE))
|
|
{
|
|
clrVidFuncs(vidIOU);
|
|
SS_RESET(vidIOU, SS_80STORE);
|
|
if (SS_ISSET(vidIOU, SS_PAGE2))
|
|
{
|
|
WriteFunction[0x08] = WTXTM;
|
|
WriteFunction[0x09] = WTXTM;
|
|
WriteFunction[0x0A] = WTXTM;
|
|
WriteFunction[0x0B] = WTXTM;
|
|
}
|
|
else
|
|
{
|
|
WriteFunction[0x04] = WTXTM;
|
|
WriteFunction[0x05] = WTXTM;
|
|
WriteFunction[0x06] = WTXTM;
|
|
WriteFunction[0x07] = WTXTM;
|
|
}
|
|
}
|
|
break;
|
|
case IO_OFFSET(SET80STORE):
|
|
if (!SS_ISSET(vidIOU, SS_80STORE))
|
|
{
|
|
clrVidFuncs(vidIOU);
|
|
SS_SET(vidIOU, SS_80STORE);
|
|
if (SS_ISSET(vidIOU, SS_PAGE2))
|
|
{
|
|
if ((grMode > GRMODE_COLOR) && prefs.enable80Col)
|
|
{
|
|
WriteFunction[0x04] = WATXTM;
|
|
WriteFunction[0x05] = WATXTM;
|
|
WriteFunction[0x06] = WATXTM;
|
|
WriteFunction[0x07] = WATXTM;
|
|
ReadFunction[0x04] = RATXTM;
|
|
ReadFunction[0x05] = RATXTM;
|
|
ReadFunction[0x06] = RATXTM;
|
|
ReadFunction[0x07] = RATXTM;
|
|
}
|
|
else
|
|
{
|
|
WriteFunction[0x04] = READONLY;
|
|
WriteFunction[0x05] = READONLY;
|
|
WriteFunction[0x06] = READONLY;
|
|
WriteFunction[0x07] = READONLY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteFunction[0x04] = WTXTM;
|
|
WriteFunction[0x05] = WTXTM;
|
|
WriteFunction[0x06] = WTXTM;
|
|
WriteFunction[0x07] = WTXTM;
|
|
}
|
|
}
|
|
break;
|
|
case IO_OFFSET(RDMAINRAM):
|
|
if (SS_ISSET(memIOU, SS_RAMRD))
|
|
{
|
|
SS_RESET(memIOU, SS_RAMRD);
|
|
for (page = 0x02; page < 0xC0; page++)
|
|
ReadFunction[page] = NULL;
|
|
setVideoFuncs(false);
|
|
}
|
|
break;
|
|
case IO_OFFSET(RDCARDRAM):
|
|
if (!SS_ISSET(memIOU, SS_RAMRD))
|
|
{
|
|
SS_SET(memIOU, SS_RAMRD);
|
|
for (page = 0x02; page < 0xC0; page++)
|
|
ReadFunction[page] = RRMP;
|
|
}
|
|
break;
|
|
case IO_OFFSET(WRMAINRAM):
|
|
if (SS_ISSET(memIOU, SS_RAMWRT))
|
|
{
|
|
SS_RESET(memIOU, SS_RAMWRT);
|
|
for (page = 0x02; page < 0xC0; page++)
|
|
WriteFunction[page] = NULL;
|
|
setVideoFuncs(false);
|
|
}
|
|
break;
|
|
case IO_OFFSET(WRCARDRAM):
|
|
if (!SS_ISSET(memIOU, SS_RAMWRT))
|
|
{
|
|
SS_SET(memIOU, SS_RAMWRT);
|
|
for (page = 0x02; page < 0xC0; page++)
|
|
WriteFunction[page] = READONLY;
|
|
}
|
|
break;
|
|
case IO_OFFSET(SETSLOTCXROM):
|
|
if (SS_ISSET(memIOU, SS_SLOTCXROM))
|
|
{
|
|
SS_RESET(memIOU, SS_SLOTCXROM);
|
|
ReadFunction[0xC1] = RRMP;
|
|
ReadFunction[0xC2] = RRMP;
|
|
ReadFunction[0xC4] = RRMP;
|
|
ReadFunction[0xC5] = RRMP;
|
|
ReadFunction[0xC6] = RRMP;
|
|
ReadFunction[0xC7] = RRMP;
|
|
MemSet(AppleMemory + 0xC100, 512, 0);
|
|
MemSet(AppleMemory + 0xC400, 1024, 0);
|
|
ReadFunction[0xC6] = NULL;
|
|
MemMove(AppleMemory + 0x00C600, BootROM, 256);
|
|
}
|
|
break;
|
|
case IO_OFFSET(SETINTCXROM):
|
|
if (!SS_ISSET(memIOU, SS_SLOTCXROM))
|
|
{
|
|
SS_SET(memIOU, SS_SLOTCXROM);
|
|
ReadFunction[0xC1] = NULL;
|
|
ReadFunction[0xC2] = NULL;
|
|
ReadFunction[0xC4] = NULL;
|
|
ReadFunction[0xC5] = NULL;
|
|
ReadFunction[0xC6] = NULL;
|
|
ReadFunction[0xC7] = NULL;
|
|
MemMove(AppleMemory + 0xC100, AppleROM, 512);
|
|
MemMove(AppleMemory + 0xC400, AppleROM + 0x300, 1024);
|
|
}
|
|
break;
|
|
case IO_OFFSET(SETINTC3ROM):
|
|
if (!SS_ISSET(memIOU, SS_SLOTC3ROM))
|
|
{
|
|
SS_SET(memIOU, SS_SLOTC3ROM);
|
|
MemMove(AppleMemory + 0xC300, AppleROM + 0x200, 256);
|
|
}
|
|
break;
|
|
case IO_OFFSET(SETSLOTC3ROM):
|
|
if (SS_ISSET(memIOU, SS_SLOTC3ROM))
|
|
{
|
|
SS_RESET(memIOU, SS_SLOTC3ROM);
|
|
MemSet(AppleMemory + 0xC300, 256, 0);
|
|
}
|
|
break;
|
|
case IO_OFFSET(CLR80VID):
|
|
SS_RESET(vidIOU, SS_80VID);
|
|
setVideoFuncs(true);
|
|
break;
|
|
case IO_OFFSET(SET80VID):
|
|
SS_SET(vidIOU, SS_80VID);
|
|
setVideoFuncs(true);
|
|
break;
|
|
}
|
|
}
|
|
void updateLanguageCardMapping(void)
|
|
{
|
|
READBYTE rfn;
|
|
WRITEBYTE wfn;
|
|
UInt16 page;
|
|
|
|
if (SS_ISSET(memIOU, SS_LCRAMMAP))
|
|
{
|
|
MemMove(AppleMemory + 0xD000, AuxMemory + (SS_ISSET(memIOU, SS_LCBNK2) ? 0x1000 : 0x0000), 4096);
|
|
MemMove(AppleMemory + 0xE000, AuxMemory + 0x2000, 8192);
|
|
}
|
|
else
|
|
{
|
|
MemMove(AppleMemory + 0xD000, AppleROM + 0xD000 - 0xC100, 4096+8192);
|
|
}
|
|
if (SS_ISSET(memIOU, SS_LCRAMWRT))
|
|
{
|
|
if (SS_ISSET(memIOU, SS_LCRAMMAP))
|
|
{
|
|
wfn = SS_ISSET(memIOU, SS_LCBNK2) ? AuxMapWriteBnk2 : AuxMapWriteBnk1;
|
|
WriteFunction[0xD0] = WriteFunction[0xD1] = wfn;
|
|
WriteFunction[0xD2] = WriteFunction[0xD3] = wfn;
|
|
WriteFunction[0xD4] = WriteFunction[0xD5] = wfn;
|
|
WriteFunction[0xD6] = WriteFunction[0xD7] = wfn;
|
|
WriteFunction[0xD8] = WriteFunction[0xD9] = wfn;
|
|
WriteFunction[0xDA] = WriteFunction[0xDB] = wfn;
|
|
WriteFunction[0xDC] = WriteFunction[0xDD] = wfn;
|
|
WriteFunction[0xDE] = WriteFunction[0xDF] = wfn;
|
|
for (page = 0xE0; page <= 0xFF; page++)
|
|
WriteFunction[page] = AuxMapWrite;
|
|
}
|
|
else
|
|
{
|
|
wfn = SS_ISSET(memIOU, SS_LCBNK2) ? AuxWriteBnk2 : AuxWriteBnk1;
|
|
WriteFunction[0xD0] = WriteFunction[0xD1] = wfn;
|
|
WriteFunction[0xD2] = WriteFunction[0xD3] = wfn;
|
|
WriteFunction[0xD4] = WriteFunction[0xD5] = wfn;
|
|
WriteFunction[0xD6] = WriteFunction[0xD7] = wfn;
|
|
WriteFunction[0xD8] = WriteFunction[0xD9] = wfn;
|
|
WriteFunction[0xDA] = WriteFunction[0xDB] = wfn;
|
|
WriteFunction[0xDC] = WriteFunction[0xDD] = wfn;
|
|
WriteFunction[0xDE] = WriteFunction[0xDF] = wfn;
|
|
for (page = 0xE0; page <= 0xFF; page++)
|
|
WriteFunction[page] = AuxWrite;
|
|
}
|
|
}
|
|
else
|
|
for (page = 0xD0; page <= 0xFF; page++)
|
|
WriteFunction[page] = READONLY;
|
|
for (page = 0xD0; page <= 0xFF; page++)
|
|
ReadFunction[page] = NULL;
|
|
clrPcCheckFuncs();
|
|
auxDirty = 0;
|
|
}
|
|
void toggleLanguageCard(UInt8 io)
|
|
{
|
|
READBYTE rfn;
|
|
WRITEBYTE wfn;
|
|
UInt16 page, dirty, pc6502;
|
|
|
|
/* set IOU soft switches */
|
|
dirty = memIOU;
|
|
switch (io)
|
|
{
|
|
case 0x00:
|
|
case 0x04:
|
|
SS_SET(memIOU, SS_LCBNK2);
|
|
SS_SET(memIOU, SS_LCRAMMAP);
|
|
SS_RESET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x01:
|
|
case 0x05:
|
|
SS_SET(memIOU, SS_LCBNK2);
|
|
SS_RESET(memIOU, SS_LCRAMMAP);
|
|
SS_SET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x02:
|
|
case 0x06:
|
|
SS_SET(memIOU, SS_LCBNK2);
|
|
SS_RESET(memIOU, SS_LCRAMMAP);
|
|
SS_RESET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x03:
|
|
case 0x07:
|
|
SS_SET(memIOU, SS_LCBNK2);
|
|
SS_SET(memIOU, SS_LCRAMMAP);
|
|
SS_SET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x08:
|
|
case 0x0C:
|
|
SS_RESET(memIOU, SS_LCBNK2);
|
|
SS_SET(memIOU, SS_LCRAMMAP);
|
|
SS_RESET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x09:
|
|
case 0x0D:
|
|
SS_RESET(memIOU, SS_LCBNK2);
|
|
SS_RESET(memIOU, SS_LCRAMMAP);
|
|
SS_SET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x0A:
|
|
case 0x0E:
|
|
SS_RESET(memIOU, SS_LCBNK2);
|
|
SS_RESET(memIOU, SS_LCRAMMAP);
|
|
SS_RESET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
case 0x0B:
|
|
case 0x0F:
|
|
SS_RESET(memIOU, SS_LCBNK2);
|
|
SS_SET(memIOU, SS_LCRAMMAP);
|
|
SS_SET(memIOU, SS_LCRAMWRT);
|
|
break;
|
|
}
|
|
/*
|
|
* Update only the changed state.
|
|
*/
|
|
if ((dirty ^= memIOU))
|
|
{
|
|
if (dirty & SS_LCRAMMAP)
|
|
{
|
|
updateLanguageCardMapping();
|
|
}
|
|
else
|
|
{
|
|
if (SS_ISSET(memIOU, SS_LCRAMMAP))
|
|
{
|
|
if (dirty & SS_LCBNK2)
|
|
{
|
|
pc6502 = state6502.LongRegs.PC-state6502.LongRegs.MEMBASE;
|
|
if ((pc6502 >= 0xD000) && (pc6502 < 0xE000))
|
|
{
|
|
updateLanguageCardMapping();
|
|
return;
|
|
}
|
|
if (!auxDirty)
|
|
{
|
|
/*
|
|
* Install special versions of JMP, JSR, BRK, RTI, and RTS that check for memory ranges.
|
|
*/
|
|
setPcCheckFuncs();
|
|
auxDirty = 1;
|
|
}
|
|
rfn = SS_ISSET(memIOU, SS_LCBNK2) ? AuxReadBnk2 : AuxReadBnk1;
|
|
ReadFunction[0xD0] = ReadFunction[0xD1] = rfn;
|
|
ReadFunction[0xD2] = ReadFunction[0xD3] = rfn;
|
|
ReadFunction[0xD4] = ReadFunction[0xD5] = rfn;
|
|
ReadFunction[0xD6] = ReadFunction[0xD7] = rfn;
|
|
ReadFunction[0xD8] = ReadFunction[0xD9] = rfn;
|
|
ReadFunction[0xDA] = ReadFunction[0xDB] = rfn;
|
|
ReadFunction[0xDC] = ReadFunction[0xDD] = rfn;
|
|
ReadFunction[0xDE] = ReadFunction[0xDF] = rfn;
|
|
}
|
|
if (SS_ISSET(memIOU, SS_LCRAMWRT))
|
|
{
|
|
wfn = SS_ISSET(memIOU, SS_LCBNK2) ? AuxMapWriteBnk2 : AuxMapWriteBnk1;
|
|
WriteFunction[0xD0] = WriteFunction[0xD1] = wfn;
|
|
WriteFunction[0xD2] = WriteFunction[0xD3] = wfn;
|
|
WriteFunction[0xD4] = WriteFunction[0xD5] = wfn;
|
|
WriteFunction[0xD6] = WriteFunction[0xD7] = wfn;
|
|
WriteFunction[0xD8] = WriteFunction[0xD9] = wfn;
|
|
WriteFunction[0xDA] = WriteFunction[0xDB] = wfn;
|
|
WriteFunction[0xDC] = WriteFunction[0xDD] = wfn;
|
|
WriteFunction[0xDE] = WriteFunction[0xDF] = wfn;
|
|
if (dirty & SS_LCRAMWRT)
|
|
for (page = 0xE0; page <= 0xFF; page++)
|
|
WriteFunction[page] = AuxMapWrite;
|
|
}
|
|
else if (dirty & SS_LCRAMWRT)
|
|
for (page = 0xD0; page <= 0xFF; page++)
|
|
WriteFunction[page] = READONLY;
|
|
}
|
|
else
|
|
{
|
|
if (SS_ISSET(memIOU, SS_LCRAMWRT))
|
|
{
|
|
wfn = SS_ISSET(memIOU, SS_LCBNK2) ? AuxWriteBnk2 : AuxWriteBnk1;
|
|
WriteFunction[0xD0] = WriteFunction[0xD1] = wfn;
|
|
WriteFunction[0xD2] = WriteFunction[0xD3] = wfn;
|
|
WriteFunction[0xD4] = WriteFunction[0xD5] = wfn;
|
|
WriteFunction[0xD6] = WriteFunction[0xD7] = wfn;
|
|
WriteFunction[0xD8] = WriteFunction[0xD9] = wfn;
|
|
WriteFunction[0xDA] = WriteFunction[0xDB] = wfn;
|
|
WriteFunction[0xDC] = WriteFunction[0xDD] = wfn;
|
|
WriteFunction[0xDE] = WriteFunction[0xDF] = wfn;
|
|
if (dirty & SS_LCRAMWRT)
|
|
for (page = 0xE0; page <= 0xFF; page++)
|
|
WriteFunction[page] = AuxWrite;
|
|
}
|
|
else if (dirty & SS_LCRAMWRT)
|
|
for (page = 0xD0; page <= 0xFF; page++)
|
|
WriteFunction[page] = READONLY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void setMemFuncs(void)
|
|
{
|
|
UInt16 page;
|
|
|
|
updateLanguageCardMapping();
|
|
if (!SS_ISSET(memIOU, SS_SLOTCXROM))
|
|
{
|
|
ReadFunction[0xC1] = RRMP;
|
|
ReadFunction[0xC2] = RRMP;
|
|
ReadFunction[0xC4] = RRMP;
|
|
ReadFunction[0xC5] = RRMP;
|
|
ReadFunction[0xC6] = RRMP;
|
|
ReadFunction[0xC7] = RRMP;
|
|
}
|
|
if (SS_ISSET(memIOU, SS_RAMRD))
|
|
for (page = 0x02; page < 0xC0; page++)
|
|
ReadFunction[page] = RRMP;
|
|
if (SS_ISSET(memIOU, SS_RAMWRT))
|
|
for (page = 0x02; page < 0xC0; page++)
|
|
WriteFunction[page] = READONLY;
|
|
ReadFunction[0xC0] = RIOM;
|
|
ReadFunction[0xC3] = RC3M;
|
|
WriteFunction[0xC0] = WIOM;
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* *
|
|
* Disk access control *
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
#define RAW_TRACK_BYTES 6192
|
|
#define RAW_SECTOR_BYTES 387
|
|
#define DOS_TRACK_BYTES 4096
|
|
#define RAW_TRACK_BITS (RAW_TRACK_BYTES*8)
|
|
#define NO_OF_PHASES 8
|
|
#define MAX_PHYSICAL_TRACK_NO (40*NO_OF_PHASES)
|
|
#define MAX_DRIVE_NO 2
|
|
|
|
static Int8 stepper_movement_table[16][NO_OF_PHASES] =
|
|
{
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0}, /* all electromagnets off */
|
|
{ 0, -1, -2, -3, 0, 3, 2, 1}, /* EM 1 on */
|
|
{ 2, 1, 0, -1, -2, -3, 0, 3}, /* EM 2 on */
|
|
{ 1, 0, -1, -2, -3, 0, 3, 2}, /* EMs 1 & 2 on */
|
|
{ 0, 3, 2, 1, 0, -1, -2, -3}, /* EM 3 on */
|
|
{ 0, -1, 0, 1, 0, -1, 0, 1}, /* EMs 1 & 3 on */
|
|
{ 3, 2, 1, 0, -1, -2, -3, 0}, /* EMs 2 & 3 on */
|
|
{ 2, 1, 0, -1, -2, -3, 0, 3}, /* EMs 1, 2 & 3 on */
|
|
{-2, -3, 0, 3, 2, 1, 0, -1}, /* EM 4 on */
|
|
{-1, -2, -3, 0, 3, 2, 1, 0}, /* EMs 1 & 4 on */
|
|
{ 0, 1, 0, -1, 0, 1, 0, -1}, /* EMs 2 & 4 */
|
|
{ 0, -1, -2, -3, 0, 3, 2, 1}, /* EMs 1, 2 & 4 on */
|
|
{-3, 0, 3, 2, 1, 0, -1, -2}, /* EMs 3 & 4 on */
|
|
{-2, -3, 0, 3, 2, 1, 0, -1}, /* EMs 1, 3 & 4 on */
|
|
{ 0, 3, 2, 1, 0, -1, -2, -3}, /* EMs 2, 3 & 4 on */
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0}
|
|
}; /* all electromagnets on */
|
|
|
|
static Boolean track_buffer_valid;
|
|
static Int8 write_mode;
|
|
static UInt8 *nibble_buf;
|
|
static UInt16 track_buffer_pos;
|
|
static UInt16 track_buffer_size;
|
|
static UInt8 write_protect;
|
|
static UInt8 address_latch, data_latch;
|
|
static Boolean motor_state;
|
|
static UInt8 drive_no = 0;
|
|
struct stateDisk_t
|
|
{
|
|
char diskname[32];
|
|
LocalID diskdb;
|
|
DmOpenRef diskimage;
|
|
MemHandle track_buffer_handle;
|
|
Int16 physical_track_no;
|
|
Int16 stepper_status;
|
|
Boolean track_buffer_valid;
|
|
Boolean track_buffer_raw;
|
|
Boolean track_buffer_dirty;
|
|
Int8 track_count;
|
|
Int8 track_buffer_track;
|
|
Int8 write_mode;
|
|
Int16 last_track;
|
|
UInt8 *nibble_buf;
|
|
UInt16 track_buffer_pos;
|
|
UInt16 track_buffer_size;
|
|
UInt8 write_protect;
|
|
UInt8 logical_track;
|
|
UInt8 address_latch, data_latch;
|
|
} stateDisk[2] = {0};
|
|
UInt32 DskIoICountDiff = 0;
|
|
UInt32 LastDskIoICount = 0;
|
|
|
|
/*
|
|
* SLOT 6 IO read.
|
|
*/
|
|
RDMEM_CWRAP(SLT6, address)
|
|
{
|
|
return (BootROM[address & 0xFF]);
|
|
}
|
|
void setCurrentDisk(int drive)
|
|
{
|
|
if (drive != drive_no)
|
|
{
|
|
if (drive_no)
|
|
showDriveState(drive_no, false);
|
|
drive_no = drive;
|
|
showDiskName(drive_no, stateDisk[drive_no - 1].diskname);
|
|
showDriveState(drive_no, motor_state);
|
|
}
|
|
drive--;
|
|
track_buffer_valid = stateDisk[drive].track_buffer_valid;
|
|
write_mode = stateDisk[drive].write_mode;
|
|
nibble_buf = stateDisk[drive].nibble_buf;
|
|
track_buffer_pos = stateDisk[drive].track_buffer_pos;
|
|
track_buffer_size = stateDisk[drive].track_buffer_size;
|
|
write_protect = stateDisk[drive].write_protect;
|
|
address_latch = stateDisk[drive].address_latch;
|
|
data_latch = stateDisk[drive].data_latch;
|
|
}
|
|
int getCurrentDrive(void)
|
|
{
|
|
return (drive_no | (motor_state ? 0x80 : 0));
|
|
}
|
|
void updateCurrentDisk(int drive)
|
|
{
|
|
if (drive--)
|
|
{
|
|
stateDisk[drive].track_buffer_pos = track_buffer_pos;
|
|
stateDisk[drive].address_latch = address_latch;
|
|
stateDisk[drive].data_latch = data_latch;
|
|
}
|
|
}
|
|
void load_track_buffer(int drive)
|
|
{
|
|
UInt8 *values;
|
|
|
|
if (stateDisk[--drive].diskdb)
|
|
{
|
|
stateDisk[drive].logical_track = (stateDisk[drive].physical_track_no + 1) >> 2;
|
|
if (stateDisk[drive].logical_track >= stateDisk[drive].track_count)
|
|
stateDisk[drive].logical_track = stateDisk[drive].track_count - 1;
|
|
if ((stateDisk[drive].logical_track != stateDisk[drive].last_track) || !stateDisk[drive].track_buffer_valid)
|
|
{
|
|
if (stateDisk[drive].track_buffer_dirty && !stateDisk[drive].track_buffer_raw)
|
|
{
|
|
stateDisk[drive].track_buffer_handle = DmGetRecord(stateDisk[drive].diskimage, stateDisk[drive].last_track);
|
|
values = (UInt8 *) MemHandleLock(stateDisk[drive].track_buffer_handle);
|
|
NibblesToSectors(stateDisk[drive].nibble_buf, values, 254, stateDisk[drive].last_track);
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
DmReleaseRecord(stateDisk[drive].diskimage, stateDisk[drive].last_track, true);
|
|
}
|
|
else if (stateDisk[drive].track_buffer_handle)
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
stateDisk[drive].track_buffer_handle = DmQueryRecord(stateDisk[drive].diskimage, stateDisk[drive].logical_track);
|
|
values = (UInt8 *) MemHandleLock(stateDisk[drive].track_buffer_handle);
|
|
if (stateDisk[drive].track_buffer_raw)
|
|
stateDisk[drive].nibble_buf = values;
|
|
else
|
|
{
|
|
SectorsToNibbles(values, stateDisk[drive].nibble_buf, 254, stateDisk[drive].logical_track);
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
stateDisk[drive].track_buffer_handle = 0;
|
|
}
|
|
stateDisk[drive].last_track = stateDisk[drive].logical_track;
|
|
stateDisk[drive].track_buffer_track = stateDisk[drive].physical_track_no;
|
|
stateDisk[drive].track_buffer_dirty = false;
|
|
stateDisk[drive].track_buffer_valid = true;
|
|
stateDisk[drive].track_buffer_pos = 0;
|
|
if (drive_no == (drive + 1))
|
|
setCurrentDisk(drive_no);
|
|
}
|
|
}
|
|
}
|
|
void write_nibble(UInt8 data)
|
|
{
|
|
if (!track_buffer_valid)
|
|
load_track_buffer(drive_no);
|
|
else if (DskIoICountDiff > 100)
|
|
{
|
|
if (DskIoICountDiff < 65536)
|
|
{
|
|
/*
|
|
* Advance position based on instruction count.
|
|
*/
|
|
track_buffer_pos += (DskIoICountDiff >> 12) * (track_buffer_size >> 4); /* divide by ~4096 instructions per sector */
|
|
if (track_buffer_pos >= track_buffer_size)
|
|
track_buffer_pos -= track_buffer_size;
|
|
}
|
|
if (track_buffer_size == RAW_TRACK_BYTES)
|
|
{
|
|
/*
|
|
* Round position up to a sector boundary.
|
|
*/
|
|
if (track_buffer_pos > RAW_SECTOR_BYTES*15)
|
|
track_buffer_pos = 0;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*14)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*15;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*13)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*14;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*12)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*13;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*11)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*12;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*10)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*11;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*9)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*10;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*8)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*9;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*7)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*8;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*6)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*7;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*5)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*6;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*4)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*5;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*3)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*4;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*2)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*3;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*1)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*2;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*0)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*1;
|
|
}
|
|
}
|
|
nibble_buf[track_buffer_pos] = data;
|
|
if (++track_buffer_pos >= track_buffer_size)
|
|
track_buffer_pos = 0;
|
|
stateDisk[drive_no - 1].track_buffer_dirty = true;
|
|
}
|
|
UInt8 read_nibble()
|
|
{
|
|
UInt8 data;
|
|
|
|
if (!track_buffer_valid)
|
|
load_track_buffer(drive_no);
|
|
else if (DskIoICountDiff > 100)
|
|
{
|
|
if (DskIoICountDiff < 65536)
|
|
{
|
|
/*
|
|
* Advance position based on instruction count.
|
|
*/
|
|
track_buffer_pos += (DskIoICountDiff >> 12) * (track_buffer_size >> 4); /* divide by ~4096 instructions per sector */
|
|
if (track_buffer_pos >= track_buffer_size)
|
|
track_buffer_pos -= track_buffer_size;
|
|
}
|
|
if (track_buffer_size == RAW_TRACK_BYTES)
|
|
{
|
|
/*
|
|
* Round position up to a sector boundary.
|
|
*/
|
|
if (track_buffer_pos > RAW_SECTOR_BYTES*15)
|
|
track_buffer_pos = 0;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*14)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*15;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*13)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*14;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*12)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*13;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*11)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*12;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*10)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*11;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*9)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*10;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*8)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*9;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*7)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*8;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*6)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*7;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*5)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*6;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*4)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*5;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*3)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*4;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*2)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*3;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*1)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*2;
|
|
else if (track_buffer_pos > RAW_SECTOR_BYTES*0)
|
|
track_buffer_pos = RAW_SECTOR_BYTES*1;
|
|
}
|
|
}
|
|
data = nibble_buf[track_buffer_pos];
|
|
if (++track_buffer_pos >= track_buffer_size)
|
|
track_buffer_pos = 0;
|
|
return (data);
|
|
}
|
|
void toggleStepper(int drive, UInt8 magnet, UInt8 on)
|
|
{
|
|
if (drive--)
|
|
{
|
|
if (on)
|
|
stateDisk[drive].stepper_status |= (1 << magnet);
|
|
else
|
|
stateDisk[drive].stepper_status &= ~(1 << magnet);
|
|
if (motor_state)
|
|
{
|
|
stateDisk[drive].physical_track_no += stepper_movement_table[stateDisk[drive].stepper_status][stateDisk[drive].physical_track_no & 0x07];
|
|
if (stateDisk[drive].physical_track_no < 0)
|
|
stateDisk[drive].physical_track_no = 0;
|
|
else if (stateDisk[drive].physical_track_no >= MAX_PHYSICAL_TRACK_NO)
|
|
stateDisk[drive].physical_track_no = MAX_PHYSICAL_TRACK_NO - 1;
|
|
stateDisk[drive].track_buffer_valid = track_buffer_valid = false;
|
|
}
|
|
}
|
|
}
|
|
void toggleMotor(int drive, Boolean on_off)
|
|
{
|
|
if (drive--)
|
|
{
|
|
if (motor_state != on_off)
|
|
{
|
|
motor_state = on_off;
|
|
showDriveState(drive_no, motor_state);
|
|
}
|
|
}
|
|
}
|
|
void positionDisk(int drive, UInt16 new_track, UInt16 new_position)
|
|
{
|
|
drive--;
|
|
stateDisk[drive].physical_track_no = new_track << 2;
|
|
if (stateDisk[drive].physical_track_no >= MAX_PHYSICAL_TRACK_NO)
|
|
stateDisk[drive].physical_track_no = MAX_PHYSICAL_TRACK_NO - 1;
|
|
load_track_buffer(drive + 1);
|
|
stateDisk[drive].track_buffer_pos = new_position;
|
|
if (stateDisk[drive].track_buffer_pos >= stateDisk[drive].track_buffer_size)
|
|
stateDisk[drive].track_buffer_pos = 0;
|
|
if (drive_no == (drive + 1))
|
|
setCurrentDisk(drive_no);
|
|
}
|
|
void queryDisk(int drive, UInt16 *track, UInt16 *pos)
|
|
{
|
|
if (drive-- == drive_no)
|
|
updateCurrentDisk(drive_no);
|
|
*track = stateDisk[drive].physical_track_no >> 2;
|
|
*pos = stateDisk[drive].track_buffer_pos;
|
|
}
|
|
int mountDisk(int drive, char *file, Boolean writeable)
|
|
{
|
|
UInt16 attrs;
|
|
UInt32 type, numrecs, ttlbytes, recbytes;
|
|
umountDisk(drive--);
|
|
stateDisk[drive].track_buffer_pos = 0;
|
|
stateDisk[drive].track_buffer_dirty = false;
|
|
stateDisk[drive].diskdb = file ? DmFindDatabase(0, file) : 0;
|
|
if (stateDisk[drive].diskdb)
|
|
{
|
|
StrCopy(stateDisk[drive].diskname, file);
|
|
stateDisk[drive].diskimage = DmOpenDatabase(0, stateDisk[drive].diskdb, 0);
|
|
DmDatabaseInfo(0, stateDisk[drive].diskdb, NULL, &attrs, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type, NULL);
|
|
stateDisk[drive].track_buffer_raw = (type == (UInt32)'RDSK') ? true : false;
|
|
stateDisk[drive].write_protect = attrs & dmHdrAttrReadOnly ? true : !writeable;
|
|
DmDatabaseSize(0, stateDisk[drive].diskdb, &numrecs, &ttlbytes, &recbytes);
|
|
stateDisk[drive].track_count = numrecs;
|
|
if (stateDisk[drive].track_buffer_raw)
|
|
{
|
|
stateDisk[drive].track_buffer_size = recbytes / stateDisk[drive].track_count;
|
|
}
|
|
else
|
|
{
|
|
stateDisk[drive].nibble_buf = MemPtrNew(RAW_TRACK_BYTES);
|
|
stateDisk[drive].track_buffer_size = RAW_TRACK_BYTES;
|
|
init_GCR_table();
|
|
}
|
|
stateDisk[drive].track_buffer_handle = 0;
|
|
stateDisk[drive].track_buffer_valid = false;
|
|
}
|
|
else
|
|
{
|
|
stateDisk[drive].diskname[0] = '\0';
|
|
stateDisk[drive].diskimage = 0;
|
|
stateDisk[drive].nibble_buf = MemPtrNew(RAW_TRACK_BYTES);
|
|
MemSet(stateDisk[drive].nibble_buf, RAW_TRACK_BYTES, 0xFF);
|
|
stateDisk[drive].track_buffer_handle = 0;
|
|
stateDisk[drive].track_buffer_size = RAW_TRACK_BYTES;
|
|
stateDisk[drive].track_buffer_valid = true;
|
|
stateDisk[drive].write_protect = true;
|
|
}
|
|
if (drive_no == (drive + 1))
|
|
{
|
|
drive_no = 0;
|
|
setCurrentDisk(drive + 1);
|
|
}
|
|
else
|
|
{
|
|
showDiskName(drive + 1, stateDisk[drive].diskname);
|
|
showDriveState(drive + 1, false);
|
|
}
|
|
return (stateDisk[drive].diskdb != 0);
|
|
}
|
|
void umountDisk(int drive)
|
|
{
|
|
UInt8 *values;
|
|
|
|
if (drive-- == drive_no)
|
|
updateCurrentDisk(drive_no);
|
|
if (stateDisk[drive].diskdb)
|
|
{
|
|
if (!stateDisk[drive].track_buffer_raw)
|
|
{
|
|
if (stateDisk[drive].track_buffer_dirty)
|
|
{
|
|
stateDisk[drive].track_buffer_handle = DmGetRecord(stateDisk[drive].diskimage, stateDisk[drive].last_track);
|
|
values = (UInt8 *) MemHandleLock(stateDisk[drive].track_buffer_handle);
|
|
NibblesToSectors(stateDisk[drive].nibble_buf, values, 254, stateDisk[drive].last_track);
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
DmReleaseRecord(stateDisk[drive].diskimage, stateDisk[drive].last_track, true);
|
|
}
|
|
MemPtrFree(stateDisk[drive].nibble_buf);
|
|
}
|
|
else if (stateDisk[drive].track_buffer_handle)
|
|
{
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
}
|
|
DmCloseDatabase(stateDisk[drive].diskimage);
|
|
stateDisk[drive].diskdb = 0;
|
|
}
|
|
else if (stateDisk[drive].nibble_buf)
|
|
MemPtrFree(stateDisk[drive].nibble_buf);
|
|
stateDisk[drive].nibble_buf = NULL;
|
|
stateDisk[drive].track_buffer_handle = 0;
|
|
stateDisk[drive].track_buffer_valid = false;
|
|
toggleMotor(drive + 1, 0);
|
|
}
|
|
void resetDisks(void)
|
|
{
|
|
#if 0
|
|
UInt8 drive, *values;
|
|
|
|
updateCurrentDisk(drive_no);
|
|
for (drive = 0; drive < 2; drive++)
|
|
{
|
|
if (stateDisk[drive].diskdb)
|
|
{
|
|
if (!stateDisk[drive].track_buffer_raw)
|
|
{
|
|
if (stateDisk[drive].track_buffer_dirty)
|
|
{
|
|
stateDisk[drive].track_buffer_handle = DmGetRecord(stateDisk[drive].diskimage, stateDisk[drive].last_track);
|
|
values = (UInt8 *) MemHandleLock(stateDisk[drive].track_buffer_handle);
|
|
NibblesToSectors(stateDisk[drive].nibble_buf, values, 254, stateDisk[drive].last_track);
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
DmReleaseRecord(stateDisk[drive].diskimage, stateDisk[drive].last_track, true);
|
|
}
|
|
}
|
|
else if (stateDisk[drive].track_buffer_handle)
|
|
{
|
|
MemHandleUnlock(stateDisk[drive].track_buffer_handle);
|
|
}
|
|
}
|
|
toggleMotor(drive + 1, 0);
|
|
stateDisk[drive].physical_track_no = 0;
|
|
stateDisk[drive].track_buffer_handle = 0;
|
|
stateDisk[drive].track_buffer_pos = 0;
|
|
stateDisk[drive].track_buffer_valid = false;
|
|
stateDisk[drive].track_buffer_dirty = false;
|
|
}
|
|
#else
|
|
umountDisk(1);
|
|
umountDisk(2);
|
|
mountDisk(1, stateDisk[0].diskname, stateDisk[0].write_protect);
|
|
mountDisk(2, stateDisk[1].diskname, stateDisk[1].write_protect);
|
|
#endif
|
|
drive_no = 0;
|
|
setCurrentDisk(1);
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* *
|
|
* I/O page access *
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
/*
|
|
* I/O Read.
|
|
*/
|
|
UInt8 RIOM(UInt16);
|
|
UInt8 cwrap_RIOM(UInt16);
|
|
UInt8 awrap_RIOM(UInt16 a)
|
|
{
|
|
asm(".global RIOM");
|
|
asm("RIOM:");
|
|
/*
|
|
* Quick check for Disk read byte.
|
|
*/
|
|
asm("cmp.b #0xEC, %d6");
|
|
asm("bne.b L1");
|
|
/*
|
|
* Check for buffer valid.
|
|
*/
|
|
asm("tst.b track_buffer_valid@END.w(%a5)");
|
|
asm("beq.b L1");
|
|
/*
|
|
* Update access time.
|
|
*/
|
|
asm("move.l AppleInstrInc@END.w(%a5), %d0");
|
|
asm("sub.l %d1, %d0");
|
|
asm("add.l AppleInstrCount@END.w(%a5), %d0");
|
|
asm("move.l %d0, %a6");
|
|
asm("sub.l LastDskIoICount@END.w(%a5), %d0");
|
|
asm("move.l %d0, DskIoICountDiff@END.w(%a5)");
|
|
asm("move.l %a6, LastDskIoICount@END.w(%a5)");
|
|
/*
|
|
* Check for back-to-back read.
|
|
*/
|
|
asm("cmp.l #100, %d0"); // This is the instruction count that determines if it is back-to-back
|
|
asm("bgt.b L1");
|
|
/*
|
|
* Check for read or write.
|
|
*/
|
|
asm("tst.b write_mode@END.w(%a5)");
|
|
asm("bne.b L1");
|
|
/*
|
|
* Read next byte in buffer and advance position.
|
|
*/
|
|
asm("move.w track_buffer_pos@END.w(%a5), %a6");
|
|
asm("move.l nibble_buf@END.w(%a5), %d0");
|
|
asm("move.b (%a6,%d0.l), %d0");
|
|
asm("exg %a6, %d0");
|
|
asm("addq.w #1, %d0");
|
|
asm("cmp.w track_buffer_size@END.w(%a5), %d0");
|
|
asm("blt.b L2");
|
|
asm("clr.w %d0");
|
|
asm("L2:");
|
|
asm("move.w %d0, track_buffer_pos@END.w(%a5)");
|
|
asm("exg %a6, %d0");
|
|
asm("and.l #0xFF, %d0");
|
|
asm("move.b #0x0C, address_latch@END.w(%a5)"); // Update address latch
|
|
asm("rts");
|
|
/*
|
|
* Call C-wrapped function.
|
|
*/
|
|
asm("L1:");
|
|
asm("movem.l #0x1FFE, (state6502+4)@END.w(%a5)");
|
|
asm("move.w %d6, -(%sp)");
|
|
asm("bsr.w cwrap_RIOM");
|
|
asm("addq.l #2, %sp");
|
|
asm("and.l #0xFF,%d0");
|
|
asm("movem.l (state6502+4)@END.w(%a5), #0x1FFE");
|
|
asm("rts");
|
|
}
|
|
UInt8 cwrap_RIOM(UInt16 address)
|
|
{
|
|
UInt8 data = 0;
|
|
|
|
switch ((UInt8)address)
|
|
{
|
|
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
|
|
case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F:
|
|
data = kbdIOU;
|
|
break;
|
|
case 0x10:
|
|
data = kbdIOU & 0x7F;
|
|
if (kbdCount)
|
|
{
|
|
kbdHead = (kbdHead + 1) & KBD_BUF_MASK;
|
|
if (--kbdCount)
|
|
kbdIOU = kbdBuffer[kbdHead];
|
|
else
|
|
kbdIOU &= 0x7F;
|
|
}
|
|
else
|
|
kbdIOU &= 0x7F;
|
|
break;
|
|
case 0x11:
|
|
if (SS_ISSET(memIOU, SS_LCBNK2))
|
|
data = 0x80;
|
|
break;
|
|
case 0x12:
|
|
data = 0x80;
|
|
break;
|
|
case 0x13:
|
|
if (SS_ISSET(memIOU, SS_LCRAMMAP))
|
|
data = 0x80;
|
|
break;
|
|
case 0x14:
|
|
if (SS_ISSET(memIOU, SS_LCRAMWRT))
|
|
data = 0x80;
|
|
break;
|
|
case 0x15:
|
|
if (SS_ISSET(memIOU, SS_SLOTCXROM))
|
|
data = 0x80;
|
|
break;
|
|
case 0x16:
|
|
break;
|
|
case 0x17:
|
|
if (SS_ISSET(memIOU, SS_SLOTC3ROM))
|
|
data = 0x80;
|
|
break;
|
|
case 0x18:
|
|
if (SS_ISSET(vidIOU, SS_80STORE))
|
|
data = 0x80;
|
|
break;
|
|
case 0x19:
|
|
if (SS_ISSET(vidIOU, SS_VBLBAR))
|
|
data = 0x80;
|
|
break;
|
|
case 0x1A:
|
|
if (SS_ISSET(vidIOU, SS_TEXT))
|
|
data = 0x80;
|
|
break;
|
|
case 0x1B:
|
|
if (SS_ISSET(vidIOU, SS_MIXED))
|
|
data = 0x80;
|
|
break;
|
|
case 0x1C:
|
|
if (SS_ISSET(vidIOU, SS_PAGE2))
|
|
data = 0x80;
|
|
break;
|
|
case 0x1D:
|
|
if (SS_ISSET(vidIOU, SS_HIRES))
|
|
data = 0x80;
|
|
break;
|
|
case 0x1E:
|
|
break;
|
|
case 0x1F:
|
|
if (SS_ISSET(vidIOU, SS_80VID))
|
|
data = 0x80;
|
|
break;
|
|
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
|
|
case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F:
|
|
break;
|
|
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
|
|
case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F:
|
|
spkrIOU++;
|
|
break;
|
|
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
|
|
case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F:
|
|
break;
|
|
case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
|
|
case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F:
|
|
toggleVideoSoftSwitch(IO_OFFSET(address));
|
|
break;
|
|
case 0x60:
|
|
break;
|
|
case 0x61:
|
|
if (btnIOU[0])
|
|
data = 0x80;
|
|
break;
|
|
case 0x62:
|
|
if (btnIOU[1])
|
|
data = 0x80;
|
|
break;
|
|
case 0x63:
|
|
if (btnIOU[2])
|
|
data = 0x80;
|
|
break;
|
|
case 0x64:
|
|
if (pdlIOU[0])
|
|
{
|
|
pdlIOU[0]--;
|
|
data = 0x80;
|
|
}
|
|
break;
|
|
case 0x65:
|
|
if (pdlIOU[1])
|
|
{
|
|
pdlIOU[1]--;
|
|
data = 0x80;
|
|
}
|
|
break;
|
|
case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B:
|
|
break;
|
|
case 0x6C:
|
|
if (pdlIOU[0])
|
|
{
|
|
pdlIOU[0]--;
|
|
data = 0x80;
|
|
}
|
|
break;
|
|
case 0x6D:
|
|
if (pdlIOU[1])
|
|
{
|
|
pdlIOU[1]--;
|
|
data = 0x80;
|
|
}
|
|
break;
|
|
case 0x6E:
|
|
case 0x6F:
|
|
break;
|
|
case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
|
|
case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
|
|
pdlIOU[0] = pdlResetIOU[0];
|
|
pdlIOU[1] = pdlResetIOU[1];
|
|
break;
|
|
case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
|
|
case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
|
|
toggleLanguageCard(IO_OFFSET(address));
|
|
break;
|
|
case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
|
|
case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
|
|
break;
|
|
case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7:
|
|
case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF:
|
|
break;
|
|
case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
|
|
case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF:
|
|
break;
|
|
case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7:
|
|
case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
|
|
break;
|
|
case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7:
|
|
case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF:
|
|
break;
|
|
case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7:
|
|
toggleStepper(drive_no, (address >> 1) & 0x07, address & 1);
|
|
break;
|
|
case 0xE8:
|
|
toggleMotor(drive_no, 0);
|
|
break;
|
|
case 0xE9:
|
|
toggleMotor(drive_no, 1);
|
|
break;
|
|
case 0xEA:
|
|
if (drive_no != 1)
|
|
{
|
|
updateCurrentDisk(drive_no);
|
|
setCurrentDisk(1);
|
|
}
|
|
break;
|
|
case 0xEB:
|
|
if (drive_no != 2)
|
|
{
|
|
updateCurrentDisk(drive_no);
|
|
setCurrentDisk(2);
|
|
}
|
|
break;
|
|
case 0xEC:
|
|
if (write_mode)
|
|
write_nibble(data_latch);
|
|
else
|
|
data = read_nibble();
|
|
address_latch = 0x0C;
|
|
break;
|
|
case 0xED:
|
|
address_latch = 0x0D;
|
|
break;
|
|
case 0xEE:
|
|
if (address_latch == 0x0D)
|
|
/* check write protect */
|
|
data = write_protect ? 0xFF: 0x00;
|
|
address_latch = 0x0E;
|
|
write_mode = 0;
|
|
break;
|
|
case 0xEF:
|
|
address_latch = 0x0F;
|
|
write_mode = 1;
|
|
break;
|
|
case 0xF0:
|
|
case 0xF1:
|
|
case 0xF2:
|
|
case 0xF3:
|
|
case 0xF4:
|
|
case 0xF5:
|
|
case 0xF6:
|
|
case 0xF7:
|
|
case 0xF8:
|
|
case 0xF9:
|
|
case 0xFA:
|
|
case 0xFB:
|
|
case 0xFC:
|
|
case 0xFD:
|
|
case 0xFE:
|
|
case 0xFF:
|
|
break;
|
|
}
|
|
return (data);
|
|
}
|
|
/*
|
|
* I/O Write.
|
|
*/
|
|
void WIOM(UInt16, UInt8);
|
|
void cwrap_WIOM(UInt16, UInt8);
|
|
void awrap_wIOM(UInt16 a, UInt8 d)
|
|
{
|
|
asm(".global WIOM");
|
|
asm("WIOM:");
|
|
/*
|
|
* Quick check for Disk read byte.
|
|
*/
|
|
asm("cmp.b #0xEC, %d6");
|
|
asm("bne.b L3");
|
|
/*
|
|
* Update IO access time.
|
|
*/
|
|
asm("move.l %d0, %a6");
|
|
asm("move.l AppleInstrInc@END.w(%a5), %d0");
|
|
asm("sub.l %d1, %d0");
|
|
asm("add.l AppleInstrCount@END.w(%a5), %d0");
|
|
asm("sub.l LastDskIoICount@END.w(%a5), %d0");
|
|
asm("move.l %d0, DskIoICountDiff@END.w(%a5)");
|
|
asm("add.l %d0, LastDskIoICount@END.w(%a5)");
|
|
asm("move.l %a6, %d0");
|
|
/*
|
|
* Call C-wrapped function.
|
|
*/
|
|
asm("L3:");
|
|
asm("movem.l #0x1FFF, state6502@END.w(%a5)");
|
|
asm("move.b %d0, -(%sp)");
|
|
asm("move.w %d6, -(%sp)");
|
|
asm("bsr.w cwrap_WIOM");
|
|
asm("addq.l #4, %sp");
|
|
asm("movem.l state6502@END.w(%a5), #0x1FFF");
|
|
asm("rts");
|
|
}
|
|
void cwrap_WIOM(UInt16 address, UInt8 data)
|
|
{
|
|
AppleMemory[address] = data;
|
|
switch ((UInt8)address)
|
|
{
|
|
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
|
|
case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F:
|
|
writeMemorySoftSwitch(IO_OFFSET(address));
|
|
break;
|
|
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
|
|
case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F:
|
|
if (kbdCount)
|
|
{
|
|
kbdHead = (kbdHead + 1) & KBD_BUF_MASK;
|
|
if (--kbdCount)
|
|
kbdIOU = kbdBuffer[kbdHead];
|
|
else
|
|
kbdIOU &= 0x7F;
|
|
}
|
|
else
|
|
kbdIOU &= 0x7F;
|
|
break;
|
|
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
|
|
case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F:
|
|
break;
|
|
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
|
|
case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F:
|
|
spkrIOU++;
|
|
break;
|
|
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
|
|
case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F:
|
|
break;
|
|
case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
|
|
case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F:
|
|
toggleVideoSoftSwitch(IO_OFFSET(address));
|
|
break;
|
|
case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
|
|
case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F:
|
|
break;
|
|
case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
|
|
case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
|
|
pdlIOU[0] = pdlResetIOU[0];
|
|
pdlIOU[1] = pdlResetIOU[1];
|
|
break;
|
|
case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
|
|
case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
|
|
toggleLanguageCard(IO_OFFSET(address));
|
|
break;
|
|
case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
|
|
case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
|
|
break;
|
|
case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7:
|
|
case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF:
|
|
break;
|
|
case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
|
|
case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF:
|
|
break;
|
|
case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7:
|
|
case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
|
|
break;
|
|
case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7:
|
|
case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF:
|
|
break;
|
|
case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7:
|
|
toggleStepper(drive_no, (address >> 1) & 0x07, address & 1);
|
|
break;
|
|
case 0xE8:
|
|
toggleMotor(drive_no, 0);
|
|
break;
|
|
case 0xE9:
|
|
toggleMotor(drive_no, 1);
|
|
break;
|
|
case 0xEA:
|
|
if (drive_no != 1)
|
|
{
|
|
updateCurrentDisk(drive_no);
|
|
setCurrentDisk(1);
|
|
}
|
|
break;
|
|
case 0xEB:
|
|
if (drive_no != 2)
|
|
{
|
|
updateCurrentDisk(drive_no);
|
|
setCurrentDisk(2);
|
|
}
|
|
break;
|
|
case 0xEC:
|
|
if (write_mode)
|
|
write_nibble(data_latch);
|
|
else
|
|
read_nibble();
|
|
address_latch = 0x0C;
|
|
break;
|
|
case 0xED:
|
|
data_latch = data;
|
|
address_latch = 0x0D;
|
|
break;
|
|
case 0xEE:
|
|
address_latch = 0x0E;
|
|
write_mode = 0;
|
|
break;
|
|
case 0xEF:
|
|
data_latch = data;
|
|
address_latch = 0x0F;
|
|
write_mode = 1;
|
|
break;
|
|
case 0xF0: case 0xF1: case 0xF2: case 0xF3: case 0xF4: case 0xF5: case 0xF6: case 0xF7:
|
|
case 0xF8: case 0xF9: case 0xFA: case 0xFB: case 0xFC: case 0xFD: case 0xFE: case 0xFF:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* *
|
|
* Sector <=> Nibble Encoding *
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
static UInt8 GCR_encoding_table[64] = {
|
|
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
|
|
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
|
|
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
|
|
0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
|
|
0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
|
|
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
|
|
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
|
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
|
|
|
|
static UInt8 GCR_decoding_table[256];
|
|
static UInt8 Swap_Bit[4] = { 0, 2, 1, 3 }; /* swap lower 2 bits */
|
|
static UInt8 GCR_buffer[256];
|
|
static UInt8 GCR_buffer2[86];
|
|
|
|
static UInt16 Position=0;
|
|
static UInt8 *Track_Nibble;
|
|
|
|
/* physical sector no. to DOS 3.3 logical sector no. table */
|
|
static UInt8 Logical_Sector[16] = {
|
|
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
|
|
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
|
|
|
|
static UInt8 Physical_Sector[16];
|
|
|
|
|
|
#define FM_ENCODE(x) gcrWriteNibble( ((x) >> 1) | 0xAA );\
|
|
gcrWriteNibble( (x) | 0xAA )
|
|
|
|
static void init_GCR_table(void)
|
|
{
|
|
static UInt8 initialized = 0;
|
|
int i;
|
|
|
|
if ( !initialized ) {
|
|
for( i = 0; i < 64; i++ )
|
|
GCR_decoding_table[GCR_encoding_table[i]] = i;
|
|
for( i = 0; i < 16; i++ )
|
|
Physical_Sector[Logical_Sector[i]] = i;
|
|
initialized = 1;
|
|
}
|
|
}
|
|
|
|
static UInt8 gcr_read_nibble(void)
|
|
{
|
|
UInt8 data;
|
|
|
|
data = Track_Nibble[Position++];
|
|
if ( Position >= RAW_TRACK_BYTES )
|
|
Position = 0;
|
|
return data;
|
|
}
|
|
|
|
static void gcr_write_nibble( UInt8 data )
|
|
{
|
|
Track_Nibble[Position++] = data;
|
|
if ( Position >= RAW_TRACK_BYTES )
|
|
Position = 0;
|
|
}
|
|
|
|
static void decode62( UInt8 *page )
|
|
{
|
|
int i, j;
|
|
|
|
/* get 6 bits from GCR_buffer & 2 from GCR_buffer2 */
|
|
for( i = 0, j = 86; i < 256; i++ ) {
|
|
if ( --j < 0 ) j = 85;
|
|
page[i] = (GCR_buffer[i] << 2) | Swap_Bit[GCR_buffer2[j] & 0x03];
|
|
GCR_buffer2[j] >>= 2;
|
|
}
|
|
}
|
|
|
|
static void encode62( UInt8 *page )
|
|
{
|
|
int i, j;
|
|
|
|
/* 86 * 3 = 258, so the first two byte are encoded twice */
|
|
GCR_buffer2[0] = Swap_Bit[page[1]&0x03];
|
|
GCR_buffer2[1] = Swap_Bit[page[0]&0x03];
|
|
|
|
/* save higher 6 bits in GCR_buffer and lower 2 bits in GCR_buffer2 */
|
|
for( i = 255, j = 2; i >= 0; i--, j = j == 85? 0: j + 1 ) {
|
|
GCR_buffer2[j] = (GCR_buffer2[j] << 2) | Swap_Bit[page[i]&0x03];
|
|
GCR_buffer[i] = page[i] >> 2;
|
|
}
|
|
|
|
/* clear off higher 2 bits of GCR_buffer2 set in the last call */
|
|
for( i = 0; i < 86; i++ )
|
|
GCR_buffer2[i] &= 0x3f;
|
|
}
|
|
|
|
/*
|
|
* write an FM encoded value, used in writing address fields
|
|
*/
|
|
static void FM_encode( UInt8 data )
|
|
{
|
|
gcr_write_nibble( (data >> 1) | 0xAA );
|
|
gcr_write_nibble( data | 0xAA );
|
|
}
|
|
|
|
/*
|
|
* return an FM encoded value, used in reading address fields
|
|
*/
|
|
static UInt8 FM_decode(void)
|
|
{
|
|
int tmp;
|
|
|
|
/* C does not specify order of operand evaluation, don't
|
|
* merge the following two expression into one
|
|
*/
|
|
tmp = (gcr_read_nibble() << 1) | 0x01;
|
|
return gcr_read_nibble() & tmp;
|
|
}
|
|
|
|
static void write_sync( int length )
|
|
{
|
|
while( length-- )
|
|
gcr_write_nibble( 0xFF );
|
|
}
|
|
|
|
/*
|
|
* read_address_field: try to read a address field in a track
|
|
* returns 1 if succeed, 0 otherwise
|
|
*/
|
|
static int read_address_field( int *volume, int *track, int *sector )
|
|
{
|
|
int max_try;
|
|
UInt8 nibble;
|
|
|
|
max_try = 100;
|
|
while( --max_try ) {
|
|
nibble = gcr_read_nibble();
|
|
check_D5:
|
|
if ( nibble != 0xD5 )
|
|
continue;
|
|
nibble = gcr_read_nibble();
|
|
if ( nibble != 0xAA )
|
|
goto check_D5;
|
|
nibble = gcr_read_nibble();
|
|
if ( nibble != 0x96 )
|
|
goto check_D5;
|
|
*volume = FM_decode();
|
|
*track = FM_decode();
|
|
*sector = FM_decode();
|
|
return ( *volume ^ *track ^ *sector ) == FM_decode() &&
|
|
gcr_read_nibble() == 0xDE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void write_address_field( int volume, int track, int sector )
|
|
{
|
|
/*
|
|
* write address mark
|
|
*/
|
|
gcr_write_nibble( 0xD5 );
|
|
gcr_write_nibble( 0xAA );
|
|
gcr_write_nibble( 0x96 );
|
|
|
|
/*
|
|
* write Volume, Track, Sector & Check-sum
|
|
*/
|
|
FM_encode( volume );
|
|
FM_encode( track );
|
|
FM_encode( sector );
|
|
FM_encode( volume ^ track ^ sector );
|
|
|
|
/*
|
|
* write epilogue
|
|
*/
|
|
gcr_write_nibble( 0xDE );
|
|
gcr_write_nibble( 0xAA );
|
|
gcr_write_nibble( 0xEB );
|
|
}
|
|
|
|
/*
|
|
* read_data_field: read_data_field into GCR_buffers, return 0 if fail
|
|
*/
|
|
static int read_data_field(void)
|
|
{
|
|
int i, max_try;
|
|
UInt8 nibble, checksum;
|
|
|
|
/*
|
|
* read data mark
|
|
*/
|
|
max_try = 32;
|
|
while( --max_try ) {
|
|
nibble = gcr_read_nibble();
|
|
check_D5:
|
|
if ( nibble != 0xD5 )
|
|
continue;
|
|
nibble = gcr_read_nibble();
|
|
if ( nibble != 0xAA )
|
|
goto check_D5;
|
|
nibble = gcr_read_nibble();
|
|
if ( nibble == 0xAD )
|
|
break;
|
|
}
|
|
if ( !max_try ) /* fails to get address mark */
|
|
return 0;
|
|
|
|
for( i = 0x55, checksum = 0; i >= 0; i-- ) {
|
|
checksum ^= GCR_decoding_table[gcr_read_nibble()];
|
|
GCR_buffer2[i] = checksum;
|
|
}
|
|
|
|
for( i = 0; i < 256; i++ ) {
|
|
checksum ^= GCR_decoding_table[gcr_read_nibble()];
|
|
GCR_buffer[i] = checksum;
|
|
}
|
|
|
|
/* verify sector checksum */
|
|
if ( checksum ^ GCR_decoding_table[gcr_read_nibble()] )
|
|
return 0;
|
|
|
|
/* check epilogue */
|
|
return gcr_read_nibble() == 0xDE && gcr_read_nibble() == 0xAA;
|
|
}
|
|
|
|
static void write_data_field(void)
|
|
{
|
|
int i;
|
|
UInt8 last, checksum;
|
|
|
|
/* write prologue */
|
|
gcr_write_nibble( 0xD5 );
|
|
gcr_write_nibble( 0xAA );
|
|
gcr_write_nibble( 0xAD );
|
|
|
|
/* write GCR encode data */
|
|
for( i = 0x55, last = 0; i >= 0; i-- ) {
|
|
checksum = last^ GCR_buffer2[i];
|
|
gcr_write_nibble( GCR_encoding_table[checksum] );
|
|
last = GCR_buffer2[i];
|
|
}
|
|
for( i = 0; i < 256; i++ ) {
|
|
checksum = last ^ GCR_buffer[i];
|
|
gcr_write_nibble( GCR_encoding_table[checksum] );
|
|
last = GCR_buffer[i];
|
|
}
|
|
|
|
/* write checksum and epilogue */
|
|
gcr_write_nibble( GCR_encoding_table[last] );
|
|
gcr_write_nibble( 0xDE );
|
|
gcr_write_nibble( 0xAA );
|
|
gcr_write_nibble( 0xEB );
|
|
}
|
|
|
|
void SectorsToNibbles( UInt8 *sectors, UInt8 *nibbles, int volume, int track )
|
|
{
|
|
int i;
|
|
|
|
Track_Nibble = nibbles;
|
|
Position = 0;
|
|
|
|
/*write_sync( 128 );*/
|
|
for( i = 0; i < 16; i ++ ) {
|
|
encode62( sectors + Logical_Sector[i] * 0x100 );
|
|
write_sync( 16 );
|
|
write_address_field( volume, track, i );
|
|
write_sync( 8 );
|
|
write_data_field();
|
|
}
|
|
}
|
|
|
|
int NibblesToSectors( UInt8 *nibbles, UInt8 *sectors, int volume, int track )
|
|
{
|
|
int i, scanned[16], max_try, sectors_read;
|
|
int vv, tt, ss; /* volume, track no. and sector no. */
|
|
|
|
Track_Nibble = nibbles;
|
|
Position = 0;
|
|
|
|
for( i = 0; i < 16; i++ )
|
|
scanned[i] = 0;
|
|
sectors_read = 0;
|
|
|
|
max_try = 200;
|
|
while( --max_try ) {
|
|
if ( !read_address_field( &vv, &tt, &ss ) )
|
|
continue;
|
|
|
|
if ( (volume && vv != volume ) || tt != track || ss < 0 || ss > 15 ){
|
|
//printf("phy sector %d address field invalid\n", ss );
|
|
continue; /* invalid values for vv, tt and ss, try again */
|
|
}
|
|
|
|
ss = Logical_Sector[ss];
|
|
if ( scanned[ss] ) /* sector has been read */
|
|
continue;
|
|
|
|
if ( read_data_field() ) {
|
|
decode62( sectors + ss * 0x100 );
|
|
scanned[ss] = 1; /* this sector's ok */
|
|
sectors_read++;
|
|
}
|
|
else {
|
|
//printf("fail reading data field of logical sector %d\n", ss );
|
|
}
|
|
}
|
|
|
|
/* if has failed to read any one sector, report error */
|
|
if ( sectors_read == 16 )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|