Jump to content
IGNORED

Sending data back to Atari Basic in an INPUT command


danwinslow

Recommended Posts

Looking for some help on the subject of sending data from a driver, in an 'INPUT' call, back to an atari basic variable:

INPUT #2, A$

The actual data address of the receiving variable (A$) doesn't seem to be in the IOCB anywhere. I don't think I'm supposed to go find A$ and start stuffing data in there. If I had to guess, and I do, I'd put the address of the data and the length in the IOCB somewhere, and then when returning to BASIC it would do the actual load into the string. Is that close?

 

Any advice appreciated.

Link to comment
Share on other sites

Looking for some help on the subject of sending data from a driver, in an 'INPUT' call, back to an atari basic variable:

INPUT #2, A$

The actual data address of the receiving variable (A$) doesn't seem to be in the IOCB anywhere. I don't think I'm supposed to go find A$ and start stuffing data in there. If I had to guess, and I do, I'd put the address of the data and the length in the IOCB somewhere, and then when returning to BASIC it would do the actual load into the string. Is that close?

 

Any advice appreciated.

You gotta init the string..

10 DIM A$(1000)

20 A$="X"

30A$(1000)=A$

40 A$(2)=A$

now you have 1000 'X' in A$

you get the address of A$:

LOC=ADR(A$)

If you don't init the string, it is like 1 byte and you can't put anything in it.

 

Keep in mind that Atari BASIC moves strings around, so do the ADR just before

you need it.

You could load up a machine code with:

10 RESTORE 1000

12 STRADR=ADR(A$):REM after you init it

20 FOR N=0 TO 79:READ X:POKE STRADR+N,X:NEXT N

1000 DATA 96,100,so forth for 80 bytes of ml

now you have your ml in A$

then you can

100 X=USR(ADR A$):REM after A$ is inited and loaded up

the first bytes of your ml have to set up correctly.

You have to PLA at least once because the USR call pushes the

number of arguments 1 byte, even if no arguments it would be 0.

You might be working with a IOCB and have X=USR(ADR(A$))16,32

This is quite fuzzy, as I'm going by memory 30 years old.

Wait! I'm looking in 'Your Atari Computer' Poole and McNiff page 402.

 

Current top of stack (low address of your code)

USR call pushes:

Number of USR arguments (you can pass numbers after the string address). X=USR(ADR A$),16,32

passing usually the number of the IOCB block * 16

oh:

X=USR(ADR A$),10,20,30

number of arguments, 1 byte

HI of first USR argument

LO of first USR argument

so on HI LO for usr arguments

I remember now:

You have to have at least one PLA in your code because of the

USR call will always pull the number of USR arguments off the stack, then

nothing if zero usr arguments are called.

So, we have:

number of usr arguments, 1 byte

HI LO of the value of any usr arguments, nothing if no usr argements

Now the stack has the RETURN address in the BASIC program, which you don't have to worry about

the USR call automaticly pushes that. It is where a RTS in your code will return to BASIC

 

The 'stack' is the place where your ml is, ADR(A$). and I guess it is as big as your inited string.

You may run into a problem with putting your code in a string if a 155 is in there, you

can't have a 155 in A$, it terminates it. I don't remember the solution to that problem.

Maybe you have to have two strings if there is a 155.

'nuf said.

Edited by russg
Link to comment
Share on other sites

That's all good info, thanks. But I have a different problem I think...

I have written a CIO handler, I:

From basic, I want to call:

OPEN #2,12,0,"I:"

INPUT #2,A$

This will call the 'read' function of my handler. My question is, how do I feed that data back to basic correctly so it winds up contained in A$, just as if I'd done an INPUT #x,A$, from say, the keyboard?

Link to comment
Share on other sites

That's all good info, thanks. But I have a different problem I think...

I have written a CIO handler, I:

From basic, I want to call:

OPEN #2,12,0,"I:"

INPUT #2,A$

This will call the 'read' function of my handler. My question is, how do I feed that data back to basic correctly so it winds up contained in A$, just as if I'd done an INPUT #x,A$, from say, the keyboard?

