X-Git-Url: http://git.joshuawise.com/fpgaboy.git/blobdiff_plain/43282fcf13e571f4a57dd8df10ea9b4f7f8d1bca..refs/heads/master:/ethernet.asm diff --git a/ethernet.asm b/ethernet.asm index 4bcee82..8fb4fed 100644 --- a/ethernet.asm +++ b/ethernet.asm @@ -1,35 +1,10 @@ + 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 @@ -58,22 +33,35 @@ myGW: db $0A, $00, $00, $01 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 @@ -86,7 +74,7 @@ getpacket: PUTHEX b PUTHEX c PUTSIL " byte packet from hardware." - ld hl, $D000 + ld hl, packet call ether_input ret @@ -133,82 +121,307 @@ ether_input: 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