aboutsummaryrefslogtreecommitdiff
path: root/io.s
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2022-10-24 03:35:32 -0400
committerB. Watson <urchlay@slackware.uk>2022-10-24 03:35:32 -0400
commit0ce40f8d4e3ded5b6f80a810d33eae532d84c634 (patch)
tree5693eeddad62706819249539fdd0660b7442255b /io.s
downloaddla-asm-0ce40f8d4e3ded5b6f80a810d33eae532d84c634.tar.gz
initial commit (v0.0.3)
Diffstat (limited to 'io.s')
-rw-r--r--io.s203
1 files changed, 203 insertions, 0 deletions
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 <wherever> ; 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.