• Please review our updated Terms and Rules here

want an MS-DOS web server? :)

Mike Chambers

Veteran Member
Joined
Sep 2, 2006
Messages
2,621
here's an MS-DOS web server i wrote. Runs on any x86 PC ever built that has a suitable network packet driver.

it's here on this page as "WebServ" - http://rubbermallet.org/mysoftware.html

direct download link - http://rubbermallet.org/download/websv09a.zip
EDIT: download the newer version... theres a major bugfix in it that makes sure pending transfers are killed when the client aborts the connect. before, it would keep sending until it reached EOF...... http://rubbermallet.org/download/websv09b.zip

hope somebody can use it.. have fun! :D

i am actually running the software on the 386 right now.... its at http://retrobox.scieron.com
i may not have it running by the time you read this... you can try though.

here's a screenshot btw: (EDIT: changed screenshot to reflect latest version)
webserv1.png
 
Last edited:
wow.

I already have a webserver on my XT up and running but I think I'll try yours as well

Currently I use EZNOS www.eznos.org/html/ez-nos_.html
I also tried Jaffa: www.rowan.sensation.net.au/programs.html

i've seen EZNOS but never really tried it... as for Jaffa, mine is much faster. i don't know what rowan did in his code, but it spends too much time updating the screen as opposed to serving data. on anything less than a 486, its VERY slow.

let me know what you think after you try it plz! :)
 
actually, atari2600...

you can use it in windows. just install bochs or VMware workstation. you can use those emulators and install MS-DOS on them. for bochs you'll need an NE2000-compatible packet driver, or for VMware you'll need an AMD PCnet packet driver.

kind of a pain in the ass and dumb to install the ~100 MB VMware workstation just to run a 57 KB web server program, but it will work. :)
 
Is your server compatible w/ windows 2000 &/or XP?

If all you want is a web server, there are plenty of alternatives (including Microsoft's IIS or Apache). If you do some judicious Googling, you can even find examples of small web servers written in a plethora of languages. I just did a quick Google search and found the source code for a very small web server written in Python (currently my favorite language):

http://fragments.turtlemeat.com/pythonwebserver.php

Pretty interesting, and with a bit of tinkering, you could make it multi-threaded, so it can respond to multiple requests simultaneously (or what passes for that on a single-cpu computer).
 
If all you want is a web server, there are plenty of alternatives (including Microsoft's IIS or Apache). If you do some judicious Googling, you can even find examples of small web servers written in a plethora of languages. I just did a quick Google search and found the source code for a very small web server written in Python (currently my favorite language):

http://fragments.turtlemeat.com/pythonwebserver.php

Pretty interesting, and with a bit of tinkering, you could make it multi-threaded, so it can respond to multiple requests simultaneously (or what passes for that on a single-cpu computer).

cool little web server there that you linked. python is a pretty neat language, i've never really looked at it before. it looks very easy to use.


my webserver also lacks multi-threading. i might work on that for the big v1.0 :p

it works fine handling low demand of small files as is. after i add log and error file support, i'll upload the code if anybody is interested.
 
I really need to find a way to network my 5150 so I can try these out :)

PLIP :)

http://www.crynwr.com/drivers/plip.zip

uses parallel port ethernet packet emulation. just find a plip host for windows and you're set.

