abstract drawing so devices may have full or half resolution; rebuild BIOS interface

This commit is contained in:
Jorj Bauer 2018-02-17 20:44:04 -05:00
parent bfd1720a0c
commit f131910df2
35 changed files with 1347 additions and 431 deletions

View File

@ -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 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 ROMS=apple/applemmu-rom.h apple/diskii-rom.h apple/parallel-rom.h apple/hd32-rom.h

View File

@ -10,10 +10,13 @@
BLACKANDWHITE: monochrome, but use B&W instead of B&G; 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 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 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) { \ #define extendDirtyRect(x,y) { \
if (!dirty) { \ if (!dirty) { \
dirtyRect.left = x; \ dirtyRect.left = x; \
@ -37,39 +40,9 @@
} \ } \
} }
#if DISPLAYRUN == 512 #define drawApplePixel(c,x,y) { g_display->cacheDoubleWidePixel(x,y,c); }
#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 draw2Pixels(cA, cB, x, y) { g_display->cache2DoubleWidePixels(x,y,cA, cB); }
#define DrawLoresPixelAt(c, x, y) { \ #define DrawLoresPixelAt(c, x, y) { \
uint8_t pixel = c & 0x0F; \ uint8_t pixel = c & 0x0F; \
@ -88,11 +61,10 @@
#include "globals.h" #include "globals.h"
AppleDisplay::AppleDisplay(uint8_t *vb) : VMDisplay(vb) AppleDisplay::AppleDisplay() : VMDisplay()
{ {
this->switches = NULL; this->switches = NULL;
textColor = g_displayType == m_monochrome?c_green:c_white;
modeChange(); modeChange();
} }
@ -234,19 +206,36 @@ inline void AppleDisplay::Draw14DoubleHiresPixelsAt(uint16_t addr)
bitTrain <<= 7; bitTrain <<= 7;
bitTrain |= (b1B & 0x7F); bitTrain |= (b1B & 0x7F);
// Now we pop groups of 4 bits off the bottom and draw our // Now we pop groups of 4 bits off the bottom and draw.
// 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.
for (int8_t xoff = 0; xoff < 14; xoff += 2) { for (int8_t xoff = 0; xoff < 14; xoff += 2) {
drawApplePixel(bitTrain & 0x0F, col+xoff, row); if (g_displayType == m_ntsclike) {
drawApplePixel(bitTrain & 0x0F, col+xoff+1, row); // 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; bitTrain >>= 4;
} } // for
} }
} }
@ -310,15 +299,7 @@ inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr)
for (int8_t xoff = 0; xoff < 14; xoff += 2) { for (int8_t xoff = 0; xoff < 14; xoff += 2) {
if (g_displayType == m_monochrome) { if (g_displayType == m_ntsclike) {
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) {
// Use the NTSC-like color mode, where we're only 140 pixels wide. // Use the NTSC-like color mode, where we're only 140 pixels wide.
bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne); bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne);
@ -338,7 +319,7 @@ inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr)
break; break;
} }
draw2Pixels( (color << 4) | color, col+xoff, row ); draw2Pixels( color, color, col+xoff, row );
} else { } else {
// Use the "perfect" color mode, like the Apple RGB monitor showed. // Use the "perfect" color mode, like the Apple RGB monitor showed.
bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne); bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne);
@ -357,22 +338,10 @@ inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr)
color = c_white; color = c_white;
break; break;
} }
uint16_t twoColors; draw2Pixels( (color==c_white || (bitTrain & 0x02)) ? color : c_black,
(color==c_white || (bitTrain & 0x01)) ? color : c_black,
if (color == c_black || color == c_white || bitTrain & 0x01) { col+xoff, row );
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);
} }
bitTrain >>= 2; bitTrain >>= 2;
} }
@ -402,32 +371,23 @@ void AppleDisplay::redraw80ColumnText(uint8_t startingY)
// Only draw onscreen locations // Only draw onscreen locations
if (row >= startingY && col <= 39 && row <= 23) { if (row >= startingY && col <= 39 && row <= 23) {
// Even characters are in bank 0 ram. Odd characters are in bank // Even characters are in bank 0 ram. Odd characters are in bank
// 1 ram. Technically, this would need 560 columns to work // 1 ram. Draw to the physical display and let it figure out
// correctly - and I don't have that, so it's going to be a bit // whether or not there are enough physical pixels to display
// wonky. // the 560 columns we'd need for this.
//
// 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.
// Draw the first of two characters // Draw the first of two characters
cptr = xlateChar(mmu->readDirect(addr, 1), &invert); cptr = xlateChar(mmu->readDirect(addr, 1), &invert);
for (uint8_t y2 = 0; y2<8; y2++) { for (uint8_t y2 = 0; y2<8; y2++) {
uint8_t d = *(cptr + y2); uint8_t d = *(cptr + y2);
for (uint8_t x2 = 0; x2 <= 7; x2+=2) { for (uint8_t x2 = 0; x2 <= 7; x2++) {
uint16_t basex = ((col * 2) * 7) & 0xFFFE; // even aligned uint16_t basex = (col*2)*7;
bool pixelOn = ( (d & (1<<x2)) | (d & (1<<(x2+1))) ); bool pixelOn = (d & (1<<x2));
if (pixelOn) { if (pixelOn) {
uint8_t val = (invert ? c_black : textColor); uint8_t val = (invert ? c_black : c_white);
drawApplePixel(val, (basex+x2)/2, row*8+y2); g_display->cachePixel(basex + x2, row*8+y2, val);
} else { } else {
uint8_t val = (invert ? textColor : c_black); uint8_t val = (invert ? c_white : c_black);
drawApplePixel(val, (basex+x2)/2, row*8+y2); 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); cptr = xlateChar(mmu->readDirect(addr, 0), &invert);
for (uint8_t y2 = 0; y2<8; y2++) { for (uint8_t y2 = 0; y2<8; y2++) {
uint8_t d = *(cptr + y2); uint8_t d = *(cptr + y2);
for (uint8_t x2 = 0; x2 <= 7; x2+=2) { for (uint8_t x2 = 0; x2 <= 7; x2++) {
uint16_t basex = ((col * 2 + 1) * 7) & 0xFFFE; // even aligned -- +1 for the second character uint16_t basex = (col*2+1)*7;
bool pixelOn = ( (d & (1<<x2)) | (d & (1<<(x2+1))) ); bool pixelOn = (d & (1<<x2));
if (pixelOn) { if (pixelOn) {
uint8_t val = (invert ? c_black : textColor); uint8_t val = (invert ? c_black : c_white);
drawApplePixel(val, (basex+x2)/2, row*8+y2); g_display->cachePixel(basex + x2, row*8+y2, val);
} else { } else {
uint8_t val = (invert ? textColor : c_black); uint8_t val = (invert ? c_white : c_black);
drawApplePixel(val, (basex+x2)/2, row*8+y2); g_display->cachePixel(basex + x2, row*8+y2, val);
} }
} }
} }
@ -478,10 +438,10 @@ void AppleDisplay::redraw40ColumnText(uint8_t startingY)
uint8_t d = *(cptr + y2); uint8_t d = *(cptr + y2);
for (uint8_t x2 = 0; x2 < 7; x2++) { for (uint8_t x2 = 0; x2 < 7; x2++) {
if (d & 1) { 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); drawApplePixel(val, col*7+x2, row*8+y2);
} else { } else {
uint8_t val = (invert ? textColor : c_black); uint8_t val = (invert ? c_white : c_black);
drawApplePixel(val, col*7+x2, row*8+y2); drawApplePixel(val, col*7+x2, row*8+y2);
} }
d >>= 1; d >>= 1;
@ -646,7 +606,6 @@ void AppleDisplay::didRedraw()
void AppleDisplay::displayTypeChanged() void AppleDisplay::displayTypeChanged()
{ {
textColor = g_displayType == m_monochrome?c_green:c_white;
modeChange(); modeChange();
} }

View File

@ -39,7 +39,7 @@ class AppleMMU;
class AppleDisplay : public VMDisplay{ class AppleDisplay : public VMDisplay{
public: public:
AppleDisplay(uint8_t *vb); AppleDisplay();
virtual ~AppleDisplay(); virtual ~AppleDisplay();
virtual bool needsRedraw(); virtual bool needsRedraw();
virtual void didRedraw(); virtual void didRedraw();
@ -76,8 +76,6 @@ class AppleDisplay : public VMDisplay{
AiieRect dirtyRect; AiieRect dirtyRect;
uint16_t *switches; // pointer to the MMU's switches uint16_t *switches; // pointer to the MMU's switches
uint16_t textColor;
}; };
#endif #endif

View File

@ -9,6 +9,11 @@
AppleUI::AppleUI() AppleUI::AppleUI()
{ {
redrawFrame = false;
redrawDriveLatches = false;
redrawDriveActivity = false;
driveInserted[0] = driveInserted[1] = 0;
driveActivity[0] = driveActivity[1] = 0;
} }
AppleUI::~AppleUI() AppleUI::~AppleUI()
@ -20,58 +25,21 @@ void AppleUI::drawStaticUIElement(uint8_t element)
// Only one static UI element right now... // Only one static UI element right now...
if (element != UIeOverlay) if (element != UIeOverlay)
return; return;
redrawFrame = true;
g_display->drawImageOfSizeAt(displayBitmap, DBITMAP_WIDTH, DBITMAP_HEIGHT, 0, 0);
} }
void AppleUI::drawOnOffUIElement(uint8_t element, bool state) void AppleUI::drawOnOffUIElement(uint8_t element, bool state)
{ {
uint16_t xoff = 55; if (element == UIeDisk1_state ||
uint8_t yoff = 216; element == UIeDisk2_state) {
uint16_t xsize; driveInserted[element-UIeDisk1_state] = state;
uint8_t ysize; redrawDriveLatches = true;
const uint8_t *img; }
else if (element == UIeDisk1_activity ||
switch (element) { element == UIeDisk2_activity) {
case UIeDisk1_state: driveActivity[element-UIeDisk1_activity] = state;
xoff = 55; redrawDriveActivity = true;
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;
} }
g_display->drawImageOfSizeAt(img, xsize, ysize, xoff, yoff);
} }
void AppleUI::drawPercentageUIElement(uint8_t element, uint8_t percent) 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) { if (element != UIePowerPercentage) {
return; return;
} }
drawBatteryStatus(percent); // Temporarily disabled; the API for this needs updating for resolution-independent display coordinates
// drawBatteryStatus(percent);
} }
void AppleUI::drawBatteryStatus(uint8_t percent) void AppleUI::drawBatteryStatus(uint8_t percent)
{ {
uint16_t xoff = 301; uint16_t xoff = 301*2;
uint16_t yoff = 222; uint16_t yoff = 222;
// the area around the apple is 12 wide; it's exactly 11 high the // 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);
}
}
}

View File

@ -23,6 +23,15 @@ class AppleUI : public VMui {
virtual void drawPercentageUIElement(uint8_t element, uint8_t percent); virtual void drawPercentageUIElement(uint8_t element, uint8_t percent);
void drawBatteryStatus(uint8_t percent); void drawBatteryStatus(uint8_t percent);
virtual void blit();
private:
bool redrawFrame;
bool redrawDriveLatches;
bool redrawDriveActivity;
bool driveInserted[2];
bool driveActivity[2];
}; };

View File

@ -14,7 +14,7 @@ const char *suspendHdr = "Sus1";
AppleVM::AppleVM() AppleVM::AppleVM()
{ {
// FIXME: all this typecasting makes me knife-stabby // FIXME: all this typecasting makes me knife-stabby
vmdisplay = new AppleDisplay(videoBuffer); vmdisplay = new AppleDisplay();
mmu = new AppleMMU((AppleDisplay *)vmdisplay); mmu = new AppleMMU((AppleDisplay *)vmdisplay);
vmdisplay->SetMMU((AppleMMU *)mmu); vmdisplay->SetMMU((AppleMMU *)mmu);

View File

@ -29,7 +29,7 @@ enum {
#define NUM_TITLES 4 #define NUM_TITLES 4
const char *menuTitles[NUM_TITLES] = { "Aiie", "VM", "Hardware", "Disks" }; 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 }; const uint8_t aiieActions[] = { ACT_ABOUT };
@ -117,7 +117,6 @@ bool BIOS::runUntilDone()
currentCPUSpeedIndex = CPUSPEED_QUAD; currentCPUSpeedIndex = CPUSPEED_QUAD;
int8_t prevAction = ACT_EXIT; int8_t prevAction = ACT_EXIT;
bool volumeDidChange = 0;
while (1) { while (1) {
switch (prevAction = GetAction(prevAction)) { switch (prevAction = GetAction(prevAction)) {
case ACT_EXIT: case ACT_EXIT:
@ -141,7 +140,14 @@ bool BIOS::runUntilDone()
break; break;
case ACT_SPEED: case ACT_SPEED:
currentCPUSpeedIndex++; 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; currentCPUSpeedIndex %= 4;
#endif
switch (currentCPUSpeedIndex) { switch (currentCPUSpeedIndex) {
case CPUSPEED_HALF: case CPUSPEED_HALF:
g_speed = 1023000/2; g_speed = 1023000/2;
@ -209,14 +215,12 @@ bool BIOS::runUntilDone()
if (g_volume > 15) { if (g_volume > 15) {
g_volume = 15; g_volume = 15;
} }
volumeDidChange = true;
break; break;
case ACT_VOLMINUS: case ACT_VOLMINUS:
g_volume--; g_volume--;
if (g_volume < 0) { if (g_volume < 0) {
g_volume = 0; g_volume = 0;
} }
volumeDidChange = true;
break; break;
case ACT_SUSPEND: case ACT_SUSPEND:
@ -243,7 +247,7 @@ bool BIOS::runUntilDone()
g_display->blit(r); g_display->blit(r);
// return true if any persistent setting changed that we want to store in eeprom // return true if any persistent setting changed that we want to store in eeprom
return volumeDidChange; return true;
} }
void BIOS::WarmReset() void BIOS::WarmReset()

115
cpu.cpp
View File

@ -29,117 +29,6 @@
// serialize suspend/restore token // serialize suspend/restore token
#define CPUMAGIC 0x65 #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] = { optype_t opcodes[256] = {
{ O_BRK, A_IMP, 7 }, // 0x00 { O_BRK, A_IMP, 7 }, // 0x00
{ O_ORA , A_INX , 6 }, // 0x01 [2] i.e. "ORA ($44,X)" { 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_INY , A_IMP , 2 }, // 0xC8
{ O_CMP , A_IMM , 2 }, // 0xC9 { O_CMP , A_IMM , 2 }, // 0xC9
{ O_DEX , A_IMP , 2 }, // 0xCA { 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_CPY , A_ABS , 4 }, // 0xCC
{ O_CMP , A_ABS , 4 }, // 0xCD { O_CMP , A_ABS , 4 }, // 0xCD
{ O_DEC , A_ABS , 6 }, // 0xCE { O_DEC , A_ABS , 6 }, // 0xCE
@ -630,6 +519,7 @@ uint8_t Cpu::step()
// treat these as IMPLIED // treat these as IMPLIED
break; break;
case A_IMP: case A_IMP:
case A_ACC:
// implied: nothing to do. These have a parameter that refers to a // implied: nothing to do. These have a parameter that refers to a
// specific register or particular action to a register // specific register or particular action to a register
break; break;
@ -847,6 +737,7 @@ uint8_t Cpu::step()
SETNZY; SETNZY;
break; break;
case O_NOP: case O_NOP:
case O_WAI:
break; break;
case O_TAX: case O_TAX:
x = a; x = a;

113
cpu.h
View File

@ -6,6 +6,119 @@
class MMU; 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. // Flags (P) register bit definitions.
// Negative // Negative
#define F_N (1<<7) #define F_N (1<<7)

View File

@ -9,7 +9,7 @@ PhysicalSpeaker *g_speaker = NULL;
PhysicalPaddles *g_paddles = NULL; PhysicalPaddles *g_paddles = NULL;
PhysicalPrinter *g_printer = NULL; PhysicalPrinter *g_printer = NULL;
VMui *g_ui; VMui *g_ui;
int16_t g_volume = 15; int8_t g_volume = 15;
uint8_t g_displayType = 3; // FIXME m_perfectcolor uint8_t g_displayType = 3; // FIXME m_perfectcolor
VMRam g_ram; VMRam g_ram;
volatile bool g_inInterrupt = false; volatile bool g_inInterrupt = false;

View File

@ -1,3 +1,6 @@
#ifndef __GLOBALS_H
#define __GLOBALS_H
#include <stdint.h> #include <stdint.h>
#include "filemanager.h" #include "filemanager.h"
@ -40,7 +43,7 @@ extern PhysicalSpeaker *g_speaker;
extern PhysicalPaddles *g_paddles; extern PhysicalPaddles *g_paddles;
extern PhysicalPrinter *g_printer; extern PhysicalPrinter *g_printer;
extern VMui *g_ui; extern VMui *g_ui;
extern int16_t g_volume; extern int8_t g_volume;
extern uint8_t g_displayType; extern uint8_t g_displayType;
extern VMRam g_ram; extern VMRam g_ram;
extern volatile bool g_inInterrupt; extern volatile bool g_inInterrupt;
@ -48,3 +51,5 @@ extern volatile uint8_t g_debugMode;
extern bool g_prioritizeDisplay; extern bool g_prioritizeDisplay;
extern volatile bool g_biosInterrupt; extern volatile bool g_biosInterrupt;
extern uint32_t g_speed; extern uint32_t g_speed;
#endif

View File

@ -15,6 +15,7 @@
#include "linux-printer.h" #include "linux-printer.h"
#include "appleui.h" #include "appleui.h"
#include "bios.h" #include "bios.h"
#include "nix-prefs.h"
#include "globals.h" #include "globals.h"
@ -43,6 +44,8 @@ volatile bool wantSuspend = false;
volatile bool wantResume = false; volatile bool wantResume = false;
void doDebugging(); void doDebugging();
void readPrefs();
void writePrefs();
void sigint_handler(int n) void sigint_handler(int n)
{ {
@ -353,6 +356,9 @@ int main(int argc, char *argv[])
g_display->redraw(); g_display->redraw();
/* Load prefs & reset globals appropriately now */
readPrefs();
if (argc >= 2) { if (argc >= 2) {
printf("Inserting disk %s\n", argv[1]); printf("Inserting disk %s\n", argv[1]);
((AppleVM *)g_vm)->insertDisk(0, argv[1]); ((AppleVM *)g_vm)->insertDisk(0, argv[1]);
@ -385,7 +391,7 @@ int main(int argc, char *argv[])
if (bios.runUntilDone()) { if (bios.runUntilDone()) {
// if it returned true, we have something to store // if it returned true, we have something to store
// persistently in EEPROM. // persistently in EEPROM.
// writePrefs(); writePrefs();
} }
printf("BIOS done\n"); printf("BIOS done\n");
@ -419,8 +425,7 @@ int main(int argc, char *argv[])
// fill disk buffer when needed // fill disk buffer when needed
((AppleVM*)g_vm)->disk6->fillDiskBuffer(); ((AppleVM*)g_vm)->disk6->fillDiskBuffer();
// Make this a little friendlier, and the expense of some framerate? g_ui->blit();
// usleep(10000);
if (g_vm->vmdisplay->needsRedraw()) { if (g_vm->vmdisplay->needsRedraw()) {
AiieRect what = g_vm->vmdisplay->getDirtyRect(); AiieRect what = g_vm->vmdisplay->getDirtyRect();
// make sure to clear the flag before drawing; there's no lock // make sure to clear the flag before drawing; there's no lock
@ -538,3 +543,57 @@ void doDebugging()
break;*/ 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");
}

View File

@ -13,6 +13,9 @@
#include "apple/appleui.h" #include "apple/appleui.h"
#define SCREENINSET_X (18*2)
#define SCREENINSET_Y (13*2)
// RGB map of each of the lowres colors // RGB map of each of the lowres colors
const uint16_t loresPixelColors[16] = { 0x0000, // 0 black const uint16_t loresPixelColors[16] = { 0x0000, // 0 black
0xC006, // 1 magenta 0xC006, // 1 magenta
@ -34,6 +37,8 @@ const uint16_t loresPixelColors[16] = { 0x0000, // 0 black
FBDisplay::FBDisplay() FBDisplay::FBDisplay()
{ {
memset((void *)videoBuffer, 0, sizeof(videoBuffer));
fb_fd = open("/dev/fb0",O_RDWR); fb_fd = open("/dev/fb0",O_RDWR);
//Get variable screen information //Get variable screen information
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo); ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
@ -44,12 +49,11 @@ FBDisplay::FBDisplay()
ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo); ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
// This shouldn't be necessary, but I'm not seeing FBIOPUT working yet // 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 // request what we want, rather than hoping we got it
// 16bpp 320x240 (for now) vinfo.width = 640;
vinfo.width = 320; vinfo.height = 480;
vinfo.height = 240;
vinfo.bits_per_pixel = 16; vinfo.bits_per_pixel = 16;
int ret = ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo); int ret = ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo);
printf("Return from FBIOPUT_VSCREENINFO: %d\n", ret); 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 // primarily for the device, where it's in and out of the
// bios. Draws the background image. // bios. Draws the background image.
printf("redraw background\n");
g_ui->drawStaticUIElement(UIeOverlay); g_ui->drawStaticUIElement(UIeOverlay);
printf("static done\n");
if (g_vm && g_ui) { if (g_vm && g_ui) {
// determine whether or not a disk is inserted & redraw each drive // 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(UIeDisk1_state, ((AppleVM *)g_vm)->DiskName(0)[0] == '\0');
g_ui->drawOnOffUIElement(UIeDisk2_state, ((AppleVM *)g_vm)->DiskName(1)[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, void FBDisplay::drawImageOfSizeAt(const uint8_t *img,
uint16_t sizex, uint8_t sizey, uint16_t sizex, uint8_t sizey,
uint16_t wherex, uint8_t wherey) uint16_t wherex, uint8_t wherey)
@ -89,29 +94,49 @@ void FBDisplay::drawImageOfSizeAt(const uint8_t *img,
for (uint8_t y=0; y<sizey; y++) { for (uint8_t y=0; y<sizey; y++) {
for (uint16_t x=0; x<sizex; x++) { for (uint16_t x=0; x<sizex; x++) {
const uint8_t *p = &img[(y * sizex + x)*3]; const uint8_t *p = &img[(y * sizex + x)*3];
drawPixel(x+wherex, y+wherey, p[0], p[1], p[2]); drawPixel((x+wherex)*2, y+wherey, p[0], p[1], p[2]);
drawPixel((x+wherex)*2+1, y+wherey, p[0], p[1], p[2]);
} }
} }
} }
#define BASEX 18
#define BASEY 13
void FBDisplay::blit(AiieRect r) void FBDisplay::blit(AiieRect r)
{ {
uint8_t *videoBuffer = g_vm->videoBuffer; // FIXME: poking deep for (uint16_t y=r.top*2; y<r.bottom*2; y++) {
for (uint16_t x=r.left*2; x<r.right*2; x++) {
uint8_t colorIdx = videoBuffer[y*FBDISPLAY_WIDTH+x];
for (uint8_t y=0; y<192; y++) { uint16_t color;
for (uint16_t x=0; x<280; x++) { color = loresPixelColors[colorIdx];
uint16_t pixel = (y*DISPLAYRUN+x)/2;
uint8_t colorIdx; if (g_displayType == m_monochrome) {
if (x & 1) { // Tricky. Grayscale from luminance. Turn each value into a
colorIdx = videoBuffer[pixel] & 0x0F; // 5-bit value, so they have equal weights; then calculate
} else { // luminance; then turn that back in to 5/6/5.
colorIdx = videoBuffer[pixel] >> 4; 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; else if (g_displayType == m_blackAndWhite) {
*((uint16_t*)(fbp + location)) = loresPixelColors[colorIdx]; // 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 ) ); ( (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) 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; *((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) 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); *((uint16_t*)(fbp + location)) = _888to565(r,g,b);
} }
@ -203,10 +238,41 @@ void FBDisplay::flush()
void FBDisplay::clrScr() void FBDisplay::clrScr()
{ {
for (uint8_t y=0; y<vinfo.height; y++) { for (uint8_t y=0; y<vinfo.height/2; y++) { // /2 because drawPixel doubles vertically
for (uint16_t x=0; x<vinfo.width; x++) { for (uint16_t x=0; x<vinfo.width; x++) {
drawPixel(x, y, 0x0000); drawPixel(x, y, 0x0000);
} }
} }
} }
// All of these double vertically b/c we're 480, but the original is 240
void FBDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
{
videoBuffer[y*2*FBDISPLAY_WIDTH+x] = color;
videoBuffer[((y*2)+1)*FBDISPLAY_WIDTH+x] = color;
}
// "DoubleWide" means "please double the X because I'm in low-res width mode"
void FBDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color)
{
videoBuffer[y*2*FBDISPLAY_WIDTH+x*2] = color;
videoBuffer[y*2*FBDISPLAY_WIDTH+x*2+1] = color;
videoBuffer[(y*2+1)*FBDISPLAY_WIDTH+x*2] = color;
videoBuffer[(y*2+1)*FBDISPLAY_WIDTH+x*2+1] = color;
}
void FBDisplay::cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorB, uint8_t colorA)
{
videoBuffer[y*2*FBDISPLAY_WIDTH+x*2] = colorA;
videoBuffer[y*2*FBDISPLAY_WIDTH+x*2+1] = colorA;
videoBuffer[y*2*FBDISPLAY_WIDTH+x*2+2] = colorB;
videoBuffer[y*2*FBDISPLAY_WIDTH+x*2+3] = colorB;
videoBuffer[(y*2+1)*FBDISPLAY_WIDTH+x*2] = colorA;
videoBuffer[(y*2+1)*FBDISPLAY_WIDTH+x*2+1] = colorA;
videoBuffer[(y*2+1)*FBDISPLAY_WIDTH+x*2+2] = colorB;
videoBuffer[(y*2+1)*FBDISPLAY_WIDTH+x*2+3] = colorB;
}

View File

@ -6,6 +6,9 @@
#include "physicaldisplay.h" #include "physicaldisplay.h"
#define FBDISPLAY_WIDTH (320*2)
#define FBDISPLAY_HEIGHT (240*2)
class FBDisplay : public PhysicalDisplay { class FBDisplay : public PhysicalDisplay {
public: public:
FBDisplay(); FBDisplay();
@ -19,12 +22,21 @@ class FBDisplay : public PhysicalDisplay {
virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color); 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 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 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 drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str);
virtual void flush(); virtual void flush();
virtual void clrScr(); 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: private:
volatile uint8_t videoBuffer[FBDISPLAY_HEIGHT * FBDISPLAY_WIDTH];
int fb_fd; int fb_fd;
struct fb_fix_screeninfo finfo; struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo; struct fb_var_screeninfo vinfo;

View File

@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <linux/input.h> #include <linux/input.h>
#include <dirent.h>
#include "sdl-paddles.h" #include "sdl-paddles.h"
#include "globals.h" #include "globals.h"
@ -14,9 +15,25 @@
LinuxKeyboard::LinuxKeyboard(VMKeyboard *k) : PhysicalKeyboard(k) LinuxKeyboard::LinuxKeyboard(VMKeyboard *k) : PhysicalKeyboard(k)
{ {
fd = open("/dev/input/by-path/platform-20980000.usb-usb-0:1:1.0-event-kbd", // We want the first USB keyboard that we find. This object reads
O_RDONLY | O_NONBLOCK); // 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() LinuxKeyboard::~LinuxKeyboard()

134
nix/debugger.cpp Normal file
View File

@ -0,0 +1,134 @@
#include "debugger.h"
#include "globals.h"
#include "disassembler.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
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; idx<sizeof(cmdbuf); idx++) {
cmdbuf[idx] = g_vm->getMMU()->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;
}

22
nix/debugger.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __DEBUGGER_H
#define __DEBUGGER_H
#include <pthread.h>
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

166
nix/disassembler.cpp Executable file
View File

@ -0,0 +1,166 @@
#include "disassembler.h"
#include "cpu.h"
#include <stdio.h>
// 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);
}

15
nix/disassembler.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __DISASSEMBLER_H
#define __DISASSEMBLER_H
#include <stdint.h>
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

66
nix/nix-prefs.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "nix-prefs.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <string.h>
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;
}

18
nix/nix-prefs.h Normal file
View File

@ -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

View File

@ -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, 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 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; 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: protected:
char overlayMessage[40]; char overlayMessage[40];
}; };

41
prefsstore.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __PREFSSTORE_H
#define __PREFSSTORE_H
#include <stdint.h>
// 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

View File

@ -13,6 +13,8 @@
#include "sdl-printer.h" #include "sdl-printer.h"
#include "appleui.h" #include "appleui.h"
#include "bios.h" #include "bios.h"
#include "nix-prefs.h"
#include "debugger.h"
#include "globals.h" #include "globals.h"
@ -20,10 +22,11 @@
//#define SHOWFPS //#define SHOWFPS
//#define SHOWPC //#define SHOWPC
//#define DEBUGCPU #define DEBUGCPU
//#define SHOWMEMPAGE //#define SHOWMEMPAGE
BIOS bios; BIOS bios;
Debugger debugger;
static struct timespec nextInstructionTime, startTime; static struct timespec nextInstructionTime, startTime;
@ -40,7 +43,11 @@ char disk2name[256] = "\0";
volatile bool wantSuspend = false; volatile bool wantSuspend = false;
volatile bool wantResume = false; volatile bool wantResume = false;
volatile bool cpuDebuggerRunning = false;
void doDebugging(); void doDebugging();
void readPrefs();
void writePrefs();
void sigint_handler(int n) void sigint_handler(int n)
{ {
@ -172,23 +179,12 @@ static void *cpu_thread(void *dummyptr) {
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles); ((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
#ifdef DEBUGCPU #ifdef DEBUGCPU
{ debugger.step();
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 #endif
if (send_rst) { if (send_rst) {
cpuDebuggerRunning = true;
#if 0 #if 0
printf("Scheduling suspend request...\n"); printf("Scheduling suspend request...\n");
wantSuspend = true; wantSuspend = true;
@ -320,6 +316,9 @@ int main(int argc, char *argv[])
// g_display->blit(); // g_display->blit();
g_display->redraw(); g_display->redraw();
/* Load prefs & reset globals appropriately now */
readPrefs();
if (argc >= 2) { if (argc >= 2) {
printf("Inserting disk %s\n", argv[1]); printf("Inserting disk %s\n", argv[1]);
((AppleVM *)g_vm)->insertDisk(0, argv[1]); ((AppleVM *)g_vm)->insertDisk(0, argv[1]);
@ -350,7 +349,7 @@ int main(int argc, char *argv[])
printf("Invoking BIOS\n"); printf("Invoking BIOS\n");
if (bios.runUntilDone()) { if (bios.runUntilDone()) {
// if it returned true, we have something to store persistently in EEPROM. // if it returned true, we have something to store persistently in EEPROM.
// writePrefs(); writePrefs();
} }
printf("BIOS done\n"); printf("BIOS done\n");
@ -384,8 +383,6 @@ int main(int argc, char *argv[])
// fill disk buffer when needed // fill disk buffer when needed
((AppleVM*)g_vm)->disk6->fillDiskBuffer(); ((AppleVM*)g_vm)->disk6->fillDiskBuffer();
// Make this a little friendlier, and the expense of some framerate?
// usleep(10000);
if (g_vm->vmdisplay->needsRedraw()) { if (g_vm->vmdisplay->needsRedraw()) {
AiieRect what = g_vm->vmdisplay->getDirtyRect(); AiieRect what = g_vm->vmdisplay->getDirtyRect();
// make sure to clear the flag before drawing; there's no lock // 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_vm->vmdisplay->didRedraw();
g_display->blit(what); g_display->blit(what);
} }
g_ui->blit();
g_printer->update(); g_printer->update();
g_keyboard->maintainKeyboard(); g_keyboard->maintainKeyboard();
@ -504,3 +502,57 @@ void doDebugging()
break;*/ 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");
}

View File

@ -9,6 +9,9 @@
#include "apple/appleui.h" #include "apple/appleui.h"
#define SCREENINSET_X (18*2)
#define SCREENINSET_Y (13*2)
// RGB map of each of the lowres colors // RGB map of each of the lowres colors
const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black
{ 195, 0, 48 }, // magenta { 195, 0, 48 }, // magenta
@ -30,6 +33,8 @@ const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black
SDLDisplay::SDLDisplay() SDLDisplay::SDLDisplay()
{ {
memset(videoBuffer, 0, sizeof(videoBuffer));
// FIXME: abstract constants // FIXME: abstract constants
screen = SDL_CreateWindow("Aiie!", screen = SDL_CreateWindow("Aiie!",
SDL_WINDOWPOS_UNDEFINED, 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, void SDLDisplay::drawImageOfSizeAt(const uint8_t *img,
uint16_t sizex, uint8_t sizey, uint16_t sizex, uint8_t sizey,
uint16_t wherex, uint8_t wherey) uint16_t wherex, uint8_t wherey)
@ -77,34 +85,40 @@ void SDLDisplay::drawImageOfSizeAt(const uint8_t *img,
for (uint16_t x=0; x<sizex; x++) { for (uint16_t x=0; x<sizex; x++) {
const uint8_t *p = &img[(y * sizex + x)*3]; const uint8_t *p = &img[(y * sizex + x)*3];
p = &img[(y * sizex + x)*3]; p = &img[(y * sizex + x)*3];
drawPixel(x+wherex, y+wherey, p[0], p[1], p[2]); drawPixel((x+wherex)*2, y+wherey, p[0], p[1], p[2]);
drawPixel((x+wherex)*2+1, y+wherey, p[0], p[1], p[2]);
} }
} }
} }
#define BASEX 36 // Blit the videoBuffer to the display device, in the rect given
#define BASEY 26
void SDLDisplay::blit(AiieRect r) void SDLDisplay::blit(AiieRect r)
{ {
uint8_t *videoBuffer = g_vm->videoBuffer; // 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 y=r.top*2; y<r.bottom*2; y++) {
for (uint16_t x=0; x<280; x++) { for (uint16_t x=r.left*2; x<r.right*2; x++) {
uint16_t pixel = (y*DISPLAYRUN+x)/2; uint8_t colorIdx = videoBuffer[y*SDLDISPLAY_WIDTH+x];
uint8_t colorIdx;
if (x & 1) { uint8_t r, g, b;
colorIdx = videoBuffer[pixel] & 0x0F; r = loresPixelColors[colorIdx][0];
} else { g = loresPixelColors[colorIdx][1];
colorIdx = videoBuffer[pixel] >> 4; b = loresPixelColors[colorIdx][2];
if (g_displayType == m_monochrome) {
float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b;
r = b = 0;
g = fv;
} }
for (uint8_t xoff=0; xoff<2; xoff++) { else if (g_displayType == m_blackAndWhite) {
for (uint8_t yoff=0; yoff<2; yoff++) { float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b;
// FIXME: validate BPP >= 3? r = g = b = fv;
SDL_SetRenderDrawColor(renderer, loresPixelColors[colorIdx][0], loresPixelColors[colorIdx][1], loresPixelColors[colorIdx][2], 255);
SDL_RenderDrawPoint(renderer, x*2+xoff+BASEX, y*2+yoff+BASEY);
}
} }
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); 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) void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
{ {
uint8_t uint8_t
@ -129,21 +149,17 @@ void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
b = (color & 0x1F) << 3; b = (color & 0x1F) << 3;
// Pixel-doubling // Pixel-doubling vertically
for (int yoff=0; yoff<2; yoff++) { for (int yoff=0; yoff<2; yoff++) {
for (int xoff=0; xoff<2; xoff++) { putpixel(renderer, x, yoff+y*2, r, g, b);
putpixel(renderer, xoff+x*2, yoff+y*2, r, g, b);
}
} }
} }
void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t 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 yoff=0; yoff<2; yoff++) {
for (int xoff=0; xoff<2; xoff++) { putpixel(renderer, x, yoff+y*2, r, g, b);
putpixel(renderer, xoff+x*2, yoff+y*2, r, g, b);
}
} }
} }
@ -209,3 +225,35 @@ void SDLDisplay::clrScr()
SDL_RenderPresent(renderer); // perform the render 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;
}

View File

@ -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, uint16_t color);
virtual void drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b); 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 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 drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str);
virtual void clrScr(); 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: private:
uint8_t videoBuffer[SDLDISPLAY_HEIGHT * SDLDISPLAY_WIDTH];
SDL_Window *screen; SDL_Window *screen;
SDL_Renderer *renderer; SDL_Renderer *renderer;
}; };

1
teensy/prefsstore.h Symbolic link
View File

@ -0,0 +1 @@
../prefsstore.h

View File

@ -36,26 +36,64 @@
#include "applevm.h" #include "applevm.h"
// RGB map of each of the lowres colors // RGB map of each of the lowres colors
const uint16_t loresPixelColors[16] = { 0x0000, // 0 black const uint8_t loresPixelColors[16*2] = { 0x00,0x00, // 0 black
0xC006, // 1 magenta 0xC0,0x06, // 1 magenta
0x0010, // 2 dark blue 0x00,0x10, // 2 dark blue
0xA1B5, // 3 purple 0xA1,0xB5, // 3 purple
0x0480, // 4 dark green 0x04,0x80, // 4 dark green
0x6B4D, // 5 dark grey 0x6B,0x4D, // 5 dark grey
0x1B9F, // 6 med blue 0x1B,0x9F, // 6 med blue
0x0DFD, // 7 light blue 0x0D,0xFD, // 7 light blue
0x92A5, // 8 brown 0x92,0xA5, // 8 brown
0xF8C5, // 9 orange 0xF8,0xC5, // 9 orange
0x9555, // 10 light gray 0x95,0x55, // 10 light gray
0xFCF2, // 11 pink 0xFC,0xF2, // 11 pink
0x07E0, // 12 green 0x07,0xE0, // 12 green
0xFFE0, // 13 yellow 0xFF,0xE0, // 13 yellow
0x87F0, // 14 aqua 0x87,0xF0, // 14 aqua
0xFFFF // 15 white 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() TeensyDisplay::TeensyDisplay()
{ {
memset(videoBuffer, 0, sizeof(videoBuffer));
pinMode(DB_8, OUTPUT); pinMode(DB_8, OUTPUT);
pinMode(DB_9, OUTPUT); pinMode(DB_9, OUTPUT);
pinMode(DB_10, OUTPUT); pinMode(DB_10, OUTPUT);
@ -98,14 +136,19 @@ TeensyDisplay::TeensyDisplay()
cbi(P_CS, B_CS); 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(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(0x0C,0x0000); // power control2 [0]
LCD_Write_COM_DATA(0x0D,0x080C); // power control3 [VRH3, VRH2, invalid bits] 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(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(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 // 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,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 // 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(0x16,0xEF1C); // horiz porch (default)
LCD_Write_COM_DATA(0x17,0x0003); // vertical porch 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(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(0x0F,0x0000); // gate scan start posn
LCD_Write_COM_DATA(0x41,0x0000); // vertical scroll control1 LCD_Write_COM_DATA(0x41,0x0000); // vertical scroll control1
LCD_Write_COM_DATA(0x42,0x0000); // vertical scroll control2 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(0x25,0x8000); // frame frequency (OSC3)
LCD_Write_COM_DATA(0x4f,0x0000); // Set GDDRAM Y address counter LCD_Write_COM_DATA(0x4f,0x0000); // Set GDDRAM Y address counter
LCD_Write_COM_DATA(0x4e,0x0000); // Set GDDRAM X address counter LCD_Write_COM_DATA(0x4e,0x0000); // Set GDDRAM X address counter
#if 0 #if 1
// Set data access speed optimization (?) // 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(0x28, 0x0006);
LCD_Write_COM_DATA(0x2F, 0x12BE); LCD_Write_COM_DATA(0x2F, 0x12BE);
LCD_Write_COM_DATA(0x12, 0x6CEB); LCD_Write_COM_DATA(0x12, 0x6CEB);
@ -287,6 +331,11 @@ void TeensyDisplay::drawPixel(uint16_t x, uint16_t y)
clrXY(); 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) void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
{ {
cbi(P_CS, B_CS); cbi(P_CS, B_CS);
@ -361,8 +410,6 @@ void TeensyDisplay::drawNextPixel(uint16_t color)
void TeensyDisplay::blit(AiieRect r) 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. // remember these are "starts at pixel number" values, where 0 is the first.
#define HOFFSET 18 #define HOFFSET 18
#define VOFFSET 13 #define VOFFSET 13
@ -380,17 +427,37 @@ void TeensyDisplay::blit(AiieRect r)
// send the pixel data // send the pixel data
sbi(P_RS, B_RS); sbi(P_RS, B_RS);
uint16_t pixel; uint8_t *vbufPtr;
for (uint8_t y=r.top; y<=r.bottom; y++) { 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++) { for (uint16_t x=r.left; x<=r.right; x++) {
pixel = y * (DISPLAYRUN >> 1) + (x >> 1);
uint8_t colorIdx; uint8_t colorIdx;
if (!(x & 0x01)) { if (!(x & 0x01)) {
colorIdx = videoBuffer[pixel] >> 4; colorIdx = *vbufPtr >> 4;
} else { } 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); 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);
}
}

View File

@ -4,8 +4,10 @@
#include <Arduino.h> #include <Arduino.h>
#include "physicaldisplay.h" #include "physicaldisplay.h"
#define TEENSY_DHEIGHT 240 #define TEENSY_DHEIGHT 192
#define TEENSY_DWIDTH 320 #define TEENSY_DWIDTH 280
// run length of one row of pixels
#define TEENSY_DRUN (TEENSY_DWIDTH/2)
#define regtype volatile uint8_t #define regtype volatile uint8_t
#define regsize 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 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: protected:
void moveTo(uint16_t col, uint16_t row); void moveTo(uint16_t col, uint16_t row);
void drawNextPixel(uint16_t color); 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);
virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color); 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 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_Writ_Bus(uint8_t VH,uint8_t VL) __attribute__((always_inline));
inline void LCD_Write_COM(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 needsRedraw;
bool driveIndicator[2]; bool driveIndicator[2];
bool driveIndicatorDirty; bool driveIndicatorDirty;
// video buffer is 4bpp
uint8_t videoBuffer[TEENSY_DHEIGHT * TEENSY_DWIDTH / 2];
}; };
#endif #endif

41
teensy/teensy-prefs.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "teensy-prefs.h"
#include <EEPROM.h>
TeensyPrefs::TeensyPrefs()
{
}
TeensyPrefs::~TeensyPrefs()
{
}
bool TeensyPrefs::readPrefs(prefs_t *readTo)
{
uint8_t *pp = (uint8_t *)readTo;
for (uint16_t i=0; i<sizeof(prefs_t); i++) {
*pp++ = EEPROM.read(i);
}
if (readTo->magic != 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<sizeof(prefs_t); i++) {
EEPROM.write(i, *pp++);
}
return true;
}

15
teensy/teensy-prefs.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _TEENSY_PREFSSTORE_H
#define _TEENSY_PREFSSTORE_H
#include "prefsstore.h"
class TeensyPrefs : public PrefsStore {
public:
TeensyPrefs();
virtual ~TeensyPrefs();
virtual bool readPrefs(prefs_t *readTo);
virtual bool writePrefs(prefs_t *newPrefs);
};
#endif

View File

@ -1,6 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include <SPI.h> #include <SPI.h>
#include <EEPROM.h>
#include <TimeLib.h> #include <TimeLib.h>
#include <TimerOne.h> #include <TimerOne.h>
#include "bios.h" #include "bios.h"
@ -12,6 +11,8 @@
#include "teensy-paddles.h" #include "teensy-paddles.h"
#include "teensy-filemanager.h" #include "teensy-filemanager.h"
#include "appleui.h" #include "appleui.h"
#include "teensy-prefs.h"
#define RESETPIN 39 #define RESETPIN 39
#define BATTERYPIN 32 #define BATTERYPIN 32
#define SPEAKERPIN A21 #define SPEAKERPIN A21
@ -192,6 +193,7 @@ void biosInterrupt()
g_speaker->maintainSpeaker(-1, -1); g_speaker->maintainSpeaker(-1, -1);
// Force the display to redraw // Force the display to redraw
g_display->redraw();
((AppleDisplay*)(g_vm->vmdisplay))->modeChange(); ((AppleDisplay*)(g_vm->vmdisplay))->modeChange();
// Poll the keyboard before we start, so we can do selftest on startup // 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 - // but the display tears. So there's a global - g_prioritizeDisplay -
// which lets the user pick which they want. // which lets the user pick which they want.
g_prioritizeDisplay = false;
if (g_prioritizeDisplay) if (g_prioritizeDisplay)
Timer1.stop(); Timer1.stop();
g_ui->blit();
g_vm->vmdisplay->lockDisplay(); g_vm->vmdisplay->lockDisplay();
if (g_vm->vmdisplay->needsRedraw()) { if (g_vm->vmdisplay->needsRedraw()) {
AiieRect what = g_vm->vmdisplay->getDirtyRect(); 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() void readPrefs()
{ {
prefs p; TeensyPrefs np;
uint8_t *pp = (uint8_t *)&p; prefs_t p;
if (np.readPrefs(&p)) {
Serial.println("reading prefs");
for (uint8_t i=0; i<sizeof(prefs); i++) {
*pp++ = EEPROM.read(i);
}
if (p.magic == MAGIC) {
// looks valid! Use it.
Serial.println("prefs valid! Restoring volume");
if (p.volume > 15) {
p.volume = 15;
}
if (p.volume < 0) {
p.volume = 0;
}
g_volume = p.volume; 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 if (p.hd1[0]) {
g_volume = 0; ((AppleVM *)g_vm)->insertHD(0, p.hd1);
}
if (p.hd2[0]) {
((AppleVM *)g_vm)->insertHD(1, p.hd2);
}
}
} }
void writePrefs() void writePrefs()
{ {
Serial.println("writing prefs"); TeensyPrefs np;
Timer1.stop(); prefs_t p;
prefs p; g_display->clrScr();
uint8_t *pp = (uint8_t *)&p; 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.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<sizeof(prefs); i++) { Timer1.stop();
EEPROM.write(i, *pp++); bool ret = np.writePrefs(&p);
}
Timer1.start(); Timer1.start();
} }

3
vm.h
View File

@ -10,7 +10,6 @@
#define DISPLAYWIDTH 320 #define DISPLAYWIDTH 320
#define DISPLAYHEIGHT 240 #define DISPLAYHEIGHT 240
#define DISPLAYRUN 320 // how wide each row is in pixels in the buffer (for faster math)
class VM { class VM {
public: public:
@ -28,8 +27,6 @@ class VM {
virtual void triggerPaddleInCycles(uint8_t paddleNum, uint16_t cycleCount) = 0; virtual void triggerPaddleInCycles(uint8_t paddleNum, uint16_t cycleCount) = 0;
uint8_t videoBuffer[DISPLAYRUN * DISPLAYHEIGHT / 2];
VMDisplay *vmdisplay; VMDisplay *vmdisplay;
MMU *mmu; MMU *mmu;
bool hasIRQ; bool hasIRQ;

View File

@ -14,8 +14,8 @@ typedef struct {
class VMDisplay { class VMDisplay {
public: public:
VMDisplay(uint8_t *vb) { videoBuffer = vb; } VMDisplay() { }
virtual ~VMDisplay() { videoBuffer = NULL; }; virtual ~VMDisplay() { };
virtual void SetMMU(MMU *m) { mmu = m; } virtual void SetMMU(MMU *m) { mmu = m; }
@ -27,7 +27,6 @@ class VMDisplay {
virtual void unlockDisplay() = 0; virtual void unlockDisplay() = 0;
MMU *mmu; MMU *mmu;
uint8_t *videoBuffer;
}; };
#endif #endif

2
vmui.h
View File

@ -32,6 +32,8 @@ class VMui
virtual void drawStaticUIElement(uint8_t element) = 0; virtual void drawStaticUIElement(uint8_t element) = 0;
virtual void drawOnOffUIElement(uint8_t element, bool state) = 0; // on or off virtual void drawOnOffUIElement(uint8_t element, bool state) = 0; // on or off
virtual void drawPercentageUIElement(uint8_t element, uint8_t percent) = 0; // 0-100 virtual void drawPercentageUIElement(uint8_t element, uint8_t percent) = 0; // 0-100
virtual void blit() = 0; // perform any drawing
}; };
#endif #endif