Skip to content

Commit

Permalink
Refactor tzx and tap loading
Browse files Browse the repository at this point in the history
  • Loading branch information
cgreening committed Nov 15, 2024
1 parent b967d8d commit 43bbc6c
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 149 deletions.
2 changes: 1 addition & 1 deletion firmware/src/AudioOutput/BuzzerOuput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ bool IRAM_ATTR BuzzerOutput::onTimer()
{
uint16_t micSample = adc1_get_raw(ADC1_CHANNEL_4);
micAve = (micAve * 99 + micSample) / 100;
uint8_t micValue = micSample > micAve ? 1 : 0;
uint8_t micValue = micSample > (micAve * 101)/100 ? 1 : 0;
xQueueSendFromISR(micValueQueue, &micValue, &xHigherPriorityTaskWoken);
mCount++;
// get the first sample from the buffer - shift it up to 9 bits for max resolution
Expand Down
157 changes: 21 additions & 136 deletions firmware/src/Screens/EmulatorScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,161 +3,46 @@
#include "../Emulator/spectrum.h"
#include "../Emulator/snaps.h"
#include "../AudioOutput/AudioOutput.h"
#include "../GPIOTapeLoader/GPIOTapeLoader.h"
#include "../Files/Files.h"
#include "EmulatorScreen.h"
#include "NavigationStack.h"
#include "SaveSnapshotScreen.h"
#include "../TZX/ZXSpectrumTapeListener.h"
#include "../TZX/DummyListener.h"
#include "../TZX/tzx_cas.h"
#include "utils.h"
#include "EmulatorScreen/Renderer.h"
#include "EmulatorScreen/Machine.h"
#include "EmulatorScreen/GameLoader.h"

EmulatorScreen::EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput) : Screen(tft, audioOutput)
{
renderer = new Renderer(tft);
machine = new Machine(renderer, audioOutput, nullptr);
gameLoader = new GameLoader(machine, renderer, audioOutput);
pinMode(0, INPUT_PULLUP);
}


void EmulatorScreen::loadTape(std::string filename)
{
ScopeGuard guard([&]()
{
renderer->setIsLoading(false);
if (m_audioOutput) m_audioOutput->resume(); });
// stop audio playback
if (m_audioOutput)
{
m_audioOutput->pause();
}
uint64_t startTime = get_usecs();
renderer->setIsLoading(true);
Serial.printf("Loading tape %s\n", filename.c_str());
machine->startLoading();
Serial.printf("Loading tape file\n");
FILE *fp = fopen(filename.c_str(), "rb");
if (fp == NULL)
{
Serial.println("Error: Could not open file.");
std::cout << "Error: Could not open file." << std::endl;
return;
}
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
Serial.printf("File size %d\n", file_size);
uint8_t *tzx_data = (uint8_t *)ps_malloc(file_size);
if (!tzx_data)
{
Serial.println("Error: Could not allocate memory.");
return;
}
fread(tzx_data, 1, file_size, fp);
fclose(fp);
// load the tape
TzxCas tzxCas;
DummyListener *dummyListener = new DummyListener();
dummyListener->start();
if (filename.find(".tap") != std::string::npos || filename.find(".TAP") != std::string::npos)
{
tzxCas.load_tap(dummyListener, tzx_data, file_size);
}
else
{
tzxCas.load_tzx(dummyListener, tzx_data, file_size);
}
dummyListener->finish();
uint64_t totalTicks = dummyListener->getTotalTicks();
Serial.printf("Total cycles: %lld\n", dummyListener->getTotalTicks());
delete dummyListener;
int count = 0;
int borderPos = 0;
uint8_t currentBorderColors[312] = {0};
ZXSpectrumTapeListener *listener = new ZXSpectrumTapeListener(machine->getMachine(), [&](uint64_t progress)
{
// approximate the border position - not very accutare but good enough
// get the border color
currentBorderColors[borderPos] = machine->getMachine()->hwopt.BorderColor & B00000111;
borderPos++;
count++;
if (borderPos == 312) {
borderPos = 0;
renderer->triggerDraw(machine->getMachine()->mem.currentScreen, currentBorderColors);
}
if (count % 4000 == 0) {
float machineTime = (float) listener->getTotalTicks() / 3500000.0f;
float wallTime = (float) (get_usecs() - startTime) / 1000000.0f;
Serial.printf("Total execution time: %fs\n", (float) listener->getTotalExecutionTime() / 1000000.0f);
Serial.printf("Total machine time: %f\n", machineTime);
Serial.printf("Wall Clock time: %fs\n", wallTime);
Serial.printf("Speed Up: %f\n", machineTime/wallTime);
Serial.printf("Progress: %lld\n", progress * 100 / totalTicks);
// draw a progreess bar
renderer->setLoadProgress(progress * 100 / totalTicks);
vTaskDelay(1);
} });
listener->start();
if (filename.find(".tap") != std::string::npos || filename.find(".TAP") != std::string::npos)
{
Serial.printf("Loading tap file\n");
tzxCas.load_tap(listener, tzx_data, file_size);
}
else
{
Serial.printf("Loading tzx file\n");
tzxCas.load_tzx(listener, tzx_data, file_size);
}
Serial.printf("Tape loaded\n");
listener->finish();
Serial.printf("*********************");
Serial.printf("Total execution time: %lld\n", listener->getTotalExecutionTime());
Serial.printf("Total cycles: %lld\n", listener->getTotalTicks());
Serial.printf("*********************");
free(tzx_data);
delete listener;
}