or, you could spend a few (And i only mean a few, they're cheap) dollars on an 8-bit ISA network card. i have a generic NE1000 compatible card from like 1985, but it's horrible. i reccommend you get a 3com 3c507 or an NE2000 compatible card.

i see you live in england... here's one for you
http://cgi.ebay.co.uk/ETHERNODE-BOX...ryZ11180QQssPageNameZWDVWQQrdZ1QQcmdZViewItem
 
seems it DOES run in virtual pc 2004 just fine. man i didnt know it was free know, that's awesome! now only if it was open source :)

you need this packet driver to try it in vitrual pc 2004: http://crynwr.com/drivers/dc21x4pk_11_4.zip

webserv-vpc.jpg


if you're wondering about the 4 second delay between those file sends, it's only because i was running totally off floppy disk heh. it would be pretty much instant if it was off a hard disk.
 
Nice! I might use it to host some image files once I get a new PSU for my rig! (I swear, I'm getting around to it!)

(By the way, JPEG screenshots have that "compressed" look. PNG is WAY better for screenshots...)
 
okay, i've got version 0.9 beta ready... i've fixed all bugs that i know of (refer to CHANGES.TXT to see what i've done)

it also logs to file now, which can be modified/disabled in the WEBSERV.INI - change the log filename to %none to disable it
http://rubbermallet.org/download/websv09b.zip

also, here's the source :)
Code:
' WebServ 0.9 beta (c)2006 Mike Chambers
' Simple single-threaded web server written in QuickBASIC.
' Visit http://rubbermallet.org for updated
'
' QB must be loaded with QB.QLB quicklibrary like this:
' QB.EXE /L QB.QLB

TYPE Registers
     ax AS INTEGER
     bx AS INTEGER
     cx AS INTEGER
     dx AS INTEGER
     bp AS INTEGER
     si AS INTEGER
     di AS INTEGER
     flags AS INTEGER
     ds AS INTEGER
     es AS INTEGER
END TYPE

'Define custom data type for nTCP-related data.
TYPE nTCP
        Enabled AS INTEGER
        IntVector AS INTEGER
        ErrorCode AS INTEGER
        LocalIP AS LONG
        LocalNetmask AS LONG
        LocalGateway AS LONG
        LocalDNS AS LONG
        LocalDomain AS STRING * 256
        DomainLen AS INTEGER
        Timeserver AS LONG
        MTU AS INTEGER
        TTL AS INTEGER
        TOS AS INTEGER
        MSS AS INTEGER
        RWIN AS INTEGER
        FreeInputPkts AS INTEGER
        FreeOutputPkts AS INTEGER
        Timeout AS INTEGER
END TYPE

'Define custom error type constants and other frequently used values.
CONST errBadCall = 1
CONST errCritical = 2
CONST errNoHandles = 3
CONST errBadHandle = 4
CONST errTimeout = 5
CONST errBadSession = 6

CONST sckListening = 1
CONST sckOpen = 4
CONST sckClosed = 7

DECLARE FUNCTION Conv2IP$ (DWord AS LONG)
DECLARE FUNCTION Conv2DWord$ (inString AS STRING)
DECLARE FUNCTION HighByte% (Word AS INTEGER)
DECLARE FUNCTION LowByte% (Word AS INTEGER)

'IMPORTANT: DO NOT USE THE tcpFindInt FUNCTION AS IT IS NOW!
'IT DOES NOT WORK RIGHT. I AM WORKING ON IT.
DECLARE FUNCTION tcpFindInt% (startSegment AS INTEGER, endSegment AS INTEGER)
DECLARE FUNCTION tcpInit% (Vector AS INTEGER)
DECLARE FUNCTION tcpConnect% (RemoteIP AS STRING, RemotePort AS INTEGER)
DECLARE FUNCTION tcpStatus% (tcpHandle AS INTEGER)
DECLARE FUNCTION MakeReg% (h AS INTEGER, l AS INTEGER)
DECLARE FUNCTION tcpListen% (ListenPort AS INTEGER)
DECLARE SUB AddBuffer (textadd AS STRING)
DECLARE SUB DrawScreen ()
DECLARE SUB tcpUnload ()
DECLARE SUB tcpSetTimeout (TimeoutSeconds AS INTEGER)
DECLARE SUB RegBlank ()
DECLARE FUNCTION tcpGetData$ (tcpHandle AS INTEGER)
DECLARE SUB tcpClose (tcpHandle AS INTEGER)
DECLARE FUNCTION tcpInBuffer% (tcpHandle AS INTEGER)
DECLARE FUNCTION tcpInputQueue% (tcpHandle AS INTEGER)
DECLARE FUNCTION tcpRemoteIP$ (tcpHandle AS INTEGER)
DECLARE SUB tcpSendData (tcpHandle AS INTEGER, Data2Send AS STRING)
DECLARE SUB tcpDoIO ()
DECLARE SUB showErrorMsg ()

DECLARE SUB interruptx (intnum AS INTEGER, inreg AS Registers, outreg AS Registers)

'The debugShowReturn subroutine simply prints all the register values
'stored in tcpReturn to the screen. You really shouldn't have any reason
'to use it unless you are curious about the specifics behind NTCPDRV.
'It is mainly here for my own purposes.
DECLARE SUB debugShowReturn ()

DEFINT A-Z

'Dimension variables that are shared between all subroutines and functions.
DIM SHARED tcpCall AS Registers
DIM SHARED tcpReturn AS Registers
DIM SHARED tcpDriver AS nTCP
DIM SHARED isActive AS INTEGER
DIM SHARED strGetData AS STRING * 1500
DIM SHARED strSendData AS STRING * 1500

DIM SHARED mime(1 TO 100, 1 TO 2) AS STRING
DIM SHARED mimetotal AS INTEGER

DIM SHARED stat(0 TO 1) AS STRING
DIM SHARED scrlog(0 TO 17) AS STRING
DIM SHARED logfile$

'This makes all CPU-blocking calls timeout after 5 seconds.
tcpSetTimeout 5

SCREEN 0: WIDTH 80, 25: CLS
logfile$ = "%none"

AddBuffer "---------------------------------------"
AddBuffer "WebServ v0.9 beta (c)2006 Mike Chambers"
AddBuffer "http://www.rubbermallet.org"
AddBuffer "---------------------------------------"
AddBuffer "Please direct all bug reports to: half_eaten@yahoo.com"
AddBuffer ""
AddBuffer "Loading WEBSERV.INI configuration..."
DrawScreen
OPEN "WEBSERV.INI" FOR BINARY AS #1: filsiz = LOF(1): CLOSE #1
IF filsiz = 0 THEN
        AddBuffer "FATAL ERROR: Can't find configuration!"
        AddBuffer "Press any key to quit to DOS..."
        DO: LOOP UNTIL INKEY$ <> ""
        COLOR 7, 0: CLS : END
END IF

OPEN "WEBSERV.INI" FOR INPUT AS #1
mimenum = 1
DO UNTIL EOF(1)
        INPUT #1, directive$
        IF LEFT$(directive$, 1) <> "#" THEN
        SELECT CASE LCASE$(directive$)
        CASE "tcpvector"
                LINE INPUT #1, vect$
                tcpVect% = VAL("&H" + vect$)
                AddBuffer "Using TCPDRV interrupt vector " + HEX$(tcpVect%) + "h."
       
        CASE "folder"
                LINE INPUT #1, htdocs$
                AddBuffer "Using documents folder " + htdocs$
        
        CASE "port"
                INPUT #1, portnr%
                AddBuffer "Serving on port" + STR$(portnr%) + "."

        CASE "logfile"
                LINE INPUT #1, logtemp$
                AddBuffer "Using output text log file " + logtemp$

        CASE "mime"
                INPUT #1, fileext$
                LINE INPUT #1, mimename$
                'AddBuffer "Adding mimetype: " + fileext$ + " = " + mimename$
                mime(mimenum, 1) = fileext$
                mime(mimenum, 2) = mimename$
                mimenum = mimenum + 1

        CASE "error"
                LINE INPUT #1, err404$
                AddBuffer "Using error page " + err404$
       
        CASE "hostname"
                LINE INPUT #1, localname$
        END SELECT
        DrawScreen
        END IF
LOOP
CLOSE #1
mimetotal = mimenum - 1
AddBuffer "Loaded" + STR$(mimenum) + " mime types from configuration file."
DrawScreen

IF tcpInit(tcpVect%) <> 1 THEN END
AddBuffer ""
logfile$ = logtemp$
IF logfile$ <> "%none" THEN
        OPEN logfile$ FOR BINARY AS #2: CLOSE #2
        'Make sure file exists before opening FOR APPEND to avoid errors.
        OPEN logfile$ FOR APPEND AS #2
        PRINT #2,
        PRINT #2, "----[ LOG SECTION BEGIN @ " + TIME$ + " ON " + DATE$ + " ]----"
        CLOSE #2
END IF

AddBuffer "Web server initialized."
IF LCASE$(localname$) = "%ip" THEN localname$ = Conv2IP$(tcpDriver.LocalIP)

DO
stat(0) = "Waiting for connection on port" + STR$(portnr%) + "..."
stat(1) = STRING$(76, "°")
DrawScreen
hand% = tcpListen(portnr%)
DO
        IF INKEY$ = CHR$(27) THEN tcpClose hand%: END
LOOP UNTIL tcpStatus(hand%) = sckOpen

header$ = ""
DO
        nd$ = tcpGetData(hand%)
        IF nd$ <> "" THEN header$ = header$ + nd$
        tcpDoIO
        retn% = tcpStatus(hand%)
        IF retn% = sckClosed OR retn% = 0 THEN EXIT DO
LOOP UNTIL INSTR(1, header$, CHR$(13) + CHR$(10) + CHR$(13) + CHR$(10))
xp = INSTR(1, LCASE$(header$), "get ")
IF xp THEN
        header$ = MID$(header$, xp + 4)
        IF LEFT$(header$, 1) <> "/" THEN header$ = "/" + header$
        xp = INSTR(1, header$, " ")
        IF xp THEN header$ = LEFT$(header$, xp - 1)
        filename$ = ""
        FOR n = 1 TO LEN(header$)
                cc$ = MID$(header$, n, 1)
                SELECT CASE cc$
                CASE ""
                CASE "/"
                        filename$ = filename$ + "\"
                CASE ELSE
                        filename$ = filename$ + cc$
                END SELECT
        NEXT n
                IF RIGHT$(filename$, 1) = "\" THEN filename$ = filename$ + "INDEX.HTM"
                             
                shl$ = "DIR " + htdocs$ + filename$ + " /b >exist.tmp"
                LOCATE 1, 1: SHELL shl$
                OPEN "exist.tmp" FOR BINARY AS #1: CLOSE #1
                OPEN "exist.tmp" FOR INPUT AS #1
                IF LOF(1) THEN LINE INPUT #1, check$ ELSE check$ = ""
                CLOSE #1
                KILL "exist.tmp"
                IF check$ = "" THEN errorpage = 1 ELSE errorpage = 0

                FOR n = LEN(filename$) TO 1 STEP -1
                        IF MID$(filename$, n, 1) = "." THEN EXIT FOR
                NEXT n
                FOR num = 1 TO mimetotal
                        IF LCASE$(MID$(filename$, n)) = LCASE$(mime(num, 1)) THEN
                                content$ = mime(num, 2)
                                EXIT FOR
                        END IF
                NEXT num
                IF num > mimetotal THEN content$ = "text/text"
                IF errorpage = 1 THEN content$ = "text/html"
               
                buffadd$ = DATE$ + " " + TIME$ + " - " + tcpRemoteIP(hand%) + " GET " + filename$
                IF errorpage = 0 THEN
                        OPEN htdocs$ + filename$ FOR BINARY AS #1
                        buffadd$ = buffadd$ + " (" + MID$(STR$(LOF(1)), 2) + " bytes)"
                ELSE
                        OPEN err404$ FOR BINARY AS #1
                        buffadd$ = buffadd$ + " (404 not found)"
                END IF
                AddBuffer buffadd$
                DrawScreen

                tcpSendData hand%, "HTTP/1.1 200 OK" + CHR$(13) + CHR$(10)
                tcpSendData hand%, "Content-type: " + content$ + CHR$(13) + CHR$(10)
                tcpSendData hand%, "Content-length:" + STR$(LOF(1)) + CHR$(13) + CHR$(10) + CHR$(13) + CHR$(10)

                ratio! = 76 / 100
                exitserv = 0
                DO UNTIL EOF(1) OR LOF(1) <= LOC(1)
                IF INKEY$ = CHR$(27) THEN exitserv = 1: EXIT DO
                a$ = SPACE$(1500)
                IF LOF(1) - LOC(1) < 1500 THEN a$ = SPACE$(LOF(1) - LOC(1))
                GET #1, , a$
                IF errorpage = 1 THEN
                        xp = INSTR(1, a$, "%date")
                        IF xp THEN a$ = LEFT$(a$, xp - 1) + DATE$ + MID$(a$, xp + 5)

                        xp = INSTR(1, a$, "%time")
                        IF xp THEN a$ = LEFT$(a$, xp - 1) + TIME$ + MID$(a$, xp + 5)

                        xp = INSTR(1, a$, "%host")
                        IF xp THEN a$ = LEFT$(a$, xp - 1) + localname$ + MID$(a$, xp + 5)
                END IF
                tcpSendData hand%, a$
               
                retn% = tcpStatus(hand%)
                IF retn% = 0 OR retn% = sckClosed THEN EXIT DO

                pct% = FIX((LOC(1) / LOF(1)) * 100)
                stat(0) = "Sending file: " + filename$ + " (" + MID$(STR$(LOC(1)), 2) + " of" + STR$(LOF(1)) + ") -" + STR$(pct%) + "%"
                stat(1) = STRING$(FIX(pct% * ratio!), "²") + STRING$(76 - FIX(pct% * ratio!), "°")
                LOCATE 2, 1
                PRINT "º " + LEFT$(stat(0) + SPACE$(76), 76) + " º";
                PRINT "º " + LEFT$(stat(1) + SPACE$(76), 76) + " º";
                LOOP
                CLOSE #1
                IF exitserv = 1 THEN EXIT DO
END IF
tcpClose hand%
LOOP
tcpClose hand% 'Just to make sure.
END

SUB AddBuffer (textadd AS STRING)
IF logfile$ <> "%none" THEN OPEN logfile$ FOR APPEND AS #2

DIM tempText AS STRING
tempText = textadd
DO
FOR tn = 1 TO 17
        scrlog(tn - 1) = scrlog(tn)
NEXT tn
scrlog(17) = LEFT$(tempText, 76)
IF logfile$ <> "%none" THEN PRINT #2, LEFT$(tempText, 76)

tempText = MID$(tempText, 77)
LOOP UNTIL tempText = ""
IF logfile$ <> "%none" THEN CLOSE #2
END SUB

DEFSNG A-Z
FUNCTION Conv2DWord$ (inString AS STRING)
'The inString variable should be an IPv4 address in the standard
'format of x.x.x.x

DIM tempArray(3) AS STRING * 1
DIM tempVal AS INTEGER

tempVal = INSTR(1, inString, ".")
IF tempVal THEN
        tempArray(0) = CHR$(VAL(LEFT$(inString, tempVal)))
        inString = MID$(inString, tempVal + 1)
ELSE
        EXIT FUNCTION
END IF

tempVal = INSTR(1, inString, ".")
IF tempVal THEN
        tempArray(1) = CHR$(VAL(LEFT$(inString, tempVal)))
        inString = MID$(inString, tempVal + 1)
ELSE
        EXIT FUNCTION
END IF

tempVal = INSTR(1, inString, ".")
IF tempVal THEN
        tempArray(2) = CHR$(VAL(LEFT$(inString, tempVal)))
        inString = MID$(inString, tempVal + 1)
ELSE
        EXIT FUNCTION
END IF

tempArray(3) = CHR$(VAL(inString))
Conv2DWord$ = tempArray(0) + tempArray(1) + tempArray(2) + tempArray(3)
END FUNCTION

FUNCTION Conv2IP$ (DWord AS LONG)
DIM tempStr AS STRING * 4

tempStr = MKL$(DWord)
Conv2IP$ = MID$(STR$(ASC(LEFT$(tempStr, 1))), 2) + "." + MID$(STR$(ASC(MID$(tempStr, 2, 1))), 2) + "." + MID$(STR$(ASC(MID$(tempStr, 3, 1))), 2) + "." + MID$(STR$(ASC(RIGHT$(tempStr, 1))), 2)
END FUNCTION

SUB debugShowReturn
PRINT "   ax:" + STR$(tcpReturn.ax) + " (" + HEX$(tcpReturn.ax) + "h)"
PRINT "   bx:" + STR$(tcpReturn.bx) + " (" + HEX$(tcpReturn.bx) + "h)"
PRINT "   cx:" + STR$(tcpReturn.cx) + " (" + HEX$(tcpReturn.cx) + "h)"
PRINT "   dx:" + STR$(tcpReturn.dx) + " (" + HEX$(tcpReturn.dx) + "h)"
PRINT "   bp:" + STR$(tcpReturn.bp) + " (" + HEX$(tcpReturn.bp) + "h)"
PRINT "   si:" + STR$(tcpReturn.si) + " (" + HEX$(tcpReturn.si) + "h)"
PRINT "   di:" + STR$(tcpReturn.di) + " (" + HEX$(tcpReturn.di) + "h)"
PRINT "flags:" + STR$(tcpReturn.flags) + " (" + HEX$(tcpReturn.flags) + "h)"
PRINT "   ds:" + STR$(tcpReturn.ds) + " (" + HEX$(tcpReturn.ds) + "h)"
PRINT "   es:" + STR$(tcpReturn.es) + " (" + HEX$(tcpReturn.es) + "h)"
END SUB

DEFINT A-Z
SUB DrawScreen
COLOR 7, 0: LOCATE 1, 1
PRINT "É͹ Transfer Ì" + STRING$(65, "Í") + "»";
PRINT "º " + LEFT$(stat(0) + SPACE$(76), 76) + " º";
PRINT "º " + LEFT$(stat(1) + SPACE$(76), 76) + " º";
PRINT "È" + STRING$(78, "Í") + "¼";
PRINT "É͹ Log Ì" + STRING$(70, "Í") + "»";
FOR tn = 0 TO 17
        PRINT "º " + LEFT$(scrlog(tn) + SPACE$(76), 76) + " º";
NEXT tn
PRINT "È" + STRING$(78, "Í") + "¼";
LOCATE 25, 45
PRINT "To shut down this server, hit ESC.";
END SUB

DEFSNG A-Z
FUNCTION HighByte% (Word AS INTEGER)
HighByte% = ASC(RIGHT$(MKI$(Word), 1))
END FUNCTION

FUNCTION LowByte% (Word AS INTEGER)
LowByte% = ASC(LEFT$(MKI$(Word), 1))
END FUNCTION

FUNCTION MakeReg% (h AS INTEGER, l AS INTEGER)
MakeReg% = CVI(CHR$(l) + CHR$(h))
END FUNCTION

SUB RegBlank
tcpCall.ax = 0
tcpCall.bx = 0
tcpCall.cx = 0
tcpCall.dx = 0
tcpCall.bp = 0
tcpCall.si = 0
tcpCall.di = 0
tcpCall.flags = 0
tcpCall.ds = 0
tcpCall.es = 0
END SUB

SUB showErrorMsg
PRINT "ERROR CODE" + STR$(tcpDriver.ErrorCode) + "!!! ";
SELECT CASE tcpDriver.ErrorCode
        CASE errBadCall
                PRINT "err_badcall"
        CASE errCritical
                PRINT "err_critical"
        CASE errNoHandles
                PRINT "err_nohandles"
        CASE errBadHandle
                PRINT "err_badhandle"
        CASE errTimeout
                PRINT "err_timeout"
        CASE errBadSession
                PRINT "err_badsession"
END SELECT
END SUB

SUB tcpClose (tcpHandle AS INTEGER)
tcpCall.ax = MakeReg(&H11, 1)
tcpCall.bx = tcpHandle
tcpCall.dx = 0

CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)
tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END SUB

FUNCTION tcpConnect% (RemoteIP AS STRING, RemotePort AS INTEGER)
DIM tempRemoteIP AS STRING
tempRemoteIP = Conv2DWord(RemoteIP)

tcpCall.ax = MakeReg(&H10, 0)
tcpCall.bx = 0
tcpCall.cx = RemotePort
tcpCall.dx = tcpDriver.Timeout
tcpCall.di = CVI(LEFT$(tempRemoteIP, 2))
tcpCall.si = CVI(RIGHT$(tempRemoteIP, 2))
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)

dummyLocalPortNr% = tcpReturn.ax
'PRINT "Local port:"; dummyLocalPortNr%

tcpConnect% = tcpReturn.bx 'Makes this function return the TCPDRV handle number.
tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END FUNCTION

SUB tcpDoIO
'Allows TCPDRV to perform processing of data. THIS MUST BE CALLED REGULARLY
'for packets to be processed! If you don't do this, TCPDRV will eventually
'crash when it runs out of storage space for input and output queues.

'RegBlank
tcpCall.ax = MakeReg(&H2, 0)
tcpCall.dx = 0 'tcpDriver.Timeout
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)

tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END SUB

FUNCTION tcpFindInt% (startSegment AS INTEGER, endSegment AS INTEGER)
'If either or both startSegment or endSegment is zero, use standard NTCPDRV
'segment range of 60h to 7Fh for scanning.
IF startSegment = 0 OR endSegment = 0 THEN
        startSegment = &H60
        endSegment = &H7F
END IF

DIM tempScan AS INTEGER
DIM tempScan2 AS LONG

FOR tempScan = startSegment TO endSegment
        DEF SEG = tempScan
        FOR tempScan2 = 0 TO 65535
                'If current offset in this segment is the letter T
                '(ASCII value 84), then check the next 7 bytes for "CP_DRVR"
                '(excluding quotes). If they are, we've found our NTCPDRV
                'interrupt vector! :)
                IF PEEK(tempScan2) = 84 THEN
                        'This next line is huge, sorry. I know I could have had
                        'it convert each of these bytes to its ASCII string value
                        'with CHR$(PEEK(blah)) and put them together as tempStr$
                        'then say `IF tempStr$ = "CP_DRVR" THEN' for much sexier
                        'looking code, but I want these routines to be as fast as
                        'possible. We would waste valuable CPU cycles with that
                        'process. What if somebody is running this on an 8088? :)
                        IF PEEK(tempScan2 + 1) = 67 AND PEEK(tempScan2 + 2) = 80 AND PEEK(tempScan2 + 3) = 95 AND PEEK(tempScan2 + 4) = 68 AND PEEK(tempScan2 + 5) = 82 AND PEEK(tempScan2 + 6) = 86 AND PEEK(tempScan2 + 7) = 82 THEN
                                'Got our segment, so return it!
                                tcpFindInt% = tempScan + 1
                                EXIT FUNCTION
                        END IF

                END IF
        NEXT tempScan2
