diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..db25df0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.uaem +*.Bak diff --git a/AMOSPro_BSDSocket.Lib b/AMOSPro_BSDSocket.Lib new file mode 100644 index 0000000..8264959 Binary files /dev/null and b/AMOSPro_BSDSocket.Lib differ diff --git a/API.md b/API.md new file mode 100644 index 0000000..7c1e5ef --- /dev/null +++ b/API.md @@ -0,0 +1,357 @@ +## BSD Socket Extension API + +Most functions will return -2 if the bsdsocket.library is +not open. + +### Setup + +#### ADDR=Socket Library Open + +Try to open bsdsocket.library version 4. + +##### Returns + +* 0 if opening failed +* Memory address of library on success + * If needed, you'll be able to directly access library functions + using this address. + +#### Socket Library Close + +Close bsdsocket.library. This is safe to call if the library +is not open + +#### RESULT=Socket Set Nonblocking(Socket, IsNonblocking BOOL) + +Make a socket blocking (False, default), or nonblocking (True). + +##### Returns + +* Result of IoctlSocket call. + +#### RESULT=Socket Reuse Addr(Socket) + +Make a listening socket reuse the address it's trying to bind to. +You probably want to call this right before Socket Listen. + +##### Returns + +* Result of setsockopt call. + + + +### Connections + +#### SOCKET=Socket Create Inet Socket + +Create a new Internet socket for reading or writing. + +##### Returns + +* Socket number on success +* -1 on failure + +#### RESULT=Socket Connect(Socket to IPAddress$, Port) + +Attempt to connect to a remote host. Currently doesn't +support DNS lookups. + +##### Returns + +* 0 on connected +* -1 on error + * If your socket is non-blocking, you have to check + the socket with Socket Select and Socket Getsockopt Int + to see if the connection succeeded +* -11 port out of range + +#### RESULT=Socket Reuse Addr(Socket) + +Set a server socket to reuse an interface and port that had +been used recently. You likely want this if you're building +something that listens on a port for connections. This calls +setsockopt() for you. + +##### Returns + +The result of calling setsockopt() while setting your socket +to reuse addresses. + +#### RESULT=Socket Bind(Socket to IPAddress, Port) + +Attempt to bind a socket to a network interface. Use +the string "IPADDR_ANY" for IPAddress to bind to all +interfaces. + +##### Returns + +* 0 on success +* -1 on other error +* -11 port out of range + +#### RESULT=Socket Listen(Socket) + +Start listening for connections. + +##### Returns + +* 0 on success +* -1 on failure + +#### NEW_SOCKET=Socket Accept(Socket) + +Get the socket that connected to this one. Wait for a connect +if this socket is blocking. + +##### Warning + +If this socket is blocking (the default), you will likely +get AMOS stuck in a state you can't recover from, due to +how AMOS takes over system signals! Be sure to make your +socket non-blocking and use Fdsets and Select! + +##### Returns + +* The remote socket number on success +* -1 on failure + +#### RESULT=Socket Async Wait Reading(Socket, Wait_ms) + +Wait the given number of milliseconds for the nonblocking socket to be ready for reading. +Use this when you're waiting for a client to connect to you, or if you're waiting for +a remote socket to send you data. + +##### Returns + +* 0 on timeout +* -1,-2 on error +* 1 on success + +#### RESULT=Socket Async Wait Writing(Socket, Wait_ms) + +Wait the given number of milliseconds for the nonblocking socket to be ready for writing. +Use this when you're connecting to a remote server and want to know if the connection +has been completed. + +##### Returns + +* 0 on timeout +* -1,-2 on error +* 1 on success + +#### RESULT=Socket Set Timeout(Socket, Wait_ms) + +Set a socket to timeout after Wait_ms milliseconds if reading or writing doesn't complete. + +##### Returns + +* 0 on success +* -1 on error + +#### RESULT=Socket Close(Socket) + +Close a socket. + +##### Returns + +* 0 on success +* -1 on error + + + +### Data Transfers + +#### SENT=Socket Send$(Socket, String$) + +Send a string to a connected socket. + +##### Bugs + +For some reason, this command performs incorrectly when +used in a Procedure to send a passed-in variable, +and Varptr() is not called on the string variable to be sent +beforehand within the procedure itself. I believe this is +a bug in AMOS itself. You'll know you're having this issue if +the byte count returned is abnormally high. + +If you're using this in a Procedure, call Varptr() first: + +``` +Procedure SEND_STRING[SOCKET, S$] + ' _shrug_ + _=Varptr(S$) + + BYTES=Socket Send$(SOCKET,S$) +End Proc +``` + +##### Returns + +* Number of characters sent +* -1 on other error + +#### SENT=Socket Send(Socket, Data Pointer, Length) + +Send a block of data to a connected socket. + +##### Returns + +* Number of characters sent +* -1 on other error + +#### DATA$=Socket Recv$(Socket, MaxLength) + +Retrieve at most MaxLength bytes from Socket, and put them into a string. +If Len(DATA$) < MaxLength, you've read the last bit of data from the socket. + +##### Returns + +* String of data, which is blank if there is no more data. + +### LENGTH=Socket Recv(Socket to Dataptr, MaxLength) + +Retrieve at most MaxLength bytes from Socket, and put them into the memory +address at Dataptr. + +#### Returns + +* Count of bytes read +* -1 on error + + + +### Informational + +#### HOST=Socket Get Host(Socket) + +Get the IPv4 (Long) host value the given socket is using. + +##### Returns + +* Host as a long value + +#### PORT=Socket Get Port(Socket) + +Get the 16-bit port (Word) value the given socket is using. + +##### Returns + +* Port as a word value + +#### RESULT$=Socket Inet Ntoa$(Host) + +Turn a long Host address into a string. + +##### Returns + +* IP address as string + +#### RESULT=Socket Errno + +Get the error from the last command. Note that this is +not cleared on a successful command! + +##### Returns + +Error number from last call. Look in for more +details. + +#### RESULT$=Dns Get Address By Name$(Domain Name$) + +Look up the first IP address associated with this hostname. + +##### Warning + +This is dependent on your stack's name resolution. If DNS lookups +aren't working correctly, you may have to wait for the lookup to time +out. There's no way to set this timeout, or cancel or override it via AMOS. + +##### Returns + +String with IP address, or blank string on error. + +#### RESULT=Socket Status(Socket) + +Returns basic connection information about a socket. + +##### Warning + +Since none of the socket processing of this extension happens in the +background, you're likely better off using other means to detect +socket status. + +* Disconnected: Try a recv and get zero bytes back. Check Socket Errno. +* Broken: Try a send. If it fails, check Socket Errno. +* Ready: Use the Async Wait functions. + +##### Returns + +Status of socket: + +* 0 = Closed +* 2 = Listening +* 6 = Connecting +* 7 = Connected + + + +### Low Level + +#### RESULT=Socket Setsockopt Int(Socket, Option, Value) + +Set a socket option. You probably want SO_REUSEADDR, +which is Option=$4, and you want Value=True. Or, use +Socket Reuse Addr(). + +##### Returns + +* Result of setsockopt call + +#### RESULT=Socket Getsockopt Int(Socket, Option) + +Get a socket option. You probably want SO_ERROR, +which is Option=$1007, and it's what you check when you +attempt a connection with a non-blocking socket. + +##### Returns + +* Result of getsockopt call + +#### ADDR=Socket Fdset Zero(fd_set) + +Clear out the specified fd_set. + +##### Returns + +* Address to that particular fd_set +* -1 if fd_set out of range. You get 16 of them. + +#### ADDR=Socket Fdset Set(fd_set, Socket to Value BOOL) + +Set or clear a socket bit in an fd_set. + +##### Returns + +* Address to that particular fd_set +* -1 if fd_set is out of range or socket is out of range. + +#### RESULT=Socket Fdset Is Set(fd_set, Socket) + +See if the particular socket remained after a Socket Select call. + +##### Returns + +* True or False if the socket is set or not + +#### RESULT=Socket Select(Max Socket, Read fd_set, Write fd_set, Error fd_set, TimeoutMS) + +Wait for the specified number of milliseconds. If any of the sockets +in any of the fd_sets become interesting during that time, stop +waiting, clear out the uninteresting sockets in the fd_sets, and return +how many sockets were left. + +##### Returns + +* 0 on timeout +* -1 on error +* # of interesting sockets on success diff --git a/HTTP Client Example.amos b/HTTP Client Example.amos new file mode 100644 index 0000000..ebcc951 Binary files /dev/null and b/HTTP Client Example.amos differ diff --git a/README.md b/README.md index d841839..e900a1b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,166 @@ # amos-pro-bsdsocket-extension -An extension to use BSD Socket functionality within AMOS Professional. \ No newline at end of file +Want to get online with your AMOS program? Now you can in (some) style! + +This extension provides a wrapper around the BSD Socket library +provided by your Internet stack. There's some attempt to roll up some +of the low-level functionality into something more usable by AMOS. + +## Installation + +* Copy `AMOSPro_BSDSocket.Lib` to `AMOSPro_System:APSystem/` +* Within AMOS Professional: + * Config -> Set Interpreter + * Load Default Configuration + * Set Loaded Extensions + * Extension Slot 18 -- `AMOSPro_BSDSocket.Lib` + * Exit + * Save Configuration + * Restart AMOS Professional + * Type in the following and Run: + + ``` + Print Socket Library Open + ``` + + The line should tokenize, and running it should print -1 if you have no + Internet stack running, or a large number if you do. + +## Examples + +### Communicating with an HTTP server + +``` +LIB=Socket Library Open + +If LIB<=0 + End +End If + +SOCKET=Socket Create Inet Socket +_=Socket Set Nonblocking(SOCKET,True) + +' connect to aminet.net +IP$=Dns Get Address By Name$("aminet.net") +ALREADY_CONNECTED=Socket Connect(SOCKET To IP$,80) + +Reserve As Work 30,1024 + +For I=1 To 100 + If ALREADY_CONNECTED=-1 + RESULT=Socket Wait Async Writing(SOCKET,100) + + If RESULT>0 + ALREADY_CONNECTED=0 + End If + End If + + If ALREADY_CONNECTED=0 + HTTPGET$="GET / HTTP/1.0"+Chr$(10)+"Host: amiga"+Chr$(10)+Chr$(10) + Print "Making HTTP request to aminet.net" + _=Socket Send(SOCKET,Varptr(HTTPGET$),Len(HTTPGET$)) + + For J=1 To 100 + RESULT=Socket Wait Async Reading(SOCKET,5000) + If RESULT>0 + OUT=Socket Recv(SOCKET To Start(30),1024) + _DATA$="" + For K=0 To OUT-1 + _DATA$=_DATA$+Chr$(Peek(Start(30)+K)) + Next K + Print _DATA$ + If OUT<1024 + Exit 2 + End If + End If + Next J + Exit + End If +Next I + +Socket Library Close +``` + +### Starting a server + +``` +If Socket Library Open<=0 + End +End If + +SOCKET=Socket Create Inet Socket + +_=Socket Set Nonblocking(SOCKET,True) +_=Socket Reuse Addr(SOCKET) + +RESULT=Socket Bind(SOCKET To "INADDR_ANY",8000) + +_=Socket Listen(SOCKET) +Print "listening on port 8000" + +For I=1 To 100 + RESULT=Socket Wait Async Reading(SOCKET,500) + + If RESULT>0 + _REMOTE_SOCKET=Socket Accept(SOCKET) + + Print Socket Inet Ntoa$(Socket Get Host(_REMOTE_SOCKET)) + Print Socket Get Port(_REMOTE_SOCKET) + + Print Socket Recv$(_REMOTE_SOCKET,1024) + + Exit + End If + Wait Vbl +Next I + +Socket Library Close +``` + +## Who's using the extension? + +* [AQUABYSS](https://agedcode.com/agedcode/en/games/aquabyss) + * As of April 2023, the extension's been in heavy use for over a month + on the client side of the game. + +Doing something cool with the extension? +[Contact me](https://theindustriousrabbit.com/about) and I'll add it to the list! + +## General Notes + +* The BSD Socket library, and all sockets, will close automatically when the program + runs again. Sockets will stay open after stopping you code so you can + debug issues from the AMOS console. +* Since AMOS takes over Ctrl-C handling, you have to continually + poll for socket statuses on nonblocking sockets. Trying to use + a blocking socket will likely cause AMOS to hang indefinitely! + * Using the Wait Async functions with small timeouts are the + most system-friendly way of doing this. +* MiamiDX can be fiddly, at least it is on my MiSTer set up. + If Internet connectivity is not working, try re-connecting to the network. + +## Versioning + +This project uses semantic versioning. + +* If the version number is below 1.0.0, API is not stable. +* If the major number increases, API has changed. + +## License + +Copyright 2023 John Bintz. Licensed under the MIT License. + +If you use this in your project, and you really want to, +throw a link to theindustriousrabbit.com somewhere. + +## Feedback? Bug reports? + +Go to the [About section on The Industrious Rabbit](https://theindustriousrabbit.com/about) +to contact me. + +## Development + +### Environment + +* Clone the [AMOS Professional source code](https://github.com/AOZ-Studio/AMOS-Professional-Official) +* Copy the contents of diff --git a/http get v3.amos b/http get v3.amos new file mode 100644 index 0000000..0265493 Binary files /dev/null and b/http get v3.amos differ diff --git a/src/BSDSocket.s b/src/BSDSocket.s new file mode 100644 index 0000000..eb3c758 --- /dev/null +++ b/src/BSDSocket.s @@ -0,0 +1,2066 @@ +; BSDSocket Extension for AMOS Professional +; Copyright 2023 John Bintz +; Licensed under the MIT License +; +; Writing this code the right way, 25 years later. + +; extension number 18 +ExtNb equ 18-1 +Version MACRO + dc.b "0.7.0-20230220" + ENDM +VerNumber equ $1 + +;--------------------------------------------------------------------- +; Include the files automatically calculated by +; Library_Digest.AMOS +;--------------------------------------------------------------------- + Incdir "Stuff:development/amos-professional-official-mas/" + Incdir "Stuff:development/amos-professional-official-mas/includes/" + Include "BSDSocket_Size.s" + Include "BSDSocket_Labels.s" + Include "+AMOS_Includes.s" + Include "socket_lvo.i" + +; get the effective address of something in extension memory +Dlea MACRO + MOVE.L ExtAdr+ExtNb*16(A5),\2 + ADD.W #\1-MB,\2 + ENDM + +; load the base of extension memory into a register +Dload MACRO + MOVE.L ExtAdr+ExtNb*16(A5),\1 + ENDM + +; call an AmigaOS function via LVO(A6) +CALLLIB MACRO + JSR _LVO\1(A6) + ENDM + +; bsdsocket library stuff +; ported from the various C include headers +SOCK_STREAM EQU 1 +PF_INET EQU 2 +AF_INET EQU PF_INET +IPPROTO_TCP EQU 6 + +INADDR_ANY EQU 0 + +FIONBIO EQU $8004667E +FIONASYNC EQU $8004667D + +SOL_SOCKET EQU $FFFF +SO_REUSEADDR EQU $4 + +MAX_SOCKETS EQU 64 + +len_sockaddr_in EQU 16 +sockaddr_in_sin_len EQU 0 +sockaddr_in_sin_family EQU 1 +sockaddr_in_sin_port EQU 2 +sockaddr_in_sin_addr EQU 4 + +; global errors +Error_OtherError EQU -1 +Error_LibraryNotOpen EQU -2 +Error_PortOutOfRange EQU -11 +Error_FdsetOutOfRange EQU -11 +Error_UnableToBind EQU -12 + +; wrap code that doesn't take arguments with these +PreserveStackInstruction MACRO + MOVEM.L A2-A6/D6-D7,-(SP) + ENDM +RestoreStackInstruction MACRO + MOVEM.L (SP)+,A2-A6/D6-D7 + ENDM + +; wrap code that takes arguments with these +PreserveStackFunction MACRO + MOVEM.L A2/A4-A6/D6-D7,-(SP) + ENDM +RestoreStackFunction MACRO + MOVEM.L (SP)+,A2/A4-A6/D6-D7 + ENDM + +LoadBSDSocketBase MACRO + MOVE.L BSDSocketBase-MB(A3),A6 + ENDM + +WithDataStorage MACRO + MOVE.L A3,-(SP) + Dload A3 + ENDM + +EndDataStorage MACRO + MOVE.L (SP)+,A3 + ENDM + +; fdset macros +EnsureValidFdset MACRO + CMP.L #MaxFd_sets,\1 + BLT \2 + + MOVE.L #Error_FdsetOutOfRange,D3 + RestoreStackFunction + Ret_Int + ENDM + +EnsureValidFdsetBit MACRO + CMP.L #64,\1 + BGE _EnsureValidFdsetBit_Fail\@ + BRA \2 +_EnsureValidFdsetBit_Fail\@: + MOVE.L \3,D3 + RestoreStackFunction + Ret_Int + ENDM + +LeaFdset MACRO + MOVE.L \1,-(SP) + Dlea fd_sets,\2 ; base of all, these are longs + ROL.L #3,\1 ; multiply by 8 + ADD.L \1,\2 ; add to base of all + MOVE.L (SP)+,\1 + ENDM + +; LeaFdsetForBit fd_set reg,target address,target bit in address +LeaFdsetForBit MACRO + LeaFdset \1,\2 ; get fdset base address in \2 + MOVE.L D3,-(SP) + MOVE.L \3,D3 ; Put target bit into D3 + ROR.L #5,D3 ; lop off the first 5 bits + AND.L #$7,D3 ; only keep the top three + ROL.L #2,D3 ; multiply by 4 + ADD.L D3,\2 ; add that value to the fdset address + + AND.L #$1F,\3 ; only keep 0-31 in \3 + + MOVEQ #1,D3 + ROL.L \3,D3 ; shift that bit left as many as target + MOVE.L D3,\3 ; put that in the target + MOVE.L (SP)+,D3 + + ENDM + +; d0 = value to be divided +; d1 = divisor +; returns: +; d0 = divided value +; d1 = remainder +LongDivideD0ByD1 MACRO + CMP.L D0,D1 + BMI _LongDivide_StartDivide\@ + + MOVE.L D0,D1 + MOVEQ #0,D0 + BRA _LongDivide_Skip\@ + +_LongDivide_StartDivide\@: + + MOVEM.L D2-D4,-(SP) + MOVEQ #0,D2 ; remainder + MOVE.L #31,D3 ; bit tracking + ; d4 tracks the status register + +_LongDivide_ContinueDivide\@: + ASL.L #1,D0 + SCS D4 ; bit that got rolled out + AND.L #1,D4 + ROL.L #1,D2 + ADD.L D4,D2 ; roll the value onto the remainder + + MOVE.L D2,D4 + SUB.L D1,D4 + + BMI _LongDivide_NotDivisible\@ + ADDQ #1,D0 + MOVE.L D4,D2 + +_LongDivide_NotDivisible\@: + DBRA D3,_LongDivide_ContinueDivide\@ + MOVE.L D2,D1 + MOVEM.L (SP)+,D2-D4 + +_LongDivide_Skip\@: + ENDM + +EvenOutStringAddress MACRO + MOVE.W \1,\2 + AND.W #$0001,\2 + ADD.W \2,\1 + ENDM + +; check if we've opened the bsd socket library +; we do this a lot, this could probably become something +; we jsr to... +; TODO make into routine +EnsureBSDSocketLibrary MACRO + MOVEM.L A2,-(SP) + Dload A2 + TST.L BSDSocketBase-MB(A2) + MOVEM.L (SP)+,A2 + + BNE \1 + + MOVE.L #Error_LibraryNotOpen,D3 + ENDM + +; TODO token casing consistency + +Start dc.l C_Tk-C_Off + dc.l C_Lib-C_Tk + dc.l C_Title-C_Lib + dc.l C_End-C_Title + ; don't copy first library routine unless needed + dc.w 0 + ; API we're using + dc.b "AP20" + +;--------------------------------------------------------------------- +; Creates the pointers to functions +;--------------------------------------------------------------------- + MCInit +C_Off + REPT Lib_Size + MC + ENDR + +; make it way harder to get these confused +AddTokenInstruction MACRO + dc.w L_\1,L_Nul + ENDM + +AddTokenFunction MACRO + dc.w L_Nul,L_\1 + ENDM + +****************************************************************** +* TOKEN TABLE + Addresses + +; TOKEN_START +C_Tk dc.w 1,0 + dc.b $80,-1 + + AddTokenFunction SocketLibraryOpen + dc.b "socket library ope","n"+$80,"0",-1 + + AddTokenInstruction SocketLibraryClose + dc.b "socket library clos","e"+$80,"I",-1 + + AddTokenFunction SocketCreateInetSocket + dc.b "socket create inet socke","t"+$80,"0",-1 + + AddTokenFunction SocketConnect + dc.b "socket connec","t"+$80,"00t2,0",-1 + + AddTokenFunction SocketSendString + dc.b "socket send","$"+$80,"00,2",-1 + + AddTokenFunction SocketBind + dc.b "socket bin","d"+$80,"00t2,0",-1 + + AddTokenFunction SocketErrno + dc.b "socket errn","o"+$80,"0",-1 + + AddTokenFunction SocketListen + dc.b "socket liste","n"+$80,"00",-1 + + AddTokenFunction SocketAccept + dc.b "socket accep","t"+$80,"00",-1 + + AddTokenFunction SocketSetNonblocking + dc.b "socket set nonblockin","g"+$80,"00,0",-1 + + AddTokenFunction SocketSetsockoptInt + dc.b "socket setsockopt in","t"+$80,"00,0,0",-1 + + AddTokenFunction SocketFdsetZero + dc.b "socket fdset zer","o"+$80,"00",-1 + + AddTokenFunction SocketFdsetSet + dc.b "socket fdset se","t"+$80,"00,0t0",-1 + + AddTokenFunction SocketFdsetIsSet + dc.b "socket fdset is se","t"+$80,"00,0",-1 + + AddTokenFunction SocketSelect + dc.b "socket selec","t"+$80,"00,0,0,0,0",-1 + + AddTokenFunction SocketGetDebugArea + dc.b "socket get debug are","a"+$80,"0",-1 + + AddTokenFunction SocketGetHost + dc.b "socket get hos","t"+$80,"00",-1 + + AddTokenFunction SocketInetNtoA + dc.b "socket inet ntoa","$"+$80,"20",-1 + + AddTokenFunction SocketRecvString + dc.b "socket recv","$"+$80,"20,0",-1 + + AddTokenFunction SocketRecvData + dc.b "socket rec","v"+$80,"00t0,0",-1 + + AddTokenFunction SocketGetPort + dc.b "socket get por","t"+$80,"00",-1 + + AddTokenFunction SocketGetsockoptInt + dc.b "socket getsockopt in","t"+$80,"00,0",-1 + + AddTokenFunction SocketSendData + dc.b "socket sen","d"+$80,"00,0,0",-1 + + AddTokenFunction SocketWaitAsyncWriting + dc.b "socket wait async writin","g"+$80,"00,0",-1 + + AddTokenFunction SocketWaitAsyncReading + dc.b "socket wait async readin","g"+$80,"00,0",-1 + + AddTokenFunction SocketReuseAddr + dc.b "socket reuse add","r"+$80,"00",-1 + + AddTokenFunction DnsGetHostAddressByName + dc.b "dns get address by name","$"+$80,"22",-1 + + AddTokenFunction SocketSetTimeout + dc.b "socket set timeou","t"+$80,"00,0",-1 + + AddTokenFunction SocketCloseSocket + dc.b "socket close socke","t"+$80,"00",-1 + +; TOKEN_END + dc.w 0 + dc.l 0 ; Important! + + +;--------------------------------------------------------------------- + Lib_Ini 0 +;--------------------------------------------------------------------- + +C_Lib + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; COLD START +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Def Cold +; - - - - - - - - - - - - - + cmp.l #"APex",d1 Version 1.10 or over? + bne.s BadVer + movem.l a3-a6,-(sp) + + ; ensure we can get to our data area + lea MB(pc),a2 + move.l a2,ExtAdr+ExtNb*16(a5) + + ; cleanup on exit code in editor + lea Warm(pc),a0 + move.l a0,ExtAdr+ExtNb*16+4(a5) + + ; cleanup when shutting down program + lea End(pc),a0 + move.l a0,ExtAdr+ExtNb*16+8(a5) + + movem.l (sp)+,a3-a6 + moveq #ExtNb,d0 ; * NO ERRORS + move.w #VerNumber,d1 ; * Current version + rts +; In case this extension is runned on AMOSPro V1.00 +BadVer moveq #-1,d0 ; * Bad version number + sub.l a0,a0 + rts + +MB: +BSDSocketBase dc.l 0 ; library base + +IoctlSockOptScratch ds.l 2 +len_sockaddr_in_ptr dc.l len_sockaddr_in + +MaxFd_sets EQU 16 +fd_sets dcb.l (MaxFd_sets*2),0 +select_timeval dcb.l 2,0 +timeout_timeval dcb.l (MAX_SOCKETS*2),0 +getsockopt_len ds.l 1 + +MaxSocketSeen dc.w 0 + +sockaddr_ram ds.l 1 +AcceptScratchArea ds.b 16 +SelectScratchArea ds.l 2 + +DebugArea dcb.l 8,0 + +BSDSocketLibrary dc.b "bsdsocket.library",0 +INADDR_ANY_String dc.b "INADDR_ANY",0 + even + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; SCREEN RESET: back to AMOS requester (Called by AMOSPro) +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Warm +; - - - - - - - - - - - - - + Rbsr L_DoSocketLibraryClose + rts + +; Program shutdown +End + Rbsr L_DoSocketLibraryClose + rts + +;-------------------------------------------------------------------- + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Leave one empty routine here! +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Empty +; - - - - - - - - - - - - - + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Library Open +; +; Attempt to open bsdsocket.library version 4 +; +; @return int address of library if opened, 0 if opening failed +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketLibraryOpen +; - - - - - - - - - - - - - + PreserveStackInstruction + + ; ensure the library is closed before we try to reopen it + Rbsr L_DoSocketLibraryClose + + Dload A3 + MOVEQ #4,D0 + Dlea BSDSocketLibrary,A1 + MOVEA.L 4,A6 + CALLLIB OpenLibrary + + MOVE.L D0,BSDSocketBase-MB(A3) + BEQ _SocketLibraryOpen_Finish + + ; reserve ram for sockaddr_ins + MOVE.L #MAX_SOCKETS*len_sockaddr_in,D0 + Rjsr L_RamFast + BEQ _SocketLibraryOpen_Finish + + MOVE.L D0,sockaddr_ram-MB(A3) + +_SocketLibraryOpen_Finish: + MOVE.L BSDSocketBase-MB(A3),D3 + + RestoreStackInstruction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Socket Library Close +; +; Close bsdsocket.library if it was opened before. Does nothing if it's not. +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketLibraryClose +; - - - - - - - - - - - - - + PreserveStackInstruction + + Rbsr L_DoSocketLibraryClose + + RestoreStackInstruction + RTS + + +; Utility functions + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; DoSocketSockaddrFree(sockaddr address D3) +; +; Free memory from sockaddrin +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Lib_Def DoSocketSockaddrFree + + MOVEM.L A1/D0,-(SP) + MOVE.L D3,A1 ; address + MOVE.L #len_sockaddr_in,D0 + Rjsr L_RamFree + MOVEM.L (SP)+,A1/D0 + + RTS + + +; - - - - - - - - - - +; d0 = SocketIPAddressPortToSockaddr(socket d0, ip address d1, port d2) +; +; Turn an IP address and port into a sockaddr_in strucure +; +; d0 - 0 on success, -1 on failure +; - - - - - - - - + Lib_Def SocketIPAddressPortToSockaddr + + ; bail early if the port is too big + CMP.L #65535,D2 + BLS _ToSockaddr_PortOK + + MOVE.L #Error_PortOutOfRange,D0 + RTS + +_ToSockaddr_PortOK: + + MOVEM.L A0/A3/D3,-(SP) + + Dload A3 + MOVE.L #len_sockaddr_in,D3 + MULU D0,D3 ; D0 is socket + MOVE.L sockaddr_ram-MB(A3),A0 + ADD.L D3,A0 ; A0 contains our offset in ram + + MOVE.B #len_sockaddr_in,sockaddr_in_sin_len(A0) + MOVE.B #AF_INET,sockaddr_in_sin_family(A0) + MOVE.W D2,sockaddr_in_sin_port(A0) + + MOVEM.L A0-A3/D3,-(SP) + + MOVE.L D1,A0 ; ip address + ADDQ #2,A0 ; string data starts 2 bytes in + + ; if the string contains "INADDR_ANY", we use that value instead + MOVE.L A0,A1 + Dlea INADDR_ANY_String,A2 + MOVEQ #0,D3 + +_ToSockaddr_KeepCheckingString_1: + MOVE.B (A2)+,D3 + BNE _ToSockaddr_KeepCheckingString_2 + + MOVE.L #INADDR_ANY,D0 + BRA _ToSockaddr_DoneParsing + +_ToSockaddr_KeepCheckingString_2: + CMP.B (A1)+,D3 + BEQ _ToSockaddr_KeepCheckingString_1 + +_ToSockaddr_ParseIPAddress: + Dload A3 + LoadBSDSocketBase + CALLLIB inet_addr + +_ToSockaddr_DoneParsing: + MOVEM.L (SP)+,A0-A3/D3 + MOVE.L D0,sockaddr_in_sin_addr(A0) + + MOVE.L A0,D0 + MOVEM.L (SP)+,A0/A3/D3 + RTS + +; basics + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Create Inet Socket +; +; Create an Internet-ready socket, the one you're most +; likely wanting to create. +; +; @return number of socket, -1 on failure, -2 on library not open +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketCreateInetSocket +; - - - - - - - - - - - - - + PreserveStackInstruction + + EnsureBSDSocketLibrary _SocketCreateInetSocket_LibraryOpen + + RestoreStackInstruction + Ret_Int + +_SocketCreateInetSocket_LibraryOpen: + + MOVE.L #PF_INET,D0 + MOVE.L #SOCK_STREAM,D1 + MOVE.L #IPPROTO_TCP,D2 + Dload A3 + LoadBSDSocketBase + CALLLIB socket + + MOVE.L D0,D3 + + ; if this socket number is higher than the last one we've seen + ; increment it + BMI _SocketCreateInetSocket_Done + + MOVEQ #0,D1 + MOVE.W MaxSocketSeen-MB(A3),D1 + CMP.L D0,D1 + BGT _SocketCreateInetSocket_Done + + MOVE.W D0,MaxSocketSeen-MB(A3) + + +_SocketCreateInetSocket_Done: + + RestoreStackInstruction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Connect(LONG SocketID to String IP Address, LONG Port) +; +; Connect a socket to a remote IP address and port. +; +; @return: +; 0 on connect +; -1 on other error +; -2 on library not open +; -11 on port out of range +; -12 on unable to connect +; -13 on unable to allocate ram for sockaddr +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketConnect +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D2 ; port + MOVE.L (A3)+,D1 ; ip address + MOVE.L (A3)+,D0 ; socket id + + EnsureBSDSocketLibrary _SocketConnect_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketConnect_LibraryOpen: + + MOVE.L D0,-(SP) ; socket id onto stack + + Rbsr L_SocketIPAddressPortToSockaddr + TST.L D0 + BGT _SocketConnect_SockaddrIn + + MOVE.L (SP)+,D1 + + MOVE.L D0,D3 + RestoreStackFunction + Ret_Int + +_SocketConnect_SockaddrIn: + + MOVE.L D0,A0 + MOVE.L (SP)+,D0 + + MOVE.L D0,-(SP) + MOVEM.L A0/A3,-(SP) + Dload A3 + ; socket id is in D0 + ; sockaddr_in is in A0 + MOVE.L #len_sockaddr_in,D1 ; len + LoadBSDSocketBase + CALLLIB connect + MOVEM.L (SP)+,A0/A3 + + MOVE.L D0,D3 + MOVE.L (SP)+,D0 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Send$(LONG SocketID, String Data) +; +; Send a string to a connected socket +; +; @return: +; number of characters sent +; -1 on other error +; -2 on library not open +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketSendString +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,A0 ; string + MOVE.L (A3)+,D0 ; socket id + MOVE.W (A0)+,D1 ; string length, increment pointer + + EnsureBSDSocketLibrary _SocketSendString_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketSendString_LibraryOpen: + MOVEQ #0,D2 ; flags + + WithDataStorage + LoadBSDSocketBase + CALLLIB send + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Send(LONG SocketID, void * Data, LONG length) +; +; Send a block of data to a connected socket +; +; @return: +; number of characters sent +; -1 on other error +; -2 on library not open +; -3 on negative length +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketSendData +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D1 ; length + MOVE.L (A3)+,A0 ; data + MOVE.L (A3)+,D0 ; socket + + EnsureBSDSocketLibrary _SocketSendData_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketSendData_LibraryOpen: + TST.L D1 + BGE _SocketSendData_NotNegative + + MOVE.L #-3,D3 + + RestoreStackFunction + Ret_Int + +_SocketSendData_NotNegative: + MOVEQ #0,D2 ; flags + + WithDataStorage + LoadBSDSocketBase + CALLLIB send + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Bind(LONG SocketID to String IP Address, LONG Port) +; +; Bind a socket to a local IP address and port +; +; @return: +; 0 on success +; -1 on other error +; -2 on library not open +; -11 on port out of range +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketBind +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D2 ; port + MOVE.L (A3)+,D1 ; ip address + MOVE.L (A3)+,D0 ; socket id + + EnsureBSDSocketLibrary _SocketBind_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketBind_LibraryOpen: + ; store current socket id for later + MOVE.L D0,-(SP) + + Rbsr L_SocketIPAddressPortToSockaddr + TST.L D0 + BGE _SocketBind_SockaddrIn + + MOVE.L (SP)+,D1 + MOVE.L D0,D3 + RestoreStackFunction + Ret_Int + +_SocketBind_SockaddrIn: + MOVE.L D0,A0 + MOVE.L (SP)+,D0 + + WithDataStorage + ; socket id is in D0 + ; sockaddr_in is in A0 + MOVE.L #len_sockaddr_in,D1 ; len + LoadBSDSocketBase + CALLLIB bind + EndDataStorage + + MOVE.L D0,D3 + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Errno +; +; Get the BSDSocket error number of the last failed command +; +; @return: +; Last BSD Socket error +; -2 on library not open +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketErrno +; - - - - - - - - - - - - - + PreserveStackInstruction + + EnsureBSDSocketLibrary _SocketErrno_LibraryOpen + + RestoreStackInstruction + Ret_Int + +_SocketErrno_LibraryOpen: + WithDataStorage + LoadBSDSocketBase + CALLLIB Errno + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackInstruction + RTS + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Listen(Socket ID LONG) +; +; Start listening on a socket +; +; @return: +; 0 on sucess +; -1 on failure +; -2 on library not open +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketListen +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D0 ; Socket ID + + EnsureBSDSocketLibrary _SocketListen_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketListen_LibraryOpen: + MOVE.L D3,D2 + MOVEQ #5,D1 + + WithDataStorage + LoadBSDSocketBase + CALLLIB listen + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + RTS + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Accept(Socket ID LONG) +; +; Accept a connection on a socket. This is blocking unless you set +; Ioctl to be non-blocking! +; +; @return: +; remote socket +; -1 on failure +; -2 on library not open +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketAccept +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D0 ; socket + + EnsureBSDSocketLibrary _SocketAccept_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketAccept_LibraryOpen: + + MOVE.L D3,D2 + + WithDataStorage + Dlea AcceptScratchArea,A0 + Dlea len_sockaddr_in_ptr,A1 + LoadBSDSocketBase + CALLLIB accept + EndDataStorage + + MOVE.L D0,D3 + BMI _SocketAccept_Failed + + Dload A0 + + ; update highest socket number seen + MOVEQ #0,D1 + MOVE.W MaxSocketSeen-MB(A0),D1 + CMP.L D0,D1 + + BGT _SocketAccept_CopySockaddrIn + MOVE.W D0,MaxSocketSeen-MB(A0) + +_SocketAccept_CopySockaddrIn: + ; we're putting sockaddr in info into our own storage area + MOVE.L D0,D1 ; new socket in d1 + MULU #len_sockaddr_in,D1 + MOVE.L sockaddr_ram-MB(A0),A0 + ADD.L D1,A0 ; new socket sockaddr_in target in A0 + + Dlea AcceptScratchArea,A1 + MOVEQ #len_sockaddr_in,D1 + SUBQ #1,D1 +_SocketAccept_CopyLoop: + MOVE.B (A1)+,(A0)+ + DBRA D1,_SocketAccept_CopyLoop + +_SocketAccept_Failed: + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Set Nonblocking(Socket ID LONG, Mode BOOLEAN) +; +; Configure a socket to be nonblocking (true) or blocking (false) +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketSetNonblocking +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L (A3)+,D0 ; socket + MOVE.L D3,D1 ; mode + + EnsureBSDSocketLibrary _SocketSetNonblocking_LibraryOpen + + RestoreStackFunction + RTS + +_SocketSetNonblocking_LibraryOpen: + TST.L D1 + BEQ _SocketSetNonblocking_IsBlocking + MOVEQ #1,D1 + BRA _SocketSetNonblocking_Ioctl + +_SocketSetNonblocking_IsBlocking: + MOVEQ #0,D1 + +_SocketSetNonblocking_Ioctl: + + WithDataStorage + MOVE.L D1,IoctlSockOptScratch-MB(A3) + Dlea IoctlSockOptScratch,A0 + MOVE.L #FIONBIO,D1 + LoadBSDSocketBase + CALLLIB IoctlSocket + + MOVE.L D0,D3 + EndDataStorage + + RestoreStackFunction + Ret_Int + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Setsockopt Int(Socket ID LONG, Option LONG, Value LONG) +; +; Set a socket option +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketSetsockoptInt +; - - - - - - - - - - - - - + PreserveStackFunction + + ; Value is in D3 + MOVE.L (A3)+,D2 ; Option + MOVE.L (A3)+,D0 ; Socket ID + + EnsureBSDSocketLibrary _SocketSetsockoptInt_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketSetsockoptInt_LibraryOpen: + Rbsr L_SetSockoptInt + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Getsockopt Int(Socket ID LONG, Option LONG) +; +; Get a socket option's value +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketGetsockoptInt +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D2 ; option + MOVE.L (A3)+,D0 ; socket + + EnsureBSDSocketLibrary _SocketGetsockoptInt_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketGetsockoptInt_LibraryOpen: + MOVE.L #SOL_SOCKET,D1 ; level + + MOVE.L A3,-(SP) + WithDataStorage + Dlea IoctlSockOptScratch,A0 ; optval + MOVE.L #4,getsockopt_len-MB(A3) + Dlea getsockopt_len,A1 + + LoadBSDSocketBase + CALLLIB getsockopt + EndDataStorage + MOVE.L (SP)+,A3 + + MOVE.L D0,D3 + BPL _SocketGetsockoptInt_GetValue + + RestoreStackFunction + Ret_Int + +_SocketGetsockoptInt_GetValue + Dlea IoctlSockOptScratch,A0 + MOVE.L (A0),D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Fdset Zero(fd_set LONG) +; +; Clear out the indicated fd_set. +; +; @return Address of fd_set (2 longs), -1 if our of range, -2 no library +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketFdsetZero +; - - - - - - - - - - - - - + PreserveStackFunction + EnsureBSDSocketLibrary _SocketFdsetZero_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketFdsetZero_LibraryOpen: + EnsureValidFdset D3,_SocketFdsetZero_ClearFdset + +_SocketFdsetZero_ClearFdset + LeaFdset D3,A0 + MOVE.L A0,D3 + CLR.L (A0)+ + CLR.L (A0)+ + + RestoreStackFunction + Ret_Int + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Fdset Set(fd_set LONG, bit LONG to value BOOL) +; +; Set or unset an fd_set's bit +; +; @return Address of fd_set (2 longs), -1 if out of range, -2 no library +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketFdsetSet +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D2 ; value + MOVE.L (A3)+,D1 ; bit + MOVE.L (A3)+,D0 ; fd_set + + EnsureBSDSocketLibrary _SocketFdsetSet_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketFdsetSet_LibraryOpen: + EnsureValidFdset D0,_SocketFdsetSet_CheckBit + +_SocketFdsetSet_CheckBit: + EnsureValidFdsetBit D1,_SocketFdsetSet_CheckNegative,#Error_FdsetOutOfRange + +_SocketFdsetSet_CheckNegative: + CMP.L #0,D1 + BGE _SocketFdsetSet_SetFdset + + MOVE.L #Error_FdsetOutOfRange,D3 + + RestoreStackFunction + Ret_Int + +_SocketFdsetSet_SetFdset: + ; get correct fdset + + MOVE.L A0,-(SP) + LeaFdsetForBit D0,A0,D1 ; D1 contains bit mask + + TST.L D2 ; zero/false for clearing + BEQ _SocketFdsetSet_Clear + + ; setting + MOVE.L (A0),D3 + OR.L D1,D3 + BRA _SocketFdsetSet_Done + +_SocketFdsetSet_Clear: + MOVE.L #$FFFFFFFF,D4 + SUB.L D1,D4 + + MOVE.L (A0),D3 + AND.L D4,D3 + +_SocketFdsetSet_Done: + MOVE.L D3,(A0) + MOVE.L (SP)+,D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Fdset Is Set(fd_set LONG, Bit LONG) +; +; Return the status of a bit in an fd_set. +; +; @return 0 if not set, -1 if it is set, -2 on library not open +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketFdsetIsSet +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D1 ; bit + MOVE.L (A3)+,D0 ; fdset + + EnsureBSDSocketLibrary _SocketFdsetIsSet_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketFdsetIsSet_LibraryOpen: + EnsureValidFdset D0,_SocketFdsetIsSet_CheckBit + +_SocketFdsetIsSet_CheckBit: + EnsureValidFdsetBit D1,_SocketFdsetIsSet_CheckNegative,#Error_FdsetOutOfRange + +_SocketFdsetIsSet_CheckNegative: + CMP.L #0,D1 + BGE _SocketFdsetIsSet_CheckFdsetBit + + MOVE.L #Error_FdsetOutOfRange,D1 + + RestoreStackFunction + Ret_Int + +_SocketFdsetIsSet_CheckFdsetBit: + ; get correct fdset + LeaFdsetForBit D0,A0,D1 ; D1 contains bit mask + + MOVE.L (A0),D3 + AND.L D1,D3 ; compare mask to value + + BNE _SocketFdsetIsSet_IsSet + + MOVEQ #0,D3 + BRA _SocketFdsetIsSet_Done + +_SocketFdsetIsSet_IsSet: + MOVE.L #-1,D3 + +_SocketFdsetIsSet_Done: + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Select(Max Socket long, Read fd_set long, Write fd_set long, error fd_set long, ms timeout long) +; +; Wait on the socket identifiers in the fd_sets, until the timeout expires. +; +; @return +; -2 on library not open +; -20 on read fd_set out of range +; -21 on write fd_set out of range +; -22 on error fd_set out of range +; -23 on ms must be positive +; number of sockets found on success +; 0 on timeout +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketSelect +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D4 ; ms timeout + MOVE.L (A3)+,D3 ; error fd_set + MOVE.L (A3)+,D2 ; write fd_set + MOVE.L (A3)+,D1 ; read fd_set + MOVE.L (A3)+,D0 ; fd count + + EnsureBSDSocketLibrary _SocketSelect_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketSelect_LibraryOpen: + EnsureValidFdsetBit D1,_SocketSelect_CheckWrite,#-20 + +_SocketSelect_CheckWrite: + EnsureValidFdsetBit D2,_SocketSelect_CheckError,#-21 + +_SocketSelect_CheckError: + EnsureValidFdsetBit D3,_SocketSelect_CheckTimeout,#-22 + +_SocketSelect_CheckTimeout: + CMP.L #0,D4 + BGE _SocketSelect_MaybeReadFdset + + MOVE.L #-23,D3 + + RestoreStackFunction + Ret_Int + +_SocketSelect_MaybeReadFdset: + MOVE.L #0,A0 + CMP.L #-1,D1 + BEQ _SocketSelect_MaybeWriteFdset + LeaFdset D1,A0 + +_SocketSelect_MaybeWriteFdset + MOVE.L #0,A1 + CMP.L #-1,D2 + BEQ _SocketSelect_MaybeErrorFdset + LeaFdset D2,A1 + +_SocketSelect_MaybeErrorFdset + MOVE.L #0,A2 + CMP.L #-1,D3 + BEQ _SocketSelect_PerformSelect + LeaFdset D3,A2 + +_SocketSelect_PerformSelect + MOVE.L A3,-(SP) + ; D4 contains microseconds + Rbsr L_MicrosecondsToTimeval ; A3 contains select_timeval + + MOVEQ #0,D1 + + Dload A4 + + MOVE.L BSDSocketBase-MB(A4),A6 + CALLLIB WaitSelect + MOVE.L (SP)+,A3 + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Get Debug Area +; +; Get the memory location for debug info. Put stuff in debug area while +; working on this extension and look it up in AMOS later. +; +; @return address of debug area +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketGetDebugArea +; - - - - - - - - - - - - - + PreserveStackFunction + + Dlea DebugArea,A0 + MOVE.L A0,D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Get Host(Socket LONG) +; +; Get the host long value for this socket's sockaddr_in. +; +; @return host long value +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketGetHost +; - - - - - - - - - - - - - + PreserveStackFunction + + MULU #len_sockaddr_in,D3 + Dload A0 + MOVE.L sockaddr_ram-MB(A0),A0 + ADD.L D3,A0 + MOVE.L sockaddr_in_sin_addr(A0),D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Get Port(Socket LONG) +; +; Get the port word value for this socket's sockaddr_in. +; +; @return port word value +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketGetPort +; - - - - - - - - - - - - - + PreserveStackFunction + + MULU #len_sockaddr_in,D3 + Dload A0 + MOVE.L sockaddr_ram-MB(A0),A0 + ADD.L D3,A0 + MOVEQ #0,D3 + MOVE.W sockaddr_in_sin_port(A0),D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Inet Ntoa$(host LONG) +; +; Turn a host into a string +; +; @return host long value +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketInetNtoA +; - - - - - - - - - - - - - + PreserveStackFunction + EnsureBSDSocketLibrary _SocketInetNtoa_LibraryOpen + + RestoreStackInstruction + Ret_Int + +_SocketInetNtoa_LibraryOpen: + MOVE.L D3,D0 + WithDataStorage + LoadBSDSocketBase + CALLLIB Inet_NtoA + EndDataStorage + + MOVEQ #0,D3 + MOVE.L D0,A0 + MOVE.L D0,A2 ; A0/A1 will get nuked by L_Demande + +_SocketInetNtoA_StringSizeLoop: + TST.B (A0)+ + BNE _SocketInetNtoA_StringSizeLoop + + MOVE.L A0,D3 + SUB.L A2,D3 ; D3 = length + Rjsr L_Demande ; string base address is in A0/A1 + + MOVE.W D3,(A0)+ ; length of string + SUBQ #1,D3 + +_SocketInetNtoA_StringCopyLoop: + MOVE.B (A2,D3),(A0,D3) + DBRA D3,_SocketInetNtoA_StringCopyLoop + + ; make the address even + EvenOutStringAddress A0,D3 + + MOVE.L A0,HiChaine(A5) ; Does L_Demande nuke A5 too? + + MOVE.L A1,D3 ; string return + + RestoreStackFunction + Ret_String + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Recv$(socket LONG, max length LONG) +; +; Receive at most length bytes of data from the socket +; +; @return string of bytes +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketRecvString +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L (A3)+,D1 ; socket + MOVE.L D3,D0 ; length + + EnsureBSDSocketLibrary _SocketRecvString_LibraryOpen + + MOVE.L ChVide(A5),D3 + + RestoreStackFunction + Ret_String + +_SocketRecvString_LibraryOpen: + TST.L D0 + BGT _SocketRecvString_NotNegative + + MOVE.L ChVide(A5),D3 + + RestoreStackFunction + Ret_String + +_SocketRecvString_NotNegative: + CMP.L #65536,D0 + BLT _SocketRecvString_NotTooLong + + MOVE.L ChVide(A5),D3 + + RestoreStackFunction + Ret_String + +_SocketRecvString_NotTooLong: + MOVE.L A3,-(SP) ; preserve A3 + + MOVE.L D0,-(SP) ; preserve D0 + ; reserve memory for recv buffer + Rjsr L_RamFast ; D0 now contains memory pointer + BNE _SocketRecvString_BufferAllocated + ; reserve failed + MOVE.L (SP)+,D0 + + MOVE.L (SP)+,A3 + + MOVE.L ChVide(A5),D3 ; empty string + RestoreStackFunction + Ret_String + +_SocketRecvString_BufferAllocated: + MOVE.L D0,A0 ; buffer address + MOVE.L D1,D0 ; socket + MOVE.L (SP)+,D1 ; length + MOVEQ #0,D2 ; flags + + MOVEM.L A0-A1/D1,-(SP) + + WithDataStorage + LoadBSDSocketBase + CALLLIB recv ; D0 has received length or -1 + EndDataStorage + + MOVEM.L (SP)+,A0-A1/D1 + + ; did we receive data? if we didn't get a single byte, we're done + TST.L D0 + BGT _SocketRecvString_DataReceived + + ; free ram + MOVE.L D1,D0 + MOVE.L A0,A1 + Rjsr L_RamFree + + MOVE.L (SP)+,A3 + + MOVE.L ChVide(A5),D3 + RestoreStackFunction + Ret_String + + ; TODO received data is wrong somewhere + +_SocketRecvString_DataReceived: + ; D0 contains receive length + + MOVE.L D1,-(SP) + MOVEQ #0,D3 + MOVE.W D0,D3 ; prep for L_Demande. can we request a zero length string? + MOVE.L A0,A2 ; L_Demande blows away A0 and A1, A2 now contains buffer + Rjsr L_Demande ; A0/A1 contain string address + + MOVE.W D3,(A0)+ ; put in string length + SUBQ #1,D3 ; reduce by one for DBRA + MOVE.L A2,A3 ; A3 now contains start of buffer + +_SocketRecvString_CopyData: + MOVE.B (A2,D3),(A0,D3) + DBRA D3,_SocketRecvString_CopyData + + EvenOutStringAddress A0,D2 + + ; add to string manager for garbage collection? + MOVE.L A0,HiChaine(A5) + MOVE.L (SP)+,D0 + + ; free ram + MOVE.L A1,-(SP) ; A1 contains our string addres + + ;MOVE.L D1,D0 + MOVE.L A3,A1 + Rjsr L_RamFree + + MOVE.L (SP)+,D3 ; string return + + MOVE.L (SP)+,A3 + + RestoreStackFunction + Ret_String + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Recv(socket LONG to address LONG, max size LONG) +; +; Receive at most length bytes of data from the socket into an existing +; memory area +; +; @return length read, -? on failure +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketRecvData +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D1 ; length + MOVE.L (A3)+,A0 ; address + MOVE.L (A3)+,D0 ; socket + MOVEQ #0,D2 ; flags + + EnsureBSDSocketLibrary _SocketRecvData_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketRecvData_LibraryOpen: + TST.L D1 + BGT _SocketRecvData_PositiveLength + + MOVE.L #-3,D3 + + RestoreStackFunction + Ret_Int + +_SocketRecvData_PositiveLength: + WithDataStorage + LoadBSDSocketBase + CALLLIB recv ; D0 has received length or -1 + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + + + + ; actually close the library + + Lib_Def DoSocketLibraryClose + + Dload A3 + TST.L sockaddr_ram-MB(A3) + BEQ _DoSocketLibraryClose_CloseLibrary + + MOVE.L sockaddr_ram-MB(A3),A1 + MOVE.L #MAX_SOCKETS*len_sockaddr_in,D0 + Rjsr L_RamFree + CLR.L sockaddr_ram-MB(A3) ; prevent double free + +_DoSocketLibraryClose_CloseLibrary: + + MOVE.L BSDSocketBase-MB(A3),D0 + + BEQ _DoSocketLibraryClose_Skip + CLR.L BSDSocketBase-MB(A3) ; prevent double free + + MOVE.L D0,A1 + MOVE.L 4,A6 + CALLLIB CloseLibrary + +_DoSocketLibraryClose_Skip: + RTS + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Wait Async Writing(Socket long, ms timeout long) +; +; Wait for this socket to be ready for writing, with a timeout +; +; @return +; 1 on success +; -2 on library not open +; -3 on not interesting yet +; 0 on timeout +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketWaitAsyncWriting +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D4 ; ms timeout + MOVE.L (A3)+,D0 ; socket + + EnsureBSDSocketLibrary _SocketWaitAsyncWriting_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncWriting_LibraryOpen: + MOVEM.L A3-A4,-(SP) + Dload A4 + + MOVE.L D4,-(SP) + MOVEQ #0,D1 + LeaFdset D1,A0 + ; Clear fdset + CLR.L (A0)+ + CLR.L (A0)+ + + ; we'll use the first FdSet + MOVE.L D0,D2 + LeaFdsetForBit D1,A0,D2 + MOVE.L D2,(A0) + MOVE.L (SP)+,D4 + + Rbsr L_MicrosecondsToTimeval ; A3 contains timeval + + Dload A4 + MOVE.L D0,-(SP) + ; set up WaitSelect + MOVEQ #0,D1 + LeaFdset D1,A1 ; write fdset + + MOVE.L #0,A0 ; read fdset + MOVE.L #0,A2 ; error fdset + MOVEQ #0,D0 + MOVE.W MaxSocketSeen-MB(A4),D0 + ADDQ #1,D0 ; number of file descriptors + 1 + MOVEQ #0,D1 ; mask + + MOVE.L BSDSocketBase-MB(A4),A6 + CALLLIB WaitSelect + + TST.L D0 ; timeout, pass through + BPL _SocketWaitAsyncWriting_Done + MOVE.L (SP)+,D0 ; D0 contains socket again + + Dload A4 + ; a socket became interesting, check the Fdset + MOVE.L D0,-(SP) + MOVE.L D0,D3 ; socket in D3 + MOVEQ #0,D1 ; fdset in D1 + LeaFdsetForBit D1,A0,D3 ; A0 is mem, D3 is mask + AND.L D3,(A0) ; D3 should mask (A0) + BEQ _SocketWaitAsyncWriting_CheckSockopt ; if yes, is ready + MOVE.L (SP)+,D0 + + ; not interesting yet + MOVEM.L (SP)+,A3-A4 + MOVE.L #-3,D3 + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncWriting_CheckSockopt: + MOVE.L (SP)+,D0 ; socket + MOVE.L #$1007,D2 ; option + MOVE.L #SOL_SOCKET,D1 ; level + + MOVE.L A3,-(SP) + + WithDataStorage + Dlea IoctlSockOptScratch,A0 ; optval + MOVE.L #4,getsockopt_len-MB(A3) + Dlea getsockopt_len,A1 + + LoadBSDSocketBase + CALLLIB getsockopt + EndDataStorage + + MOVE.L (SP)+,A3 + + TST.L D0 + BPL _SocketWaitAsyncWriting_Ready + + MOVEM.L (SP)+,A3-A4 + MOVE.L #-3,D3 + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncWriting_Ready: + MOVEM.L (SP)+,A3-A4 + MOVEQ #1,D3 + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncWriting_Done: + MOVE.L (SP)+,D1 + MOVEM.L (SP)+,A3-A4 + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Wait Async Reading(Socket long, ms timeout long) +; +; Wait for this socket to be ready for reading, with a timeout +; +; @return +; 1 on success +; -2 on library not open +; -3 on not interesting yet +; 0 on timeout +; -1 on error +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketWaitAsyncReading +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D4 ; ms timeout + MOVE.L (A3)+,D0 ; socket + + EnsureBSDSocketLibrary _SocketWaitAsyncReading_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncReading_LibraryOpen: + MOVEM.L A3-A4,-(SP) + Dload A4 + + MOVE.L D4,-(SP) + MOVEQ #0,D1 + LeaFdset D1,A0 + ; Clear fdset + CLR.L (A0)+ + CLR.L (A0)+ + + ; we'll use the first FdSet + MOVE.L D0,D2 + LeaFdsetForBit D1,A0,D2 + MOVE.L D2,(A0) + MOVE.L (SP)+,D4 + + Rbsr L_MicrosecondsToTimeval ; A3 contains timeval + + Dload A4 + MOVE.L D0,-(SP) + ; set up WaitSelect + MOVEQ #0,D1 + LeaFdset D1,A0 ; read fdset + + MOVE.L #0,A1 ; write fdset + MOVE.L #0,A2 ; error fdset + MOVEQ #0,D0 + MOVE.W MaxSocketSeen-MB(A4),D0 + ADDQ #1,D0 ; number of file descriptors + 1 + MOVEQ #0,D1 + + MOVE.L BSDSocketBase-MB(A4),A6 + CALLLIB WaitSelect + + TST.L D0 ; timeout, pass through + BEQ _SocketWaitAsyncReading_Done + BMI _SocketWaitAsyncReading_Done + MOVE.L (SP)+,D0 ; D0 contains socket again + + ;Dload A4 + ; a socket became interesting, check the Fdset + MOVE.L D0,D3 ; socket in D3 + MOVEQ #0,D1 ; fdset in D1 + LeaFdsetForBit D1,A0,D3 ; A0 is mem, D3 is mask + AND.L D3,(A0) ; D3 should mask (A0) + BEQ _SocketWaitAsyncReading_Ready ; if yes, is ready + + ; not interesting yet + MOVEM.L (SP)+,A3-A4 + MOVE.L #-3,D3 + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncReading_Ready: + MOVEM.L (SP)+,A3-A4 + MOVEQ #1,D3 + + RestoreStackFunction + Ret_Int + +_SocketWaitAsyncReading_Done: + MOVE.L (SP)+,D1 + MOVEM.L (SP)+,A3-A4 + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Reuse Addr(Socket ID LONG) +; +; Specifically set the SO_REUSEADDR function on a socket +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketReuseAddr +; - - - - - - - - - - - - - + PreserveStackFunction + EnsureBSDSocketLibrary _SocketReuseAddr_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketReuseAddr_LibraryOpen: + MOVE.L D3,D0 + MOVE.L #SO_REUSEADDR,D2 + MOVEQ #1,D3 + + Rbsr L_SetSockoptInt + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; A3 MicrosecondsToTimeval(Microseconds D4) +; +; Create a Timeval struct from microseconds +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Lib_Def MicrosecondsToTimeval + + MULU #1000,D4 ; we accept milliseconds + ; turn this into seconds and microseconds via divide + + MOVEM.L D0/D1/A5,-(SP) + MOVE.L D4,D0 + MOVE.L #1000000,D1 + + LongDivideD0ByD1 + + Dlea select_timeval,A3 + MOVE.L D0,(A3)+ + MOVE.L D1,(A3)+ + MOVEM.L (SP)+,D0/D1/A5 + Dlea select_timeval,A3 + + RTS + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; D0 SetSockoptInt(Socket D0, Option D2, Value D3) +; +; Set a socket option +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Lib_Def SetSockoptInt + + MOVEM.L A0/D1-D3,-(SP) + + MOVE.L #SOL_SOCKET,D1 ; Level + + WithDataStorage + Dlea IoctlSockOptScratch,A0 ; optval + MOVE.L D3,A0 + MOVEQ #4,D3 + + LoadBSDSocketBase + CALLLIB setsockopt + EndDataStorage + + MOVEM.L (SP)+,A0/D1-D3 + RTS + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Dns Get Host Address By Name$(Name String) +; +; Get the status of a socket +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par DnsGetHostAddressByName + + PreserveStackFunction + EnsureBSDSocketLibrary _DnsGetHostAddressByName_LibraryOpen + + MOVE.L ChVide(A5),D3 + + RestoreStackFunction + Ret_String + +_DnsGetHostAddressByName_LibraryOpen: + + ; string so you need demande, hichaine, and chvide + MOVE.L D3,A0 ; name + ADD.L #2,A0 ; skip length, string is null terminated (?) + + WithDataStorage + LoadBSDSocketBase + CALLLIB gethostbyname + EndDataStorage + + TST.L D0 + BNE _DnsGetHostAddressByName_GetIPAddress + + RestoreStackFunction + + MOVE.L ChVide(A5),D3 + Ret_String + +_DnsGetHostAddressByName_GetIPAddress: + + MOVE.L D0,A0 + MOVE.L 16(A0),A1 ; **h_addr_list + MOVE.L (A1),A1 ; *h_addr_list + MOVE.L (A1),D0 ; h_addr_list[0] + + WithDataStorage + LoadBSDSocketBase + CALLLIB Inet_NtoA + EndDataStorage + + MOVE.L D0,A2 + MOVE.L A2,-(SP) + MOVEQ #0,D3 + +_DnsGetHostAddressByName_GetIPAddressLength: + ADDQ #1,D3 + TST.B (A2)+ + BNE _DnsGetHostAddressByName_GetIPAddressLength + MOVE.L (SP)+,A2 + + Rjsr L_Demande ; string is in A0/A1 + MOVE.W D3,(A0)+ + SUBQ #1,D3 + +_DnsGetHostAddressByName_KeepCopying: + MOVE.B (A2,D3),(A0,D3) + DBRA D3,_DnsGetHostAddressByName_KeepCopying + + EvenOutStringAddress A0,D0 + + MOVE.L A0,HiChaine(A5) + MOVE.L A1,D3 + + RestoreStackFunction + + Ret_String + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Set Timeout(Socket ID LONG, ms Long) +; +; Set SO_SNDTIMEO and SO_RCVTIMEO on a socket +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketSetTimeout +; - - - - - - - - - - - - - + PreserveStackFunction + + MOVE.L D3,D4 ; milliseconts + MOVE.L (A3)+,D0 ; socket id + + EnsureBSDSocketLibrary _SocketSetTimeout_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketSetTimeout_LibraryOpen: + MOVE.L A3,-(SP) + Rbsr L_MicrosecondsToTimeval + MOVE.L A3,A0 ; select_timeval + MOVE.L (SP)+,A3 + + MOVE.L D0,D1 ; socket id + ROL.L #1,D1 ; *2 + Dlea timeout_timeval,A1 + ADD.L D1,A1 + MOVE.L (A0)+,(A1)+ ; copy timeval + MOVE.L (A0)+,(A1)+ + SUB.L #8,A1 ; go back + + WithDataStorage + ; socket is d0 + MOVE.L #SOL_SOCKET,D1 ; Level + MOVE.L #$1005,D2 ; SO_SNDTIMEO + MOVE.L A1,A0 ; timeval + MOVEQ #8,D3 + + MOVEM.L D0/A0,-(SP) + LoadBSDSocketBase + CALLLIB setsockopt + + TST.L D0 + BNE _SocketSetTimeout_Fail + MOVEM.L (SP)+,D0/A0 ; socket/timeval + + MOVE.L #SOL_SOCKET,D1 + MOVE.L #$1006,D2 ; SO_RCVTIMEO + MOVEQ #8,D3 + + LoadBSDSocketBase + CALLLIB setsockopt + + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + +_SocketSetTimeout_Fail: + MOVEM.L (SP)+,D1/A0 + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; =Socket Close Socket(Socket ID LONG) +; +; Close a socket +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Par SocketCloseSocket +; - - - - - - - - - - - - - + PreserveStackFunction + EnsureBSDSocketLibrary _SocketCloseSocket_LibraryOpen + + RestoreStackFunction + Ret_Int + +_SocketCloseSocket_LibraryOpen: + + MOVE.L D3,D0 + + WithDataStorage + LoadBSDSocketBase + CALLLIB CloseSocket + EndDataStorage + + MOVE.L D0,D3 + + RestoreStackFunction + Ret_Int + +; +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Even if you do not have error messages, you MUST +; leave TWO routines empty at the end... +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_Empty + Lib_Empty +; - - - - - - - - - - - - - + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Finish the library +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Lib_End +; - - - - - - - - - - - - - + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; TITLE OF THE EXTENSION +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +C_Title dc.b "AMOSPro BSD Socket Library " + Version + dc.b " (code.hackerbun.dev)" + dc.b 0,"$VER: " + Version + dc.b 0 + Even + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; END OF THE EXTENSION +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +C_End dc.w 0 + even diff --git a/src/absdsocket b/src/absdsocket new file mode 100644 index 0000000..47d09f7 --- /dev/null +++ b/src/absdsocket @@ -0,0 +1,9 @@ +echo "***Assembling AMOSPro_BSDSocket.Lib" +assign AMOSPro_System: MiSTerHD:Development/AMOSPro/AMOS_Pro +Stuff:Development/AMOS-Professional-Official-mas/c/Library_Digest BSDSocket.s +Stuff:Development/AMOS-Professional-Official-mas/c/Genam FROM BSDSocket.s TO AMOSPro_BSDSocket.Lib +;delete >NIL: BSDSocket_Labels.s +;delete >NIL: BSDSocket_Size.s +copy AMOSPro_System:APSystem/AMOSPro_BSDSocket.lib AMOSPro_System:APSystem/AMOSPro_BSDSocket.lib.bak +copy AMOSPro_BSDSocket.Lib AMOSPro_System:APSystem/ +apcmp "aqua_test_plugin.amos" inclib diff --git a/the minimum.amos b/the minimum.amos new file mode 100644 index 0000000..8720d01 Binary files /dev/null and b/the minimum.amos differ diff --git a/updated sockaddr in.amos b/updated sockaddr in.amos new file mode 100644 index 0000000..69eeabc Binary files /dev/null and b/updated sockaddr in.amos differ