this seems more like a standard IOCB question to me, no? doesn't seem like it has anything to do with BASIC specifically.

 

I think you would just put at most [iCBLZ/HZ] bytes in the buffer pointed at by ICBALZ/HZ. There is also a special case, I think, where [iCBLZ/HZ] is zero, and you return a single byte in the accumulator. Also, there is 'get text record' vs 'get binary record,' as get binary record gets up to the number of bytes specified in the buffer length, or however many are available. get text record sends up to the number of bytes specified in the buffer length, or until the first newline, whichever comes first.

 

of course, better to wait for someone who *knows* what they're talking about, as all this is educated guessing. But the general principal is that you will put data in the buffer pointed at by ICBALZ/HZ, and there is one special case, ICBLZ/HZ == 0 when a single byte is returned in the accumulator.

Edited by Joey Z
Link to comment
Share on other sites

The INPUT statement needs to handle both truncating arguments and multiple arguments per line, so it reads into a temporary buffer at LBUFF and then copies from there into the string. For this reason, neither CIO nor the device driver ever sees the variables involved.

 

CIO, in turn, calls the GET BYTE routine on the driver for one character at a time until it either runs out of buffer space or gets EOL ($9B), expecting each character to be returned in the A register. X also points to the originating IOCB if you want to look up other parameters, but keep in mind that the buffer parameters are temporarily stored in the zero-page IOCB and won't match the originating IOCB during the transfer. Also, you can't just transfer into the buffer directly as CIO will still keep calling GET BYTE and storing its result in the buffer itself.

 

This "by the book" sequence works, but is slow for large transfers. It is possible to speed up the transfer by modifying the GET BYTE routine to transfer additional bytes to ICBALZ/ICBAHZ and update ICBLLZ/ICBLHZ behind CIO's back, similar to the way that DOS does burst I/O. However, several cases must be tested when doing this, including GET CHARACTERS with length 0, GET CHARACTERS with length > 0, GET RECORD with sufficient buffer space, GET RECORD with a truncated result, and GET RECORD with EOF.

Link to comment
Share on other sites

Yes, that's right, and in fact Basic delivers you an IOCB which already has it's own internal parsing buffer loaded into ICBAL, LBUFF at $580. I have been trying to copy stuff in there and returning to basic to see if it winds up in A$ correctly, but no luck so far.

 

Of course not. As every handler, your handler has a GET entry point, actually the third vector pointed to by the HATABS entry. This vector has to deliver one byte per call, in the A register, and CIO and BASIC then place that into the variable, actually through a detour. Not the handler itself, which is not responsible to copying data into the RAM. That's CIOs job. (Theoretically. The FMS implements a bypass called "bursting", but we don't need to complicate things at this time).

 

BTW, Basic passes INBUFF as buffer to CIO, and INPUT copies the data from there into the variable. That implies, however, that you cannot read more than 256 bytes at once with INPUT.

 

If I may make a suggestion: Probably try to explain which type of problem you're trying to solve. It sounds to me as if you somehow got on the wrong track.

 

Link to comment
Share on other sites

Not everyone is a handler expert. I did not know anything about how basic handles the situation, and thus the post. Its news to me that basic works by calling get single character over and over, that seems horribly slow.

 

Anyway, thanks for the info fjc, phaeron and thorfdbg.After a little more experimentation, it seems to work exactly as you've said - it just calls GETCHAR over and over until it hits a $9B.I'll have to figure out how to tell that basic is calling me as opposed to some other language, I don't want to make it all work like that. I may have to do the packet IO with XIO calls after all.

Edited by danwinslow
Link to comment
Share on other sites

If you implement burst mode you can overcome any processing overhead for single byte reads. The application might implement its own line buffer and request - say - 256 bytes to fill said buffer. You can then transfer 256 bytes from source directly to the specified buffer address. Might be unusual to see this kind of buffer management in a BASIC program, but anyone coding in assembler could surely take advantage of it.

Link to comment
Share on other sites

