diff --git a/Makefile b/Makefile index 9780a3a..54b22c6 100755 --- a/Makefile +++ b/Makefile @@ -9,9 +9,9 @@ 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 bios.o apple/noslotclock.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 +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 nix/nix-prefs.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 +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 nix/nix-prefs.o nix/debugger.o nix/disassembler.o ROMS=apple/applemmu-rom.h apple/diskii-rom.h apple/parallel-rom.h apple/hd32-rom.h diff --git a/apple/appledisplay.cpp b/apple/appledisplay.cpp index 211579c..1494684 100644 --- a/apple/appledisplay.cpp +++ b/apple/appledisplay.cpp @@ -10,10 +10,13 @@ BLACKANDWHITE: monochrome, but use B&W instead of B&G; NTSCLIKE: reduce the resolution to 140 pixels wide, similar to how an NTSC monitor would blend it PERFECTCOLOR: as the Apple RGB monitor shows it, which means you can't have a solid color field + + The only two we have to worry about here are NTSCLIKE and PERFECTCOLOR. The mono and B&W modes + are handled in the individual display drivers, where colors are changed to one or the other. + The NTSCLIKE and PERFECTCOLOR modes change which actual pixels are set on or off, though, + and that's a quirk specific to the Apple 2... */ - - #define extendDirtyRect(x,y) { \ if (!dirty) { \ dirtyRect.left = x; \ @@ -37,39 +40,9 @@ } \ } -#if DISPLAYRUN == 512 - -#define drawApplePixel(c, x, y) { \ - uint16_t idx = (((y) << 9) + (x)) >> 1; \ - if ((x) & 1) { \ - videoBuffer[idx] = (videoBuffer[idx] & 0xF0) | (c); \ - } else { \ - videoBuffer[idx] = (videoBuffer[idx] & 0x0F) | ((c) << 4); \ - } \ - } - -#define draw2Pixels(cAB, x, y) { \ - videoBuffer[(((y) <<9) + (x)) >> 1] = cAB; \ - } - - -#else - -#define drawApplePixel(c, x, y) { \ - uint16_t idx = ((y) * DISPLAYRUN + (x)) / 2; \ - if ((x) & 1) { \ - videoBuffer[idx] = (videoBuffer[idx] & 0xF0) | (c); \ - } else { \ - videoBuffer[idx] = (videoBuffer[idx] & 0x0F) | ((c) << 4); \ - } \ - } - -#define draw2Pixels(cAB, x, y) { \ - videoBuffer[((y) * DISPLAYRUN + (x)) /2] = cAB; \ - } - -#endif +#define drawApplePixel(c,x,y) { g_display->cacheDoubleWidePixel(x,y,c); } +#define draw2Pixels(cA, cB, x, y) { g_display->cache2DoubleWidePixels(x,y,cA, cB); } #define DrawLoresPixelAt(c, x, y) { \ uint8_t pixel = c & 0x0F; \ @@ -88,11 +61,10 @@ #include "globals.h" -AppleDisplay::AppleDisplay(uint8_t *vb) : VMDisplay(vb) +AppleDisplay::AppleDisplay() : VMDisplay() { this->switches = NULL; - textColor = g_displayType == m_monochrome?c_green:c_white; modeChange(); } @@ -234,19 +206,36 @@ inline void AppleDisplay::Draw14DoubleHiresPixelsAt(uint16_t addr) bitTrain <<= 7; bitTrain |= (b1B & 0x7F); - // Now we pop groups of 4 bits off the bottom and draw our - // NTSC-style-only color. The display for this project only has - // 320 columns, so it's silly to try to do 560 columns of - // monochrome; and likewise, we can't do "perfect" representation - // of shifted color pixels. So NTSC it is, and we'll draw two screen - // pixels for every color. + // Now we pop groups of 4 bits off the bottom and draw. for (int8_t xoff = 0; xoff < 14; xoff += 2) { - drawApplePixel(bitTrain & 0x0F, col+xoff, row); - drawApplePixel(bitTrain & 0x0F, col+xoff+1, row); + if (g_displayType == m_ntsclike) { + // NTSC-like color - use drawApplePixel to show the messy NTSC color bleeds. + // This draws two doubled pixels with greater color, but lower pixel, resolution. + drawApplePixel(bitTrain & 0x0F, col+xoff, row); + drawApplePixel(bitTrain & 0x0F, col+xoff+1,row); + } else { + // Perfect color, B&W, monochrome. Draw an exact version of the pixels, and let + // the physical display figure out if they need to be reduced to B&W or not. + + uint8_t color = bitTrain & 0x0F; + + g_display->cachePixel((col*2)+(xoff*2), row, + ((bitTrain & 0x01) ? color : c_black)); + + g_display->cachePixel((col*2)+(xoff*2)+1, row, + ((bitTrain & 0x02) ? color : c_black)); + + g_display->cachePixel((col*2)+(xoff*2)+2, row, + ((bitTrain & 0x04 )? color : c_black)); + + g_display->cachePixel((col*2)+(xoff*2)+3, row, + ((bitTrain & 0x08 ) ? color : c_black)); + } bitTrain >>= 4; - } + } // for + } } @@ -310,15 +299,7 @@ inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr) for (int8_t xoff = 0; xoff < 14; xoff += 2) { - if (g_displayType == m_monochrome) { - draw2Pixels(((bitTrain & 0x01 ? c_green : c_black) << 4) | - (bitTrain & 0x02 ? c_green : c_black), - col+xoff, row); - } else if (g_displayType == m_blackAndWhite) { - draw2Pixels(((bitTrain & 0x01 ? c_white : c_black) << 4) | - (bitTrain & 0x02 ? c_white : c_black), - col+xoff, row); - } else if (g_displayType == m_ntsclike) { + if (g_displayType == m_ntsclike) { // Use the NTSC-like color mode, where we're only 140 pixels wide. bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne); @@ -338,7 +319,7 @@ inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr) break; } - draw2Pixels( (color << 4) | color, col+xoff, row ); + draw2Pixels( color, color, col+xoff, row ); } else { // Use the "perfect" color mode, like the Apple RGB monitor showed. bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne); @@ -357,22 +338,10 @@ inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr) color = c_white; break; } - - uint16_t twoColors; - - if (color == c_black || color == c_white || bitTrain & 0x01) { - twoColors = color; - } else { - twoColors = c_black; - } - twoColors <<= 4; - - if (color == c_black || color == c_white || bitTrain & 0x02) { - twoColors |= color; - } else { - twoColors |= c_black; - } - draw2Pixels(twoColors, col+xoff, row); + + draw2Pixels( (color==c_white || (bitTrain & 0x02)) ? color : c_black, + (color==c_white || (bitTrain & 0x01)) ? color : c_black, + col+xoff, row ); } bitTrain >>= 2; } @@ -402,32 +371,23 @@ void AppleDisplay::redraw80ColumnText(uint8_t startingY) // Only draw onscreen locations if (row >= startingY && col <= 39 && row <= 23) { // Even characters are in bank 0 ram. Odd characters are in bank - // 1 ram. Technically, this would need 560 columns to work - // correctly - and I don't have that, so it's going to be a bit - // wonky. - // - // First pass: draw two pixels on top of each other, clearing - // only if both are black. This would be blocky but probably - // passable if it weren't for the fact that characters are 7 - // pixels wide, so we wind up sharing a half-pixel between two - // characters. So we'll render these as 3-pixel-wide characters - // and make sure they always even-align the drawing on the left - // side so we don't overwrite every other one on the left or - // right side. + // 1 ram. Draw to the physical display and let it figure out + // whether or not there are enough physical pixels to display + // the 560 columns we'd need for this. // Draw the first of two characters cptr = xlateChar(mmu->readDirect(addr, 1), &invert); for (uint8_t y2 = 0; y2<8; y2++) { uint8_t d = *(cptr + y2); - for (uint8_t x2 = 0; x2 <= 7; x2+=2) { - uint16_t basex = ((col * 2) * 7) & 0xFFFE; // even aligned - bool pixelOn = ( (d & (1<cachePixel(basex + x2, row*8+y2, val); } else { - uint8_t val = (invert ? textColor : c_black); - drawApplePixel(val, (basex+x2)/2, row*8+y2); + uint8_t val = (invert ? c_white : c_black); + g_display->cachePixel(basex + x2, row*8+y2, val); } } } @@ -436,15 +396,15 @@ void AppleDisplay::redraw80ColumnText(uint8_t startingY) cptr = xlateChar(mmu->readDirect(addr, 0), &invert); for (uint8_t y2 = 0; y2<8; y2++) { uint8_t d = *(cptr + y2); - for (uint8_t x2 = 0; x2 <= 7; x2+=2) { - uint16_t basex = ((col * 2 + 1) * 7) & 0xFFFE; // even aligned -- +1 for the second character - bool pixelOn = ( (d & (1<cachePixel(basex + x2, row*8+y2, val); } else { - uint8_t val = (invert ? textColor : c_black); - drawApplePixel(val, (basex+x2)/2, row*8+y2); + uint8_t val = (invert ? c_white : c_black); + g_display->cachePixel(basex + x2, row*8+y2, val); } } } @@ -478,10 +438,10 @@ void AppleDisplay::redraw40ColumnText(uint8_t startingY) uint8_t d = *(cptr + y2); for (uint8_t x2 = 0; x2 < 7; x2++) { if (d & 1) { - uint8_t val = (invert ? c_black : textColor); + uint8_t val = (invert ? c_black : c_white); drawApplePixel(val, col*7+x2, row*8+y2); } else { - uint8_t val = (invert ? textColor : c_black); + uint8_t val = (invert ? c_white : c_black); drawApplePixel(val, col*7+x2, row*8+y2); } d >>= 1; @@ -646,7 +606,6 @@ void AppleDisplay::didRedraw() void AppleDisplay::displayTypeChanged() { - textColor = g_displayType == m_monochrome?c_green:c_white; modeChange(); } diff --git a/apple/appledisplay.h b/apple/appledisplay.h index 59fe843..30b62f9 100644 --- a/apple/appledisplay.h +++ b/apple/appledisplay.h @@ -39,7 +39,7 @@ class AppleMMU; class AppleDisplay : public VMDisplay{ public: - AppleDisplay(uint8_t *vb); + AppleDisplay(); virtual ~AppleDisplay(); virtual bool needsRedraw(); virtual void didRedraw(); @@ -76,8 +76,6 @@ class AppleDisplay : public VMDisplay{ AiieRect dirtyRect; uint16_t *switches; // pointer to the MMU's switches - - uint16_t textColor; }; #endif diff --git a/apple/appleui.cpp b/apple/appleui.cpp index af397c8..240a6ce 100644 --- a/apple/appleui.cpp +++ b/apple/appleui.cpp @@ -9,6 +9,11 @@ AppleUI::AppleUI() { + redrawFrame = false; + redrawDriveLatches = false; + redrawDriveActivity = false; + driveInserted[0] = driveInserted[1] = 0; + driveActivity[0] = driveActivity[1] = 0; } AppleUI::~AppleUI() @@ -20,58 +25,21 @@ void AppleUI::drawStaticUIElement(uint8_t element) // Only one static UI element right now... if (element != UIeOverlay) return; - - g_display->drawImageOfSizeAt(displayBitmap, DBITMAP_WIDTH, DBITMAP_HEIGHT, 0, 0); + redrawFrame = true; } void AppleUI::drawOnOffUIElement(uint8_t element, bool state) { - uint16_t xoff = 55; - uint8_t yoff = 216; - uint16_t xsize; - uint8_t ysize; - const uint8_t *img; - - switch (element) { - case UIeDisk1_state: - xoff = 55; - yoff = 216; - xsize = 43; - ysize = 20; - img = state ? driveLatchOpen : driveLatch; - break; - case UIeDisk2_state: - xoff = 55+134; - yoff = 216; - xsize = 43; - ysize = 20; - img = state ? driveLatchOpen : driveLatch; - break; - case UIeDisk1_activity: - case UIeDisk2_activity: - { - uint16_t xoff = 125; - uint16_t yoff = 213; - if (element == UIeDisk2_activity) - xoff += 135; - for (int x=0; x<6; x++) { - // 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); - } - } - - return; - default: - return; + if (element == UIeDisk1_state || + element == UIeDisk2_state) { + driveInserted[element-UIeDisk1_state] = state; + redrawDriveLatches = true; + } + else if (element == UIeDisk1_activity || + element == UIeDisk2_activity) { + driveActivity[element-UIeDisk1_activity] = state; + redrawDriveActivity = true; } - - g_display->drawImageOfSizeAt(img, xsize, ysize, xoff, yoff); } void AppleUI::drawPercentageUIElement(uint8_t element, uint8_t percent) @@ -80,12 +48,13 @@ void AppleUI::drawPercentageUIElement(uint8_t element, uint8_t percent) if (element != UIePowerPercentage) { return; } - drawBatteryStatus(percent); + // Temporarily disabled; the API for this needs updating for resolution-independent display coordinates + // drawBatteryStatus(percent); } void AppleUI::drawBatteryStatus(uint8_t percent) { - uint16_t xoff = 301; + uint16_t xoff = 301*2; uint16_t yoff = 222; // the area around the apple is 12 wide; it's exactly 11 high the @@ -129,5 +98,47 @@ void AppleUI::drawBatteryStatus(uint8_t percent) } } +void AppleUI::blit() +{ + if (redrawFrame) { + redrawFrame = false; + g_display->drawImageOfSizeAt(displayBitmap, DBITMAP_WIDTH, DBITMAP_HEIGHT, 0, 0); + } + + if (redrawDriveLatches) { + redrawDriveLatches = false; + uint16_t xoff = 55; + uint8_t yoff = 216; + uint16_t xsize; + uint8_t ysize; + const uint8_t *img; + + xsize = 43; + ysize = 20; + img = driveInserted[0] ? driveLatchOpen : driveLatch; + g_display->drawImageOfSizeAt(img, xsize, ysize, xoff, yoff); + + xoff += 134; + img = driveInserted[1] ? driveLatchOpen : driveLatch; + g_display->drawImageOfSizeAt(img, xsize, ysize, xoff, yoff); + } + + if (redrawDriveActivity) { + redrawDriveActivity = false; + + uint16_t xoff = 125; + uint8_t yoff = 213; + + for (int x=0; x<6; x++) { + g_display->drawUIPixel(x + xoff, yoff, driveActivity[0] ? 0xF800 : 0x8AA9); + g_display->drawUIPixel(x + xoff, yoff + 1, driveActivity[0] ? 0xF800 : 0x8AA9); + + g_display->drawUIPixel(x + xoff + 135, yoff, driveActivity[1] ? 0xF800 : 0x8AA9); + g_display->drawUIPixel(x + xoff + 135, yoff + 1, driveActivity[1] ? 0xF800 : 0x8AA9); + } + } + +} + diff --git a/apple/appleui.h b/apple/appleui.h index f12be97..c6ff11e 100644 --- a/apple/appleui.h +++ b/apple/appleui.h @@ -23,6 +23,15 @@ class AppleUI : public VMui { virtual void drawPercentageUIElement(uint8_t element, uint8_t percent); void drawBatteryStatus(uint8_t percent); + + virtual void blit(); + + private: + bool redrawFrame; + bool redrawDriveLatches; + bool redrawDriveActivity; + bool driveInserted[2]; + bool driveActivity[2]; }; diff --git a/apple/applevm.cpp b/apple/applevm.cpp index bf0a382..62c2e81 100644 --- a/apple/applevm.cpp +++ b/apple/applevm.cpp @@ -14,7 +14,7 @@ const char *suspendHdr = "Sus1"; AppleVM::AppleVM() { // FIXME: all this typecasting makes me knife-stabby - vmdisplay = new AppleDisplay(videoBuffer); + vmdisplay = new AppleDisplay(); mmu = new AppleMMU((AppleDisplay *)vmdisplay); vmdisplay->SetMMU((AppleMMU *)mmu); diff --git a/bios.cpp b/bios.cpp index 35592d8..f366d75 100644 --- a/bios.cpp +++ b/bios.cpp @@ -29,7 +29,7 @@ enum { #define NUM_TITLES 4 const char *menuTitles[NUM_TITLES] = { "Aiie", "VM", "Hardware", "Disks" }; -const uint8_t titleWidths[NUM_TITLES] = {45 , 28, 80, 45 }; +const uint8_t titleWidths[NUM_TITLES] = {45, 28, 80, 45 }; const uint8_t aiieActions[] = { ACT_ABOUT }; @@ -117,7 +117,6 @@ bool BIOS::runUntilDone() currentCPUSpeedIndex = CPUSPEED_QUAD; int8_t prevAction = ACT_EXIT; - bool volumeDidChange = 0; while (1) { switch (prevAction = GetAction(prevAction)) { case ACT_EXIT: @@ -141,7 +140,14 @@ bool BIOS::runUntilDone() break; case ACT_SPEED: currentCPUSpeedIndex++; +#ifdef TEENSYDUINO + // The Teensy doesn't have any overhead to spare. Allow slowing + // down the virtual CPU, but not speeding it up... + currentCPUSpeedIndex %= 2; +#else + // Other variants can support double and quad speeds. currentCPUSpeedIndex %= 4; +#endif switch (currentCPUSpeedIndex) { case CPUSPEED_HALF: g_speed = 1023000/2; @@ -209,14 +215,12 @@ bool BIOS::runUntilDone() 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: @@ -243,7 +247,7 @@ bool BIOS::runUntilDone() g_display->blit(r); // return true if any persistent setting changed that we want to store in eeprom - return volumeDidChange; + return true; } void BIOS::WarmReset() diff --git a/cpu.cpp b/cpu.cpp index 294d81b..fb4bffc 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -29,117 +29,6 @@ // serialize suspend/restore token #define CPUMAGIC 0x65 -enum optype { - O_ILLEGAL, - O_ADC, - O_AND, - O_ASL, - O_ASL_ACC, - O_BCC, - O_BCS, - O_BEQ, - O_BIT, - O_BMI, - O_BNE, - O_BPL, - O_BRA, - O_BRK, - O_BVC, - O_BVS, - O_CLC, - O_CLD, - O_CLI, - O_CLV, - O_CMP, - O_CPX, - O_CPY, - O_DEC, - O_DEC_ACC, - O_DEX, - O_DEY, - O_EOR, - O_INC, - O_INC_ACC, - O_INX, - O_INY, - O_JMP, - O_JSR, - O_LDA, - O_LDX, - O_LDY, - O_LSR, - O_LSR_ACC, - O_NOP, - O_ORA, - O_PHA, - O_PHP, - O_PHX, - O_PHY, - O_PLA, - O_PLP, - O_PLX, - O_PLY, - O_ROL, - O_ROL_ACC, - O_ROR, - O_ROR_ACC, - O_RTI, - O_RTS, - O_SBC, - O_SEC, - O_SED, - O_SEI, - O_STA, - O_STX, - O_STY, - O_STZ, - O_TAX, - O_TAY, - O_TRB, - O_TSB, - O_TSX, - O_TXA, - O_TXS, - O_TYA, - - O_BBR, - O_BBS, - O_RMB, - O_SMB, - - // and the "illegal" opcodes (those that don't officially exist for - // the 65c02, but have repeatable results) - O_DCP -}; - -// accumulator mode is implied -#define A_ACC A_IMP - -enum addrmode { - A_ILLEGAL, - A_IMM, - A_ABS, - A_ZER, - A_IMP, - A_REL, - A_ABI, - A_ZEX, - A_ZEY, - A_ZIND, - A_ABX, - A_ABXI, - A_ABY, - A_INX, - A_INY, - A_ZPREL -}; - -typedef struct { - optype op; - addrmode mode; - uint8_t cycles; -} optype_t; - optype_t opcodes[256] = { { O_BRK, A_IMP, 7 }, // 0x00 { O_ORA , A_INX , 6 }, // 0x01 [2] i.e. "ORA ($44,X)" @@ -345,7 +234,7 @@ optype_t opcodes[256] = { { O_INY , A_IMP , 2 }, // 0xC8 { O_CMP , A_IMM , 2 }, // 0xC9 { O_DEX , A_IMP , 2 }, // 0xCA - { O_ILLEGAL, A_ILLEGAL, 2 }, // 0xCB + { O_WAI , A_IMP , 2 }, // 0xCB { O_CPY , A_ABS , 4 }, // 0xCC { O_CMP , A_ABS , 4 }, // 0xCD { O_DEC , A_ABS , 6 }, // 0xCE @@ -630,6 +519,7 @@ uint8_t Cpu::step() // treat these as IMPLIED break; case A_IMP: + case A_ACC: // implied: nothing to do. These have a parameter that refers to a // specific register or particular action to a register break; @@ -847,6 +737,7 @@ uint8_t Cpu::step() SETNZY; break; case O_NOP: + case O_WAI: break; case O_TAX: x = a; diff --git a/cpu.h b/cpu.h index 0aea6ae..d1eec8f 100644 --- a/cpu.h +++ b/cpu.h @@ -6,6 +6,119 @@ class MMU; +enum addrmode { + A_ILLEGAL, + A_IMM, + A_ABS, + A_ZER, + A_IMP, + A_ACC, + A_REL, + A_ABI, + A_ZEX, + A_ZEY, + A_ZIND, + A_ABX, + A_ABXI, + A_ABY, + A_INX, + A_INY, + A_ZPREL +}; + +enum optype { + O_ILLEGAL, + O_ADC, + O_AND, + O_ASL, + O_ASL_ACC, + O_BCC, + O_BCS, + O_BEQ, + O_BIT, + O_BMI, + O_BNE, + O_BPL, + O_BRA, + O_BRK, + O_BVC, + O_BVS, + O_CLC, + O_CLD, + O_CLI, + O_CLV, + O_CMP, + O_CPX, + O_CPY, + O_DEC, + O_DEC_ACC, + O_DEX, + O_DEY, + O_EOR, + O_INC, + O_INC_ACC, + O_INX, + O_INY, + O_JMP, + O_JSR, + O_LDA, + O_LDX, + O_LDY, + O_LSR, + O_LSR_ACC, + O_NOP, + O_ORA, + O_PHA, + O_PHP, + O_PHX, + O_PHY, + O_PLA, + O_PLP, + O_PLX, + O_PLY, + O_ROL, + O_ROL_ACC, + O_ROR, + O_ROR_ACC, + O_RTI, + O_RTS, + O_SBC, + O_SEC, + O_SED, + O_SEI, + O_STA, + O_STX, + O_STY, + O_STZ, + O_TAX, + O_TAY, + O_TRB, + O_TSB, + O_TSX, + O_TXA, + O_TXS, + O_TYA, + + O_BBR, + O_BBS, + O_RMB, + O_SMB, + + O_WAI, + + // and the "illegal" opcodes (those that don't officially exist for + // the 65c02, but have repeatable results) + O_DCP +}; + +typedef struct { + optype op; + addrmode mode; + uint8_t cycles; +} optype_t; + +extern optype_t opcodes[256]; + // Flags (P) register bit definitions. // Negative #define F_N (1<<7) diff --git a/globals.cpp b/globals.cpp index 13db554..a64de20 100644 --- a/globals.cpp +++ b/globals.cpp @@ -9,7 +9,7 @@ PhysicalSpeaker *g_speaker = NULL; PhysicalPaddles *g_paddles = NULL; PhysicalPrinter *g_printer = NULL; VMui *g_ui; -int16_t g_volume = 15; +int8_t g_volume = 15; uint8_t g_displayType = 3; // FIXME m_perfectcolor VMRam g_ram; volatile bool g_inInterrupt = false; diff --git a/globals.h b/globals.h index bce8e5e..67bec83 100644 --- a/globals.h +++ b/globals.h @@ -1,3 +1,6 @@ +#ifndef __GLOBALS_H +#define __GLOBALS_H + #include #include "filemanager.h" @@ -40,7 +43,7 @@ extern PhysicalSpeaker *g_speaker; extern PhysicalPaddles *g_paddles; extern PhysicalPrinter *g_printer; extern VMui *g_ui; -extern int16_t g_volume; +extern int8_t g_volume; extern uint8_t g_displayType; extern VMRam g_ram; extern volatile bool g_inInterrupt; @@ -48,3 +51,5 @@ extern volatile uint8_t g_debugMode; extern bool g_prioritizeDisplay; extern volatile bool g_biosInterrupt; extern uint32_t g_speed; + +#endif diff --git a/linuxfb/aiie.cpp b/linuxfb/aiie.cpp index 8e74495..f1bf72c 100644 --- a/linuxfb/aiie.cpp +++ b/linuxfb/aiie.cpp @@ -15,6 +15,7 @@ #include "linux-printer.h" #include "appleui.h" #include "bios.h" +#include "nix-prefs.h" #include "globals.h" @@ -43,6 +44,8 @@ volatile bool wantSuspend = false; volatile bool wantResume = false; void doDebugging(); +void readPrefs(); +void writePrefs(); void sigint_handler(int n) { @@ -353,6 +356,9 @@ int main(int argc, char *argv[]) g_display->redraw(); + /* Load prefs & reset globals appropriately now */ + readPrefs(); + if (argc >= 2) { printf("Inserting disk %s\n", argv[1]); ((AppleVM *)g_vm)->insertDisk(0, argv[1]); @@ -385,7 +391,7 @@ int main(int argc, char *argv[]) if (bios.runUntilDone()) { // if it returned true, we have something to store // persistently in EEPROM. - // writePrefs(); + writePrefs(); } printf("BIOS done\n"); @@ -419,8 +425,7 @@ int main(int argc, char *argv[]) // fill disk buffer when needed ((AppleVM*)g_vm)->disk6->fillDiskBuffer(); - // Make this a little friendlier, and the expense of some framerate? - // usleep(10000); + g_ui->blit(); if (g_vm->vmdisplay->needsRedraw()) { AiieRect what = g_vm->vmdisplay->getDirtyRect(); // make sure to clear the flag before drawing; there's no lock @@ -538,3 +543,57 @@ void doDebugging() break;*/ } } + +void readPrefs() +{ + NixPrefs np; + prefs_t p; + if (np.readPrefs(&p)) { + g_volume = p.volume; + g_displayType = p.displayType; + g_debugMode = p.debug; + g_prioritizeDisplay = p.priorityMode; + g_speed = (p.speed * (1023000/2)); // steps of half normal speed + if (g_speed < (1023000/2)) + g_speed = (1023000/2); + if (p.disk1[0]) { + ((AppleVM *)g_vm)->insertDisk(0, p.disk1); + strcpy(disk1name, p.disk1); + } + if (p.disk2[0]) { + ((AppleVM *)g_vm)->insertDisk(1, p.disk2); + strcpy(disk2name, p.disk2); + } + + if (p.hd1[0]) { + ((AppleVM *)g_vm)->insertHD(0, p.hd1); + } + + if (p.hd2[0]) { + ((AppleVM *)g_vm)->insertHD(1, p.hd2); + } + } +} + +void writePrefs() +{ + NixPrefs np; + prefs_t p; + + p.magic = PREFSMAGIC; + p.prefsSize = sizeof(prefs_t); + p.version = PREFSVERSION; + + p.volume = g_volume; + p.displayType = g_displayType; + p.debug = g_debugMode; + p.priorityMode = g_prioritizeDisplay; + p.speed = g_speed / (1023000/2); + strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0)); + strcpy(p.disk2, ((AppleVM *)g_vm)->DiskName(1)); + strcpy(p.hd1, ((AppleVM *)g_vm)->HDName(0)); + strcpy(p.hd2, ((AppleVM *)g_vm)->HDName(1)); + + bool ret = np.writePrefs(&p); + printf("writePrefs returns %s\n", ret ? "true" : "false"); +} diff --git a/linuxfb/fb-display.cpp b/linuxfb/fb-display.cpp index 3fe2ccd..14b76c4 100644 --- a/linuxfb/fb-display.cpp +++ b/linuxfb/fb-display.cpp @@ -13,6 +13,9 @@ #include "apple/appleui.h" +#define SCREENINSET_X (18*2) +#define SCREENINSET_Y (13*2) + // RGB map of each of the lowres colors const uint16_t loresPixelColors[16] = { 0x0000, // 0 black 0xC006, // 1 magenta @@ -34,6 +37,8 @@ const uint16_t loresPixelColors[16] = { 0x0000, // 0 black FBDisplay::FBDisplay() { + memset((void *)videoBuffer, 0, sizeof(videoBuffer)); + fb_fd = open("/dev/fb0",O_RDWR); //Get variable screen information ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo); @@ -44,12 +49,11 @@ FBDisplay::FBDisplay() 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"); + system("fbset -xres 640 -yres 480 -depth 16"); // request what we want, rather than hoping we got it - // 16bpp 320x240 (for now) - vinfo.width = 320; - vinfo.height = 240; + vinfo.width = 640; + vinfo.height = 480; vinfo.bits_per_pixel = 16; int ret = ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo); printf("Return from FBIOPUT_VSCREENINFO: %d\n", ret); @@ -71,17 +75,18 @@ 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"); } +// drawImageOfSizeAt will horizontally scale out the image b/c the +// images themselves aren't aware of the double resolution. This is an +// inconsistency that probably should be addressed. FIXME? void FBDisplay::drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey) @@ -89,29 +94,49 @@ void FBDisplay::drawImageOfSizeAt(const uint8_t *img, for (uint8_t y=0; yvideoBuffer; // FIXME: poking deep + for (uint16_t y=r.top*2; y> 4; + uint16_t color; + color = loresPixelColors[colorIdx]; + + if (g_displayType == m_monochrome) { + // Tricky. Grayscale from luminance. Turn each value into a + // 5-bit value, so they have equal weights; then calculate + // luminance; then turn that back in to 5/6/5. + float fv = (0.2125 * ((color & 0xF800) >> 11)); + fv += (0.7154 * ((color & 0x07E0) >> 6)); // 6 bits of green + // turned in to 5 + // bits + fv += (0.0721 * ((color & 0x001F))); + + color = ((uint16_t)fv << 6); } - long location = (x+vinfo.xoffset+BASEX) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset+BASEY) * finfo.line_length; - *((uint16_t*)(fbp + location)) = loresPixelColors[colorIdx]; + else if (g_displayType == m_blackAndWhite) { + // Tricky. Grayscale from luminance. Turn each value into a + // 5-bit value, so they have equal weights; then calculate + // luminance; then turn that back in to 5/6/5. + float fv = (0.2125 * ((color & 0xF800) >> 11)); + fv += (0.7154 * ((color & 0x07E0) >> 6)); // 6 bits of green + // turned in to 5 + // bits + fv += (0.0721 * ((color & 0x001F))); + + color = ((uint16_t)fv << 11) | ((uint16_t)fv << 6) | ((uint16_t)fv); + } + + + long location = (x+vinfo.xoffset+SCREENINSET_X) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset+SCREENINSET_Y) * finfo.line_length; + *((uint16_t*)(fbp + location)) = color; } } @@ -128,17 +153,27 @@ inline uint16_t _888to565(uint8_t r, uint8_t g, uint8_t b) ( (b & 0xF8) >> 3 ) ); } -// external method +void FBDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color) +{ + drawPixel(x*2, y, color); + drawPixel(x*2+1, y, color); +} + +// external method. Doubles vertically. 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; + long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y*2+vinfo.yoffset) * finfo.line_length; + *((uint16_t*)(fbp + location)) = color; + location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y*2+1+vinfo.yoffset) * finfo.line_length; *((uint16_t*)(fbp + location)) = color; } -// external method +// external method. Doubles vertically. 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; + long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y*2+vinfo.yoffset) * finfo.line_length; + *((uint16_t*)(fbp + location)) = _888to565(r,g,b); + location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y*2+1+vinfo.yoffset) * finfo.line_length; *((uint16_t*)(fbp + location)) = _888to565(r,g,b); } @@ -203,10 +238,41 @@ void FBDisplay::flush() void FBDisplay::clrScr() { - for (uint8_t y=0; y #include #include +#include #include "sdl-paddles.h" #include "globals.h" @@ -14,9 +15,25 @@ 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); - + // We want the first USB keyboard that we find. This object reads + // events directly from it, rather than using standard I/O. + // So we're looking for something matching this pattern: + // /dev/input/by-path/platform-20980000.usb-usb-*-event-kbd + + DIR *dirp = opendir("/dev/input/by-path"); + if (!dirp) + return; // No USB devices? :/ + + struct dirent *dp; + while ((dp = readdir(dirp)) != NULL) { + if (!strcmp(&dp->d_name[strlen(dp->d_name)-4], "-kbd")) { + char buf[MAXPATH]; + sprintf(buf, "/dev/input/by-path/%s", dp->d_name); + fd = open(buf, O_RDONLY | O_NONBLOCK); + printf("Opened keyboard %s\n", buf); + break; + } + } } LinuxKeyboard::~LinuxKeyboard() diff --git a/nix/debugger.cpp b/nix/debugger.cpp new file mode 100644 index 0000000..7de43de --- /dev/null +++ b/nix/debugger.cpp @@ -0,0 +1,134 @@ +#include "debugger.h" +#include "globals.h" +#include "disassembler.h" + +#include +#include +#include + +#include +#include + +#include + +Disassembler dis; + +static void *cpu_thread(void *objptr) { + Debugger *obj = (Debugger *)objptr; + + while (1) { + struct sockaddr_in client; + socklen_t clilen = sizeof(client); + + int newsockfd = accept(obj->sd, (struct sockaddr *)&client, &clilen); + + if (newsockfd < 0) { + perror("ERROR on accept"); + exit(1); + } + + obj->setSocket(newsockfd); + + sleep(1); + } +} + +Debugger::Debugger() +{ + struct sockaddr_in server; + int optval; + + sd = socket(AF_INET, SOCK_STREAM, 0); + cd = -1; + + optval=1; + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, + (void*)&optval, sizeof(optval)); + + memset(&server, 0, sizeof(struct sockaddr_in)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(12345); + + if (bind(sd, (struct sockaddr *) &server, sizeof(server)) < 0) { + perror("error binding to debug socket"); + exit(1); + } + + listen(sd,5); + + + if (!pthread_create(&listenThreadID, NULL, &cpu_thread, (void *)this)) { + ; // ... what? + } +} + +Debugger::~Debugger() +{ +} + +void Debugger::step() +{ + static char buf[256]; + + if (cd != -1) { + bzero(buf,256); + int n = read( cd,buf,255 ); + + if (n < 0) { + // error + close(cd); + cd = -1; + return; + } + + if (n > 0) { + if (buf[0] == 'c') { + // Continue - close connection + close(cd); + return; + } + // ... ? + // b - set breakpoint + // s - step over + // S - step out + // c - continue (close connection) + // d - disassemble @ current PC + } + + // Print the status back out the socket + uint8_t p = g_cpu->flags; + snprintf(buf, sizeof(buf), "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':' ' + ); + write(cd, buf, strlen(buf)); + + uint8_t cmdbuf[50]; + + uint16_t loc=g_cpu->pc; + for (int i=0; i<50/3; i++) { + for (int idx=0; idxgetMMU()->read(loc+idx); + } + loc += dis.instructionToMnemonic(loc, cmdbuf, buf, sizeof(buf)); + write(cd, buf, strlen(buf)); + buf[0] = 13; + buf[1] = 10; + write(cd, buf, 2); + } + } +} + + +void Debugger::setSocket(int fd) +{ + cd = fd; +} diff --git a/nix/debugger.h b/nix/debugger.h new file mode 100644 index 0000000..207e8a5 --- /dev/null +++ b/nix/debugger.h @@ -0,0 +1,22 @@ +#ifndef __DEBUGGER_H +#define __DEBUGGER_H + +#include + +class Debugger { + public: + Debugger(); + ~Debugger(); + + void setSocket(int cliSock); + void step(); + + // private: + int sd; // server (listener) + int cd; // client (connected to us) + pthread_t listenThreadID; + +}; + + +#endif diff --git a/nix/disassembler.cpp b/nix/disassembler.cpp new file mode 100755 index 0000000..edeec08 --- /dev/null +++ b/nix/disassembler.cpp @@ -0,0 +1,166 @@ +#include "disassembler.h" +#include "cpu.h" + +#include + +// all the lower-case opcodes here are 65c02. +static const char *opnames[256] = { + "BRK", "ORA", "???", "???", "tsb", "ORA", "ASL", "rmb0", + "PHP", "ORA", "ASL", "???", "tsb", "ORA", "ASL", "bbr0", + "BPL", "ORA", "ora", "???", "trb", "ORA", "ASL", "rmb1", + "CLC", "ORA", "inc", "???", "trb", "ORA", "ASL", "bbr1", + "JSR", "AND", "???", "???", "BIT", "AND", "ROL", "rmb2", + "PLP", "AND", "ROL", "???", "BIT", "AND", "ROL", "bbr2", + "BMI", "AND", "and", "???", "bit", "AND", "ROL", "rmb3", + "SEC", "AND", "dec", "???", "bit", "AND", "ROL", "bbr3", + "RTI", "EOR", "???", "???", "???", "EOR", "LSR", "rmb4", + "PHA", "EOR", "LSR", "???", "JMP", "EOR", "LSR", "bbr4", + "BVC", "EOR", "eor", "???", "???", "EOR", "LSR", "rmb5", + "CLI", "EOR", "phy", "???", "???", "EOR", "LSR", "bbr5", + "RTS", "ADC", "???", "???", "stz", "ADC", "ROR", "rmb6", + "PLA", "ADC", "ROR", "???", "JMP", "ADC", "ROR", "bbr6", + "BVS", "ADC", "adc", "???", "stz", "ADC", "ROR", "rmb7", + "SEI", "ADC", "ply", "???", "jmp", "ADC", "ROR", "bbr7", // 0x78-0x7F + "bra", "STA", "???", "???", "STY", "STA", "STX", "smb0", + "DEY", "bit", "TXA", "???", "STY", "STA", "STX", "bbs0", + "BCC", "STA", "sta", "???", "STY", "STA", "STX", "smb1", + "TYA", "STA", "TXS", "???", "stz", "STA", "stz", "bbs1", + "LDY", "LDA", "LDX", "???", "LDY", "LDA", "LDX", "smb2", + "TAY", "LDA", "TAX", "???", "LDY", "LDA", "LDX", "bbs2", + "BCS", "LDA", "lda", "???", "LDY", "LDA", "LDX", "smb3", + "CLV", "LDA", "TSX", "???", "LDY", "LDA", "LDX", "bbs3", + "CPY", "CMP", "???", "???", "CPY", "CMP", "DEC", "smb4", + "INY", "CMP", "DEX", "wai", "CPY", "CMP", "DEC", "bbs4", // 0xC8-0xCF + "BNE", "CMP", "cmp", "???", "???", "CMP", "DEC", "smb5", + "CLD", "CMP", "phx", "dcp", "???", "CMP", "DEC", "bbs5", // 0xD8-0xDF + "CPX", "SBC", "???", "???", "CPX", "SBC", "INC", "smb6", + "INX", "SBC", "NOP", "???", "CPX", "SBC", "INC", "bbs6", // 0xE8-0xEF + "BEQ", "SBC", "sbc", "???", "???", "SBC", "INC", "smb7", + "SED", "SBC", "plx", "???", "???", "SBC", "INC", "bbs7", // 0xF0-0xFF +}; + +typedef struct _opmode { + addrmode mode; + const char *prefix; + const char *suffix; +} opmode_t; + +opmode_t opmodes[16] = { { A_IMP, "", "" }, + { A_IMM, "#", "" }, + { A_ABS, "", ""}, + { A_ZER, "", ""}, + { A_REL, "", ""}, + { A_ABI, "(", ")"}, + { A_ZEX, "", ",X"}, + { A_ZEY, "", ",Y"}, + { A_ZIND, "(", ")"}, + { A_ABX, "", ",X"}, + { A_ABXI, "(", ",X)"}, + { A_ABY, "", ",Y"}, + { A_INX, "(", ",X)"}, + { A_INY, "(", "),Y"}, + { A_ZPREL,"", "" }, + { A_ACC, "A", ""}, +}; + +Disassembler::Disassembler() +{ +} + +Disassembler::~Disassembler() +{ +} + +uint8_t Disassembler::instructionBytes(uint8_t i) +{ + switch (opcodes[i].mode) { + case A_REL: + return 2; + case A_IMM: + return 2; + case A_ABS: + return 3; + case A_ZER: + return 2; + case A_IMP: + return 1; + case A_ACC: + return 1; + case A_ABI: + return 3; + case A_ZEX: + return 2; + case A_ZEY: + return 2; + case A_ABX: + return 3; + case A_ABXI: + return 3; + case A_ABY: + return 3; + case A_INX: + return 2; + case A_INY: + return 2; + case A_ZIND: + return 2; + case A_ZPREL: + return 3; + case A_ILLEGAL: + return 1; + } + + // Default to 1 byte for anything that falls through + return 1; +} + +opmode_t opmodeForInstruction(uint8_t ins) +{ + for (int i=0; i<16; i++) { + if (opcodes[ins].mode == opmodes[i].mode) { + return opmodes[i]; + } + } + + /* NOTREACHED */ + opmode_t ret = { A_ILLEGAL, "", "" }; + return ret; +} + +uint8_t Disassembler::instructionToMnemonic(uint16_t addr, uint8_t *p, char *outp, uint16_t outpSize) +{ + const char *mn = opnames[*p]; + addrmode amode = opcodes[*p].mode; + uint16_t target = 0; + char arg[40] = "\0"; + + switch (amode) { + case A_REL: + target = addr + *(int8_t *)(p+1) + 2; // FIXME: is this correct? + break; + case A_ABS: + case A_ABY: + case A_ABX: + case A_ABI://indirect + target = (*(p+2) << 8) | (*(p+1)); // FIXME: is this correct? + break; + default: + target = 0; + break; + } + + opmode_t om = opmodeForInstruction(*p); + switch (instructionBytes(*p)) { + case 1: + // no arguments + break; + case 2: + case 3: + sprintf(arg, "%s$%X%s", om.prefix, target, om.suffix); + break; + } + + sprintf(outp, "$%.4X %s %s", addr, mn, arg); + + return instructionBytes(*p); +} diff --git a/nix/disassembler.h b/nix/disassembler.h new file mode 100644 index 0000000..b3a0ea6 --- /dev/null +++ b/nix/disassembler.h @@ -0,0 +1,15 @@ +#ifndef __DISASSEMBLER_H +#define __DISASSEMBLER_H + +#include + +class Disassembler { + public: + Disassembler(); + ~Disassembler(); + + uint8_t instructionBytes(uint8_t i); + uint8_t instructionToMnemonic(uint16_t addr, uint8_t *p, char *outp, uint16_t outpSize); +}; + +#endif diff --git a/nix/nix-prefs.cpp b/nix/nix-prefs.cpp new file mode 100644 index 0000000..cf5edb5 --- /dev/null +++ b/nix/nix-prefs.cpp @@ -0,0 +1,66 @@ +#include "nix-prefs.h" + +#include +#include +#include +#include +#include +#include + +NixPrefs::NixPrefs() +{ + struct passwd *pw = getpwuid(getuid()); + + char *homedir = pw->pw_dir; + prefsFilePath = (char *)malloc(strlen(homedir) + 1 + strlen(".aiie") + 1); + + strcpy(prefsFilePath, homedir); + strcat(prefsFilePath, "/"); + strcat(prefsFilePath, ".aiie"); +} + +NixPrefs::~NixPrefs() +{ + if (prefsFilePath) + free(prefsFilePath); +} + +bool NixPrefs::readPrefs(prefs_t *readTo) +{ + FILE *f = fopen(prefsFilePath, "r"); + if (!f) + return false; + + if (fread(readTo, sizeof(prefs_t), 1, f) != 1) { + fclose(f); + return false; + } + + fclose(f); + if (readTo->magic != PREFSMAGIC) { + return false; + } + if (readTo->prefsSize != sizeof(prefs_t)) { + return false; + } + if (readTo->version != PREFSVERSION) { + return false; + } + + return true; +} + +bool NixPrefs::writePrefs(prefs_t *newPrefs) +{ + FILE *f = fopen(prefsFilePath, "w"); + if (!f) + return false; + + if (fwrite(newPrefs, sizeof(prefs_t), 1, f) != 1) { + fclose(f); + return false; + } + + fclose(f); + return true; +} diff --git a/nix/nix-prefs.h b/nix/nix-prefs.h new file mode 100644 index 0000000..93f698b --- /dev/null +++ b/nix/nix-prefs.h @@ -0,0 +1,18 @@ +#ifndef _NIX_PREFSSTORE_H +#define _NIX_PREFSSTORE_H + +#include "prefsstore.h" + +class NixPrefs : public PrefsStore { + public: + NixPrefs(); + virtual ~NixPrefs(); + + virtual bool readPrefs(prefs_t *readTo); + virtual bool writePrefs(prefs_t *newPrefs); + + private: + char *prefsFilePath; +}; + +#endif diff --git a/physicaldisplay.h b/physicaldisplay.h index e9f2bb0..0258752 100644 --- a/physicaldisplay.h +++ b/physicaldisplay.h @@ -23,8 +23,19 @@ 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 drawUIPixel(uint16_t x, uint16_t y, uint16_t color) = 0; + virtual void clrScr() = 0; + // methods to draw in to the buffer - not directly to the screen. + + // First, methods that expect *us* to pixel-double the width... + virtual void cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color) = 0; + virtual void cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorA, uint8_t colorB) = 0; + + // Then the direct-pixel methods + virtual void cachePixel(uint16_t x, uint16_t y, uint8_t color) = 0; + protected: char overlayMessage[40]; }; diff --git a/prefsstore.h b/prefsstore.h new file mode 100644 index 0000000..fc8450c --- /dev/null +++ b/prefsstore.h @@ -0,0 +1,41 @@ +#ifndef __PREFSSTORE_H +#define __PREFSSTORE_H + +#include + +// Fun trivia: the Apple //e was in production from January 1983 to +// November 1993. And the 65C02 in them supported weird BCD math modes. +#define PREFSMAGIC 0x01831093 +#define PREFSVERSION 1 + +// The Teensy 3.6 has 4096 bytes of flash. We want this to stay under +// that size. +typedef struct _prefs { + uint32_t magic; + uint16_t prefsSize; + uint8_t version; + + uint8_t volume; + uint8_t displayType; + uint8_t debug; + uint8_t priorityMode; + uint8_t speed; + + char reserved[255]; // 255 is the Teensy MAXPATH size + + char disk1[255]; + char disk2[255]; + char hd1[255]; + char hd2[255]; +} prefs_t; + +class PrefsStore { + public: + PrefsStore() {}; + virtual ~PrefsStore() {}; + + virtual bool readPrefs(prefs_t *readTo) = 0; + virtual bool writePrefs(prefs_t *newPrefs) = 0; +}; + +#endif diff --git a/sdl/aiie.cpp b/sdl/aiie.cpp index 15413c9..d1ac683 100644 --- a/sdl/aiie.cpp +++ b/sdl/aiie.cpp @@ -13,6 +13,8 @@ #include "sdl-printer.h" #include "appleui.h" #include "bios.h" +#include "nix-prefs.h" +#include "debugger.h" #include "globals.h" @@ -20,10 +22,11 @@ //#define SHOWFPS //#define SHOWPC -//#define DEBUGCPU +#define DEBUGCPU //#define SHOWMEMPAGE BIOS bios; +Debugger debugger; static struct timespec nextInstructionTime, startTime; @@ -40,7 +43,11 @@ char disk2name[256] = "\0"; volatile bool wantSuspend = false; volatile bool wantResume = false; +volatile bool cpuDebuggerRunning = false; + void doDebugging(); +void readPrefs(); +void writePrefs(); void sigint_handler(int n) { @@ -172,23 +179,12 @@ static void *cpu_thread(void *dummyptr) { ((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':' ' - ); - } + debugger.step(); #endif if (send_rst) { + cpuDebuggerRunning = true; + #if 0 printf("Scheduling suspend request...\n"); wantSuspend = true; @@ -320,6 +316,9 @@ int main(int argc, char *argv[]) // g_display->blit(); g_display->redraw(); + /* Load prefs & reset globals appropriately now */ + readPrefs(); + if (argc >= 2) { printf("Inserting disk %s\n", argv[1]); ((AppleVM *)g_vm)->insertDisk(0, argv[1]); @@ -350,7 +349,7 @@ int main(int argc, char *argv[]) printf("Invoking BIOS\n"); if (bios.runUntilDone()) { // if it returned true, we have something to store persistently in EEPROM. - // writePrefs(); + writePrefs(); } printf("BIOS done\n"); @@ -384,8 +383,6 @@ int main(int argc, char *argv[]) // 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 @@ -393,6 +390,7 @@ int main(int argc, char *argv[]) g_vm->vmdisplay->didRedraw(); g_display->blit(what); } + g_ui->blit(); g_printer->update(); g_keyboard->maintainKeyboard(); @@ -504,3 +502,57 @@ void doDebugging() break;*/ } } + +void readPrefs() +{ + NixPrefs np; + prefs_t p; + if (np.readPrefs(&p)) { + g_volume = p.volume; + g_displayType = p.displayType; + g_debugMode = p.debug; + g_prioritizeDisplay = p.priorityMode; + g_speed = (p.speed * (1023000/2)); // steps of half normal speed + if (g_speed < (1023000/2)) + g_speed = (1023000/2); + if (p.disk1[0]) { + ((AppleVM *)g_vm)->insertDisk(0, p.disk1); + strcpy(disk1name, p.disk1); + } + if (p.disk2[0]) { + ((AppleVM *)g_vm)->insertDisk(1, p.disk2); + strcpy(disk2name, p.disk2); + } + + if (p.hd1[0]) { + ((AppleVM *)g_vm)->insertHD(0, p.hd1); + } + + if (p.hd2[0]) { + ((AppleVM *)g_vm)->insertHD(1, p.hd2); + } + } +} + +void writePrefs() +{ + NixPrefs np; + prefs_t p; + + p.magic = PREFSMAGIC; + p.prefsSize = sizeof(prefs_t); + p.version = PREFSVERSION; + + p.volume = g_volume; + p.displayType = g_displayType; + p.debug = g_debugMode; + p.priorityMode = g_prioritizeDisplay; + p.speed = g_speed / (1023000/2); + strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0)); + strcpy(p.disk2, ((AppleVM *)g_vm)->DiskName(1)); + strcpy(p.hd1, ((AppleVM *)g_vm)->HDName(0)); + strcpy(p.hd2, ((AppleVM *)g_vm)->HDName(1)); + + bool ret = np.writePrefs(&p); + printf("writePrefs returns %s\n", ret ? "true" : "false"); +} diff --git a/sdl/sdl-display.cpp b/sdl/sdl-display.cpp index 128260a..12637f2 100644 --- a/sdl/sdl-display.cpp +++ b/sdl/sdl-display.cpp @@ -9,6 +9,9 @@ #include "apple/appleui.h" +#define SCREENINSET_X (18*2) +#define SCREENINSET_Y (13*2) + // RGB map of each of the lowres colors const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black { 195, 0, 48 }, // magenta @@ -30,6 +33,8 @@ const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black SDLDisplay::SDLDisplay() { + memset(videoBuffer, 0, sizeof(videoBuffer)); + // FIXME: abstract constants screen = SDL_CreateWindow("Aiie!", SDL_WINDOWPOS_UNDEFINED, @@ -69,6 +74,9 @@ void SDLDisplay::redraw() } } +// drawImageOfSizeAt will horizontally scale out the image b/c the +// images themselves aren't aware of the double resolution. This is an +// inconsistency that probably should be addressed. FIXME? void SDLDisplay::drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey) @@ -77,34 +85,40 @@ void SDLDisplay::drawImageOfSizeAt(const uint8_t *img, for (uint16_t x=0; xvideoBuffer; // FIXME: poking deep + // The dimensions here are the dimensions of the variable screen -- + // not the border. We don't support updating the border *except* by + // drawing directly to the screen device... - 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; + for (uint16_t y=r.top*2; y= 3? - SDL_SetRenderDrawColor(renderer, loresPixelColors[colorIdx][0], loresPixelColors[colorIdx][1], loresPixelColors[colorIdx][2], 255); - SDL_RenderDrawPoint(renderer, x*2+xoff+BASEX, y*2+yoff+BASEY); - } + else if (g_displayType == m_blackAndWhite) { + float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b; + r = g = b = fv; } + + SDL_SetRenderDrawColor(renderer, r, g, b, 255); + SDL_RenderDrawPoint(renderer, x+SCREENINSET_X, y+SCREENINSET_Y); } } @@ -121,6 +135,12 @@ inline void putpixel(SDL_Renderer *renderer, int x, int y, uint8_t r, uint8_t g, SDL_RenderDrawPoint(renderer, x, y); } +void SDLDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color) +{ + drawPixel(x*2, y, color); + drawPixel(x*2+1, y, color); +} + void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color) { uint8_t @@ -129,21 +149,17 @@ void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color) b = (color & 0x1F) << 3; - // Pixel-doubling + // Pixel-doubling vertically for (int yoff=0; yoff<2; yoff++) { - for (int xoff=0; xoff<2; xoff++) { - putpixel(renderer, xoff+x*2, yoff+y*2, r, g, b); - } + putpixel(renderer, x, yoff+y*2, r, g, b); } } void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b) { - // Pixel-doubling + // Pixel-doubling vertically for (int yoff=0; yoff<2; yoff++) { - for (int xoff=0; xoff<2; xoff++) { - putpixel(renderer, xoff+x*2, yoff+y*2, r, g, b); - } + putpixel(renderer, x, yoff+y*2, r, g, b); } } @@ -209,3 +225,35 @@ void SDLDisplay::clrScr() SDL_RenderPresent(renderer); // perform the render } +void SDLDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) +{ + videoBuffer[y*2*SDLDISPLAY_WIDTH+x] = color; + videoBuffer[((y*2)+1)*SDLDISPLAY_WIDTH+x] = color; +} + +// "DoubleWide" means "please double the X because I'm in low-res width mode" +void SDLDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color) +{ + videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2] = color; + videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+1] = color; + + videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2] = color; + videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+1] = color; +} + +void SDLDisplay::cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorB, uint8_t colorA) +{ + videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2] = colorA; + videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+1] = colorA; + + videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+2] = colorB; + videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+3] = colorB; + + videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2] = colorA; + videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+1] = colorA; + + videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+2] = colorB; + videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+3] = colorB; +} + + diff --git a/sdl/sdl-display.h b/sdl/sdl-display.h index ee5e82b..77b15a2 100644 --- a/sdl/sdl-display.h +++ b/sdl/sdl-display.h @@ -28,11 +28,20 @@ class SDLDisplay : public PhysicalDisplay { 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 drawUIPixel(uint16_t x, uint16_t y, uint16_t color); + 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 clrScr(); + virtual void cachePixel(uint16_t x, uint16_t y, uint8_t color); + virtual void cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color); + virtual void cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorA, uint8_t colorB); + + private: + uint8_t videoBuffer[SDLDISPLAY_HEIGHT * SDLDISPLAY_WIDTH]; + SDL_Window *screen; SDL_Renderer *renderer; }; diff --git a/teensy/prefsstore.h b/teensy/prefsstore.h new file mode 120000 index 0000000..ba3970e --- /dev/null +++ b/teensy/prefsstore.h @@ -0,0 +1 @@ +../prefsstore.h \ No newline at end of file diff --git a/teensy/teensy-display.cpp b/teensy/teensy-display.cpp index bbd00cb..8d80fca 100644 --- a/teensy/teensy-display.cpp +++ b/teensy/teensy-display.cpp @@ -36,26 +36,64 @@ #include "applevm.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 +const uint8_t loresPixelColors[16*2] = { 0x00,0x00, // 0 black + 0xC0,0x06, // 1 magenta + 0x00,0x10, // 2 dark blue + 0xA1,0xB5, // 3 purple + 0x04,0x80, // 4 dark green + 0x6B,0x4D, // 5 dark grey + 0x1B,0x9F, // 6 med blue + 0x0D,0xFD, // 7 light blue + 0x92,0xA5, // 8 brown + 0xF8,0xC5, // 9 orange + 0x95,0x55, // 10 light gray + 0xFC,0xF2, // 11 pink + 0x07,0xE0, // 12 green + 0xFF,0xE0, // 13 yellow + 0x87,0xF0, // 14 aqua + 0xFF,0xFF // 15 white +}; + +const uint8_t loresPixelColorsGreen[16*2] = { 0x00, 0x00, + 0x01, 0x40, + 0x00, 0x40, + 0x02, 0x80, + 0x03, 0x00, + 0x03, 0x40, + 0x03, 0x00, + 0x04, 0x80, + 0x02, 0xC0, + 0x02, 0x40, + 0x05, 0x00, + 0x05, 0x40, + 0x05, 0x80, + 0x07, 0x00, + 0x06, 0x80, + 0x07, 0xC0 +}; + +const uint8_t loresPixelColorsWhite[16*2] = { 0x00, 0x00, + 0x29, 0x45, + 0x08, 0x41, + 0x52, 0x8A, + 0x63, 0x0C, + 0x6B, 0x4D, + 0x63, 0x0C, + 0x94, 0x92, + 0x5A, 0xCB, + 0x4A, 0x49, + 0xA5, 0x14, + 0xAD, 0x55, + 0xB5, 0x96, + 0xE7, 0x1C, + 0xD6, 0x9A, + 0xFF, 0xDF }; TeensyDisplay::TeensyDisplay() { + memset(videoBuffer, 0, sizeof(videoBuffer)); + pinMode(DB_8, OUTPUT); pinMode(DB_9, OUTPUT); pinMode(DB_10, OUTPUT); @@ -98,14 +136,19 @@ TeensyDisplay::TeensyDisplay() cbi(P_CS, B_CS); - LCD_Write_COM_DATA(0x00,0x0001); // oscillator start [enable] + // Setup here is from the document "Driver IC SSD1289.pdf" + // https://forum.allaboutcircuits.com/attachments/driver-ic-ssd1289-pdf.71570/ + LCD_Write_COM_DATA(0x00,0x0001); // R00h: enable the oscillator LCD_Write_COM_DATA(0x03,0xA8A4); // power control [%1010 1000 1010 1000] == DCT3, DCT1, BT2, DC3, DC1, AP2 + LCD_Write_COM_DATA(0x0C,0x0000); // power control2 [0] LCD_Write_COM_DATA(0x0D,0x080C); // power control3 [VRH3, VRH2, invalid bits] LCD_Write_COM_DATA(0x0E,0x2B00); // power control4 VCOMG, VDV3, VDV1, VDV0 LCD_Write_COM_DATA(0x1E,0x00B7); // power control5 nOTP, VCM5, VCM4, VCM2, VCM1, VCM0 // LCD_Write_COM_DATA(0x01,0x2B3F); // driver control output REV, BGR, TB, MUX8, MUX5, MUX4, MUX3, MUX2, MUX1, MUX0 - // Change that: TB = 0 please + + // This sets the direction of the scan. These two are mirror + // opposites. The first is right in my setup. LCD_Write_COM_DATA(0x01,0x293F); // driver control output REV, BGR, TB, MUX8, MUX5, MUX4, MUX3, MUX2, MUX1, MUX0 // LCD_Write_COM_DATA(0x01,0x693F); // driver control output RL, REV, BGR, TB, MUX8, MUX5, MUX4, MUX3, MUX2, MUX1, MUX0 @@ -122,7 +165,8 @@ TeensyDisplay::TeensyDisplay() LCD_Write_COM_DATA(0x16,0xEF1C); // horiz porch (default) LCD_Write_COM_DATA(0x17,0x0003); // vertical porch LCD_Write_COM_DATA(0x07,0x0233); // display control VLE1, GON, DTE, D1, D0 - LCD_Write_COM_DATA(0x0B,0x0000); // frame cycle control + LCD_Write_COM_DATA(0x0B,0x5308); // frame cycle control: %0101 0011 0000 1000 + LCD_Write_COM_DATA(0x0F,0x0000); // gate scan start posn LCD_Write_COM_DATA(0x41,0x0000); // vertical scroll control1 LCD_Write_COM_DATA(0x42,0x0000); // vertical scroll control2 @@ -148,8 +192,8 @@ TeensyDisplay::TeensyDisplay() LCD_Write_COM_DATA(0x25,0x8000); // frame frequency (OSC3) LCD_Write_COM_DATA(0x4f,0x0000); // Set GDDRAM Y address counter LCD_Write_COM_DATA(0x4e,0x0000); // Set GDDRAM X address counter -#if 0 - // Set data access speed optimization (?) +#if 1 + // Set data access speed optimization (?) per pg. 50; doesn't actually seem to change anything though? LCD_Write_COM_DATA(0x28, 0x0006); LCD_Write_COM_DATA(0x2F, 0x12BE); LCD_Write_COM_DATA(0x12, 0x6CEB); @@ -287,6 +331,11 @@ void TeensyDisplay::drawPixel(uint16_t x, uint16_t y) clrXY(); } +void TeensyDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color) +{ + drawPixel(x,y,color); +} + void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color) { cbi(P_CS, B_CS); @@ -361,8 +410,6 @@ void TeensyDisplay::drawNextPixel(uint16_t color) void TeensyDisplay::blit(AiieRect r) { - uint8_t *videoBuffer = g_vm->videoBuffer; // FIXME: poking deep - // remember these are "starts at pixel number" values, where 0 is the first. #define HOFFSET 18 #define VOFFSET 13 @@ -380,17 +427,37 @@ void TeensyDisplay::blit(AiieRect r) // send the pixel data sbi(P_RS, B_RS); - uint16_t pixel; + uint8_t *vbufPtr; for (uint8_t y=r.top; y<=r.bottom; y++) { + vbufPtr = &videoBuffer[y * TEENSY_DRUN + r.left]; for (uint16_t x=r.left; x<=r.right; x++) { - pixel = y * (DISPLAYRUN >> 1) + (x >> 1); uint8_t colorIdx; if (!(x & 0x01)) { - colorIdx = videoBuffer[pixel] >> 4; + colorIdx = *vbufPtr >> 4; } else { - colorIdx = videoBuffer[pixel] & 0x0F; + // alpha the right-ish pixel over the left-ish pixel. + colorIdx = *vbufPtr & 0x0F; + } + colorIdx <<= 1; + + // The colors are broken up in to two 8-bit values to speed things up. + const uint8_t *p; + + if (g_displayType == m_monochrome) { + p = &loresPixelColorsGreen[colorIdx]; + } + else if (g_displayType == m_blackAndWhite) { + p = &loresPixelColorsWhite[colorIdx]; + } else { + p = &loresPixelColors[colorIdx]; + } + + LCD_Writ_Bus(*p, *(p+1)); + + if (x & 0x01) { + // When we do the odd pixels, then move the pixel pointer to the next pixel + vbufPtr++; } - LCD_Writ_Bus(loresPixelColors[colorIdx]>>8,loresPixelColors[colorIdx]); } } cbi(P_CS, B_CS); @@ -495,3 +562,58 @@ void TeensyDisplay::drawImageOfSizeAt(const uint8_t *img, } } } + +// "DoubleWide" means "please double the X because I'm in low-res +// width mode". But we only have half the horizontal width required on +// the Teensy, so it's divided in half. And then we drop to 4-bit +// colors, so it's divided in half again. +void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color) +{ + uint8_t b = videoBuffer[y*TEENSY_DRUN+(x>>1)]; + + if (x & 1) { + // Low nybble + b = (b & 0xF0) | (color & 0x0F); + } else { + // High nybble + b = (color << 4) | (b & 0x0F); + } + videoBuffer[y*TEENSY_DRUN+(x>>1)] = b; +} + +// This exists for 4bpp optimization. We could totally call +// cacheDoubleWidePixel twice, but the (x&1) pfutzing is messy if +// we're just storing both halves anyway... +void TeensyDisplay::cache2DoubleWidePixels(uint16_t x, uint16_t y, + uint8_t colorA, uint8_t colorB) +{ + videoBuffer[y*TEENSY_DRUN+(x>>1)] = (colorB << 4) | colorA; +} + +// This is the full 560-pixel-wide version -- and we only have 280 +// pixels wide. So we'll divide x by 2. And then at 4bpp, we divide by +// 2 again. +// On odd-numbered X pixels, we also alpha-blend -- "black" means "clear" +void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) +{ + if (x&1) { + x >>= 1; // divide by 2, then this is mostly cacheDoubleWidePixel. Except... + uint8_t b = videoBuffer[y*TEENSY_DRUN+(x>>1)]; + + if (x & 1) { + // Low nybble + if (color == c_black) + color = b & 0x0F; + b = (b & 0xF0) | (color & 0x0F); + } else { + // High nybble + if (color == c_black) + color = (b & 0xF0) >> 4; + b = (color << 4) | (b & 0x0F); + } + videoBuffer[y*TEENSY_DRUN+(x>>1)] = b; + } else { + cacheDoubleWidePixel(x/2, y, color); + } +} + diff --git a/teensy/teensy-display.h b/teensy/teensy-display.h index cba647c..0d09be2 100644 --- a/teensy/teensy-display.h +++ b/teensy/teensy-display.h @@ -4,8 +4,10 @@ #include #include "physicaldisplay.h" -#define TEENSY_DHEIGHT 240 -#define TEENSY_DWIDTH 320 +#define TEENSY_DHEIGHT 192 +#define TEENSY_DWIDTH 280 +// run length of one row of pixels +#define TEENSY_DRUN (TEENSY_DWIDTH/2) #define regtype volatile uint8_t #define regsize uint8_t @@ -41,6 +43,10 @@ class TeensyDisplay : public PhysicalDisplay { virtual void drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey); + virtual void cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color); + virtual void cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorA, uint8_t colorB); + virtual void cachePixel(uint16_t x, uint16_t y, uint8_t color); + protected: void moveTo(uint16_t col, uint16_t row); void drawNextPixel(uint16_t color); @@ -62,6 +68,7 @@ class TeensyDisplay : public PhysicalDisplay { virtual void drawPixel(uint16_t x, uint16_t y); 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 drawUIPixel(uint16_t x, uint16_t y, uint16_t color); inline void LCD_Writ_Bus(uint8_t VH,uint8_t VL) __attribute__((always_inline)); inline void LCD_Write_COM(uint8_t VL) __attribute__((always_inline)); @@ -72,6 +79,9 @@ class TeensyDisplay : public PhysicalDisplay { bool needsRedraw; bool driveIndicator[2]; bool driveIndicatorDirty; + + // video buffer is 4bpp + uint8_t videoBuffer[TEENSY_DHEIGHT * TEENSY_DWIDTH / 2]; }; #endif diff --git a/teensy/teensy-prefs.cpp b/teensy/teensy-prefs.cpp new file mode 100644 index 0000000..80ef165 --- /dev/null +++ b/teensy/teensy-prefs.cpp @@ -0,0 +1,41 @@ +#include "teensy-prefs.h" + +#include + +TeensyPrefs::TeensyPrefs() +{ +} + +TeensyPrefs::~TeensyPrefs() +{ +} + +bool TeensyPrefs::readPrefs(prefs_t *readTo) +{ + uint8_t *pp = (uint8_t *)readTo; + for (uint16_t i=0; imagic != PREFSMAGIC) { + return false; + } + if (readTo->prefsSize != sizeof(prefs_t)) { + return false; + } + if (readTo->version != PREFSVERSION) { + return false; + } + + return true; +} + +bool TeensyPrefs::writePrefs(prefs_t *newPrefs) +{ + uint8_t *pp = (uint8_t *)newPrefs; + for (uint16_t i=0; i #include -#include #include #include #include "bios.h" @@ -12,6 +11,8 @@ #include "teensy-paddles.h" #include "teensy-filemanager.h" #include "appleui.h" +#include "teensy-prefs.h" + #define RESETPIN 39 #define BATTERYPIN 32 #define SPEAKERPIN A21 @@ -192,6 +193,7 @@ void biosInterrupt() g_speaker->maintainSpeaker(-1, -1); // Force the display to redraw + g_display->redraw(); ((AppleDisplay*)(g_vm->vmdisplay))->modeChange(); // Poll the keyboard before we start, so we can do selftest on startup @@ -267,9 +269,9 @@ 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_ui->blit(); g_vm->vmdisplay->lockDisplay(); if (g_vm->vmdisplay->needsRedraw()) { AiieRect what = g_vm->vmdisplay->getDirtyRect(); @@ -368,58 +370,59 @@ void doDebugging() } } -typedef struct _prefs { - uint32_t magic; - int16_t volume; -} prefs; - -// Fun trivia: the Apple //e was in production from January 1983 to -// November 1993. And the 65C02 in them supported weird BCD math modes. -#define MAGIC 0x01831093 - void readPrefs() { - prefs p; - uint8_t *pp = (uint8_t *)&p; - - Serial.println("reading prefs"); - - for (uint8_t i=0; i 15) { - p.volume = 15; - } - if (p.volume < 0) { - p.volume = 0; - } - + TeensyPrefs np; + prefs_t p; + if (np.readPrefs(&p)) { g_volume = p.volume; - return; - } + g_displayType = p.displayType; + g_debugMode = p.debug; + g_prioritizeDisplay = p.priorityMode; + g_speed = (p.speed * (1023000/2)); // steps of half normal speed + if (g_speed < (1023000/2)) + g_speed = (1023000/2); + if (p.disk1[0]) { + ((AppleVM *)g_vm)->insertDisk(0, p.disk1); + } + if (p.disk2[0]) { + ((AppleVM *)g_vm)->insertDisk(1, p.disk2); + } - // use defaults - g_volume = 0; + if (p.hd1[0]) { + ((AppleVM *)g_vm)->insertHD(0, p.hd1); + } + + if (p.hd2[0]) { + ((AppleVM *)g_vm)->insertHD(1, p.hd2); + } + } } void writePrefs() { - Serial.println("writing prefs"); - Timer1.stop(); + TeensyPrefs np; + prefs_t p; - prefs p; - uint8_t *pp = (uint8_t *)&p; + g_display->clrScr(); + g_display->drawString(M_SELECTED, 80, 100,"Writing prefs..."); + g_display->flush(); + + p.magic = PREFSMAGIC; + p.prefsSize = sizeof(prefs_t); + p.version = PREFSVERSION; - p.magic = MAGIC; p.volume = g_volume; + p.displayType = g_displayType; + p.debug = g_debugMode; + p.priorityMode = g_prioritizeDisplay; + p.speed = g_speed / (1023000/2); + strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0)); + strcpy(p.disk2, ((AppleVM *)g_vm)->DiskName(1)); + strcpy(p.hd1, ((AppleVM *)g_vm)->HDName(0)); + strcpy(p.hd2, ((AppleVM *)g_vm)->HDName(1)); - for (uint8_t i=0; i