aboutsummaryrefslogtreecommitdiff
path: root/io.s
blob: 7b63f95e1b37c9177ce93c1294ec8586ca5f8bb0 (plain)
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

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Subroutine: printchr
 ;
 ; Print ATASCII character in A, without preserving registers.
 ; Assumes IOCB #0 is opened on the E: device, which is how the
 ; Atari boots up. Uses "call-by-RTS" (weird looking but standard).
 ;
 ; Note that this will work even if the E: handler has been replaced,
 ; e.g. with COL80 or COL64 or such.
 ;
 ; Hint: if you want to print graphics instead of actual cursor controls
 ; or insert/delete/clear/etc, print an Escape character ($1B) before each,
 ; or set DSPFLG ($2FE) to a non-zero value.
 ;
printchr:
 tay        ; save A (character to print).
 lda ICPTH  ; set up stack, so it looks like a JSR to the
 pha        ;   put-one-byte address for E:,
 lda ICPTL  ;   which the OS has conveniently stashed
 pha        ;   in IOCB #0.
 tya        ; restore A (put-one-byte argument).
 rts        ; "return" to put-one-byte, which will return to printchr's caller.

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Subroutine: getchr
 ;
 ; Read ATASCII character from keyboard, return in A, without
 ; preserving registers.
 ;
 ; Uses the published and immutable KEYBDV address in the ROM, meaning
 ; it (a) doesn't require an IOCB open to the K: device, and (b)
 ; it will not use any replacement K: handler that might be loaded
 ; (however, unlike E:, replacing the OS K: device is so rare that
 ; I've never heard of it being done).
 ;
 ; Hint: This is a "blocking" function call: it waits until a key is
 ; pressed. If you want to poll (only read input when it's available),
 ; check CH ($02FC): if it's $FF, no key is pressed.
 ;
 ; Note: if you really do want to read from the E: device, change
 ; the two KEYBDV's below to EDITRV. E: will read an entire line,
 ; including editing (backspace, insert/delete, cursor moves, etc)
 ; the first time it's called, and return only the first character
 ; read. Further calls will return the rest of the characters, one at
 ; a time, with $9B (EOL) as the last one.
 ;
getchr:
 lda KEYBDV+5 ; set up stack, so it looks like a JSR to the
 pha          ;   get-one-byte address for K:, 
 lda KEYBDV+4 ;   which the OS ROM keeps in the
 pha          ;   KEYBDV table ($E420).
 rts          ; "return" to get-one-byte, which will return to getchr's caller.

 ; These next two are 'wrappers' for the above, which preserve
 ; the X register. Very convenient for use in a loop. If you don't
 ; need these, don't copy them into your code. If you do need them,
 ; remember that they call printchr and getchr, so you have to copy
 ; those also.

 ; Subroutine: Print A register in hex.
 ; Preserves X (but not A or Y).
printhex:
 pha           ; stash argument
 lsr           ; shift right 4 times,
 lsr           ;   to get the first hex digit
 lsr           ;   (aka nybble) into the bottom
 lsr           ;    4 bit positions.
 jsr printxdig ; print the top nybble.
 pla           ; restore original value...
 and #$0f      ; mask off high nybble
 ; fall through to print the 2nd digit.

 ; Subroutine: Print a nybble (A=0 to $0f) in hex.
printxdig:
 ora #$30 ; 0-9 now ASCII...
 cmp #$3a ; do we have A-F?
 bcc xok  ; if not, don't adjust it
 adc #$26 ; A-F now ASCII: $3a + $26 + 1 (carry always set) = $61 (a)
xok:
 ; fall through to print the digit.

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Subroutine: printchrx
 ;
 ; Print ATASCII character in A, preserving X register.
 ;
 ; Preserves X register (but nothing else), so it can be called from
 ; within a loop that uses X for a counter, without having to worry
 ; about it.
 ;
 ; On exit, A holds a copy of the X register, if you can think of
 ; a use for that.
 ;
 ; Calls printchr.
 ;
printchrx:
 tay          ; save A (character to print).
 txa          ; save X,
 pha          ;   on stack.
 tya          ; restore A.
 jsr printchr ; print the character.
 pla          ; restore X,
 tax          ;   from stack.
 rts          ; this a regular RTS (returns to printchrx's caller).

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Subroutine: getchrx
 ;
 ; Read ATASCII char from keyboard, return in A, preserving X register.
 ; Actually, the return value here is also in Y, if you can think of a
 ; use for that.
 ;
 ; Calls getchr.
 ;
getchrx:
 txa          ; save X,
 pha          ;   on stack.
 jsr getchr     ; get the character.
 tay          ; save A (our return value).
 pla          ; restore X,
 tax          ;   from stack.
 tya          ; restore return value to A.
 rts          ; regular RTS.

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Subroutine: printmsg
 ;
 ; Print the null-terminated message pointed to by A (low) and X (high).
 ; Limited to <= 256 character messages.
 ;
 ; Trashes all registers, plus uses FR0 for temp storage.
 ; Calls printchr.
 ;
printmsg:
 sta FR0
 stx FR0+1
 lda #0
 sta FR0+2
pmloop:
 ldy FR0+2
 lda (FR0),y
 beq pmdone
 jsr printchr
 inc FR0+2
 bne pmloop
pmdone:
 rts