]> Shamusworld >> Repos - apple2/blobdiff - src/mmu.cpp
Docs were missing GPLv3. Thanks to schampailler for the heads up. :-)
[apple2] / src / mmu.cpp
index dc5e57c68c4a68371c0ba78ffbd55651548e3788..326451091748a04c4aa44407b7751ee51a15832a 100644 (file)
@@ -9,7 +9,7 @@
 // WHO  WHEN        WHAT
 // ---  ----------  -----------------------------------------------------------
 // JLH  09/27/2013  Created this file
-
+//
 
 #include "mmu.h"
 #include "apple2.h"
@@ -19,7 +19,6 @@
 #include "sound.h"
 #include "video.h"
 
-
 // Debug defines
 //#define LC_DEBUG
 
@@ -82,7 +81,6 @@ uint8_t * lcBankMemoryW   = &ram[0xD000];     // $D000 - $DFFF (write)
 uint8_t * upperMemoryR    = &ram[0xE000];      // $E000 - $FFFF (read)
 uint8_t * upperMemoryW    = &ram[0xE000];      // $E000 - $FFFF (write)
 
-
 // Function prototypes
 uint8_t ReadNOP(uint16_t);
 void WriteNOP(uint16_t, uint8_t);
@@ -136,13 +134,13 @@ void SwitchHIRESW(uint16_t, uint8_t);
 uint8_t SwitchDHIRESR(uint16_t);
 void SwitchDHIRESW(uint16_t, uint8_t);
 void SwitchIOUDIS(uint16_t, uint8_t);
+uint8_t ReadCassetteIn(uint16_t);
 uint8_t ReadButton0(uint16_t);
 uint8_t ReadButton1(uint16_t);
 uint8_t ReadPaddle0(uint16_t);
 uint8_t ReadIOUDIS(uint16_t);
 uint8_t ReadDHIRES(uint16_t);
 
