diff --git a/.gitignore b/.gitignore index 97bf4a3..6372f9a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ apple/hd32-rom.h aiie-sdl mockingboard-d.rom *.d - +*.dSYM +suspend.vm diff --git a/Makefile b/Makefile index dcf8e35..1997de9 100755 --- a/Makefile +++ b/Makefile @@ -8,19 +8,19 @@ CXXFLAGS=-Wall -I/usr/include/SDL2 -I .. -I . -I apple -I nix -I sdl -I/usr/loca TSRC=cpu.cpp util/testharness.cpp -COMMONSRCS=cpu.cpp apple/appledisplay.cpp apple/applekeyboard.cpp apple/applemmu.cpp apple/applevm.cpp apple/diskii.cpp apple/nibutil.cpp LRingBuffer.cpp globals.cpp apple/parallelcard.cpp apple/fx80.cpp lcg.cpp apple/hd32.cpp images.cpp apple/appleui.cpp vmram.cpp bios.cpp apple/noslotclock.cpp apple/woz.cpp apple/crc32.c apple/woz-serializer.cpp +COMMONSRCS=cpu.cpp apple/appledisplay.cpp apple/applekeyboard.cpp apple/applemmu.cpp apple/applevm.cpp apple/diskii.cpp apple/nibutil.cpp LRingBuffer.cpp globals.cpp apple/parallelcard.cpp apple/fx80.cpp lcg.cpp apple/hd32.cpp images.cpp apple/appleui.cpp vmram.cpp bios.cpp apple/noslotclock.cpp apple/woz.cpp apple/crc32.c apple/woz-serializer.cpp apple/mouse.c -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 apple/woz.o apple/crc32.o apple/woz-serializer.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 apple/woz.o apple/crc32.o apple/woz-serializer.o apple/mouse.o FBSRCS=linuxfb/linux-speaker.cpp linuxfb/fb-display.cpp linuxfb/linux-keyboard.cpp linuxfb/fb-paddles.cpp nix/nix-filemanager.cpp linuxfb/aiie.cpp linuxfb/linux-printer.cpp nix/nix-clock.cpp nix/nix-prefs.cpp 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 -SDLSRCS=sdl/sdl-speaker.cpp sdl/sdl-display.cpp sdl/sdl-keyboard.cpp sdl/sdl-paddles.cpp nix/nix-filemanager.cpp sdl/aiie.cpp sdl/sdl-printer.cpp nix/nix-clock.cpp nix/nix-prefs.cpp nix/debugger.cpp nix/disassembler.cpp +SDLSRCS=sdl/sdl-speaker.cpp sdl/sdl-display.cpp sdl/sdl-keyboard.cpp sdl/sdl-paddles.cpp nix/nix-filemanager.cpp sdl/aiie.cpp sdl/sdl-printer.cpp nix/nix-clock.cpp nix/nix-prefs.cpp nix/debugger.cpp nix/disassembler.cpp sdl/sdl-mouse.cpp -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 +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 sdl/sdl-mouse.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 .PHONY: roms clean @@ -40,7 +40,7 @@ test: $(TSRC) ./testharness -f tests/65c02-all.bin -s 0x200 roms: apple2e.rom disk.rom parallel.rom HDDRVR.BIN - ./util/genrom.pl apple2e.rom disk.rom parallel.rom HDDRVR.BIN + ./util/genrom.pl apple2e.rom disk.rom parallel.rom HDDRVR.BIN apple/applemmu-rom.h: roms diff --git a/apple/appledisplay.cpp b/apple/appledisplay.cpp index 1494684..ea2c4d5 100644 --- a/apple/appledisplay.cpp +++ b/apple/appledisplay.cpp @@ -216,10 +216,13 @@ inline void AppleDisplay::Draw14DoubleHiresPixelsAt(uint16_t addr) 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. + // the physical display figure out if they need to be reduced to B&W or not + // (for the most part - the m_blackAndWhite piece here allows full-res displays + // to give the crispest resolution.) uint8_t color = bitTrain & 0x0F; - + if (g_displayType == m_blackAndWhite) { color = c_white; } + g_display->cachePixel((col*2)+(xoff*2), row, ((bitTrain & 0x01) ? color : c_black)); diff --git a/apple/applemmu.cpp b/apple/applemmu.cpp index 8f6c789..84216e2 100644 --- a/apple/applemmu.cpp +++ b/apple/applemmu.cpp @@ -55,16 +55,24 @@ page 0-1 4 pages (1k) [altzp] 0xC1-0xCF 15 * 2 pages = 30 pages (7.5k) [intcxrom, slotLatch] 0xD0 - 0xDF 16 * 5 pages = 80 pages (20k) [altzp, bank2, {r,w}bsr] 0xE0 - 0xFF 32 * 3 pages = 96 pages (24k) [altzp, {r,w}bsr] +... plus 8 additional pages for the mouse ROM -= 147.75k (591 pages) stored off-chip += 147.75k (591 pages) stored off-chip (+ 8 more) Current read page table [512 bytes] in real ram Current write page table [512 bytes] in real ram */ -// This has a split memory model so more often addressed pages can be -// in internal memory, and an external memory can hold others. +// All the pages. Because we don't have enough RAM for both the +// display's DMA and the Apple's 148k (128k + ROM space), we're using +// an external SRAM for some of this. Anything that's accessed very +// often should be in the *low* pages, b/c those are in internal +// Teensy RAM. When we run out of preallocated RAM (cf. vmram.h), we +// fall over to an external 256kB SRAM (which is much slower). +// +// Zero page (and its alts) are the most used pages (the stack is in +// page 1). // // We want the video display pages in real RAM as much as possible, // since blits wind up touching so much of it. If we can keep that in @@ -81,19 +89,19 @@ enum { MP_ZP = 0, // page 0/1 * 2 page variants = 4; 0..3 MP_4 = 4, // 0x04 - 0x07 (text display pages) * 2 variants = 8; 4..11 MP_20 = 12, // 0x20 - 0x5F * 2 variants = 128; 12..139 - // Pages that can go to external RAM if needed: + // Pages that can go to external SRAM: MP_2 = 140, // 0x02 - 0x03 * 2 variants = 4; 140..143 MP_8 = 144, // 0x08 - 0x1F * 2 = 48; 144..191 MP_60 = 192, // 0x60 - 0xBF * 2 = 192; 192..383 - MP_C1 = 384, // start of 0xC1-0xCF * 2 page variants = 30; 384-413 - MP_D0 = 414, // start of 0xD0-0xDF * 5 page variants = 80; 414-493 - MP_E0 = 493, // start of 0xE0-0xFF * 3 page variants = 96; 494-589 - MP_C0 = 590 - // = 591 pages in all (147.75k) + MP_C1 = 384, // start of 0xC1-0xC7 * 2 page variants = 14; 384-397 + MP_C8 = 398, // 0xc8 - 0xcf, 3 page variants = 24; 398 - 421 + MP_D0 = 422, // start of 0xD0-0xDF * 5 page variants = 80; 422 - 501 + MP_E0 = 502, // start of 0xE0-0xFF * 3 page variants = 96; 502 - 597 + MP_C0 = 598 + // = 599 pages in all (149.75k) }; - static uint16_t _pageNumberForRam(uint8_t highByte, uint8_t variant) { if (highByte <= 1) { @@ -118,11 +126,14 @@ static uint16_t _pageNumberForRam(uint8_t highByte, uint8_t variant) if (highByte == 0xc0) { return MP_C0; } - if (highByte <= 0xCF) { - // 0xC1-0xCF 15 * 2 pages = 30 pages (7.5k) + if (highByte <= 0xC7) { + // 0xC1-0xC7 return ((highByte - 0xC1) * 2 + variant + MP_C1); } - + if (highByte <= 0xCF) { + // bank-switched ROM. 0 = built-in; 1 = 80-column (slot 3); 2 = mouse (slot 4) + return ((highByte - 0xC8) * 3 + variant + MP_C8); + } if (highByte <= 0xDF) { // 0xD0 - 0xDF 16 * 5 pages = 80 pages (20k) return ((highByte - 0xD0) * 5 + variant + MP_D0); @@ -220,6 +231,8 @@ bool AppleMMU::Deserialize(int8_t fd) return false; } + + void AppleMMU::Reset() { resetRAM(); @@ -914,6 +927,9 @@ void AppleMMU::resetRAM() // Load system ROM for (uint16_t i=0x80; i<=0xFF; i++) { + uint16_t page0 = _pageNumberForRam(i, 0); + uint16_t page1 = _pageNumberForRam(i, 1); + for (uint16_t k=0; k<0x100; k++) { uint16_t idx = ((i-0x80) << 8) | k; #ifdef TEENSYDUINO @@ -921,36 +937,30 @@ void AppleMMU::resetRAM() #else uint8_t v = romData[idx]; #endif - for (int j=0; j<5; j++) { - // For the ROM section from 0xc100 .. 0xcfff, we load in to - // an alternate page space (INTCXROM). - uint16_t page0 = _pageNumberForRam(i, 0); + // The space from 0xc1 through 0xcf is ROM image territory. We + // load the C3 ROM in to page 0, but not page 1; and then we + // load c800.CFFF to both main ROM (page 0) and the C3 aux ROM + // (page 1) to convince the VM that we've got 128k of RAM and an + // 80-column card. - if (i >= 0xc1 && i <= 0xcf) { - // If we want to convince the VM we've got 128k of RAM, we - // need to load C3 ROM in page 0 (but not 1, meaning there's - // a board installed); and C800.CFFF in both page [0] and [1] - // (meaning there's an extended 80-column ROM available, - // that is also physically in the slot). - // Everything else goes in page [1]. - - uint16_t page1 = _pageNumberForRam(i, 1); - - if (i == 0xc3) { - g_ram.writeByte((page0 << 8) | (k & 0xFF), v); - } - else if (i >= 0xc8) { - g_ram.writeByte((page0 << 8) | (k & 0xFF), v); - g_ram.writeByte((page1 << 8) | (k & 0xFF), v); - } - else { - g_ram.writeByte((page1 << 8) | (k & 0xFF), v); - } - } else { - // Everything else goes in page 0. + if (i >= 0xc1 && i <= 0xcf) { + if (i == 0xc3) { + // C300..C3FF => built-in ROM g_ram.writeByte((page0 << 8) | (k & 0xFF), v); } + else if (i >= 0xc8) { + // C800..CFFF => built-in ROM and slot 3 extended ROM + g_ram.writeByte((page0 << 8) | (k & 0xFF), v); + g_ram.writeByte((page1 << 8) | (k & 0xFF), v); + } + else { + // C000..C2FF and C400..c7FF are main ROM + g_ram.writeByte((page1 << 8) | (k & 0xFF), v); + } + } else { + // Everything else goes in page 0. + g_ram.writeByte((page0 << 8) | (k & 0xFF), v); } } } @@ -959,12 +969,33 @@ void AppleMMU::resetRAM() for (uint8_t slotnum = 1; slotnum <= 7; slotnum++) { uint16_t page0 = _pageNumberForRam(0xC0 + slotnum, 0); if (slots[slotnum]) { + // Load the primary ROM for this peripheral (0xCsXX..0xCsFF) uint8_t tmpBuf[256]; memset(tmpBuf, 0, sizeof(tmpBuf)); slots[slotnum]->loadROM(tmpBuf); for (int i=0; i<256; i++) { g_ram.writeByte( (page0 << 8) + i, tmpBuf[i] ); } + + // See if there's an extended 2k ROM for this peripheral (0xC800..0xCFFF) + if (slots[slotnum]->hasExtendedRom()) { + for (int j=0; j<8; j++) { + // Load each of the 256 byte chunks separately to its own VMRam page + uint16_t slotPage = 0; + if (slotnum == 4) { + slotPage = _pageNumberForRam(0xC8 + j, 2); + } else { +#ifndef TEENSYDUINO + fprintf(stderr, "ERROR: unsupported extended ROM peripheral in slot %d\n", slotnum); + exit(1); +#endif + } + if (slotPage) { + uint8_t *p = g_ram.memPtr(slotPage << 8); + slots[slotnum]->loadExtendedRom(p, j * 256); + } + } + } } } @@ -1063,12 +1094,17 @@ void AppleMMU::updateMemoryPages() // If slotLatch is set (!= -1), then we are mapping 2k of ROM // for a given peripheral to C800..CFFF. if (slotLatch != -1) { - // FIXME: the only peripheral we support this with right now is - // the 80-column card. + // FIXME: this is a hacky mess. Slot 3 (the 80-col card) is + // supported, as page "1"; and Slot 4 (the mouse card) is + // supported as page "2". if (slotLatch == 3) { for (int i=0xc8; i <= 0xcf; i++) { readPages[i] = _pageNumberForRam(i, 1); } + } else if (slotLatch == 4) { + for (int i=0xc8; i <= 0xcf; i++) { + readPages[i] = _pageNumberForRam(i, 2); + } } } diff --git a/apple/applevm.cpp b/apple/applevm.cpp index ec81a30..f182b9f 100644 --- a/apple/applevm.cpp +++ b/apple/applevm.cpp @@ -35,6 +35,9 @@ AppleVM::AppleVM() hd32 = new HD32((AppleMMU *)mmu); ((AppleMMU *)mmu)->setSlot(7, hd32); + + mouse = new Mouse(); + ((AppleMMU *)mmu)->setSlot(4, mouse); } AppleVM::~AppleVM() @@ -133,6 +136,7 @@ void AppleVM::cpuMaintenance(int64_t cycles) keyboard->maintainKeyboard(cycles); disk6->maintenance(cycles); + mouse->maintainMouse(cycles); } void AppleVM::Reset() @@ -189,3 +193,8 @@ VMKeyboard * AppleVM::getKeyboard() { return keyboard; } + +bool AppleVM::isMouseEnabled() +{ + return mouse->isEnabled(); +} diff --git a/apple/applevm.h b/apple/applevm.h index 554e8e3..f3a490b 100644 --- a/apple/applevm.h +++ b/apple/applevm.h @@ -7,6 +7,7 @@ #include "hd32.h" #include "vmkeyboard.h" #include "parallelcard.h" +#include "mouse.h" #include "vm.h" class AppleVM : public VM { @@ -34,11 +35,14 @@ class AppleVM : public VM { virtual VMKeyboard *getKeyboard(); + bool isMouseEnabled(); + DiskII *disk6; HD32 *hd32; protected: VMKeyboard *keyboard; ParallelCard *parallel; + Mouse *mouse; }; diff --git a/apple/mouse.cpp b/apple/mouse.cpp new file mode 100644 index 0000000..f7e4be6 --- /dev/null +++ b/apple/mouse.cpp @@ -0,0 +1,361 @@ +#include "mouse.h" +#include +#include "globals.h" + +enum { + SW_W_INIT = 0x00, + SW_R_HOMEMOUSE = 0x08, + SW_R_POSMOUSE = 0x09, + SW_R_CLEARMOUSE = 0x0A, + SW_R_READMOUSE = 0x0B, + SW_R_INITMOUSE = 0x0C, + SW_W_CLAMPMOUSE = 0x0D, + SW_R_SERVEMOUSE = 0x0E, + SW_W_SETMOUSE = 0x0F +}; + +// The first 3 soft switch bits technically should pass directly to +// the PIA6821. In practice, so far, I've only seen the first (SW_W_INIT) +// used when PR#4 is invoked to initialize the mouse interface from DOS, +// so for the moment I'll just catch that specifically. + +enum { + ST_MOUSEENABLE = 1, + ST_INTMOUSE = 2, + ST_INTBUTTON = 4, + ST_INTVBL = 8 +}; + +Mouse::Mouse() +{ + status = 0; + interruptsTriggered = 0; + lastX = lastY = 0; + lastXForInt = lastYForInt = 0; + lastButton = false; + lastButtonForInt = false; +} + +Mouse::~Mouse() +{ +} + +bool Mouse::Serialize(int8_t fd) +{ + return true; +} + +bool Mouse::Deserialize(int8_t fd) +{ + return true; +} + +void Mouse::Reset() +{ +} + +uint8_t Mouse::readSwitches(uint8_t s) +{ + switch (s) { + default: + printf("mouse: unknown switch read 0x%X\n", s); + }; + return 0xFF; +} + +void Mouse::writeSwitches(uint8_t s, uint8_t v) +{ + switch (s) { + /* Many of these were designed to be reads, because they don't have to + * modify any state inside the VM directly -- but it's important (per docs) + * that we return A, X, and Y as they were when we were called. So these + * are all now writes, which don't modify A/X/Y. */ + case SW_R_HOMEMOUSE: + g_mouse->setPosition( (g_vm->getMMU()->read(0x578) << 8) | g_vm->getMMU()->read(0x478), + (g_vm->getMMU()->read(0x5F8) << 8) | g_vm->getMMU()->read(0x4F8) + ); + break; + case SW_R_POSMOUSE: + g_mouse->setPosition( (g_vm->getMMU()->read(0x578+4) << 8) | g_vm->getMMU()->read(0x478+4), + (g_vm->getMMU()->read(0x5F8+4) << 8) | g_vm->getMMU()->read(0x4F8+4) + ); + break; + case SW_R_CLEARMOUSE: + g_vm->getMMU()->write(0x578+4, 0); + g_vm->getMMU()->write(0x478+4, 0); + g_vm->getMMU()->write(0x5F8+4, 0); + g_vm->getMMU()->write(0x4F8+4, 0); + g_mouse->setPosition(0,0); + break; + case SW_R_READMOUSE: + { + uint16_t xpos, ypos; + g_mouse->getPosition(&xpos, &ypos); + if (lastX != xpos || lastY != ypos) { + interruptsTriggered |= 0x20; // "x or y changed since last reading" + lastX = xpos; lastY = ypos; + } + curButton = g_mouse->getButton(); + uint8_t newStatus = g_vm->getMMU()->read(0x778+4) & ~0xC0; + if (curButton) { newStatus |= 0x80; }; + if (lastButton) { newStatus |= 0x40; }; + + g_vm->getMMU()->write(0x578+4, (xpos >> 8) & 0xFF); // high X + g_vm->getMMU()->write(0x478+4, xpos & 0xFF); // low X + g_vm->getMMU()->write(0x5F8+4, (ypos >> 8) & 0xFF); // high Y + g_vm->getMMU()->write(0x4F8+4, ypos); // low Y + } + break; + case SW_R_INITMOUSE: + // Set clamp to (0,0) - (1023,1023) + g_vm->getMMU()->write(0x578, 0); // high of lowclamp + g_vm->getMMU()->write(0x478, 0); // low of lowclamp + g_vm->getMMU()->write(0x5F8, 0x03); // high of highclamp + g_vm->getMMU()->write(0x4F8, 0xFF); // low of highclamp + g_mouse->setClamp(XCLAMP, 0, 1023); + g_mouse->setClamp(YCLAMP, 0, 1023); + break; + case SW_R_SERVEMOUSE: + if (lastButton) interruptsTriggered |= 0x40; + if (curButton != lastButton) { + interruptsTriggered |= 0x80; + lastButton = curButton; + } + g_vm->getMMU()->write(0x778+4, interruptsTriggered); + g_vm->getMMU()->write(0x6B8+4, interruptsTriggered); // hack to appease ROM + interruptsTriggered = 0; + break; + case SW_W_INIT: + v &= 0x03; // just the low 3 bits apparently? + // printf("Simple init: value is 0x%X\n", v); + status = v; + g_vm->getMMU()->write(0x7f8 + 4, v); + break; + case SW_W_CLAMPMOUSE: + { + uint16_t lowval = (g_vm->getMMU()->read(0x578) << 8) | (g_vm->getMMU()->read(0x478)); + uint16_t highval = (g_vm->getMMU()->read(0x5F8) << 8) | (g_vm->getMMU()->read(0x4F8)); + if (v) { + g_mouse->setClamp(YCLAMP, lowval, highval); + } else { + // X is clamping + g_mouse->setClamp(XCLAMP, lowval, highval); + } + } + break; + case SW_W_SETMOUSE: + status = v; + g_vm->getMMU()->write(0x7f8 + 4, v); + break; + default: + printf("mouse: unknown switch write 0x%X = 0x%2X\n", s, v); + break; + } +} + +void Mouse::loadROM(uint8_t *toWhere) +{ + /* This is a custom-built ROM which hands off control to the C++ code via + * soft switch writes. It's hard-coded to work with the mouse in Slot 4. + * + * ; $c400 is the entry point for PR#4 + * $C400 2C 58 FF BIT $FF58 + * $C403 70 1B BVS IOHandler + * ; $c405 is the entry point for IN#4 when we set KSW + * $C405 38 SEC + * $C406 90 18 BCC IOHandler + * $C408 B8 CLV + * $C409 50 15 BVC IOHandler ; always branch + * $C40B 01 20 8D 8D 8D ; data (ID bytes & such) + * $C410 8D 00 60 68 76 7B 80 85 ; data (lookup table of entrypoints) + * $C418 8F 94 8D 8D 8D 8D 8D 8D ; data (lookup table of entrypoints) + * ; IOHandler ORG $c420 + * $C420 48 PHA ; save registers on stack + * $C421 98 TYA + * $C422 48 PHA + * $C423 8A TXA + * $C424 48 PHA + * $C425 08 PHP + * $C426 78 SEI ; disable interrupts + * $C427 20 58 FF JSR $FF58 ; JSR to this well-known location that has + * $C42A BA TSX ; an RTS (normally). Then when we get + * $C42B BD 00 01 LDA $100,X ; back, pull our address off the stack + * $C42E AA TAX ; and save the high byte in X + * $C42F 0A ASL + * $C430 0A ASL + * $C431 0A ASL + * $C432 0A ASL + * $C433 A8 TAY ; and (high byte << 4) in Y + * $C434 28 PLP ; restore interrupt state + * $C435 50 0F BVC $C446 + * $C437 A5 38 LDA $0 + * $C439 D0 0D BNE $C448 + * $C43B 8A TXA + * $C43C 45 39 EOR $0 + * $C43E D0 08 BNE $C448 + * $C440 A9 05 LDA #$0 + * $C442 85 38 STA $0 + * $C444 D0 0B BNE SendInputToDOS + * $C446 B0 09 BCS SendInputToDOS + * $C448 68 PLA ; restore registers + * $C449 AA TAX + * $C44A 68 PLA + * $C44B EA NOP + * $C44C 68 PLA + * $C44D EA NOP + * $C44E EA NOP + * $C44F EA NOP + * $C450 60 RTS + * ; SendInputToDOS ORG $c451 + * $C451 EA NOP + * $C452 EA NOP + * $C453 EA NOP + * $C454 68 PLA + * $C455 BD 38 06 LDA $638,X ; X is $C4, so this is $6F8+n - which is + * $C458 AA TAX ; a reserved hole + * $C459 68 PLA + * $C45A A8 TAY + * $C45B 68 PLA + * $C45C BD 00 02 LDA $200,X ; keyboard buffer output + * $C45F 60 RTS + * ; SetMouse ORG $c460 + * $C460 C9 10 CMP #$0 + * $C462 B0 29 BCS ExitWithError + * $C464 8D CF C0 STA $C0CF ; soft switch 0x0F invokes SetMouse + * $C467 60 RTS + * ; ServeMouse ORG $c468 + * $C468 48 PHA + * $C469 18 CLC ; use CLC/BCC to force relative jump + * $C46A 90 2D BCC ServeMouseWorker + * ; ServeMouseExit ORG $c46c + * $C46C BD B8 06 LDA $6B8,X ; check what interrupts we say we serviced + * $C46F 29 0E AND #$0 + * $C471 D0 01 BNE $C474 ; if we serviced any, leave carry clear + * $C473 38 SEC ; but set carry if we serviced none + * $C474 68 PLA + * $C475 60 RTS + * ; ReadMouse ORG $c476 + * $C476 8D CB C0 STA $C0CB ; soft switch 0x0B + * $C479 18 CLC + * $C47A 60 RTS + * ; ClearMouse ORG $c47b + * $C47B 8D CA C0 STA $C0CA ; soft switch 0x0A + * $C47E 18 CLC + * $C47F 60 RTS + * ; PosMouse ORG $c480 + * $C480 8D C9 C0 STA $C0C9 ; soft switch 0x09 + * $C483 18 CLC + * $C484 60 RTS + * ; ClampMouse ORG $c485 + * $C485 C9 02 CMP #$0 + * $C487 B0 04 BCS $C48D + * $C489 8D CD C0 STA $C0CD ; soft switch 0x0D + * $C48C 60 RTS + * ; ExitWithError ORG $c48d + * $C48D 38 SEC ; the spec says carry is set on errors + * $C48E 60 RTS + * ; HomeMouse ORG $c48f + * $C48F 8D C8 C0 STA $C0C8 ; soft switch 0x08 + * $C492 18 CLC + * $C493 60 RTS + * ; InitMouse ORG $c494 + * $C494 8D CC C0 STA $C0CC ; soft switch 0x0C + * $C497 18 CLC + * $C498 60 RTS + * ; ServeMouseWorker ORG $c499 + * $C499 78 SEI ; disable interrupts + * $C49A 8D CE C0 STA $C0CE ; soft switch 0x0E + * $C49D A2 04 LDX #$0 + * $C49F 18 CLC + * $C4A0 90 CA BCC ServeMouseExit ; force relative jump + * ; $C4A2..C4FA is dead space (all $FF) + * $C4FB D6 FF FF FF 01 ; data (ID bytes) + */ + + uint8_t rom[256] = { 0x2c, 0x58, 0xff, 0x70, 0x1B, 0x38, 0x90, 0x18, // C400 + 0xb8, 0x50, 0x15, 0x01, 0x20, 0x8d, 0x8d, 0x8d, + 0x8d, 0x00, 0x60, 0x68, 0x76, 0x7b, 0x80, 0x85, // C410 + 0x8f, 0x94, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x48, 0x98, 0x48, 0x8a, 0x48, 0x08, 0x78, 0x20, // C420 + 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0xAA, 0x0A, + 0x0A, 0x0A, 0x0A, 0xA8, 0x28, 0x50, 0x0F, 0xA5, // C430 + 0x38, 0xd0, 0x0d, 0x8a, 0x45, 0x39, 0xd0, 0x08, + 0xa9, 0x05, 0x85, 0x38, 0xd0, 0x0b, 0xb0, 0x09, // C440 + 0x68, 0xaa, 0x68, 0xea, 0x68, 0xea, 0xea, 0xea, + 0x60, 0xea, 0xea, 0xea, 0x68, 0xbd, 0x38, 0x06, // C450 + 0xaa, 0x68, 0xa8, 0x68, 0xbd, 0x00, 0x02, 0x60, + 0xc9, 0x10, 0xb0, 0x29, 0x8d, 0xcf, 0xc0, 0x60, // C460 + 0x48, 0x18, 0x90, 0x2d, 0xbd, 0xb8, 0x06, 0x29, + 0x0e, 0xd0, 0x01, 0x38, 0x68, 0x60, 0x8d, 0xcb, // C470 + 0xc0, 0x18, 0x60, 0x8d, 0xca, 0xc0, 0x18, 0x60, + + 0x8d, 0xc9, 0xc0, 0x18, 0x60, 0xc9, 0x02, 0xb0, // C480 + 0x04, 0x8d, 0xcd, 0xc0, 0x60, 0x38, 0x60, 0x8d, + 0xc8, 0xc0, 0x18, 0x60, 0x8d, 0xcc, 0xc0, 0x18, // C490 + 0x60, 0x78, 0x8d, 0xce, 0xc0, 0xa2, 0x04, 0x18, + 0x90, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C4A0 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // C4B0 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // C4C0 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // C4D0 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // C4E0 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // C4F0 + 0xff, 0xff, 0xff, 0xd6, 0xff, 0xff, 0xff, 0x01 }; + memcpy(toWhere, rom, 256); +} + +bool Mouse::hasExtendedRom() +{ + return true; +} + +void Mouse::loadExtendedRom(uint8_t *toWhere, uint16_t byteOffset) +{ + // There's no extended ROM needed, b/c we do the extended ROM work + // directly in C++ +} + +void Mouse::maintainMouse(int64_t cycleCount) +{ + // Fake a 60Hz VBL in case we need it for our interrupts + static int64_t nextInterruptTime = cycleCount + 17050; + + if ( (status & ST_MOUSEENABLE) && + (status & ST_INTVBL) && + (cycleCount >= nextInterruptTime) ) { + g_cpu->irq(); + + interruptsTriggered |= ST_INTVBL; + + nextInterruptTime += 17050; + } else { + uint16_t xpos, ypos; + g_mouse->getPosition(&xpos, &ypos); + + if ( (status & ST_MOUSEENABLE) && + (status & ST_INTMOUSE) && + (xpos != lastXForInt || ypos != lastYForInt) ) { + g_cpu->irq(); + + interruptsTriggered |= ST_INTMOUSE; + lastXForInt = xpos; lastYForInt = ypos; + } else if ( (status & ST_MOUSEENABLE) && + (status & ST_INTBUTTON) && + lastButtonForInt != g_mouse->getButton()) { + g_cpu->irq(); + + interruptsTriggered |= ST_INTBUTTON; + lastButtonForInt = g_mouse->getButton(); + } + } + /* FIXME: still need button */ +} + +bool Mouse::isEnabled() +{ + return status & ST_MOUSEENABLE; +} diff --git a/apple/mouse.h b/apple/mouse.h new file mode 100644 index 0000000..bafa602 --- /dev/null +++ b/apple/mouse.h @@ -0,0 +1,50 @@ +#ifndef __APPLEMOUSE_H +#define __APPLEMOUSE_H + +#ifdef TEENSYDUINO +#include +#else +#include +#include +#endif + +#include "applemmu.h" +#include "slot.h" + +class Mouse : public Slot { + public: + Mouse(); + virtual ~Mouse(); + + virtual bool Serialize(int8_t fd); + virtual bool Deserialize(int8_t fd); + + virtual void Reset(); // used by BIOS cold-boot + virtual uint8_t readSwitches(uint8_t s); + virtual void writeSwitches(uint8_t s, uint8_t v); + virtual void loadROM(uint8_t *toWhere); + + virtual bool hasExtendedRom(); + virtual void loadExtendedRom(uint8_t *toWhere, uint16_t byteOffset); + + void maintainMouse(int64_t cycleCount); + + bool isEnabled(); + +private: + uint8_t status; + uint8_t interruptsTriggered; + + // Previous state vars used when we're asked "did this change since last time" + uint16_t lastX, lastY; + bool lastButton; + + bool curButton; + + // second set of previous state vars for use when checking if an interrupt + // needs to fire based on a change + uint16_t lastXForInt, lastYForInt; + bool lastButtonForInt; +}; + +#endif diff --git a/apple/pia6821.cpp b/apple/pia6821.cpp new file mode 100644 index 0000000..bb26c53 --- /dev/null +++ b/apple/pia6821.cpp @@ -0,0 +1,43 @@ +#include "pia6821.h" + +PIA6821::PIA6821() +{ +} + +PIA6821::~PIA6821() +{ +} + +uint8_t PIA6821::read(uint8_t addr) +{ + uint8_t rv; + + switch (addr) { + case DDRA: + if (cra & 0x04) { // DDR or Peripherial Interface access control + // peripheral + // rv = readPeripheralA(); + // FIXME continue here + } else { + rv = ddra; + } + break; + case CTLA: + rv = cra; + break; + case DDRB: + if (crb & 0x04) { + // rv = readPeripheralB(); + // FIXME continue here + } else { + rv = ddrb; + } + break; + case CTLB: + rv = crb; + break; + } + + return rv; +} + diff --git a/apple/pia6821.h b/apple/pia6821.h new file mode 100644 index 0000000..9317d63 --- /dev/null +++ b/apple/pia6821.h @@ -0,0 +1,31 @@ +#ifndef _PIA6821_H +#define _PIA6821_H + +#include + +// http://webpages.charter.net/coinopcauldron/piaarticle.html + +#define DDRA 0 +#define CTLA 1 +#define DDRB 2 +#define CTLB 3 + +class PIA6821 { + public: + PIA6821(); + ~PIA6821(); + + uint8_t read(uint8_t addr); + + + private: + uint8_t porta, portb; + uint8_t ddra, ddrb; + uint8_t cra, crb; // control registers + /* + 2 ports - porta, portb + */ + +}; + +#endif diff --git a/apple/slot.h b/apple/slot.h index e394e06..fedf709 100644 --- a/apple/slot.h +++ b/apple/slot.h @@ -22,6 +22,9 @@ class Slot { virtual void writeSwitches(uint8_t s, uint8_t v) = 0; virtual void loadROM(uint8_t *toWhere) = 0; + + virtual bool hasExtendedRom() { return false; }; + virtual void loadExtendedRom(uint8_t *toWhere, uint16_t byteOffset) {}; }; #endif diff --git a/globals.cpp b/globals.cpp index e614a4c..8f7f2ea 100644 --- a/globals.cpp +++ b/globals.cpp @@ -5,6 +5,7 @@ Cpu *g_cpu = NULL; VM *g_vm = NULL; PhysicalDisplay *g_display = NULL; PhysicalKeyboard *g_keyboard = NULL; +PhysicalMouse *g_mouse = NULL; PhysicalSpeaker *g_speaker = NULL; PhysicalPaddles *g_paddles = NULL; PhysicalPrinter *g_printer = NULL; diff --git a/globals.h b/globals.h index 5edd239..634c8fa 100644 --- a/globals.h +++ b/globals.h @@ -8,6 +8,7 @@ #include "vm.h" #include "physicaldisplay.h" #include "physicalkeyboard.h" +#include "physicalmouse.h" #include "physicalspeaker.h" #include "physicalpaddles.h" #include "physicalprinter.h" @@ -41,6 +42,7 @@ extern Cpu *g_cpu; extern VM *g_vm; extern PhysicalDisplay *g_display; extern PhysicalKeyboard *g_keyboard; +extern PhysicalMouse *g_mouse; extern PhysicalSpeaker *g_speaker; extern PhysicalPaddles *g_paddles; extern PhysicalPrinter *g_printer; diff --git a/nix/debugger.cpp b/nix/debugger.cpp index aea5cc4..85ab710 100644 --- a/nix/debugger.cpp +++ b/nix/debugger.cpp @@ -51,6 +51,9 @@ Debugger::Debugger() server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(12345); + steppingOut = false; + singleStep = false; + if (bind(sd, (struct sockaddr *) &server, sizeof(server)) < 0) { perror("error binding to debug socket"); exit(1); @@ -71,10 +74,14 @@ Debugger::~Debugger() bool getAddress(const char *buf, unsigned int *addrOut) { unsigned int val; - if (sscanf(buf, " 0x%X", &val) == 1) { + if (sscanf(buf, " 0x%X", &val) == 1 || + sscanf(buf, " 0x%x", &val) == 1 + ) { *addrOut = val; return true; - } else if (sscanf(buf, " $%X", &val) == 1) { + } else if (sscanf(buf, " $%X", &val) == 1 || + sscanf(buf, " $%x", &val) == 1 + ) { *addrOut = val; return true; } else if (sscanf(buf, " %d", &val) == 1) { @@ -91,11 +98,10 @@ bool getAddress(const char *buf, unsigned int *addrOut) #define HEXCHAR(x) ((x>='0'&&x<='9')?x-'0':(x>='a'&&x<='f')?x-'a'+10:(x>='A'&&x<='F')?x-'A'+10:(x=='i' || x=='I')?1:(x=='o' || x=='O')?0:0) #define FROMHEXP(p) ((HEXCHAR(*p) << 4) | HEXCHAR(*(p+1))) -bool steppingOut = false; - void Debugger::step() { static char buf[256]; + uint8_t cmdbuf[50]; // FIXME: add more than just RTS(0x60) here if (steppingOut && @@ -124,38 +130,49 @@ void Debugger::step() return; } - uint8_t cmdbuf[50]; - - uint16_t loc=g_cpu->pc; - for (int i=0; i<50/3; i++) { - for (int idx=0; idxgetMMU()->read(loc+idx); - } - loc += dis.instructionToMnemonic(loc, cmdbuf, buf, sizeof(buf)); - write(cd, buf, strlen(buf)); - buf[0] = 13; - buf[1] = 10; - write(cd, buf, 2); - } - - - if (breakpoint && g_cpu->pc != breakpoint) { + if (!singleStep && breakpoint && g_cpu->pc != breakpoint) { // Running until we reach the breakpoint return; } + singleStep = false; // we have taken a single step, so reset flag uint8_t b; // byte value used in parsing unsigned int val; // common value buffer used in parsing - GETCH; - snprintf(buf, sizeof(buf), "Got char %d\012\015", b); - write(cd, buf, strlen(buf)); + doover: + do { + GETCH; + } while (b != 'c' && // continue (with breakpoint set) + b != 'q' && // quit + b != 's' && // single step + b != 'S' && // step out + b != 'b' && // set breakpoint + b != 'd' && // show disassembly + b != 'L' && // load memory (lines) + b != '*' // show memory (byte) + ); switch (b) { - case 'c': // Continue - close connection and let execution flow + case 'c': // continue (if there is a breakpoint set) + if (breakpoint) { + snprintf(buf, sizeof(buf), "Continuing until breakpoint 0x%X\012\015", breakpoint); + write(cd, buf, strlen(buf)); + } else { + snprintf(buf, sizeof(buf), "No breakpoint to continue until\012\015"); + write(cd, buf, strlen(buf)); + goto doover; + } + break; + + case 'q': // Close debugging socket and quit printf("Closing debugging socket\n"); + breakpoint = 0; close(cd); cd=-1; break; + + case 's': + singleStep = true; // for when breakpoint is set: just step once + break; case 'S': steppingOut = true; @@ -176,6 +193,23 @@ void Debugger::step() write(cd, buf, strlen(buf)); break; + case 'd': // show disassembly @ PC + { + uint16_t loc=g_cpu->pc; + for (int i=0; i<50/3; i++) { + for (int idx=0; idxgetMMU()->read(loc+idx); + } + loc += dis.instructionToMnemonic(loc, cmdbuf, buf, sizeof(buf)); + write(cd, buf, strlen(buf)); + buf[0] = 13; + buf[1] = 10; + write(cd, buf, 2); + } + } + goto doover; + break; + case 'L': // Load data to memory. Use: "L 0x
\n" followed by lines of packed hex; ends with a blank line { printf("Loading data\n"); @@ -198,6 +232,7 @@ void Debugger::step() } } } + goto doover; break; case '*': // read 1 byte of memory. Use '* 0x
' @@ -214,6 +249,7 @@ void Debugger::step() write(cd, buf, strlen(buf)); } } + goto doover; break; case 'G': // Goto (set PC) diff --git a/nix/debugger.h b/nix/debugger.h index 05d7bc7..66936c9 100644 --- a/nix/debugger.h +++ b/nix/debugger.h @@ -19,6 +19,8 @@ class Debugger { pthread_t listenThreadID; uint32_t breakpoint; + bool steppingOut; + bool singleStep; }; diff --git a/nix/disassembler.cpp b/nix/disassembler.cpp index edeec08..db29676 100755 --- a/nix/disassembler.cpp +++ b/nix/disassembler.cpp @@ -133,6 +133,7 @@ uint8_t Disassembler::instructionToMnemonic(uint16_t addr, uint8_t *p, char *out addrmode amode = opcodes[*p].mode; uint16_t target = 0; char arg[40] = "\0"; + char bytes[10] = "\0"; switch (amode) { case A_REL: @@ -153,14 +154,19 @@ uint8_t Disassembler::instructionToMnemonic(uint16_t addr, uint8_t *p, char *out switch (instructionBytes(*p)) { case 1: // no arguments + sprintf(bytes, " %.2X ", *(uint8_t *)p); break; case 2: + sprintf(arg, "%s$%X%s", om.prefix, target, om.suffix); + sprintf(bytes, " %.2X %.2X ", *(uint8_t *)p, *(uint8_t *)(p+1)); + break; case 3: sprintf(arg, "%s$%X%s", om.prefix, target, om.suffix); + sprintf(bytes, "%.2X %.2X %.2X ", *(uint8_t *)p, *(uint8_t *)(p+1), *(uint8_t *)(p+2)); break; } - sprintf(outp, "$%.4X %s %s", addr, mn, arg); + sprintf(outp, "$%.4X %s %s %s", addr, bytes, mn, arg); return instructionBytes(*p); } diff --git a/physicalmouse.h b/physicalmouse.h new file mode 100644 index 0000000..4a1d489 --- /dev/null +++ b/physicalmouse.h @@ -0,0 +1,27 @@ +#ifndef __PHYSICALMOUSE_H +#define __PHYSICALMOUSE_H + +#include + +enum { + XCLAMP = 0, + YCLAMP = 1 +}; + +class PhysicalMouse { + public: + PhysicalMouse() { lowClamp[XCLAMP] = lowClamp[YCLAMP] = 0; highClamp[XCLAMP] = highClamp[YCLAMP] = 1023; } + virtual ~PhysicalMouse() {}; + + virtual void maintainMouse() = 0; + + virtual void setClamp(uint8_t direction, uint16_t low, uint16_t high) { lowClamp[direction] = low; highClamp[direction] = high; } + virtual void setPosition(uint16_t x, uint16_t y) = 0; + virtual void getPosition(uint16_t *x, uint16_t *y) = 0; + virtual bool getButton() = 0; + +protected: + uint16_t lowClamp[2], highClamp[2]; +}; + +#endif diff --git a/sdl/aiie.cpp b/sdl/aiie.cpp index 04ba5c4..97648e7 100644 --- a/sdl/aiie.cpp +++ b/sdl/aiie.cpp @@ -7,6 +7,7 @@ #include "applevm.h" #include "sdl-display.h" #include "sdl-keyboard.h" +#include "sdl-mouse.h" #include "sdl-speaker.h" #include "sdl-paddles.h" #include "nix-filemanager.h" @@ -291,7 +292,7 @@ struct timespec runMaintenance(struct timespec now) initialized = true; } - timespec_add_us(&startTime, 100000*cycleCount, &nextRuntime); // FIXME: what's a good time here? 1/10 sec? + timespec_add_us(&startTime, 16667*cycleCount, &nextRuntime); // FIXME: what's a good time here? 60 Hz? // Check if it's time to run - and if not, return how long it will // be until we need to run @@ -305,6 +306,7 @@ struct timespec runMaintenance(struct timespec now) if (!g_biosInterrupt) { // If the BIOS is running, then let it handle the keyboard directly g_keyboard->maintainKeyboard(); + g_mouse->maintainMouse(); } doDebugging(); @@ -389,6 +391,7 @@ int main(int argc, char *argv[]) g_vm = new AppleVM(); g_keyboard = new SDLKeyboard(g_vm->getKeyboard()); + g_mouse = new SDLMouse(); // Now that the VM exists and it has created an MMU, we tell the CPU how to access memory through the MMU. g_cpu->SetMMU(g_vm->getMMU()); diff --git a/sdl/sdl-keyboard.cpp b/sdl/sdl-keyboard.cpp index 89b0b1a..bb81c34 100644 --- a/sdl/sdl-keyboard.cpp +++ b/sdl/sdl-keyboard.cpp @@ -1,6 +1,7 @@ #include "sdl-keyboard.h" #include "sdl-paddles.h" +#include "sdl-mouse.h" #include "globals.h" SDLKeyboard::SDLKeyboard(VMKeyboard *k) : PhysicalKeyboard(k) @@ -149,12 +150,18 @@ void SDLKeyboard::maintainKeyboard() if (event.key.repeat == 0) handleKeypress(&event.key); break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + ((SDLMouse *)g_mouse)->mouseButtonEvent(event.type == SDL_MOUSEBUTTONDOWN); + break; case SDL_MOUSEMOTION: // We are handling the SDL input loop, so need to pass this off to the paddles. :/ // FIXME: nasty rooting around in other objects and typecasting. // FIXME: event.motion.state & SDL_BUTTON_LMASK, et al? ((SDLPaddles *)g_paddles)->gotMouseMovement(event.motion.x, event.motion.y); + ((SDLMouse *)g_mouse)->gotMouseEvent(event.motion.state, // button + event.motion.xrel, event.motion.yrel); break; case SDL_QUIT: diff --git a/sdl/sdl-mouse.cpp b/sdl/sdl-mouse.cpp new file mode 100644 index 0000000..c7e386e --- /dev/null +++ b/sdl/sdl-mouse.cpp @@ -0,0 +1,62 @@ +#include "sdl-mouse.h" + +#include "globals.h" + +SDLMouse::SDLMouse() : PhysicalMouse() +{ + xpos = ypos = 0; + button = false; +} + +SDLMouse::~SDLMouse() +{ +} + +void SDLMouse::gotMouseEvent(uint32_t buttonState, int32_t x, int32_t y) +{ + xpos += x; ypos += y; + + if (xpos < lowClamp[XCLAMP]) xpos=lowClamp[XCLAMP]; + if (xpos > highClamp[XCLAMP]) xpos=highClamp[XCLAMP]; + if (ypos < lowClamp[YCLAMP]) ypos = lowClamp[YCLAMP]; + if (ypos > highClamp[YCLAMP]) ypos = highClamp[YCLAMP]; +} + +void SDLMouse::mouseButtonEvent(bool state) +{ + button = state; +} + +void SDLMouse::maintainMouse() +{ +} + +void SDLMouse::setPosition(uint16_t x, uint16_t y) +{ + xpos = x; + ypos = y; + + if (xpos < lowClamp[XCLAMP]) xpos=lowClamp[XCLAMP]; + if (xpos > highClamp[XCLAMP]) xpos=highClamp[XCLAMP]; + if (ypos < lowClamp[YCLAMP]) ypos = lowClamp[YCLAMP]; + if (ypos > highClamp[YCLAMP]) ypos = highClamp[YCLAMP]; +} + +void SDLMouse::getPosition(uint16_t *x, uint16_t *y) +{ + if (xpos < lowClamp[XCLAMP]) xpos=lowClamp[XCLAMP]; + if (xpos > highClamp[XCLAMP]) xpos=highClamp[XCLAMP]; + if (ypos < lowClamp[YCLAMP]) ypos = lowClamp[YCLAMP]; + if (ypos > highClamp[YCLAMP]) ypos = highClamp[YCLAMP]; + + uint16_t outx = xpos; + uint16_t outy = ypos; + + *x = outx; + *y = outy; +} + +bool SDLMouse::getButton() +{ + return button; +} diff --git a/sdl/sdl-mouse.h b/sdl/sdl-mouse.h new file mode 100644 index 0000000..9da03db --- /dev/null +++ b/sdl/sdl-mouse.h @@ -0,0 +1,26 @@ +#ifndef __SDL_MOUSE_H +#define __SDL_MOUSE_H + +#include "physicalmouse.h" + +#include + +class SDLMouse : public PhysicalMouse { + public: + SDLMouse(); + virtual ~SDLMouse(); + + virtual void maintainMouse(); + + virtual void setPosition(uint16_t x, uint16_t y); + virtual void getPosition(uint16_t *x, uint16_t *y); + virtual bool getButton(); + + void gotMouseEvent(uint32_t buttonState, int32_t x, int32_t y); + void mouseButtonEvent(bool state); +private: + int32_t xpos, ypos; + bool button; +}; + +#endif diff --git a/serialize.h b/serialize.h new file mode 100644 index 0000000..f40812a --- /dev/null +++ b/serialize.h @@ -0,0 +1,142 @@ +#ifndef __SERIALIZE_H +#define __SERIALIZE_H + +#define serialize8(var) { uint8_t buf = (uint8_t)var; \ + if (g_filemanager->write(fd, &buf, 1) != 1) { \ + printf("Failed to write 1 byte\n"); \ + goto err; \ + } \ +} + +#define serializeMagic(var) { \ + printf("Serializing magic '%d'\n", var); \ + uint8_t buf = var; \ + if (g_filemanager->write(fd, &buf, 1) != 1) { \ + printf("Failed to write 1 byte of magic\n"); \ + goto err; \ + } \ +} + +#define serialize16(var) { \ + uint8_t buf[2]; \ + uint8_t ptr = 0; \ + buf[ptr++] = ((var >> 8) & 0xFF); \ + buf[ptr++] = ((var ) & 0xFF); \ + if (g_filemanager->write(fd, buf, 2) != 2) { \ + printf("Failed to write 2 bytes\n"); \ + goto err; \ + } \ +} + +#define serialize32(var) { \ + uint8_t buf[4]; \ + uint8_t ptr = 0; \ + buf[ptr++] = ((var >> 24) & 0xFF); \ + buf[ptr++] = ((var >> 16) & 0xFF); \ + buf[ptr++] = ((var >> 8) & 0xFF); \ + buf[ptr++] = ((var ) & 0xFF); \ + if (g_filemanager->write(fd, buf, 4) != 4) { \ + printf("Failed to write 4 bytes\n"); \ + goto err; \ + } \ +} + +#define serialize64(var) { \ + uint8_t buf[8]; \ + uint8_t ptr = 0; \ + buf[ptr++] = ((var >> 56) & 0xFF); \ + buf[ptr++] = ((var >> 48) & 0xFF); \ + buf[ptr++] = ((var >> 40) & 0xFF); \ + buf[ptr++] = ((var >> 32) & 0xFF); \ + buf[ptr++] = ((var >> 24) & 0xFF); \ + buf[ptr++] = ((var >> 16) & 0xFF); \ + buf[ptr++] = ((var >> 8) & 0xFF); \ + buf[ptr++] = ((var ) & 0xFF); \ + if (g_filemanager->write(fd, buf, 8) != 8) { \ + printf("Failed to write 8 bytes\n"); \ + goto err; \ + } \ +} + +#define serializeString(s) { \ + if (g_filemanager->write(fd, s, strlen(s)+1) != strlen(s)+1) { \ + printf("Failed to write string '%s'\n", s); \ + goto err; \ + } \ +} + +#define deserialize8(var) { \ + uint8_t buf; \ + if (g_filemanager->read(fd, &buf, 1) != 1) { \ + printf("Failed to deserialize 1 byte\n"); \ + goto err; \ + } \ + var = buf; \ +} + +#define deserializeMagic(expect) { \ + uint8_t buf; \ + printf("Deserializing magic, expecting 0x%X\n", expect); \ + if (g_filemanager->read(fd, &buf, 1) != 1) { \ + printf("Failed to deserialize 1 byte of magic\n"); \ + goto err; \ + } \ + if (buf != expect) { \ + printf("magic error: 0x%X does not match expected 0x%X\n", buf, expect); \ + goto err; \ + } \ +} + +#define deserialize16(var) { \ + uint8_t buf[2]; \ + uint8_t ptr = 0; \ + if (g_filemanager->read(fd, buf, 2) != 2) { \ + printf("Failed to deserialize 2 bytes\n"); \ + goto err; \ + } \ + var = buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; } + +#define deserialize32(var) { \ + uint8_t buf[4]; \ + uint8_t ptr = 0; \ + if (g_filemanager->read(fd, buf, 4) != 4) { \ + printf("Failed to deserialize 4 bytes\n"); \ + goto err; \ + } \ + var = buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; } + +#define deserialize64(var) { \ + uint8_t buf[8]; \ + uint8_t ptr = 0; \ + if (g_filemanager->read(fd, buf, 8) != 8) { \ + printf("Failed to deserialize 8 bytes\n"); \ + goto err; \ + } \ + var = buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; \ + var <<= 8; var |= buf[ptr++]; } + +#define deserializeString(var) { \ + uint8_t c; \ + char *ptr = var; \ + while (1) { \ + if (g_filemanager->read(fd, &c, 1) != 1) { \ + printf("Failed to read string byte\n"); \ + goto err; \ + } \ + printf(". "); \ + *(ptr++) = c; \ + if (c == 0) { break; } \ + } \ +} + +#endif diff --git a/teensy/mouse.cpp b/teensy/mouse.cpp new file mode 120000 index 0000000..b2d16ed --- /dev/null +++ b/teensy/mouse.cpp @@ -0,0 +1 @@ +../apple/mouse.cpp \ No newline at end of file diff --git a/teensy/mouse.h b/teensy/mouse.h new file mode 120000 index 0000000..67eec56 --- /dev/null +++ b/teensy/mouse.h @@ -0,0 +1 @@ +../apple/mouse.h \ No newline at end of file diff --git a/teensy/physicalmouse.h b/teensy/physicalmouse.h new file mode 120000 index 0000000..80c4786 --- /dev/null +++ b/teensy/physicalmouse.h @@ -0,0 +1 @@ +../physicalmouse.h \ No newline at end of file diff --git a/teensy/serialize.h b/teensy/serialize.h new file mode 120000 index 0000000..c72647a --- /dev/null +++ b/teensy/serialize.h @@ -0,0 +1 @@ +../serialize.h \ No newline at end of file diff --git a/teensy/teensy-display.cpp b/teensy/teensy-display.cpp index 3d38079..e3b916a 100644 --- a/teensy/teensy-display.cpp +++ b/teensy/teensy-display.cpp @@ -345,10 +345,14 @@ void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) // with a color between the two. #if 1 - // This is straight blending, R/G/B average + // This is straight blending, R/G/B average, except in B&W mode uint16_t origColor = dmaBuffer[y+VOFFSET][(x>>1)+HOFFSET]; uint16_t newColor = loresPixelColors[color]; - cacheDoubleWidePixel(x>>1, y, blendColors(origColor, newColor)); + if (g_displayType == m_blackAndWhite) { + cacheDoubleWidePixel(x>>1, y, (origColor && newColor) ? 0xFFFF : 0x0000); + } else { + cacheDoubleWidePixel(x>>1, y, blendColors(origColor, newColor)); + } #endif #if 0 diff --git a/teensy/teensy-keyboard.cpp b/teensy/teensy-keyboard.cpp index 7912fbc..7df9b1b 100644 --- a/teensy/teensy-keyboard.cpp +++ b/teensy/teensy-keyboard.cpp @@ -4,6 +4,9 @@ #include "LRingBuffer.h" #include "teensy-println.h" +#include "globals.h" +#include "teensy-mouse.h" + const byte ROWS = 5; const byte COLS = 13; @@ -71,12 +74,13 @@ void TeensyKeyboard::pressedKey(uint8_t key) break; case PK_RSHFT: rightShiftPressed = 1; - break; + break; case PK_LOCK: capsLock = !capsLock; break; case PK_LA: - leftApplePressed = 1; + ((TeensyMouse *)g_mouse)->mouseButtonEvent(true); + leftApplePressed = 1; break; case PK_RA: rightApplePressed = 1; @@ -162,6 +166,7 @@ void TeensyKeyboard::releasedKey(uint8_t key) rightShiftPressed = 0; break; case PK_LA: + ((TeensyMouse *)g_mouse)->mouseButtonEvent(false); leftApplePressed = 0; break; case PK_RA: @@ -218,9 +223,15 @@ void TeensyKeyboard::maintainKeyboard() switch (keypad.key[i].kstate) { case PRESSED: vmkeyboard->keyDepressed(keypad.key[i].kchar); + if (keypad.key[i].kchar == PK_LSHFT) { + ((TeensyMouse *)g_mouse)->mouseButtonEvent(true); + } break; case RELEASED: vmkeyboard->keyReleased(keypad.key[i].kchar); + if (keypad.key[i].kchar == PK_LSHFT) { + ((TeensyMouse *)g_mouse)->mouseButtonEvent(false); + } break; case HOLD: case IDLE: diff --git a/teensy/teensy-mouse.cpp b/teensy/teensy-mouse.cpp new file mode 100644 index 0000000..b04974d --- /dev/null +++ b/teensy/teensy-mouse.cpp @@ -0,0 +1,85 @@ +#include +#include "teensy-mouse.h" + +#include "globals.h" + +#include "applevm.h" + +TeensyMouse::TeensyMouse() : PhysicalMouse() +{ + xpos = ypos = 0; + button = false; +} + +TeensyMouse::~TeensyMouse() +{ +} + +void TeensyMouse::gotMouseEvent(uint32_t buttonState, int32_t x, int32_t y) +{ + xpos += x; ypos += y; + + if (xpos < lowClamp[XCLAMP]) xpos=lowClamp[XCLAMP]; + if (xpos > highClamp[XCLAMP]) xpos=highClamp[XCLAMP]; + if (ypos < lowClamp[YCLAMP]) ypos = lowClamp[YCLAMP]; + if (ypos > highClamp[YCLAMP]) ypos = highClamp[YCLAMP]; +} + +void TeensyMouse::mouseButtonEvent(bool state) +{ + button = state; +} + +void TeensyMouse::maintainMouse() +{ + if (((AppleVM *)g_vm)->isMouseEnabled()) { + // only do this if the mouse card is enabled, so we're not incurring + // analogRead delays constantly + uint8_t paddle0 = g_paddles->paddle0(); + uint8_t paddle1 = g_paddles->paddle1(); + int16_t dx=0, dy=0; + if (paddle0 <= 25) { + dx = -1; + } else if (paddle0 >= 245) { + dx = 1; + } + if (paddle1 <= 25) { + dy = -1; + } else if (paddle1 >= 245) { + dy = 1; + } + if (dx || dy) { + gotMouseEvent(button, dx, dy); + } + } +} + +void TeensyMouse::setPosition(uint16_t x, uint16_t y) +{ + xpos = x; + ypos = y; + + if (xpos < lowClamp[XCLAMP]) xpos=lowClamp[XCLAMP]; + if (xpos > highClamp[XCLAMP]) xpos=highClamp[XCLAMP]; + if (ypos < lowClamp[YCLAMP]) ypos = lowClamp[YCLAMP]; + if (ypos > highClamp[YCLAMP]) ypos = highClamp[YCLAMP]; +} + +void TeensyMouse::getPosition(uint16_t *x, uint16_t *y) +{ + if (xpos < lowClamp[XCLAMP]) xpos=lowClamp[XCLAMP]; + if (xpos > highClamp[XCLAMP]) xpos=highClamp[XCLAMP]; + if (ypos < lowClamp[YCLAMP]) ypos = lowClamp[YCLAMP]; + if (ypos > highClamp[YCLAMP]) ypos = highClamp[YCLAMP]; + + uint16_t outx = xpos; + uint16_t outy = ypos; + + *x = outx; + *y = outy; +} + +bool TeensyMouse::getButton() +{ + return button; +} diff --git a/teensy/teensy-mouse.h b/teensy/teensy-mouse.h new file mode 100644 index 0000000..8365d70 --- /dev/null +++ b/teensy/teensy-mouse.h @@ -0,0 +1,24 @@ +#ifndef __TEENSY_MOUSE_H +#define __TEENSY_MOUSE_H + +#include "physicalmouse.h" + +class TeensyMouse : public PhysicalMouse { + public: + TeensyMouse(); + virtual ~TeensyMouse(); + + virtual void maintainMouse(); + + virtual void setPosition(uint16_t x, uint16_t y); + virtual void getPosition(uint16_t *x, uint16_t *y); + virtual bool getButton(); + + void gotMouseEvent(uint32_t buttonState, int32_t x, int32_t y); + void mouseButtonEvent(bool state); +private: + int32_t xpos, ypos; + bool button; +}; + +#endif diff --git a/teensy/teensy.ino b/teensy/teensy.ino index 36f39e1..0b22b97 100644 --- a/teensy/teensy.ino +++ b/teensy/teensy.ino @@ -6,6 +6,7 @@ #include "applevm.h" #include "teensy-display.h" #include "teensy-keyboard.h" +#include "teensy-mouse.h" #include "teensy-speaker.h" #include "teensy-paddles.h" #include "teensy-filemanager.h" @@ -211,6 +212,7 @@ void setup() // And the physical keyboard needs hooks in to the virtual keyboard... println(" keyboard"); g_keyboard = new TeensyKeyboard(g_vm->getKeyboard()); + g_mouse = new TeensyMouse(); println(" paddles"); g_paddles = new TeensyPaddles(A3, A2, g_invertPaddleX, g_invertPaddleY); @@ -286,7 +288,8 @@ void runMaintenance(uint32_t now) static uint32_t nextRuntime = 0; if (now >= nextRuntime) { - nextRuntime = now + 100000; // FIXME: what's a good time here? 1/10 sec? + // Run maintenance at 60 Hz because the mouse will need it + nextRuntime = now + 16667; if (!resetButtonDebouncer.read()) { // This is the BIOS interrupt. Wait for it to clear and process it. @@ -297,6 +300,7 @@ void runMaintenance(uint32_t now) } if (!g_biosInterrupt) { + g_mouse->maintainMouse(); g_keyboard->maintainKeyboard(); usb.maintain(); } diff --git a/vmram.cpp b/vmram.cpp index 6e861c7..8f58f78 100644 --- a/vmram.cpp +++ b/vmram.cpp @@ -9,11 +9,10 @@ #include "globals.h" #ifdef TEENSYDUINO -#include "iocompat.h" -EXTMEM uint8_t preallocatedRam[591*256]; +EXTMEM uint8_t preallocatedRam[599*256]; #else #include -uint8_t preallocatedRam[591*256]; +uint8_t preallocatedRam[599*256]; #endif #ifndef TEENSYDUINO @@ -47,6 +46,13 @@ void VMRam::writeByte(uint32_t addr, uint8_t value) preallocatedRam[addr] = value; } +uint8_t *VMRam::memPtr(uint32_t addr) +{ + // printf("Asked for preallocated RAM pointer at 0x%X\n", addr); + // printf("Base is 0x%llX\n", (unsigned long long) preallocatedRam); + return &preallocatedRam[addr]; +} + bool VMRam::Serialize(int8_t fd) { uint32_t size = sizeof(preallocatedRam); diff --git a/vmram.h b/vmram.h index 1d3e158..38af993 100644 --- a/vmram.h +++ b/vmram.h @@ -15,13 +15,15 @@ class VMRam { uint8_t readByte(uint32_t addr); void writeByte(uint32_t addr, uint8_t value); + uint8_t *memPtr(uint32_t addr); + bool Serialize(int8_t fd); bool Deserialize(int8_t fd); bool Test(); private: - // We need 591 pages of 256 bytes for the //e. + // We need 599 pages of 256 bytes for the //e. // // This previously used a split memory model where some of this was // in internal ram and some was external - and while that's not true @@ -37,7 +39,7 @@ class VMRam { // Pages 4-7 are 0x200 - 0x3FF. We want those in RAM too (text pages). // Has to be static if we're using the EXTMEM sectioning, so it's now in vmram.cpp :/ - //EXTMEM uint8_t preallocatedRam[591*256]; + //EXTMEM uint8_t preallocatedRam[599*256]; };