Introduced C interface to IP65.

The IP5 usage of ld65 segments and zeropage variables was made compatible with cc65 C programs already a while ago. This commit is the next logical step which is to introduce the actual C interface to IP65.

IP65 for C programs shares the the ip65.lib / ip65_tcp.lib with IP65 for assembler programs. However the various libraries from the 'drivers' are not reused. Instead there's exactly one library for every target named ip65_<target>.lib. Those libraries contain only functions used by ip65.lib / ip65_tcp.lib.

TODOs:

- Introduce c64_timer.s and atr_timer.s.
- Add a C interface to the rest of the IP65 functionality (especially TCP).
This commit is contained in:
Oliver Schmidt 2017-11-05 14:28:49 +01:00
parent 32a66c5d7c
commit 6680772b04
17 changed files with 1107 additions and 21 deletions

View File

@ -1,3 +1,5 @@
# For assembler programs
# ----------------------
# c64rrnet.lib : C64 with RR-Net (or clone) (default base addr: $de0x)
# c64eth64.lib : C64 with ETH64 (default base addr: $de0x)
# c64wiz811.lib : C64 with WIZ811MJ (default base addr: $de0x)
@ -9,20 +11,32 @@
# atrdragon.lib : ATARI 8-bit with Dragon Cart (default base addr: $d500)
# vic20rrnet.lib : VIC20 with RR-Net or clone (default base addr: $980x)
# For C programs
# --------------
# ip65_c64.lib : C64 with RR-Net or ETH64 or WIZ811MJ (default base addr: $de0x)
# ip65_apple2.lib : Apple ][ with Uthernet or LANceGS or Uthernet II (default slot: #3)
# ip65_atari.lib : ATARI 8-bit with Dragon Cart (default base addr: $d500)
DRIVERS=\
c64rrnet.lib \
c64eth64.lib \
c64wiz811.lib \
c64combo.lib \
ip65_c64.lib \
a2uther.lib \
a2lancegs.lib \
a2uther2.lib \
a2combo.lib \
ip65_apple2.lib \
atrdragon.lib \
ip65_atari.lib \
vic20rrnet.lib
all: $(DRIVERS)
$(DRIVERS):
ar65 a $@ $^
%.o: %.s
ca65 -D DYN_DRV=0 $<
@ -35,6 +49,10 @@ C64OBJS=\
c64vt100.o \
cbmcharconv.o
C64_OBJS=\
c64timer.o \
c64_input.o
A2OBJS=\
a2print.o \
a2timer.o \
@ -44,6 +62,10 @@ A2OBJS=\
a2vt100.o \
a2charconv.o
A2_OBJS=\
a2_timer.o \
a2_input.o
ATROBJS=\
atrprint.o \
atrtimer.o \
@ -54,6 +76,10 @@ ATROBJS=\
atrvt100font.o \
atrcharconv.o
ATR_OBJS=\
atrtimer.o \
atr_input.o
VIC20OBJS=\
vic20print.o \
vic20timer.o \
@ -63,35 +89,52 @@ VIC20OBJS=\
vic20vt100.o \
cbmcharconv.o
c64rrnet.lib: rr-net.o cs8900a.o cs8900adriver.o ethernet.o c64init.o $(C64OBJS)
ar65 a $@ $^
CS8900AOBJS=\
cs8900a.o \
cs8900adriver.o \
ethernet.o
c64eth64.lib: eth64.o lan91c96.o lan91c96driver.o ethernet.o c64init.o $(C64OBJS)
ar65 a $@ $^
LAN91C96OBJS=\
lan91c96.o \
lan91c96driver.o \
ethernet.o
c64wiz811.lib: wiz811mj.o w5100.o w5100driver.o ethernet.o c64init.o $(C64OBJS)
ar65 a $@ $^
W5100OBJS=\
w5100.o \
w5100driver.o \
ethernet.o
c64combo.lib: rr-net.o cs8900a.o eth64.o lan91c96.o wiz811mj.o w5100.o ethernetcombo.o c64init.o $(C64OBJS)
ar65 a $@ $^
COMBOOBJS=\
cs8900a.o \
lan91c96.o \
w5100.o \
ethernetcombo.o
a2uther.lib: uthernet.o cs8900a.o cs8900adriver.o ethernet.o a2init.o $(A2OBJS)
ar65 a $@ $^
c64rrnet.lib: c64init.o rr-net.o $(CS8900AOBJS) $(C64OBJS)
a2lancegs.lib: lancegs.o lan91c96.o lan91c96driver.o ethernet.o a2init.o $(A2OBJS)
ar65 a $@ $^
c64eth64.lib: c64init.o eth64.o $(LAN91C96OBJS) $(C64OBJS)
a2uther2.lib: uthernet2.o w5100.o w5100driver.o ethernet.o a2init.o $(A2OBJS)
ar65 a $@ $^
c64wiz811.lib: c64init.o wiz811mj.o $(W5100OBJS) $(C64OBJS)
a2combo.lib: uthernet.o cs8900a.o lancegs.o lan91c96.o uthernet2.o w5100.o ethernetcombo.o a2initcombo.o $(A2OBJS)
ar65 a $@ $^
c64combo.lib: c64init.o rr-net.o eth64.o wiz811mj.o $(COMBOOBJS) $(C64OBJS)
atrdragon.lib: dragoncart.o cs8900a.o cs8900adriver.o ethernet.o atrinit.o $(ATROBJS)
ar65 a $@ $^
ip65_c64.lib: c64init.o rr-net.o eth64.o wiz811mj.o $(COMBOOBJS) $(C64_OBJS)
vic20rrnet.lib: vic20-rr-net.o cs8900a.o cs8900adriver.o ethernet.o vic20init.o $(VIC20OBJS)
ar65 a $@ $^
a2uther.lib: a2init.o uthernet.o $(CS8900AOBJS) $(A2OBJS)
a2lancegs.lib: a2init.o lancegs.o $(LAN91C96OBJS) $(A2OBJS)
a2uther2.lib: a2init.o uthernet2.o $(W5100OBJS) $(A2OBJS)
a2combo.lib: a2initcombo.o uthernet.o lancegs.o uthernet2.o $(COMBOOBJS) $(A2OBJS)
ip65_apple2.lib: a2initcombo.o uthernet.o lancegs.o uthernet2.o $(COMBOOBJS) $(A2_OBJS)
atrdragon.lib: atrinit.o dragoncart.o $(CS8900AOBJS) $(ATROBJS)
ip65_atari.lib: atrinit.o dragoncart.o $(CS8900AOBJS) $(ATR_OBJS)
vic20rrnet.lib: vic20init.o vic20-rr-net.o $(CS8900AOBJS) $(VIC20OBJS)
clean:
-rm -f *.o