Not everyone is a handler expert. I did not know anything about how basic handles the situation, and thus the post. Its news to me that basic works by calling get single character over and over, that seems horribly slow.

 

Anyway, thanks for the info fjc, phaeron and thorfdbg.After a little more experimentation, it seems to work exactly as you've said - it just calls GETCHAR over and over until it hits a $9B.I'll have to figure out how to tell that basic is calling me as opposed to some other language, I don't want to make it all work like that. I may have to do the packet IO with XIO calls after all.

Actually, it's not quite as bad. It's not BASIC that loops, it is CIO that loops - for INPUT at least. For the GET #x,A statement, yes, you'll have to do the looping.

 

Unfortunately, XIO is not really able to run block-I/O for you. For Atari Basic, you'd need a short USR subroutine to call CIO in the block mode. Yes, you can put a string as last argument, but Basic takes that as a file name and does not transport its length to CIO as it should.

Link to comment
Share on other sites

If you implement burst mode you can overcome any processing overhead for single byte reads. The application might implement its own line buffer and request - say - 256 bytes to fill said buffer. You can then transfer 256 bytes from source directly to the specified buffer address. Might be unusual to see this kind of buffer management in a BASIC program, but anyone coding in assembler could surely take advantage of it.

 

Sure, I was thinking of some kind of XIO or user call that would take the address of a pre-filled basic variable and just dump the data directly into there, which is what happens in a non-basic call. You lose the parsing and the length and so forth though, and it would be kind of cool to let people use things like

PRINT #2,USERID$,LINE$,STATUS

on one end of a tcp connection and be able to support

INPUT #2,U$,L$.S

on the other.

 

Anyway, thanks for info all, gonna mess around with it for a while.

Link to comment
Share on other sites

Not everyone is a handler expert. I did not know anything about how basic handles the situation, and thus the post. Its news to me that basic works by calling get single character over and over, that seems horribly slow.

 

Anyway, thanks for the info fjc, phaeron and thorfdbg.After a little more experimentation, it seems to work exactly as you've said - it just calls GETCHAR over and over until it hits a $9B.I'll have to figure out how to tell that basic is calling me as opposed to some other language, I don't want to make it all work like that. I may have to do the packet IO with XIO calls I do

I don't think it's as complicated as you make it out to be. in the handler, there is no 'put' routine for multiple bytes, it's ALWAYS put byte, same for get, there is ONLY get byte. this is (I guess) the nature of an IOCB handler. So you simply take a byte from the accumulator? and put it to your device, or you take a byte from the device, and return it in the accumulator? If you want to do away with the slow byte-by-byte style, there are tricks, such as manually filling the buffer in cases where more than one byte is being transferred, and then updating the buffer address in the ZIOCB, and returning the last byte in the accumulator.

 

This is independent of BASIC, ALL IOCB calls do put/get byte repeatedly.

Edited by Joey Z
Link to comment
Share on other sites

The nature of CIO is that it distances the handlers from user programs so things like final address for packets of data become irrelevant and mostly anonymous.

 

If you're worried about speed, maybe you could implement a "Special" command which does direct access to data in blocks at a time instead of a single byte.

Though the problem then becomes you lose versatility and have a device stuck with a unique command that doesn't work on any other.

Link to comment
Share on other sites

Get and put are inherently dual purpose. BUFLEN=0 means single byte transfer using the accumulator, while BUFLEN and BUFADR (in the case of put/get) signify a direct block transfer. So your get/put routines need to react to the buffer length being non-zero and act accordingly.

The way I understand it, no, not the handler routines. The handler routines are get and put BYTE, while the IOCB calls are get and put BUFFER or BYTE. see mapping the atari, the HATABS section, and also the january 1982 Compute! article they reference there. The IOCB translates the GET and PUT calls to GET BYTE and PUT BYTE calls to the handler.

 

http://www.atariarchives.org/mapping/memorymap.php#794-831

https://archive.org/details/1982-01-compute-magazine <-- page 120, example source later, around 130

