aiie/apple/mouse.cpp
2021-01-17 21:04:06 -05:00

256 lines
6.7 KiB
C++

#include "mouse.h"
#include <string.h>
#include "globals.h"
// This ROM is part of the Aiie source code, but it was compiled and
// bundled as a binary. If you want to see what it's doing, take a
// look at mouserom.asm.
#include "mouse-rom.h"
enum {
SW_W_INITPR = 0x00,
SW_W_HANDLEIN = 0x01,
SW_R_ERR = 0x07,
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
};
enum {
ST_MOUSEENABLE = 0x01,
ST_INTMOUSE = 0x02,
ST_INTBUTTON = 0x04,
ST_INTVBL = 0x08,
// 0x10 is reserved
ST_XYCHANGED = 0x20,
ST_BUTTONWASDOWN = 0x40,
ST_BUTTONDOWN = 0x80
};
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()
{
}
void Mouse::performHack()
{
char buf[20];
uint16_t xpos, ypos;
g_mouse->getPosition(&xpos, &ypos);
bool curButton = g_mouse->getButton();
static bool prevButton = false;
bool isKeyPressed = ((AppleMMU *)(g_vm->getMMU()))->readDirect(0xC010, 0) & 0x80 ? true : false;
uint8_t buttonPressData = (curButton ? 1 : 3) + (prevButton ? 0 : 1);
sprintf(buf, "%d,%d,%s%d\015", xpos, ypos,
isKeyPressed ? "-" : "",
buttonPressData);
for (int i=0; i<strlen(buf); i++) {
g_vm->getMMU()->write(0x200 + i, buf[i] | 0x80);
}
// Put the string length in 0x638+$c0+n == $6FC for slot #4.
// The caller will pick that up and return it as the length of the buffer,
// which that caller will interpret as the next byte it has to display to
// the screen in the GETLN call; and since it's a return, it will display
// *nothing* and start parsing the string immediately.
g_vm->getMMU()->write(0x6fc, strlen(buf)-1);
}
uint8_t Mouse::readSwitches(uint8_t 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_W_INITPR:
v &= 0x01;
//printf("Simple init: value is 0x%X\n", v);
g_vm->getMMU()->write(0x7f8 + 4, v);
break;
case SW_W_HANDLEIN:
performHack();
break;
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);
curButton = g_mouse->getButton();
uint8_t newStatus = g_vm->getMMU()->read(0x778+4) & ~0xC7; // clears low 3 bits per docs
if (curButton) { newStatus |= ST_BUTTONDOWN; };
if (lastButton) { newStatus |= ST_BUTTONWASDOWN; };
lastButton = curButton;
if (lastX != xpos || lastY != ypos) {
newStatus |= ST_XYCHANGED;
lastX = xpos; lastY = ypos;
}
MMU *m = g_vm->getMMU();
m->write(0x578+4, (xpos >> 8) & 0xFF); // high X
m->write(0x478+4, xpos & 0xFF); // low X
m->write(0x5F8+4, (ypos >> 8) & 0xFF); // high Y
m->write(0x4F8+4, ypos); // low Y
m->write(0x778+4, newStatus);
}
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:
g_vm->getMMU()->write(0x778+4, interruptsTriggered);
g_vm->getMMU()->write(0x6B8+4, interruptsTriggered); // hack to appease ROM
interruptsTriggered = 0;
g_cpu->deassertIrq();
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));
// Fix for Blazing Paddles, which requests a negative lowval,
// but we're casting them as unsigned
if (lowval > highval) {
highval = (lowval + highval) & 0xFFFF;
lowval = 0;
}
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)
{
#ifdef TEENSYDUINO
Serial.println("loading Mouse rom");
for (uint16_t i=0; i<=0xFF; i++) {
toWhere[i] = pgm_read_byte(&romData[i]);
}
#else
printf("loading Mouse rom\n");
memcpy(toWhere, romData, 256);
#endif
}
bool Mouse::hasExtendedRom()
{
return false;
}
void Mouse::loadExtendedRom(uint8_t *toWhere, uint16_t byteOffset)
{
}
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->assertIrq();
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->assertIrq();
interruptsTriggered |= ST_INTMOUSE;
lastXForInt = xpos; lastYForInt = ypos;
} else if ( (status & ST_MOUSEENABLE) &&
(status & ST_INTBUTTON) &&
lastButtonForInt != g_mouse->getButton()) {
g_cpu->assertIrq();
interruptsTriggered |= ST_INTBUTTON;
lastButtonForInt = g_mouse->getButton();
}
}
}
bool Mouse::isEnabled()
{
return status & ST_MOUSEENABLE;
}