Skip to content

Commit

Permalink
Support for AppleColor / Video7 DHGR mixed mode (#523) (PR #620)
Browse files Browse the repository at this point in the history
Supported modes selected via toggling AN3 and clocking in 80COL:
- 140 color mode (Apple calls this mode 2, Video7 calls this mode 0).
- mixed mode (Apple calls this mode 3, Video7 calls this mode 2).
- 560 mono mode (Apple calls this mode 1, Video7 calls this mode 3).

Save-state is also persists the extra state.

And there's a few corrections to APPLE2E.SYM for the 80STORE and 80COL I/O addresses.
  • Loading branch information
tomcw committed Feb 2, 2019
1 parent 8f7ae7d commit dd53812
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 58 deletions.
17 changes: 10 additions & 7 deletions bin/APPLE2E.SYM
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
; Apple 2 Rom Symbol Table
; Version 5 Renamed "KBD/CLR80COL" -> "KBD/CLR80STORE", "SET80COL" -> "SET80STORE"
; Renamed "CLR80VID" -> "CLR80COL", "SET80VID" -> "SET80COL"
; Renamed "RD80COL" -> "RD80STORE", "RD80VID" -> "RD80COL"
; Version 4 GH#484
; Version 3 Cross-referenced/Sync'd with APPLE2E.SYM & A2_BASIC.SYM
Added Text Holes
; Added Text Holes
; Version 2 by Michael Pohoreski - AppleWin Debugger Dev
; Version 1 Original

Expand Down Expand Up @@ -91,8 +94,8 @@
07FB A2C.TEMP2

; Softswitches
C000 KBD/CLR80COL
C001 SET80COL
C000 KBD/CLR80STORE
C001 SET80STORE
C002 RDMAINRAM
C003 RDCARDRAM
C004 WRMAINRAM
Expand All @@ -103,8 +106,8 @@ C008 SETSTDZP
C009 SETALTZP
C00A SETINTC3ROM
C00B SETSLOTC3ROM
C00C CLR80VID
C00D SET80VID
C00C CLR80COL
C00D SET80COL
C00E CLRALTCHAR
C00F SETALTCHAR
C010 KBDSTRB
Expand All @@ -115,12 +118,12 @@ C014 RDRAMWRT
C015 RDCXROM
C016 RDALTZP
C017 RDC3ROM
C018 RD80COL
C018 RD80STORE
C019 RDVBLBAR
C01A RDTEXT
C01C RDPAGE2
C01E ALTCHARSET
C01F RD80VID
C01F RD80COL
C020 TAPEOUT
C030 SPKR
C050 TXTCLR
Expand Down
2 changes: 1 addition & 1 deletion source/Disk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1301,7 +1301,7 @@ static BYTE __stdcall Disk_IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON

//===========================================================================

// Unit version history:
// Unit version history:
// 2: Added: Format Track state & DiskLastCycle
// 3: Added: DiskLastReadLatchCycle
static const UINT kUNIT_VERSION = 3;
Expand Down
79 changes: 63 additions & 16 deletions source/Memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "Speaker.h"
#include "Tape.h"
#include "Video.h"
#include "RGBMonitor.h"

#include "z80emu.h"
#include "Z80VICE/z80.h"
Expand Down Expand Up @@ -2045,7 +2046,13 @@ LPVOID MemGetSlotParameters(UINT uSlot)

//

static const UINT kUNIT_AUXSLOT_VER = 1;
// Unit version history:
// 2: Added version field to card's state
static const UINT kUNIT_AUXSLOT_VER = 2;

// Unit version history:
// 2: Added: RGB card state
static const UINT kUNIT_VER = 2;

#define SS_YAML_VALUE_CARD_80COL "80 Column"
#define SS_YAML_VALUE_CARD_EXTENDED80COL "Extended 80 Column"
Expand Down Expand Up @@ -2198,32 +2205,42 @@ void MemSaveSnapshotAux(YamlSaveHelper& yamlSaveHelper)
}

yamlSaveHelper.UnitHdr(MemGetSnapshotUnitAuxSlotName(), kUNIT_AUXSLOT_VER);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);

std::string card = g_uMaxExPages == 0 ? SS_YAML_VALUE_CARD_80COL : // todo: support empty slot
g_uMaxExPages == 1 ? SS_YAML_VALUE_CARD_EXTENDED80COL :
SS_YAML_VALUE_CARD_RAMWORKSIII;
yamlSaveHelper.SaveString(SS_YAML_KEY_CARD, card.c_str());
yamlSaveHelper.Save("%s: 0x%02X # [0,1..7F] 0=no aux mem, 1=128K system, etc\n", SS_YAML_KEY_NUMAUXBANKS, g_uMaxExPages);
yamlSaveHelper.Save("%s: 0x%02X # [ 0..7E] 0=memaux\n", SS_YAML_KEY_ACTIVEAUXBANK, g_uActiveBank);

for(UINT uBank = 1; uBank <= g_uMaxExPages; uBank++)
// Unit state
{
MemSaveSnapshotMemory(yamlSaveHelper, false, uBank);
YamlSaveHelper::Label unitState(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);

std::string card = g_uMaxExPages == 0 ? SS_YAML_VALUE_CARD_80COL : // todo: support empty slot
g_uMaxExPages == 1 ? SS_YAML_VALUE_CARD_EXTENDED80COL :
SS_YAML_VALUE_CARD_RAMWORKSIII;

yamlSaveHelper.SaveString(SS_YAML_KEY_CARD, card.c_str());
yamlSaveHelper.Save("%s: %d\n", SS_YAML_KEY_VERSION, kUNIT_VER);

// Card state
{
YamlSaveHelper::Label cardState(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);

yamlSaveHelper.Save("%s: 0x%02X # [0,1..7F] 0=no aux mem, 1=128K system, etc\n", SS_YAML_KEY_NUMAUXBANKS, g_uMaxExPages);
yamlSaveHelper.Save("%s: 0x%02X # [ 0..7E] 0=memaux\n", SS_YAML_KEY_ACTIVEAUXBANK, g_uActiveBank);

for(UINT uBank = 1; uBank <= g_uMaxExPages; uBank++)
{
MemSaveSnapshotMemory(yamlSaveHelper, false, uBank);
}

RGB_SaveSnapshot(yamlSaveHelper);
}
}
}

bool MemLoadSnapshotAux(YamlLoadHelper& yamlLoadHelper, UINT version)
static void MemLoadSnapshotAuxCommon(YamlLoadHelper& yamlLoadHelper, const std::string& card)
{
if (version != kUNIT_AUXSLOT_VER)
throw std::string(SS_YAML_KEY_UNIT ": AuxSlot: Version mismatch");

// "State"
UINT numAuxBanks = yamlLoadHelper.LoadUint(SS_YAML_KEY_NUMAUXBANKS);
UINT activeAuxBank = yamlLoadHelper.LoadUint(SS_YAML_KEY_ACTIVEAUXBANK);

SS_CARDTYPE type = CT_Empty;
std::string card = yamlLoadHelper.LoadString(SS_YAML_KEY_CARD);
if (card == SS_YAML_VALUE_CARD_80COL)
{
type = CT_80Col;
Expand Down Expand Up @@ -2282,6 +2299,36 @@ bool MemLoadSnapshotAux(YamlLoadHelper& yamlLoadHelper, UINT version)

memaux = RWpages[g_uActiveBank];
// NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v2()
}

static void MemLoadSnapshotAuxVer1(YamlLoadHelper& yamlLoadHelper)
{
std::string card = yamlLoadHelper.LoadString(SS_YAML_KEY_CARD);
MemLoadSnapshotAuxCommon(yamlLoadHelper, card);
}

static void MemLoadSnapshotAuxVer2(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
{
std::string card = yamlLoadHelper.LoadString(SS_YAML_KEY_CARD);
UINT cardVersion = yamlLoadHelper.LoadUint(SS_YAML_KEY_VERSION);

if (!yamlLoadHelper.GetSubMap(std::string(SS_YAML_KEY_STATE)))
throw std::string(SS_YAML_KEY_UNIT ": Expected sub-map name: " SS_YAML_KEY_STATE);

MemLoadSnapshotAuxCommon(yamlLoadHelper, card);

RGB_LoadSnapshot(yamlLoadHelper);
}

bool MemLoadSnapshotAux(YamlLoadHelper& yamlLoadHelper, UINT version)
{
if (version < 1 || version > kUNIT_AUXSLOT_VER)
throw std::string(SS_YAML_KEY_UNIT ": AuxSlot: Version mismatch");

if (version == 1)
MemLoadSnapshotAuxVer1(yamlLoadHelper);
else
MemLoadSnapshotAuxVer2(yamlLoadHelper, version);

return true;
}
43 changes: 41 additions & 2 deletions source/NTSC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,19 @@ inline bool GetColorBurst( void )

//===========================================================================

void update7MonoPixels( uint16_t bits )
{
g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1;
g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1;
g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1;
g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1;
g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1;
g_pFuncUpdateBnWPixel(bits & 1); bits >>= 1;
g_pFuncUpdateBnWPixel(bits & 1);
}

//===========================================================================

// NB. g_nLastColumnPixelNTSC = bits.b13 will be superseded by these parent funcs which use bits.b14:
// . updateScreenDoubleHires80(), updateScreenDoubleLores80(), updateScreenText80()
inline void updatePixels( uint16_t bits )
Expand Down Expand Up @@ -1362,8 +1375,34 @@ void updateScreenDoubleHires80Simplified (long cycles6502 ) // wsUpdateVideoDblH
else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint16_t addr = getVideoScannerAddressHGR();
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress);
g_pVideoAddress += 14;
uint8_t a = *MemGetAuxPtr(addr);
uint8_t m = *MemGetMainPtr(addr);

if (RGB_Is560Mode() || (RGB_IsMixMode() && !((a | m) & 0x80)))
{
update7MonoPixels(a);
update7MonoPixels(m);
}
else if (!RGB_IsMixMode() || (RGB_IsMixMode() && (a & m & 0x80)))
{
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, true, true);
g_pVideoAddress += 14;
}
else // RGB_IsMixMode() && ((a ^ m) & 0x80)
{
if (a & 0x80) // RGB color, then monochrome
{
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, true ,false);
g_pVideoAddress += 7;
update7MonoPixels(m);
}
else // monochrome, then RGB color
{
update7MonoPixels(a);
UpdateDHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress, false, true);
g_pVideoAddress += 7;
}
}
}
}
updateVideoScannerHorzEOLSimple();
Expand Down
128 changes: 116 additions & 12 deletions source/RGBMonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Memory.h" // MemGetMainPtr() MemGetAuxPtr()
#include "Video.h"
#include "RGBMonitor.h"
#include "YamlHelper.h"

const int SRCOFFS_LORES = 0; // 0
const int SRCOFFS_HIRES = (SRCOFFS_LORES + 16); // 16
Expand Down Expand Up @@ -542,30 +543,38 @@ void UpdateHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)

//===========================================================================

void UpdateDHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)
#define COLOR ((xpixel + PIXEL) & 3)
#define VALUE (dwordval >> (4 + PIXEL - COLOR))

void UpdateDHiResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress, bool updateAux, bool updateMain)
{
const int xpixel = x*14;

uint8_t *pAux = MemGetAuxPtr(addr);
uint8_t *pAux = MemGetAuxPtr(addr);
uint8_t *pMain = MemGetMainPtr(addr);

BYTE byteval1 = (x > 0) ? *(pMain-1) : 0;
BYTE byteval2 = *pAux;
BYTE byteval3 = *pMain;
BYTE byteval4 = (x < 39) ? *(pAux+1) : 0;
BYTE byteval1 = (x > 0) ? *(pMain-1) : 0;
BYTE byteval2 = *pAux;
BYTE byteval3 = *pMain;
BYTE byteval4 = (x < 39) ? *(pAux+1) : 0;

DWORD dwordval = (byteval1 & 0x70) | ((byteval2 & 0x7F) << 7) |
((byteval3 & 0x7F) << 14) | ((byteval4 & 0x07) << 21);

#define PIXEL 0
#define COLOR ((xpixel + PIXEL) & 3)
#define VALUE (dwordval >> (4 + PIXEL - COLOR))
if (updateAux)
{
CopySource(7,2, SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR, LOBYTE(VALUE)<<1, pVideoAddress);
pVideoAddress += 7;
}
#undef PIXEL

#define PIXEL 7
CopySource(7,2, SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR, LOBYTE(VALUE)<<1, pVideoAddress+7);
if (updateMain)
{
CopySource(7,2, SRCOFFS_DHIRES+10*HIBYTE(VALUE)+COLOR, LOBYTE(VALUE)<<1, pVideoAddress);
}
#undef PIXEL
#undef COLOR
#undef VALUE
}

