initial commit
This commit is contained in:
parent
f3880aba6d
commit
c14ba040b2
|
@ -0,0 +1,2 @@
|
||||||
|
*.uaem
|
||||||
|
*.Bak
|
Binary file not shown.
|
@ -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 <sys/error.h> 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
|
Binary file not shown.
165
README.md
165
README.md
|
@ -1,3 +1,166 @@
|
||||||
# amos-pro-bsdsocket-extension
|
# amos-pro-bsdsocket-extension
|
||||||
|
|
||||||
An extension to use BSD Socket functionality within AMOS Professional.
|
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
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -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
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue