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]; };