Fixed keyboard handling, consolidated video handling.

The keyboard handler now properly emulates the //e's 2-key rollover.
However, we might have to add in a key repeat delay if it makes it so
that you can't type on the console anymore. This had to be done because
SDL will throw in key delay if you rely on it's SDL_KEYDOWN message and
this caused many games with keyboard input to be unplayable. Also, we
consolidated the video handling files, as there was very little in
video.cpp and so we moved it into applevideo.cpp and renamed that file
to video.cpp. Things are a lot cleaner as a result of this merge. :-)
This commit is contained in:
Shamus Hammons 2017-06-04 22:28:52 -05:00
parent 717eaa2219
commit 978531c95e
11 changed files with 1211 additions and 1230 deletions

View File

@ -105,9 +105,8 @@ OBJS = \
obj/font12pt.o \
obj/font14pt.o \
obj/gui.o \
\
\
obj/apple2-icon-64x64.o \
obj/applevideo.o \
obj/ay8910.o \
obj/charset.o \
obj/dis65c02.o \

View File

@ -2,7 +2,7 @@
// Apple 2 SDL Portable Apple Emulator
//
// by James Hammons
// © 2014 Underground Software
// © 2017 Underground Software
//
// Loosely based on AppleWin by Tom Charlesworth which was based on AppleWin by
// Oliver Schmidt which was based on AppleWin by Michael O'Brien. :-) Parts are
@ -34,23 +34,21 @@
#include <SDL2/SDL.h>
#include <fstream>
#include <string>
#include <iomanip>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <time.h>
#include "log.h"
#include "video.h"
#include "sound.h"
#include "settings.h"
#include "v65c02.h"
#include "applevideo.h"
#include "timing.h"
#include "floppy.h"
#include "firmware.h"
#include "floppy.h"
#include "log.h"
#include "mmu.h"
#include "settings.h"
#include "sound.h"
#include "timing.h"
#include "v65c02.h"
#include "video.h"
#include "gui/gui.h"
// Debug and misc. defines
@ -95,6 +93,12 @@ static bool pauseMode = false;
static bool fullscreenDebounce = false;
static bool capsLock = false;
static bool capsLockDebounce = false;
static bool resetKeyDown = false;
// Vars to handle the //e's 2-key rollover
static SDL_Keycode keysHeld[2];
static uint8_t keysHeldAppleCode[2];
static uint8_t keyDownCount = 0;
// Local functions
@ -110,18 +114,16 @@ static void BlinkTimer(void);
#ifdef THREADED_65C02
// Test of threaded execution of 6502
static SDL_Thread * cpuThread = NULL;
//static SDL_mutex * cpuMutex = NULL;
static SDL_cond * cpuCond = NULL;
static SDL_sem * mainSem = NULL;
static bool cpuFinished = false;
//static bool cpuSleep = false;
// NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz
// Let's try a thread...
//
// Here's how it works: Execute 1 frame's worth, then sleep. Other stuff wakes
// it up
// it up.
//
int CPUThreadFunc(void * data)
{
@ -129,26 +131,20 @@ int CPUThreadFunc(void * data)
// Also, must be created in the thread that uses it...
SDL_mutex * cpuMutex = SDL_CreateMutex();
// decrement mainSem...
//SDL_SemWait(mainSem);
#ifdef CPU_THREAD_OVERFLOW_COMPENSATION
float overflow = 0.0;
#endif
do
{
// This is never set to true anywhere...
// if (cpuSleep)
// SDL_CondWait(cpuCond, cpuMutex);
// decrement mainSem...
#ifdef THREAD_DEBUGGING
WriteLog("CPU: SDL_SemWait(mainSem);\n");
#endif
SDL_SemWait(mainSem);
// There are exactly 800 slices of 21.333 cycles per frame, so it works out
// evenly.
// There are exactly 800 slices of 21.333 cycles per frame, so it works
// out evenly.
#ifdef THREAD_DEBUGGING
WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
#endif
@ -163,6 +159,11 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
overflow -= 1.0;
}
// If the CTRL+Reset key combo is being held, make sure the RESET
// line stays asserted:
if (resetKeyDown)
mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
Execute65C02(&mainCPU, cycles);
WriteSampleToBuffer();
@ -174,7 +175,7 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
#endif
SDL_mutexP(cpuMutex);
// increment mainSem...
// increment mainSem...
#ifdef THREAD_DEBUGGING
WriteLog("CPU: SDL_SemPost(mainSem);\n");
#endif
@ -374,7 +375,7 @@ int main(int /*argc*/, char * /*argv*/[])
{
InitLog("./apple2.log");
LoadSettings();
srand(time(NULL)); // Initialize RNG
srand(time(NULL)); // Initialize RNG
// Zero out memory
memset(ram, 0, 0x10000);
@ -401,14 +402,14 @@ int main(int /*argc*/, char * /*argv*/[])
if (!InitVideo())
{
std::cout << "Aborting!" << std::endl;
printf("Could not init screen: aborting!\n");
return -1;
}
GUI::Init(sdlRenderer);
WriteLog("About to initialize audio...\n");
SoundInit();
// Have to do this *after* video init but *before* sound init...!
//Shouldn't be necessary since we're not doing emulation in the ISR...
if (settings.autoStateSaving)
{
// Load last state from file...
@ -416,16 +417,16 @@ int main(int /*argc*/, char * /*argv*/[])
WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
}
WriteLog("About to initialize audio...\n");
SoundInit();
SetupBlurTable(); // Set up the color TV emulation blur table
running = true; // Set running status...
InitializeEventList(); // Clear the event list before we use it...
SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals
running = true;
InitializeEventList();
// Set frame to fire at 1/60 s interval
SetCallbackTime(FrameCallback, 16666.66666667);
// Set up blinking at 1/4 s intervals
SetCallbackTime(BlinkTimer, 250000);
startTicks = SDL_GetTicks();
#ifdef THREADED_65C02
// Kick off the CPU...
cpuCond = SDL_CreateCond();
mainSem = SDL_CreateSemaphore(1);
cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
@ -538,16 +539,69 @@ Z $DA $9A $DA $9A
ESC $9B $9B $9B $9B No xlation
*/
//
// Apple //e scancodes. Tables are normal (0), +CTRL (1), +SHIFT (2),
// +CTRL+SHIFT (3). Order of keys is:
// Delete, left, tab, down, up, return, right, escape
// Space, single quote, comma, minus, period, slash
// Numbers 0-9
// Semicolon, equals, left bracket, backslash, right bracket, backquote
// Letters a-z (lowercase)
//
// N.B.: The Apple //e keyboard maps its shift characters like most modern US
// keyboards, so this table should suffice for the shifted keys just fine.
//
uint8_t apple2e_keycode[4][56] = {
{ // Normal
0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
0x20, 0x27, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3B, 0x3D, 0x5B, 0x5C, 0x5D, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74,
0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
},
{ // With CTRL held
0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
0x20, 0x27, 0x2C, 0x1F, 0x2E, 0x2F,
0x30, 0x31, 0x00, 0x33, 0x34, 0x35, 0x1E, 0x37, 0x38, 0x39,
0x3B, 0x3D, 0x1B, 0x1C, 0x1D, 0x60,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
},
{ // With Shift held
0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
0x20, 0x22, 0x3C, 0x5F, 0x3E, 0x3F,
0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28,
0x3A, 0x2B, 0x7B, 0x7C, 0x7D, 0x7E,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56, 0x57, 0x58, 0x59, 0x5A
},
{ // With CTRL+Shift held
0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
0x20, 0x22, 0x3C, 0x1F, 0x3E, 0x3F,
0x29, 0x21, 0x00, 0x23, 0x24, 0x25, 0x1E, 0x26, 0x2A, 0x28,
0x3A, 0x2B, 0x1B, 0x1C, 0x1D, 0x7E,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
}
};
static uint32_t frameCount = 0;
static void FrameCallback(void)
{
SDL_Event event;
uint8_t keyIndex;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
// Problem with using SDL_TEXTINPUT is that it causes key delay. :-/
// We get key delay regardless... :-/
#if 0
case SDL_TEXTINPUT:
//Need to do some key translation here, and screen out non-apple keys as well...
@ -568,6 +622,10 @@ static void FrameCallback(void)
break;
#endif
case SDL_KEYDOWN:
// We do our own repeat handling thank you very much! :-)
if (event.key.repeat != 0)
break;
// Use CTRL+SHIFT+Q to exit, as well as the usual window decoration
// method
if ((event.key.keysym.mod & KMOD_CTRL)
@ -580,196 +638,109 @@ static void FrameCallback(void)
return;
}
// CTRL+RESET key emulation (mapped to CTRL+`)
// This doesn't work...
// if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
// if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
// CTRL+RESET key emulation (mapped to CTRL+HOME)
if ((event.key.keysym.mod & KMOD_CTRL)
&& (event.key.keysym.sym == SDLK_BACKQUOTE))
&& (event.key.keysym.sym == SDLK_HOME))
{
//NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
//ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
//seems to leave the machine in an inconsistent state vis-a-vis the language card... [does it anymore?]
resetKeyDown = true;
break;
}
if (event.key.keysym.sym == SDLK_RIGHT)
lastKeyPressed = 0x15, keyDown = true;
else if (event.key.keysym.sym == SDLK_LEFT)
lastKeyPressed = 0x08, keyDown = true;
else if (event.key.keysym.sym == SDLK_UP)
lastKeyPressed = 0x0B, keyDown = true;
else if (event.key.keysym.sym == SDLK_DOWN)
lastKeyPressed = 0x0A, keyDown = true;
else if (event.key.keysym.sym == SDLK_RETURN)
lastKeyPressed = 0x0D, keyDown = true;
else if (event.key.keysym.sym == SDLK_ESCAPE)
lastKeyPressed = 0x1B, keyDown = true;
else if (event.key.keysym.sym == SDLK_BACKSPACE)
lastKeyPressed = 0x7F, keyDown = true;
// There has GOT to be a better way off mapping SDLKs to our
// keyindex. But for now, this should suffice.
keyIndex = 0xFF;
// Fix CTRL+key combo...
if (event.key.keysym.mod & KMOD_CTRL)
switch (event.key.keysym.sym)
{
if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
{
lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
keyDown = true;
//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
break;
}
case SDLK_BACKSPACE: keyIndex = 0; break;
case SDLK_LEFT: keyIndex = 1; break;
case SDLK_TAB: keyIndex = 2; break;
case SDLK_DOWN: keyIndex = 3; break;
case SDLK_UP: keyIndex = 4; break;
case SDLK_RETURN: keyIndex = 5; break;
case SDLK_RIGHT: keyIndex = 6; break;
case SDLK_ESCAPE: keyIndex = 7; break;
case SDLK_SPACE: keyIndex = 8; break;
case SDLK_QUOTE: keyIndex = 9; break;
case SDLK_COMMA: keyIndex = 10; break;
case SDLK_MINUS: keyIndex = 11; break;
case SDLK_PERIOD: keyIndex = 12; break;
case SDLK_SLASH: keyIndex = 13; break;
case SDLK_0: keyIndex = 14; break;
case SDLK_1: keyIndex = 15; break;
case SDLK_2: keyIndex = 16; break;
case SDLK_3: keyIndex = 17; break;
case SDLK_4: keyIndex = 18; break;
case SDLK_5: keyIndex = 19; break;
case SDLK_6: keyIndex = 20; break;
case SDLK_7: keyIndex = 21; break;
case SDLK_8: keyIndex = 22; break;
case SDLK_9: keyIndex = 23; break;
case SDLK_SEMICOLON: keyIndex = 24; break;
case SDLK_EQUALS: keyIndex = 25; break;
case SDLK_LEFTBRACKET: keyIndex = 26; break;
case SDLK_BACKSLASH: keyIndex = 27; break;
case SDLK_RIGHTBRACKET: keyIndex = 28; break;
case SDLK_BACKQUOTE: keyIndex = 29; break;
case SDLK_a: keyIndex = 30; break;
case SDLK_b: keyIndex = 31; break;
case SDLK_c: keyIndex = 32; break;
case SDLK_d: keyIndex = 33; break;
case SDLK_e: keyIndex = 34; break;
case SDLK_f: keyIndex = 35; break;
case SDLK_g: keyIndex = 36; break;
case SDLK_h: keyIndex = 37; break;
case SDLK_i: keyIndex = 38; break;
case SDLK_j: keyIndex = 39; break;
case SDLK_k: keyIndex = 40; break;
case SDLK_l: keyIndex = 41; break;
case SDLK_m: keyIndex = 42; break;
case SDLK_n: keyIndex = 43; break;
case SDLK_o: keyIndex = 44; break;
case SDLK_p: keyIndex = 45; break;
case SDLK_q: keyIndex = 46; break;
case SDLK_r: keyIndex = 47; break;
case SDLK_s: keyIndex = 48; break;
case SDLK_t: keyIndex = 49; break;
case SDLK_u: keyIndex = 50; break;
case SDLK_v: keyIndex = 51; break;
case SDLK_w: keyIndex = 52; break;
case SDLK_x: keyIndex = 53; break;
case SDLK_y: keyIndex = 54; break;
case SDLK_z: keyIndex = 55; break;
}
#if 1
// Fix SHIFT+key combo...
if (event.key.keysym.mod & KMOD_SHIFT)
// Stuff the key in if we have a valid one...
if (keyIndex != 0xFF)
{
if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
{
lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 0x41;
keyDown = true;
//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
break;
}
else if (event.key.keysym.sym == SDLK_1)
{
lastKeyPressed = '!';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_2)
{
lastKeyPressed = '@';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_3)
{
lastKeyPressed = '#';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_4)
{
lastKeyPressed = '$';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_5)
{
lastKeyPressed = '%';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_6)
{
lastKeyPressed = '^';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_7)
{
lastKeyPressed = '&';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_8)
{
lastKeyPressed = '*';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_9)
{
lastKeyPressed = '(';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_0)
{
lastKeyPressed = ')';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_MINUS)
{
lastKeyPressed = '_';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_EQUALS)
{
lastKeyPressed = '+';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_LEFTBRACKET)
{
lastKeyPressed = '{';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_RIGHTBRACKET)
{
lastKeyPressed = '}';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_BACKSLASH)
{
lastKeyPressed = '|';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_SEMICOLON)
{
lastKeyPressed = ':';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_QUOTE)
{
lastKeyPressed = '"';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_COMMA)
{
lastKeyPressed = '<';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_PERIOD)
{
lastKeyPressed = '>';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_SLASH)
{
lastKeyPressed = '?';
keyDown = true;
break;
}
else if (event.key.keysym.sym == SDLK_BACKQUOTE)
{
lastKeyPressed = '~';
keyDown = true;
break;
}
}
#endif
// Handle Shift, CTRL, & Shift+CTRL combos
uint8_t table = 0;
// General keys...
if (event.key.keysym.sym >= SDLK_SPACE && event.key.keysym.sym <= SDLK_z)
{
lastKeyPressed = event.key.keysym.sym;
if (event.key.keysym.mod & KMOD_CTRL)
table |= 1;
if (event.key.keysym.mod & KMOD_SHIFT)
table |= 2;
lastKeyPressed = apple2e_keycode[table][keyIndex];
keyDown = true;
// Check for Caps Lock key...
if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z && capsLock)
lastKeyPressed -= 0x20;
keyDownCount++;
// Buffer the key held. Note that the last key is always
// stuffed into keysHeld[0].
if (keyDownCount >= 2)
{
keysHeld[1] = keysHeld[0];
keysHeldAppleCode[1] = keysHeldAppleCode[0];
if (keyDownCount > 2)
keyDownCount = 2;
}
keysHeld[0] = event.key.keysym.sym;
keysHeldAppleCode[0] = lastKeyPressed;
break;
}
@ -788,15 +759,14 @@ static void FrameCallback(void)
SpawnMessage("*** RESUME ***");
}
}
// Paddle buttons 0 & 1
if (event.key.keysym.sym == SDLK_LALT)
// Buttons 0 & 1
else if (event.key.keysym.sym == SDLK_LALT)
openAppleDown = true;
if (event.key.keysym.sym == SDLK_RALT)
else if (event.key.keysym.sym == SDLK_RALT)
closedAppleDown = true;
if (event.key.keysym.sym == SDLK_F11)
dumpDis = !dumpDis; // Toggle the disassembly process
// Toggle the disassembly process
else if (event.key.keysym.sym == SDLK_F11)
dumpDis = !dumpDis;
/*else if (event.key.keysym.sym == SDLK_F9)
{
@ -809,7 +779,7 @@ static void FrameCallback(void)
// SpawnMessage("Image swapped...");
}//*/
if (event.key.keysym.sym == SDLK_F2)
else if (event.key.keysym.sym == SDLK_F2)
TogglePalette();
else if (event.key.keysym.sym == SDLK_F3)
CycleScreenTypes();
@ -841,8 +811,7 @@ static void FrameCallback(void)
fullscreenDebounce = true;
}
}
if (event.key.keysym.sym == SDLK_CAPSLOCK)
else if (event.key.keysym.sym == SDLK_CAPSLOCK)
{
if (!capsLockDebounce)
{
@ -852,38 +821,75 @@ static void FrameCallback(void)
}
break;
case SDL_KEYUP:
if (event.key.keysym.sym == SDLK_F12)
fullscreenDebounce = false;
if (event.key.keysym.sym == SDLK_CAPSLOCK)
else if (event.key.keysym.sym == SDLK_CAPSLOCK)
capsLockDebounce = false;
// Paddle buttons 0 & 1
if (event.key.keysym.sym == SDLK_LALT)
else if (event.key.keysym.sym == SDLK_LALT)
openAppleDown = false;
if (event.key.keysym.sym == SDLK_RALT)
else if (event.key.keysym.sym == SDLK_RALT)
closedAppleDown = false;
else if ((event.key.keysym.mod & KMOD_CTRL)
&& (event.key.keysym.sym == SDLK_HOME))
resetKeyDown = false;
else
{
// Handle key buffering 'key up' event (2 key rollover)
if ((keyDownCount == 1) && (event.key.keysym.sym == keysHeld[0]))
{
keyDownCount--;
}
else if (keyDownCount == 2)
{
if (event.key.keysym.sym == keysHeld[0])
{
keyDownCount--;
keysHeld[0] = keysHeld[1];
keysHeldAppleCode[0] = keysHeldAppleCode[1];
}
else if (event.key.keysym.sym == keysHeld[1])
{
keyDownCount--;
}
}
}
break;
case SDL_MOUSEBUTTONDOWN:
GUI::MouseDown(event.motion.x, event.motion.y, event.motion.state);
break;
case SDL_MOUSEBUTTONUP:
GUI::MouseUp(event.motion.x, event.motion.y, event.motion.state);
break;
case SDL_MOUSEMOTION:
GUI::MouseMove(event.motion.x, event.motion.y, event.motion.state);
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_LEAVE)
GUI::MouseMove(0, 0, 0);
break;
case SDL_QUIT:
running = false;
}
}
// Stuff the Apple keyboard buffer, if any keys are pending
// N.B.: May have to simulate the key repeat delay too
if (keyDownCount > 0)
{
lastKeyPressed = keysHeldAppleCode[0];
keyDown = true;
}
// Handle power request from the GUI
if (powerStateChangeRequested)
{
@ -904,8 +910,8 @@ static void FrameCallback(void)
powerStateChangeRequested = false;
}
RenderVideoFrame(); // Render Apple screen to buffer
RenderScreenBuffer(); // Render buffer to host screen
// Render the Apple screen + GUI overlay
RenderAppleScreen(sdlRenderer);
GUI::Render(sdlRenderer);
SDL_RenderPresent(sdlRenderer);
SetCallbackTime(FrameCallback, 16666.66666667);
@ -928,7 +934,7 @@ if (counter == 60)
// This is the problem: If you set the interval to 16, it runs faster than
// 1/60s per frame. If you set it to 17, it runs slower. What we need is to
// have it do 16 for one frame, then 17 for two others. Then it should average
// out to 1/60s per frame every 3 frames.
// out to 1/60s per frame every 3 frames. [And now it does!]
frameCount = (frameCount + 1) % 3;
uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);

