Add assembler client
This commit is contained in:
parent
7c24814352
commit
24255230c3
|
@ -18,3 +18,11 @@ This is the code used in the [23 Year Old Code](https://youtu.be/l4GNHJfYOUU) vi
|
|||
prints out what is received, like a very basic `netcat`.
|
||||
|
||||
This is the code used in the [Simple Server in C for the Commodore Amiga](https://youtu.be/i6_PQUsayN4) video.
|
||||
|
||||
## Client in Assembler
|
||||
|
||||
`client_in_asm` uses AsmPro and the AmiTCK SDK to build a simple client that
|
||||
connects to a specified host and port and sends some data, like the other end
|
||||
of a very basic `netcat`.
|
||||
|
||||
This is the code used in the [Building a simple Amiga network client...in assembler?!?](https://youtu.be/HbMPBbF9-RE) video.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Amiga BSD Socket Client in Assembler and C
|
||||
|
||||
The versions are identical, except that the C version uses `printf` instead
|
||||
of `PutStr()`.
|
||||
|
||||
## Assembler Version
|
||||
|
||||
Built using AsmPro. You'll have to adjust the `INCDIR` locations and build
|
||||
the LVOs for the `bsdsocket` and `dos` libraries. You can get the socket
|
||||
FD file from the AmiTCP SDK.
|
||||
|
||||
```
|
||||
fd2pragma fd/dos.fd to include_i/ special 20
|
||||
fd2pragma fd/socket.fd to include_i/ special 20
|
||||
```
|
||||
|
||||
## C Version
|
||||
|
||||
Built using SAS/C 6.58 and the
|
||||
[AmiTCP 4.3 SDK](http://aminet.net/package/comm/tcp/AmiTCP-SDK-4.3).
|
||||
|
||||
`sc link client.c`
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,400 @@
|
|||
FUNC_CNT SET -30
|
||||
FUNCDEF MACRO
|
||||
_LVO\1 EQU FUNC_CNT
|
||||
FUNC_CNT SET FUNC_CNT-6
|
||||
ENDM
|
||||
|
||||
INCDIR "AsmPro:Include/include_i/"
|
||||
INCLUDE "exec/exec_lib.i"
|
||||
INCLUDE "dos/dos_lvo.i"
|
||||
INCLUDE "exec/libraries.i"
|
||||
INCLUDE "exec/memory.i"
|
||||
INCLUDE "dos/dos.i"
|
||||
INCLUDE "socket_lvo.i"
|
||||
|
||||
PrintString MACRO
|
||||
MOVE.L \1,D1
|
||||
MOVE.L DOSBase,A6
|
||||
CALLLIB _LVOPutStr ; dos/PutStr
|
||||
ENDM
|
||||
|
||||
OpenLibrary MACRO
|
||||
MOVEQ \2,D0
|
||||
MOVE.L \1,A1
|
||||
MOVEA.L 4,A6
|
||||
CALLLIB _LVOOpenLibrary ; exec/OpenLibrary
|
||||
ENDM
|
||||
|
||||
ExitProgram MACRO
|
||||
MOVEQ \1,D0
|
||||
RTS
|
||||
ENDM
|
||||
|
||||
DoCloseDOSLibrary MACRO
|
||||
MOVEA.L DOSBase,A1
|
||||
MOVEA.L 4,A6
|
||||
CALLLIB _LVOCloseLibrary ; exec/CloseLibrary
|
||||
ENDM
|
||||
|
||||
DoCloseBSDSocketLibrary MACRO
|
||||
MOVEA.L BSDSocketBase,A1
|
||||
MOVEA.L 4,A6
|
||||
CALLLIB _LVOCloseLibrary ; exec/CloseLibrary
|
||||
ENDM
|
||||
|
||||
DeallocateBuffer MACRO
|
||||
MOVE.L ReadBuffer,A1
|
||||
MOVE.L #READ_BUFFER_SIZE,D0
|
||||
MOVEA.L 4,A6
|
||||
CALLLIB _LVOFreeMem ; exec/FreeMem
|
||||
ENDM
|
||||
|
||||
DoCloseSocket MACRO
|
||||
MOVE.L SocketID,D0
|
||||
MOVE.L BSDSocketBase,A6
|
||||
CALLLIB _LVOCloseSocket ; bsdsocket/CloseSocket
|
||||
ENDM
|
||||
|
||||
; our user message has a limit of this many characters
|
||||
READ_BUFFER_SIZE EQU 1024
|
||||
|
||||
; 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
|
||||
|
||||
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
|
||||
|
||||
Start:
|
||||
OpenLibrary #DOSLibrary,#36
|
||||
MOVE.L D0,DOSBase
|
||||
TST.L D0
|
||||
BNE OpenBSDSocketLibrary
|
||||
|
||||
; if this fails, we have bigger problems
|
||||
MOVE.L #20,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
OpenBSDSocketLibrary:
|
||||
OpenLibrary #BSDSocketLibrary,#4
|
||||
MOVE.L D0,BSDSocketBase
|
||||
TST.L D0
|
||||
BNE ReserveBuffer
|
||||
|
||||
PrintString #UnableToOpenBSDSocketLibrary
|
||||
|
||||
; be sure to unwind everything we've opened so far
|
||||
; i tried using this with codewatcher and i think there's
|
||||
; something up with trying to read command line argument
|
||||
; when run under codewatcher.
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
ReserveBuffer:
|
||||
MOVE.L #READ_BUFFER_SIZE,D0
|
||||
; we don't care where the memory comes from
|
||||
; nor should we!
|
||||
MOVE.L #MEMF_CLEAR,D1
|
||||
MOVEA.L 4,A6
|
||||
CALLLIB _LVOAllocMem ; dos/AllocMem
|
||||
MOVE.L D0,ReadBuffer
|
||||
TST.L D0
|
||||
BNE ReadIPAddress
|
||||
|
||||
PrintString #UnableToAllocateBuffer
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
ReadIPAddress:
|
||||
MOVE.L ReadBuffer,D1
|
||||
MOVE.L #READ_BUFFER_SIZE,D2
|
||||
MOVEQ #0,D3
|
||||
MOVE.L DOSBase,A6
|
||||
CALLLIB _LVOReadItem ; dos/ReadItem
|
||||
|
||||
; fail on an error or a missing parameter
|
||||
CMP.L #ITEM_ERROR,D0
|
||||
BEQ NoIPAddress
|
||||
CMP.L #ITEM_NOTHING,D0
|
||||
BEQ NoIPAddress
|
||||
|
||||
BRA ParseIPAddress
|
||||
|
||||
NoIPAddress:
|
||||
PrintString #UnableToReadArguments
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
ParseIPAddress:
|
||||
MOVE.L ReadBuffer,A0
|
||||
MOVE.L BSDSocketBase,A6
|
||||
CALLLIB _LVOinet_addr ; bsdsocket/inet_addr
|
||||
MOVE.L D0,IPAddress
|
||||
|
||||
TST.L D0
|
||||
BNE ReadPort
|
||||
|
||||
PrintString #UnableToParseIPAddress
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
ReadPort:
|
||||
MOVE.L ReadBuffer,D1
|
||||
MOVE.L #READ_BUFFER_SIZE,D2
|
||||
MOVEQ #0,D3
|
||||
MOVE.L DOSBase,A6
|
||||
CALLLIB _LVOReadItem ; dos/ReadItem
|
||||
|
||||
; fail on an error or a missing parameter
|
||||
CMP.L #ITEM_ERROR,D0
|
||||
BEQ NoPort
|
||||
CMP.L #ITEM_NOTHING,D0
|
||||
BEQ NoPort
|
||||
|
||||
BRA ParsePort
|
||||
|
||||
NoPort:
|
||||
PrintString #UnableToReadArguments
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
|
||||
ParsePort:
|
||||
; the Amiga library way to do atol()
|
||||
MOVE.L ReadBuffer,D1
|
||||
MOVE.L #PortLong,D2
|
||||
MOVE.L DOSBase,A6
|
||||
CALLLIB _LVOStrToLong ; dos/StrToLong
|
||||
|
||||
CMP.L #-1,D0
|
||||
BNE _ParsePort_Continue
|
||||
|
||||
PrintString #UnableToParsePort
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
_ParsePort_Continue:
|
||||
MOVE.L PortLong,D0
|
||||
|
||||
; we could just crop the provided port at a word boundary,
|
||||
; but let's be nice and tell the user they entered in a bad
|
||||
; port number.
|
||||
CMP.L #65536,D0
|
||||
BLT ReadRemainingCommandLine
|
||||
|
||||
PrintString #PortOutOfRange
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
ReadRemainingCommandLine:
|
||||
; lop off the actual port number
|
||||
MOVE.W D0,Port
|
||||
|
||||
; this is the Amiga equivalent of stdin?
|
||||
MOVE.L DOSBase,A6
|
||||
CALLLIB _LVOInput ; dos/Input
|
||||
MOVE.L D0,Stdin
|
||||
|
||||
MOVE.L ReadBuffer,FGetCLocation
|
||||
MOVEQ #0,D2
|
||||
|
||||
_ReadRemainingCommandLine_Loop:
|
||||
; loop until fgetc returns -1 or 10
|
||||
MOVE.L Stdin,D1
|
||||
MOVE.L DOSBase,A6
|
||||
CALLLIB _LVOFGetC ; dos/FGetC
|
||||
|
||||
MOVE.L FGetCLocation,A0
|
||||
CMP.L #-1,D0 ; end on EOF...
|
||||
BEQ _ReadRemainingCommandLine_Finish
|
||||
CMP.L #10,D0 ; or a 10, from you physically hitting enter in the CLI
|
||||
BEQ _ReadRemainingCommandLine_Finish
|
||||
|
||||
MOVE.B D0,(A0)+
|
||||
ADDQ #1,D2
|
||||
MOVE.L A0,FGetCLocation
|
||||
BRA _ReadRemainingCommandLine_Loop
|
||||
|
||||
_ReadRemainingCommandLine_Finish:
|
||||
; add the chr(10) back, and end the string with a null
|
||||
MOVE.B #10,(A0)+
|
||||
MOVE.B #0,(A0)+
|
||||
|
||||
; we need to know how many characters total we read
|
||||
ADDQ #2,D2
|
||||
MOVE.L D2,ReadBufferLength
|
||||
|
||||
CreateSocket:
|
||||
MOVE.L #PF_INET,D0
|
||||
MOVE.L #SOCK_STREAM,D1
|
||||
MOVE.L #IPPROTO_TCP,D2
|
||||
MOVE.L BSDSocketBase,A6
|
||||
CALLLIB _LVOsocket ; exec/socket
|
||||
MOVE.L D0,SocketID
|
||||
|
||||
PrintIPAddress:
|
||||
; exec/RawDoFmt is basically sprintf.
|
||||
LEA IPAddrString,A0
|
||||
|
||||
; load up the printf arguments
|
||||
LEA DoFmtBuffer,A1
|
||||
MOVE.L IPAddress,(A1)+
|
||||
MOVE.W Port,(A1)+
|
||||
MOVE.L SocketID,(A1)+
|
||||
LEA DoFmtBuffer,A1
|
||||
|
||||
LEA _StuffChar,A2
|
||||
LEA PrintBuffer,A3
|
||||
|
||||
MOVE.L 4,A6
|
||||
CALLLIB _LVORawDoFmt ; exec/RawDoFmt
|
||||
|
||||
PrintString #PrintBuffer
|
||||
|
||||
MOVE.L SocketID,D0
|
||||
CMP.L #-1,D0
|
||||
BNE ConnectSocket
|
||||
|
||||
PrintString #UnableToOpenSocket
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
ConnectSocket:
|
||||
; populate sockaddr_in
|
||||
LEA ConnectSockAddrIn,A0
|
||||
MOVE.B #AF_INET,sockaddr_in_sin_family(A0)
|
||||
MOVE.W Port,sockaddr_in_sin_port(A0)
|
||||
MOVE.L IPAddress,sockaddr_in_sin_addr(A0)
|
||||
|
||||
MOVE.L SocketID,D0
|
||||
MOVE.L #len_sockaddr_in,D1
|
||||
MOVE.L BSDSocketBase,A6
|
||||
CALLLIB _LVOconnect ; bsdsocket/connect
|
||||
|
||||
; a socket is connected if the response is 0
|
||||
TST.L D0
|
||||
BEQ SendData
|
||||
|
||||
PrintString #UnableToConnectSocket
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
SendData:
|
||||
MOVE.L SocketID,D0
|
||||
MOVE.L ReadBuffer,A0
|
||||
MOVE.L ReadBufferLength,D1
|
||||
MOVEQ #0,D2
|
||||
MOVE.L BSDSocketBase,A6
|
||||
CALLLIB _LVOsend ; bsdsocket/send
|
||||
|
||||
CMP.L #-1,D0
|
||||
BNE CloseSocket
|
||||
|
||||
PrintString #UnableToSendData
|
||||
|
||||
MOVE.L #1,ExitCode
|
||||
BRA Teardown
|
||||
|
||||
CloseSocket:
|
||||
MOVE.L #0,ExitCode
|
||||
|
||||
; check all of the things we might have allocated and
|
||||
; tear them down if we did, then return the appropriate exit code.
|
||||
Teardown:
|
||||
MOVE.L SocketID,D0
|
||||
CMP.L #-1,D0
|
||||
BEQ _Teardown_DeallocateBuffer
|
||||
|
||||
DoCloseSocket
|
||||
_Teardown_DeallocateBuffer:
|
||||
MOVE.L ReadBuffer,D0
|
||||
BEQ _Teardown_CloseBSDSocketLibrary
|
||||
|
||||
DeallocateBuffer
|
||||
_Teardown_CloseBSDSocketLibrary:
|
||||
MOVE.L BSDSocketBase,D0
|
||||
BEQ _Teardown_CloseDOSLibrary
|
||||
|
||||
DoCloseBSDSocketLibrary
|
||||
_Teardown_CloseDOSLibrary:
|
||||
MOVE.L DOSBase,D0
|
||||
BEQ _Teardown_Exit
|
||||
|
||||
DoCloseDOSLibrary
|
||||
_Teardown_Exit:
|
||||
MOVEQ #0,D0
|
||||
MOVE.L ExitCode,D0
|
||||
RTS
|
||||
|
||||
; RawDoFmt helper
|
||||
_StuffChar:
|
||||
MOVE.B D0,(a3)+
|
||||
RTS
|
||||
|
||||
|
||||
; libraries we open
|
||||
DOSBase DCB.L 1,0
|
||||
BSDSocketBase DCB.L 1,0
|
||||
|
||||
; allocated memory
|
||||
ReadBuffer DCB.L 1,0
|
||||
ReadBufferLength DS.L 1
|
||||
|
||||
; return code for the program
|
||||
ExitCode DS.L 1
|
||||
|
||||
; reading command line args
|
||||
Stdin DS.L 1
|
||||
|
||||
; user parameters
|
||||
IPAddress DS.L 1
|
||||
Port DS.W 1
|
||||
PortLong DS.L 1
|
||||
|
||||
; message to send
|
||||
FGetCChar DS.L 1
|
||||
FGetCLocation DS.L 1
|
||||
|
||||
; establishing a connection
|
||||
SocketID DCB.L 1,-1
|
||||
ConnectSockAddrIn DCB.B 16,0
|
||||
|
||||
; place to build a string for printing via RawDoFmt
|
||||
PrintBuffer DS.B 100
|
||||
|
||||
; stack for accepting RawDoFmt arguments as values
|
||||
DoFmtBuffer DS.B 32
|
||||
|
||||
DOSLibrary DC.B "dos.library",0
|
||||
BSDSocketLibrary DC.B "bsdsocket.library",0
|
||||
|
||||
;FakeIP DC.B "1.2.3.4",0
|
||||
;FakePort DC.B "1234",0
|
||||
|
||||
; user messages
|
||||
UnableToOpenBSDSocketLibrary DC.B "Unable to open bsdsocket.library. Have you started a TCP stack?",10,0
|
||||
UnableToAllocateBuffer DC.B "Unable to allocate buffer",10,0
|
||||
UnableToReadArguments DC.B "client <ip address> <port> <message...>",10,0
|
||||
UnableToParseIPAddress DC.B "Unable to parse IP Address",10,0
|
||||
UnableToOpenSocket DC.B "Unable to open socket",10,0
|
||||
UnableToConnectSocket DC.B "Unable to connect socket",10,0
|
||||
UnableToSendData DC.B "Unable to send data",10,0
|
||||
UnableToParsePort DC.B "Unable to parse port",10,0
|
||||
PortOutOfRange DC.B "Port out of range (0-65535)",10,0
|
||||
;Here DC.B "Here",10,0
|
||||
IPAddrString DC.B "IP address is %8lx, port is %d, socket is %ld",10,0
|
||||
;FGetCResult DC.B "%ld %ld %ld",10,0
|
|
@ -0,0 +1,145 @@
|
|||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <proto/exec.h>
|
||||
#include <proto/dos.h>
|
||||
|
||||
#include <exec/memory.h>
|
||||
|
||||
#include <proto/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct Library *SocketBase;
|
||||
char *ReadBuffer;
|
||||
int SocketID = -1;
|
||||
|
||||
#define READ_BUFFER_SIZE (1024)
|
||||
|
||||
void teardown() {
|
||||
if (SocketBase && SocketID != -1) {
|
||||
CloseSocket(SocketID);
|
||||
}
|
||||
|
||||
if (ReadBuffer) {
|
||||
FreeMem(ReadBuffer, READ_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (SocketBase) {
|
||||
CloseLibrary(SocketBase);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int ReadItemResult;
|
||||
ULONG IPAddress;
|
||||
long Port;
|
||||
char CurrentChar;
|
||||
int ReadIndex;
|
||||
BPTR Stdin;
|
||||
|
||||
struct sockaddr_in ConnectSockaddrIn;
|
||||
|
||||
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) {
|
||||
printf("Unable to open bsdsocket.library. Have you started a TCP stack?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(ReadBuffer = AllocMem(READ_BUFFER_SIZE, MEMF_CLEAR))) {
|
||||
printf("Unable to allocate buffer\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// yes we could use argc/argv but we're mimicking what's in the
|
||||
// original assembler code
|
||||
|
||||
ReadItemResult = ReadItem(ReadBuffer, READ_BUFFER_SIZE, NULL);
|
||||
if (
|
||||
ReadItemResult == ITEM_ERROR ||
|
||||
ReadItemResult == ITEM_NOTHING
|
||||
) {
|
||||
printf("client <ip address> <port> <message...>\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(IPAddress = inet_addr(ReadBuffer))) {
|
||||
printf("Unable to parse IP Address\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ReadItemResult = ReadItem(ReadBuffer, READ_BUFFER_SIZE, NULL);
|
||||
if (
|
||||
ReadItemResult == ITEM_ERROR ||
|
||||
ReadItemResult == ITEM_NOTHING
|
||||
) {
|
||||
printf("client <ip address> <port> <message...>\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (-1 == (StrToLong(ReadBuffer, &Port))) {
|
||||
printf("client <ip address> <port> <message...>\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Port > 65535) {
|
||||
printf("Port out of range (0-65535)\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
Stdin = Input();
|
||||
|
||||
Port = (short)Port;
|
||||
CurrentChar = FGetC(Stdin);
|
||||
ReadIndex = 0;
|
||||
|
||||
while (CurrentChar != -1 && CurrentChar != 10) {
|
||||
ReadBuffer[ReadIndex++] = CurrentChar;
|
||||
printf("%d\n",CurrentChar);
|
||||
CurrentChar = FGetC(Stdin);
|
||||
}
|
||||
|
||||
ReadBuffer[ReadIndex++] = 10;
|
||||
ReadBuffer[ReadIndex++] = 0;
|
||||
|
||||
SocketID = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
// we'll skip on using RawDoFmt here
|
||||
printf(
|
||||
"IP address is %8x, port is %d, socket is %d\n",
|
||||
IPAddress,
|
||||
Port,
|
||||
SocketID
|
||||
);
|
||||
|
||||
if (SocketID == -1) {
|
||||
printf("Unable to open socket\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ConnectSockaddrIn.sin_family = AF_INET;
|
||||
ConnectSockaddrIn.sin_port = Port;
|
||||
ConnectSockaddrIn.sin_addr.s_addr = IPAddress;
|
||||
|
||||
if (connect(SocketID, (struct sockaddr *)&ConnectSockaddrIn, sizeof(struct sockaddr_in)) == -1) {
|
||||
printf("Unable to connect socket\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (send(SocketID, ReadBuffer, ReadIndex, 0) == -1) {
|
||||
printf("Unable to send data\n");
|
||||
teardown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
teardown();
|
||||
return 0;
|
||||
|
||||
}
|
Loading…
Reference in New Issue