Skip to content

Instantly share code, notes, and snippets.

@HonkeyKong
Last active February 22, 2024 17:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HonkeyKong/2d5c27b0eaac443e11b56794a661a8a9 to your computer and use it in GitHub Desktop.
Save HonkeyKong/2d5c27b0eaac443e11b56794a661a8a9 to your computer and use it in GitHub Desktop.
Source code for Maniac Mansion mouse hack. Assembles with Ophis.
.data
.alias Port1 $4016
.alias Port2 $4017
.alias MOUSE_RIGHT %10000000
.alias MOUSE_LEFT %01000000
.alias M_SENSITIVITY %00110000
.alias MOUSE_DIRECTION %10000000
.alias MOUSE_MOTION %01111111
.alias BUTTON_A %10000000
.alias BUTTON_B %01000000
.alias BUTTON_SELECT %00100000
.alias BUTTON_START %00010000
.alias GameInput $02 ; This is where the game reads buttons.
.alias InputBuffer $04 ; This is the previous button buffer.
.alias LowerButtons $2F ; This is the lower buttons. (B/Select/Start)
.alias CutscenePlaying $6B08 ; Non-zero when cutscene is playing.
.alias UpperButtons $710E ; This is the upper buttons. (A)
.alias CursorXPos $7116 ; Cursor X Position.
.alias CursorYPos $7118 ; Cursor Y Position.
.alias ReadCounter $7221 ; Counter used by controller read subroutine.
.org $7FF0 ; End of external work RAM
.space MouseButtons 1 ; Mouse buttons, sensitivity and low signature bit.
.space MouseXMotion 1 ; Mouse direction and X motion in mickeys.
.space MouseYMotion 1 ; Mouse direction and Y motion in mickeys.
.space MouseMotion 1 ; Mouse motion in mickeys, distilled from X or Y.
.space MouseBuffer 1 ; Buffer with previous frame's mouse buttons.
.space SigHi 1 ; High signature byte, should always be zero.
.space InjectLower 1 ; Bits to inject into lower input buffer.
.space InjectUpper 1 ; Bits to inject into upper input buffer.
.space MouseDetected 1 ; Whether or not a mouse has been detected.
.space MouseTweaked 1 ; Whether or not the mouse sensitivity is set.
.checkpc $7FFF ; Just a quick check to make sure we're not
; spilling over into the ROM/Mapper space.
.text
; This is the first free space in PRG 0
; Put your JSR at $8320, AKA $330 in the ROM.
; Replace 68 FB with 10 98. Then, paste this
; assembled code at $1820 in the ROM file.
.org $9810
ReadControls:
; Push the registers off to the stack
PHA
TYA
PHA
TXA
PHA
; Read first two bytes and detect mouse.
JSR DetectMouse
; If the mouse wasn't detected, skip all this.
BIT MouseDetected
BMI +++
; Third byte is Y motion. Bit 7 determines direction.
LDX #$08
* LDA Port2
LSR
ROL MouseYMotion
DEX
CPX #$00
BNE -
; Fourth byte is X motion. Bit 7 determines direction.
LDX #$08
* LDA Port2
LSR
ROL MouseXMotion
DEX
CPX #$00
BNE -
; This is basically identical to the game's read routine.
* LDX #$01
* LDA GameInput, X
STA InputBuffer, X
LDA #$08
STA ReadCounter
LDA #$01
STA Port1
LDA #$00
STA Port1
* ASL GameInput, X
LDA Port1, X
AND #$01
ORA GameInput, X
STA GameInput, X
DEC ReadCounter
BNE -
DEX
BPL --
LDA InputBuffer
AND #BUTTON_B|BUTTON_SELECT|BUTTON_START
BNE +
LDA GameInput
AND #BUTTON_B|BUTTON_SELECT|BUTTON_START
BEQ +
STA LowerButtons
* LDA InputBuffer
AND #BUTTON_A
BNE +
LDA GameInput
AND #BUTTON_A
BEQ +
STA UpperButtons
LDA MouseDetected
BEQ ++
; Welcome back! Now we're processing the mouse input.
; !! Uncomment this line when building for hardware. !!
* JSR TweakMouse ; Tweak mouse sensitivity now that we're not reading it.
JSR ProcessMouse ; Process mouse motion and buttons.
; First load the lower button bits, like A
LDA LowerButtons ; Load the game's A button buffer.
ORA InjectLower ; OR our bits from the mouse into it.
STA LowerButtons ; Store the new mask in LowerButtons.
; Now we do the same for the upper bits
LDA UpperButtons ; Load the B/Start/Select buffer.
ORA InjectUpper ; OR the mouse bits in like before.
STA UpperButtons ; Store the new mask in UpperButtons.
; Now restore all the registers from the stack and return.
* PLA
TAX
PLA
TAY
PLA
RTS
ProcessMouse:
; Process mouse input.
LDA MouseDetected ; Check if mouse is detected
BNE + ; If detected, start processing.
RTS ; If not detected, skip processing.
* LDA #$00
STA InjectLower ; Clear our lower injection buffer.
STA InjectUpper ; Clear our upper injection buffer.
LDA MouseBuffer ; Read the mouse buffer.
AND #MOUSE_RIGHT ; Check the right button.
BNE + ; Skip if it's held.
LDA MouseButtons ; Read the mouse buttons.
AND #MOUSE_RIGHT ; Check the right button.
BEQ + ; Skip if it's not pressed.
LDA CutscenePlaying ; Check for a cutscene playing.
BEQ PressSelect ; Skip to Select if not.
PressB:
LDA InjectLower ; Load the lower injection buffer.
ORA #BUTTON_B ; OR the B button.
STA InjectLower ; Store the lower injection buffer.
JMP + ; Jump to the left button.
PressSelect:
LDA InjectLower ; Load the lower injection buffer.
ORA #BUTTON_SELECT ; OR the select button.
STA InjectLower ; Store the lower injection buffer.
* LDA MouseBuffer ; Same as above, but for the left button.
AND #MOUSE_LEFT
BNE +
LDA MouseButtons
AND #MOUSE_LEFT
BEQ +
LDA UpperButtons ; Load the upper injection buffer.
ORA #BUTTON_A ; OR the A button.
STA UpperButtons ; Store the upper injection buffer.
* LDA MouseButtons ; Load the mouse buttons.
STA MouseBuffer ; Copy into the buffer for the next frame.
LDA MouseYMotion ; Read the mouse Y motion.
AND #MOUSE_DIRECTION ; Check the direction.
BEQ ++ ; Branch ahead if it's moving down.
LDA MouseYMotion ; Load the motion again.
AND #MOUSE_MOTION ; Mask out the direction.
STA MouseMotion ; Store motion amount.
LDA CursorYPos ; Load Cursor Y position.
SEC ; Set the carry bit.
SBC MouseMotion ; Subtract the motion amount.
BCS +
LDA #$00
* STA CursorYPos ; Store the new Y position.
JMP +++ ; We're done with Y, jump to X.
* LDA MouseYMotion ; Read the mouse Y motion.
AND #MOUSE_MOTION ; Mask out the direction.
STA MouseMotion ; Store motion amount.
LDA CursorYPos ; Load cursor Y position.
CLC ; Clear the carry bit.
ADC MouseMotion ; Add the motion amount.
BCC +
LDA #$FF
* STA CursorYPos ; Store the new Y position.
* LDA MouseXMotion ; This is the same stuff.
AND #MOUSE_DIRECTION ; It just processes the X axis.
BEQ ++
LDA MouseXMotion
AND #MOUSE_MOTION
STA MouseMotion
LDA CursorXPos
SEC
SBC MouseMotion
BCS +
LDA #$00
* STA CursorXPos
JMP +++
* LDA MouseXMotion
AND #MOUSE_MOTION
STA MouseMotion
LDA CursorXPos
CLC
ADC MouseMotion
BCC +
LDA #$FF
* STA CursorXPos
* RTS
DetectMouse:
; Strobe port 1 to latch the controller ports
LDA #$01
STA Port1
LDA #$00
STA Port1
; Start reading bits from port 2
LDX #$08
* LDA Port2 ; Read port 2
LSR ; Shift first bit right into carry
ROL SigHi ; Rotate bit left into SigHi
DEX ; Decrement X
CPX #$00 ; Is it zero?
BNE - ; If no, repeat.
BIT SigHi ; Check the signature high byte
BMI ++ ; Exit if it's zero.
LDX #$08
* LDA Port2 ; Read the next 8 bits of port 2
LSR ; Shift into carry
ROL MouseButtons ; Rotate left into button mask
DEX ; Decrement X
CPX #$00 ; Check for zero
BNE - ; Loop back
LDA MouseButtons ; Read mouse buttons
AND #$0F ; Mask lower 4 bits
CMP #$01 ; Is only bit 1 raised?
BNE + ; It's a SNES controller. Abort!
LDA #$01 ; Looks like we have a mouse
STA MouseDetected ; Set the detected flag
RTS ; Return from Subroutine.
* LDA #$00 ; If we got here, there's no mouse.
STA MouseDetected ; Clear the detected flag.
RTS ; Return
; This subroutine serves as our means of
; setting the sensitivity on the SNES mouse
; to an acceptable threshold.
TweakMouse:
LDA MouseTweaked
BPL +
LDY #$01 ; Prepare to latch the controllers.
STY Port1 ; Latch port 1
LDA Port2 ; Strobe port 2 to change sensitivity.
DEY ; Decrement Y
STY Port1 ; Write port 1 to finish latch.
LDA #$01 ; Load 1 into accumulator
STA MouseTweaked ; Set the tweak flag.
* RTS ; Return
; End mouse processing
.byte 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment