Skip to content

Commit

Permalink
implements time travel
Browse files Browse the repository at this point in the history
  • Loading branch information
cgreening committed Nov 28, 2024
1 parent 0e23c0b commit 1b5a14d
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 40 deletions.
3 changes: 3 additions & 0 deletions firmware/src/Emulator/keyboard_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ enum SpecKeys
SPECKEY_M,
SPECKEY_SYMB,
SPECKEY_SPACE,
// indicates that everything above is not a normal key
SPECKEY_MAX_NORMAL,
// joystick keys
JOYK_UP,
JOYK_DOWN,
JOYK_LEFT,
Expand Down
10 changes: 6 additions & 4 deletions firmware/src/Emulator/spectrum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,12 @@ void ZXSpectrum::updateKey(SpecKeys key, uint8_t state)
kempston_port &= 0b11101111;
break;
default:
if (state == 1)
speckey[key2specy[0][key]] &= key2specy[1][key];
else
speckey[key2specy[0][key]] |= ((key2specy[1][key]) ^ 0xFF);
if (key < SPECKEY_MAX_NORMAL) {
if (state == 1)
speckey[key2specy[0][key]] &= key2specy[1][key];
else
speckey[key2specy[0][key]] |= ((key2specy[1][key]) ^ 0xFF);
}
break;
}
}
Expand Down
60 changes: 59 additions & 1 deletion firmware/src/Screens/EmulatorScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,45 @@ void EmulatorScreen::updateKey(SpecKeys key, uint8_t state)
// Serial.printf("Audio file closed\n");
// }
// }
machine->updateKey(key, state);
if (!isInTimeTravelMode)
{
machine->updateKey(key, state);
}
}

void EmulatorScreen::pressKey(SpecKeys key)
{
if (key == SPECKEY_MENU)
{
// TODO show menu for save snapshot, load snapshot, time travel
if (!isInTimeTravelMode)
{
pause();
isInTimeTravelMode = true;
machine->startTimeTravel();
drawTimeTravel();
}
// showSaveSnapshotScreen();
} else if (isInTimeTravelMode)
{
// TODO cancel time travel
if (key == SPECKEY_ENTER)
{
machine->stopTimeTravel();
isInTimeTravelMode = false;
resume();
}
else
{
if (key == SPECKEY_5) {
machine->stepBack();
}
if (key == SPECKEY_8) {
machine->stepForward();
}
drawTimeTravel();
}
}
}

void EmulatorScreen::showSaveSnapshotScreen()
Expand All @@ -134,3 +172,23 @@ void EmulatorScreen::loadTape(std::string filename)
machine->resume();
isLoading = false;
}

void EmulatorScreen::drawTimeTravel() {
m_tft.fillRect(0, 0, m_tft.width(), 20, TFT_BLACK);

m_tft.loadFont(GillSans_15_vlw);
m_tft.setTextColor(TFT_WHITE, TFT_BLACK);

// Draw the left control "<5"
m_tft.drawString("<5", 5, 0);

// Draw the center text "Time Travel - Enter=Jump"
Point centerSize = m_tft.measureString("Time Travel - Enter=Jump");
int centerX = (m_tft.width() - centerSize.x) / 2;
m_tft.drawString("Time Travel - Enter=Jump", centerX, 0);

// Draw the right control "8>"
Point rightSize = m_tft.measureString("8>");
int rightX = m_tft.width() - rightSize.x - 5; // 5 pixels from right edge
m_tft.drawString("8>", rightX, 0);
}
3 changes: 3 additions & 0 deletions firmware/src/Screens/EmulatorScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ class EmulatorScreen : public Screen
IFiles *m_files;
void triggerLoadTape();
bool isLoading = false;
bool isInTimeTravelMode = false;
void drawTimeTravel();
public:
EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput, IFiles *files);
void updateKey(SpecKeys key, uint8_t state);
void pressKey(SpecKeys key);
void run(std::string filename, models_enum model);
void pause();
void resume();
Expand Down
45 changes: 41 additions & 4 deletions firmware/src/Screens/EmulatorScreen/Machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <list>
#include <vector>
#include <deque>
#include "Renderer.h"
#include "../../Emulator/spectrum.h"
#include "../../Serial.h"

Expand All @@ -23,9 +24,14 @@ struct MemoryBank {
};

struct TimeTravelInstant {
// the current memory pages
uint8_t hwBank = 0;
// the memory banks
std::vector<MemoryBank *> memoryBanks;
// the z80 registers
Z80Regs z80Regs;
uint8_t borderColors[32] = {0};
// the border colors
uint8_t borderColors[312] = {0};
};

