;ACME 0.94 ;!sl "ddrv.l" ; Name DuoDriver ; Purpose Input driver for mouse and joystick ; Author (c) Marco Baye, 1999 ; Licence Free software ; Changes: ; 23 Apr 1999 Release 2.20. Internal info: ; DuoDriver v2.20 by Mac Bacon 23 Apr 1999. Freeware! ; Somewhen Added self-calibration, forming release 3.00. Internal info: ; Mac Bacon:DuoDrv3,PD ; 21 Jul 1999 Used reverse subtraction, forming release 3.01. Internal info: ; Mac Bacon:DuoDrv3,PD ; 1 Aug 1999 Release 4.00. ; Both 128 and 64 versions ; Now supports overlay-sprite mouse pointer ; Binary includes sprites ; Released in GO64 8/1999 (without release number). ; 3 Aug 1999 Same source file for both 128 and 64 versions. Release 4.01. ; Apart from that, virtually identical to release 4.00. ; 04 Feb 2003 Beautified ; 05 Feb 2003 Added "SpriteLine" macro and made sprites inline ; 26 May 2005 Release 4.02. All changes since release 4.00 are source-only! ; The resulting binaries are identical to those of release 4.00 ; (which were included in GO64 magazine 8/1999) ; 26 Mar 2006 Release 4.03. Adjusted source to ACME 0.91 capabilities. ; 25 Nov 2007 Release 4.04. Adjusted source to ACME 0.94 capabilities. ; This source code file uses conditional assembly ; to decide which version to produce (C64 or C128). ; Select type of binary to assemble (64 => c64, anything else => c128) !ifndef SYSTEM { !warn "Label SYSTEM not defined. Use -DSYSTEM=64 to build C64 version, -DSYSTEM=128 to build C128 version. Will now default to C64 version." SYSTEM = 64 } !if SYSTEM != 64 & SYSTEM != 128 { !serious "Please use either -DSYSTEM=64 or -DSYSTEM=128 when assembling this project." } ; --- Configurable values ; Start address, output file name and VIC location !if SYSTEM = 64 { *=$c000 !to "ddrv64.prg", cbm VIC_Base = $d000 } !if SYSTEM = 128 { *=$0c00 !to "ddrv128.prg", cbm VIC_Base = $11d6; Location of mirror registers } ; Pointer's maximum coordinates MaximumCoordinateX = 319; VIC value ; MaximumCoordinateX = 639; VDC value MaximumCoordinateY = 199 ; Maximum pixel step size ("speed") for joystick acceleration routine. MaxStep = $10; (max. $7f) ; Distance before acceleration starts, in pixels. MaxTime = $04; (max. $7f) ; Sprites to use for overlay pointer Sprite_A = 0 Sprite_B = 1 ; Coordinates of "pointer pixel" within pointer sprites; adjust these ; if you use different sprites. (0,0) is sprite's upper left pixel. Sprite_HotspotX = 1 Sprite_HotspotY = 1 ; Locations to store button states, $ff = pressed, $00 = not pressed. ; Mouse uses both buttons, joystick only uses "LeftButton". ; Location to store pointer's current character coordinates. !if SYSTEM = 64 { LeftButton = $a4 RightButton = $a5 CharX = $b3 CharY = $b4 } !if SYSTEM = 128 { LeftButton = $fa RightButton = $ff CharX = $9b CharY = $9c } ; Location to store pointer's current pixel coordinates. The driver ; code relies on having *four consecutive* bytes: ; x low, x high, y low, y high Coordinates = $fb; $fb-$fe ; --- System constants ; Interrupt vector sys_iirq = $0314 ; I/O registers sid_pot = $d419 cia1_pra = $dc00 cia1_prb = $dc01 cia1_ddrb = $dc03 mmu_cr = $ff00; c128 only ; --- Label definitions ; New names for some precalculated values, only to improve ; readability. Don't change these. PointerXnow = Coordinates PointerYnow = Coordinates + 2 SpriteA_X = VIC_Base + 2*Sprite_A SpriteA_Y = VIC_Base + 2*Sprite_A + 1 SpriteB_X = VIC_Base + 2*Sprite_B SpriteB_Y = VIC_Base + 2*Sprite_B + 1 Sprites_OF = VIC_Base + 16; X Overflow ; The character "^" in the following calculation means "to the power ; of". It is ACME syntax - if your assembler cannot do this, you may ; want to use hardcoded values here instead of calculations. Sprites_Bitmask = 2^Sprite_A + 2^Sprite_B ;alternative: ; Sprites_Bitmask = 1<Entry php sei sta sys_iirq stx sys_iirq+1 plp !if SYSTEM=128 { lda mmu_cr tay and #$fe; activate I/O chips sta mmu_cr } ; Init mouse buttons lda #$11 sta cia1_prb !if SYSTEM=128 {sty mmu_cr } !if SYSTEM = 64 { ; Copy sprites to tape buffer ldx #127 - lda Sprites,x sta $0340,x dex bpl - lda #Sprites_Bitmask ; Set sprite block pointers ldx #$0d stx 2040+Sprite_A inx stx 2040+Sprite_B ; Activate pointer sprites ora VIC_Base+21 sta VIC_Base+21 } rts ; --- Variables ; Pixel counter before accelerating JoyWaittime !byte 0 ; --- Main code Entry ; The driver consists of several distinct parts. To minimise ; performance wastage, you should remove all parts you don't need for ; the specific application. ; --- Part 0, initialisations ; Make sure decimal mode is off cld ; Set button states to "not pressed", so the other parts only have to ; deal with setting them to "pressed". lda #$00 sta LeftButton sta RightButton ; --- Part 1, handling mouse movements ; mouse x ldx #$00; 0 means "x stuff" jsr PotDelta ; Now signed x movement is in A/Y. Add to current x value. clc adc PointerXnow sta PointerXnow tya adc PointerXnow+1 sta PointerXnow+1 ; mouse y ldx #$01; 1 means "y stuff" jsr PotDelta ; Now signed y movement is in A/Y. Mouse and computer use different y ; directions, so don't add to, but subtract from current y value. ; This is a reverse subtraction - it might be harder to understand, ; but it is both faster and smaller than the usual way. clc sbc PointerYnow eor #$ff sta PointerYnow tya sbc PointerYnow+1 eor #$ff sta PointerYnow+1 ; --- Part 2, handling mouse buttons ; Prepare CIA by setting bits to input ldy #$11 sty cia1_ddrb ldx #$ff; $ff means "pressed" lda #$10; check left button bit cia1_prb bne + stx LeftButton; store state + lda #$01; check right button bit cia1_prb bne + stx RightButton; store state ; Reset CIA to normal state + ldy #$00 sty cia1_ddrb ; --- Part 3, handling the joystick ; Fetch byte holding direction flags lda cia1_pra tax; ...and remember it ; Check 'up' direction ror bcs ++ ; Subtract current step size from y value if needed. tay sec lda PointerYnow sbc JoyStepsize sta PointerYnow bcs + dec PointerYnow+1 + tya ; Check 'down' direction ++ ror bcs ++ ; Add current step size to y value if needed. tay ;clc; C is always clear here lda PointerYnow adc JoyStepsize sta PointerYnow bcc + inc PointerYnow+1 + tya ; Check 'left' direction ++ ror bcs ++ ; Subtract current step size from x value if needed. tay sec lda PointerXnow sbc JoyStepsize sta PointerXnow bcs + dec PointerXnow+1 + tya ; Check 'right' direction ++ ror bcs ++ ; Add current step size to x value if needed. tay ;clc; C is always clear here lda PointerXnow adc JoyStepsize sta PointerXnow bcc + inc PointerXnow+1 + tya ++ ; --- Part 4, handling joystick button ror bcs + lda #$ff; $ff means "pressed" sta LeftButton + ; --- Part 5, joystick acceleration ; Restore joystick direction bits and check whether to set speed to ; zero. txa and #$0f; Clear unneeded bits cmp #$0f; Any direction bit ? bne + ; No direction was used, so reset speed and wait counter to normal. lda #$01 sta JoyStepsize lda #MaxTime sta JoyWaittime jmp Part5End + ; A direction bit was used, so check whether to accelerate: If speed ; is already maximum speed, don't accelerate. JoyStepsize=*+1 lda #$00; (self-modifying) ; If the variable "JoyStepsize" would have been defined as a separate ; location (using "!byte"), it would have taken a byte of memory. By ; storing the value inside an LDA command's argument, we save that one ; byte. It might make a difference. :) cmp #MaxStep; If speed is max., bcs Part5End; don't accelerate. ; Speed isn't maximum yet. Check whether ; we have to wait before accelerating. dec JoyWaittime bpl Part5End ; Counter has underrun, so accelerate. inc JoyWaittime; reset counter inc JoyStepsize; increase speed Part5End ; --- Part 6, restrict coordinate range ; restrict x value ldx #$00; 0 means "x stuff" jsr Restrict ; restrict y value ldx #$02; 2 means "y stuff" jsr Restrict ; --- Part 7, positioning sprites ; Set sprites' x positions lda PointerXnow clc adc #SpriteOffset_X sta SpriteA_X; set both sprites sta SpriteB_X lda Sprites_OF; get x overflow bcs SetOF ldx PointerXnow+1 bne SetOF and #Sprites_Bitmask XOR $ff bcc StoreOF; C is clear here SetOF ora #Sprites_Bitmask StoreOF sta Sprites_OF; set x overflow ; Set sprites' y positions lda PointerYnow clc adc #SpriteOffset_Y sta SpriteA_Y sta SpriteB_Y ; The y value's high byte is useless in this case. ; --- Part 8, making char coordinates ; Convert x coordinate. There are different "best" routines for ; different resolutions, so I've given the VIC and VDC routines. lda PointerXnow lsr lsr lsr ldx PointerXnow+1 ;ora OrTable,x; VDC only (see below for data table) beq +; VIC only ora #$20; VIC only + sta CharX ; Convert y coordinate. lda PointerYnow lsr lsr lsr sta CharY ; --- Add further parts here ; Here you can add further routines, for example to use the button ; states to fake keypresses etc. ; --- The end ; The initialisation routine sets the argument to the address of the ; previous IRQ routine. mod16=*+1: jmp $ffff; (self-modifying) ; This table is for part 8. ;OrTable !byte 0,32,64; VDC only ; --- "Restrict" subroutine PointerXmax !word MaximumCoordinateX PointerYmax !word MaximumCoordinateY ; "y" word must follow directly after "x" word in memory. Restrict ; Restrict internal coordinates to configured range. Entry conditions: ; X is direction handle (0 = x, 2 = y) lda PointerXnow+1,x bmi SetTo0 cmp PointerXmax+1,x bcc Eosr bne + lda PointerXmax,x cmp PointerXnow,x bcs Eosr + lda PointerXmax,x ldy PointerXmax+1,x jmp DefCo SetTo0 lda #0 tay DefCo sta PointerXnow,x sty PointerXnow+1,x Eosr rts ; --- "Pot" subroutine ; This routine computes the mouse movements and therefore contains the ; self-calibration stuff and the other improvements over the standard ; 1351 driver. PotMax !word 0; max. POTs yet plus 1 ! PotMin !word $ffff; lowest POTs yet PotOld !word 0; old values PotWidth !word 0; interval width HalfPotWidth !word 0; half width ; (buffered for speed increase) ; The above variables are not really words: The first byte is the x ; value, the second byte is the y value respectively. ; Compute the signed distance of mouse movement. ; Entry conditions: X is direction handle (0 = x, 1 = y) ; Exit conditions: A/Y are signed distance (low/high) ; First, get new value and clear "recalculate signal width" flag. PotDelta lda sid_pot,x ldy #$00 ; Check whether new value is lower than lowest known. cmp PotMin,x bcs + ; Store new "lowest" und set "recalculate signal width" flag. sta PotMin,x ldy #$ff + ; Check whether new value is higher than highest known. cmp PotMax,x bcc + ; Set "recalculate signal width" flag and store new "highest". ldy #$ff pha; Remember current value adc #$00; Add one (C is set) sta PotMax,x ; Value $ff (0 after adding) means that there is no mouse connected, ; so reset min/max in that case. beq ResetMM; Stack is untidy... pla; Restore current value + ; If flag is set, recalculate signal width. iny; Check flag bne ++ tay; Buffer current value. lda PotMax,x; Get highest+1 sec; Subtract lowest sbc PotMin,x bcc + sta PotWidth,x; Store signal lsr; width and half signal sta HalfPotWidth,x; width + tya; Restore current value. ++ ; Calculate distance tay; Buffer current value. sec sbc PotOld,x pha tya sta PotOld,x pla beq zero; If not moved, exit. bcc minus; Negative difference ; Positive difference: ; Check whether movement caused a value wrap-around. cmp HalfPotWidth,x bcc Decrease beq Decrease ; It did, so calculate "real" distance and jump to exit ;sec; C is always set here sbc PotWidth,x; Fix distance ; We now know that the (fixed) distance is really negative, so we ; finally wipe out that annoying bit 0 noise by incrementing the ; value. Increase ;clc; C is always clear here adc #$01 beq zero; If increasing gives zero, jump to zero handler. ldy #$ff; Set up high byte for negative values. rts ; Negative difference: ; Check whether movement caused a value wrap-around. minus eor #$ff; Complement ; If we would do a real negation (by adding "1"), then we would need ; to branch using BCC *and* BEQ. So the above way might be harder to ; understand, but it is both shorter *and* faster - which I like. :) cmp HalfPotWidth,x eor #$ff; Restore value bcc Increase ; Movement caused a value wrap-around, so calculate "real" distance and exit. clc adc PotWidth,x; Fix distance ; We now know that the (fixed) distance is really positive, so we ; finally wipe out that annoying bit 0 noise by decrementing the value. Decrease sec sbc #$01 ; No difference or positive difference; both need zero as the high byte. zero ldy #0 rts ; If there is no mouse, reset "lowest" ("highest" will have been reset ; already) and return zero. ResetMM tay; Set Y to zero. pla; Tidy stack lda #$ff; Reset "lowest" sta PotMin,x tya; Return with A/Y = 0 rts ; --- Include sprites ; Because the c64 version copies the sprite data into the tape buffer ; on initialisation, the data is included right here. ; In the c128 version, we skip memory until we reach $0e00 - this is ; where the sprites are stored by default. !if SYSTEM = 128 { !align $ffff, $e00, $0 } !macro SpriteLine .v { !by .v>>16, (.v>>8)&255, .v&255 } Sprites ; 765432107654321076543210 +SpriteLine %........................ +SpriteLine %.#...................... +SpriteLine %.##..................... +SpriteLine %.###.................... +SpriteLine %.####................... +SpriteLine %.#####.................. +SpriteLine %.######................. +SpriteLine %.#######................ +SpriteLine %.########............... +SpriteLine %.#########.............. +SpriteLine %.########............... +SpriteLine %.######................. +SpriteLine %.######................. +SpriteLine %.##..##................. +SpriteLine %.#....##................ +SpriteLine %......##................ +SpriteLine %.......##............... +SpriteLine %.......##............... +SpriteLine %........##.............. +SpriteLine %........##.............. +SpriteLine %........................ !byte 0; pad to 64-byte block ; 765432107654321076543210 +SpriteLine %##...................... +SpriteLine %###..................... +SpriteLine %####.................... +SpriteLine %#####................... +SpriteLine %######.................. +SpriteLine %#######................. +SpriteLine %########................ +SpriteLine %#########............... +SpriteLine %##########.............. +SpriteLine %###########............. +SpriteLine %###########............. +SpriteLine %#########............... +SpriteLine %########................ +SpriteLine %########................ +SpriteLine %###..####............... +SpriteLine %##...####............... +SpriteLine %......####.............. +SpriteLine %......####.............. +SpriteLine %.......####............. +SpriteLine %.......####............. +SpriteLine %........###.............