-
 // The main Apple //e memory map
 AddressMap memoryMap[] = {
        { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
@@ -190,6 +188,8 @@ AddressMap memoryMap[] = {
        { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
        { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
        { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
+       // $C060 is Cassette IN.  No idea what it reads with N/C
+       { 0xC060, 0xC060, AM_READ, 0, 0, ReadCassetteIn, 0 },
        { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
        { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
        { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
@@ -230,7 +230,6 @@ Read from $C800-$CFFF causes I/O STROBE to go low (and INTCXROM and INTC8ROM are
 
 */
 
-
 void SetupAddressMap(void)
 {
        for(uint32_t i=0; i<0x10000; i++)
@@ -318,7 +317,6 @@ void SetupAddressMap(void)
        SwitchLC();
 }
 
-
 //
 // Reset the MMU state after a power down event
 //
@@ -328,17 +326,22 @@ void ResetMMUPointers(void)
        {
                mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
                mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+               mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+               mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
        }
        else
        {
-               mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+// Shouldn't mainMemoryTextR depend on ramrd???  (I think it should...)
+               mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
                mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+               mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
+               mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
        }
 
        mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
-       mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
        mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
-       mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
+//     mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
+//     mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
 
 //     slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
 //     slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
@@ -353,7 +356,6 @@ WriteLog("ALTZP = %s\n", (altzp ? "ON" : "off"));
 #endif
 }
 
-
 //
 // Set up slot access
 //
@@ -415,7 +417,6 @@ So maybe...
 */
 }
 
-
 //
 // Built-in functions
 //
@@ -430,12 +431,10 @@ uint8_t ReadNOP(uint16_t)
        return 0xFF;
 }
 
-
 void WriteNOP(uint16_t, uint8_t)
 {
 }
 
-
 uint8_t ReadMemory(uint16_t address)
 {
 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
@@ -444,7 +443,6 @@ uint8_t ReadMemory(uint16_t address)
        return (*addrPtrRead[address])[addrOffset[address]];
 }
 
-
 void WriteMemory(uint16_t address, uint8_t byte)
 {
        // We can write protect memory this way, but it adds a branch to the mix.
@@ -456,7 +454,6 @@ void WriteMemory(uint16_t address, uint8_t byte)
        (*addrPtrWrite[address])[addrOffset[address]] = byte;
 }
 
-
 //
 // The main memory access functions used by V65C02
 //
@@ -490,7 +487,6 @@ if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
 #endif
 }
 
-
 void AppleWriteMem(uint16_t address, uint8_t byte)
 {
 #if 0
@@ -523,7 +519,6 @@ if (address == 0x000D)
        (*(funcMapWrite[address]))(address, byte);
 }
 
-
 //
 // Generic slot handlers.  These are set up here so that we can catch INTCXROM,
 // INTC8ROM & SLOTC3ROM here instead of having to catch them in each slot handler.
@@ -546,7 +541,6 @@ uint8_t SlotR(uint16_t address)
        return (*(slotHandlerR[slot]))(address & 0xFF);
 }
 
-
 void SlotW(uint16_t address, uint8_t byte)
 {
        if (intCXROM)
@@ -564,7 +558,6 @@ void SlotW(uint16_t address, uint8_t byte)
        (*(slotHandlerW[slot]))(address & 0xFF, byte);
 }
 
-
 //
 // Slot handling for 2K address space at $C800-$CFFF
 //
@@ -576,7 +569,6 @@ uint8_t Slot2KR(uint16_t address)
        return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
 }
 
-
 void Slot2KW(uint16_t address, uint8_t byte)
 {
        if (intCXROM || intC8ROM)
@@ -585,7 +577,6 @@ void Slot2KW(uint16_t address, uint8_t byte)
        (*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
 }
 
-
 //
 // Actual emulated I/O functions follow
 //
@@ -594,51 +585,54 @@ uint8_t ReadKeyboard(uint16_t /*addr*/)
        return lastKeyPressed | ((uint8_t)keyDown << 7);
 }
 
-
 void Switch80STORE(uint16_t address, uint8_t)
 {
        store80Mode = (bool)(address & 0x01);
 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
 
+       // It seems this affects more than just the text RAM, it also seems to affect the graphics RAM as well...
        if (store80Mode)
        {
                mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
                mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+               mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+               mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
        }
        else
        {
-               mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+               mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
                mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+               mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
+               mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
        }
 }
 
-
 void SwitchRAMRD(uint16_t address, uint8_t)
 {
        ramrd = (bool)(address & 0x01);
        mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
-       mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
+//     mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
 
        if (store80Mode)
                return;
 
        mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
+       mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
 }
 
-
 void SwitchRAMWRT(uint16_t address, uint8_t)
 {
        ramwrt = (bool)(address & 0x01);
        mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
-       mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
+//     mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
 
        if (store80Mode)
                return;
 
        mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+       mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
 }
 
-
 //
 // Since any slots that aren't populated are set to read from the ROM anyway,
 // we only concern ourselves with switching populated slots here.  (Note that
@@ -674,7 +668,6 @@ WriteLog("Setting SLOTCXROM to %s...\n", (address & 0x01 ? "ON" : "off"));
 #endif
 }
 
-
 void SwitchALTZP(uint16_t address, uint8_t)
 {
        altzp = (bool)(address & 0x01);
@@ -705,7 +698,6 @@ void SwitchSLOTC3ROM(uint16_t address, uint8_t)
 #endif
 }
 
-
 /*
 We need to see where this is being switched from; if we know that, we can switch in the appropriate ROM to $C800-$CFFF.  N.B.: Will probably need a custom handler routine, as some cards (like the Apple Hi-Speed SCSI card) split the 2K range into a 1K RAM space and a 1K bank switch ROM space.
 */
@@ -723,7 +715,6 @@ WriteLog("Hitting INTC8ROM (read)...\n");
        return rom[0xCFFF];
 }
 
-
 //
 // This resets the INTC8ROM switch (RW)
 //
@@ -733,20 +724,17 @@ WriteLog("Hitting INTC8ROM (write)...\n");
        intC8ROM = false;
 }
 
-
 void Switch80COL(uint16_t address, uint8_t)
 {
        col80Mode = (bool)(address & 0x01);
 }
 
-
 void SwitchALTCHARSET(uint16_t address, uint8_t)
 {
        alternateCharset = (bool)(address & 0x01);
 WriteLog("Setting ALTCHARSET to %s...\n", (alternateCharset ? "ON" : "off"));
 }
 
-
 uint8_t ReadKeyStrobe(uint16_t)
 {
        // No character data is read from here, just the 'any key was pressed'
@@ -756,13 +744,11 @@ uint8_t ReadKeyStrobe(uint16_t)
        return byte;
 }
 
-
 uint8_t ReadBANK2(uint16_t)
 {
        return (lcState < 0x04 ? 0x80 : 0x00);
 }
 
-
 uint8_t ReadLCRAM(uint16_t)
 {
        // If bits 0 & 1 are set, but not at the same time, then it's ROM
@@ -770,119 +756,104 @@ uint8_t ReadLCRAM(uint16_t)
        return (lcROM ? 0x00 : 0x80);
 }
 
-
 uint8_t ReadRAMRD(uint16_t)
 {
        return (uint8_t)ramrd << 7;
 }
 
-
 uint8_t ReadRAMWRT(uint16_t)
 {
        return (uint8_t)ramwrt << 7;
 }
 
-
 uint8_t ReadSLOTCXROM(uint16_t)
 {
        return (uint8_t)intCXROM << 7;
 }
 
-
 uint8_t ReadALTZP(uint16_t)
 {
        return (uint8_t)altzp << 7;
 }
 
-
 uint8_t ReadSLOTC3ROM(uint16_t)
 {
        return (uint8_t)slotC3ROM << 7;
 }
 
-
 uint8_t Read80STORE(uint16_t)
 {
        return (uint8_t)store80Mode << 7;
 }
 
-
 uint8_t ReadVBL(uint16_t)
 {
        return (uint8_t)vbl << 7;
 }
 
-
 uint8_t ReadTEXT(uint16_t)
 {
        return (uint8_t)textMode << 7;
 }
 
-
 uint8_t ReadMIXED(uint16_t)
 {
        return (uint8_t)mixedMode << 7;
 }
 
-
 uint8_t ReadPAGE2(uint16_t)
 {
        return (uint8_t)displayPage2 << 7;
 }
 
-
 uint8_t ReadHIRES(uint16_t)
 {
        return (uint8_t)hiRes << 7;
 }
 
-
 uint8_t ReadALTCHARSET(uint16_t)
 {
        return (uint8_t)alternateCharset << 7;
 }
 
-
 uint8_t Read80COL(uint16_t)
 {
        return (uint8_t)col80Mode << 7;
 }
 
-
 void WriteKeyStrobe(uint16_t, uint8_t)
 {
        keyDown = false;
 }
 
-
 uint8_t ReadSpeaker(uint16_t)
 {
        ToggleSpeaker();
-       return 0;
+//     return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void WriteSpeaker(uint16_t, uint8_t)
 {
        ToggleSpeaker();
 }
 
-
 uint8_t SwitchLCR(uint16_t address)
 {
        lcState = address & 0x0B;
        SwitchLC();
-       return 0;
+//     return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void SwitchLCW(uint16_t address, uint8_t)
 {
        lcState = address & 0x0B;
        SwitchLC();
 }
 
-
 void SwitchLC(void)
 {
        switch (lcState)
@@ -958,37 +929,39 @@ WriteLog("SwitchLC: Read/write bank 2\n");
        }
 }
 
-
 uint8_t SwitchTEXTR(uint16_t address)
 {
 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
        textMode = (bool)(address & 0x01);
-       return 0;
+//     return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void SwitchTEXTW(uint16_t address, uint8_t)
 {
 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
        textMode = (bool)(address & 0x01);
 }
 
-
 uint8_t SwitchMIXEDR(uint16_t address)
 {
 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
        mixedMode = (bool)(address & 0x01);
-       return 0;
+//     return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void SwitchMIXEDW(uint16_t address, uint8_t)
 {
 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
        mixedMode = (bool)(address & 0x01);
 }
 
-
+/*
+80STORE, PAGE2, and HIRES bank switch the primary display pages, $400--$7FF and $2000--$3FFF, between motherboard RAM and auxiliary card RAM.  If 80STORE is set and HIRES is reset, then PAGE2 switches between motherboard RAM and auxiliary card RAM for reading and writing in the $400--$7FF range.  If 80STORE is set and HIRES is set, then PAGE2 switches between motherboard RAM and auxiliary card RAM for reading and writing in the $400--$7FF and $2000--$3FFF ranges.  PAGE2 set selects auxiliary card RAM, and PAGE2 reset selects motherboard RAM.  If 80STORE is reset, then RAMRD and RAMWRT will bank switch the $400-$7FF and $2000--$3FFF ranges along with the rest of the $200--$BFFF range.
+*/
 uint8_t SwitchPAGE2R(uint16_t address)
 {
 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
@@ -998,12 +971,18 @@ WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
        {
                mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
                mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+
+               if (hiRes)
+               {
+                       mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+                       mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+               }
        }
 
-       return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void SwitchPAGE2W(uint16_t address, uint8_t)
 {
 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
@@ -1013,25 +992,30 @@ WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
        {
                mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
                mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+
+               if (hiRes)
+               {
+                       mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+                       mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+               }
        }
 }
 
-
 uint8_t SwitchHIRESR(uint16_t address)
 {
 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
        hiRes = (bool)(address & 0x01);
-       return 0;
+//     return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void SwitchHIRESW(uint16_t address, uint8_t)
 {
 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
        hiRes = (bool)(address & 0x01);
 }
 
-
 uint8_t SwitchDHIRESR(uint16_t address)
 {
 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
@@ -1039,10 +1023,11 @@ WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "
        if (ioudis)
                dhires = !((bool)(address & 0x01));
 
-       return 0;
+//     return 0;
+       // Seems this is needed for some things...
+       return ReadFloatingBus(0);
 }
 
-
 void SwitchDHIRESW(uint16_t address, uint8_t)
 {
 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
@@ -1050,25 +1035,29 @@ WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "
                dhires = !((bool)(address & 0x01));
 }
 
-
 void SwitchIOUDIS(uint16_t address, uint8_t)
 {
        ioudis = !((bool)(address & 0x01));
 }
 
+uint8_t ReadCassetteIn(uint16_t)
+{
+       // No idea what it's supposed to return if there's no cassette attached, so let's try this (Serpentine crashes if $FF is returned...)
+       // Serpentine crashes even earlier if $00 is returned...  Now what???
+       // This seems to work for Serpentine, not sure what's supposed to be returned here...  Maybe it's an RNG when N/C?
+       return 0x5A;
+}
 
 uint8_t ReadButton0(uint16_t)
 {
        return (uint8_t)openAppleDown << 7;
 }
 
-
 uint8_t ReadButton1(uint16_t)
 {
        return (uint8_t)closedAppleDown << 7;
 }
 
-
 // The way the paddles work is that a strobe is written (or read) to $C070,
 // then software counts down the time that it takes for the paddle outputs
 // to have bit 7 return to 0. If there are no paddles connected, bit 7
@@ -1079,19 +1068,16 @@ uint8_t ReadPaddle0(uint16_t)
        return 0xFF;
 }
 
-
 uint8_t ReadIOUDIS(uint16_t)
 {
        return (uint8_t)ioudis << 7;
 }
 
-
 uint8_t ReadDHIRES(uint16_t)
 {
        return (uint8_t)dhires << 7;
 }
 
-
 // Whenever a read is done to a MMIO location that is unconnected to anything,
 // it actually sees the RAM access done by the video generation hardware. Some
 // programs exploit this, so we emulate it here.
@@ -1131,7 +1117,6 @@ uint8_t ReadFloatingBus(uint16_t)
                        | (!store80Mode && displayPage2 ? 0x4000 : 0)
                        | ((vcount & 0x07) << 10);
 
-       // The address so read is *always* in main RAM, not alt RAM
+       // The address so read is *always* in main RAM, never alt RAM
        return ram[address];
 }
-