Jump to content
danwinslow

Dragoncart driver

Recommended Posts

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.

post-6548-0-07895600-1423434985_thumb.png

Edited by danwinslow
  • Like 6

Share this post


Link to post
Share on other sites

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)

post-6548-0-52166800-1423957856_thumb.png

Edited by danwinslow
  • Like 3

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 by danwinslow

Share this post


Link to post
Share on other sites

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 by Alfred

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

 

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.

Share this post


Link to post
Share on other sites

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 by danwinslow
  • Like 2

Share this post


Link to post
Share on other sites

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 by danwinslow

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 by flashjazzcat

Share this post


Link to post
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.

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