; ; akbd.s ; ; Keyboard handling (MDW) ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sapphire library. ; ; Sapphire is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Sapphire is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Sapphire. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis ;----- External dependencies ------------------------------------------------ GET sapphire:keyMap GET sapphire:intKeys ;----- Macros --------------------------------------------------------------- MACRO $label KEYCMP $reg,$key [ $key > &100 CMP $reg,# ( $key - &100 ) << 8 | CMP $reg,# $key ] MEND MACRO $label KEYRNG $reg,$low,$high [ $low > &100 SUB R14,$reg,# ( $low - &100 ) << 8 CMP R14,# ( $high - $low - &100 ) << 8 | SUB R14,$reg,# $low CMP R14,# $high-$low ] MEND ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY ; --- akbd_test --- ; ; On entry: R0 == internal key number to test ; ; On exit: CS if key was pressed, CC otherwise ; ; Use: Informs you whether a given key is currently being pressed. EXPORT akbd_test akbd_test ROUT STMFD R13!,{R0-R2,R14} ;Save some registers MOV R2,#&FF ;Test internal key number EOR R1,R0,#&FF ;Wierd translation for key MOV R0,#&81 ;Test for key pressed SWI OS_Byte ;Do the test nicely CMP R1,#0 ;Is it pressed down? LDMFD R13!,{R0-R2,R14} ;Restore registers anyhow ORRNES PC,R14,#C_flag ;Yes -- set C on exit BICEQS PC,R14,#C_flag ;No -- clear C on exit LTORG ; --- akbd_translate --- ; ; On entry: R0 == Wimp key number ; ; On exit: R0 == Straylight extended keyset key number ; ; Use: Translates a Wimp key number into one of the more specific ; Straylight key numbers. akbd__s EQU (1<<0) akbd__c EQU (1<<1) EXPORT akbd_translate akbd_translate ROUT ; --- Deep magic warning --- ; ; This routine is really not very pleasant. If you can, ; avoid messing about with it. It contains several `clever' ; tricks to try and reduce the code size and time taken, ; which will probably end up being utterly incomprehensible. STMFD R13!,{R6-R10,R14} ;Save some registers away MOV R10,R0 ;Look after the keynumber MOV R9,R10 ;Keep another copy ; --- Do a clever translation on the Wimp key --- ; ; The idea is that instead of distinguishing the two keymap ; halves by whether but 8 is set, we distinuguish by which ; byte the value is in -- byte 0 for bottom-half values and ; byte 1 for top-half values. This has an advantage in that ; *all* key values are now available as immediate constants. ; ; There is in fact a slight problem with this: &100 and &000 ; become indistinguishable. This is OK, though, since the ; value &100 cannot be returned by the Wimp. CMP R10,#&100 ;Is it in the top half? MOVGE R10,R10,LSL #8 ;Yes -- move it into byte 2 ANDGE R10,R10,#&FF00 ;And clear the top bit ; --- Handle PageUp/PageDown nicely --- BIC R14,R10,#&3100 ;Clear Shift and Control bits CMP R14,#&8E00 ;Is it PageUp/PageDown? BEQ %50 ;Yes -- handle it specially ; --- Remove some immediate no-hopers --- KEYCMP R10,&180 ;Is it really big? BGE %99 ;Yes -- skip everything KEYRNG R10,'a','z' ;Is it a simple character? BLS %99 ;Yes -- skip everything KEYRNG R10,'A','Z' ;Check upper case too BLS %99 ;Yes -- skip everything KEYRNG R10,'[',']' ;Go for the brackets too BLS %99 ;Skip if in there ; --- Work out the modifiers now --- MOV R8,#0 ;No modifiers found yet MOV R0,#intk_Shift ;Check for shift held down BL akbd_test ;Is Shift being pressed? ORRCS R8,R8,#akbd__s ;Yes -- set the bit then MOV R0,#intk_Ctrl ;Check for ctrl too BL akbd_test ;Is Ctrl being pressed? ORRCS R8,R8,#akbd__c ;Yes -- set that bit ; --- Various key checks --- KEYCMP R10,key_Delete ;Check for Delete key BEQ akbd__del ;Yes -- handle it KEYCMP R10,' ' ;Check for Space key BEQ akbd__space ;Yes -- handle it ; --- Handle the mappings to `Return' --- KEYCMP R10,&0D ;Check for Return key BNE %10 ;No -- skip ahead a little MOV R0,#intk_kEnter ;Check for Enter key BL akbd_test ;Scan the keyboard again BCS akbd__enter ;Yes -- handle it MOV R0,#intk_M ;Check for the M key BL akbd_test ;Scan the keyboard again BCS %10 ;If so, skip to ctrl keys ADD R9,PC,#0 ;Point to the table B %90 ;And sort that out nicely DCW key_Return,key_sReturn DCW key_cReturn,key_scReturn akbd__enter ADD R9,PC,#0 ;Point to the table B %90 ;And sort that out nicely DCW key_kEnter,key_skEnter DCW key_ckEnter,key_sckEnter akbd__space ADD R9,PC,#0 ;Point to the table B %90 ;And sort that out nicely DCW key_Space,key_sSpace DCW key_cSpace,key_scSpace akbd__del ADD R9,PC,#0 ;Point to the table B %90 ;And sort that out nicely DCW key_Delete,key_sDelete DCW key_cDelete,key_scDelete ; --- Handle control keys now (at last!) --- 10 CMP R10,#&1B ;Is it a control key? BGE %20 ;No -- skip forwards then MOV R14,#&00BB ;Part of the weird key mask ORR R14,R14,#&0300 ;Rest of the mask MOV R7,#1 ;Will be shifted about TST R14,R7,LSL R10 ;Check the weirdness bit then BEQ %15 ;If not *very* strange, skip CMP R10,#8 ;Is it a backspace? BNE %11 ;No -- skip ahead MOV R0,#intk_H ;Could be a ctrl-H BL akbd_test ;So check for that then MOVCC R0,#intk_8 ;Or a ctrl-8 BLCC akbd_test ;So check for that too BCS %11 ;Either -- skip ahead ADD R9,PC,#0 ;Point to table B %90 ;Handle table nicely DCW key_Backspace,key_sBackspace DCW key_cBackspace,key_scBackspace ; --- Now handle keys which could be numbers --- akbd__numKeys DCB 0,intk_1,0,intk_3,intk_4 DCB intk_5,0,intk_7,intk_8,intk_9 11 CMP R10,#0 ;Is it a null code? BEQ %13 ;Yes -- it could be anything! ADR R14,akbd__numKeys ;Point to internal key table LDRB R0,[R14,R10] ;Load the correct number BL akbd_test ;Test the key's state BCC %15 ;It's not that weird then 12 TST R8,#akbd__s ;Was Shift being pressed? ADDNE R9,R9,#&150 ;Yes -- add in offset ADDEQ R9,R9,#&130 ;No -- add different offset B %99 ;And come right out of this ; --- Handle a 0 key number --- 13 MOV R0,#intk_0 ;Is it control-0? BL akbd_test ;Check the keyboard MOVCC R9,#2 ;No -- then it's really 2 B %12 ;Now handle Shift status ; --- It's a sensible control key --- 15 TST R8,#akbd__s ;Is shift held down? ADDNE R9,R9,#&100 ;Yes -- add the offset nicely B %99 ;And return the key number ; --- Try to handle number key presses --- akbd__padTab DCB intk_k0,intk_k1,intk_k2,intk_k3,intk_k4 DCB intk_k5,intk_k6,intk_k7,intk_k8,intk_k9 20 SUB R0,R10,#'0' ;Chop off bottom limit CMP R0,#9 ;Is it a numeric key? BHI %30 ;No -- skip ahead then ADR R14,akbd__padTab ;Point to key number table LDRB R0,[R14,R0] ;Load the correct key number BL akbd_test ;Check the key number nicely ADDCS R9,R9,#&1C0 - '0' ;If on keypad, add offset ADDCS R9,R9,R8,LSL #4 ;And add in the modifiers B %99 ;Return this value to caller ; --- Handle other keypad bits --- ; ; This is somewhat clever in places, so watch out. Rather ; than branching on a match, or having an unconditional ; CMP for each case, and a further CMP at the end, we ; drop through to *all* subsequent comparisons, and take ; this into account. ; ; Skeptics may say that this actually wastes an instruction. ; Not true -- the setting up requires one value to be ; initialised to an invalid value spotted at the end anyway, ; and the base values are not valid immediate constants ; anyway. Oddly enough, &168 == &5A >> 2, which is valid! 30 MOV R7,#&168 ;Initial value for base MOV R0,#0 ;Initial value for icode CMP R10,#'/' SUBEQ R7,R7,#1 ADDEQ R0,R0,#intk_kSlash-intk_kStar CMPNE R10,#'*' SUBEQ R7,R7,#1 ADDEQ R0,R0,#intk_kStar-intk_kHash CMPNE R10,#'#' SUBEQ R7,R7,#1 ADDEQ R0,R0,#intk_kHash-intk_kMinus CMPNE R10,#'-' SUBEQ R7,R7,#1 ADDEQ R0,R0,#intk_kMinus-intk_kPlus CMPNE R10,#'+' SUBEQ R7,R7,#2 ADDEQ R0,R0,#intk_kPlus-intk_kDot CMPNE R10,#'.' SUBEQ R7,R7,#1 ADDEQ R0,R0,#intk_kDot BNE %40 ;If no match above, skip BL akbd_test ;Test the key's state BCC %40 ;If not pressed, skip ; --- Now return the correct value --- ; ; We use the barrel shifter to copy the modifier key states ; to the ARM flags. MOV R9,R7 MOVS R14,R8,LSR #1 ;Copy ctrl to !Z, sh to C SUBNE R9,R9,#64 ;If ctrl, subtract 64 ADDCS R9,R9,#16 ;If shift, add 16 ADDHI R9,R9,#16 ;If both, subtract only 32 B %99 ;Return this value ; --- Now tidy up any other weirdnesses --- 40 CMP R10,#&01E ;Is it a home lookalike? BEQ akbd__home ;Yes -- handle it nicely CMP R10,#&01B ;Is it an escape press? BEQ akbd__esc ;Yes -- handle that KEYRNG R10,&1C,&1F ;Is it one of the others? BHI %99 ;No -- nothing else for it TST R8,#akbd__s ;Is Shift being pressed? ADDNE R9,R9,#&130 ;Yes -- offset correctly ADDEQ R9,R9,#&110 ;No -- use different offset B %99 ;Return this value akbd__home MOV R0,#intk_6 ;It could be control-6 BL akbd_test ;Check this possibility ADRCC R9,akbd__homeMods ;No -- point to home table BCC %90 ;And process it as normal TST R8,#akbd__s ;Is Shift being pressed? ADDNE R9,R9,#key_sc6-&1E ;Yes -- handle this nicely ADDEQ R9,R9,#key_c6-&1E ;No -- return other value B %99 ;And return to caller akbd__homeMods DCW key_Home,key_sHome DCW key_cHome,key_scHome akbd__esc MOV R0,#intk_LSquare ;It could be control-[ BL akbd_test ;Check this possibility ADRCC R9,akbd__escMods ;No -- point to escape table BCC %90 ;And process it as normal TST R8,#akbd__s ;Is Shift being pressed? ADDNE R9,R9,#key_scLSquare-&1B ;Yes -- handle this nicely ADDEQ R9,R9,#key_cLSquare-&1B ;No -- return other value B %99 ;And return to caller akbd__escMods DCW key_Esc,key_sEsc DCW key_cEsc,key_scEsc ; --- Distinguish Page keys from Shift+Up/Down --- 50 TST R10,#&0100 ;Is it up or down? MOVNE R0,#intk_PageUp ;If up, check PageUp key MOVEQ R0,#intk_PageDown ;Otherwise, check PageDown BL akbd_test ;Check the key's pressedness MOVNE R0,#intk_k9 ;Can use keypad with NumLock! MOVEQ R0,#intk_k3 ;So check for this as well BLCC akbd_test ;Check the key's pressedness EORCS R9,R9,#&050 ;If it is, mangle correctly B %99 ;Return this value to caller ; --- R9 points to a modifier table --- ; ; Entries in the table are held in halfwords, to save space 90 TST R8,#akbd__s ;Is shift set? BIC R8,R8,#akbd__s ;Clear shift bit anyway LDR R9,[R9,R8,LSL #1] ;Load word from address MOVNE R9,R9,LSR #16 ;If set, shift word down BIC R9,R9,#&ff000000 ;Clear top byte of key value BIC R9,R9,#&00ff0000 ;Clear next byte of key too ; --- Standard return-to-caller code --- 99 MOV R0,R9 ;Return the key number LDMFD R13!,{R6-R10,PC}^ ;Return all registers LTORG ; --- akbd_pollKey --- ; ; On entry: -- ; ; On exit: CC if a key was in the buffer and ; R0 == wimp translated key code ; else CS and ; R0 corrupted ; ; Use: Reports whether the user has typed ahead, and if so what the ; keypress. Note that the keypresses returned are WIMP-type, ; not Straylight extended ones so you will have to use ; akbd_translate if you need the extended type. ; ; This call could be used to allow buffering of keypresses: ; on a Key_Pressed event you would call this routine until ; it returns FALSE and store the codes it returns in a buffer ; along with the code from the event. EXPORT akbd_pollKey akbd_pollKey ROUT STMFD R13!,{R1,R2,R14} ;Stack some registers ; --- Find out if there is a key waiting --- MOV R0,#129 ;Scan for a key MOV R1,#0 ;No time limit MOV R2,#0 SWI OS_Byte ;Read it then CMP R2,#&FF ;Was there a time out BEQ %95akbd_pollKey ;Yes -- return ; --- Check for extended key codes --- CMP R1,#0 ;Was 0 returned? BNE %90akbd_pollKey ;No -- return happily MOV R0,#129 ;Scan for a key MOV R1,#0 ;No time limit MOV R2,#0 SWI OS_Byte ;Read it then CMP R1,#0 ;Is it still 0? ADDNE R1,R1,#&100 ;No -- return extended no. 90akbd_pollKey MOV R0,R1 ;No -- get the character code LDMFD R13!,{R1,R2,R14} ;Load back registers BICS PC,R14,#C_flag ;Return with C clear 95akbd_pollKey LDMFD R13!,{R1,R2,R14} ;Load back registers ORRS PC,R14,#C_flag ;Return with C set LTORG ;----- That's all, folks ---------------------------------------------------- END