Skip to content

Commit

Permalink
Tape loading and simplification
Browse files Browse the repository at this point in the history
  • Loading branch information
cgreening committed Nov 21, 2024
1 parent 5b45cf3 commit aac70b2
Show file tree
Hide file tree
Showing 22 changed files with 174 additions and 44 deletions.
2 changes: 1 addition & 1 deletion desktop/Makefile.ems
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ all: $(TARGET)

# Create executable from object files
$(TARGET): $(OBJS) Makefile
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) `sdl2-config --libs` $(LDFLAGS)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) `sdl2-config --libs` $(LDFLAGS) --embed-file filesystem

# Object file rules (object files are placed next to the source files)
%.o: %.cpp Makefile
Expand Down
11 changes: 6 additions & 5 deletions desktop/src/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -380,13 +380,9 @@ int main()
return -1;
}

audioOutput = new SDLAudioOutput(machine);
audioOutput->start(15600);

// run the spectrum for 4 second so that it boots up
int start = SDL_GetTicks();

#ifndef __EMSCRIPTEN__
for(int i = 0; i < 200; i++) {
machine->runForFrame(nullptr, nullptr);
}
Expand All @@ -402,9 +398,14 @@ int main()
int end = SDL_GetTicks();
std::cout << "Time to boot spectrum: " << end - start << "ms" << std::endl;
// load a tap file
#if __EMSCRIPTEN__
std::string filename = "filesystem/manic.z80";
#else
std::string filename = OpenFileDialog();
loadGame(filename);
#endif
loadGame(filename);
audioOutput = new SDLAudioOutput(machine);
audioOutput->start(15600);
isRunning = true;
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(main_loop, 50, 1);
Expand Down
1 change: 1 addition & 0 deletions firmware/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ build_flags =
-Wl,-Map,output.map
build_unflags =
-std=gnu++11
-fno-rtti

lib_deps =
SPI
Expand Down
2 changes: 2 additions & 0 deletions firmware/src/Emulator/spectrum.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ class ZXSpectrum
uint8_t ulaport_FF = 0xFF;
bool micLevel = false;
uint8_t borderColors[312] = {0};
// indicates that the ROM loading routine is active
bool romLoadingRoutineHit = false;

ZXSpectrum();
void reset();
Expand Down
8 changes: 6 additions & 2 deletions firmware/src/Emulator/z80/z80.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,13 @@ uint16_t Z80Run(Z80Regs *regs, int numcycles)
micValue += spectrum->hwopt.SoundBits != 0 ? totalCycles : 0;
/* patch ROM loading routine */
// address contributed by Ignacio Burgueño :)
// if (r_PC >= 0x0556 && r_PC <= 0x056c) {
if (r_PC >= 0x04C2 && r_PC < 0x09F4) {
// set a flag to indicate that the ROM loading routine has been hit
spectrum->romLoadingRoutineHit = true;
// printf("ROM loading routine hit\n");
// }
} else {
spectrum->romLoadingRoutineHit = false;
}
}
return micValue;
}
Expand Down
23 changes: 21 additions & 2 deletions firmware/src/Files/Files.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,16 @@ class FileInfo
: title(title), path(path) {}
std::string getTitle() const { return title; }
std::string getPath() const { return path; }
std::string getExtension() const
{
size_t pos = title.find_last_of('.');
if (pos != std::string::npos)
{
std::string extension = title.substr(pos);
return StringUtils::downcase(extension);
}
return "";
}

private:
std::string title;
Expand Down Expand Up @@ -220,14 +230,23 @@ using FileLetterCountPtr = std::shared_ptr<FileLetterCount>;
using FileLetterCountVector = std::vector<FileLetterCountPtr>;

// Files class to list files in a directory
class IFiles
{
public:
virtual bool isAvailable() = 0;
virtual void createDirectory(const char *folder) = 0;
virtual FileLetterCountVector getFileLetters(const char *folder, const std::vector<std::string> &extensions) = 0;
virtual FileInfoVector getFileStartingWithPrefix(const char *folder, const char *prefix, const std::vector<std::string> &extensions) = 0;
};

