mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-11-22 00:32:39 +00:00
bios rework in SDL base
This commit is contained in:
parent
16fbb37f90
commit
538898d371
858
bios.cpp
858
bios.cpp
@ -14,7 +14,7 @@ extern Bounce resetButtonDebouncer;
|
||||
extern void runDebouncer();
|
||||
#endif
|
||||
|
||||
// Experimenting with using EXTMEM to cache all the filenames in a directory
|
||||
// using EXTMEM to cache all the filenames in a directory
|
||||
#ifndef TEENSYDUINO
|
||||
#define EXTMEM
|
||||
#endif
|
||||
@ -28,6 +28,24 @@ EXTMEM char cachedFilter[BIOS_MAXPATH] = {0};
|
||||
EXTMEM struct _cacheEntry biosCache[BIOSCACHESIZE];
|
||||
uint16_t numCacheEntries = 0;
|
||||
|
||||
// When selecting files...
|
||||
char fileFilter[16]; // FIXME length & Strcpy -> strncpy
|
||||
uint16_t fileSelectionFor; // define what the returned name is for
|
||||
|
||||
// menu screen enums
|
||||
enum {
|
||||
BIOS_AIIE = 0,
|
||||
BIOS_VM = 1,
|
||||
BIOS_HARDWARE = 2,
|
||||
BIOS_DISKS = 3,
|
||||
|
||||
BIOS_ABOUT = 4,
|
||||
BIOS_PADDLES = 5,
|
||||
BIOS_SELECTFILE = 6,
|
||||
|
||||
BIOS_DONE = 99,
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
ACT_EXIT = 1,
|
||||
@ -81,7 +99,7 @@ const char *staticPathConcat(const char *rootPath, const char *filePath)
|
||||
|
||||
BIOS::BIOS()
|
||||
{
|
||||
selectedMenu = 1;
|
||||
selectedMenu = BIOS_VM;
|
||||
selectedMenuItem = 0;
|
||||
|
||||
selectedFile = -1;
|
||||
@ -124,7 +142,470 @@ void BIOS::DrawMenuBar()
|
||||
}
|
||||
}
|
||||
|
||||
bool BIOS::loop()
|
||||
{
|
||||
static bool needsinit = true;
|
||||
if (needsinit) {
|
||||
g_filemanager->getRootPath(rootPath, sizeof(rootPath));
|
||||
needsinit = false;
|
||||
}
|
||||
|
||||
static bool needsRedraw = true;
|
||||
|
||||
if (selectedMenu == BIOS_DONE) {
|
||||
// We're returning to the bios a second time
|
||||
selectedMenu = BIOS_VM;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
if (resetButtonDebouncer.read() == LOW) {
|
||||
// wait until it's no longer pressed
|
||||
while (resetButtonDebouncer.read() == LOW)
|
||||
runDebouncer();
|
||||
delay(100); // wait long enough for it to debounce
|
||||
return BIOS_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool hitReturn = false;
|
||||
|
||||
uint16_t rv;
|
||||
if (g_keyboard->kbhit()) {
|
||||
switch (g_keyboard->read()) {
|
||||
case PK_DARR:
|
||||
selectedMenuItem++; // modded by current action
|
||||
needsRedraw = true;
|
||||
break;
|
||||
case PK_UARR:
|
||||
selectedMenuItem--; // modded by current action
|
||||
needsRedraw = true;
|
||||
break;
|
||||
case PK_RARR:
|
||||
selectedMenu++;
|
||||
selectedMenu %= NUM_TITLES;
|
||||
needsRedraw = true;
|
||||
break;
|
||||
case PK_LARR:
|
||||
selectedMenu--;
|
||||
if (selectedMenu < 0) {
|
||||
selectedMenu = NUM_TITLES-1;
|
||||
}
|
||||
needsRedraw = true;
|
||||
break;
|
||||
case PK_RET:
|
||||
hitReturn = true;
|
||||
needsRedraw = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (selectedMenu) {
|
||||
case BIOS_AIIE:
|
||||
rv = AiieMenuHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
case BIOS_VM:
|
||||
rv = VmMenuHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
case BIOS_HARDWARE:
|
||||
rv = HardwareMenuHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
case BIOS_DISKS:
|
||||
rv = DisksMenuHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
case BIOS_ABOUT:
|
||||
rv = AboutScreenHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
case BIOS_PADDLES:
|
||||
rv = PaddlesScreenHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
case BIOS_SELECTFILE:
|
||||
rv = SelectFileScreenHandler(needsRedraw, hitReturn);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rv != selectedMenu) {
|
||||
needsRedraw = true;
|
||||
selectedMenu = rv;
|
||||
}
|
||||
else
|
||||
needsRedraw = false; // assume the handler drew
|
||||
|
||||
return ((selectedMenu == BIOS_DONE) ? false : true);
|
||||
}
|
||||
|
||||
uint16_t BIOS::AiieMenuHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
static bool localRedraw = true;
|
||||
if (selectedMenuItem < 0)
|
||||
selectedMenuItem = sizeof(aiieActions)-1;
|
||||
selectedMenuItem %= sizeof(aiieActions);
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
g_display->clrScr();
|
||||
DrawMenuBar();
|
||||
DrawAiieMenu();
|
||||
g_display->flush();
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
// there is only ACT_ABOUT
|
||||
return BIOS_ABOUT;
|
||||
}
|
||||
|
||||
return BIOS_AIIE;
|
||||
}
|
||||
|
||||
uint16_t BIOS::VmMenuHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
static bool localRedraw = true;
|
||||
|
||||
if (selectedMenuItem < 0)
|
||||
selectedMenuItem = sizeof(vmActions)-1;
|
||||
selectedMenuItem %= sizeof(vmActions);
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
g_display->clrScr();
|
||||
DrawMenuBar();
|
||||
DrawVMMenu();
|
||||
|
||||
g_display->flush();
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
if (isActionActive(vmActions[selectedMenuItem])) {
|
||||
switch (vmActions[selectedMenuItem]) {
|
||||
case ACT_EXIT:
|
||||
return BIOS_DONE;
|
||||
case ACT_RESET:
|
||||
WarmReset();
|
||||
return BIOS_DONE;
|
||||
case ACT_COLDBOOT:
|
||||
ColdReboot();
|
||||
return BIOS_DONE;
|
||||
case ACT_MONITOR:
|
||||
((AppleVM *)g_vm)->Monitor();
|
||||
return BIOS_DONE;
|
||||
case ACT_DEBUG:
|
||||
g_debugMode++;
|
||||
g_debugMode %= 9; // FIXME: abstract max #
|
||||
localRedraw = true;
|
||||
return BIOS_VM;
|
||||
case ACT_SUSPEND:
|
||||
g_display->clrScr();
|
||||
g_display->drawString(M_SELECTED, 80, 100,"Suspending VM...");
|
||||
g_display->flush();
|
||||
// CPU is already suspended, so this is safe...
|
||||
((AppleVM *)g_vm)->Suspend("suspend.vm");
|
||||
localRedraw = true;
|
||||
return BIOS_VM;
|
||||
case ACT_RESTORE:
|
||||
g_display->clrScr();
|
||||
g_display->drawString(M_SELECTED, 80, 100,"Resuming VM...");
|
||||
g_display->flush();
|
||||
((AppleVM *)g_vm)->Resume("suspend.vm");
|
||||
return BIOS_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BIOS_VM;
|
||||
}
|
||||
|
||||
uint16_t BIOS::HardwareMenuHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
static bool localRedraw = true;
|
||||
|
||||
if (selectedMenuItem < 0)
|
||||
selectedMenuItem = sizeof(hardwareActions)-1;
|
||||
selectedMenuItem %= sizeof(hardwareActions);
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
g_display->clrScr();
|
||||
DrawMenuBar();
|
||||
DrawHardwareMenu();
|
||||
g_display->flush();
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
if (isActionActive(hardwareActions[selectedMenuItem])) {
|
||||
switch (hardwareActions[selectedMenuItem]) {
|
||||
case ACT_DISPLAYTYPE:
|
||||
g_displayType++;
|
||||
g_displayType %= 4; // FIXME: abstract max #
|
||||
((AppleDisplay*)g_display)->displayTypeChanged();
|
||||
localRedraw = true;
|
||||
break;
|
||||
|
||||
case ACT_SPEED:
|
||||
currentCPUSpeedIndex++;
|
||||
currentCPUSpeedIndex %= 4;
|
||||
switch (currentCPUSpeedIndex) {
|
||||
case CPUSPEED_HALF:
|
||||
g_speed = 1023000/2;
|
||||
break;
|
||||
case CPUSPEED_DOUBLE:
|
||||
g_speed = 1023000*2;
|
||||
break;
|
||||
case CPUSPEED_QUAD:
|
||||
g_speed = 1023000*4;
|
||||
break;
|
||||
default:
|
||||
g_speed = 1023000;
|
||||
break;
|
||||
}
|
||||
localRedraw = true;
|
||||
break;
|
||||
|
||||
case ACT_PADX_INV:
|
||||
g_invertPaddleX = !g_invertPaddleX;
|
||||
#ifdef TEENSYDUINO
|
||||
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
|
||||
#endif
|
||||
localRedraw = true;
|
||||
break;
|
||||
|
||||
case ACT_PADY_INV:
|
||||
g_invertPaddleY = !g_invertPaddleY;
|
||||
#ifdef TEENSYDUINO
|
||||
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
|
||||
#endif
|
||||
localRedraw = true;
|
||||
break;
|
||||
|
||||
case ACT_PADDLES:
|
||||
return BIOS_PADDLES;
|
||||
|
||||
case ACT_VOLPLUS:
|
||||
g_volume ++;
|
||||
if (g_volume > 15) {
|
||||
g_volume = 15;
|
||||
}
|
||||
localRedraw = true;
|
||||
break;
|
||||
|
||||
case ACT_VOLMINUS:
|
||||
g_volume--;
|
||||
if (g_volume < 0) {
|
||||
g_volume = 0;
|
||||
}
|
||||
localRedraw = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BIOS_HARDWARE;
|
||||
}
|
||||
|
||||
uint16_t BIOS::DisksMenuHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
static bool localRedraw = true;
|
||||
|
||||
if (selectedMenuItem < 0)
|
||||
selectedMenuItem = sizeof(diskActions)-1;
|
||||
selectedMenuItem %= sizeof(diskActions);
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
g_display->clrScr();
|
||||
DrawMenuBar();
|
||||
DrawDisksMenu();
|
||||
g_display->flush();
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
if (isActionActive(diskActions[selectedMenuItem])) {
|
||||
switch (diskActions[selectedMenuItem]) {
|
||||
case ACT_DISK1:
|
||||
if (((AppleVM *)g_vm)->DiskName(0)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectDisk(0);
|
||||
localRedraw = true;
|
||||
break;
|
||||
} else {
|
||||
strcpy(fileFilter, "dsk,.po,nib,woz");
|
||||
fileSelectionFor = ACT_DISK1;
|
||||
return BIOS_SELECTFILE;
|
||||
}
|
||||
break;
|
||||
case ACT_DISK2:
|
||||
if (((AppleVM *)g_vm)->DiskName(1)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectDisk(1);
|
||||
localRedraw = true;
|
||||
break;
|
||||
} else {
|
||||
strcpy(fileFilter, "dsk,.po,nib,woz");
|
||||
fileSelectionFor = ACT_DISK2;
|
||||
return BIOS_SELECTFILE;
|
||||
}
|
||||
break;
|
||||
case ACT_HD1:
|
||||
if (((AppleVM *)g_vm)->HDName(0)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectHD(0);
|
||||
localRedraw = true;
|
||||
break;
|
||||
} else {
|
||||
strcpy(fileFilter, "img");
|
||||
fileSelectionFor = ACT_HD1;
|
||||
return BIOS_SELECTFILE;
|
||||
}
|
||||
break;
|
||||
case ACT_HD2:
|
||||
if (((AppleVM *)g_vm)->HDName(1)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectHD(1);
|
||||
localRedraw = true;
|
||||
break;
|
||||
} else {
|
||||
strcpy(fileFilter, "img");
|
||||
fileSelectionFor = ACT_HD2;
|
||||
return BIOS_SELECTFILE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BIOS_DISKS;
|
||||
};
|
||||
|
||||
uint16_t BIOS::AboutScreenHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
static bool localRedraw = true;
|
||||
selectedMenuItem = 0;
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
g_display->clrScr();
|
||||
|
||||
g_display->drawString(M_SELECTED,
|
||||
0,
|
||||
0,
|
||||
"Aiie! - an Apple //e emulator");
|
||||
|
||||
g_display->drawString(M_NORMAL,
|
||||
15, 20,
|
||||
"(c) 2017-2020 Jorj Bauer");
|
||||
|
||||
g_display->drawString(M_NORMAL,
|
||||
15, 38,
|
||||
"https://github.com/JorjBauer/aiie/");
|
||||
|
||||
g_display->drawString(M_NORMAL,
|
||||
0,
|
||||
200,
|
||||
"Press return");
|
||||
|
||||
g_display->flush();
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
return BIOS_AIIE;
|
||||
}
|
||||
|
||||
return BIOS_ABOUT;
|
||||
}
|
||||
|
||||
uint16_t BIOS::PaddlesScreenHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
static bool localRedraw = true;
|
||||
selectedMenuItem = 0;
|
||||
static uint8_t lastPaddleX = g_paddles->paddle0();
|
||||
static uint8_t lastPaddleY = g_paddles->paddle1();
|
||||
|
||||
if (g_paddles->paddle0() != lastPaddleX) {
|
||||
lastPaddleX = g_paddles->paddle0();
|
||||
localRedraw = true;
|
||||
}
|
||||
if (g_paddles->paddle1() != lastPaddleY) {
|
||||
lastPaddleY = g_paddles->paddle1();
|
||||
localRedraw = true;
|
||||
}
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
char buf[50];
|
||||
g_display->clrScr();
|
||||
sprintf(buf, "Paddle X: %d ", lastPaddleX);
|
||||
g_display->drawString(M_NORMAL, 0, 12, buf);
|
||||
sprintf(buf, "Paddle Y: %d ", lastPaddleY);
|
||||
g_display->drawString(M_NORMAL, 0, 42, buf);
|
||||
g_display->drawString(M_NORMAL, 0, 92, "Press return to exit");
|
||||
g_display->flush();
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
return BIOS_HARDWARE;
|
||||
}
|
||||
|
||||
return BIOS_PADDLES;
|
||||
}
|
||||
|
||||
uint16_t BIOS::SelectFileScreenHandler(bool needsRedraw, bool performAction)
|
||||
{
|
||||
if (selectedMenuItem < 0)
|
||||
selectedMenuItem = BIOS_MAXFILES + 1;
|
||||
selectedMenuItem %= BIOS_MAXFILES + 2;
|
||||
|
||||
static bool localRedraw = true;
|
||||
static int8_t sel = 0;
|
||||
static int8_t page = 0;
|
||||
static uint16_t fileCount = 0;
|
||||
|
||||
if (needsRedraw || localRedraw) {
|
||||
fileCount = DrawDiskNames(page, sel, fileFilter);
|
||||
|
||||
localRedraw = false;
|
||||
}
|
||||
|
||||
if (performAction) {
|
||||
if (sel == 0) {
|
||||
page--;
|
||||
if (page < 0) page = 0;
|
||||
// else sel = BIOS_MAXFILES + 1;
|
||||
localRedraw = true;
|
||||
}
|
||||
else if (sel == BIOS_MAXFILES+1) {
|
||||
if (fileCount == BIOS_MAXFILES) { // don't let them select
|
||||
// 'Next' if there were no
|
||||
// files in the list or if the
|
||||
// list isn't full
|
||||
page++;
|
||||
//sel = 0;
|
||||
localRedraw = true;
|
||||
}
|
||||
} else if (strcmp(fileDirectory[sel-1], "../") == 0) {
|
||||
// Go up a directory (strip a directory name from rootPath)
|
||||
stripDirectory();
|
||||
page = 0;
|
||||
//sel = 0;
|
||||
localRedraw = true;
|
||||
} else if (fileDirectory[sel-1][strlen(fileDirectory[sel-1])-1] == '/') {
|
||||
// Descend in to the directory. FIXME: file path length?
|
||||
strcat(rootPath, fileDirectory[sel-1]);
|
||||
sel = 0;
|
||||
page = 0;
|
||||
localRedraw = true;
|
||||
} else {
|
||||
selectedFile = sel - 1;
|
||||
g_display->flush();
|
||||
return BIOS_DISKS;
|
||||
}
|
||||
}
|
||||
return BIOS_SELECTFILE;
|
||||
}
|
||||
|
||||
/*
|
||||
bool BIOS::runUntilDone()
|
||||
{
|
||||
// Reset the cache
|
||||
@ -145,129 +626,10 @@ bool BIOS::runUntilDone()
|
||||
int8_t prevAction = ACT_EXIT;
|
||||
while (1) {
|
||||
switch (prevAction = GetAction(prevAction)) {
|
||||
case ACT_EXIT:
|
||||
goto done;
|
||||
case ACT_COLDBOOT:
|
||||
ColdReboot();
|
||||
goto done;
|
||||
case ACT_RESET:
|
||||
WarmReset();
|
||||
goto done;
|
||||
case ACT_MONITOR:
|
||||
((AppleVM *)g_vm)->Monitor();
|
||||
goto done;
|
||||
case ACT_DISPLAYTYPE:
|
||||
g_displayType++;
|
||||
g_displayType %= 4; // FIXME: abstract max #
|
||||
((AppleDisplay*)g_display)->displayTypeChanged();
|
||||
break;
|
||||
case ACT_ABOUT:
|
||||
showAbout();
|
||||
break;
|
||||
case ACT_SPEED:
|
||||
currentCPUSpeedIndex++;
|
||||
currentCPUSpeedIndex %= 4;
|
||||
switch (currentCPUSpeedIndex) {
|
||||
case CPUSPEED_HALF:
|
||||
g_speed = 1023000/2;
|
||||
break;
|
||||
case CPUSPEED_DOUBLE:
|
||||
g_speed = 1023000*2;
|
||||
break;
|
||||
case CPUSPEED_QUAD:
|
||||
g_speed = 1023000*4;
|
||||
break;
|
||||
default:
|
||||
g_speed = 1023000;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ACT_DEBUG:
|
||||
g_debugMode++;
|
||||
g_debugMode %= 9; // FIXME: abstract max #
|
||||
break;
|
||||
case ACT_DISK1:
|
||||
if (((AppleVM *)g_vm)->DiskName(0)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectDisk(0);
|
||||
} else {
|
||||
if (SelectDiskImage("dsk,.po,nib,woz")) {
|
||||
((AppleVM *)g_vm)->insertDisk(0, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACT_DISK2:
|
||||
if (((AppleVM *)g_vm)->DiskName(1)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectDisk(1);
|
||||
} else {
|
||||
if (SelectDiskImage("dsk,.po,nib,woz")) {
|
||||
((AppleVM *)g_vm)->insertDisk(1, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACT_HD1:
|
||||
if (((AppleVM *)g_vm)->HDName(0)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectHD(0);
|
||||
} else {
|
||||
if (SelectDiskImage("img")) {
|
||||
((AppleVM *)g_vm)->insertHD(0, staticPathConcat(rootPath, fileDirectory[selectedFile]));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACT_HD2:
|
||||
if (((AppleVM *)g_vm)->HDName(1)[0] != '\0') {
|
||||
((AppleVM *)g_vm)->ejectHD(1);
|
||||
} else {
|
||||
if (SelectDiskImage("img")) {
|
||||
((AppleVM *)g_vm)->insertHD(1, staticPathConcat(rootPath, fileDirectory[selectedFile]));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACT_PADX_INV:
|
||||
g_invertPaddleX = !g_invertPaddleX;
|
||||
#ifdef TEENSYDUINO
|
||||
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
|
||||
#endif
|
||||
break;
|
||||
case ACT_PADY_INV:
|
||||
g_invertPaddleY = !g_invertPaddleY;
|
||||
#ifdef TEENSYDUINO
|
||||
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
|
||||
#endif
|
||||
break;
|
||||
case ACT_PADDLES:
|
||||
ConfigurePaddles();
|
||||
break;
|
||||
case ACT_VOLPLUS:
|
||||
g_volume ++;
|
||||
if (g_volume > 15) {
|
||||
g_volume = 15;
|
||||
}
|
||||
break;
|
||||
case ACT_VOLMINUS:
|
||||
g_volume--;
|
||||
if (g_volume < 0) {
|
||||
g_volume = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACT_SUSPEND:
|
||||
g_display->clrScr();
|
||||
g_display->drawString(M_SELECTED, 80, 100,"Suspending VM...");
|
||||
g_display->flush();
|
||||
// CPU is already suspended, so this is safe...
|
||||
((AppleVM *)g_vm)->Suspend("suspend.vm");
|
||||
break;
|
||||
case ACT_RESTORE:
|
||||
// CPU is already suspended, so this is safe...
|
||||
g_display->clrScr();
|
||||
g_display->drawString(M_SELECTED, 80, 100,"Resuming VM...");
|
||||
g_display->flush();
|
||||
((AppleVM *)g_vm)->Resume("suspend.vm");
|
||||
break;
|
||||
***
|
||||
// ConfigurePaddles();
|
||||
***
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,6 +642,7 @@ bool BIOS::runUntilDone()
|
||||
// return true if any persistent setting changed that we want to store in eeprom
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
void BIOS::WarmReset()
|
||||
{
|
||||
@ -292,91 +655,6 @@ void BIOS::ColdReboot()
|
||||
g_cpu->Reset();
|
||||
}
|
||||
|
||||
uint8_t BIOS::GetAction(int8_t selection)
|
||||
{
|
||||
while (1) {
|
||||
DrawMainMenu();
|
||||
while (!g_keyboard->kbhit()
|
||||
#ifdef TEENSYDUINO
|
||||
&&
|
||||
(resetButtonDebouncer.read() == HIGH)
|
||||
#endif
|
||||
) {
|
||||
#ifdef TEENSYDUINO
|
||||
runDebouncer();
|
||||
delay(10);
|
||||
#else
|
||||
usleep(100);
|
||||
#endif
|
||||
// Wait for either a keypress or the reset button to be pressed
|
||||
}
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
if (resetButtonDebouncer.read() == LOW) {
|
||||
// wait until it's no longer pressed
|
||||
while (resetButtonDebouncer.read() == LOW)
|
||||
runDebouncer();
|
||||
delay(100); // wait long enough for it to debounce
|
||||
// then return an exit code
|
||||
return ACT_EXIT;
|
||||
}
|
||||
#else
|
||||
// FIXME: look for F10 or ESC & return ACT_EXIT?
|
||||
#endif
|
||||
|
||||
// selectedMenuItem and selectedMenu can go out of bounds here, and that's okay;
|
||||
// the current menu (and the menu bar) will re-pin it appropriately...
|
||||
switch (g_keyboard->read()) {
|
||||
case PK_DARR:
|
||||
selectedMenuItem++;
|
||||
break;
|
||||
case PK_UARR:
|
||||
selectedMenuItem--;
|
||||
break;
|
||||
case PK_RARR:
|
||||
selectedMenu++;
|
||||
break;
|
||||
case PK_LARR:
|
||||
selectedMenu--;
|
||||
break;
|
||||
case PK_RET:
|
||||
{
|
||||
int8_t activeAction = getCurrentMenuAction();
|
||||
if (activeAction > 0) {
|
||||
return activeAction;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BIOS::getCurrentMenuAction()
|
||||
{
|
||||
int8_t ret = -1;
|
||||
|
||||
switch (selectedMenu) {
|
||||
case 0: // Aiie
|
||||
if (isActionActive(aiieActions[selectedMenuItem]))
|
||||
return aiieActions[selectedMenuItem];
|
||||
break;
|
||||
case 1: // VM
|
||||
if (isActionActive(vmActions[selectedMenuItem]))
|
||||
return vmActions[selectedMenuItem];
|
||||
break;
|
||||
case 2: // Hardware
|
||||
if (isActionActive(hardwareActions[selectedMenuItem]))
|
||||
return hardwareActions[selectedMenuItem];
|
||||
break;
|
||||
case 3: // Disks
|
||||
if (isActionActive(diskActions[selectedMenuItem]))
|
||||
return diskActions[selectedMenuItem];
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool BIOS::isActionActive(int8_t action)
|
||||
{
|
||||
// don't return true for disk events that aren't valid
|
||||
@ -673,110 +951,6 @@ void BIOS::DrawCurrentMenu()
|
||||
}
|
||||
}
|
||||
|
||||
void BIOS::DrawMainMenu()
|
||||
{
|
||||
g_display->clrScr();
|
||||
// g_display->drawString(M_NORMAL, 0, 0, "BIOS Configuration");
|
||||
|
||||
DrawMenuBar();
|
||||
|
||||
DrawCurrentMenu();
|
||||
|
||||
g_display->flush();
|
||||
}
|
||||
|
||||
void BIOS::ConfigurePaddles()
|
||||
{
|
||||
while (1) {
|
||||
bool needsUpdate = true;
|
||||
uint8_t lastPaddleX = g_paddles->paddle0();
|
||||
uint8_t lastPaddleY = g_paddles->paddle1();
|
||||
|
||||
if (g_paddles->paddle0() != lastPaddleX) {
|
||||
lastPaddleX = g_paddles->paddle0();
|
||||
needsUpdate = true;
|
||||
}
|
||||
if (g_paddles->paddle1() != lastPaddleY) {
|
||||
lastPaddleY = g_paddles->paddle1();
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
char buf[50];
|
||||
g_display->clrScr();
|
||||
sprintf(buf, "Paddle X: %d ", lastPaddleX);
|
||||
g_display->drawString(M_NORMAL, 0, 12, buf);
|
||||
sprintf(buf, "Paddle Y: %d ", lastPaddleY);
|
||||
g_display->drawString(M_NORMAL, 0, 42, buf);
|
||||
g_display->drawString(M_NORMAL, 0, 92, "Exit with any key");
|
||||
g_display->flush();
|
||||
}
|
||||
|
||||
if (g_keyboard->kbhit()) {
|
||||
g_keyboard->read(); // throw out keypress
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return true if the user selects an image
|
||||
// sets selectedFile (index; -1 = "nope") and fileDirectory[][] (names of up to BIOS_MAXFILES files)
|
||||
bool BIOS::SelectDiskImage(const char *filter)
|
||||
{
|
||||
int8_t sel = 0;
|
||||
int8_t page = 0;
|
||||
uint16_t fileCount = 0;
|
||||
while (1) {
|
||||
fileCount = DrawDiskNames(page, sel, filter);
|
||||
|
||||
while (!g_keyboard->kbhit())
|
||||
;
|
||||
switch (g_keyboard->read()) {
|
||||
case PK_DARR:
|
||||
sel++;
|
||||
sel %= BIOS_MAXFILES + 2;
|
||||
break;
|
||||
case PK_UARR:
|
||||
sel--;
|
||||
if (sel < 0)
|
||||
sel = BIOS_MAXFILES + 1;
|
||||
break;
|
||||
case PK_ESC:
|
||||
return false;
|
||||
case PK_RET:
|
||||
if (sel == 0) {
|
||||
page--;
|
||||
if (page < 0) page = 0;
|
||||
// else sel = BIOS_MAXFILES + 1;
|
||||
}
|
||||
else if (sel == BIOS_MAXFILES+1) {
|
||||
if (fileCount == BIOS_MAXFILES) { // don't let them select 'Next' if there were no files in the list or if the list isn't full
|
||||
page++;
|
||||
//sel = 0;
|
||||
}
|
||||
} else if (strcmp(fileDirectory[sel-1], "../") == 0) {
|
||||
// Go up a directory (strip a directory name from rootPath)
|
||||
stripDirectory();
|
||||
page = 0;
|
||||
//sel = 0;
|
||||
continue;
|
||||
} else if (fileDirectory[sel-1][strlen(fileDirectory[sel-1])-1] == '/') {
|
||||
// Descend in to the directory. FIXME: file path length?
|
||||
strcat(rootPath, fileDirectory[sel-1]);
|
||||
sel = 0;
|
||||
page = 0;
|
||||
continue;
|
||||
} else {
|
||||
selectedFile = sel - 1;
|
||||
g_display->flush();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
void BIOS::stripDirectory()
|
||||
{
|
||||
rootPath[strlen(rootPath)-1] = '\0'; // remove the last character
|
||||
@ -919,31 +1093,45 @@ uint16_t BIOS::GatherFilenames(uint8_t pageOffset, const char *filter)
|
||||
}
|
||||
}
|
||||
|
||||
void BIOS::showAbout()
|
||||
{
|
||||
g_display->clrScr();
|
||||
#if 0
|
||||
***
|
||||
switch (fileSelectionFor) {
|
||||
case ACT_DISK1:
|
||||
if (SelectDiskImage("dsk,.po,nib,woz")) {
|
||||
((AppleVM *)g_vm)->insertDisk(0, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
|
||||
return BIOS_DONE;
|
||||
...
|
||||
case ACT_DISK2:
|
||||
if (SelectDiskImage("dsk,.po,nib,woz")) {
|
||||
((AppleVM *)g_vm)->insertDisk(1, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
|
||||
return BIOS_DONE;
|
||||
|
||||
g_display->drawString(M_SELECTED,
|
||||
0,
|
||||
0,
|
||||
"Aiie! - an Apple //e emulator");
|
||||
...
|
||||
case ACT_HD1:
|
||||
***
|
||||
if (SelectDiskImage("img")) {
|
||||
((AppleVM *)g_vm)->insertHD(0, staticPathConcat(rootPath, fileDirectory[selectedFile]));
|
||||
return BIOS_DONE;
|
||||
}
|
||||
|
||||
g_display->drawString(M_NORMAL,
|
||||
15, 20,
|
||||
"(c) 2017-2020 Jorj Bauer");
|
||||
case ACT_HD2:
|
||||
***
|
||||
if (SelectDiskImage("img")) {
|
||||
((AppleVM *)g_vm)->insertHD(1, staticPathConcat(rootPath, fileDirectory[selectedFile]));
|
||||
return BIOS_DONE;
|
||||
}
|
||||
|
||||
g_display->drawString(M_NORMAL,
|
||||
15, 38,
|
||||
"https://github.com/JorjBauer/aiie/");
|
||||
|
||||
g_display->drawString(M_NORMAL,
|
||||
0,
|
||||
200,
|
||||
"Press any key");
|
||||
...
|
||||
|
||||
g_display->flush();
|
||||
/*
|
||||
int8_t sel = 0;
|
||||
int8_t page = 0;
|
||||
uint16_t fileCount = 0;
|
||||
while (1) {
|
||||
fileCount = DrawDiskNames(page, sel, filter);
|
||||
*/
|
||||
|
||||
while (!g_keyboard->kbhit())
|
||||
;
|
||||
g_keyboard->read(); // throw out the keypress
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
20
bios.h
20
bios.h
@ -15,10 +15,12 @@ class BIOS {
|
||||
BIOS();
|
||||
~BIOS();
|
||||
|
||||
// return true if a persistent change needs to be stored in EEPROM
|
||||
bool runUntilDone();
|
||||
// return true as long as it's still running
|
||||
bool loop();
|
||||
|
||||
private:
|
||||
uint16_t MainMenuHandler();
|
||||
|
||||
void DrawMenuBar();
|
||||
void DrawCurrentMenu();
|
||||
void DrawAiieMenu();
|
||||
@ -26,25 +28,27 @@ class BIOS {
|
||||
void DrawHardwareMenu();
|
||||
void DrawDisksMenu();
|
||||
|
||||
uint16_t AiieMenuHandler(bool needsRedraw, bool performAction);
|
||||
uint16_t VmMenuHandler(bool needsRedraw, bool performAction);
|
||||
uint16_t HardwareMenuHandler(bool needsRedraw, bool performAction);
|
||||
uint16_t DisksMenuHandler(bool needsRedraw, bool performAction);
|
||||
uint16_t AboutScreenHandler(bool needsRedraw, bool performAction);
|
||||
uint16_t PaddlesScreenHandler(bool needsRedraw, bool performAction);
|
||||
uint16_t SelectFileScreenHandler(bool needsRedraw, bool performAction);
|
||||
|
||||
uint8_t GetAction(int8_t prevAction);
|
||||
bool isActionActive(int8_t action);
|
||||
void DrawMainMenu();
|
||||
|
||||
int8_t getCurrentMenuAction();
|
||||
|
||||
void WarmReset();
|
||||
void ColdReboot();
|
||||
|
||||
bool SelectDiskImage(const char *filter);
|
||||
uint16_t DrawDiskNames(uint8_t page, int8_t selection, const char *filter);
|
||||
uint16_t GatherFilenames(uint8_t pageOffset, const char *filter);
|
||||
|
||||
void ConfigurePaddles();
|
||||
|
||||
void stripDirectory();
|
||||
|
||||
void showAbout();
|
||||
|
||||
uint16_t cacheAllEntries(const char *filter);
|
||||
void sortCachedEntries();
|
||||
void swapCacheEntries(int a, int b);
|
||||
|
515
sdl/aiie.cpp
515
sdl/aiie.cpp
@ -20,22 +20,14 @@
|
||||
|
||||
#include "timeutil.h"
|
||||
|
||||
//#define SHOWFPS
|
||||
//#define SHOWPC
|
||||
//#define SHOWMEMPAGE
|
||||
|
||||
BIOS bios;
|
||||
Debugger debugger;
|
||||
|
||||
struct timespec nextInstructionTime, startTime;
|
||||
|
||||
#define NB_ENABLE 1
|
||||
#define NB_DISABLE 0
|
||||
|
||||
int send_rst = 0;
|
||||
|
||||
pthread_t cpuThreadID;
|
||||
|
||||
char disk1name[256] = "\0";
|
||||
char disk2name[256] = "\0";
|
||||
|
||||
@ -91,106 +83,275 @@ void write(void *arg, uint16_t address, uint8_t v)
|
||||
// no action; this is a dummy function until we've finished initializing...
|
||||
}
|
||||
|
||||
static void *cpu_thread(void *dummyptr) {
|
||||
struct timespec currentTime;
|
||||
static struct timespec runBIOS(struct timespec now)
|
||||
{
|
||||
static bool initialized = false;
|
||||
static struct timespec startTime;
|
||||
static struct timespec nextRuntime;
|
||||
static uint64_t cycleCount = 0;
|
||||
|
||||
#if 0
|
||||
int policy;
|
||||
struct sched_param param;
|
||||
pthread_getschedparam(pthread_self(), &policy, ¶m);
|
||||
param.sched_priority = sched_get_priority_max(policy);
|
||||
pthread_setschedparam(pthread_self(), policy, ¶m);
|
||||
#endif
|
||||
if (!initialized) {
|
||||
do_gettime(&startTime);
|
||||
do_gettime(&nextRuntime);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
timespec_add_us(&startTime, 100000*cycleCount, &nextRuntime); // FIXME: what's a good time here? 1/10 sec?
|
||||
|
||||
// Check if it's time to run - and if not, return how long it will
|
||||
// be until we need to run
|
||||
struct timespec diff = tsSubtract(nextRuntime, now);
|
||||
if (diff.tv_sec > 0 || diff.tv_nsec > 0) {
|
||||
// The caller can decide to nanosleep(&diff, NULL)
|
||||
return diff;
|
||||
}
|
||||
|
||||
cycleCount++;
|
||||
|
||||
if (!bios.loop()) {
|
||||
printf("BIOS loop has exited\n");
|
||||
g_biosInterrupt = false; // that's all she wrote!
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
static struct timespec runCPU(struct timespec now)
|
||||
{
|
||||
static bool initialized = false;
|
||||
static struct timespec startTime;
|
||||
static struct timespec nextInstructionTime;
|
||||
|
||||
if (!initialized) {
|
||||
do_gettime(&startTime);
|
||||
do_gettime(&nextInstructionTime);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// Check for interrupt-like actions before running the CPU
|
||||
if (wantSuspend) {
|
||||
printf("CPU halted; suspending VM\n");
|
||||
g_vm->Suspend("suspend.vm");
|
||||
printf("... done; resuming CPU.\n");
|
||||
wantSuspend = false;
|
||||
}
|
||||
if (wantResume) {
|
||||
printf("CPU halted; resuming VM\n");
|
||||
g_vm->Resume("suspend.vm");
|
||||
printf("... done. resuming CPU.\n");
|
||||
wantResume = false;
|
||||
}
|
||||
|
||||
// Determine correct time for next CPU cycle
|
||||
timespec_add_cycles(&startTime, g_cpu->cycles, &nextInstructionTime);
|
||||
|
||||
// Check if it's time to run - and if not, return how long it will be until we need to run
|
||||
struct timespec diff = tsSubtract(nextInstructionTime, now);
|
||||
if (diff.tv_sec > 0 || diff.tv_nsec > 0) {
|
||||
// The caller can decide to nanosleep(&diff, NULL)
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Run the CPU
|
||||
uint8_t executed = 0;
|
||||
if (debugger.active()) {
|
||||
// With the debugger running, we need to single-step through
|
||||
// instructions.
|
||||
executed = g_cpu->Run(1);
|
||||
} else {
|
||||
// Otherwise we can run a bunch of instructions at once to
|
||||
// save on the overhead.
|
||||
executed = g_cpu->Run(24);
|
||||
}
|
||||
|
||||
// The paddles need to be triggered in real-time on the CPU
|
||||
// clock. That happens from the VM's CPU maintenance poller.
|
||||
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
|
||||
|
||||
if (debugger.active()) {
|
||||
debugger.step();
|
||||
// FIXME need to reset starttime for this and g_cpu->cycles
|
||||
}
|
||||
|
||||
if (send_rst) {
|
||||
cpuDebuggerRunning = true;
|
||||
|
||||
_init_darwin_shim();
|
||||
do_gettime(&startTime);
|
||||
printf("Start time: %lu,%lu\n", startTime.tv_sec, startTime.tv_nsec);
|
||||
do_gettime(&nextInstructionTime);
|
||||
printf("Sending reset\n");
|
||||
g_cpu->Reset();
|
||||
|
||||
send_rst = 0;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
printf("free-running\n");
|
||||
#define TARGET_FPS 30
|
||||
struct timespec runDisplay(struct timespec now)
|
||||
{
|
||||
static bool initialized = false;
|
||||
static struct timespec startTime;
|
||||
static struct timespec nextRuntime;
|
||||
static uint64_t cycleCount = 0;
|
||||
|
||||
// In this loop, we determine when the next CPU event is; sleep until
|
||||
// that event; and then perform the event. There are also peripheral
|
||||
// maintenance calls embedded in the loop...
|
||||
if (!initialized) {
|
||||
do_gettime(&startTime);
|
||||
do_gettime(&nextRuntime);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
timespec_add_us(&startTime, (1000000/TARGET_FPS)*cycleCount, &nextRuntime); // 1000000 uS/S and 30fps target
|
||||
|
||||
while (1) {
|
||||
if (g_biosInterrupt) {
|
||||
printf("BIOS blocking\n");
|
||||
while (g_biosInterrupt) {
|
||||
usleep(100);
|
||||
}
|
||||
printf("BIOS block complete\n");
|
||||
// Check if it's time to run - and if not, return how long it will
|
||||
// be until we need to run
|
||||
struct timespec diff = tsSubtract(nextRuntime, now);
|
||||
if (diff.tv_sec > 0 || diff.tv_nsec > 0) {
|
||||
// The caller can decide to nanosleep(&diff, NULL)
|
||||
return diff;
|
||||
}
|
||||
|
||||
cycleCount++;
|
||||
|
||||
if (!g_biosInterrupt) {
|
||||
g_ui->blit();
|
||||
g_vm->vmdisplay->lockDisplay();
|
||||
if (g_vm->vmdisplay->needsRedraw()) {
|
||||
AiieRect what = g_vm->vmdisplay->getDirtyRect();
|
||||
g_vm->vmdisplay->didRedraw();
|
||||
g_display->blit(what);
|
||||
}
|
||||
g_vm->vmdisplay->unlockDisplay();
|
||||
|
||||
// For SDL, I'm throwing the printer update in with the display update...
|
||||
g_printer->update();
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
if (wantSuspend) {
|
||||
printf("CPU halted; suspending VM\n");
|
||||
g_vm->Suspend("suspend.vm");
|
||||
printf("... done; resuming CPU.\n");
|
||||
|
||||
wantSuspend = false;
|
||||
}
|
||||
if (wantResume) {
|
||||
printf("CPU halted; resuming VM\n");
|
||||
g_vm->Resume("suspend.vm");
|
||||
printf("... done. resuming CPU.\n");
|
||||
void doDebugging()
|
||||
{
|
||||
char buf[25];
|
||||
static time_t startAt = time(NULL);
|
||||
static uint32_t loopCount = 0;
|
||||
|
||||
wantResume = false;
|
||||
}
|
||||
|
||||
do_gettime(¤tTime);
|
||||
|
||||
// Determine the next CPU runtime (nextInstructionTime)
|
||||
timespec_add_cycles(&startTime, g_cpu->cycles, &nextInstructionTime);
|
||||
|
||||
// Sleep until the CPU is ready to run.
|
||||
|
||||
// tsSubtract doesn't return negatives; it bounds at zero. So if
|
||||
// either result is zero then it's time to run something.
|
||||
|
||||
struct timespec cpudiff = tsSubtract(nextInstructionTime, currentTime);
|
||||
|
||||
if (cpudiff.tv_sec > 0 || cpudiff.tv_nsec > 0) {
|
||||
// Sleep until the it's ready and loop...
|
||||
nanosleep(&cpudiff, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cpudiff.tv_sec == 0 && cpudiff.tv_nsec == 0) {
|
||||
// Run the CPU; it's caught up to "real time"
|
||||
|
||||
uint8_t executed = 0;
|
||||
if (debugger.active()) {
|
||||
// With the debugger running, we need to single-step through
|
||||
// instructions.
|
||||
executed = g_cpu->Run(1);
|
||||
} else {
|
||||
// Otherwise we can run a bunch of instructions at once to
|
||||
// save on the overhead.
|
||||
executed = g_cpu->Run(24);
|
||||
}
|
||||
|
||||
// The paddles need to be triggered in real-time on the CPU
|
||||
// clock. That happens from the VM's CPU maintenance poller.
|
||||
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
|
||||
|
||||
if (debugger.active()) {
|
||||
debugger.step();
|
||||
}
|
||||
|
||||
if (send_rst) {
|
||||
cpuDebuggerRunning = true;
|
||||
|
||||
printf("Sending reset\n");
|
||||
g_cpu->Reset();
|
||||
|
||||
send_rst = 0;
|
||||
switch (g_debugMode) {
|
||||
case D_SHOWFPS:
|
||||
{
|
||||
// display some FPS data
|
||||
loopCount++;
|
||||
uint32_t lenSecs = time(NULL) - startAt;
|
||||
if (lenSecs >= 5) {
|
||||
sprintf(buf, "%u FPS", loopCount / lenSecs);
|
||||
g_display->debugMsg(buf);
|
||||
startAt = time(NULL);
|
||||
loopCount = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case D_SHOWMEMFREE:
|
||||
// sprintf(buf, "%lu %u", FreeRamEstimate(), heapSize());
|
||||
// g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWPADDLES:
|
||||
sprintf(buf, "%u %u", g_paddles->paddle0(), g_paddles->paddle1());
|
||||
g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWPC:
|
||||
sprintf(buf, "%X", g_cpu->pc);
|
||||
g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWCYCLES:
|
||||
sprintf(buf, "%llX", g_cpu->cycles);
|
||||
g_display->debugMsg(buf);
|
||||
break;
|
||||
/*
|
||||
case D_SHOWBATTERY:
|
||||
// sprintf(buf, "BAT %d", analogRead(BATTERYPIN));
|
||||
// g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWTIME:
|
||||
// sprintf(buf, "%.2d:%.2d:%.2d", hour(), minute(), second());
|
||||
// g_display->debugMsg(buf);
|
||||
break;*/
|
||||
}
|
||||
}
|
||||
|
||||
struct timespec runMaintenance(struct timespec now)
|
||||
{
|
||||
static bool initialized = false;
|
||||
static struct timespec startTime;
|
||||
static struct timespec nextRuntime;
|
||||
static uint64_t cycleCount = 0;
|
||||
|
||||
if (!initialized) {
|
||||
do_gettime(&startTime);
|
||||
do_gettime(&nextRuntime);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
timespec_add_us(&startTime, 100000*cycleCount, &nextRuntime); // FIXME: what's a good time here? 1/10 sec?
|
||||
|
||||
// Check if it's time to run - and if not, return how long it will
|
||||
// be until we need to run
|
||||
struct timespec diff = tsSubtract(nextRuntime, now);
|
||||
if (diff.tv_sec > 0 || diff.tv_nsec > 0) {
|
||||
// The caller can decide to nanosleep(&diff, NULL)
|
||||
return diff;
|
||||
}
|
||||
|
||||
cycleCount++;
|
||||
if (!g_biosInterrupt) {
|
||||
// If the BIOS is running, then let it handle the keyboard directly
|
||||
g_keyboard->maintainKeyboard();
|
||||
}
|
||||
|
||||
doDebugging();
|
||||
g_ui->drawPercentageUIElement(UIePowerPercentage, 100);
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
struct timespec now;
|
||||
do_gettime(&now);
|
||||
|
||||
struct timespec shortest;
|
||||
|
||||
static bool wasBios = false; // so we can tell when it's done
|
||||
if (g_biosInterrupt) {
|
||||
shortest = runBIOS(now);
|
||||
wasBios = true;
|
||||
} else {
|
||||
if (wasBios) {
|
||||
// bios has just exited
|
||||
writePrefs();
|
||||
wasBios = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_biosInterrupt) {
|
||||
shortest = runCPU(now); // about 13% CPU utilization on my laptop
|
||||
}
|
||||
struct timespec diff;
|
||||
diff = runDisplay(now); // about 47% CPU utilization on my laptop
|
||||
if (tsCompare(&shortest, &diff) > 0)
|
||||
shortest = diff;
|
||||
diff = runMaintenance(now); // about 1% CPU utilization on my laptop
|
||||
if (tsCompare(&shortest, &diff) > 0)
|
||||
shortest = diff;
|
||||
|
||||
// If they all have time remaining then sleep until one is ready
|
||||
if (shortest.tv_sec || shortest.tv_nsec) {
|
||||
nanosleep(&shortest, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
_init_darwin_shim();
|
||||
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
|
||||
g_speaker = new SDLSpeaker();
|
||||
@ -250,177 +411,11 @@ int main(int argc, char *argv[])
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGPIPE, SIG_IGN); // debugger might have a SIGPIPE happen if the remote end drops
|
||||
|
||||
printf("creating CPU thread\n");
|
||||
if (!pthread_create(&cpuThreadID, NULL, &cpu_thread, (void *)NULL)) {
|
||||
printf("thread created\n");
|
||||
// pthread_setschedparam(cpuThreadID, SCHED_RR, PTHREAD_MAX_PRIORITY);
|
||||
}
|
||||
|
||||
g_speaker->begin();
|
||||
|
||||
int64_t lastCycleCount = -1;
|
||||
printf("Starting loop\n");
|
||||
while (1) {
|
||||
|
||||
if (g_biosInterrupt) {
|
||||
printf("Invoking BIOS\n");
|
||||
if (bios.runUntilDone()) {
|
||||
// if it returned true, we have something to store persistently in EEPROM.
|
||||
writePrefs();
|
||||
}
|
||||
printf("BIOS done\n");
|
||||
|
||||
// if we turned off debugMode, make sure to clear the debugMsg
|
||||
if (g_debugMode == D_NONE) {
|
||||
g_display->debugMsg("");
|
||||
}
|
||||
|
||||
g_biosInterrupt = false;
|
||||
|
||||
// clear the CPU next-step counters
|
||||
g_cpu->cycles = 0;
|
||||
do_gettime(&startTime);
|
||||
do_gettime(&nextInstructionTime);
|
||||
|
||||
// FIXME: drain whatever's in the speaker queue
|
||||
|
||||
/* FIXME
|
||||
// Force the display to redraw
|
||||
((AppleDisplay*)(g_vm->vmdisplay))->modeChange();
|
||||
*/
|
||||
|
||||
// Poll the keyboard before we start, so we can do selftest on startup
|
||||
g_keyboard->maintainKeyboard();
|
||||
}
|
||||
|
||||
|
||||
static int64_t usleepcycles = 16384*4; // step-down for display drawing. Dynamically updated based on FPS calculations.
|
||||
|
||||
if (g_vm->vmdisplay->needsRedraw()) {
|
||||
AiieRect what = g_vm->vmdisplay->getDirtyRect();
|
||||
// make sure to clear the flag before drawing; there's no lock
|
||||
// on didRedraw, so the other thread might update it
|
||||
g_vm->vmdisplay->didRedraw();
|
||||
g_display->blit(what);
|
||||
}
|
||||
g_ui->blit();
|
||||
|
||||
g_printer->update();
|
||||
g_keyboard->maintainKeyboard();
|
||||
|
||||
doDebugging();
|
||||
|
||||
g_ui->drawPercentageUIElement(UIePowerPercentage, 100);
|
||||
|
||||
// calculate FPS & dynamically step up/down as necessary
|
||||
static time_t startAt = time(NULL);
|
||||
static uint32_t loopCount = 0;
|
||||
loopCount++;
|
||||
uint32_t lenSecs = time(NULL) - startAt;
|
||||
if (lenSecs >= 5) {
|
||||
float fps = loopCount / lenSecs;
|
||||
|
||||
#ifdef SHOWFPS
|
||||
char buf[25];
|
||||
sprintf(buf, "%f FPS [delay %u]", fps, usleepcycles);
|
||||
g_display->debugMsg(buf);
|
||||
#endif
|
||||
|
||||
if (fps > 30 && usleepcycles < 0x3FFFFFFF) {
|
||||
usleepcycles *= 2;
|
||||
} else if (fps < 20 && usleepcycles > 0xF) {
|
||||
usleepcycles /= 2;
|
||||
}
|
||||
|
||||
// reset the counter & we'll adjust again in 5 seconds
|
||||
loopCount = 0;
|
||||
startAt = time(NULL);
|
||||
}
|
||||
if (usleepcycles >= 2) {
|
||||
usleep(usleepcycles);
|
||||
}
|
||||
|
||||
#ifdef SHOWPC
|
||||
{
|
||||
char buf[25];
|
||||
sprintf(buf, "%X", g_cpu->pc);
|
||||
g_display->debugMsg(buf);
|
||||
}
|
||||
#endif
|
||||
#ifdef SHOWMEMPAGE
|
||||
{
|
||||
char buf[40];
|
||||
sprintf(buf, "AUX %c/%c BNK %d BSR %c/%c ZP %c 80 %c INT %c",
|
||||
g_vm->auxRamRead?'R':'_',
|
||||
g_vm->auxRamWrite?'W':'_',
|
||||
g_vm->bank1,
|
||||
g_vm->readbsr ? 'R':'_',
|
||||
g_vm->writebsr ? 'W':'_',
|
||||
g_vm->altzp ? 'Y':'_',
|
||||
g_vm->_80store ? 'Y' : '_',
|
||||
g_vm->intcxrom ? 'Y' : '_');
|
||||
g_display->debugMsg(buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (g_cpu->cycles == lastCycleCount) {
|
||||
// If the CPU didn't advance during our last loop, then delay
|
||||
// here; there can't be any substantial updates, so no need to
|
||||
// beat up the host machine
|
||||
|
||||
usleep(100000);
|
||||
} else {
|
||||
lastCycleCount = g_cpu->cycles;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void doDebugging()
|
||||
{
|
||||
char buf[25];
|
||||
static time_t startAt = time(NULL);
|
||||
static uint32_t loopCount = 0;
|
||||
|
||||
switch (g_debugMode) {
|
||||
case D_SHOWFPS:
|
||||
{
|
||||
// display some FPS data
|
||||
loopCount++;
|
||||
uint32_t lenSecs = time(NULL) - startAt;
|
||||
if (lenSecs >= 5) {
|
||||
sprintf(buf, "%u FPS", loopCount / lenSecs);
|
||||
g_display->debugMsg(buf);
|
||||
startAt = time(NULL);
|
||||
loopCount = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case D_SHOWMEMFREE:
|
||||
// sprintf(buf, "%lu %u", FreeRamEstimate(), heapSize());
|
||||
// g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWPADDLES:
|
||||
sprintf(buf, "%u %u", g_paddles->paddle0(), g_paddles->paddle1());
|
||||
g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWPC:
|
||||
sprintf(buf, "%X", g_cpu->pc);
|
||||
g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWCYCLES:
|
||||
sprintf(buf, "%llX", g_cpu->cycles);
|
||||
g_display->debugMsg(buf);
|
||||
break;
|
||||
/*
|
||||
case D_SHOWBATTERY:
|
||||
// sprintf(buf, "BAT %d", analogRead(BATTERYPIN));
|
||||
// g_display->debugMsg(buf);
|
||||
break;
|
||||
case D_SHOWTIME:
|
||||
// sprintf(buf, "%.2d:%.2d:%.2d", hour(), minute(), second());
|
||||
// g_display->debugMsg(buf);
|
||||
break;*/
|
||||
loop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,46 +169,52 @@ uint8_t keyPending;
|
||||
bool SDLKeyboard::kbhit()
|
||||
{
|
||||
SDL_Event event;
|
||||
if (SDL_PollEvent( &event ) &&
|
||||
event.type == SDL_KEYDOWN) {
|
||||
SDL_KeyboardEvent *key = &event.key;
|
||||
if ( (key->keysym.sym >= 'a' && key->keysym.sym <= 'z') ||
|
||||
(key->keysym.sym >= '0' && key->keysym.sym <= '9') ||
|
||||
key->keysym.sym == '-' ||
|
||||
key->keysym.sym == '=' ||
|
||||
key->keysym.sym == '[' ||
|
||||
key->keysym.sym == '`' ||
|
||||
key->keysym.sym == ']' ||
|
||||
key->keysym.sym == '\\' ||
|
||||
key->keysym.sym == ';' ||
|
||||
key->keysym.sym == '\'' ||
|
||||
key->keysym.sym == ',' ||
|
||||
key->keysym.sym == '.' ||
|
||||
key->keysym.sym == '/' ||
|
||||
key->keysym.sym == ' ' ||
|
||||
key->keysym.sym == 27 || // ESC
|
||||
key->keysym.sym == 13 || // return
|
||||
key->keysym.sym == 9) { // tab
|
||||
keyPending = key->keysym.sym;
|
||||
hasKeyPending = true;
|
||||
} else {
|
||||
switch (key->keysym.sym) {
|
||||
case SDLK_UP:
|
||||
keyPending = PK_UARR;
|
||||
if (SDL_PollEvent( &event )) {
|
||||
if (event.type == SDL_QUIT) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
SDL_KeyboardEvent *key = &event.key;
|
||||
|
||||
if ( (key->keysym.sym >= 'a' && key->keysym.sym <= 'z') ||
|
||||
(key->keysym.sym >= '0' && key->keysym.sym <= '9') ||
|
||||
key->keysym.sym == '-' ||
|
||||
key->keysym.sym == '=' ||
|
||||
key->keysym.sym == '[' ||
|
||||
key->keysym.sym == '`' ||
|
||||
key->keysym.sym == ']' ||
|
||||
key->keysym.sym == '\\' ||
|
||||
key->keysym.sym == ';' ||
|
||||
key->keysym.sym == '\'' ||
|
||||
key->keysym.sym == ',' ||
|
||||
key->keysym.sym == '.' ||
|
||||
key->keysym.sym == '/' ||
|
||||
key->keysym.sym == ' ' ||
|
||||
key->keysym.sym == 27 || // ESC
|
||||
key->keysym.sym == 13 || // return
|
||||
key->keysym.sym == 9) { // tab
|
||||
keyPending = key->keysym.sym;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
keyPending = PK_DARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
keyPending = PK_RARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
keyPending = PK_LARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
} else {
|
||||
switch (key->keysym.sym) {
|
||||
case SDLK_UP:
|
||||
keyPending = PK_UARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
keyPending = PK_DARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
keyPending = PK_RARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
keyPending = PK_LARR;
|
||||
hasKeyPending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ static void audioCallback(void *unused, Uint8 *stream, int len)
|
||||
} else if (audioRunning==1) {
|
||||
// waiting for first fill; return an empty buffer.
|
||||
memset(stream, 0, SDLSIZE*SAMPLEBYTES);
|
||||
pthread_mutex_unlock(&togmutex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user