mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-12-09 20:50:26 +00:00
added suspend/resume
This commit is contained in:
parent
061eca8c3a
commit
753a9a5f24
177
LRingBuffer.cpp
Normal file
177
LRingBuffer.cpp
Normal 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;
|
||||
}
|
@ -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();
|
||||
|
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
|
||||
|
||||
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
|
||||
|
||||
|
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"
|
||||
|
||||
// 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]);
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
83
cpu.cpp
@ -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
3
cpu.h
@ -28,6 +28,9 @@ class Cpu {
|
||||
Cpu();
|
||||
~Cpu();
|
||||
|
||||
bool Serialize(int8_t fh);
|
||||
bool Deserialize(int8_t fh);
|
||||
|
||||
void Reset();
|
||||
|
||||
void nmi();
|
||||
|
@ -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
3
mmu.h
@ -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
|
||||
|
39
sdl/aiie.cpp
39
sdl/aiie.cpp
@ -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(¤tTime);
|
||||
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"
|
||||
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||