mirror of https://github.com/JorjBauer/aiie.git
added suspend/resume
This commit is contained in:
parent
061eca8c3a
commit
753a9a5f24
|
@ -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;
|
||||||
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
#ifndef __RINGBUFFER_H
|
#ifndef __LRINGBUFFER_H
|
||||||
#define __RINGBUFFER_H
|
#define __LRINGBUFFER_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class RingBuffer {
|
class LRingBuffer {
|
||||||
public:
|
public:
|
||||||
RingBuffer(int16_t length);
|
LRingBuffer(int16_t length);
|
||||||
~RingBuffer();
|
~LRingBuffer();
|
||||||
|
|
||||||
|
bool Serialize(int8_t fd);
|
||||||
|
bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -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
|
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
|
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
|
||||||
|
|
||||||
|
|
113
RingBuffer.cpp
113
RingBuffer.cpp
|
@ -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;
|
|
||||||
}
|
|
|
@ -13,6 +13,9 @@
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
// Serializing token for MMU data
|
||||||
|
#define MMUMAGIC 'M'
|
||||||
|
|
||||||
// apple //e memory map
|
// apple //e memory map
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -57,6 +60,113 @@ AppleMMU::~AppleMMU()
|
||||||
// FIXME: clean up the memory we allocated
|
// 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()
|
void AppleMMU::Reset()
|
||||||
{
|
{
|
||||||
resetRAM();
|
resetRAM();
|
||||||
|
@ -680,7 +790,7 @@ void AppleMMU::resetRAM()
|
||||||
preWriteFlag = false;
|
preWriteFlag = false;
|
||||||
|
|
||||||
// Clear all the pages
|
// 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++) {
|
for (uint8_t j=0; j<5; j++) {
|
||||||
if (ramPages[i][j]) {
|
if (ramPages[i][j]) {
|
||||||
for (uint16_t k=0; k<0x100; k++) {
|
for (uint16_t k=0; k<0x100; k++) {
|
||||||
|
@ -741,6 +851,10 @@ void AppleMMU::resetRAM()
|
||||||
|
|
||||||
void AppleMMU::setSlot(int8_t slotnum, Slot *peripheral)
|
void AppleMMU::setSlot(int8_t slotnum, Slot *peripheral)
|
||||||
{
|
{
|
||||||
|
if (slots[slotnum]) {
|
||||||
|
delete slots[slotnum];
|
||||||
|
}
|
||||||
|
|
||||||
slots[slotnum] = peripheral;
|
slots[slotnum] = peripheral;
|
||||||
if (slots[slotnum]) {
|
if (slots[slotnum]) {
|
||||||
slots[slotnum]->loadROM(ramPages[0xC0 + slotnum][0]);
|
slots[slotnum]->loadROM(ramPages[0xC0 + slotnum][0]);
|
||||||
|
|
|
@ -35,6 +35,9 @@ class AppleMMU : public MMU {
|
||||||
AppleMMU(AppleDisplay *display);
|
AppleMMU(AppleDisplay *display);
|
||||||
virtual ~AppleMMU();
|
virtual ~AppleMMU();
|
||||||
|
|
||||||
|
virtual bool Serialize(int8_t fd);
|
||||||
|
virtual bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
virtual uint8_t read(uint16_t address);
|
virtual uint8_t read(uint16_t address);
|
||||||
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage);
|
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage);
|
||||||
virtual void write(uint16_t address, uint8_t v);
|
virtual void write(uint16_t address, uint8_t v);
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
const char *suspendHdr = "Sus1";
|
||||||
|
|
||||||
AppleVM::AppleVM()
|
AppleVM::AppleVM()
|
||||||
{
|
{
|
||||||
// FIXME: all this typecasting makes me knife-stabby
|
// FIXME: all this typecasting makes me knife-stabby
|
||||||
|
@ -46,6 +49,81 @@ AppleVM::~AppleVM()
|
||||||
delete parallel;
|
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
|
// fixme: make member vars
|
||||||
unsigned long paddleCycleTrigger[2] = {0, 0};
|
unsigned long paddleCycleTrigger[2] = {0, 0};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ class AppleVM : public VM {
|
||||||
AppleVM();
|
AppleVM();
|
||||||
virtual ~AppleVM();
|
virtual ~AppleVM();
|
||||||
|
|
||||||
|
void Suspend(const char *fn);
|
||||||
|
void Resume(const char *fn);
|
||||||
|
|
||||||
void cpuMaintenance(uint32_t cycles);
|
void cpuMaintenance(uint32_t cycles);
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
|
|
||||||
#include "diskii-rom.h"
|
#include "diskii-rom.h"
|
||||||
|
|
||||||
|
#define DISKIIMAGIC 0xAA
|
||||||
|
|
||||||
DiskII::DiskII(AppleMMU *mmu)
|
DiskII::DiskII(AppleMMU *mmu)
|
||||||
{
|
{
|
||||||
this->trackBuffer = new RingBuffer(NIBTRACKSIZE);
|
this->trackBuffer = new LRingBuffer(NIBTRACKSIZE);
|
||||||
this->rawTrackBuffer = (uint8_t *)malloc(4096);
|
this->rawTrackBuffer = (uint8_t *)malloc(4096);
|
||||||
|
|
||||||
this->mmu = mmu;
|
this->mmu = mmu;
|
||||||
|
@ -46,6 +48,101 @@ DiskII::~DiskII()
|
||||||
free(this->rawTrackBuffer); this->rawTrackBuffer = NULL;
|
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()
|
void DiskII::Reset()
|
||||||
{
|
{
|
||||||
curPhase[0] = curPhase[1] = 0;
|
curPhase[0] = curPhase[1] = 0;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "applemmu.h"
|
#include "applemmu.h"
|
||||||
#include "Slot.h"
|
#include "Slot.h"
|
||||||
|
|
||||||
#include "RingBuffer.h"
|
#include "LRingBuffer.h"
|
||||||
#include "nibutil.h"
|
#include "nibutil.h"
|
||||||
|
|
||||||
class DiskII : public Slot {
|
class DiskII : public Slot {
|
||||||
|
@ -20,6 +20,9 @@ class DiskII : public Slot {
|
||||||
DiskII(AppleMMU *mmu);
|
DiskII(AppleMMU *mmu);
|
||||||
virtual ~DiskII();
|
virtual ~DiskII();
|
||||||
|
|
||||||
|
virtual bool Serialize(int8_t fd);
|
||||||
|
virtual bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
virtual void Reset(); // used by BIOS cold-boot
|
virtual void Reset(); // used by BIOS cold-boot
|
||||||
virtual uint8_t readSwitches(uint8_t s);
|
virtual uint8_t readSwitches(uint8_t s);
|
||||||
virtual void writeSwitches(uint8_t s, uint8_t v);
|
virtual void writeSwitches(uint8_t s, uint8_t v);
|
||||||
|
@ -52,7 +55,7 @@ class DiskII : public Slot {
|
||||||
volatile int8_t curPhase[2];
|
volatile int8_t curPhase[2];
|
||||||
volatile bool trackDirty; // does this track need flushing to disk?
|
volatile bool trackDirty; // does this track need flushing to disk?
|
||||||
uint8_t readWriteLatch;
|
uint8_t readWriteLatch;
|
||||||
RingBuffer *trackBuffer; // nibblized data
|
LRingBuffer *trackBuffer; // nibblized data
|
||||||
uint8_t *rawTrackBuffer; // not nibblized data
|
uint8_t *rawTrackBuffer; // not nibblized data
|
||||||
|
|
||||||
bool writeMode;
|
bool writeMode;
|
||||||
|
|
|
@ -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()
|
void HD32::Reset()
|
||||||
{
|
{
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
|
|
|
@ -12,13 +12,16 @@
|
||||||
#include "applemmu.h"
|
#include "applemmu.h"
|
||||||
#include "Slot.h"
|
#include "Slot.h"
|
||||||
|
|
||||||
#include "RingBuffer.h"
|
#include "LRingBuffer.h"
|
||||||
|
|
||||||
class HD32 : public Slot {
|
class HD32 : public Slot {
|
||||||
public:
|
public:
|
||||||
HD32(AppleMMU *mmu);
|
HD32(AppleMMU *mmu);
|
||||||
virtual ~HD32();
|
virtual ~HD32();
|
||||||
|
|
||||||
|
virtual bool Serialize(int8_t fd);
|
||||||
|
virtual bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
virtual void Reset(); // used by BIOS cold-boot
|
virtual void Reset(); // used by BIOS cold-boot
|
||||||
virtual uint8_t readSwitches(uint8_t s);
|
virtual uint8_t readSwitches(uint8_t s);
|
||||||
virtual void writeSwitches(uint8_t s, uint8_t v);
|
virtual void writeSwitches(uint8_t s, uint8_t v);
|
||||||
|
@ -45,7 +48,7 @@ class HD32 : public Slot {
|
||||||
uint8_t enabled;
|
uint8_t enabled;
|
||||||
|
|
||||||
uint8_t errorState[2]; // status of last operation
|
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
|
uint16_t diskBlock[2]; // currently selected block
|
||||||
|
|
||||||
int8_t fd[2];
|
int8_t fd[2];
|
||||||
|
|
|
@ -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, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
|
||||||
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
|
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)
|
uint8_t diskType, int8_t track)
|
||||||
{
|
{
|
||||||
int checksum;
|
int checksum;
|
||||||
|
@ -126,7 +126,7 @@ void nibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||||
// trackBuf. This reads from the circular buffer trackBuffer, so if
|
// trackBuf. This reads from the circular buffer trackBuffer, so if
|
||||||
// there's not enough data there, the results are somewhat
|
// there's not enough data there, the results are somewhat
|
||||||
// unpredictable.
|
// 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
|
// Basic check that there's enough buffer data in trackBuffer. Note
|
||||||
// that we're not checking it against startAt; we could be wrapping
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void encodeData(RingBuffer *trackBuffer, uint8_t *data)
|
void encodeData(LRingBuffer *trackBuffer, uint8_t *data)
|
||||||
{
|
{
|
||||||
int16_t i;
|
int16_t i;
|
||||||
int ptr2 = 0;
|
int ptr2 = 0;
|
||||||
|
@ -221,7 +221,7 @@ void encodeData(RingBuffer *trackBuffer, uint8_t *data)
|
||||||
trackBuffer->addByte(_trans[lastv]);
|
trackBuffer->addByte(_trans[lastv]);
|
||||||
}
|
}
|
||||||
|
|
||||||
nibErr denibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||||
uint8_t diskType, int8_t track)
|
uint8_t diskType, int8_t track)
|
||||||
{
|
{
|
||||||
// We can't tell exactly what the length should be, b/c there might
|
// We can't tell exactly what the length should be, b/c there might
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "RingBuffer.h"
|
#include "LRingBuffer.h"
|
||||||
|
|
||||||
#define NIBTRACKSIZE 0x1A00
|
#define NIBTRACKSIZE 0x1A00
|
||||||
// Minimum viable nibblized sector size. With GAP bytes, could be much longer.
|
// Minimum viable nibblized sector size. With GAP bytes, could be much longer.
|
||||||
|
@ -32,13 +32,13 @@ enum nibErr {
|
||||||
errorMissingSectors = 2
|
errorMissingSectors = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
void nibblizeTrack(RingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||||
uint8_t diskType, int8_t track);
|
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);
|
uint8_t diskType, int8_t track);
|
||||||
|
|
||||||
|
|
||||||
bool decodeData(RingBuffer *trackBuffer, uint16_t startAt, uint8_t *output);
|
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output);
|
||||||
void encodeData(RingBuffer *trackBuffer, uint8_t *data);
|
void encodeData(LRingBuffer *trackBuffer, uint8_t *data);
|
||||||
|
|
||||||
|
|
|
@ -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()
|
void ParallelCard::Reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ class ParallelCard : public Slot {
|
||||||
ParallelCard();
|
ParallelCard();
|
||||||
virtual ~ParallelCard();
|
virtual ~ParallelCard();
|
||||||
|
|
||||||
|
virtual bool Serialize(int8_t fd);
|
||||||
|
virtual bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
virtual void Reset(); // used by BIOS cold-boot
|
virtual void Reset(); // used by BIOS cold-boot
|
||||||
virtual uint8_t readSwitches(uint8_t s);
|
virtual uint8_t readSwitches(uint8_t s);
|
||||||
virtual void writeSwitches(uint8_t s, uint8_t v);
|
virtual void writeSwitches(uint8_t s, uint8_t v);
|
||||||
|
|
|
@ -13,6 +13,9 @@ class Slot {
|
||||||
public:
|
public:
|
||||||
virtual ~Slot() {};
|
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 void Reset() = 0; // for use at cold-boot
|
||||||
|
|
||||||
virtual uint8_t readSwitches(uint8_t s) = 0;
|
virtual uint8_t readSwitches(uint8_t s) = 0;
|
||||||
|
|
83
cpu.cpp
83
cpu.cpp
|
@ -1,9 +1,14 @@
|
||||||
|
#ifdef TEENSYDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "mmu.h"
|
#include "mmu.h"
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
// To see calls to unimplemented opcodes, define this:
|
// To see calls to unimplemented opcodes, define this:
|
||||||
//#define VERBOSE_CPU_ERRORS
|
//#define VERBOSE_CPU_ERRORS
|
||||||
|
|
||||||
|
@ -21,6 +26,9 @@
|
||||||
#define writemem(addr, val) mmu->write(addr, val)
|
#define writemem(addr, val) mmu->write(addr, val)
|
||||||
#define readmem(addr) mmu->read(addr)
|
#define readmem(addr) mmu->read(addr)
|
||||||
|
|
||||||
|
// serialize suspend/restore token
|
||||||
|
#define CPUMAGIC 0x65
|
||||||
|
|
||||||
enum optype {
|
enum optype {
|
||||||
O_ILLEGAL,
|
O_ILLEGAL,
|
||||||
O_ADC,
|
O_ADC,
|
||||||
|
@ -412,6 +420,81 @@ Cpu::~Cpu()
|
||||||
mmu = NULL;
|
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()
|
void Cpu::Reset()
|
||||||
{
|
{
|
||||||
a = 0;
|
a = 0;
|
||||||
|
|
3
cpu.h
3
cpu.h
|
@ -28,6 +28,9 @@ class Cpu {
|
||||||
Cpu();
|
Cpu();
|
||||||
~Cpu();
|
~Cpu();
|
||||||
|
|
||||||
|
bool Serialize(int8_t fh);
|
||||||
|
bool Deserialize(int8_t fh);
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
void nmi();
|
void nmi();
|
||||||
|
|
|
@ -9,10 +9,90 @@
|
||||||
#define MAXPATH 255
|
#define MAXPATH 255
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FMMAGIC 'F'
|
||||||
|
|
||||||
class FileManager {
|
class FileManager {
|
||||||
public:
|
public:
|
||||||
virtual ~FileManager() {};
|
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 int8_t openFile(const char *name) = 0;
|
||||||
virtual void closeFile(int8_t fd) = 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 uint8_t readByteAt(int8_t fd, uint32_t pos) = 0;
|
||||||
virtual bool writeByteAt(int8_t fd, uint8_t v, 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
|
#endif
|
||||||
|
|
3
mmu.h
3
mmu.h
|
@ -12,6 +12,9 @@ class MMU {
|
||||||
virtual uint8_t read(uint16_t mem) = 0;
|
virtual uint8_t read(uint16_t mem) = 0;
|
||||||
virtual void write(uint16_t mem, uint8_t val) = 0;
|
virtual void write(uint16_t mem, uint8_t val) = 0;
|
||||||
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage) = 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
|
#endif
|
||||||
|
|
39
sdl/aiie.cpp
39
sdl/aiie.cpp
|
@ -35,6 +35,9 @@ pthread_t cpuThreadID;
|
||||||
char disk1name[256] = "\0";
|
char disk1name[256] = "\0";
|
||||||
char disk2name[256] = "\0";
|
char disk2name[256] = "\0";
|
||||||
|
|
||||||
|
volatile bool wantSuspend = false;
|
||||||
|
volatile bool wantResume = false;
|
||||||
|
|
||||||
void sigint_handler(int n)
|
void sigint_handler(int n)
|
||||||
{
|
{
|
||||||
send_rst = 1;
|
send_rst = 1;
|
||||||
|
@ -92,6 +95,21 @@ static void *cpu_thread(void *dummyptr) {
|
||||||
|
|
||||||
printf("free-running\n");
|
printf("free-running\n");
|
||||||
while (1) {
|
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...
|
// cycle down the CPU...
|
||||||
do_gettime(¤tTime);
|
do_gettime(¤tTime);
|
||||||
struct timespec diff = tsSubtract(nextInstructionTime, currentTime);
|
struct timespec diff = tsSubtract(nextInstructionTime, currentTime);
|
||||||
|
@ -136,6 +154,17 @@ static void *cpu_thread(void *dummyptr) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (send_rst) {
|
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
|
#if 0
|
||||||
printf("Sending reset\n");
|
printf("Sending reset\n");
|
||||||
g_cpu->Reset();
|
g_cpu->Reset();
|
||||||
|
@ -145,7 +174,9 @@ static void *cpu_thread(void *dummyptr) {
|
||||||
//g_vm->Reset();
|
//g_vm->Reset();
|
||||||
//g_cpu->Reset();
|
//g_cpu->Reset();
|
||||||
//((AppleVM *)g_vm)->insertDisk(0, "disks/DIAGS.DSK");
|
//((AppleVM *)g_vm)->insertDisk(0, "disks/DIAGS.DSK");
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
// Swap disks
|
// Swap disks
|
||||||
if (disk1name[0] && disk2name[0]) {
|
if (disk1name[0] && disk2name[0]) {
|
||||||
printf("Swapping disks\n");
|
printf("Swapping disks\n");
|
||||||
|
@ -155,8 +186,9 @@ static void *cpu_thread(void *dummyptr) {
|
||||||
printf("Inserting disk %s in drive 2\n", disk1name);
|
printf("Inserting disk %s in drive 2\n", disk1name);
|
||||||
((AppleVM *)g_vm)->insertDisk(1, disk1name);
|
((AppleVM *)g_vm)->insertDisk(1, disk1name);
|
||||||
}
|
}
|
||||||
/*
|
#endif
|
||||||
#else
|
|
||||||
|
#if 0
|
||||||
MMU *mmu = g_vm->getMMU();
|
MMU *mmu = g_vm->getMMU();
|
||||||
|
|
||||||
printf("PC: 0x%X\n", g_cpu->pc);
|
printf("PC: 0x%X\n", g_cpu->pc);
|
||||||
|
@ -165,7 +197,6 @@ static void *cpu_thread(void *dummyptr) {
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
|
||||||
printf("Dropping to monitor\n");
|
printf("Dropping to monitor\n");
|
||||||
// drop directly to monitor.
|
// drop directly to monitor.
|
||||||
g_cpu->pc = 0xff69; // "call -151"
|
g_cpu->pc = 0xff69; // "call -151"
|
||||||
|
|
|
@ -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()
|
void SDLClock::Reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ class SDLClock : public Slot {
|
||||||
SDLClock(AppleMMU *mmu);
|
SDLClock(AppleMMU *mmu);
|
||||||
virtual ~SDLClock();
|
virtual ~SDLClock();
|
||||||
|
|
||||||
|
virtual bool Serialize(int8_t fd);
|
||||||
|
virtual bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
virtual uint8_t readSwitches(uint8_t s);
|
virtual uint8_t readSwitches(uint8_t s);
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
#include "sdl-filemanager.h"
|
#include "sdl-filemanager.h"
|
||||||
|
|
||||||
|
|
||||||
SDLFileManager::SDLFileManager()
|
SDLFileManager::SDLFileManager()
|
||||||
{
|
{
|
||||||
numCached = 0;
|
numCached = 0;
|
||||||
|
@ -92,7 +91,7 @@ bool SDLFileManager::readTrack(int8_t fd, uint8_t *toWhere, bool isNib)
|
||||||
// open, seek, read, close.
|
// open, seek, read, close.
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
int ffd = open(cachedNames[fd], O_RDONLY);
|
int ffd = open(cachedNames[fd], O_RDONLY);
|
||||||
if (ffd) {
|
if (ffd != -1) {
|
||||||
lseek(ffd, fileSeekPositions[fd], SEEK_SET);
|
lseek(ffd, fileSeekPositions[fd], SEEK_SET);
|
||||||
if (isNib) {
|
if (isNib) {
|
||||||
ret = (read(ffd, toWhere, 0x1A00) == 0x1A00);
|
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.
|
// open, seek, read, close.
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
int ffd = open(cachedNames[fd], O_RDONLY);
|
int ffd = open(cachedNames[fd], O_RDONLY);
|
||||||
if (ffd) {
|
if (ffd != -1) {
|
||||||
lseek(ffd, fileSeekPositions[fd], SEEK_SET);
|
lseek(ffd, fileSeekPositions[fd], SEEK_SET);
|
||||||
if (isNib) {
|
if (isNib) {
|
||||||
ret = (read(ffd, toWhere, 416) == 416);
|
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.
|
// open, seek, write, close.
|
||||||
int ffd = open(cachedNames[fd], O_WRONLY);
|
int ffd = open(cachedNames[fd], O_WRONLY);
|
||||||
if (ffd) {
|
if (ffd != -1) {
|
||||||
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
|
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
|
||||||
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
|
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
|
||||||
return false;
|
return false;
|
||||||
|
@ -171,7 +170,7 @@ bool SDLFileManager::writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib)
|
||||||
|
|
||||||
// open, seek, write, close.
|
// open, seek, write, close.
|
||||||
int ffd = open(cachedNames[fd], O_WRONLY);
|
int ffd = open(cachedNames[fd], O_WRONLY);
|
||||||
if (ffd) {
|
if (ffd != -1) {
|
||||||
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
|
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
|
||||||
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
|
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
|
||||||
return false;
|
return false;
|
||||||
|
@ -202,7 +201,7 @@ uint8_t SDLFileManager::readByteAt(int8_t fd, uint32_t pos)
|
||||||
// open, seek, read, close.
|
// open, seek, read, close.
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
int ffd = open(cachedNames[fd], O_RDONLY);
|
int ffd = open(cachedNames[fd], O_RDONLY);
|
||||||
if (ffd) {
|
if (ffd != -1) {
|
||||||
lseek(ffd, pos, SEEK_SET);
|
lseek(ffd, pos, SEEK_SET);
|
||||||
ret = (read(ffd, &v, 1) == 1);
|
ret = (read(ffd, &v, 1) == 1);
|
||||||
close(ffd);
|
close(ffd);
|
||||||
|
@ -226,8 +225,8 @@ bool SDLFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
|
||||||
|
|
||||||
// open, seek, write, close.
|
// open, seek, write, close.
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
int ffd = open(cachedNames[fd], O_WRONLY);
|
int ffd = open(cachedNames[fd], O_WRONLY|O_CREAT, 0644);
|
||||||
if (ffd) {
|
if (ffd != -1) {
|
||||||
lseek(ffd, pos, SEEK_SET);
|
lseek(ffd, pos, SEEK_SET);
|
||||||
ret = (write(ffd, &v, 1) == 1);
|
ret = (write(ffd, &v, 1) == 1);
|
||||||
close(ffd);
|
close(ffd);
|
||||||
|
@ -236,3 +235,61 @@ bool SDLFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,11 @@ class SDLFileManager : public FileManager {
|
||||||
virtual uint8_t readByteAt(int8_t fd, uint32_t pos);
|
virtual uint8_t readByteAt(int8_t fd, uint32_t pos);
|
||||||
virtual bool writeByteAt(int8_t fd, uint8_t v, 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:
|
private:
|
||||||
int8_t numCached;
|
int8_t numCached;
|
||||||
char cachedNames[MAXFILES][MAXPATH];
|
|
||||||
unsigned long fileSeekPositions[MAXFILES];
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../LRingBuffer.cpp
|
|
@ -0,0 +1 @@
|
||||||
|
../LRingBuffer.h
|
|
@ -22,11 +22,13 @@ enum {
|
||||||
ACT_HD2 = 9,
|
ACT_HD2 = 9,
|
||||||
ACT_VOLPLUS = 10,
|
ACT_VOLPLUS = 10,
|
||||||
ACT_VOLMINUS = 11,
|
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",
|
"Reset",
|
||||||
"Cold Reboot",
|
"Cold Reboot",
|
||||||
"Drop to Monitor",
|
"Drop to Monitor",
|
||||||
|
@ -37,7 +39,9 @@ const char *titles[NUM_ACTIONS] = { "Resume",
|
||||||
"%s HD 1",
|
"%s HD 1",
|
||||||
"%s HD 2",
|
"%s HD 2",
|
||||||
"Volume +",
|
"Volume +",
|
||||||
"Volume -"
|
"Volume -",
|
||||||
|
"Suspend",
|
||||||
|
"Restore"
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: abstract the pin # rather than repeating it here
|
// FIXME: abstract the pin # rather than repeating it here
|
||||||
|
@ -162,6 +166,15 @@ bool BIOS::runUntilDone()
|
||||||
}
|
}
|
||||||
volumeDidChange = true;
|
volumeDidChange = true;
|
||||||
break;
|
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_DISK2:
|
||||||
case ACT_HD1:
|
case ACT_HD1:
|
||||||
case ACT_HD2:
|
case ACT_HD2:
|
||||||
|
case ACT_SUSPEND:
|
||||||
|
case ACT_RESTORE:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ACT_VOLPLUS:
|
case ACT_VOLPLUS:
|
||||||
|
@ -313,7 +328,7 @@ void BIOS::DrawMainMenu(int8_t selection)
|
||||||
|
|
||||||
// draw the volume bar
|
// draw the volume bar
|
||||||
uint16_t volCutoff = 300.0 * (float)((float) g_volume / 15.0);
|
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);
|
((TeensyDisplay *)g_display)->moveTo(10, y);
|
||||||
for (uint16_t x = 0; x< 300; x++) {
|
for (uint16_t x = 0; x< 300; x++) {
|
||||||
((TeensyDisplay *)g_display)->drawNextPixel( x <= volCutoff ? 0xFFFF : 0x0010 );
|
((TeensyDisplay *)g_display)->drawNextPixel( x <= volCutoff ? 0xFFFF : 0x0010 );
|
||||||
|
|
|
@ -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()
|
void TeensyClock::Reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@ class TeensyClock : public Slot {
|
||||||
TeensyClock(AppleMMU *mmu);
|
TeensyClock(AppleMMU *mmu);
|
||||||
virtual ~TeensyClock();
|
virtual ~TeensyClock();
|
||||||
|
|
||||||
|
virtual bool Serialize(int8_t fd);
|
||||||
|
virtual bool Deserialize(int8_t fd);
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
virtual uint8_t readSwitches(uint8_t s);
|
virtual uint8_t readSwitches(uint8_t s);
|
||||||
|
|
|
@ -79,6 +79,8 @@ void TeensyFileManager::closeFile(int8_t fd)
|
||||||
{
|
{
|
||||||
// invalidate the raw file cache
|
// invalidate the raw file cache
|
||||||
if (rawFd != -1) {
|
if (rawFd != -1) {
|
||||||
|
Serial.print("Invalidating raw file cache ");
|
||||||
|
Serial.println(rawFd);
|
||||||
f_close(&rawFil);
|
f_close(&rawFil);
|
||||||
rawFd = -1;
|
rawFd = -1;
|
||||||
}
|
}
|
||||||
|
@ -298,21 +300,26 @@ bool TeensyFileManager::_prepCache(int8_t fd)
|
||||||
// Not our cached file, or we have no cached file
|
// Not our cached file, or we have no cached file
|
||||||
if (rawFd != -1) {
|
if (rawFd != -1) {
|
||||||
// Close the old one if we had one
|
// Close the old one if we had one
|
||||||
Serial.println("closing old HD cache");
|
Serial.println("closing old cache file");
|
||||||
f_close(&rawFil);
|
f_close(&rawFil);
|
||||||
rawFd = -1;
|
rawFd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Serial.println("opening new cache file");
|
||||||
// Open the new one
|
// Open the new one
|
||||||
TCHAR buf[MAXPATH];
|
TCHAR buf[MAXPATH];
|
||||||
char2tchar(cachedNames[fd], MAXPATH, buf);
|
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) {
|
if (rc) {
|
||||||
Serial.println("readByteAt: failed to open");
|
Serial.print("_prepCache: failed to open ");
|
||||||
|
Serial.println(cachedNames[fd]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Serial.println("new cache file open");
|
|
||||||
rawFd = fd; // cache is live
|
rawFd = fd; // cache is live
|
||||||
|
Serial.print("New cache file is ");
|
||||||
|
Serial.println(fd);
|
||||||
|
} else {
|
||||||
|
// Serial.println("reopning same cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!rc);
|
return (!rc);
|
||||||
|
@ -364,3 +371,65 @@ bool TeensyFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
|
||||||
return (ret == 1);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,14 @@ class TeensyFileManager : public FileManager {
|
||||||
virtual uint8_t readByteAt(int8_t fd, uint32_t pos);
|
virtual uint8_t readByteAt(int8_t fd, uint32_t pos);
|
||||||
virtual bool writeByteAt(int8_t fd, uint8_t v, 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:
|
private:
|
||||||
bool _prepCache(int8_t fd);
|
bool _prepCache(int8_t fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int8_t numCached;
|
int8_t numCached;
|
||||||
char cachedNames[MAXFILES][MAXPATH];
|
|
||||||
unsigned long fileSeekPositions[MAXFILES];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "teensy-keyboard.h"
|
#include "teensy-keyboard.h"
|
||||||
#include <Keypad.h>
|
#include <Keypad.h>
|
||||||
#include <RingBuffer.h>
|
#include "LRingBuffer.h"
|
||||||
|
|
||||||
const byte ROWS = 5;
|
const byte ROWS = 5;
|
||||||
const byte COLS = 13;
|
const byte COLS = 13;
|
||||||
|
@ -17,7 +17,7 @@ char keys[ROWS][COLS] = {
|
||||||
uint8_t rowsPins[ROWS] = { 33, 34, 35, 36, 37 };
|
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 };
|
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);
|
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[] = { '<', // ,
|
static uint8_t shiftedNumber[] = { '<', // ,
|
||||||
'_', // -
|
'_', // -
|
||||||
|
|
3
vm.h
3
vm.h
|
@ -17,6 +17,9 @@ class VM {
|
||||||
VM() { mmu=NULL; vmdisplay = NULL; videoBuffer = (uint8_t *)calloc(DISPLAYRUN * DISPLAYHEIGHT / 2, 1); hasIRQ = false;}
|
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 ~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 void SetMMU(MMU *mmu) { this->mmu = mmu; }
|
||||||
virtual MMU *getMMU() { return mmu; }
|
virtual MMU *getMMU() { return mmu; }
|
||||||
virtual VMKeyboard *getKeyboard() = 0;
|
virtual VMKeyboard *getKeyboard() = 0;
|
||||||
|
|
Loading…
Reference in New Issue