View File

@ -1,923 +0,0 @@
//
// Apple 2 video support
//
// All the video modes that a real Apple 2 supports are handled here
//
// by James Hammons
// (c) 2005-2017 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 12/01/2005 Added color TV/monochrome emulation to hi-res code
// JLH 12/09/2005 Cleaned up color TV emulation code
// JLH 12/09/2005 Fixed lo-res color TV/mono emulation modes
//
// STILL TO DO:
//
// - Fix LoRes mode green mono to skip every other scanline instead of fill
// like white mono does [DONE]
// - Double HiRes [DONE]
// - 80 column text [DONE]
// - Fix OSD text display so that it's visible no matter what background is there [DONE]
//
// Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
#include "applevideo.h"
#include <string.h> // for memset()
#include <stdio.h>
#include <stdarg.h> // for va_* stuff
#include "apple2.h"
#include "charset.h"
#include "video.h"
#include "gui/font14pt.h"
#include "gui/gui.h"
/* Reference: Technote tn-iigs-063 "Master Color Values"
Color Color Register LR HR DHR Master Color R,G,B
Name Value # # # Value
----------------------------------------------------
Black 0 0 0,4 0 $0000 (0,0,0)
(Magenta) Deep Red 1 1 1 $0D03 (D,0,3)
Dark Blue 2 2 8 $0009 (0,0,9)
(Violet) Purple 3 3 2 9 $0D2D (D,2,D)
Dark Green 4 4 4 $0072 (0,7,2)
(Gray 1) Dark Gray 5 5 5 $0555 (5,5,5)
(Blue) Medium Blue 6 6 6 C $022F (2,2,F)
(Cyan) Light Blue 7 7 D $06AF (6,A,F)
Brown 8 8 2 $0850 (8,5,0)
Orange 9 9 5 3 $0F60 (F,6,0)
(Gray 2) Light Gray A A A $0AAA (A,A,A)
Pink B B B $0F98 (F,9,8)
(Green) Light Green C C 1 6 $01D0 (1,D,0)
Yellow D D 7 $0FF0 (F,F,0)
(Aqua) Aquamarine E E E $04F9 (4,F,9)
White F F 3,7 F $0FFF (F,F,F)
LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res */
// Global variables
bool flash = false;
bool textMode = true;
bool mixedMode = false;
bool displayPage2 = false;
bool hiRes = false;
bool alternateCharset = false;
bool col80Mode = false;
// Local variables
// We set up the colors this way so that they'll be endian safe
// when we cast them to a uint32_t. Note that the format is RGBA.
// "Master Color Values" palette
static uint8_t colors[16 * 4] = {
0x00, 0x00, 0x00, 0xFF, // Black
0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
0x00, 0x00, 0x99, 0xFF, // Dark Blue
0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
0x00, 0x77, 0x22, 0xFF, // Dark Green
0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
0x88, 0x55, 0x00, 0xFF, // Brown
0xFF, 0x66, 0x00, 0xFF, // Orange
0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
0xFF, 0x99, 0x88, 0xFF, // Pink
0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
0xFF, 0xFF, 0x00, 0xFF, // Yellow
0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
0xFF, 0xFF, 0xFF, 0xFF // White
};
// This palette comes from ApplePC's colors (more realistic to my eye ;-)
static uint8_t altColors[16 * 4] = {
0x00, 0x00, 0x00, 0xFF,
0x7D, 0x20, 0x41, 0xFF,
0x41, 0x30, 0x7D, 0xFF,
0xBE, 0x51, 0xBE, 0xFF,
0x00, 0x5D, 0x3C, 0xFF,
0x7D, 0x7D, 0x7D, 0xFF,
0x41, 0x8E, 0xBA, 0xFF,
0xBE, 0xAE, 0xFB, 0xFF,
0x3C, 0x4D, 0x00, 0xFF,
0xBA, 0x6D, 0x41, 0xFF,
0x7D, 0x7D, 0x7D, 0xFF,
0xFB, 0x9E, 0xBE, 0xFF,
0x3C, 0xAA, 0x3C, 0xFF,
0xBA, 0xCB, 0x7D, 0xFF,
0x7D, 0xDB, 0xBA, 0xFF,
0xFB, 0xFB, 0xFB, 0xFF };
// Lo-res starting line addresses
static uint16_t lineAddrLoRes[24] = {
0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
// Hi-res starting line addresses
static uint16_t lineAddrHiRes[192] = {
0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
uint16_t appleHiresToMono[0x200] = {
0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
// Second half adds in the previous byte's lo pixel
0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
};
//static uint8_t blurTable[0x800][8]; // Color TV blur table
static uint8_t blurTable[0x80][8]; // Color TV blur table
static uint8_t mirrorTable[0x100];
static uint32_t * palette = (uint32_t *)altColors;
enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
static uint8_t screenType = ST_COLOR_TV;
// Local functions
static void Render40ColumnTextLine(uint8_t line);
static void Render80ColumnTextLine(uint8_t line);
static void Render40ColumnText(void);
static void Render80ColumnText(void);
static void RenderLoRes(uint16_t toLine = 24);
static void RenderHiRes(uint16_t toLine = 192);
static void RenderDHiRes(uint16_t toLine = 192);
void SetupBlurTable(void)
{
// NOTE: This table only needs to be 7 bits wide instead of 11, since the
// 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.
#if 0
// for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
{
/* uint16_t w3 = bitPat & 0x888;
uint16_t w2 = bitPat & 0x444;
uint16_t w1 = bitPat & 0x222;
uint16_t w0 = bitPat & 0x111;*/
uint16_t w3 = bitPat & 0x88;
uint16_t w2 = bitPat & 0x44;
uint16_t w1 = bitPat & 0x22;
uint16_t w0 = bitPat & 0x11;
uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
for(int8_t i=7; i>=0; i--)
{
uint8_t color = (((blurred0 >> i) & 0x01) << 3)
| (((blurred1 >> i) & 0x01) << 2)
| (((blurred2 >> i) & 0x01) << 1)
| ((blurred3 >> i) & 0x01);
blurTable[bitPat][7 - i] = color;
}
}
#else
for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
{
uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
for(int8_t i=7; i>=0; i--)
{
uint8_t color = (((blurred0 >> i) & 0x01) << 3)
| (((blurred1 >> i) & 0x01) << 2)
| (((blurred2 >> i) & 0x01) << 1)
| ((blurred3 >> i) & 0x01);
blurTable[bitPat >> 4][7 - i] = color;
}
}
#endif
for(int i=0; i<256; i++)
{
mirrorTable[i] = ((i & 0x01) << 7)
| ((i & 0x02) << 5)
| ((i & 0x04) << 3)
| ((i & 0x08) << 1)
| ((i & 0x10) >> 1)
| ((i & 0x20) >> 3)
| ((i & 0x40) >> 5)
| ((i & 0x80) >> 7);
}
}
void TogglePalette(void)
{
if (palette == (uint32_t *)colors)
{
palette = (uint32_t *)altColors;
SpawnMessage("Color TV palette");
}
else
{
palette = (uint32_t *)colors;
SpawnMessage("\"Master Color Values\" palette");
}
}
void CycleScreenTypes(void)
{
char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
screenType++;
if (screenType == ST_LAST_ENTRY)
screenType = ST_FIRST_ENTRY;
SpawnMessage("%s", scrTypeStr[screenType]);
}
static uint32_t msgTicks = 0;
static char message[4096];
void SpawnMessage(const char * text, ...)
{
va_list arg;
va_start(arg, text);
vsprintf(message, text, arg);
va_end(arg);
msgTicks = 120;
}
static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
static void DrawString(void)
{
//This approach works, and seems to be fast enough... Though it probably would
//be better to make the oversized font to match this one...
for(uint32_t x=7; x<=9; x++)
for(uint32_t y=7; y<=9; y++)
DrawString2(x, y, 0x00000000);
DrawString2(8, 8, 0x0020FF20);
}
static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
{
//uint32_t x = 8, y = 8;
uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
// uint32_t color = 0x0020FF20;
//This could be done ahead of time, instead of on each pixel...
//(Now it is!)
uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
for(uint32_t i=0; i<length; i++)
{
uint8_t c = message[i];
c = (c < 32 ? 0 : c - 32);
uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
{
for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
{
/* uint8_t fontTrans = font1[fontAddr++];
// uint32_t newTrans = (fontTrans * transparency / 255) << 24;
uint32_t newTrans = fontTrans << 24;
uint32_t pixel = newTrans | color;
*(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
// uint8_t trans = font1[fontAddr++];
uint8_t trans = font2[fontAddr++];
if (trans)
{
uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
uint8_t eBlue = (existingColor >> 16) & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eRed = existingColor & 0xFF;
//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
//Here we've modified it to have 33 levels of transparency (could have any # we want!)
//because dividing by 32 is faster than dividing by 31...!
uint8_t invTrans = 255 - trans;
uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
//THIS IS NOT ENDIAN SAFE
//NB: Setting the alpha channel here does nothing.
*(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
}
}
}
address += FONT_WIDTH;
}
}
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];
// Render character at (x, y)
for(int cy=0; cy<8; cy++)
{
for(int cx=0; cx<7; cx++)
{
uint32_t pixel = 0xFF000000;
if (alternateCharset)
{
if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
pixel = pixelOn;
if (chr < 0x80)
pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
if ((chr & 0xC0) == 0x40 && flash)
pixel = 0xFF000000;
}
else
{
if (textChar2e[(chr * 56) + cx + (cy * 7)])
pixel = pixelOn;
}
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
// QnD method to get blank alternate lines in text mode
if (screenType == ST_GREEN_MONO)
pixel = 0xFF000000;
{
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
}
}
}
}
}
static void Render80ColumnTextLine(uint8_t line)
{
uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
for(int x=0; x<80; x++)
{
#if 0
// This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
if (x > 39)
chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
#else
uint8_t chr;
if (x & 0x01)
chr = ram[lineAddrLoRes[line] + (x >> 1)];
else
chr = ram2[lineAddrLoRes[line] + (x >> 1)];
#endif
// Render character at (x, y)
for(int cy=0; cy<8; cy++)
{
for(int cx=0; cx<7; cx++)
{
uint32_t pixel = 0xFF000000;
if (alternateCharset)
{
if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
pixel = pixelOn;
if (chr < 0x80)
pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
if ((chr & 0xC0) == 0x40 && flash)
pixel = 0xFF000000;
}
else
{
if (textChar2e[(chr * 56) + cx + (cy * 7)])
pixel = pixelOn;
}
scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
// QnD method to get blank alternate lines in text mode
if (screenType == ST_GREEN_MONO)
pixel = 0xFF000000;
scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
}
}
}
}
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 RenderLoRes(uint16_t toLine/*= 24*/)
{
// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
// Also, we could set up three different Render functions depending on which
// render type was set and call it with a function pointer. Would be faster
// then the nested ifs we have now.
/*
Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
Color #s correspond to the bit patterns in reverse... Interesting!
00 00 00 -> 0 [0000] -> 0 (lores color #)
3c 4d 00 -> 8 [0001] -> 8? BROWN
00 5d 3c -> 4 [0010] -> 4? DARK GREEN
3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
41 30 7d -> 2 [0100] -> 2? DARK BLUE
7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
ba 6d 41 -> 9 [1001] -> 9? ORANGE
7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
ba cb 7d -> 13 [1011] -> 13? YELLOW
be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
fb 9e be -> 11 [1101] -> 11? PINK
be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
fb fb fb -> 15 [1111] -> 15 WHITE
*/
uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
//This is the old "perfect monitor" rendering code...
/* if (screenType != ST_COLOR_TV) // Not correct, but for now...
//if (1)
{
for(uint16_t y=0; y<toLine; y++)
{
for(uint16_t x=0; x<40; x++)
{
uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
uint32_t pixel = palette[scrByte & 0x0F];
for(int cy=0; cy<4; cy++)
for(int cx=0; cx<14; cx++)
scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
pixel = palette[scrByte >> 4];
for(int cy=4; cy<8; cy++)
for(int cx=0; cx<14; cx++)
scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
}
}
}
else//*/
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
for(uint16_t y=0; y<toLine; y++)
{
// Do top half of lores screen bytes...
uint32_t previous3Bits = 0;
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;
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
| ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
| (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
// 0ppp 1111 1111 1111 11|11 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0x7F000000) >> 24;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint8_t color = blurTable[bitPat][j];
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];
}
}
}
previous3Bits = pixels & 0x70000000;
}
else
{
for(int j=0; j<28; j++)
{
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);
}
pixels <<= 1;
}
}
}
// Now do bottom half...
previous3Bits = 0;
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;
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
| ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
| (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
// 0ppp 1111 1111 1111 11|11 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0x7F000000) >> 24;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint8_t color = blurTable[bitPat][j];
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];
}
}
}
previous3Bits = pixels & 0x70000000;
}
else
{
for(int j=0; j<28; j++)
{
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);
}
pixels <<= 1;
}
}
}
}
}
static void RenderHiRes(uint16_t toLine/*= 192*/)
{
//printf("RenderHiRes to line %u\n", toLine);
// NOTE: Not endian safe. !!! FIX !!! [DONE]
#if 0
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
#else
// Now it is. Now roll this fix into all the other places... !!! FIX !!!
// The colors are set in the 8-bit array as R G B A
uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
uint32_t * colorPtr = (uint32_t *)monoColors;
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
#endif
for(uint16_t y=0; y<toLine; y++)
{
uint16_t previousLoPixel = 0;
uint32_t previous3bits = 0;
for(uint16_t x=0; x<40; x+=2)
{
uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
pixels = previous3bits | (pixels << 14) | pixels2;
//testing (this shows on the screen, so it's OK)
//if (x == 0)
//{
// pixels = 0x7FFFFFFF;
//}
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
// 0ppp 1111 1111 1111 1111 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0x7F000000) >> 24;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint8_t color = blurTable[bitPat][j];
#if 0
//This doesn't seem to make things go any faster...
//It's the OpenGL render that's faster... Hmm...
scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
#else
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
#endif
}
}
previous3bits = pixels & 0x70000000;
}
else
{
for(int j=0; j<28; j++)
{
scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
if (screenType == ST_GREEN_MONO)
pixels &= 0x07FFFFFF;
scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
pixels <<= 1;
}
}
}
}
}
static void RenderDHiRes(uint16_t toLine/*= 192*/)
{
// Now it is. Now roll this fix into all the other places... !!! FIX !!!
// The colors are set in the 8-bit array as R G B A
uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
uint32_t * colorPtr = (uint32_t *)monoColors;
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
for(uint16_t y=0; y<toLine; y++)
{
uint32_t previous4bits = 0;
for(uint16_t x=0; x<40; x+=2)
{
uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
pixels = pixels | (mirrorTable[screenByte & 0x7F]);
screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
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
// 0ppp 1111 1111 1111 1111 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0xFE000000) >> 25;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint32_t color = palette[blurTable[bitPat][j]];
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
}
}
previous4bits = pixels & 0xF0000000;
}
else
{
for(int j=0; j<28; j++)
{
scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
if (screenType == ST_GREEN_MONO)
pixels &= 0x07FFFFFF;
scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
pixels <<= 1;
}
}
}
}
}
void RenderVideoFrame(void)
{
if (GUI::powerOnState == true)
{
if (textMode)
{
if (!col80Mode)
Render40ColumnText();
else
Render80ColumnText();
}
else
{
if (mixedMode)
{
if (hiRes)
{
RenderHiRes(160);
Render40ColumnTextLine(20);
Render40ColumnTextLine(21);
Render40ColumnTextLine(22);
Render40ColumnTextLine(23);
}
else
{
RenderLoRes(20);
Render40ColumnTextLine(20);
Render40ColumnTextLine(21);
Render40ColumnTextLine(22);
Render40ColumnTextLine(23);
}
}
else
{
if (dhires)
RenderDHiRes();
else if (hiRes)
RenderHiRes();
else
RenderLoRes();
}
}
}
else
{
memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
}
if (msgTicks)
{
DrawString();
msgTicks--;
}
}