46
drivers/a2_input.s Normal file
View File

@ -0,0 +1,46 @@
.export check_for_abort_key
.export abort_key
.exportzp abort_key_default = $9b
.exportzp abort_key_disable = $80
.data
abort_key: .byte $9b ; ESC
.code
; check whether the abort key is being pressed
; inputs: none
; outputs: sec if abort key pressed, clear otherwise
check_for_abort_key:
lda $c000 ; current key pressed
cmp abort_key
bne :+
bit $c010 ; clear the keyboard strobe
sec
rts
: clc
rts
; -- LICENSE FOR a2_inputc.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
;
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
;
; The Original Code is ip65.
;
; The Initial Developer of the Original Code is Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --

73
drivers/a2_timer.s Normal file
View File

@ -0,0 +1,73 @@
; timer routines
;
; unfortunately the standard Apple 2 has no CIA or VBI, so for the moment, we will
; make each call to 'timer_read' delay for a little while
; this kludge will make the polling loops work at least
;
; timer_read is meant to return a counter with millisecond resolution
.include "../inc/common.i"
.export timer_init
.export timer_read
.export timer_seconds
.bss
current_time_value: .res 2
.code
; reset timer to 0
; inputs: none
; outputs: none
timer_init:
ldax #0
stax current_time_value
rts
; this SHOULD just read the current timer value
; but since a standard apple 2 has no dedicated timing circuit,
; each call to this function actually delays a while, then updates the current value
; inputs: none
; outputs: AX = current timer value (roughly equal to number of milliseconds since the last call to 'timer_init')
timer_read:
bit $c082 ; switch in ROM
lda #111
jsr $fca8 ; wait for about 33ms
clc
lda #33
adc current_time_value
sta current_time_value
bcc :+
inc current_time_value+1
: ldax current_time_value
bit $c080 ; switch in LC bank 2 for R/O
rts
timer_seconds:
lda #0
rts
; -- LICENSE FOR a2_timerc.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
;
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
;
; The Original Code is ip65.
;
; The Initial Developer of the Original Code is Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --

49
drivers/atr_input.s Normal file
View File

@ -0,0 +1,49 @@
.include "atari.inc"
.export check_for_abort_key
.export abort_key
.exportzp abort_key_default = 1
.exportzp abort_key_disable = 0
.data
abort_key: .byte 1
.code
; check whether the abort key is being pressed
; inputs: none
; outputs: sec if abort key pressed, clear otherwise
check_for_abort_key:
lda abort_key ; is "abort" enabled?
beq nokey ; no
lda BRKKEY
bne nokey
dec BRKKEY
sec
rts
nokey:
clc
rts
;-- LICENSE FOR atr_inputs.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
;
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
;
; The Original Code is ip65.
;
; The Initial Developer of the Original Code is Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --

54
drivers/c64_input.s Normal file
View File

@ -0,0 +1,54 @@
.export check_for_abort_key
.export abort_key
.exportzp abort_key_default = $3f
.exportzp abort_key_disable = $ff
.data
abort_key: .byte $3f ; RUN/STOP
.code
; check whether the abort key is being pressed
; inputs: none
; outputs: sec if abort key pressed, clear otherwise
check_for_abort_key:
lda $cb ; current key pressed
cmp abort_key
bne no_key
@flush_loop:
ldy #$ff
jsr $f142 ; not officially documented - where F13E (GETIN) falls through to if device # is 0 (KEYBD)
cpy #$ff ; Y gets modified iff there's a character available - this approach allows to read ^@ as 0
bne @flush_loop
lda $cb ; current key pressed
cmp abort_key
beq @flush_loop
sec
rts
no_key:
clc
rts
; -- LICENSE FOR c64_input.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
;
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
;
; The Original Code is ip65.
;
; The Initial Developer of the Original Code is Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --

181
inc/ip65.h Normal file
View File

@ -0,0 +1,181 @@
#ifndef _IP65_H
#define _IP65_H
// Error codes
//
#define IP65_ERROR_PORT_IN_USE $80
#define IP65_ERROR_TIMEOUT_ON_RECEIVE $81
#define IP65_ERROR_TRANSMIT_FAILED $82
#define IP65_ERROR_TRANSMISSION_REJECTED_BY_PEER $83
#define IP65_ERROR_INPUT_TOO_LARGE $84
#define IP65_ERROR_DEVICE_FAILURE $85
#define IP65_ERROR_ABORTED_BY_USER $86
#define IP65_ERROR_LISTENER_NOT_AVAILABLE $87
#define IP65_ERROR_CONNECTION_RESET_BY_PEER $89
#define IP65_ERROR_CONNECTION_CLOSED $8A
#define IP65_ERROR_FILE_ACCESS_FAILURE $90
#define IP65_ERROR_MALFORMED_URL $A0
#define IP65_ERROR_DNS_LOOKUP_FAILED $A1
// Last error code
//
extern unsigned char ip65_error;
// MAC address of local machine (will be overwritten if ip65_init is called)
//
extern unsigned char cfg_mac[6];
// IP address of local machine (will be overwritten if dhcp_init is called)
//
extern unsigned long cfg_ip;
// Netmask of local network (will be overwritten if dhcp_init is called)
//
extern unsigned long cfg_netmask;
// IP address of router on local network (will be overwritten if dhcp_init is called)
//
extern unsigned long cfg_gateway;
// IP address of dns server to use (will be overwritten if dhcp_init is called)
//
extern unsigned long cfg_dns;
// Will be set to address of DHCP server that configuration was obtained from
//
extern unsigned long dhcp_server;
//
//
extern unsigned char* tcp_packet;
extern unsigned int tcp_packet_len;
// Initialise the IP stack
//
// This calls the individual protocol & driver initialisations, so this is
// the only *_init routine that must be called by a user application,
// except for dhcp_init which must also be called if the application
// is using DHCP rather than hardcoded IP configuration.
//
// Inputs: None
// Output: 1 if there was an error, 0 otherwise
//
unsigned char ip65_init(void);
// Main IP polling loop
//
// This routine should be periodically called by an application at any time
// that an inbound packet needs to be handled.
// It is 'non-blocking', i.e. it will return if there is no packet waiting to be
// handled. Any inbound packet will be handed off to the appropriate handler.
//
// Inputs: None
// Output: 1 if no packet was waiting or packet handling caused error, 0 otherwise
//
unsigned char ip65_process(void);
// Generate a 'random' 16 bit word
//
// Entropy comes from the last ethernet frame, counters, and timer.
//
// Inputs: None
// Output: Pseudo-random 16 bit number
//
unsigned int ip65_random_word(void);
// Convert 4 octets (IP address, netmask) into a string representing a dotted quad
//
// The string is returned in a statically allocated buffer, which subsequent calls
// will overwrite.
//
// Inputs: quad: IP address
// Output: Null terminated string containing dotted quad (e.g. "192.168.1.0")
//
char* __fastcall__ dotted_quad(unsigned long quad);
// Convert a string representing a dotted quad (IP address, netmask) into 4 octets
//
// Inputs: quad: Null terminated string containing dotted quad (e.g. "192.168.1.0"),
// to simplify URL parsing, a ':' or '/' can also terminate the string.
// Output: IP address, 0 on error
//
unsigned long __fastcall__ parse_dotted_quad(char* quad);
// Minimal DHCP client implementation
//
// IP addresses are requested from a DHCP server (aka 'leased') but are not renewed
// or released. Although this is not correct behaviour according to the DHCP RFC,
// this works fine in practice in a typical home network environment.
//
// Inputs: None (although ip65_init should be called first)
// Output: 0 if IP config has been sucesfully obtained and cfg_ip, cfg_netmask,
// cfg_gateway and cfg_dns will be set per response from dhcp server.
// dhcp_server will be set to address of server that provided configuration.
// 1 if there was an error
//
unsigned char dhcp_init(void);
// Resolve a string containing a hostname (or a dotted quad) to an IP address
//
// Inputs: hostname: pointer to null terminated string that contains either
// a DNS hostname (e.g. "host.example.com") or an address
// in "dotted quad" format (e.g. "192.168.1.0").
// Output: IP address of the hostname, 0 on error
//
unsigned long __fastcall__ dns_resolve(const char* hostname);
// Send a ping (ICMP echo request) to a remote host, and wait for a response
//
// Inputs: dest: Destination IP address
// Output: 0 if no response, otherwise time (in miliseconds) for host to respond
//
unsigned int __fastcall__ icmp_ping(unsigned long dest);
// Add a UDP listener
//
// Inputs: port: UDP port to listen on
// callback: Vector to call when UDP packet arrives on specified port
// Output: 1 if too may listeners already installed, 0 otherwise
//
unsigned char __fastcall__ udp_add_listener(unsigned int port, void (*callback)(void));
// Remove a UDP listener
//
// Inputs: port: UDP port to stop listening on
// Output: 0 if handler found and removed,
// 1 if handler for specified port not found
//
unsigned char __fastcall__ udp_remove_listener(unsigned int port);
// Access to received UDP packet
//
// Access to the four items below is only valid in the context of a callback
// added with udp_add_listener.
//
extern unsigned char udp_recv_buf[1476]; // Buffer with data received
unsigned int udp_recv_len(void); // Length of data received
unsigned long udp_recv_src(void); // Source IP address
unsigned int udp_recv_src_port(void); // Source port
// Send a UDP packet
//
// If the correct MAC address can't be found in the ARP cache then
// an ARP request is sent - and the UDP packet is NOT sent. The caller
// should wait a while calling ip65_process (to allow time for an ARP
// response to arrive) and then call upd_send again. This behavior
// makes sense as a UDP packet may get lost in transit at any time
// so the caller should to be prepared to resend it after a while
// anyway.
//
// Inputs: buf: Pointer to buffer containing data to be sent
// len: Length of data to send (exclusive of any headers)
// dest: Destination IP address
// dest_port: Destination port
// src_port: Source port
// Output: 1 if an error occured, 0 otherwise
//
unsigned char __fastcall__ udp_send(const unsigned char* buf, unsigned int len,
unsigned long dest, unsigned int dest_port,
unsigned int src_port);
#endif

View File

@ -13,14 +13,20 @@ IP65OBJS=\
arithmetic.o \
arp.o \
config.o \
config_c.o \
copymem.o \
dhcp.o \
dhcp_c.o \
dns.o \
dns_c.o \
dottedquad.o \
dottedquad_c.o \
eth.o \
http.o \
httpd.o \
icmp_c.o \
ip65.o \
ip65_c.o \
tftp.o \
timer.o \
output_buffer.o \
@ -28,6 +34,7 @@ IP65OBJS=\
sntp.o \
string_utils.o \
udp.o \
udp_c.o \
url.o
ip65.lib: $(IP65OBJS) ip.o icmp.o

27
ip65/config_c.s Normal file
View File

@ -0,0 +1,27 @@
.include "../inc/common.i"
.export _cfg_mac
.export _cfg_ip
.export _cfg_netmask
.export _cfg_gateway
.export _cfg_dns
.export _dhcp_server
.import cfg_mac
.import cfg_ip
.import cfg_netmask
.import cfg_gateway
.import cfg_dns
.import dhcp_server
_cfg_mac := cfg_mac
_cfg_ip := cfg_ip
_cfg_netmask := cfg_netmask
_cfg_gateway := cfg_gateway
_cfg_dns := cfg_dns
_dhcp_server := dhcp_server

12
ip65/dhcp_c.s Normal file
View File

@ -0,0 +1,12 @@
.include "../inc/common.i"
.export _dhcp_init
.import dhcp_init
_dhcp_init:
jsr dhcp_init
ldx #$00
txa
rol
rts

25
ip65/dns_c.s Normal file
View File

@ -0,0 +1,25 @@
.include "../inc/common.i"
.export _dns_resolve
.import dns_set_hostname
.import dns_resolve
.import dns_ip
.importzp sreg
_dns_resolve:
jsr dns_set_hostname
bcs error
jsr dns_resolve
bcs error
ldax dns_ip+2
stax sreg
ldax dns_ip
rts
error:
ldx #$00
txa
stax sreg
rts

92
ip65/dottedquad_c.s Normal file
View File

@ -0,0 +1,92 @@
.include "../inc/common.i"
.export _dotted_quad
.export _parse_dotted_quad
.import parse_dotted_quad
.import dotted_quad_value
.importzp sreg, tmp1, tmp2, tmp3
.bss
dotted_quad: .res 4*4 ; "xxx.xxx.xxx.xxx\0"
.code
_dotted_quad:
stax dotted_quad_value
ldax sreg
stax dotted_quad_value+2
ldx #$00
ldy #$00
: jsr convert_byte
inx
cpx #4
bcc :-
dey
lda #$00
sta dotted_quad,y ; replace last dot with '\0'
ldax #dotted_quad
rts
convert_byte:
; hex to bcd routine taken from Andrew Jacob's code at http://www.6502.org/source/integers/hex2dec-more.htm
sed ; switch to decimal mode
lda #$00 ; ensure the result is clear
sta tmp1 ; BCD low
sta tmp2 ; BCD high
lda #8 ; the number of source bits
sta tmp3
: asl dotted_quad_value,x ; shift out one bit
lda tmp1 ; and add into result
adc tmp1
sta tmp1
lda tmp2 ; propagating any carry
adc tmp2
sta tmp2
dec tmp3 ; and repeat for next bit
bne :-
cld ; back to binary
lda tmp2
beq :+
ora #'0'
sta dotted_quad,y ; write x00 if not 0
iny
: lda tmp1
lsr
lsr
lsr
lsr
beq :+
ora #'0'
sta dotted_quad,y ; write 0x0 if not 0
iny
: lda tmp1
and #$0F
ora #'0'
sta dotted_quad,y ; write 00x
iny
lda #'.'
sta dotted_quad,y ; write dot
iny
rts
_parse_dotted_quad:
jsr parse_dotted_quad
bcs error
ldax dotted_quad_value+2
stax sreg
ldax dotted_quad_value
rts
error:
ldx #$00
txa
stax sreg
rts

