; 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????) Buffers (see comments below) ; [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. :-( ; Remote controller/keyboard interface. This watches the Maple devices ; for controllers and keyboards; when one is found, it is watched for ; state changes. It also watches for network packets. When a request ; packet is received, the state of all devices is reported. Rather ; than requesting Maple reports only when a request packet is ; received, we watch the Maple bus constantly, with device state being ; generated out of cached status in RAM. This allows us to respond ; faster to request packets. Our main loop looks like ; ; while (1) ; build next Maple request and send it ; check for Ethernet packet, handle if present ; wait for Maple request to complete, update state ; ; We communicate via raw Ethernet. We use Ethernet packet type 0xFEDB ; (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.) ; ; 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 Request presence reports. No following data. This is ; sent by someone interested in using our services; we ; respond with a 0x01 packet. 0x00 packets are sent to ; the broadcast address; all other pacets are unicast to ; their receivers. PROTO_OPC_HAIL = 0x00 ; ; 0x01 No following data. This is sent by us in response to a ; 0x00 packet and indicates a willingness to send ; updates. PROTO_OPC_HAILACK = 0x01 ; ; 0x02 Request update. No following data. This is sent by ; our client; it provokes a 0x03 packet from us. PROTO_OPC_REQUEST = 0x02 ; ; 0x03 This is followed by a status report. This consists of ; some global state followed by four device status ; reports, one for each Maple bus slot. The global state ; is eight bytes, which hold, little-endian, the number ; of complete Maple bus scans executed as of when the ; status report was generated. Each of the four device ; status reports begins with a status octet. This is ; 0x00 There is nothing in this slot. ; 0x01 There is something unrecognized in this slot. ; 0x02 There is a controller in this slot. ; 0x03 There is a keyboard in this slot. ; For 0x00 and 0x01, this is the only octet in that ; device status report. For 0x02, this octet is followed ; by eight octets giving the current state reported by ; the controller. For 0x03, this is followed by eight ; octets giving key-down status as reported by the ; keyboard. PROTO_OPC_RESPONSE = 0x03 OUR_ETHER_TYPE = 0xfedb 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" .include "maple-bits.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. ; Maple buffer layout. Normally, maple_cmd and maple_resp would be ; down in our "data segment", next to (eg) maple_type. But when I do ; that, I never get anything. I don't know why; DMA_ADDRMASK includes ; enough high bits to cover buffers from 80000000 through 9fffffe0. ; Perhaps the hardware actually uses fewer bits than DMA_ADDRMASK ; implies? In any case, I find I have to put the maple buffers lower ; in core in order to make things work. . = 0x8c020000 .align 32 maple_cmd: .space 64 .align 32 maple_resp: .space 1024 ; End Maple buffer layout. . = 0x8ef00000 .sz any .pr any SETS.L #main,r0 jmp @r0 nop SETCONST ; Our "data segment". .align 4 opkts: .long opkt_0, opkt_1, opkt_2, opkt_3 maple_stati: .long maple_status_0, maple_status_1, maple_status_2, maple_status_3 maple_cycles: .space 8 rxcfg: .space 4 cur_opkt: .space 4 ipkt: .space 4+MAX_ETHER_LEN cur_opkt_x: .space 1 opkt_busy: .space 1 my_MAC: .space 6 client_MAC: .space 6 maple_status_0: .space 9 maple_status_1: .space 9 maple_status_2: .space 9 maple_status_3: .space 9 cur_maple_dev: .space 1 maple_dev_phase: .space 1 maple_type: .space 1 ; End of "data segment". ; Our "text segment". .align 2 init_data: SETS.L #opkt_busy,r0 SETS.L #0,r1 mov.b r1,@r0 SETS.L #maple_cycles,r0 mov.l r1,@r0 mov.l r1,@(4,r0) SETS.L #maple_status_0,r0 mov.b r1,@r0 SETS.L #maple_status_1,r0 mov.b r1,@r0 SETS.L #maple_status_2,r0 mov.b r1,@r0 SETS.L #maple_status_3,r0 mov.b r1,@r0 SETS.L #cur_maple_dev,r0 mov.b r1,@r0 SETS.L #maple_dev_phase,r0 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 check_maple: SETS.L #BUS_STATE,r1 mov.l @r1,r0 tst #BUS_STATE_RUNNING,r0 bt 1f rts nop 1: sts.l pr,@-r15 SETS.L #maple_resp,r9 ocbi @r9 mov.l @r9,r4 not r4,r0 tst r0,r0 bt maple_not_there SHLR #24,r4/r3 cmp/pl r4 bf maple_too_short mov.l @(4,r9),r0 SETS.L #0x01000000,r1 cmp/eq r1,r0 bt maple_ctlr SETS.L #0x40000000,r1 cmp/eq r1,r0 bf maple_bad maple_kbd: ; keyboard bra 1f mov #0x03,r0 maple_ctlr: ; controller mov #0x02,r0 1: ; Something there (code in r0) ; See if what we just did was a GETCOND or DEVINFO SETS.L #maple_dev_phase,r1 SETS.L #maple_type,r2 mov.b @r1,r3 tst r3,r3 bf 1f ; It was DEVINFO ; Save device type and do a GETCOND mov.b r0,@r2 mov #1,r0 bra maple_start_next mov.b r0,@r1 1: ; It was GETCOND ; Check that it's the device we're expecting mov.b @r2,r3 cmp/eq r0,r3 bf maple_bad ; device code changed! ; Check we got 3 longs of data mov #3,r0 cmp/eq r0,r4 bf maple_bad ; length wrong ; Save the data mov r3,r0 mov.l @(12,r9),r2 bra maple_save_result mov.l @(8,r9),r1 maple_too_short: ; Response isn't ffffffff, but is too short maple_bad: ; Something unrecognized/unexpected is there bra maple_save_result mov #1,r0 maple_not_there: ; Response is ffffffff (nothing there) mov #0,r0 maple_save_result: SETS.L #cur_maple_dev,r3 SETS.L #maple_stati,r4 mov.b @r3,r3 SHLL #2,r3 add r3,r4 mov.l @r4,r3 mov.b r0,@r3 add #1,r3 SETS.L #8,r4 1: mov.b r1,@r3 SHLR #8,r1 mov r2,r0 SHLL #24,r0/r5 SHLR #8,r2 dt r4 add #1,r3 bf/s 1b or r0,r1 ; Set up for the next device SETS.L #cur_maple_dev,r1 mov.b @r1,r0 add #1,r0 and #3,r0 mov.b r0,@r1 tst r0,r0 bf 1f SETS.L #maple_cycles,r1 sett mov.l @r1,r2 mov.l @(4,r1),r3 addc r0,r2 addc r0,r3 mov.l r2,@r1 mov.l r3,@(4,r1) 1: SETS.L #maple_dev_phase,r1 SETS.L #0,r0 mov.b r0,@r1 maple_start_next: bsr start_maple nop maple_ret: lds.l @r15+,pr rts nop ; 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 power 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 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_RESPONSE,r0 SETS.L #maple_cycles,r1 bsr opkt_append mov #8,r0 SETS.L #maple_stati,r9 SETS.L #4,r8 2: mov.l @r9+,r7 mov.b @r7+,r0 and #3,r0 bsr opkt_append_byte mov r0,r6 mov r6,r0 tst #2,r0 bt 1f SETS.L #8,r0 bsr opkt_append mov r7,r1 1: dt r8 bf 2b ; Marker for humans bsr opkt_append_byte mov #'*,r0 ; Send it! (Tail merged.) bra opkt_send lds.l @r15+,pr send_hail_ack: sts.l pr,@-r15 bsr find_free_opkt nop ; destination MAC SETS.L #client_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_HAILACK,r0 ; Send it! (Tail merged.) bra opkt_send lds.l @r15+,pr 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 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_HAIL != 0] | [PROTO_OPC_REQUEST != 2] .error received packet opcode tests need work .endif tst r0,r0 bt ipkt_hail dt r0 dt r0 bt ipkt_request ; 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_hail: ; HAIL from a client; ack it 1: ; Save the source MAC and send a HAILACK ; 9 here is 1 for the opcode byte, 2 for the Ethernet packet ; type, and 6 for the MAC. add #-9,r2 SETS.L #client_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 #client_at_msg,r1 bsr putstr nop bsr report_client_mac nop bsr send_hail_ack nop bra 9b nop client_at_msg: .asciz "Client at " .align 2 ipkt_request: ; Request from a client. Save MAC (printing if different from ; last) and generate a response. ; 9 here is 1 for the opcode byte, 2 for the Ethernet packet ; type, and 6 for the MAC. add #-9,r2 SETS.L #client_MAC,r1 SETS.L #6,r3 SETS.L #0,r4 2: mov.b @r2+,r0 mov.b @r1,r5 cmp/eq r0,r5 bt 1f add #1,r4 1: mov.b r0,@r1 dt r3 bf/s 2b add #1,r1 tst r4,r4 bt 1f SETS.L #client_at_msg,r1 bsr putstr nop bsr report_client_mac nop 1: bsr send_response 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 ; Initialize the Maple hardware. bsr init_maple 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 3: bsr start_maple nop ; Main loop. Busy-wait, checking for incoming packets, ; incoming serial data, and Maple command completion. 1: bsr check_recv_packet nop bf 2f bsr process_packet nop 2: bsr check_serial nop bsr check_maple 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 = .