Skip to content

Commit 4fd5ae0

Browse files
authored
getchar() implementation for osi-c1p target (#43)
* Port keyboard reading function from cc65 * Minor cleanup and clarifications Put state of keyboard polling routine into dedicated section. Adjust size of storage region for keyboard variables. * Cleanup assembler code Removed assignments to register X that were made due to cc65 calling convention. The only return value used is stored in __CHARBUF. Always store return value in __CHARBUF. * More idiomatic assignment of zero-page sections Improved according to review comment: #43 (comment) * Invoke __kbhit via C function call Improve readability.
1 parent aee65c9 commit 4fd5ae0

File tree

5 files changed

+279
-14
lines changed

5 files changed

+279
-14
lines changed

mos-platform/osi-c1p/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_platform_library(osi-c1p-c
1919
abort.c
2020
putchar.cc
2121
getchar.c
22+
kbhit.s
2223
)
2324
target_include_directories(osi-c1p-c SYSTEM BEFORE PUBLIC .)
2425

mos-platform/osi-c1p/crt0.s

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
; Assembler crt0 file to clear the screen before the
2-
; call to main.
2+
; call to main and to initialize the keyboard reading
3+
; code.
34

45
.section .init.40,"axR",@progbits
6+
jsr __initkbhit
57
jsr __clrscr
68

mos-platform/osi-c1p/getchar.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
#include <stdio.h>
22
#include <stdlib.h>
33

4-
int getchar() {
5-
puts("getchar() not yet implemented");
6-
abort();
4+
extern void __kbhit(void);
5+
extern unsigned char volatile __CHARBUF;
6+
7+
int getchar(void)
8+
{
9+
do
10+
{
11+
__kbhit();
12+
} while (!__CHARBUF);
13+
14+
int const result = __CHARBUF;
15+
16+
__CHARBUF = 0;
17+
18+
return result;
719
}

mos-platform/osi-c1p/kbhit.s

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
; Copyright 2022 LLVM-MOS Project
2+
;
3+
; Licensed under the Apache License, Version 2.0 (the "License");
4+
; you may not use this file except in compliance with the License.
5+
; You may obtain a copy of the License at
6+
;
7+
; http://www.apache.org/licenses/LICENSE-2.0
8+
;
9+
; Unless required by applicable law or agreed to in writing, software
10+
; distributed under the License is distributed on an "AS IS" BASIS,
11+
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
; See the License for the specific language governing permissions and
13+
; limitations under the License.
14+
;
15+
; This module is based on the corresponding cc65 osic1p kbhit.s module as of
16+
; commit 5aa75ae81f07724ef8c16d61e42b74a20296acae, and was modified for the
17+
; use in llvm-mos-sdk:
18+
;
19+
; https://github.com/cc65/cc65/blob/5aa75ae81f07724ef8c16d61e42b74a20296acae/libsrc/osic1p/kbhit.s.
20+
;
21+
; The original cc65 license was:
22+
;
23+
; This software is provided 'as-is', without any express or implied warranty.
24+
; In no event will the authors be held liable for any damages arising from
25+
; the use of this software.
26+
;
27+
; Permission is granted to anyone to use this software for any purpose,
28+
; including commercial applications, and to alter it and redistribute it
29+
; freely, subject to the following restrictions:
30+
;
31+
; 1. The origin of this software must not be misrepresented; you must not
32+
; claim that you wrote the original software. If you use this software in
33+
; a product, an acknowledgment in the product documentation would be
34+
; appreciated but is not required.
35+
;
36+
; 2. Altered source versions must be plainly marked as such, and must not
37+
; be misrepresented as being the original software.
38+
;
39+
; 3. This notice may not be removed or altered from any source distribution.
40+
41+
42+
; The method to detect a pressed key is based on the documentation in
43+
; "Section 3 Programmed Key Functions" in "The Challenger Character Graphics
44+
; Reference Manual"
45+
; We only want to return true for characters that can be returned by getchar(),
46+
; but not for keys like <Shift> or <Ctrl>. Therefore a special handling is
47+
; needed for the first row. This is implemented by a bit mask that is stored
48+
; in KBDTMP and that is set to zero after the first round.
49+
50+
; Put keyboard state into dedicated zero-page storage.
51+
;
52+
; "za" attribute means:
53+
; "z": allocate in page zero
54+
; "a": section is "allocatable", must be allocated a runtime address
55+
.section .zp.kbd,"za",@nobits
56+
57+
; Internal state that needs to be preserved across calls.
58+
; The getchar() function uses __CHARBUF to retrieve
59+
; the return value.
60+
.global __CHARBUF
61+
__CHARBUF: .ds.b 1 ; Character buffer
62+
LASTSCAN: .ds.b 1 ; Result of previous keyboard scan
63+
DBNCCNT: .ds.b 1 ; Debounce counter
64+
KBDTMP: .ds.b 1 ; Temporary values
65+
CTRLSHIFT: .ds.b 1 ; State of CTRL and SHIFT keys
66+
67+
KBD = $DF00 ; Polled keyboard register
68+
69+
; Initialize one-character buffer that is filled by kbhit().
70+
; Must be called before main().
71+
.section .text.initkbhit
72+
.global __initkbhit
73+
__initkbhit:
74+
lda #$00
75+
sta __CHARBUF ; No character in buffer initially
76+
sta LASTSCAN ; Initialize keyboard state
77+
sta DBNCCNT
78+
sta KBDTMP
79+
sta CTRLSHIFT
80+
81+
rts
82+
83+
; Routine to get character from keyboard and return it in A.
84+
; Based on the OSI ROM routine at $FD00 but uses different
85+
; storage locations.
86+
87+
.section .text.kbhit
88+
.global __kbhit
89+
__kbhit:
90+
lda __CHARBUF ; Check for previously saved character
91+
beq LFD05
92+
rts ; A contains non-zero character code meaning true
93+
LFD05: lda #$80 ; Bit mask for initial keyboard row
94+
LFD06: jsr LFCBE ; Write keyboard row
95+
jsr LFCC6 ; Read keyboard column
96+
bne LFD13 ; Branch if a key in this column was pressed
97+
lsr a ; Otherwise shift mask to next row
98+
bne LFD06 ; If not done yet, check next row
99+
beq LFD3A ; Branch if last row reached and no key pressed
100+
LFD13: lsr a ; Have a key press. Shift LSB into carry
101+
bcc LFD1F ; Branch if no key pressed in column 0
102+
txa ; Key pressed in row zero. Get the column data
103+
and #$20 ; Mask only the bit for <ESC> as it is the only key in row zero that returns key press
104+
beq LFD3A ; Branch if <ESC> was not the key
105+
lda #$1B ; Set character to <ESC>
106+
bne LFD50 ; Do more processing
107+
LFD1F: jsr LFE86 ; Shift to find bit that is set (in Y)
108+
tya ; Get bit
109+
sta KBDTMP ; Save it
110+
asl a ; Multiply by 7 by shifting left three times (X8)...
111+
asl a
112+
asl a
113+
sec ; ...then subtracting one
114+
sbc KBDTMP
115+
sta KBDTMP ; Save value*7 for later lookup in table
116+
txa ; Get the keyboard column
117+
lsr a ; Shift out bit zero (only key there is <SHIFT LOCK>)
118+
asl a ; And shift back
119+
jsr LFE86 ; Shift to find bit that is set (in Y)
120+
beq LFD47 ; Branch if no keys pressed
121+
lda #$00
122+
LFD3A: sta CTRLSHIFT ; Save state of <CTRL> and shift keys
123+
LFD3D: sta LASTSCAN
124+
lda #$02 ; Count used for key debouncing
125+
sta DBNCCNT
126+
lda #$00 ; Return false
127+
sta __CHARBUF
128+
rts
129+
LFD47: clc
130+
tya ; Get bit number of pressed key
131+
adc KBDTMP ; Add previously calculated offset for keyboard row*7
132+
tay
133+
lda LFF3B,y ; Read ASCII code for key from table
134+
LFD50: cmp LASTSCAN ; Debounce - same as last key scan?
135+
bne LFD3D ; If not, try again
136+
dec DBNCCNT ; Decrement debounce counter
137+
beq LFD5F ; Branch if done debouncing
138+
jsr LFCDF ; Wait for short delay to debounce keyboard
139+
beq __kbhit ; Go back and scan keyboard.
140+
LFD5F: ldx #$64 ; Was <CONTROL> key down?
141+
cmp CTRLSHIFT
142+
bne LFD68 ; Branch if not
143+
ldx #$0F
144+
LFD68: stx DBNCCNT
145+
sta CTRLSHIFT
146+
cmp #$21
147+
bmi LFDD0 ; Done, return key
148+
cmp #$5F
149+
beq LFDD0 ; Done, return key
150+
lda #$01
151+
jsr LFCBE ; Write keyboard row
152+
jsr LFCCF ; Read keyboard column
153+
sta KBDTMP
154+
tax
155+
and #$06
156+
bne LFDA2
157+
bit LASTSCAN
158+
bvc LFDBB
159+
txa
160+
eor #$01
161+
and #$01
162+
beq LFDBB
163+
lda #$20
164+
bit KBDTMP
165+
bvc LFDC3
166+
lda #$C0
167+
bne LFDC3
168+
LFDA2: bit LASTSCAN
169+
bvc LFDAA
170+
txa
171+
beq LFDBB
172+
LFDAA: ldy LASTSCAN
173+
cpy #$31
174+
bcc LFDB9
175+
cpy #$3C
176+
bcs LFDB9
177+
lda #$F0
178+
bne LFDBB
179+
LFDB9: lda #$10
180+
LFDBB: bit KBDTMP
181+
bvc LFDC3
182+
clc
183+
adc #$C0
184+
LFDC3: clc
185+
adc LASTSCAN
186+
and #$7F
187+
bit KBDTMP
188+
bpl LFDD0
189+
ora #$80
190+
LFDD0: sta KBDTMP ; Save pressed key and return in __CHARBUF
191+
sta __CHARBUF
192+
rts
193+
194+
; Write keyboard row with value in A.
195+
; Invert the bits before writing.
196+
; Returns original value of A.
197+
198+
LFCBE: eor #$FF
199+
sta KBD
200+
eor #$FF
201+
rts
202+
203+
; Read keyboard column and return in X.
204+
; Sets Z flag if no keys were pressed.
205+
; Saves current value of A.
206+
207+
LFCC6: pha ; Save A
208+
jsr LFCCF ; Read keyboard column
209+
tax ; Save in X
210+
pla ; Restore A
211+
dex ; Decrement and then increment to
212+
inx ; preserve value of X but set flags
213+
rts
214+
215+
; Read keyboard column.
216+
; Invert the bits (pressed key(s) will show up as ones).
217+
218+
LFCCF: lda KBD ; Read keyboard hardware
219+
eor #$FF ; Invert the bits
220+
rts
221+
222+
; Short fixed delay routine.
223+
224+
LFCDF: ldy #$10
225+
LFCE1: ldx #$40
226+
LFCE3: dex
227+
bne LFCE3
228+
dey
229+
bne LFCE1
230+
rts
231+
232+
; Shift A left until we find a 1 in the most significant bit.
233+
; Return the bit number in Y.
234+
235+
LFE86: ldy #$08
236+
LFE88: dey
237+
asl a
238+
bcc LFE88
239+
rts
240+
241+
; Lookup table of keyboard keys for each scan row.
242+
LFF3B: .byte $BD
243+
.byte 'P', ';', '/', ' ', 'Z', 'A', 'Q'
244+
.byte ',', 'M', 'N', 'B', 'V', 'C', 'X'
245+
.byte 'K', 'J', 'H', 'G', 'F', 'D', 'S'
246+
.byte 'I', 'U', 'Y', 'T', 'R', 'E', 'W'
247+
.byte $00, $00, $0D, $0A, 'O', 'L', '.'
248+
.byte $00, '_', '-', ':', '0', '9', '8'
249+
.byte '7', '6', '5', '4', '3', '2', '1'

mos-platform/osi-c1p/link.ld

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,27 @@
22
* OSI Challenger 1P Linker Script
33
* RAM size 32 kB (TODO make this configurable)
44
*/
5+
6+
/*
7+
* Provide 16 imaginary (zero page) registers located in 0x02 - 0x21 range.
8+
*/
9+
PROVIDE(__rc0 = 0x02);
10+
INCLUDE imag-regs.ld
11+
ASSERT(__rc0 == 0x02, "Inconsistent zero page map.")
12+
ASSERT(__rc31 == 0x21, "Inconsistent zero page map.")
513

614
MEMORY {
715
ram (rw) : ORIGIN = 0x200, LENGTH = 0x7E00
16+
zpstorage (rw): ORIGIN = __rc31 + 1, LENGTH = 0x05
817
}
918

1019
SECTIONS {
20+
.zp : { *(.zp) *(.zp.*) } > zpstorage
1121
INCLUDE c.ld
1222
}
1323

14-
/*
15-
* Provide 16 imaginary (zero page) registers located in 0x02 - 0x21 range.
16-
*/
17-
18-
PROVIDE(__rc0 = 0x02);
19-
INCLUDE imag-regs.ld
20-
ASSERT(__rc0 == 0x02, "Inconsistent zero page map.")
21-
ASSERT(__rc31 == 0x21, "Inconsistent zero page map.")
22-
2324
/* Set initial soft stack address to end of RAM area. (It grows down.) */
24-
/* TODO make dependent on RAM size */
25+
/* TODO make RAM size configurable */
2526
__stack = 0x7FFF;
2627

2728
OUTPUT_FORMAT {

0 commit comments

Comments
 (0)