reset_vector: # bfc00000 bal cpu_initcpu_init performs any hardware specific initialization. eg. initialize the serial port that will be used to communicate with the SerialICE Controller, initialize the DRAM controller. It does not need to initialize the caches.
Once this step has been completed. We initialize the CAUSE and SR registers,
# make sure the sw bits of the CAUSE register are zero .set noreorder mtc0 zero,C0_CAUSE .set reorder # enable ints in SR MASK+IEC li k0,(SR_BEV|SR_IEC|UART_INTBIT) .set noreorder mtc0 k0,C0_SR .set reorderand then save some values in the savearea. Note that SAVEAREA has a kseg1 value.
la k0,SAVEAREA la t0,get_word sw t0,ICE_GWP*4(k0) la t0,put_word sw t0,ICE_PWP*4(k0) li t0,IBUFSIZE sw t0,ICE_IBS*4(k0) li t0,REG_MAP sw t0,ICE_MAP*4(k0) li t0,SA_VERS sw t0,ICE_SAV*4(k0) li t0,ICE_SAHSIZE sw t0,ICE_SAH*4(k0) #ifdef MIPSEB li t0,0 #else li t0,1 #endif sw t0,ICE_LE*4(k0)This block of code writes seven values into the savearea. This information is needed by the SerialICE Driver or DLL. The savearea is in two parts, a header, and registers. The size of the header is placed in ICE_SAH, following this are a number of saved registers. Which registers, and how many, are indicated by the ICE_MAP value.
#ifdef RUN_APPLICATION # execute a prom-resident application b cstartup #else # wait here for the host to speak to me 1: b 1b #endifWhen the kernel receives an interrupt, the hardware transfers control to the general exception vector. The kernel forces kseg1 (non cacheable) execution and then saves 9 registers in the savearea. These registers are required for the kernel's operation.
gen_vector: # bfc00180 # save regs la k0,SAVEAREA sw AT,ICE_AT*4(k0) sw v0,ICE_V0*4(k0) sw a0,ICE_A0*4(k0) sw a1,ICE_A1*4(k0) sw a2,ICE_A2*4(k0) sw a3,ICE_A3*4(k0) # make sure that we are in kseg1 la a3,1f li a2,K1BASE or a3,a2 j a3 1: sw t0,ICE_T0*4(k0) sw t1,ICE_T1*4(k0) sw t2,ICE_T2*4(k0) sw t3,ICE_T3*4(k0) sw t4,ICE_T4*4(k0) sw s0,ICE_S0*4(k0) sw ra,ICE_RA*4(k0) .set noreorder mfc0 t0,C0_EPC nop .set reorder sw t0,ICE_EPC*4(k0)Register s0 is used as a pointer to the current location in the memory-based instruction buffer. So we need to initialize it here.
# init s0 (KSEG1) li s0,INSTR_BUFFERNow we need to find out what the cause of the exception was. The pseudo code is as follows:
if (not hw int) goto send_ack; else if (not my int) goto send_ack; else if (not attn byte) goto restore_rfe; else goto send_ack;If the exception was not a hardware interrupt jump to send_ack. If it was a hardware, but it wasn't a serial-port interrupt jump to send_ack. If it was a serial-port interrupt, but it wasn't a valid ATTN byte jump to restore_rfe. For all other cases jump to send_ack. The real code follows:
# read the CAUSE register .set noreorder mfc0 a0,C0_CAUSE nop .set reorder # hw int? and t0,a0,CAUSE_EXCMASK bne t0,zero,send_ack # brif not a hw int # It is a hw int. But is it my int? .set noreorder mfc0 t0,C0_SR nop .set reorder and t0,a0 # qualify the CAUSE bits and t0,UART_INTBIT beq t0,zero,send_ack # brif not mine # make sure that this is a *real* attn byte # read the byte li a2,UART_BASE lw k0,UART_RXHR(a2) li k1,ATTN bne k0,k1,restore_rfe # brif not an attn byte # fall thru to send_ack .end gen_vectorsend_ack sends the special ACK character. It also sets t0 and t1 to zero. These two registers are used by the SerialICE Driver (running on the SerialICE Controller) to hold the current address and the current data value. The driver assumes that these registers start with a value of zero, and so they must be initialized here.
send_ack: li a2,UART_BASE # make sure that the tx is ready 1: lw k0,UART_TXS(a2) and k0,TXS_TXRDY beq k0,zero,1b li k0,ACK sw k0,UART_TXHR(a2) # make sure that r8 and r9 are zero. li t0,0 li t1,0 # fall thru to ice_loop .end send_ackThe next block of code is the main ice_loop. This is where the kernel remains while it is in control (state1). Pseudo code for this block follows:
for (;;) {
w = get_word();
if (w == SENDA0) {
*s0++ = 0; *s0++ = J_RA; *s0++ = 0;
a0 = (* s0)();
put_word(a0);
}
else if (w == RUN_MODE) {
restore_regs;
rfe;
}
else if (w == SENDSAP) {
a0 = &savearea;
put_word(a0);
}
else *s0++ = w;
}
Each time around the loop, the kernel performs the following actions:
ice_loop: bal get_cmd # check for SENDA0 li a2,SENDA0 bne a2,v0,1f # It is SENDA0. Execute the code in INSTR_BUFFER and send # the value of register a0. # Make sure that the routine ends with a "j ra". sw zero,(s0) li k0,J_RA_INSTR sw k0,4(s0) sw zero,8(s0) # Make sure that the writes complete before the jal. .set noreorder nop nop nop .set reorder # Reset s0 to point to start of INSTR_BUFFER. li s0,INSTR_BUFFER jal s0 # execute INSTR_BUFFER bal put_word # send A0 b ice_loop 1: # check for RUN_MODE li a2,RUN_MODE bne a2,v0,1f restore_rfe: # It is RUN_MODE. Transfer control to the client. # restore regs la k0,SAVEAREA lw AT,ICE_AT*4(k0) lw v0,ICE_V0*4(k0) lw a0,ICE_A0*4(k0) lw a1,ICE_A1*4(k0) lw a2,ICE_A2*4(k0) lw a3,ICE_A3*4(k0) lw t0,ICE_T0*4(k0) lw t1,ICE_T1*4(k0) lw t2,ICE_T2*4(k0) lw t3,ICE_T3*4(k0) lw t4,ICE_T4*4(k0) lw s0,ICE_S0*4(k0) lw ra,ICE_RA*4(k0) .set noreorder lw k0,ICE_EPC*4(k0) nop j k0 # jump to client rfe .set reorder 1: # check for SENDSAP li a2,SENDSAP bne a2,v0,1f # It is SENDSAP. Send address of SAVEAREA. la a0,SAVEAREA or a0,1 # indicate new format bal put_word b ice_loop 1: # else. Not a special word. sw v0,(s0) # save word in INSTR_BUFFER addu s0,4 # ready for next word b ice_loop .end ice_loopThe get_cmd function reads 4 bytes from the serial port, and assembles them into a 32-bit word. The word is placed in register v0. This routine differs from the get_word routine that is used for download, in that it will always respond to an ATTN byte (0x55).
get_cmd:
li a2,UART_BASE
li a1,4 # get 4 bytes
# wait for rxrdy
3: lw k0,UART_RXS(a2)
and k0,RXS_RXRDY
beq k0,zero,3b
# get the byte
lw k0,UART_RXHR(a2)
# first byte?
bne a1,4,2f # brif not first byte
# is the byte a wakeup?
bne k0,ATTN,2f # brif not a wakeup
# wait for txrdy
1: lw k0,UART_TXS(a2)
and k0,TXS_TXRDY
beq k0,zero,1b
# send an ack
li k0,ACK
sw k0,UART_TXHR(a2)
b 3b
2: sll v0,8 # move word into position
or v0,k0 # merge byte with word
subu a1,1 # bytecount--
bne a1,zero,3b # do next byte
j ra
.end get_cmd
get_word:
li a2,UART_BASE
li a1,4
1: lw k0,UART_RXS(a2)
and k0,RXS_RXRDY
beq k0,zero,1b
lw k0,UART_RXHR(a2)
sll v0,8
or v0,k0
subu a1,1
bne a1,zero,1b
j ra
.end get_word
The put_word function transmits the 32-bit contents of register a0 as 4
successive bytes.
put_word:
li a2,UART_BASE
li a1,4
1: lw k0,UART_TXS(a2)
and k0,TXS_TXRDY
beq k0,zero,1b
sw a0,UART_TXHR(a2)
srl a0,8
subu a1,1
bne a1,zero,1b
j ra
.end put_word