Added initial emulator configuration window, cleanup of settings code.

The settings code now actually saves updated settings to the config
file.  Also wrote the beginnings of a proper Config window, and a few
of the options actually work (which ones work is left as an exercise
for the reader).
This commit is contained in:
Shamus Hammons 2019-04-16 22:29:58 -05:00
parent efc3eeaa6f
commit c970c23a2e
23 changed files with 566 additions and 352 deletions

View File

@ -101,6 +101,7 @@ LIBS = $(SDL_LIBS) -lstdc++ -lz -lm $(GLLIB) -pg
INCS = -I. -I./src
OBJS = \
obj/config.o \
obj/diskselector.o \
obj/font10pt.o \
obj/font12pt.o \
@ -122,7 +123,6 @@ OBJS = \
obj/log.o \
obj/mmu.o \
obj/mockingboard.o \
obj/sdlemu_config.o \
obj/settings.o \
obj/sound.o \
obj/timing.o \

View File

@ -3,22 +3,28 @@
#
# Apple ROM paths
#default
#Not used anymore
#BIOSROM = ./ROMs/apple2e-enhanced.rom
#BIOSROM = ./ROMs/apple2e.rom
#Not used anymore
#diskROM = ./ROMs/disk.rom
#ROMs = ./ROMs
#default
#disks = ./disks
#harddrive = ./disks/Pitch-Dark-20180731.2mg
# Auto state loading/saving upon starting/quitting Apple2 (1 - use, 0 - don't use)
# Defaults
disks = ./disks/
#These are the defaults--we don't advertise it just yet... ;-)
autoSaveState = 0
#autoStateFilename = ./apple2auto.state
harddrive1 = Pitch-Dark-20180731.2mg
harddrive2 =
harddrive3 =
harddrive4 =
harddrive5 =
harddrive6 =
harddrive7 =
# Auto state loading/saving upon starting/quitting Apple2 (1-use, 0-don't use)
# These are the defaults--we don't advertise it just yet... ;-)
autoSaveState = 1
autoStateFilename = ./apple2auto.state
# OpenGL filtering type: 1 - blurry, 0 - sharp
@ -36,3 +42,10 @@ useJoystick = 0
joyport = 0
windowX = 770
windowY = 146
hardwareTypeNTSC = 1
renderType = 0
useOpenGL = 1

View File

