added suspend/resume

This commit is contained in:
Jorj Bauer 2017-12-30 15:20:34 -05:00
parent 061eca8c3a
commit 753a9a5f24
35 changed files with 937 additions and 162 deletions

177
LRingBuffer.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "LRingBuffer.h"
#include <stdlib.h>
#include "globals.h"
#define RINGBUFFERMAGIC '0'
LRingBuffer::LRingBuffer(int16_t length)
{
this->buffer = (uint8_t *)malloc(length);
this->max = length;
this->fill = 0;
this->ptr = 0;
this->cursor = 0;
}
LRingBuffer::~LRingBuffer()
{
free (this->buffer);
}
bool LRingBuffer::Serialize(int8_t fd)
{
g_filemanager->writeByte(fd, RINGBUFFERMAGIC);
g_filemanager->writeByte(fd, (max >> 8) & 0xFF);
g_filemanager->writeByte(fd, (max ) & 0xFF);
g_filemanager->writeByte(fd, (ptr >> 8) & 0xFF);
g_filemanager->writeByte(fd, (ptr ) & 0xFF);
g_filemanager->writeByte(fd, (fill >> 8) & 0xFF);
g_filemanager->writeByte(fd, (fill ) & 0xFF);
g_filemanager->writeByte(fd, (cursor >> 8) & 0xFF);
g_filemanager->writeByte(fd, (cursor ) & 0xFF);
for (uint16_t i=0; i<max; i++) {
g_filemanager->writeByte(fd, buffer[i]);
}
g_filemanager->writeByte(fd, RINGBUFFERMAGIC);
return true;
}
bool LRingBuffer::Deserialize(int8_t fd)
{
if (g_filemanager->readByte(fd) != RINGBUFFERMAGIC)
return false;
max = g_filemanager->readByte(fd);
max <<= 8;
max |= g_filemanager->readByte(fd);
ptr = g_filemanager->readByte(fd);
ptr <<= 8;
ptr |= g_filemanager->readByte(fd);
fill = g_filemanager->readByte(fd);
fill <<= 8;
fill |= g_filemanager->readByte(fd);
cursor = g_filemanager->readByte(fd);
cursor <<= 8;
cursor |= g_filemanager->readByte(fd);
if (buffer)
free(buffer);
buffer = (uint8_t *)malloc(max);
for (uint16_t i=0; i<max; i++) {
buffer[i] = g_filemanager->readByte(fd);
}
if (g_filemanager->readByte(fd) != RINGBUFFERMAGIC)
return false;
return true;
}
void LRingBuffer::clear()
{
this->fill = 0;
}
bool LRingBuffer::isFull()
{
return (this->max == this->fill);
}
bool LRingBuffer::hasData()
{
return (this->fill != 0);
}
bool LRingBuffer::addByte(uint8_t b)
{
if (this->max == this->fill)
return false;
int idx = (this->ptr + this->fill) % this->max;
this->buffer[idx] = b;
this->fill++;
return true;
}
bool LRingBuffer::replaceByte(uint8_t b)
{
if (cursor < fill) {
buffer[cursor] = b;
cursor++;
if (cursor >= fill) {
cursor = 0;
}
return true;
}
return false;
}
bool LRingBuffer::addBytes(uint8_t *b, int count)
{
for (int i=0; i<count; i++) {
if (!addByte(b[i]))
return false;
}
return true;
}
uint8_t LRingBuffer::consumeByte()
{
if (this->fill == 0)
return 0;
uint8_t ret = this->buffer[this->ptr];
this->fill--;
this->ptr++;
this->ptr %= this->max;
return ret;
}
uint8_t LRingBuffer::peek(int16_t idx)
{
uint16_t p = (this->ptr + idx) % this->max;
return this->buffer[p];
}
int16_t LRingBuffer::count()
{
return this->fill;
}
uint16_t LRingBuffer::getPeekCursor()
{
return this->cursor;
}
void LRingBuffer::setPeekCursor(int16_t idx)
{
this->cursor = idx;
}
void LRingBuffer::resetPeekCursor()
{
this->cursor = 0;
}
uint8_t LRingBuffer::peekNext()
{
uint8_t ret = peek(cursor);
cursor++;
if (cursor >= fill) {
cursor = 0;
}
return ret;
}

View File

