Jump to content
IGNORED

cc65 users: AtaSimpleIO


Recommended Posts

For those of you who use cc65 to program the Atari 800, I have a library called AtaSimpleIO.  It is a library to handle text display and keyboard input.  It is a minimal library that displays text with a minimum of processing.  Basically, it calls the ROM directly.  I don't remember the size of the Atari 8 version, but the CBM versions to test the code is worth <1k.  You can get it at c65 additions - Manage /ui at SourceForge.net.  I also have a new version of AltInput, which is a function to handle line input with a minimum of functionality, but I don't think it is online.  Try it out!

Link to comment
Share on other sites

In De-Re Atari CIO is covered starting on Page 8-22

 

In Mapping the Atari have a look at Pages 81 through 88 where device handlers/tables/vectors are described.

 

CIO works by opening a channel, depending on the Open mode, you can read/write to a device through

its channel table, the values required for the table are described in those pages in Mapping the Atari.

Each channel has a 16 byte block starting at $340

 

All commands end with a JSR CIOV ($E456)

simple example, closes channel 2, then opens it for screen device "E:"

 

010000 CHANNEL LDA #CLOSE ; Close Channel 2
010010   LDX #$20
010020   STA $0342,X
010030   JSR CIOV
010040   LDA #OPEN ; Open Channel 2 for Screen Editor
010050   STA $0342,X
010060   LDA #NAME&255
010070   STA $0344,X
010080   LDA #NAME/256
010090   STA $0345,X
010100   LDA #3
010110   STA $0348,X
010120   LDA #0
010130   STA $0349,X
010140   LDA #12     ;R/W
010150   STA $034A,X
010160   LDA #0
010170   STA $034B,X
010180   JSR CIOV
010190   RTS 

010200 NAME .BYTE "E:",$9B

;

; Address of message text passed in A and Y registers
010210 MESSAGE LDX #$20 ; Write a message to the screen
010220   STA $0344,X ; low byte of text address
010230   TYA 
010240   STA $0345,X ; high byte of text address
010250   LDA #PUTREC
010260   STA $0342,X
010270   LDA #120
010280   STA $0348,X ; max bytes to write (low)
010290   LDA #0
010300   STA $0349,X ; max bytes to write (high)
010310   JSR CIOV
010320   RTS 

 

 

  • Like 1
Link to comment
Share on other sites

I would like to point out that cc65 already ships with non-standard header <conio.h> which provides faster routines for screen input/output. It is because the video RAM is addressed directly. Theses functions are available for the atari target.

The <stdio.h> provides functions that are using the E: device through CIO. 

 

I am pointing it out, so you do not waste time developing something that already exists.

 

Link to comment
Share on other sites

The purpose of my SimpleIO libraries is to provide as little extra processing of data as possible.  It is meant as a more optimal version of printf() and other console I/O functions.  The CBM version comes with binaries to test the included functions which are worth <1k per version.  If it's not possible on the Atari 8 versions, just tell me, and I will stop working on it for now.

Link to comment
Share on other sites

36 minutes ago, Harry Potter said:

The purpose of my SimpleIO libraries is to provide as little extra processing of data as possible.  It is meant as a more optimal version of printf() and other console I/O functions.  The CBM version comes with binaries to test the included functions which are worth <1k per version.  If it's not possible on the Atari 8 versions, just tell me, and I will stop working on it for now.

That's kind of what conio.h does already. I don't think you are way offbase with your typed prints printu, printc, etc., as a smaller printf replacement, but they should be thin wrappers on top of the basic CIO calls that support outputting things to screen. So I wouldn't call it entirely unnecessary, but I wouldn't call it really valuable either.

Link to comment
Share on other sites

I always assumed that (f)putc and putchar just call CIO.

 

If you really want to make a faster screen IO library, you should write directly to the screen memory and ignore the E: CIO handler. Use SAVMSC (88,89 ($58,$59)) for that. Note that you have to write ANTIC screen codes instead of ATASCII.

 

Link to comment
Share on other sites

28 minutes ago, Harry Potter said:

So, the verdict is it's not worth it.  :(  

 

if someone really wants, he can use the standard library but here a friend creates a custom one that is supposed to give some benefit, for example in the form of extreme shortening of the code.

Before you give up, know that the OS has calls that have the same addresses for all XL OS versions, so procedures like:

 

GRAPHICS
PLOT
PRINT
DISPLAY
DRAWTO
FILL
LOCATE
INPUT
GETKEY
CONVERT

you can implement without any obstacles and they will be much faster and shorter. do not be discouraged by "good advice".

 

classic example

    ldx    #<hello
    ldy    #>hello
    jsr    $c642
    ...

hello    dta    c'Hello, world!',$9b

 

 

  • Like 1
Link to comment
Share on other sites

What xxl says is true, if you limit your target to a certain OS (family). Used a lot in sizecoding competitions, but for production code, you need several binaries to target the old and the new OS vectors.

 

There's an in-between BTW. The direct vectors are in tables in the OS ROM, and you can read them and copy the address to self modifying code. JMP $0000, and then change the LSB/MSB of the argument. Only once. After that, you can call that JMP routine. Some extra cycles burnt, but OS agnostic, and faster than going through CIO.

Edited by ivop
Link to comment
Share on other sites

Something like this can serve as a concise means of calling the keyboard handler's 'get' routine, providing the handler table is arranged as per the stock OS:

.proc GetKey
	lda $E425
	pha
	lda $E424
	pha
	rts
.endp

