diff --git a/bin/History.txt b/bin/History.txt index d969fd36..4578fb44 100644 --- a/bin/History.txt +++ b/bin/History.txt @@ -8,6 +8,18 @@ https://github.com/AppleWin/AppleWin/issues/new Tom Charlesworth + +1.28.5.0 - 6 Apr 2019 +--------------------- +. [Change #631] Improvements for the RGB AppleColor card: + - Relax the video-mode precondition to just ignore if VF_MIXED (previously required HIRES on) for Apple II Desktop. + - Changing from DHGR B&W mode to HGR remains in B&W (color burst if off). + - For '50% scan lines', don't blend in NTSC B&W mode, as this was inconsistent with the RGB colour rendering. +. [Change #633] Improvements for the RGB AppleColor card: + - Improved the video-mode precondition to ignore if 80COL ($C00C/D) occurs before DHIRESON ($C05F) for Renegade. + - Support new switch -rgb-card-invert-bit7 to invert bit7 for Dragon Wars. + + 1.28.4.0 - 16 Mar 2019 ---------------------- . [Change #616] Improved accuracy for 'RGB (Color Monitor)' for hires. diff --git a/help/CommandLine.html b/help/CommandLine.html index f6dec26e..aca64edb 100644 --- a/help/CommandLine.html +++ b/help/CommandLine.html @@ -97,15 +97,19 @@
  • Either: Toggle between windowed and full screen video modes (default).
  • Or: Allow the emulated Apple II to read the Enter key state when Alt (Open Apple key) is pressed. + -rgb-card-invert-bit7
    + Force the RGB card (in "Color (RGB Monitor)" video mode) to invert bit7 in MIX mode. Enables the correct rendering for Dragon Wars.

    Debug arguments:

    -l or -log
    - Enable logging. Creates an AppleWin.log file

    + Enable logging. Creates an AppleWin.log file.

    -m
    - Disable DirectSound support

    + Disable DirectSound support.

    -no-printscreen-dlg
    - Suppress the warning message-box if AppleWin fails to capture the PrintScreen key

    + Suppress the warning message-box if AppleWin fails to capture the PrintScreen key.

    + -screenshot-and-exit
    + For testing. Use in combination with -load-state.

    diff --git a/resource/version.h b/resource/version.h index ec8ebb7a..4b56dec7 100644 --- a/resource/version.h +++ b/resource/version.h @@ -1,4 +1,4 @@ -#define APPLEWIN_VERSION 1,28,4,0 +#define APPLEWIN_VERSION 1,28,5,0 #define xstr(a) str(a) #define str(a) #a diff --git a/source/Applewin.cpp b/source/Applewin.cpp index a28c5f74..4cf53765 100644 --- a/source/Applewin.cpp +++ b/source/Applewin.cpp @@ -53,6 +53,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Speech.h" #endif #include "Video.h" +#include "RGBMonitor.h" #include "NTSC.h" #include "Configuration/About.h" @@ -1179,6 +1180,7 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int) int newVideoType = -1; int newVideoStyleEnableMask = 0; int newVideoStyleDisableMask = 0; + LPSTR szScreenshotFilename = NULL; while (*lpCmdLine) { @@ -1415,6 +1417,15 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int) { newVideoStyleDisableMask = VS_COLOR_VERTICAL_BLEND; } + else if (strcmp(lpCmdLine, "-rgb-card-invert-bit7") == 0) // GH#633 + { + RGB_SetInvertBit7(true); + } + else if (strcmp(lpCmdLine, "-screenshot-and-exit") == 0) // GH#616: For testing - Use in combination with -load-state + { + szScreenshotFilename = GetCurrArg(lpNextArg); + lpNextArg = GetNextArg(lpNextArg); + } else // unsupported { LogFileOutput("Unsupported arg: %s\n", lpCmdLine); @@ -1655,6 +1666,12 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int) LogFileOutput("Main: Snapshot_Startup()\n"); } + if (szScreenshotFilename) + { + Video_RedrawAndTakeScreenShot(szScreenshotFilename); + bShutdown = true; + } + if (bShutdown) { PostMessage(g_hFrameWindow, WM_DESTROY, 0, 0); // Close everything down diff --git a/source/Common.h b/source/Common.h index 3a0eaabe..f72fbfa4 100644 --- a/source/Common.h +++ b/source/Common.h @@ -163,8 +163,8 @@ enum eIRQSRC {IS_6522=0, IS_SPEECH, IS_SSC, IS_MOUSE}; #define APPLECLONE_MASK 0x100 #define IS_APPLE2 ((g_Apple2Type & (APPLE2E_MASK|APPLE2C_MASK)) == 0) -#define IS_APPLE2E (g_Apple2Type & APPLE2E_MASK) -#define IS_APPLE2C (g_Apple2Type & APPLE2C_MASK) +#define IS_APPLE2E() (g_Apple2Type & APPLE2E_MASK) +#define IS_APPLE2C() (g_Apple2Type & APPLE2C_MASK) #define IS_CLONE() (g_Apple2Type & APPLECLONE_MASK) // NB. These get persisted to the Registry & save-state file, so don't change the values for these enums! @@ -215,9 +215,14 @@ inline bool IsApple2PlusOrClone(eApple2Type type) // Apple ][,][+ or clone ][,][ } extern eApple2Type g_Apple2Type; -inline bool IsOriginal2E(void) +inline bool IsEnhancedIIE(void) { - return (g_Apple2Type == A2TYPE_APPLE2E); + return ( (g_Apple2Type == A2TYPE_APPLE2EENHANCED) || (g_Apple2Type == A2TYPE_TK30002E) ); +} + +inline bool IsEnhancedIIEorIIC(void) +{ + return ( (g_Apple2Type == A2TYPE_APPLE2EENHANCED) || (g_Apple2Type == A2TYPE_TK30002E) || IS_APPLE2C() ); } enum eBUTTON {BUTTON0=0, BUTTON1}; diff --git a/source/Joystick.cpp b/source/Joystick.cpp index 30e77c23..a7edb82a 100644 --- a/source/Joystick.cpp +++ b/source/Joystick.cpp @@ -643,7 +643,7 @@ void JoyReset() } //=========================================================================== -BYTE __stdcall JoyResetPosition(WORD, WORD, BYTE, BYTE, ULONG nExecutedCycles) +void JoyResetPosition(ULONG nExecutedCycles) { CpuCalcCycles(nExecutedCycles); g_nJoyCntrResetCycle = g_nCumulativeCycles; @@ -652,8 +652,6 @@ BYTE __stdcall JoyResetPosition(WORD, WORD, BYTE, BYTE, ULONG nExecutedCycles) CheckJoystick0(); if((joyinfo[joytype[1]] == DEVICE_JOYSTICK) || (joyinfo[joytype[1]] == DEVICE_JOYSTICK_THUMBSTICK2)) CheckJoystick1(); - - return MemReadFloatingBus(nExecutedCycles); } //=========================================================================== diff --git a/source/Joystick.h b/source/Joystick.h index 09f322c9..668d650a 100644 --- a/source/Joystick.h +++ b/source/Joystick.h @@ -30,4 +30,4 @@ void JoyLoadSnapshot(class YamlLoadHelper& yamlLoadHelper); BYTE __stdcall JoyReadButton(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); BYTE __stdcall JoyReadPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); -BYTE __stdcall JoyResetPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); +void JoyResetPosition(ULONG nExecutedCycles); diff --git a/source/Memory.cpp b/source/Memory.cpp index 88bfa9cc..5457e991 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -75,6 +75,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define SW_SLOTC3ROM (memmode & MF_SLOTC3ROM) #define SW_INTCXROM (memmode & MF_INTCXROM) #define SW_WRITERAM (memmode & MF_WRITERAM) +#define SW_IOUDIS (memmode & MF_IOUDIS) /* MEMORY MANAGEMENT SOFT SWITCHES @@ -90,6 +91,8 @@ MEMORY MANAGEMENT SOFT SWITCHES $C009 W ALTZPON Enable aux memory from $0000-$01FF & avl BSR $C00A W SLOTC3ROMOFF Enable main ROM from $C300-$C3FF $C00B W SLOTC3ROMON Enable slot ROM from $C300-$C3FF + $C07E W IOUDIS [Enhanced //e] On: disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch + $C07F W IOUDIS [Enhanced //e] Off: enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch VIDEO SOFT SWITCHES $C00C W 80COLOFF Turn off 80 column display @@ -104,6 +107,8 @@ VIDEO SOFT SWITCHES $C055 R/W PAGE2ON Select page2 display (or aux video memory) $C056 R/W HIRESOFF Select low resolution graphics $C057 R/W HIRESON Select high resolution graphics + $C05E R/W DHIRESOFF Select single (7M) resolution graphics + $C05F R/W DHIRESON Select double (14M) resolution graphics SOFT SWITCH STATUS FLAGS $C010 R7 AKD 1=key pressed 0=keys free (clears strobe) @@ -122,6 +127,8 @@ SOFT SWITCH STATUS FLAGS $C01D R7 HIRES 1=high resolution graphics 0=low resolution $C01E R7 ALTCHARSET 1=alt character set on 0=alt char set off $C01F R7 80COL 1=80 col display on 0=80 col display off + $C07E R7 RDIOUDIS [Enhanced //e] 1=IOUDIS off 0=IOUDIS on + $C07F R7 RDDHIRES [Enhanced //e] 1=DHIRES on 0=DHIRES off */ @@ -211,6 +218,9 @@ static UINT g_uActiveBank = 0; // 0 = aux 64K for: //e extended 80 Col card, static LPBYTE RWpages[kMaxExMemoryBanks]; // pointers to RW memory banks #endif +static const UINT kNumAnnunciators = 4; +static bool g_Annunciator[kNumAnnunciators] = {}; + BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles); //============================================================================= @@ -468,8 +478,9 @@ static BYTE __stdcall IORead_C05x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG case 0xB: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xC: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); - case 0xE: return VideoSetMode(pc, addr, bWrite, d, nExecutedCycles); - case 0xF: return VideoSetMode(pc, addr, bWrite, d, nExecutedCycles); + case 0xE: // fall through... + case 0xF: return (!SW_IOUDIS) ? VideoSetMode(pc, addr, bWrite, d, nExecutedCycles) + : IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); } return 0; @@ -493,8 +504,9 @@ static BYTE __stdcall IOWrite_C05x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON case 0xB: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xC: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); - case 0xE: return VideoSetMode(pc, addr, bWrite, d, nExecutedCycles); - case 0xF: return VideoSetMode(pc, addr, bWrite, d, nExecutedCycles); + case 0xE: // fall through... + case 0xF: return (!SW_IOUDIS) ? VideoSetMode(pc, addr, bWrite, d, nExecutedCycles) + : IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); } return 0; @@ -540,9 +552,12 @@ static BYTE __stdcall IOWrite_C06x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON static BYTE __stdcall IORead_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles) { + // Apple//e TRM, pg-258: "Reading or writing any address in the range $C070-$C07F also triggers the paddle timer and resets the VBLINT(*)." (*) //c only! + JoyResetPosition(nExecutedCycles); //$C07X Analog input reset + switch (addr & 0xf) { - case 0x0: return JoyResetPosition(pc, addr, bWrite, d, nExecutedCycles); //$C070 Analog input reset + case 0x0: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0x1: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0x2: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0x3: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); @@ -556,8 +571,10 @@ static BYTE __stdcall IORead_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG case 0xB: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0xC: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); - case 0xE: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); - case 0xF: return MemReadFloatingBus(VideoGetSWDHIRES(), nExecutedCycles); + case 0xE: return IsEnhancedIIE() ? MemReadFloatingBus(SW_IOUDIS ? true : false, nExecutedCycles) // GH#636 + : IO_Null(pc, addr, bWrite, d, nExecutedCycles); + case 0xF: return IsEnhancedIIEorIIC() ? MemReadFloatingBus(VideoGetSWDHIRES(), nExecutedCycles) // GH#636 + : IO_Null(pc, addr, bWrite, d, nExecutedCycles); } return 0; @@ -565,9 +582,12 @@ static BYTE __stdcall IORead_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG static BYTE __stdcall IOWrite_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles) { + // Apple//e TRM, pg-258: "Reading or writing any address in the range $C070-$C07F also triggers the paddle timer and resets the VBLINT(*)." (*) //c only! + JoyResetPosition(nExecutedCycles); //$C07X Analog input reset + switch (addr & 0xf) { - case 0x0: return JoyResetPosition(pc, addr, bWrite, d, nExecutedCycles); + case 0x0: break; #ifdef RAMWORKS case 0x1: return MemSetPaging(pc, addr, bWrite, d, nExecutedCycles); // extended memory card set page case 0x2: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); @@ -587,15 +607,16 @@ static BYTE __stdcall IOWrite_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON case 0xB: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0xC: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); - - //http://www.kreativekorp.com/miscpages/a2info/iomemory.shtml - //- Apparently Apple//e & //c (but maybe enhanced//e not //e?) - //IOUDISON (W): $C07E Disable IOU - //IOUDISOFF (W): $C07F Enable IOU - //RDIOUDIS (R7): $C07E Status of IOU Disabling - //RDDHIRES (R7): $C07F Status of Double HiRes - case 0xE: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); // TODO: IOUDIS - case 0xF: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); // TODO: IOUDIS + case 0xE: if (IsEnhancedIIE()) + SetMemMode(memmode & ~MF_IOUDIS); // disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch + else + return IO_Null(pc, addr, bWrite, d, nExecutedCycles); + break; + case 0xF: if (IsEnhancedIIE()) + SetMemMode(memmode | MF_IOUDIS); // enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch + else + return IO_Null(pc, addr, bWrite, d, nExecutedCycles); + break; } return 0; @@ -649,12 +670,15 @@ BYTE __stdcall IO_Null(WORD programcounter, WORD address, BYTE write, BYTE value BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nExecutedCycles) { // Apple//e ROM: - // . PC=FA6F: LDA $C058 (SETAN0) - // . PC=FA72: LDA $C05A (SETAN1) - // . PC=C2B5: LDA $C05D (CLRAN2) + // . $FA6F: LDA $C058 (SETAN0) ; AN0 = TTL LO + // . $FA72: LDA $C05A (SETAN1) ; AN1 = TTL LO + // . $C2B5: LDA $C05D (CLRAN2) ;SETUP + // . $C2B8: LDA $C05F (CLRAN3) ; ANNUNCIATORS // NB. AN3: For //e & //c these locations are now used to enabled/disabled DHIRES + g_Annunciator[(address>>1) & 3] = (address&1) ? true : false; + if (address >= 0xC058 && address <= 0xC05B) { JoyportControl(address & 0x3); // AN0 and AN1 control @@ -1997,7 +2021,7 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE bool MemOptimizeForModeChanging(WORD programcounter, WORD address) { - if (IS_APPLE2E) + if (IS_APPLE2E()) { // IF THE EMULATED PROGRAM HAS JUST UPDATED THE MEMORY WRITE MODE AND IS // ABOUT TO UPDATE THE MEMORY READ MODE, HOLD OFF ON ANY PROCESSING UNTIL @@ -2034,6 +2058,13 @@ LPVOID MemGetSlotParameters(UINT uSlot) //=========================================================================== +bool MemGetAnnunciator(UINT annunciator) +{ + return g_Annunciator[annunciator]; +} + +//=========================================================================== + // NB. Don't need to save 'modechanging', as this is just an optimisation to save calling UpdatePaging() twice. // . If we were to save the state when 'modechanging' is set, then on restoring the state, the 6502 code will immediately update the read memory mode. // . This will work correctly. @@ -2044,6 +2075,7 @@ LPVOID MemGetSlotParameters(UINT uSlot) #define SS_YAML_KEY_IOSELECT_INT "IO_SELECT_InternalROM" // INTC8ROM #define SS_YAML_KEY_EXPANSIONROMTYPE "Expansion ROM Type" #define SS_YAML_KEY_PERIPHERALROMSLOT "Peripheral ROM Slot" +#define SS_YAML_KEY_ANNUNCIATOR "Annunciator" // @@ -2053,7 +2085,8 @@ static const UINT kUNIT_AUXSLOT_VER = 2; // Unit version history: // 2: Added: RGB card state -static const UINT kUNIT_VER = 2; +// 3: Extended: RGB card state ('80COL changed') +static const UINT kUNIT_CARD_VER = 3; #define SS_YAML_VALUE_CARD_80COL "80 Column" #define SS_YAML_VALUE_CARD_EXTENDED80COL "Extended 80 Column" @@ -2117,6 +2150,12 @@ void MemSaveSnapshot(YamlSaveHelper& yamlSaveHelper) yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT_INT, INTC8ROM ? 1 : 0); yamlSaveHelper.SaveUint(SS_YAML_KEY_EXPANSIONROMTYPE, (UINT) g_eExpansionRomType); yamlSaveHelper.SaveUint(SS_YAML_KEY_PERIPHERALROMSLOT, g_uPeripheralRomSlot); + + for (UINT i=0; i kUNIT_AUXSLOT_VER) + if (unitVersion < 1 || unitVersion > kUNIT_AUXSLOT_VER) throw std::string(SS_YAML_KEY_UNIT ": AuxSlot: Version mismatch"); - if (version == 1) + if (unitVersion == 1) MemLoadSnapshotAuxVer1(yamlLoadHelper); else - MemLoadSnapshotAuxVer2(yamlLoadHelper, version); + MemLoadSnapshotAuxVer2(yamlLoadHelper); return true; } diff --git a/source/Memory.h b/source/Memory.h index 847e4234..e562ed01 100644 --- a/source/Memory.h +++ b/source/Memory.h @@ -14,6 +14,7 @@ #define MF_SLOTC3ROM 0x00000100 #define MF_INTCXROM 0x00000200 #define MF_WRITERAM 0x00000400 // Language Card RAM is Write Enabled +#define MF_IOUDIS 0x00000800 // Disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch (0=on) (Enhanced //e only) #define MF_IMAGEMASK 0x000003F7 #define MF_LANGCARD_MASK (MF_WRITERAM|MF_HIGHRAM|MF_BANK2) @@ -81,11 +82,12 @@ void MemReset (); void MemResetPaging (); void MemUpdatePaging(BOOL initialize); LPVOID MemGetSlotParameters (UINT uSlot); +bool MemGetAnnunciator(UINT annunciator); std::string MemGetSnapshotUnitAuxSlotName(void); void MemSaveSnapshot(class YamlSaveHelper& yamlSaveHelper); -bool MemLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version); +bool MemLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT unitVersion); void MemSaveSnapshotAux(class YamlSaveHelper& yamlSaveHelper); -bool MemLoadSnapshotAux(class YamlLoadHelper& yamlLoadHelper, UINT version); +bool MemLoadSnapshotAux(class YamlLoadHelper& yamlLoadHelper, UINT unitVersion); BYTE __stdcall IO_Null(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles); diff --git a/source/NTSC.cpp b/source/NTSC.cpp index ea3f70b2..fe7b7c82 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -659,8 +659,8 @@ inline void updateFramebufferMonitorSingleScanline( uint16_t signal, bgra_t *pTa /* */ uint32_t *pLine0Address = getScanlineThis0Address(); /* */ uint32_t *pLine1Address = getScanlineNext1Address(); const uint32_t color0 = getScanlineColor( signal, pTable ); - const uint32_t color1 = ((color0 & 0x00fcfcfc) >> 2); // 25% Blend (original) -// const uint32_t color1 = ((color0 & 0x00fefefe) >> 1); // 50% Blend -- looks OK most of the time; Archon looks poor + const uint32_t color1 = 0; // Remove blending for consistent DHGR MIX mode (GH#631) +// const uint32_t color1 = ((color0 & 0x00fcfcfc) >> 2); // 25% Blend (original) /* */ *pLine1Address = color1 | ALPHA32_MASK; /* */ *pLine0Address = color0; @@ -1378,6 +1378,12 @@ void updateScreenDoubleHires80Simplified (long cycles6502 ) // wsUpdateVideoDblH uint8_t a = *MemGetAuxPtr(addr); uint8_t m = *MemGetMainPtr(addr); + if (RGB_IsMixModeInvertBit7()) // Invert high bit? (GH#633) + { + a ^= 0x80; + m ^= 0x80; + } + if (RGB_Is160Mode()) { int width = UpdateDHiRes160Cell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress); @@ -1591,8 +1597,24 @@ static void updateScreenSingleHires40Simplified (long cycles6502) else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START) { uint16_t addr = getVideoScannerAddressHGR(); - UpdateHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress); - g_pVideoAddress += 14; + + if (!RGB_Is560Mode()) + { + UpdateHiResCell(g_nVideoClockHorz-VIDEO_SCANNER_HORZ_START, g_nVideoClockVert, addr, g_pVideoAddress); + g_pVideoAddress += 14; + } + else // Color Burst is off - duplicate code from updateScreenSingleHires40() (GH#631) + { + uint8_t *pMain = MemGetMainPtr(addr); + uint8_t m = pMain[0]; + uint16_t bits = g_aPixelDoubleMaskHGR[m & 0x7F]; // Optimization: hgrbits second 128 entries are mirror of first 128 + if (m & 0x80) + bits = (bits << 1) | g_nLastColumnPixelNTSC; + updatePixels( bits ); + + if (g_nVideoClockHorz == (VIDEO_SCANNER_MAX_HORZ-1)) + g_nLastColumnPixelNTSC = 0; + } } } updateVideoScannerHorzEOLSimple(); diff --git a/source/RGBMonitor.cpp b/source/RGBMonitor.cpp index 3c0f5787..3312b3a6 100644 --- a/source/RGBMonitor.cpp +++ b/source/RGBMonitor.cpp @@ -738,6 +738,8 @@ void VideoInitializeOriginal(baseColors_t pBaseNtscColors) static UINT g_rgbFlags = 0; static UINT g_rgbMode = 0; static WORD g_rgbPrevAN3Addr = 0; +static bool g_rgbSet80COL = false; +static bool g_rgbInvertBit7 = false; // Video7 RGB card: // . Clock in the !80COL state to define the 2 flags: F2, F1 @@ -745,17 +747,27 @@ static WORD g_rgbPrevAN3Addr = 0; // . NB. There's a final 5th AN3 transition to set DHGR mode void RGB_SetVideoMode(WORD address) { - if ((address&~1) != 0x5E) // 0x5E or 0x5F? + if ((address&~1) == 0x0C) // 0x0C or 0x0D? (80COL) + { + g_rgbSet80COL = true; + return; + } + + if ((address&~1) != 0x5E) // 0x5E or 0x5F? (DHIRES) return; // Precondition before toggling 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)) + // . Apple II desktop sets DHGR B&W mode with HIRES off! (GH#631) + // Maybe there is no video-mode precondition? + // . After setting 80COL on/off then need a 0x5E->0x5F toggle. So if we see a 0x5F then reset (GH#633) + if ((g_uVideoMode & VF_MIXED) || (g_rgbSet80COL && address == 0x5F)) { g_rgbMode = 0; g_rgbPrevAN3Addr = 0; + g_rgbSet80COL = false; return; } @@ -767,6 +779,7 @@ void RGB_SetVideoMode(WORD address) } g_rgbPrevAN3Addr = address; + g_rgbSet80COL = false; } bool RGB_Is140Mode(void) // Extended 80-Column Text/AppleColor Card's Mode 2 @@ -789,6 +802,11 @@ bool RGB_Is560Mode(void) // Extended 80-Column Text/AppleColor Card's Mode 1 return g_rgbMode == 3; } +bool RGB_IsMixModeInvertBit7(void) +{ + return RGB_IsMixMode() && g_rgbInvertBit7; +} + void RGB_ResetState(void) { g_rgbFlags = 0; @@ -796,6 +814,11 @@ void RGB_ResetState(void) g_rgbPrevAN3Addr = 0; } +void RGB_SetInvertBit7(bool state) +{ + g_rgbInvertBit7 = state; +} + //=========================================================================== #define SS_YAML_KEY_RGB_CARD "AppleColor RGB Adaptor" @@ -804,6 +827,8 @@ void RGB_ResetState(void) #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" +#define SS_YAML_KEY_RGB_80COL_CHANGED "80COL changed" +#define SS_YAML_KEY_RGB_INVERT_BIT7 "Invert bit7" void RGB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper) { @@ -812,9 +837,11 @@ void RGB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper) 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); + yamlSaveHelper.SaveBool(SS_YAML_KEY_RGB_80COL_CHANGED, g_rgbSet80COL); + yamlSaveHelper.SaveBool(SS_YAML_KEY_RGB_INVERT_BIT7, g_rgbInvertBit7); } -void RGB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper) +void RGB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT cardVersion) { if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_RGB_CARD)) throw std::string("Card: Expected key: ") + std::string(SS_YAML_KEY_RGB_CARD); @@ -823,5 +850,11 @@ void RGB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper) g_rgbMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_RGB_MODE); g_rgbPrevAN3Addr = yamlLoadHelper.LoadUint(SS_YAML_KEY_RGB_PREVIOUS_AN3); + if (cardVersion >= 3) + { + g_rgbSet80COL = yamlLoadHelper.LoadBool(SS_YAML_KEY_RGB_80COL_CHANGED); + g_rgbInvertBit7 = yamlLoadHelper.LoadBool(SS_YAML_KEY_RGB_INVERT_BIT7); + } + yamlLoadHelper.PopMap(); } diff --git a/source/RGBMonitor.h b/source/RGBMonitor.h index 0f7a16b8..14cb72c0 100644 --- a/source/RGBMonitor.h +++ b/source/RGBMonitor.h @@ -13,7 +13,9 @@ bool RGB_Is140Mode(void); bool RGB_Is160Mode(void); bool RGB_IsMixMode(void); bool RGB_Is560Mode(void); +bool RGB_IsMixModeInvertBit7(void); void RGB_ResetState(void); +void RGB_SetInvertBit7(bool state); void RGB_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper); -void RGB_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper); +void RGB_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT cardVersion); diff --git a/source/SaveState.cpp b/source/SaveState.cpp index f566a9dd..11537916 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -64,7 +64,11 @@ static YamlHelper yamlHelper; #define SS_FILE_VER 2 -#define UNIT_APPLE2_VER 2 +// Unit version history: +// v2: Extended: keyboard (added 'Key Waiting'), memory (LC mem type for II/II+, inverted MF_INTCXROM bit) +// v3: Extended: memory (added 'AnnunciatorN') +#define UNIT_APPLE2_VER 3 + #define UNIT_SLOTS_VER 1 //----------------------------------------------------------------------------- diff --git a/source/Video.cpp b/source/Video.cpp index 6185f131..f65d2168 100644 --- a/source/Video.cpp +++ b/source/Video.cpp @@ -127,7 +127,7 @@ static const bool g_bVideoScannerNTSC = true; // NTSC video scanning (or PAL) bool g_bShowPrintScreenWarningDialog = true; void Util_MakeScreenShotFileName( char *pFinalFileName_ ); bool Util_TestScreenShotFileName( const char *pFileName ); - void Video_SaveScreenShot( const char *pScreenShotFileName, const VideoScreenShot_e ScreenShotType ); + void Video_SaveScreenShot( const VideoScreenShot_e ScreenShotType, const char *pScreenShotFileName ); void Video_MakeScreenShot( FILE *pFile, const VideoScreenShot_e ScreenShotType ); void videoCreateDIBSection(); @@ -959,10 +959,20 @@ void Video_TakeScreenShot( const VideoScreenShot_e ScreenShotType ) g_nLastScreenShot++; } - Video_SaveScreenShot( sScreenShotFileName, ScreenShotType ); + Video_SaveScreenShot( ScreenShotType, sScreenShotFileName ); g_nLastScreenShot++; } +void Video_RedrawAndTakeScreenShot( const char* pScreenshotFilename ) +{ + _ASSERT(pScreenshotFilename); + if (!pScreenshotFilename) + return; + + VideoRedrawScreen(); + Video_SaveScreenShot( SCREENSHOT_560x384, pScreenshotFilename ); +} + WinBmpHeader_t g_tBmpHeader; #if SCREENSHOT_TGA @@ -1121,7 +1131,7 @@ static void Video_MakeScreenShot(FILE *pFile, const VideoScreenShot_e ScreenShot } //=========================================================================== -static void Video_SaveScreenShot( const char *pScreenShotFileName, const VideoScreenShot_e ScreenShotType ) +static void Video_SaveScreenShot( const VideoScreenShot_e ScreenShotType, const char *pScreenShotFileName ) { FILE *pFile = fopen( pScreenShotFileName, "wb" ); if( pFile ) diff --git a/source/Video.h b/source/Video.h index 69888c14..2a7e2253 100644 --- a/source/Video.h +++ b/source/Video.h @@ -204,7 +204,8 @@ enum VideoScreenShot_e SCREENSHOT_560x384 = 0, SCREENSHOT_280x192 }; -void Video_TakeScreenShot( VideoScreenShot_e iScreenShotType ); +void Video_TakeScreenShot( VideoScreenShot_e ScreenShotType ); +void Video_RedrawAndTakeScreenShot( const char* pScreenshotFilename ); void Video_SetBitmapHeader( WinBmpHeader_t *pBmp, int nWidth, int nHeight, int nBitsPerPixel ); BYTE VideoSetMode(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);