Created
February 26, 2018 00:06
-
-
Save tmk/5604a84f40cefeb5b359a114634db221 to your computer and use it in GitHub Desktop.
AT2XT keyboard converter which allows users to attach AT keyboards to XT class computers. http://www.vcfed.org/forum/showthread.php?26426-AT2XT-keyboard-converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nolist | |
; Macro counters. | |
_procnest set 0 ; procedure nesting counter | |
_keepcnt set 0 ; KEEP macro counter | |
;* Macros for PIC 12XX Programming. | |
; -------------------------------- | |
; | |
; proc/endproc macros define symbols that demarcate the beginning | |
; and end of a procedure. Very useful for debugging, as | |
; start_<number>Name | |
; end_<number>Name | |
; | |
; Call out the beginning and end locations of a procedure in the | |
; listing and its nesting level. We tried to have the endproc | |
; macro check for proper nesting, but gpasm has a bug that prevents | |
; this. | |
; | |
proc macro what ; begin procedure | |
what | |
start_#v(_procnest)what | |
_procnest set _procnest+1 | |
endm | |
endproc macro what ; end procedure | |
_procnest set _procnest-1 | |
end_#v(_procnest)what | |
endm | |
; Keep/unkeep - Bracket code that must stay together. | |
; --------------------------------------------------- | |
; | |
; These are mostly for documentation. When using | |
; any of the skip instructions, it's important that | |
; succeeding instructions not be moved, particularly | |
; in the case of nested skips, such as: | |
; | |
; btfss.. | |
; btfss... | |
; goto... | |
; | |
keep macro | |
_keepcnt set _keepcnt+1 | |
endm | |
unkeep macro | |
_keepcnt set _keepcnt-1 | |
if _keepcnt != 0 | |
error "Unkeep without a matching keep" | |
endif | |
endm | |
; Move immedate value to register - uses W | |
movif macro what,reg ; move immediate to register | |
movlw what | |
movwf reg | |
endm | |
; Move register-to-register - uses W | |
movff macro reg1,reg2 ; move register to register | |
movfw reg1 | |
movwf reg2 | |
endm | |
; Set bank 0. | |
; | |
; We re-enable the "not in zero bank" warning. | |
; | |
bank0 macro ; set bank 0 | |
bcf STATUS,RP0 | |
errorlevel +302 | |
endm | |
; Set bank 1. | |
; | |
; We disable the "not in zero bank" warning. | |
; | |
bank1 macro ; set bank 1 | |
bsf STATUS,RP0 | |
errorlevel -302 | |
endm | |
; Decrement, branch if not zero. | |
dbnz macro what,where ; decrement reg, branch nonzero | |
decfsz what,f | |
goto where | |
endm | |
; Branch if bit set. | |
bbs macro reg,bit,where ; branch if bit set | |
btfsc reg,bit | |
goto where | |
endm | |
; Branch if bit clear. | |
bbc macro reg,bit,where ; branch if bit clear | |
btfss reg,bit | |
goto where | |
endm | |
; Logical shift right. | |
slrf macro reg,dest | |
clrc | |
rrf reg,dest | |
endm | |
; Logical shift left. | |
sllf macro reg,dest | |
clrc | |
rlf reg,dest | |
endm | |
; Negate (2's complement) W. | |
negw macro | |
sublw 0 | |
endm | |
; Complement (1's complement) W. | |
notw macro | |
xorlw 0xff | |
endm | |
; Wait (stall) on bit clear. That is, stall until bit set. | |
stallc macro reg,bit | |
btfss reg,bit | |
goto $-1 | |
endm | |
; Wait (stall) on bit set. That is, stall until bit clear. | |
stalls macro reg,bit | |
btfsc reg,bit | |
goto $-1 | |
endm | |
; For 16-bit operations, big-endian register assignment is assmed. | |
; if R holds the MSB of a 16-bit quantity, then R+1 holds the | |
; LSB. | |
; Move double register to register. | |
movfd macro reg1,reg2 | |
movfw reg1 | |
movwf reg2 | |
movfw reg1+1 | |
movwf reg2+1 | |
endm | |
; Shift right double register (16 bit) | |
slrd macro reg | |
clrc | |
rrf reg,f | |
rrf reg+1,f | |
endm | |
; Shift left double register (16 bit) | |
slld macro reg | |
clrc | |
rlf reg+1,f | |
rlf reg,f | |
endm | |
; Delay 2 instruction cycles. | |
delay2 macro | |
goto $+1 | |
endm | |
list | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
title "PC AT/PS/2 to XT keycode translator." | |
list p=12F629 | |
radix dec | |
; noexpand ; I like neat listings | |
include "p12f629.inc" | |
include "maclib.inc" | |
org 2100h ; EPROM area | |
DE "[ATXTKEY Ver. 0.94]" | |
;* AT/PS2 to XT Keyboard Translator. | |
; --------------------------------- | |
; | |
; Version 0.90 Chuck Guzis, June, 2009 | |
; -- initial working release | |
; Version 0.92, September, 2009 | |
; -- cleans up host-to-keyboard timing a bit | |
; Version 0.93, August, 2010 | |
; -- Change to pass auxiliary code 0xE0 through | |
; to host without translation. | |
; Version 0.94, January, 2011 | |
; -- Changed translation table length to 144 bytes to handle | |
; erroneous F7 (scan code 83h) conversion. Main routine | |
; modified to perform threshold check instead of bit 7 check | |
; to filter special codes. | |
; | |
; Copyright by Charles P. Guzis, all rights reserved. | |
; | |
; Chuck Guzis retains all rights to this code, but gives permission | |
; to use and modify it for non-commercial, non-profit applications, | |
; provided that this copyright notice is reproduced in its entirety. | |
; | |
; This translator uses a small PIC12F629 microcontroller to translate | |
; an AT or PS/2 keyboard interface to that of the PC XT. Shift-lock | |
; status is noted and the keyboard LEDs are illuminated accordingly. | |
; | |
; The electrical interface to the 12F629 is very simple: | |
; | |
; Pin 1 = +5 supply (should have a 47 uF decoupling capacitor to Gnd | |
; Pin 2 = Clock from the PC XT host | |
; Pin 3 = Data from the PC XT host | |
; Pin 4 = If jumpered to ground, passes E0 codes 0.93 | |
; Pin 5 = AT keyboard clock (pulled high by 4.7K to +5) | |
; Pin 6 = AT keyboard data (pulled high by 4.7K to +5) | |
; Pin 7 = AT clock pulldown (a 1N4148 diode is connected between | |
; Pin 7 (cathode end) and pin 5 (anode end) | |
; Pin 8 = Gnd | |
; | |
; The 1N4148 diode isn't critical--any general-purpose signal | |
; diode will work. It's there to make GP0 behave as an open- | |
; drain driver. Since the PIC is a CMOS device, the extra | |
; diode drop will have no significant effect on operation. | |
; | |
; One could enable the MCLEAR function on the PIC and tie pin 4 to | |
; the RESET line from the XT host, but since the original XT keyboard | |
; doesn't use this signal, neither did I. | |
; | |
; The XT reset line is not used, as the XT BIOS asserts reset by | |
; holding the host clock low for at least 20 mS. | |
; | |
; Just so you don't have to look it up, here are the pinouts | |
; for both the XT (5 pin DIN) and PS/2 (6 pin mini-DIN) conectors: | |
; | |
; PS/2 connector: | |
; 1 - Data | |
; 3 - Gnd | |
; 4 - +5 | |
; 5 - Clock | |
; | |
; XT Connector: | |
; 1 - Clock | |
; 2 - Data | |
; 3 - Reset, not used | |
; 4 - Gnd | |
; 5 - +5 | |
; | |
; A 32-byte buffer is implemented for received keyboard data. | |
; This is actually twice the size of the AT keyboard buffer. | |
; Extraneous code bytes E0 and E1 are ignored from the AT keybaord. | |
; | |
; A 12F675 can be used in this application, but see the commented-out | |
; line in the "Setup" routine. | |
; | |
; 0.93 Note: | |
; If the pin 4 select will be used, pin 4 needs to be externally | |
; pulled up to +5V -- a 1K-100K resistor should be added between | |
; pin 1 and pin 4. | |
__CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT ;Internal osc. | |
subtitle "Symbol definitions" | |
page | |
; Our GPIO bit assignments by position. | |
bnKeyClockPD equ 0 ; AT key pulldown (GPIO 0, pin 7) | |
bnKeyData equ 1 ; AT keyboard data (GPIO 1, pin 6) | |
bnKeyClock equ 2 ; AT Keyboard clock (GPIO 2, pin 5) | |
bnPassE0 equ 3 ; MCLEAR, unused (GPIO 3, pin 4) | |
bnHostData equ 4 ; XT host data (GPIO 4, pin 3) | |
bnHostClock equ 5 ; XT host clock (GPIO 5, pin 2) | |
; Same as the above, but by bit values. | |
bvKeyClockPD equ 1<<bnKeyClockPD ; AT key pulldown (GPIO 0, pin 7) | |
bvKeyData equ 1<<bnKeyData ; AT keyboard data (GPIO 1, pin 6) | |
bvKeyClock equ 1<<bnKeyClock ; AT keyboard clock (GPIO 2, pin 5) | |
bvPassE0 equ 1<<bnPassE0 ; MCLEAR, (GPIO 3, pin 4) | |
bvHostData equ 1<<bnHostData ; XT host data (GPIO 4, pin 3) | |
bvHostClock equ 1<<bnHostClock ; XT host clock (GPIO 5, pin 2) | |
; Bits relating to keyboard state in rATFlags. | |
; | |
; Observe that the LED (shift lock) keys are in the precise | |
; position that the AT keyboard ED command expects them. | |
bnATRelease equ 7 ; we saw a key-up | |
bnATScrollLock equ 0 ; scroll lock | |
bnATNumLock equ 1 ; numeric lock | |
bnATCapsLock equ 2 ; caps lock | |
bvATRelease equ 1<<bnATRelease ; if key-up seen | |
bvATScrollLock equ 1<<bnATScrollLock ; scroll lock | |
bvATNumLock equ 1<<bnATNumLock ; numeric lock | |
bvATCapsLock equ 1<<bnATCapsLock ; caps are locked | |
; Register holding flags for the "send byte" code in the | |
; interrupt service code. | |
bnATSendReq equ 0 ; if set, there's a character in | |
; rATSendByte that needs to go | |
bnACKSeen equ 1 ; ACK isn't put into the queue; | |
; rather when received, this flag | |
; is set. | |
; Input queue area starts at 0x40 and goes through 0x5f | |
QUEUE_SIZE equ 32 ; cells to use in queue | |
; XT scancodes for special keys. | |
XTSC_CAPSLOCK equ 0x3a ; Caps lock | |
XTSC_SCRLOCK equ 0x46 ; Scroll lock | |
XTSC_NUMLOCK equ 0x45 ; Numeric lock | |
XTSC_OKAY equ 0xaa ; Scan code for "Diagnostics Passed" | |
; AT command codes. | |
ATSC_SETLED equ 0xed ; set LEDs | |
ATSC_RESET equ 0xff ; reset | |
ATSC_ACK equ 0xfa ; acknowledge | |
ATSC_OK equ 0xaa ; diagnostics okay | |
subtitle "Data area (register) layout" | |
page | |
; Register file definitions. | |
cblock 0x20 | |
rXTByte :1 ; byte to send - used by SendXTByte | |
rXTBitCount :1 ; bits left to send | |
rATChar :1 ; character returned by ReadKey | |
rATFlags :1 ; flags set by AT codes | |
rTemp1 :1 ; Scratch reg 1 | |
rTemp2 :1 ; Scratch reg 2 | |
; Interrupt routine registers. | |
rIntSaveW :1 ; Savearea for W | |
rIntSaveS :1 ; Savearea for status word | |
rIntSaveFSR :1 ; FSR save | |
rIntKCount :1 ; keyboard data bit counter | |
rIntKData :1 ; keyboard code accumulator | |
rIntTemp :1 ; temporary for the ISR | |
; Code shared by interrupt and mainline routines. | |
; Be careful to observe who reads and writes to avoid race conditions! | |
rATSendByte :1 ; byte to send to keyboard | |
rATSendFlags :1 ; flags for the send routine | |
; Keyboard buffer pointers. | |
rKeyQIn :1 ; in | |
rKeyQOut :1 ; out | |
endc | |
; The keyboard buffer. | |
cblock 0x40 | |
rKeyQFirst :QUEUE_SIZE ; circular queue | |
endc | |
;* Boot entry. | |
; ========== | |
Start org 0x0000 ;program starts at location 000 | |
call SetUp | |
goto Main ; start the routine | |
subtitle "Interrupt servicer" | |
page | |
; Interrupt servicing routine. | |
; ---------------------------- | |
; | |
; We interrupt on the AT keyboard clock bit high-to-low transition. | |
; Spurious interrupts are possible, so check and discard them. | |
; | |
org 0x0004 | |
proc IntServ | |
movwf rIntSaveW | |
swapf STATUS,w ; movfw changes the Z status so can't use | |
bank0 | |
movwf rIntSaveS ; save the status word | |
; When we come in here, we've triggered on a high-to-low transition of the | |
; AT keyboard clock. If the keyboard is sending data to us, the clock | |
; will be low and the data line will be low also (start bit). If we're | |
; sending data to the keyboard, the data line will be low because we | |
; pulled it low. | |
keep | |
btfss GPIO,bnKeyData ; if keyboard data high, ignore | |
btfsc GPIO,bnKeyClock ; if keyboard clock high, ignore | |
goto IntServExit ; ignore the interrupt | |
unkeep | |
; See if we're asking to send a byte. If we get a collision, | |
; (a byte coming in when we're trying to send), it's not fatal. | |
bbs rATSendFlags,bnATSendReq,IntServ20 ; if sending | |
movif 8,rIntKCount ; bit counter | |
clrc ; clear carry | |
clrf rIntKData ; clear accumulated bytes | |
; Wait for the clock to transit high, then low (skip the start bit). | |
IntServ2: | |
stallc GPIO,bnKeyClock ; wait for clock to rise | |
stalls GPIO,bnKeyClock ; and wat for clock to fall again | |
; Get the data bits | |
keep | |
btfsc GPIO,bnKeyData ; skip if a 0 bit | |
setc ; set a 1 bit | |
unkeep | |
rrf rIntKData,f ; the keyboard data bits | |
dbnz rIntKCount,IntServ2 ; for next bit | |
; We still have two bits to go, parity and stop. We skip them. | |
stallc GPIO,bnKeyClock ; wait for clock to rise | |
stalls GPIO,bnKeyClock ; and fall again (skip parity bit) | |
stallc GPIO,bnKeyClock ; wait for clock rise | |
stalls GPIO,bnKeyClock ; and fall again (skip stop bit) | |
; We special-case ACK. Because it occurs after a command has been | |
; sent, we can't put it into the queue or the routine sending the | |
; command may never see it. | |
movfw rIntKData ; get data just received | |
xorlw ATSC_ACK ; see if ACK | |
bnz IntServ14 ; skip. | |
bsf rATSendFlags,bnACKSeen ; say we saw it | |
goto IntServExit ; don't put it in the queue | |
; Now, add the assembled byte to the buffer. Note that if IN+1 == OUT | |
; there's no room in the buffer, so we toss the keystroke. | |
IntServ14: | |
incf rKeyQIn,w ; advance | |
andlw 0x5f ; enforce a wrap-around | |
movwf rIntTemp ; save it | |
sublw rKeyQOut ; see if equal | |
bz IntServExit ; if queue full, drop the character | |
; N.B.: If we see that we have problems with overflow, we can drive the | |
; keyboard clock low and essentially shut things off until we have a | |
; chance to empty the queue. | |
movff FSR,rIntSaveFSR ; save current FSR | |
movff rIntKData,INDF ; store the byte | |
movff rIntTemp,rKeyQIn ; update the "in" pointer | |
movff rIntSaveFSR,FSR ; restore FSR | |
goto IntServExit ; all done | |
; This is where we send code to the AT Keyboard. | |
; At entry here, the clock line has just been released and | |
; the edge of the keyboard-generated clock brings us here. | |
IntServ20: | |
bcf rATSendFlags,bnACKSeen ; say we haven't seen ACK | |
bcf rATSendFlags,bnATSendReq ; clear request flag | |
movif 8,rIntKCount ; how many bits to send out | |
clrf rIntTemp ; parity counter | |
IntServ22: | |
rrf rATSendByte,f ; get a bit | |
bc IntServ24 ; if a 1 | |
bcf GPIO,bnKeyData ; set a zero | |
goto IntServ26 ; keep going | |
IntServ24: | |
bsf GPIO,bnKeyData ; set a 1 | |
incf rIntTemp,f ; count parity | |
IntServ26: | |
stallc GPIO,bnKeyClock ; wait for clock to rise | |
; The keyboard samples the bit while the clock is high. | |
stalls GPIO,bnKeyClock ; wait for clock to fall | |
nop | |
nop | |
nop | |
nop ; kill a few microseconds | |
dbnz rIntKCount,IntServ22 ; for next bit | |
; We need to add the parity bit. If we have an even number of | |
; 1 bits in the data stream, we add a 1 bit, otherwise, a zero. | |
keep | |
btfsc rIntTemp,0 ; skip if even number of bits | |
bcf GPIO,bnKeyData ; add 1 | |
btfss rIntTemp,0 ; skip if odd number of bits | |
bsf GPIO,bnKeyData ; add 0 | |
unkeep | |
stallc GPIO,bnKeyClock ; wait for clock to rise | |
; The parity bit is sampled here. | |
stalls GPIO,bnKeyClock ; wait for clock to fall | |
; Now, float the data line. The keyboard will drop the data | |
; line after the clock rises to signal acceptance. | |
nop | |
nop ; kill 2 uS | |
bank1 | |
bsf TRISIO,bnKeyData ; set data line to input | |
bank0 | |
stallc GPIO,bnKeyClock ; wait for high clock | |
; Wait for the device to bring data low. | |
clrf TMR1L | |
movif (256-5),TMR1H ; set about 20 msec | |
bcf PIR1,TMR1IF ; clear interupt flag | |
IntServ32: | |
bbs PIR1,TMR1IF,IntServ40 ; if timeout | |
bbs GPIO,bnKeyData,IntServ32; stall until data low | |
; Data is low, wait for clock to fall. | |
IntServ34: | |
bbs PIR1,TMR1IF,IntServ40 ; if timeout | |
bbs GPIO,bnKeyClock,IntServ34 ; wait around | |
; Now wait for a high clock. | |
IntServ36: | |
bbs PIR1,TMR1IF,IntServ40 ; if timeout, just quit | |
bbc GPIO,bnKeyClock,IntServ36 ; wait for rising clock | |
; The keyboard responded as expected, if we get here. | |
; Most keyboards (but not all) should respond with an ACK character. | |
IntServ40: | |
; Return from interrupt. | |
IntServExit: | |
bcf INTCON,INTF ; clear the old interrupt | |
bsf INTCON,INTE ; re-enable interrupts | |
swapf rIntSaveS,w | |
movwf STATUS ; restore status | |
swapf rIntSaveW,f | |
swapf rIntSaveW,w | |
retfie ; return from interrupt | |
endproc IntServ | |
subtitle "Read/write key code routines" | |
page | |
;* ReadKey - Read a key from the queue. | |
; ------------------------------------ | |
; | |
; If there are no keystrokes in the buffer, stall until | |
; some arrive. Returns next keystroke in rATChar and Z clear, | |
; or Z set if no character. | |
; | |
proc ReadKey | |
ReadKey2: | |
movfw rKeyQOut | |
movwf FSR ; prepare for fetching | |
xorwf rKeyQIn,w | |
bz ReadKey4 ; return Z set if empty | |
; Retrieve the keystroke. | |
movff INDF,rATChar ; get the key | |
incf rKeyQOut,w ; advance | |
andlw 0x5f ; wrap-around | |
movwf rKeyQOut ; update pointer | |
clrz ; say we have one | |
ReadKey4: | |
return | |
endproc ReadKey | |
;* SendXTBit - Send a bit to the XT keyboard port. | |
; ----------------------------------------------- | |
; | |
; Note that bits are clocked by the XT on the high-to-low | |
; transition of the clock. This routine strobes the bit | |
; onto the XT data line, waits for 50 usec, then drops | |
; the clock line for 50 usec, then raises it and exits. | |
; | |
; On entry, the data bit is in C (carry). | |
; | |
proc SendXTBit | |
keep | |
skpnc | |
bsf GPIO,bnHostData ; if send a 1 | |
skpc | |
bcf GPIO,bnHostData ; if send a 0 | |
unkeep | |
movlw 50/4 ; 50 usec | |
bcf GPIO,bnHostClock ; drop the clock line | |
movlw 50/4 | |
call ShortDelay | |
bsf GPIO,bnHostClock ; raise clock | |
return | |
endproc SendXTBit | |
;* SendXTByte - Send a byte to the XT. | |
; ----------------------------------- | |
; | |
; Inserts a start bit, waits for XTClock and XTData to go high before | |
; sending. | |
; | |
; Byte to send is in rXTByte. | |
; | |
; We need to frame things accurately, so data bit transitions are made | |
; in the center of the clock-low bit cell. | |
; | |
; The XT interface is very simple. Bits are shifted into an 74LS322 shift | |
; register. When the start bit reaches the high-order bit of the shift | |
; register, it sets an interrupt, disables shifting and pulls data low one | |
; shift time later. In other words, the start bit is shifted out. | |
; The bit order is LSB first for 8 data bits. After that, the interface | |
; is essentially blind until the PC has picked up the character. | |
; | |
; Many keyboards start out by strobing a low "pseudo-start" bit, then | |
; follow with a high "real start". Apparently, this results in more | |
; stable performace. We'll do the same thing. | |
; | |
; Clock is held low by the PC as an inhibit--the keyboard should (and | |
; cannot) send data until both data and clock lines have been allowed | |
; to go high. | |
; | |
; The actual data rate only needs to be slower than the processor clock, | |
; as a conditioning circuit formed by two flip-flops is clocked from | |
; that to avoid any problems that might arise with ringing. | |
; | |
proc SendXTByte | |
; Wait for high level on XTClock--and XTData. | |
SendXTByte2: | |
keep | |
btfsc GPIO,bnHostClock ; loop if clock low | |
btfss GPIO,bnHostData ; loop if data low | |
goto SendXTByte2 | |
unkeep | |
; Put XTClock and XTData into output mode with clock and data high. | |
bsf GPIO,bnHostClock | |
bsf GPIO,bnHostData | |
bank1 | |
bcf TRISIO,bnHostClock ; enable clock output | |
bcf TRISIO,bnHostData ; enable data output | |
bank0 | |
; Start off with a 0 bit, then a 1 bit. | |
keep | |
clrc | |
call SendXTBit | |
setc | |
call SendXTBit | |
unkeep | |
; Now, send the remaining 8 bits of the scan code. | |
movif 8,rXTBitCount ; number of bits to send | |
SendXTByte4: | |
rrf rXTByte,f | |
call SendXTBit ; send, starting with the LSbit | |
dbnz rXTBitCount,SendXTByte4 ; loop | |
; Raise the data line, pause, then set the clock and data to input. | |
bsf GPIO,bnHostData | |
; At this point, the PC will be driving data low until the interrupt | |
; has been serviced, so set data and clock to input. | |
bank1 | |
bsf TRISIO,bnHostData ; disable data | |
bsf TRISIO,bnHostClock ; disable clock output | |
bank0 | |
return | |
endproc SendXTByte | |
;* SendATByte - Send a byte to the keyboard. | |
; ----------------------------------------- | |
; | |
; This is tricky. We have to take the AT keybaord | |
; clock low for more than 60 uS, drop the data line, then | |
; let the clock go. | |
; | |
; The interrupt routine will pick up when the next clock hits, | |
; as long as rATSendByte is nonzero--whence our kludge. | |
; | |
; W has the data we want to send. | |
; | |
proc SendATByte | |
bcf INTCON,INTE ; inhibit clock interrupt | |
movwf rATSendByte ; save it for the interrupt routine | |
movlw 15/4 ; more than 1 bit time | |
call ShortDelay | |
stallc GPIO,bnKeyClock ; stall until keyboard clock high | |
bcf GPIO,bnKeyClockPD ; pull the clock low | |
; Stall with the clock low to prevent keyboard from starting a new | |
; character. We also have to mask the clock interrupt, since we're | |
; pulling the clock low. | |
movlw 100/4 | |
call ShortDelay ; stall for 100 uS | |
bcf INTCON,T0IF ; clear interrupt flag which was set | |
bank1 | |
bcf TRISIO,bnKeyData ; set key data to output | |
bank0 | |
bcf GPIO,bnKeyData ; bring data low | |
movlw 30/4 ; more than 1 bit time | |
call ShortDelay | |
bcf rATSendFlags,bnACKSeen ; clear the "ACK seen" flag | |
bsf rATSendFlags,bnATSendReq ; say we have a byte | |
bsf INTCON,INTE ; enable interrupt | |
bsf GPIO,bnKeyClockPD ; let keyboard clock float high | |
; We stall until the byte gets sent. We wait for an ACK--if we | |
; don't get it within 20 milliseconds, we proceed as if we got it | |
; anyway. | |
clrf TMR1L | |
movif (256-5),TMR1H ; set about 20 msec | |
bcf PIR1,TMR1IF ; clear interupt flag | |
SendATByte4: | |
bbs PIR1,TMR1IF,SendATByte8 ; if timeout | |
bbc rATSendFlags,bnACKSeen,SendATByte4 ; loop until ACK set | |
; We timed out here, or we got an ACK. so we set the data to input. | |
SendATByte8: | |
bank1 | |
bsf TRISIO,bnKeyData ; set data to input--in case it's not | |
bank0 | |
return ; all done | |
endproc SendATByte | |
subtitle "Miscellaneous routines." | |
page | |
;* ShortDelay - Delay less than 1 msec. | |
; ------------------------------------ | |
; | |
; On entry, W has delay time in 4 microsecond units. | |
; Resolution is about 4-8 uS. | |
; | |
proc ShortDelay | |
sublw 255 | |
movwf TMR0 | |
bcf INTCON,T0IF ; clear interrupt flag | |
stallc INTCON,T0IF ; stall until interrupt set | |
return | |
endproc ShortDelay | |
;* CheckLockKey - Check for Caps, Num and Scroll Lock. | |
; --------------------------------------------------- | |
; | |
; Just toggles the state of the LEDs. The XT scan code (make) | |
; is in W. | |
; | |
proc CheckLockKey | |
movwf rTemp1 ; save the code | |
keep | |
btfsc rTemp1,7 ; if it's not a break code | |
return | |
unkeep | |
movlw XTSC_CAPSLOCK | |
xorwf rTemp1,w | |
bnz CheckLockKey2 ; if not capslock | |
movlw bvATCapsLock | |
xorwf rATFlags,f ; toggle caps lock | |
goto UpdateLED ; go update LED status | |
CheckLockKey2: | |
movlw XTSC_SCRLOCK | |
xorwf rTemp1,w | |
bnz CheckLockKey4 ; if not scroll lock | |
movlw bvATScrollLock | |
xorwf rATFlags,f ; toggle scroll lock | |
goto UpdateLED ; update the LED status | |
CheckLockKey4: | |
movlw XTSC_NUMLOCK | |
xorwf rTemp1,w | |
bnz CheckLockKey10 ; if not numlock | |
movlw bvATNumLock | |
xorwf rATFlags,f ; toggle num lock | |
goto UpdateLED ; update the LED status | |
CheckLockKey10: | |
return ; all done | |
endproc CheckLockKey | |
;* UpdateLED - Update the LEDs. | |
; --------------------------- | |
; | |
; Done all at once. | |
; | |
proc UpdateLED | |
movlw ATSC_SETLED ; set/reset LED | |
call SendATByte ; send the command | |
movfw rATFlags ; get LED status | |
andlw 7 ; isolate | |
call SendATByte ; return state | |
return ; exit | |
endproc UpdateLED | |
;* PollHost - See what the XT is doing. | |
; ------------------------------------ | |
; | |
; At some point, the XT host will drop the keyboard clock | |
; line for 20 mS or more. When this happens, we need to | |
; simulate a keyboard reset. | |
; | |
; We also clear all of our keyboard flags (and eventually reset | |
; the AT keyboard). | |
; | |
proc PollHost | |
bbs GPIO,bnHostClock,PollHost10 ; exit if clock high | |
; Clock has gone low; time it. | |
clrf TMR1L | |
movif (256-5),TMR1H ; set about 10 msec | |
bcf PIR1,TMR1IF ; clear interupt flag | |
stallc GPIO,bnHostClock ; stall unit XT Clock high | |
; Clock is high again--see for how long. | |
bbc PIR1,TMR1IF,PollHost10 ; not long enough, just exit | |
; Send out a "Diagnostics passed byte" | |
movif XTSC_OKAY,rXTByte | |
call SendXTByte ; send it | |
; Reset the AT keyboard also. | |
movlw ATSC_RESET | |
call SendATByte ; send a reset to the AT keyboard | |
clrf rATFlags ; clear flags | |
call UpdateLED ; update LEDs | |
PollHost10: | |
return | |
endproc PollHost | |
subtitle "Scan code translation" | |
page | |
;* Keyboard translation lookup. | |
; | |
; We put the translation subroutine here, as there's no chance of | |
; crossing a page boundary. | |
include "xttrans.inc" ; keycode translator | |
subtitle "Main routine" | |
page | |
;* Main Loop. | |
; ---------- | |
; | |
; Here's the control flow: | |
; | |
; a. Check the host--has the clock been pulled low for more than | |
; 10 mS.? If so, issue a reset to the AT keyboard and clear | |
; our shift LED status and send an 0xaa to the host. | |
; b. See if an AT keystroke is available. If so, look at it-- | |
; discard E0 and E1 keystrokes, as they don't apply to the | |
; smaller keyboard of the XT. Discard any other keycodes | |
; with bit 7 set, as they're probably messages (such as AA) | |
; that the XT host can't process. Note the occurrence of F0 | |
; (key up) keystrokes. Translate whatever's left. | |
; c. If any of the translated keycodes are shift-lock releases | |
; update the corresponding LED. | |
; d. Send the keystroke (with key-up modifier) to the host. | |
; e. Go to (a) | |
; | |
proc Main | |
Main2: | |
call PollHost ; poll the host interface | |
; Main loop. Sit around and cycle until there's data in the | |
; AT keyboard receive buffer. | |
Main4: | |
call ReadKey ; read something from the buffer | |
bz Main2 ; no data | |
movfw rATChar ; get the key | |
addlw 256-KEY_TABLE_LENGTH ; check to see if less than 90h | |
bnc Main6 ; if less | |
movfw rATChar ; get the byte again | |
movwf rXTByte ; in case it's an E0 0.93 | |
xorlw 0xe0 ; check for E0 0.93 | |
keep ; 0.93 | |
bbs GPIO,bnPassE0,Main5 ; see if we need to pass E0 code 0.93 | |
bz Main7 ; go send it without filtering 0.93 | |
Main5: | |
unkeep ; 0.93 | |
xorlw 0xe0 ^ 0xf0 ; restore character 0.93 | |
keep | |
skpnz | |
bsf rATFlags,bnATRelease ; key is released | |
unkeep | |
goto Main4 ; get next keystroke | |
; Something resembling a normal keycode has arrived. | |
Main6: | |
movfw rATChar | |
call TransATXT ; translate | |
iorlw 0 ; test for zero | |
bz Main8 ; skip if undefined | |
keep | |
btfsc rATFlags,bnATRelease | |
iorlw 128 ; add the release flag | |
unkeep | |
movwf rXTByte | |
call CheckLockKey ; check for lock key | |
Main7: | |
call SendXTByte ; send it | |
Main8: | |
bcf rATFlags,bnATRelease ; clear key-up flag | |
goto Main2 ; keep going | |
endproc Main | |
subtitle "Initialization code" | |
page | |
;* Setup - Boot setup tasks. | |
; ------------------------- | |
; | |
; Set up I/O ports, timers and interrupts. | |
; Initialize variables. | |
; | |
proc SetUp | |
bank1 | |
; Option Register settings: | |
; Weak pullup on GPIO enabled | |
; Interrupt on falling edge of GP2 | |
; Use CPU clock | |
; Increment on high-to-low transistion | |
; Assign prescaler to TMR0 | |
; Prescale clock by 4 | |
OPTIONS equ (1<<T0SE)+(1<<PS0) | |
movif OPTIONS,OPTION_REG ; /4 counter, internal clock | |
; to TMR, enable pullups, GP2INT falling | |
; Just in case there's no keyboard or PC attached, we enable weak | |
; pullups on all pins to keep them from floating. | |
movif (bvKeyClock+bvKeyData+bvPassE0+bvHostClock+bvHostData),WPU | |
; Initially, the only outputs that are enabled are for the AT clock | |
; pulldown. Everything else is input. | |
movif (bvKeyClock+bvKeyData+bvHostData+bvHostClock),TRISIO | |
; Calibrate the internal oscillator | |
call 0x3ff ; get the calibration value | |
movwf OSCCAL ; calibrate oscillator | |
; The following line should be uncommented if a 12F675 is being used. | |
; It disables the AD converter and sets GP2 to digital mode. | |
; clrf 0x1f ; ANSEL (12F675 only) | |
bank0 | |
movif 7,CMCON ; disable comparator | |
; Initialize variables. | |
movif rKeyQFirst,rKeyQIn | |
movwf rKeyQOut ; set IN=OUT on keyboard queue | |
clrf rATFlags ; LED status, key-up, etc. | |
clrf rATSendFlags ; clear flags | |
clrf rATSendByte ; clear the host-to-keyboard flag | |
; Set the outputs to a known state. | |
movif bvKeyClockPD+bvHostData+bvHostClock,GPIO | |
; Timer0 is used as a sub-1 mS timer, while timer1 is used for | |
; millisecond intervals (mostly as a deadman). | |
; Timer 0 Control Settings: | |
; | |
; Prescale by 4 | |
; Use internal clock | |
; Timer 1 enabled | |
TIMER1_CONTROL equ (1<<T1CKPS1)+(1<<TMR1ON) | |
movif TIMER1_CONTROL,T1CON ; TIMER1 internal, osc, /4, enabled | |
movif (1<<GIE)+(1<<INTE),INTCON ; GP2 interrupts | |
bsf T1CON,TMR1ON ; start timer 1 | |
return ; exit | |
endproc Setup | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; AT-XT translation routine. | |
; -------------------------- | |
; | |
; Given an AT code, return an XT code. | |
; Enter with W = AT code | |
; Exit with W = XT code or 0 if no mapping | |
; Uses rTemp1 | |
; | |
KEY_TABLE_LENGTH equ 0x90 ; number of entries in the table | |
if ($ & 255) > (256-(KEY_TABLE_LENGTH + 5)) ; it'll cross a page boundary | |
if (256 - ($ & 255)) > 32 | |
messg "More than 32 bytes of padding added before translate table -" | |
messg "You may want to move it." | |
endif | |
org ($ & 0xff00) + 256 | |
endif ; move to the next page | |
TransATXT: | |
movwf rTemp1 ; save index temporarily | |
movlw high TransTable | |
movwf PCLATH ; establish table high address | |
movfw rTemp1 ; get the index | |
addwf PCL,f ; computed jump | |
TransTable: | |
retlw 0 ; AT Code 00, Undefined | |
retlw 0x43 ; AT Code 01, Key 120 | |
retlw 0 ; AT Code 02, Undefined | |
retlw 0x3f ; AT Code 03, Key 116 | |
retlw 0x3d ; AT Code 04, Key 114 | |
retlw 0x3b ; AT Code 05, Key 112 | |
retlw 0x3c ; AT Code 06, Key 113 | |
retlw 0x58 ; AT Code 07, Key 123 | |
retlw 0 ; AT Code 08, Undefined | |
retlw 0x44 ; AT Code 09, Key 121 | |
retlw 0x42 ; AT Code 0a, Key 119 | |
retlw 0x40 ; AT Code 0b, Key 117 | |
retlw 0x3e ; AT Code 0c, Key 115 | |
retlw 0x0f ; AT Code 0d, Key 16 | |
retlw 0x29 ; AT Code 0e, Key 1 | |
retlw 0 ; AT Code 0f, Undefined | |
retlw 0 ; AT Code 10, Undefined | |
retlw 0x38 ; AT Code 11, Key 60 | |
retlw 0x2a ; AT Code 12, Key 44 | |
retlw 0 ; AT Code 13, Undefined | |
retlw 0x1d ; AT Code 14, Key 58 | |
retlw 0x10 ; AT Code 15, Key 17 | |
retlw 0x02 ; AT Code 16, Key 2 | |
retlw 0 ; AT Code 17, Undefined | |
retlw 0 ; AT Code 18, Undefined | |
retlw 0 ; AT Code 19, Undefined | |
retlw 0x2c ; AT Code 1a, Key 46 | |
retlw 0x1f ; AT Code 1b, Key 32 | |
retlw 0x1e ; AT Code 1c, Key 31 | |
retlw 0x11 ; AT Code 1d, Key 18 | |
retlw 0x03 ; AT Code 1e, Key 3 | |
retlw 0 ; AT Code 1f, Undefined | |
retlw 0 ; AT Code 20, Undefined | |
retlw 0x2e ; AT Code 21, Key 48 | |
retlw 0x2d ; AT Code 22, Key 47 | |
retlw 0x20 ; AT Code 23, Key 33 | |
retlw 0x12 ; AT Code 24, Key 19 | |
retlw 0x05 ; AT Code 25, Key 5 | |
retlw 0x04 ; AT Code 26, Key 4 | |
retlw 0 ; AT Code 27, Undefined | |
retlw 0 ; AT Code 28, Undefined | |
retlw 0x39 ; AT Code 29, Key 61 | |
retlw 0x2f ; AT Code 2a, Key 49 | |
retlw 0x21 ; AT Code 2b, Key 34 | |
retlw 0x14 ; AT Code 2c, Key 21 | |
retlw 0x13 ; AT Code 2d, Key 20 | |
retlw 0x06 ; AT Code 2e, Key 6 | |
retlw 0 ; AT Code 2f, Undefined | |
retlw 0 ; AT Code 30, Undefined | |
retlw 0x31 ; AT Code 31, Key 51 | |
retlw 0x30 ; AT Code 32, Key 50 | |
retlw 0x23 ; AT Code 33, Key 36 | |
retlw 0x22 ; AT Code 34, Key 35 | |
retlw 0x15 ; AT Code 35, Key 22 | |
retlw 0x07 ; AT Code 36, Key 7 | |
retlw 0 ; AT Code 37, Undefined | |
retlw 0 ; AT Code 38, Undefined | |
retlw 0 ; AT Code 39, Undefined | |
retlw 0x32 ; AT Code 3a, Key 52 | |
retlw 0x24 ; AT Code 3b, Key 37 | |
retlw 0x16 ; AT Code 3c, Key 23 | |
retlw 0x08 ; AT Code 3d, Key 8 | |
retlw 0x09 ; AT Code 3e, Key 9 | |
retlw 0 ; AT Code 3f, Undefined | |
retlw 0 ; AT Code 40, Undefined | |
retlw 0x33 ; AT Code 41, Key 53 | |
retlw 0x25 ; AT Code 42, Key 38 | |
retlw 0x17 ; AT Code 43, Key 24 | |
retlw 0x18 ; AT Code 44, Key 25 | |
retlw 0x0b ; AT Code 45, Key 11 | |
retlw 0x0a ; AT Code 46, Key 10 | |
retlw 0 ; AT Code 47, Undefined | |
retlw 0 ; AT Code 48, Undefined | |
retlw 0x34 ; AT Code 49, Key 54 | |
retlw 0x35 ; AT Code 4a, Key 55 | |
retlw 0x26 ; AT Code 4b, Key 39 | |
retlw 0x27 ; AT Code 4c, Key 40 | |
retlw 0x19 ; AT Code 4d, Key 26 | |
retlw 0x0c ; AT Code 4e, Key 12 | |
retlw 0 ; AT Code 4f, Undefined | |
retlw 0 ; AT Code 50, Undefined | |
retlw 0 ; AT Code 51, Undefined | |
retlw 0x28 ; AT Code 52, Key 41 | |
retlw 0 ; AT Code 53, Undefined | |
retlw 0x1a ; AT Code 54, Key 27 | |
retlw 0x0d ; AT Code 55, Key 13 | |
retlw 0 ; AT Code 56, Undefined | |
retlw 0 ; AT Code 57, Undefined | |
retlw 0x3a ; AT Code 58, Key 30 | |
retlw 0x36 ; AT Code 59, Key 57 | |
retlw 0x1c ; AT Code 5a, Key 43 | |
retlw 0x1b ; AT Code 5b, Key 28 | |
retlw 0 ; AT Code 5c, Undefined | |
retlw 0x2b ; AT Code 5d, Key 42 | |
retlw 0 ; AT Code 5e, Undefined | |
retlw 0 ; AT Code 5f, Undefined | |
retlw 0 ; AT Code 60, Undefined | |
retlw 0x56 ; AT Code 61, Key 45 | |
retlw 0 ; AT Code 62, Undefined | |
retlw 0 ; AT Code 63, Undefined | |
retlw 0 ; AT Code 64, Undefined | |
retlw 0 ; AT Code 65, Undefined | |
retlw 0x0e ; AT Code 66, Key 15 | |
retlw 0 ; AT Code 67, Undefined | |
retlw 0 ; AT Code 68, Undefined | |
retlw 0x4f ; AT Code 69, Key 93 | |
retlw 0 ; AT Code 6a, Undefined | |
retlw 0x4b ; AT Code 6b, Key 92 | |
retlw 0x47 ; AT Code 6c, Key 91 | |
retlw 0 ; AT Code 6d, Undefined | |
retlw 0 ; AT Code 6e, Undefined | |
retlw 0 ; AT Code 6f, Undefined | |
retlw 0x52 ; AT Code 70, Key 99 | |
retlw 0x53 ; AT Code 71, Key 104 | |
retlw 0x50 ; AT Code 72, Key 98 | |
retlw 0x4c ; AT Code 73, Key 97 | |
retlw 0x4d ; AT Code 74, Key 102 | |
retlw 0x48 ; AT Code 75, Key 96 | |
retlw 0x01 ; AT Code 76, Key 110 | |
retlw 0x45 ; AT Code 77, Key 90 | |
retlw 0x57 ; AT Code 78, Key 122 | |
retlw 0x4e ; AT Code 79, Key 106 | |
retlw 0x51 ; AT Code 7a, Key 103 | |
retlw 0x4a ; AT Code 7b, Key 105 | |
retlw 0x37 ; AT Code 7c, Key 100 | |
retlw 0x49 ; AT Code 7d, Key 101 | |
retlw 0x46 ; AT Code 7e, Key 125 | |
retlw 0 ; AT Code 7f, Undefined | |
retlw 0 ; AT Code 80, Undefined | |
retlw 0 ; AT Code 81, Undefined | |
retlw 0 ; AT Code 82, Undefined | |
retlw 0x41 ; AT Code 83, Key 118 | |
retlw 0 ; AT Code 84, Undefined | |
retlw 0 ; AT Code 85, Undefined | |
retlw 0 ; AT Code 86, Undefined | |
retlw 0 ; AT Code 87, Undefined | |
retlw 0 ; AT Code 88, Undefined | |
retlw 0 ; AT Code 89, Undefined | |
retlw 0 ; AT Code 8a, Undefined | |
retlw 0 ; AT Code 8b, Undefined | |
retlw 0 ; AT Code 8c, Undefined | |
retlw 0 ; AT Code 8d, Undefined | |
retlw 0 ; AT Code 8e, Undefined | |
retlw 0 ; AT Code 8f, Undefined | |
if ($-TransTable) != KEY_TABLE_LENGTH | |
error "Translation table is the wrong length!" | |
endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment