+ INCLUDE "serial.inc"
+
ETH_STATUS EQU $FF68
ETH_DATA EQU $FF69
+ARP_ENTRIES EQU 6
+ARP_ENTRY_SIZE EQU 13
-PUTC: MACRO
- ld a, \1
- call putc
- ENDM
-
-PUTS: MACRO
- ld hl, \1
- call puts
- ENDM
-
-PUTSI: MACRO
- push hl
- call putsi
- db \1, 0
- pop hl
- ENDM
-
-PUTSIL: MACRO
- push hl
- call putsi
- db \1, 13, 10, 0
- pop hl
- ENDM
-
-PUTHEX: MACRO
- ld a, \1
- call puthex
- ENDM
-
SECTION "config", HOME[$0]
myMAC: db $12, $34, $56, $78, $9A, $00
myIP: db $0A, $00, $00, $02
start:
ld sp, $D800
+ call zerobss
PUTSIL "Ethernet test ROM"
-.lp: xor a ; Rest state machines
+.lp: xor a ; Reset state machines
ld [ETH_STATUS], a
- ld a, [ETH_STATUS] ; New shit?
+ ld a, [ETH_STATUS] ; Load current status
ld [$FF51], a
and $02 ; New packet?
call nz, getpacket
jr .lp
+zerobss:
+ ld hl, bssstart
+ ld bc, bssend - bssstart
+.lp: ld a, b
+ or c
+ ret z
+ dec bc
+ xor a
+ ld [hli], a
+ ld a, h
+ jr .lp
+
getpacket:
ld a, [ETH_DATA]
ld b, a
ld a, [ETH_DATA]
ld c, a
push bc
- ld hl, $D000
+ ld hl, packet
.cplp: ld a, b
or c
jr z, .done
PUTHEX b
PUTHEX c
PUTSIL " byte packet from hardware."
- ld hl, $D000
+ ld hl, packet
call ether_input
ret
inc hl
dec c
jr nz, .maclp
- PUTSIL "ether_input: It's to us!"
+ ; Done? Then it was to us!
+ jr .us
+.notus: ; Was it to broadcast?
pop hl
-
- ret
-.notus: PUTSIL "ether_input: Not to us..."
+ push hl
+ ld c, $06
+.bclp: ld a, [hli]
+ cp $FF
+ jr nz, .notbc
+ dec c
+ jr nz, .bclp
+
+.us: PUTSIL "ether_input: It's to us!"
+ ; Which protocol does it belong to?
pop hl
- ret
+ ld b, $00
+ ld c, 12
+ add hl, bc ; Skip source and dest MAC.
+ ld a, [hli]
+ cp $08
+ jr nz, .unknownproto
+ ld a, [hli]
+ cp $06
+ jr z, .arp
+ cp $00
+ jp z, .ip
+ jr .unknownproto
-; SERIAL ROUTINES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+.notbc: pop hl
+ PUTSIL "ether_input: Not addressed to us and not broadcast."
+ ret
+
+.unknownproto:
+ PUTSIL "ether_input: Unknown protocol; dropping"
+ ret
-putc:
- push af
-.waitport:
- ld a,[$FF53]
- and $01
- jr nz,.waitport
- pop af
- ld [$FF52],a
+.arp:
+ PUTSIL "ether_input: ARP packet"
+ call arp_input
+ ret
+.ip:
+ PUTSIL "ether_input: IP packet"
+ call ip_input
ret
-puts:
+arp_input:
+ ; First, verify that this is an ARP packet that we can handle.
+ ld c, 7
+ ld de, .expectedarp
+.cklp: xor a
+ cp c
+ jr z, .ckdone
+ ld a, [de]
+ cp [hl]
+ jr nz, .badarp
+ inc de
+ inc hl
+ dec c
+ jr .cklp
+.ckdone:
ld a, [hli]
- cp $00
- ret z
- call putc
- jr puts
-
-EX_SP_HL: MACRO
- push de
- di
- add sp, 2
- pop de
+ cp 1
+ jr z, .req
+ cp 2
+ jp z, .resp
+.badarp:
+ PUTSIL "arp_input: Unknown protocol hardware or request"
+ ret
+.expectedarp:
+ db $00, $01, $08, $00, $06, $04, $00
+
+.req:
+ PUTSIL "arp_input.req: Request"
+ ; Is it asking about us?
push hl
- ld l, e
- ld h, d
- add sp, -2
- ei
- pop de
- ENDM
-
-putsi:
- pop hl
- push af
- push bc
- push de
-.lp: ld a, [hli]
- or a
- jr z, .done
- call putc
- jr .lp
-.done: pop de
- pop bc
- pop af
+ ld b, 0
+ ld c, $10 ; SHA + SPA + THA
+ add hl, bc
+ ld de, myIP
+ ld c, 4
+.iplp: ld a, [de]
+ cp [hl]
+ jp nz, .notourip
+ inc de
+ inc hl
+ dec c
+ jr nz, .iplp
+ PUTSIL "arp_input.req: Aimed at us."
+ pop hl ; Now synthesize a response packet.
push hl
+ dec hl
+ ld a, $02 ; Reply!
+ ld [hli], a
+ ld d, h
+ ld e, l
+ ld hl, 10 ; SHA + SPA
+ add hl, de ; Now DE points at SHA, and HL points at THA
+ ld c, 10
+.dcl: ld a, [de]
+ ld [hli], a
+ inc de
+ dec c
+ jr nz, .dcl
+ pop hl ; HL now points at SHA
+ ld de, myMAC
+ ld c, $06
+.mcl: ld a, [de]
+ ld [hli], a
+ inc de
+ dec c
+ jr nz, .mcl
+ ld de, myIP ; HL now points at SPA
+ ld c, $04
+.icl: ld a, [de]
+ ld [hli], a
+ inc de
+ dec c
+ jr nz, .icl
+ ld d, h
+ ld e, l ; HL now points at THA
+ ld hl, -32
+ add hl, de ; HL has now been backed up to the start of the ethernet header.
+ ld d, h
+ ld e, l ; DE now points at the dest
+ ld hl, 6
+ add hl, de ; HL now points at the source
+ ld c, $06
+.sdcl: ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .sdcl
+ ld hl, myMAC ; DE now points at the source
+ ld c, $06
+.smcl: ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .smcl
+ ld hl, -12
+ add hl, de ; HL now points at the start of the packet -- should be C000!
+ ld b, 0
+ ld c, 64 ; Minimum size packet; avoid transmitting a runt
+ call packet_send
+ PUTSIL "arp_input.req: sent response!"
+ ret
+.notourip:
+ PUTSIL "arp_input.req: Not aimed at us; ignoring."
+ pop hl
+ ret
+
+.resp:
+ PUTSIL "arp_input.resp: Response"
ret
-puthex: ; Put two hex nibbles to the serial console.
- push bc
- push hl
- push af
- swap a
- and $0F
- ld hl,hex
- ld b,0
- ld c,a
- add hl,bc
+ip_input:
+ ; check for ipv4
ld a, [hl]
- call putc
- pop af
+ ld c, a
+ and $F0
+ swap a
+ sub $04
+ jr z, .ip_isv4
+ PUTSIL "ip_input: not ipv4 -- fuck off"
+ ret
+.ip_isv4
+ ; get length
+ ld a, c
and $0F
- ld hl,hex
- ld c,a
- add hl,bc
- ld a, [hl]
- call putc
+ sla a
+ ld c, a
+ sla a
+ sub $14
+ ld b, a
+ push bc
+
+ ; check checksum
+ xor a
+ ld d, a
+ ld e, a
+ push hl
+.ip_cksumloop:
+ ld a, [hli]
+ ld b, a
+ ld a, [hli]
+ add e
+ ld e, a
+ ld a, b
+ add d
+ ld d, a
+ jr nc, .ip_noinc
+ inc de
+.ip_noinc:
+ dec c
+ jr nz, .ip_cksumloop
+
+ ; checksum ok
pop hl
+ ld a, d
+ or e
+ jr z, .ip_cksumgood
+ ld a, d
+ and e
+ cpl
+ or a
+ jr z, .ip_cksumgood
+ PUTSIL "ip_input: checksum fail -- bailing"
+ pop hl
+ ret
+.ip_cksumgood:
+ PUTSIL "ip_input: checksum good"
+ ld b, $00
+ ld c, $0C
+ add hl, bc
+ ld c, $04
+
+ ; print source IP
+ PUTSI "ip_input: this packet is from 0x"
+.ip_get_srcip:
+ ld a, [hli]
+ PUTHEX a
+ dec c
+ jr nz, .ip_get_srcip
+ PUTSIL "."
+
+ ; check dest IP
+ ld d, myIP >> 8
+ ld e, myIP & $FF
+ ld c, $04
+.ip_chkip:
+ ld b, [hl]
+ inc hl
+ ld a, [de]
+ inc de
+ cp b
+ jr nz, .ip_ipfail
+ dec c
+ jr nz, .ip_chkip
+
+ PUTSIL "ip_input: wheeeeeeeeeeeeeeee"
+ ; get protocol in A
+ ld d, $FF
+ ld e, -$0B
+ add hl, de
+ ld a, [hl]
+ ld d, $00
+ ld e, $0B
+ add hl, de
+
+ ; make hl point at beginning of data
pop bc
+ ld c, b
+ ld b, $00
+ add hl, bc
+
+ ; ok, pass it off to something else
+ cp $01
+ jr z, .ip_icmp
+ cp $11
+ jr z, .ip_udp
+ ret
+
+.ip_icmp:
+ call icmp_input
+ ret
+
+.ip_udp:
+ call udp_input
+ ret
+
+.ip_ipfail:
+ PUTSIL "ip_input: this packet is not to our IP address. Bailing."
+ pop hl
+ ret
+
+icmp_input:
+ PUTSIL "icmp_input: too lazy to parse -- fuck off"
+ ret
+
+udp_input:
+ PUTSIL "udp_input: too lazy to parse -- fuck off"
ret
-hex: db "0123456789ABCDEF"
+
+packet_send:
+ ld a, [ETH_STATUS] ; Wait for the port to become available.
+ and $01
+ jr nz, packet_send
+ ld a, b
+ ld [ETH_DATA], a
+ ld a, c
+ ld [ETH_DATA], a
+.pkts: ld a, b
+ or c
+ ret z
+ dec bc
+ ld a, [hli]
+ ld [ETH_DATA], a
+ jr .pkts
+
+ SECTION "ethdata", BSS
+bssstart DS 0
+packet DS 1552
+arptable DS ARP_ENTRIES * ARP_ENTRY_SIZE
+bssend DS 0