18
ip65/icmp_c.s Normal file
View File

@ -0,0 +1,18 @@
.include "../inc/common.i"
.export _icmp_ping
.import icmp_echo_ip
.import icmp_ping
.importzp sreg
_icmp_ping:
stax icmp_echo_ip
ldax sreg
stax icmp_echo_ip+2
jsr icmp_ping
bcc :+
ldx #$00
txa
: rts

29
ip65/ip65_c.s Normal file
View File

@ -0,0 +1,29 @@
.include "../inc/common.i"
.export _ip65_init
.export _ip65_process
.export _ip65_random_word
.export _ip65_error
.import ip65_init
.import ip65_process
.import ip65_random_word
.import ip65_error
_ip65_init:
jsr ip65_init
ldx #$00
txa
rol
rts
_ip65_process:
jsr ip65_process
ldx #$00
txa
rol
rts
_ip65_random_word := ip65_random_word
_ip65_error := ip65_error

83
ip65/udp_c.s Normal file
View File

@ -0,0 +1,83 @@
.include "../inc/common.i"
.export _udp_add_listener
.export _udp_remove_listener
.export _udp_recv_buf
.export _udp_recv_len
.export _udp_recv_src
.export _udp_recv_src_port
.export _udp_send
.import udp_add_listener
.import udp_remove_listener
.import ip_inp
.import udp_inp
.import udp_send
.import udp_callback
.importzp ip_src
.importzp udp_src_port
.importzp udp_len
.importzp udp_data
.import udp_send_len
.import udp_send_dest
.import udp_send_dest_port
.import udp_send_src_port
.import popax, popeax
.importzp sreg
_udp_add_listener:
stax udp_callback
jsr popax
jsr udp_add_listener
ldx #$00
txa
rol
rts
_udp_remove_listener:
jsr udp_remove_listener
ldx #$00
txa
rol
rts
_udp_recv_buf := udp_inp+udp_data
_udp_recv_len:
lda udp_inp+udp_len+1
ldx udp_inp+udp_len
sec
sbc #udp_data
bcs :+
dex
: rts
_udp_recv_src:
ldax ip_inp+ip_src+2
stax sreg
ldax ip_inp+ip_src
rts
_udp_recv_src_port:
lda udp_inp+udp_src_port+1
ldx udp_inp+udp_src_port
rts
_udp_send:
stax udp_send_src_port
jsr popax
stax udp_send_dest_port
jsr popeax
stax udp_send_dest
ldax sreg
stax udp_send_dest+2
jsr popax
stax udp_send_len
jsr popax
jsr udp_send
ldx #$00
txa
rol
rts

View File

