; This is designed to be serial-line downloaded to cdcode. ; ; Our memory map: ; ; [8c000000,8c010000) Stack (r15 set by cdcode) ; [8c010000,8c01????) cdcode ; [8ef00000,8ef?????) Us ; This code is heavy on the magic numbers. All the other BBA-driving ; code I've found is at least as bad. :-( ; We fetch data via raw Ethernet. We use Ethernet packet type 0xFEDC ; (not formally assigned to anything as far as I can tell). The ; protocol is very simple. (All payloads described below are ; self-terminating and may be padded - must be, if the resulting ; packet would otherwise be shorter than the minimum Ethernet size. ; Padding, if present, must be ignored by packet receivers.) ; ; The paradigm is that the server sends data in blobs. Every blob is a ; single contiguous set of bytes to be written; blobs are addressed ; with cookies. A cookie is a 4-octet value which is opaque to the ; client. The initial cookie is provided by the server in a 0x01 ; packet; each 0x03 packet which carries data includes a next cookie ; along with it. ; ; Every packet has an opcode octet as its first octet. Here are the ; possible opcode octets with their semantics and, where applicable, ; the following data. ; ; 0x00 No following data. This is sent by a downloader to ; request a download. Any server willing to serve the ; client should respond with 0x01. 0x00 packets are sent ; to the Ethernet broadcast address; all other packets ; are unicast to their receivers. PROTO_OPC_DLREQ = 0x00 ; ; 0x01 - cookie (4 octets) ; This is sent by a server in response to a 0x00 packet ; and indicates a willingness to send a file to that ; downloader. The cookie is the initial cookie for the ; client to start downloading with. PROTO_OPC_DLRESP = 0x01 ; ; 0x02 - cookie (4 octets) ; This is sent by a downloader to request a block of its ; file. The cookie comes from a 0x01 or 0x03 packet. PROTO_OPC_DATAREQ = 0x02 ; ; 0x03 - cookie (4 octets) ; - status octet ; 0x00 Success PROTO_DATARESP_SUCCESS = 0x00 ; - next cookie (4 bytes) ; - address, A (4 bytes, big-endian) ; - length in octets, N (2 bytes, big-endian) ; - data (N octets) ; 0x01 Done ; - entry point offset (4 bytes, big-endian) PROTO_DATARESP_DONE = 0x01 ; 0x02 Invalid cookie PROTO_DATARESP_BADCOOKIE = 0x02 ; 0x03 Other failure PROTO_DATARESP_FAILURE = 0x03 ; This is sent by a server in response to a 0x02 packet. ; If the downloader's cookie is valid, the server ; responds with either a 0x00 response, if there is ; anything more to be sent to the client, or a 0x01 ; response, if not. If the client's cookie is bad, the ; server generates a 0x02 response. If anything else ; goes wrong, the server generates a 0x03 response. In ; every case, the client's cookie is echoed before the ; status octet; in the 0x00 case, the server also ; provides a next cookie for the client to use to fetch ; the next blob of data. The N value in a 0x03 packet ; must be no greater than 1024. PROTO_OPC_DATARESP = 0x03 OUR_ETHER_TYPE = 0xfedc MIN_ETHER_RECV = 32 ; reject if shorter than this MIN_ETHER_XMIT = 64 ; pad to this on output MAX_ETHER_LEN = 1600 DCACHE_LINE_SIZE = 32 .include "regs.s" ; Layout within the gapspci bridge bounce buffer area. . = 0x01840000 | P2_BITS rxbuff_bounce: .space 16384 + 16 .align DCACHE_LINE_SIZE opkt_0: .space 2000 .align DCACHE_LINE_SIZE opkt_1: .space 2000 .align DCACHE_LINE_SIZE opkt_2: .space 2000 .align DCACHE_LINE_SIZE opkt_3: .space 2000 .align DCACHE_LINE_SIZE ; End of bridge bounce buffer layout. . = 0x8ef00000 .sz any .pr any SETS.L #main,r0 jmp @r0 nop SETCONST ; Our "data segment". .align 4 next_pkt_cookie: .space 4 rxcfg: .space 4 cur_opkt: .space 4 opkts: .long opkt_0, opkt_1, opkt_2, opkt_3 ipkt: .space 4+MAX_ETHER_LEN cur_opkt_x: .space 1 opkt_busy: .space 1 my_MAC: .space 6 server_MAC: .space 6 broadcast_MAC: .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff first_pkt: .space 1 ; End of "data segment". ; Our "text segment". .align 2 init_data: SETS.L #opkt_busy,r0 SETS.L #0,r1 rts mov.b r1,@r0 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 and ; return true. bsr 8f nop bra 9f sett 1: bsr 8f nop bra 4b 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 SETCONST ; Pushes any dirty cache lines covering a block. Block base address in ; r1, length in r0. Code assumes r0 > 0. Trashes r0/r1. .if @FLG[DCACHE_LINE_SIZE] != @CLG[DCACHE_LINE_SIZE] .error Code assumes DCACHE_LINE_SIZE is a pwoer of two .endif push_dcache: mov.l r2,@-r15 SETS.L #DCACHE_LINE_SIZE-1,r2 and r1,r2 sub r2,r1 add r2,r0 SETS.L #DCACHE_LINE_SIZE-1,r2 add r2,r0 not r2,r2 and r2,r0 ; r1/r0 now ptr/len, rounded to cache-line boundaries SHXR #@FLG[DCACHE_LINE_SIZE],r0/r2 1: ocbwb @r1 dt r0 bf/s 1b add #DCACHE_LINE_SIZE,r1 rts mov.l @r15+,r2 ; Invalidate the whole icache. This is complicated only because we ; have to be running in P2 to fiddle CCR. Destroys no registers. invalidate_icache: mov.l r0,@-r15 mov.l r1,@-r15 mov.l r2,@-r15 mov.l r3,@-r15 mov.l r4,@-r15 SETS.L #CCR,r2 SETS.L #CCR_ICI,r3 mova 2f,r0 mov r0,r4 mova 1f,r0 SETS.L #Px_MASKOFF,r1 and r1,r0 SETS.L #P2_BITS,r1 or r1,r0 jmp @r0 nop .align 4 1: mov.l @r2,r0 or r3,r0 mov.l r0,@r2 nop nop nop nop nop nop nop nop jmp @r4 nop .align 4 2: mov.l @r15+,r4 mov.l @r15+,r3 mov.l @r15+,r2 mov.l @r15+,r1 rts mov.l @r15+,r0 find_free_opkt: SETS.L #opkt_busy,r3 mov.b @r3,r0 and #0xf,r0 SETS.L #0,r1 1: tst #1,r0 bt 1f SHXR #1,r0 bra 1b add #1,r1 1: mov r1,r0 tst #4,r0 bf wait_free_opkt 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_TOK | BBA_RTK_TX_STATUS_TUN | BBA_RTK_TX_STATUS_TABT] != 0x1110 .error Assumptions about BBA_RTK_TX_STATUS_ALL masks are wrong .endif sts.l pr,@-r15 1: bsr rd_w mov #BBA_RTK_TX_STATUS_ALL,r1 SHXR #4,r0/r2 tst r0,r0 bt 1b lds.l @r15+,pr mov r0,r1 SHXR #4,r0/r2 or r0,r1 SHXR #4,r1/r2 or r1,r0 and #0xf,r0 not r0,r0 mov.b @r3,r1 and r1,r0 bra find_free_opkt mov.b r0,@r3 ; Append multiple octets to an outgoing packet. Data specified via ; pointer in r1, length in r0; length must be >0. Trashes r2/r3/r4. opkt_append: 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 ; Append a single octet (in r0) to the outgoing packet. Trashes r1/r2. opkt_append_byte: SETS.L #cur_opkt,r1 mov.l @r1,r2 mov.b r0,@r2 add #1,r2 rts mov.l r2,@r1 opkt_send: sts.l pr,@-r15 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 #MIN_ETHER_XMIT,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 push_dcache 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<>8,r0 bsr opkt_append_byte mov #OUR_ETHER_TYPE&0xff,r0 ; Payload bsr opkt_append_byte mov #PROTO_OPC_DLREQ,r0 ; Send it! (Tail merged.) bra opkt_send lds.l @r15+,pr 9: .asciz "initial req"(13,10) .align 2 send_data_req: sts.l pr,@-r15 bsr find_free_opkt nop ; destination MAC SETS.L #server_MAC,r1 bsr opkt_append mov #6,r0 ; source MAC SETS.L #my_MAC,r1 bsr opkt_append mov #6,r0 ; Ethernet frame type bsr opkt_append_byte mov #OUR_ETHER_TYPE>>8,r0 bsr opkt_append_byte mov #OUR_ETHER_TYPE&0xff,r0 ; Payload bsr opkt_append_byte mov #PROTO_OPC_DATAREQ,r0 SETS.L #next_pkt_cookie,r3 SETS.L #4,r4 1: bsr opkt_append_byte mov.b @r3+,r0 dt r4 bf 1b ; Send it! (Tail merged.) bra opkt_send lds.l @r15+,pr lds.l @r15+,pr rts nop SETCONST process_packet: sts.l pr,@-r15 SETS.L #ipkt,r2 mov.l @r2+,r7 SETS.L #14,r0 cmp/hi r7,r0 bt 9f SETS.L #my_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 ; both mov.b and cmp/eq sign-extend mov.b @r2+,r0 cmp/eq #OUR_ETHER_TYPE>>8,r0 bf 9b mov.b @r2+,r0 cmp/eq #OUR_ETHER_TYPE&0xff,r0 bt 1f SETS.L #bad_type_msg,r1 bsr putstr nop bra 9b nop bad_type_msg: .asciz "Not our ethertype"(13,10) .align 2 1: ; check the opcode octet mov.b @r2+,r0 .if [PROTO_OPC_DLRESP != 1] | [PROTO_OPC_DATARESP != 3] .error received packet opcode tests need work .endif dt r0 bt ipkt_dlresp dt r0 dt r0 bt ipkt_dataresp ; Unrecognized opcode - ignore SETS.L #bad_opcode_msg,r1 bsr putstr nop bra 9b nop bad_opcode_msg: .asciz "Unrecognized opcode"(13,10) .align 2 nop ipkt_dlresp: ; DLRESP from a server; see if we want it SETS.L #first_pkt,r1 mov.b @r1,r0 tst #1,r0 bf 1f SETS.L #not_first_msg,r1 bsr putstr nop bra 9b nop not_first_msg: .asciz "Not first packet"(13,10) .align 2 1: ; We want it; save the source MAC and cookie, clear first_pkt, ; and send our first DATAREQ request. SETS.L #next_pkt_cookie,r1 SETS.L #4,r3 1: mov.b @r2+,r0 mov.b r0,@r1 dt r3 bf/s 1b add #1,r1 ; 13 here is 4 for the cookie, 1 for the opcode byte, 2 for the ; Ethernet packet type, and 6 for the MAC. add #-13,r2 SETS.L #server_MAC,r1 SETS.L #6,r3 1: mov.b @r2+,r0 mov.b r0,@r1 dt r3 bf/s 1b add #1,r1 SETS.L #server_at_msg,r1 bsr putstr nop bsr report_server_mac nop SETS.L #0,r1 SETS.L #first_pkt,r2 bsr send_data_req mov.b r1,@r2 bra 9b nop server_at_msg: .asciz "Server at " .align 2 ipkt_dataresp: ; Response from a server; see if we want it. SETS.L #first_pkt,r1 mov.b @r1,r0 tst #1,r0 bt 1f SETS.L #want_first_msg,r1 bsr putstr nop bra 9b nop want_first_msg: .asciz "Expecting first packet"(13,10) .align 2 1: ; We're expecting data; check the cookie SETS.L #next_pkt_cookie,r1 SETS.L #4,r3 1: mov.b @r2+,r0 mov.b @r1+,r4 cmp/eq r0,r4 bf 2f dt r3 bf 1b 1: ; The cookie matches; check the status octet. .if [PROTO_DATARESP_SUCCESS != 0] | [PROTO_DATARESP_DONE != 1] | [PROTO_DATARESP_BADCOOKIE != 2] | [PROTO_DATARESP_FAILURE != 3] .error received dataresp status tests need work .endif mov.b @r2+,r0 tst r0,r0 bt data_status_success dt r0 bt data_status_done dt r0 bt data_status_badcookie dt r0 bt data_status_failure SETS.L #bad_data_status_msg,r1 bsr putstr nop bra 9b nop 2: SETS.L #wrong_cookie_msg,r1 bsr putstr nop bra 9b nop bad_data_status_msg: .asciz "Bad data status octet"(13,10) wrong_cookie_msg: .asciz "Cookie wrong"(13,10) .align 2 data_status_done: ; Done. Extract the entry point, invalidate icache, and jump. SETS.L #4,r1 1: mov.b @r2+,r0 extu.b r0,r0 SHLL #8,r3/r4 dt r1 or r0,r3 bf 1b bra invalidate_icache lds r3,pr data_status_badcookie: SETS.L #first_pkt,r0 SETS.L #1,r1 bsr send_first_req mov.b r1,@r0 SETS.L #bad_cookie_msg,r1 bsr putstr nop bra 9b nop bad_cookie_msg: .asciz "Bad cookie error, restarting"(13,10) .align 2 data_status_failure: ; Some other error. Not much to do but restart. SETS.L #first_pkt,r0 SETS.L #1,r1 bsr send_first_req mov.b r1,@r0 SETS.L #error_msg,r1 bsr putstr nop bra 9b nop error_msg: .asciz "Unexpected error, restarting"(13,10) .align 2 data_status_success: ; Success. Save the next cookie. SETS.L #4,r1 SETS.L #next_pkt_cookie,r4 1: dt r1 mov.b @r2+,r0 mov.b r0,@r4 bf/s 1b add #1,r4 ; Extract the address. SETS.L #4,r1 1: mov.b @r2+,r0 SHLL #8,r3/r4 extu.b r0,r0 dt r1 bf/s 1b or r0,r3 ; Extract the length. mov.b @r2+,r0 extu.b r0,r0 mov.b @r2+,r1 SHLL #8,r0/r4 extu.b r1,r1 or r1,r0 ; Is the length > 1024 or < 1? Bogus if so. SETS.L #1024,r1 cmp/hi r1,r0 bt data_bad_length tst r0,r0 bf data_ok_length data_bad_length: SETS.L #bad_length_msg,r1 bsr putstr nop bra 9b nop bad_length_msg: .asciz "Bogus data length"(13,10) .align 2 data_ok_length: ; Copy data from the packet to memory, push dcache, update. ; Address is in r3, length in r0, packet pointer in r2. tst r0,r0 bt 2f mov r3,r5 mov r0,r4 1: mov.b @r2+,r1 mov.b r1,@r5 dt r0 bf/s 1b add #1,r5 mov r3,r1 bsr push_dcache mov r4,r0 2: ; Done with this packet. Request more data. bsr send_data_req nop bra 9b nop ; 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. .align 2 main: 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 ; Set up state variables. bsr init_data nop ; 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. SETS.L #my_mac_msg,r1 bsr putstr nop bsr report_my_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 ; Interrupts don't work (yet) - see the comment on enable_interrupts. ; bsr enable_interrupts ; nop 3: ; Init state and send our initial packet. SETS.L #first_pkt,r0 SETS.L #1,r1 bsr send_first_req mov.b r1,@r0 ; 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. ; ; If we wait too long (loop too many times), restart. SETS.L #0,r0 mov.l r0,@-r15 1: bsr check_recv_packet nop bf 2f add #4,r15 SETS.L #0,r0 mov.l r0,@-r15 bsr process_packet nop 2: bsr check_serial nop mov.l @r15,r0 add #1,r0 mov.l r0,@r15 SETS.L #0x000fffff,r1 tst r1,r0 bf 1b ; Retry. SETS.L #first_pkt,r1 mov.b @r1,r1 tst r1,r1 bf 3b SETS.L #0xff000000,r1 tst r1,r0 bf 3b bsr send_data_req nop bra 1b nop my_mac_msg: .asciz "My MAC " ; The code above jumps to fail if initialization fails. This ; just reports the failure and resets. 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 #0xa0000000,r0 ; hard reset vector jmp @r0 nop SETCONST ; Not sure we actually need to align the VBR; the only reason I ; have to suspect we might is that it's the kind of thing I've ; seen relatively often before - interrupt/trap vector tables ; often need to be aligned, not infrequently to a remarkably ; strict boundary. I see no indication in the manuals that ; the SH requires _any_ alignment, but it's easy to do and ; definitely won't hurt anything. (No explicit indication, ; that is. It is implicit in the execution of code at ; VBR+0x100, VBR+0x400, and VBR+0x600 that VBR must be even.) .align 0x10000 ; Exception handling consists of: ; - Save PC and SR in SPC and SSR ; - Set SR bit BL to 1 (block exceptions/interrupts) ; - Set SR bit MD to 1 (privileged mode) ; - Set SR bit RB to 1 (r0-r7 bank 1) ; - Write code to EXPEVT or INTEVT ; - Set PC to vector addr, resume execution intvec = . . = intvec + 0x100 SETS.L #0x100,r2 SETS.L #EXPEVT,r0 mov.l @r0,r3 SETS.L #INTEVT,r0 SETS.L #regdump,r1 jmp @r1 mov.l @r0,r4 SETCONST . = intvec + 0x400 SETS.L #0x400,r2 SETS.L #EXPEVT,r0 mov.l @r0,r3 SETS.L #INTEVT,r0 SETS.L #regdump,r1 jmp @r1 mov.l @r0,r4 SETCONST . = intvec + 0x600 SETS.L #0x600,r2 SETS.L #EXPEVT,r0 mov.l @r0,r3 SETS.L #INTEVT,r0 SETS.L #regdump,r1 jmp @r1 mov.l @r0,r4 SETCONST . = intvec + 0x1000 crash_msg_0: .asciz (13,10,10)"FATAL TRAP"(13,10)"R0 " crash_msg_1: .asciz " R1 " crash_msg_2: .asciz " R2 " crash_msg_3: .asciz " R3 " crash_msg_4: .asciz (13,10)"R4 " crash_msg_5: .asciz " R5 " crash_msg_6: .asciz " R6 " crash_msg_7: .asciz " R7 " crash_msg_8: .asciz (13,10)"R8 " crash_msg_9: .asciz " R9 " crash_msg_10: .asciz " R10 " crash_msg_11: .asciz " R11 " crash_msg_12: .asciz (13,10)"R12 " crash_msg_13: .asciz " R13 " crash_msg_14: .asciz " R14 " crash_msg_15: .asciz " R15 " crash_msg_gbr: .asciz (13,10)"GBR " crash_msg_sr: .asciz " SR " crash_msg_pc: .asciz " PC " crash_msg_mach: .asciz (13,10)"MACH" crash_msg_macl: .asciz " MACL" crash_msg_pr: .asciz " PR " crash_msg_vec: .asciz (13,10)"vector" crash_msg_expevt: .asciz " EXPEVT" crash_msg_intevt: .asciz " INTEVT" crash_msg_done: .asciz (13,10) crash_msg_equal: .asciz " = " .align 4 crash_msgs: .long crash_msg_0 .long crash_msg_1 .long crash_msg_2 .long crash_msg_3 .long crash_msg_4 .long crash_msg_5 .long crash_msg_6 .long crash_msg_7 .long crash_msg_8 .long crash_msg_9 .long crash_msg_10 .long crash_msg_11 .long crash_msg_12 .long crash_msg_13 .long crash_msg_14 .long crash_msg_15 .long crash_msg_gbr .long crash_msg_sr .long crash_msg_pc .long crash_msg_mach .long crash_msg_macl .long crash_msg_pr .long crash_msg_vec .long crash_msg_expevt .long crash_msg_intevt .long 0 .align 2 regdump: mov r15,r5 SETS.L #intstacktop,r15 mov.l r4,@-r15 mov.l r3,@-r15 mov.l r2,@-r15 sts.l pr,@-r15 sts.l macl,@-r15 sts.l mach,@-r15 stc.l spc,@-r15 stc.l ssr,@-r15 stc.l gbr,@-r15 mov.l r5,@-r15 mov.l r14,@-r15 mov.l r13,@-r15 mov.l r12,@-r15 mov.l r11,@-r15 mov.l r10,@-r15 mov.l r9,@-r15 mov.l r8,@-r15 stc.l r7_bank,@-r15 stc.l r6_bank,@-r15 stc.l r5_bank,@-r15 stc.l r4_bank,@-r15 stc.l r3_bank,@-r15 stc.l r2_bank,@-r15 stc.l r1_bank,@-r15 stc.l r0_bank,@-r15 SETS.L #SCIF_BASE,r14 SETS.L #crash_msgs,r9 SETS.L #putstr,r8 SETS.L #printhex8,r7 SETS.L #putchar,r6 1: mov.l @r9+,r1 tst r1,r1 bt 1f jsr @r8 nop SETS.L #crash_msg_equal,r1 jsr @r8 nop jsr @r7 mov.l @r15+,r1 bra 1b nop 1: SETS.L #crash_msg_done,r1 jsr @r8 nop jsr @r6 mov #0,r1 SETS.L #0xa0000000,r0 ; hard-reset vector jmp @r0 nop SETCONST .align 4 .space 0x1000 intstacktop = .