io.asm (8040B)
1 ;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free 2 ;;; software under the GNU GPL v3 license or any later version. See 3 ;;; COPYING in the root directory for details. 4 5 ;;; Serial IO and string manipulation 6 7 io .namespace 8 9 .if SYMON 10 acia_base = $8800 11 .else 12 acia_base = $c000 13 .endif 14 acia_data_reg = acia_base 15 acia_status_reg = acia_base + $1 16 acia_cmd_reg = acia_base + $2 17 acia_ctrl_reg = acia_base + $3 18 19 .section zero_page 20 ;; Temporary zero page variables; 21 ;; only used in subroutine 22 puts_str: .word ? 23 gets_len: .byte ? 24 gets_str: .word ? 25 .send zero_page 26 27 .section rom 28 29 ;;; init_acia 30 ;;; Initialize acai for communication 31 ;;; Input: 32 ;;; - 33 ;;; Output: 34 ;;; - 35 ;;; Changes: 36 ;;; a, acai registers 37 init_acia: 38 .block 39 ;; Reset acai 40 sta io.acia_status_reg 41 lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit 42 sta io.acia_ctrl_reg 43 ;; No parity, no echo, no interrupts, DTR ready 44 lda #%11001011 45 sta io.acia_cmd_reg 46 rts 47 .bend 48 49 ;;; ---------------------------------------------------------- 50 ;;; STANDARD LIBRARY FUNCTIONS 51 ;;; 52 ;;; These functions may be used by user programs. Labels 53 ;;; marked with 'EXPORT' are exported to liba.h. When 54 ;;; the user program imports this header file, the functions 55 ;;; are available. 56 ;;; ---------------------------------------------------------- 57 58 ;;; puts 59 ;;; Send up to 256 characters terminated by null via acia 60 ;;; Input: 61 ;;; puts_str, put_str+1: 62 ;;; 2 bytes on zero page containing string address 63 ;;; Output: 64 ;;; - 65 ;;; Changes: 66 ;;; a, x, y, puts_str, put_str+1 67 puts: 68 .block 69 ;; Send string terminated by '\0' 70 ldy #$00 71 _puts_loop: 72 lda (io.puts_str),y 73 beq _puts_end 74 phy 75 jsr io.putc 76 ply 77 iny 78 jmp _puts_loop 79 _puts_end: 80 rts 81 .bend 82 83 ;;; putc 84 ;;; Send character via acia 85 ;;; Input: 86 ;;; a: 87 ;;; Character to be printed 88 ;;; Output: 89 ;;; - 90 ;;; Changes: 91 ;;; x, acai registers 92 putc: 93 .block 94 ;; Send character. 95 ;; Due to bugs in the register and interrupt 96 ;; handling of the WDC 65C02, we have to use 97 ;; a manual delay. 98 sta io.acia_data_reg 99 .switch CLOCK_SPEED 100 .case 8 ; 8 Mhz Clock 101 SERIAL_SEND_DELAY_X = $c3 102 SERIAL_SEND_DELAY_Y = $04 103 .case 4 ; 4 Mhz Clock 104 SERIAL_SEND_DELAY_X = $c3 105 SERIAL_SEND_DELAY_Y = $02 106 .case 2 ; 2 Mhz Clock 107 SERIAL_SEND_DELAY_X = $c2 108 SERIAL_SEND_DELAY_Y = $01 109 .default ; 1 Mhz Clock 110 SERIAL_SEND_DELAY_X = $88 111 SERIAL_SEND_DELAY_Y = $01 112 .endswitch 113 ldy #SERIAL_SEND_DELAY_Y 114 outer_loop: 115 ldx #SERIAL_SEND_DELAY_X 116 inner_loop: 117 dex 118 bne inner_loop 119 dey 120 bne outer_loop 121 rts 122 .bend 123 124 ;;; puth 125 ;;; Convert byte to hex and send via acia 126 ;;; Input: 127 ;;; a: 128 ;;; Byte to be printed 129 ;;; Output: 130 ;;; - 131 ;;; Changes: 132 ;;; a, x, acai registers, c-flag 133 puth: 134 .block 135 ;; Send byte a as hex number 136 pha 137 lsr a 138 lsr a 139 lsr a 140 lsr a 141 jsr io.puth_nibble 142 pla 143 jsr io.puth_nibble 144 rts 145 .bend 146 147 ;;; puth_nibble 148 ;;; Convert lower half of byte to hex and send via acia 149 ;;; Input: 150 ;;; a: 151 ;;; Nibble to be printed 152 ;;; Output: 153 ;;; - 154 ;;; Changes: 155 ;;; a, x, acai registers, c-flag 156 puth_nibble: 157 .block 158 ;; Print hex digit 159 clc 160 and #$0F 161 adc #$30 ; Decimal number 162 cmp #$3A ; >10 ? 163 bmi _puth_putc 164 adc #$26 165 _puth_putc: 166 jsr io.putc 167 rts 168 .bend 169 170 ;;; putd 171 ;;; Output decimal number 172 ;;; Input: 173 ;;; a: 174 ;;; The number 175 ;;; Output: 176 ;;; - 177 ;;; Changes: 178 ;;; a, x, y, acai registers, c-flag 179 putd: 180 .block 181 ldy #$00 182 first_digit: 183 cmp #100 184 bcc first_digit_set 185 sec 186 sbc #100 187 iny 188 jmp first_digit 189 first_digit_set: 190 ldx #$00 191 pha 192 tya 193 beq skip_first_leading_zero 194 jsr io.puth_nibble 195 ldx #$01 ; Force printing of zeros in rest of term 196 skip_first_leading_zero: 197 pla 198 second_digit: 199 cmp #10 200 bcc second_digit_set 201 sec 202 sbc #10 203 iny 204 jmp second_digit 205 second_digit_set: 206 pha 207 tya 208 cpx #$01 209 beq do_not_skip_potential_second_leading_zero 210 cmp #$00 211 beq skip_second_leading_zero 212 do_not_skip_potential_second_leading_zero: 213 jsr io.puth_nibble 214 skip_second_leading_zero: 215 pla 216 jmp io.puth_nibble 217 .bend 218 219 220 ;;; putnl 221 ;;; Send newline via acia 222 ;;; Input: 223 ;;; - 224 ;;; Output: 225 ;;; - 226 ;;; Changes: 227 ;;; a 228 putnl: 229 .block 230 lda #$0d 231 jsr io.putc 232 lda #$0a 233 jsr io.putc 234 rts 235 .bend 236 237 ;;; getc 238 ;;; Read character from acia. Blocks until character is read 239 ;;; Input: 240 ;;; - 241 ;;; Output: 242 ;;; a: 243 ;;; Character read 244 ;;; Changes: 245 ;;; a, acai registers 246 getc: 247 .block 248 ;; Read character from acia 249 lda io.acia_status_reg 250 and #%00001000 251 beq io.getc 252 lda io.acia_data_reg 253 rts 254 .bend 255 256 ;;; getc_seed_rng 257 ;;; Like getc, but also updates lsfr 258 ;;; This is our only source of randomness right now: 259 ;;; While we are busy waiting for a character, lsfr is 260 ;;; updated. 261 ;;; Input: 262 ;;; - 263 ;;; Output: 264 ;;; a: 265 ;;; Character read 266 ;;; Changes: 267 ;;; a, acai registers, lsfr_state 268 getc_seed_rng: 269 .block 270 ;; Read character from acia 271 ;; We also use the time between keystrokes 272 ;; as entropy source for the RNG 273 jsr lfsr.step 274 lda io.acia_status_reg 275 and #%00001000 276 beq io.getc_seed_rng 277 lda io.acia_data_reg 278 rts 279 .bend 280 281 getc_nonblocking: 282 .block 283 ;; Non-blocking read: If 284 ;; character has been read, 285 ;; it is returned in A and 286 ;; C is cleared. If not, 287 ;; C is set 288 sec 289 lda io.acia_status_reg 290 and #%00001000 291 beq nothing_read 292 clc 293 lda io.acia_data_reg 294 nothing_read: 295 rts 296 .bend 297 298 ;;; gets 299 ;;; Read up to 255 characters terminated by CR via acia 300 ;;; Input: 301 ;;; gets_len: 302 ;;; Max. length of input. 303 ;;; gets_str, gets_str+1: 304 ;;; 2 bytes on zero page containing destination address 305 ;;; +-------------------------------------------------------+ 306 ;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! | 307 ;;; +-------------------------------------------------------+ 308 ;;; Note that a terminating zero is added to the string. 309 ;;; Therefore gets_len+1 bytes may be written! 310 ;;; Output: 311 ;;; Zero terminated string at gets_str, gets_str+1 312 ;;; Changes: 313 ;;; a, y, ACAI registers 314 gets: 315 .block 316 ;; Read string terminated by CR 317 ldy #$00 318 loop: 319 phy 320 jsr io.getc 321 jsr io.putc 322 ply 323 cmp #$0d ; GOT CR? 324 beq terminate_string ; if not 325 sta (io.gets_str), y ; store character 326 iny ; if max length 327 cpy io.gets_len ; not reached, 328 bne loop ; continue loop. 329 terminate_string: 330 lda #$00 331 sta (io.gets_str), y 332 rts 333 .bend 334 335 ;;; str2byte 336 ;;; Convert number (<= 255) given as string to byte. 337 ;;; Expects zero-terminated string on stack 338 ;;; Input: 339 ;;; Zero-terminated string on stack 340 ;;; Output: 341 ;;; a: 342 ;;; Byte value 343 ;;; Changes: 344 ;;; a, x, y 345 str2byte: 346 .block 347 jsr ds.create_stack_frame 348 #ds.PUSH $04 ; Local variable (string of length four) 349 lda #$00 350 pha 351 ldy #$00 352 add_digit: 353 lda (ds.frame_ptr),y 354 beq done 355 sec ; Convert next digit 356 sbc #'0' ; from ascii to 357 sta (ds.frame_ptr),y ; number. 358 pla ; Multiply current 359 jsr io.multiply_by_ten ; accumulator by 10. 360 clc ; Add new 361 adc (ds.frame_ptr),y ; digit. 362 pha 363 iny ; Advance to next digit 364 jmp add_digit 365 done: 366 pla 367 #ds.sta_LOCAL 0 368 jsr ds.delete_stack_frame 369 .bend 370 371 ;;; multiply_by_ten 372 ;;; Multiply A by ten. 373 ;;; Input: 374 ;;; a: Number 375 ;;; Output: 376 ;;; a: Number * 10 377 ;;; Changes: 378 ;;; a, c 379 multiply_by_ten: 380 .block 381 sta (ds.ptr) 382 lda #$00 383 clc 384 .for i = 0, i < 10, i += 1 385 adc (ds.ptr) 386 .next 387 rts 388 .bend 389 390 .send rom 391 .endn