.title xhboot .enabl lc ;+ ; ; DEQNA Boot ROM replacement. The original DEQNA/DELQA ROM is copyrighted by ; DEC and anyway the diagnostics depend on specific details of the hardware. ; ; By John Wilson . ; ; Copyright (C) 2000 by Digby's Bitpile, Inc. All rights reserved. ; Free distribution is allowed as long as source is available, giving Digby's ; Bitpile, Inc. first billing as the author. ; ; 02/09/2000 JMBW Created. ; 02/24/2000 JMBW Works. ; ; The code here is all position-independent, except for the addresses used for ; holding the boot/diag ROM and receive packet buffer while booting. ; ;- laddr= 2000 ;load address for boot/diag ROM paddr= 120000 ;address of RX packet buffer haddr= 150000 ;high relocation addr (last 4 KB of memory) ; xh$ad0= 00 ;offsets for reading 6-byte SA PROM xh$ad1= 02 xh$ad2= 04 xh$ad3= 06 xh$ad4= 10 xh$ad5= 12 ; xh$rdl= 04 ;offset for writing rcv buffer descriptor list xh$xdl= 10 ;offset for writing xmt buffer descriptor list ; xh$vec= 14 ;offset for reading/writing vec and DELQA bit xh$csr= 16 ;offset of CSR ; ; CSR bits: ; xh.ri= 100000 ;R/CL rcv int req ;xh.rr= 040000 ;(reserved) xh.ca= 020000 ;RO carrier xh.ok= 010000 ;RO xcvr fuse OK ;xh.rr= 004000 ;(reserved) xh.se= 002000 ;RW sanity timer enable xh.el= 001000 ;RW external loopback xh.il= 000400 ;RW internal loopback (active low) xh.xi= 000200 ;R/CL xmt int req xh.ie= 000100 ;RW interrupt enable xh.rl= 000040 ;RO rcv list invalid xh.xl= 000020 ;RO xmt list invalid xh.bd= 000010 ;RW put boot/diag ROM in rcv buf chain xh.ni= 000004 ;RO NXM int xh.sr= 000002 ;RW software reset xh.re= 000001 ;receiver enable ; .macro mova addr,reg ;move address (using PIC code) mov pc,reg ;addr of next instruction add #-.,reg ;add offset .endm ; .asect .=0 ; MOP packet layout mp$dst: .blkb 6 ;dest addr mp$src: .blkb 6 ;source addr mp$prt: .word ;protocol (60-01) mp$len: .word ;length of data field (PDP-11 byte order) mp$dat: ;data field mp$cod: .blkb ;first byte is command code ; .=0 ;starts at 0, so generate as .LDA not .SAV start: nop ;+00 = NOP like any bootstrap br boot1 ;+02 = BR to bootstrap br diag ;+04 = BR to hardware/citizenship test .word 7776 ;+06 = offset of checksum word br boot1 ;+10 = BR to test+bootstrap word12: .word 0 ;+12 = addr to JMP to on error, or 0 to HALT ; boot1: br boot ;jump over diagnostic/init code ; ; Entry conditions: ; R1 CSR address ; R2 work space (identity-mapped if MMU is turned on, so OK for DMA) ; PC *not* necessarily identity-mapped so no DMA ; diag: inc r2 ;force even bic #1,r2 ; set up descriptor addresses in scratch buffer (DMA accessible) mov r2,txbdla ;transmit buffer descriptor list add #<6*3>*2,r2 ;leave space for three descriptors mov r2,rxbdla ;receive buffer descriptor list add #<6*3>*2,r2 ;SETUP packet buffer comes next ; reset port mov #xh.sr,xh$csr(r1) ;set SR clr xh$csr(r1) ;clear it again ; fetch SA PROM contents mov r1,r3 ;copy starting addr mova us,r4 ;point at where to keep our addr mov #6,r5 ;byte count 10$: mov (r3)+,r0 ;get next byte of addr movb r0,(r4)+ dec r5 ;done all? bne 10$ ;loop if not mov #xh.il,xh$csr(r1) ;no internal loopback ; build SETUP packet to set this receive address ; (no broadcast address needed yet though) mov #256.,r0 ;init byte count add r0,r2 ;point past end asr r0 ;word count 20$: clr -(r2) ;clear a word dec r0 ;done all? bne 20$ ;loop if not mov r2,r5 ;copy addr mova us,r0 ;point at our address mov #6,r3 ;outer loop count 30$: mov #7,r4 ;inner loop count inc r5 ;skip empty first column 40$: movb (r0),100(r5) ;write 2nd copy movb (r0),(r5)+ ;and 1st copy, increment dec r4 ;done all? bne 40$ ;loop if not inc r0 ;bump to next byte of addr dec r3 ;done all 6 bytes? bne 30$ ;loop if not ; add broadcast address to list mov r2,r5 ;copy again add #50+1,r5 ;point at end of first target addr 50$: movb #377,(r5) ;insert an FF sub #10,r5 ;back to previous byte cmp r5,r2 ;past beginning? bhi 50$ ;loop if not ; send packet 3 times, to turn off all LEDs on real DEQNA mov r2,r5 ;copy yet again mov #128.+124,r4 ;timeout=4 minutes, turn off LED #1 60$: ; DEQNA gets stuck unless it has a valid receive buffer for dumping ; the ESETUP packet (echo of SETUP packet) mov rxbdla,r0 ;get starting RX BDL address mov r0,xh$rdl(r1) ;set low word of BDL address mov #100000,r3 ;handy constant mov r3,(r0)+ ;init flag word mov r3,(r0)+ ;set addr desc bits, addr MSBs=0 mov r5,(r0) ;starting addr add #1024.,(r0)+ ;receive ESETUP in memory following SETUP mov #-<400/2>,(r0)+ ;word count mov r3,(r0)+ ;not yet filled mov r3,(r0)+ ;unequal bytes, according to manual mov r3,(r0)+ ;next descriptor is owned by DEQNA but invalid mov #5+6,r2 ;word count to finish 2 descs 70$: clr (r0)+ ;clear dec r2 ;done all? bne 70$ ;loop if not clr xh$rdl+2(r1) ;write high addr bits ; ready to receive (and ignore) ESETUP, now send SETUP mov r4,-(sp) ;save mov r5,-(sp) mov #130000,r2 ;valid, end of msg, SETUP packet call txfrm1 ;send it mov (sp)+,r5 ;restore mov (sp)+,r4 80$: mov xh$csr(r1),r0 ;fetch CSR bpl 80$ ;haven't received ESETUP echo yet mov r0,xh$csr(r1) ;here it is, clear XH.RI add #4,r4 ;bump to next LED bit #14,r4 ;done all 3? bne 60$ ;loop if not ; return success (as if we really tested something) clr r0 ;pointless to test emulated hardware rts pc ;so always say we're happy ; ; Entry conditions: ; R0 boot flag (should be 0) ; R1 CSR address ; boot: ; download the rest of the boot/diag ROM mov #xh.sr,xh$csr(r1) ;set SR clr xh$csr(r1) ;clear it again mov #xh.el,xh$csr(r1) ;set external loopback mova bdbdl,r0 ;get addr of boot/diag BDL mov r0,xh$rdl(r1) ;low word of rcv BDL addr clr xh$rdl+2(r1) ;high word =0 mov #xh.el!xh.il!xh.bd,xh$csr(r1) ;set bit to download ROM mov #250.,r0 ;loop count 10$: tst (r1) ;Q-bus cycle, takes 1/2 usec or so dec r0 ;count it bne 10$ ;need to delay 100 usec mov #xh.el!xh.il,xh$csr(r1) ;clear it again, should start loading 20$: cmp bddone,#140000 ;buf control bits still 10? blt 20$ ;yes jmp @#30$+laddr ;continue in new copy 30$: mov #laddr,sp ;stack goes underneath us mov #12000,r2 ;scratch area for SETUP packet etc. call diag ;reinit port, get value for US mov #xh.il!xh.re,xh$csr(r1) ;enable receiver ; I don't know what the rules are for load addresses, but the image ; I'm testing against starts at 002000 so we'll go to high mem on the ; assumption that that's typical mov #haddr,r5 ;high memory mov #laddr,r4 ;where we loaded mov #10000/2,r3 ;word count 40$: mov (r4)+,(r5)+ ;copy it dec r3 ;loop bne 40$ add #haddr-laddr,pc ;up we go ; set addresses of buffer descriptor lists mova txbdl,r0 ;find final addr of TX BDL mov r0,txbdla ;save for TXFRM mova rxbdl,r0 ;same for RX BDL mov r0,rxbdla 50$: ; send the "request program" packet (to load server mcast group) mova reqprg,r5 ;point at packet mov #60.,r4 ;length call txfrm ;send it 60$: call rxfrm ;get response bcs 50$ ;retry tst mp$len(r5) ;must have NZ length beq 60$ cmpb mp$cod(r5),#3 ;code = assistance volunteer? bne 60$ ; someone likes us, ask them personally mova them,r0 ;where to keep their addr mov #6,r2 ;count add r2,r5 ;point at source addr 70$: movb (r5)+,(r0)+ ;copy into THEM dec r2 bne 70$ mova reqprg,r5 ;point at packet mov #60.,r4 ;length 80$: mov r5,frmadr ;save addr/length mov r4,frmlen 90$: mov frmadr,r5 ;fetch mov frmlen,r4 call txfrm ;send packet 100$: ; wait for next memory load [with transfer address] frame call rxfrm ;get response bcs 90$ ;retry tst mp$len(r5) ;must have NZ length beq 100$ movb mp$cod(r5),r0 ;get code beq 120$ ;code = memory load w/transfer addr cmp r0,#2 ;code = memory load? beq 140$ 110$: ; ask for whatever frame we expect now mova reqmld,r5 ;point at packet mov #60.,r4 ;length br 80$ ;go send it 120$: ; memory load w/transfer addr cmp mp$len(r5),#6 ;missing load address *and* image data? beq 130$ ;yes, easy sub #4,mp$len(r5) ;remove length from count bcs 90$ ;invalid packet, ignore call ldmem ;load memory bcs 90$ ;invalid clr r0 ;clear addr bisb (r5)+,r0 ;load low 16 bits of xfr addr swab r0 bisb (r5)+,r0 swab r0 jmp (r0) ;and away we go 130$: call ldmem ;load nothing, check seq # bcs 90$ ;bad sequence # jmp (r3) 140$: call ldmem ;load memory bcs 90$ ;invalid packet, ignore br 110$ ; ldmem: ; load memory from packet at (R5), C=1 if bad format ; (otherwise R5 updated to point past end of data, R3=load addr) mov mp$len(r5),r2 ;get length sub #1+1+4,r2 ;account for code, sequence #, load addr bcs 40$ ;length too short add #mp$cod+1,r5 ;advance to seq # clr r0 ;zero-extend bisb (r5)+,r0 ;get sequence # beq 10$ ;already 0, reset numbers cmpb r0,seq ;what we expect? bne 40$ ;no 10$: inc r0 ;# to expect next movb r0,seq ;update clr r3 ;init addr bisb (r5)+,r3 ;pick up low word swab r3 bisb (r5)+,r3 swab r3 ;get it the right way around add #2,r5 ;skip high 2 bytes of addr tst r2 ;check length beq 30$ ;nothing to do 20$: movb (r5)+,(r3)+ ;copy a byte dec r2 ;loop bne 20$ 30$: tst (pc)+ ;happy 40$: sec ;error rts pc ; ; Buffer descriptor list for downloading boot/diag ROM. ; bdbdl: ; first buf is for first 2 KB (manual says 2 KB at a time) .word 100000 ;flag word .word 100000,laddr ;addr for first chunk (2 KB) .word -<2048./2> ;word count for 2 KB .word 100000,0 ;not yet filled ; second buf is for all but the last word of the second 2 KB .word 100000 ;flag word .word 100000,laddr+2048. ;addr for second chunk (2 KB -2) .word -<2048./2> ;word count for 2 KB bddone: .word 100000,0 ;not yet filled ; end of descriptor list .rept 2 ;just to be safe (does DEQNA read an extra?) .word 100000 ;flag word .word 0,0 ;address (invalid) .word 0 ;length .word 0,0 ;status words, end of chain .endr ;+ ; ; Transmit a frame. ; ; r5 address of packet (in low 64 KB), dest/src addrs will be inserted ; r4 length (in bytes) ; r1 CSR base (preserved) ; ;- txfrm: ; prepend source address mova them,r0 ;point at remote Ethernet address mov #6+6,r2 ;byte count 10$: movb (r0)+,(r5)+ ;insert our source address dec r2 ;done all? bne 10$ ;loop if not sub #6+6,r5 ;point back at start ; fill out BDL for this packet mov #120000,r2 ;initialize addr descriptor bits txfrm1: ; enter here to send setup packet (regs as above, plus address ; descriptor bits (for 2nd word of buf descriptor) are in R2 cmp r4,#60. ;runt packet? bhi 10$ mov #60.,r4 ;add whatever junk follows it in memory if so 10$: bit #1,r4 ;odd? beq 20$ bis #200,r2 ;yes, set "low byte only termination" bit inc r4 ;and count it as a word 20$: mov txbdla,r0 ;get starting TX BDL address mov r0,xh$xdl(r1) ;set low word of BDL address mov #100000,(r0)+ ;init flag word mov r2,(r0)+ ;save addr desc bits, addr MSBs=0 mov r5,(r0)+ ;starting addr asr r4 ;word count neg r4 ;2's comp mov r4,(r0)+ ;save mov #100000,(r0)+ ;not yet filled mov #6+6+1,r2 ;word count to finish this and 2 more descs 30$: clr (r0)+ ;clear dec r2 ;done all? bne 30$ ;loop if not ; actually start transmission clr xh$xdl+2(r1) ;write high addr bits 50$: mov xh$csr(r1),r0 ;wait for XI tstb r0 bpl 50$ bic #100000,r0 ;don't acknowledge reception mov r0,xh$csr(r1) ;but clear XI rts pc ;+ ; ; Receive a packet. ; ; r5 returns address ; r4 returns length ; r1 CSR base (preserved) ; ;;; Should return C=1 on a timeout, but I haven't written that yet -- can't ;;; count on a delay loop even if it touches the DEQNA CSR on each iteration ;;; because the DEQNA may be emulated so we don't necessarily get the length ;;; of a DEQNA bus cycle in there. So maybe we should depend on there being ;;; a KW11L style clock? ; ;- rxfrm: bit #xh.rl,xh$csr(r1) ;null desc. from last time picked up yet? beq rxfrm ;can't set new RX BDL head until then, so spin mov #paddr,r5 ;point at packet buf mov rxbdla,r0 ;get starting RX BDL address mov r0,xh$rdl(r1) ;set low word of BDL address mov #100000,r3 ;handy constant mov r3,(r0)+ ;init flag word (DEQNA owns descriptor) mov r3,(r0)+ ;set addr desc bits, addr MSBs=0 mov r5,(r0)+ ;starting addr mov #-<<6+6+2+1500.>/2>,(r0)+ ;word count mov r3,(r0)+ ;not yet filled mov r3,(r0)+ ;unequal bytes, according to manual mov r3,(r0)+ ;next descriptor is owned by DEQNA but invalid mov #5+6,r2 ;word count to 2 empty descs 10$: clr (r0)+ ;clear dec r2 ;done all? bne 10$ ;loop if not ; actually start reception (packet probably buffered inside DEQNA) clr xh$rdl+2(r1) ;write high addr bits 20$: mov xh$csr(r1),r0 ;wait for RI bpl 20$ bic #200,r0 ;don't acknowledge transmission mov r0,xh$csr(r1) ;but clear RI ;;; timeout would be nice ; check dest addr to make sure it's ours mov r5,r3 ;copy start of packet mova us,r2 ;our addr mov #6,r0 ;byte count 30$: cmpb (r3)+,(r2)+ ;match? bne rxfrm ;no, keep looking dec r0 ;count it bne 30$ ;loop if not done cmp mp$prt(r5),prot ;does protocol match? bne rxfrm ;ignore if not ; it's ours, extract the length and return mov rxbdla,r0 ;get starting RX BDL address mov 10(r0),r4 ;get bits 10:8 of packet length bic #^C3400,r4 ;isolate bisb 12(r0),r4 ;get bits 7:0 add #60.,r4 ;add base length, C=0 rts pc ;happy return ; ; Copyright notice must appear in binary file: ; .ascii /Copyright (C) 2000 by Digby's Bitpile, Inc. / .asciz /All rights reserved./ ; ; REQUEST PROGRAM frame. ; reqprg: .blkb 6+6 ;dest, source addrs .byte 140,001 ;60-01 .word 20$-10$ ;length, LSB first 10$: .byte 8. ;CODE = REQUEST PROGRAM .byte 5 ;DEVICE TYPE = DEQNA .byte 1 ;FORMAT VERSION = 1 .byte 1 ;PROGRAM TYPE = tertiary loader .byte 0 ;SOFTWARE ID (default) .blkb 16. ;(don't need name) .byte 0 ;PROCESSOR = system processor (default) 20$: ; end ; ; REQUEST MEMORY LOAD frame. ; reqmld: .blkb 6+6 ;dest, source addrs .byte 140,001 ;60-01 .word 3 ;length, LSB first .byte 10. ;CODE = REQUEST MEMORY LOAD seq: .byte 0 ;sequence # we want .byte 0 ;we would have crashed if there were an error ; .even frmadr: .blkw ;addr of frame we're transmitting frmlen: .blkw ;length of frame we're transmitting ; txbdla: .blkw ;TX BDL address rxbdla: .blkw ;RX BDL address ; ; These descriptor list areas are used for the bootstrap entry point. ; ; The diagnostic entry point allocates them in the scratch area that R2 points ; to, since the code area may not be identity-mapped so DMA may not work ; here). ; txbdl: .blkw 6*3 ;one real descriptor, two null ones mark end ;(just to be safe) rxbdl: .blkw 6*3 ;as above ; them: ; their Ethernet address (whoever we're talking to right now) .byte 253,000,000,001,000,000 ;AB-00-00-01-00-00 us: .blkb 6 ;our Ethernet address prot: .byte 140,001 ;protocol (60-01) ; .end start