-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathmain.asm
468 lines (390 loc) · 15.3 KB
/
main.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
; PS/2 Keyboard Controller (for PIC16F627A)
;
; Troy's HBC-56 homebrew computer
;
; Copyright (c) 2021 Troy Schrapel
;
; This code is licensed under the MIT license
;
; https://github.com/visrealm/hbc-56
PROCESSOR 16F627A
#include <xc.inc>
CONFIG FOSC = HS ; Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN)
CONFIG WDTE = OFF ; Watchdog Timer Enable bit (WDT disabled)
CONFIG PWRTE = OFF ; Power-up Timer Enable bit (PWRT disabled)
CONFIG MCLRE = ON ; RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
CONFIG BOREN = OFF ; Brown-out Detect Enable bit (BOD disabled)
CONFIG LVP = OFF ; Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
CONFIG CPD = OFF ; Data EE Memory Code Protection bit (Data memory code protection off)
CONFIG CP = OFF ; Flash Program Memory Code Protection bit (Code protection off)
; PIC16F627A
; PINOUT
; +----------+
; DAT_RDY (O) - RA2 ==| 1 18 |== RA1 - PS/2 DATA (I)
; _INT (O) - RA3 ==| 2 17 |== RA0 - PS/2 CLOCK (I)
; NEXT_CODE (I) - RA4 ==| 3 16 |== RA7 - XTAL 1
; _RESET (I) - RA5 ==| 4 15 |== RA6 - XTAL 2
; GND ==| 5 14 |== VCC
; SCANCODE0 (O) - RB0 ==| 6 13 |== RB7 - SCANCODE7 (O)
; SCANCODE1 (O) - RB1 ==| 7 12 |== RB6 - SCANCODE6 (O)
; SCANCODE2 (O) - RB2 ==| 8 11 |== RB5 - SCANCODE5 (O)
; SCANCODE3 (O) - RB3 ==| 9 10 |== RB4 - SCANCODE4 (O)
; +----------+
;
; NEXT_CODE - Positive edge-triggered input which triggers the controller
; to output the next scancode. DAT_RDY becomes low until that next
; scancode is ready on RB
#define CLOCK_PIN RA0
#define CLOCK_IO TRISA0
#define DATA_PIN RA1
#define DATA_IO TRISA1
#define DAT_RDY_PIN RA2
#define DAT_RDY_IO TRISA2
#define _INT_PIN RA3
#define _INT_IO TRISA3
CPUFREQ equ 20000000 ; CPU frequency (20MHz)
DELAY_US_COUNT equ (CPUFREQ / 10000000)
BYTE_BITS equ 8
BUFFER_BITS equ 4 ; 2^4 bytes in buffer
BUFFER_SIZE equ (1 << BUFFER_BITS) ; (16)
BUFFER_MASK equ (BUFFER_SIZE - 1) ; 0x0F
PS2_TEST_PASS equ 0xAA
; ------------------------------------------------------------------------------
; udata - variables
; ------------------------------------------------------------------------------
PSECT udata
delay_us: ds 1
delay_us_counter: ds 1
buffer: ds BUFFER_SIZE ; circular buffer
bufferHead: ds 1 ; circular buffer head pointer
bufferTail: ds 1 ; circular buffer tail pointer
rxByteTmp: ds 1 ; temporary storage for received byte
txByteTmp: ds 1 ; temporary storage for sent byte
loopVar: ds 1 ; looping variable
; ------------------------------------------------------------------------------
; bank0 - Switch to bank0
; ------------------------------------------------------------------------------
bank0 macro
bcf RP0 ; we don't use bank 1/2, so only need to clear RP0
bsf T0IE ; enable interrupt
endm
; ------------------------------------------------------------------------------
; bank1 - Switch to bank1
; ------------------------------------------------------------------------------
bank1 macro
bcf T0IE ; disable interrupt
bsf RP0 ; we don't use bank 1/2, so only need to set RP0
endm
; ------------------------------------------------------------------------------
; skipIfQueueNotEmpty - Skip the next instruction if the queue is not empty
; ------------------------------------------------------------------------------
skipIfQueueNotEmpty macro
movf bufferTail,w
xorwf bufferHead,w ; xor rather than subtract to avoid setting carry
btfsc ZERO
endm
; ------------------------------------------------------------------------------
; clearOutput - Clear the output register
; ------------------------------------------------------------------------------
clearOutput macro
clrf PORTB
endm
; ------------------------------------------------------------------------------
; writeOutput - Write W to the output register
; ------------------------------------------------------------------------------
writeOutput macro
movwf PORTB
endm
; ------------------------------------------------------------------------------
; resetTimer
; ------------------------------------------------------------------------------
resetTimer macro
clrf TMR0 ; Set timer to 255 (one increment will trigger interrupt)
decf TMR0
bcf T0IF ; Clear Timer0 interrupt flag
endm
; ------------------------------------------------------------------------------
; skipIfOutputEmpty - Skip the next instruction if the output is empty
; ------------------------------------------------------------------------------
skipIfOutputEmpty macro
movf PORTB,w ; check current OE value
btfss ZERO ; if not zero
endm
PSECT resetVec,class=CODE,delta=2
; ------------------------------------------------------------------------------
; resetVec - entry point
; ------------------------------------------------------------------------------
resetVec:
goto main
PSECT intVec,class=CODE,delta=2
; ------------------------------------------------------------------------------
; intVec - interrupt handler
; ------------------------------------------------------------------------------
intVec:
bcf DAT_RDY_PIN ; clear data ready
clearOutput ; clear output
; set up timer again
resetTimer
retfie
PSECT code
; ------------------------------------------------------------------------------
; main - program entry
; ------------------------------------------------------------------------------
main:
call init ; initialise the registers
call waitForKbSelfTest ; wait for the keyboard
resetTimer
bsf GIE ; Global interrupt enable
call loop ; enter the main loop
; ------------------------------------------------------------------------------
; init - initialise the MCU registers
; ------------------------------------------------------------------------------
init:
clrf STATUS
bank0
clrf PORTA ; Initialize GPIO by clearing output
clrf PORTB ; Initialize GPIO by clearing output
bsf _INT_PIN ; Turn off interrupt out
movlw CMCON_CM_MASK ; Turn comparators off
movwf CMCON
bank1
bcf GIE ; Global interrupt disable
clrf TRISA ; Initialize GPIO by setting all pins as output
clrf TRISB ; Initialize GPIO by setting all pins as output
bsf CLOCK_IO ; set Clock as floating (input)
bsf DATA_IO ; set Data as floating (input)
bsf TRISA4 ; Timer0 clock pin (input)
bsf TRISA5 ; Reset pin (input)
bsf T0CS ; Timer0 external clock mode (counter mode)
bcf T0SE ; Timer0 increment on rising edge
bcf T0IF ; Clear Timer0 interrupt flag
bank0
clrf bufferHead ; clear variables
clrf bufferTail
clrf rxByteTmp
clrf txByteTmp
return
; ------------------------------------------------------------------------------
; waitForKbSelfTest - Wait for Kb self test code and reply
; ------------------------------------------------------------------------------
; My Perixx PERIBOARD-409 releatedly sends 0xAA until a response is received
; from the host. This subroutine waits for 0xAA and sends a response.
; ------------------------------------------------------------------------------
waitForKbSelfTest:
movlw 255
call delayUs
call readByte ; read a byte into rxByteTmp
movf rxByteTmp,w
xorlw PS2_TEST_PASS ; Self-test pass?
btfss ZERO ; loop again if not zero
goto waitForKbSelfTest
movlw 200
call delayUs
call pullClockUp ; initiate a send
call pullDataUp
movlw 40
call delayUs
call pullClockDown
movlw 200
call delayUs
call pullDataDown
call releaseClock
; Note: due to my keyboard not sending clock signals??? we just wait
; and return
nop
nop
call pullDataUp
movlw 180
call delayUs
call releaseData
call longDelay
return
; ------------------------------------------------------------------------------
; loop - main program loop
; ------------------------------------------------------------------------------
loop:
btfsc DAT_RDY_PIN ; skip if data is ready... already
goto checkForKeyboardInput
call qPopFront ; get the received scancode
btfsc ZERO ; was queue empty?
goto checkForKeyboardInput
writeOutput ; output it
bsf DAT_RDY_PIN ; data is now ready to be read
skipIfQueueNotEmpty
bsf _INT_PIN ; clear interrupt
checkForKeyboardInput:
btfsc CLOCK_PIN ; check if clock is low
goto loop ; if it's not, skip the read
call readByte
call qPushBack
dataIsReady:
bcf _INT_PIN ; Interrupt
movlw 100
call delayUs
goto loop
; ------------------------------------------------------------------------------
; qPushBack - Push data to end of queue
; ------------------------------------------------------------------------------
; Inputs: rxByteTmp - value to push
; ------------------------------------------------------------------------------
qPushBack:
movlw buffer ; set up FSR to buffer tail
bcf CARRY
addwf bufferTail,w
movwf FSR
movf rxByteTmp,w ; write rxByteTmp to buffer
movwf INDF
incf bufferTail ; increment bufferTail pointer
movlw BUFFER_MASK ; roll around if pointer
andwf bufferTail,f ; past end
return
; ------------------------------------------------------------------------------
; qPopFront - Pop data from the front of the queue
; ------------------------------------------------------------------------------
; Returns: Value returned in W
; 0 if queue is empty
; ------------------------------------------------------------------------------
qPopFront:
clrw
skipIfQueueNotEmpty ; return if queue is empty
return
movlw buffer ; set up FSR to buffer head
bcf CARRY
addwf bufferHead,w
movwf FSR
incf bufferHead ; increment bufferHead pointer
movlw BUFFER_MASK ; roll around if pointer
andwf bufferHead,f ; past end
movf INDF,w ; output value to W
return
; ------------------------------------------------------------------------------
; sendByte - send a byte of data over PS/2 interface
; ------------------------------------------------------------------------------
; Inputs: W - value to send
; ------------------------------------------------------------------------------
sendByte:
movwf txByteTmp ; set up temp value
call pullClockDown ; pull the clock line down
movlw 25 ; wait 100us
call delayUs
call pullDataDown
call releaseClock
nop
nop
call pullDataUp ; data high
movlw BYTE_BITS ; set up loop variable (8 bits)
movwf loopVar
sendBitLoop: ; send a bit
rrf rxByteTmp ; rotate the value to get bit 0 in CARRY
call waitForClockLow ; wait for clock low
call pullDataDown
btfsc CARRY ; skip if CARRY is 0
call pullDataUp
call waitForClockHigh
decfsz loopVar ; next bit?
goto sendBitLoop
call releaseData
return
; ------------------------------------------------------------------------------
; readByte - read a byte from the PS/2 interface
; ------------------------------------------------------------------------------
; Returns: rxByteTmp - value read
; ------------------------------------------------------------------------------
readByte:
call waitForClockLow ; read start bit
clrf rxByteTmp
movlw BYTE_BITS ; set up loop (8 bits)
movwf loopVar
call waitForClockHigh
readBitLoop: ; read a bit
bcf CARRY ; clear carry
call waitForClockLow
btfsc DATA_PIN ; skip if DATA is 0
bsf CARRY ; set CARRY (if data 1)
call waitForClockHigh
rrf rxByteTmp ; rotate CARRY into temp value
decfsz loopVar ; next bit?
goto readBitLoop
return
; ------------------------------------------------------------------------------
; waitForClockHigh - loops until the CLOCK input pin is high
; ------------------------------------------------------------------------------
waitForClockHigh:
btfss CLOCK_PIN
goto waitForClockHigh
return
; ------------------------------------------------------------------------------
; waitForClockLow - loops until the CLOCK input pin is low
; ------------------------------------------------------------------------------
waitForClockLow:
btfsc CLOCK_PIN
goto waitForClockLow
return
; ------------------------------------------------------------------------------
; pullClockDown - set the CLOCK pin to an output (value 0)
; ------------------------------------------------------------------------------
pullClockDown:
bank1
bcf CLOCK_IO
bank0
bcf CLOCK_PIN
return
; ------------------------------------------------------------------------------
; pullClockUp - set the CLOCK pin to an input (floating/pulled high)
; ------------------------------------------------------------------------------
pullClockUp:
releaseClock:
bank1
bsf CLOCK_IO
bank0
return
; ------------------------------------------------------------------------------
; pullDataDown - set the DATA pin to an output (value 0)
; ------------------------------------------------------------------------------
pullDataDown:
bank1
bcf DATA_IO
bank0
bcf DATA_PIN
return
; ------------------------------------------------------------------------------
; pullDataUp - set the DATA pin to an input (floating/pulled high)
; ------------------------------------------------------------------------------
pullDataUp:
releaseData:
bank1
bsf DATA_IO
bank0
return
; ------------------------------------------------------------------------------
; longDelay - a long delay...
; ------------------------------------------------------------------------------
longDelay:
movlw 255
call delayUs
movlw 255
call delayUs
movlw 255
call delayUs
movlw 255
call delayUs
movlw 255
call delayUs
return
; ------------------------------------------------------------------------------
; delayUs - delay a number of microseconds
; ------------------------------------------------------------------------------
; Inputs: W - number of microseconds
; ------------------------------------------------------------------------------
delayUs:
movwf delay_us
delayUs1:
movlw DELAY_US_COUNT
movwf delay_us_counter
delayUs2:
decfsz delay_us_counter,f
goto delayUs2
decfsz delay_us,f
goto delayUs1
return
END resetVec