commit a7943b50ec7fbcaac40f5256a6cc6ce1a1b1f272 Author: Quinn Dunki Date: Tue Jul 14 18:21:27 2015 -0700 Inital commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad69748 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +mousedemo.lst diff --git a/AppleCommander.jar b/AppleCommander.jar new file mode 100644 index 0000000..f74e7cb Binary files /dev/null and b/AppleCommander.jar differ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d12537a --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +# +# Makefile +# Mouse driver +# +# Created by Quinn Dunki on 7/14/15. +# One Girl, One Laptop Productions +# http://www.quinndunki.com +# http://www.quinndunki.com/blondihacks +# + + +CL65=cl65 +AC=AppleCommander.jar +ADDR=6000 + +PGM=mousedemo + +all: $(PGM) + + +$(PGM): + @PATH=$(PATH):/usr/local/bin; $(CL65) -t apple2enh --start-addr $(ADDR) -l$(PGM).lst $(PGM).s + java -jar $(AC) -d $(PGM).dsk $(PGM) + java -jar $(AC) -p $(PGM).dsk $(PGM) BIN 0x$(ADDR) < $(PGM) + rm -f $(PGM) + rm -f $(PGM).o + osascript V2Make.scpt $(PROJECT_DIR) $(PGM) + +clean: + rm -f $(PGM) + rm -f $(PGM).o + diff --git a/Mouse.xcodeproj/project.pbxproj b/Mouse.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3f2fb5c --- /dev/null +++ b/Mouse.xcodeproj/project.pbxproj @@ -0,0 +1,199 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXFileReference section */ + 70355B941B55977F00D79881 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 70355B951B55977F00D79881 /* mouse.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = mouse.s; sourceTree = ""; }; + 70355B961B55977F00D79881 /* mousedemo.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = mousedemo.s; sourceTree = ""; }; + 70355B971B55977F00D79881 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 70355B981B5597EC00D79881 /* macros.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = macros.s; sourceTree = ""; }; + 70355B991B5597EC00D79881 /* switches.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = switches.s; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 70355B891B55975C00D79881 = { + isa = PBXGroup; + children = ( + 70355B971B55977F00D79881 /* README.md */, + 70355B941B55977F00D79881 /* Makefile */, + 70355B981B5597EC00D79881 /* macros.s */, + 70355B991B5597EC00D79881 /* switches.s */, + 70355B951B55977F00D79881 /* mouse.s */, + 70355B961B55977F00D79881 /* mousedemo.s */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + 70355B8E1B55975C00D79881 /* Mouse */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "$(ACTION)"; + buildConfigurationList = 70355B911B55975C00D79881 /* Build configuration list for PBXLegacyTarget "Mouse" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + dependencies = ( + ); + name = Mouse; + passBuildSettingsInEnvironment = 1; + productName = Mouse; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXProject section */ + 70355B8A1B55975C00D79881 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = "Quinn Dunki"; + TargetAttributes = { + 70355B8E1B55975C00D79881 = { + CreatedOnToolsVersion = 6.4; + }; + }; + }; + buildConfigurationList = 70355B8D1B55975C00D79881 /* Build configuration list for PBXProject "Mouse" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 70355B891B55975C00D79881; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 70355B8E1B55975C00D79881 /* Mouse */, + ); + }; +/* End PBXProject section */ + +/* Begin XCBuildConfiguration section */ + 70355B8F1B55975C00D79881 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 70355B901B55975C00D79881 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 70355B921B55975C00D79881 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUGGING_SYMBOLS = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 70355B931B55975C00D79881 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 70355B8D1B55975C00D79881 /* Build configuration list for PBXProject "Mouse" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 70355B8F1B55975C00D79881 /* Debug */, + 70355B901B55975C00D79881 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 70355B911B55975C00D79881 /* Build configuration list for PBXLegacyTarget "Mouse" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 70355B921B55975C00D79881 /* Debug */, + 70355B931B55975C00D79881 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 70355B8A1B55975C00D79881 /* Project object */; +} diff --git a/Mouse.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Mouse.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..5d6f320 --- /dev/null +++ b/Mouse.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Mouse.xcodeproj/project.xcworkspace/xcuserdata/qd.xcuserdatad/UserInterfaceState.xcuserstate b/Mouse.xcodeproj/project.xcworkspace/xcuserdata/qd.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..69bc63d Binary files /dev/null and b/Mouse.xcodeproj/project.xcworkspace/xcuserdata/qd.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Mouse.xcodeproj/xcuserdata/qd.xcuserdatad/xcschemes/Mouse.xcscheme b/Mouse.xcodeproj/xcuserdata/qd.xcuserdatad/xcschemes/Mouse.xcscheme new file mode 100644 index 0000000..560c915 --- /dev/null +++ b/Mouse.xcodeproj/xcuserdata/qd.xcuserdatad/xcschemes/Mouse.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mouse.xcodeproj/xcuserdata/qd.xcuserdatad/xcschemes/xcschememanagement.plist b/Mouse.xcodeproj/xcuserdata/qd.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..192a19d --- /dev/null +++ b/Mouse.xcodeproj/xcuserdata/qd.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Mouse.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 70355B8E1B55975C00D79881 + + primary + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..683230e --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +*Mouse Driver for Apple II computers* + +Supports the Apple //e Enhanced with a mouse card in any slot. Also supports Apple //c and //c+. May work on Apple IIgs as well. + + + +


+ +Known issues +------------ + +- Hitting Reset during a WeeGUI application will leave your Apple II in an unsafe state. +- Calling WGEraseView on a view that shares border rendering with other views will require manually redrawing those views. +- Included sample ASMDEMO has a slight tendancy to step on ProDOS's buffers. This is mostly harmless. + + +To Do: +------ + diff --git a/V2Make.scpt b/V2Make.scpt new file mode 100644 index 0000000..9dfae24 Binary files /dev/null and b/V2Make.scpt differ diff --git a/macros.s b/macros.s new file mode 100644 index 0000000..2c6f932 --- /dev/null +++ b/macros.s @@ -0,0 +1,120 @@ +; +; macros.s +; Generally useful macros for 6502 code +; +; Created by Quinn Dunki on 8/15/14. +; Copyright (c) 2014 One Girl, One Laptop Productions. All rights reserved. +; + + +; Macros + +.macro SETSWITCH name ; Sets the named softswitch (assumes write method) + sta name +.endmacro + + +.macro SAVE_AXY ; Saves all registers + pha + phx + phy +.endmacro + + +.macro RESTORE_AXY ; Restores all registers + ply + plx + pla +.endmacro + + +.macro SAVE_AY ; Saves accumulator and Y index + pha + phy +.endmacro + + +.macro RESTORE_AY ; Restores accumulator and Y index + ply + pla +.endmacro + + +.macro SAVE_AX ; Saves accumulator and X index + pha + phx +.endmacro + + +.macro RESTORE_AX ; Restores accumulator and X index + plx + pla +.endmacro + + +.macro SAVE_XY ; Saves X and Y index + phx + phy +.endmacro + + +.macro RESTORE_XY ; Restores X and Y index + ply + plx +.endmacro + + +.macro SAVE_ZPP ; Saves Zero Page locations we use for parameters + lda PARAM0 + pha + lda PARAM1 + pha + lda PARAM2 + pha + lda PARAM3 + pha +.endmacro + + +.macro RESTORE_ZPP ; Restores Zero Page locations we use for parameters + pla + sta PARAM3 + pla + sta PARAM2 + pla + sta PARAM1 + pla + sta PARAM0 +.endmacro + + +.macro SAVE_ZPS ; Saves Zero Page locations we use for scratch + lda SCRATCH0 + pha + lda SCRATCH1 + pha +.endmacro + + +.macro RESTORE_ZPS ; Restores Zero Page locations we use for scratch + pla + sta SCRATCH1 + pla + sta SCRATCH0 +.endmacro + + +.macro PARAM16 addr + lda #addr + sta PARAM1 +.endmacro + + +.macro CALL16 func,addr + PARAM16 addr + jsr func +.endmacro + + diff --git a/mouse.s b/mouse.s new file mode 100644 index 0000000..998b2ab --- /dev/null +++ b/mouse.s @@ -0,0 +1,505 @@ +; +; mouse.s +; Mouse driver for Apple //e Enhanced with mouse card (any slot), or Apple //c(+) +; +; Created by Quinn Dunki on 7/14/15. +; Copyright (c) 2014 One Girl, One Laptop Productions. All rights reserved. +; + + +.include "macros.s" +.include "switches.s" + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Mouse clamping values. These are set to a convenient one-byte +; range, but you can change to suit your application. If you +; change them, also change the scaling math below (search for SCALING) +; Hardware produces values 0-1023 in both dimensions if not clamped +SCALE_X_IIE = $027f ; 640-1 +SCALE_Y_IIE = $02e0 ; 736 + +; //c tracks much slower, so smaller clamps and no scaling works better +SCALE_X_IIC = $004f ; 8-1 +SCALE_Y_IIC = $0017 ; 24-1 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ProDOS ROM entry points and constants +; +PRODOS_MLI = $bf00 + +ALLOC_INTERRUPT = $40 +DEALLOC_INTERRUPT = $41 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Mouse firmware ROM entry points and constants +; + +; These mouse firmware entry points are offsets from the firmware +; entry point of the slot, and also indirect. +SETMOUSE = $12 +SERVEMOUSE = $13 +READMOUSE = $14 +CLEARMOUSE = $15 +POSMOUSE = $16 +CLAMPMOUSE = $17 +HOMEMOUSE = $18 +INITMOUSE = $19 + +MOUSTAT = $0778 ; + Slot Num +MOUSE_XL = $0478 ; + Slot Num +MOUSE_XH = $0578 ; + Slot Num +MOUSE_YL = $04f8 ; + Slot Num +MOUSE_YH = $05f8 ; + Slot Num +MOUSE_CLAMPL = $04f8 ; Upper mouse clamp (LSB). Slot independent. +MOUSE_CLAMPH = $05f8 ; Upper mouse clamp (MSB). Slot independent. +MOUSE_ZEROL = $0478 ; Zero value of mouse (LSB). Slot independent. +MOUSE_ZEROH = $0578 ; Zero value of mouse (MSB). Slot independent. + +MOUSTAT_MASK_BUTTONINT = %00000100 +MOUSTAT_MASK_MOVEINT = %00000010 +MOUSTAT_MASK_DOWN = %10000000 +MOUSTAT_MASK_WASDOWN = %01000000 +MOUSTAT_MASK_MOVED = %00100000 + +MOUSEMODE_OFF = $00 ; Mouse off +MOUSEMODE_PASSIVE = $01 ; Passive mode (polling only) +MOUSEMODE_MOVEINT = $03 ; Interrupts on movement +MOUSEMODE_BUTINT = $05 ; Interrupts on button +MOUSEMODE_COMBINT = $07 ; Interrupts on movement and button + + +; Mouse firmware is all indirectly called, because +; it moved around a lot in different Apple II ROM +; versions. This macro helps abstracts this for us. +.macro CALLMOUSE name + ldx #name + jsr WGCallMouse +.endmacro + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGEnableMouse +; Prepares the mouse for use +; +WGEnableMouse: + pha + + SETSWITCH PAGE2OFF + + ; Find slot number and calculate the various indirections needed + jsr WGFindMouse + bcs WGEnableMouse_Error + + ; Note if we're a //e or //c, because mouse tracking and interrupts are different + lda $fbb3 + cmp #$06 + bne WGEnableMouse_Error ; II or II+? Sorry... + lda $fbc0 + bne WGEnableMouse_IIe + lda #1 + sta WG_APPLEIIC + +WGEnableMouse_IIe: + ; Install our interrupt handler via ProDOS (play nice!) + jsr PRODOS_MLI + .byte ALLOC_INTERRUPT + .addr WG_PRODOS_ALLOC + bne WGEnableMouse_Error ; ProDOS will return here with Z clear on error + + ; Initialize the mouse + stz WG_MOUSEPOS_X + stz WG_MOUSEPOS_Y + stz WG_MOUSEBG + + CALLMOUSE INITMOUSE + bcs WGEnableMouse_Error ; Firmware sets carry if mouse is not available + + CALLMOUSE CLEARMOUSE + + lda #MOUSEMODE_COMBINT ; Enable combination interrupt mode + CALLMOUSE SETMOUSE + + ; Set the mouse's zero postion to (1,1), since we're in text screen space + stz MOUSE_ZEROH + lda #0 + sta MOUSE_ZEROL + lda #1 + CALLMOUSE CLAMPMOUSE + lda #0 + CALLMOUSE CLAMPMOUSE + + ; Scale the mouse's range into something easy to do math with, + ; while retaining as much range of motion and precision as possible + lda WG_APPLEIIC + bne WGEnableMouse_ConfigIIc ; Sorry //c, no scaling for you + + lda #SCALE_X_IIE + sta MOUSE_CLAMPH + lda #0 + CALLMOUSE CLAMPMOUSE + + lda #SCALE_Y_IIE + sta MOUSE_CLAMPH + lda #1 + CALLMOUSE CLAMPMOUSE + bra WGEnableMouse_Activate + +WGEnableMouse_Error: + stz WG_MOUSEACTIVE + +WGEnableMouse_done: ; Exit point here for branch range + pla + rts + +WGEnableMouse_ConfigIIc: ; //c's tracking is weird. Need to clamp to a much smaller range + lda #SCALE_X_IIC + sta MOUSE_CLAMPH + lda #0 + CALLMOUSE CLAMPMOUSE + + lda #SCALE_Y_IIC + sta MOUSE_CLAMPH + lda #1 + CALLMOUSE CLAMPMOUSE + +WGEnableMouse_Activate: + lda #1 + sta WG_MOUSEACTIVE + + cli ; Once all setup is done, it's safe to enable interrupts + bra WGEnableMouse_done + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGDisableMouse +; Shuts off the mouse when we're done with it +; +WGDisableMouse: + pha + + SETSWITCH PAGE2OFF + + lda WG_MOUSEACTIVE ; Never activated the mouse + beq WGDisableMouse_done + + lda MOUSEMODE_OFF + CALLMOUSE SETMOUSE + + stz WG_MOUSEACTIVE + + ; Remove our interrupt handler via ProDOS (done playing nice!) + lda WG_PRODOS_ALLOC+1 ; Copy interrupt ID that ProDOS gave us + sta WG_PRODOS_DEALLOC+1 + + jsr PRODOS_MLI + .byte DEALLOC_INTERRUPT + .addr WG_PRODOS_DEALLOC + + jsr WGUndrawPointer ; Be nice if we're disabled during a program + +WGDisableMouse_done: + pla + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGCallMouse +; Calls a mouse firmware routine. Here's where we handle all +; the layers of indirection needed to call mouse firmware. The +; firmware moved in ROM several times over the life of the +; Apple II, so it's kind of a hassle to call it. +; X: Name of routine (firmware offset constant) +; Side effects: Clobbers all registers +WGCallMouse: + stx WGCallMouse+4 ; Use self-modifying code to smooth out some indirection + + ; This load address is overwritten by the above code, AND by the mouse set + ; up code, to make sure we have the right slot entry point and firmware + ; offset + ldx $c400 ; Self-modifying code! + stx WG_MOUSE_JUMPL ; Get low byte of final jump from firmware + + php ; Note that mouse firmware is not re-entrant, + sei ; so we must disable interrupts inside them + + jsr WGCallMouse_redirect + plp ; Restore interrupts to previous state + rts + +WGCallMouse_redirect: + ldx WG_MOUSE_JUMPH + ldy WG_MOUSE_SLOTSHIFTED + jmp (WG_MOUSE_JUMPL) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGFindMouse +; Figures out which slot (//e) or port (//c) the mouse is in. +; It moved around a lot over the years. Sets it to 0 if no mouse +; could be found +; OUT C: Set if no mouse could be found +WGFindMouse: + SAVE_AX + + ldx #7 + +WGFindMouse_loop: + txa ; Compute slot firmware locations for this loop + ora #$c0 + sta WGFindMouse_loopModify+2 ; Self-modifying code! + sta WGFindMouse_loopModify+9 + sta WGFindMouse_loopModify+16 + sta WGFindMouse_loopModify+23 + sta WGFindMouse_loopModify+30 + +WGFindMouse_loopModify: + ; Check for the magic 5-byte pattern that gives away the mouse card + lda $c005 ; These addresses are modified in place on + cmp #$38 ; each loop iteration + bne WGFindMouse_nextSlot + lda $c007 + cmp #$18 + bne WGFindMouse_nextSlot + lda $c00b + cmp #$01 + bne WGFindMouse_nextSlot + lda $c00c + cmp #$20 + bne WGFindMouse_nextSlot + lda $c0fb + cmp #$d6 + bne WGFindMouse_nextSlot + bra WGFindMouse_found + +WGFindMouse_nextSlot: + dex + bmi WGFindMouse_none + bra WGFindMouse_loop + +WGFindMouse_found: + ; Found it! Now configure all our indirection lookups + stx WG_MOUSE_SLOT + lda #$c0 + ora WG_MOUSE_SLOT + sta WG_MOUSE_JUMPH + sta WGCallMouse+5 ; Self-modifying code! + txa + asl + asl + asl + asl + sta WG_MOUSE_SLOTSHIFTED + clc + bra WGFindMouse_done + +WGFindMouse_none: + stz WG_MOUSE_SLOT + sec + +WGFindMouse_done: + RESTORE_AX + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGMouseInterruptHandler +; Handles interrupts that may be related to the mouse +; This is a ProDOS-compliant interrupt handling routine, and +; should be installed and removed via ProDOS as needed. +; +; IMPORTANT: This routine is NOT MLI-reentrant, which means MLI +; calls can NOT be made within this handler. See page 108 of the +; ProDOS 8 Technical Reference Manual if this feature needs to be +; added. +; +WGMouseInterruptHandler: + cld ; ProDOS interrupt handlers must open with this + SAVE_AXY + + CALLMOUSE SERVEMOUSE + bcs WGMouseInterruptHandler_disregard + + php + sei + + lda PAGE2 ; Need to preserve text bank, because we may interrupt rendering + pha + SETSWITCH PAGE2OFF + + ldx WG_MOUSE_SLOT + lda MOUSTAT,x ; Check interrupt status bits first, because READMOUSE clears them + and #MOUSTAT_MASK_BUTTONINT + bne WGMouseInterruptHandler_button + + jsr WGUndrawPointer ; Erase the old mouse pointer + + ; Read the mouse state. Note that interrupts need to remain + ; off until after the data is copied. + CALLMOUSE READMOUSE + + ldx WG_MOUSE_SLOT + lda MOUSTAT,x ; Movement/button status bits are now valid + sta WG_MOUSE_STAT + + lda WG_APPLEIIC + bne WGMouseInterruptHandler_IIc + + ; Read mouse position and transform it into screen space + ; SCALING: If you change the clamps, change this division from + ; 1024 to match your new values. + lsr MOUSE_XH,x + ror MOUSE_XL,x + lsr MOUSE_XH,x + ror MOUSE_XL,x + lsr MOUSE_XH,x + ror MOUSE_XL,x + + lda MOUSE_XL,x + sta WG_MOUSEPOS_X + + lsr MOUSE_YH,x + ror MOUSE_YL,x + lsr MOUSE_YH,x + ror MOUSE_YL,x + lsr MOUSE_YH,x + ror MOUSE_YL,x + lsr MOUSE_YH,x + ror MOUSE_YL,x + lsr MOUSE_YH,x + ror MOUSE_YL,x + + lda MOUSE_YL,x + sta WG_MOUSEPOS_Y + bra WGMouseInterruptHandler_draw + +WGMouseInterruptHandler_IIc: ; IIc tracks much slower, so don't scale + lda MOUSE_XL,x + sta WG_MOUSEPOS_X + lda MOUSE_YL,x + sta WG_MOUSEPOS_Y + +WGMouseInterruptHandler_draw: + jsr WGDrawPointer ; Redraw the pointer + bra WGMouseInterruptHandler_intDone + +WGMouseInterruptHandler_disregard: + ; Carry will still be set here, to notify ProDOS that + ; this interrupt was not ours + RESTORE_AXY + rts + +WGMouseInterruptHandler_button: + CALLMOUSE READMOUSE + ldx WG_MOUSE_SLOT + lda MOUSTAT,x ; Movement/button status bits are now valid + sta WG_MOUSE_STAT + + bit WG_MOUSE_STAT ; Check for rising edge of button state + bpl WGMouseInterruptHandler_intDone + bvs WGMouseInterruptHandler_intDone ; Held, so ignore (//c only, but more elegant code to leave in for both) + + ; Button went down, so make a note of location for later + lda WG_MOUSEPOS_X + sta WG_MOUSECLICK_X + lda WG_MOUSEPOS_Y + sta WG_MOUSECLICK_Y + +WGMouseInterruptHandler_intDone: + pla ; Restore text bank + bpl WGMouseInterruptHandler_intDoneBankOff + SETSWITCH PAGE2ON + bra WGMouseInterruptHandler_done + +WGMouseInterruptHandler_intDoneBankOff: + SETSWITCH PAGE2OFF + +WGMouseInterruptHandler_done: + RESTORE_AXY + + plp + clc ; Notify ProDOS this was our interrupt + rts + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGUndrawPointer +; Unplots the mouse pointer at current location +; Stub for your use +; +WGUndrawPointer: + rts + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGDrawPointer +; Plots the mouse pointer at current location +; Stub for your use +; +WGDrawPointer: + rts + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Mouse API state +; + +; Useful things you can poll in your code: +WG_MOUSEACTIVE: +.byte 0 +WG_MOUSEPOS_X: +.byte 39 +WG_MOUSEPOS_Y: +.byte 11 +WG_MOUSECLICK_X: +.byte $ff +WG_MOUSECLICK_Y: +.byte 0 + + +; Internal state for the driver (no touchy!) +WG_MOUSE_STAT: +.byte 0 +WG_MOUSEBG: +.byte 0 +WG_APPLEIIC: +.byte 0 +WG_MOUSE_JUMPL: +.byte 0 +WG_MOUSE_JUMPH: +.byte 0 +WG_MOUSE_SLOT: +.byte 0 +WG_MOUSE_SLOTSHIFTED: +.byte 0 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ProDOS system call parameter blocks +; +WG_PRODOS_ALLOC: + .byte 2 + .byte 0 ; ProDOS returns an ID number for the interrupt here + .addr WGMouseInterruptHandler + +WG_PRODOS_DEALLOC: + .byte 1 + .byte 0 ; To be filled with ProDOS ID number + + diff --git a/mousedemo.dsk b/mousedemo.dsk new file mode 100644 index 0000000..62e0df7 Binary files /dev/null and b/mousedemo.dsk differ diff --git a/mousedemo.s b/mousedemo.s new file mode 100644 index 0000000..ac34948 --- /dev/null +++ b/mousedemo.s @@ -0,0 +1,74 @@ +; +; mousedemo.s +; Mouse driver sample application +; +; Created by Quinn Dunki on 7/14/15. +; Copyright (c) 2015 One Girl, One Laptop Productions. All rights reserved. +; + + +.org $6000 + +COUT = $fded +PRBYTE = $fdda + + +main: ; BRUN lands here + + jsr WGEnableMouse + +loop: + ; Print current mouse position + lda #'X' + $80 + jsr COUT + lda #'=' + $80 + jsr COUT + + lda WG_MOUSEPOS_X + jsr PRBYTE + + lda #' ' + $80 + jsr COUT + lda #'Y' + $80 + jsr COUT + lda #'=' + $80 + jsr COUT + + lda WG_MOUSEPOS_Y + jsr PRBYTE + + lda WG_MOUSECLICK_X + bmi lineDone + lda #' ' + $80 + jsr COUT + lda #'!' + $80 + jsr COUT + lda #$ff + sta WG_MOUSECLICK_X + +lineDone: + lda #13 + $80 + jsr COUT + + ; Check for any key to quit + lda KBD + bpl loop ; No key pending + + ; Clean up and return to BASIC + sta KBDSTRB ; Clear strobe + + jsr WGDisableMouse + rts + + + +.include "mouse.s" + + +; Suppress some linker warnings - Must be the last thing in the file +; This is because Quinn doesn't really know how to use ca65 properly +.SEGMENT "ZPSAVE" +.SEGMENT "EXEHDR" +.SEGMENT "STARTUP" +.SEGMENT "INIT" +.SEGMENT "LOWCODE" diff --git a/switches.s b/switches.s new file mode 100644 index 0000000..81181c8 --- /dev/null +++ b/switches.s @@ -0,0 +1,32 @@ +; +; switches.s +; Softswitches for Apple ][ +; +; Created by Quinn Dunki on 8/15/14. +; Copyright (c) 2014 One Girl, One Laptop Productions. All rights reserved. +; + + +PAGE2 = $c01c ; Read bit 7 +PAGE2OFF = $c054 ; Read/Write +PAGE2ON = $c055 ; Read/Write + +COL80 = $c01f ; Read bit 7 +COL80OFF = $c00c ; Write +COL80ON = $c00d ; Write + +STORE80 = $c018 ; Read bit 7 +STORE80OFF = $c000 ; Write +STORE80ON = $c001 ; Write + +TEXT = $c01a ; Read bit 7 +TEXTOFF = $c050 ; Read/Write +TEXTON = $C051 ; Read/Write + +KBD = $c000 ; Read +KBDSTRB = $c010 ; Read/Write + +RDVBLBAR = $C019 ; Read bit 7 (active low) + +OURCH = $057b ; 80 col cursor position (H) +OURCV = $05fb ; 80 col cursor position (V)