Increase your Embedded skills with our new online classes (from Just Rs750/-) for details click here

Home > Code Library > ps/2 keyboard interface with 8051 Assembly code

ps/2 keyboard interface with 8051 Assembly code

November 28th, 2009

PS/2 KEYBOARD

ps2 keyboad connetor with 8051

General description

The PS/2 keyboard was originally an extension of the AT device.  It supported a few additional host-to-keyboard commands and featured a smaller connector.  These were the only differences between the two devices.  However, computer hardware has never been about standards as much as compatibility.  For this reason, any keyboard you buy today will be compatible with PS/2 and AT systems, but it may not fully support all the features of the original devices.

Modern PS/2 (AT) compatible keyboards

  • Any number of keys (usually 101 or 104)
  • 5-pin or 6-pin connector; adaptor usually included
  • Bi-directional serial protocol
  • Only scan code set 2 guaranteed.
  • Acknowledges all commands; may not act on all of them.

Keyboards consist of a large matrix of keys, all of which are monitored by an on-board processor (called the “keyboard encoder”.)  The specific processor varies from keyboard-to-keyboard but they all basically do the same thing:  Monitor which key(s) is being pressed/released and send the appropriate data to the host.  This processor takes care of all the debouncing and buffers any data in its 16-byte buffer, if needed.  Your motherboard contains a “keyboard controller” that is in charge of decoding all of the data received from the keyboard and informing your software of what’s going on.  All communication between the host and the keyboard uses an IBM protocol.

Scan Codes

The keyboard’s processor spends most of its time “scanning”, or monitoring, the matrix of keys.  If it finds that any key is being pressed, released, or held down, the keyboard will send a packet of information known as a “scan code” to your computer.  There is two different types of scan codes: “make codes” and “break codes”.  A make code is sent when a key is pressed or held down.  A break code is sent when a key is released.  Every key is assigned its own unique make code and break code so the host can determine exactly what happened to which key by looking at a single scan code.  The set of make and break codes for every key comprises a “scan code set”.  There are three standard scan code sets, named one, two, and three.  All modern keyboards default to set two.

Fig3.2.1 Scan Code1

Fig3.2.2 Scan Code2

Make Codes, Break Codes, and Typematic Repeat

Whenever a key is pressed, that key’s make code is sent to the computer.  Keep in mind that a make code only represents a key on a keyboard–it does not represent the character printed on that key.  This means that there is no defined relationship between a make code and an ASCII code.  It’s up to the host to translate scan codes to characters or commands.

Although most set two make codes are only one-byte wide, there are a handful of “extended keys” whose make codes are two or four bytes wide.  These make codes can be identified by the fact that their first byte is E0h.

Just as a make code is sent to the computer whenever a key is pressed, a break code is sent whenever a key is released.  In addition to every key having its own unique make code, they all have their own unique break code. Fortunately, however, you won’t always have to use lookup tables to figure out a key’s break code–certain relationships do exist between make codes and break codes.  Most set two break codes are two bytes long where the first byte is F0h and the second byte is the make code for that key.  Break codes for extended keys are usually three bytes long where the first two bytes are E0h, F0h, and the last byte is the last byte of that key’s make code

Physical Interface

The physical PS/2 port is one of two styles of connectors:  The 5-pin DIN or the 6-pin mini-DIN.  Both connectors are completely (electrically) similar; the only practical difference between the two is the arrangement of pins.  This means the two types of connectors can easily be changed with simple hard-wired adaptors.  These costs about $6 each or you can make your own by matching the pins on any two connectors.  The DIN standard was created by the German Standardization Organization (Deutsches Institut fuer Norm).  PC keyboards use either a 6-pin mini-DIN or a 5-pin DIN connector.  Keyboards with the 6-pin mini-DIN are often referred to as “PS/2″ keyboards, while those with the 5-pin DIN are called “AT” devices (“XT” keyboards also used the 5-pin DIN, but they are quite old and haven’t been made for many years.)  All modern keyboards built for the PC is either PS/2, AT, or USB.
The cable connecting the keyboard/mouse to the computer is usually about six feet long and consists of four to six 26 AWG wires surrounded by a thin layer of Mylar foil shielding.  If you need a longer cable, you can buy PS/2 extension cables from most consumer electronics stores.  You should not connect multiple extension cables together.  If you need a 30-foot keyboard cable, buy a 30-foot keyboard cable.  Do not simply connect five 6-foot cables together.  Doing so could result in poor communication between the keyboard/mouse and the host.

As a side note, there is one other type of connector you may run into on keyboards. While most keyboard cables are hard-wired to the keyboard, there are some whose cable is not permanently attached and come as a separate component.  These cables have a DIN connector on one end (the end that connects to the computer) and a SDL (Shielded Data Link) connector on the keyboard end.  A company called “AMP” created SDL.  This connector is somewhat similar to a telephone connector in that it has wires and springs rather than pins, and a clip holds it in place.  The pinouts for each connector are shown below:

Connector diagram

Male

(Plug)

Female

(Socket)

5-pin DIN (AT/XT):
1 – Clock
2 – Data
3 – Not Implemented
4 – Ground
5 – Vcc (+5V)

Male

(Plug)

Female

(Socket)

6-pin Mini-DIN (PS/2):
1 – Data
2 – Not Implemented
3 – Ground
4 – Vcc (+5V)
5 – Clock
6 – Not Implemented

6-pin SDL:
A – Not Implemented
B – Data
C – Ground
D – Clock
E – Vcc (+5V)
F – Not Implemented

Fig3.4 Connector Diagram
Electrical Interface

Vcc/Ground provide power to the keyboard/mouse.  The keyboard or mouse should not draw more than 275 mA from the host and care must be taken to avoid transient surges.  Such surges can be caused by “hot-plugging” a keyboard/mouse                  (ie, connect/disconnect the device while the computer’s power is on.)  Older motherboards had a surface-mounted fuse protecting the keyboard and mouse ports.  When this fuse blew, the motherboard was useless to the consumer, and non-fixable to the average technician.  Most new motherboards use auto-reset “Poly” fuses that go a long way to remedy this problem.

Summary: Power Specifications
Vcc = +4.5V to +5.5V.
Max Current = 275 mA.

The Data and Clock lines are both open-collector with pullup resistors to Vcc.  An “open-collector” interface has two possible state: low, or high impedance.  In the “low” state, a transistor pulls the line to ground level.  In the “high impedance” state, the interface acts as an open circuit and doesn’t drive the line low or high. Furthermore, a “pullup” resistor is connected between the bus and Vcc so the bus is pulled high if none of the devices on the bus are actively pulling it low.  The exact value of this resistor isn’t too important (1~10 kOhms); larger resistances result in less power consumption and smaller resistances result in a faster rise time.  A general open-collector interface is shown below:
Figure 3.5: General open-collector interface.

A line is pulled to ground by setting the corresponding pin to output, and writing a “zero” to that port.  The line is set to the “high impedance” state by setting the pin to input.

Communication

The PS/2 mouse and keyboard implement a bi-directional synchronous serial protocol.  The bus is “idle” when both lines are high (open-collector).  This is the only state where the keyboard/mouse is allowed begin transmitting data.  The host has ultimate control over the bus and may inhibit communication at any time by pulling the Clock line low.

The device always generates the clock signal.  If the host wants to send data, it must first inhibit communication from the device by pulling Clock low.  The host then pulls Data low and releases Clock.  This is the “Request-to-Send” state and signals the device to start generating clock pulses.

Summary: Bus States
Data = high, Clock = high:  Idle state.
Data = high, Clock = low:  Communication Inhibited.
Data = low, Clock = high:  Host Request-to-Send

All data is transmitted one byte at a time and each byte is sent in a frame consisting of 11-12 bits.  These bits are:

  • 1 start bit.  This is always 0.
  • 8 data bits, least significant bit first.
  • 1 parity bit (odd parity).
  • 1 stop bit.  This is always 1.
  • 1 acknowledge bit (host-to-device communication only)

The parity bit is set if there is an even number of 1′s in the data bits and reset (0) if there is an odd number of 1′s in the data bits.  The number of 1′s in the data bits plus the parity bit always add up to an odd number (odd parity.)  This is used for error detection.  The keyboard/mouse must check this bit and if incorrect it should respond as if it had received an invalid command.

Data sent from the device to the host is read on the falling edge of the clock signal; data sent from the host to the device is read on the rising edge. The clock frequency must be in the range 10 – 16.7 kHz.  This means clock must be high for 30 – 50 microseconds and low for 30 – 50 microseconds..  If you’re designing a keyboard, mouse, or host emulator, you should modify/sample the Data line in the middle of each cell.  I.e.  15 – 25 microseconds after the appropriate clock transition.  Again, the keyboard/mouse always generates the clock signal, but the host always has ultimate control over communication.

Communication: Device-to-Host

The Data and Clock lines are both open collector.  A resistor is connected between each line and +5V, so the idle state of the bus is high. When the keyboard or mouse wants to send information, it first checks the Clock line to make sure it’s at a high logic level.  If it’s not, the host is inhibiting communication and the device must buffer any to-be-sent data until the host releases Clock.  The Clock line must be continuously high for at least 50 microseconds before the device can begin to transmit its data.

As I mentioned in the previous section, the keyboard and mouse use a serial protocol with 11-bit frames.  These bits are:

  • 1 start bit.  This is always 0.
  • 8 data bits, least significant bit first.
  • 1 parity bit (odd parity).
  • 1 stop bit.  This is always 1.

The keyboard/mouse writes a bit on the Data line when Clock is high, and it is read by the host when Clock is low.  Figures 2 and 3 illustrate this.

Fig3.6.1:  Device-to-host communication.

The clock frequency is 10-16.7 kHz.  The time from the rising edge of a clock pulse to a Data transition must be at least 5 microseconds.  The time from a data transition to the falling edge of a clock pulse must be at least 5 microseconds and no greater than 25 microseconds.

The host may inhibit communication at any time by pulling the Clock line low for at least 100 microseconds.  If a transmission is inhibited before the 11th clock pulse, the device must abort the current transmission and prepare to retransmit the current “chunk” of data when host releases Clock.  A “chunk” of data could be a make code, break code, device ID, mouse movement packet, etc.  For example, if a keyboard is interrupted while sending the second byte of a two-byte break code, it will need to retransmit both bytes of that break code, not just the one that was interrupted.

If the host pulls clock low before the first high-to-low clock transition, or after the falling edge of the last clock pulse, the keyboard/mouse does not need to retransmit any data.  However, if new data is created that needs to be transmitted, it will have to be buffered until the host releases Clock.  Keyboards have a 16-byte buffer for this purpose.  If more than 16 bytes worth of keystrokes occur, further keystrokes will be ignored until there’s room in the buffer.  Mice only store the most current movement packet for transmission.

Host-to-Device Communication

The packet is sent a little differently in host-to-device communication…

First of all, the PS/2 device always generates the clock signal.  If the host wants to send data, it must first put the Clock and Data lines in a “Request-to-send” state as follows:

  • Inhibit communication by pulling Clock low for at least 100 microseconds.
  • Apply “Request-to-send” by pulling Data low, and then release Clock.

The device should check for this state at intervals not to exceed 10 milliseconds.  When the device detects this state, it will begin generating Clock signals and clock in eight data bits and one stop bit.  The host changes the Data line only when the Clock line is low, and the device reads data when Clock is high.  This is opposite of what occurs in device-to-host communication.

