From 0ce40f8d4e3ded5b6f80a810d33eae532d84c634 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Mon, 24 Oct 2022 03:35:32 -0400 Subject: initial commit (v0.0.3) --- io.s | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 io.s (limited to 'io.s') diff --git a/io.s b/io.s new file mode 100644 index 0000000..98dc219 --- /dev/null +++ b/io.s @@ -0,0 +1,203 @@ + ; 20220930 bkw, aka Urchlay on libera IRC, aka urchlay@slackware.uk: + + ; Example code for calling CIO through the back door, like BASIC does. + ; Provided under the terms of the WTFPL: Do WTF you want to with this. + + ; Verbose documentation here. To skip to the actual code, search for + ; three ; characters. + + ; There's a lot of old code that calls the OS ROM's print-character + ; and read-character addresses directly. These were never published + ; by Atari as part of their API... and in fact they changed between + ; the 400/800 and XL/XE, which is a major reason why certain software + ; is "OS B only" or "XL only". People coming from other platforms such + ; at the C=64 or Apple II were used to their ROMs having fixed + ; addresses to JSR to, for print-character and read-character, and + ; so they used $F6A4 to print and $F6E2 to read... which were + ; never guaranteed by Atari not to change. And they did change, + ; in the XL OS. + + ; The pedantically correct way to print a character is to set up + ; a 1-byte buffer for IOCB #0, and call CIOV with ICCOM set to + ; $09 (aka 'put record'). Even Atari decided this was too much + ; work, so they also provided a handy "put-one-byte" vector in + ; EDITRV, which gets copied to ICPTL/H when the OS opens the E: + ; device... BASIC uses this to print characters, and you can, too. It + ; works on any revision of the Atari OS, because it's part of the + ; OS specification: if it *didn't* work on some OS version, neither + ; would Atari BASIC, which would count as a show-stopper! + + ; Atari didn't provide a similar slot in the IOCB for the + ; get-one-byte vector... and generally, if you're interested in + ; reading input one character at a time, you don't want IOCB #0 (E:) + ; anyway. You want the K: device (which returns immediately after + ; each keypress, rather than waiting for a whole line of input). The + ; correct way to read from the keyboard is to open an IOCB (other + ; than #0) to the K: device, set up that IOCB, including a 1-byte + ; buffer, and call CIOV with ICCOM set to $05 (aka get-record). But + ; it turns out that the K: device has a get-one-byte routine that (a) + ; can be found in a published location (KEYBDV table) that doesn't + ; change with ROM revision, and (b) works without even having an IOCB + ; open for K:. + + ; The vectors are stored as "address minus one", because they're + ; intended to be called via the RTS instruction (probably Atari did + ; this because the JSR instruction doesn't have an indexed mode like + ; JMP does). Read on, to see how to call them. The calling sequence + ; isn't as convenient as the illegal entry points (or the Commodore's + ; Kernal, which does publish print-acumulator and get-1-byte + ; vectors), but it's a lot less code than the 'proper' IOCB setup + ; would be. And if you copy/paste from this file, you just call these + ; subroutines in your code (as convenient as the Commodore). + + ; You are welcome to copy the code in this file into your own + ; project. It's unencumbered: I release it under the WTFPL. I would + ; just say it's public domain, but I have been told by people who + ; ought to know that some countries don't actually recognize public + ; domain in their law. WTFPL explicitly says you can do whatever you + ; want with this. + + ; Examples: + + ; You could make your own "memo pad mode" with this: + ; + ; main: + ; jsr getchr + ; jsr printchr + ; jmp main + + ; Print a null-terminated string, up to 256 bytes long: + ; + ; ldx #0 + ; msgloop: + ; lda message,x + ; beq msgdone + ; jsr printchrx + ; inx + ; bne msgloop + ; msgdone: + ; rts ; or whatever other code goes here... + ; + ; message: .byte "Hello, World!",$9b,$00 + + ; Environment: + + ; The code depends on a few symbols (equates) being defined. How you + ; do this depends on the assembler you're using. + + ; .include "atari.inc" ; for ca65 + ; .include "sysequ.m65" ; for atasm + ; include atari8.h ; dasm, if it actually had this file :( + + ; If your assembler doesn't have a file of Atari symbols, use: + ; ICPTL = $0346 + ; ICPTH = $0347 + ; KEYBDV = $E420 + ; EDITRV = $E400 ; only if you change getchr to use this. + ; ...of course your assembler might want EQU or .EQU instead of = signs. + + ; .org ; your assembler may want org without the dot, or *= + + ;;; Start of actual code. + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; 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: 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. -- cgit v1.2.3