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
|
||||
|
||||
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