// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 09/27/2013 Created this file
-
+//
#include "mmu.h"
#include "apple2.h"
#include "sound.h"
#include "video.h"
-
// Debug defines
//#define LC_DEBUG
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);
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 },
{ 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 },
*/
-
void SetupAddressMap(void)
{
for(uint32_t i=0; i<0x10000; i++)
SwitchLC();
}
-
//
// Reset the MMU state after a power down event
//
{
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]);
#endif
}
-
//
// Set up slot access
//
*/
}
-
//
// Built-in functions
//
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]]);
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.
(*addrPtrWrite[address])[addrOffset[address]] = byte;
}
-
//
// The main memory access functions used by V65C02
//
#endif
}
-
void AppleWriteMem(uint16_t address, uint8_t byte)
{
#if 0
(*(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.
return (*(slotHandlerR[slot]))(address & 0xFF);
}
-
void SlotW(uint16_t address, uint8_t byte)
{
if (intCXROM)
(*(slotHandlerW[slot]))(address & 0xFF, byte);
}
-
//
// Slot handling for 2K address space at $C800-$CFFF
//
return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
}
-
void Slot2KW(uint16_t address, uint8_t byte)
{
if (intCXROM || intC8ROM)
(*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
}
-
//
// Actual emulated I/O functions follow
//
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
#endif
}
-
void SwitchALTZP(uint16_t address, uint8_t)
{
altzp = (bool)(address & 0x01);
#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.
*/
return rom[0xCFFF];
}
-
//
// This resets the INTC8ROM switch (RW)
//
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'
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
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;
}
-
void WriteSpeaker(uint16_t, uint8_t)
{
ToggleSpeaker();
}
-
uint8_t SwitchLCR(uint16_t address)
{
lcState = address & 0x0B;
return 0;
}
-
void SwitchLCW(uint16_t address, uint8_t)
{
lcState = address & 0x0B;
SwitchLC();
}
-
void SwitchLC(void)
{
switch (lcState)
}
}
-
uint8_t SwitchTEXTR(uint16_t address)
{
WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
return 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"));
return 0;
}
-
void SwitchMIXEDW(uint16_t address, uint8_t)
{
WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
mixedMode = (bool)(address & 0x01);
}
-
uint8_t SwitchPAGE2R(uint16_t address)
{
WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
{
mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+ mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+ mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
}
return 0;
}
-
void SwitchPAGE2W(uint16_t address, uint8_t)
{
WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
{
mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+ 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"));
return 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"));
return 0;
}
-
void SwitchDHIRESW(uint16_t address, uint8_t)
{
WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
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
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.
| (!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];
}
-