void EmulatorScreen::run(std::string filename)
void EmulatorScreen::run(std::string filename, models_enum model)
{
m_tft.fillScreen(TFT_BLACK);
renderer->start();
// audioFile = fopen("/fs/audio.raw", "wb");
auto bl = BusyLight();
// check for tap or tpz files
std::string ext = filename.substr(filename.find_last_of(".") + 1);
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c)
{ return std::tolower(c); });
if (ext == "tap" || ext == "tzx")
{
machine->setup(SPECMDL_128K);
loadTape(filename.c_str());
machine->start();
}
else
machine->setup(model);
if (filename.size() > 0)
{
// generic loading of z80 and sna files
machine->setup(SPECMDL_48K);
Load(machine->getMachine(), filename.c_str());
machine->start();
// check for tap or tpz files
std::string ext = filename.substr(filename.find_last_of(".") + 1);
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c)
{ return std::tolower(c); });
if (ext == "tap" || ext == "tzx")
{
gameLoader->loadTape(filename.c_str());
}
else
{
// generic loading of z80 and sna files
Load(machine->getMachine(), filename.c_str());
}
}
}

void EmulatorScreen::run48K()
{
m_tft.fillScreen(TFT_BLACK);
renderer->start();
machine->setup(SPECMDL_48K);
machine->start();
}

void EmulatorScreen::run128K()
{
m_tft.fillScreen(TFT_BLACK);
renderer->start();
machine->setup(SPECMDL_128K);
machine->start();
}

Expand Down
15 changes: 6 additions & 9 deletions firmware/src/Screens/EmulatorScreen.h
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
#pragma once

#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <stdint.h>
#include <string>
#include "Screen.h"
#include "EmulatorScreen/Renderer.h"
#include "EmulatorScreen/Machine.h"

class TFTDisplay;
class AudioOutput;
class ZXSpectrum;
class TouchKeyboard;
class Machine;
class GameLoader;
class Renderer;

class EmulatorScreen : public Screen
{
private:
Renderer *renderer = nullptr;
Machine *machine = nullptr;
void loadTape(std::string filename);
GameLoader *gameLoader = nullptr;
public:
EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput);
void updatekey(SpecKeys key, uint8_t state);
void run(std::string filename);
void run48K();
void run128K();
void run(std::string filename, models_enum model);
void pause();
void resume();
void showSaveSnapshotScreen();
Expand Down
110 changes: 110 additions & 0 deletions firmware/src/Screens/EmulatorScreen/GameLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include <iostream>
#include "../../TZX/ZXSpectrumTapeListener.h"
#include "../../TZX/DummyListener.h"
#include "../../TZX/tzx_cas.h"
#include "./Machine.h"
#include "./GameLoader.h"
#include "./Renderer.h"
#include "../../AudioOutput/AudioOutput.h"
#include "../../utils.h"


GameLoader::GameLoader(Machine *machine, Renderer *renderer, AudioOutput *audioOutput) : machine(machine), renderer(renderer), audioOutput(audioOutput) {}

