In late 2005 I decided to write a TCP/IP stack for DOS that would run on an ancient machine like an IBM PCjr. The first problem that I ran into was figuring out how to actually send and receive bytes using an Ethernet card.
TCP/IP is a layered protocol that eventually requires you to send raw bits down a wire. Creating the packets to send down the wire in memory isn't difficult - it requires a lot of data structures and pointers but it is well within the grasp of a good C programmer. Interfacing to the hardware is a problem though - most of us are not trained to write device drivers, it is a difficult environment to debug in, and there are a lot of possible Ethernet cards out there.
Luckily there was a solution for these problems - the packet driver specification. (This bridge had been crossed many years ago.) The packet driver specification creates a high level programming interface for Ethernet cards that allows one to ignore the low level details of the cards and program the various cards using a relatively simple set of calls. The details on the packet driver spec can be found here: http://www.crynwr.com/packet_driver.html
Still, not everything was perfect. I still had to get Turbo C++ 3.0 (my language of choice) to use the packet driver API. The packet driver API is based on software interrupts which allow it to be used from almost any programming language that can generate an interrupt. Software interrupts are not extremely difficult to use, but they are certainly more work than a normal function call.
Some research and digging led me to the interrupt keyword in the compiler. It looked as though Turbo C++ 3.0 was setup to do this kind of software interrupt work with minimal fuss.
My first attempts at sending packets were successful - my Linux box on the network saw my hideous first attempts at ARP. But trying to receive packets was a nightmare - if I received a packet the machine would immediately crash. I had a breakthrough when I figured out that the Xircom PE3-10BT I was using would always fail when I received a packet, but an NE2000 clone would let me receive a packet just fine without crashing first.
After a few days of debugging and some consultation with Russell Nelson at Crynwr I was able to get around the problem. (Russell knew what it was right away when I said Xircom.) After that, I was able to send and receive packets with impunity!
This is sample code that shows how to interface Turbo C++ 3.0 to a packet driver. It also includes some buffer management code that is used for receiving incoming data from the packet driver. The buffer management code is fairly high performance code. I use this code as the foundation for my TCP/IP stack so it is well tested. It also includes the assembler code that I used to work around 'The Xircom Problem.' The work-around is safe for any packet driver.
Documentation is in the form of comments interspersed in the code. It is also expected that you have the packet driver specification handy and are familiar with it.
The sample program does two things:
Obviously there is a lot of code to add if you want this to do
something useful, but it will get you started.
If this gets anybody interested in DOS networking and saves them from the pain and frustration I went through, it will be worth it. ;-)
The original compiler is Turbo C++ 3.0. If you port to another compiler please drop me a line and let me know how it went.
Compile using the 'compact' memory model, which assumes code is in one segment but data can be in multiple segments. This is because we are using 'far' pointers. You can compile with a memory model other than compact as long as you preserve the far pointers.
Click here to download tcpacket.zip
|http://www.crynwr.com/packet_driver.html||The packet driver specification|
|http://www.crynwr.com/drivers/||The Crynwr packet drivers for many older Ethernet cards|
|http://www.brutman.com/Dos_Networking/||My tutorial on DOS Networking|
|http://www.brutman.com/mTCP/||mTCP home page - this is my TCP/IP for DOS project|