@ -1,12 +1,15 @@
#ifndef __RINGBUFFER_H
#define __RINGBUFFER_H
#ifndef __LRINGBUFFER_H
#define __LRINGBUFFER_H
#include <stdint.h>
class RingBuffer {
class LRingBuffer {
public:
RingBuffer(int16_t length);
~RingBuffer();
LRingBuffer(int16_t length);
~LRingBuffer();
bool Serialize(int8_t fd);
bool Deserialize(int8_t fd);
void clear();

View File

@ -6,7 +6,7 @@ CXXFLAGS=-Wall -I .. -I . -I apple -I sdl -I/usr/local/include/SDL2 -O3 -g
TSRC=cpu.cpp util/testharness.cpp
COMMONOBJS=cpu.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o RingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o
COMMONOBJS=cpu.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o LRingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o
SDLOBJS=sdl/sdl-speaker.o sdl/sdl-display.o sdl/sdl-keyboard.o sdl/sdl-paddles.o sdl/sdl-filemanager.o sdl/aiie.o sdl/sdl-printer.o sdl/sdl-clock.o

View File

@ -1,113 +0,0 @@
#include "RingBuffer.h"
#include <stdlib.h>
RingBuffer::RingBuffer(int16_t length)
{
this->buffer = (uint8_t *)malloc(length);
this->max = length;
this->fill = 0;
this->ptr = 0;
this->cursor = 0;
}
RingBuffer::~RingBuffer()
{
free (this->buffer);
}
void RingBuffer::clear()
{
this->fill = 0;
}
bool RingBuffer::isFull()
{
return (this->max == this->fill);
}
bool RingBuffer::hasData()
{
return (this->fill != 0);
}
bool RingBuffer::addByte(uint8_t b)
{
if (this->max == this->fill)
return false;
int idx = (this->ptr + this->fill) % this->max;
this->buffer[idx] = b;
this->fill++;
return true;
}
bool RingBuffer::replaceByte(uint8_t b)
{
if (cursor < fill) {
buffer[cursor] = b;
cursor++;
if (cursor >= fill) {
cursor = 0;
}
return true;
}
return false;
}
bool RingBuffer::addBytes(uint8_t *b, int count)
{
for (int i=0; i<count; i++) {
if (!addByte(b[i]))
return false;
}
return true;
}
uint8_t RingBuffer::consumeByte()
{
if (this->fill == 0)
return 0;
uint8_t ret = this->buffer[this->ptr];
this->fill--;
this->ptr++;
this->ptr %= this->max;
return ret;
}
uint8_t RingBuffer::peek(int16_t idx)
{
uint16_t p = (this->ptr + idx) % this->max;
return this->buffer[p];
}
int16_t RingBuffer::count()
{
return this->fill;
}
uint16_t RingBuffer::getPeekCursor()
{
return this->cursor;
}
void RingBuffer::setPeekCursor(int16_t idx)
{
this->cursor = idx;
}
void RingBuffer::resetPeekCursor()
{
this->cursor = 0;
}
uint8_t RingBuffer::peekNext()
{
uint8_t ret = peek(cursor);
cursor++;
if (cursor >= fill) {
cursor = 0;
}
return ret;
}

View File

@ -13,6 +13,9 @@
#include "globals.h"
// Serializing token for MMU data
#define MMUMAGIC 'M'
// apple //e memory map
/*
@ -57,6 +60,113 @@ AppleMMU::~AppleMMU()
// FIXME: clean up the memory we allocated
}
bool AppleMMU::Serialize(int8_t fd)
{
g_filemanager->writeByte(fd, MMUMAGIC);
g_filemanager->writeByte(fd, (switches >> 8) & 0xFF);
g_filemanager->writeByte(fd, (switches ) & 0xFF);
g_filemanager->writeByte(fd, auxRamRead ? 1 : 0);
g_filemanager->writeByte(fd, auxRamWrite ? 1 : 0);
g_filemanager->writeByte(fd, bank2 ? 1 : 0);
g_filemanager->writeByte(fd, readbsr ? 1 : 0);
g_filemanager->writeByte(fd, writebsr ? 1 : 0);
g_filemanager->writeByte(fd, altzp ? 1 : 0);
g_filemanager->writeByte(fd, intcxrom ? 1 : 0);
g_filemanager->writeByte(fd, slot3rom ? 1 : 0);
g_filemanager->writeByte(fd, slotLatch);
g_filemanager->writeByte(fd, preWriteFlag ? 1 : 0);
g_filemanager->writeByte(fd, anyKeyDown ? 1 : 0);
g_filemanager->writeByte(fd, keyboardStrobe);
for (uint16_t i=0; i<0x100; i++) {
for (uint8_t j=0; j<5; j++) {
g_filemanager->writeByte(fd, MMUMAGIC);
if (ramPages[i][j]) {
g_filemanager->writeByte(fd, 1);
for (uint16_t k=0; k<0x100; k++) {
g_filemanager->writeByte(fd, ramPages[i][j][k]);
}
} else {
g_filemanager->writeByte(fd, 0);
}
}
}
// readPages & writePages don't need suspending, but we will need to
// recalculate after resume
// Not suspending/resuming slots b/c they're a fixed configuration
// in this project.
g_filemanager->writeByte(fd, MMUMAGIC);
return true;
}
bool AppleMMU::Deserialize(int8_t fd)
{
if (g_filemanager->readByte(fd) != MMUMAGIC) {
return false;
}
switches = g_filemanager->readByte(fd);
switches <<= 8;
switches |= g_filemanager->readByte(fd);
auxRamRead = g_filemanager->readByte(fd);
auxRamWrite = g_filemanager->readByte(fd);
bank2 = g_filemanager->readByte(fd);
readbsr = g_filemanager->readByte(fd);
writebsr = g_filemanager->readByte(fd);
altzp = g_filemanager->readByte(fd);
intcxrom = g_filemanager->readByte(fd);
slot3rom = g_filemanager->readByte(fd);
slotLatch = g_filemanager->readByte(fd);
preWriteFlag = g_filemanager->readByte(fd);
anyKeyDown = g_filemanager->readByte(fd);
keyboardStrobe = g_filemanager->readByte(fd);
for (uint16_t i=0; i<0x100; i++) {
for (uint8_t j=0; j<5; j++) {
if (g_filemanager->readByte(fd) != MMUMAGIC) {
#ifndef TEENSYDUINO
printf("Page %d/%d bad magic\n", i, j);
#endif
return false;
}
if (g_filemanager->readByte(fd)) {
// This page has data
#ifndef TEENSYDUINO
if (!ramPages[i][j]) {
printf("ERROR: shouldn't be writing to this page\n");
exit(1);
}
#endif
for (uint16_t k=0; k<0x100; k++) {
ramPages[i][j][k] = g_filemanager->readByte(fd);
}
} else {
#ifndef TEENSYDUINO
if (ramPages[i][j]) {
printf("ERROR: this page exists but wasn't serialized?\n");
exit(1);
}
#endif
}
}
}
if (g_filemanager->readByte(fd) != MMUMAGIC)
return false;
// Reset readPages[] and writePages[] and the display
resetDisplay();
return true;
}
void AppleMMU::Reset()
{
resetRAM();
@ -680,7 +790,7 @@ void AppleMMU::resetRAM()
preWriteFlag = false;
// Clear all the pages
for (uint8_t i=0; i<0xFF; i++) {
for (uint16_t i=0; i<=0xFF; i++) {
for (uint8_t j=0; j<5; j++) {
if (ramPages[i][j]) {
for (uint16_t k=0; k<0x100; k++) {
@ -741,6 +851,10 @@ void AppleMMU::resetRAM()
void AppleMMU::setSlot(int8_t slotnum, Slot *peripheral)
{
if (slots[slotnum]) {
delete slots[slotnum];
}
slots[slotnum] = peripheral;
if (slots[slotnum]) {
slots[slotnum]->loadROM(ramPages[0xC0 + slotnum][0]);

View File

@ -35,6 +35,9 @@ class AppleMMU : public MMU {
AppleMMU(AppleDisplay *display);
virtual ~AppleMMU();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
virtual uint8_t read(uint16_t address);
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage);
virtual void write(uint16_t address, uint8_t v);

View File

@ -8,6 +8,9 @@
#include "globals.h"
#include <errno.h>
const char *suspendHdr = "Sus1";
AppleVM::AppleVM()
{
// FIXME: all this typecasting makes me knife-stabby
@ -46,6 +49,81 @@ AppleVM::~AppleVM()
delete parallel;
}
void AppleVM::Suspend(const char *fn)
{
/* Open a new suspend file via the file manager; tell all our
objects to serialize in to it; close the file */
int8_t fh = g_filemanager->openFile(fn);
if (fh == -1) {
// Unable to open; skip suspend
return;
}
/* Header */
for (int i=0; i<strlen(suspendHdr); i++) {
g_filemanager->writeByte(fh, suspendHdr[i]);
}
/* Tell all of the peripherals to suspend */
if (g_cpu->Serialize(fh) &&
disk6->Serialize(fh) &&
hd32->Serialize(fh)
) {
#ifndef TEENSYDUINO
printf("All serialized successfully\n");
#else
Serial.println("All serialized successfully");
#endif
}
g_filemanager->closeFile(fh);
}
void AppleVM::Resume(const char *fn)
{
/* Open the given suspend file via the file manager; tell all our
objects to deserialize from it; close the file */
int8_t fh = g_filemanager->openFile(fn);
if (fh == -1) {
// Unable to open; skip resume
#ifdef TEENSYDUINO
Serial.print("Unable to open resume file ");
Serial.println(fn);
#else
printf("Unable to open resume file\n");
#endif
return;
}
/* Header */
for (int i=0; i<strlen(suspendHdr); i++) {
if (g_filemanager->readByte(fh) != suspendHdr[i]) {
/* Failed to read correct header; abort */
return;
}
}
/* Tell all of the peripherals to resume */
if (g_cpu->Deserialize(fh) &&
disk6->Deserialize(fh) &&
hd32->Deserialize(fh)
) {
#ifndef TEENSYDUINO
printf("All deserialized successfully\n");
#endif
} else {
#ifndef TEENSYDUINO
printf("Deserialization failed\n");
exit(1);
#endif
}
g_filemanager->closeFile(fh);
}
// fixme: make member vars
unsigned long paddleCycleTrigger[2] = {0, 0};

View File

@ -19,6 +19,9 @@ class AppleVM : public VM {
AppleVM();
virtual ~AppleVM();
void Suspend(const char *fn);
void Resume(const char *fn);
void cpuMaintenance(uint32_t cycles);
virtual void Reset();

View File

@ -16,9 +16,11 @@
#include "diskii-rom.h"
#define DISKIIMAGIC 0xAA
DiskII::DiskII(AppleMMU *mmu)
{
this->trackBuffer = new RingBuffer(NIBTRACKSIZE);
this->trackBuffer = new LRingBuffer(NIBTRACKSIZE);
this->rawTrackBuffer = (uint8_t *)malloc(4096);
this->mmu = mmu;
@ -46,6 +48,101 @@ DiskII::~DiskII()
free(this->rawTrackBuffer); this->rawTrackBuffer = NULL;
}
bool DiskII::Serialize(int8_t fd)
{
/* Make sure to flush anything to disk first */
checkFlush(curHalfTrack[selectedDisk]>>1);
if (trackToFlush != -1) {
flushTrack(trackToFlush, diskToFlush);
}
trackToFlush = -1;
g_filemanager->writeByte(fd, DISKIIMAGIC);
g_filemanager->writeByte(fd, curHalfTrack[0]);
g_filemanager->writeByte(fd, curHalfTrack[1]);
g_filemanager->writeByte(fd, curPhase[0]);
g_filemanager->writeByte(fd, curPhase[1]);
g_filemanager->writeByte(fd, readWriteLatch);
g_filemanager->writeByte(fd, writeMode);
g_filemanager->writeByte(fd, writeProt);
// Don't save disk[0,1]; save their file names & cursors
g_filemanager->SerializeFile(fd, disk[0]);
g_filemanager->SerializeFile(fd, disk[1]);
g_filemanager->writeByte(fd, indicatorIsOn[0]);
g_filemanager->writeByte(fd, indicatorIsOn[1]);
g_filemanager->writeByte(fd, diskType[0]);
g_filemanager->writeByte(fd, diskType[1]);
g_filemanager->writeByte(fd, selectedDisk);
trackBuffer->Serialize(fd);
// FIXME: don't know if we need these
// trackDirty - should always be unset, since we just flushed
// rawTrackBuffer - only used when reading an image
// trackToRead - should be able to leave this unset & reread
// trackToFlush - just flushed
// diskToFlush - just flushed
g_filemanager->writeByte(fd, DISKIIMAGIC);
return true;
}
bool DiskII::Deserialize(int8_t fd)
{
/* Make sure to flush anything to disk first */
checkFlush(curHalfTrack[selectedDisk]>>1);
if (trackToFlush != -1) {
flushTrack(trackToFlush, diskToFlush);
}
trackToFlush = -1;
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
return false;
}
curHalfTrack[0] = g_filemanager->readByte(fd);
curHalfTrack[1] = g_filemanager->readByte(fd);
curPhase[0] = g_filemanager->readByte(fd);
curPhase[1] = g_filemanager->readByte(fd);
readWriteLatch = g_filemanager->readByte(fd);
writeMode = g_filemanager->readByte(fd);
writeProt = g_filemanager->readByte(fd);
disk[0] = g_filemanager->DeserializeFile(fd);
disk[1] = g_filemanager->DeserializeFile(fd);
indicatorIsOn[0] = g_filemanager->readByte(fd);
indicatorIsOn[1] = g_filemanager->readByte(fd);
diskType[0] = g_filemanager->readByte(fd);
diskType[1] = g_filemanager->readByte(fd);
selectedDisk = g_filemanager->readByte(fd);
trackBuffer->Deserialize(fd);
// Reset the dirty caches and whatnot
trackDirty = -1;
trackToRead = -1;
trackToFlush = -1;
diskToFlush = -1;
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
return false;
}
return true;
}
void DiskII::Reset()
{
curPhase[0] = curPhase[1] = 0;

View File

@ -12,7 +12,7 @@
#include "applemmu.h"
#include "Slot.h"
#include "RingBuffer.h"
#include "LRingBuffer.h"
#include "nibutil.h"
class DiskII : public Slot {
@ -20,6 +20,9 @@ class DiskII : public Slot {
DiskII(AppleMMU *mmu);
virtual ~DiskII();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
virtual void Reset(); // used by BIOS cold-boot
virtual uint8_t readSwitches(uint8_t s);
virtual void writeSwitches(uint8_t s, uint8_t v);
@ -52,7 +55,7 @@ class DiskII : public Slot {
volatile int8_t curPhase[2];
volatile bool trackDirty; // does this track need flushing to disk?
uint8_t readWriteLatch;
RingBuffer *trackBuffer; // nibblized data
LRingBuffer *trackBuffer; // nibblized data
uint8_t *rawTrackBuffer; // not nibblized data
bool writeMode;

View File

@ -58,6 +58,16 @@ HD32::~HD32()
{
}
bool HD32::Serialize(int8_t fd)
{
return true;
}
bool HD32::Deserialize(int8_t fd)
{
return true;
}
void HD32::Reset()
{
enabled = 1;

View File

@ -12,13 +12,16 @@
#include "applemmu.h"
#include "Slot.h"
#include "RingBuffer.h"
#include "LRingBuffer.h"
class HD32 : public Slot {
public:
HD32(AppleMMU *mmu);
virtual ~HD32();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
virtual void Reset(); // used by BIOS cold-boot
virtual uint8_t readSwitches(uint8_t s);
virtual void writeSwitches(uint8_t s, uint8_t v);
@ -45,7 +48,7 @@ class HD32 : public Slot {
uint8_t enabled;
uint8_t errorState[2]; // status of last operation
uint16_t memBlock[2]; // ??
uint16_t memBlock[2]; // pointer to memory (for writing blocks to disk)
uint16_t diskBlock[2]; // currently selected block
int8_t fd[2];

View File

@ -56,7 +56,7 @@ const static uint8_t _detrans[0x80] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
void nibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track)
{
int checksum;
@ -126,7 +126,7 @@ void nibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
// trackBuf. This reads from the circular buffer trackBuffer, so if
// there's not enough data there, the results are somewhat
// unpredictable.
bool decodeData(RingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
{
// Basic check that there's enough buffer data in trackBuffer. Note
// that we're not checking it against startAt; we could be wrapping
@ -178,7 +178,7 @@ bool decodeData(RingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
return true;
}
void encodeData(RingBuffer *trackBuffer, uint8_t *data)
void encodeData(LRingBuffer *trackBuffer, uint8_t *data)
{
int16_t i;
int ptr2 = 0;
@ -221,7 +221,7 @@ void encodeData(RingBuffer *trackBuffer, uint8_t *data)
trackBuffer->addByte(_trans[lastv]);
}
nibErr denibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track)
{
// We can't tell exactly what the length should be, b/c there might

View File

@ -8,7 +8,7 @@
#include <time.h>
#endif
#include "RingBuffer.h"
#include "LRingBuffer.h"
#define NIBTRACKSIZE 0x1A00
// Minimum viable nibblized sector size. With GAP bytes, could be much longer.
@ -32,13 +32,13 @@ enum nibErr {
errorMissingSectors = 2
};
void nibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track);
nibErr denibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track);
bool decodeData(RingBuffer *trackBuffer, uint16_t startAt, uint8_t *output);
void encodeData(RingBuffer *trackBuffer, uint8_t *data);
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output);
void encodeData(LRingBuffer *trackBuffer, uint8_t *data);

View File

@ -13,6 +13,16 @@ ParallelCard::~ParallelCard()
{
}
bool ParallelCard::Serialize(int8_t fd)
{
return true;
}
bool ParallelCard::Deserialize(int8_t fd)
{
return true;
}
void ParallelCard::Reset()
{
}

View File

@ -18,6 +18,9 @@ class ParallelCard : public Slot {
ParallelCard();
virtual ~ParallelCard();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
virtual void Reset(); // used by BIOS cold-boot
virtual uint8_t readSwitches(uint8_t s);
virtual void writeSwitches(uint8_t s, uint8_t v);

View File

@ -13,6 +13,9 @@ class Slot {
public:
virtual ~Slot() {};
virtual bool Serialize(int8_t fd) = 0;
virtual bool Deserialize(int8_t fd) = 0;
virtual void Reset() = 0; // for use at cold-boot
virtual uint8_t readSwitches(uint8_t s) = 0;

83
cpu.cpp
View File

@ -1,9 +1,14 @@
#ifdef TEENSYDUINO
#include <Arduino.h>
#endif
#include "cpu.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "mmu.h"
#include "globals.h"
// To see calls to unimplemented opcodes, define this:
//#define VERBOSE_CPU_ERRORS
@ -21,6 +26,9 @@
#define writemem(addr, val) mmu->write(addr, val)
#define readmem(addr) mmu->read(addr)
// serialize suspend/restore token
#define CPUMAGIC 0x65
enum optype {
O_ILLEGAL,
O_ADC,
@ -412,6 +420,81 @@ Cpu::~Cpu()
mmu = NULL;
}
bool Cpu::Serialize(int8_t fh)
{
g_filemanager->writeByte(fh, CPUMAGIC);
g_filemanager->writeByte(fh, (pc >> 8) & 0xFF);
g_filemanager->writeByte(fh, (pc ) & 0xFF);
g_filemanager->writeByte(fh, sp);
g_filemanager->writeByte(fh, a);
g_filemanager->writeByte(fh, x);
g_filemanager->writeByte(fh, y);
g_filemanager->writeByte(fh, flags);
g_filemanager->writeByte(fh, (cycles >> 24) & 0xFF);
g_filemanager->writeByte(fh, (cycles >> 16) & 0xFF);
g_filemanager->writeByte(fh, (cycles >> 8) & 0xFF);
g_filemanager->writeByte(fh, (cycles ) & 0xFF);
g_filemanager->writeByte(fh, irqPending ? 1 : 0);
if (!mmu->Serialize(fh)) {
#ifndef TEENSYDUINO
printf("MMU serialization failed\n");
#else
Serial.println("MMU serialization failed");
#endif
return false;
}
g_filemanager->writeByte(fh, CPUMAGIC);
return true; // FIXME: no error checking on writes
}
bool Cpu::Deserialize(int8_t fh)
{
if (g_filemanager->readByte(fh) != CPUMAGIC)
return false;
pc = g_filemanager->readByte(fh);
pc <<= 8;
pc |= g_filemanager->readByte(fh);
sp = g_filemanager->readByte(fh);
a = g_filemanager->readByte(fh);
x = g_filemanager->readByte(fh);
y = g_filemanager->readByte(fh);
flags = g_filemanager->readByte(fh);
cycles = g_filemanager->readByte(fh);
cycles <<= 8;
cycles |= g_filemanager->readByte(fh);
cycles <<= 8;
cycles |= g_filemanager->readByte(fh);
cycles <<= 8;
cycles |= g_filemanager->readByte(fh);
irqPending = g_filemanager->readByte(fh) ? true : false;
if (!mmu->Deserialize(fh)) {
#ifndef TEENSYDUINO
printf("MMU deserialization failed\n");
#endif
return false;
}
if (g_filemanager->readByte(fh) != CPUMAGIC)
return false;
#ifndef TEENSYDUINO
printf("CPU deserialization complete\n");
#endif
return true;
}
void Cpu::Reset()
{
a = 0;

3
cpu.h
View File

@ -28,6 +28,9 @@ class Cpu {
Cpu();
~Cpu();
bool Serialize(int8_t fh);
bool Deserialize(int8_t fh);
void Reset();
void nmi();

View File

@ -9,10 +9,90 @@
#define MAXPATH 255
#endif
#define FMMAGIC 'F'
class FileManager {
public:
virtual ~FileManager() {};
virtual bool SerializeFile(int8_t outfd, int8_t fd) {
writeByte(outfd, FMMAGIC);
if (fd == -1 ||
cachedNames[fd][0] == '\0') {
// No file to cache; we're done
writeByte(outfd, 0);
writeByte(outfd, FMMAGIC);
return true;
}
// have a file to cache; set a marker and continue
writeByte(outfd, 1);
int8_t l = 0;
char *p = cachedNames[fd];
while (*p) {
l++;
p++;
}
writeByte(outfd, l);
for (int i=0; i<l; i++) {
writeByte(outfd, cachedNames[fd][i]);
}
writeByte(outfd, (fileSeekPositions[fd] >> 24) & 0xFF);
writeByte(outfd, (fileSeekPositions[fd] >> 16) & 0xFF);
writeByte(outfd, (fileSeekPositions[fd] >> 8) & 0xFF);
writeByte(outfd, (fileSeekPositions[fd] ) & 0xFF);
writeByte(outfd, FMMAGIC);
return true;
}
virtual int8_t DeserializeFile(int8_t infd) {
if (readByte(infd) != FMMAGIC)
return -1;
if (readByte(infd) == 0) {
// No file was cached. Verify footer and we're done without error
if (readByte(infd) != FMMAGIC) {
// FIXME: no way to raise this error.
return -1;
}
return -1;
}
char buf[MAXPATH];
int8_t l = readByte(infd);
for (int i=0; i<l; i++) {
buf[i] = readByte(infd);
}
buf[l] = '\0';
int8_t ret = openFile(buf);
if (ret == -1)
return ret;
fileSeekPositions[ret] = readByte(infd);
fileSeekPositions[ret] <<= 8;
fileSeekPositions[ret] = readByte(infd);
fileSeekPositions[ret] <<= 8;
fileSeekPositions[ret] = readByte(infd);
fileSeekPositions[ret] <<= 8;
fileSeekPositions[ret] = readByte(infd);
if (readByte(infd) != FMMAGIC)
return -1;
return ret;
}
virtual int8_t openFile(const char *name) = 0;
virtual void closeFile(int8_t fd) = 0;
@ -27,6 +107,13 @@ class FileManager {
virtual uint8_t readByteAt(int8_t fd, uint32_t pos) = 0;
virtual bool writeByteAt(int8_t fd, uint8_t v, uint32_t pos) = 0;
virtual uint8_t readByte(int8_t fd) = 0;
virtual bool writeByte(int8_t fd, uint8_t v) = 0;
protected:
unsigned long fileSeekPositions[MAXFILES];
char cachedNames[MAXFILES][MAXPATH];
};
#endif

3
mmu.h
View File

@ -12,6 +12,9 @@ class MMU {
virtual uint8_t read(uint16_t mem) = 0;
virtual void write(uint16_t mem, uint8_t val) = 0;
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage) = 0;
virtual bool Serialize(int8_t fd) = 0;
virtual bool Deserialize(int8_t fd) = 0;
};
#endif

View File

@ -35,6 +35,9 @@ pthread_t cpuThreadID;
char disk1name[256] = "\0";
char disk2name[256] = "\0";
volatile bool wantSuspend = false;
volatile bool wantResume = false;
void sigint_handler(int n)
{
send_rst = 1;
@ -92,6 +95,21 @@ static void *cpu_thread(void *dummyptr) {
printf("free-running\n");
while (1) {
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;
}
// cycle down the CPU...
do_gettime(&currentTime);
struct timespec diff = tsSubtract(nextInstructionTime, currentTime);
@ -136,6 +154,17 @@ static void *cpu_thread(void *dummyptr) {
#endif
if (send_rst) {
#if 0
printf("Scheduling suspend request...\n");
wantSuspend = true;
#endif
#if 1
printf("Scheduling resume resume request...\n");
wantResume = true;
#endif
#if 0
printf("Sending reset\n");
g_cpu->Reset();
@ -145,7 +174,9 @@ static void *cpu_thread(void *dummyptr) {
//g_vm->Reset();
//g_cpu->Reset();
//((AppleVM *)g_vm)->insertDisk(0, "disks/DIAGS.DSK");
#else
#endif
#if 0
// Swap disks
if (disk1name[0] && disk2name[0]) {
printf("Swapping disks\n");
@ -155,8 +186,9 @@ static void *cpu_thread(void *dummyptr) {
printf("Inserting disk %s in drive 2\n", disk1name);
((AppleVM *)g_vm)->insertDisk(1, disk1name);
}
/*
#else
#endif
#if 0
MMU *mmu = g_vm->getMMU();
printf("PC: 0x%X\n", g_cpu->pc);
@ -165,7 +197,6 @@ static void *cpu_thread(void *dummyptr) {
}
printf("\n");
printf("Dropping to monitor\n");
// drop directly to monitor.
g_cpu->pc = 0xff69; // "call -151"

View File

@ -37,6 +37,16 @@ SDLClock::~SDLClock()
{
}
bool SDLClock::Serialize(int8_t fd)
{
return true;
}
bool SDLClock::Deserialize(int8_t fd)
{
return true;
}
void SDLClock::Reset()
{
}

View File

@ -12,6 +12,9 @@ class SDLClock : public Slot {
SDLClock(AppleMMU *mmu);
virtual ~SDLClock();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
virtual void Reset();
virtual uint8_t readSwitches(uint8_t s);

View File

@ -8,7 +8,6 @@
#include "sdl-filemanager.h"
SDLFileManager::SDLFileManager()
{
numCached = 0;
@ -92,7 +91,7 @@ bool SDLFileManager::readTrack(int8_t fd, uint8_t *toWhere, bool isNib)
// open, seek, read, close.
bool ret = false;
int ffd = open(cachedNames[fd], O_RDONLY);
if (ffd) {
if (ffd != -1) {
lseek(ffd, fileSeekPositions[fd], SEEK_SET);
if (isNib) {
ret = (read(ffd, toWhere, 0x1A00) == 0x1A00);
@ -117,7 +116,7 @@ bool SDLFileManager::readBlock(int8_t fd, uint8_t *toWhere, bool isNib)
// open, seek, read, close.
bool ret = false;
int ffd = open(cachedNames[fd], O_RDONLY);
if (ffd) {
if (ffd != -1) {
lseek(ffd, fileSeekPositions[fd], SEEK_SET);
if (isNib) {
ret = (read(ffd, toWhere, 416) == 416);
@ -146,7 +145,7 @@ bool SDLFileManager::writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib)
// open, seek, write, close.
int ffd = open(cachedNames[fd], O_WRONLY);
if (ffd) {
if (ffd != -1) {
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
return false;
@ -171,7 +170,7 @@ bool SDLFileManager::writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib)
// open, seek, write, close.
int ffd = open(cachedNames[fd], O_WRONLY);
if (ffd) {
if (ffd != -1) {
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
return false;
@ -202,7 +201,7 @@ uint8_t SDLFileManager::readByteAt(int8_t fd, uint32_t pos)
// open, seek, read, close.
bool ret = false;
int ffd = open(cachedNames[fd], O_RDONLY);
if (ffd) {
if (ffd != -1) {
lseek(ffd, pos, SEEK_SET);
ret = (read(ffd, &v, 1) == 1);
close(ffd);
@ -226,8 +225,8 @@ bool SDLFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
// open, seek, write, close.
bool ret = false;
int ffd = open(cachedNames[fd], O_WRONLY);
if (ffd) {
int ffd = open(cachedNames[fd], O_WRONLY|O_CREAT, 0644);
if (ffd != -1) {
lseek(ffd, pos, SEEK_SET);
ret = (write(ffd, &v, 1) == 1);
close(ffd);
@ -236,3 +235,61 @@ bool SDLFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
return ret;
}
bool SDLFileManager::writeByte(int8_t fd, uint8_t v)
{
if (fd < 0 || fd >= numCached)
return false;
if (cachedNames[fd][0] == 0)
return false;
uint32_t pos = fileSeekPositions[fd];
// open, seek, write, close.
bool ret = false;
int ffd = open(cachedNames[fd], O_WRONLY|O_CREAT, 0644);
if (ffd != -1) {
lseek(ffd, pos, SEEK_SET);
ret = (write(ffd, &v, 1) == 1);
if (!ret) {
printf("error writing: %d\n", errno);
}
close(ffd);
} else {
printf("Failed to open '%s' for writing: %d\n",
cachedNames[fd], errno);
}
fileSeekPositions[fd]++;
return ret;
}
uint8_t SDLFileManager::readByte(int8_t fd)
{
if (fd < 0 || fd >= numCached)
return -1; // FIXME: error handling?
if (cachedNames[fd][0] == 0)
return -1; // FIXME: error handling?
uint8_t v = 0;
uint32_t pos = fileSeekPositions[fd];
// open, seek, read, close.
bool ret = false;
int ffd = open(cachedNames[fd], O_RDONLY);
if (ffd != -1) {
lseek(ffd, pos, SEEK_SET);
ret = (read(ffd, &v, 1) == 1);
close(ffd);
}
fileSeekPositions[fd]++;
if (!ret) {
printf("ERROR reading: %d\n", errno);
}
// FIXME: error handling?
return v;
}

View File

@ -8,7 +8,7 @@ class SDLFileManager : public FileManager {
public:
SDLFileManager();
virtual ~SDLFileManager();
virtual int8_t openFile(const char *name);
virtual void closeFile(int8_t fd);
@ -24,10 +24,11 @@ class SDLFileManager : public FileManager {
virtual uint8_t readByteAt(int8_t fd, uint32_t pos);
virtual bool writeByteAt(int8_t fd, uint8_t v, uint32_t pos);
virtual uint8_t readByte(int8_t fd);
virtual bool writeByte(int8_t fd, uint8_t v);
private:
int8_t numCached;
char cachedNames[MAXFILES][MAXPATH];
unsigned long fileSeekPositions[MAXFILES];
};

1
teensy/LRingBuffer.cpp Symbolic link
View File

@ -0,0 +1 @@
../LRingBuffer.cpp

1
teensy/LRingBuffer.h Symbolic link
View File

@ -0,0 +1 @@
../LRingBuffer.h

View File

@ -22,11 +22,13 @@ enum {
ACT_HD2 = 9,
ACT_VOLPLUS = 10,
ACT_VOLMINUS = 11,
ACT_SUSPEND = 12,
ACT_RESTORE = 13,
NUM_ACTIONS = 12
NUM_ACTIONS = 14
};
const char *titles[NUM_ACTIONS] = { "Resume",
const char *titles[NUM_ACTIONS] = { "Resume VM",
"Reset",
"Cold Reboot",
"Drop to Monitor",
@ -37,7 +39,9 @@ const char *titles[NUM_ACTIONS] = { "Resume",
"%s HD 1",
"%s HD 2",
"Volume +",
"Volume -"
"Volume -",
"Suspend",
"Restore"
};
// FIXME: abstract the pin # rather than repeating it here
@ -162,6 +166,15 @@ bool BIOS::runUntilDone()
}
volumeDidChange = true;
break;
case ACT_SUSPEND:
// 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...
((AppleVM *)g_vm)->Resume("suspend.vm");
break;
}
}
@ -236,6 +249,8 @@ bool BIOS::isActionActive(int8_t action)
case ACT_DISK2:
case ACT_HD1:
case ACT_HD2:
case ACT_SUSPEND:
case ACT_RESTORE:
return true;
case ACT_VOLPLUS:
@ -313,7 +328,7 @@ void BIOS::DrawMainMenu(int8_t selection)
// draw the volume bar
uint16_t volCutoff = 300.0 * (float)((float) g_volume / 15.0);
for (uint8_t y=200; y<=210; y++) {
for (uint8_t y=220; y<=230; y++) {
((TeensyDisplay *)g_display)->moveTo(10, y);
for (uint16_t x = 0; x< 300; x++) {
((TeensyDisplay *)g_display)->drawNextPixel( x <= volCutoff ? 0xFFFF : 0x0010 );

View File

@ -37,6 +37,16 @@ TeensyClock::~TeensyClock()
{
}
bool TeensyClock::Serialize(int8_t fd)
{
return true;
}
bool TeensyClock::Deserialize(int8_t fd)
{
return true;
}
void TeensyClock::Reset()
{
}

View File

@ -16,6 +16,9 @@ class TeensyClock : public Slot {
TeensyClock(AppleMMU *mmu);
virtual ~TeensyClock();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
virtual void Reset();
virtual uint8_t readSwitches(uint8_t s);

View File

@ -79,6 +79,8 @@ void TeensyFileManager::closeFile(int8_t fd)
{
// invalidate the raw file cache
if (rawFd != -1) {
Serial.print("Invalidating raw file cache ");
Serial.println(rawFd);
f_close(&rawFil);
rawFd = -1;
}
@ -298,21 +300,26 @@ bool TeensyFileManager::_prepCache(int8_t fd)
// Not our cached file, or we have no cached file
if (rawFd != -1) {
// Close the old one if we had one
Serial.println("closing old HD cache");
Serial.println("closing old cache file");
f_close(&rawFil);
rawFd = -1;
}
Serial.println("opening new cache file");
// Open the new one
TCHAR buf[MAXPATH];
char2tchar(cachedNames[fd], MAXPATH, buf);
rc = f_open(&rawFil, (TCHAR*) buf, FA_READ|FA_WRITE);
rc = f_open(&rawFil, (TCHAR*) buf, FA_READ|FA_WRITE|FA_OPEN_ALWAYS);
if (rc) {
Serial.println("readByteAt: failed to open");
Serial.print("_prepCache: failed to open ");
Serial.println(cachedNames[fd]);
return false;
}
Serial.println("new cache file open");
rawFd = fd; // cache is live
Serial.print("New cache file is ");
Serial.println(fd);
} else {
// Serial.println("reopning same cache");
}
return (!rc);
@ -364,3 +371,65 @@ bool TeensyFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
return (ret == 1);
}
uint8_t TeensyFileManager::readByte(int8_t fd)
{
// open, seek, read, close.
if (fd < 0 || fd >= numCached)
return false;
if (cachedNames[fd][0] == 0)
return false;
FRESULT rc;
_prepCache(fd);
uint32_t pos = fileSeekPositions[fd];
rc = f_lseek(&rawFil, pos);
if (rc) {
Serial.println("readByteAt: seek failed");
return false;
}
uint8_t b;
UINT v;
f_read(&rawFil, &b, 1, &v);
fileSeekPositions[fd]++;
// FIXME: check v == 1 & handle error
return b;
}
bool TeensyFileManager::writeByte(int8_t fd, uint8_t v)
{
// open, seek, write, close.
if (fd < 0 || fd >= numCached) {
Serial.println("failed writeByte - invalid fd");
return false;
}
if (cachedNames[fd][0] == 0) {
Serial.println("failed writeByte - no cache name");
return false;
}
FRESULT rc;
_prepCache(fd);
uint32_t pos = fileSeekPositions[fd];
rc = f_lseek(&rawFil, pos);
UINT ret;
f_write(&rawFil, &v, 1, &ret);
fileSeekPositions[fd]++;
if (ret != 1) {
Serial.println("Write failed");
}
return (ret == 1);
}

View File

@ -8,7 +8,7 @@ class TeensyFileManager : public FileManager {
public:
TeensyFileManager();
virtual ~TeensyFileManager();
virtual int8_t openFile(const char *name);
virtual void closeFile(int8_t fd);
@ -23,14 +23,15 @@ class TeensyFileManager : public FileManager {
virtual uint8_t readByteAt(int8_t fd, uint32_t pos);
virtual bool writeByteAt(int8_t fd, uint8_t v, uint32_t pos);
virtual uint8_t readByte(int8_t fd);
virtual bool writeByte(int8_t fd, uint8_t v);
private:
bool _prepCache(int8_t fd);
private:
int8_t numCached;
char cachedNames[MAXFILES][MAXPATH];
unsigned long fileSeekPositions[MAXFILES];
};
#endif

View File

@ -1,7 +1,7 @@
#include <Arduino.h>
#include "teensy-keyboard.h"
#include <Keypad.h>
#include <RingBuffer.h>
#include "LRingBuffer.h"
const byte ROWS = 5;
const byte COLS = 13;
@ -17,7 +17,7 @@ char keys[ROWS][COLS] = {
uint8_t rowsPins[ROWS] = { 33, 34, 35, 36, 37 };
uint8_t colsPins[COLS] = { 0, 1, 3, 4, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
Keypad keypad(makeKeymap(keys), rowsPins, colsPins, ROWS, COLS);
RingBuffer buffer(10); // 10 keys should be plenty, right?
LRingBuffer buffer(10); // 10 keys should be plenty, right?
static uint8_t shiftedNumber[] = { '<', // ,
'_', // -

3
vm.h
View File

@ -17,6 +17,9 @@ class VM {
VM() { mmu=NULL; vmdisplay = NULL; videoBuffer = (uint8_t *)calloc(DISPLAYRUN * DISPLAYHEIGHT / 2, 1); hasIRQ = false;}
virtual ~VM() { if (mmu) delete mmu; if (vmdisplay) delete vmdisplay; free(videoBuffer); }
virtual void Suspend(const char *fn) = 0;
virtual void Resume(const char *fn) = 0;
virtual void SetMMU(MMU *mmu) { this->mmu = mmu; }
virtual MMU *getMMU() { return mmu; }
virtual VMKeyboard *getKeyboard() = 0;