After the stop bit is received, the device will acknowledge the received byte by bringing the Data line low and generating one last clock pulse.  If the host does not release the Data line after the 11th clock pulse, the device will continue to generate clock pulses until the Data line is released (the device will then generate an error.)

The host may abort transmission at time before the 11th clock pulse (acknowledge bit) by holding Clock low for at least 100 microseconds.

To make this process a little easier to understand, here’s the steps the host must follow to send data to a PS/2 device:

1)   Bring the Clock line low for at least 100 microseconds.
2)   Bring the Data line low.
3)   Release the Clock line.
4)   Wait for the device to bring the Clock line low.
5)   Set/reset the Data line to send the first data bit
6)   Wait for the device to bring Clock high.
7)   Wait for the device to bring Clock low.
8)   Repeat steps 5-7 for the other seven data bits and the parity bit
9)   Release the Data line.
10) Wait for the device to bring Data low.
11) Wait for the device to bring Clock low.
12) Wait for the device to release Data and Clock

Figure 3 shows this graphically and Figure 4 separates the timing to show which signals are generated by the host, and which are generated by the PS/2 device.  Notice the change in timing for the “ack” bit–the data transition occurs when the Clock line is high (rather than when it is low as is the case for the other 11 bits.)

Fig 3.6.2:  Host-to-Device Communication.



Fig3.6.3:  Detailed host-to-device communication.

Referring to Figure 4, there are two time quantities the host looks for.  (a) is the time it takes the device to begin generating clock pulses after the host initially takes the Clock line low, which must be no greater than 15 ms. (b) is the time it takes for the  packet to be sent, which must be no greater than 2ms.  If either of these time limits is not met, the host should generate an error.  Immediately after the “ack” is received, the host may bring the Clock line low to inhibit communication while it processes data.  If the command sent by the host requires a response, that response must be received no later than 20 ms after the host releases the Clock line.  If this does not happen, the host generates an error.

Keyboard Scan Codes: Set 2
*All values are in hexadecimal

101-, 102-, and 104-key keyboards:

KEY

MAKE

BREAK

-----

KEY

MAKE

BREAK

—–

KEY

MAKE

BREAK

A

1C

F0,1C

9

46

F0,46

