Total Replay exposed (yet again!) a couple of flaws in apple2's
emulation: it would crash on the Serpentine demo and would display bad
graphics on DHIRES screen transitions. The latter was caused by a lack
of CASIN emulation (and it's still not confirmed that it's correct), and
the latter was caused by bad handling of the the STORE80 softswitch.
Has it been almost a year since the last commit? How time does fly...
:-/
web/source/
misc/
changes-since-last-commit.txt
web/source/
misc/
changes-since-last-commit.txt
+*.txt
+disks.old/
+disks.old2/
+pix/
#
export PATH=/opt/mxe/usr/bin:$PATH
#make CROSS=i686-pc-mingw32- clean && make CROSS=i686-pc-mingw32-
#
export PATH=/opt/mxe/usr/bin:$PATH
#make CROSS=i686-pc-mingw32- clean && make CROSS=i686-pc-mingw32-
-make CROSS=x86_64-w64-mingw32.static- clean \
- && make CROSS=x86_64-w64-mingw32.static- \
+#make CROSS=x86_64-w64-mingw32.static- clean
+make CROSS=i686-w64-mingw32.static- clean \
+ && make CROSS=i686-w64-mingw32.static- \
&& upx -9v apple2.exe
#TARGET = apple2
&& upx -9v apple2.exe
#TARGET = apple2
// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 09/27/2013 Created this file
// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 09/27/2013 Created this file
#include "mmu.h"
#include "apple2.h"
#include "mmu.h"
#include "apple2.h"
#include "sound.h"
#include "video.h"
#include "sound.h"
#include "video.h"
// Debug defines
//#define LC_DEBUG
// Debug defines
//#define LC_DEBUG
uint8_t * upperMemoryR = &ram[0xE000]; // $E000 - $FFFF (read)
uint8_t * upperMemoryW = &ram[0xE000]; // $E000 - $FFFF (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);
// 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 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);
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 },
// 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 },
{ 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 },
{ 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++)
void SetupAddressMap(void)
{
for(uint32_t i=0; i<0x10000; i++)
//
// Reset the MMU state after a power down event
//
//
// Reset the MMU state after a power down event
//
{
mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
{
mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+ mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+ mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
- 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]);
mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+ mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
+ mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
}
mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
}
mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
- mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
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]);
// slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
// slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
//
// Set up slot access
//
//
// Set up slot access
//
//
// Built-in functions
//
//
// Built-in functions
//
void WriteNOP(uint16_t, uint8_t)
{
}
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]]);
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]];
}
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.
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;
}
(*addrPtrWrite[address])[addrOffset[address]] = byte;
}
//
// The main memory access functions used by V65C02
//
//
// The main memory access functions used by V65C02
//
void AppleWriteMem(uint16_t address, uint8_t byte)
{
#if 0
void AppleWriteMem(uint16_t address, uint8_t byte)
{
#if 0
(*(funcMapWrite[address]))(address, byte);
}
(*(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.
//
// 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);
}
return (*(slotHandlerR[slot]))(address & 0xFF);
}
void SlotW(uint16_t address, uint8_t byte)
{
if (intCXROM)
void SlotW(uint16_t address, uint8_t byte)
{
if (intCXROM)
(*(slotHandlerW[slot]))(address & 0xFF, byte);
}
(*(slotHandlerW[slot]))(address & 0xFF, byte);
}
//
// Slot handling for 2K address space at $C800-$CFFF
//
//
// Slot handling for 2K address space at $C800-$CFFF
//
return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
}
return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
}
void Slot2KW(uint16_t address, uint8_t byte)
{
if (intCXROM || intC8ROM)
void Slot2KW(uint16_t address, uint8_t byte)
{
if (intCXROM || intC8ROM)
(*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
}
(*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
}
//
// Actual emulated I/O functions follow
//
//
// Actual emulated I/O functions follow
//
return lastKeyPressed | ((uint8_t)keyDown << 7);
}
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"));
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]);
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]);
- mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
+ mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
mainMemoryTextW = (ramwrt ? &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]);
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]);
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]);
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]);
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
//
// 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
void SwitchALTZP(uint16_t address, uint8_t)
{
altzp = (bool)(address & 0x01);
void SwitchALTZP(uint16_t address, uint8_t)
{
altzp = (bool)(address & 0x01);
/*
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.
*/
/*
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.
*/
//
// This resets the INTC8ROM switch (RW)
//
//
// This resets the INTC8ROM switch (RW)
//
void Switch80COL(uint16_t address, uint8_t)
{
col80Mode = (bool)(address & 0x01);
}
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"));
}
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'
uint8_t ReadKeyStrobe(uint16_t)
{
// No character data is read from here, just the 'any key was pressed'
uint8_t ReadBANK2(uint16_t)
{
return (lcState < 0x04 ? 0x80 : 0x00);
}
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
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);
}
return (lcROM ? 0x00 : 0x80);
}
uint8_t ReadRAMRD(uint16_t)
{
return (uint8_t)ramrd << 7;
}
uint8_t ReadRAMRD(uint16_t)
{
return (uint8_t)ramrd << 7;
}
uint8_t ReadRAMWRT(uint16_t)
{
return (uint8_t)ramwrt << 7;
}
uint8_t ReadRAMWRT(uint16_t)
{
return (uint8_t)ramwrt << 7;
}
uint8_t ReadSLOTCXROM(uint16_t)
{
return (uint8_t)intCXROM << 7;
}
uint8_t ReadSLOTCXROM(uint16_t)
{
return (uint8_t)intCXROM << 7;
}
uint8_t ReadALTZP(uint16_t)
{
return (uint8_t)altzp << 7;
}
uint8_t ReadALTZP(uint16_t)
{
return (uint8_t)altzp << 7;
}
uint8_t ReadSLOTC3ROM(uint16_t)
{
return (uint8_t)slotC3ROM << 7;
}
uint8_t ReadSLOTC3ROM(uint16_t)
{
return (uint8_t)slotC3ROM << 7;
}
uint8_t Read80STORE(uint16_t)
{
return (uint8_t)store80Mode << 7;
}
uint8_t Read80STORE(uint16_t)
{
return (uint8_t)store80Mode << 7;
}
uint8_t ReadVBL(uint16_t)
{
return (uint8_t)vbl << 7;
}
uint8_t ReadVBL(uint16_t)
{
return (uint8_t)vbl << 7;
}
uint8_t ReadTEXT(uint16_t)
{
return (uint8_t)textMode << 7;
}
uint8_t ReadTEXT(uint16_t)
{
return (uint8_t)textMode << 7;
}
uint8_t ReadMIXED(uint16_t)
{
return (uint8_t)mixedMode << 7;
}
uint8_t ReadMIXED(uint16_t)
{
return (uint8_t)mixedMode << 7;
}
uint8_t ReadPAGE2(uint16_t)
{
return (uint8_t)displayPage2 << 7;
}
uint8_t ReadPAGE2(uint16_t)
{
return (uint8_t)displayPage2 << 7;
}
uint8_t ReadHIRES(uint16_t)
{
return (uint8_t)hiRes << 7;
}
uint8_t ReadHIRES(uint16_t)
{
return (uint8_t)hiRes << 7;
}
uint8_t ReadALTCHARSET(uint16_t)
{
return (uint8_t)alternateCharset << 7;
}
uint8_t ReadALTCHARSET(uint16_t)
{
return (uint8_t)alternateCharset << 7;
}
uint8_t Read80COL(uint16_t)
{
return (uint8_t)col80Mode << 7;
}
uint8_t Read80COL(uint16_t)
{
return (uint8_t)col80Mode << 7;
}
void WriteKeyStrobe(uint16_t, uint8_t)
{
keyDown = false;
}
void WriteKeyStrobe(uint16_t, uint8_t)
{
keyDown = false;
}
uint8_t ReadSpeaker(uint16_t)
{
ToggleSpeaker();
return 0;
}
uint8_t ReadSpeaker(uint16_t)
{
ToggleSpeaker();
return 0;
}
void WriteSpeaker(uint16_t, uint8_t)
{
ToggleSpeaker();
}
void WriteSpeaker(uint16_t, uint8_t)
{
ToggleSpeaker();
}
uint8_t SwitchLCR(uint16_t address)
{
lcState = address & 0x0B;
uint8_t SwitchLCR(uint16_t address)
{
lcState = address & 0x0B;
void SwitchLCW(uint16_t address, uint8_t)
{
lcState = address & 0x0B;
SwitchLC();
}
void SwitchLCW(uint16_t address, uint8_t)
{
lcState = address & 0x0B;
SwitchLC();
}
void SwitchLC(void)
{
switch (lcState)
void SwitchLC(void)
{
switch (lcState)
uint8_t SwitchTEXTR(uint16_t address)
{
WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
uint8_t SwitchTEXTR(uint16_t address)
{
WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
void SwitchTEXTW(uint16_t address, uint8_t)
{
WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
textMode = (bool)(address & 0x01);
}
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"));
uint8_t SwitchMIXEDR(uint16_t address)
{
WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
void SwitchMIXEDW(uint16_t address, uint8_t)
{
WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
mixedMode = (bool)(address & 0x01);
}
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"));
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]);
{
mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
+ mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
+ mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
void SwitchPAGE2W(uint16_t address, uint8_t)
{
WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
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]);
{
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"));
uint8_t SwitchHIRESR(uint16_t address)
{
WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
void SwitchHIRESW(uint16_t address, uint8_t)
{
WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
hiRes = (bool)(address & 0x01);
}
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"));
uint8_t SwitchDHIRESR(uint16_t address)
{
WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
void SwitchDHIRESW(uint16_t address, uint8_t)
{
WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
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));
}
dhires = !((bool)(address & 0x01));
}
void SwitchIOUDIS(uint16_t address, uint8_t)
{
ioudis = !((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 ReadButton0(uint16_t)
{
return (uint8_t)openAppleDown << 7;
}
uint8_t ReadButton1(uint16_t)
{
return (uint8_t)closedAppleDown << 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
// 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
uint8_t ReadIOUDIS(uint16_t)
{
return (uint8_t)ioudis << 7;
}
uint8_t ReadIOUDIS(uint16_t)
{
return (uint8_t)ioudis << 7;
}
uint8_t ReadDHIRES(uint16_t)
{
return (uint8_t)dhires << 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.
// 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);
| (!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
static void RenderDLoRes(uint16_t toLine = 24);
static void RenderHiRes(uint16_t toLine = 192);
static void RenderDHiRes(uint16_t toLine = 192);
static void RenderDLoRes(uint16_t toLine = 24);
static void RenderHiRes(uint16_t toLine = 192);
static void RenderDHiRes(uint16_t toLine = 192);
-static void RenderVideoFrame(/*uint32_t *, int*/);
-
+static void RenderVideoFrame();
void SetupBlurTable(void)
{
void SetupBlurTable(void)
{
// last four bits are copies of the previous four...
// Odd. Doing the bit patterns from 0-$7F doesn't work, but going
// from 0-$7FF stepping by 16 does. Hm.
// last four bits are copies of the previous four...
// Odd. Doing the bit patterns from 0-$7F doesn't work, but going
// from 0-$7FF stepping by 16 does. Hm.
- // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
+ // Well, it seems that going from 0-$7F doesn't have enough precision
+ // to do the job.
for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
{
uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
{
uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
void TogglePalette(void)
{
if (palette == (uint32_t *)colors)
void TogglePalette(void)
{
if (palette == (uint32_t *)colors)
void CycleScreenTypes(void)
{
char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
void CycleScreenTypes(void)
{
char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
SpawnMessage("%s", scrTypeStr[screenType]);
}
SpawnMessage("%s", scrTypeStr[screenType]);
}
void ToggleTickDisplay(void)
{
showFrameTicks = !showFrameTicks;
}
void ToggleTickDisplay(void)
{
showFrameTicks = !showFrameTicks;
}
static uint32_t msgTicks = 0;
static char message[4096];
static uint32_t msgTicks = 0;
static char message[4096];
va_end(arg);
msgTicks = 120;
va_end(arg);
msgTicks = 120;
-//WriteLog("\n%s\n", message);
static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
static void DrawString(void)
{
static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
static void DrawString(void)
{
DrawString2(8, 8, 0x0020FF20, message);
}
DrawString2(8, 8, 0x0020FF20, message);
}
static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
{
//This approach works, and seems to be fast enough... Though it probably would
static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
{
//This approach works, and seems to be fast enough... Though it probably would
DrawString2(x, y, color, msg);
}
DrawString2(x, y, color, msg);
}
static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
{
uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
{
uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
static void DrawFrameTicks(void)
{
uint32_t color = 0x00FF2020;
static void DrawFrameTicks(void)
{
uint32_t color = 0x00FF2020;
if ((frameTimePtr % 15) == 0)
{
if ((frameTimePtr % 15) == 0)
{
-// uint32_t prevClock = (frameTimePtr + 1) % 60;
uint64_t prevClock = (frameTimePtr + 1) % 60;
uint64_t prevClock = (frameTimePtr + 1) % 60;
-// float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
sprintf(msg, "%.1lf FPS", fps);
}
double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
sprintf(msg, "%.1lf FPS", fps);
}
DrawString(20, 24, color, msg);
}
DrawString(20, 24, color, msg);
}
static void Render40ColumnTextLine(uint8_t line)
{
uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
for(int x=0; x<40; x++)
{
static void Render40ColumnTextLine(uint8_t line)
{
uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
for(int x=0; x<40; x++)
{
- uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
+ uint8_t chr = ram[lineAddrLoRes[line] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x];
// Render character at (x, y)
// Render character at (x, y)
static void Render80ColumnTextLine(uint8_t line)
{
uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
static void Render80ColumnTextLine(uint8_t line)
{
uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
static void Render40ColumnText(void)
{
for(uint8_t line=0; line<24; line++)
Render40ColumnTextLine(line);
}
static void Render40ColumnText(void)
{
for(uint8_t line=0; line<24; line++)
Render40ColumnTextLine(line);
}
static void Render80ColumnText(void)
{
for(uint8_t line=0; line<24; line++)
Render80ColumnTextLine(line);
}
static void Render80ColumnText(void)
{
for(uint8_t line=0; line<24; line++)
Render80ColumnTextLine(line);
}
static void RenderLoRes(uint16_t toLine/*= 24*/)
{
// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
static void RenderLoRes(uint16_t toLine/*= 24*/)
{
// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
for(uint16_t x=0; x<40; x+=2)
{
for(uint16_t x=0; x<40; x+=2)
{
- uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
- uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
+ uint8_t scrByte1 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
+ uint8_t scrByte2 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
for(uint32_t cy=0; cy<8; cy++)
{
scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
for(uint32_t cy=0; cy<8; cy++)
{
scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-// scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
for(uint32_t cy=0; cy<8; cy++)
{
scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
for(uint32_t cy=0; cy<8; cy++)
{
scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-// scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
for(uint16_t x=0; x<40; x+=2)
{
for(uint16_t x=0; x<40; x+=2)
{
- uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
- uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
+ uint8_t scrByte1 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
+ uint8_t scrByte2 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
for(uint32_t cy=8; cy<16; cy++)
{
scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
for(uint32_t cy=8; cy<16; cy++)
{
scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-// scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
for(uint32_t cy=8; cy<16; cy++)
{
scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
for(uint32_t cy=8; cy<16; cy++)
{
scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-// scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
//
// Render the Double Lo Res screen (HIRES off, DHIRES on)
//
//
// Render the Double Lo Res screen (HIRES off, DHIRES on)
//
static void RenderHiRes(uint16_t toLine/*= 192*/)
{
#if 0
static void RenderHiRes(uint16_t toLine/*= 192*/)
{
#if 0
for(uint16_t x=0; x<40; x+=2)
{
for(uint16_t x=0; x<40; x+=2)
{
- uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+ uint8_t screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x];
uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
- screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+ screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x + 1];
uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
static void RenderDHiRes(uint16_t toLine/*= 192*/)
{
// Now it is. Now roll this fix into all the other places... !!! FIX !!!
static void RenderDHiRes(uint16_t toLine/*= 192*/)
{
// Now it is. Now roll this fix into all the other places... !!! FIX !!!
for(uint16_t x=0; x<40; x+=2)
{
for(uint16_t x=0; x<40; x+=2)
{
- uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+ uint8_t screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x];
uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
- screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+
+ screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x + 1];
pixels = pixels | (mirrorTable[screenByte & 0x7F]);
pixels = pixels | (mirrorTable[screenByte & 0x7F]);
- screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+
+ screenByte = ram2[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x];
pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
- screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+
+ screenByte = ram2[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x + 1];
pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
pixels = previous4bits | (pixels >> 1);
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
pixels = previous4bits | (pixels >> 1);
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
void RenderVideoFrame(void)
{
if (GUI::powerOnState == true)
void RenderVideoFrame(void)
{
if (GUI::powerOnState == true)
//
// Prime SDL and create surfaces
//
//
// Prime SDL and create surfaces
//
//
// Free various SDL components
//
//
// Free various SDL components
//
WriteLog("Video: Done.\n");
}
WriteLog("Video: Done.\n");
}
//
// Render the Apple video screen to the primary texture
//
//
// Render the Apple video screen to the primary texture
//
SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
}
SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
}
//
// Fullscreen <-> window switching
//
//
// Fullscreen <-> window switching
//
if (retVal != 0)
WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
}
if (retVal != 0)
WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
}