template <class FileSystemT>
class Files
class FilesImplementation: public IFiles
{
private:
FileSystemT *fileSystem;

public:
Files(FileSystemT *fileSystem) : fileSystem(fileSystem)
FilesImplementation(FileSystemT *fileSystem) : fileSystem(fileSystem)
{
}

Expand Down
8 changes: 4 additions & 4 deletions firmware/src/Screens/AlphabetPicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
#include "NavigationStack.h"
#include "PickerScreen.h"

template <class Files_T, class FilePickerScreen_T>
template <class FilePickerScreen_T>
class AlphabetPicker : public PickerScreen<FileLetterCountPtr>
{
private:
Files_T *m_files;
IFiles *m_files;
std::string m_path;
std::vector<std::string> m_extensions;
public:
AlphabetPicker(std::string title, Files_T *files, TFTDisplay &tft, AudioOutput *audioOutput, std::string path, std::vector<std::string> extensions)
AlphabetPicker(std::string title, IFiles *files, TFTDisplay &tft, AudioOutput *audioOutput, std::string path, std::vector<std::string> extensions)
: m_files(files), PickerScreen(title, tft, audioOutput), m_path(path), m_extensions(extensions)
{
}
void onItemSelect(FileLetterCountPtr item, int index) override
{
FilePickerScreen_T *filePickerScreen = new FilePickerScreen_T(m_tft, m_audioOutput);
FilePickerScreen_T *filePickerScreen = new FilePickerScreen_T(m_tft, m_audioOutput, m_files);
drawBusy();
filePickerScreen->setItems(m_files->getFileStartingWithPrefix(m_path.c_str(), item->getLetter().c_str(), m_extensions));
m_navigationStack->push(filePickerScreen);
Expand Down
60 changes: 58 additions & 2 deletions firmware/src/Screens/EmulatorScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,60 @@
#include "../Emulator/snaps.h"
#include "../AudioOutput/AudioOutput.h"
#include "../Files/Files.h"
#include "ErrorScreen.h"
#include "EmulatorScreen.h"
#include "AlphabetPicker.h"
#include "GameFilePickerScreen.h"
#include "NavigationStack.h"
#include "SaveSnapshotScreen.h"
#include "EmulatorScreen/Renderer.h"
#include "EmulatorScreen/Machine.h"
#include "EmulatorScreen/GameLoader.h"

EmulatorScreen::EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput) : Screen(tft, audioOutput)
const std::vector<std::string> tap_extensions = {".tap", ".tzx"};
const std::vector<std::string> no_sd_card_error = {"No SD Card", "Insert an SD Card", "to load games"};
const std::vector<std::string> no_files_error = {"No games found", "on the SD Card", "add Z80 or SNA files"};

void EmulatorScreen::triggerLoadTape()
{
if (isLoading) {
return;
}
machine->pause();
renderer->pause();
if (!m_files->isAvailable())
{
ErrorScreen *errorScreen = new ErrorScreen(
no_sd_card_error,
m_tft,
m_audioOutput);
m_navigationStack->push(errorScreen);
return;
}
drawBusy();
FileLetterCountVector fileLetterCounts = m_files->getFileLetters("/", tap_extensions);
if (fileLetterCounts.size() == 0)
{
ErrorScreen *errorScreen = new ErrorScreen(
no_files_error,
m_tft,
m_audioOutput);
m_navigationStack->push(errorScreen);
return;
}
AlphabetPicker<GameFilePickerScreen> *alphabetPicker = new AlphabetPicker<GameFilePickerScreen>("Select Tape File", m_files, m_tft, m_audioOutput, "/", tap_extensions);
alphabetPicker->setItems(fileLetterCounts);
m_navigationStack->push(alphabetPicker);
}

EmulatorScreen::EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput, IFiles *files)
: Screen(tft, audioOutput), m_files(files)
{
renderer = new Renderer(tft);
machine = new Machine(renderer, audioOutput);
machine = new Machine(renderer, audioOutput, [&]()
{
Serial.println("ROM loading routine hit");
triggerLoadTape(); });
gameLoader = new GameLoader(machine, renderer, audioOutput);
pinMode(0, INPUT_PULLUP);
}
Expand All @@ -34,6 +77,7 @@ void EmulatorScreen::run(std::string filename, models_enum model)
{ return std::tolower(c); });
if (ext == "tap" || ext == "tzx")
{
machine->startLoading();
gameLoader->loadTape(filename.c_str());
}
else
Expand Down Expand Up @@ -79,3 +123,15 @@ void EmulatorScreen::showSaveSnapshotScreen()
{
m_navigationStack->push(new SaveSnapshotScreen(m_tft, m_audioOutput, machine->getMachine()));
}