//===========================================================================
Expand All @@ -592,7 +601,7 @@ void UpdateLoResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)
// Tested with FT's Ansi Story
void UpdateDLoResCell (int x, int y, uint16_t addr, bgra_t *pVideoAddress)
{
BYTE auxval = *MemGetAuxPtr(addr);
BYTE auxval = *MemGetAuxPtr(addr);
const BYTE mainval = *MemGetMainPtr(addr);

const BYTE auxval_h = auxval >> 4;
Expand Down Expand Up @@ -649,3 +658,98 @@ void VideoInitializeOriginal(baseColors_t pBaseNtscColors)
PalIndex2RGB[HGR_VIOLET] = PalIndex2RGB[MAGENTA];
#endif
}

//===========================================================================

#define DBG_SUPPORT_A2OSX

static UINT g_rgbFlags = 0;
static UINT g_rgbMode = 0;
static WORD g_rgbPrevAN3Addr = 0;

// Video7 RGB card:
// . Clock in the !80COL state to define the 2 flags: F2, F1
// . Clocking done by toggling AN3
// . NB. There's a final 5th AN3 transition to set DHGR mode
void RGB_SetVideoMode(WORD address)
{
if ((address&~1) != 0x5E) // 0x5E or 0x5F?
return;

// Precondition before togglng AN3:
// . Video7 manual: set 80STORE, but "King's Quest 1"(*) will re-enable RGB card's MIX mode with only VF_TEXT & VF_HIRES set!
// . "Extended 80-Column Text/AppleColor Card" manual: TEXT off($C050), MIXED off($C052), HIRES on($C057)
// . (*) "King's Quest 1" - see routine at 0x5FD7 (trigger by pressing TAB twice)
if ((g_uVideoMode & (VF_MIXED|VF_HIRES)) != (VF_HIRES))
{
g_rgbMode = 0;
g_rgbPrevAN3Addr = 0;
return;
}

if (address == 0x5F && g_rgbPrevAN3Addr == 0x5E) // Check for AN3 clock transition
{
g_rgbFlags = (g_rgbFlags<<1) & 3;
g_rgbFlags |= ((g_uVideoMode & VF_80COL) ? 0 : 1); // clock in !80COL
g_rgbMode = g_rgbFlags; // latch F2,F1
}

g_rgbPrevAN3Addr = address;
}

bool RGB_Is140Mode(void) // Extended 80-Column Text/AppleColor Card's Mode 2
{
return g_rgbMode == 0;
}

//bool RGB_Is160Mode(void) // Extended 80-Column Text/AppleColor Card: N/A
//{
// return g_rgbMode == 1;
//}

bool RGB_IsMixMode(void) // Extended 80-Column Text/AppleColor Card's Mode 3
{
return g_rgbMode == 2;
}

bool RGB_Is560Mode(void) // Extended 80-Column Text/AppleColor Card's Mode 1
{
return g_rgbMode == 3;
}

void RGB_ResetState(void)
{
g_rgbFlags = 0;
g_rgbMode = 0;
g_rgbPrevAN3Addr = 0;
}

//===========================================================================

#define SS_YAML_KEY_RGB_CARD "AppleColor RGB Adaptor"
// NB. No version - this is determined by the parent card

#define SS_YAML_KEY_RGB_FLAGS "RGB mode flags"
#define SS_YAML_KEY_RGB_MODE "RGB mode"
#define SS_YAML_KEY_RGB_PREVIOUS_AN3 "Previous AN3"

void RGB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", SS_YAML_KEY_RGB_CARD);

yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_RGB_FLAGS, g_rgbFlags);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_RGB_MODE, g_rgbMode);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_RGB_PREVIOUS_AN3, g_rgbPrevAN3Addr);
}

void RGB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper)
{
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_RGB_CARD))
throw std::string("Card: Expected key: ") + std::string(SS_YAML_KEY_RGB_CARD);

g_rgbFlags = yamlLoadHelper.LoadUint(SS_YAML_KEY_RGB_FLAGS);
g_rgbMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_RGB_MODE);
g_rgbPrevAN3Addr = yamlLoadHelper.LoadUint(SS_YAML_KEY_RGB_PREVIOUS_AN3);

yamlLoadHelper.PopMap();
}
Loading

0 comments on commit dd53812

Please sign in to comment.