; This is designed to be serial-line downloaded to cdcode. ; ; Our memory map: ; ; [8c000000,8c010000) Stack (r15 set by cdcode) ; [8c010000,8c01????) cdcode ; [8c020000,8c02????) Us ; This code is heavy on the magic numbers. All the other BBA-driving ; code I've found is at least as bad. :-( .include "regs.s" .include "maple-bits.s" ; Layout within the gapspci bridge bounce buffer area. . = 0x01840000 | P2_BITS rxbuff_bounce: .space 16384 + 16 opkt_0: .space 2000 opkt_1: .space 2000 opkt_2: .space 2000 opkt_3: .space 2000 ; End of bridge bounce buffer layout. . = 0x8c020000 .sz any .pr any SETS.L #main,r0 jmp @r0 nop SETCONST ; Our "data segment". MIN_ETHER_LEN = 32 ; I think 64 is the actual value, not sure MAX_ETHER_LEN = 1600 .align 4 rxcfg: .space 4 top_sp: .space 4 cur_opkt: .space 4 ipkt: .space 4+MAX_ETHER_LEN opkts: .long opkt_0, opkt_1, opkt_2, opkt_3 cur_opkt_x: .space 1 opkt_busy: .space 1 MAC: .space 6 my_ip: .byte 10, 0, 7, 98 ; End of "data segment". ; Our "text segment". ; The only things startup.s sets up that cdcode hasn't already done for ; us are (1) fpscr and (2) clearing bss. We don't have bss because we ; aren't linked by a conventional linker, and we don't use floating ; point so we don't care about fpscr. We do, however, need to set up ; the VBR. We also save r10-r14 against returning to cdcode. .align 2 main: mov.l r14,@-r15 mov.l r13,@-r15 mov.l r12,@-r15 mov.l r11,@-r15 mov.l r10,@-r15 ldc r14,gbr stc sr,r1 SETS.L #~[SR_FD|SR_RB|SR_BL],r2 and r2,r1 ldc r1,sr ; Note that r0-r7 may have just changed if we switched banks. mov #0,r1 lds r1,fpscr .sz 0 .pr 0 SETS.L #intvec,r0 ldc r0,vbr ; Real code begins here. ; Make sure all interrupts are blocked. stc sr,r0 or #0xf0,r0 ldc r0,sr ; Save our stack pointer. This allows code to jump to done, ; below, to return to cdcode, without having to worry about ; cleaning up the stack first - done can just set r15 to this ; value to yank leftover cruft off the stack. SETS.L #top_sp,r0 mov.l r15,@r0 ; Set up the PCI bridge. bsr init_gapspci nop bt fail ; Initialize the Ethernet itself. bsr init_ether nop bt fail ; I don't know why, but it doesn't always work the first time. ; Wait until link comes up (that much seems to work reliably), ; then init the chip again. *grr* bsr await_link nop bsr init_ether nop bt fail ; Print out our MAC. bsr report_mac nop ; Wait until we have link. This can take a nontrivial time if ; the chip has to do Nway negotiation and suchlike. bsr await_link nop ; bsr enable_interrupts ; nop ; Set up state variables. bsr init_data nop ; Main loop. Busy-wait, alternating checks for incoming ; packets and incoming serial data. We don't check for ; outgoing packets here; when a packet is sent, we send it on ; its way and don't care about completion until we want to ; send another packet, at which time we check. If the chip's ; transmit queue ever fills up, we may have to add a queue of ; packets waiting to be stuffed into the hardware. Not yet. 1: bsr check_recv_packet nop bsr check_serial nop bra 1b nop ; The code above jumps to fail if initialization fails. This ; just reports the failure and falls into the return-to-cdcode ; code at done. failmsg: .asciz "Setup failed"(13,10) .align 2 fail: SETS.L #failmsg,r1 bsr putstr nop ; fall through done: bsr putc mov #13,r1 bsr putc mov #10,r1 SETS.L #top_sp,r0 mov.l @r0,r15 mov.l @r15+,r10 mov.l @r15+,r11 lds r11,pr mov.l @r15+,r12 mov.l @r15+,r13 mov.l @r15+,r14 rts nop report_mac: sts pr,r4 SETS.L #5,r3 SETS.L #MAC,r2 2: mov.b @r2+,r1 bsr printhexN mov #2,r0 bsr putc mov #':,r1 1: dt r3 bf 2b mov.b @r2+,r1 bsr printhexN mov #2,r0 bsr putc mov #13,r1 lds r4,pr bra putc mov #10,r1 g2_lock: ; This is conceptually what NetBSD and libronin call G2_LOCK(). ; It returns a cookie in r0 which needs to be passed to ; g2_unlock. ; ; This saves and restores all other registers it uses. ; mov.l r1,@-r15 stc sr,r0 mov.l r0,@-r15 .if @IS_UB[SR_IMASK_MASK<>2,r0 cmp/hi r1,r0 bt 1f ; if too short SETS.L #MAX_ETHER_LEN>>2,r0 cmp/hi r0,r1 bt 1f ; if too long ; Now, r1 has longs-to-copy; r3/r4 have been updated to skip ; the header word. SETS.L #ipkt,r7 mov r8,r0 SHLR #16,r0 mov.l r0,@r7 3: mov r5,r0 add #4,r7 mov.l @(r0,r3),r0 mov.l r0,@r7 add #4,r3 and r2,r3 dt r1 bf/s 3b add #-4,r4 ; Packet copied to ibuf. Update the chip's registers, process ; the packet, and go back to see if we (then) have another ; packet. bsr 8f nop bsr process_packet nop bra 4b nop 1: bsr 8f nop 9: lds.l @r15+,pr rts nop 2: SHLR #16,r8/r0 add #4+3,r0 ; 4 for len/stat, 3 for roundup shlr2 r0 shll2 r0 add r0,r3 and r2,r3 sub r0,r4 bsr 8f nop bra 4b nop 8: add #-16,r3 mov r3,r0 and r2,r0 bra wr_w mov #BBA_RTK_CUR_PKT_READ,r1 check_serial: sts.l pr,@-r15 bsr nbgetchar nop cmp/pz r0 bf 1f bra done nop 1: lds.l @r15+,pr rts nop ; Input number is in r1. Trashes r1, r0, macl, mach; preserves others. printdec: mov.l r2,@-r15 mov.l r3,@-r15 mov.l r4,@-r15 sts.l pr,@-r15 mov #-1,r0 mov.l r0,@-r15 SETS.L #10,r0 ; Dividing by 10 is a pain. But multiplying by 0xcccccccd and ; then shifting right by 35 bits produces the same result and ; is much less of a pain - and is lots faster, too. SETS.L #0xcccccccd,r2 1: cmp/hi r1,r0 bt 1f dmulu.l r2,r1 sts mach,r3 SHLR #3,r3 ; r3 is now floor(r1/10) dmulu.l r3,r0 sts macl,r4 sub r4,r1 mov.l r1,@-r15 bra 1b mov r3,r1 1: bsr putc add #'0,r1 mov.l @r15+,r1 cmp/pz r1 bt 1b lds.l @r15+,pr mov.l @r15+,r4 mov.l @r15+,r3 rts mov.l @r15+,r2 printhex8: mov #8,r0 printhexN: mov.l r4,@-r15 mov r0,r4 add #-8,r0 neg r0,r0 SHLL #2,r0 shld r0,r1 mov.l r3,@-r15 mov.l r2,@-r15 sts.l pr,@-r15 mova 9f,r0 mov r0,r3 mov r1,r2 1: mov r2,r0 SHLR #28,r0 SHLL #4,r2 add r3,r0 bsr putc mov.b @r0,r1 dt r4 bf 1b lds.l @r15+,pr mov.l @r15+,r2 mov.l @r15+,r3 rts mov.l @r15+,r4 .align 4 9: .ascii "0123456789abcdef" .align 2 putc: 1: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_TX_SHIFT,r0 and #SCFDR2_TX_MASK,r0 cmp/eq #16,r0 bt 1b mov r1,r0 mov.b r0,@(SCFTDR2-SCIF_BASE,gbr) rts nop putchar2: sts.l pr,@-r15 bsr putchar mov.l r1,@-r15 bra 1f mov.l @r15+,r1 putchar: sts.l pr,@-r15 1: bsr putc nop 1: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_TX_SHIFT,r0 tst #SCFDR2_TX_MASK,r0 bf 1b lds.l @r15+,pr rts nop putcflush: bra 1b sts.l pr,@-r15 putstr: 1: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_TX_SHIFT,r0 and #SCFDR2_TX_MASK,r0 cmp/eq #16,r0 bt 1b mov.b @r1+,r0 tst r0,r0 bt 1f bra 1b mov.b r0,@(SCFTDR2-SCIF_BASE,gbr) 1: rts nop nbgetchar: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_RX_SHIFT,r0/r1 tst #SCFDR2_RX_MASK,r0 bt 1f mov.b @(SCFRDR2-SCIF_BASE,gbr),r0 extu.b r0,r1 mov.w @(SCLSR2-SCIF_BASE,gbr),r0 mov #0,r0 mov.w r0,@(SCLSR2-SCIF_BASE,gbr) rts mov r1,r0 1: rts mov #-1,r0 SETCONST print_packet: mov.l r2,@-r15 mov.l r3,@-r15 sts.l pr,@-r15 SETS.L #2000,r2 cmp/hi r2,r0 bf 1f bsr putc mov #'!,r1 bra done nop 1: mov r1,r2 mov r0,r3 bsr printdec mov r0,r1 bsr putc mov #':,r1 1: bsr putc mov #' ,r1 mov.b @r2+,r1 bsr printhexN mov #2,r0 dt r3 bf 1b bsr putc mov #13,r1 bsr putc mov #10,r1 lds.l @r15+,pr mov.l @r15+,r3 rts mov.l @r15+,r2 process_packet: sts.l pr,@-r15 SETS.L #ipkt,r2 mov.l @r2+,r7 mov r2,r1 bsr print_packet mov r7,r0 SETS.L #14,r0 cmp/hi r7,r0 bt 9f bsr putc mov #'P,r1 SETS.L #MAC,r3 SETS.L #3,r4 SETS.L #6,r5 1: mov.b @r2+,r0 mov.b @r3+,r1 cmp/eq r0,r1 movt r6 cmp/eq #0xff,r0 movt r0 shll r0 or r6,r0 dt r5 bf/s 1b and r0,r4 tst r4,r4 bf 1f SETS.L #not_ours_msg,r1 bsr putstr nop 9: lds.l @r15+,pr rts nop not_ours_msg: .asciz "Not for us"(13,10) .align 2 1: add #6,r2 bsr putc mov #'O,r1 mov.b @r2+,r0 cmp/eq #0x08,r0 bf 9b mov.b @r2+,r0 cmp/eq #0x00,r0 bt packet_IP cmp/eq #0x06,r0 bt packet_ARP bra 9b nop ARP_fixed: .byte 0, 1, 8, 0, 6, 4 packet_ARP: bsr putc mov #'A,r1 ; Payload contains ; 2 Hardware type (ether = 1) ; 2 Protocol type (IP = 0x0800) ; 1 Harware address length, H ; 1 Protocol address length, P ; 2 Opcode (request=1, reply=2) ; H Packet sender hardware address ; P Packet sender protocol address ; N Packet target hardware address ; P Packet target protocol address SETS.L #14+8+6+4+6+4,r0 cmp/hi r7,r0 bt 9b bsr putc mov #'L,r1 SETS.L #ARP_fixed,r3 SETS.L #6,r5 1: mov.b @r2+,r0 mov.b @r3+,r1 cmp/eq r0,r1 bf 9b dt r5 bf 1b bsr putc mov #'H,r1 mov.w @r2+,r0 swap.b r0,r0 cmp/eq #1,r0 bt packet_ARP_request cmp/eq #2,r0 bt packet_ARP_reply bsr putc mov #'U,r1 bra 9b nop packet_ARP_request: bsr putc mov #'Q,r1 add #16,r2 SETS.L #my_ip,r3 SETS.L #4,r5 1: mov.b @r2+,r0 mov.b @r3+,r1 cmp/eq r0,r1 bf 9b dt r5 bf 1b bsr putc mov #'V,r1 ; Someone wants our address. Compose a reply. bsr find_free_opkt nop SETS.L #ipkt+4+14+8,r8 mov r8,r1 bsr opkt_append mov #6,r0 SETS.L #MAC,r1 bsr opkt_append mov #6,r0 mov r8,r1 add #-10,r1 bsr opkt_append mov #2,r0 SETS.L #ARP_fixed,r1 bsr opkt_append mov #6,r0 bsr opkt_append_byte mov #0,r0 bsr opkt_append_byte mov #2,r0 SETS.L #MAC,r1 bsr opkt_append mov #6,r0 SETS.L #my_ip,r1 bsr opkt_append mov #4,r0 mov r8,r1 bsr opkt_append mov #10,r0 bsr opkt_send nop bra 9b nop packet_ARP_reply: bsr putc mov #'R,r1 bra 9b nop packet_IP: bsr putc mov #'I,r1 bra 9b nop 1: .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,-1 find_free_opkt: SETS.L #opkt_busy,r3 mov.b @r3,r0 and #0xf,r0 SETS.L #1b,r1 mov.b @(r0,r1),r0 cmp/eq #-1,r0 bt wait_free_opkt SETS.L #opkts,r1 SETS.L #1,r2 shld r0,r2 mov.b @r3,r1 or r2,r1 mov.b r1,@r3 SETS.L #cur_opkt_x,r1 mov.b r0,@r1 SHLL #2,r0 SETS.L #opkts,r1 mov.l @(r0,r1),r0 SETS.L #cur_opkt,r1 rts mov.l r0,@r1 wait_free_opkt: .if BBA_RTK_TX_STATUS_OWN != 1 .error Code assumes BBA_RTK_TX_STATUS_OWN is 1 .endif sts.l pr,@-r15 1: bsr rd_w mov #BBA_RTK_TX_STATUS_ALL,r1 tst #0xf,r0 bt 1b and #0xf,r0 not r0,r0 mov.b @r3,r1 and r1,r0 bra find_free_opkt mov.b r0,@r3 opkt_append: ; ptr in r1, len in r0; len must be >0 SETS.L #cur_opkt,r2 mov.l @r2,r3 1: mov.b @r1+,r4 mov.b r4,@r3 add #1,r3 dt r0 bf 1b rts mov.l r3,@r2 opkt_append_byte: ; byte in r0 SETS.L #cur_opkt,r1 mov.l @r1,r2 mov.b r0,@r2 add #1,r2 rts mov.l r2,@r1 opkt_send: SETS.L #cur_opkt_x,r0 SETS.L #opkts,r1 mov.b @r0,r2 mov r2,r0 SHLL #2,r0 mov.l @(r0,r1),r3 SETS.L #cur_opkt,r1 mov.l @r1,r4 mov r4,r5 sub r3,r5 ; r2 is cur opkt index ; r3 is cur opkt base ; r4 is cur opkt pointer ; r5 is opkt len SETS.L #64,r1 cmp/hi r5,r1 bf 1f SETS.L #'=,r0 sub r5,r1 2: mov.b r0,@r4 dt r1 bf/s 2b add #1,r4 mov r4,r5 sub r3,r5 1: ; Any padding needed now applied mov r3,r1 bsr print_packet mov r5,r0 mov r3,r0 SETS.L #0xffffffff&~Px_MASK,r1 and r1,r0 mov #BBA_RTK_TXADDR,r1 mov r2,r6 SHLL #2,r6 bsr wr_l add r6,r1 SETS.L #BBA_RTK_TXSTAT_EARLYTXTHRESH_MASK<