void EmulatorScreen::loadTape(std::string filename)
{
isLoading = true;
renderer->resume();
renderer->setNeedsRedraw();
gameLoader->loadTape(filename);
renderer->setIsLoading(false);
renderer->setNeedsRedraw();
machine->resume();
isLoading = false;
}
6 changes: 5 additions & 1 deletion firmware/src/Screens/EmulatorScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ class EmulatorScreen : public Screen
Machine *machine = nullptr;
GameLoader *gameLoader = nullptr;
FILE *audioFile = nullptr;
IFiles *m_files;
void triggerLoadTape();
bool isLoading = false;
public:
EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput);
EmulatorScreen(TFTDisplay &tft, AudioOutput *audioOutput, IFiles *files);
void updatekey(SpecKeys key, uint8_t state);
void run(std::string filename, models_enum model);
void pause();
Expand All @@ -28,4 +31,5 @@ class EmulatorScreen : public Screen
void didAppear() {
resume();
}
void loadTape(std::string filename);
};
2 changes: 0 additions & 2 deletions firmware/src/Screens/EmulatorScreen/GameLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#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)
Expand All @@ -25,7 +24,6 @@ void GameLoader::loadTape(std::string filename)
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)
Expand Down
14 changes: 13 additions & 1 deletion firmware/src/Screens/EmulatorScreen/Machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ void Machine::runEmulator() {
renderer->resetFrameCount();
cycleCount = 0;
}
if (machine->romLoadingRoutineHit)
{
romLoadingRoutineHitCallback();
}
}
else
{
Expand All @@ -36,7 +40,9 @@ void Machine::runEmulator() {
}


Machine::Machine(Renderer *renderer, AudioOutput *audioOutput): renderer(renderer), audioOutput(audioOutput) {
Machine::Machine(Renderer *renderer, AudioOutput *audioOutput, std::function<void()> romLoadingRoutineHitCallback)
: renderer(renderer), audioOutput(audioOutput), romLoadingRoutineHitCallback(romLoadingRoutineHitCallback) {
Serial.println("Creating machine");
machine = new ZXSpectrum();
}

Expand All @@ -47,12 +53,14 @@ void Machine::updatekey(SpecKeys key, uint8_t state) {
}

void Machine::setup(models_enum model) {
Serial.println("Setting up machine");
machine->reset();
machine->init_spectrum(model);
machine->reset_spectrum(machine->z80Regs);
}

void Machine::start(FILE *audioFile) {
Serial.println("Starting machine");
this->audioFile = audioFile;
isRunning = true;
xTaskCreatePinnedToCore(runnerTask, "z80Runner", 8192, this, 5, NULL, 0);
Expand Down Expand Up @@ -80,13 +88,17 @@ void Machine::startLoading()
machine->runForFrame(nullptr, nullptr);
}
renderer->triggerDraw(machine->mem.currentScreen, machine->borderColors);
// TODO load screenshot...
if (machine->hwopt.hw_model == SPECMDL_48K)
{
tapKey(SPECKEY_J);
machine->updatekey(SPECKEY_SYMB, 1);
tapKey(SPECKEY_P);
tapKey(SPECKEY_P);
tapKey(SPECKEY_SHIFT);
tapKey(SPECKEY_K);
machine->updatekey(SPECKEY_SYMB, 0);
machine->updatekey(SPECKEY_SHIFT, 0);
tapKey(SPECKEY_ENTER);
}
else
Expand Down
5 changes: 4 additions & 1 deletion firmware/src/Screens/EmulatorScreen/Machine.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "stdio.h"
#include <functional>
#include "../../Emulator/spectrum.h"

void runnerTask(void *pvParameter);
Expand All @@ -26,8 +27,10 @@ class Machine {
FILE *audioFile = nullptr;
// keeps track of how many tstates we've run
uint32_t cycleCount = 0;
// callback for when rom loading routine is hit
std::function<void()> romLoadingRoutineHitCallback;
public:
Machine(Renderer *renderer, AudioOutput *audioOutput);
Machine(Renderer *renderer, AudioOutput *audioOutput, std::function<void()> romLoadingRoutineHitCallback);
void updatekey(SpecKeys key, uint8_t state);
void setup(models_enum model);
void start(FILE *audioFile);
Expand Down
6 changes: 3 additions & 3 deletions firmware/src/Screens/EmulatorScreen/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ void Renderer::drawBorder(int startPos, int endPos, int offset, int length, int
for (int borderPos = startPos; borderPos < endPos;)
{
uint8_t borderColor = currentBorderColors[borderPos + offset];
if (drawnBorderColors[borderPos + offset] != borderColor)
if (drawnBorderColors[borderPos + offset] != borderColor || firstDraw)
{
// Find consecutive lines with the same color
int rangeStart = borderPos;
while (borderPos < endPos && currentBorderColors[borderPos + offset] == borderColor &&
drawnBorderColors[borderPos + offset] != borderColor)
while (borderPos < endPos && ((currentBorderColors[borderPos + offset] == borderColor &&
drawnBorderColors[borderPos + offset] != borderColor) || firstDraw))
{
drawnBorderColors[borderPos + offset] = borderColor;
borderPos++;
Expand Down
3 changes: 3 additions & 0 deletions firmware/src/Screens/EmulatorScreen/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,7 @@ class Renderer {
void setLoadProgress(uint16_t progress) {
loadProgress = progress;
}
void setNeedsRedraw() {
firstDraw = true;
}
};
1 change: 1 addition & 0 deletions firmware/src/Screens/ErrorScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "Screen.h"
#include "../TFT/TFTDisplay.h"
#include "fonts/GillSans_30_vlw.h"
#include "NavigationStack.h"

class TFTDisplay;

Expand Down
Loading

0 comments on commit aac70b2

Please sign in to comment.