@ -29,6 +29,7 @@ UDP =\
parsequerystring \
sntp \
tftp \
udp \
vt100
TCP =\
@ -91,6 +92,8 @@ vt100.com: ATARI_CFG = ../apps/atrtelnet.cfg
%-slotscan.o: %.s
ca65 -D A2_SLOT_SCAN -o $@ $<
%.o: %.c
%.prg: %.o ip65 drivers $(INCFILES)
ld65 -o $*.prg -C c64.cfg -m $*.c64.map -vm $< $(IP65LIB) $(C64DRIVERLIB) c64.lib
@ -103,6 +106,18 @@ vt100.com: ATARI_CFG = ../apps/atrtelnet.cfg
%.vicprg: %.o ip65 drivers $(INCFILES)
ld65 -o $*.vicprg -C vic20-32k.cfg -m $*.vic.map -vm $< $(IP65LIB) $(VICDRIVERLIB) vic20.lib
%.prg: %.c ip65 drivers $(INCFILES)
cl65 -o $*.prg -O -t c64 -m $*.c64.map -vm $< $(IP65LIB) ../drivers/ip65_c64.lib
rm $*.o
%.bin: %.c ip65 drivers $(INCFILES)
cl65 -o $*.bin -O -t apple2 -m $*.a2.map -vm $< $(IP65LIB) ../drivers/ip65_apple2.lib
rm $*.o
%.com: %.c ip65 drivers $(INCFILES)
cl65 -o $*.com -O -t atari -m $*.atr.map -vm $< $(IP65LIB) ../drivers/ip65_atari.lib
rm $*.o
ip65test.d64: prg
$(C1541) -format ip65,00 d64 $@
$(C1541) -attach $@ -write dns.prg dns,p
@ -114,6 +129,7 @@ ip65test.d64: prg
$(C1541) -attach $@ -write ping.prg ping,p
$(C1541) -attach $@ -write sntp.prg sntp,p
$(C1541) -attach $@ -write tftp.prg tftp,p
$(C1541) -attach $@ -write udp.prg udp,p
$(C1541) -attach $@ -write vt100.prg vt100,p
ip65test.dsk: bin
@ -128,6 +144,7 @@ ip65test.dsk: bin
java -jar $(AC) -cc65 $@ ping bin < ping.bin
java -jar $(AC) -cc65 $@ sntp bin < sntp.bin
java -jar $(AC) -cc65 $@ tftp bin < tftp.bin
java -jar $(AC) -cc65 $@ udp bin < udp.bin
java -jar $(AC) -cc65 $@ vt100 bin < vt100.bin
ip65test.atr: com
@ -143,12 +160,16 @@ ip65test.atr: com
cp ping.com atr/ping.com
cp sntp.com atr/sntp.com
cp tftp.com atr/tftp.com
cp udp.com atr/udp.com
cp vt100.com atr/vt100.com
$(DIR2ATR) -b Dos25 1040 $@ atr
rm -r atr
%.exe: %.c
cl /Fe:$@ $^
clean:
make -C ../ip65 clean
make -C ../drivers clean
-rm -f *.o *.prg *.bin *.com *.vicprg *.map
-rm -f *.o *.prg *.bin *.com *.vicprg *.map *.obj *.exe
-rm -f ip65test.d64 ip65test.dsk ip65test.atr

212
test/peer.c Normal file
View File

