diff --git a/LRingBuffer.cpp b/LRingBuffer.cpp
index e8e3a3e..18cc14a 100644
--- a/LRingBuffer.cpp
+++ b/LRingBuffer.cpp
@@ -142,7 +142,10 @@ uint8_t LRingBuffer::consumeByte()
uint8_t LRingBuffer::peek(int16_t idx)
{
- uint16_t p = (this->ptr + idx) % this->max;
+ if (!this->fill)
+ return 0; // No data in buffer; nothing to see
+
+ uint16_t p = (this->ptr + idx) % this->fill;
return this->buffer[p];
}
diff --git a/Makefile b/Makefile
index b94348f..a870d2e 100755
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,31 @@
LDFLAGS=-L/usr/local/lib
-SDLLIBS=-lSDL2
+SDLLIBS=-lSDL2 -lpthread
+FBLIBS=-lpthread
-CXXFLAGS=-Wall -I .. -I . -I apple -I sdl -I/usr/local/include/SDL2 -O3 -g
+CXXFLAGS=-Wall -I/usr/include/SDL2 -I .. -I . -I apple -I nix -I sdl -I/usr/local/include/SDL2 -g -O3
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 LRingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o images.o apple/appleui.o vmram.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 images.o apple/appleui.o vmram.o bios.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
+FBOBJS=linuxfb/linux-speaker.o linuxfb/fb-display.o linuxfb/linux-keyboard.o linuxfb/fb-paddles.o nix/nix-filemanager.o linuxfb/aiie.o linuxfb/linux-printer.o nix/nix-clock.o
+
+SDLOBJS=sdl/sdl-speaker.o sdl/sdl-display.o sdl/sdl-keyboard.o sdl/sdl-paddles.o nix/nix-filemanager.o sdl/aiie.o sdl/sdl-printer.o nix/nix-clock.o
ROMS=apple/applemmu-rom.h apple/diskii-rom.h apple/parallel-rom.h apple/hd32-rom.h
-all: sdl
+.PHONY: roms
+
+all:
+ @echo You want \'make sdl\' or \'make linuxfb\'.
sdl: roms $(COMMONOBJS) $(SDLOBJS)
g++ $(LDFLAGS) $(SDLLIBS) -o aiie-sdl $(COMMONOBJS) $(SDLOBJS)
+linuxfb: roms $(COMMONOBJS) $(FBOBJS)
+ g++ $(LDFLAGS) $(FBLIBS) -o aiie-fb $(COMMONOBJS) $(FBOBJS)
+
clean:
rm -f *.o *~ */*.o */*~ testharness.basic testharness.verbose testharness.extended apple/diskii-rom.h apple/applemmu-rom.h apple/parallel-rom.h aiie-sdl
diff --git a/README.md b/README.md
index de05d6f..539ec64 100644
--- a/README.md
+++ b/README.md
@@ -76,20 +76,13 @@ perfect. Do as you see fit :)
Environment and Libraries
-------------------------
-I built this with arduino 1.8.1 and TeensyDuino 1.35.
+I built this with arduino 1.8.5 and TeensyDuino 1.40.
https://www.pjrc.com/teensy/td_download.html
These libraries I'm using right from Teensy's environment: TimerOne;
SPI; EEPROM; Time; Keypad.
-There's an error in the Time library. On Macs, where the filesystem is
-case-insensitive, you'll have to do something like this:
-
-
- $ mv /Applications/Arduino.app/Contents/Java//hardware/teensy/avr/libraries/Time/Time.h /Applications/Arduino.app/Contents/Java//hardware/teensy/avr/libraries/Time/_Time.h
-
-
I'm also using these libraries that don't come with TeensyDuino:
### SdFat ###
diff --git a/apple/appledisplay.h b/apple/appledisplay.h
index 35e2a96..59fe843 100644
--- a/apple/appledisplay.h
+++ b/apple/appledisplay.h
@@ -55,6 +55,8 @@ class AppleDisplay : public VMDisplay{
void displayTypeChanged();
+ const unsigned char *xlateChar(uint8_t c, bool *invert);
+
private:
bool deinterlaceAddress(uint16_t address, uint8_t *row, uint8_t *col);
@@ -64,8 +66,6 @@ class AppleDisplay : public VMDisplay{
void Draw14HiresPixelsAt(uint16_t addr);
void Draw80LoresPixelAt(uint8_t c, uint8_t x, uint8_t y, uint8_t offset);
- const unsigned char *xlateChar(uint8_t c, bool *invert);
-
void redraw40ColumnText(uint8_t startingY);
void redraw80ColumnText(uint8_t startingY);
void redrawHires();
diff --git a/apple/applekeyboard.cpp b/apple/applekeyboard.cpp
index 3e2cc01..4e0fec9 100644
--- a/apple/applekeyboard.cpp
+++ b/apple/applekeyboard.cpp
@@ -45,7 +45,7 @@ uint8_t AppleKeyboard::translateKeyWithModifiers(uint8_t k)
k = k - 'A' + 'a';
}
- if (keysDown[_CTRL]) {
+ if (keysDown[PK_CTRL]) {
if (k >= 'a' && k <= 'z') {
return k - 'a' + 1;
}
@@ -56,7 +56,7 @@ uint8_t AppleKeyboard::translateKeyWithModifiers(uint8_t k)
return k - 'a' + 'A';
}
- if (keysDown[LSHFT] || keysDown[RSHFT]) {
+ if (keysDown[PK_LSHFT] || keysDown[PK_RSHFT]) {
if (k >= 'a' && k <= 'z') {
return k - 'a' + 'A';
}
@@ -125,15 +125,15 @@ void AppleKeyboard::keyDepressed(uint8_t k)
keyThatIsRepeating = translateKeyWithModifiers(k);
startRepeatTimer = g_cpu->cycles + STARTREPEAT;
mmu->keyboardInput(keyThatIsRepeating);
- } else if (k == LA) {
+ } else if (k == PK_LA) {
// Special handling: apple keys
mmu->setAppleKey(0, true);
return;
- } else if (k == RA) {
+ } else if (k == PK_RA) {
// Special handling: apple keys
mmu->setAppleKey(1, true);
return;
- } else if (k == LOCK) {
+ } else if (k == PK_LOCK) {
// Special handling: caps lock
capsLockEnabled = !capsLockEnabled;
return;
@@ -145,15 +145,15 @@ void AppleKeyboard::keyReleased(uint8_t k)
keysDown[k] = false;
// Special handling: apple keys
- if (k == LA) {
+ if (k == PK_LA) {
mmu->setAppleKey(0, false);
return;
}
- if (k == RA) {
+ if (k == PK_RA) {
mmu->setAppleKey(1, false);
return;
}
- if (k == LOCK) {
+ if (k == PK_LOCK) {
// Nothing to do when the caps lock key is released.
return;
}
diff --git a/apple/applemmu.cpp b/apple/applemmu.cpp
index df807a8..22039b2 100644
--- a/apple/applemmu.cpp
+++ b/apple/applemmu.cpp
@@ -54,33 +54,68 @@ Current write page table [512 bytes] in real ram
*/
-// All the pages...
+// All the pages. Because we don't have enough RAM for both the
+// display's DMA and the Apple's 148k (128k + ROM space), we're using
+// an external SRAM for some of this. Anything that's accessed very
+// often should be in the *low* pages, b/c those are in internal
+// Teensy RAM. When we run out of preallocated RAM (cf. vmram.h), we
+// fall over to an external 256kB SRAM (which is much slower).
+//
+// Zero page (and its alts) are the most used pages (the stack is in
+// page 1).
+//
+// We want the video display pages in real RAM as much as possible,
+// since blits wind up touching so much of it. If we can keep that in
+// main RAM, then the blits won't try to read the external SRAM while
+// the CPU is writing to it.
+//
+//
+// After that it's all a guess. Should it be slot ROMs?
+// extended RAM? Hires RAM? FIXME: do some analysis of common memory
+// hotspots...
+
enum {
- MP_ZP = 0, // page 0/1 * 2 page variants; 0..3
- MP_C1 = 4, // start of 0xC1-0xCF * 2 page variants = 30, 4..33
- MP_D0 = 34, // start of 0xD0-0xDF * 5 page variants = 80; 34..113
- MP_E0 = 114, // start of 0xE0-0xFF * 3 page variants = 96; 114..209
- MP_2 = 210, // start of 0x02-0xBF * 2 page variants = 380; 210..589
+ // Pages we want to fall to internal RAM:
+ MP_ZP = 0, // page 0/1 * 2 page variants = 4; 0..3
+ MP_4 = 4, // 0x04 - 0x07 (text display pages) * 2 variants = 8; 4..11
+ MP_20 = 12, // 0x20 - 0x5F * 2 variants = 128; 12..139
+ // Pages that can go to external SRAM:
+ MP_2 = 140, // 0x02 - 0x03 * 2 variants = 4; 140..143
+ MP_8 = 144, // 0x08 - 0x1F * 2 = 48; 144..191
+ MP_60 = 192, // 0x60 - 0xBF * 2 = 192; 192..383
+
+ MP_C1 = 384, // start of 0xC1-0xCF * 2 page variants = 30; 384-413
+ MP_D0 = 414, // start of 0xD0-0xDF * 5 page variants = 80; 414-493
+ MP_E0 = 493, // start of 0xE0-0xFF * 3 page variants = 96; 494-589
MP_C0 = 590
// = 591 pages in all (147.75k)
};
+
static uint16_t _pageNumberForRam(uint8_t highByte, uint8_t variant)
{
if (highByte <= 1) {
// zero page.
return highByte + (variant*2) + MP_ZP;
}
-
- if (highByte <= 0xBF) {
- // 2-0xBF 190 * 2 pages = 380 pages = 95k
+ if (highByte <= 3) {
return ((highByte - 2) * 2 + variant + MP_2);
}
-
- if (highByte == 0xC0) {
+ if (highByte <= 7) {
+ return ((highByte - 4) * 2 + variant + MP_4);
+ }
+ if (highByte <= 0x1f) {
+ return ((highByte - 8) * 2 + variant + MP_8);
+ }
+ if (highByte <= 0x5f) {
+ return ((highByte - 0x20) * 2 + variant + MP_20);
+ }
+ if (highByte <= 0xbf) {
+ return ((highByte - 0x60) * 2 + variant + MP_60);
+ }
+ if (highByte == 0xc0) {
return MP_C0;
}
-
if (highByte <= 0xCF) {
// 0xC1-0xCF 15 * 2 pages = 30 pages (7.5k)
return ((highByte - 0xC1) * 2 + variant + MP_C1);
@@ -1059,5 +1094,5 @@ void AppleMMU::updateMemoryPages()
void AppleMMU::setAppleKey(int8_t which, bool isDown)
{
assert(which <= 1);
- g_ram.writeByte(0xC061 + which, isDown ? 0x80 : 0x00);
+ g_ram.writeByte((writePages[0xC0] << 8) | (0x61 + which), isDown ? 0x80 : 0x00);
}
diff --git a/apple/applemmu.h b/apple/applemmu.h
index 0473648..1ea8758 100644
--- a/apple/applemmu.h
+++ b/apple/applemmu.h
@@ -3,7 +3,7 @@
#include
#include "appledisplay.h"
-#include "Slot.h"
+#include "slot.h"
#include "mmu.h"
// when we read a nondeterministic result, we return FLOATING. Maybe
diff --git a/apple/appleui.cpp b/apple/appleui.cpp
index d94e260..af397c8 100644
--- a/apple/appleui.cpp
+++ b/apple/appleui.cpp
@@ -55,8 +55,14 @@ void AppleUI::drawOnOffUIElement(uint8_t element, bool state)
if (element == UIeDisk2_activity)
xoff += 135;
for (int x=0; x<6; x++) {
- g_display->drawPixel(x + xoff, yoff, state ? 0xF800 : 0x8AA9);
- g_display->drawPixel(x + xoff, yoff + 1, state ? 0xF800 : 0x8AA9);
+ // Can't draw this from inside the interrupt; might already be
+ // drawing the screen from outside the interrupt. Temporary
+ // hack - remove this completely; FIXME: update diskii.cpp to
+ // queue it somehow, for drawing in a maintenance function, to
+ // be called from the main thread and not the interrupt
+
+ // g_display->drawPixel(x + xoff, yoff, state ? 0xF800 : 0x8AA9);
+ // g_display->drawPixel(x + xoff, yoff + 1, state ? 0xF800 : 0x8AA9);
}
}
diff --git a/apple/applevm.cpp b/apple/applevm.cpp
index 71113af..4c47a7f 100644
--- a/apple/applevm.cpp
+++ b/apple/applevm.cpp
@@ -30,8 +30,8 @@ AppleVM::AppleVM()
teensyClock = new TeensyClock((AppleMMU *)mmu);
((AppleMMU *)mmu)->setSlot(5, teensyClock);
#else
- sdlClock = new SDLClock((AppleMMU *)mmu);
- ((AppleMMU *)mmu)->setSlot(5, sdlClock);
+ nixClock = new NixClock((AppleMMU *)mmu);
+ ((AppleMMU *)mmu)->setSlot(5, nixClock);
#endif
hd32 = new HD32((AppleMMU *)mmu);
@@ -43,7 +43,7 @@ AppleVM::~AppleVM()
#ifdef TEENSYDUINO
delete teensyClock;
#else
- delete sdlClock;
+ delete nixClock;
#endif
delete disk6;
delete parallel;
diff --git a/apple/applevm.h b/apple/applevm.h
index 904d0f3..60930e5 100644
--- a/apple/applevm.h
+++ b/apple/applevm.h
@@ -10,7 +10,7 @@
#ifdef TEENSYDUINO
#include "teensy-clock.h"
#else
-#include "sdl-clock.h"
+#include "nix-clock.h"
#endif
#include "vm.h"
@@ -47,7 +47,7 @@ class AppleVM : public VM {
#ifdef TEENSYDUINO
TeensyClock *teensyClock;
#else
- SDLClock *sdlClock;
+ NixClock *nixClock;
#endif
};
diff --git a/apple/diskii.cpp b/apple/diskii.cpp
index dfc8693..e55c1cf 100644
--- a/apple/diskii.cpp
+++ b/apple/diskii.cpp
@@ -28,8 +28,6 @@ DiskII::DiskII(AppleMMU *mmu)
curPhase[0] = curPhase[1] = 0;
curHalfTrack[0] = curHalfTrack[1] = 0;
- trackDirty = false;
- trackToRead = -1;
trackToFlush = -1;
writeMode = false;
@@ -51,10 +49,6 @@ 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);
@@ -81,13 +75,6 @@ bool DiskII::Serialize(int8_t fd)
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;
@@ -97,10 +84,6 @@ 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;
@@ -130,10 +113,7 @@ bool DiskII::Deserialize(int8_t 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;
@@ -147,8 +127,6 @@ void DiskII::Reset()
curPhase[0] = curPhase[1] = 0;
curHalfTrack[0] = curHalfTrack[1] = 0;
- trackDirty = false;
- trackToRead = -1;
trackToFlush = -1;
writeMode = false;
@@ -159,12 +137,12 @@ void DiskII::Reset()
ejectDisk(1);
}
+// FIXME: why does this need an argument?
void DiskII::checkFlush(int8_t track)
{
- if (trackDirty && trackToFlush == -1) {
- diskToFlush = selectedDisk;
- trackToFlush = track;
- trackDirty = false; // just so we don't overwrite disk/track to flush before continuing...
+ if (trackToFlush != -1) {
+ flushTrack(trackToFlush, selectedDisk);
+ trackToFlush = -1;
}
}
@@ -249,11 +227,6 @@ uint8_t DiskII::readSwitches(uint8_t s)
// Any even address read returns the readWriteLatch (UTA2E Table 9.1,
// p. 9-12, note 2)
- // if ((s & 1) == 0 && curHalfTrack[selectedDisk] <= 3) {
- // printf("Read: %X\n", readWriteLatch);
- // fflush(stdout);
- // }
-
return (s & 1) ? FLOATING : readWriteLatch;
}
@@ -381,8 +354,11 @@ void DiskII::setPhase(uint8_t phase)
if (curHalfTrack[selectedDisk]>>1 != prevHalfTrack>>1) {
// We're changing track - flush the old track back to disk
checkFlush(prevHalfTrack>>1);
- // mark the new track to be read
- trackToRead = curHalfTrack[selectedDisk]>>1;
+
+ // Prime the cache by reading the current track
+ if (disk[selectedDisk] != -1) {
+ readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
+ }
}
}
@@ -438,11 +414,19 @@ void DiskII::insertDisk(int8_t driveNum, const char *filename, bool drawIt)
// convertDskToNib("/tmp/debug.nib");
#endif
}
+
+ if (driveNum == selectedDisk) {
+ readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
+ }
}
void DiskII::ejectDisk(int8_t driveNum)
{
if (disk[driveNum] != -1) {
+ if (selectedDisk == driveNum) {
+ checkFlush(0); // FIXME: bogus argument
+ trackBuffer->clear();
+ }
g_filemanager->closeFile(disk[driveNum]);
disk[driveNum] = -1;
g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, true);
@@ -463,11 +447,10 @@ void DiskII::select(int8_t which)
// set the selected disk drive
selectedDisk = which;
- // trackToRead = curHalfTrack[selectedDisk]>>1;
- trackToRead = -1; // Assume we don't have to read anything on the
- // newly selected drive. When we get the first
- // read, we'll notice the track buffer is empty...
- trackBuffer->clear();
+ // Preread the current track
+ if (disk[selectedDisk] != -1) {
+ readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
+ }
}
}
@@ -481,11 +464,15 @@ uint8_t DiskII::readOrWriteByte()
if (!trackBuffer->hasData()) {
// Error: writing to empty track buffer? That's a raw write w/o
- // knowing where we are on the disk.
+ // knowing where we are on the disk. Dangerous, at very least;
+ // I'm not sure what the best action would be here. For the
+ // moment, just refuse to write it.
+
+ g_display->debugMsg("DII: unguarded write");
return GAP;
}
- trackDirty = true;
+ trackToFlush = curHalfTrack[selectedDisk]>>1;
// It's possible that a badly behaving OS could try to write more
// data than we have buffer to handle. Don't let it. We should
// only need something like 500 bytes, at worst. In the typical
@@ -520,15 +507,6 @@ uint8_t DiskII::readOrWriteByte()
return 0;
}
- // trackToRead is -1 when we have a filled buffer, or we have no data at all.
- // trackToRead is != -1 when we're flushing our buffer and re-filling it.
- //
- // Don't fill it right here, b/c we don't want to bog down the CPU
- // thread/ISR.
- if (trackToRead == curHalfTrack[selectedDisk]>>1) {// waiting for a read to complete for the current track
- return GAP;
- }
-
// return 0x00 every other byte. Helps the logic sequencer stay in sync.
// Otherwise we wind up waiting long periods of time for it to sync up,
// presumably because we're overrunning it (returning data faster than
@@ -541,43 +519,13 @@ uint8_t DiskII::readOrWriteByte()
whitespace = !whitespace;
- if ((trackToRead != -1) || !trackBuffer->hasData()) {
- checkFlush(curHalfTrack[selectedDisk]>>1);
-
- // Need to read in a track of data and nibblize it. We'll return 0xFF
- // until that completes.
-
- // This might update trackToRead with a different track than the
- // one we're reading. When we finish the read, we'll need to check
- // to be sure that we're still trying to read the same track that
- // we started with.
- trackToRead = curHalfTrack[selectedDisk]>>1;
-
- // While we're waiting for the sector to come around, we'll return
- // GAP bytes.
- return GAP;
- }
-
- return trackBuffer->peekNext();
+ uint8_t ret = trackBuffer->peekNext();
+ return ret;
}
-void DiskII::fillDiskBuffer()
+void DiskII::readDiskTrack(int8_t diskWeAreUsing, int8_t trackWeAreReading)
{
- if (trackToFlush != -1) {
- flushTrack(trackToFlush, diskToFlush); // in case it's dirty: flush before changing drives
- trackBuffer->clear();
-
- trackToFlush = -1;
- }
-
- // No work to do if trackToRead is -1
- if (trackToRead == -1)
- return;
-
- trackDirty = false;
-
- int8_t trackWeAreReading = trackToRead;
- int8_t diskWeAreUsing = selectedDisk;
+ checkFlush(trackWeAreReading); // FIXME: bogus argument
trackBuffer->clear();
trackBuffer->setPeekCursor(0);
@@ -592,7 +540,7 @@ void DiskII::fillDiskBuffer()
g_filemanager->seekBlock(disk[diskWeAreUsing], trackWeAreReading * 16 + i, true);
if (!g_filemanager->readBlock(disk[diskWeAreUsing], rawTrackBuffer, true)) {
// FIXME: error handling?
- trackToRead = -1;
+ g_display->debugMsg("DII: FM nib read failure");
return;
}
trackBuffer->addBytes(rawTrackBuffer, 416);
@@ -603,22 +551,16 @@ void DiskII::fillDiskBuffer()
g_filemanager->seekBlock(disk[diskWeAreUsing], trackWeAreReading * 16, false);
if (!g_filemanager->readTrack(disk[diskWeAreUsing], rawTrackBuffer, false)) {
// FIXME: error handling?
- trackToRead = -1;
+ g_display->debugMsg("DII: FM block read failure");
return;
}
nibblizeTrack(trackBuffer, rawTrackBuffer, diskType[diskWeAreUsing], curHalfTrack[selectedDisk]>>1);
}
+}
- // Make sure we're still intending to read the track we just read
- if (trackWeAreReading != trackToRead ||
- diskWeAreUsing != selectedDisk) {
- // Abort and let it start over next time
- return;
- }
-
- // Buffer is full, we're done - reset trackToRead and that will let the reads reach the CPU!
- trackToRead = -1;
+void DiskII::fillDiskBuffer()
+{
}
const char *DiskII::DiskName(int8_t num)
@@ -646,19 +588,20 @@ void DiskII::flushTrack(int8_t track, int8_t sel)
{
// safety check: if we're write-protected, then how did we get here?
if (writeProt) {
- g_display->debugMsg("Write Protected");
+ g_display->debugMsg("DII: Write Protected");
return;
}
if (!trackBuffer->hasData()) {
// Dunno what happened - we're writing but haven't initialized the sector buffer?
+ g_display->debugMsg("DII: uninit'd write");
return;
}
if (diskType[sel] == nibDisk) {
// Write the whole track out exactly as we've got it. Hopefully
// someone has re-calcuated appropriate checksums on it...
- g_display->debugMsg("Not writing Nib image");
+ g_display->debugMsg("DII: Not writing Nib image");
return;
}
@@ -670,6 +613,7 @@ void DiskII::flushTrack(int8_t track, int8_t sel)
return;
case errorMissingSectors:
+ // The nibblized track doesn't contain all possible sectors - so it's broken. Drop the write.
g_display->debugMsg("DII: missing sectors");
trackBuffer->clear();
break;
diff --git a/apple/diskii.h b/apple/diskii.h
index 3c991e7..1570b30 100644
--- a/apple/diskii.h
+++ b/apple/diskii.h
@@ -10,7 +10,7 @@
#include "filemanager.h"
#include "applemmu.h"
-#include "Slot.h"
+#include "slot.h"
#include "LRingBuffer.h"
#include "nibutil.h"
@@ -45,6 +45,7 @@ class DiskII : public Slot {
uint8_t readOrWriteByte();
void checkFlush(int8_t track);
+ void readDiskTrack(int8_t diskWeAreUsing, int8_t trackWeAreReading);
#ifndef TEENSYDUINO
void convertDskToNib(const char *outFN);
@@ -53,7 +54,6 @@ class DiskII : public Slot {
private:
volatile int8_t curHalfTrack[2];
volatile int8_t curPhase[2];
- volatile bool trackDirty; // does this track need flushing to disk?
uint8_t readWriteLatch;
LRingBuffer *trackBuffer; // nibblized data
uint8_t rawTrackBuffer[4096]; // not nibblized data
@@ -65,11 +65,9 @@ class DiskII : public Slot {
int8_t disk[2];
volatile uint8_t indicatorIsOn[2];
uint8_t diskType[2];
-
- volatile int8_t trackToRead; // -1 when we're idle; not -1 when we need to read a track.
- volatile int8_t selectedDisk;
volatile int8_t trackToFlush; // -1 when there's none
- volatile int8_t diskToFlush; // which selected disk are we writing to?
+
+ volatile int8_t selectedDisk;
};
#endif
diff --git a/apple/hd32.h b/apple/hd32.h
index 39ee4fa..e01b76f 100644
--- a/apple/hd32.h
+++ b/apple/hd32.h
@@ -10,7 +10,7 @@
#include "filemanager.h"
#include "applemmu.h"
-#include "Slot.h"
+#include "slot.h"
#include "LRingBuffer.h"
diff --git a/apple/nibutil.cpp b/apple/nibutil.cpp
index 9bc4283..865ca68 100644
--- a/apple/nibutil.cpp
+++ b/apple/nibutil.cpp
@@ -16,7 +16,7 @@
// time. With this off, we present a minimum number of gaps (that
// hopefully aren't too short for the ROM to be able to write
// correctly)
-//#define LONGGAPS
+// #define LONGGAPS
#define DISK_VOLUME 254
diff --git a/apple/parallelcard.h b/apple/parallelcard.h
index 9ff20eb..9821b52 100644
--- a/apple/parallelcard.h
+++ b/apple/parallelcard.h
@@ -9,7 +9,7 @@
#endif
#include "applemmu.h"
-#include "Slot.h"
+#include "slot.h"
class Fx80;
diff --git a/bios.cpp b/bios.cpp
new file mode 100644
index 0000000..ddedced
--- /dev/null
+++ b/bios.cpp
@@ -0,0 +1,722 @@
+#include "globals.h"
+#include "bios.h"
+
+#include "applevm.h"
+#include "physicalkeyboard.h"
+#include "physicaldisplay.h"
+#include "cpu.h"
+
+
+enum {
+ ACT_EXIT = 1,
+ ACT_RESET = 2,
+ ACT_COLDBOOT = 3,
+ ACT_MONITOR = 4,
+ ACT_DISPLAYTYPE = 5,
+ ACT_DEBUG = 6,
+ ACT_DISK1 = 7,
+ ACT_DISK2 = 8,
+ ACT_HD1 = 9,
+ ACT_HD2 = 10,
+ ACT_VOLPLUS = 11,
+ ACT_VOLMINUS = 12,
+ ACT_SUSPEND = 13,
+ ACT_RESTORE = 14,
+ ACT_PRIMODE = 15,
+ ACT_SPEED = 16,
+};
+
+#define NUM_TITLES 3
+const char *menuTitles[NUM_TITLES] = { "VM", "Hardware", "Disks" };
+const uint8_t titleWidths[NUM_TITLES] = { 28, 80, 45 };
+
+const uint8_t vmActions[] = { ACT_EXIT, ACT_RESET, ACT_COLDBOOT, ACT_MONITOR,
+ ACT_DEBUG, ACT_SUSPEND, ACT_RESTORE };
+const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_SPEED,
+ ACT_PRIMODE, ACT_VOLPLUS, ACT_VOLMINUS };
+const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2,
+ ACT_HD1, ACT_HD2 };
+
+#define CPUSPEED_HALF 0
+#define CPUSPEED_FULL 1
+#define CPUSPEED_DOUBLE 2
+#define CPUSPEED_QUAD 3
+
+// FIXME: abstract the pin # rather than repeating it here
+#define RESETPIN 39
+
+const char *staticPathConcat(const char *rootPath, const char *filePath)
+{
+ static char buf[MAXPATH];
+ strncpy(buf, rootPath, sizeof(buf)-1);
+ strncat(buf, filePath, sizeof(buf)-strlen(buf)-1);
+
+ return buf;
+}
+
+BIOS::BIOS()
+{
+ selectedMenu = 0;
+ selectedMenuItem = 0;
+
+ selectedFile = -1;
+ for (int8_t i=0; idrawPixel(xpos+x, 0, 0xFFFF);
+ g_display->drawPixel(xpos+x, 16, 0xFFFF);
+ }
+ for (int y=0; y<=16; y++) {
+ g_display->drawPixel(xpos, y, 0xFFFF);
+ g_display->drawPixel(xpos + titleWidths[i] + 2*XPADDING, y, 0xFFFF);
+ }
+
+ xpos += XPADDING;
+
+ g_display->drawString(selectedMenu == i ? M_SELECTDISABLED : M_DISABLED,
+ xpos, 2, menuTitles[i]);
+ xpos += titleWidths[i] + XPADDING;
+ }
+}
+
+
+bool BIOS::runUntilDone()
+{
+ g_filemanager->getRootPath(rootPath, sizeof(rootPath));
+
+ // FIXME: abstract these constant speeds
+ currentCPUSpeedIndex = CPUSPEED_FULL;
+ if (g_speed == 1023000/2)
+ currentCPUSpeedIndex = CPUSPEED_HALF;
+ if (g_speed == 1023000*2)
+ currentCPUSpeedIndex = CPUSPEED_DOUBLE;
+ if (g_speed == 1023000*4)
+ currentCPUSpeedIndex = CPUSPEED_QUAD;
+
+ int8_t prevAction = ACT_EXIT;
+ bool volumeDidChange = 0;
+ while (1) {
+ switch (prevAction = GetAction(prevAction)) {
+ case ACT_EXIT:
+ goto done;
+ case ACT_COLDBOOT:
+ ColdReboot();
+ goto done;
+ case ACT_RESET:
+ WarmReset();
+ goto done;
+ case ACT_MONITOR:
+ ((AppleVM *)g_vm)->Monitor();
+ goto done;
+ case ACT_DISPLAYTYPE:
+ g_displayType++;
+ g_displayType %= 4; // FIXME: abstract max #
+ ((AppleDisplay*)g_display)->displayTypeChanged();
+ break;
+ case ACT_SPEED:
+ currentCPUSpeedIndex++;
+ currentCPUSpeedIndex %= 4;
+ switch (currentCPUSpeedIndex) {
+ case CPUSPEED_HALF:
+ g_speed = 1023000/2;
+ break;
+ case CPUSPEED_DOUBLE:
+ g_speed = 1023000*2;
+ break;
+ case CPUSPEED_QUAD:
+ g_speed = 1023000*4;
+ break;
+ default:
+ g_speed = 1023000;
+ break;
+ }
+ break;
+ case ACT_DEBUG:
+ g_debugMode++;
+ g_debugMode %= 8; // FIXME: abstract max #
+ break;
+ case ACT_PRIMODE:
+ g_prioritizeDisplay = !g_prioritizeDisplay;
+ break;
+ case ACT_DISK1:
+ if (((AppleVM *)g_vm)->DiskName(0)[0] != '\0') {
+ ((AppleVM *)g_vm)->ejectDisk(0);
+ } else {
+ if (SelectDiskImage()) {
+ ((AppleVM *)g_vm)->insertDisk(0, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
+ goto done;
+ }
+ }
+ break;
+ case ACT_DISK2:
+ if (((AppleVM *)g_vm)->DiskName(1)[0] != '\0') {
+ ((AppleVM *)g_vm)->ejectDisk(1);
+ } else {
+ if (SelectDiskImage()) {
+ ((AppleVM *)g_vm)->insertDisk(1, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
+ goto done;
+ }
+ }
+ break;
+ case ACT_HD1:
+ if (((AppleVM *)g_vm)->HDName(0)[0] != '\0') {
+ ((AppleVM *)g_vm)->ejectHD(0);
+ } else {
+ if (SelectDiskImage()) {
+ ((AppleVM *)g_vm)->insertHD(0, staticPathConcat(rootPath, fileDirectory[selectedFile]));
+ goto done;
+ }
+ }
+ break;
+ case ACT_HD2:
+ if (((AppleVM *)g_vm)->HDName(1)[0] != '\0') {
+ ((AppleVM *)g_vm)->ejectHD(1);
+ } else {
+ if (SelectDiskImage()) {
+ ((AppleVM *)g_vm)->insertHD(1, staticPathConcat(rootPath, fileDirectory[selectedFile]));
+ goto done;
+ }
+ }
+ break;
+ case ACT_VOLPLUS:
+ g_volume ++;
+ if (g_volume > 15) {
+ g_volume = 15;
+ }
+ volumeDidChange = true;
+ break;
+ case ACT_VOLMINUS:
+ g_volume--;
+ if (g_volume < 0) {
+ g_volume = 0;
+ }
+ volumeDidChange = true;
+ break;
+
+ case ACT_SUSPEND:
+ // CPU is already suspended, so this is safe...
+ ((AppleVM *)g_vm)->Suspend("suspend.vm");
+ break;
+ case ACT_RESTORE:
+ // CPU is already suspended, so this is safe...
+ ((AppleVM *)g_vm)->Resume("suspend.vm");
+ break;
+ }
+ }
+
+ done:
+ // Undo whatever damage we've done to the screen
+ g_display->redraw();
+ AiieRect r = { 0, 0, 191, 279 };
+ g_display->blit(r);
+
+ // return true if any persistent setting changed that we want to store in eeprom
+ return volumeDidChange;
+}
+
+void BIOS::WarmReset()
+{
+ g_cpu->Reset();
+}
+
+void BIOS::ColdReboot()
+{
+ g_vm->Reset();
+ g_cpu->Reset();
+}
+
+uint8_t BIOS::GetAction(int8_t selection)
+{
+ while (1) {
+ DrawMainMenu();
+ while (!g_keyboard->kbhit()
+#ifdef TEENSYDUINO
+ &&
+ (digitalRead(RESETPIN) == HIGH)
+#endif
+ ) {
+ ;
+ // Wait for either a keypress or the reset button to be pressed
+ }
+
+#ifdef TEENSYDUINO
+ if (digitalRead(RESETPIN) == LOW) {
+ // wait until it's no longer pressed
+ while (digitalRead(RESETPIN) == HIGH)
+ ;
+ delay(100); // wait long enough for it to debounce
+ // then return an exit code
+ return ACT_EXIT;
+ }
+#else
+ // FIXME: look for F10 or ESC & return ACT_EXIT?
+#endif
+
+ // selectedMenuItem and selectedMenu can go out of bounds here, and that's okay;
+ // the current menu (and the menu bar) will re-pin it appropriately...
+ switch (g_keyboard->read()) {
+ case PK_DARR:
+ selectedMenuItem++;
+ break;
+ case PK_UARR:
+ selectedMenuItem--;
+ break;
+ case PK_RARR:
+ selectedMenu++;
+ break;
+ case PK_LARR:
+ selectedMenu--;
+ break;
+ case PK_RET:
+ {
+ int8_t activeAction = getCurrentMenuAction();
+ if (activeAction > 0) {
+ return activeAction;
+ }
+ }
+ break;
+ }
+ }
+}
+
+int8_t BIOS::getCurrentMenuAction()
+{
+ int8_t ret = -1;
+
+ switch (selectedMenu) {
+ case 0: // VM
+ if (isActionActive(vmActions[selectedMenuItem]))
+ return vmActions[selectedMenuItem];
+ break;
+ case 1: // Hardware
+ if (isActionActive(hardwareActions[selectedMenuItem]))
+ return hardwareActions[selectedMenuItem];
+ break;
+ case 2: // Disks
+ if (isActionActive(diskActions[selectedMenuItem]))
+ return diskActions[selectedMenuItem];
+ break;
+ }
+
+ return ret;
+}
+
+bool BIOS::isActionActive(int8_t action)
+{
+ // don't return true for disk events that aren't valid
+ switch (action) {
+ case ACT_EXIT:
+ case ACT_RESET:
+ case ACT_COLDBOOT:
+ case ACT_MONITOR:
+ case ACT_DISPLAYTYPE:
+ case ACT_SPEED:
+ case ACT_DEBUG:
+ case ACT_PRIMODE:
+ case ACT_DISK1:
+ case ACT_DISK2:
+ case ACT_HD1:
+ case ACT_HD2:
+ case ACT_SUSPEND:
+ case ACT_RESTORE:
+ return true;
+
+ case ACT_VOLPLUS:
+ return (g_volume < 15);
+ case ACT_VOLMINUS:
+ return (g_volume > 0);
+ }
+
+ /* NOTREACHED */
+ return false;
+}
+
+void BIOS::DrawVMMenu()
+{
+ if (selectedMenuItem < 0)
+ selectedMenuItem = sizeof(vmActions)-1;
+
+ selectedMenuItem %= sizeof(vmActions);
+
+ char buf[40];
+ for (int i=0; idrawString(selectedMenuItem == i ? M_SELECTED : M_NORMAL, 10, 20 + 14 * i, buf);
+ } else {
+ g_display->drawString(selectedMenuItem == i ? M_SELECTDISABLED : M_DISABLED, 10, 20 + 14 * i, buf);
+ }
+ }
+}
+
+void BIOS::DrawHardwareMenu()
+{
+ if (selectedMenuItem < 0)
+ selectedMenuItem = sizeof(hardwareActions)-1;
+
+ selectedMenuItem %= sizeof(hardwareActions);
+
+ char buf[40];
+ for (int i=0; idrawString(selectedMenuItem == i ? M_SELECTED : M_NORMAL, 10, 20 + 14 * i, buf);
+ } else {
+ g_display->drawString(selectedMenuItem == i ? M_SELECTDISABLED : M_DISABLED, 10, 20 + 14 * i, buf);
+ }
+ }
+
+ // draw the volume bar
+ uint16_t volCutoff = 300.0 * (float)((float) g_volume / 15.0);
+ for (uint8_t y=234; y<=235; y++) {
+ for (uint16_t x = 0; x< 300; x++) {
+ g_display->drawPixel( x, y, x <= volCutoff ? 0xFFFF : 0x0010 );
+ }
+ }
+}
+
+void BIOS::DrawDisksMenu()
+{
+ if (selectedMenuItem < 0)
+ selectedMenuItem = sizeof(diskActions)-1;
+
+ selectedMenuItem %= sizeof(diskActions);
+
+ char buf[80];
+ for (int i=0; iDiskName(diskActions[i]==ACT_DISK2 ? 1 : 0);
+ // Get the name of the file; strip off the directory
+ const char *endPtr = &insertedDiskName[strlen(insertedDiskName)-1];
+ while (endPtr != insertedDiskName &&
+ *endPtr != '/') {
+ endPtr--;
+ }
+ if (*endPtr == '/') {
+ endPtr++;
+ }
+
+ if (insertedDiskName[0]) {
+ snprintf(buf, sizeof(buf), "Eject Disk %d [%s]", diskActions[i]==ACT_DISK2 ? 2 : 1, endPtr);
+ } else {
+ sprintf(buf, "Insert Disk %d", diskActions[i]==ACT_DISK2 ? 2 : 1);
+ }
+ }
+ break;
+ case ACT_HD1:
+ case ACT_HD2:
+ {
+ const char *insertedDiskName = ((AppleVM *)g_vm)->HDName(diskActions[i]==ACT_HD2 ? 1 : 0);
+ // Get the name of the file; strip off the directory
+ const char *endPtr = &insertedDiskName[strlen(insertedDiskName)-1];
+ while (endPtr != insertedDiskName &&
+ *endPtr != '/') {
+ endPtr--;
+ }
+ if (*endPtr == '/') {
+ endPtr++;
+ }
+
+ if (insertedDiskName[0]) {
+ snprintf(buf, sizeof(buf), "Remove HD %d [%s]", diskActions[i]==ACT_HD2 ? 2 : 1, endPtr);
+ } else {
+ sprintf(buf, "Connect HD %d", diskActions[i]==ACT_HD2 ? 2 : 1);
+ }
+ }
+ break;
+ }
+
+ if (isActionActive(diskActions[i])) {
+ g_display->drawString(selectedMenuItem == i ? M_SELECTED : M_NORMAL, 10, 20 + 14 * i, buf);
+ } else {
+ g_display->drawString(selectedMenuItem == i ? M_SELECTDISABLED : M_DISABLED, 10, 20 + 14 * i, buf);
+ }
+ }
+}
+
+
+void BIOS::DrawCurrentMenu()
+{
+ switch (selectedMenu) {
+ case 0: // VM
+ DrawVMMenu();
+ break;
+ case 1: // Hardware
+ DrawHardwareMenu();
+ break;
+ case 2: // Disks
+ DrawDisksMenu();
+ break;
+ }
+}
+
+void BIOS::DrawMainMenu()
+{
+ g_display->clrScr();
+ // g_display->drawString(M_NORMAL, 0, 0, "BIOS Configuration");
+
+ DrawMenuBar();
+
+ DrawCurrentMenu();
+
+ g_display->flush();
+}
+
+
+// return true if the user selects an image
+// sets selectedFile (index; -1 = "nope") and fileDirectory[][] (names of up to BIOS_MAXFILES files)
+bool BIOS::SelectDiskImage()
+{
+ int8_t sel = 0;
+ int8_t page = 0;
+
+ while (1) {
+ DrawDiskNames(page, sel);
+
+ while (!g_keyboard->kbhit())
+ ;
+ switch (g_keyboard->read()) {
+ case PK_DARR:
+ sel++;
+ sel %= BIOS_MAXFILES + 2;
+ break;
+ case PK_UARR:
+ sel--;
+ if (sel < 0)
+ sel = BIOS_MAXFILES + 1;
+ break;
+ case PK_ESC:
+ return false;
+ case PK_RET:
+ if (sel == 0) {
+ page--;
+ if (page < 0) page = 0;
+ // else sel = BIOS_MAXFILES + 1;
+ }
+ else if (sel == BIOS_MAXFILES+1) {
+ page++;
+ //sel = 0;
+ } else {
+ if (strcmp(fileDirectory[sel-1], "../") == 0) {
+ // Go up a directory (strip a directory name from rootPath)
+ stripDirectory();
+ page = 0;
+ //sel = 0;
+ continue;
+ } else if (fileDirectory[sel-1][strlen(fileDirectory[sel-1])-1] == '/') {
+ // Descend in to the directory. FIXME: file path length?
+ strcat(rootPath, fileDirectory[sel-1]);
+ sel = 0;
+ page = 0;
+ continue;
+ } else {
+ selectedFile = sel - 1;
+ g_display->flush();
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ g_display->flush();
+}
+
+void BIOS::stripDirectory()
+{
+ rootPath[strlen(rootPath)-1] = '\0'; // remove the last character
+
+ while (rootPath[0] && rootPath[strlen(rootPath)-1] != '/') {
+ rootPath[strlen(rootPath)-1] = '\0'; // remove the last character again
+ }
+
+ // We're either at the previous directory, or we've nulled out the whole thing.
+
+ if (rootPath[0] == '\0') {
+ // Never go beyond this
+ strcpy(rootPath, "/");
+ }
+}
+
+void BIOS::DrawDiskNames(uint8_t page, int8_t selection)
+{
+ uint8_t fileCount = GatherFilenames(page);
+ g_display->clrScr();
+ g_display->drawString(M_NORMAL, 0, 12, "BIOS Configuration - pick disk");
+
+ if (page == 0) {
+ g_display->drawString(selection == 0 ? M_SELECTDISABLED : M_DISABLED, 10, 50, "");
+ } else {
+ g_display->drawString(selection == 0 ? M_SELECTED : M_NORMAL, 10, 50, "");
+ }
+
+ uint8_t i;
+ for (i=0; idrawString((i == selection-1) ? M_SELECTED : M_NORMAL, 10, 50 + 14 * (i+1), fileDirectory[i]);
+ } else {
+ g_display->drawString((i == selection-1) ? M_SELECTDISABLED : M_DISABLED, 10, 50+14*(i+1), "-");
+ }
+
+ }
+
+ // FIXME: this doesn't accurately say whether or not there *are* more.
+ if (fileCount == BIOS_MAXFILES || fileCount == 0) {
+ g_display->drawString((i+1 == selection) ? M_SELECTDISABLED : M_DISABLED, 10, 50 + 14 * (i+1), "");
+ } else {
+ g_display->drawString(i+1 == selection ? M_SELECTED : M_NORMAL, 10, 50 + 14 * (i+1), "");
+ }
+
+ g_display->flush();
+}
+
+
+uint8_t BIOS::GatherFilenames(uint8_t pageOffset)
+{
+ uint8_t startNum = 10 * pageOffset;
+ uint8_t count = 0; // number we're including in our listing
+
+ while (1) {
+ char fn[BIOS_MAXPATH];
+ int8_t idx = g_filemanager->readDir(rootPath, "dsk,.po,nib,img", fn, startNum + count, BIOS_MAXPATH);
+
+ if (idx == -1) {
+ return count;
+ }
+
+ idx++;
+
+ strncpy(fileDirectory[count], fn, BIOS_MAXPATH);
+ count++;
+
+ if (count >= BIOS_MAXFILES) {
+ return count;
+ }
+ }
+}
+
diff --git a/bios.h b/bios.h
new file mode 100644
index 0000000..c327f2b
--- /dev/null
+++ b/bios.h
@@ -0,0 +1,54 @@
+#ifndef __BIOS_H
+#define __BIOS_H
+
+#ifdef TEENSYDUINO
+#include
+#else
+#include
+#endif
+
+#define BIOS_MAXFILES 10 // number of files in a page of listing
+#define BIOS_MAXPATH 40 // maximum length of a single filename that we'll support
+
+class BIOS {
+ public:
+ BIOS();
+ ~BIOS();
+
+ // return true if a persistent change needs to be stored in EEPROM
+ bool runUntilDone();
+
+ private:
+ void DrawMenuBar();
+ void DrawCurrentMenu();
+ void DrawVMMenu();
+ void DrawHardwareMenu();
+ void DrawDisksMenu();
+
+ uint8_t GetAction(int8_t prevAction);
+ bool isActionActive(int8_t action);
+ void DrawMainMenu();
+
+ int8_t getCurrentMenuAction();
+
+ void WarmReset();
+ void ColdReboot();
+
+ bool SelectDiskImage();
+ void DrawDiskNames(uint8_t page, int8_t selection);
+ uint8_t GatherFilenames(uint8_t pageOffset);
+
+ void stripDirectory();
+
+ private:
+ int8_t selectedFile;
+ char fileDirectory[BIOS_MAXFILES][BIOS_MAXPATH+1];
+
+ char rootPath[255-BIOS_MAXPATH];
+
+ int8_t selectedMenu;
+ int8_t selectedMenuItem;
+ uint8_t currentCPUSpeedIndex;
+};
+
+#endif
diff --git a/filemanager.h b/filemanager.h
index 82f42f1..162f32b 100644
--- a/filemanager.h
+++ b/filemanager.h
@@ -110,6 +110,8 @@ class FileManager {
virtual uint8_t readByte(int8_t fd) = 0;
virtual bool writeByte(int8_t fd, uint8_t v) = 0;
+ virtual void getRootPath(char *toWhere, int8_t maxLen) = 0;
+
protected:
unsigned long fileSeekPositions[MAXFILES];
char cachedNames[MAXFILES][MAXPATH];
diff --git a/globals.cpp b/globals.cpp
index b9dc0e2..13db554 100644
--- a/globals.cpp
+++ b/globals.cpp
@@ -12,3 +12,8 @@ VMui *g_ui;
int16_t g_volume = 15;
uint8_t g_displayType = 3; // FIXME m_perfectcolor
VMRam g_ram;
+volatile bool g_inInterrupt = false;
+volatile uint8_t g_debugMode = D_NONE;
+bool g_prioritizeDisplay = false;
+volatile bool g_biosInterrupt = false;
+uint32_t g_speed = 1023000; // Hz
diff --git a/globals.h b/globals.h
index 3c4ad46..bce8e5e 100644
--- a/globals.h
+++ b/globals.h
@@ -11,6 +11,26 @@
#include "vmui.h"
#include "vmram.h"
+// display modes
+enum {
+ M_NORMAL = 0,
+ M_SELECTED = 1,
+ M_DISABLED = 2,
+ M_SELECTDISABLED = 3
+};
+
+// debug modes
+enum {
+ D_NONE = 0,
+ D_SHOWFPS = 1,
+ D_SHOWMEMFREE = 2,
+ D_SHOWPADDLES = 3,
+ D_SHOWPC = 4,
+ D_SHOWCYCLES = 5,
+ D_SHOWBATTERY = 6,
+ D_SHOWTIME = 7
+};
+
extern FileManager *g_filemanager;
extern Cpu *g_cpu;
extern VM *g_vm;
@@ -23,3 +43,8 @@ extern VMui *g_ui;
extern int16_t g_volume;
extern uint8_t g_displayType;
extern VMRam g_ram;
+extern volatile bool g_inInterrupt;
+extern volatile uint8_t g_debugMode;
+extern bool g_prioritizeDisplay;
+extern volatile bool g_biosInterrupt;
+extern uint32_t g_speed;
diff --git a/linuxfb/aiie.cpp b/linuxfb/aiie.cpp
new file mode 100644
index 0000000..8e74495
--- /dev/null
+++ b/linuxfb/aiie.cpp
@@ -0,0 +1,540 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "applevm.h"
+#include "fb-display.h"
+#include "linux-keyboard.h"
+#include "linux-speaker.h"
+#include "fb-paddles.h"
+#include "nix-filemanager.h"
+#include "linux-printer.h"
+#include "appleui.h"
+#include "bios.h"
+
+#include "globals.h"
+
+#include "timeutil.h"
+
+//#define SHOWFPS
+//#define SHOWPC
+//#define DEBUGCPU
+//#define SHOWMEMPAGE
+
+BIOS bios;
+
+static struct timespec nextInstructionTime, startTime;
+
+#define NB_ENABLE 1
+#define NB_DISABLE 0
+
+int send_rst = 0;
+
+pthread_t cpuThreadID;
+
+char disk1name[256] = "\0";
+char disk2name[256] = "\0";
+
+volatile bool wantSuspend = false;
+volatile bool wantResume = false;
+
+void doDebugging();
+
+void sigint_handler(int n)
+{
+ send_rst = 1;
+}
+
+void nonblock(int state)
+{
+ struct termios ttystate;
+
+ //get the terminal state
+ tcgetattr(STDIN_FILENO, &ttystate);
+
+ if (state==NB_ENABLE)
+ {
+ //turn off canonical mode
+ ttystate.c_lflag &= ~ICANON;
+ //minimum of number input read.
+ ttystate.c_cc[VMIN] = 1;
+ }
+ else if (state==NB_DISABLE)
+ {
+ //turn on canonical mode
+ ttystate.c_lflag |= ICANON;
+ }
+ //set the terminal attributes.
+ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
+
+}
+
+uint8_t read(void *arg, uint16_t address)
+{
+ // no action; this is a dummy function until we've finished initializing...
+ return 0x00;
+}
+
+void write(void *arg, uint16_t address, uint8_t v)
+{
+ // no action; this is a dummy function until we've finished initializing...
+}
+
+static void *cpu_thread(void *dummyptr) {
+ struct timespec currentTime;
+ struct timespec nextCycleTime;
+ uint32_t nextSpeakerCycle = 0;
+
+#if 0
+ int policy;
+ struct sched_param param;
+ pthread_getschedparam(pthread_self(), &policy, ¶m);
+ param.sched_priority = sched_get_priority_max(policy);
+ pthread_setschedparam(pthread_self(), policy, ¶m);
+#endif
+
+ _init_darwin_shim();
+ do_gettime(&startTime);
+ printf("Start time: %lu,%lu\n", startTime.tv_sec, startTime.tv_nsec);
+ do_gettime(&nextInstructionTime);
+
+ printf("free-running\n");
+ while (1) {
+ if (g_biosInterrupt) {
+ printf("BIOS blocking\n");
+ while (g_biosInterrupt) {
+ usleep(100);
+ }
+ printf("BIOS block complete\n");
+ }
+
+ 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;
+ }
+
+ do_gettime(¤tTime);
+
+ /* The speaker is our priority. The CPU runs in batches anyway,
+ sometimes a little behind and sometimes a little ahead; but the
+ speaker has to be right on time. */
+
+ struct timespec diff;
+
+#if 0
+ // Wait until nextSpeakerCycle
+ timespec_add_cycles(&startTime, nextSpeakerCycle, &nextCycleTime);
+
+ diff = tsSubtract(nextCycleTime, currentTime);
+ if (diff.tv_sec >= 0 || diff.tv_nsec >= 0) {
+ nanosleep(&diff, NULL);
+ }
+
+ // Speaker runs 48 cycles behind the CPU (an arbitrary number)
+ if (nextSpeakerCycle >= 48) {
+ timespec_add_cycles(&startTime, nextSpeakerCycle-48, &nextCycleTime);
+ uint64_t microseconds = nextCycleTime.tv_sec * 1000000 +
+ (double)nextCycleTime.tv_nsec / 1000.0;
+ g_speaker->maintainSpeaker(nextSpeakerCycle-48, microseconds);
+ }
+
+ // Bump speaker cycle for next go-round
+ nextSpeakerCycle++;
+#endif
+
+ /* Next up is the CPU. */
+
+ // tsSubtract doesn't return negatives; it bounds at 0.
+ diff = tsSubtract(nextInstructionTime, currentTime);
+
+ uint8_t executed = 0;
+ if (diff.tv_sec == 0 && diff.tv_nsec == 0) {
+#ifdef DEBUGCPU
+ executed = g_cpu->Run(1);
+#else
+ executed = g_cpu->Run(24);
+#endif
+ // calculate the real time that we should be at now, and schedule
+ // that as our next instruction time
+ timespec_add_cycles(&startTime, g_cpu->cycles, &nextInstructionTime);
+
+ // The paddles need to be triggered in real-time on the CPU
+ // clock. That happens from the VM's CPU maintenance poller.
+ ((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
+
+#ifdef DEBUGCPU
+ {
+ uint8_t p = g_cpu->flags;
+ printf("OP: $%02x A: %02x X: %02x Y: %02x PC: $%04x SP: %02x Flags: %c%cx%c%c%c%c%c\n",
+ g_vm->getMMU()->read(g_cpu->pc),
+ g_cpu->a, g_cpu->x, g_cpu->y, g_cpu->pc, g_cpu->sp,
+ p & (1<<7) ? 'N':' ',
+ p & (1<<6) ? 'V':' ',
+ p & (1<<4) ? 'B':' ',
+ p & (1<<3) ? 'D':' ',
+ p & (1<<2) ? 'I':' ',
+ p & (1<<1) ? 'Z':' ',
+ p & (1<<0) ? 'C':' '
+ );
+ }
+#endif
+
+ if (send_rst) {
+#if 0
+ printf("Scheduling suspend request...\n");
+ wantSuspend = true;
+#endif
+#if 0
+ printf("Scheduling resume resume request...\n");
+ wantResume = true;
+#endif
+
+#if 0
+ printf("Sending reset\n");
+ g_cpu->Reset();
+
+ // testing startup keyboard presses - perform Apple //e self-test
+ //g_vm->getKeyboard()->keyDepressed(RA);
+ //g_vm->Reset();
+ //g_cpu->Reset();
+ //((AppleVM *)g_vm)->insertDisk(0, "disks/DIAGS.DSK");
+#endif
+
+#if 0
+ // Swap disks
+ if (disk1name[0] && disk2name[0]) {
+ printf("Swapping disks\n");
+
+ printf("Inserting disk %s in drive 1\n", disk2name);
+ ((AppleVM *)g_vm)->insertDisk(0, disk2name);
+ printf("Inserting disk %s in drive 2\n", disk1name);
+ ((AppleVM *)g_vm)->insertDisk(1, disk1name);
+ }
+#endif
+
+#if 0
+ MMU *mmu = g_vm->getMMU();
+
+ printf("PC: 0x%X\n", g_cpu->pc);
+ for (int i=g_cpu->pc; ipc + 0x100; i++) {
+ printf("0x%X ", mmu->read(i));
+ }
+ printf("\n");
+
+ printf("Dropping to monitor\n");
+ // drop directly to monitor.
+ g_cpu->pc = 0xff69; // "call -151"
+ mmu->read(0xC054); // make sure we're in page 1
+ mmu->read(0xC056); // and that hires is off
+ mmu->read(0xC051); // and text mode is on
+ mmu->read(0xC08A); // and we have proper rom in place
+ mmu->read(0xc008); // main zero-page
+ mmu->read(0xc006); // rom from cards
+ mmu->write(0xc002 + mmu->read(0xc014)? 1 : 0, 0xff); // make sure aux ram read and write match
+ mmu->write(0x20, 0); // text window
+ mmu->write(0x21, 40);
+ mmu->write(0x22, 0);
+ mmu->write(0x23, 24);
+ mmu->write(0x33, '>');
+ mmu->write(0x48, 0); // from 0xfb2f: part of text init
+#endif
+
+ send_rst = 0;
+ }
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct vt_stat vts;
+ int newVT;
+ int initialVT;
+
+ if ((fd=open("/dev/console", O_WRONLY)) < 0) {
+ perror("opening /dev/console");
+ exit(1);
+ }
+
+ ioctl(fd, VT_GETSTATE, &vts);
+ initialVT = vts.v_active; // find what VT we were on originally
+ ioctl(fd, VT_OPENQRY, &newVT);
+ if (newVT == -1) {
+ printf("No VTs available");
+ exit(1);
+ }
+
+ // Switch to new VT
+ ioctl(fd, VT_ACTIVATE, newVT);
+ ioctl(0, VT_WAITACTIVE, newVT);
+
+ printf("Now on VT %d\n", newVT);
+
+ // If we want stdout/stderr to move with us to the new VT, do this sorta thing, but to the right TTY
+ // freopen("/dev/tty2", "w", stdout);
+ // freopen("/dev/tty2", "w", stderr);
+
+ // Turn off cursor
+ system("echo 0 > /sys/class/graphics/fbcon/cursor_blink");
+
+#if 0
+ // Timing consistency check
+
+ sleep(2); // kinda random, hopefully sloppy? - to make startTime != 0,0
+ printf("starting time consistency check\n");
+ do_gettime(&startTime);
+ for (int i=0; i<10000000; i++) {
+
+ // Calculate the time delta from startTime to cycle # i
+ timespec_add_cycles(&startTime, i, &nextInstructionTime);
+
+ // Recalculate the time difference between nextInstructionTime and startTime
+ struct timespec runtime = tsSubtract(nextInstructionTime, startTime);
+
+ // See if it's the same as cycles_since_time
+ double guesstimate = cycles_since_time(&runtime);
+ printf("cycle %d guesstimate %f\n", i, guesstimate);
+ if (guesstimate != i) {
+ printf("FAILED: cycle %d has guesstimate %f\n", i, guesstimate);
+ exit(1);
+ }
+ }
+
+ printf("All ok\n");
+
+ exit(1);
+#endif
+
+ g_speaker = new LinuxSpeaker();
+ g_printer = new LinuxPrinter();
+
+ // create the filemanager - the interface to the host file system.
+ g_filemanager = new NixFileManager();
+
+ g_display = new FBDisplay();
+ // g_displayType = m_blackAndWhite;
+
+ g_ui = new AppleUI();
+
+ // paddles have to be created after g_display created the window
+ g_paddles = new FBPaddles();
+
+ // Next create the virtual CPU. This needs the VM's MMU in order to run, but we don't have that yet.
+ g_cpu = new Cpu();
+
+ // Create the virtual machine. This may read from g_filemanager to get ROMs if necessary.
+ // (The actual Apple VM we've built has them compiled in, though.) It will create its virutal
+ // hardware (MMU, video driver, floppy, paddles, whatever).
+ g_vm = new AppleVM();
+
+ g_keyboard = new LinuxKeyboard(g_vm->getKeyboard());
+
+ // Now that the VM exists and it has created an MMU, we tell the CPU how to access memory through the MMU.
+ g_cpu->SetMMU(g_vm->getMMU());
+
+ // Now that all the virtual hardware is glued together, reset the VM
+ g_vm->Reset();
+ g_cpu->rst();
+
+ g_display->redraw();
+
+ if (argc >= 2) {
+ printf("Inserting disk %s\n", argv[1]);
+ ((AppleVM *)g_vm)->insertDisk(0, argv[1]);
+ strcpy(disk1name, argv[1]);
+ }
+
+ if (argc == 3) {
+ printf("Inserting disk %s\n", argv[2]);
+ ((AppleVM *)g_vm)->insertDisk(1, argv[2]);
+ strcpy(disk2name, argv[2]);
+ }
+
+ // FIXME: fixed test disk...
+ // ((AppleVM *)g_vm)->insertHD(0, "hd32.img");
+
+
+ nonblock(NB_ENABLE);
+
+ signal(SIGINT, sigint_handler);
+
+ printf("creating CPU thread\n");
+ if (!pthread_create(&cpuThreadID, NULL, &cpu_thread, (void *)NULL)) {
+ printf("thread created\n");
+ // pthread_setschedparam(cpuThreadID, SCHED_RR, PTHREAD_MAX_PRIORITY);
+ }
+
+ while (1) {
+ if (g_biosInterrupt) {
+ printf("Invoking BIOS\n");
+ if (bios.runUntilDone()) {
+ // if it returned true, we have something to store
+ // persistently in EEPROM.
+ // writePrefs();
+ }
+ printf("BIOS done\n");
+
+ // if we turned off debugMode, make sure to clear the debugMsg
+ if (g_debugMode == D_NONE) {
+ g_display->debugMsg("");
+ }
+
+ g_biosInterrupt = false;
+
+ // clear the CPU next-step counters
+ g_cpu->cycles = 0;
+ do_gettime(&startTime);
+ do_gettime(&nextInstructionTime);
+
+ // Drain the speaker queue (FIXME: a little hacky)
+ g_speaker->maintainSpeaker(-1, -1);
+
+ /* FIXME
+ // Force the display to redraw
+ ((AppleDisplay*)(g_vm->vmdisplay))->modeChange();
+ */
+
+ // Poll the keyboard before we start, so we can do selftest on startup
+ g_keyboard->maintainKeyboard();
+ }
+
+
+ static uint32_t usleepcycles = 16384; // step-down for display drawing. Dynamically updated based on FPS calculations.
+
+ // fill disk buffer when needed
+ ((AppleVM*)g_vm)->disk6->fillDiskBuffer();
+
+ // Make this a little friendlier, and the expense of some framerate?
+ // usleep(10000);
+ if (g_vm->vmdisplay->needsRedraw()) {
+ AiieRect what = g_vm->vmdisplay->getDirtyRect();
+ // make sure to clear the flag before drawing; there's no lock
+ // on didRedraw, so the other thread might update it
+ g_vm->vmdisplay->didRedraw();
+ g_display->blit(what);
+ }
+
+ g_printer->update();
+ g_keyboard->maintainKeyboard();
+ g_ui->drawPercentageUIElement(UIePowerPercentage, 100);
+
+ doDebugging();
+
+ // calculate FPS & dynamically step up/down as necessary
+ static time_t startAt = time(NULL);
+ static uint32_t loopCount = 0;
+ loopCount++;
+ uint32_t lenSecs = time(NULL) - startAt;
+ if (lenSecs >= 5) {
+ float fps = loopCount / lenSecs;
+
+#ifdef SHOWFPS
+ char buf[25];
+ sprintf(buf, "%f FPS [delay %u]", fps, usleepcycles);
+ g_display->debugMsg(buf);
+#endif
+
+ if (fps > 60) {
+ usleepcycles *= 2;
+ } else if (fps < 40) {
+ usleepcycles /= 2;
+ }
+
+ // reset the counter & we'll adjust again in 5 seconds
+ loopCount = 0;
+ startAt = time(NULL);
+ }
+ if (usleepcycles >= 2) {
+ usleep(usleepcycles);
+ }
+
+#ifdef SHOWPC
+ {
+ char buf[25];
+ sprintf(buf, "%X", g_cpu->pc);
+ g_display->debugMsg(buf);
+ }
+#endif
+#ifdef SHOWMEMPAGE
+ {
+ char buf[40];
+ sprintf(buf, "AUX %c/%c BNK %d BSR %c/%c ZP %c 80 %c INT %c",
+ g_vm->auxRamRead?'R':'_',
+ g_vm->auxRamWrite?'W':'_',
+ g_vm->bank1,
+ g_vm->readbsr ? 'R':'_',
+ g_vm->writebsr ? 'W':'_',
+ g_vm->altzp ? 'Y':'_',
+ g_vm->_80store ? 'Y' : '_',
+ g_vm->intcxrom ? 'Y' : '_');
+ g_display->debugMsg(buf);
+ }
+
+#endif
+
+
+ }
+}
+
+void doDebugging()
+{
+ char buf[25];
+ static time_t startAt = time(NULL);
+ static uint32_t loopCount = 0;
+
+ switch (g_debugMode) {
+ case D_SHOWFPS:
+ {
+ // display some FPS data
+ loopCount++;
+ uint32_t lenSecs = time(NULL) - startAt;
+ if (lenSecs >= 5) {
+ sprintf(buf, "%u FPS", loopCount / lenSecs);
+ g_display->debugMsg(buf);
+ startAt = time(NULL);
+ loopCount = 0;
+ }
+ }
+ break;
+ case D_SHOWMEMFREE:
+ // sprintf(buf, "%lu %u", FreeRamEstimate(), heapSize());
+ // g_display->debugMsg(buf);
+ break;
+ case D_SHOWPADDLES:
+ sprintf(buf, "%u %u", g_paddles->paddle0(), g_paddles->paddle1());
+ g_display->debugMsg(buf);
+ break;
+ case D_SHOWPC:
+ sprintf(buf, "%X", g_cpu->pc);
+ g_display->debugMsg(buf);
+ break;
+ case D_SHOWCYCLES:
+ sprintf(buf, "%X", g_cpu->cycles);
+ g_display->debugMsg(buf);
+ break;
+ /*
+ case D_SHOWBATTERY:
+ // sprintf(buf, "BAT %d", analogRead(BATTERYPIN));
+ // g_display->debugMsg(buf);
+ break;
+ case D_SHOWTIME:
+ // sprintf(buf, "%.2d:%.2d:%.2d", hour(), minute(), second());
+ // g_display->debugMsg(buf);
+ break;*/
+ }
+}
diff --git a/linuxfb/fb-display.cpp b/linuxfb/fb-display.cpp
new file mode 100644
index 0000000..3fe2ccd
--- /dev/null
+++ b/linuxfb/fb-display.cpp
@@ -0,0 +1,212 @@
+#include // isgraph
+#include
+#include
+#include
+
+#include "fb-display.h"
+
+#include "bios-font.h"
+#include "images.h"
+
+#include "globals.h"
+#include "applevm.h"
+
+#include "apple/appleui.h"
+
+// RGB map of each of the lowres colors
+const uint16_t loresPixelColors[16] = { 0x0000, // 0 black
+ 0xC006, // 1 magenta
+ 0x0010, // 2 dark blue
+ 0xA1B5, // 3 purple
+ 0x0480, // 4 dark green
+ 0x6B4D, // 5 dark grey
+ 0x1B9F, // 6 med blue
+ 0x0DFD, // 7 light blue
+ 0x92A5, // 8 brown
+ 0xF8C5, // 9 orange
+ 0x9555, // 10 light gray
+ 0xFCF2, // 11 pink
+ 0x07E0, // 12 green
+ 0xFFE0, // 13 yellow
+ 0x87F0, // 14 aqua
+ 0xFFFF // 15 white
+};
+
+FBDisplay::FBDisplay()
+{
+ fb_fd = open("/dev/fb0",O_RDWR);
+ //Get variable screen information
+ ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
+
+ printf("bpp: %d\n", vinfo.bits_per_pixel);
+
+ //Get fixed screen information
+ ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
+
+ // This shouldn't be necessary, but I'm not seeing FBIOPUT working yet
+ system("fbset -xres 320 -yres 240 -depth 16");
+
+ // request what we want, rather than hoping we got it
+ // 16bpp 320x240 (for now)
+ vinfo.width = 320;
+ vinfo.height = 240;
+ vinfo.bits_per_pixel = 16;
+ int ret = ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo);
+ printf("Return from FBIOPUT_VSCREENINFO: %d\n", ret);
+
+ ret = ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
+ printf("Return from FBIOGET_VSCREENINFO: %d\n", ret);
+
+ printf("Screen is %d x %d @ %d\n", vinfo.width, vinfo.height, vinfo.bits_per_pixel);
+
+ screensize = vinfo.yres_virtual * finfo.line_length;
+ fbp = (uint8_t *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, (off_t)0);
+}
+
+FBDisplay::~FBDisplay()
+{
+}
+
+void FBDisplay::redraw()
+{
+ // primarily for the device, where it's in and out of the
+ // bios. Draws the background image.
+ printf("redraw background\n");
+ g_ui->drawStaticUIElement(UIeOverlay);
+ printf("static done\n");
+ if (g_vm && g_ui) {
+ // determine whether or not a disk is inserted & redraw each drive
+ g_ui->drawOnOffUIElement(UIeDisk1_state, ((AppleVM *)g_vm)->DiskName(0)[0] == '\0');
+ g_ui->drawOnOffUIElement(UIeDisk2_state, ((AppleVM *)g_vm)->DiskName(1)[0] == '\0');
+ }
+ printf("return\n");
+}
+
+void FBDisplay::drawImageOfSizeAt(const uint8_t *img,
+ uint16_t sizex, uint8_t sizey,
+ uint16_t wherex, uint8_t wherey)
+{
+ for (uint8_t y=0; yvideoBuffer; // FIXME: poking deep
+
+ for (uint8_t y=0; y<192; y++) {
+ for (uint16_t x=0; x<280; x++) {
+ uint16_t pixel = (y*DISPLAYRUN+x)/2;
+ uint8_t colorIdx;
+ if (x & 1) {
+ colorIdx = videoBuffer[pixel] & 0x0F;
+ } else {
+ colorIdx = videoBuffer[pixel] >> 4;
+ }
+ long location = (x+vinfo.xoffset+BASEX) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset+BASEY) * finfo.line_length;
+ *((uint16_t*)(fbp + location)) = loresPixelColors[colorIdx];
+ }
+ }
+
+ if (overlayMessage[0]) {
+ drawString(M_SELECTDISABLED, 1, 240 - 16 - 12, overlayMessage);
+ }
+
+}
+
+inline uint16_t _888to565(uint8_t r, uint8_t g, uint8_t b)
+{
+ return ( (r & 0xF8) << 8 |
+ ( (g & 0xFC) << 3) |
+ ( (b & 0xF8) >> 3 ) );
+}
+
+// external method
+void FBDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
+{
+ long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length;
+ *((uint16_t*)(fbp + location)) = color;
+}
+
+// external method
+void FBDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b)
+{
+ long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length;
+ *((uint16_t*)(fbp + location)) = _888to565(r,g,b);
+}
+
+void FBDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c)
+{
+ int8_t xsize = 8,
+ ysize = 0x0C,
+ offset = 0x20;
+ uint16_t temp;
+
+ c -= offset;// font starts with a space
+
+ uint16_t offPixel, onPixel;
+ switch (mode) {
+ case M_NORMAL:
+ onPixel = 0xFFFF;
+ offPixel = 0x0010;
+ break;
+ case M_SELECTED:
+ onPixel = 0x0000;
+ offPixel = 0xFFFF;
+ break;
+ case M_DISABLED:
+ default:
+ onPixel = 0x7BEF;
+ offPixel = 0x0000;
+ break;
+ case M_SELECTDISABLED:
+ onPixel = 0x7BEF;
+ offPixel = 0xFFE0;
+ break;
+ }
+
+ temp=(c*ysize);
+ for (int8_t y_off = 0; y_off <= ysize; y_off++) {
+ uint8_t ch = BiosFont[temp];
+ for (int8_t x_off = 0; x_off <= xsize; x_off++) {
+ if (ch & (1 << (7-x_off))) {
+ drawPixel(x + x_off, y + y_off, onPixel);
+ } else {
+ drawPixel(x + x_off, y + y_off, offPixel);
+ }
+ }
+ temp++;
+ }
+
+}
+
+void FBDisplay::drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str)
+{
+ int8_t xsize = 8; // width of a char in this font
+
+ for (int8_t i=0; i
+#include
+
+#include "physicaldisplay.h"
+
+class FBDisplay : public PhysicalDisplay {
+ public:
+ FBDisplay();
+ virtual ~FBDisplay();
+
+ virtual void blit(AiieRect r);
+ virtual void redraw();
+
+ virtual void drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey);
+
+ virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color);
+ virtual void drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b);
+
+ virtual void drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c);
+ virtual void drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str);
+ virtual void flush();
+ virtual void clrScr();
+
+ private:
+ int fb_fd;
+ struct fb_fix_screeninfo finfo;
+ struct fb_var_screeninfo vinfo;
+ long screensize;
+ uint8_t *fbp;
+};
+
+#endif
diff --git a/linuxfb/fb-paddles.cpp b/linuxfb/fb-paddles.cpp
new file mode 100644
index 0000000..db01e9c
--- /dev/null
+++ b/linuxfb/fb-paddles.cpp
@@ -0,0 +1,29 @@
+#include
+#include "fb-paddles.h"
+
+#include "globals.h"
+
+FBPaddles::FBPaddles()
+{
+ p0 = p1 = 127;
+}
+
+FBPaddles::~FBPaddles()
+{
+}
+
+void FBPaddles::startReading()
+{
+ g_vm->triggerPaddleInCycles(0, 12 * p0);
+ g_vm->triggerPaddleInCycles(1, 12 * p1);
+}
+
+uint8_t FBPaddles::paddle0()
+{
+ return p0;
+}
+
+uint8_t FBPaddles::paddle1()
+{
+ return p1;
+}
diff --git a/linuxfb/fb-paddles.h b/linuxfb/fb-paddles.h
new file mode 100644
index 0000000..1fe7459
--- /dev/null
+++ b/linuxfb/fb-paddles.h
@@ -0,0 +1,17 @@
+#include
+
+#include "physicalpaddles.h"
+
+class FBPaddles : public PhysicalPaddles {
+ public:
+ FBPaddles();
+ virtual ~FBPaddles();
+
+ virtual void startReading();
+ virtual uint8_t paddle0();
+ virtual uint8_t paddle1();
+
+ public:
+ uint8_t p0;
+ uint8_t p1;
+};
diff --git a/linuxfb/linux-keyboard.cpp b/linuxfb/linux-keyboard.cpp
new file mode 100644
index 0000000..f656c97
--- /dev/null
+++ b/linuxfb/linux-keyboard.cpp
@@ -0,0 +1,221 @@
+#include "linux-keyboard.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "sdl-paddles.h"
+#include "globals.h"
+
+#include "physicalkeyboard.h"
+
+LinuxKeyboard::LinuxKeyboard(VMKeyboard *k) : PhysicalKeyboard(k)
+{
+ fd = open("/dev/input/by-path/platform-20980000.usb-usb-0:1:1.0-event-kbd",
+ O_RDONLY | O_NONBLOCK);
+
+}
+
+LinuxKeyboard::~LinuxKeyboard()
+{
+ close(fd);
+}
+
+// FIXME: dummy value
+#define BIOSKEY 254
+
+static uint8_t keymap[] = {
+ 0, // keycode 0 doesn't exist
+ ESC,
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ '0',
+ '-',
+ '=',
+ DEL,
+ TAB,
+ 'q',
+ 'w',
+ 'e',
+ 'r',
+ 't',
+ 'y',
+ 'u',
+ 'i',
+ 'o',
+ 'p',
+ '[',
+ ']',
+ 13,
+ _CTRL,
+ 'a',
+ 's',
+ 'd',
+ 'f',
+ 'g',
+ 'h',
+ 'j',
+ 'k',
+ 'l',
+ ';',
+ '\'',
+ '`',
+ LSHFT,
+ '\\',
+ 'z',
+ 'x',
+ 'c',
+ 'v',
+ 'b',
+ 'n',
+ 'm',
+ ',',
+ '.',
+ '/',
+ RSHFT,
+ '*',
+ LA,
+ ' ',
+ LOCK,
+ 0, // F1,
+ 0, // F2,
+ 0, // F3,
+ 0, // F4,
+ 0, // F5,
+ 0, // F6,
+ 0, // F7,
+ 0, // F8,
+ 0, // F9,
+ 0, // F10,
+ 0, // numlock
+ 0, // scrolllock
+ 0, // HOME7
+ 0, // UP8
+ 0, // PGUP 9
+ 0,
+ 0, // LEFT4
+ '5', // number pad 5?
+ 0, // RTARROW6
+ '+',
+ 0, // END1
+ 0, // DOWN2
+ 0, // PGDN3
+ 0, // INS
+ 0, // DEL
+ 0,
+ 0,
+ 0,
+ 0, // F11
+ BIOSKEY, // F12
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ RET, // number pad enter?
+ _CTRL, // Right control?
+ '/',
+ 0, // prtscr
+ RA,
+ 0,
+ 0, // HOME
+ UARR,
+ 0, // PGUP
+ LARR,
+ RARR,
+ 0, // END
+ DARR,
+ 0, // PGDN
+ 0, // INSERT
+ DEL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0 // PAUSE
+};
+
+
+static uint8_t mapkeycode(uint16_t v)
+{
+ if (v < sizeof(keymap))
+ return keymap[v];
+ else
+ return 0;
+}
+
+void LinuxKeyboard::maintainKeyboard()
+{
+ struct input_event ev;
+ ssize_t n;
+
+ n = ::read(fd, &ev, sizeof ev);
+ if (n == sizeof(ev)) {
+ if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2) {
+ uint8_t code = mapkeycode(ev.code);
+ if (code == BIOSKEY) {
+ g_biosInterrupt = true;
+ return;
+ }
+
+ if (code) {
+ switch (ev.value) {
+ case 0: // release
+ vmkeyboard->keyReleased(code);
+ break;
+ case 1: // press
+ vmkeyboard->keyDepressed(code);
+ break;
+ case 2: // autorepeat
+ break;
+ }
+ }
+ }
+ }
+}
+
+bool keyHitPending = false;
+uint8_t keyPending;
+
+bool LinuxKeyboard::kbhit()
+{
+ struct input_event ev;
+ ssize_t n;
+
+ n = ::read(fd, &ev, sizeof ev);
+ if (n == sizeof(ev)) {
+ if (ev.type == EV_KEY && ev.value == 1) {
+ uint8_t code = mapkeycode(ev.code);
+ if (code && code != BIOSKEY) {
+ keyHitPending = true;
+ keyPending = code;
+ }
+ }
+ }
+ return keyHitPending;
+}
+
+int8_t LinuxKeyboard::read()
+{
+ if (keyHitPending) {
+ keyHitPending = false;
+ return keyPending;
+ }
+ return 0;
+}
+
diff --git a/linuxfb/linux-keyboard.h b/linuxfb/linux-keyboard.h
new file mode 100644
index 0000000..aa8671a
--- /dev/null
+++ b/linuxfb/linux-keyboard.h
@@ -0,0 +1,21 @@
+#ifndef __LINUX_KEYBOARD_H
+#define __LINUX_KEYBOARD_H
+
+#include "physicalkeyboard.h"
+#include "vmkeyboard.h"
+
+class LinuxKeyboard : public PhysicalKeyboard {
+ public:
+ LinuxKeyboard(VMKeyboard *k);
+ virtual ~LinuxKeyboard();
+
+ virtual void maintainKeyboard();
+
+ virtual bool kbhit();
+ virtual int8_t read();
+
+ private:
+ int fd;
+};
+
+#endif
diff --git a/linuxfb/linux-printer.cpp b/linuxfb/linux-printer.cpp
new file mode 100644
index 0000000..126375b
--- /dev/null
+++ b/linuxfb/linux-printer.cpp
@@ -0,0 +1,21 @@
+#include "linux-printer.h"
+
+LinuxPrinter::LinuxPrinter()
+{
+}
+
+LinuxPrinter::~LinuxPrinter()
+{
+}
+
+void LinuxPrinter::update()
+{
+}
+
+void LinuxPrinter::addLine(uint8_t *rowOfBits)
+{
+}
+
+void LinuxPrinter::moveDownPixels(uint8_t p)
+{
+}
diff --git a/linuxfb/linux-printer.h b/linuxfb/linux-printer.h
new file mode 100644
index 0000000..e942bf1
--- /dev/null
+++ b/linuxfb/linux-printer.h
@@ -0,0 +1,21 @@
+#ifndef __LINUX_PRINTER_H
+#define __LINUX_PRINTER_H
+
+#include
+#include
+
+#include "physicalprinter.h"
+
+class LinuxPrinter : public PhysicalPrinter {
+ public:
+ LinuxPrinter();
+ virtual ~LinuxPrinter();
+
+ virtual void addLine(uint8_t *rowOfBits); // must be 960 pixels wide (120 bytes)
+
+ virtual void update();
+
+ virtual void moveDownPixels(uint8_t p);
+};
+
+#endif
diff --git a/linuxfb/linux-speaker.cpp b/linuxfb/linux-speaker.cpp
new file mode 100644
index 0000000..c2cc8cb
--- /dev/null
+++ b/linuxfb/linux-speaker.cpp
@@ -0,0 +1,31 @@
+#include "linux-speaker.h"
+#include
+#include
+
+#include "globals.h"
+
+#include "timeutil.h"
+
+LinuxSpeaker::LinuxSpeaker()
+{
+}
+
+LinuxSpeaker::~LinuxSpeaker()
+{
+}
+
+void LinuxSpeaker::toggle(uint32_t c)
+{
+}
+
+void LinuxSpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
+{
+}
+
+void LinuxSpeaker::beginMixing()
+{
+}
+
+void LinuxSpeaker::mixOutput(uint8_t v)
+{
+}
diff --git a/linuxfb/linux-speaker.h b/linuxfb/linux-speaker.h
new file mode 100644
index 0000000..4ecf915
--- /dev/null
+++ b/linuxfb/linux-speaker.h
@@ -0,0 +1,21 @@
+#ifndef __SDLSPEAKER_H
+#define __SDLSPEAKER_H
+
+#include
+#include
+#include "physicalspeaker.h"
+
+#define SPEAKERQUEUESIZE 64
+
+class LinuxSpeaker : public PhysicalSpeaker {
+ public:
+ LinuxSpeaker();
+ virtual ~LinuxSpeaker();
+
+ virtual void toggle(uint32_t c);
+ virtual void maintainSpeaker(uint32_t c, uint64_t microseconds);
+ virtual void beginMixing();
+ virtual void mixOutput(uint8_t v);
+};
+
+#endif
diff --git a/linuxfb/timeutil.h b/linuxfb/timeutil.h
new file mode 100644
index 0000000..6e7dcca
--- /dev/null
+++ b/linuxfb/timeutil.h
@@ -0,0 +1,157 @@
+#include
+//#include
+// Derived from
+// http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
+
+#define ORWL_NANO (+1.0E-9)
+#define ORWL_GIGA UINT64_C(1000000000)
+#define NANOSECONDS_PER_SECOND 1000000000UL
+#define CYCLES_PER_SECOND g_speed
+
+static double orwl_timebase = 0.0;
+static uint64_t orwl_timestart = 0;
+static void _init_darwin_shim(void) {
+#if 0
+ mach_timebase_info_data_t tb = { 0 };
+ mach_timebase_info(&tb);
+ orwl_timebase = tb.numer;
+ orwl_timebase /= tb.denom;
+ orwl_timestart = mach_absolute_time();
+#endif
+}
+
+static int do_gettime(struct timespec *tp) {
+#if 0
+ double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase;
+ tp->tv_sec = diff * ORWL_NANO;
+ tp->tv_nsec = diff - (tp->tv_sec * ORWL_GIGA);
+#else
+ clock_gettime(CLOCK_MONOTONIC, tp);
+#endif
+ return 0;
+}
+
+// adds the number of nanoseconds that 'cycles' takes to *start and
+// returns it in *out
+static void timespec_add_cycles(struct timespec *start,
+ int32_t cycles,
+ struct timespec *out)
+{
+ out->tv_sec = start->tv_sec;
+ out->tv_nsec = start->tv_nsec;
+
+ uint64_t nanosToAdd = (double)((double)cycles * (double) (NANOSECONDS_PER_SECOND) / (double)CYCLES_PER_SECOND);
+
+ out->tv_sec += (nanosToAdd / NANOSECONDS_PER_SECOND);
+ out->tv_nsec += (nanosToAdd % NANOSECONDS_PER_SECOND);
+
+ if (out->tv_nsec >= NANOSECONDS_PER_SECOND) {
+ out->tv_sec++ ;
+ out->tv_nsec -= NANOSECONDS_PER_SECOND;
+ }
+}
+
+static unsigned long cycles_since_time(struct timespec *start)
+{
+ unsigned long ret = start->tv_sec * CYCLES_PER_SECOND;
+ ret += (double)((double)start->tv_nsec * (double)0.001023 + (double) 0.01); // 0.01 for rounding error; one cycle ~= 977517nS, and 977517 * .000001023 is only 0.999999891.
+ return ret;
+}
+
+// adds the number of microseconds given to *start and
+// returns it in *out
+static void timespec_add_us(struct timespec *start,
+ uint64_t micros,
+ struct timespec *out)
+{
+ out->tv_sec = start->tv_sec;
+ out->tv_nsec = start->tv_nsec;
+
+ uint64_t nanosToAdd = micros * 1000L;
+ out->tv_sec += (nanosToAdd / NANOSECONDS_PER_SECOND);
+ out->tv_nsec += (nanosToAdd % NANOSECONDS_PER_SECOND);
+
+ if (out->tv_nsec >= 1000000000L) {
+ out->tv_sec++ ;
+ out->tv_nsec -= 1000000000L;
+ }
+}
+
+static void timespec_diff(struct timespec *start,
+ struct timespec *end,
+ struct timespec *diff,
+ bool *negative) {
+ struct timespec t;
+
+ if (negative)
+ {
+ *negative = false;
+ }
+
+ // if start > end, swizzle...
+ if ( (start->tv_sec > end->tv_sec) || ((start->tv_sec == end->tv_sec) && (start->tv_nsec > end->tv_nsec)) )
+ {
+ t=*start;
+ *start=*end;
+ *end=t;
+ if (negative)
+ {
+ *negative = true;
+ }
+ }
+
+ // assuming time_t is signed ...
+ if (end->tv_nsec < start->tv_nsec)
+ {
+ t.tv_sec = end->tv_sec - start->tv_sec - 1;
+ t.tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
+ }
+ else
+ {
+ t.tv_sec = end->tv_sec - start->tv_sec;
+ t.tv_nsec = end->tv_nsec - start->tv_nsec;
+ }
+
+ diff->tv_sec = t.tv_sec;
+ diff->tv_nsec = t.tv_nsec;
+}
+
+// tsCompare: return -1, 0, 1 for (a < b), (a == b), (a > b)
+static int8_t tsCompare(struct timespec *A, struct timespec *B)
+{
+ if (A->tv_sec < B->tv_sec)
+ return -1;
+
+ if (A->tv_sec > B->tv_sec)
+ return 1;
+
+ if (A->tv_nsec < B->tv_nsec)
+ return -1;
+
+ if (A->tv_nsec > B->tv_nsec)
+ return 1;
+
+ return 0;
+}
+
+// return time1 - time2. If time1 <= time2, then return 0.
+static struct timespec tsSubtract(struct timespec time1, struct timespec time2)
+{
+ struct timespec result;
+ if ((time1.tv_sec < time2.tv_sec) ||
+ ((time1.tv_sec == time2.tv_sec) &&
+ (time1.tv_nsec <= time2.tv_nsec))) {/* TIME1 <= TIME2? */
+ result.tv_sec = result.tv_nsec = 0 ;
+ } else {/* TIME1 > TIME2 */
+ result.tv_sec = time1.tv_sec - time2.tv_sec ;
+ if (time1.tv_nsec < time2.tv_nsec) {
+ result.tv_nsec = time1.tv_nsec + 1000000000L - time2.tv_nsec ;
+ result.tv_sec-- ;/* Borrow a second. */
+ } else {
+ result.tv_nsec = time1.tv_nsec - time2.tv_nsec ;
+ }
+ }
+
+ return (result) ;
+}
+
diff --git a/sdl/sdl-clock.cpp b/nix/nix-clock.cpp
similarity index 90%
rename from sdl/sdl-clock.cpp
rename to nix/nix-clock.cpp
index e7ae371..f546fbf 100644
--- a/sdl/sdl-clock.cpp
+++ b/nix/nix-clock.cpp
@@ -1,7 +1,7 @@
#include // memset
#include
-#include "sdl-clock.h"
+#include "nix-clock.h"
#include "applemmu.h" // for FLOATING
/*
@@ -28,30 +28,30 @@ static void timeToProDOS(uint16_t year, uint8_t month, uint8_t day, uint8_t hour
proDOStimeOut[3] = minute & 0x3F;
}
-SDLClock::SDLClock(AppleMMU *mmu)
+NixClock::NixClock(AppleMMU *mmu)
{
this->mmu = mmu;
}
-SDLClock::~SDLClock()
+NixClock::~NixClock()
{
}
-bool SDLClock::Serialize(int8_t fd)
+bool NixClock::Serialize(int8_t fd)
{
return true;
}
-bool SDLClock::Deserialize(int8_t fd)
+bool NixClock::Deserialize(int8_t fd)
{
return true;
}
-void SDLClock::Reset()
+void NixClock::Reset()
{
}
-uint8_t SDLClock::readSwitches(uint8_t s)
+uint8_t NixClock::readSwitches(uint8_t s)
{
// When any switch is read, we'll put the current time in the prodos time buffer
time_t lt;
@@ -91,13 +91,13 @@ uint8_t SDLClock::readSwitches(uint8_t s)
return FLOATING;
}
-void SDLClock::writeSwitches(uint8_t s, uint8_t v)
+void NixClock::writeSwitches(uint8_t s, uint8_t v)
{
// printf("unimplemented write to the clock - 0x%X\n", v);
}
// FIXME: this assumes slot #5
-void SDLClock::loadROM(uint8_t *toWhere)
+void NixClock::loadROM(uint8_t *toWhere)
{
memset(toWhere, 0xEA, 256); // fill the page with NOPs
diff --git a/sdl/sdl-clock.h b/nix/nix-clock.h
similarity index 67%
rename from sdl/sdl-clock.h
rename to nix/nix-clock.h
index e12bf55..ad508e3 100644
--- a/sdl/sdl-clock.h
+++ b/nix/nix-clock.h
@@ -1,16 +1,18 @@
-#ifndef __SDLCLOCK_H
-#define __SDLCLOCK_H
+#ifndef __NIXCLOCK_H
+#define __NIXCLOCK_H
#include
#include
-#include "Slot.h"
+#include "slot.h"
#include "applemmu.h"
-class SDLClock : public Slot {
+// Simple clock for *nix
+
+class NixClock : public Slot {
public:
- SDLClock(AppleMMU *mmu);
- virtual ~SDLClock();
+ NixClock(AppleMMU *mmu);
+ virtual ~NixClock();
virtual bool Serialize(int8_t fd);
virtual bool Deserialize(int8_t fd);
diff --git a/sdl/sdl-filemanager.cpp b/nix/nix-filemanager.cpp
similarity index 66%
rename from sdl/sdl-filemanager.cpp
rename to nix/nix-filemanager.cpp
index 9e22b09..7bcd6dd 100644
--- a/sdl/sdl-filemanager.cpp
+++ b/nix/nix-filemanager.cpp
@@ -5,19 +5,22 @@
#include
#include
#include
+#include
-#include "sdl-filemanager.h"
+#include "nix-filemanager.h"
-SDLFileManager::SDLFileManager()
+#define ROOTDIR "./disks/"
+
+NixFileManager::NixFileManager()
{
numCached = 0;
}
-SDLFileManager::~SDLFileManager()
+NixFileManager::~NixFileManager()
{
}
-int8_t SDLFileManager::openFile(const char *name)
+int8_t NixFileManager::openFile(const char *name)
{
// See if there's a hole to re-use...
for (int i=0; i= numCached)
@@ -53,7 +56,7 @@ void SDLFileManager::closeFile(int8_t fd)
cachedNames[fd][0] = '\0';
}
-const char *SDLFileManager::fileName(int8_t fd)
+const char *NixFileManager::fileName(int8_t fd)
{
if (fd < 0 || fd >= numCached)
return NULL;
@@ -61,13 +64,96 @@ const char *SDLFileManager::fileName(int8_t fd)
return cachedNames[fd];
}
-int8_t SDLFileManager::readDir(const char *where, const char *suffix, char *outputFN, int8_t startIdx, uint16_t maxlen)
+int8_t NixFileManager::readDir(const char *where, const char *suffix, char *outputFN, int8_t startIdx, uint16_t maxlen)
{
- // not used in this version
- return -1;
+ int idx = 1;
+ if (strcmp(where, ROOTDIR)) {
+ // First entry is always "../"
+ if (startIdx == 0) {
+ strcpy(outputFN, "../");
+ return 0;
+ }
+ } else {
+ idx = 0; // we skipped ROOTDIR
+ }
+
+ DIR *dirp = opendir(where);
+ if (!dirp)
+ return -1;
+
+ struct dirent *dp;
+
+ outputFN[0] = '\0';
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_name[0] == '.') {
+ // Skip any dot files (and dot directories)
+ continue;
+ }
+
+ // FIXME: skip any non-files and non-directories
+
+ if (suffix && !(dp->d_type & DT_DIR) && strlen(dp->d_name) >= 3) {
+ // It's a valid file. If it doesn't match any of our suffixes,
+ // then skip it.
+ char pat[40];
+ strncpy(pat, suffix, sizeof(pat)); // make a working copy of the suffixes
+
+ char *fsuff = &dp->d_name[strlen(dp->d_name)-3];
+
+ if (strstr(pat, ",")) {
+ // We have a list of suffixes. Check each of them.
+
+ bool matchesAny = false;
+ char *tok = strtok((char *)pat, ",");
+ while (tok) {
+ // FIXME: assumes 3 character suffixes
+ if (!strncasecmp(fsuff, tok, 3)) {
+ matchesAny = true;
+ break;
+ }
+
+ tok = strtok(NULL, ",");
+ }
+
+ if (!matchesAny) {
+ continue;
+ }
+ } else {
+ // One single suffix - check it
+ if (strcasecmp(fsuff, suffix)) {
+ continue;
+ }
+ }
+ }
+ // If we get here, it's something we want to show.
+ if (idx == startIdx) {
+ // Fill in the reply
+ strncpy(outputFN, dp->d_name, maxlen-1);
+
+ if (dp->d_type & DT_DIR) {
+ // suffix
+ strcat(outputFN, "/");
+ }
+ break;
+ }
+
+ // Next!
+ idx++;
+ }
+
+ // Exited the loop - all done.
+ closedir(dirp);
+
+ if (!outputFN[0]) {
+ // didn't find any more
+ return -1;
+ }
+
+ return idx;
}
-void SDLFileManager::seekBlock(int8_t fd, uint16_t block, bool isNib)
+void NixFileManager::seekBlock(int8_t fd, uint16_t block, bool isNib)
{
if (fd < 0 || fd >= numCached)
return;
@@ -80,7 +166,7 @@ void SDLFileManager::seekBlock(int8_t fd, uint16_t block, bool isNib)
}
-bool SDLFileManager::readTrack(int8_t fd, uint8_t *toWhere, bool isNib)
+bool NixFileManager::readTrack(int8_t fd, uint8_t *toWhere, bool isNib)
{
if (fd < 0 || fd >= numCached)
return false;
@@ -104,7 +190,7 @@ bool SDLFileManager::readTrack(int8_t fd, uint8_t *toWhere, bool isNib)
return ret;
}
-bool SDLFileManager::readBlock(int8_t fd, uint8_t *toWhere, bool isNib)
+bool NixFileManager::readBlock(int8_t fd, uint8_t *toWhere, bool isNib)
{
// open, seek, read, close.
if (fd < 0 || fd >= numCached)
@@ -129,7 +215,7 @@ bool SDLFileManager::readBlock(int8_t fd, uint8_t *toWhere, bool isNib)
return ret;
}
-bool SDLFileManager::writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib)
+bool NixFileManager::writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib)
{
// open, seek, write, close.
if (fd < 0 || fd >= numCached)
@@ -159,7 +245,7 @@ bool SDLFileManager::writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib)
return true;
}
-bool SDLFileManager::writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib)
+bool NixFileManager::writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib)
{
// open, seek, write, close.
if (fd < 0 || fd >= numCached)
@@ -188,7 +274,7 @@ bool SDLFileManager::writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib)
return true;
}
-uint8_t SDLFileManager::readByteAt(int8_t fd, uint32_t pos)
+uint8_t NixFileManager::readByteAt(int8_t fd, uint32_t pos)
{
if (fd < 0 || fd >= numCached)
return -1; // FIXME: error handling?
@@ -215,7 +301,7 @@ uint8_t SDLFileManager::readByteAt(int8_t fd, uint32_t pos)
return v;
}
-bool SDLFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
+bool NixFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
{
if (fd < 0 || fd >= numCached)
return false;
@@ -235,7 +321,7 @@ bool SDLFileManager::writeByteAt(int8_t fd, uint8_t v, uint32_t pos)
return ret;
}
-bool SDLFileManager::writeByte(int8_t fd, uint8_t v)
+bool NixFileManager::writeByte(int8_t fd, uint8_t v)
{
if (fd < 0 || fd >= numCached)
return false;
@@ -263,7 +349,7 @@ bool SDLFileManager::writeByte(int8_t fd, uint8_t v)
return ret;
}
-uint8_t SDLFileManager::readByte(int8_t fd)
+uint8_t NixFileManager::readByte(int8_t fd)
{
if (fd < 0 || fd >= numCached)
return -1; // FIXME: error handling?
@@ -293,3 +379,8 @@ uint8_t SDLFileManager::readByte(int8_t fd)
return v;
}
+void NixFileManager::getRootPath(char *toWhere, int8_t maxLen)
+{
+ strcpy(toWhere, ROOTDIR);
+ // strncpy(toWhere, ROOTDIR, maxLen);
+}
diff --git a/sdl/sdl-filemanager.h b/nix/nix-filemanager.h
similarity index 82%
rename from sdl/sdl-filemanager.h
rename to nix/nix-filemanager.h
index 690a109..c2e7aa4 100644
--- a/sdl/sdl-filemanager.h
+++ b/nix/nix-filemanager.h
@@ -1,13 +1,13 @@
-#ifndef __SDL_FILEMANAGER_H
-#define __SDL_FILEMANAGER_H
+#ifndef __NIX_FILEMANAGER_H
+#define __NIX_FILEMANAGER_H
#include "filemanager.h"
#include
-class SDLFileManager : public FileManager {
+class NixFileManager : public FileManager {
public:
- SDLFileManager();
- virtual ~SDLFileManager();
+ NixFileManager();
+ virtual ~NixFileManager();
virtual int8_t openFile(const char *name);
virtual void closeFile(int8_t fd);
@@ -27,6 +27,8 @@ class SDLFileManager : public FileManager {
virtual uint8_t readByte(int8_t fd);
virtual bool writeByte(int8_t fd, uint8_t v);
+ void getRootPath(char *toWhere, int8_t maxLen);
+
private:
int8_t numCached;
diff --git a/physicaldisplay.h b/physicaldisplay.h
index 6b4a10f..e9f2bb0 100644
--- a/physicaldisplay.h
+++ b/physicaldisplay.h
@@ -10,6 +10,7 @@ class PhysicalDisplay {
PhysicalDisplay() { overlayMessage[0] = '\0'; }
virtual ~PhysicalDisplay() {};
+ virtual void flush() = 0; // flush any pending drawings
virtual void redraw() = 0; // total redraw, assuming nothing
virtual void blit(AiieRect r) = 0; // redraw just the VM display area
@@ -22,6 +23,8 @@ class PhysicalDisplay {
virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color) = 0;
virtual void drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b) = 0;
+ virtual void clrScr() = 0;
+
protected:
char overlayMessage[40];
};
diff --git a/physicalkeyboard.h b/physicalkeyboard.h
index 9342ee4..01c4810 100644
--- a/physicalkeyboard.h
+++ b/physicalkeyboard.h
@@ -5,22 +5,22 @@
#include "vmkeyboard.h"
-#define ESC 0x1B
-#define DEL 0x7F
-#define RET 0x0D
-#define TAB 0x09
-#define LARR 0x08 // control-H
-#define RARR 0x15 // control-U
-#define DARR 0x0A
-#define UARR 0x0B
+#define PK_ESC 0x1B
+#define PK_DEL 0x7F
+#define PK_RET 0x0D
+#define PK_TAB 0x09
+#define PK_LARR 0x08 // control-H
+#define PK_RARR 0x15 // control-U
+#define PK_DARR 0x0A
+#define PK_UARR 0x0B
// Virtual keys
-#define _CTRL 0x81
-#define LSHFT 0x82
-#define RSHFT 0x83
-#define LOCK 0x84 // caps lock
-#define LA 0x85 // left (open) apple, aka paddle0 button
-#define RA 0x86 // right (closed) apple aka paddle1 button
+#define PK_CTRL 0x81
+#define PK_LSHFT 0x82
+#define PK_RSHFT 0x83
+#define PK_LOCK 0x84 // caps lock
+#define PK_LA 0x85 // left (open) apple, aka paddle0 button
+#define PK_RA 0x86 // right (closed) apple aka paddle1 button
class PhysicalKeyboard {
public:
@@ -29,6 +29,9 @@ class PhysicalKeyboard {
virtual void maintainKeyboard() = 0;
+ virtual bool kbhit() = 0;
+ virtual int8_t read() = 0;
+
protected:
VMKeyboard *vmkeyboard;
};
diff --git a/sdl/aiie.cpp b/sdl/aiie.cpp
index 363ac48..15413c9 100644
--- a/sdl/aiie.cpp
+++ b/sdl/aiie.cpp
@@ -9,19 +9,22 @@
#include "sdl-keyboard.h"
#include "sdl-speaker.h"
#include "sdl-paddles.h"
-#include "sdl-filemanager.h"
+#include "nix-filemanager.h"
#include "sdl-printer.h"
#include "appleui.h"
+#include "bios.h"
#include "globals.h"
#include "timeutil.h"
-#define SHOWFPS
+//#define SHOWFPS
//#define SHOWPC
//#define DEBUGCPU
//#define SHOWMEMPAGE
+BIOS bios;
+
static struct timespec nextInstructionTime, startTime;
#define NB_ENABLE 1
@@ -37,7 +40,7 @@ char disk2name[256] = "\0";
volatile bool wantSuspend = false;
volatile bool wantResume = false;
-volatile uint64_t hitcount = 0, misscount = 0;
+void doDebugging();
void sigint_handler(int n)
{
@@ -99,6 +102,14 @@ static void *cpu_thread(void *dummyptr) {
printf("free-running\n");
while (1) {
+ if (g_biosInterrupt) {
+ printf("BIOS blocking\n");
+ while (g_biosInterrupt) {
+ usleep(100);
+ }
+ printf("BIOS block complete\n");
+ }
+
if (wantSuspend) {
printf("CPU halted; suspending VM\n");
g_vm->Suspend("suspend.vm");
@@ -125,10 +136,7 @@ static void *cpu_thread(void *dummyptr) {
struct timespec diff = tsSubtract(nextCycleTime, currentTime);
if (diff.tv_sec >= 0 || diff.tv_nsec >= 0) {
- hitcount++;
nanosleep(&diff, NULL);
- } else {
- misscount++;
}
// Speaker runs 48 cycles behind the CPU (an arbitrary number)
@@ -282,7 +290,7 @@ int main(int argc, char *argv[])
g_printer = new SDLPrinter();
// create the filemanager - the interface to the host file system.
- g_filemanager = new SDLFileManager();
+ g_filemanager = new NixFileManager();
g_display = new SDLDisplay();
// g_displayType = m_blackAndWhite;
@@ -338,12 +346,41 @@ int main(int argc, char *argv[])
}
while (1) {
- static uint32_t usleepcycles = 16384; // step-down for display drawing. Dynamically updated based on FPS calculations.
- static uint8_t ctr = 0;
- if (++ctr == 0) {
- printf("hit: %llu; miss: %llu; pct: %f\n", hitcount, misscount, (double)misscount / (double)(misscount + hitcount));
+ if (g_biosInterrupt) {
+ printf("Invoking BIOS\n");
+ if (bios.runUntilDone()) {
+ // if it returned true, we have something to store persistently in EEPROM.
+ // writePrefs();
+ }
+ printf("BIOS done\n");
+
+ // if we turned off debugMode, make sure to clear the debugMsg
+ if (g_debugMode == D_NONE) {
+ g_display->debugMsg("");
+ }
+
+ g_biosInterrupt = false;
+
+ // clear the CPU next-step counters
+ g_cpu->cycles = 0;
+ do_gettime(&startTime);
+ do_gettime(&nextInstructionTime);
+
+ // Drain the speaker queue (FIXME: a little hacky)
+ g_speaker->maintainSpeaker(-1, -1);
+
+ /* FIXME
+ // Force the display to redraw
+ ((AppleDisplay*)(g_vm->vmdisplay))->modeChange();
+ */
+
+ // Poll the keyboard before we start, so we can do selftest on startup
+ g_keyboard->maintainKeyboard();
}
+
+ static uint32_t usleepcycles = 16384; // step-down for display drawing. Dynamically updated based on FPS calculations.
+
// fill disk buffer when needed
((AppleVM*)g_vm)->disk6->fillDiskBuffer();
@@ -359,6 +396,9 @@ int main(int argc, char *argv[])
g_printer->update();
g_keyboard->maintainKeyboard();
+
+ doDebugging();
+
g_ui->drawPercentageUIElement(UIePowerPercentage, 100);
// calculate FPS & dynamically step up/down as necessary
@@ -417,3 +457,50 @@ int main(int argc, char *argv[])
}
}
+void doDebugging()
+{
+ char buf[25];
+ static time_t startAt = time(NULL);
+ static uint32_t loopCount = 0;
+
+ switch (g_debugMode) {
+ case D_SHOWFPS:
+ {
+ // display some FPS data
+ loopCount++;
+ uint32_t lenSecs = time(NULL) - startAt;
+ if (lenSecs >= 5) {
+ sprintf(buf, "%u FPS", loopCount / lenSecs);
+ g_display->debugMsg(buf);
+ startAt = time(NULL);
+ loopCount = 0;
+ }
+ }
+ break;
+ case D_SHOWMEMFREE:
+ // sprintf(buf, "%lu %u", FreeRamEstimate(), heapSize());
+ // g_display->debugMsg(buf);
+ break;
+ case D_SHOWPADDLES:
+ sprintf(buf, "%u %u", g_paddles->paddle0(), g_paddles->paddle1());
+ g_display->debugMsg(buf);
+ break;
+ case D_SHOWPC:
+ sprintf(buf, "%X", g_cpu->pc);
+ g_display->debugMsg(buf);
+ break;
+ case D_SHOWCYCLES:
+ sprintf(buf, "%X", g_cpu->cycles);
+ g_display->debugMsg(buf);
+ break;
+ /*
+ case D_SHOWBATTERY:
+ // sprintf(buf, "BAT %d", analogRead(BATTERYPIN));
+ // g_display->debugMsg(buf);
+ break;
+ case D_SHOWTIME:
+ // sprintf(buf, "%.2d:%.2d:%.2d", hour(), minute(), second());
+ // g_display->debugMsg(buf);
+ break;*/
+ }
+}
diff --git a/sdl/sdl-display.cpp b/sdl/sdl-display.cpp
index fe4310d..128260a 100644
--- a/sdl/sdl-display.cpp
+++ b/sdl/sdl-display.cpp
@@ -50,6 +50,11 @@ SDLDisplay::~SDLDisplay()
SDL_Quit();
}
+void SDLDisplay::flush()
+{
+ SDL_RenderPresent(renderer);
+}
+
void SDLDisplay::redraw()
{
// primarily for the device, where it's in and out of the
@@ -197,8 +202,10 @@ void SDLDisplay::drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str
}
}
-void SDLDisplay::debugMsg(const char *msg)
+void SDLDisplay::clrScr()
{
- printf("%s\n", msg);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // set to white
+ SDL_RenderClear(renderer); // clear it to the selected color
+ SDL_RenderPresent(renderer); // perform the render
}
diff --git a/sdl/sdl-display.h b/sdl/sdl-display.h
index 5405350..ee5e82b 100644
--- a/sdl/sdl-display.h
+++ b/sdl/sdl-display.h
@@ -13,13 +13,6 @@
#define SDLDISPLAY_WIDTH (320*2)
#define SDLDISPLAY_HEIGHT (240*2)
-enum {
- M_NORMAL = 0,
- M_SELECTED = 1,
- M_DISABLED = 2,
- M_SELECTDISABLED = 3
-};
-
class SDLDisplay : public PhysicalDisplay {
public:
SDLDisplay();
@@ -28,6 +21,8 @@ class SDLDisplay : public PhysicalDisplay {
virtual void blit(AiieRect r);
virtual void redraw();
+ virtual void flush();
+
virtual void drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey);
virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color);
@@ -35,7 +30,7 @@ class SDLDisplay : public PhysicalDisplay {
virtual void drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c);
virtual void drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str);
- virtual void debugMsg(const char *msg);
+ virtual void clrScr();
private:
SDL_Window *screen;
diff --git a/sdl/sdl-keyboard.cpp b/sdl/sdl-keyboard.cpp
index 9b64924..12c07ec 100644
--- a/sdl/sdl-keyboard.cpp
+++ b/sdl/sdl-keyboard.cpp
@@ -20,6 +20,13 @@ void SDLKeyboard::handleKeypress(SDL_KeyboardEvent *key)
{
bool releaseEvent = key->type == SDL_KEYUP;
+ if (key->type == SDL_KEYDOWN &&
+ key->keysym.sym == SDLK_F10) {
+ // Invoke BIOS
+ g_biosInterrupt = true;
+ return;
+ }
+
if ( (key->keysym.sym >= 'a' && key->keysym.sym <= 'z') ||
(key->keysym.sym >= '0' && key->keysym.sym <= '9') ||
key->keysym.sym == '-' ||
@@ -54,76 +61,76 @@ void SDLKeyboard::handleKeypress(SDL_KeyboardEvent *key)
// delete key
if (key->keysym.sym == 8) {
if (releaseEvent)
- vmkeyboard->keyReleased(DEL);
+ vmkeyboard->keyReleased(PK_DEL);
else
- vmkeyboard->keyDepressed(DEL);
+ vmkeyboard->keyDepressed(PK_DEL);
return;
}
//modifier handling
if (key->keysym.sym == SDLK_CAPSLOCK) {
if (releaseEvent)
- vmkeyboard->keyReleased(LOCK);
+ vmkeyboard->keyReleased(PK_LOCK);
else
- vmkeyboard->keyDepressed(LOCK);
+ vmkeyboard->keyDepressed(PK_LOCK);
}
if (key->keysym.sym == SDLK_LSHIFT ||
key->keysym.sym == SDLK_RSHIFT) {
if (releaseEvent)
- vmkeyboard->keyReleased(LSHFT);
+ vmkeyboard->keyReleased(PK_LSHFT);
else
- vmkeyboard->keyDepressed(LSHFT);
+ vmkeyboard->keyDepressed(PK_LSHFT);
}
// arrows
if (key->keysym.sym == SDLK_LEFT) {
if (releaseEvent)
- vmkeyboard->keyReleased(LARR);
+ vmkeyboard->keyReleased(PK_LARR);
else
- vmkeyboard->keyDepressed(LARR);
+ vmkeyboard->keyDepressed(PK_LARR);
}
if (key->keysym.sym == SDLK_RIGHT) {
if (releaseEvent)
- vmkeyboard->keyReleased(RARR);
+ vmkeyboard->keyReleased(PK_RARR);
else
- vmkeyboard->keyDepressed(RARR);
+ vmkeyboard->keyDepressed(PK_RARR);
}
if (key->keysym.sym == SDLK_LEFT) {
if (releaseEvent)
- vmkeyboard->keyReleased(LARR);
+ vmkeyboard->keyReleased(PK_LARR);
else
- vmkeyboard->keyDepressed(LARR);
+ vmkeyboard->keyDepressed(PK_LARR);
}
if (key->keysym.sym == SDLK_UP) {
if (releaseEvent)
- vmkeyboard->keyReleased(UARR);
+ vmkeyboard->keyReleased(PK_UARR);
else
- vmkeyboard->keyDepressed(UARR);
+ vmkeyboard->keyDepressed(PK_UARR);
}
if (key->keysym.sym == SDLK_DOWN) {
if (releaseEvent)
- vmkeyboard->keyReleased(DARR);
+ vmkeyboard->keyReleased(PK_DARR);
else
- vmkeyboard->keyDepressed(DARR);
+ vmkeyboard->keyDepressed(PK_DARR);
}
// Paddles
if (key->keysym.sym == SDLK_LGUI) {
if (releaseEvent)
- vmkeyboard->keyReleased(LA);
+ vmkeyboard->keyReleased(PK_LA);
else
- vmkeyboard->keyDepressed(LA);
+ vmkeyboard->keyDepressed(PK_LA);
}
if (key->keysym.sym == SDLK_RGUI) {
if (releaseEvent)
- vmkeyboard->keyReleased(RA);
+ vmkeyboard->keyReleased(PK_RA);
else
- vmkeyboard->keyDepressed(RA);
+ vmkeyboard->keyDepressed(PK_RA);
}
}
@@ -155,3 +162,64 @@ void SDLKeyboard::maintainKeyboard()
}
}
}
+
+bool hasKeyPending;
+uint8_t keyPending;
+
+bool SDLKeyboard::kbhit()
+{
+ SDL_Event event;
+ if (SDL_PollEvent( &event ) &&
+ event.type == SDL_KEYDOWN) {
+ SDL_KeyboardEvent *key = &event.key;
+ if ( (key->keysym.sym >= 'a' && key->keysym.sym <= 'z') ||
+ (key->keysym.sym >= '0' && key->keysym.sym <= '9') ||
+ key->keysym.sym == '-' ||
+ key->keysym.sym == '=' ||
+ key->keysym.sym == '[' ||
+ key->keysym.sym == '`' ||
+ key->keysym.sym == ']' ||
+ key->keysym.sym == '\\' ||
+ key->keysym.sym == ';' ||
+ key->keysym.sym == '\'' ||
+ key->keysym.sym == ',' ||
+ key->keysym.sym == '.' ||
+ key->keysym.sym == '/' ||
+ key->keysym.sym == ' ' ||
+ key->keysym.sym == 27 || // ESC
+ key->keysym.sym == 13 || // return
+ key->keysym.sym == 9) { // tab
+ keyPending = key->keysym.sym;
+ hasKeyPending = true;
+ } else {
+ switch (key->keysym.sym) {
+ case SDLK_UP:
+ keyPending = PK_UARR;
+ hasKeyPending = true;
+ break;
+ case SDLK_DOWN:
+ keyPending = PK_DARR;
+ hasKeyPending = true;
+ break;
+ case SDLK_RIGHT:
+ keyPending = PK_RARR;
+ hasKeyPending = true;
+ break;
+ case SDLK_LEFT:
+ keyPending = PK_LARR;
+ hasKeyPending = true;
+ break;
+ }
+ }
+ }
+ return hasKeyPending;
+}
+
+int8_t SDLKeyboard::read()
+{
+ // Meh
+ hasKeyPending = false;
+ return keyPending;
+}
+
+
diff --git a/sdl/sdl-keyboard.h b/sdl/sdl-keyboard.h
index ff78eab..ab53a4d 100644
--- a/sdl/sdl-keyboard.h
+++ b/sdl/sdl-keyboard.h
@@ -13,6 +13,9 @@ class SDLKeyboard : public PhysicalKeyboard {
virtual void maintainKeyboard();
+ virtual bool kbhit();
+ virtual int8_t read();
+
private:
void handleKeypress(SDL_KeyboardEvent *key);
};
diff --git a/sdl/sdl-speaker.cpp b/sdl/sdl-speaker.cpp
index d75546d..076b065 100644
--- a/sdl/sdl-speaker.cpp
+++ b/sdl/sdl-speaker.cpp
@@ -20,14 +20,19 @@ static pthread_mutex_t togmutex = PTHREAD_MUTEX_INITIALIZER;
static void audioCallback(void *unused, Uint8 *stream, int len)
{
- FILE *f = (FILE *)unused;
-
pthread_mutex_lock(&sndmutex);
+
+ if (g_biosInterrupt) {
+ // While the BIOS is running, we don't put samples in the audio
+ // queue.
+ memset(stream, 0, len);
+ pthread_mutex_unlock(&sndmutex);
+ return;
+ }
+
if (bufIdx >= len) {
memcpy(stream, soundBuf, len);
- fwrite(soundBuf, 1, len, f);
-
if (bufIdx > len) {
// move the remaining data down
memcpy(soundBuf, &soundBuf[len], bufIdx - len + 1);
@@ -52,7 +57,7 @@ void ResetDCFilter(); // FIXME: remove
SDLSpeaker::SDLSpeaker()
{
toggleState = false;
- mixerValue = 0x8000;
+ mixerValue = 0x80;
toggleCount = toggleReadPtr = toggleWritePtr = 0;
@@ -66,8 +71,6 @@ SDLSpeaker::SDLSpeaker()
lastCycleCount = 0;
lastSampleCount = 0;
- FILE *f = fopen("out.dat", "w");
-
SDL_AudioSpec audioDevice;
SDL_AudioSpec audioActual;
SDL_memset(&audioDevice, 0, sizeof(audioDevice));
@@ -76,7 +79,7 @@ SDLSpeaker::SDLSpeaker()
audioDevice.channels = 1;
audioDevice.samples = 4096; // 4096 bytes @ 44100Hz is about 1/10th second out of sync - should be okay for this testing
audioDevice.callback = audioCallback;
- audioDevice.userdata = (void *)f;
+ audioDevice.userdata = NULL;
SDL_OpenAudio(&audioDevice, &audioActual); // FIXME retval
printf("Actual: freq %d channels %d samples %d\n",
@@ -87,7 +90,6 @@ SDLSpeaker::SDLSpeaker()
SDLSpeaker::~SDLSpeaker()
{
- pclose(f);
}
void SDLSpeaker::toggle(uint32_t c)
@@ -140,22 +142,31 @@ void SDLSpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
bool didChange = false;
pthread_mutex_lock(&togmutex);
- while (toggleCount && c >= toggleTimes[toggleReadPtr]) {
- // Override the mixer with a 1-bit "Terribad" audio sample change
- toggleState = !toggleState;
- toggleCount--;
- toggleReadPtr++;
- if (toggleReadPtr >= SPEAKERQUEUESIZE)
- toggleReadPtr = 0;
- didChange = true;
+
+ if (c == -1 && microseconds == -1) {
+ // flushing
+ printf("Flush sound output\n");
+ toggleReadPtr = toggleWritePtr = 0;
+ toggleCount = 0;
+ } else {
+ while (toggleCount && c >= toggleTimes[toggleReadPtr]) {
+ // Override the mixer with a 1-bit "Terribad" audio sample change
+ toggleState = !toggleState;
+ toggleCount--;
+ toggleReadPtr++;
+ if (toggleReadPtr >= SPEAKERQUEUESIZE)
+ toggleReadPtr = 0;
+ didChange = true;
+ }
}
+
pthread_mutex_unlock(&togmutex);
// FIXME: removed all the mixing code
// Add samples from the last time to this time
// mixerValue = (toggleState ? 0x1FF : 0x00);
- mixerValue = (toggleState ? 0x8000 : ~0x8000);
+ mixerValue = (toggleState ? 0x00 : ~0x80);
// FIXME: DC filter isn't correct yet
// mixerValue = DCFilter(mixerValue);
@@ -165,14 +176,18 @@ void SDLSpeaker::maintainSpeaker(uint32_t c, uint64_t microseconds)
if (numSamples) {
lastSampleCount = sampleCount;
- mixerValue >>= 12; // convert from 16 bit to 8 bit; then drop volume by 50%
-
pthread_mutex_lock(&sndmutex);
if (bufIdx + numSamples >= sizeof(soundBuf)) {
- printf("Sound overrun!\n");
+ static uint8_t errcnt = 0;
+ if (++errcnt <= 10) {
+ printf("Sound overrun!\n");
+ }
numSamples = sizeof(soundBuf) - bufIdx - 1;
}
+
+ mixerValue >>= (8-(g_volume/2));
+
memset(&soundBuf[bufIdx], mixerValue, numSamples);
bufIdx += numSamples;
pthread_mutex_unlock(&sndmutex);
diff --git a/sdl/sdl-speaker.h b/sdl/sdl-speaker.h
index 10a8ec0..f85efe7 100644
--- a/sdl/sdl-speaker.h
+++ b/sdl/sdl-speaker.h
@@ -5,7 +5,7 @@
#include
#include "physicalspeaker.h"
-#define SPEAKERQUEUESIZE 64
+#define SPEAKERQUEUESIZE 1024
class SDLSpeaker : public PhysicalSpeaker {
public:
@@ -17,7 +17,7 @@ class SDLSpeaker : public PhysicalSpeaker {
virtual void beginMixing();
virtual void mixOutput(uint8_t v);
private:
- int16_t mixerValue;
+ uint8_t mixerValue;
bool toggleState;
uint32_t toggleTimes[SPEAKERQUEUESIZE];
@@ -27,8 +27,6 @@ class SDLSpeaker : public PhysicalSpeaker {
uint64_t lastCycleCount;
uint64_t lastSampleCount;
-
- FILE *f;
};
#endif
diff --git a/sdl/timeutil.h b/sdl/timeutil.h
index 8de3ee7..82e2518 100644
--- a/sdl/timeutil.h
+++ b/sdl/timeutil.h
@@ -6,7 +6,7 @@
#define ORWL_NANO (+1.0E-9)
#define ORWL_GIGA UINT64_C(1000000000)
#define NANOSECONDS_PER_SECOND 1000000000UL
-#define CYCLES_PER_SECOND 1023000UL
+#define CYCLES_PER_SECOND g_speed
static double orwl_timebase = 0.0;
static uint64_t orwl_timestart = 0;
@@ -34,7 +34,7 @@ static void timespec_add_cycles(struct timespec *start,
out->tv_sec = start->tv_sec;
out->tv_nsec = start->tv_nsec;
- uint64_t nanosToAdd = (double)((double)cycles * (double) (NANOSECONDS_PER_SECOND) / (double)1023000);
+ uint64_t nanosToAdd = (double)((double)cycles * (double) (NANOSECONDS_PER_SECOND) / (double)CYCLES_PER_SECOND);
out->tv_sec += (nanosToAdd / NANOSECONDS_PER_SECOND);
out->tv_nsec += (nanosToAdd % NANOSECONDS_PER_SECOND);
diff --git a/teensy/bios.cpp b/teensy/bios.cpp
deleted file mode 100644
index 7da6f69..0000000
--- a/teensy/bios.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-#include "bios.h"
-
-#include "applevm.h"
-#include "physicalkeyboard.h"
-#include "teensy-keyboard.h"
-#include "cpu.h"
-#include "teensy-filemanager.h"
-#include "teensy-display.h"
-
-#include "globals.h"
-
-enum {
- ACT_EXIT = 0,
- ACT_RESET = 1,
- ACT_COLDBOOT = 2,
- ACT_MONITOR = 3,
- ACT_DISPLAYTYPE = 4,
- ACT_DEBUG = 5,
- ACT_DISK1 = 6,
- ACT_DISK2 = 7,
- ACT_HD1 = 8,
- ACT_HD2 = 9,
- ACT_VOLPLUS = 10,
- ACT_VOLMINUS = 11,
- ACT_SUSPEND = 12,
- ACT_RESTORE = 13,
- ACT_PRIMODE = 14,
-
- NUM_ACTIONS = 15
-};
-
-const char *titles[NUM_ACTIONS] = { "Resume VM",
- "Reset",
- "Cold Reboot",
- "Drop to Monitor",
- "Display: %s",
- "Debug: %s",
- "%s Disk 1",
- "%s Disk 2",
- "%s HD 1",
- "%s HD 2",
- "Volume +",
- "Volume -",
- "Suspend",
- "Restore",
- "Prioritize %s"
-};
-
-// FIXME: abstract the pin # rather than repeating it here
-#define RESETPIN 39
-
-extern int16_t g_volume; // FIXME: external global. icky.
-extern uint8_t debugMode; // and another. :/
-extern bool g_prioritizeDisplay; // And a third!
-
-// FIXME: and these need abstracting out of the main .ino !
-enum {
- D_NONE = 0,
- D_SHOWFPS = 1,
- D_SHOWMEMFREE = 2,
- D_SHOWPADDLES = 3,
- D_SHOWPC = 4,
- D_SHOWCYCLES = 5,
- D_SHOWBATTERY = 6,
- D_SHOWTIME = 7
-};
-
-const char *staticPathConcat(const char *rootPath, const char *filePath)
-{
- static char buf[MAXPATH];
- strncpy(buf, rootPath, sizeof(buf)-1);
- strncat(buf, filePath, sizeof(buf)-strlen(buf)-1);
-
- return buf;
-}
-
-
-BIOS::BIOS()
-{
- strcpy(rootPath, "/A2DISKS/");
-
- selectedFile = -1;
- for (int8_t i=0; iMonitor();
- goto done;
- case ACT_DISPLAYTYPE:
- g_displayType++;
- g_displayType %= 4; // FIXME: abstract max #
- ((AppleDisplay*)g_display)->displayTypeChanged();
- break;
- case ACT_DEBUG:
- debugMode++;
- debugMode %= 8; // FIXME: abstract max #
- break;
- case ACT_PRIMODE:
- g_prioritizeDisplay = !g_prioritizeDisplay;
- break;
- case ACT_DISK1:
- if (((AppleVM *)g_vm)->DiskName(0)[0] != '\0') {
- ((AppleVM *)g_vm)->ejectDisk(0);
- } else {
- if (SelectDiskImage()) {
- ((AppleVM *)g_vm)->insertDisk(0, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
- goto done;
- }
- }
- break;
- case ACT_DISK2:
- if (((AppleVM *)g_vm)->DiskName(1)[0] != '\0') {
- ((AppleVM *)g_vm)->ejectDisk(1);
- } else {
- if (SelectDiskImage()) {
- ((AppleVM *)g_vm)->insertDisk(1, staticPathConcat(rootPath, fileDirectory[selectedFile]), false);
- goto done;
- }
- }
- break;
- case ACT_HD1:
- if (((AppleVM *)g_vm)->HDName(0)[0] != '\0') {
- ((AppleVM *)g_vm)->ejectHD(0);
- } else {
- if (SelectDiskImage()) {
- ((AppleVM *)g_vm)->insertHD(0, staticPathConcat(rootPath, fileDirectory[selectedFile]));
- goto done;
- }
- }
- break;
- case ACT_HD2:
- if (((AppleVM *)g_vm)->HDName(1)[0] != '\0') {
- ((AppleVM *)g_vm)->ejectHD(1);
- } else {
- if (SelectDiskImage()) {
- ((AppleVM *)g_vm)->insertHD(1, staticPathConcat(rootPath, fileDirectory[selectedFile]));
- goto done;
- }
- }
- break;
- case ACT_VOLPLUS:
- g_volume ++;
- if (g_volume > 15) {
- g_volume = 15;
- }
- volumeDidChange = true;
- break;
- case ACT_VOLMINUS:
- g_volume--;
- if (g_volume < 0) {
- g_volume = 0;
- }
- volumeDidChange = true;
- break;
-
- case ACT_SUSPEND:
- // CPU is already suspended, so this is safe...
- ((AppleVM *)g_vm)->Suspend("suspend.vm");
- break;
- case ACT_RESTORE:
- // CPU is already suspended, so this is safe...
- ((AppleVM *)g_vm)->Resume("suspend.vm");
- break;
- }
- }
-
- done:
- // Undo whatever damage we've done to the screen
- g_display->redraw();
- g_display->blit({0, 0, 191, 279});
-
- // return true if any persistent setting changed that we want to store in eeprom
- return volumeDidChange;
-}
-
-void BIOS::WarmReset()
-{
- g_cpu->Reset();
-}
-
-void BIOS::ColdReboot()
-{
- g_vm->Reset();
- g_cpu->Reset();
-}
-
-uint8_t BIOS::GetAction(int8_t selection)
-{
- while (1) {
- DrawMainMenu(selection);
- while (!((TeensyKeyboard *)g_keyboard)->kbhit() &&
- (digitalRead(RESETPIN) == HIGH)) {
- ;
- // Wait for either a keypress or the reset button to be pressed
- }
-
- if (digitalRead(RESETPIN) == LOW) {
- // wait until it's no longer pressed
- while (digitalRead(RESETPIN) == HIGH)
- ;
- delay(100); // wait long enough for it to debounce
- // then return an exit code
- return ACT_EXIT;
- }
-
- switch (((TeensyKeyboard *)g_keyboard)->read()) {
- case DARR:
- selection++;
- selection %= NUM_ACTIONS;
- break;
- case UARR:
- selection--;
- if (selection < 0)
- selection = NUM_ACTIONS-1;
- break;
- case RET:
- if (isActionActive(selection))
- return selection;
- break;
- }
- }
-}
-
-bool BIOS::isActionActive(int8_t action)
-{
- // don't return true for disk events that aren't valid
- switch (action) {
- case ACT_EXIT:
- case ACT_RESET:
- case ACT_COLDBOOT:
- case ACT_MONITOR:
- case ACT_DISPLAYTYPE:
- case ACT_DEBUG:
- case ACT_PRIMODE:
- case ACT_DISK1:
- case ACT_DISK2:
- case ACT_HD1:
- case ACT_HD2:
- case ACT_SUSPEND:
- case ACT_RESTORE:
- return true;
-
- case ACT_VOLPLUS:
- return (g_volume < 15);
- case ACT_VOLMINUS:
- return (g_volume > 0);
- }
-
- /* NOTREACHED */
- return false;
-}
-
-void BIOS::DrawMainMenu(int8_t selection)
-{
- ((TeensyDisplay *)g_display)->clrScr();
- g_display->drawString(M_NORMAL, 0, 0, "BIOS Configuration");
- for (int i=0; iDiskName(i - ACT_DISK1)[0] ? "Eject" : "Insert");
- } else if (i == ACT_HD1 || i == ACT_HD2) {
- sprintf(buf, titles[i], ((AppleVM *)g_vm)->HDName(i - ACT_HD1)[0] ? "Eject" : "Insert");
- } else if (i == ACT_DISPLAYTYPE) {
- switch (g_displayType) {
- case m_blackAndWhite:
- sprintf(buf, titles[i], "B&W");
- break;
- case m_monochrome:
- sprintf(buf, titles[i], "Mono");
- break;
- case m_ntsclike:
- sprintf(buf, titles[i], "NTSC-like");
- break;
- case m_perfectcolor:
- sprintf(buf, titles[i], "RGB");
- break;
- }
- } else if (i == ACT_DEBUG) {
- switch (debugMode) {
- case D_NONE:
- sprintf(buf, titles[i], "off");
- break;
- case D_SHOWFPS:
- sprintf(buf, titles[i], "Show FPS");
- break;
- case D_SHOWMEMFREE:
- sprintf(buf, titles[i], "Show mem free");
- break;
- case D_SHOWPADDLES:
- sprintf(buf, titles[i], "Show paddles");
- break;
- case D_SHOWPC:
- sprintf(buf, titles[i], "Show PC");
- break;
- case D_SHOWCYCLES:
- sprintf(buf, titles[i], "Show cycles");
- break;
- case D_SHOWBATTERY:
- sprintf(buf, titles[i], "Show battery");
- break;
- case D_SHOWTIME:
- sprintf(buf, titles[i], "Show time");
- break;
- }
- } else if (i == ACT_PRIMODE) {
- if (g_prioritizeDisplay)
- sprintf(buf, titles[i], "display");
- else
- sprintf(buf, titles[i], "r/t audio");
- } else {
- strcpy(buf, titles[i]);
- }
-
- if (isActionActive(i)) {
- g_display->drawString(selection == i ? M_SELECTED : M_NORMAL, 10, 20 + 14 * i, buf);
- } else {
- g_display->drawString(selection == i ? M_SELECTDISABLED : M_DISABLED, 10, 20 + 14 * i, buf);
- }
- }
-
- // draw the volume bar
- uint16_t volCutoff = 300.0 * (float)((float) g_volume / 15.0);
- for (uint8_t y=234; y<=235; y++) {
- ((TeensyDisplay *)g_display)->moveTo(10, y);
- for (uint16_t x = 0; x< 300; x++) {
- ((TeensyDisplay *)g_display)->drawNextPixel( x <= volCutoff ? 0xFFFF : 0x0010 );
- }
- }
-}
-
-
-// return true if the user selects an image
-// sets selectedFile (index; -1 = "nope") and fileDirectory[][] (names of up to BIOS_MAXFILES files)
-bool BIOS::SelectDiskImage()
-{
- int8_t sel = 0;
- int8_t page = 0;
-
- while (1) {
- DrawDiskNames(page, sel);
-
- while (!((TeensyKeyboard *)g_keyboard)->kbhit())
- ;
- switch (((TeensyKeyboard *)g_keyboard)->read()) {
- case DARR:
- sel++;
- sel %= BIOS_MAXFILES + 2;
- break;
- case UARR:
- sel--;
- if (sel < 0)
- sel = BIOS_MAXFILES + 1;
- break;
- case RET:
- if (sel == 0) {
- page--;
- if (page < 0) page = 0;
- // else sel = BIOS_MAXFILES + 1;
- }
- else if (sel == BIOS_MAXFILES+1) {
- page++;
- //sel = 0;
- } else {
- if (strcmp(fileDirectory[sel-1], "../") == 0) {
- // Go up a directory (strip a directory name from rootPath)
- stripDirectory();
- page = 0;
- //sel = 0;
- continue;
- } else if (fileDirectory[sel-1][strlen(fileDirectory[sel-1])-1] == '/') {
- // Descend in to the directory. FIXME: file path length?
- strcat(rootPath, fileDirectory[sel-1]);
- sel = 0;
- page = 0;
- continue;
- } else {
- selectedFile = sel - 1;
- return true;
- }
- }
- break;
- }
- }
-}
-
-void BIOS::stripDirectory()
-{
- rootPath[strlen(rootPath)-1] = '\0'; // remove the last character
-
- while (rootPath[0] && rootPath[strlen(rootPath)-1] != '/') {
- rootPath[strlen(rootPath)-1] = '\0'; // remove the last character again
- }
-
- // We're either at the previous directory, or we've nulled out the whole thing.
-
- if (rootPath[0] == '\0') {
- // Never go beyond this
- strcpy(rootPath, "/");
- }
-}
-
-void BIOS::DrawDiskNames(uint8_t page, int8_t selection)
-{
- uint8_t fileCount = GatherFilenames(page);
- ((TeensyDisplay *)g_display)->clrScr();
- g_display->drawString(M_NORMAL, 0, 12, "BIOS Configuration - pick disk");
-
- if (page == 0) {
- g_display->drawString(selection == 0 ? M_SELECTDISABLED : M_DISABLED, 10, 50, "");
- } else {
- g_display->drawString(selection == 0 ? M_SELECTED : M_NORMAL, 10, 50, "");
- }
-
- uint8_t i;
- for (i=0; idrawString((i == selection-1) ? M_SELECTED : M_NORMAL, 10, 50 + 14 * (i+1), fileDirectory[i]);
- } else {
- g_display->drawString((i == selection-1) ? M_SELECTDISABLED : M_DISABLED, 10, 50+14*(i+1), "-");
- }
-
- }
-
- // FIXME: this doesn't accurately say whether or not there *are* more.
- if (fileCount == BIOS_MAXFILES || fileCount == 0) {
- g_display->drawString((i+1 == selection) ? M_SELECTDISABLED : M_DISABLED, 10, 50 + 14 * (i+1), "");
- } else {
- g_display->drawString(i+1 == selection ? M_SELECTED : M_NORMAL, 10, 50 + 14 * (i+1), "");
- }
-}
-
-
-uint8_t BIOS::GatherFilenames(uint8_t pageOffset)
-{
- uint8_t startNum = 10 * pageOffset;
- uint8_t count = 0; // number we're including in our listing
-
- while (1) {
- char fn[BIOS_MAXPATH];
- int8_t idx = g_filemanager->readDir(rootPath, "dsk,.po,nib,img", fn, startNum + count, BIOS_MAXPATH);
-
- if (idx == -1) {
- return count;
- }
-
- idx++;
-
- strncpy(fileDirectory[count], fn, BIOS_MAXPATH);
- count++;
-
- if (count >= BIOS_MAXFILES) {
- return count;
- }
- }
-}
-
diff --git a/teensy/bios.cpp b/teensy/bios.cpp
new file mode 120000
index 0000000..1d17faf
--- /dev/null
+++ b/teensy/bios.cpp
@@ -0,0 +1 @@
+../bios.cpp
\ No newline at end of file
diff --git a/teensy/bios.h b/teensy/bios.h
deleted file mode 100644
index 67be37e..0000000
--- a/teensy/bios.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef __BIOS_H
-#define __BIOS_H
-
-#include
-
-#define BIOS_MAXFILES 10 // number of files in a page of listing
-#define BIOS_MAXPATH 40 // maximum length of a single filename that we'll support
-
-class BIOS {
- public:
- BIOS();
- ~BIOS();
-
- // return true if a persistent change needs to be stored in EEPROM
- bool runUntilDone();
-
- private:
- uint8_t GetAction(int8_t prevAction);
- bool isActionActive(int8_t action);
- void DrawMainMenu(int8_t selection);
-
- void WarmReset();
- void ColdReboot();
-
- bool SelectDiskImage();
- void DrawDiskNames(uint8_t page, int8_t selection);
- uint8_t GatherFilenames(uint8_t pageOffset);
-
- void stripDirectory();
-
- private:
- int8_t selectedFile;
- char fileDirectory[BIOS_MAXFILES][BIOS_MAXPATH+1];
-
- char rootPath[255-BIOS_MAXPATH];
-};
-
-#endif
diff --git a/teensy/bios.h b/teensy/bios.h
new file mode 120000
index 0000000..99545c4
--- /dev/null
+++ b/teensy/bios.h
@@ -0,0 +1 @@
+../bios.h
\ No newline at end of file
diff --git a/teensy/parallelsram.cpp b/teensy/parallelsram.cpp
new file mode 100644
index 0000000..032108a
--- /dev/null
+++ b/teensy/parallelsram.cpp
@@ -0,0 +1,218 @@
+#include "parallelsram.h"
+
+// Assumes any Output Enable pin is hardwired-enabled;
+// any Chip Enable pin is hardwared-enabled.
+//
+// Uses the low 8 bits of Port D as I/O lines (2, 14, 7, 8, 6, 20, 21, 5).
+//
+// R/W (aka WriteEnable) is on pin 31.
+
+#define RAM_RW 34
+
+// The Address pins (19 of them). It would be nice to have these
+// easily bitwise-manipulable, instead of having to set each bit
+// individually.
+//
+// We can use 12 bits of Port C: 15, 22, 23, 9, 10, 13, 11, 12, 35, 36, 37, 38
+// And then 6 bits of Port B: 16 17 19 18 49 50
+//
+// And hard wire one bit low (we don't need all 19 lines). That gets us
+// 256 Kb of RAM which should be sufficient.
+
+static uint8_t addrPins[] = { 15, 22, 23, 9, 10, 13, 11, 12, 35, 36, 37, 38,
+ 16, 17, 19, 18, 49, 50
+};
+
+#if 0
+#define DELAY { delayMicroseconds(1); /* overkill, but useful for debugging */ }
+#else
+#define DELAY { __asm__ volatile ("nop"); __asm__ volatile ("nop"); \
+__asm__ volatile ("nop"); __asm__ volatile ("nop"); \
+__asm__ volatile ("nop"); __asm__ volatile ("nop"); \
+__asm__ volatile ("nop"); __asm__ volatile ("nop"); \
+ }
+#endif
+
+#define OE_ON { /*if (noe != 255) {digitalWrite(noe, LOW);}*/ }
+#define OE_OFF { /*if (noe != 255) {digitalWrite(noe, HIGH);}*/ }
+
+#define CE_ON { /*if (n_ce != 255) {digitalWrite(n_ce, LOW);} if (p_ce != 255) { digitalWrite(p_ce, HIGH); }*/ }
+#define CE_OFF { /*if (n_ce != 255) {digitalWrite(n_ce, HIGH);} if (p_ce != 255) { digitalWrite(p_ce, LOW); }*/ }
+
+#define WE_ON { digitalWriteFast(RAM_RW, LOW); }
+#define WE_OFF { digitalWriteFast(RAM_RW, HIGH); }
+
+ParallelSRAM::ParallelSRAM()
+{
+ pinMode(RAM_RW, OUTPUT);
+
+ // Port D is our I/O port. Use the AVR emulation layer to set up the
+ // pins once, and then we'll just fiddle with the DDR, input, and
+ // output directly.
+
+ // Enable it as a digital port...
+ // SIM_SCGC5 |= SIM_SCGC5_PORTD;
+ //... what else? How do we set PORTD_PCR[0-7]?
+
+ pinMode(2, INPUT);
+ pinMode(14, INPUT);
+ pinMode(7, INPUT);
+ pinMode(8, INPUT);
+ pinMode(6, INPUT);
+ pinMode(20, INPUT);
+ pinMode(21, INPUT);
+ pinMode(5, INPUT);
+ isInput = true;
+
+ // Set up the address pins
+ for (int i=0; i> 12);
+
+#if 0
+ for (uint8_t i=0; i
+
+class ParallelSRAM {
+ public:
+ ParallelSRAM();
+ ~ParallelSRAM();
+
+ void SetPins();
+
+ uint8_t read(uint32_t addr);
+ void write(uint32_t addr, uint8_t v);
+
+ protected:
+ uint8_t getInput();
+ void setOutput(uint8_t v);
+ void setAddress(uint32_t addr);
+
+ private:
+ bool isInput;
+};
+
+#endif
diff --git a/teensy/teensy-display.cpp b/teensy/teensy-display.cpp
index 47ec5d3..bbd00cb 100644
--- a/teensy/teensy-display.cpp
+++ b/teensy/teensy-display.cpp
@@ -436,18 +436,31 @@ void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c)
}
temp=(c*ysize);
+
+ // FIXME: the embedded moveTo() and setPixel() calls *should* work
+ // -- and do, for the most part. But in the BIOS they cut off after
+ // about half the screen. Using drawPixel() is substantially less
+ // efficient, but works properly.
+
for (int8_t y_off = 0; y_off <= ysize; y_off++) {
- moveTo(x, y + y_off);
+ //moveTo(x, y + y_off); // does a cbi(P_CS, B_CS)
uint8_t ch = pgm_read_byte(&BiosFont[temp]);
for (int8_t x_off = 0; x_off <= xsize; x_off++) {
if (ch & (1 << (7-x_off))) {
- setPixel(onPixel);
+ drawPixel(x+x_off, y+y_off, onPixel);
+ //setPixel(onPixel);
} else {
- setPixel(offPixel);
+ drawPixel(x+x_off, y+y_off, offPixel);
+ //setPixel(offPixel);
}
}
temp++;
}
+
+ // Need to leave cbi set for the next draw operation. Particularly important
+ // on startup, when transitioning from '@' to 'Apple //e', while also drawing
+ // overlay text.
+ cbi(P_CS, B_CS);
}
void TeensyDisplay::drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str)
diff --git a/teensy/teensy-display.h b/teensy/teensy-display.h
index 685bc71..cba647c 100644
--- a/teensy/teensy-display.h
+++ b/teensy/teensy-display.h
@@ -7,13 +7,6 @@
#define TEENSY_DHEIGHT 240
#define TEENSY_DWIDTH 320
-enum {
- M_NORMAL = 0,
- M_SELECTED = 1,
- M_DISABLED = 2,
- M_SELECTDISABLED = 3
-};
-
#define regtype volatile uint8_t
#define regsize uint8_t
@@ -40,7 +33,8 @@ class TeensyDisplay : public PhysicalDisplay {
virtual void blit(AiieRect r);
virtual void redraw();
- void clrScr();
+ virtual void clrScr();
+ virtual void flush() {};
virtual void drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c);
virtual void drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str);
diff --git a/teensy/teensy-filemanager.cpp b/teensy/teensy-filemanager.cpp
index 8f895b9..0c5f87d 100644
--- a/teensy/teensy-filemanager.cpp
+++ b/teensy/teensy-filemanager.cpp
@@ -115,7 +115,7 @@ int8_t TeensyFileManager::readDir(const char *where, const char *suffix, char *o
// multiple suffixes to check - all must be 3 chars long, FIXME
bool matchesAny = false;
const char *p = suffix;
- while (p && strlen(p)) {
+ while (*p && strlen(p)) {
if (!strncasecmp(fsuff, p, 3)) {
matchesAny = true;
break;
@@ -383,3 +383,8 @@ bool TeensyFileManager::writeByte(int8_t fd, uint8_t v)
return true;
}
+void TeensyFileManager::getRootPath(char *toWhere, int8_t maxLen)
+{
+ strcpy(toWhere, "/A2DISKS/");
+ // strncpy(toWhere, "/A2DISKS/", maxLen);
+}
diff --git a/teensy/teensy-filemanager.h b/teensy/teensy-filemanager.h
index 0c91413..a2aff88 100644
--- a/teensy/teensy-filemanager.h
+++ b/teensy/teensy-filemanager.h
@@ -26,6 +26,8 @@ class TeensyFileManager : public FileManager {
virtual uint8_t readByte(int8_t fd);
virtual bool writeByte(int8_t fd, uint8_t v);
+
+ virtual void getRootPath(char *toWhere, int8_t maxLen);
private:
bool _prepCache(int8_t fd);
diff --git a/teensy/teensy-keyboard.cpp b/teensy/teensy-keyboard.cpp
index e6629e9..855543c 100644
--- a/teensy/teensy-keyboard.cpp
+++ b/teensy/teensy-keyboard.cpp
@@ -7,11 +7,11 @@ const byte ROWS = 5;
const byte COLS = 13;
char keys[ROWS][COLS] = {
- { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', DEL },
- { ESC, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']' },
- { _CTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', RET },
- { LSHFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', RSHFT, 0 },
- { LOCK, '`', TAB, '\\', LA, ' ', RA, LARR, RARR, DARR, UARR, 0, 0 }
+ { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', PK_DEL },
+ { PK_ESC, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']' },
+ { PK_CTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', PK_RET },
+ { PK_LSHFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', PK_RSHFT, 0 },
+ { PK_LOCK, '`', PK_TAB, '\\', PK_LA, ' ', PK_RA, PK_LARR, PK_RARR, PK_DARR, PK_UARR, 0, 0 }
};
uint8_t rowsPins[ROWS] = { 33, 34, 35, 36, 37 };
@@ -62,29 +62,29 @@ void TeensyKeyboard::pressedKey(uint8_t key)
if (key & 0x80) {
// it's a modifier key.
switch (key) {
- case _CTRL:
+ case PK_CTRL:
ctrlPressed = 1;
break;
- case LSHFT:
+ case PK_LSHFT:
leftShiftPressed = 1;
break;
- case RSHFT:
+ case PK_RSHFT:
rightShiftPressed = 1;
break;
- case LOCK:
+ case PK_LOCK:
capsLock = !capsLock;
break;
- case LA:
+ case PK_LA:
leftApplePressed = 1;
break;
- case RA:
+ case PK_RA:
rightApplePressed = 1;
break;
}
return;
}
- if (key == ' ' || key == DEL || key == ESC || key == RET || key == TAB) {
+ if (key == ' ' || key == PK_DEL || key == PK_ESC || key == PK_RET || key == PK_TAB) {
buffer.addByte(key);
return;
}
@@ -151,19 +151,19 @@ void TeensyKeyboard::releasedKey(uint8_t key)
if (key & 0x80) {
// it's a modifier key.
switch (key) {
- case _CTRL:
+ case PK_CTRL:
ctrlPressed = 0;
break;
- case LSHFT:
+ case PK_LSHFT:
leftShiftPressed = 0;
break;
- case RSHFT:
+ case PK_RSHFT:
rightShiftPressed = 0;
break;
- case LA:
+ case PK_LA:
leftApplePressed = 0;
break;
- case RA:
+ case PK_RA:
rightApplePressed = 0;
break;
}
diff --git a/teensy/teensy-keyboard.h b/teensy/teensy-keyboard.h
index 7a10d73..5edc24a 100644
--- a/teensy/teensy-keyboard.h
+++ b/teensy/teensy-keyboard.h
@@ -12,8 +12,8 @@ class TeensyKeyboard : public PhysicalKeyboard {
virtual void maintainKeyboard();
// Interface used by the BIOS...
- bool kbhit();
- int8_t read();
+ virtual bool kbhit();
+ virtual int8_t read();
private:
diff --git a/teensy/teensy.ino b/teensy/teensy.ino
index ad46bb0..a6747fc 100644
--- a/teensy/teensy.ino
+++ b/teensy/teensy.ino
@@ -12,9 +12,8 @@
#include "teensy-paddles.h"
#include "teensy-filemanager.h"
#include "appleui.h"
-
#define RESETPIN 39
-#define BATTERYPIN A19
+#define BATTERYPIN 32
#define SPEAKERPIN A21
#include "globals.h"
@@ -25,20 +24,8 @@ uint32_t startMicros;
BIOS bios;
-enum {
- D_NONE = 0,
- D_SHOWFPS = 1,
- D_SHOWMEMFREE = 2,
- D_SHOWPADDLES = 3,
- D_SHOWPC = 4,
- D_SHOWCYCLES = 5,
- D_SHOWBATTERY = 6,
- D_SHOWTIME = 7
-};
-uint8_t debugMode = D_NONE;
-bool g_prioritizeDisplay = false; // prioritize real-time audio by default, not the display
-
-#define SPEEDCTL 0.97751710654936461388 // that's how many microseconds per cycle @ 1.023 MHz
+// How many microseconds per cycle
+#define SPEEDCTL ((float)1000000/(float)g_speed)
static time_t getTeensy3Time() { return Teensy3Clock.get(); }
@@ -139,8 +126,8 @@ void setup()
// ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/JORJ/disk_s6d1.dsk", false);
// ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/GAMES/ALIBABA.DSK", false);
- pinMode(56, OUTPUT);
- pinMode(57, OUTPUT);
+ // pinMode(56, OUTPUT);
+ // pinMode(57, OUTPUT);
Serial.print("Free RAM: ");
Serial.println(FreeRamEstimate());
@@ -193,7 +180,7 @@ void biosInterrupt()
}
// if we turned off debugMode, make sure to clear the debugMsg
- if (debugMode == D_NONE) {
+ if (g_debugMode == D_NONE) {
g_display->debugMsg("");
}
@@ -216,8 +203,10 @@ void biosInterrupt()
//bool debugState = false;
//bool debugLCDState = false;
+
void runCPU()
{
+ g_inInterrupt = true;
// Debugging: to watch when the speaker is triggered...
// static bool debugState = false;
// debugState = !debugState;
@@ -229,9 +218,9 @@ void runCPU()
// directly from within it, so it needs to be real-ish time.
if (micros() > nextInstructionMicros) {
// Debugging: to watch when the CPU is triggered...
- static bool debugState = false;
- debugState = !debugState;
- digitalWrite(56, debugState);
+ // static bool debugState = false;
+ // debugState = !debugState;
+ // digitalWrite(56, debugState);
uint8_t executed = g_cpu->Run(24);
@@ -242,6 +231,8 @@ void runCPU()
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
}
+
+ g_inInterrupt = false;
}
void loop()
@@ -276,6 +267,7 @@ void loop()
// but the display tears. So there's a global - g_prioritizeDisplay -
// which lets the user pick which they want.
+ g_prioritizeDisplay = false;
if (g_prioritizeDisplay)
Timer1.stop();
g_vm->vmdisplay->lockDisplay();
@@ -288,13 +280,13 @@ void loop()
if (g_prioritizeDisplay)
Timer1.start();
- static unsigned long nextBattCheck = 0;
+ static unsigned long nextBattCheck = millis() + 30;// debugging
static int batteryLevel = 0; // static for debugging code! When done
// debugging, this can become a local
// in the appropriate block below
if (millis() >= nextBattCheck) {
// FIXME: what about rollover?
- nextBattCheck = millis() + 3 * 1000; // check every 30 seconds
+ nextBattCheck = millis() + 3 * 1000; // check every 3 seconds
// This is a bit disruptive - but the external 3.3v will drop along with the battery level, so we should use the more stable (I hope) internal 1.7v.
// The alternative is to build a more stable buck/boost regulator for reference...
@@ -316,7 +308,7 @@ void loop()
* 3.46v = 144 - 146
* 4.21v = 172
*/
-#if 1
+#if 0
Serial.print("battery: ");
Serial.println(batteryLevel);
#endif
@@ -334,7 +326,7 @@ void loop()
void doDebugging()
{
char buf[25];
- switch (debugMode) {
+ switch (g_debugMode) {
case D_SHOWFPS:
// display some FPS data
static uint32_t startAt = millis();
diff --git a/vmdisplay.h b/vmdisplay.h
index 736472a..cf12a9c 100644
--- a/vmdisplay.h
+++ b/vmdisplay.h
@@ -1,6 +1,8 @@
#ifndef __VMDISPLAY_H
#define __VMDISPLAY_H
+#include
+
class MMU;
typedef struct {
diff --git a/vmram.cpp b/vmram.cpp
index 655854b..f0f1182 100644
--- a/vmram.cpp
+++ b/vmram.cpp
@@ -1,3 +1,7 @@
+#ifdef TEENSYDUINO
+#include
+#endif
+
#include "vmram.h"
#include
#include "globals.h"
@@ -5,7 +9,8 @@
#ifndef TEENSYDUINO
#include
#else
-#define assert(x)
+#define assert(x) { if (!(x)) {Serial.print("assertion failed at "); Serial.println(__LINE__); delay(10000);} }
+//#define assert(x) { }
#endif
// Serializing token for RAM data
@@ -22,9 +27,15 @@ void VMRam::init()
}
}
-uint8_t VMRam::readByte(uint32_t addr) { assert(addr < sizeof(preallocatedRam)); return preallocatedRam[addr]; }
+uint8_t VMRam::readByte(uint32_t addr)
+{
+ return preallocatedRam[addr];
+}
-void VMRam::writeByte(uint32_t addr, uint8_t value) { assert(addr < sizeof(preallocatedRam)); preallocatedRam[addr] = value; }
+void VMRam::writeByte(uint32_t addr, uint8_t value)
+{
+ preallocatedRam[addr] = value;
+}
bool VMRam::Serialize(int8_t fd)
{
@@ -73,3 +84,8 @@ bool VMRam::Deserialize(int8_t fd)
return true;
}
+
+bool VMRam::Test()
+{
+ return true;
+}
diff --git a/vmram.h b/vmram.h
index a3ddd29..4b2cd46 100644
--- a/vmram.h
+++ b/vmram.h
@@ -18,8 +18,32 @@ class VMRam {
bool Serialize(int8_t fd);
bool Deserialize(int8_t fd);
+ bool Test();
+
private:
- uint8_t preallocatedRam[591*256]; // 591 pages of RAM
+ // We need 591 pages of 256 bytes for the //e. There's not
+ // enough RAM in the Teensy 3.6 for both this (nearly 148k)
+ // and the display's DMA (320*240*2 = 150k).
+ //
+ // We could put all of the //e RAM in an external SRAM -- but the
+ // external SRAM access is necessarily slower than just reading the
+ // built-in RAM. So this is a hybrid: we allocate some internal
+ // SRAM from the Teensy, and will use it for the low addresses of
+ // our VM space; and anything above that goes to the external SRAM.
+ //
+ // Changing this invalidates the save files, so don't just change it
+ // willy-nilly :)
+ //
+ // Zero-page should be in internal RAM (it's changed very often). Some
+ // other pages that are read or written often should probably go in
+ // here too. The order of the pages (in apple/applemmu.cpp) defines
+ // what order the pages are referenced in the VMRam object; the lowest
+ // wind up in internal RAM.
+
+ // Pages 0-3 are ZP; we want those in RAM.
+ // Pages 4-7 are 0x200 - 0x3FF. We want those in RAM too (text pages).
+
+ uint8_t preallocatedRam[591*256];
};