danwinslow Posted February 8, 2015 Share Posted February 8, 2015 (edited) Hi folks. Looks like I'll be working on the driver pretty seriously over the next few months, so I thought I'd start a little tracking thread, for those who are interested and also for me to get help with technical details as I run into them. This is not going to be a high volume thread, just a few updates now and then as things happen. Don't bother asking for downloads or posting 'any news??" over and over, I won't respond. When there's news I'll post it, when there's a download I'll say so. Anyways, had some success getting things to work under BASIC. Had to do a good bit of zeropage reorganizing and making sure my usage of it got saved and restored...turns out BASIC is kind of touchy about it's zeropage variables. There's an "I:" handler, that takes 2K of memory that BASIC can interact with. The 2K is mostly data buffer so that the stack can write packets directly into the handler memory and BASIC can get the data without a lot of extra copying things around into and out of extended memory. So, here's a little basic program that prints out the current IP and MAC : 10 DIM A$(100) 20 CLOSE #2 30 OPEN #2,12,0,"I:" 40 XIO 13,#2,0,0,A$ 50 CLOSE #2 60 PRINT "IP : "; 70 PRINT PEEK(ADR(A$)+6);".";PEEK(ADR( A$)+7);".";PEEK(ADR(A$)+;".";PEEK(AD R(A$)+ 80 PRINT "MAC: "; 90 PRINT PEEK(ADR(A$)+0);":";PEEK(ADR( A$)+1);":";PEEK(ADR(A$)+2); 100 PRINT ":";PEEK(ADR(A$)+3);":";PEEK (ADR(A$)+4);":";PEEK(ADR(A$)+5) The extra screen garbage at the top is some debug from the VBI's, that won't be there in the production driver. The MAC is printed in decimal; I didn't feel like trying to do a conversion in BASIC to hex at this point, that 54 is actually a hex 36, and thats the official ATARI MAC designator. The next thing I'm going to do is to read & write a UDP packet from BASIC. Edited February 8, 2015 by danwinslow 6 Quote Link to comment Share on other sites More sharing options...
kenjennings Posted February 9, 2015 Share Posted February 9, 2015 Got the dragoncart. Collected my 800XL and sio2sd from storage. just need to find some space to setup. Quote Link to comment Share on other sites More sharing options...
+David_P Posted February 9, 2015 Share Posted February 9, 2015 This looks fantastic. Maybe a TDLine style additional line on the screen would be useful for debugging? Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 9, 2015 Author Share Posted February 9, 2015 Ken, altirra has good dragoncart support too. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 14, 2015 Author Share Posted February 14, 2015 (edited) Ok, had to detour a little to get a resolve working. Can now run a dns resolve from BASIC. Send a UDP packet is next up. 10 DIM Z$(100) 15 Z$="WWW.CISCO.COM" 16 Z$(14)=CHR$(0) 20 CLOSE #2 30 OPEN #2,12,0,"I:" 40 XIO 27,#2,1,0,Z$ 50 PRINT "IP: "; 60 PRINT PEEK(ADR(Z$)+0);"."; 70 PRINT PEEK(ADR(Z$)+1);"."; 80 PRINT PEEK(ADR(Z$)+2);"."; 90 PRINT PEEK(ADR(Z$)+3) Edited February 14, 2015 by danwinslow 3 Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted February 15, 2015 Share Posted February 15, 2015 Gonna have to test this. If you define a variable to the address of Z$ (Z=ADR(Z$)) the BASIC can be a bit briefer. Also: does the driver not interpret EOL as a string terminator? If it did, you can avoid adding CHR$(0). Quote Link to comment Share on other sites More sharing options...
Shawn Jefferson Posted February 15, 2015 Share Posted February 15, 2015 Probably the driver is using null terminated strings ("C" style). Quote Link to comment Share on other sites More sharing options...
+David_P Posted February 15, 2015 Share Posted February 15, 2015 So the XIO command is modifying the string then? Might it be better to then leave the first four bytes as null/undefined when passing it to the XIO routine, so that the original address sent for resolution remains at Z$(4)? There are situations where it's useful to still have the original data. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 15, 2015 Author Share Posted February 15, 2015 (edited) Gonna have to test this. If you define a variable to the address of Z$ (Z=ADR(Z$)) the BASIC can be a bit briefer. Also: does the driver not interpret EOL as a string terminator? If it did, you can avoid adding CHR$(0). Hmm good point about the basic, not my strong suit. I'll mess with that to check. Probably the driver is using null terminated strings ("C" style). Well, sort of. The drivers all assembler, but something in there seems to want null terminated, or more likely I'm not copying things right from the IOCB handler. I also found out that my cable companies DNS server does NOT RETURN AN ERROR FOR A NONEXISTANT ADDRESS, it returns some wierd ip in the UK : 92.242.140.2. I spent 2 hours yesterday trying to figure out why I kept getting that address! So the XIO command is modifying the string then? Might it be better to then leave the first four bytes as null/undefined when passing it to the XIO routine, so that the original address sent for resolution remains at Z$(4)? There are situations where it's useful to still have the original data. Yes, its kind of an issue I need to decide about. The handler works by copying iocb values into a larger structure that I call my 'control' structure. This is really sort of a 'socket struct', in which you can fill out all sorts of things, packet buffers, from and to ports, protocol, source and dest addresses, etc. It also has a command byte that specifies the operation, and then that all gets passed to the driver, which does the op, and then passes the struct back. The driver also sets stuff in there to tell you what happened. So right now, in BASIC, I am trying to just use XIO for various things, map that onto the control struct in the handler, pass it to the driver, and map it back. That '27' in the XIO call is the 'resolve' command number in the driver. The problem is, though, that there isn't really enough structure available in the XIO calls that I know of to really do a nice mapping, and so I've done dumb stuff like overwriting the request parameter. I am mulling over just exposing the control struct directly in BASIC, as a 40 byte string, that then you fill out and call the XIO handler with, so we would skip all the mapping back and forth. I'm concerned that it would make the BASIC more difficult, as you'd have to handle a bunch of direct addresses and other assembler/c style buffers and things, and you'd have to do all your own setup of certain of the values. It would sure make things easier on me and the handler smaller though. What do you guys think about this issue? *edit* OR, I guess I could implement an XIO call 'set output buffer', that would allow you to specify a universal result output buffer, or offset the result as David mentioned. Since that particular input is variably sized, it might be kinda finicky to handle the offset. Edited February 15, 2015 by danwinslow Quote Link to comment Share on other sites More sharing options...
w1k Posted February 15, 2015 Share Posted February 15, 2015 i missing good telnet for dragont cart.. supporting irc, telnet, 22, 25, 110.. Quote Link to comment Share on other sites More sharing options...
Alfred Posted February 15, 2015 Share Posted February 15, 2015 (edited) The Percom block exists. No reason why there can't be a Dragoncart block. >> I am mulling over just exposing the control struct directly in BASIC, as a 40 byte string, that then you fill out and call the XIO handler with, so we would skip all the mapping back and forth. I'm concerned that it would make the BASIC more difficult, as you'd have to handle a bunch of direct addresses and other assembler/c style buffers and things, and you'd have to do all your own setup of certain of the values. It would sure make things easier on me and the handler smaller though. What do you guys think about this issue? *edit* OR, I guess I could implement an XIO call 'set output buffer', that would allow you to specify a universal result output buffer, or offset the result as David mentioned. Since that particular input is variably sized, it might be kinda finicky to handle the offset. Edited February 15, 2015 by Alfred Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 15, 2015 Author Share Posted February 15, 2015 Not sure what you mean, Alfred. Percom block? I'll see what I can find out about it, but I assume you mean some kind of dedicated buffer for working with percom disks? Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted February 15, 2015 Share Posted February 15, 2015 Have a look at the SDX manual: perhaps the method described for calling the CHKDSK CIO function would be appropriate (I forget offhand, but I think the device spec is passed in the CIO address and the buffer address passed in the CIO buffer size word). I'd make EOLs and NULLs string terminators in the driver, then everything's covered transparently. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 15, 2015 Author Share Posted February 15, 2015 (edited) Ok, I see info about the percom block at http://www.atarimax.com/jindroush.atari.org/asio.html and yes, that's exactly the kind of thing I am thinking about, so that's encouraging. Jon - yep! doing that today. Edited February 15, 2015 by danwinslow Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 19, 2015 Author Share Posted February 19, 2015 Ok, so how do I return things from an XIO call? Such as a 2 byte address, for instance. I want to return an address of a struct in the handler. I can write the data into a passed in string, but that seems clumsy. I guess I could set up an INPUT #x,a type of call but that seems kind of clumsy too. I saw some examples of sparta XIO calls returning things in the IOCB itself, which the basic would then have to get manually. I want to do something like 10 XIO CMD,#2,1,0,A where the 1 in icax1 indicates the socket slot I want ( out of 1-4 ), and I want to return the address in A, but BASIC only wants string there. I'm not sure how to do what I want with just XIO. Should I punt the XIO idea and just write a small USR() handler to be included in the program? Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted February 19, 2015 Share Posted February 19, 2015 I think you copy stuff you want to return into the ziocb. This gets returned in the calling CIO block when the system drops back out of the OS call. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 19, 2015 Author Share Posted February 19, 2015 OK.....but what happens then? There doesn't seem to be a 'BASIC' way to return an address into a numeric variable from XIO. I was considering something like 10 XIO cmd,#2,1,0,A$ 20 GET #2,b But apparently that just returns 1 byte. I can of course overwrite the first 2 bytes of A$ with the address directly in the XIO call, but treating that as an output variable is kind of non-standard. Or, I could go with something like 5 IOCB=2 10 XIO cmd,#2,0,1,A$ 20 B=PEEK(832+(IOCB*16)+ICAX3)+PEEK((833+(IOCB*16)+ICAX4)*256) Where B would get the address of the control struct for that socket. That might not be too bad; then setting things would become : 50 POKE(B+1,SEND_PACKET_COMMAND) 60 POKE(B+16,ADR($PACKET)/256):POKE(B+15,ADR($PACKET)-(PEEK(B+16)*256) 60 XIO 22,#2,0,1,"" Looks like USR() can return a two byte result, so I think I'll experiment with a small USR() function to call the handler after it's opened...seems like that would give me the most flexibility and folks are used to that kind of thing when working with drivers from BASIC. My question there would be : do I need one for every different BASIC or is the calling methodology petty much the same between BASICs. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted February 19, 2015 Share Posted February 19, 2015 I'd stick with the CIO, using the PEEK method you outline in the example: much more elegant and language agnostic than USR(). You could (as possibly already mentioned) simply pass a string (i.e. caller's buffer address) to the driver, which the driver would then populate the buffer with the necessary info. Or just write the struct directly into the string (XIO would then pass the address of the struct buffer). What's the VBL for, BTW? Is it currently just a testing/debugging aid, or is hardware polling somehow delegated to the NMI? Quote Link to comment Share on other sites More sharing options...
kenjennings Posted February 20, 2015 Share Posted February 20, 2015 Looks like USR() can return a two byte result, so I think I'll experiment with a small USR() function to call the handler after it's opened...seems like that would give me the most flexibility and folks are used to that kind of thing when working with drivers from BASIC. My question there would be : do I need one for every different BASIC or is the calling methodology petty much the same between BASICs. Other BASICs compatible with Atari BASIC support the same stack/calling convention for USR() as Atari BASIC. Off the top of my head that's OSS BASIC XL, OSS BASIC XE, and TurboBASIC XL. And Altirra's BASIC. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted February 20, 2015 Author Share Posted February 20, 2015 So let it be written...so let it be DONE! Thanks Ken,. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 3, 2015 Author Share Posted March 3, 2015 (edited) Ok, Minor victory today, got a UDP packet sent from Atari Basic via the driver. 10 OPEN #2,0,1,"I:" 20 XIO 20,#2,0,1,"" 30 IOCB=832+(2*16) 40 C=PEEK(IOCB+12)+(PEEK(IOCB+13)*256) 50 U=PEEK(IOCB+14)+(PEEK(IOCB+15)*256) 90 DIM A$(64):A$="HI THERE FROM ATARI BASIC!" 95 DIM IP$(14):IP$="192.168.2.2" 100 A=USR(U,7,1,ADR(A$),LEN(A$),45000, 44000,ADR(IP$),LEN(A$)) The USR() function is a helper wrapper for Basic, you can do all the same ops with the XIO interface, but its a little clunkier. You have 4 'socket' slots, which are selected by the fourth number in the XIO calls : XIO operation,#iocb,unused,socket, ( other params ) and in the USR call USR(address,operation,socket, ( variable based on operation ) Once you get the socket set up you don't have to pass the other parameters unless you need to change something, so a line 110 with USR(U,"other data",10) would send the new data to the same ip:port you just sent to. 10 open the driver 20 initialize socket 1 , passes back the USR() address Edited March 3, 2015 by danwinslow 2 Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted March 3, 2015 Share Posted March 3, 2015 Great. Following with interest! Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 4, 2015 Author Share Posted March 4, 2015 (edited) I am having an intermittent error pop up under heavy use of the USR() command from basic. After a while of running ( 1 minute-ish ), basic will stop with a 'Error 3', which means 'A value is outside its expected range', a tremendously helpful message indeed. This error does NOT occur in emulation ( Altirra 2.50 ), which adds to the fun. After a laborious cycle of 'comment out - test - comment out - etc.' I have isolated it to the code that polls the Dragoncart for a message : <pre> eth_rx: lda #$24 ; check rx status sta cs_packet_page lda #$01 sta cs_packet_page + 1 lda cs_packet_data + 1 and #$0d bne :+ sec ; no packet ready rts </pre> If that code is being run, Basic craps out with an error 3 after a while, if it's not, it doesn't. cs_packet_page is a Dragoncart register at $D50A. Seeing that $D500 range being in play makes me suspicious that those writes are somehow interfering with the internal Basic on my 130xe. Bear in mind that the access happens during a deferred vertical blank interrupt, although I don't know what that would have to do with it. Any ideas? Does $D50A access trigger any internal hardware actions? Does built-in Basic still pay attention to that area? I get the feeling that, very occasionally, I get an interrupt at the precise instant that Basic is fiddling around with something during the USR() call, and when the interrupt returns, something has changed that makes Basic unhappy. I do completely save and restore all registers and zero page variables when I enter/leave the interrupt, but I wonder if there's something special about that address range. Nothing seems to be disturbed, the basic program is fine and will rerun normally if I enter 'run' again. The system does not become unstable. Edited March 4, 2015 by danwinslow Quote Link to comment Share on other sites More sharing options...
danwinslow Posted March 4, 2015 Author Share Posted March 4, 2015 Well, I did the obvious and added a 'Trap xxx' command that just re-executes the line...that seems to work ok but I'd rather not have to do that. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted March 5, 2015 Share Posted March 5, 2015 (edited) I'd suspect you're right about the interrupt firing during the USR call, perhaps altering some variables common to both the interrupt and USR code at the precise moment the USR return value is being set up. Just a guess, anyway. Writing a multitasking OS has reminded me it's easy to encounter contention when designing ISRs. Perhaps block the ISR if mainline code is currently inside the USR and see where that goes. Nice thing about IRQs is you can block them with SEI and they'll still fire when you come out of the critical section. Edited March 5, 2015 by flashjazzcat 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.