void GameLoader::loadTape(std::string filename)
{
ScopeGuard guard([&]()
{
renderer->setIsLoading(false);
if (audioOutput) audioOutput->resume(); });
// stop audio playback
if (audioOutput)
{
audioOutput->pause();
}
uint64_t startTime = get_usecs();
renderer->setIsLoading(true);
Serial.printf("Loading tape %s\n", filename.c_str());
machine->startLoading();
Serial.printf("Loading tape file\n");
FILE *fp = fopen(filename.c_str(), "rb");
if (fp == NULL)
{
Serial.println("Error: Could not open file.");
std::cout << "Error: Could not open file." << std::endl;
return;
}
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
Serial.printf("File size %d\n", file_size);
uint8_t *tzx_data = (uint8_t *)ps_malloc(file_size);
if (!tzx_data)
{
Serial.println("Error: Could not allocate memory.");
return;
}
fread(tzx_data, 1, file_size, fp);
fclose(fp);
// load the tape
TzxCas tzxCas;
DummyListener *dummyListener = new DummyListener();
dummyListener->start();
if (filename.find(".tap") != std::string::npos || filename.find(".TAP") != std::string::npos)
{
tzxCas.load_tap(dummyListener, tzx_data, file_size);
}
else
{
tzxCas.load_tzx(dummyListener, tzx_data, file_size);
}
dummyListener->finish();
uint64_t totalTicks = dummyListener->getTotalTicks();
Serial.printf("Total cycles: %lld\n", dummyListener->getTotalTicks());
delete dummyListener;
int count = 0;
int borderPos = 0;
uint8_t currentBorderColors[312] = {0};
ZXSpectrumTapeListener *listener = new ZXSpectrumTapeListener(machine->getMachine(), [&](uint64_t progress)
{
// approximate the border position - not very accutare but good enough
// get the border color
currentBorderColors[borderPos] = machine->getMachine()->hwopt.BorderColor & B00000111;
borderPos++;
count++;
if (borderPos == 312) {
borderPos = 0;
renderer->triggerDraw(machine->getMachine()->mem.currentScreen, currentBorderColors);
}
if (count % 4000 == 0) {
float machineTime = (float) listener->getTotalTicks() / 3500000.0f;
float wallTime = (float) (get_usecs() - startTime) / 1000000.0f;
Serial.printf("Total execution time: %fs\n", (float) listener->getTotalExecutionTime() / 1000000.0f);
Serial.printf("Total machine time: %f\n", machineTime);
Serial.printf("Wall Clock time: %fs\n", wallTime);
Serial.printf("Speed Up: %f\n", machineTime/wallTime);
Serial.printf("Progress: %lld\n", progress * 100 / totalTicks);
// draw a progreess bar
renderer->setLoadProgress(progress * 100 / totalTicks);
vTaskDelay(1);
} });
listener->start();
if (filename.find(".tap") != std::string::npos || filename.find(".TAP") != std::string::npos)
{
Serial.printf("Loading tap file\n");
tzxCas.load_tap(listener, tzx_data, file_size);
}
else
{
Serial.printf("Loading tzx file\n");
tzxCas.load_tzx(listener, tzx_data, file_size);
}
Serial.printf("Tape loaded\n");
listener->finish();
Serial.printf("*********************");
Serial.printf("Total execution time: %lld\n", listener->getTotalExecutionTime());
Serial.printf("Total cycles: %lld\n", listener->getTotalTicks());
Serial.printf("*********************");
free(tzx_data);
delete listener;
}
18 changes: 18 additions & 0 deletions firmware/src/Screens/EmulatorScreen/GameLoader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <string>

class Machine;
class AudioOutput;
class Renderer;

class GameLoader
{
private:
Machine *machine = nullptr;
Renderer *renderer = nullptr;
AudioOutput *audioOutput = nullptr;
public:
GameLoader(Machine *machine, Renderer *renderer, AudioOutput *audioOutput);
void loadTape(std::string filename);
};
4 changes: 3 additions & 1 deletion firmware/src/Screens/GameFilePickerScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class GameFilePickerScreen : public PickerScreen<FileInfoPtr>
void onItemSelect(FileInfoPtr item, int index) {
drawBusy();
EmulatorScreen *emulatorScreen = new EmulatorScreen(m_tft, m_audioOutput);
emulatorScreen->run(item->getPath());
// TODO - we should pick the machine to run on - 48k or 128k
// there's no way to know from the file name or the file contents
emulatorScreen->run(item->getPath(), models_enum::SPECMDL_128K);
m_navigationStack->push(emulatorScreen);
}
void onBack() {
Expand Down
4 changes: 2 additions & 2 deletions firmware/src/Screens/MainMenuScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ class MainMenuScreen : public PickerScreen<MenuItemPtr>
void run48K()
{
EmulatorScreen *emulatorScreen = new EmulatorScreen(m_tft, m_audioOutput);
emulatorScreen->run48K();
emulatorScreen->run("", models_enum::SPECMDL_48K);
// touchKeyboard->setToggleMode(true);
m_navigationStack->push(emulatorScreen);
}
void run128K()
{
EmulatorScreen *emulatorScreen = new EmulatorScreen(m_tft, m_audioOutput);
emulatorScreen->run128K();
emulatorScreen->run("", models_enum::SPECMDL_128K);
// touchKeyboard->setToggleMode(true);
m_navigationStack->push(emulatorScreen);
}
Expand Down

0 comments on commit 43bbc6c

Please sign in to comment.