View File

@ -1,26 +0,0 @@
//
// Apple 2 video support
//
#ifndef __APPLEVIDEO_H__
#define __APPLEVIDEO_H__
// Global variables (exported)
extern bool flash;
extern bool textMode;
extern bool mixedMode;
extern bool displayPage2;
extern bool hiRes;
extern bool alternateCharset;
extern bool col80Mode;
// Functions (exported)
void SetupBlurTable(void);
void TogglePalette(void);
void CycleScreenTypes(void);
void SpawnMessage(const char * text, ...);
void RenderVideoFrame(void);
#endif // __APPLEVIDEO_H__

View File

@ -6,10 +6,11 @@
//
#include "dis65c02.h"
#include <stdio.h>
#include <string.h>
#include "v65c02.h"
#include "log.h"
#include "v65c02.h"
// External shit
@ -79,7 +80,6 @@ static uint8_t mnemonics[256][5] = {
static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst)
{
char buf[32];
// WriteLog("%04X: ", src);
sprintf(outbuf, "%04X: ", src);
uint8_t cnt = 0;
@ -89,7 +89,6 @@ static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst)
for(uint32_t i=src; i<dst; i++)
{
// WriteLog("%02X ", mainCPU.RdMem(i));
sprintf(buf, "%02X ", mainCPU.RdMem(i));
strcat(outbuf, buf);
cnt++;
@ -97,7 +96,6 @@ static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst)
// Pad the leftover spaces...
for(int i=cnt; i<3; i++)
// WriteLog(" ");
{
sprintf(buf, " ");
strcat(outbuf, buf);
@ -114,71 +112,70 @@ int Decode65C02(char * outbuf, uint16_t pc)
uint16_t addr = pc;
uint16_t w;
uint8_t opcode = mainCPU.RdMem(addr++); // Get the opcode
uint8_t opcode = mainCPU.RdMem(addr++); // Get the opcode
switch (op_mat[opcode]) // Decode the addressing mode...
switch (op_mat[opcode]) // Decode the addressing mode...
{
case 0: // Illegal
case 0: // Illegal
sprintf(buf, "???");
break;
case 1: // Immediate
case 1: // Immediate
sprintf(buf, "%s #$%02X", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 2: // Zero page
case 2: // Zero page
sprintf(buf, "%s $%02X", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 3: // Zero page, X
case 3: // Zero page, X
sprintf(buf, "%s $%02X,X", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 4: // Zero page, Y
case 4: // Zero page, Y
sprintf(buf, "%s $%02X,Y", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 5: // Zero page indirect
case 5: // Zero page indirect
sprintf(buf, "%s ($%02X)", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 6: // Zero page, X indirect
case 6: // Zero page, X indirect
sprintf(buf, "%s ($%02X,X)", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 7: // Zero page, Y indirect
case 7: // Zero page, Y indirect
sprintf(buf, "%s ($%02X),Y", mnemonics[opcode], mainCPU.RdMem(addr++));
break;
case 8: // Absolute
case 8: // Absolute
w = mainCPU.RdMem(addr++);
w |= mainCPU.RdMem(addr++) << 8;
sprintf(buf, "%s $%04X", mnemonics[opcode], w);
break;
case 9: // Absolute, X
case 9: // Absolute, X
w = mainCPU.RdMem(addr++);
w |= mainCPU.RdMem(addr++) << 8;
sprintf(buf, "%s $%04X,X", mnemonics[opcode], w);
break;
case 10: // Absolute, Y
case 10: // Absolute, Y
w = mainCPU.RdMem(addr++);
w |= mainCPU.RdMem(addr++) << 8;
sprintf(buf, "%s $%04X,Y", mnemonics[opcode], w);
break;
case 11: // Indirect
case 11: // Indirect
w = mainCPU.RdMem(addr++);
w |= mainCPU.RdMem(addr++) << 8;
sprintf(buf, "%s ($%04X)", mnemonics[opcode], w);
break;
case 12: // Indirect, X
case 12: // Indirect, X
w = mainCPU.RdMem(addr++);
w |= mainCPU.RdMem(addr++) << 8;
sprintf(buf, "%s ($%04X,X)", mnemonics[opcode], w);
break;
case 13: // Relative
case 13: // Relative
sprintf(buf, "%s $%04X", mnemonics[opcode], addr + (int16_t)((int8_t)mainCPU.RdMem(addr)) + 1);
addr++;
break;
case 14: // Inherent
case 14: // Inherent
sprintf(buf, "%s ", mnemonics[opcode]);
break;
}
DisplayBytes(buf2, pc, addr); // Show bytes
// WriteLog("%-16s", outbuf); // Display opcode & addressing, etc.
sprintf(outbuf, "%s %-14s", buf2, buf); // Display opcode & addressing, etc.
DisplayBytes(buf2, pc, addr); // Show bytes
sprintf(outbuf, "%s %-14s", buf2, buf); // Display opcode & addressing, etc.
return addr - pc;
}

View File

@ -19,7 +19,8 @@
#include <string.h>
#include "apple2.h"
#include "log.h"
#include "applevideo.h" // For message spawning... Though there's probably a better approach than this!
#include "video.h" // For message spawning... Though there's probably a
// better approach than this!
// Useful enums

View File

@ -101,7 +101,7 @@ void DiskSelector::Init(SDL_Renderer * renderer)
WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
windowPixels[i] = 0xEF00FF00;
windowPixels[i] = 0xEF007F00;
SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
FindDisks(NULL);

View File

@ -22,7 +22,6 @@
#include "gui.h"
#include "apple2.h"
#include "applevideo.h"
#include "diskselector.h"
#include "log.h"
#include "video.h"

View File

@ -13,10 +13,10 @@
#include "mmu.h"
#include "apple2.h"
#include "applevideo.h"
#include "firmware.h"
#include "log.h"
#include "sound.h"
#include "video.h"
// Debug defines
@ -451,7 +451,9 @@ void SwitchALTCHARSET(uint16_t address, uint8_t)
uint8_t ReadKeyStrobe(uint16_t)
{
uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
// No character data is read from here, just the 'any key was pressed' signal...
// uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
uint8_t byte = (uint8_t)keyDown << 7;
keyDown = false;
return byte;
}

View File

@ -1,31 +1,936 @@
//
// VIDEO.CPP: SDL2/local hardware specific video routines
// Apple 2 video support
//
// All the video modes that a real Apple 2 supports are handled here
//
// by James Hammons
// (c) 2005-2017 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 01/04/2006 Added changelog ;-)
// JLH 01/20/2006 Cut out unnecessary buffering
// JLH 12/01/2005 Added color TV/monochrome emulation to hi-res code
// JLH 12/09/2005 Cleaned up color TV emulation code
// JLH 12/09/2005 Fixed lo-res color TV/mono emulation modes
//
// STILL TO DO:
//
// - Fix LoRes mode green mono to skip every other scanline instead of fill
// like white mono does [DONE]
// - Double HiRes [DONE]
// - 80 column text [DONE]
// - Fix OSD text display so that it's visible no matter what background is there [DONE]
//
// Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
#include "video.h"
#include <string.h> // (for memset, etc... Lazy!)
#include <malloc.h>
#include <string.h> // for memset()
#include <stdio.h>
#include <stdarg.h> // for va_* stuff
#include "apple2.h"
#include "apple2-icon-64x64.h"
#include "charset.h"
#include "log.h"
#include "settings.h"
#include "gui/font14pt.h"
#include "gui/gui.h"
/* Reference: Technote tn-iigs-063 "Master Color Values"
Color Color Register LR HR DHR Master Color R,G,B
Name Value # # # Value
----------------------------------------------------
Black 0 0 0,4 0 $0000 (0,0,0)
(Magenta) Deep Red 1 1 1 $0D03 (D,0,3)
Dark Blue 2 2 8 $0009 (0,0,9)
(Violet) Purple 3 3 2 9 $0D2D (D,2,D)
Dark Green 4 4 4 $0072 (0,7,2)
(Gray 1) Dark Gray 5 5 5 $0555 (5,5,5)
(Blue) Medium Blue 6 6 6 C $022F (2,2,F)
(Cyan) Light Blue 7 7 D $06AF (6,A,F)
Brown 8 8 2 $0850 (8,5,0)
Orange 9 9 5 3 $0F60 (F,6,0)
(Gray 2) Light Gray A A A $0AAA (A,A,A)
Pink B B B $0F98 (F,9,8)
(Green) Light Green C C 1 6 $01D0 (1,D,0)
Yellow D D 7 $0FF0 (F,F,0)
(Aqua) Aquamarine E E E $04F9 (4,F,9)
White F F 3,7 F $0FFF (F,F,F)
LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res
N.B.: These colors look like shit */
// Global variables
bool flash = false;
bool textMode = true;
bool mixedMode = false;
bool displayPage2 = false;
bool hiRes = false;
bool alternateCharset = false;
bool col80Mode = false;
SDL_Renderer * sdlRenderer = NULL;
// Local variables
// Local vars
static SDL_Window * sdlWindow = NULL;
static SDL_Texture * sdlTexture = NULL;
static uint32_t * scrBuffer;
static int scrPitch;
// Exported vars
SDL_Renderer * sdlRenderer = NULL;
uint32_t scrBuffer[VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t)];
// We set up the colors this way so that they'll be endian safe
// when we cast them to a uint32_t. Note that the format is RGBA.
// "Master Color Values" palette
static uint8_t colors[16 * 4] = {
0x00, 0x00, 0x00, 0xFF, // Black
0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
0x00, 0x00, 0x99, 0xFF, // Dark Blue
0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
0x00, 0x77, 0x22, 0xFF, // Dark Green
0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
0x88, 0x55, 0x00, 0xFF, // Brown
0xFF, 0x66, 0x00, 0xFF, // Orange
0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
0xFF, 0x99, 0x88, 0xFF, // Pink
0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
0xFF, 0xFF, 0x00, 0xFF, // Yellow
0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
0xFF, 0xFF, 0xFF, 0xFF // White
};
// This palette comes from ApplePC's colors (more realistic to my eye ;-)
static uint8_t altColors[16 * 4] = {
0x00, 0x00, 0x00, 0xFF,
0x7D, 0x20, 0x41, 0xFF,
0x41, 0x30, 0x7D, 0xFF,
0xBE, 0x51, 0xBE, 0xFF,
0x00, 0x5D, 0x3C, 0xFF,
0x7D, 0x7D, 0x7D, 0xFF,
0x41, 0x8E, 0xBA, 0xFF,
0xBE, 0xAE, 0xFB, 0xFF,
0x3C, 0x4D, 0x00, 0xFF,
0xBA, 0x6D, 0x41, 0xFF,
0x7D, 0x7D, 0x7D, 0xFF,
0xFB, 0x9E, 0xBE, 0xFF,
0x3C, 0xAA, 0x3C, 0xFF,
0xBA, 0xCB, 0x7D, 0xFF,
0x7D, 0xDB, 0xBA, 0xFF,
0xFB, 0xFB, 0xFB, 0xFF };
// Lo-res starting line addresses
static uint16_t lineAddrLoRes[24] = {
0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
// Hi-res starting line addresses
static uint16_t lineAddrHiRes[192] = {
0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
uint16_t appleHiresToMono[0x200] = {
0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
// Second half adds in the previous byte's lo pixel
0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
};
//static uint8_t blurTable[0x800][8]; // Color TV blur table
static uint8_t blurTable[0x80][8]; // Color TV blur table
static uint8_t mirrorTable[0x100];
static uint32_t * palette = (uint32_t *)altColors;
enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
static uint8_t screenType = ST_COLOR_TV;
// Local functions
static void Render40ColumnTextLine(uint8_t line);
static void Render80ColumnTextLine(uint8_t line);
static void Render40ColumnText(void);
static void Render80ColumnText(void);
static void RenderLoRes(uint16_t toLine = 24);
static void RenderHiRes(uint16_t toLine = 192);
static void RenderDHiRes(uint16_t toLine = 192);
static void RenderVideoFrame(/*uint32_t *, int*/);
void SetupBlurTable(void)
{
// NOTE: This table only needs to be 7 bits wide instead of 11, since the
// 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.
#if 0
// for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
{
/* uint16_t w3 = bitPat & 0x888;
uint16_t w2 = bitPat & 0x444;
uint16_t w1 = bitPat & 0x222;
uint16_t w0 = bitPat & 0x111;*/
uint16_t w3 = bitPat & 0x88;
uint16_t w2 = bitPat & 0x44;
uint16_t w1 = bitPat & 0x22;
uint16_t w0 = bitPat & 0x11;
uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
for(int8_t i=7; i>=0; i--)
{
uint8_t color = (((blurred0 >> i) & 0x01) << 3)
| (((blurred1 >> i) & 0x01) << 2)
| (((blurred2 >> i) & 0x01) << 1)
| ((blurred3 >> i) & 0x01);
blurTable[bitPat][7 - i] = color;
}
}
#else
for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
{
uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
for(int8_t i=7; i>=0; i--)
{
uint8_t color = (((blurred0 >> i) & 0x01) << 3)
| (((blurred1 >> i) & 0x01) << 2)
| (((blurred2 >> i) & 0x01) << 1)
| ((blurred3 >> i) & 0x01);
blurTable[bitPat >> 4][7 - i] = color;
}
}
#endif
for(int i=0; i<256; i++)
{
mirrorTable[i] = ((i & 0x01) << 7)
| ((i & 0x02) << 5)
| ((i & 0x04) << 3)
| ((i & 0x08) << 1)
| ((i & 0x10) >> 1)
| ((i & 0x20) >> 3)
| ((i & 0x40) >> 5)
| ((i & 0x80) >> 7);
}
}
void TogglePalette(void)
{
if (palette == (uint32_t *)colors)
{
palette = (uint32_t *)altColors;
SpawnMessage("Color TV palette");
}
else
{
palette = (uint32_t *)colors;
SpawnMessage("\"Master Color Values\" palette");
}
}
void CycleScreenTypes(void)
{
char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
screenType++;
if (screenType == ST_LAST_ENTRY)
screenType = ST_FIRST_ENTRY;
SpawnMessage("%s", scrTypeStr[screenType]);
}
static uint32_t msgTicks = 0;
static char message[4096];
void SpawnMessage(const char * text, ...)
{
va_list arg;
va_start(arg, text);
vsprintf(message, text, arg);
va_end(arg);
msgTicks = 120;
}
static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
static void DrawString(void)
{
//This approach works, and seems to be fast enough... Though it probably would
//be better to make the oversized font to match this one...
for(uint32_t x=7; x<=9; x++)
for(uint32_t y=7; y<=9; y++)
DrawString2(x, y, 0x00000000);
DrawString2(8, 8, 0x0020FF20);
}
static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
{
//uint32_t x = 8, y = 8;
uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
// uint32_t color = 0x0020FF20;
//This could be done ahead of time, instead of on each pixel...
//(Now it is!)
uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
for(uint32_t i=0; i<length; i++)
{
uint8_t c = message[i];
c = (c < 32 ? 0 : c - 32);
uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
{
for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
{
/* uint8_t fontTrans = font1[fontAddr++];
// uint32_t newTrans = (fontTrans * transparency / 255) << 24;
uint32_t newTrans = fontTrans << 24;
uint32_t pixel = newTrans | color;
*(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
// uint8_t trans = font1[fontAddr++];
uint8_t trans = font2[fontAddr++];
if (trans)
{
uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
uint8_t eBlue = (existingColor >> 16) & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eRed = existingColor & 0xFF;
//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
//Here we've modified it to have 33 levels of transparency (could have any # we want!)
//because dividing by 32 is faster than dividing by 31...!
uint8_t invTrans = 255 - trans;
uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
//THIS IS NOT ENDIAN SAFE
//NB: Setting the alpha channel here does nothing.
*(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
}
}
}
address += FONT_WIDTH;
}
}
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];
// Render character at (x, y)
for(int cy=0; cy<8; cy++)
{
for(int cx=0; cx<7; cx++)
{
uint32_t pixel = 0xFF000000;
if (alternateCharset)
{
if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
pixel = pixelOn;
if (chr < 0x80)
pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
if ((chr & 0xC0) == 0x40 && flash)
pixel = 0xFF000000;
}
else
{
if (textChar2e[(chr * 56) + cx + (cy * 7)])
pixel = pixelOn;
}
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
// QnD method to get blank alternate lines in text mode
if (screenType == ST_GREEN_MONO)
pixel = 0xFF000000;
{
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
}
}
}
}
}
static void Render80ColumnTextLine(uint8_t line)
{
uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
for(int x=0; x<80; x++)
{
#if 0
// This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
if (x > 39)
chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
#else
uint8_t chr;
if (x & 0x01)
chr = ram[lineAddrLoRes[line] + (x >> 1)];
else
chr = ram2[lineAddrLoRes[line] + (x >> 1)];
#endif
// Render character at (x, y)
for(int cy=0; cy<8; cy++)
{
for(int cx=0; cx<7; cx++)
{
uint32_t pixel = 0xFF000000;
if (alternateCharset)
{
if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
pixel = pixelOn;
if (chr < 0x80)
pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
if ((chr & 0xC0) == 0x40 && flash)
pixel = 0xFF000000;
}
else
{
if (textChar2e[(chr * 56) + cx + (cy * 7)])
pixel = pixelOn;
}
scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
// QnD method to get blank alternate lines in text mode
if (screenType == ST_GREEN_MONO)
pixel = 0xFF000000;
scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
}
}
}
}
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 RenderLoRes(uint16_t toLine/*= 24*/)
{
// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
// Also, we could set up three different Render functions depending on which
// render type was set and call it with a function pointer. Would be faster
// then the nested ifs we have now.
/*
Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
Color #s correspond to the bit patterns in reverse... Interesting!
00 00 00 -> 0 [0000] -> 0 (lores color #)
3c 4d 00 -> 8 [0001] -> 8? BROWN
00 5d 3c -> 4 [0010] -> 4? DARK GREEN
3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
41 30 7d -> 2 [0100] -> 2? DARK BLUE
7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
ba 6d 41 -> 9 [1001] -> 9? ORANGE
7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
ba cb 7d -> 13 [1011] -> 13? YELLOW
be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
fb 9e be -> 11 [1101] -> 11? PINK
be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
fb fb fb -> 15 [1111] -> 15 WHITE
*/
uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
//This is the old "perfect monitor" rendering code...
/* if (screenType != ST_COLOR_TV) // Not correct, but for now...
//if (1)
{
for(uint16_t y=0; y<toLine; y++)
{
for(uint16_t x=0; x<40; x++)
{
uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
uint32_t pixel = palette[scrByte & 0x0F];
for(int cy=0; cy<4; cy++)
for(int cx=0; cx<14; cx++)
scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
pixel = palette[scrByte >> 4];
for(int cy=4; cy<8; cy++)
for(int cx=0; cx<14; cx++)
scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
}
}
}
else//*/
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
for(uint16_t y=0; y<toLine; y++)
{
// Do top half of lores screen bytes...
uint32_t previous3Bits = 0;
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;
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
| ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
| (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
// 0ppp 1111 1111 1111 11|11 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0x7F000000) >> 24;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint8_t color = blurTable[bitPat][j];
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];
}
}
}
previous3Bits = pixels & 0x70000000;
}
else
{
for(int j=0; j<28; j++)
{
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);
}
pixels <<= 1;
}
}
}
// Now do bottom half...
previous3Bits = 0;
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;
scrByte1 = mirrorNybble[scrByte1];
scrByte2 = mirrorNybble[scrByte2];
// This is just a guess, but it'll have to do for now...
uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
| ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
| (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
// 0ppp 1111 1111 1111 11|11 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0x7F000000) >> 24;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint8_t color = blurTable[bitPat][j];
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];
}
}
}
previous3Bits = pixels & 0x70000000;
}
else
{
for(int j=0; j<28; j++)
{
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);
}
pixels <<= 1;
}
}
}
}
}
static void RenderHiRes(uint16_t toLine/*= 192*/)
{
//printf("RenderHiRes to line %u\n", toLine);
// NOTE: Not endian safe. !!! FIX !!! [DONE]
#if 0
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
#else
// Now it is. Now roll this fix into all the other places... !!! FIX !!!
// The colors are set in the 8-bit array as R G B A
uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
uint32_t * colorPtr = (uint32_t *)monoColors;
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
#endif
for(uint16_t y=0; y<toLine; y++)
{
uint16_t previousLoPixel = 0;
uint32_t previous3bits = 0;
for(uint16_t x=0; x<40; x+=2)
{
uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
previousLoPixel = (screenByte << 2) & 0x0100;
pixels = previous3bits | (pixels << 14) | pixels2;
//testing (this shows on the screen, so it's OK)
//if (x == 0)
//{
// pixels = 0x7FFFFFFF;
//}
// We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
// 0ppp 1111 1111 1111 1111 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0x7F000000) >> 24;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint8_t color = blurTable[bitPat][j];
#if 0
//This doesn't seem to make things go any faster...
//It's the OpenGL render that's faster... Hmm...
scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
#else
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
#endif
}
}
previous3bits = pixels & 0x70000000;
}
else
{
for(int j=0; j<28; j++)
{
scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
if (screenType == ST_GREEN_MONO)
pixels &= 0x07FFFFFF;
scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
pixels <<= 1;
}
}
}
}
}
static void RenderDHiRes(uint16_t toLine/*= 192*/)
{
// Now it is. Now roll this fix into all the other places... !!! FIX !!!
// The colors are set in the 8-bit array as R G B A
uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
uint32_t * colorPtr = (uint32_t *)monoColors;
uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
for(uint16_t y=0; y<toLine; y++)
{
uint32_t previous4bits = 0;
for(uint16_t x=0; x<40; x+=2)
{
uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
pixels = pixels | (mirrorTable[screenByte & 0x7F]);
screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
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
// 0ppp 1111 1111 1111 1111 1111 1111 1111
// 31 27 23 19 15 11 7 3 0
if (screenType == ST_COLOR_TV)
{
for(uint8_t i=0; i<7; i++)
{
uint8_t bitPat = (pixels & 0xFE000000) >> 25;
pixels <<= 4;
for(uint8_t j=0; j<4; j++)
{
uint32_t color = palette[blurTable[bitPat][j]];
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
}
}
previous4bits = pixels & 0xF0000000;
}
else
{
for(int j=0; j<28; j++)
{
scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
if (screenType == ST_GREEN_MONO)
pixels &= 0x07FFFFFF;
scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
pixels <<= 1;
}
}
}
}
}
void RenderVideoFrame(void)
{
if (GUI::powerOnState == true)
{
if (textMode)
{
if (!col80Mode)
Render40ColumnText();
else
Render80ColumnText();
}
else
{
if (mixedMode)
{
if (hiRes)
{
RenderHiRes(160);
Render40ColumnTextLine(20);
Render40ColumnTextLine(21);
Render40ColumnTextLine(22);
Render40ColumnTextLine(23);
}
else
{
RenderLoRes(20);
Render40ColumnTextLine(20);
Render40ColumnTextLine(21);
Render40ColumnTextLine(22);
Render40ColumnTextLine(23);
}
}
else
{
if (dhires)
RenderDHiRes();
else if (hiRes)
RenderHiRes();
else
RenderLoRes();
}
}
}
else
{
memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
}
if (msgTicks)
{
DrawString();
msgTicks--;
}
}
//
@ -39,7 +944,8 @@ bool InitVideo(void)
return false;
}
int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
// int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
if (retVal != 0)
{
@ -62,6 +968,8 @@ bool InitVideo(void)
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
SetupBlurTable();
WriteLog("Video: Successfully initialized.\n");
return true;
}
@ -73,19 +981,23 @@ bool InitVideo(void)
void VideoDone(void)
{
WriteLog("Video: Shutting down SDL...\n");
SDL_DestroyTexture(sdlTexture);
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(sdlWindow);
SDL_Quit();
WriteLog("Video: Done.\n");
}
//
// Render the screen buffer to the primary screen surface
// Render the Apple video screen to the primary texture
//
void RenderScreenBuffer(void)
void RenderAppleScreen(SDL_Renderer * renderer)
{
SDL_UpdateTexture(sdlTexture, NULL, scrBuffer, VIRTUAL_SCREEN_WIDTH * sizeof(Uint32));
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
RenderVideoFrame();
SDL_UnlockTexture(sdlTexture);
SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
}
@ -103,4 +1015,3 @@ void ToggleFullScreen(void)
WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
}

View File

@ -1,12 +1,11 @@
//
// VIDEO.H: Header file
// Apple 2/host video support
//
#ifndef __VIDEO_H__
#define __VIDEO_H__
#include <SDL2/SDL.h>
#include <stdint.h> // For uint32_t
// These are double the normal width because we use sub-pixel rendering.
//#define VIRTUAL_SCREEN_WIDTH 280
@ -14,16 +13,32 @@
//#define VIRTUAL_SCREEN_HEIGHT 192
#define VIRTUAL_SCREEN_HEIGHT 384
// Global variables (exported)
extern bool flash;
extern bool textMode;
extern bool mixedMode;
extern bool displayPage2;
extern bool hiRes;
extern bool alternateCharset;
extern bool col80Mode;
extern SDL_Renderer * sdlRenderer;
// Functions (exported)
//void SetupBlurTable(void);
void TogglePalette(void);
void CycleScreenTypes(void);
void SpawnMessage(const char * text, ...);
bool InitVideo(void);
void VideoDone(void);
void RenderScreenBuffer(void);
void RenderAppleScreen(SDL_Renderer *);
void ToggleFullScreen(void);
// Exported crap
extern SDL_Renderer * sdlRenderer;
extern uint32_t scrBuffer[];
extern uint32_t mainScrBuffer[];
//extern uint32_t * scrBuffer;
//extern int scrPitch;
#endif // __VIDEO_H__