@ -57,8 +57,9 @@
#include "sound.h"
#include "timing.h"
#include "video.h"
#include "gui/gui.h"
#include "gui/diskselector.h"
#include "gui/config.h"
#include "gui/gui.h"
// Debug and misc. defines
@ -673,9 +674,9 @@ while (pc < 0x9FF)
}
#endif
SaveSettings();
SoundDone();
VideoDone();
SaveSettings();
LogDone();
return 0;
@ -752,6 +753,10 @@ static void FrameCallback(void)
if (event.key.repeat != 0)
break;
// This breaks IMEs and the like, but we'll do simple for now
if (GUI::KeyDown(event.key.keysym.sym))
break;
// Use CTRL+SHIFT+Q to exit, as well as the usual window decoration
// method
if ((event.key.keysym.mod & KMOD_CTRL)
@ -1033,7 +1038,7 @@ else if (event.key.keysym.sym == SDLK_F9)
// Hide the mouse if it's been 1s since the last time it was moved
// N.B.: Should disable mouse hiding if it's over the GUI...
if ((hideMouseTimeout > 0) && !(GUI::sidebarState == SBS_SHOWN || DiskSelector::showWindow == true))
if ((hideMouseTimeout > 0) && !(GUI::sidebarState == SBS_SHOWN || DiskSelector::showWindow == true || Config::showWindow == true))
hideMouseTimeout--;
else if (hideMouseTimeout == 0)
{

View File

@ -31,9 +31,9 @@ uint8_t standardTMAP[160] = {
//
// Note that size *must* be a valid pointer, otherwise this will go BOOM
// sizePtr is optional
//
uint8_t * ReadFile(const char * filename, uint32_t * size)
uint8_t * ReadFile(const char * filename, uint32_t * sizePtr/*= NULL*/)
{
FILE * fp = fopen(filename, "rb");
@ -41,13 +41,16 @@ uint8_t * ReadFile(const char * filename, uint32_t * size)
return NULL;
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
uint32_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
uint8_t * buffer = (uint8_t *)malloc(*size);
fread(buffer, 1, *size, fp);
uint8_t * buffer = (uint8_t *)malloc(size);
fread(buffer, 1, size, fp);
fclose(fp);
if (sizePtr != NULL)
*sizePtr = size;
return buffer;
}

View File

@ -99,7 +99,7 @@ struct WOZ2
};
// Exported functions
uint8_t * ReadFile(const char * filename, uint32_t * size);
uint8_t * ReadFile(const char * filename, uint32_t * sizePtr = NULL);
void InitWOZ2Headers(WOZ2 &);
uint8_t * InitWOZ(uint32_t * pSize = NULL);
uint8_t * UpconvertWOZ1ToWOZ2(uint8_t * woz1Data, uint32_t woz1Size, uint32_t * newSize);

View File

@ -52,19 +52,19 @@ enum { DSS_SHOWING, DSS_HIDING, DSS_SHOWN, DSS_HIDDEN, DSS_LSB_SHOWING, DSS_LSB_
#define DS_YPOS ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
bool entered = false;
int driveNumber;
int diskSelectorState = DSS_HIDDEN;
int diskSelected = -1;
int lastDiskSelected = -1;
int numColumns;
int colStart = 0;
int dxLeft = 0;
int dxRight = 0;
int rsbPos = DS_WIDTH;
int lsbPos = -40;
int textScrollCount = 0;
bool refresh = false;
static bool entered = false;
static int driveNumber;
static int diskSelectorState = DSS_HIDDEN;
static int diskSelected = -1;
static int lastDiskSelected = -1;
static int numColumns;
static int colStart = 0;
static int dxLeft = 0;
static int dxRight = 0;
static int rsbPos = DS_WIDTH;
static int lsbPos = -40;
static int textScrollCount = 0;
static bool refresh = false;
/*
So, how this will work for multiple columns, where the number of columns is greater than 3, is to have an arrow button pop up on the left or right hand side (putting the mouse on the left or right side of the disk selector activates (shows) the button, if such a move can be made. Button hides when the mouse moves out of the hot zone or when it has no more effect.
@ -110,9 +110,7 @@ struct FileStruct
static SDL_Texture * window = NULL;
static SDL_Texture * charStamp = NULL;
static uint32_t windowPixels[DS_WIDTH * DS_HEIGHT];
static uint32_t stamp[FONT_WIDTH * FONT_HEIGHT];
SDL_Texture * scrollLeftIcon = NULL;
SDL_Texture * scrollRightIcon = NULL;
bool DiskSelector::showWindow = false;
@ -123,8 +121,6 @@ void DiskSelector::Init(SDL_Renderer * renderer)
{
window = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
SDL_TEXTUREACCESS_TARGET, DS_WIDTH, DS_HEIGHT);
charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
if (!window)
{
@ -135,16 +131,13 @@ void DiskSelector::Init(SDL_Renderer * renderer)
if (SDL_SetTextureBlendMode(window, SDL_BLENDMODE_BLEND) == -1)
WriteLog("GUI (DiskSelector): Could not set blend mode for window.\n");
if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
scrollLeftIcon = GUI::CreateTexture(renderer, &scroll_left);
scrollRightIcon = GUI::CreateTexture(renderer, &scroll_right);
for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
windowPixels[i] = 0xEF007F00;
SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
SDL_UpdateTexture(window, NULL, windowPixels, DS_WIDTH * sizeof(Uint32));
FindDisks();
DrawFilenames(renderer);
}
@ -393,7 +386,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
if (i >= fsList[partialColStart + y].image.length())
break;
DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
GUI::DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
}
}
}
@ -408,7 +401,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
if (i >= fsList[fsStart + y].image.length())
break;
DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
GUI::DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
}
}
@ -432,7 +425,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
break;
bool invert = (diskSelected == (int)fsStart ? true : false);
DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
GUI::DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
}
count++;
@ -452,7 +445,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
if (i >= fsList[diskSelected].image.length())
break;
DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
GUI::DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
}
}
@ -461,22 +454,6 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
}
void DiskSelector::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*=false*/)
{
uint32_t inv = (invert ? 0x000000FF : 0x00000000);
uint32_t pixel = 0xFFFFC000; // RRGGBBAA
uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
SDL_Rect dst;
dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
stamp[i] = (pixel | ptr[i]) ^ inv;
SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
SDL_RenderCopy(renderer, charStamp, NULL, &dst);
}
void DiskSelector::ShowWindow(int drive)
{
diskSelectorState = DSS_SHOWN;
@ -486,6 +463,18 @@ void DiskSelector::ShowWindow(int drive)
}
void DiskSelector::HideWindow(void)
{
diskSelectorState = DSS_HIDDEN;
dxLeft = 0;
dxRight = 0;
rsbPos = DS_WIDTH;
lsbPos = -40;
showWindow = false;
refresh = true;
}
void DiskSelector::MouseDown(int32_t x, int32_t y, uint32_t buttons)
{
if (!showWindow || !entered)
@ -689,7 +678,7 @@ void DiskSelector::HandleGUIState(void)
void DiskSelector::HandleSelection(SDL_Renderer * renderer)
{
SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
SDL_UpdateTexture(window, NULL, windowPixels, DS_WIDTH * sizeof(Uint32));
DrawFilenames(renderer);
refresh = false;
}

View File

@ -21,8 +21,8 @@ class DiskSelector
static bool CheckManifest(const char *, DiskSet *);
static bool HasLegalExtension(const char *);
static void DrawFilenames(SDL_Renderer *);
static void DrawCharacter(SDL_Renderer *, int, int, uint8_t, bool inv=false);
static void ShowWindow(int);
static void HideWindow(void);
static void MouseDown(int32_t, int32_t, uint32_t);
static void MouseUp(int32_t, int32_t, uint32_t);
static void MouseMove(int32_t, int32_t, uint32_t);

View File

@ -3,7 +3,7 @@
//
// Graphical User Interface support
// by James Hammons
// © 2014 Underground Software
// © 2014-2019 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
@ -21,9 +21,13 @@
//
#include "gui.h"
#include <vector>
#include "apple2.h"
#include "config.h"
#include "diskselector.h"
#include "elements.h"
#include "floppydrive.h"
#include "font10pt.h"
#include "log.h"
#include "video.h"
@ -80,6 +84,14 @@ const char ejectIcon[(8 * 7) + 1] =
"@@@@@@@@"
"@@@@@@@@";
const char newDiskIcon[(30 * 6) + 1] =
"@@ @@ @@@@@ @@ @@ @@ "
"@@@ @@ @@ @@ @@ @@@@ "
"@@@@ @@ @@@@ @@ @ @@ @@@@@@"
"@@ @@@@ @@ @@@@@@@ @@ "
"@@ @@@ @@ @@@@@@@ @@@@@ "
"@@ @@ @@@@@ @@ @@ @@@@ ";
const char driveLight[(5 * 5) + 1] =
" @@@ "
"@@@@@"
@ -88,7 +100,6 @@ const char driveLight[(5 * 5) + 1] =
" @@@ ";
SDL_Texture * GUI::overlay = NULL;
SDL_Rect GUI::olDst;
int GUI::sidebarState = SBS_HIDDEN;
@ -117,10 +128,16 @@ const char iconHelp[7][80] = { "Turn emulated Apple off/on",
"Configure Apple2" };
bool disk1EjectHovered = false;
bool disk2EjectHovered = false;
bool disk1NewDiskHovered = false;
bool disk2NewDiskHovered = false;
SDL_Texture * GUI::charStamp = NULL;
uint32_t GUI::stamp[FONT_WIDTH * FONT_HEIGHT];
#define SIDEBAR_X_POS (VIRTUAL_SCREEN_WIDTH - 80)
//std::vector<void *> objList;
GUI::GUI(void)
{
@ -136,6 +153,8 @@ void GUI::Init(SDL_Renderer * renderer)
{
overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
SDL_TEXTUREACCESS_TARGET, 128, 380);
charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
if (!overlay)
{
@ -146,6 +165,9 @@ void GUI::Init(SDL_Renderer * renderer)
if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
WriteLog("GUI: Could not set blend mode for overlay.\n");
if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
WriteLog("GUI: Could not set blend mode for charStamp.\n");
for(uint32_t i=0; i<128*380; i++)
texturePointer[i] = 0xB0A000A0;
@ -185,6 +207,7 @@ void GUI::Init(SDL_Renderer * renderer)
}
DiskSelector::Init(renderer);
Config::Init(renderer);
WriteLog("GUI: Successfully initialized.\n");
}
@ -206,6 +229,7 @@ SDL_Texture * GUI::CreateTexture(SDL_Renderer * renderer, const void * source)
void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
{
DiskSelector::MouseDown(x, y, buttons);
Config::MouseDown(x, y, buttons);
if (sidebarState != SBS_SHOWN)
return;
@ -231,9 +255,10 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
SpawnMessage("*** DISK #1 EJECTED ***");
}
if (!disk1EjectHovered)
if (!disk1EjectHovered && !disk1NewDiskHovered)
{
// Load the disk selector
Config::HideWindow();
DiskSelector::ShowWindow(0);
}
@ -248,9 +273,10 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
SpawnMessage("*** DISK #2 EJECTED ***");
}
if (!disk2EjectHovered)
if (!disk2EjectHovered && !disk2NewDiskHovered)
{
// Load the disk selector
Config::HideWindow();
DiskSelector::ShowWindow(1);
}
@ -271,6 +297,9 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
// Configuration
case 6:
SpawnMessage("*** CONFIGURATION ***");
// Load the configuration window
DiskSelector::HideWindow();
Config::ShowWindow();
break;
}
}
@ -279,12 +308,14 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
void GUI::MouseUp(int32_t x, int32_t y, uint32_t buttons)
{
DiskSelector::MouseUp(x, y, buttons);
Config::MouseUp(x, y, buttons);
}
void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
{
DiskSelector::MouseMove(x, y, buttons);
Config::MouseMove(x, y, buttons);
if (sidebarState != SBS_SHOWN)
{
@ -335,6 +366,16 @@ void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
&& (y >= (117 + 31 + 2))
&& (y < (117 + 31 + 2 + 7)) ? true : false);
disk1NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
&& (x < (SIDEBAR_X_POS + 24 + 6 + 30))
&& (y >= (63 + 31 + 2))
&& (y < (63 + 31 + 2 + 6)) ? true : false);
disk2NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
&& (x < (SIDEBAR_X_POS + 24 + 6 + 30))
&& (y >= (117 + 31 + 2))
&& (y < (117 + 31 + 2 + 6)) ? true : false);
if (iconSelected != lastIconSelected)
{
HandleIconSelection(sdlRenderer);
@ -355,6 +396,12 @@ void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
}
bool GUI::KeyDown(uint32_t key)
{
return Config::KeyDown(key);
}
void GUI::HandleIconSelection(SDL_Renderer * renderer)
{
// Set up drive icons in their current states
@ -409,6 +456,7 @@ void GUI::AssembleDriveIcon(SDL_Renderer * renderer, int driveNumber)
DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
DrawDriveLight(renderer, driveNumber);
DrawEjectButton(renderer, driveNumber);
DrawNewDiskButton(renderer, driveNumber);
// Set render target back to default
SDL_SetRenderTarget(renderer, NULL);
@ -430,6 +478,21 @@ void GUI::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
}
void GUI::DrawNewDiskButton(SDL_Renderer * renderer, int driveNumber)
{
if (!floppyDrive[0].IsEmpty(driveNumber))
return;
uint8_t r = 0x00, g = 0xAA, b = 0x00;
if ((driveNumber == 0 && disk1NewDiskHovered)
|| (driveNumber == 1 && disk2NewDiskHovered))
r = 0x20, g = 0xFF, b = 0x20;
DrawCharArray(renderer, newDiskIcon, 6, 31, 30, 6, r, g, b);
}
void GUI::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
{
int lightState = floppyDrive[0].DriveLightStatus(driveNumber);
@ -463,6 +526,85 @@ void GUI::DrawCharArray(SDL_Renderer * renderer, const char * array, int x,
}
void GUI::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
{
uint32_t inv = (invert ? 0x000000FF : 0x00000000);
uint32_t pixel = 0xFFFFC000; // RRGGBBAA
uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
SDL_Rect dst;
dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
stamp[i] = (pixel | ptr[i]) ^ inv;
SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
SDL_RenderCopy(renderer, charStamp, NULL, &dst);
}
//
// N.B.: This draws a char at an abosulte X/Y position, not on a grid
//
void GUI::DrawCharacterVert(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
{
uint32_t inv = (invert ? 0x000000FF : 0x00000000);
uint32_t pixel = 0xFFFFC000; // RRGGBBAA
uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
SDL_Rect dst;
dst.x = x, dst.y = y, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
SDL_Point pt = { FONT_WIDTH - 1, FONT_HEIGHT - 1 };
for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
stamp[i] = (pixel | ptr[i]) ^ inv;
SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_NONE);
SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
SDL_RenderCopyEx(renderer, charStamp, NULL, &dst, 270.0, &pt, SDL_FLIP_NONE);
SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND);
}
void GUI::DrawString(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
{
int len = strlen(s);
for(int i=0; i<len; i++)
DrawCharacter(renderer, x + i, y, s[i], invert);
}
//
// N.B.: This draws a char at an abosulte X/Y position, not on a grid
//
void GUI::DrawStringVert(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
{
int len = strlen(s);
for(int i=0; i<len; i++)
DrawCharacterVert(renderer, x, y - (FONT_WIDTH * i), s[i], invert);
}
void GUI::DrawBox(SDL_Renderer * renderer, int x, int y, int w, int h, int r/*= 0x00*/, int g/*= 0xAA*/, int b/*= 0x00*/)
{
SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
for(int i=0; i<w; i++)
{
SDL_RenderDrawPoint(renderer, x + i, y);
SDL_RenderDrawPoint(renderer, x + i, y + h - 1);
}
for(int i=0; i<h; i++)
{
SDL_RenderDrawPoint(renderer, x, y + i);
SDL_RenderDrawPoint(renderer, x + w - 1, y + i);
}
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
}
void GUI::HandleGUIState(void)
{
olDst.x += dx;
@ -500,6 +642,7 @@ void GUI::DrawSidebarIcons(SDL_Renderer * renderer)
void GUI::Render(SDL_Renderer * renderer)
{
// Sanity check
if (!overlay)
return;
@ -512,6 +655,7 @@ void GUI::Render(SDL_Renderer * renderer)
// Hmm.
DiskSelector::Render(renderer);
Config::Render(renderer);
}

View File

@ -23,12 +23,19 @@ class GUI
static void MouseDown(int32_t, int32_t, uint32_t);
static void MouseUp(int32_t, int32_t, uint32_t);
static void MouseMove(int32_t, int32_t, uint32_t);
static bool KeyDown(uint32_t);
static void HandleIconSelection(SDL_Renderer *);
static void AssembleDriveIcon(SDL_Renderer *, int);
static void DrawEjectButton(SDL_Renderer *, int);
static void DrawNewDiskButton(SDL_Renderer *, int);
static void DrawDriveLight(SDL_Renderer *, int);
static void DrawCharArray(SDL_Renderer *, const char *, int x,
int y, int w, int h, int r, int g, int b);
static void DrawCharacter(SDL_Renderer *, int, int, uint8_t, bool inv = false);
static void DrawCharacterVert(SDL_Renderer *, int, int, uint8_t, bool inv = false);
static void DrawString(SDL_Renderer *, int, int, const char *, bool inv = false);
static void DrawStringVert(SDL_Renderer *, int, int, const char *, bool inv = false);
static void DrawBox(SDL_Renderer *, int, int, int, int, int r = 0x00, int g = 0xAA, int b = 0x00);
static void HandleGUIState(void);
static void DrawSidebarIcons(SDL_Renderer *);
static void Render(SDL_Renderer *);
@ -41,6 +48,10 @@ class GUI
static int32_t iconSelected;
static bool hasKeyboardFocus;
static bool powerOnState;
private:
static SDL_Texture * charStamp;
static uint32_t stamp[];
};
#endif // __GUI_H__

View File

@ -618,10 +618,12 @@ void InstallHardDrive(uint8_t slot)
{
SlotData hd = { SlotIOR, SlotIOW, SlotROM, 0, SlotIOExtraR, SlotIOExtraW };
InstallSlotHandler(slot, &hd);
char fnBuf[MAX_PATH + 1];
// If this fails to read the file, the pointer is set to NULL
uint32_t size = 0;
hdData = ReadFile(settings.hdPath, &size);
sprintf(fnBuf, "%s%s", settings.disksPath, settings.hd[0]);
hdData = ReadFile(fnBuf, &size);
if (hdData)
WriteLog("HD: Read Hard Drive image file, %u bytes ($%X)\n", size - 0x40, size - 0x40);

View File

@ -1,151 +0,0 @@
/*
* SDLEMU library - Free sdl related functions library
* Copyrigh(c) 1999-2002 sdlemu development crew
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <list>
#include "sdlemu_config.h"
using namespace std;
class token_list
{
public:
token_list(const string &name) : m_name(name), m_value(""), m_token("") {}
void add_token_variable(const string &var) { m_token = var; }
void add_token_value(const string &value) { m_value = value; }
const string &LineName() const { return m_name; }
const string &Token() const { return m_token; }
const string &Value() const { return m_value; }
private:
std::string m_name;
std::string m_value;
std::string m_token;
};
std::list<token_list> vec;
void string_tokenize_variable()
{
list<token_list>::iterator p;
const string delim = " ";
for(p = vec.begin(); p != vec.end(); p++) {
string::size_type lastPos = (*p).LineName().find_first_not_of(delim, 0);
string::size_type pos = (*p).LineName().find_first_of(delim, lastPos);
if(string::npos != pos && string::npos != lastPos) {
string s = (*p).LineName().substr(lastPos, pos - lastPos);
(*p).add_token_variable(s);
}
}
}
void string_tokenize_value()
{
list<token_list>::iterator p;
const string delim = " =\n\t\r"; // "\r" needed for Win32 compatibility...
for(p = vec.begin(); p != vec.end(); p++) {
string::size_type lastPos = (*p).LineName().find_first_of(delim, 0);
string::size_type pos = (*p).LineName().find_first_not_of(delim, lastPos);
if(string::npos != pos && string::npos != lastPos) {
string s = (*p).LineName().substr(pos);
(*p).add_token_value(s);
}
}
}
int sdlemu_init_config(const char *filename)
{
FILE *f = fopen(filename, "r");
if(!f) return 0;
fseek(f, 0, SEEK_END);
int len = ftell(f);
fseek(f, 0, SEEK_SET);
char *s = new char[len];
fread(s, 1, len, f);
string str(s);
const string delim = "\n\r"; // "\r" needed for Win32 compatibility...
string::size_type lastPos = str.find_first_not_of(delim, 0);
string::size_type pos = str.find_first_of(delim, lastPos);
while (string::npos != pos || string::npos != lastPos) {
string string = str.substr(lastPos, pos - lastPos);
if(string[0] == '#')
{
}
else if(string[0] == '[')
{
}
else
{
vec.push_back(string);
}
lastPos = str.find_first_not_of(delim, pos);
pos = str.find_first_of(delim, lastPos);
}
string_tokenize_variable();
string_tokenize_value();
delete [] s;
fclose(f);
return 1;
}
const char *sdlemu_getval_string(const char *key_string, const char *default_string)
{
list<token_list>::iterator p;
for(p = vec.begin(); p != vec.end(); p++) {
if(strcmp((*p).Token().c_str(), key_string) == 0)
return (*p).Value().c_str();
}
return default_string;
}
int sdlemu_getval_int(const char *key_string, int default_int)
{
list<token_list>::iterator p;
for(p = vec.begin(); p != vec.end(); p++) {
if(strcmp((*p).Token().c_str(), key_string) == 0) {
const char *ret = (*p).Value().c_str();
if(ret) return atoi(ret);
}
}
return default_int;
}
int sdlemu_getval_bool(const char *key_string, int default_int)
{
list<token_list>::iterator p;
for(p = vec.begin(); p != vec.end(); p++) {
if(strcmp((*p).Token().c_str(), key_string) == 0) {
const char *ret = (*p).Value().c_str();
if(ret) return atoi(ret)>0;
}
}
return default_int;
}

View File

@ -1,17 +0,0 @@
#ifndef __SDL_CONFIG_H__
#define __SDL_CONFIG_H__
#ifdef __cplusplus
extern "C" {
#endif
int sdlemu_init_config(const char * filename);
const char * sdlemu_getval_string(const char * key_string, const char * default_string);
int sdlemu_getval_int(const char * key_string, int default_int);
int sdlemu_getval_bool(const char * key_string, int default_int);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,36 +1,50 @@
//
// SETTINGS.CPP: Game configuration loading/saving support
// settings.cpp: Apple2 configuration loading/saving support
//
// by James Hammons
// (C) 2005 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 01/04/2006 Added changelog ;-)
// (C) 2019 Underground Software
//
#include "settings.h"
#include <stdlib.h>
#include <map>
#include <string>
#include <SDL2/SDL.h>
#include "sdlemu_config.h"
#include "fileio.h"
#include "log.h"
#include "video.h"
using namespace std;
// Global variables
Settings settings;
// Private variables
static const char configPath[5][32] = {
"./apple2.cfg", // CWD
"~/apple2.cfg", // Home directory
"~/.apple2/apple2.cfg", // Home under .apple2 directory
"/etc/apple2.cfg", // /etc
"apple2.cfg" // Somewhere in the path
};
static int8_t configLoc = -1;
static std::map<std::string, std::string> keystore;
// Private function prototypes
static int8_t FindConfig(void);
static void ParseConfigFile(void);
static bool GetValue(const char *, bool);
static int GetValue(const char *, int);
static const char * GetValue(const char *, const char *);
static void SetValue(const char * key, bool value);
static void SetValue(const char * key, int value);
static void SetValue(const char * key, unsigned int value);
static void SetValue(const char * key, const char * value);
static void CheckForTrailingSlash(char * path);
static void UpdateConfigFile(void);
//
@ -38,74 +52,31 @@ static void CheckForTrailingSlash(char * path);
//
void LoadSettings(void)
{
if (sdlemu_init_config("./apple2.cfg") == 0 // CWD
&& sdlemu_init_config("~/apple2.cfg") == 0 // Home
&& sdlemu_init_config("~/.apple2/apple2.cfg") == 0 // Home under .apple2 directory
&& sdlemu_init_config("apple2.cfg") == 0) // Somewhere in the path
WriteLog("Settings: Couldn't find configuration file. Using defaults...\n");
ParseConfigFile();
settings.useJoystick = sdlemu_getval_bool("useJoystick", false);
settings.joyport = sdlemu_getval_int("joyport", 0);
settings.hardwareTypeNTSC = sdlemu_getval_bool("hardwareTypeNTSC", true);
settings.frameSkip = sdlemu_getval_int("frameSkip", 0);
settings.fullscreen = sdlemu_getval_bool("fullscreen", false);
settings.useOpenGL = sdlemu_getval_bool("useOpenGL", true);
settings.glFilter = sdlemu_getval_int("glFilterType", 0);
settings.renderType = sdlemu_getval_int("renderType", 0);
settings.autoStateSaving = sdlemu_getval_bool("autoSaveState", true);
settings.useJoystick = GetValue("useJoystick", false);
settings.joyport = GetValue("joyport", 0);
settings.hardwareTypeNTSC = GetValue("hardwareTypeNTSC", true);
settings.fullscreen = GetValue("fullscreen", false);
settings.useOpenGL = GetValue("useOpenGL", true);
settings.glFilter = GetValue("glFilterType", 0);
settings.renderType = GetValue("renderType", 0);
settings.autoStateSaving = GetValue("autoSaveState", true);
settings.winX = sdlemu_getval_int("windowX", 250);
settings.winY = sdlemu_getval_int("windowY", 100);
settings.winX = GetValue("windowX", 250);
settings.winY = GetValue("windowY", 100);
// Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
settings.p1KeyBindings[0] = sdlemu_getval_int("p1k_up", SDL_SCANCODE_UP);
settings.p1KeyBindings[1] = sdlemu_getval_int("p1k_down", SDL_SCANCODE_DOWN);
settings.p1KeyBindings[2] = sdlemu_getval_int("p1k_left", SDL_SCANCODE_LEFT);
settings.p1KeyBindings[3] = sdlemu_getval_int("p1k_right", SDL_SCANCODE_RIGHT);
settings.p1KeyBindings[4] = sdlemu_getval_int("p1k_c", SDL_SCANCODE_Z);
settings.p1KeyBindings[5] = sdlemu_getval_int("p1k_b", SDL_SCANCODE_X);
settings.p1KeyBindings[6] = sdlemu_getval_int("p1k_a", SDL_SCANCODE_C);
settings.p1KeyBindings[7] = sdlemu_getval_int("p1k_option", SDL_SCANCODE_APOSTROPHE);
settings.p1KeyBindings[8] = sdlemu_getval_int("p1k_pause", SDL_SCANCODE_RETURN);
settings.p1KeyBindings[9] = sdlemu_getval_int("p1k_0", SDL_SCANCODE_KP_0);
settings.p1KeyBindings[10] = sdlemu_getval_int("p1k_1", SDL_SCANCODE_KP_1);
settings.p1KeyBindings[11] = sdlemu_getval_int("p1k_2", SDL_SCANCODE_KP_2);
settings.p1KeyBindings[12] = sdlemu_getval_int("p1k_3", SDL_SCANCODE_KP_3);
settings.p1KeyBindings[13] = sdlemu_getval_int("p1k_4", SDL_SCANCODE_KP_4);
settings.p1KeyBindings[14] = sdlemu_getval_int("p1k_5", SDL_SCANCODE_KP_5);
settings.p1KeyBindings[15] = sdlemu_getval_int("p1k_6", SDL_SCANCODE_KP_6);
settings.p1KeyBindings[16] = sdlemu_getval_int("p1k_7", SDL_SCANCODE_KP_7);
settings.p1KeyBindings[17] = sdlemu_getval_int("p1k_8", SDL_SCANCODE_KP_8);
settings.p1KeyBindings[18] = sdlemu_getval_int("p1k_9", SDL_SCANCODE_KP_9);
settings.p1KeyBindings[19] = sdlemu_getval_int("p1k_pound", SDL_SCANCODE_KP_DIVIDE);
settings.p1KeyBindings[20] = sdlemu_getval_int("p1k_star", SDL_SCANCODE_KP_MULTIPLY);
// strcpy(settings.BIOSPath, sdlemu_getval_string("BIOSROM", "./ROMs/apple2e-enhanced.rom"));
strcpy(settings.disksPath, GetValue("disks", "./disks/"));
strcpy(settings.hd[0], GetValue("harddrive1", "./disks/Pitch-Dark-20180731.2mg"));
strcpy(settings.hd[1], GetValue("harddrive2", ""));
strcpy(settings.hd[2], GetValue("harddrive3", ""));
strcpy(settings.hd[3], GetValue("harddrive4", ""));
strcpy(settings.hd[4], GetValue("harddrive5", ""));
strcpy(settings.hd[5], GetValue("harddrive6", ""));
strcpy(settings.hd[6], GetValue("harddrive7", ""));
strcpy(settings.autoStatePath, GetValue("autoStateFilename", "./apple2auto.state"));
settings.p2KeyBindings[0] = sdlemu_getval_int("p2k_up", SDL_SCANCODE_UP);
settings.p2KeyBindings[1] = sdlemu_getval_int("p2k_down", SDL_SCANCODE_DOWN);
settings.p2KeyBindings[2] = sdlemu_getval_int("p2k_left", SDL_SCANCODE_LEFT);
settings.p2KeyBindings[3] = sdlemu_getval_int("p2k_right", SDL_SCANCODE_RIGHT);
settings.p2KeyBindings[4] = sdlemu_getval_int("p2k_c", SDL_SCANCODE_Z);
settings.p2KeyBindings[5] = sdlemu_getval_int("p2k_b", SDL_SCANCODE_X);
settings.p2KeyBindings[6] = sdlemu_getval_int("p2k_a", SDL_SCANCODE_C);
settings.p2KeyBindings[7] = sdlemu_getval_int("p2k_option", SDL_SCANCODE_APOSTROPHE);
settings.p2KeyBindings[8] = sdlemu_getval_int("p2k_pause", SDL_SCANCODE_RETURN);
settings.p2KeyBindings[9] = sdlemu_getval_int("p2k_0", SDL_SCANCODE_KP_0);
settings.p2KeyBindings[10] = sdlemu_getval_int("p2k_1", SDL_SCANCODE_KP_1);
settings.p2KeyBindings[11] = sdlemu_getval_int("p2k_2", SDL_SCANCODE_KP_2);
settings.p2KeyBindings[12] = sdlemu_getval_int("p2k_3", SDL_SCANCODE_KP_3);
settings.p2KeyBindings[13] = sdlemu_getval_int("p2k_4", SDL_SCANCODE_KP_4);
settings.p2KeyBindings[14] = sdlemu_getval_int("p2k_5", SDL_SCANCODE_KP_5);
settings.p2KeyBindings[15] = sdlemu_getval_int("p2k_6", SDL_SCANCODE_KP_6);
settings.p2KeyBindings[16] = sdlemu_getval_int("p2k_7", SDL_SCANCODE_KP_7);
settings.p2KeyBindings[17] = sdlemu_getval_int("p2k_8", SDL_SCANCODE_KP_8);
settings.p2KeyBindings[18] = sdlemu_getval_int("p2k_9", SDL_SCANCODE_KP_9);
settings.p2KeyBindings[19] = sdlemu_getval_int("p2k_pound", SDL_SCANCODE_KP_DIVIDE);
settings.p2KeyBindings[20] = sdlemu_getval_int("p2k_star", SDL_SCANCODE_KP_MULTIPLY);
strcpy(settings.BIOSPath, sdlemu_getval_string("BIOSROM", "./ROMs/apple2e-enhanced.rom"));
strcpy(settings.disksPath, sdlemu_getval_string("disks", "./disks"));
strcpy(settings.hdPath, sdlemu_getval_string("harddrive", "./disks/Pitch-Dark-20180731.2mg"));
strcpy(settings.autoStatePath, sdlemu_getval_string("autoStateFilename", "./apple2auto.state"));
CheckForTrailingSlash(settings.disksPath);
}
@ -116,6 +87,148 @@ void LoadSettings(void)
void SaveSettings(void)
{
SDL_GetWindowPosition(sdlWindow, &settings.winX, &settings.winY);
SetValue("autoSaveState", settings.autoStateSaving);
SetValue("autoStateFilename", settings.autoStatePath);
SetValue("useJoystick", settings.useJoystick);
SetValue("joyport", settings.joyport);
SetValue("hardwareTypeNTSC", settings.hardwareTypeNTSC);
SetValue("fullscreen", settings.fullscreen);
SetValue("useOpenGL", settings.useOpenGL);
SetValue("glFilterType", settings.glFilter);
SetValue("renderType", settings.renderType);
SetValue("windowX", settings.winX);
SetValue("windowY", settings.winY);
SetValue("disks", settings.disksPath);
SetValue("harddrive1", settings.hd[0]);
SetValue("harddrive2", settings.hd[1]);
SetValue("harddrive3", settings.hd[2]);
SetValue("harddrive4", settings.hd[3]);
SetValue("harddrive5", settings.hd[4]);
SetValue("harddrive6", settings.hd[5]);
SetValue("harddrive7", settings.hd[6]);
UpdateConfigFile();
}
static int8_t FindConfig()
{
for(uint8_t i=0; i<5; i++)
{
FILE * f = fopen(configPath[i], "r");
if (f != NULL)
{
fclose(f);
return i;
}
}
return -1;
}
//
// Read & parse the configuration file into our keystore
//
static void ParseConfigFile(void)
{
configLoc = FindConfig();
if (configLoc == -1)
{
WriteLog("Settings: Couldn't find configuration file. Using defaults...\n");
return;
}
char * buf = (char *)ReadFile(configPath[configLoc]);
std::string s(buf);
const std::string delim = "\n\r";
std::string::size_type start = s.find_first_not_of(delim, 0);
std::string::size_type end = s.find_first_of(delim, start);
while ((start != std::string::npos) || (end != std::string::npos))
{
std::string sub = s.substr(start, end - start);
if ((sub[0] != '#') && (sub[0] != '['))
{
std::string::size_type kStart = sub.find_first_not_of(" ", 0);
std::string::size_type kEnd = sub.find_first_of(" ", kStart);
std::string::size_type vStart = sub.find_first_of(" =\t\n\r", 0);
std::string::size_type vEnd = sub.find_first_not_of(" =\t\n\r", vStart);
if ((kStart != std::string::npos) && (kEnd != std::string::npos)
&& (vStart != std::string::npos) && (vEnd != std::string::npos))
{
keystore[sub.substr(kStart, kEnd - kStart)] = sub.substr(vEnd);
}
}
start = s.find_first_not_of(delim, end);
end = s.find_first_of(delim, start);
}
free(buf);
}
static bool GetValue(const char * key, bool def)
{
if (keystore.find(key) == keystore.end())
return def;
return (atoi(keystore[key].c_str()) == 0 ? false : true);
}
static int GetValue(const char * key, int def)
{
if (keystore.find(key) == keystore.end())
return def;
return atoi(keystore[key].c_str());
}
static const char * GetValue(const char * key, const char * def)
{
if (keystore.find(key) == keystore.end())
return def;
return keystore[key].c_str();
}
static void SetValue(const char * key, bool value)
{
keystore[key] = (value ? "1" : "0");
}
static void SetValue(const char * key, int value)
{
char buf[64];
sprintf(buf, "%i", value);
keystore[key] = buf;
}
static void SetValue(const char * key, unsigned int value)
{
char buf[64];
sprintf(buf, "%u", value);
keystore[key] = buf;
}
static void SetValue(const char * key, const char * value)
{
keystore[key] = value;
}
@ -124,8 +237,85 @@ void SaveSettings(void)
//
static void CheckForTrailingSlash(char * path)
{
if (strlen(path) > 0)
if (path[strlen(path) - 1] != '/')
strcat(path, "/"); // NOTE: Possible buffer overflow
uint32_t len = strlen(path);
// If the length is greater than zero, and the last char in the string is
// not a forward slash, and there's room for one more character, then add a
// trailing slash.
if ((len > 0) && (path[len - 1] != '/') && (len < MAX_PATH))
strcat(path, "/");
}
//
// Update the values in the config file (if one exists) with updated values in
// the keystore.
//
static void UpdateConfigFile(void)
{
// Sanity check
if (configLoc == -1)
{
WriteLog("Settings: Creating default config...\n");
configLoc = 0;
}
char * buf = (char *)ReadFile(configPath[configLoc]);
FILE * f = fopen(configPath[configLoc], "w");
if (f == NULL)
{
WriteLog("Settings: Could not open config file for writing!\n");
free(buf);
return;
}
std::string s(buf);
const std::string delim = "\n\r";
std::string::size_type start = 0;
std::string::size_type end = s.find_first_of(delim, start);
while (end != std::string::npos)
{
if (end > start)
{
std::string sub = s.substr(start, end - start);
if ((sub[0] != '#') && (sub[0] != '['))
{
std::string::size_type kStart = sub.find_first_not_of(" ", 0);
std::string::size_type kEnd = sub.find_first_of(" ", kStart);
if ((kStart != std::string::npos)
&& (kEnd != std::string::npos))
{
std::string key = sub.substr(kStart, kEnd - kStart);
if (keystore.find(key) != keystore.end())
{
sub = key + " = " + keystore[key];
keystore.erase(key);
}
}
}
fprintf(f, "%s\n", sub.c_str());
}
else
fprintf(f, "\n");
start = end + 1;
end = s.find_first_of(delim, start);
}
std::map<std::string, std::string>::iterator i;
for(i=keystore.begin(); i!=keystore.end(); i++)
fprintf(f, "%s = %s\n", i->first.c_str(), i->second.c_str());
fclose(f);
free(buf);
}

View File

@ -10,7 +10,7 @@
#include <limits.h>
#define MAX_PATH _POSIX_PATH_MAX
#else
#include <stdlib.h> // for MAX_PATH on MinGW/Darwin
#include <stdlib.h> // for MAX_PATH on MinGW/Darwin
// Win64 kludge
#ifndef MAX_PATH
#define MAX_PATH _MAX_PATH // Ugh.
@ -28,26 +28,19 @@ struct Settings
bool fullscreen;
bool useOpenGL;
uint32_t glFilter;
uint32_t frameSkip;
uint32_t renderType;
bool autoStateSaving; // Auto-state loading/saving on entry/exit
// Window settings
int winX;
int winY;
// Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
uint16_t p1KeyBindings[21];
uint16_t p2KeyBindings[21];
int winX, winY;
// Paths
char BIOSPath[MAX_PATH];
char disksPath[MAX_PATH];
char hdPath[MAX_PATH];
char autoStatePath[MAX_PATH];
char BIOSPath[MAX_PATH + 1];
char disksPath[MAX_PATH + 1];
char autoStatePath[MAX_PATH + 1];
char hd[7][MAX_PATH + 1];
};
// Render types

View File

@ -1197,6 +1197,12 @@ bool InitVideo(void)
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
// Start in fullscreen, if user requested it via config file
int response = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
if (response != 0)
WriteLog("Video::FullScreen: SDL error = %s\n", SDL_GetError());
SetupBlurTable();
WriteLog("Video: Successfully initialized.\n");

View File

@ -29,43 +29,63 @@ h1.title
margin-top: 45px;
}
img.flt-l
.flt-l
{
float: left;
margin-right: 8px;
padding-right: 8px;
}
img.flt-r
.flt-r
{
float: right;
margin-left: 8px;
padding-left: 8px;
}
figure.flt-r
.clr-l
{
clear: left;
}
.clr-r
{
clear: right;
}
figure
{
float: right;
margin-top: 0;
margin-left: 8px;
margin-left: 0;
margin-right: 0;
font-style: italic;
font-size: 90%;
}
figure.small33
img
{
width: 33%;
max-width: 100%;
object-fit: contain;
}
img.small50
img.border
{
margin: 5px;
border: 1px solid #CF5F00;
border-radius: 4px;
padding: 5px;
background-color: #FFAF00;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.small33
{
width: 33%;
height: auto;
}
.small50
{
width: 50%;
height: 50%;
}
img.small33
{
width: 33%;
height: 33%;
height: auto;
}
#title

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -21,7 +21,7 @@
<p><img class="flt-l" src="img/a2-icon-64x64.png">This is the home of the Apple2 portable Apple //e emulator.&ensp;It&rsquo;s based on GCC and SDL2, and runs on Linux, Windows, and MacOS X.&ensp;It&rsquo;s powered by Virtual 65C02&trade;, and sports an easy to use yet powerful interface.&ensp;It also has WOZ support!&ensp;The source is licensed under the GPL version 3.</p>
<figure class="flt-r">
<img src="img/ss-04s.png">
<img class="border" src="img/ss-04s.png">
<figcaption>Apple2 running &ldquo;The Bard&rsquo;s Tale&rdquo;</figcaption>
</figure>
@ -51,11 +51,21 @@
<h3>How to Use It</h3>
<figure class="flt-r small33">
<img src="img/ss-01s.png" width="100%">
<figure class="clr-r flt-r small33">
<img class="border" src="img/ss-01s.png" width="100%">
<figcaption>Apple2&rsquo;s Control Bar</figcaption>
</figure>
<figure class="clr-r flt-r small33">
<img class="border" src="img/ss-02s.png">
<figcaption>Apple2&rsquo;s Disk Selector</figcaption>
</figure>
<figure class="clr-r flt-r small33">
<img class="border" src="img/ss-03s.png">
<figcaption>Scrolling in the Disk Selector</figcaption>
</figure>
<p>By mousing over the right side of the screen, the emulator control bar will appear; moving the mouse off of the bar will cause it to disappear.&ensp;On the bar are seven icons, labeled (from top to bottom): power, disk one, disk two, swap disks, save state, load state, and configure.&ensp;Here&rsquo;s what they do:</p>
<ul>
@ -68,10 +78,6 @@
<li>Configure: configure various behaviors of the Apple2 emulator</li>
</ul>
<img class="flt-l small33" src="img/ss-02s.png">
<img class="flt-l small33" src="img/ss-03s.png">
<p>In addition to the aforementioned control bar, Apple2 also supports the following function keys:</p>
<ul>