danwinslow Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted October 11, 2015 Author Share Posted October 11, 2015 ok, looks like you load it into LBUFF, at $580. Basic picks it up from there...until it gets a 9B or runs out of space or something. Still expirementing. Quote Link to comment Share on other sites More sharing options...
Sikor Posted October 11, 2015 Share Posted October 11, 2015 So, You use any Atari DOS with input? Sample program is: 10 DIM A$[10] 20 OPEN #2,4,0,"D:DATA.TXT" 30 INPUT #2,A$ 40 CLOSE #2 Open file must be in format declared by program, not only text. Quote Link to comment Share on other sites More sharing options...
russg Posted October 11, 2015 Share Posted October 11, 2015 (edited) 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 October 11, 2015 by russg Quote Link to comment Share on other sites More sharing options...
danwinslow Posted October 11, 2015 Author Share Posted October 11, 2015 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? Quote Link to comment Share on other sites More sharing options...
Joey Z Posted October 11, 2015 Share Posted October 11, 2015 (edited) 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 October 11, 2015 by Joey Z Quote Link to comment Share on other sites More sharing options...
danwinslow Posted October 11, 2015 Author Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 11, 2015 Share Posted October 11, 2015 Does CIO INPUT not simply repeatedly call the device handler's GET ONE BYTE routine until it hits $9B? That's my recollection of the situation. I thought - from the driver side - the OS would just generate a series of GET requests. Been a long time since I wrote a handler, though. Quote Link to comment Share on other sites More sharing options...
phaeron Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted October 11, 2015 Author Share Posted October 11, 2015 (edited) 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 October 11, 2015 by danwinslow Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted October 11, 2015 Author Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
Joey Z Posted October 11, 2015 Share Posted October 11, 2015 (edited) 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 October 11, 2015 by Joey Z Quote Link to comment Share on other sites More sharing options...
Rybags Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 11, 2015 Share Posted October 11, 2015 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. Quote Link to comment Share on other sites More sharing options...
Joey Z Posted October 12, 2015 Share Posted October 12, 2015 (edited) 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 October 12, 2015 by Joey Z Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 12, 2015 Share Posted October 12, 2015 (edited) 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 October 12, 2015 by flashjazzcat 1 Quote Link to comment Share on other sites More sharing options...
Joey Z Posted October 12, 2015 Share Posted October 12, 2015 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. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 12, 2015 Share Posted October 12, 2015 (edited) 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 October 12, 2015 by flashjazzcat Quote Link to comment Share on other sites More sharing options...
Joey Z Posted October 12, 2015 Share Posted October 12, 2015 (edited) 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 October 12, 2015 by Joey Z Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 12, 2015 Share Posted October 12, 2015 (edited) 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 October 12, 2015 by flashjazzcat Quote Link to comment Share on other sites More sharing options...
Joey Z Posted October 12, 2015 Share Posted October 12, 2015 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' Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted October 12, 2015 Share Posted October 12, 2015 Right: you answered my question. Clearly it does. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.