NEXT tempScan
tcpFindInt% = 0
'Yeah, yeah... I know tcpFindInt% should already be zero since we could
'not locate a NTCPDRV interrupt segment in the given range, but now we
'KNOW it is, right? Can't go wrong with that! :)
END FUNCTION

FUNCTION tcpGetData$ (tcpHandle AS INTEGER)
tcpCall.ax = MakeReg(&H12, 1)
tcpCall.bx = tcpHandle
tcpCall.es = VARSEG(strGetData)
tcpCall.di = VARPTR(strGetData)
tcpCall.cx = 1500
tcpCall.dx = 0
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)
tcpGetData$ = LEFT$(strGetData, tcpReturn.ax)

tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END FUNCTION

FUNCTION tcpInBuffer% (tcpHandle AS INTEGER)
dummy1% = tcpStatus%(tcpHandle)
IF isActive > 0 THEN
        tcpInBuffer% = tcpReturn.ax
END IF
tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END FUNCTION

FUNCTION tcpInit% (Vector AS INTEGER)
'If given vector value is zero, assume the usual vector 61h.
IF Vector = 0 THEN Vector = &H61

tcpCall.ax = MakeReg(&H0, &HFF)
CALL interruptx(Vector, tcpCall, tcpReturn)

'This next line sets tcpDriver.Enabled to zero, sets the tcpInit function
'return value to zero, and exits the function immediately if the interrupt
'call did not return the expected value of zero for a functional TCPDRV
'vector at the specified segment.
IF tcpReturn.ax <> 0 THEN tcpDriver.Enabled = 0: tcpInit% = 0: EXIT FUNCTION

tcpInit% = 1
tcpDriver.Enabled = 1
tcpDriver.IntVector = Vector
DEF SEG = tcpReturn.es
offset = tcpReturn.di

'The following lines of code parse all of the network-related data
'from the pointer value returned by TCPDRV.
tcpDriver.LocalIP = CVL(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)) + CHR$(PEEK(offset + 2)) + CHR$(PEEK(offset + 3)))
offset = offset + 4

tcpDriver.LocalNetmask = CVL(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)) + CHR$(PEEK(offset + 2)) + CHR$(PEEK(offset + 3)))
offset = offset + 4

tcpDriver.LocalGateway = CVL(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)) + CHR$(PEEK(offset + 2)) + CHR$(PEEK(offset + 3)))
offset = offset + 4

tcpDriver.LocalDNS = CVL(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)) + CHR$(PEEK(offset + 2)) + CHR$(PEEK(offset + 3)))
offset = offset + 4

tcpDriver.Timeserver = CVL(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)) + CHR$(PEEK(offset + 2)) + CHR$(PEEK(offset + 3)))
offset = offset + 4

tcpDriver.MTU = CVI(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)))
offset = offset + 2

tcpDriver.TTL = PEEK(offset)
tcpDriver.TOS = PEEK(offset + 1)
offset = offset + 4 'Skip two unused bytes after TTL and TOS data.

