/
Typematic.asm
284 lines (234 loc) · 7.36 KB
/
Typematic.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
; [The "BSD licence"]
; Copyright (c) 2009 Ben Gruver
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; 1. Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; 3. The name of the author may not be used to endorse or promote products
; derived from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
DSEG
;The initial typematic delay is TypematicInitialDelay * 50ms
TypematicInitialDelay: DS 01h
;The delay for typematic repeat is TypematicRepeatDelay1 * 10ms + TypematicRepeatDelay2 * 1ms
TypematicRepeatDelay1: DS 01h
TypematicRepeatDelay2: DS 01h
TimerInterval1: DS 01h ;the low byte of the current timer interval
TimerInterval2: DS 01h ;the high byte of the current timer interval
TimerTicksLeft: DS 01h ;how many ticks of the current interval type are left
TypematicButtonIndex: DS 01h ;the button to repeat
TypematicButtonMode: DS 01h ;the mode of the button to repeat
CSEG
ResetTypematicValues:
;set typematic rate to 10.9 CPS
MOV TypematicRepeatDelay1, #02h
MOV TypematicRepeatDelay2, #02h
;set typematic delay to 500ms
MOV TypematicInitialDelay, #0Ah
RET
StartTypematicDelayTimer:
;first, check if timer0's interrupts are enabled. If they are disabled,
;it means we're in the timer interrupt handler, and we shouldn't
;touch the timer
MOV C, ET0
JNC StartTypematicDelayTimerEnd
;set the timer for 50000
MOV TL0, #0B0h
MOV TH0, #03Ch
MOV TimerInterval1, #0B0h
MOV TimerInterval2, #03Ch
MOV TimerTicksLeft, TypematicInitialDelay
MOV TypematicButtonIndex, ButtonIndex
MOV TypematicButtonMode, Mode
;and enable the timer
SETB TR0
StartTypematicDelayTimerEnd:
RET
;Modifies:
; Registers:
; DPTR, A, B, R0, R1, R6, R7, PSW
; Memory:
; ButtonIndex, TimerTicksLeft, TimerInterval1, TimerInterval2, KeybordBuffer, KeybordBufferPosition
; Timer Registers/Flags
; TR0, TL0, TH0
; Ports:
; KBData, KBClock
TypematicTimerTick:
PUSH ACC
PUSH PSW
PUSH AR0
PUSH B
PUSH AR1
PUSH AR2
PUSH AR3
PUSH AR4
PUSH AR5
PUSH AR6
PUSH AR7
PUSH DPH
PUSH DPL
PUSH ButtonIndex
CALL _TypematicTimerTick
POP ButtonIndex
POP DPL
POP DPH
POP AR7
POP AR6
POP AR5
POP AR4
POP AR3
POP AR2
POP AR1
POP B
POP AR0
POP PSW
POP ACC
RET
_TypematicTimerTick:
;first, check TimerTicksLeft. If that is not 0, then all we have
;to do is put TimerInterval1 and TimerInterval2 into TL0 and TH0, and
;decrement TimerTicksLeft
CLR TR0
MOV A, TimerTicksLeft
JZ CheckTimerType
MOV TL0, TimerInterval1
MOV TH0, TimerInterval2
DEC TimerTicksLeft
SETB TR0
RET
CheckTimerType:
;we've completed this timer interval. What we do next depends
;on what type of timer interval we just finished. We can tell
;by looking at the low byte of the timer value
MOV A, TimerInterval1
HandleInitialDelayLapse:
;is this the initial delay timer?
CJNE A, #0B0h, HandleLongRepeatDelayLapse
;disable timer 0 interrupts - this is mainly to flag that we're
;in the interrupt handler
CLR ET0
;calculate the ButtonStates table index from the button index
MOV A, TypematicButtonIndex
RR A
ANL A, #07Fh
;get the byte containing the state for the key from ButtonStates
ADD A, #ButtonStates
MOV R0, A
MOV A, @R0
MOV R0, A
;we need to figure out which nibble of the byte we just retrieved
;contain the button state that we are interested in
MOV A, TypematicButtonIndex
ANL A, #00000001B
JNZ InitialDelay_CheckModOdd
;the button index is even, so we get the least significant nibble
MOV A, R0
ANL A, #00001111B
JMP InitialDelay_GotButtonStates
InitialDelay_CheckModOdd:
;the button index is even, so we get the most significant nibble
MOV A, R0
SWAP A
ANL A, #00001111B
InitialDelay_GotButtonStates:
;now the first (least significant) nibble of A contains the button state
;that we're interested in.
;if the button is no longer pressed, then stop the timer and don't send a key
JNZ InitialDelay_RepeatScancode
CLR TR0
SETB ET0
RET
InitialDelay_RepeatScancode:
;repeat the scancode
MOV A, TypematicButtonMode
MOV ButtonIndex, TypematicButtonIndex
;use the _internal procedure, which doesn't save/restore the registers,
;because we don't care if they get clobbered
CALL HandleButtonPress_internal
SETB ET0
;change to the long repeat delay (10000uS)
MOV TL0, #0F0h
MOV TH0, #0D8h
MOV TimerInterval1, #0F0h
MOV TimerInterval2, #0D8h
MOV TimerTicksLeft, TypematicRepeatDelay1
SETB TR0
RET
HandleLongRepeatDelayLapse:
;is this the long repeat delay?
CJNE A, #0F0h, HandleShortRepeatDelayLapse
;change to the short repeat delay (1000uS)
MOV TL0, #018h
MOV TH0, #0FCh
MOV TimerInterval1, #018h
MOV TimerInterval2, #0FCh
MOV TimerTicksLeft, TypematicRepeatDelay2
SETB TR0
RET
HandleShortRepeatDelayLapse:
;disable timer 0 interrupts - this is mainly to flag that we're
;in the interrupt handler
CLR ET0
;calculate the ButtonStates table index from the button index
MOV A, TypematicButtonIndex
RR A
ANL A, #07Fh
;get the byte containing the state for the key from ButtonStates
ADD A, #ButtonStates
MOV R0, A
MOV A, @R0
MOV R0, A
;we need to figure out which nibble of the byte we just retrieved
;contain the button state that we are interested in
MOV A, TypematicButtonIndex
ANL A, #00000001B
JNZ CheckModOdd
;the button index is even, so we get the least significant nibble
MOV A, R0
ANL A, #00001111B
JMP InitialDelay_GotButtonStates
CheckModOdd:
;the button index is even, so we get the most significant nibble
MOV A, R0
SWAP A
ANL A, #00001111B
GotButtonStates:
;now the first (least significant) nibble of A contains the button state
;that we're interested in.
;if the button is no longer pressed, then stop the timer and don't send a key
JNZ RepeatScancode
CLR TR0
SETB ET0
RET
RepeatScancode:
;repeat the scancode
MOV A, TypematicButtonMode
MOV ButtonIndex, TypematicButtonIndex
;use the _internal procedure, which doesn't save/restore the registers,
;because we don't care if they get clobbered
CALL HandleButtonPress_internal
SETB ET0
;change back to the long repeat delay (10000uS)
MOV TL0, #0F0h
MOV TH0, #0D8h
MOV TimerInterval1, #0F0h
MOV TimerInterval2, #0D8h
MOV TimerTicksLeft, TypematicRepeatDelay1
SETB TR0
RET