2016-12-15 18:57:30 +00:00
' Applesoft BASIC Webserver
' by Vince Weaver <vince@deater.net>
'
2016-11-23 21:33:28 +00:00
1 REM *** Setup UTHERNET II - W5100
2016-12-14 16:29:18 +00:00
' SLOT0=$C080 49280 SLOT4=$C0C0 49344
' SLOT1=$C090 49296 SLOT5=$C0D0 49360
' SLOT2=$C0A0 49312 SLOT6=$C0E0 49376
' SLOT3=$C0B0 49328 SLOT7=$C0F0 49392
'
' Set up the memory addresses to use
'
2 REM *** OURS IS IN SLOT3 ($C0B0)
3 SLOT = 49328 : REM * * * $ C0B0
4 MR = SLOT + 4 : REM * * * MODE REGISTER C0B4
5 HA = SLOT + 5 : LA = SLOT + 6 : REM * * * HIGH / LOW ADDR $ C0B5 , $ C0B6
7 DP = SLOT + 7 : REM * * * DATA PORT $ C0B7
'
' Init the W5100
'
2016-11-23 21:33:28 +00:00
10 REM *** Init W5100
2016-12-05 19:48:22 +00:00
12 POKE MR , 128 : REM RESET W5100
14 POKE MR , 3 : REM AUTOINCREMENT
2016-12-14 16:29:18 +00:00
20 REM *** Setup MAC Address 41:50:50:4c:45:32
2016-12-05 19:48:22 +00:00
22 POKE HA , 0 : POKE LA , 9
2016-12-14 16:29:18 +00:00
23 POKE DP , 65 : POKE DP , 80 : POKE DP , 80 : POKE DP , 76 : POKE DP , 69 : POKE DP , 50
30 REM *** Setup IP Address 192.168.8.15
2016-12-05 19:48:22 +00:00
32 POKE LA , 15
2016-12-14 16:29:18 +00:00
33 POKE DP , 192 : POKE DP , 168 : POKE DP , 8 : POKE DP , 15
2016-11-23 21:33:28 +00:00
40 PRINT "UTHERNET II READY: 192.168.8.15"
2016-12-14 16:29:18 +00:00
'
2016-12-16 21:51:54 +00:00
' Setup Machine Language Memcpy routine
' NOTE! This code assumes the Uthernet is in slot 3
' FIXME: patch on the fly once it works
' See Appendix 1 at the end of this for more details
2016-12-16 21:47:25 +00:00
'
50 FOR I = 0 TO 72 : READ X: POKE 768 + I , X: NEXT I
51 DATA 169 , 0 , 133 , 6 , 169 , 64 , 133 , 7 , 162 , 11 , 240 , 36 , 160 , 0 , 177 , 6
52 DATA 141 , 183 , 192 , 213 , 9 , 208 , 15 , 217 , 8 , 0 , 208 , 10 , 169 , 64 , 141 , 181
53 DATA 192 , 169 , 0 , 141 , 182 , 192 , 200 , 208 , 229 , 230 , 7 , 202 , 208 , 224 , 162 , 10
54 DATA 177 , 6 , 141 , 183 , 192 , 217 , 8 , 0 , 208 , 10 , 169 , 64 , 141 , 181 , 192 , 169
55 DATA 0 , 141 , 182 , 192 , 200 , 202 , 208 , 232 , 96
'
2016-12-14 16:29:18 +00:00
' Setup Socket 0
'
100 REM *** Setup Socket 0
2016-12-06 20:17:07 +00:00
102 PRINT "** Setting up Socket 0"
2016-12-05 19:48:22 +00:00
105 POKE HA , 0 : POKE LA , 26 : REM RX MEMSIZE
110 POKE DP , 3 : REM 8 kB RX buffer
115 POKE DP , 3 : REM 8 kB TX buffer
200 REM *** Setup TCP MODE on SOCKET 0
205 POKE HA , 4 : POKE LA , 0 : REM * * * 0 x400 mode
210 POKE DP , 65 : REM * * * 0 x41 MAC FILTER ( non - promisc ) TCP
300 REM ** Setup Source PORT
2016-12-06 20:17:07 +00:00
303 PRINT "** Setting up to use TCP port 80"
305 POKE HA , 4 : POKE LA , 4 : REM * * * 0 x404 port
310 POKE DP , 0 : POKE DP , 80 : REM * * * http port 80
2016-12-14 16:29:18 +00:00
'
' OPEN the socket
'
2016-12-05 19:48:22 +00:00
400 REM *** OPEN socket
2016-12-06 20:17:07 +00:00
404 PRINT "** OPENing socket"
2016-12-05 19:48:22 +00:00
405 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
410 POKE DP , 1 : REM * * * OPEN
2016-12-06 20:17:07 +00:00
'
2016-12-14 16:29:18 +00:00
' Check return value
'
2016-12-05 19:48:22 +00:00
500 REM *** Check if opened
2016-12-06 20:17:07 +00:00
505 POKE HA , 4 : POKE LA , 3 : REM * * * 0 x403 status register
510 RE = PEEK ( DP )
515 PRINT "** STATUS IS " ; RE ;
520 IF RE = 19 THEN PRINT " OPENED" : GOTO 600
2016-12-14 16:29:18 +00:00
530 IF RE = 0 THEN PRINT " CLOSED, ERROR" : GOTO 5000
540 PRINT "UNKNOWN ERROR " ; RE
550 GOTO 5000
'
' LISTEN on the socket
2016-12-06 20:17:07 +00:00
'
600 REM *** Connection opened, Listen
605 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
610 POKE DP , 2 : REM * * * LISTEN
2016-12-14 16:29:18 +00:00
'
' Check return value
'
2016-12-06 20:17:07 +00:00
620 REM *** Check if successful
625 POKE HA , 4 : POKE LA , 3 : REM * * * 0 x403 status register
630 RE = PEEK ( DP )
635 PRINT "** STATUS IS " ; RE ;
640 IF RE = 20 THEN PRINT " LISTENING" : GOTO 700
2016-12-14 16:29:18 +00:00
650 IF RE = 0 THEN PRINT " CLOSED, ERROR" : GOTO 5000
655 PRINT "UNKNOWN ERROR " ; RE
675 GOTO 5000
'
' Wait for incoming connection
2016-12-06 20:17:07 +00:00
'
700 REM *** Wait for incoming connection
705 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
710 POKE DP , 2 : REM * * * LISTEN
2016-12-14 16:29:18 +00:00
'
' Check for result
'
2016-12-06 20:17:07 +00:00
720 REM *** Check if successful
725 POKE HA , 4 : POKE LA , 3 : REM * * * 0 x403 status register
730 RE = PEEK ( DP )
2016-12-14 18:47:46 +00:00
740 IF RE = 23 THEN GOTO 800: REM ESTABLISHED
2016-12-14 16:29:18 +00:00
745 IF RE < > 20 THEN PRINT "WAITING: UNEXPECTED STATUS=" ; RE
2016-12-06 20:17:07 +00:00
750 GOTO 700: REM * * * Repeat until connected
'
2016-12-14 18:47:46 +00:00
' Established, repeat waiting for incoming data
2016-12-14 16:29:18 +00:00
'
2016-12-14 18:47:46 +00:00
800 PRINT "ESTABLISHED"
2016-12-06 20:17:07 +00:00
802 POKE HA , 4 : POKE LA , 38 : REM * * * 0 x426 Received Size
805 SH = PEEK ( DP ) : SL = PEEK ( DP )
810 SI = ( SH * 256 ) + SL
820 IF SI < > 0 THEN GOTO 900
2016-12-14 16:29:18 +00:00
'
' Should we delay? busy polling seems wasteful
'
2016-12-06 20:17:07 +00:00
830 REM DELAY?
2016-12-14 16:29:18 +00:00
840 GOTO 802
'
' We have some data, let's read it
2016-12-06 20:17:07 +00:00
'
900 POKE HA , 4 : POKE LA , 40 : REM * * * 0 x428 Received ptr
2016-12-07 22:05:30 +00:00
905 OH = PEEK ( DP ) : OL = PEEK ( DP )
2016-12-14 16:29:18 +00:00
910 RF = ( OH * 256 ) + OL
920 REM *** MASK WITH 0x1ff
2016-12-14 18:47:46 +00:00
925 R % = RF / 8192 : RM = RF - ( 8192 * R % )
930 RA = RM + 24576 : REM $ 6000
940 PRINT "READ OFFSET=" ; RM ; " READ ADDRESS=" ; RA ; " READ SIZE=" ; SI
2016-12-14 16:29:18 +00:00
'
2016-12-14 21:52:54 +00:00
' Check for buffer wraparound
'
942 BW = 0
945 IF ( SI + TA > = 32768 ) THEN BW = 1 : BO = 32768 - TA: PRINT "RX BUFFER WRAPAROUND IN " ; BO
'
2016-12-14 16:29:18 +00:00
' Print received packet
2016-12-06 20:17:07 +00:00
'
1000 REM *** PRINT PACKET
2016-12-14 21:15:14 +00:00
1001 FL = 1 : FL$ = ""
2016-12-14 16:29:18 +00:00
1003 R % = RA / 256
1005 POKE HA , R % : POKE LA , RA - ( R % * 256 )
2016-12-06 20:17:07 +00:00
1010 FOR I = 1 TO SI
2016-12-14 19:20:23 +00:00
1020 C = PEEK ( DP ) : C$ = CHR$ ( C )
2016-12-14 21:15:14 +00:00
1025 IF FL = 1 THEN FL$ = FL$ + C$
1027 IF C = 10 THEN FL = 0
2016-12-14 19:20:23 +00:00
1030 IF C < > 10 THEN PRINT C$ ;
2016-12-14 21:52:54 +00:00
1032 IF BW = 0 THEN GOTO 1040
1033 BO = BO -1 : IF BO = 0 THEN POKE HA , 96 : POKE LA , 0 : BW = 0
2016-12-06 20:17:07 +00:00
1040 NEXT I
2016-12-14 21:52:54 +00:00
'
' Deal with first line
'
2016-12-14 21:15:14 +00:00
1050 PRINT "FIRST LINE=" ; FL$
1060 IF LEFT$ ( FL$ , 3 ) < > "GET" GOTO 7000
1065 N$ = ""
1070 FOR I = 6 TO LEN ( FL$ )
1075 M$ = MID$ ( FL$ , I , 1 )
1080 IF M$ = " " GOTO 1090
1085 N$ = N$ + M$
1087 NEXT I
1090 IF N$ = "" THEN N$ = "index.html"
1095 PRINT "SENDING FILE: " ; N$
2016-12-06 20:17:07 +00:00
'
2016-12-07 02:50:58 +00:00
' TODO: handle wraparound of 8kb buffer
'
2016-12-14 16:29:18 +00:00
'
2016-12-14 21:52:54 +00:00
' Update read pointer
2016-12-14 16:29:18 +00:00
'
2016-12-07 02:50:58 +00:00
1100 REM *** Update read pointer
2016-12-14 16:29:18 +00:00
1110 POKE HA , 4 : POKE LA , 40 : REM * * * 0 x428 Received ptr
2016-12-14 17:18:06 +00:00
1120 RA = RF + SI
2016-12-14 16:29:18 +00:00
1130 R % = RA / 256
1140 POKE DP , R % : POKE DP , RA - ( R % * 256 )
2016-12-14 18:47:46 +00:00
1150 REM *** RECEIVE
1160 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
1170 POKE DP , 64 : REM * * * RECV
2016-12-14 19:20:23 +00:00
'
' Load file from disk
'
1200 REM *** LOAD FILE
2016-12-14 21:15:14 +00:00
1202 X$ = RIGHT$ ( N$ , 3 ) : M$ = "text/html"
2016-12-15 18:40:03 +00:00
1203 IF X$ = "txt" THEN M$ = "text/plain"
1204 IF X$ = "png" THEN M$ = "image/png"
1205 IF X$ = "jpg" THEN M$ = "image/jpg"
2016-12-16 17:33:46 +00:00
1206 IF X$ = "ico" THEN M$ = "image/x-icon"
1207 IF N$ = "teapot.html" GOTO 9000
1208 ONERR GOTO 8000
1209 PRINT "LOADING " ; N$
2016-12-14 21:15:14 +00:00
1210 PRINT CHR$ ( 4 ) + "BLOAD " ; N$
1215 POKE 216 , 0 : REM CANCEL ONERR
2016-12-14 19:20:23 +00:00
1220 FS = PEEK ( 43616 ) + 256 * PEEK ( 43617 ) : REM FILESIZE
2016-12-15 18:40:03 +00:00
1225 PRINT "DONE LOADING"
2016-12-14 19:20:23 +00:00
' assume loaded at 0x4000, text page 2
' and that max size is 8kb
2016-12-14 21:15:14 +00:00
1240 A$ = "HTTP/1.1 200 OK" + CHR$ ( 13 ) + CHR$ ( 10 )
1250 A$ = A$ + "Server: VMW-web" + CHR$ ( 13 ) + CHR$ ( 10 )
1260 A$ = A$ + "Content-Length: " + STR$ ( FS ) + CHR$ ( 13 ) + CHR$ ( 10 )
1280 A$ = A$ + "Content-Type: " + M$ + CHR$ ( 13 ) + CHR$ ( 10 ) + CHR$ ( 13 ) + CHR$ ( 10 )
2016-12-14 16:29:18 +00:00
'
2016-12-14 21:15:14 +00:00
1380 PRINT "SENDING:" : PRINT A$
2016-12-15 18:40:03 +00:00
1385 C = 0
2016-12-14 16:29:18 +00:00
'
2016-12-14 21:15:14 +00:00
' read TX free size reg (0x420)
2016-12-14 17:18:06 +00:00
'
2016-12-14 19:20:23 +00:00
1700 SI = LEN ( A$ ) + FS
1710 IF ( SI > 8192 ) THEN PRINT "FILE TOO BIG!" : REM GOTO 403 ?
2016-12-14 18:47:46 +00:00
1800 POKE HA , 4 : POKE LA , 32 : REM * * * 0 x420 FREESIZE
1810 OH = PEEK ( DP ) : OL = PEEK ( DP )
1815 FR = ( OH * 256 ) + OL
1820 PRINT "FREE: " ; FR
1830 IF SI > FR GOTO 1800: REM REPEAT UNTIL FREE
'
' Read tx offset
'
2016-12-14 17:18:06 +00:00
1900 POKE HA , 4 : POKE LA , 36 : REM * * * 0 x424 TX write ptr
2016-12-07 22:05:30 +00:00
1905 OH = PEEK ( DP ) : OL = PEEK ( DP )
1910 TF = ( OH * 256 ) + OL
2016-12-14 17:18:06 +00:00
1920 REM *** MASK WITH 0x1ff
2016-12-14 18:47:46 +00:00
1925 T % = TF / 8192 : TM = TF - ( 8192 * T % )
1930 TA = TM + 16384 : REM $ 4000
1940 PRINT "OH/OL=" ; OH ; "/" ; OL ; " TX OFFSET=" ; TM ; " TX ADDRESS=" ; TA ; " TX SIZE=" ; SI
2016-12-14 17:18:06 +00:00
'
2016-12-14 21:52:54 +00:00
' Check for buffer wraparound
'
2016-12-16 21:47:25 +00:00
1942 BW = 0 : BO = 0
2016-12-14 21:52:54 +00:00
1945 IF ( SI + TA > = 24576 ) THEN BW = 1 : BO = 24576 - TA: PRINT "TX BUFFER WRAPAROUND IN " ; BO
'
2016-12-14 17:18:06 +00:00
' Write data to TX buffer
2016-12-16 21:47:25 +00:00
' First write header
2016-12-14 17:18:06 +00:00
'
2016-12-07 22:17:21 +00:00
2000 T % = TA / 256
2005 POKE HA , T % : POKE LA , TA - ( T % * 256 )
2016-12-14 19:20:23 +00:00
2010 FOR I = 1 TO LEN ( A$ )
2015 POKE DP , ASC ( MID$ ( A$ , I , 1 ) )
2016-12-14 21:52:54 +00:00
2017 IF BW = 0 THEN GOTO 2020
2016-12-14 22:20:28 +00:00
2018 BO = BO -1 : IF BO = 0 THEN POKE HA , 64 : POKE LA , 0 : BW = 0
2016-12-14 19:20:23 +00:00
2020 NEXT I
2016-12-16 21:47:25 +00:00
'
' Write disk part
'
2016-12-14 19:20:23 +00:00
2025 FOR I = 1 TO FS
2016-12-15 18:40:03 +00:00
2026 C = C + 1 : IF C = 50 THEN PRINT "." ; : C = 0
2016-12-14 19:20:23 +00:00
2030 POKE DP , PEEK ( 16383 + I )
2016-12-14 21:52:54 +00:00
2032 IF BW = 0 THEN GOTO 2035
2016-12-14 22:20:28 +00:00
2033 BO = BO -1 : IF BO = 0 THEN POKE HA , 64 : POKE LA , 0 : BW = 0
2016-12-14 19:20:23 +00:00
2035 NEXT I
2016-12-15 18:40:03 +00:00
2040 PRINT
2016-12-14 17:18:06 +00:00
'
2016-12-16 21:47:25 +00:00
' The above is slow
' Intead use our machine language routine
'
'2025 B%=BO/256:POKE 9,B%:POKE 8,BO-(B%*256)
'2027 B%=FS/256:POKE 11,B%:POKE 10,FS-(B%*256)
'2030 CALL 768
'
2016-12-14 17:18:06 +00:00
' Update TX write ptr
'
2016-12-07 22:05:30 +00:00
2050 REM ** UPDATE TX WRITE PTR
2060 POKE HA , 4 : POKE LA , 36 : REM * * * 0 x424 TX write ptr
2016-12-14 17:18:06 +00:00
2075 TA = TF + SI
2016-12-07 22:17:21 +00:00
2080 T % = TA / 256
2016-12-14 17:18:06 +00:00
2085 POKE DP , T % : POKE DP , TA - ( T % * 256 )
2016-12-14 18:47:46 +00:00
2090 PRINT "UPDATE TX TO " ; T % ; "/" ; TA - ( T % * 256 )
2016-12-14 17:18:06 +00:00
'
' SEND packet
'
2016-12-07 22:05:30 +00:00
2100 REM *** SEND
2016-12-15 18:40:03 +00:00
2102 PRINT "SENDING"
2016-12-07 22:05:30 +00:00
2105 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
2110 POKE DP , 32 : REM * * * SEND
2016-12-14 16:29:18 +00:00
'
' Return to reading
'
2016-12-14 17:18:06 +00:00
4000 REM *** Check if successful
4010 POKE HA , 4 : POKE LA , 3 : REM * * * 0 x403 status register
4020 RE = PEEK ( DP )
4030 PRINT "STATUS AFTER SEND " ; RE
2016-12-14 18:47:46 +00:00
4035 IF RE = 28 THEN GOTO 6000: REM CLOSE_WAIT
4040 IF RE = 0 THEN GOTO 400: REM CLOSED
2016-12-14 17:18:06 +00:00
4060 REM *** RECEIVE
4075 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
4080 POKE DP , 64 : REM * * * RECV
2016-12-14 18:47:46 +00:00
4090 GOTO 800
2016-12-14 16:29:18 +00:00
'
' Close the socket
'
2016-12-14 18:47:46 +00:00
5000 REM *** CLOSE AND EXIT
2016-12-14 16:29:18 +00:00
5010 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
5020 POKE DP , 16 : REM * * * CLOSE
5030 END
2016-12-14 18:47:46 +00:00
6000 REM *** CLOSE AND RELISTEN
6010 POKE HA , 4 : POKE LA , 1 : REM * * * 0 x401 command register
6020 POKE DP , 16 : REM * * * CLOSE
' Check status?
6030 GOTO 400
'
2016-12-14 21:15:14 +00:00
'
' ERROR MESSAGES
'
'
7000 REM 400 BAD REQUEST
7005 S$ = "400 Bad Request"
7010 M$ = "<html><head><title>400 Bad Request</title></head><body><h1>Bad Request</h1><p>Your browser sent a request that this server could not understand.<br /></p></body></html>" + CHR$ ( 13 ) + CHR$ ( 10 )
7020 GOTO 9100
8000 REM 404 NOT FOUND
8003 POKE 216 , 0 : REM CANCEL ONERR
8004 PRINT "DISK ERROR: " ; PEEK ( 222 )
8005 S$ = "404 Not Found"
8010 M$ = "<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>File not found.<br /></p></body></html>" + CHR$ ( 13 ) + CHR$ ( 10 )
8020 GOTO 9100
9000 REM 418 TEAPOT
9005 S$ = "418 I'm a Teapot"
9010 M$ = "<html><head><title>418 I'm a Teapot</title></head><body><h1>I'm a Teapot</h1><p>Short *and* stout.<br /></p></body></html>" + CHR$ ( 13 ) + CHR$ ( 10 )
'
' Make header
'
9100 A$ = "HTTP/1.1 " + S$ + CHR$ ( 13 ) + CHR$ ( 10 ) + "Server: VMW-web" + CHR$ ( 13 ) + CHR$ ( 10 )
9105 A$ = A$ + "Content-Length: " + STR$ ( LEN ( M$ ) ) + CHR$ ( 13 ) + CHR$ ( 10 )
9110 A$ = A$ + "Connection: close" + CHR$ ( 13 ) + CHR$ ( 10 ) + "Content-Type: text/html; charset=iso-8859-1" + CHR$ ( 13 ) + CHR$ ( 10 ) + CHR$ ( 13 ) + CHR$ ( 10 )
' Poke as if we had loaded from disk
9200 FS = LEN ( M$ )
9210 FOR I = 1 TO FS
9220 POKE 16383 + I , ASC ( MID$ ( M$ , I , 1 ) )
9300 NEXT I
9310 GOTO 1380
'
2016-12-14 18:47:46 +00:00
' STATUSES
' p28 of W5100 manual
' 0x0 0 SOCK_CLOSED
' 0x13 SOCK_INIT
' 0x14 SOCK_LISTEN
' 0x17 23 SOCK_ESTABLISHED
' 0x1C 28 SOCK_CLOSE_WAIT
' 0x22 SOCK_UDP
' 0x32 SOCK_IPRAW
' 0x42 SOCK_MACRAW
' 0x5f SOCK_PPOE
2016-12-16 21:51:54 +00:00
'
' Appendix 1: The memcpy machine code
'
'
'PTR EQU $06
'PTRH EQU $07
'
'WRAPL EQU $08
'WRAPH EQU $09
'
'SIZEL EQU $0A
'SIZEH EQU $0B
'
'tx_copy:
'
' lda #0 ; always copying from 0x4000
' sta PTR
' lda #$40
' sta PTR+1
'
' ldx #SIZEH ; number of 256-byte blocks
' beq copy_remainder ; if none, skip ahead
'
' ldy #0
'copy256:
' lda (PTR),y
' sta $C0B7 ; change based on uthernet slot
'
' cmp WRAPH,x
' bne nowrap256
'
' cmp WRAPL,y
' bne nowrap256
'
' lda #$40
' sta $C0B5
' lda #$00
' sta $C0B6 ; wrap tx buffer address to 0x4000
'
'nowrap256:
' iny
' bne copy256
'
' inc PTR+1 ; update 16-bit pointer
' dex ; finish a 256 byte block
' bne copy256
'
' ldx #SIZEL
'copy_remainder:
' lda (PTR),y
' sta $C0B7 ; change based on uthernet slot
'
' cmp WRAPL,y
' bne nowrap_r
'
' lda #$40
' sta $C0B5
' lda #$00
' sta $C0B6 ; wrap tx buffer address to 0x4000
'
'nowrap_r:
' iny
' dex
' bne copy_remainder
'
' rts