tcpDriver.MSS = CVI(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)))
offset = offset + 2

tcpDriver.RWIN = CVI(CHR$(PEEK(offset)) + CHR$(PEEK(offset + 1)))
offset = offset + 4 'Skip two unused bytes after RWIN data.

'The following code segment gets the local domain string.
'TCPDRV allocates 256 bytes for this string, because that is the maximum
'allowed length of a domain name. If the string is shorter than 256 bytes,
'then TCPDRV fills the remainder of the string with ASCII code 255. The
'following code stops reading from the domain string as soon is it sees
'the first byte with a value of 255 for performance reasons.
DIM tempNum AS INTEGER
DIM tempCurByte AS STRING * 1
DIM tempStr AS STRING
FOR tempNum = offset TO offset + 255
        tempCurByte = CHR$(PEEK(tempNum))
        IF tempCurByte = CHR$(255) THEN
                tcpDriver.DomainLen = tempNum - offset
                tcpDriver.LocalDomain = tempStr
        ELSE
                tempStr = tempStr + tempCurByte
        END IF
NEXT tempNum

tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END FUNCTION

FUNCTION tcpListen% (ListenPort AS INTEGER)
tcpCall.ax = MakeReg(&H10, 1)
tcpCall.bx = ListenPort
tcpCall.cx = 0
tcpCall.dx = tcpDriver.Timeout
tcpCall.si = 0
tcpCall.di = 0
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)

