izapple2/cardMouse.go
2024-11-04 21:49:33 +01:00

272 lines
6.8 KiB
Go

package izapple2
import (
"fmt"
)
/*
Mouse card implementation. Does not emulate a real card, only the behaviour. Idea taken
from aiie (https://hackaday.io/project/19925-aiie-an-embedded-apple-e-emulator/log/188017-entry-23-here-mousie-mousie-mousie)
See:
https://www.apple.asimov.net/documentation/hardware/io/AppleMouse%20II%20User%27s%20Manual.pdf
https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/Digitizers/Apple%20Mouse%20Interface%20Card/Documentation/Apple%20II%20Mouse%20Technical%20Notes.pdf
http://www.1000bit.it/support/manuali/apple/technotes/mous/tn.mous.2.html
The management of IN# and PR# is copied from cardInOut
Not compatible with A2OSX that needs interrupts on VBL
*/
// CardMouse represents a SmartPort card
type CardMouse struct {
cardBase
lastX, lastY uint16
lastPressed bool
minX, minY, maxX, maxY uint16
mode uint8
response string
iOut int
iIn int
}
func newCardMouseBuilder() *cardBuilder {
return &cardBuilder{
name: "Mouse Card",
description: "Mouse card implementation, does not emulate a real card, only the firmware behaviour",
buildFunc: func(params map[string]string) (Card, error) {
return &CardMouse{
maxX: 0x3ff,
maxY: 0x3ff,
}, nil
},
}
}
const (
mouseXLo = uint16(0x478)
mouseYLo = uint16(0x4f8)
mouseXHi = uint16(0x578)
mouseYHi = uint16(0x5f8)
mouseStatus = uint16(0x778)
mouseMode = uint16(0x7f8)
)
const (
mouseModeEnabled = uint8(1)
mouseModeIntMoveEnabled = uint8(2)
mouseModeIntButtonEnabled = uint8(4)
mouseModeIntVBlankEnabled = uint8(8)
)
func (c *CardMouse) set(field uint16, value uint8) {
// Update the card screen-holes
c.a.mmu.Poke(field+uint16(c.slot), value)
}
func (c *CardMouse) get(field uint16) uint8 {
// Read from the card screen-holes
return c.a.mmu.Peek(field /*+ uint16(c.slot)*/)
}
func (c *CardMouse) setMode(mode uint8) {
c.mode = mode
enabled := mode&mouseModeEnabled == 1
moveInts := mode&mouseModeIntMoveEnabled == 1
buttonInts := mode&mouseModeIntButtonEnabled == 1
vBlankInts := mode&mouseModeIntVBlankEnabled == 1
c.tracef("Mode set to 0x%02x. Enabled %v. Interrups: move=%v, button=%v, vblank=%v.\n",
mode, enabled, moveInts, buttonInts, vBlankInts)
if moveInts || buttonInts || vBlankInts {
panic("Mouse interrupts not implemented")
}
}
func (c *CardMouse) checkFromFirmware() {
pc, _ := c.a.cpu.GetPCAndSP()
if (pc >> 8) != 0xc0+uint16(c.slot) {
c.tracef("Softswitch access from outside the firmware. It will not work.\n")
}
}
func (c *CardMouse) readMouse() (uint16, uint16, bool) {
x, y, pressed := c.a.io.mouse.ReadMouse()
xTrans := uint16(uint64(c.maxX-c.minX) * uint64(x) / 65536)
yTrans := uint16(uint64(c.maxY-c.minY) * uint64(y) / 65536)
return xTrans, yTrans, pressed
}
func (c *CardMouse) assign(a *Apple2, slot int) {
a.usesMouse = true
c.addCardSoftSwitchR(0, func() uint8 {
c.checkFromFirmware()
if c.iOut == 0 {
// Create a new response
x, y, pressed := c.readMouse()
button := 1
if !pressed {
button += 2
}
if !c.lastPressed {
button++
}
keyboard := "+"
strobed := (c.a.io.softSwitchesData[ioDataKeyboard] & (1 << 7)) == 0
if !strobed {
keyboard = "-"
}
c.response = fmt.Sprintf("%v,%v,%v%v\r", x, y, keyboard, button)
}
value := uint8(c.response[c.iOut])
c.iOut++
if c.iOut == len(c.response) {
c.iOut = 0
}
value += 0x80
c.tracef("IN#%v -> %02x.\n", slot, value)
return value
}, "MOUSEOUT")
c.addCardSoftSwitchW(1, func(value uint8) {
c.checkFromFirmware()
c.tracef("PR#%v <- %02x\n", slot, value)
if c.iIn == 0 {
// We care only about the first byte
c.setMode(value & 0x0f)
}
c.iIn++
if value == 13 {
c.iIn = 0 // Ready for the next command
}
}, "MOUSEIN")
c.addCardSoftSwitchW(2, func(value uint8) {
c.checkFromFirmware()
c.tracef("SetMouse(0x%02v)\n", value)
c.setMode(value & 0x0f)
}, "SETMOUSE")
c.addCardSoftSwitchW(3, func(value uint8) {
c.checkFromFirmware()
c.tracef("ServeMouse() NOT IMPLEMENTED\n")
panic("Mouse interrupts not implemented")
}, "SERVEMOUSE")
c.addCardSoftSwitchW(4, func(value uint8) {
c.checkFromFirmware()
if c.mode&mouseModeEnabled == 1 {
x, y, pressed := c.readMouse()
status := uint8(0)
if pressed {
status |= 1 << 7
}
if c.lastPressed {
status |= 1 << 6
}
if (x != c.lastX) || (y != c.lastY) {
status |= 1 << 5
}
c.set(mouseXHi, uint8(x>>8))
c.set(mouseYHi, uint8(y>>8))
c.set(mouseXLo, uint8(x))
c.set(mouseYLo, uint8(y))
c.set(mouseStatus, status)
c.set(mouseMode, c.mode)
if (status&(1<<5) != 0) || (pressed != c.lastPressed) {
c.tracef("ReadMouse(): x: %v, y: %v, pressed: %v\n",
x, y, pressed)
}
c.lastX = x
c.lastY = y
c.lastPressed = pressed
}
}, "READMOUSE")
c.addCardSoftSwitchW(5, func(value uint8) {
c.checkFromFirmware()
c.tracef("ClearMouse() NOT IMPLEMENTED\n")
c.set(mouseXHi, 0)
c.set(mouseYHi, 0)
c.set(mouseXLo, 0)
c.set(mouseYLo, 0)
}, "CLEARMOUSE")
c.addCardSoftSwitchW(6, func(value uint8) {
c.checkFromFirmware()
c.tracef("PosMouse() NOT IMPLEMENTED\n")
}, "POSMOUSE")
c.addCardSoftSwitchW(7, func(value uint8) {
c.checkFromFirmware()
c.tracef("ClampMouse(%v)\n", value)
if value == 0 {
c.minX = uint16(c.get(mouseXLo)) + uint16(c.get(mouseXHi))<<8
c.maxX = uint16(c.get(mouseYLo)) + uint16(c.get(mouseYHi))<<8
} else if value == 1 {
c.minY = uint16(c.get(mouseXLo)) + uint16(c.get(mouseXHi))<<8
c.maxY = uint16(c.get(mouseYLo)) + uint16(c.get(mouseYHi))<<8
}
c.tracef("Current bounds: X[%v-%v], Y[%v-%v],\n", c.minX, c.maxX, c.minY, c.maxY)
}, "CLAMPMOUSE")
c.addCardSoftSwitchW(8, func(value uint8) {
c.checkFromFirmware()
c.tracef("HomeMouse() NOT IMPLEMENTED\n")
}, "HOMEMOUSE")
c.addCardSoftSwitchW(9, func(value uint8) {
c.checkFromFirmware()
c.tracef("InitMouse()\n")
c.minX = 0
c.minY = 0
c.maxX = 0x3ff
c.maxY = 0x3ff
c.mode = 0
}, "INITMOUSE")
c.addCardSoftSwitchW(0xc, func(value uint8) {
c.checkFromFirmware()
// See http://www.1000bit.it/support/manuali/apple/technotes/mous/tn.mous.2.html
c.tracef("TimeData(%v) NOT IMPLEMENTED\n", value)
}, "TIMEDATEMOUSE")
data := buildBaseInOutRom(slot)
c.romCsxx = newMemoryRangeROM(0xC200, data, "Mouse card")
// Identification as a mouse card
// From Technical Note Misc #8, "Pascal 1.1 Firmware Protocol ID Bytes":
data[0x05] = 0x38
data[0x07] = 0x18
data[0x0b] = 0x01
data[0x0c] = 0x20
// From "AppleMouse // User's Manual", Appendix B:
data[0xfb] = 0xd6
// Set 8 entrypoints to sofstwitches 2 to 1f
for i := uint8(0); i < 14; i++ {
base := 0x60 + 0x05*i
data[0x12+i] = base
data[base+0] = 0x8D // STA $C0x2
data[base+1] = 0x82 + i + uint8(slot<<4)
data[base+2] = 0xC0
data[base+3] = 0x18 // CLC ;no error
data[base+4] = 0x60 // RTS
}
c.cardBase.assign(a, slot)
}