diff --git a/Makefile b/Makefile index 27041df..9006897 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/src/apple2.cpp b/src/apple2.cpp index 36bf0b0..b15113f 100644 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -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 #include -#include #include #include #include #include +#include #include -#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); diff --git a/src/applevideo.cpp b/src/applevideo.cpp deleted file mode 100644 index 54cbdb1..0000000 --- a/src/applevideo.cpp +++ /dev/null @@ -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 -// -// 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 // for memset() -#include -#include // 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> 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> 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> 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> 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> 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--; - } -} - diff --git a/src/applevideo.h b/src/applevideo.h deleted file mode 100644 index e0707ac..0000000 --- a/src/applevideo.h +++ /dev/null @@ -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__ diff --git a/src/dis65c02.cpp b/src/dis65c02.cpp index 4841630..5869781 100644 --- a/src/dis65c02.cpp +++ b/src/dis65c02.cpp @@ -6,10 +6,11 @@ // #include "dis65c02.h" + #include #include -#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 #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 diff --git a/src/gui/diskselector.cpp b/src/gui/diskselector.cpp index c605b13..dfdbd57 100644 --- a/src/gui/diskselector.cpp +++ b/src/gui/diskselector.cpp @@ -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 // // 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 // (for memset, etc... Lazy!) -#include + +#include // for memset() +#include +#include // 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> 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> 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> 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> 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> 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()); } - diff --git a/src/video.h b/src/video.h index bd510d5..3adff99 100644 --- a/src/video.h +++ b/src/video.h @@ -1,12 +1,11 @@ // -// VIDEO.H: Header file +// Apple 2/host video support // #ifndef __VIDEO_H__ #define __VIDEO_H__ #include -#include // 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__ -