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

View File

@ -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<<x2)) | (d & (1<<(x2+1))) );
for (uint8_t x2 = 0; x2 <= 7; x2++) {
uint16_t basex = (col*2)*7;
bool pixelOn = (d & (1<<x2));
if (pixelOn) {
uint8_t val = (invert ? c_black : textColor);
drawApplePixel(val, (basex+x2)/2, row*8+y2);
uint8_t val = (invert ? c_black : c_white);
g_display->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<<x2)) | (d & (1<<(x2+1))) );
for (uint8_t x2 = 0; x2 <= 7; x2++) {
uint16_t basex = (col*2+1)*7;
bool pixelOn = (d & (1<<x2));
if (pixelOn) {
uint8_t val = (invert ? c_black : textColor);
drawApplePixel(val, (basex+x2)/2, row*8+y2);
uint8_t val = (invert ? c_black : c_white);
g_display->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();
}

View File

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

View File

@ -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);
}
}
}

View File

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

View File

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

View File

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

115
cpu.cpp
View File

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

113
cpu.h
View File

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

View File

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

View File

@ -1,3 +1,6 @@
#ifndef __GLOBALS_H
#define __GLOBALS_H
#include <stdint.h>
#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

View File

@ -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");
}

View File

@ -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; y<sizey; y++) {
for (uint16_t x=0; x<sizex; x++) {
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)
{
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++) {
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;
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<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++) {
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"
#define FBDISPLAY_WIDTH (320*2)
#define FBDISPLAY_HEIGHT (240*2)
class FBDisplay : public PhysicalDisplay {
public:
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, 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 flush();
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:
volatile uint8_t videoBuffer[FBDISPLAY_HEIGHT * FBDISPLAY_WIDTH];
int fb_fd;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;

View File

@ -6,6 +6,7 @@
#include <string.h>
#include <fcntl.h>
#include <linux/input.h>
#include <dirent.h>
#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()

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;
</