tcpListen% = tcpReturn.bx
tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END FUNCTION

DEFINT A-Z
FUNCTION tcpRemoteIP$ (tcpHandle AS INTEGER)
checkstat% = tcpStatus(tcpHandle)
IF checkstat% <> sckOpen THEN tcpRemoteIP$ = "0.0.0.0": EXIT FUNCTION

DEF SEG = tcpReturn.es
stateoffset = tcpReturn.di
ipdest$ = Conv2IP(CVL(CHR$(PEEK(stateoffset + 6)) + CHR$(PEEK(stateoffset + 7)) + CHR$(PEEK(stateoffset + 8)) + CHR$(PEEK(stateoffset + 9))))
'ipsrce$ = Conv2IP(CVL(CHR$(PEEK(stateoffset)) + CHR$(PEEK(stateoffset + 1)) + CHR$(PEEK(stateoffset + 2)) + CHR$(PEEK(stateoffset + 3))))

tcpRemoteIP$ = ipdest$
END FUNCTION

DEFSNG A-Z
SUB tcpSendData (tcpHandle AS INTEGER, Data2Send AS STRING)
strSendData = Data2Send

tcpCall.ax = MakeReg(&H13, 4)
tcpCall.bx = tcpHandle
tcpCall.es = INT(VARSEG(strSendData))
tcpCall.di = INT(VARPTR(strSendData))
tcpCall.cx = INT(LEN(Data2Send))
tcpCall.dx = tcpDriver.Timeout
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)

tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
tcpDoIO
END SUB

SUB tcpSetTimeout (TimeoutSeconds AS INTEGER)
tcpDriver.Timeout = INT(TimeoutSeconds * 18.2)
END SUB

FUNCTION tcpStatus% (tcpHandle AS INTEGER)
tcpCall.ax = MakeReg(&H14, 0)
tcpCall.bx = tcpHandle
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)

DEF SEG = tcpReturn.es
stateoffset = tcpReturn.di
ipsrce$ = Conv2IP(CVL(CHR$(PEEK(stateoffset)) + CHR$(PEEK(stateoffset + 1)) + CHR$(PEEK(stateoffset + 2)) + CHR$(PEEK(stateoffset + 3))))
ipdest$ = Conv2IP(CVL(CHR$(PEEK(stateoffset + 4)) + CHR$(PEEK(stateoffset + 5)) + CHR$(PEEK(stateoffset + 6)) + CHR$(PEEK(stateoffset + 7))))
ipprot% = PEEK(stateoffset + 8)
active% = PEEK(stateoffset + 9)
ifActive = active%
'PRINT "======================"
'PRINT "Debug: Status data return values for TCP handle"; tcpHandle
'PRINT
'PRINT "Error code:"; LowByte(tcpReturn.dx)
'PRINT "TCP state:"; HighByte(tcpReturn.dx)
'PRINT
'PRINT "Bytes available for reading:"; tcpReturn.ax
'PRINT "Bytes still being transmitted:"; tcpReturn.cx
'PRINT
'PRINT "Session info gathered from pointer" + STR$(tcpReturn.es) + ":" + MID$(STR$(tcpReturn.di), 2)
'PRINT "IP source: " + ipsrce$
'PRINT "IP dest: " + ipsrce$
'PRINT "IP prot:"; ipprot%
'PRINT "Active:"; active%
'PRINT "======================"
tcpStatus% = HighByte(tcpReturn.dx)
tcpDriver.ErrorCode = LowByte(tcpReturn.dx)
END FUNCTION

SUB tcpUnload
tcpCall.ax = MakeReg(&H1, 0)
CALL interruptx(tcpDriver.IntVector, tcpCall, tcpReturn)

tcpDriver.Enabled = 0
END SUB
 
Last edited:
btw, in the next few days i might put out final v1.0 release...

i am thinking of adding directory browsing (you could disable in config file) and (maybe) multi-threading for multiple requests at a time. :-D

also, just to note... when i am running this on my 386 DX/40, and i connect to it over my LAN i am getting a stable 75 KB/s send rate. i'm going to give it a test on my 286/12 MHz later tonight and see how it does.. i'm estimating maybe around 35-45 KB/s at the most. that'd be a decent for a PC that old.

i'd love to see what somebody gets on an 8088 like an IBM 5150/5160 with a LAN card (as opposed to one of those parallel port ethernet converter)... i have an 8088 board with an '82 fujitsu clone chip but no 8-bit drive adapters. :(
 
Last edited:
Oh, by the way- I notice a VAST difference in the contents of rubbermallet.org, so to say ;)

If you need some collection of packet drivers btw, I think I have an archive somewhere.
 
Oh, by the way- I notice a VAST difference in the contents of rubbermallet.org, so to say ;)

If you need some collection of packet drivers btw, I think I have an archive somewhere.

lol, yeah the different if pretty noticeable :)
 
Back
Top