[

54

FO,54

B

32

F0,32

`

0E

F0,0E

INSERT

E0,70

E0,F0,70

C

21

F0,21

-

4E

F0,4E

HOME

E0,6C

E0,F0,6C

D

23

F0,23

=

55

FO,55

PG UP

E0,7D

E0,F0,7D

E

24

F0,24

\

5D

F0,5D

DELETE

E0,71

E0,F0,71

F

2B

F0,2B

BKSP

66

F0,66

END

E0,69

E0,F0,69

G

34

F0,34

SPACE

29

F0,29

PG DN

E0,7A

E0,F0,7A

H

33

F0,33

TAB

0D

F0,0D

U ARROW

E0,75

E0,F0,75

I

43

F0,43

CAPS

58

F0,58

L ARROW

E0,6B

E0,F0,6B

J

3B

F0,3B

L SHFT

12

FO,12

D ARROW

E0,72

E0,F0,72

K

42

F0,42

L CTRL

14

FO,14

R ARROW

E0,74

E0,F0,74

L

4B

F0,4B

L GUI

E0,1F

E0,F0,1F

NUM

77

F0,77

M

3A

F0,3A

L ALT

11

F0,11

KP /

E0,4A

E0,F0,4A

N

31

F0,31

R SHFT

59

F0,59

KP *

7C

F0,7C

O

44

F0,44

R CTRL

E0,14

E0,F0,14

KP -

7B

F0,7B

P

4D

F0,4D

R GUI

E0,27

E0,F0,27

KP +

79

F0,79

Q

15

F0,15

R ALT

E0,11

E0,F0,11

KP EN

E0,5A

E0,F0,5A

R

2D

F0,2D

APPS

E0,2F

E0,F0,2F

KP .

71

F0,71

S

1B

F0,1B

ENTER

5A

F0,5A

KP 0

70

F0,70

T

2C

F0,2C

ESC

76

F0,76

KP 1

69

F0,69

U

3C

F0,3C

F1

05

F0,05

KP 2

72

F0,72

V

2A

F0,2A

F2

06

F0,06

KP 3

7A

F0,7A

W

1D

F0,1D

F3

04

F0,04

KP 4

6B

F0,6B

X

22

F0,22

F4

0C

F0,0C

KP 5

73

F0,73

Y

35

F0,35

F5

03

F0,03

KP 6

74

F0,74

Z

1A

F0,1A

F6

0B

F0,0B

KP 7

6C

F0,6C

0

45

F0,45

F7

83

F0,83

KP 8

75

F0,75

1

16

F0,16

F8

0A

F0,0A

KP 9

7D

F0,7D

2

1E

F0,1E

F9

01

F0,01

]

5B

F0,5B

3

26

F0,26

F10

09

F0,09

;

4C

F0,4C

4

25

F0,25

F11

78

F0,78

'

52

F0,52

5

2E

F0,2E

F12

07

F0,07

,

41

F0,41

6

36

F0,36

PRNT
SCRN

E0,12,
E0,7C

E0,F0,
7C,E0,
F0,12

.

49

F0,49

7

3D

F0,3D

SCROLL

7E

F0,7E

/

4A

F0,4A

8

3E

F0,3E

PAUSE

E1,14,77,
E1,F0,14,
F0,77

-NONE-

Table 3.1 keyboard scan codes

ACPI Scan Codes:

Key Make Code Break Code
Power E0, 37 E0, F0, 37
Sleep E0, 3F E0, F0, 3F
Wake E0, 5E E0, F0, 5E

Windows Multimedia Scan Codes:

Key Make Code Break Code
Next Track E0, 4D E0, F0, 4D
Previous Track E0, 15 E0, F0, 15
Stop E0, 3B E0, F0, 3B
Play/Pause E0, 34 E0, F0, 34
Mute E0, 23 E0, F0, 23
Volume Up E0, 32 E0, F0, 32
Volume Down E0, 21 E0, F0, 21
Media Select E0, 50 E0, F0, 50
E-Mail E0, 48 E0, F0, 48
Calculator E0, 2B E0, F0, 2B
My Computer E0, 40 E0, F0, 40
WWW Search E0, 10 E0, F0, 10
WWW Home E0, F0, 3A
WWW Back E0, 38 E0, F0, 38
WWW Forward E0, 30 E0, F0, 30
WWW Stop E0, 28 E0, F0, 28
WWW Refresh E0, 20 E0, F0, 20
WWW Favorites E0, 18 E0, F0, 18

ps/2 keyboard Assembly code with lcd:

$mod51
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
;==========================================================
;===========================================================
;SendSerial          EQU 0041h
;SendSerialHexByte   EQU 0044h
;SendSerialByte      EQU 0047h
;==========================================================
;                     PROGRAM LOCATION
;
;modified to 0000h rather than 8000h.
;===========================================================
ORG 0000h
;==========================================================
;                       PROGRAM CODE
;
;This is the actual guts of the demonstration code.
;===========================================================
LCALL lcdi
LCALL   SendSerial      ;Send ‘initializing’ message
DB      ”Initializing keyboard “,13,0
LCALL   PS2_GenericInit        ;Initialize the keyboard
LCALL   SendSerial      ;Send the ‘init done’ message
DB      ”Init successful dumping PS/2 Communication “,13,0
CLR     RI
MOV     R7,#00
Loop:
JB      RI,ExitProgram      ;If a key has been pressed, exit
; LCALL   PS2_GetByte         ;Get a scan code from the keyboard
LCALL PS2_GetScanCode
JNC     Loop                ;If no key was pressed, keep waiting
;See if the key we got IS the key we’re looking for
;JZ     skip
LCALL   datawrt
skip: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
ExitProgram:
CLR RI                      ;Clear serial input and exit back to SBCMON
RET
;*****************************************************************
;* Function: ByteTo2Hex                                          *
;* Purpose:  Convert a single byte into two hex digits           *
;* Input:    A    = Byte to convert (0×00-0xFF)                  *
;* Output:   A    = High nibble (ASCII 0×30-0×39,0×41-0×46)      *
;*           R0   = Low nibble (ASCII 0×30-0×39, 0×41-0×46)      *
;* Destroyed Registers: None                                     *
;*****************************************************************
ByteTo2Hex:
MOV     R0,A
ANL     A,#0Fh
ADD     A,#0F6h
JNC     byte_to_bcd_2
ADD     A,#07h
byte_to_bcd_2:
ADD     A,#3Ah
XCH     A,R0
SWAP    A
ANL     A,#0Fh
ADD     A,#0F6h
JNC     byte_to_bcd_3
ADD     A,#07h
byte_to_bcd_3:
ADD     A,#3Ah
RET
;*****************************************************************
;* Function: SendSerialHexByte                                   *
;* Purpose:  Sends the byte in the accumulator to the serial     *
;*           port as two hexadecimal nibbles.                    *
;* Input:    ACC: Byte to send                                   *
;* Output:   None.                                               *
;* Destroyed Registers: None                                     *
;*****************************************************************
SendSerialHexByte:
PUSH    00h             ;Save R0
PUSH    ACC             ;Save ACC
LCALL   ByteTo2Hex      ;Convert high byte to two bytes of BCD
LCALL   SendSerialByte  ;Send the high byte
MOV     A,R0            ;Send the low byte
LCALL   SendSerialByte  ;Send it
POP     ACC             ;Restore R0
POP     00h             ;Restore R0
RET
;*****************************************************************
;* Function: SendSerialByte                                      *
;* Purpose:  Sends the byte in the accumulator to the serial     *
;*           port and waits for it to be sent before returning.  *
;* Input:    ACC: Byte to send                                   *
;* Output:   None.                                               *
;* Destroyed Registers: None                                     *
;*****************************************************************
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SendSerialByte:
CLR TI          ;Prepare for transmit
MOV SBUF,A      ;Send the byte out
JNB TI,$        ;Wait for byte to be sent
RET             ;Done!
;*****************************************************************
;* Function: SendSerial                                          *
;* Purpose:  Sends the null-terminated string that immediately   *
;*           follows the LCALL that called the function to the   *
;*           serial port.                                        *
;* Input:    ACC: Byte to send                                   *
;* Output:   None.                                               *
;* Destroyed Registers: None                                     *
;*****************************************************************
SendSerial:
MOV     TMOD,#22h   ;Timer 1 and timer 0in Auto-reload mode
MOV     TH1,#0FDh   ;Reload value for 9600 baud @ 11.059 Mhz
SETB    TR1         ;Turn on timer 1 for baud rate generator
MOV     SCON,#52h   ;Receiver Enable/Timer 1 Baud Rate 8-bit
POP     DPH
POP     DPL
PUSH    ACC
PUSH    PSW
CLR     TI
SendSerial1:
CLR     A
MOVC    A,@A+DPTR
INC     DPTR
JZ      SendSerialExit
MOV     SBUF,A
JNB     TI,$
CLR     TI
;(v1.3.1) – Removed LF-appending
;———————————-
CJNE    A,#13,SendSerial1
MOV     A,#10
MOV     SBUF,A
JNB     TI,$
CLR     TI
;———————————-
SJMP    SendSerial1
SendSerialExit:
POP     PSW
POP     ACC
PUSH    DPL
PUSH    DPH
RET
PS2_GenericInit:
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_GenericInit ;If it wasn’t repeat and try to initialize again
MOV     A,#0F4h       ;Enable reporting
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_GenericInit ;If it wasn’t repeat and try to initialize again
RET
;//////////////////////////////////////////////////////////////////////////////////////////////
;PS2_MouseInit:
;       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_MouseInit ;If it wasn’t repeat and try to initialize again
;   MOV     A,#0F6h       ;Set default values
;  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_MouseInit ;If it wasn’t repeat and try to initialize again
;  MOV     A,#0F4h       ;Enable reporting
;  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_MouseInit ;If it wasn’t repeat and try to initialize again
; 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
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; lcd Routine
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
commwrt:
mov a,#00h
mov dptr,#0fff8h
movx @dptr,a
mov a,r0
mov dptr,#0fff9h
movx @dptr,a
ret
lcdi:  mov r0,#38h
acall commwrt
acall delay
mov r0,#0eh
acall commwrt
acall delay
mov r0,#01h
acall commwrt
acall delay
mov r0,#06h
acall commwrt
acall delay
mov r0,#80h
acall commwrt
acall delay
ret
datawrt:
mov R1,A
mov a,#01h
mov dptr,#0fff8h
movx @dptr,a
MOV A,R1
mov dptr,#0fff9h
movx @dptr,a
ret
delay:  mov r3,#10
here: mov r4,#255
here1:  djnz r4,here1
djnz r3,here
ret
end

$mod51

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

;==========================================================

;===========================================================

;SendSerial          EQU 0041h

;SendSerialHexByte   EQU 0044h

;SendSerialByte      EQU 0047h

;==========================================================

;                     PROGRAM LOCATION

;

;modified to 0000h rather than 8000h.

;===========================================================

ORG 0000h

;==========================================================

;                       PROGRAM CODE

;

;This is the actual guts of the demonstration code.

;===========================================================

LCALL lcdi

LCALL   SendSerial      ;Send ‘initializing’ message

DB      ”Initializing keyboard “,13,0

LCALL   PS2_GenericInit        ;Initialize the keyboard

LCALL   SendSerial      ;Send the ‘init done’ message

DB      ”Init successful dumping PS/2 Communication “,13,0

CLR     RI

MOV     R7,#00

Loop:

JB      RI,ExitProgram      ;If a key has been pressed, exit

; LCALL   PS2_GetByte         ;Get a scan code from the keyboard

LCALL PS2_GetScanCode

JNC     Loop                ;If no key was pressed, keep waiting

;See if the key we got IS the key we’re looking for

;JZ     skip

LCALL   datawrt

skip: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

ExitProgram:

CLR RI                      ;Clear serial input and exit back to SBCMON

RET

;*****************************************************************

;* Function: ByteTo2Hex                                          *

;* Purpose:  Convert a single byte into two hex digits           *

;* Input:    A    = Byte to convert (0×00-0xFF)                  *

;* Output:   A    = High nibble (ASCII 0×30-0×39,0×41-0×46)      *

;*           R0   = Low nibble (ASCII 0×30-0×39, 0×41-0×46)      *

;* Destroyed Registers: None                                     *

;*****************************************************************

ByteTo2Hex:

MOV     R0,A

ANL     A,#0Fh

ADD     A,#0F6h

JNC     byte_to_bcd_2

ADD     A,#07h

byte_to_bcd_2:

ADD     A,#3Ah

XCH     A,R0

SWAP    A

ANL     A,#0Fh

ADD     A,#0F6h

JNC     byte_to_bcd_3

ADD     A,#07h

byte_to_bcd_3:

ADD     A,#3Ah

RET

;*****************************************************************

;* Function: SendSerialHexByte                                   *

;* Purpose:  Sends the byte in the accumulator to the serial     *

;*           port as two hexadecimal nibbles.                    *

;* Input:    ACC: Byte to send                                   *

;* Output:   None.                                               *

;* Destroyed Registers: None                                     *

;*****************************************************************

SendSerialHexByte:

PUSH    00h             ;Save R0

PUSH    ACC             ;Save ACC

LCALL   ByteTo2Hex      ;Convert high byte to two bytes of BCD

LCALL   SendSerialByte  ;Send the high byte

MOV     A,R0            ;Send the low byte

LCALL   SendSerialByte  ;Send it

POP     ACC             ;Restore R0

POP     00h             ;Restore R0

RET

;*****************************************************************

;* Function: SendSerialByte                                      *

;* Purpose:  Sends the byte in the accumulator to the serial     *

;*           port and waits for it to be sent before returning.  *

;* Input:    ACC: Byte to send                                   *

;* Output:   None.                                               *

;* Destroyed Registers: None                                     *

;*****************************************************************

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

SendSerialByte:

CLR TI          ;Prepare for transmit

MOV SBUF,A      ;Send the byte out

JNB TI,$        ;Wait for byte to be sent

RET             ;Done!

;*****************************************************************

;* Function: SendSerial                                          *

;* Purpose:  Sends the null-terminated string that immediately   *

;*           follows the LCALL that called the function to the   *

;*           serial port.                                        *

;* Input:    ACC: Byte to send                                   *

;* Output:   None.                                               *

;* Destroyed Registers: None                                     *

;*****************************************************************

SendSerial:

MOV     TMOD,#22h   ;Timer 1 and timer 0in Auto-reload mode

MOV     TH1,#0FDh   ;Reload value for 9600 baud @ 11.059 Mhz

SETB    TR1         ;Turn on timer 1 for baud rate generator

MOV     SCON,#52h   ;Receiver Enable/Timer 1 Baud Rate 8-bit

POP     DPH

POP     DPL

PUSH    ACC

PUSH    PSW

CLR     TI

SendSerial1:

CLR     A

MOVC    A,@A+DPTR

INC     DPTR

JZ      SendSerialExit

MOV     SBUF,A

JNB     TI,$

CLR     TI

;(v1.3.1) – Removed LF-appending

;———————————-

CJNE    A,#13,SendSerial1

MOV     A,#10

MOV     SBUF,A

JNB     TI,$

CLR     TI

;———————————-

SJMP    SendSerial1

SendSerialExit:

POP     PSW

POP     ACC

PUSH    DPL

PUSH    DPH

RET

PS2_GenericInit:

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_GenericInit ;If it wasn’t repeat and try to initialize again

MOV     A,#0F4h       ;Enable reporting

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_GenericInit ;If it wasn’t repeat and try to initialize again

RET

;//////////////////////////////////////////////////////////////////////////////////////////////

;PS2_MouseInit:

;       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_MouseInit ;If it wasn’t repeat and try to initialize again

;   MOV     A,#0F6h       ;Set default values

;  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_MouseInit ;If it wasn’t repeat and try to initialize again

;  MOV     A,#0F4h       ;Enable reporting

;  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_MouseInit ;If it wasn’t repeat and try to initialize again

; 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

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

; lcd Routine

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

commwrt:

mov a,#00h

mov dptr,#0fff8h

movx @dptr,a

mov a,r0

mov dptr,#0fff9h

movx @dptr,a

ret

lcdi:  mov r0,#38h

acall commwrt

acall delay

mov r0,#0eh

acall commwrt

acall delay

mov r0,#01h

acall commwrt

acall delay

mov r0,#06h

acall commwrt

acall delay

mov r0,#80h

acall commwrt

acall delay

ret

datawrt:

mov R1,A

mov a,#01h

mov dptr,#0fff8h

movx @dptr,a

MOV A,R1

mov dptr,#0fff9h

movx @dptr,a

ret

delay:  mov r3,#10

here: mov r4,#255

here1:  djnz r4,here1

djnz r3,here

ret

end

Comments are closed.