Edited by Joey Z
Link to comment
Share on other sites

Well, when I want to put the byte in the accumulator I use the exact same IOCB command code I use when I want to put a buffer: code 11 (PUT BYTE in the document you link to). The only difference to the handler is whether or not BUFLEN is zero on entry, so I assume it must react accordingly. Were this not the case, burst reads in DOS, for example, would not be possible.

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

Well, when I want to put the byte in the accumulator I use the exact same IOCB command code I use when I want to put a buffer: code 11 (PUT BYTE in the document you link to). The only difference to the handler is whether or not BUFLEN is zero on entry, so I assume it must react accordingly.

that's the thing though, you are making calls to the IOCB, not the handler. The IOCB translates the commands you give it into simpler calls for the handler, in this case, get byte and put byte. The IOCB is what detects the 0 buffer length vs. nonzero, and acts accordingly, while the handler doesn't have to care about that normally, and just accepts or provides one byte per call to the get byte or put byte routine.

Link to comment
Share on other sites

See my comment regarding DOS. A request for a 10,000 byte buffer does not cause the OS to call the handler's get one byte routine 10,000 times. The handler sees that the buffer length is not zero and pulls whole sectors directly into RAM. Get and put text record work as you suggest (as Dan has discovered), but not block reads and writes.

Edited by flashjazzcat
Link to comment
Share on other sites

See my comment regarding DOS. A request for a 10,000 byte buffer does not cause the OS to call the handler's get one byte routine 10,000 times. The handler sees that the buffer length is not zero and pulls whole sectors directly into RAM. Get and put text record work as you suggest (as Dan has discovered), but not block reads and writes.

yes, you *can* do burst transfers, but the normal IOCB behavior is to call the get byte routine in the handler until you've the number of bytes in the buffer length, or if you have encountered EOL (in the case of getting a text record)

 

See here, a snippet from the OS source:

;
; LOOP TO FILL BUFFER OR END RECORD
RCI3: JSR GOHAND ;GO TO HANDLER TO GET BYTE
STA CIOCHR ;SAVE BYTE
BMI RCI4 ;END TRANSFER IF ERROR
LDY #0
STA (ICBALZ),Y ;PUT BYTE IN USER BUFFER
JSR INCBFP ;INCREMENT BUFFER POINTER
LDA ICCOMZ ;GET COMMAND CODE
AND #2 ;IS IT GET RECORD?
BNE RCI1 ;NO
;
; CHECK FOR EOL ON TEXT RECORDS
LDA CIOCHR ;GET BYTE
CMP #EOL ;IS IT AN EOL?
BNE RCI1 ;NO
JSR DECBFL ;YES, DECREMENT BUFFER LENGTH
JMP RCI4 ;END TRANSFER
;
; CHECK BUFFER FULL
RCI1: JSR DECBFL ;DECREMENT BUFFER LENGTH
BNE RCI3 ;CONTINUE IF NON ZERO

As Phaeron said, burst I/O through a handler requires a bit of trickery so that the IOCB thinks it has transferred all the bytes, basically by updating the buffer length and buffer pointer yourself, before returning the last byte in the accumulator.

 

My point being that burst transfers are not *required* but are possible, so the simplest way (albeit certainly not the fastest) is to let the IOCB repeatedly call the handler getting one byte at a time and let the IOCB code handle getting the right number of bytes, and putting them in the right place.

Edited by Joey Z
Link to comment
Share on other sites

We already established that's how get and put text record work. Buffer reads and writes are what I'm talking about, not operations which iterate through the stream until they hit EOL.

 

So does the CIO actually call the handler's get one byte routine on a buffer read with a non-zero length if the handler doesn't update the buffer length in the ZIOCB itself? I guess I didn't realize that.

Edited by flashjazzcat
Link to comment
Share on other sites

We already established that's how get and put text record work. Buffer reads and writes are what I'm talking about, not operations which iterate through the stream until they hit EOL.

flashjazzcat, the same code is used for both: note lines 10 and 11, which skip over the EOL checking for 'GET RECORD'

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...