.nolist #include "dcs6.inc" .list ;#define DEBUG 1 ;Standard Equates appData equ 8000h _chkFindSym equ 42F1h _clrLCDFull equ 4540h _MemClear equ 4C30h _createAppVar equ 4E6Ah _FlashToRam equ 5017h _LoadCIndPaged equ 501Dh ProtProgObj equ 06h AppVarObj equ 15h skDown equ 01h skLeft equ 02h skRight equ 03h skUp equ 04h skEnter equ 09h ;RAM Equates outputBuffer equ 9C29h maxPacketSize0 equ 8A3Ah incomingPipe equ maxPacketSize0+1 descriptorBuffer equ incomingPipe+1 mouseXValue equ descriptorBuffer+128 mouseYValue equ mouseXValue+2 mouseScrollValue equ mouseYValue+2 savedMouseData equ mouseScrollValue+1 mouseXThreshold equ savedMouseData+1 mouseYThreshold equ mouseXThreshold+2 mouseScrollThreshold equ mouseYThreshold+2 curChoice equ mouseScrollThreshold+1 mouseScriptIndex equ curChoice+1 mouseScript equ mouseScriptIndex+1 gestureActions equ mouseScript+10 ;Flags jerryFlags equ asm_Flag1 deviceSetUp equ 0 dataRequestSent equ 1 mouseDataSaved equ 2 mouseSwitch equ 3 clickFlags equ asm_Flag2 leftClickHeld equ 0 rightClickHeld equ 1 middleClickHeld equ 2 returnFlags equ asm_Flag3 leftClickReturned equ 0 rightClickReturned equ 1 middleClickReturned equ 2 ;Constants defaultThreshold equ 15 defaultScrollThreshold equ 3 appVarID equ 5336h .db 0BBh,6Dh JerryStart: .db 0ABh,0C9h SEStart:.db 31h,87h,50h SEBootPointer: .dw SEBoot-SEStart SEMousePointer: .dw SEMouse-SEStart SEUnloadPointer: .dw SEUnload-SEStart ;-------------------------------------------------------------------- ;The boot section contains the code to initialize the USB mouse and ; nothing else. It has been customized and streamlined for a generic ; USB mouse. ;This can be called by the mouse section if the device is unplugged ; or needs to be set up again. SEBoot: xor a ld (mouseScriptIndex),a JerryInit: xor a ld (iy+jerryFlags),a ld (iy+clickFlags),a ld (iy+returnFlags),a ;Kill USB bcall(5257h) ;Supply power to the device call USBHostInit-SEBoot+SEram ret c ;Get the configuration descriptor so we know which endpoint to read ld hl,descriptorBuffer ld de,descriptorBuffer+1 ld (hl),0 ld bc,127 push hl ldir pop de call GetConfigDescriptor-SEBoot+SEram ret c ;Configure the device (assume first config/interface for now) ld a,(descriptorBuffer+5) call ConfigureDevice-SEBoot+SEram ret c ;Search for the interrupt pipe to read from xor a ld (incomingPipe),a ld ix,descriptorBuffer parseLoop: ld b,0 ld c,(ix+0) ld a,(ix+1) cp 05h ;endpoint descriptor type jr nz,parseSkip ld a,(ix+2) bit 7,a jr z,parseSkip ;ignore outgoing pipes ld a,(ix+3) and 11b cp 3 jr nz,parseSkip ;Found an incoming interrupt pipe, set it up ld d,(ix+4) srl d srl d srl d ld a,(ix+2) and 7Fh ld b,a ld (incomingPipe),a ld e,(ix+6) call InitInterruptPipe-SEBoot+SEram jr parseDone parseSkip: add ix,bc ld a,(ix+0) or a jr nz,parseLoop parseDone: ld a,(incomingPipe) or a scf ret z ;We're set up! res 5,(iy+41h) set deviceSetUp,(iy+jerryFlags) xor a ret USBHostInit: ;Iniializes the USB mouse. ;This saves the control pipe's max packet size to (maxPacketSize0). ;It also uses the third crystal timer, as USB code usually does. in a,(4Ch) cp 5Ah ret z cp 1Ah ret z in a,(4Dh) bit 5,a jr z,n1 ld a,10h out (57h),a n1: xor a out (4Ch),a ld a,1 out (5Bh),a ld a,2 out (54h),a ld a,20h out (4Ah),a xor a out (4Bh),a call Bit3Port3A-SEBoot+SEram jr z,n2 ld a,20h out (4Bh),a n2: xor a out (54h),a ld b,1 call WaitTimerBms-SEBoot+SEram call Bit3Port3A-SEBoot+SEram jr z,n3 ld a,44h out (54h),a n3: ld a,0C4h out (54h),a ld de,0FFFFh n4: ld a,e or d scf ret z dec de in a,(4Ch) and 10111111b cp 12h jr nz,n4 in a,(4Dh) bit 5,a scf ret nz call Bit3Port3A-SEBoot+SEram jr z,InitBoot102Code ld b,1 call WaitTimerBms-SEBoot+SEram in a,(4Dh) bit 6,a scf ret nz in a,(3Ah) res 1,a out (3Ah),a in a,(39h) set 1,a out (39h),a ld a,8 out (4Ch),a ld a,1 out (8Fh),a ld de,0FFFFh n5: dec de ld a,d or e scf ret z in a,(4Dh) bit 6,a jr z,n5 in a,(39h) res 1,a out (39h),a jr InitCont InitBoot102Code: ld a,8 out (4Ch),a ld de,0FFFFh n6: dec de ld a,d or e scf ret z in a,(81h) bit 5,a jr nz,n6 ld a,1 out (8Fh),a ld b,2 call WaitTimerBms-SEBoot+SEram InitCont: ld a,0FFh out (87h),a xor a out (92h),a ld a,0FEh out (89h),a ld a,0A1h out (8Bh),a in a,(86h) ld de,0FFFFh testloop: dec de ld a,d or e scf ret z in a,(81h) bit 6,a jr z,testloop in a,(8Fh) bit 2,a scf ret z ld b,5 Loop8C: ld de,0FFFFh n7: in a,(8Ch) or a jr nz,n8 dec de ld a,d or e jr nz,n7 djnz Loop8C scf ret n8: xor a out (5Bh),a ld a,8 out (81h),a ld b,2 call WaitTimerBms-SEBoot+SEram xor a out (81h),a call WaitTimer40ms-SEBoot+SEram xor a out (80h),a ld b,1 call WaitTimerBms-SEBoot+SEram call GetMaxPacketSize0-SEBoot+SEram ret c ld (maxPacketSize0),a call WaitTimer20ms-SEBoot+SEram ld hl,bSetAddress-SEBoot+SEram call Send8PortA0-SEBoot+SEram ld a,0Ah call SendControlCmd-SEBoot+SEram ret c ld a,60h call SendControlCmd-SEBoot+SEram ret c xor a out (8Eh),a ld a,02h out (80h),a jp WaitTimer20ms-SEBoot+SEram bSetAddress: .db 00h,05h,02h,00h,00h,00h,00h,00h GetMaxPacketSize0: ld hl,bGetDeviceDescriptor-SEBoot+SEram call Send8PortA0-SEBoot+SEram ld a,0Ah call SendControlCmd-SEBoot+SEram ret c ld a,20h call SendControlCmd-SEBoot+SEram ret c ld hl,outputBuffer call Get8PortA0-SEBoot+SEram ld a,42h call SendControlCmd-SEBoot+SEram ret c ld a,(outputBuffer+7) or a ret bGetDeviceDescriptor: .db 80h,06h,00h,01h,00h,00h,08h,00h GetConfigDescriptor: ;DE => descriptor buffer ld hl,bGetConfigDescriptor-SEBoot+SEram call Send8PortA0-SEBoot+SEram ld a,0Ah call SendControlCmd-SEBoot+SEram ret c ld a,20h call SendControlCmd-SEBoot+SEram ret c di ex de,hl call getConfigDescriptorSub-SEBoot+SEram ret c ld a,42h jp SendControlCmd-SEBoot+SEram getConfigDescriptorSub: in a,(0A0h) ld (hl),a inc hl in a,(0A0h) ld (hl),a inc hl in a,(0A0h) ld (hl),a inc hl sub 3 ld c,a ld e,0FDh gdLoop: ld a,(maxPacketSize0) add a,e ld e,0 cp c jr c,gdContinue ld a,c gdContinue: ld d,a call GetAPortA0-SEBoot+SEram ld a,c sub d ld c,a jr z,gdDone ld a,20h call SendControlCmd-SEBoot+SEram jr nc,gdLoop ret gdDone: or a ret bGetConfigDescriptor: .db 80h,6,0,2,0,0,128,0 ConfigureDevice: push af ld hl,bConfigureDevice-SEBoot+SEram ld de,outputBuffer ld bc,8 push de ldir pop hl pop af ld (outputBuffer+2),a call Send8PortA0-SEBoot+SEram ld a,0Ah call SendControlCmd-SEBoot+SEram ret c ld a,60h call SendControlCmd-SEBoot+SEram ret c xor a out (8Eh),a out (91h),a jp WaitTimer20ms-SEBoot+SEram bConfigureDevice: .db 00h,09h,00h,00h,00h,00h,00h,00h Bit3Port3A: in a,(3Ah) bit 3,a ret WaitTimer40ms: call WaitTimer20ms-SEBoot+SEram WaitTimer20ms: ld b,2 WaitTimerBms: ld a,42h out (36h),a xor a out (37h),a ld a,b out (38h),a wtLoop: in a,(4) bit 7,a jr z,wtLoop xor a ret InitInterruptPipe: ;B = pipe/endpoint number ;D = packet size / 8 ;E = polling interval xor a out (5Bh),a ld a,b out (8Eh),a or 30h out (9Ah),a ld a,d out (93h),a ld a,90h out (94h),a xor a out (95h),a ld a,e out (9Bh),a ld a,0FEh out (89h),a ld a,1 out (5Bh),a ret Send8PortA0: ld a,8 SendAPortA0: ld b,a SendBPortA0: xor a out (8Eh),a sbpa0: ld a,(hl) out (0A0h),a inc hl djnz sbpa0 ret SendControlCmd: push af xor a out (8Eh),a pop af out (91h),a call WaitPort82-SEBoot+SEram ret c CheckPort91: push af in a,(91h) bit 2,a jr nz,p1scfRet bit 4,a jr nz,p1scfRet pop af or a ret p1scfRet: pop af scf ret WaitPort82: wp82Loop: in a,(82h) or a jr z,wp82Loop or a ret Get8PortA0: ld a,8 GetAPortA0: ld b,a GetBPortA0: xor a out (8Eh),a _gbpa0: in a,(0A0h) ld (hl),a inc hl djnz _gbpa0 ret .echo "SEBoot Size: " .echo $-SEBoot .echo " bytes total\n" ;-------------------------------------------------------------------- ;-------------------------------------------------------------------- ;The mouse section contains the heart of the code: ; Abort if no peripheral connected ; If not initialized, then initialize it: ; Copy a small loader to appData ; Call the loader, which will load SEBoot by: ; Looking up program and getting pointers ; Copying 768 bytes from SEBoot to SEram ; Calling SEram ; Copying 768 bytes from SEMouse to SEram ; Returning ; Initialize the rest of memory ; This will return up/down/left/right movement as well as ; left- and right-clicks. ; If middle button is clicked, configuration screen comes up. SEMouse: ;See if a peripheral is attached, abort if not in a,(4Dh) bit 5,a jr z,deviceStillAttached res deviceSetUp,(iy+jerryFlags) ld d,0 ret deviceStillAttached: bit deviceSetUp,(iy+jerryFlags) jr z,reSetUpDevice in a,(8Fh) bit 2,a jr nz,isInitted ;NOTE: I'm not sure this actually works ; in a,(54h) ; cp 44h ; jr z,isInitted ; cp 0C4h ; jr z,isInitted res deviceSetUp,(iy+jerryFlags) reSetUpDevice: ld hl,JerryInit-SEBoot+SEram ld ix,SEBoot-JerryStart call MakeSESectionCall-SEMouse+SEram bit deviceSetUp,(iy+jerryFlags) ld d,0 ret z isInitted: bit dataRequestSent,(iy+jerryFlags) jr nz,alreadySetUp ;Request some data ld hl,0 ld (mouseXValue),hl ld (mouseYValue),hl ld (outputBuffer+1),hl ;Load configuration settings ld hl,LoadConfiguration-SEUnload+SEram ld ix,SEUnload-JerryStart call MakeSESectionCall-SEMouse+SEram di xor a out (5Bh),a ld a,(incomingPipe) out (8Eh),a ld a,20h out (94h),a res 5,(iy+41h) set dataRequestSent,(iy+jerryFlags) alreadySetUp: ld a,(incomingPipe) out (8Eh),a in a,(84h) or a jr z,checkForMouseData in a,(94h) bit 2,a jr nz,skipGetData bit 6,a jr nz,skipGetData in a,(96h) or a jr z,checkForMouseData ld b,a ld hl,outputBuffer ld a,(incomingPipe) add a,0A0h ld c,a myGetDataLoop: in a,(c) ld (hl),a inc hl djnz myGetDataLoop set 5,(iy+41h) skipGetData: xor a out (94h),a ;Request some more data ld a,(incomingPipe) out (8Eh),a ld a,20h out (94h),a checkForMouseData: ld a,(mouseScriptIndex) or a jr z,checkForMouseDataContinue ;We have a return script, return the next index ld c,a inc a ld (mouseScriptIndex),a ld b,0 ld hl,mouseScript-1 add hl,bc ld a,(hl) or a ld d,a ret nz ;Done with the script ld (mouseScriptIndex),a ret checkForMouseDataContinue: bit 5,(iy+41h) jr z,checkForMouseMovement #ifdef DEBUG ld hl,0 ld (curRow),hl ld a,(outputBuffer) call DispHexA-SEMouse+SEram ld a,(outputBuffer+1) call DispHexA-SEMouse+SEram ld a,(outputBuffer+2) call DispHexA-SEMouse+SEram ld a,(outputBuffer+3) call DispHexA-SEMouse+SEram #endif res 5,(iy+41h) ;Interpret what we got from the device ld ix,outputBuffer ld a,(ix+1) ld hl,mouseXValue call AddMouseValue-SEMouse+SEram ld a,(ix+2) ld hl,mouseYValue call AddMouseValue-SEMouse+SEram ld a,(ix+0) ld (iy+clickFlags),a and (iy+returnFlags) ld (iy+returnFlags),a ld b,(ix+3) ld a,(mouseScrollValue) add a,b ld (mouseScrollValue),a checkForMouseMovement: bit mouseDataSaved,(iy+jerryFlags) jr z,checkForNewMouseMovement res mouseDataSaved,(iy+jerryFlags) ld a,(savedMouseData) ld d,a ret checkForNewMouseMovement: bit leftClickReturned,(iy+returnFlags) jr nz,skipLeftClick bit leftClickHeld,(iy+clickFlags) jr z,skipLeftClick set leftClickReturned,(iy+returnFlags) ld d,1 ret skipLeftClick: bit rightClickReturned,(iy+returnFlags) jr nz,skipRightClick bit rightClickHeld,(iy+clickFlags) jr z,skipRightClick set rightClickReturned,(iy+returnFlags) ld d,2 ret skipRightClick: bit middleClickReturned,(iy+returnFlags) jr nz,skipMiddleClick bit middleClickHeld,(iy+clickFlags) jr z,skipMiddleClick set middleClickReturned,(iy+returnFlags) ;Commented out because it just doesn't work right yet ; ld hl,DoConfiguration-SEUnload+SEram ; ld ix,SEUnload-JerryStart ; call MakeSESectionCall-SEMouse+SEram ; ;Force the cursor to the center and refresh the screen ; ld ix,mouseScript ; ld (ix+0),47+7 ; ld (ix+1),31+103 ; ld (ix+2),3 ; ld (ix+3),0 ; ld a,1 ; ld (mouseScriptIndex),a ; ld hl,0 ; ld (curRow),hl ; ld a,5 ; bcall(_PutC) ; pop hl ; push hl ; bcall(_disphl) ld d,0 di ret skipMiddleClick: ld ix,mouseScrollThreshold ld b,(ix+0) ld a,(mouseScrollValue) bit 7,a push af call z,DoMouseScrollUp-SEMouse+SEram pop af call nz,DoMouseScrollDown-SEMouse+SEram ld a,d or a ld d,0 ret nz ld hl,(mouseXValue) bit 7,h push af push hl call z,DoMouseRight-SEMouse+SEram pop hl pop af call nz,DoMouseLeft-SEMouse+SEram ld hl,(mouseYValue) bit 7,h push af push hl call z,DoMouseDown-SEMouse+SEram pop hl pop af call nz,DoMouseUp-SEMouse+SEram ld a,(iy+jerryFlags) xor 1< routine to call ;IX => offset of section to load push hl ld hl,makeSECall-SEMouse+SEram ld de,appData ld bc,makeSECallEnd-makeSECall ldir pop hl jp appData makeSECall: push hl push ix call GetLoadingAddress-makeSECall+appData pop de pop ix ret c ;BHL points to real program data push ix call BHL_plus_DE-makeSECall+appData ;BHL now points to SE section within program data ;So load it and jump to it ld a,b ld de,SEram ld bc,768 bcall(_FlashToRam) pop ix ld hl,returnPoint-makeSECall+appData push hl jp (ix) returnPoint: push af call GetLoadingAddress-makeSECall+appData pop af push af ld de,SEMouse-JerryStart call BHL_plus_DE-makeSECall+appData ;BHL now points to SEMouse ld a,b ld de,SEram ld bc,768 bcall(_FlashToRam) pop af ret GetLoadingAddress: ld hl,sProgramName-makeSECall+appData rst 20h bcall(_chkFindSym) ret c ex de,hl ;BHL now points to program data ld a,b or a jr z,msecInRAM ld de,9 call BHL_plus_DE-makeSECall+appData call myLoadCIndPaged-makeSECall+appData ld d,0 ld e,c call BHL_plus_DE-makeSECall+appData msecInRAM: call inc_BHL-makeSECall+appData call inc_BHL-makeSECall+appData ;We're now at tAsmPrgm call inc_BHL-makeSECall+appData call inc_BHL-makeSECall+appData xor a ret BHL_plus_DE: add hl,de jr bpd1 myLoadCIndPaged: bcall(_LoadCIndPaged) inc_BHL:inc hl bpd1: ld a,b or a ret z bit 7,h ret z res 7,h set 6,h inc b ret sProgramName: .db ProtProgObj,"JERRY",0 makeSECallEnd: .echo " MakeSESectionCall Size: " .echo $-MakeSESectionCall .echo " bytes\n" #ifdef DEBUG DispHexA: push af push hl push bc push af rrca rrca rrca rrca call dispha-SEMouse+SEram pop af call dispha-SEMouse+SEram pop bc pop hl pop af ret dispha: and 15 cp 10 jp nc,dhlet-SEMouse+SEram add a,48 jp IPutC-SEMouse+SEram dhlet: add a,55 IPutC: push af push bc push de push hl push ix bcall(_PutC) pop ix pop hl pop de pop bc pop af ret #endif .echo "SEMouse Size: " .echo $-SEMouse .echo " bytes total\n" ;-------------------------------------------------------------------- ;-------------------------------------------------------------------- ;The unload section contains the code to kill the connected USB ; peripheral (cut power to it) and load/edit configuration options. SEUnload: ;Kill the USB peripheral bcall(5257h) ret LoadConfiguration: ld hl,sConfigAppVar-SEUnload+SEram rst 20h bcall(_chkFindSym) jr c,loadDefaultValues ex de,hl ;BHL now points to AppVar data ld a,b or a jr z,lcInRAM ld de,9 call uBHL_plus_DE-SEUnload+SEram call umyLoadCIndPaged-SEUnload+SEram ld d,0 ld e,c call uBHL_plus_DE-SEUnload+SEram lcInRAM:call uinc_BHL-SEUnload+SEram call uinc_BHL-SEUnload+SEram ;Now at real AppVar data ;Skip past version and flag bytes ld de,5 call uBHL_plus_DE-SEUnload+SEram ld a,b ld de,appData ld bc,41 bcall(_FlashToRam) ld hl,(appData) ld (mouseXThreshold),hl ld hl,(appData+2) ld (mouseYThreshold),hl ld a,(appData+4) ld (mouseScrollThreshold),a ;Load the gesture actions ld hl,appData+5 ld de,gestureActions ld bc,9*4 ldir ret loadDefaultValues: ld hl,defaultThreshold ld (mouseXThreshold),hl ld (mouseYThreshold),hl ld a,defaultScrollThreshold ld (mouseScrollThreshold),a ld hl,gestureActions ld bc,9*4 bcall(_MemClear) ret DoConfiguration: bcall(5257h) bcall(_clrLCDFull) xor a ld (curChoice),a DisplayConfig: bcall(_homeUp) ld hl,sXSpeed-SEUnload+SEram ld b,8 ld c,0 dispLoop: res textInverse,(iy+textFlags) ld a,(curChoice) cp c jr nz,notCurrent set textInverse,(iy+textFlags) notCurrent: push bc bcall(_PutS) res textInverse,(iy+textFlags) push hl bcall(_newLine) pop hl pop bc inc c djnz dispLoop xor a out (57h),a out (5Bh),a waitLoop1: in a,(4) bit 3,a jr nz,waitLoop1 waitLoop2: in a,(4) bit 3,a jr z,waitLoop2 jr doConfigDone ei doConfigKeyLoop: bcall(_getCSC) or a jr z,doConfigKeyLoop cp skUp jr z,configHandleUp cp skDown jr z,configHandleDown cp skLeft jr z,configHandleLeft cp skRight jr z,configHandleRight cp skEnter jr nz,doConfigKeyLoop doConfigDone: di ret ;Save configuration data to AppVar ld hl,sConfigAppVar-SEUnload+SEram rst 20h bcall(_chkFindSym) jr c,noAppVarFound bcall(_DelVarArc) noAppVarFound: ld hl,sConfigAppVar-SEUnload+SEram rst 20h ;Write the new AppVar ld hl,46 bcall(_createAppVar) ret c di ret inc de inc de ex de,hl ld de,appVarID ld (hl),e inc hl ld (hl),d inc hl ld (hl),0 inc hl ld de,(mouseXThreshold) ld (hl),e inc hl ld (hl),d inc hl ld de,(mouseYThreshold) ld (hl),e inc hl ld (hl),d inc hl ld a,(mouseScrollThreshold) ld (hl),a inc hl ex de,hl ld hl,gestureActions ld bc,9*4 ldir di ret configHandleUp: ld a,(curChoice) or a jr z,doConfigKeyLoop dec a cp 3 jr nz,chuGood dec a chuGood: ld (curChoice),a jp DisplayConfig-SEUnload+SEram configHandleDown: ld a,(curChoice) cp 7 jr z,doConfigKeyLoop inc a cp 3 jr nz,chdGood inc a chdGood: ld (curChoice),a jp DisplayConfig-SEUnload+SEram configHandleLeft: configHandleRight: jp doConfigKeyLoop-SEUnload+SEram uBHL_plus_DE: add hl,de jr ubpd1 umyLoadCIndPaged: bcall(_LoadCIndPaged) uinc_BHL: inc hl ubpd1: ld a,b or a ret z bit 7,h ret z res 7,h set 6,h inc b ret sXSpeed:.db "X speed:",0 sYSpeed:.db "Y speed:",0 sScrollWheel: .db "Scroll speed:",0 sGestures: .db "Gestures:",0 sTopLeft: .db " Top left",0CEh,0 sTopRight: .db " Top right",0CEh,0 sBottomLeft: .db " Bottom left",0CEh,0 sBottomRight: .db " Bottom right",0CEh,0 sConfigAppVar: .db AppVarObj,"JCONFIG",0 .echo "SEUnload Size: " .echo $-SEUnload .echo " bytes total\n" ;-------------------------------------------------------------------- .end end