#include #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; }