class TimeTravel {
Expand Down Expand Up @@ -92,7 +98,9 @@ class TimeTravel {
// copy the z80 registers
memcpy(&instant->z80Regs, machine->z80Regs, sizeof(Z80Regs));
// copy the border colors
memcpy(instant->borderColors, machine->borderColors, 32);
memcpy(instant->borderColors, machine->borderColors, 312);
// make sure the correct paging is set
instant->hwBank = machine->mem.hwBank;
// add the instant to the list
timeTravelInstants.push_back(instant);
// if we have more than 30 seconds of time travel, remove the oldest instant
Expand All @@ -112,12 +120,14 @@ class TimeTravel {
TimeTravelInstant *instant = timeTravelInstants[index];
// copy the memory banks
for (MemoryBank *memoryBank : instant->memoryBanks) {
memcpy(machine->mem.mappedMemory[memoryBank->index], memoryBank->data, 0x4000);
memcpy(machine->mem.banks[memoryBank->index]->data, memoryBank->data, 0x4000);
}
// copy the z80 registers
memcpy(machine->z80Regs, &instant->z80Regs, sizeof(Z80Regs));
// copy the border colors
memcpy(machine->borderColors, instant->borderColors, 32);
memcpy(machine->borderColors, instant->borderColors, 312);
// make sure the correct paging is set
machine->mem.page(instant->hwBank, true);
}
// remove everything from this point in time
void reset(int index) {
Expand Down Expand Up @@ -151,6 +161,8 @@ class Machine {
uint32_t cycleCount = 0;
// time travel
TimeTravel *timeTravel;
// current time travel position
int timeTravelPosition = 0;
// callback for when rom loading routine is hit
std::function<void()> romLoadingRoutineHitCallback;
public:
Expand All @@ -164,6 +176,31 @@ class Machine {
void resume() {
isRunning = true;
}
void startTimeTravel() {
// record the current state
timeTravel->record(machine);
timeTravelPosition = timeTravel->size() - 1;
Serial.printf("Starting time travel %d\n", timeTravelPosition);
}
void stepBack() {
if (timeTravelPosition > 0) {
timeTravelPosition--;
timeTravel->rewind(machine, timeTravelPosition);
renderer->forceRedraw(machine->mem.currentScreen->data, machine->borderColors);
Serial.printf("Time travel %d\n", timeTravelPosition);
}
}
void stepForward() {
if (timeTravelPosition < timeTravel->size() - 1) {
timeTravelPosition++;
timeTravel->rewind(machine, timeTravelPosition);
renderer->forceRedraw(machine->mem.currentScreen->data, machine->borderColors);
Serial.printf("Time travel %d\n", timeTravelPosition);
}
}
void stopTimeTravel() {
timeTravel->reset(timeTravelPosition);
}
ZXSpectrum *getMachine() {
return machine;
}
Expand Down
6 changes: 6 additions & 0 deletions firmware/src/Screens/EmulatorScreen/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,10 @@ class Renderer {
void setNeedsRedraw() {
firstDraw = true;
}
void forceRedraw(const uint8_t *currentScreen, const uint8_t *borderColors) {
memcpy(currentScreenBuffer, currentScreen, 6912);
memcpy(currentBorderColors, borderColors, 312);
firstDraw = true;
drawScreen();
}
};
9 changes: 8 additions & 1 deletion firmware/src/Screens/NavigationStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

#include <vector>
#include "Screen.h"
#include "../TFT/TFTDisplay.h"

class NavigationStack
{
private:
TFTDisplay *m_tft;
public:
std::vector<Screen *> stack;
NavigationStack() {}
NavigationStack(TFTDisplay *tft) : m_tft(tft) {}
~NavigationStack() {}
Screen *getTop() {
if (stack.size() > 0) {
Expand Down Expand Up @@ -51,5 +54,9 @@ class NavigationStack
if (top) {
top->pressKey(key);
}
// TODO: Add some combination of keys to save a screenshot
// if (key == SPECKEY_MENU) {
// m_tft->saveScreenshot();
// }
};
};
12 changes: 12 additions & 0 deletions firmware/src/TFT/TFTDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ void TFTDisplay::sendTransaction(SPITransactionInfo *trans)

void TFTDisplay::sendPixels(const uint16_t *data, int numPixels)
{
writePixelsToFrameBuffer(data, numPixels);

int bytes = numPixels * 2;
for (uint32_t i = 0; i < bytes; i += DMA_BUFFER_SIZE)
{
Expand All @@ -175,6 +177,7 @@ void TFTDisplay::sendData(const uint8_t *data, int length)

void TFTDisplay::sendColor(uint16_t color, int numPixels)
{
writePixelsToFrameBuffer(color, numPixels);
for (int i = 0; i < numPixels; i += DMA_BUFFER_SIZE >> 1)
{
int len = std::min(numPixels - i, int(DMA_BUFFER_SIZE >> 1));
Expand All @@ -186,6 +189,15 @@ void TFTDisplay::sendColor(uint16_t color, int numPixels)

void TFTDisplay::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
{
// store the window for future frame buffer updates
windowX0 = x0;
windowY0 = y0;
windowX1 = x1;
windowY1 = y1;
currentX = x0;
currentY = y0;

// do the TFT window set
uint8_t data[4];
#ifdef TFT_X_OFFSET
x0+=TFT_X_OFFSET;
Expand Down
Loading

0 comments on commit 1b5a14d

Please sign in to comment.