@ -0,0 +1,212 @@
#include <stdio.h>
#include <conio.h>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define LEN 200
static void dump(unsigned char *buf, unsigned len)
{
unsigned i;
for (i = 0; i < len; ++i)
{
if ((i % 24) == 0)
{
printf("\n$%04X:", i);
}
printf(" %02X", buf[i]);
}
printf(".\n");
}
void main(void)
{
printf("Init\n");
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
return;
}
SOCKET udp = socket(AF_INET, SOCK_DGRAM , IPPROTO_UDP);
if (udp == INVALID_SOCKET)
{
return;
}
SOCKET srv = socket(AF_INET, SOCK_STREAM , IPPROTO_TCP);
if (srv == INVALID_SOCKET)
{
return;
}
u_long arg = 1;
if (ioctlsocket(udp, FIONBIO, &arg) == SOCKET_ERROR)
{
return;
}
if (ioctlsocket(srv, FIONBIO, &arg) == SOCKET_ERROR)
{
return;
}
SOCKADDR_IN local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(6502);
if (bind(udp, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR)
{
return;
}
if (bind(srv, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR)
{
return;
}
if (listen(srv, 1) == SOCKET_ERROR)
{
return;
}
SOCKADDR_IN remote;
remote.sin_addr.s_addr = INADDR_NONE;
SOCKET tcp = INVALID_SOCKET;
printf("(U)DP, (T)CP or e(X)it\n");
char key;
do
{
int len;
unsigned char buf[1500];
if (kbhit())
{
key = getch();
}
else
{
key = '\0';
}
if (key == 'u')
{
if (remote.sin_addr.s_addr == INADDR_NONE)
{
printf("Peer Unknown As Yet\n", len);
}
else
{
unsigned i;
len = LEN;
for (i = 0; i < len; ++i)
{
buf[i] = i;
}
printf("Send Len %d To %s", len, inet_ntoa(remote.sin_addr));
if (sendto(udp, buf, len, 0, (SOCKADDR *)&remote, sizeof(remote)) == SOCKET_ERROR)
{
return;
}
printf(".\n");
}
}
if (key == 't')
{
if (tcp == INVALID_SOCKET)
{
printf("No Connection\n", len);
}
else
{
unsigned i;
len = LEN;
for (i = 0; i < len; ++i)
{
buf[i] = i;
}
printf("Send Len %d", len);
if (send(tcp, buf, len, 0) == SOCKET_ERROR)
{
return;
}
printf(".\n");
}
}
unsigned remote_size = sizeof(remote);
len = recvfrom(udp, buf, sizeof(buf), 0, (SOCKADDR *)&remote, &remote_size);
if (len == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
return;
}
}
else if (len)
{
printf("Recv Len %d From %s", len, inet_ntoa(remote.sin_addr));
dump(buf, len);
}
if (tcp == INVALID_SOCKET)
{
SOCKADDR_IN conn;
unsigned conn_size = sizeof(conn);
tcp = accept(srv, (SOCKADDR *)&conn, &conn_size);
if (tcp == INVALID_SOCKET)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
return;
}
}
else
{
printf("Connect From %s\n", inet_ntoa(conn.sin_addr));
u_long arg = 1;
if (ioctlsocket(tcp, FIONBIO, &arg) == SOCKET_ERROR)
{
return;
}
}
}
else
{
len = recv(tcp, buf, sizeof(buf), 0);
if (len == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
return;
}
}
else if (len)
{
printf("Recv Len %d", len);
dump(buf, len);
}
else
{
printf("Disconnect\n");
closesocket(tcp);
tcp = INVALID_SOCKET;
}
}
Sleep(10);
}
while (key != 'x');
closesocket(udp);
closesocket(tcp);
closesocket(srv);
WSACleanup();
printf("Done\n");
}

114
test/udp.c Normal file
View File

@ -0,0 +1,114 @@
#include <cc65.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include "../inc/ip65.h"
#define LEN 500
#define SRV "192.168.0.10"
char buf[LEN];
void error_exit(void)
{
printf("Error $%X\n", ip65_error);
if (doesclrscrafterexit())
{
printf("Press any key\n");
cgetc();
}
exit(1);
}
void udp_recv(void)
{
unsigned len = udp_recv_len();
unsigned i;
printf("Recv Len %u From %s", len, dotted_quad(udp_recv_src()));
for (i = 0; i < len; ++i)
{
if ((i % 11) == 0)
{
printf("\n$%04X:", i);
}
printf(" %02X", udp_recv_buf[i]);
}
printf(".\n");
}
void main(void)
{
unsigned i;
unsigned long srv;
char key;
for (i = 0; i < LEN; ++i)
buf[i] = i;
if(!(srv = parse_dotted_quad(SRV)))
{
error_exit();
}
printf("Init\n");
if (ip65_init())
{
error_exit();
}
printf("DHCP\n");
if (dhcp_init())
{
error_exit();
}
printf("IP Addr: %s\n", dotted_quad(cfg_ip));
printf("Netmask: %s\n", dotted_quad(cfg_netmask));
printf("Gateway: %s\n", dotted_quad(cfg_gateway));
printf("DNS Srv: %s\n", dotted_quad(cfg_dns));
printf("Listen\n");
if (udp_add_listener(6502, udp_recv))
{
error_exit();
}
printf("(U)DP or e(X)it\n");
do
{
ip65_process();
if (kbhit())
{
key = cgetc();
}
else
{
key = '\0';
}
if (key == 'u')
{
printf("Send Len %d To %s", LEN, SRV);
if (udp_send(buf, LEN, srv, 6502, 6502))
{
printf("!\n");
}
else
{
printf(".\n");
}
}
}
while (key != 'x');
printf("Unlisten\n");
if (udp_remove_listener(6502))
{
error_exit();
}
printf("Done\n");
}