mirror of
https://github.com/robmcmullen/apple2.git
synced 2024-06-18 08:29:29 +00:00
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. :-)
1018 lines
32 KiB
C++
1018 lines
32 KiB
C++
//
|
|
// 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 "video.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
|
|
|
|
static SDL_Window * sdlWindow = NULL;
|
|
static SDL_Texture * sdlTexture = NULL;
|
|
static uint32_t * scrBuffer;
|
|
static int scrPitch;
|
|
|
|
// 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--;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Prime SDL and create surfaces
|
|
//
|
|
bool InitVideo(void)
|
|
{
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
|
|
{
|
|
WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
|
|
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, 0, &sdlWindow, &sdlRenderer);
|
|
|
|
if (retVal != 0)
|
|
{
|
|
WriteLog("Video: Could not window and/or renderer: %s\n", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
// Make the scaled rendering look smoother.
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
|
// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
|
SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
|
|
|
|
// Set the application's icon & title...
|
|
SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
|
|
SDL_SetWindowIcon(sdlWindow, iconSurface);
|
|
SDL_FreeSurface(iconSurface);
|
|
SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
|
|
|
|
sdlTexture = SDL_CreateTexture(sdlRenderer,
|
|
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
|
|
VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
|
|
|
|
SetupBlurTable();
|
|
|
|
WriteLog("Video: Successfully initialized.\n");
|
|
return true;
|
|
}
|
|
|
|
|
|
//
|
|
// Free various SDL components
|
|
//
|
|
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 Apple video screen to the primary texture
|
|
//
|
|
void RenderAppleScreen(SDL_Renderer * renderer)
|
|
{
|
|
SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
|
|
RenderVideoFrame();
|
|
SDL_UnlockTexture(sdlTexture);
|
|
SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// Fullscreen <-> window switching
|
|
//
|
|
void ToggleFullScreen(void)
|
|
{
|
|
settings.fullscreen = !settings.fullscreen;
|
|
|
|
int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
|
|
SDL_ShowCursor(settings.fullscreen ? 0 : 1);
|
|
|
|
if (retVal != 0)
|
|
WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
|
|
}
|
|
|