;********************************************************************** ;* 8052.com Single Board Computer -- SBCMON Demonstration Program * ;* ---> PS2/KEYBOARD DEMONSTRATION PROGRAM <--- * ;* For 8052.com SBC, hardware rev 1.3 * ;*--------------------------------------------------------------------* ;* CREDIT: This program is based on code submitted to 8052.com code * ;* library by Gabriel Lour (http://www.8052.com/codelib/kb.asm). * ;* It has been modified primarily to make it compatible with the * ;* Pinnacle 52 IDE, to conform to the style of other SBCMON example * ;* programs, and to use different pins for the keyboard connections.* ;* Also modified code so it uses "R" registers instead of specific .* ;* internal RAM addresses for variable storage. .* ;*--------------------------------------------------------------------* ;* This program is offered as-is with no warranty or guarantee of any * ;* kind. Its purpose is to serve as an educational aid in mastering * ;* the topics and concepts covered by the program. You are free to * ;* use this code for any purpose you see fit--including commercial-- * ;* but the exclusive responsibility for its use is the user of this * ;* code. This code is offered completely free of charge and VIS' and * ;* 8052.com's liability will be limited to the amount paid: zero. * ;*--------------------------------------------------------------------* ;* This program was coded to be compatible with the Pinnacle 52 IDE. * ;* Pinnacle 52 is available at http://www.vaultbbs.com/pinnacle. The * ;* program may require minor modifications in order to assemble with * ;* other assemblers. * ;*--------------------------------------------------------------------* ;* NOTE: This program was designed to be loaded into SBCMON's XRAM * ;* and executed as an SBCMON external program. It depends on one or * ;* more SBCMON library routines and, as such, will not work without * ;* SBCMON. This program is not intended to be loaded via in-system * ;* programming, only loaded within SBCMON. Should you wish to load * ;* this program via ISP as a stand-alone program, the SBCMON library * ;* entry point equates should be removed and the respective routines * ;* should be copied from SBCMON into this program. The source code * ;* for SBCMON may be found at http://www.8052.com/sbc/sbcmon. * ;*====================================================================* ;* Filename: PS2.ASM * ;* Description: This is an example program that allows a PS/2 keyboard* ;* to be attached to the 8052.com SBC using P1.2 as the clock line * ;* and P1.3 as the data line. At that point, a keyboard may be * ;* attached to the SBC and the program will display each key that * ;* is pressed. If the key is a non-printable character (ALT, DEL, * ;* SHIFT, etc. then the hex value of the internal character is * ;* displayed. * ;* * ;* See http://www.8052.com/sbc/ps2 for details. * ;********************************************************************** KEYB_DATA EQU P1.3 ;Line to which PS/2 keyboard data line is connected KEYB_CLOCK EQU P1.2 ;Line to which PS/2 keyboard clock line is connected KB_ACK EQU 0FAh ;Constant which represents the 'ACK' code sent back from keyboard KB_BREAK EQU 0F0h ;Constant which represents that a key is no longer being pressed KB_EXTENDED EQU 0E0h ;Constant which represents an extended scan code sequence ;========================================================== ; SBCMON LIBRARY ROUTINE ENTRY POINT EQUATES ; ;If you wish to make this a stand-alone program, remove ;these equates and copy the source code for the named ;routines from the SBCMON source code. SBCMON source code ;may be found at http://www.8052.com/sbc/sbcmon. ;=========================================================== SendSerial EQU 0041h SendSerialHexByte EQU 0044h SendSerialByte EQU 0047h ;========================================================== ; PROGRAM LOCATION ; ;The program is located at 8000h since this is where the ;8052.com SBC's XRAM/code memory is in the memory map. ;Programs loaded into this RAM area can be accessed as both ;data and executed as a program. If you are going to make ;this a stand-alone program the following ORG should be ;modified to 0000h rather than 8000h. ;=========================================================== ORG 8000h ;========================================================== ; PROGRAM CODE ; ;This is the actual guts of the demonstration code. ;=========================================================== LCALL SendSerial ;Send 'initializing' message DB "Initializing PS/2 keyboard",13,0 LCALL PS2_Init ;Initialize the keyboard LCALL SendSerial ;Send the 'init done' message DB "Init done, setting NUM lock LED",13,0 MOV A,#02h ;The num lock LED is initially set LCALL PS2_SetLeds ;Set the keyboards LEDs LCALL SendSerial ;Send the welcome message DB "You may type on the PS/2 keyboard. Press any key in terminal to exit.",13,0 CLR RI Loop: JB RI,ExitProgram ;If a key has been pressed, exit LCALL PS2_GetScanCode ;Get a scan code from the keyboard JNC Loop ;If no key was pressed, keep waiting JB ACC.7,Loop ;If high bit is set that means the key is no longer being pressed, we don't care CJNE A,#' ',$+3 ;Is the code returned by scancode routine a printable character? JNC DisplayByte ;If so, jump to routine to just display it as an ASCII character PUSH ACC ;Otherwise, save the value returned by the keyboard MOV A,#'{' ;Display a leading bracket LCALL SendSerialByte ;Send it to the serial port POP ACC ;Restore the value returned by the keyboard LCALL SendSerialHexByte ;Send it to the terminal as a 2-byte hex value MOV A,#'}' ;Display a trailing bracket LCALL SendSerialByte ;Send it to the serial port SJMP Loop ;Wait for the next keypress DisplayByte: LCALL SendSerialByte ;Character is printable, so just display it SJMP Loop ;Wait for next keypress ExitProgram: CLR RI ;Clear serial input and exit back to SBCMON RET ;*************************************************************** ;* Function: Scan code translation table * ;* Author: Craig Steiner * ;*-------------------------------------------------------------* ;* Description: The following codes are the scan codes returned* ;* by PS2_GetScanCode. The keyboard sends a byte(s) when a * ;* key is pressed and another sequence of bytes when the key * ;* is released. These sequences can be 1-3 bytes in length. * ;* The PS2_GetScanCode routine converts the keyboard scan * ;* codes into an internal representation that can be fit in * ;* a single byte. The constants below are the values that * ;* will be returned in the case of certain keypresses. In * ;* all cases, a value of 0-127 means the key was pressed * ;* while a value between 128-255 means the key was released. * ;*************************************************************** KS_KP_SLASH EQU 'a' KS_RIGHTCTRL EQU 'b' KS_RIGHTGUI EQU 'c' KS_RIGHTALT EQU 'd' KS_APPS EQU 'e' KS_PRTSCREEN1 EQU 'f' KS_PRTSCREEN2 EQU 'g' KS_INSERT EQU 'h' KS_HOME EQU 'i' KS_PGUP EQU 'j' KS_DELETE EQU 'k' KS_END EQU 'l' KS_PGDOWN EQU 'm' KS_UPARROW EQU 'n' KS_LEFTARROW EQU 'o' KS_DOWNARROW EQU 'p' KS_RIGHTARROW EQU 'r' KS_KP_ENTER EQU 's' KS_BACKSPACE EQU 't' KS_TAB EQU 'u' KS_CAPS EQU 'v' KS_LEFTSHIFT EQU 'w' KS_LEFTCTRL EQU 'x' KS_LEFTGUI EQU 'y' KS_LEFTALT EQU 'z' KS_F1 EQU 01h KS_F2 EQU 02h KS_F3 EQU 03h KS_F4 EQU 04h KS_F5 EQU 05h KS_F6 EQU 06h KS_F7 EQU 07h KS_F8 EQU 08h KS_F9 EQU 09h KS_F10 EQU 0Ah KS_F11 EQU 0Bh KS_F12 EQU 0Ch KS_RIGHTSHIFT EQU 0Dh KS_ENTER EQU 0Eh KS_SCROLL EQU 0Fh KS_NUMLOCK EQU 10h KS_KP_ASTERISK EQU 11h KS_KP_MINUS EQU 12h KS_KP_PLUS EQU 13h KS_KP_POINT EQU 14h KS_ACPI_POWER EQU 15h KS_ACPI_SLEEP EQU 16h KS_ACPI_WAKE EQU 17h KS_ESCAPE EQU 1Bh KS_KP_0 EQU ')' KS_KP_1 EQU '!' KS_KP_2 EQU '@' KS_KP_3 EQU '#' KS_KP_4 EQU '$' KS_KP_5 EQU '%' KS_KP_6 EQU '^' KS_KP_7 EQU '&' KS_KP_8 EQU '*' KS_KP_9 EQU '(' ;*************************************************************** ;* Function: PS2_GetByte * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: None * ;* Output Carry bit: Clear=No key returned,Set=Key returned * ;* Accumulator: Byte returned by keyboard * ;* Registers Modified: None * ;*-------------------------------------------------------------* ;* Description: Gets a single byte from the keyboard, if there * ;* is a byte to get. Returns it in the accumulator and sets * ;* carry bit. Otherwise clears the carry bit. * ;*************************************************************** PS2_GetByteSetR0: MOV A,R0 ;We don't save Accumulator since return value can be in accumulator PUSH ACC ;Protect R0 SJMP PS2_GetByte2 ;Use R0 that is passed in PS2_GetByte: MOV A,R0 ;We don't save Accumulator since return value can be in accumulator PUSH ACC ;Protect R0 MOV R0, #50 ;Wait 50 loop cycles for data line to be lowered by keyboard PS2_GetByte2: SETB KEYB_DATA ;Raise data line so it can receive data SETB KEYB_CLOCK ;Raise clock so that keyboard communication is enabled PGB_CheckAgain: JNB KEYB_CLOCK, PGB_KeyHit ;If clock line is now low that means keyboard is talking to us DJNZ R0, PGB_CheckAgain ;Check R0 number of times SJMP PGB_KeyEnd ;No keyboard response was detected in loop period PGB_KeyHit: JNB KEYB_DATA, PGB_StartOk ;Start bit must be 0 PGB_KeyEnd: CLR KEYB_CLOCK ;Disable keyboard CLR C ;Clear carry to indicate no keypress PGB_PopExit: XCH A,R0 ;Hold accumulator temporarily in R0 POP ACC ;Restore value of R0 XCH A,R0 ;Restore accumulator and R0 RET PGB_StartOk: MOV R0,#8 ;8 bits to clock into accumulator CLR A ;Accumulator initially empty PGB_KeyHit3: ACALL PS2_GetBit ;Get one bit and shift into accumulator DJNZ r0, PGB_KeyHit3 ;Execute for each of the 8 bits PUSH ACC ;Save the value read from keyboard on the stack CLR A ACALL PS2_GetBit ;Get parity bit ACALL PS2_GetBit ;Get stop bit CLR KEYB_CLOCK POP ACC ;Restore the byte we read SETB C ;Set carry flag to indicate key press SJMP PGB_PopExit ;Exit routine ;*************************************************************** ;* Function: PS2_GetBit * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: Accumulator: Starting value of clocked-in data * ;* Output Accumulator: New value of cloced-in data * ;* Registers Modified: Carry bit * ;*-------------------------------------------------------------* ;* Description: Waits for a 1-0 transition on the clock line * ;* from the keyborad. When this happens, we have valid data * ;* on the data line so we get it and rotate it into the * ;* accumulator. * ;*************************************************************** PS2_GetBit: JNB KEYB_CLOCK, $ JB KEYB_CLOCK, $ MOV C, KEYB_DATA RRC A RET ;*************************************************************** ;* Function: PS2_WaitClock * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: None * ;* Output None * ;* Registers Modified: Nine * ;*-------------------------------------------------------------* ;* Description: Waits for a 1-0 transition on the clock line * ;* from the keyborad and returns. * ;*************************************************************** PS2_WaitClock: JNB KEYB_CLOCK, $ JB KEYB_CLOCK, $ RET ;*************************************************************** ;* Function: PS2_SendByte * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: Accumulator: Value to send to keyboard * ;* Output Accumulator: New value of cloced-in data * ;* Registers Modified: Carry bit * ;*-------------------------------------------------------------* ;* Description: Waits for a 1-0 transition on the clock line * ;* from the keyborad. When this happens, we have valid data * ;* on the data line so we get it and rotate it into the * ;* accumulator. * ;*************************************************************** PS2_SendByte: XCH A,R0 ;Swap A and R0 temporarily PUSH ACC ;This saves the value of R0 on stack XCH A,R0 ;And this restores the accumulator to its original value CLR KEYB_CLOCK ;Break the Keyboard MOV R0,#00h ;Some delay (safety reasons) DJNZ R0,$ ;Loop for 256 cycles CLR KEYB_DATA ;Request to send SETB KEYB_CLOCK ;Enable the Keyboard ACALL PS2_WaitClock ;Start Bit PUSH ACC ;Protect original value of accumulator MOV R0,#8 ; 8bits to receive PSB_Xmit: RRC A ;Shift bits into carry MOV KEYB_DATA, C ;Send highest bit out to keyboard ACALL PS2_WaitClock ;Wait for keyboard to acknowledge DJNZ R0,PSB_Xmit ;Loop for each bit POP ACC ;Restore original value of accumulator MOV C,PSW.0 ;This is Even parity CPL C ;And Keyboard needs Odd parity MOV KEYB_DATA,C ;Send parity bit ACALL PS2_WaitClock ;Wait for keyboard to acknowledge bit SETB KEYB_DATA ;Send stop bit ACALL PS2_WaitClock ;Wait for keyobard to acknowledge bit ACALL PS2_WaitClock ;Wait for keyboard to acknowledge bit MOV C, KEYB_DATA ;Get the ACK bit from the keyboard, store in carry CLR KEYB_CLOCK ;Deselect the keyboard XCH A,R0 POP ACC XCH A,R0 ;Restore R0 ret ;*************************************************************** ;* Function: PS2_CheckAck * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: Accumulator: Value received from keyboard * ;* Output Carry: Clear=NAK, Set=ACK receivedn data * ;*-------------------------------------------------------------* ;* Description: Checks to see if the contents of the acc is * ;* the ACK code (FAh). If it is, it sets the carry bit. If * ;* it isn't, it clears it. * ;*************************************************************** PS2_ChkAck: CJNE A,#KB_ACK,PCA_NAK ;If character was not ACK, clr carry to indicate failure SETB C ;Set carry to indicate successful ACK RET PCA_NAK: CLR C ;Clear carry to indicate failure RET ;*************************************************************** ;* Function: PS2_Init * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: None * ;* Output Carry: Clear=Failure, Set=Success * ;*-------------------------------------------------------------* ;* Description: Initializes the keyboard and indicates whether * ;* the initialization was successful or not. * ;*************************************************************** PS2_Init: MOV A,#0FFh ;Initializtation command ACALL PS2_SendByte ;Send command to keyboard ACALL PS2_GetByte ;Get a response byte, should be ACK ACALL PS2_ChkAck ;Check to see if it was ACK JNC PS2_Init ;If it wasn't repeat and try to initialize again MOV A,#0F4h ; Enable keyboard command ACALL PS2_SendByte ;Send command to keyboard ACALL PS2_GetByte ;Get a response byte, should be ACK ACALL PS2_ChkAck ;Check to see if it was ACK JNC PS2_Err ;If not success, abort command and exit MOV A, #0F3h ; Set Typematic ACALL PS2_SendByte ;Send command to keyboard ACALL PS2_GetByte ;Get a response byte, should be ACK ACALL PS2_ChkAck ;Check to see if it was ACK JNC PS2_Err ;If not success, abort command and exit MOV A, #00h ; Typematic = 250 ms / 30 cps ACALL PS2_SendByte ;Send command to keyboard ACALL PS2_GetByte ;Get a response byte, should be ACK ACALL PS2_ChkAck ;Check to see if it was ACK JNC PS2_Err ;If not success, abort command and exit MOV A,#0 ;Keyboard starts with LEDs off, fall through to Set LEDs PS2_SetLeds: PUSH ACC ;Protect LED setting passed in accumulator MOV A, #0EDh ;Set Leds command ACALL PS2_SendByte ;Send command to keyboard ACALL PS2_GetByte ;Get a response byte, should be ACK ACALL PS2_ChkAck ;Check to see if it was ACK JNC PS2_Err ;If not success, abort command and exit POP ACC ;Restore the LED setting that was passed in ACALL PS2_SendByte ;Send command to keyboard ACALL PS2_GetByte ;Get a response byte, should be ACK ACALL PS2_ChkAck ;Check to see if it was ACK PS2_Err: RET ;*************************************************************** ;* Function: PS2_GetScanCode * ;* Author: Craig Steiner based on code by Gabriel Lour * ;* Input: None * ;* Output Carry: Clear=No key received, Set=Key received * ;* Accumulator: Scan key code * ;*-------------------------------------------------------------* ;* Description: Gets a scan code from the keyboard and returns * ;* it. A scan code is the printable alphabet and numbers in * ;* unshifted form. 0-127 means key pressed, 128+ means * ;* key released. * ;*************************************************************** PS2_GetScanCode: LCALL PS2_GetByte ;Try to get first character JC PGSC_GotKey ;If we got a byte, process it PGSC_NoKey: CLR C ;Clear carry to signify no character received RET ;No byte received, so just exit PGSC_GotKey: ;If we got a key then "B" is going to be used as our return ;register. It starts out clear MOV B,#00h ;Clear our return code MOV DPTR,#PGSC_NormalCodes ;Point initially to the normal codes PGSC_ProcKey: MOV R1,A ;Hold received character in R1 CJNE A,#KB_BREAK,PGSC_NotBreak ;If it's not a break code (F0) then continue ;If it is a break code then we set the high bit of 'B' to indicate that the ;key was released. SETB B.7 ;Set high bit PGSC_AnotherKey: MOV R0,#20 ;Try 20 times to wait for next byte from keyboard PGSC_AKeyLoop: LCALL PS2_GetByte ;We then get the next byte from keyboard JC PGSC_ProcKey ;A byte was received, so process it DJNZ R0,PGSC_AKeyLoop ;No key detected, so keep trying SJMP PGSC_NoKey ;No key was found after multiple tries PGSC_NotBreak: ;Check to see if it was an extended key CJNE A,#KB_EXTENDED,PGSC_FindCode ;If not an extended code (E0), go process it ;This means we got an extended code. If so, we set our look-up table ;to the extended scan code table and get another key and process it. MOV DPTR,#PGSC_ExtendedCodes ;Set lookup table to extended codes SJMP PGSC_AnotherKey ;Go get another key and process it PGSC_FindCode: ;This now looks for the code we received in the proper table CLR A ;Make sure offset is zero MOVC A,@A+DPTR ;Get next scan code from DPTR JZ PGSC_NoKey ;If zero then end of table, so exit XRL A,R1 ;See if the key we got IS the key we're looking for JZ PGSC_FoundKey ;If it is, so process it ;It wasn't the right key, so we increment DPTR twice to point to the ;next table entry and process that INC DPTR INC DPTR SJMP PGSC_FindCode PGSC_FoundKey: INC DPTR ;Point to the translation CLR A ;No offset MOVC A,@A+DPTR ;Get the translated character ORL A,B ;Combine it with B which may hold a "break" code SETB C ;Set carry flag to indicate we have a character RET PGSC_NormalCodes: ;These are the translations for the normal, non-extended codes. Basically ;these are the scan codes that consist of a single byte DB 01Ch,'A' DB 032h,'B' DB 021h,'C' DB 023h,'D' DB 024h,'E' DB 02Bh,'F' DB 034h,'G' DB 033h,'H' DB 043h,'I' DB 03Bh,'J' DB 042h,'K' DB 04Bh,'L' DB 03Ah,'M' DB 031h,'N' DB 044h,'O' DB 04Dh,'P' DB 015h,'Q' DB 02Dh,'R' DB 01Bh,'S' DB 02Ch,'T' DB 03Ch,'U' DB 02Ah,'V' DB 01Dh,'W' DB 022h,'X' DB 035h,'Y' DB 01Ah,'Z' DB 045h,'0' DB 016h,'1' DB 01Eh,'2' DB 026h,'3' DB 025h,'4' DB 02Eh,'5' DB 036h,'6' DB 03Dh,'7' DB 03Eh,'8' DB 046h,'9' DB 00Eh,'`' DB 04Eh,'-' DB 055h,'=' DB 05Dh,'\' DB 066h,KS_BACKSPACE DB 029h,' ' DB 00Dh,KS_TAB DB 058h,KS_CAPS DB 012h,KS_LEFTSHIFT DB 014h,KS_LEFTCTRL DB 011h,KS_LEFTALT DB 059h,KS_RIGHTSHIFT DB 05Ah,KS_ENTER DB 076h,KS_ESCAPE DB 005h,KS_F1 DB 006h,KS_F2 DB 004h,KS_F3 DB 00Ch,KS_F4 DB 003h,KS_F5 DB 00Bh,KS_F6 DB 083h,KS_F7 DB 00Ah,KS_F8 DB 001h,KS_F9 DB 009h,KS_F10 DB 078h,KS_F11 DB 007h,KS_F12 DB 073h,KS_SCROLL DB 054h,'[' DB 077h,KS_NUMLOCK DB 07Ch,KS_KP_ASTERISK DB 07Bh,KS_KP_MINUS DB 079h,KS_KP_PLUS DB 071h,KS_KP_POINT DB 070h,KS_KP_0 DB 069h,KS_KP_1 DB 072h,KS_KP_2 DB 07Ah,KS_KP_3 DB 06Bh,KS_KP_4 DB 073h,KS_KP_5 DB 074h,KS_KP_6 DB 06Ch,KS_KP_7 DB 075h,KS_KP_8 DB 07Dh,KS_KP_9 DB 05Bh,']' DB 04Ch,';' DB 052h,''' DB 041h,',' DB 049h,'.' DB 04Ah,'/' PGSC_ExtendedCodes: ;These are the translations for the extended codes. These are the codes ;that start with E0 and then a second character. This table lists that ;second character DB 01Fh,KS_LEFTGUI DB 04Ah,KS_KP_SLASH DB 014h,KS_RIGHTCTRL DB 027h,KS_RIGHTGUI DB 011h,KS_RIGHTALT DB 02Fh,KS_APPS DB 012h,KS_PRTSCREEN1 DB 07Ch,KS_PRTSCREEN2 DB 070h,KS_INSERT DB 06Ch,KS_HOME DB 07Dh,KS_PGUP DB 071h,KS_DELETE DB 069h,KS_END DB 07Ah,KS_PGDOWN DB 075h,KS_UPARROW DB 06Bh,KS_LEFTARROW DB 072h,KS_DOWNARROW DB 074h,KS_RIGHTARROW DB 05Ah,KS_KP_ENTER DB 037h,KS_ACPI_POWER DB 03Fh,KS_ACPI_SLEEP DB 05Eh,KS_ACPI_WAKE DB 0