As for direct jumps into undocumented entry points: this is just bad practice guaranteed to garner complaints from those users for whom your software crashes because the user favours some custom OS (many of which date back the Atari's heyday). Presumably anyone who asserts otherwise disregards said alternative operating systems as invalid. That's fine if you want to deliberately limit your user base.

 

Mapping the Atari (which the most excited proponent of illegal OS entry points presumably read for the first time a couple of years ago) is a conspicuous culprit here, since it pointlessly lists entry points which at the time of publication were not even guaranteed to stay put in stock operating systems. Using said entry points when the OS itself is rather elegantly designed (it inherently allows for output redirection, for example) is rather unnecessary unless you are perpetually short of half a dozen bytes to complete your project.

 

There are many ways to shorten the size of a library which are less egregious than resorting to jumps into undocumented entry points.

Edited by flashjazzcat
  • Like 3
Link to comment
Share on other sites

@flashjazzcat's method is even more elegant. I had forgotten that the vectors were minus one, specifically designed for being a return address on the stack.

 

Also, I agree on Mapping motivated bad practices by documenting both the vector locations and where it points to. Calliing the latter directly is a no go, but Chadwick didn't say that.

 

  • Like 2
Link to comment
Share on other sites

Even if "undocumented entry points" are in fact documented somewhere, this does not automatically mean that they are to be used by direct JSR calls. There are two different things: 1) the, as they say now, API contract, and 2) things documented "for your information".

 

The API contract is the stuff the OS designers guarantee to work across all OS revisions. Like CIOV $e456, accepts input parameters such and such, does this and that, returns this in X, that in Y, A is sometimes defined, but most of the time undefined. "Undefined" means that the returned value may change across OS revisions, because that value is accidental from the point of view of the program: it depends on the method used to execute the defined OS function, but the methods are in fact private internals of the OS (or any program, in fact, which exports some function to be externally called) and cannot be relied upon; some better method (shorter and/or faster) of doing things may be used in the next revision and thus the "undefined" stuff returned may change.

 

The same applies to the location where the actual OS routines reside: the actual address is most of the time accidental and belongs to the category of methods; and methods are private to the OS. It is often good to know what are the side effects, performance etc. and this its the reason for documenting such stuff as "FYI" - but relying on them is just praying for problems.

 

As about the ROM vectors at $E400, it should be remembered that only the default drivers can be called that way. In fact all CIO devices should be called so that the call goes indirectly through the HATABS.

  • Like 2
Link to comment
Share on other sites

On 9/14/2021 at 12:27 PM, flashjazzcat said:

Something like this can serve as a concise means of calling the keyboard handler's 'get' routine, providing the handler table is arranged as per the stock OS:


.proc GetKey
	lda $E425
	pha
	lda $E424
	pha
	rts
.endp

 

 

This is actually not safe either, for a different reason. CIO device handlers aren't designed to be executed directly, they expect CIO to set up a calling environment for them. This is skipped if you call the CIO handler directly instead of going through CIOV, in which case you are responsible for setting up the environment.

 

In this case, the subtle bug has to do with the way that the K: handler's GET BYTE routine handles the AUX1 permission byte. This is due to an undocumented behavior where the "forced read" function of E:, which is activated by bit 0 of AUX1 on open and tells E: to read lines from the screen without waiting for keyboard input, is actually implemented by K:. This means that the K: GET BYTE handler reads AUX1 even though K: is documented in the Atari OS manual as not having any device dependent bits in AUX1.

 

Furthermore, as with any other CIO handler, K: GET BYTE expects the AUX1 byte of the IOCB to have been copied into ICAX1Z by CIO prior to the handler being called. If you don't do this before executing the code above, then you're at the mercy of whatever byte happens to be in ICAX1Z at the time. Most of the time it'll work because this happens to have bit 0 cleared, and occasionally it will break horribly with your program endlessly getting $9B instead of a key from the keyboard. This is not theoretical; it was the cause of a long-standing Ice-T bug where the program would print endless 'j' characters in some configurations. People thought it was an incompatibility with R: handlers, but it was actually a bug with the way that the program called directly into K: using code like the above.

 

There are definitely uses for sidestepping CIO, but you have to know exactly what is involved, and the majority of references don't tell you all of the pitfalls. And as can be seen here, not even the official manuals do sometimes.

 

Edited by phaeron
  • Like 4
Link to comment
Share on other sites

6 hours ago, phaeron said:

In this case, the subtle bug has to do with the way that the K: handler's GET BYTE routine handles the AUX1 permission byte. This is due to an undocumented behavior where the "forced read" function of E:, which is activated by bit 0 of AUX1 on open and tells E: to read lines from the screen without waiting for keyboard input, is actually implemented by K:. This means that the K: GET BYTE handler reads AUX1 even though K: is documented in the Atari OS manual as not having any device dependent bits in AUX1.

 

Furthermore, as with any other CIO handler, K: GET BYTE expects the AUX1 byte of the IOCB to have been copied into ICAX1Z by CIO prior to the handler being called. If you don't do this before executing the code above, then you're at the mercy of whatever byte happens to be in ICAX1Z at the time. Most of the time it'll work because this happens to have bit 0 cleared, and occasionally it will break horribly with your program endlessly getting $9B instead of a key from the keyboard. This is not theoretical; it was the cause of a long-standing Ice-T bug where the program would print endless 'j' characters in some configurations. People thought it was an incompatibility with R: handlers, but it was actually a bug with the way that the program called directly into K: using code like the above.

Good points. I learned a lot of esoteric stuff about CIO devices recently while writing a new DOS, and not all of it is explained well in the documentation, as you say.

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...