; ARM remote emulator and monitor programme ; J. Garside - University of Manchester - March 2003 ; ; TO DO: ; ; EMULATOR ; ; Detailed undefined trapping (need reduced since V.5) ; Thumb stuff (could make (ROM) look-up table for most?) ; ; Consider storing PC+8 in register bank for reads ; ; Wrap up memory transfers to allow trap checking ; (Return {okay, abort, watchpoint}?) ; Watchpoints & Register trap points ; Aborts (enableable) ; ; Lose "tube_too" @@@ ; ; ; MONITOR ; ; Virtex bus speed changer @@@ Done?? ; Revise & expand command set ; copy ; find ; Separate breakpoint enable command (+ other flags) ; Return version number (& boot version number?) command ; ; GENERAL ; ; Finish sorting out I/O map (Virtex, ++) & proper memory handling ; (eventually) make proper interrupt driven scheduler ; ; Lots of testing :-] ; General register usage ; R7 points at shared variables ; R8 is flags for run options (emulator) ; R9 points at register definitions ; R10 is current instruction (emulator) ; R11 is PC (emulator) (leave in reg. set ? @@@) ; R12 is CPSR (emulator) Maker EQU 1 Version EQU 2 day EQU 10 month EQU 03 year EQU 03 GBLL Virtex_E Virtex_E SETL {TRUE} GET header.s ; Register definitions etc. GET link_addresses.s ; Addresses (etc.) of programmes Nflag EQU &80000000 ; N flag Zflag EQU &40000000 ; Z flag Cflag EQU &20000000 ; C flag Vflag EQU &10000000 ; V flag Lbit EQU &01000000 ; Link bit in branches Sbit EQU &00100000 ; Set flags bit Pbit EQU &01000000 ; Pre-index bit Ubit EQU &00800000 ; Up bit Bbit EQU &00400000 ; Byte bit Wbit EQU &00200000 ; Writeback bit Rbit EQU &00400000 ; SPSR indicator Ldbit EQU &00100000 ; Load bit MUL_A_bit EQU &00200000 ; Accumulate or don't MUL_U_bit EQU &00400000 ; Signed or unsigned MUL_L_bit EQU &00800000 ; Short or long LSM_S_bit EQU &00400000 ; S bit set LSM_PC_bit EQU &00008000 ; Mask for PC IO_area_start EQU &10000000 ; Virtual address IO_area_end EQU &20000000 ; Virtual address tube EQU &03000000 ; Rationalise into I/O map tube_too EQU &0000C000 ; @@@ Infringes normal map! @@@ nFIQ_wire EQU AT91_FIQ ;nIRQ_wire EQU AT91_Spartan_init Run_W_bit EQU &20 ; Bits in "run" command Run_B_bit EQU &10 ; used to allow stepping Run_M_bit EQU &08 ; through various routines Run_S_bit EQU &04 ; Run_P_bit EQU &02 ; Run_BB_bit EQU &01 ; Extra breakpoint enable Run_I_bit EQU &200 ; Run_F_bit EQU &100 ; ; These states are sorted on bits 7..6 into four categories ; Do not alter without checking cross reference #A# State_hard_reset EQU &00 ; Hardware reset State_reset EQU &01 ; Soft reset performed State_to_reset EQU &02 ; Soft reset requested State_stopped EQU &40 ; Stopped by user while running State_stop_req EQU &41 ; Stopped by software request State_stop_bkpt EQU &42 ; Stopped by breakpoint State_count_out EQU &43 ; Stopped by stepping finishing State_running EQU &80 ; Running State_running_BL EQU &81 ; Running procedure State_running_SWI EQU &82 ; Running SWI State_running_IRQ EQU &83 ; Running State_running_FIQ EQU &84 ; Running State_running_abt EQU &85 ; Running State_stepping EQU &C0 ; Stepping ; Internal interrupt bit definitions Int_timer_compare EQU &01 ; Timer matches comparison register Int_Spartan EQU &02 ; Int_Virtex EQU &04 ; Int_Ethernet EQU &08 ; Int_Rx_ready EQU &10 ; Character received on serial line Int_Tx_ready EQU &20 ; Serial transmit buffer not full Int_R_button EQU &40 ; Int_L_button EQU &80 ; UART_RxRdy EQU &01 ; UART status bits UART_TxRdy EQU &02 ; Spartan_page EQU &20000000 ; Emulator address of Spartan Virtex_page EQU &30000000 ; Emulator address of Virtex breakpoint_max EQU 8 ; Number of breakpoints watchpoint_max EQU 1 ; Number of watchpoints Terminal_feature EQU 2 ; Feature number SEE "enq_message" Terminal_Rx_buff_length EQU 16 ; Buffers for `terminal' feature Terminal_Tx_buff_length EQU 16 ; Reset_PC EQU &00000000 Reset_CPSR EQU &000000D3 ; Stack lengths in words (with considerable margin) Comm_stack_length EQU 40 Exec_stack_length EQU 40 ; 20 + 20 for Tube output desched. Int_stack_length EQU 8 ; Serial command definitions (some of them) Com_reset EQU &04 Com_enq EQU &20 Com_stop EQU &21 AREA boot, CODE, READONLY ENTRY ;Mon_ROM B Mon_ROM_start ; Start at first address ; Pack version & date into 32 bits Version_ID DCD Maker*&1000000 + Version*&10000 + day*&800 + month*&80 + year DCB "AT91 Back-end monitor/emulator version 0.2 " DCB "J. Garside, (c) University of Manchester " DCB "March 2003" ALIGN ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mon_init_table DCD TC_base + TC_CHL0 + TC_CMR, &0C00C400 ; Wave, MCK/2 DCD TC_base + TC_CHL0 + TC_RC, 16000 ; /16000 DCD TC_base + TC_CHL0 + TC_IER, &00000010 ; CPC interrupt DCD TC_base + TC_CHL0 + TC_IDR, &000000EF ; only DCD TC_base + TC_CHL0 + TC_CCR, 5 ; Start! DCD US0_base + US_IER, RxRdy ; Interrupt source(s) ; channel #0 DCD AIC_base + AIC_SPU, spurious_isr + ROM_base ; ROM DCD AIC_base + AIC_SMR2, &00000024 ; Edge triggered, pri. 4 DCD AIC_base + AIC_SMR4, &00000023 ; Edge triggered, pri. 3 DCD AIC_base + AIC_SMR8, &00000022 ; Edge triggered, pri. 2 DCD AIC_base + AIC_SVR2, Host_isr + ROM_base ; ROM DCD AIC_base + AIC_SVR4, Timer0_isr + ROM_base ; ROM DCD AIC_base + AIC_SVR8, PIO_isr + ROM_base ; ROM DCD AIC_base + AIC_IECR, &00000114 ; PIO, TC0, US0 DCD PIO_base + PIO_IER ; Data below ... DCD AT91_Spartan_IRQ :OR: AT91_Virtex_IRQ :OR: AT91_Ether_IRQ :OR: AT91_Spartan_init :OR: AT91_Virtex_init DCD PIO_base + PIO_PDR, &00000100 ; TIOB2 output DCD TC_base + TC_CHL2 + TC_CMR, &0C00C400 ; Wave, MCK/2 DCD TC_base + TC_CHL2 + TC_RC, 8 ; Toggle @ 2MHz DCD TC_base + TC_CHL2 + TC_IDR, &000000FF ; No interrupts DCD TC_base + TC_CHL2 + TC_CCR, 5 ; Start! DCD DEV_TERMINATE Mon_init_table_RAM DCD TC_base + TC_CHL0 + TC_CMR, &0C00C400 ; Wave, MCK/2 DCD TC_base + TC_CHL0 + TC_RC, 16000 ; /16000 DCD TC_base + TC_CHL0 + TC_IER, &00000010 ; CPC interrupt DCD TC_base + TC_CHL0 + TC_IDR, &000000EF ; only DCD TC_base + TC_CHL0 + TC_CCR, 5 ; Start! DCD US0_base + US_IER, RxRdy ; Interrupt source(s) ; DCD US1_base + US_IER, RxRdy ; Interrupt source(s) ; channel #0 DCD AIC_base + AIC_SPU, spurious_isr - RAM_image_start DCD AIC_base + AIC_SMR2, &00000024 ; Edge triggered, pri. 4 DCD AIC_base + AIC_SMR4, &00000023 ; Edge triggered, pri. 3 DCD AIC_base + AIC_SMR8, &00000022 ; Edge triggered, pri. 2 DCD AIC_base + AIC_SVR2, Host_isr - RAM_image_start DCD AIC_base + AIC_SVR4, Timer0_isr - RAM_image_start DCD AIC_base + AIC_SVR8, PIO_isr - RAM_image_start DCD AIC_base + AIC_IECR, &00000114 ; PIO, TC0, US0 DCD PIO_base + PIO_IER ; Data below ... DCD AT91_Spartan_IRQ :OR: AT91_Virtex_IRQ :OR: AT91_Ether_IRQ :OR: AT91_Spartan_init :OR: AT91_Virtex_init DCD PIO_base + PIO_PDR, &00000100 ; TIOB2 output DCD TC_base + TC_CHL2 + TC_CMR, &0C00C400 ; Wave, MCK/2 DCD TC_base + TC_CHL2 + TC_RC, 8 ; Toggle @ 2MHz DCD TC_base + TC_CHL2 + TC_IDR, &000000FF ; No interrupts DCD TC_base + TC_CHL2 + TC_CCR, 5 ; Start! DCD DEV_TERMINATE ; ch. 1 ; DCD AIC_base + AIC_SPU, spurious_isr + ROM_base ; ROM @@@ ; DCD AIC_base + AIC_SMR3, &00000024 ; Edge triggered, pri. 4 ; DCD AIC_base + AIC_SMR4, &00000023 ; Edge triggered, pri. 3 ; DCD AIC_base + AIC_SVR3, Host_isr + ROM_base ; ROM @@@ ; DCD AIC_base + AIC_SVR4, Timer0_isr + ROM_base ; ROM @@@ ; DCD AIC_base + AIC_IECR, &00000018 ; TC0, US1 Divisor_1kHz DCD &00418937 ; &100000000 * .001 TC0_RC DCD TC_base + TC_CHL0 + TC_RC ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RAM_image_length ; RAM area length DCD RAM_image_end - RAM_image_start Exec_RAM DCD execute_start - RAM_image_start Comm_RAM DCD command_start - RAM_image_start First_SWI_instr mov pc, lr ; Return in supervisor mode First_IRQ_instr ldr pc, [pc, #-&F20] ; Read from IVR instruction Mon_ROM_start ; tst r7, #Power_up_flag ; Power up reset? ; stmneia r2, {r0-r9} ; make startup config visible if so mov r10, #0 ; Ensure supervisor mode ldr r0, First_SWI_instr ; str r0, [r10, #&08] ; Plant return instruction swi 0 ; ldr r0, First_IRQ_instr ; str r0, [r10, #&18] ; Plant vector load ; Beware: still want initial R2, R3, R8, R9, ... mov r10, #FALSE ; Shall we copy to RAM? ldr r0, RAM_image_length ; R1 holds Internal RAM size cmp r0, r1 ; Can fit in RAM? ; also allow user input @@ bhi Mon_init_0 ; mov r10, #TRUE ; We will copy to RAM adrl r4, RAM_image_start ; Source mov r1, #0 ; Destination mov r0, r0, lsr #2 ; Length RAM_image_copy ldr r5, [r4], #4 ; str r5, [r1], #4 ; subs r0, r0, #1 ; bhs RAM_image_copy ; Extra word included Mon_init_0 mrs r0, cpsr ; Set up IRQ stack pointer bic r1, r0, #Mode_bits ; Clear mode bits orr r1, r1, #IRQ_mode ; msr cpsr_c, r1 ; Go into IRQ mode nop ; mov sp, #Int_stack - RAM_image_start msr cpsr_c, r0 ; Back to supervisor mode nop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; TEMPORARY CONFIG @@@ ; bl XPIO_init ; bl Host_buffer_init ; Initialise serial receive buffer bl Terminal_init ; Initialise terminal feature cmp r10, #TRUE ; adrnel r1, Mon_init_table ; Only ROM copy adreql r1, Mon_init_table_RAM ; Point interrupts into RAM Mon_init1 ldr r6, [r1], #4 ; Register address cmp r6, #DEV_TERMINATE ; Terminator? beq Mon_init2 ; yes - terminate ldr r0, [r1], #4 ; Get data value str r0, [r6] ; Store value b Mon_init1 ; and repeat ... Mon_init2 mov r4, r9, lsr #16 ; Upper bits of clock rate mov r0, r9, lsl #16 ; Lower bits of clock rate ldr r1, Divisor_1kHz ; MCK/1kHz mov r5, #32 ; (r1 > &FFFF) => 16 real bits div1 cmp r4, r1 ; subhs r4, r4, r1 ; adcs r0, r0, r0 ; Shift dividend & Acc sub r5, r5, #1 ; Count leaves carry alone tst r5, r5 ; adcpl r4, r4, r4 ; Top of shift register bpl div1 ; add r0, r0, #1 ; Round movs r0, r0, lsr #1 ; Always prescaled by 2 ; beq ??? ; Trap for VERY slow @@@ ldr r1, TC0_RC ; Set timer to 1kHz str r0, [r1] ; (regardless of Clock_rate) mov r7, #shared_variables - Mon_RAM_start ; @@@ bl Interrupts_init ; Emulator interrupt set up ; Needs R7 set up mrs r0, cpsr ; Enable interrupts bic r0, r0, #I_bit ; RAM image present by now msr cpsr_c, r0 ; ; Initialise memory and register definitions mov r1, #0 ; Mem start str r1, [r7, #mem_area_start - shared_variables] str r3, [r7, #mem_area_end - shared_variables] ; Covers all external RAM str r2, [r7, #mem_area_pos - shared_variables] ; Offset str r8, [r7, #Board_number - shared_variables] str r9, [r7, #Clock_rate - shared_variables] ; For the present the register image is kept in static space ; It is still defined through a pointer add r9, r7, #reg_block - shared_variables ; Point at register space str r9, [r7, #reg_area_ptr - shared_variables] ; Save pointer ; Zero user's registers on power up @@@ ; Current set followed by banked registers for : User, SVC, ABT, UND, IRQ, FIQ ; followed by 5 real SPSRs and one 'dustbin' SPSR ; plus one word to make R15 visible mov r0, #0 ; Zero options (N.B. word) str r0, [r7, #Running_flags - shared_variables] bl Breakpoint_init ; bl Watchpoint_init ; mov r1, #exec_variables - Mon_RAM_start ; Base of private variables mov r0, #Exec_stack - RAM_image_start ; Base of execution stack cmp r10, #TRUE ; Are we in RAM? adrnel lr, execute_start ; Start of execution code (ROM) ldreq lr, Exec_RAM ; Start of execution code (RAM) stmfd r0!, {r0-r12, lr} ; Make room str r0, [r1, #exec_sp - exec_variables]; Keep context mov r0, #Comm_stack - RAM_image_start ; Base of command stack adrnel lr, command_start ; Start of command code (ROM) ldreq lr, Comm_RAM ; Start of command code (RAM) stmfd r0!, {r0-r12, lr} ; Make room str r0, [r1, #com_sp - exec_variables]; Keep context ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Initialise emulator's I/O @@@ ; (If needed?) +++ ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - cmp r10, #TRUE ; Are we in RAM? moveq pc, #0 ; If so jump to reset `vector' b main_loop ; Off we go! ;------------------------------------------------------------------------------ ; Set up for internal interrupt emulator IO_PIO_base1 DCD PIO_base ; Interrupts_init stmfd sp!, {r0-r1, lr} ; mov r0, #0 ; Start at time 0 str r0, [r7, #Line_time_clk - shared_variables] strb r0, [r7, #interrupts_enable - shared_variables] ; Disable emulator interrupts strb r0, [r7, #timer_compare - shared_variables] ; Here? @@@ strb r0, [r7, #interrupts_active - shared_variables] ldr r14, IO_PIO_base1 ; Literal ldr r0, [r14, #PIO_ISR] ; Clear interrupt ldr r0, [r14, #PIO_PDSR] ; Read pin status mov r1, #0 ; tst r0, #AT91_Virtex_init ; Test ports & translate orreq r1, r1, #Int_L_button ; Active low tst r0, #AT91_Spartan_init ; orreq r1, r1, #Int_R_button ; Active low tst r0, #AT91_Ether_IRQ ; orrne r1, r1, #Int_Ethernet ; Active high tst r0, #AT91_Virtex_IRQ ; orreq r1, r1, #Int_Virtex ; Active low (?) tst r0, #AT91_Spartan_IRQ ; orreq r1, r1, #Int_Spartan ; Active low (?) strb r1, [r7, #Last_PIO_IRQ_state - shared_variables] ; Save pin state ; initialise "interrupts_active" ? ; Deal with Serial receiver etc. (?) @@@ ldmfd sp!, {r0-r1, pc} ; ;------------------------------------------------------------------------------ Breakpoint_init stmfd sp!, {r2-r3, lr} ; mov r3, #breakpoint_table - Mon_RAM_start mov r2, #breakpoint_max ; mov r14, #1 ; "Deleted" code b Brkpt_init_1 ; Brkpt_init_lp strb r14, [r3], #brk_pt_rcd_length Brkpt_init_1 subs r2, r2, #1 ; Loop entry bhs Brkpt_init_lp ; ldmfd sp!, {r2-r3, pc} ; ;------------------------------------------------------------------------------ Watchpoint_init stmfd sp!, {r2-r3, lr} ; mov r3, #watchpoint_table - Mon_RAM_start mov r2, #watchpoint_max ; mov r14, #1 ; "Deleted" code b Wchpt_init_1 ; Wchpt_init_lp strb r14, [r3], #wch_pt_rcd_length Wchpt_init_1 subs r2, r2, #1 ; Loop entry bhs Wchpt_init_lp ; ldmfd sp!, {r2-r3, pc} ; ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; Monitor initial RAM image starts here ; ALIGN Mon_RAM_image_position RAM_image_start ; New label for RAM-ing code Mon_RAM_start b main_loop ; b . ; b . ; b . ; b . ; b . ; ldr pc, [pc, #-&F20] ; Read from IVR b . ; ;------------------------------------------------------------------------------ ; Variable space ;------------------------------------------------------------------------------ Host_buffer_head DCD 0 ; Host_buffer_tail DCD 0 ; Host_buffer_start DCD 0,0,0,0 ; 16 byte serial input buffer Host_buffer_end ; Place to stick variables shared_variables Board_number DCD 0 ; Board serial number Clock_rate DCD 0 ; Processor MCK speed <16>.<16>MHz ; Probably NOT needed @@@ Line_time_clk DCD 0 ; Reserve words arm_state DCB 0 ; Running, stopped etc. arm_state_old DCB 0 ; Previous state when stopped interrupts_active DCB 0 ; Internal interrupt request interrupts_enable DCB 0 ; Internal interrupt enables Last_PIO_IRQ_state DCB 0 ; timer_compare DCB 0 ; Terminal_Rx_last DCB 0 ; Last state read from terminal ALIGN arm_step_count DCD 0 ; Steps remaining - 0 = run arm_instr_count DCD 0 ; Steps since last reset reg_area_ptr DCD 0 ; Pointer to where registers kept mem_area_start DCD 0 ; Start address mem_area_end DCD 0 ; End address + 1 mem_area_pos DCD 0 ; Position ;IO_area_start DCD 0 ; Start address ;IO_area_end DCD 0 ; End address + 1 ; Compact these to one word? @@@ Running_flags DCD 0 ; Enter SWI, BL, etc. break_enable DCB 0 ; Breakpoints should be enabled break_enabled DCB 0 ; Breakpoints are enabled ALIGN ; These are not really shared variables @@@ Feature0_count DCD 0 ; Byte counts for feature download Feature1_count DCD 0 ; Terminal_Rx_head DCD 0 ; Terminal_Rx_tail DCD 0 ; Terminal_Tx_head DCD 0 ; Terminal_Tx_tail DCD 0 ; Terminal_Rx_buff_start % Terminal_Rx_buff_length Terminal_Rx_buff_end Terminal_Tx_buff_start % Terminal_Tx_buff_length Terminal_Tx_buff_end ALIGN ; Another 44 words of register variables reg_block ; Registers kept here ; Register storage offsets Check all numeric copies now purged @@@ DCD 0, 0, 0, 0, 0, 0, 0, 0 ; R0-R7 sim_R8 DCD 0, 0, 0, 0, 0 ; Current R8-R12 sim_R13 DCD 0 ; Current R13 sim_R14 DCD 0 ; Current R14 sim_R8_user DCD 0, 0, 0, 0, 0 ; User R8-R12 sim_R13_user DCD 0, 0 ; User R13-R14 sim_R13_svc DCD 0, 0 ; Supervisor R13-R14 sim_R13_abt DCD 0, 0 ; Abort R13-R14 sim_R13_undef DCD 0, 0 ; Undefined R13-R14 sim_R13_IRQ DCD 0, 0 ; Interrupt R13-R14 sim_R8_FIQ DCD 0, 0, 0, 0, 0 ; Fast Interrupt R8-R12 sim_R13_FIQ DCD 0, 0 ; Fast Interrupt R13-R14 sim_SPSR_svc DCD 0 ; 37 sim_SPSR_abt DCD 0 ; 38 sim_SPSR_undef DCD 0 ; 39 sim_SPSR_irq DCD 0 ; 40 sim_SPSR_fiq DCD 0 ; 41 sim_CPSR DCD 0 ; 42 sim_PC DCD 0 ; 43 exec_variables exec_sp DCD 0 ; Emulator SP background store com_sp DCD 0 ; Interface SP background store run_until_PC DCD 0 ; Variables used for running run_until_SP DCD 0 ; subroutines during stepping run_until_mode DCD 0 ; Byte #0; byte #1 used for old state breakpoint_table ; Repeat record definition "breakpoint_max" times BP_defn BP_active DCB 0 ; DCB 0 ; Padding BP_t DCB 0 ; Type BP_s DCB 0 ; Size BP_aa DCD 0 ; BP_ab DCD 0 ; BP_da DCD 0 ; BP_daH DCD 0 ; BP_db DCD 0 ; BP_dbH DCD 0 ; BP_defn_end brk_pt_rcd_length EQU BP_defn_end - BP_defn % (breakpoint_max - 1) * brk_pt_rcd_length ; Would you believe this as a define space directive?! breakpoint_table_end BP_type EQU BP_t - breakpoint_table BP_addr_A EQU BP_aa - breakpoint_table BP_addr_B EQU BP_ab - breakpoint_table BP_data_A EQU BP_da - breakpoint_table BP_data_B EQU BP_db - breakpoint_table watchpoint_table ; Repeat record definition "watchpoint_max" times WP_defn WP_active DCB 0 ; DCB 0 ; Padding WP_t DCB 0 ; Type WP_s DCB 0 ; Size WP_aa DCD 0 ; WP_ab DCD 0 ; WP_da DCD 0 ; WP_daH DCD 0 ; WP_db DCD 0 ; WP_dbH DCD 0 ; WP_defn_end wch_pt_rcd_length EQU WP_defn_end - WP_defn % (watchpoint_max - 1) * wch_pt_rcd_length ; Would you believe this as a define space directive?! watchpoint_table_end WP_type EQU WP_t - watchpoint_table WP_addr_A EQU WP_aa - watchpoint_table WP_addr_B EQU WP_ab - watchpoint_table WP_data_A EQU WP_da - watchpoint_table WP_data_B EQU WP_db - watchpoint_table Mon_RAM_end % Comm_stack_length * 4 Comm_stack % Exec_stack_length * 4 Exec_stack % Int_stack_length * 4 Int_stack ;------------------------------------------------------------------------------ ; RAM-ed code ;------------------------------------------------------------------------------ main_loop mov r0, #exec_variables - Mon_RAM_start ldr sp, [r0, #exec_sp - exec_variables] ; Swap in execution context ldmfd sp!, {r0-r12, pc} ; No flags or owt @@@ ; Emulator runs one step deschedule_ex stmfd sp!, {r0-r12, lr} ; Save scratch & user SP mov r0, #exec_variables - Mon_RAM_start str sp, [r0, #exec_sp - exec_variables] ; Swap out execution context ldr sp, [r0, #com_sp - exec_variables] ; Swap in command context ldmfd sp!, {r0-r12, pc} ; No flags @@@ ; Monitor processes any pending bytes deschedule_com stmfd sp!, {r0-r12, lr} ; Save scratch & user SP mov r0, #exec_variables - Mon_RAM_start str sp, [r0, #com_sp - exec_variables] ; Swap out command context b main_loop ; Forever! ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ command_start mov r7, #shared_variables - Mon_RAM_start command_loop bl Host_in ; Get command ands r1, r0, #&C0 ; Split on top 2 bits - set flags and r0, r0, #&3F ; Lose `class' bits beq Command_monitor ; 00-3F cmp r1, #&80 ; blo Command_memory ; 40-7F beq Command_run ; 80-BF b Command_reserved ; C0-FF Command_monitor ldrb r1, [pc, r0, lsr #4] ; Divide on bits 5..4 add pc, pc, r1, lsl #2 ; DCB (Monitor_ctrl - %f1) / 4; 00-0F DCB (Monitor_load - %f1) / 4; 10-1F DCB (Monitor_enq - %f1) / 4; 20-2F DCB (Monitor_brk - %f1) / 4; 30-3F 1 ; Label after first word Monitor_ctrl and r0, r0, #&0F ; cmp r0, #(%f11 - %f10) / 2 ; bhs command_loop ; Ignore if out of range mov r0, r0, lsl #1 ; ldrsh r0, [pc, r0] ; add pc, pc, r0 ; 10 DCW Nop - %f1 ; 0 DCW Ping - %f1 ; 1 1 DCW Enq - %b1 ; 2 DCW command_loop - %b1 ; 3 - not defined DCW Reset_proc - %b1 ; 4 11 ALIGN Monitor_load and r1, r0, #&0F ; Remains of command bl Host_in ; Feature number cmp r1, #(%f11 - %f10) / 2 ; bhs command_loop ; Ignore if out of range mov r1, r1, lsl #1 ; ldrsh r1, [pc, r1] ; add pc, pc, r1 ; 10 DCW Dld_get_status - %f1 ; 0 DCW Dld_set_status - %f1 ; 1 1 DCW Send_message - %b1 ; 2 DCW Get_message - %b1 ; 3 DCW Dld_header - %b1 ; 4 DCW Dld_packet - %b1 ; 5 11 ALIGN Monitor_enq and r0, r0, #&0F ; cmp r0, #(%f11 - %f10) / 2 ; bhs command_loop ; Ignore if out of range mov r0, r0, lsl #1 ; ldrsh r0, [pc, r0] ; add pc, pc, r0 ; 10 DCW Proc_status - %f1 ; 0 DCW Proc_stop - %f1 ; 1 1 DCW Proc_pause - %b1 ; 2 DCW Proc_continue - %b1 ; 3 DCW Proc_set_flags - %b1 ; 4 DCW Proc_get_flags - %b1 ; 5 11 Monitor_brk and r1, r0, #&0D ; Remains of command tst r1, #&04 ; Only brk or wch so far @@@ ; Initialise with appropriate tables moveq r3, #breakpoint_table - Mon_RAM_start movne r3, #watchpoint_table - Mon_RAM_start moveq r6, #breakpoint_max ; Spare regisiter movne r6, #watchpoint_max ; Spare regisiter moveq r4, #brk_pt_rcd_length ; Number of bytes in definition movne r4, #wch_pt_rcd_length ; Number of bytes in definition tst r0, #&02 ; Definition or activation? bne Monitor_brk_act ; bl Host_in ; Breakpoint number mov r2, r0 ; mla r3, r4, r2, r3 ; Find definition start sub r4, r4, #2 ; Bodge for word alignment @@@ add r3, r3, #2 ; (Shouldn't really be a byte stream) @@@ tst r1, #&01 ; 0 = write, 1 = read bne Monitor_brk_rd ; ldrb r0, [r3, #-2] ; Check existing state cmp r0, #1 ; "Deleted"? moveq r0, #3 ; Activate if so streqb r0, [r3, #-2] ; Set breakpoint state Monitor_brk_wr bl Host_in ; Get byte cmp r2, r6 ; # < maximum? strlob r0, [r3], #1 ; Save if it is subs r4, r4, #1 ; bhi Monitor_brk_wr ; b command_loop ; Monitor_brk_rd cmp r2, r6 ; # < maximum? ldrlob r0, [r3], #1 ; Get if it is movhs r0, #0 ; Zero if it isn't bl Host_out ; Send byte subs r4, r4, #1 ; bhi Monitor_brk_rd ; b command_loop ; Monitor_brk_act tst r1, #&01 ; 0 = write, 1 = read bne Monitor_brk_stat ; bl Host_get_word ; Write activation bits mov r5, r0 ; bl Host_get_word ; mov r2, r0 ; tst r6, r6 ; (breakpoint_max) beq command_loop ; If no breakpoints are included Monitor_brk_wr1 mov r0, #0 ; movs r5, r5, lsr #1 ; adc r0, r0, r0 ; movs r2, r2, lsr #1 ; adcs r0, r0, r0 ; ldrneb r1, [r3] ; tstne r1, #2 ; Test if defined strneb r0, [r3] ; Store if (still) non-zero add r3, r3, r4 ; Always move on subs r6, r6, #1 ; bhi Monitor_brk_wr1 ; b command_loop ; ; Splits flags into 2 words; returns 00 for unimplemented breakpoints Monitor_brk_stat subs r2, r6, #1 ; Offset of last entry bmi Monitor_brk_stat_out ; No breakpoints allowed mul r6, r2, r4 ; Offset to last entry mov r0, #0 ; Initialise accumulators mov r2, #0 ; Monitor_brk_stat_loop ldrb r5, [r3, r6] ; (a.k.a. "BP_active") tst r5, r5, lsr #2 ; Bit 1 into carry adc r0, r0, r0 ; Accumulate tst r5, r5, lsr #1 ; Bit 0 into carry adc r2, r2, r2 ; Accumulate subs r6, r6, r4 ; bcs Monitor_brk_stat_loop ; Continue (?) Monitor_brk_stat_out bl Host_put_word ; mov r0, r2 ; bl Host_put_word ; b command_loop ; ; @@@ adr lr,.. and jump (??) ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Command_memory mov r1, r0 ; Command bl Host_get_word ; mov r2, r0 ; Address bl Host_get_halfword ; movs r3, r0 ; Length beq command_loop ; Length = 0 ands r0, r1, #&30 ; Address space beq Memory_IO ; cmp r0, #&20 ; bhs command_loop ; Unimplemented space ; else registers tst r1, #&08 ; Direction bit bne Get_reg ; Read b Put_reg ; Write Memory_IO tst r1, #&08 ; Direction bit bne Get_mem ; Read b Put_mem ; Write Command_run and r1, r0, #&3F ; Command vector ; Does anything want inverting? @@@ strb r1, [r7, #Running_flags - shared_variables] ; Save flags for BL, SWI service, breakpoints etc. tst r1, #Run_B_bit ; Breakpoints moveq r0, #FALSE ; Disable breakpoints movne r0, #TRUE ; Enable breakpoints strb r0, [r7, #break_enable - shared_variables] ; The following allows single step sequences to detect breakpoints tst r1, #Run_BB_bit ; Breakpoint on first instr. too moveq r0, #FALSE ; Disable breakpoints movne r0, #TRUE ; Enable breakpoints strb r0, [r7, #break_enabled - shared_variables] bl Host_get_word ; Number of steps str r0, [r7, #arm_step_count - shared_variables] ; Set step count cmp r0, #0 ; 0 steps => "run" moveq r0, #State_running ; Set status to run movne r0, #State_stepping ; Set status to step strb r0, [r7, #arm_state - shared_variables] ; Signal to processor ; Extra entry point here save a word Command_reserved ; Do nothing for now Nop b command_loop ; ; No operation - can be used for padding to resync. ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Resychronise Ping ldr r0, Ping_answer ; adr lr, command_loop ; b Host_put_word ; Ping_answer DCB "OK00" ; One word ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Provide board information Enq adr r1, enq_message ; ldrh r2, [r1], #2 ; Length of `auxiliary' message mov r0, #1 ; Number of memory segments @@@ ; As map is static could shrink this code @@@ mov r0, r0, lsl #3 ; Each memory segment is 8 bytes add r0, r0, #1 ; One more for the number add r0, r0, r2 ; Total number of bytes in message bl Host_put_halfword ; Message length in bytes Enq1 ldrb r0, [r1], #1 ; Send message bl Host_out ; subs r2, r2, #1 ; bne Enq1 ; mov r0, #1 ; Memory segments @@@ bl Host_out ; ldr r1, [r7, #mem_area_start - shared_variables] mov r0, r1 ; Memory start address bl Host_put_word ; ldr r2, [r7, #mem_area_end - shared_variables] sub r0, r2, r1 ; Memory segment length bl Host_put_word ; ; any more? @@@ b command_loop ; enq_message DCW %f3-%f1 ; Length of this part 1 DCB 1, 0, 0 ; ARM type, subtype DCB (%f3-%f2)/3 ; Additional features 2 DCB &11, 1, 10 ; Spartan 10 PQ100 IF Virtex_E DCB &13, 2, 30 ; Virtex 300E PQ240 ELSE DCB &12, 2, 30 ; Virtex 300 PQ240 ENDIF ; Should check for presence really @@@ DCB &00, 8, 0 ; Terminal = #2 ; See "terminal_feature" @@@ 3 ALIGN ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Soft reset request Reset_proc mov r0, #State_to_reset ; Reset state request strb r0, [r7, #arm_state - shared_variables] ; Signal to processor b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Feature number in R0 Dld_get_status mov r0, #0 ; @@@ bl Host_put_word ; b command_loop ; Dld_set_status bl Host_get_word ; @@@ b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; This buffer post-increments head and tail. ; Pointers become equal when buffer is empty. Terminal_init stmfd sp!, {r0, lr} ; mov r14, #Terminal_Rx_head - Mon_RAM_start ; Crude hack method for now @@@ mov r0, #Terminal_Rx_buff_start - Mon_RAM_start str r0, [r14] ; Initialise Rx head str r0, [r14, #Terminal_Rx_tail - Terminal_Rx_head] ; Initialise Rx tail mov r0, #Terminal_Tx_buff_start - Mon_RAM_start str r0, [r14, #Terminal_Tx_head - Terminal_Rx_head] ; Initialise Tx head str r0, [r14, #Terminal_Tx_tail - Terminal_Rx_head] ; Initialise Tx tail ldmfd sp!, {r0, pc} ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; This means we -receive- Send_message mov r1, r0 ; Feature number bl Host_in ; Length of message mov r3, #0 ; Count of bytes accepted movs r2, r0 ; Can send 0 bytes to wrong feature beq Send_message3 ; Always answers 00 ldr r4, [r7, #Terminal_Rx_head - shared_variables] ldr r5, [r7, #Terminal_Rx_tail - shared_variables] Send_message1 bl Host_in ; Get byte cmp r1, #Terminal_feature ; Should we bother? bne Send_message2 ; no - discard add r14, r4, #1 ; Look ahead cmp r14, #Terminal_Rx_buff_end - Mon_RAM_start movhs r14, #Terminal_Rx_buff_start - Mon_RAM_start cmp r14, r5 ; Caught tail pointer? strneb r0, [r4] ; Place in byte buffer if not movne r4, r14 ; update pointer addne r3, r3, #1 ; and add to acknowledgement Send_message2 subs r2, r2, #1 ; Count byte in whatever bhi Send_message1 ; ldrb r0, [r7, #interrupts_active - shared_variables] orr r0, r0, #Int_Rx_ready ; Set interrupt bit ; Next two instructions atomic strb r0, [r7, #interrupts_active - shared_variables] str r4, [r7, #Terminal_Rx_head - shared_variables] Send_message3 mov r0, r3 ; Ack with No. of bytes accepted bl Host_out ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; This means we -send- Get_message mov r1, r0 ; Feature number bl Host_in ; Max. length of message cmp r1, #Terminal_feature ; movne r0, #0 ; Zero if not talking to terminal ldr r1, [r7, #Terminal_Tx_head - shared_variables] ldr r2, [r7, #Terminal_Tx_tail - shared_variables] subs r1, r1, r2 ; Find occupancy addmi r1, r1, #Terminal_Tx_buff_end - Terminal_Tx_buff_start cmp r0, r1 ; Requested length > currently available? movhi r0, r1 ; Get lesser length bl Host_out ; Send transmission length movs r3, r0 ; beq command_loop ; Quit if nothing to send Get_message1 ldrb r0, [r2], #1 ; Get byte, post increment bl Host_out ; and send cmp r2, #Terminal_Tx_buff_end - Mon_RAM_start movhs r2, #Terminal_Tx_buff_start - Mon_RAM_start subs r3, r3, #1 ; bhi Get_message1 ; ldrb r0, [r7, #interrupts_active - shared_variables] orr r0, r0, #Int_Tx_ready ; Set interrupt bit ; Next two instructions atomic strb r0, [r7, #interrupts_active - shared_variables] str r2, [r7, #Terminal_Tx_tail - shared_variables] b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dld_header mov r1, r0 ; Feature number bl Host_get_word ; Length of file to follow add r2, r7, #Feature0_count - shared_variables str r0, [r2, r1, lsl #2] ; Save download count cmp r1, #1 ; No jump table - (compressed) blo Spartan_header ; 0 beq Virtex_header ; 1 b Dld_no_such_feature ; >= 2 Dld_packet mov r1, r0 ; Feature number bl Host_in ; Length of block to follow movs r2, r0 ; Convert 0 to 256 moveq r2, #&100 ; Length now in R2 add r3, r7, #Feature0_count - shared_variables add r3, r3, r1, lsl #2 ; Point at global counter cmp r1, #1 ; Feature number blo Spartan_packet ; 0 beq Virtex_packet ; 1 ; >= 2 fall into ... ; The following deals with undefined features Dld_no_such_feature_pkt bl Host_in ; Waste characters subs r2, r2, #1 ; bhi Dld_no_such_feature_pkt ; Dld_no_such_feature mov r0, #"N" ; Indicate failure b FPGA_pkt_outN ; ;------------------------------------------------------------------------------ ; These ought to know about the presence/absence of the devices @@@ ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Corrupts R0, R1 Spartan_header ldr r0, IO_PIO_base ; ; Force other PIO bits to correct state before resetting Spartan mov r1, #AT91_Spartan_CS1 ; str r1, [r0, #PIO_SODR] ; Output high (always PIO output) mov r1, #(AT91_Spartan_HDC :OR: AT91_FPGA_baud) str r1, [r0, #PIO_ODR] ; Output disabled str r1, [r0, #PIO_PER] ; Ensure PIO is used ; Sensitive signals floated mov r1, #AT91_Spartan_prog ; Reset Spartan str r1, [r0, #PIO_CODR] ; Programme pin low mov r2, #8 ; Delay iterations spartan_hdr0 subs r2, r2, #1 ; Leave pin low for a while bhi spartan_hdr0 ; (>300ns) str r1, [r0, #PIO_SODR] ; Programme pin high again spartan_hdr1 ldr r1, [r0, #PIO_PDSR] ; PIO pin state tst r1, #AT91_Spartan_init ; beq spartan_hdr1 ; Wait for Spartan to be ready ; Want >5us delay before downloading. ; Assumed to hide under serial comms. ; Possibilities of failure? @@@ b FPGA_pkt_out ; ; mov r0, #"A" ; ; bl Host_out ; Signal success ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Virtex_header ldr r0, IO_PIO_base ; ; Force other PIO bits to correct state before resetting Virtex mov r1, #AT91_Virtex_prog ; Reset Virtex str r1, [r0, #PIO_CODR] ; Programme pin low mov r2, #8 ; Delay iterations virtex_hdr0 subs r2, r2, #1 ; Leave pin low for a while bhi virtex_hdr0 ; str r1, [r0, #PIO_SODR] ; Programme pin high again ldr r2, Lit_EBI_base ; Set bus speed to no wait states ldr r1, Lit_EBI_fast_Virtex ; str r1, [r2, #EBI_CSR2] ; virtex_hdr1 ldr r1, [r0, #PIO_PDSR] ; PIO pin state tst r1, #AT91_Virtex_init ; beq virtex_hdr1 ; Wait for Virtex to be ready ; Possibilities of failure? @@@ b FPGA_pkt_out ; ; mov r0, #"A" ; ; bl Host_out ; Signal success ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lit_EBI_base DCD EBI_base Lit_EBI_fast_Virtex DCD (VIRTEX_base :AND: &FFF00000) :OR: CSEN :OR: BAT :OR: TDF1 :OR: Pg64M :OR: DBW8 Lit_EBI_slow_Virtex DCD (VIRTEX_base :AND: &FFF00000) :OR: CSEN :OR: BAT :OR: TDF1 :OR: Pg64M :OR: NWS2 :OR: DBW16 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; R2 is local count (1..256) R3 points to global counter ; Corrupts R0, R1, R4, R5, R6, R14 Virtex_packet mov r1, #VIRTEX_base ; Virtex address mov r5, #TRUE ; Virtex ID IF Virtex_E mov r6, #FALSE ; Don't reverse bytes when loading ELSE mov r6, #TRUE ; Reverse bytes before loading ENDIF b Spartan_pkt0 ; The rest is common Spartan_packet mov r5, #FALSE ; NOT Virtex ID mov r6, #TRUE ; Reverse bytes before loading mov r1, #SPARTAN_base ; Spartan address Spartan_pkt0 ldr r4, [r3] ; cmp r2, r4 ; bhi Dld_no_such_feature_pkt ; Waste characters and "Nack" Spartan_pkt1 bl Host_in ; Get byte cmp r6, #TRUE ; Should we flip byte? bleq reverse_byte ; Correct for Xilinx No. scheme strb r0, [r1] ; Programme FPGA sub r4, r4, #1 ; Global count subs r2, r2, #1 ; Local count bhi Spartan_pkt1 ; str r4, [r3] ; cmp r4, #0 ; Finished whole device? ; bne FPGA_pkt_out ; no ; cmp r5, #TRUE ; Am I the Virtex device? cmpeq r5, #TRUE ; or am I the Virtex device? bne FPGA_pkt_out ; no ; Now finishing off Virtex ... ldr r2, Lit_EBI_base ; Set bus speed to some wait states ldr r1, Lit_EBI_slow_Virtex ; (currently two) str r1, [r2, #EBI_CSR2] ; FPGA_pkt_out mov r0, #"A" ; Signal success FPGA_pkt_outN bl Host_out ; Entry point if failure b command_loop ; ;------------------------------------------------------------------------------ ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Stop emulation Proc_pause ; Suspend operation (same as "stop" ??) Proc_stop ldrb r0, [r7, #arm_state - shared_variables] tst r0, #&80 ; Moving already? beq command_loop ; No - ignore strb r0, [r7, #arm_state_old - shared_variables] mov r0, #State_stopped ; Set status to stop strb r0, [r7, #arm_state - shared_variables] ; Signal to processor b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Continue as before stopped - same breakpoint (etc.) enables, same number of steps ; Step if was stepping, run if was running Proc_continue ldrb r0, [r7, #arm_state - shared_variables] and r2, r0, #&C0 ; Reset, stopped, running, stepping? cmp r2, #&40 ; In a "stopped" state? bne command_loop ; No - do nothing cmp r0, #State_count_out ; States from which there is no cmpne r0, #State_stop_req ; sensible way to `continue' beq command_loop ; ldrb r0, [r7, #arm_state_old - shared_variables] and r2, r0, #&C0 ; cmp r2, #&C0 ; Was stepping? ldreq r2, [r7, #arm_step_count - shared_variables] tsteq r2, r2 ; Steps = 0, regardless? strneb r0, [r7, #arm_state - shared_variables] ; New state if wasn't stepping OR step_count non-zero b command_loop ; Proc_set_flags ldr r1, [r7, #Running_flags - shared_variables] bic r1, r1, #Run_I_bit :OR: Run_F_bit bl Host_in ; tst r0, #&02 ; orrne r1, r1, #Run_I_bit ; tst r0, #&01 ; orrne r1, r1, #Run_F_bit ; str r1, [r7, #Running_flags - shared_variables] b command_loop ; Proc_get_flags mov r0, #0 ; ldr r1, [r7, #Running_flags - shared_variables] tst r1, #Run_I_bit ; orrne r0, r0, #&02 ; tst r1, #Run_F_bit ; orrne r0, r0, #&01 ; bl Host_out ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; What is emulator doing now? Proc_status ldrb r1, [r7, #arm_state - shared_variables] mov r0, r1, lsr #6 ; Basic status class adr r2, status_table ; add r2, r2, r0, lsl #2 ; 4 byte entries and r1, r1, #&3F ; ldrb r0, [r2, #1] ; Sub-table size cmp r1, r0 ; Entry in subtable? ldrhsb r0, [r2] ; no - get default bhs Proc_status1 ; ldrb r0, [r2, #2] ; 2 add r0, pc, r0 ; R0 is offset from here to sub_table ldrb r0, [r0, r1] ; Proc_status1 bl Host_out ; Output host status ldr r0, [r7, #arm_step_count - shared_variables] bl Host_put_word ; Number of steps remaining ldr r0, [r7, #arm_instr_count - shared_variables] bl Host_put_word ; Instructions since reset b command_loop ; status_table DCB &00, 0, 0, 0 ; Reset states DCB &40, %f21-%f20 ; Reset states DCB %f20-(%b2+8), 0 ; DCB &80, %f31-%f30 ; Running states DCB %f30-(%b2+8), 0 ; DCB &80, 0, 0, 0 ; Stepping states ; Cross reference #A# 20 DCB &40 ; State_stopped DCB &44 ; State_stop_req DCB &41 ; State_stop_bkpt DCB &40 ; State_count_out 21 30 DCB &80 ; State_running DCB &80 ; State_running_BL DCB &81 ; State_running_SWI DCB &80 ; State_running_IRQ DCB &80 ; State_running_FIQ DCB &80 ; State_running_abt 31 ALIGN ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Get_reg and r5, r1, #7 ; Size mov r4, r2 ; Save address Get_reg1 mov r1, r4 ; Register address bl G_reg ; Translate & read adr lr, Get_reg2 ; Save return address mov r1, #0 ; In case of double ... ; r5 not range checked @@@ ldr r2, [pc, r5, lsl #2] ; R5 (size) must be preserved add pc, pc, r2 ; R2 already trashed by G_reg DCD Host_out - %f1 ; 1 DCD Host_put_halfword - %b1 ; DCD Host_put_word - %b1 ; DCD Host_put_double - %b1 ; Get_reg2 add r4, r4, #1 ; Next register subs r3, r3, #1 ; bne Get_reg1 ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Register addresses are bytes (quite wrongly!) Put_reg and r5, r1, #7 ; Size mov r4, r2 ; Save address Put_reg1 adr lr, Put_reg2 ; Save return address ; r5 not range checked @@@ ldr r2, [pc, r5, lsl #2] ; Preserve R5 (size) add pc, pc, r2 ; R2 scratched in P_reg DCD Host_in - %f1 ; 1 DCD Host_get_halfword - %b1 ; DCD Host_get_word - %b1 ; DCD Host_get_double - %b1 ; Put_reg2 mov r1, r4 ; bl P_reg ; Write back add r4, r4, #1 ; subs r3, r3, #1 ; bne Put_reg1 ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Get_mem and r1, r1, #7 ; cmp r1, #(%f11 - %f10) ; Only four defined sizes bhs command_loop ; ldrb r0, [pc, r1] ; Load byte offset add pc, pc, r0, lsl #2 ; Multiply offset by four 10 DCB (Get_mem_b - %f1) / 4 ; 0 DCB (Get_mem_h - %f1) / 4 ; 1 DCB (Get_mem_w - %f1) / 4 ; 2 DCB (Get_mem_d - %f1) / 4 ; 3 1 11 ALIGN Get_mem_b bl read_memory_b ; Read byte mov r0, r1 ; add r2, r2, #1 ; Increment address bl Host_out ; Output byte subs r3, r3, #1 ; Decrement count bne Get_mem_b ; and repeat as required b command_loop ; Get_mem_h bl read_memory_h ; mov r0, r1 ; add r2, r2, #2 ; bl Host_put_halfword ; subs r3, r3, #1 ; bne Get_mem_h ; b command_loop ; Get_mem_w bl read_memory_w ; mov r0, r1 ; add r2, r2, #4 ; bl Host_put_word ; subs r3, r3, #1 ; bne Get_mem_w ; b command_loop ; Get_mem_d bl read_memory_d ; R0:R1 := [R2] mov r14, r0 ; R14 is scratch here mov r0, r1 ; Swap R1, R0 mov r1, r14 ; add r2, r2, #8 ; bl Host_put_double ; subs r3, r3, #1 ; bne Get_mem_d ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Put_mem and r1, r1, #7 ; cmp r1, #(%f11 - %f10) ; Only four defined sizes bhs command_loop ; ;Proposed replacement - shorter by 7 words ... **NEW CODE** @@@ ; adr r14, Put_mem_table ; ; add r14, r14, r1, lsl #2 ; Could shrink table (?) ; ldrsh r5, [r14] ; Offset to serial input routine ; ldrsh r6, [r14, #4] ; Offset to memory output routine ;3 add r5, pc, r5 ; Address of serial input routine ;4 add r6, pc, r6 ; Address of memory output routine ; mov r4, #1 ; ; mov r4, r4, lsr r1 ; Transfer size (address increment) ; ;Put_mem_all adr lr, %f1 ; ; bx r5 ; Get element ;1 adr lr, %f2 ; ; bx r6 ; Store element ;2 add r2, r2, r4 ; Step address ; subs r3, r3, #1 ; ; bne Put_mem_all ; ; ; b command_loop ; ; ;Put_mem_table DCW Host_in - %b3 - 8, write_memory_b - %b4 - 8 ; DCW Host_get_halfword - %b3 - 8, write_memory_h - %b4 - 8 ; DCW Host_get_word - %b3 - 8, write_memory_w - %b4 - 8 ; DCW Host_get_double - %b3 - 8, write_memory_d - %b4 - 8 ; ; ALIGN ldrb r0, [pc, r1] ; Load byte offset add pc, pc, r0, lsl #2 ; Multiply offset by four 10 DCB (Put_mem_b - %f1) / 4 ; 0 DCB (Put_mem_h - %f1) / 4 ; 1 DCB (Put_mem_w - %f1) / 4 ; 2 DCB (Put_mem_d - %f1) / 4 ; 3 1 11 ALIGN Put_mem_b bl Host_in ; bl write_memory_b ; add r2, r2, #1 ; subs r3, r3, #1 ; bne Put_mem_b ; b command_loop ; Put_mem_h bl Host_get_halfword ; bl write_memory_h ; add r2, r2, #2 ; subs r3, r3, #1 ; bne Put_mem_h ; b command_loop ; Put_mem_w bl Host_get_word ; bl write_memory_w ; add r2, r2, #4 ; subs r3, r3, #1 ; bne Put_mem_w ; b command_loop ; Put_mem_d bl Host_get_double ; bl write_memory_d ; add r2, r2, #8 ; subs r3, r3, #1 ; bne Put_mem_d ; b command_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Pass register ID in R1, return value in R0 ; Corrupts R2 G_reg cmp r1, #&D1 ; Decodes to SPSR_fiq movhi r0, #0 ; Register out of range movhi pc, lr ; so return zero and r2, r1, #&1F ; Register number only cmp r2, #&11 ; Above legal range? movhi r0, #0 ; If so return zero movhi pc, lr ; and return stmfd sp!, {r4-r5, r9, lr} ; Else do some work bl Find_reg ; ldr r0, [r9, r2, lsl #2] ; Get register ldmfd sp!, {r4-r5, r9, pc} ; and return P_reg cmp r1, #&D1 ; Decodes to SPSR_fiq movhi pc, lr ; So just return and r2, r1, #&1F ; Register number only cmp r2, #&11 ; Above legal range? movhi pc, lr ; Just return cmp r2, #&10 ; CPSR beq P_reg_CPSR ; stmfd sp!, {r4-r5, r9, lr} ; Else do some work bl Find_reg ; str r0, [r9, r2, lsl #2] ; Put register ldmfd sp!, {r4-r5, r9, pc} ; and return P_reg_CPSR stmfd sp!, {r0-r6, r9, lr} ; ldr r9, [r7, #reg_area_ptr - shared_variables] ; Find register block and r1, r0, #&F ; New mode ldr r3, [r9, #sim_CPSR - reg_block] ; Old CPSR str r0, [r9, #sim_CPSR - reg_block] ; Save new CPSR and r0, r3, #&F ; Old mode bl mode_reg_swap ; Corrupts R0-R6 ldmfd sp!, {r0-r6, r9, pc} ; ; Pass (legal) register ID in R1 and (R1 AND 1F) in R2 ; Return offset (/4) in R2, area in R9 ; Corrupts: R4-R5 ; Register ID is 3 bits mode {current, user/sys, svc, abt, undef, IRQ, FIQ, ???} ; and 5 bits register address (10=CPSR, 11=SPSR, 12+ not defined) Find_reg ldr r9, [r7, #reg_area_ptr - shared_variables] ; Find register block ; and r2, r1, #&1F ; Register number only cmp r2, #&08 ; R0..R7? movlo pc, lr ; return cmp r2, #15 ; PC? moveq r2, #(sim_PC - reg_block)/4 ; PC offset moveq pc, lr ; cmp r2, #&10 ; CPSR? moveq r2, #(sim_CPSR - reg_block)/4 ; CPSR offset moveq pc, lr ; ; Unbanked registers now dealt with ldr r5, [r9, #sim_CPSR - reg_block] ; Load CPSR to R5 and r5, r5, #&F ; Mask out mode (32-bit only) adr r4, mode_tab ; Translation table ldrb r5, [r4, r5] ; Get `internal' mode encoding cmp r2, #&11 ; SPSR? beq Find_reg_SPSR ; movs r4, r1, lsr #5 ; Mode as passed in (0=current) cmpne r5, r4 ; Specified mode is current anyway? moveq pc, lr ; Then just return cmp r2, #13 ; Now in a background reg. set bhs Find_reg1 ; R13, R14 cmp r5, #6 ; Running in FIQ mode? addeq r2, r2, #(sim_R8_user - sim_R8)/4; Yes - use user offset moveq pc, lr ; and return (modes can't match) cmp r4, #6 ; Asked for FIQ mode? addeq r2, r2, #(sim_R8_FIQ - sim_R8)/4; Yes, use FIQ offset mov pc, lr ; else default to current bank Find_reg1 adr r5, R13_offset_table ; Banked R13, R14 ldrb r5, [r5, r4] ; Get extra R13 offset for mode add r2, r2, r5 ; add offset to register number mov pc, lr ; and return Find_reg_SPSR tst r1, #&E0 ; Requested specific mode? movne r5, r1, lsr #5 ; Overridden by request adr r4, SPSR_offset_table ; SPSR positions ldrb r2, [r4, r5] ; Get SPSR offset for mode mov pc, lr ; and return ; Translation table to internal mode numbers (0=current) mode_tab DCB 1, 6, 5, 2 ; User, FIQ, IRQ, Supervisor DCB 0, 0, 0, 3 ; 3* Not defined, Abort DCB 0, 0, 0, 4 ; 3* Not defined, Undefined DCB 0, 0, 0, 1 ; 3* Not defined, System R13_offset_table ; For finding SP, LR for particular mode DCB 0 DCB (sim_R13_user - sim_R13) / 4 DCB (sim_R13_svc - sim_R13) / 4 DCB (sim_R13_abt - sim_R13) / 4 DCB (sim_R13_undef - sim_R13) / 4 DCB (sim_R13_IRQ - sim_R13) / 4 DCB (sim_R13_FIQ - sim_R13) / 4 SPSR_offset_table ; For finding SPSR for particular mode DCB 0 DCB (sim_CPSR - reg_block)/4 DCB (sim_SPSR_svc - reg_block)/4 DCB (sim_SPSR_abt - reg_block)/4 DCB (sim_SPSR_undef - reg_block)/4 DCB (sim_SPSR_irq - reg_block)/4 DCB (sim_SPSR_fiq - reg_block)/4 ALIGN ;------------------------------------------------------------------------------ ; Address in R2, data returned in R0:R1 (R0 is more significant!) read_memory_d ldr r1, [r7, #mem_area_start - shared_variables]; cmp r2, r1 ; blo read_memory_IO_D ; Address too low ldr r1, [r7, #mem_area_end - shared_variables] cmp r2, r1 ; bhs read_memory_IO_D ; Address too high ldr r1, [r7, #mem_area_pos - shared_variables] add r0, r1, #4 ; Address in range ldr r1, [r2, r1] ; ldr r0, [r2, r0] ; Next word mov pc, lr ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data returned in R1 read_memory_w ldr r1, [r7, #mem_area_start - shared_variables]; cmp r2, r1 ; blo read_memory_IO_W ; Address too low ldr r1, [r7, #mem_area_end - shared_variables] cmp r2, r1 ; bhs read_memory_IO_W ; Address too high ldr r1, [r7, #mem_area_pos - shared_variables] ldr r1, [r2, r1] ; Address in range mov pc, lr ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data returned in R1 read_memory_h ldr r1, [r7, #mem_area_start - shared_variables]; cmp r2, r1 ; blo read_memory_h1 ; Address too low ldr r1, [r7, #mem_area_end - shared_variables] cmp r2, r1 ; bhs read_memory_h1 ; Address too high ldr r1, [r7, #mem_area_pos - shared_variables] ldrh r1, [r2, r1] ; Address in range mov pc, lr ; read_memory_h1 and r1, r2, #&F0000000 ; Bodgery! @@ cmp r1, #Virtex_page ; Virtex? bne read_memory_IO_H ; Not Virtex bic r1, r2, #&F0000000 ; Find page offset orr r1, r1, #VIRTEX_base ; Find true address ldrb r1, [r1] ; mov pc, lr ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data returned in R1 read_memory_b ldr r1, [r7, #mem_area_start - shared_variables]; cmp r2, r1 ; blo read_memory_b1 ; Address too low ldr r1, [r7, #mem_area_end - shared_variables] cmp r2, r1 ; bhs read_memory_b1 ; Address too high ldr r1, [r7, #mem_area_pos - shared_variables] ldrb r1, [r2, r1] ; Address in range mov pc, lr ; read_memory_b1 and r1, r2, #&F0000000 ; Bodgery! @@ cmp r1, #Spartan_page ; Spartan? bne read_memory_IO_B ; Not Spartan bic r1, r2, #&F0000000 ; Find page offset orr r1, r1, #SPARTAN_base ; Find true address ldrb r1, [r1] ; mov pc, lr ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - read_memory_IO_D stmfd sp!, {r2,lr} ; Fetch word (& align) bl read_memory_IO_W ; Fetch double word stmfd sp!, {r1} ; Save low word add r2, r2, #4 ; bl read_memory_IO_W ; Fetch double word mov r0, r1 ; High word ldmfd sp!, {r1,r2,pc} ; Restore all and return read_memory_IO_W stmfd sp!, {r0,r2,lr} ; Fetch word (& align) and r0, r2, #3 ; Byte position bic r2, r2, #3 ; read_memory_IO_W_1 bl read_memory_IO_B ; Get byte mov r0, r0, lsl #3 ; 8 * byte position mov r1, r1, ror r0 ; Fix up word ldmfd sp!, {r0,r2,pc} ; Restore and return read_memory_IO_H stmfd sp!, {r0,r2,lr} ; Fetch halfword and r0, r2, #1 ; Byte position bic r2, r2, #1 ; b read_memory_IO_W_1 ; Same from here read_memory_IO_B cmp r2, #IO_area_start ; Get I/O byte (byte #0s only) blo read_memory_abort ; Address too low cmp r2, #IO_area_end ; bhs read_memory_abort ; Address too high tst r2, #3 ; movne r1, #0 ; Return 0 unless byte #0 movne pc, lr ; stmfd sp!, {r0, lr} ; and r0, r2, #&3C ; Partial decode ldrb r0, [pc, r0, lsr #2] ; Get offset add pc, pc, r0, lsl #2 ; Multiply `offset' by 4 ; Relocatable jump table to different devices DCB (Rd_IO_portA - %f1) / 4 ; 00 DCB (Rd_IO_portB - %f1) / 4 ; 04 DCB (Rd_IO_timer - %f1) / 4 ; 08 DCB (Rd_IO_timer_cmp-%f1)/4 ; 0C 1 ; One word into table DCB (Rd_IO_RxD - %b1) / 4 ; 10 DCB (Rd_IO_serial_status - %b1) / 4 ; 14 DCB (Rd_IO_irq - %b1) / 4 ; 18 DCB (Rd_IO_ien - %b1) / 4 ; 1C DCB (Rd_serial_No- %b1) / 4 ; 20 (R2 still holds addr.) DCB (Rd_serial_No- %b1) / 4 ; 24 DCB (Rd_serial_No- %b1) / 4 ; 28 DCB (Rd_serial_No- %b1) / 4 ; 2C DCB (Rd_IO_none - %b1) / 4 ; 30 Reserved DCB (Rd_IO_none - %b1) / 4 ; 34 DCB (Rd_IO_none - %b1) / 4 ; 38 DCB (Rd_IO_none - %b1) / 4 ; 3C read_memory_abort and r1, r2, #&F0000000 ; Hack to read Ethernet chip cmp r1, #ETHERNET_base movne r1, #0 ; "Abort", for now @@@ ldreqh r1, [r2] ; ; mov r1, #0 ; "Abort", for now @@@ mov pc, lr ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Input ports Rd_IO_timer mov r0, #Line_time_clk - Mon_RAM_start Rd_IO_out ldrb r1, [r0] ; General byte-read exit point ldmfd sp!, {r0, pc} ; used to save space ; Read the bottom eight AT91 PIO lines Rd_IO_portA ldr r0, IO_PIO_base ; ldr r0, [r0, #PIO_PDSR] ; Pin state and r1, r0, #&FF ; Eight bits only ldmfd sp!, {r0, pc} ; ; Read another selection of AT91 PIO lines Rd_IO_portB stmfd sp!, {r3} ; Need more space ldr r0, IO_PIO_base ; ldr r0, [r0, #PIO_PDSR] ; Pin state ; ldr r1, IO_PIO_portB ; ; and r0, r0, r1 ; Relevant bits only ldr r1, IO_PIO_portB_inv ; eor r0, r0, r1 ; Invert some bits adr r14, IO_PIO_portB_tab ; Bit positions mov r1, #&01000000 ; Counter & Accumulator Rd_IO_portB_lp ldrb r3, [r14], #1 ; Find next bit position add r3, r3, #1 ; Shift down to carry tst r0, r0, lsr r3 ; Set carry to bit value adcs r1, r1, r1 ; Shift into accumulator bcc Rd_IO_portB_lp ; Carry set when counted out ldmfd sp!, {r3} ; ldmfd sp!, {r0, pc} ; Rd_IO_RxD ldr r14, [r7, #Terminal_Rx_tail - shared_variables] ldr r0, [r7, #Terminal_Rx_head - shared_variables] cmp r14, r0 ; Buffer empty? ldreqb r1, [r7, #Terminal_Rx_last - shared_variables] ldmeqfd sp!, {r0, pc} ; Duff read - get out ldrb r1, [r14], #1 ; Else get from buffer cmp r14, #Terminal_Rx_buff_end - Mon_RAM_start movhs r14, #Terminal_Rx_buff_start - Mon_RAM_start str r14, [r7, #Terminal_Rx_tail - shared_variables] strb r1, [r7, #Terminal_Rx_last - shared_variables] cmp r14, r0 ; Buffer now empty? ldreqb r0, [r7, #interrupts_active - shared_variables] biceq r0, r0, #Int_Rx_ready ; Clear interrupt bit streqb r0, [r7, #interrupts_active - shared_variables] ldmfd sp!, {r0, pc} ; Rd_IO_serial_status ldr r14, [r7, #Terminal_Rx_tail - shared_variables] ldr r0, [r7, #Terminal_Rx_head - shared_variables] cmp r14, r0 ; Buffer empty? moveq r1, #0 ; Set state of RxD movne r1, #UART_RxRdy ; ldr r0, [r7, #Terminal_Tx_tail - shared_variables] ldr r14, [r7, #Terminal_Tx_head - shared_variables] sub r0, r14, r0 ; Head - Tail => occupancy(ish) cmp r0, #Terminal_Tx_buff_end - Terminal_Tx_buff_start - 1 cmpne r0, #-1 ; All but caught up? ; Buffer full? orrne r1, r1, #UART_TxRdy ; ldmfd sp!, {r0, pc} ; Rd_IO_Tube Rd_IO_none mov r1, #&2A ; "Abort", for now @@@ ldmfd sp!, {r0, pc} ; Rd_IO_irq mov r0, #interrupts_active - Mon_RAM_start b Rd_IO_out ; Read byte and exit Rd_IO_ien mov r0, #interrupts_enable - Mon_RAM_start b Rd_IO_out ; Read byte and exit Rd_IO_timer_cmp mov r0, #timer_compare - Mon_RAM_start b Rd_IO_out ; Read byte and exit Rd_serial_No and r0, r2, #&0C ; Get address bits in `word' mov r0, r0, lsr #2 ; Address within word add r0, r0, #Board_number - Mon_RAM_start b Rd_IO_out ; Read byte and exit ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data in R1:R0 write_memory_d str lr, [sp, #-4]! ; May be faster than stm ldr r14, [r7, #mem_area_start - shared_variables] cmp r2, r14 ; blo write_memory_IO_D ; Address too low ldr r14, [r7, #mem_area_end - shared_variables] cmp r2, r14 ; bhs write_memory_IO_D ; Address too high ldr r14, [r7, #mem_area_pos - shared_variables] str r0, [r2, r14] ; Address in range add r14, r14, #4 ; str r1, [r2, r14] ; High word ldr pc, [sp], #4 ; May be faster than ldm ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data in R0 write_memory_w str lr, [sp, #-4]! ; May be faster than stm ldr r14, [r7, #mem_area_start - shared_variables] cmp r2, r14 ; blo write_memory_IO_W ; Address too low ldr r14, [r7, #mem_area_end - shared_variables] cmp r2, r14 ; bhs write_memory_IO_W ; Address too high ldr r14, [r7, #mem_area_pos - shared_variables] str r0, [r2, r14] ; Address in range ldr pc, [sp], #4 ; May be faster than ldm ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data in R0 write_memory_h str lr, [sp, #-4]! ; May be faster than STM ldr r14, [r7, #mem_area_start - shared_variables] cmp r2, r14 ; blo write_memory_h1 ; Address too low ldr r14, [r7, #mem_area_end - shared_variables] cmp r2, r14 ; bhs write_memory_h1 ; Address too high ldr r14, [r7, #mem_area_pos - shared_variables] strh r0, [r2, r14] ; Address in range ldr pc, [sp], #4 ; May be faster than LDM write_memory_h1 and r14, r2, #&F0000000 ; Bodgery! @@ cmp r14, #Virtex_page ; Virtex? bne write_memory_IO_H ; Not Virtex bic r14, r2, #&F0000000 ; Find page offset orr r14, r14, #VIRTEX_base ; Find true address strb r0, [r14] ; ldr pc, [sp], #4 ; May be faster than LDM ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Address in R2, data in R0 write_memory_b str lr, [sp, #-4]! ; May be faster than STM ldr r14, [r7, #mem_area_start - shared_variables] cmp r2, r14 ; blo write_mem_b1 ; Address too low ldr r14, [r7, #mem_area_end - shared_variables] cmp r2, r14 ; bhs write_mem_b1 ; Address too high ldr r14, [r7, #mem_area_pos - shared_variables] strb r0, [r2, r14] ; Address in range ldr pc, [sp], #4 ; May be faster than LDM write_mem_b1 and r14, r2, #&F0000000 ; Bodgery! @@ cmp r14, #Spartan_page ; Spartan? bne write_memory_IO_B ; Not Spartan bic r14, r2, #&F0000000 ; Find page offset orr r14, r14, #SPARTAN_base ; Find true address strb r0, [r14] ; ldr pc, [sp], #4 ; May be faster than LDM ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; LR already stacked on entry write_memory_IO_D ; Needs fixing @@@ write_memory_IO_W write_memory_IO_H write_memory_IO_B cmp r2, #tube ; Test/verification port beq Wr_IO_Tube ; cmp r2, #IO_area_start ; blo write_memory_abort ; Address too low cmp r2, #IO_area_end ; bhs write_memory_abort ; Address too high tst r2, #3 ; ldmnefd sp!, {pc} ; Return unless byte #0 and r14, r2, #&3C ; Partial decode ldrb r14, [pc, r14, lsr #2] ; Get offset add pc, pc, r14, lsl #2 ; Multiply `offset' by 4 ; Relocatable jump table to different devices DCB (Wr_IO_portA - %f1) / 4 ; 00 DCB (Wr_IO_portB - %f1) / 4 ; 04 DCB (Wr_IO_timer - %f1) / 4 ; 08 DCB (Wr_IO_timer_cmp-%f1)/4 ; 0C 1 ; One word into table DCB (Wr_IO_TxD - %b1) / 4 ; 10 DCB (Wr_IO_none - %b1) / 4 ; 14 Reserved for serial control(?) DCB (Wr_IO_irq - %b1) / 4 ; 18 DCB (Wr_IO_ien - %b1) / 4 ; 1C DCB (Wr_IO_stop - %b1) / 4 ; 20 Next four are serial number DCB (Wr_IO_none - %b1) / 4 ; 24 DCB (Wr_IO_none - %b1) / 4 ; 28 DCB (Wr_IO_none - %b1) / 4 ; 2C DCB (Wr_IO_none - %b1) / 4 ; 30 Reserved DCB (Wr_IO_none - %b1) / 4 ; 34 DCB (Wr_IO_none - %b1) / 4 ; 38 DCB (Wr_IO_none - %b1) / 4 ; 3C ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - write_memory_abort ; Not very finished! @@@ and r14, r2, #&F0000000 ; Hack to write Ethernet chip cmp r14, #ETHERNET_base streqh r0, [r2] ; ldr pc, [sp], #4 ; May be faster than ldm ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Wr_IO_timer mov r14, #Line_time_clk - Mon_RAM_start Wr_IO_R14 strb r0, [r14] ; ldmfd sp!, {pc} ; Tube_line DCD US1_base ; Tube serial line definition Wr_IO_Tube cmp r0, #cEOT ; beq Wr_IO_stop ; ^D stops execution Wr_IO_Tube1 ldr r14, Tube_line ; Load rather than stack reg. ldr r14, [r14, #US_CSR] ; Pass byte to transmit in R0 tst r14, #TxRdy ; Test if ready to transmit bne Wr_IO_Tube2 ; If set the output adr lr, Wr_IO_Tube1 ; Set return address (loop) b deschedule_ex ; Deschedule Wr_IO_Tube2 ldr r14, Tube_line ; Reload rather than stack ... str r0, [r14, #US_THR] ; ldmfd sp!, {pc} ; Return ; Special method of stopping emulator for verification prog. Wr_IO_stop ldrb r14, [r7, #arm_state - shared_variables] strb r14, [r7, #arm_state_old - shared_variables] mov r14, #State_stop_req ; Set status to stop strb r14, [r7, #arm_state - shared_variables] ; Signal to processor ldmfd sp!, {pc} ; Return Wr_IO_portA stmfd sp!, {r0} ; Need another register ldr r14, IO_PIO_base ; and r0, r0, #&FF ; str r0, [r14, #PIO_SODR] ; Set selected outputs eor r0, r0, #&FF ; str r0, [r14, #PIO_CODR] ; Clear other outputs ldmfd sp!, {r0, pc} ; ; Write another selection of AT91 PIO lines Wr_IO_portB stmfd sp!, {r0-r3} ; Need more space mov r0, r0, lsl #24 ; Left justify byte mov r1, #0 ; Accumulator mov r14, #1 ; Bit mask adr r2, IO_PIO_portB_tab ; Bit positions Wr_IO_portB_lp ldrb r3, [r2], #1 ; Find next bit position movs r0, r0, lsl #1 ; Sets C and Z for below orrcs r1, r1, r14, lsl r3 ; Conditional bit set bne Wr_IO_portB_lp ; Continue for all set bits ldr r0, IO_PIO_portB_inv ; eor r1, r1, r0 ; Invert some bits ldr r14, IO_PIO_base ; mov r3, #&FF ; Port A bit mask tst r1, #AT91_LCD_RW ; Check if R/~W going high strne r3, [r14, #PIO_ODR] ; If so, disable portA o/p ldr r0, IO_PIO_portB ; ; and r1, r1, r0 ; Should be superfluous @@@ str r1, [r14, #PIO_SODR] ; Set selected outputs eor r1, r1, r0 ; Flip relevant bits str r1, [r14, #PIO_CODR] ; Clear other outputs streq r3, [r14, #PIO_OER] ; If safe, enable portA o/p ldmfd sp!, {r0-r3, pc} ; Wr_IO_none ; @@@ ldmfd sp!, {pc} ; Wr_IO_irq mov r14, #interrupts_active - Mon_RAM_start b Wr_IO_R14 ; strb r0, [r14] & return Wr_IO_ien mov r14, #interrupts_enable - Mon_RAM_start b Wr_IO_R14 ; strb r0, [r14] & return Wr_IO_timer_cmp mov r14, #timer_compare - Mon_RAM_start b Wr_IO_R14 ; strb r0, [r14] & return Wr_IO_TxD ldr r1, [r7, #Terminal_Tx_tail - shared_variables] ldr r2, [r7, #Terminal_Tx_head - shared_variables] ; sub r1, r2, r1 ; Head - Tail => occupancy(ish) ; cmp r1, #Terminal_Tx_buff_end - Terminal_Tx_buff_start - 1 ; cmpne r1, #-1 ; All but caught up? sub r1, r1, r2 ; Tail - Head => free space(ish) subs r1, r1, #1 ; (At least) one space always free addmis r1, r1, #Terminal_Tx_buff_end - Terminal_Tx_buff_start ldmeqfd sp!, {pc} ; Buffer already full - reject strb r0, [r2], #1 ; Buffer byte, post increment cmp r2, #Terminal_Tx_buff_end - Mon_RAM_start movhs r2, #Terminal_Tx_buff_start - Mon_RAM_start str r2, [r7, #Terminal_Tx_head - shared_variables] cmp r1, #1 ; Buffer now full? ldreqb r0, [r7, #interrupts_active - shared_variables] biceq r0, r0, #Int_Tx_ready ; Clear interrupt bit streqb r0, [r7, #interrupts_active - shared_variables] ldmfd sp!, {pc} ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Literal pool IO_PIO_base DCD PIO_base ; IO_PIO_portB DCD &439C0000 ; Port B allowed bits IO_PIO_portB_inv DCD &401C0000 ; Port B inverted bits IO_PIO_portB_tab DCB 19 ; Bit #7 LHS button DCB 18 ; Bit #6 RHS button DCB 20 ; Bit #5 LCD backlight DCB 30 ; Bit #4 LED enable DCB 32 ; Bit #3 (always zero) DCB 25 ; Bit #2 LCD R/~W DCB 23 ; Bit #1 LCD RS DCB 24 ; Bit #0 LCD En ALIGN ;------------------------------------------------------------------------------ reverse_byte stmfd sp!, {lr} ; Bit reverse byte in R0 mov r14, #&01000000 ; Accumulator & bit counter reverse_byte1 movs r0, r0, lsr #1 ; LSB into carry adcs r14, r14, r14 ; Shift left from carry bcc reverse_byte1 ; Carry set to terminate mov r0, r14 ; ldmfd sp!, {pc} ; ;------------------------------------------------------------------------------ Host_get_double stmfd sp!, {r2, lr} ; Returns R1:R0 bl Host_get_word ; mov r2, r0 ; Low word bl Host_get_word ; mov r1, r0 ; High word mov r0, r2 ; ldmfd sp!, {r2, pc} ; Host_put_double stmfd sp!, {r0, lr} ; bl Host_put_word ; mov r0, r1 ; High word bl Host_put_word ; ldmfd sp!, {r0, pc} ; Host_get_word stmfd sp!, {r1, lr} ; bl Host_in ; mov r1, r0 ; bl Host_in ; orr r1, r1, r0, lsl #8 ; bl Host_in ; orr r1, r1, r0, lsl #16 ; bl Host_in ; orr r0, r1, r0, lsl #24 ; ldmfd sp!, {r1, pc} ; Host_put_halfword stmfd sp!, {r0, r2, lr} ; mov r2, #2 ; Byte count b Host_put_N ; Host_put_word stmfd sp!, {r0, r2, lr} ; mov r2, #4 ; Byte count Host_put_N bl Host_out ; Also an entry point mov r0, r0, lsr #8 ; Next byte subs r2, r2, #1 ; bhi Host_put_N ; ldmfd sp!, {r0, r2, pc} ; Host_get_halfword stmfd sp!, {r1, lr} ; bl Host_in ; mov r1, r0 ; bl Host_in ; orr r0, r1, r0, lsl #8 ; ldmfd sp!, {r1, pc} ; ;------------------------------------------------------------------------------ ; This serial code relies on the head and tail pointers being `close' in memory ; and various other values being representable as single immediates. Host_buffer_init stmfd sp!, {r0, lr} ; mov r14, #Host_buffer_head - Mon_RAM_start ; Crude hack method for now @@@ mov r0, #Host_buffer_start - Mon_RAM_start str r0, [r14] ; Initialise head str r0, [r14, #Host_buffer_tail - Host_buffer_head] ; Initialise tail ldmfd sp!, {r0, pc} ; Host_line DCD US0_base ; Host serial line definition ; Also needs changes in Mon_init_table! ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Send character in R0 to host serial line ; Offer descheduling point if transmitter busy Host_out stmfd sp!, {r1, lr} ; ldr r1, Host_line ; Pointer to UART Host_out1 ldr r14, [r1, #US_CSR] ; R14 is a scratch register here tst r14, #TxRdy ; Test if ready to transmit bne Host_out2 ; adr lr, Host_out1 ; Set return address b deschedule_com ; Host_out2 str r0, [r1, #US_THR] ; Send character ldmfd sp!, {r1, pc} ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Get character from host serial line buffer into R0 ; Offer descheduling point if nothing pending Host_in stmfd sp!, {r1-r2, lr} ; mov r2, #Host_buffer_head - Mon_RAM_start ; Crude hack method for now @@@ ldr r1, [r2, #Host_buffer_tail - Host_buffer_head] ; Get tail Host_in1 ldr r0, [r2] ; Get head cmp r0, r1 ; Pointers equal for empty bne Host_in_rdy ; adr lr, Host_in1 ; Return address b deschedule_com ; Host_in_rdy ldrb r0, [r1], #1 ; Read character, move pointer cmp r1, #Host_buffer_end - Mon_RAM_start ; but still wrap movhs r1, #Host_buffer_start - Mon_RAM_start str r1, [r2, #Host_buffer_tail - Host_buffer_head] ; Save new tail ldmfd sp!, {r1-r2, pc} ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Timer 0 interrupt service routine Timer0_isr sub lr, lr, #4 ; Correct return address stmfd sp!, {r0-r1, lr} ; ldr r1, Lit_TC0_base ; ldr r1, [r1, #TC_SR] ; Read status to clear interrupt mov r1, #FASTRAM_base ; (a.k.a. 00000000) ldr r14, [r1, #Line_time_clk - Mon_RAM_start] add r14, r14, #1 ; Increment clock str r14, [r1, #Line_time_clk - Mon_RAM_start] ldrb r0, [r1, #timer_compare - Mon_RAM_start] and r14, r14, #&FF ; Use only one byte teq r0, r14 ; Timer at target value? ldrb r0, [r1, #interrupts_active - Mon_RAM_start] orreq r0, r0, #Int_timer_compare ; Set ... bicne r0, r0, #Int_timer_compare ; ... or clear bit strb r0, [r1, #interrupts_active - Mon_RAM_start] ldr r1, Lit_AIC_base ; str r14, [r1, #AIC_EOICR] ; Signal end of interrupt ldmfd sp!, {r0-r1, pc}^ ; Return from interrupt ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PIO_isr sub lr, lr, #4 ; Correct return address stmfd sp!, {r0-r2, lr} ; ; mov r14, #RAM_base ; Indicates ISR called once on startup ; ldr r1, [r14] ; Is the initialisation incomplete? @@@ ; add r1, r1, #1 ; str r1, [r14] ldr r14, IO_PIO_base ; mov r1, #0 ; ldr r0, [r14, #PIO_ISR] ; Clear interrupt ldr r0, [r14, #PIO_PDSR] ; Read pin status mov r14, #FASTRAM_base ; (a.k.a. 00000000) tst r0, #AT91_Virtex_init ; Test ports & translate orreq r1, r1, #Int_L_button ; Active low tst r0, #AT91_Spartan_init ; orreq r1, r1, #Int_R_button ; Active low tst r0, #AT91_Ether_IRQ ; orrne r1, r1, #Int_Ethernet ; Active high tst r0, #AT91_Virtex_IRQ ; orreq r1, r1, #Int_Virtex ; Active low (?) tst r0, #AT91_Spartan_IRQ ; orreq r1, r1, #Int_Spartan ; Active low (?) ldrb r2, [r14, #Last_PIO_IRQ_state - Mon_RAM_start] strb r1, [r14, #Last_PIO_IRQ_state - Mon_RAM_start] bic r0, r2, r1 ; Bit mask to clear bic r1, r1, r2 ; Bit mask to set ldrb r2, [r14, #interrupts_active - Mon_RAM_start] bic r2, r2, r0 ; Clear some bits orr r2, r2, r1 ; Set some bits strb r2, [r14, #interrupts_active - Mon_RAM_start] ldr r14, Lit_AIC_base ; str r14, [r14, #AIC_EOICR] ; Signal end of interrupt ldmfd sp!, {r0-r2, pc}^ ; Return from interrupt ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Host_isr sub lr, lr, #4 ; Correct return address stmfd sp!, {r0, r2-r3, lr} ; ldr r14, Host_line ; Stacked; now use as scratch ldr r0, [r14, #US_CSR] ; Read status register tst r0, #RxRdy ; Only source so far beq Host_isr_out ; Just in case mov r2, #Host_buffer_head - Mon_RAM_start ; Crude hack method for now @@@ ldr r3, [r2] ; Head of buffer Host_isr_1 ldr r0, [r14, #US_RHR] ; Get character ; The following cares nothing for overruns! strb r0, [r3], #1 ; Store character, inc. pointer cmp r3, #Host_buffer_end - Mon_RAM_start movhs r3, #Host_buffer_start - Mon_RAM_start ldr r0, [r14, #US_CSR] ; Check that no more characters tst r0, #RxRdy ; are pending bne Host_isr_1 ; Oops - repeat! str r3, [r2] ; Save new head Host_isr_out ldr r14, Lit_AIC_base ; str r0, [r14, #AIC_EOICR] ; Signal end of interrupt ldmfd sp!, {r0, r2-r3, pc}^ ; Return from interrupt ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Spurious interrupt trap code @@@ revise? @@@ spurious_isr sub lr, lr, #4 ; Correct return address str lr, [sp, #-4]! ; May be faster than STM ; stmfd sp!, {lr} ; ldr r14, Lit_AIC_base ; str r14, [r1, #AIC_EOICR] ; Signal end of interrupt ldmfd sp!, {pc}^ ; Return from interrupt ;------------------------------------------------------------------------------ Lit_AIC_base DCD AIC_base ; Lit_TC0_base DCD TC_base + TC_CHL0 ; ;------------------------------------------------------------------------------ execute_start mov r7, #shared_variables - Mon_RAM_start ; Init. breakpoints here? @@@ mov r0, #State_hard_reset ; Hardware reset state b Reset_common ; Reset mov r7, #shared_variables - Mon_RAM_start ; Superfluous?? mov r0, #State_reset ; Soft reset state Reset_common strb r0, [r7, #arm_state - shared_variables] mov r0, #0 ; No of steps pending str r0, [r7, #arm_step_count - shared_variables] str r0, [r7, #arm_instr_count - shared_variables] mov r11, #Reset_PC ; PC mov r12, #Reset_CPSR ; flags str r12, [r9, #sim_CPSR - reg_block]; CPSR into dustbin str r11, [r9, #sim_PC - reg_block] ; Make PC visible ; Point R7 at shared variables ; 00-3F = Reset ; 40-7F = Stopped ; 80-BF = Running ; C0-FF = Stepping ; Add breakpoint stuff later .. :-( ; Add temporary breakpoint (step until ...) mov r0, #FALSE ; Disable breakpoints for future strb r0, [r7, #break_enable - shared_variables] Go_idle mov r0, #FALSE ; Disable breakpoints for moment strb r0, [r7, #break_enabled - shared_variables] ; Use "run with breakpoints" to allow "run until" - esp. for running procedure calls ; @@@ ?? Idle_loop ldr r12, [r9, #sim_CPSR - reg_block]; CPSR reload ldr r11, [r9, #sim_PC - reg_block] ; PC reload ldr r8, [r7, #Running_flags - shared_variables] ; This is in case these values have been modified remotely adr r3, Idle_loop1 ; (Really superfluous) bl Interrupt_check ; Idle_loop1 bl deschedule_ex ; ldrb r0, [r7, #arm_state - shared_variables] ; Load command cmp r0, #State_to_reset ; Reset commanded? beq Reset ; Reboot tst r0, #&80 ; Bit clear if stopped beq Idle_loop ; Remain stopped tst r0, #&40 ; Run or step? bne Step_loop ; Step commanded ; or fall into ... ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Run_loop ldr r12, [r9, #sim_CPSR - reg_block]; CPSR reload ldr r11, [r9, #sim_PC - reg_block] ; PC reload ldr r8, [r7, #Running_flags - shared_variables] adr r3, Run_loop1 ; Alternate `return' address bl Interrupt_check ; bl fetch ; ; Trap BL, SWI as in step routine ?? @@@ blcs step ; If no breakpoint Run_loop1 str r12, [r9, #sim_CPSR - reg_block]; CPSR into dustbin str r11, [r9, #sim_PC - reg_block] ; Make PC visible bl deschedule_ex ; Deschedule ldrb r0, [r7, #arm_state - shared_variables] tst r0, #&80 ; Stopped? beq Go_idle ; Yes, stop tst r0, #&40 ; Run or step? beq Run_loop ; Continue running ; or fall into ... ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Step_loop ldr r12, [r9, #sim_CPSR - reg_block]; CPSR reload ldr r11, [r9, #sim_PC - reg_block] ; PC reload ldr r8, [r7, #Running_flags - shared_variables] adr r3, Step_loop2 ; Alternate `return' address bl Interrupt_check ; bl fetch ; bcc Step_loop2 ; Breakpoint found and r0, r10, #&0B000000 ; cmp r0, #&0B000000 ; BL or SWI instruction? beq step_call ; Yes - what now? Step_loop1 bl step ; If no breakpoint Step_loop2 str r12, [r9, #sim_CPSR - reg_block]; CPSR into dustbin str r11, [r9, #sim_PC - reg_block] ; Make PC visible bl deschedule_ex ; Deschedule ldrb r0, [r7, #arm_state - shared_variables] tst r0, #&80 ; Stopped? beq Go_idle ; So stop! tst r0, #&40 ; Run or step? beq Run_loop ; Stop counting ldr r0, [r7, #arm_step_count - shared_variables] subs r0, r0, #1 ; Decrement count (& set flags) str r0, [r7, #arm_step_count - shared_variables] bne Step_loop ; ldrb r0, [r7, #arm_state - shared_variables] strb r0, [r7, #arm_state_old - shared_variables] mov r0, #State_count_out ; Stopped `command' strb r0, [r7, #arm_state - shared_variables] ; into command word b Go_idle ; If complete, stop ; Deal with potential running state change out of main execution path step_call tst r10, #&04000000 ; BL or SWI? bne step_call1 ; tst r8, #Run_P_bit ; BL instruction detected beq Step_loop1 ; mov r4, #State_running_BL ; bne step_call2 ; step_call1 tst r8, #Run_S_bit ; SWI instruction detected beq Step_loop1 ; mov r4, #State_running_SWI ; step_call2 adr lr, Proc_loop1 ; `Return' address b Save_state ; Save PC++ & change to state R4 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Procedure_loop ldr r12, [r9, #sim_CPSR - reg_block]; CPSR reload ldr r11, [r9, #sim_PC - reg_block] ; PC reload bic r11, r11, #3 ; Align PC mov r2, r11 ; PC bl read_memory_w ; Instruction fetch mov r10, r1 ; Instruction register add r11, r11, #4 ; and increment Proc_loop1 bl step ; Regardless of breakpoints! Proc_loop2 str r12, [r9, #sim_CPSR - reg_block]; CPSR into dustbin str r11, [r9, #sim_PC - reg_block] ; Make PC visible bl deschedule_ex ; Deschedule ldrb r0, [r7, #arm_state - shared_variables] tst r0, #&80 ; Stopped? beq Go_idle ; Yes, stop tst r0, #&40 ; Run or step? bne Step_loop ; Step commanded cmp r0, #State_running ; beq Run_loop ; Run commanded mov r6, #exec_variables - Mon_RAM_start ldr r0, [r6, #run_until_PC - exec_variables] cmp r11, r0 ; PC = return address? bne Procedure_loop ; No ldrb r0, [r6, #run_until_mode - exec_variables] and r1, r12, #&3F ; Mode is same? cmp r1, r0 ; bne Procedure_loop ; No ldr r0, [r6, #run_until_SP - exec_variables] ldr r1, [r9, #sim_R13 - reg_block] cmp r1, r0 ; SP = as called bne Procedure_loop ; No ; @@@ Is this right ??? Here?? ; Copy breakpoints -should be enabled- flag to enabled position ldrb r0, [r7, #break_enable - shared_variables] strb r0, [r7, #break_enabled - shared_variables] ; Always allows stepping of first instruction. ; Pick up old state, restore and return as appropriate ldrb r0, [r6, #run_until_mode + 1 - exec_variables] strb r0, [r7, #arm_state - shared_variables] ldrb r0, [pc, r0, lsr #6] sub pc, pc, r0, lsl #2 DCB (%f1 - Idle_loop1) / 4 ; All short backwards offsets DCB (%f1 - Idle_loop1) / 4 ; DCB (%f1 - Run_loop1 ) / 4 ; DCB (%f1 - Step_loop2) / 4 ; 1 ; mov r0, r0, lsr #6 ; Get top 2 bits of old mode ; ; ldr r0, [pc, r0, lsl #2] ; N.B. no table offset ; add pc, pc, r0 ; Despatch as appropriate to mode ; ; DCD Idle_loop1 - %f1 ; ;1 ; DCD Idle_loop1 - %b1 ; ; DCD Run_loop1 - %b1 ; ; DCD Step_loop2 - %b1 ; ;------------------------------------------------------------------------------ ; R8 holds running flags ; Returns to LR if no enabled interrupts active ; Returns to R3 if enabled interrupts active while running/stepping ; unless ISR `called', when it jumps to the procedure handler Interrupt_check ldr r0, IO_PIO_base ; ldr r0, [r0, #PIO_PDSR] ; Get pin state tst r0, #nFIQ_wire ; bne Int_chk1 ; FIQ inactive tst r12, #F_bit ; Interrupt disable flag beq enabled_FIQ ; Int_chk1 tst r12, #I_bit ; Interrupts enabled? movne pc, lr ; No mov r1, #FASTRAM_base ; (a.k.a. 00000000) ldrb r4, [r1, #interrupts_active - Mon_RAM_start] ldrb r1, [r1, #interrupts_enable - Mon_RAM_start] ands r1, r1, r4 ; Z if no enabled interrupts moveq pc, lr ; No interrupts ; tst r0, #nIRQ_wire ; ; tsteq r12, #I_bit ; Interrupts enabled? ; movne pc, lr ; adrl r1, IRQ_entry ; Point to appropriate operation mov r4, #State_running_IRQ ; tst r8, #Run_I_bit ; bne Int_procedure ; -Call- ISR Int_normal ldrb r0, [r7, #arm_state - shared_variables] tst r0, #&80 ; Running/stepping? moveq pc, lr ; No - so no action mov lr, r3 ; `Return' address mov pc, r1 ; Appropriate action first enabled_FIQ adrl r1, FIQ_entry ; Point to appropriate operation mov r4, #State_running_FIQ ; tst r8, #Run_F_bit ; beq Int_normal ; Retain operating state Int_procedure bl Save_state ; adr lr, Proc_loop2 ; Entry point in loop mov pc, r1 ; Appropriate action first ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Save state for leaving "procedure" {PC, SP, Mode, current state} ; Change to state R4 ; Corrupts R0, R2, R6 - must preserve R1 Save_state mov r6, #exec_variables - Mon_RAM_start str r11, [r6, #run_until_PC - exec_variables] ldr r0, [r9, #sim_R13 - reg_block] str r0, [r6, #run_until_SP - exec_variables] ldrb r0, [r7, #arm_state - shared_variables] and r2, r12, #&3F ; Mode orr r0, r2, r0, lsl #8 ; Hash together str r0, [r6, #run_until_mode - exec_variables] strb r4, [r7, #arm_state - shared_variables] mov pc, lr ; ;------------------------------------------------------------------------------ ; Fetch into R10 from R11 - post increment ; Check breakpoints, count instruction if `unbroken' ; Corrupts R0-R2 fetch str lr, [sp, #-4]! ; Push return address bic r11, r11, #3 ; Align PC mov r2, r11 ; PC bl read_memory_w ; Instruction fetch mov r10, r1 ; Instruction register ldrsb r0, [r7, #break_enabled - shared_variables] cmp r0, #FALSE ; Test if breakpoints disabled ; If equal then guaranteed C=1 blne breakpoint_check ; C=0 if breakpoint found ldrcc pc, [sp], #4 ; So return ; Copy breakpoints -should be enabled- flag to enabled position ldrb r0, [r7, #break_enable - shared_variables] strb r0, [r7, #break_enabled - shared_variables] ; Always allows stepping of first instruction. add r11, r11, #4 ; and increment ldr r0, [r7, #arm_instr_count - shared_variables] add r0, r0, #1 ; Count instruction str r0, [r7, #arm_instr_count - shared_variables] ldr pc, [sp], #4 ; Return ; N.B. Carry still set if returning here ;------------------------------------------------------------------------------ step str lr, [sp, #-4]! ; Push return address mov r0, r10, lsr #28 ; Condition cmp r0, #&0000000E ; Optimisation for "always" bne check_cc ; Return Z=1 for "go" step_go and r0, r10, #&0E000000 ; Op. code ldr r0, [pc, r0, lsr #23] ; N.B. no table offset add pc, pc, r0 ; DCD dp_reg - %f1 ; Relocatable jump table 1 DCD dp_imm - %b1 ; Table could be halfword @@@ DCD lsr_imm - %b1 ; one more instruction DCD lsr_reg - %b1 ; saves 3 words DCD lsm - %b1 ; DCD branch - %b1 ; DCD lsc - %b1 ; DCD sys - %b1 ; check_cc adr r1, cond_table ; ldr r0, [r1, r0, lsl #2] ; Get CC map (half) word mov r1, r12, lsr #28 ; Get flags movs r0, r0, lsl r1 ; Select bit in map bpl step_go ; ldr pc, [sp], #4 ; Skip instruction - return ; 0 bit for "go" in position indicated by flags from LHS cond_table DCD &F0F00000 ; 0 - EQ Possibly compress? DCD &0F0F0000 ; 1 - NE @@@ DCD &CCCC0000 ; 2 - CS Not too time DCD &33330000 ; 3 - CC critical DCD &FF000000 ; 4 - MI AL trapped 1st DCD &00FF0000 ; 5 - PL DCD &AAAA0000 ; 6 - VS DCD &55550000 ; 7 - VC DCD &CFCF0000 ; 8 - HI DCD &30300000 ; 9 - LS DCD &55AA0000 ; A - GE DCD &AA550000 ; B - LT DCD &5FAF0000 ; C - GT DCD &A0500000 ; D - LE DCD &00000000 ; E - AL DCD &FFFF0000 ; F - NV ; Untried ** NEW CODE ** @@@ ; adr r1, cond_table ; +2 instr. -6 words @@@ ; mov r0, r0, lsl #1 ; Can go too if R0 sorted earlier ; ldrh r0, [r1, r0] ; ^^^ MAYBE! - LSB MASK ??? ; mov r0, r0, lsl #16 ; ; mov r1, r12, lsr #28 ; Get flags ... etc. ;------------------------------------------------------------------------------ dp_reg and r0, r10, #&00000090 ; MUL, SWP, LSRH, etc. cmp r0, #&00000090 ; Extension? beq dp_reg_ext_A ; and r0, r10, #&01900000 ; MSR/MRS, etc. cmp r0, #&01000000 ; Extension? beq dp_reg_ext_B ; and r6, r10, #&000F0000 ; Rn cmp r6, #&000F0000 ; Test for PC ldrne r6, [r9, r6, lsr #14] ; Current register map addeq r6, r11, #4 ; or PC adr lr, dp_all ; Return to other dp ops. b shifted_Rm ; Shifter_carry_out in CF dp_imm and r0, r10, #&01900000 ; cmp r0, #&01000000 ; Extension? beq dp_imm_ext ; and r6, r10, #&000F0000 ; Rn cmp r6, #&000F0000 ; Test for PC ldrne r6, [r9, r6, lsr #14] ; Current register map addeq r6, r11, #4 ; or PC bl immediate ; Into R1 (also sets up CF) dp_all and r0, r10, #&01E00000 ; Operation (shifter_carry_out in CF) ldrb r0, [pc, r0, lsr #21] ; N.B. no table offset add pc, pc, r0, lsl #2 ; DCB (dp_and - %f1) / 4 ; 0 DCB (dp_eor - %f1) / 4 ; 1 DCB (dp_sub - %f1) / 4 ; 2 DCB (dp_rsb - %f1) / 4 ; 3 1 DCB (dp_add - %b1) / 4 ; 4 DCB (dp_adc - %b1) / 4 ; 5 DCB (dp_sbc - %b1) / 4 ; 6 DCB (dp_rsc - %b1) / 4 ; 7 DCB (dp_tst - %b1) / 4 ; 8 DCB (dp_teq - %b1) / 4 ; 9 DCB (dp_cmp - %b1) / 4 ; A DCB (dp_cmn - %b1) / 4 ; B DCB (dp_orr - %b1) / 4 ; C DCB (dp_mov - %b1) / 4 ; D DCB (dp_bic - %b1) / 4 ; E DCB (dp_mvn - %b1) / 4 ; F dp_sbc tst r12, #Cflag ; subeq r6, r6, #1 ; fall into ... dp_sub subs r1, r6, r1 ; b dp_arith_out ; dp_rsc tst r12, #Cflag ; subeq r1, r1, #1 ; fall into ... dp_rsb subs r1, r1, r6 ; b dp_arith_out ; dp_adc tst r12, #Cflag ; addne r6, r6, #1 ; fall into ... dp_add adds r1, r6, r1 ; dp_arith_out mrs r2, cpsr ; Keep the flags bl write_reg ; tst r10, #Sbit ; ldreq pc, [sp], #4 ; Return no flag alteration and r0, r10, #&0000F000 ; Rd field cmp r0, #&0000F000 ; Destination = PC? beq spsr_copy_up ; dp_arith_out1 and r2, r2, #&F0000000 ; All the flags for adds bic r12, r12, #&F0000000 ; orr r12, r12, r2 ; ldr pc, [sp], #4 ; Return dp_teq teq r6, r1 ; Note: shifter carry set up b dp_tst1 ; Forgivable inefficiency dp_tst tst r6, r1 ; Note: shifter carry set up dp_tst1 mrs r2, cpsr ; Keep the flags b dp_logical_out1 ; Slower, but shorter ; and r2, r2, #&E0000000 ; Not the overflow flag ; bic r12, r12, #&E0000000 ; ; orr r12, r12, r2 ; ; ldr pc, [sp], #4 ; Return dp_cmn cmn r6, r1 ; b dp_cmp1 ; ;dp_cmn rsb r1, r1, #0 ; 2's complement R1 and fall into ; The previous line doesn't work; Carry is wrong if R1=0 dp_cmp cmp r6, r1 ; dp_cmp1 mrs r2, cpsr ; Get the flags b dp_arith_out1 ; Slower, but shorter ; and r2, r2, #&F0000000 ; Use all the flags ; bic r12, r12, #&F0000000 ; ; orr r12, r12, r2 ; ; ldr pc, [sp], #4 ; Return dp_eor eors r1, r6, r1 ; Note: shifter carry set up b dp_logical_out ; dp_orr orrs r1, r6, r1 ; Note: shifter carry set up b dp_logical_out ; dp_bic mvn r1, r1 ; Note: shifter carry set up dp_and ands r1, r6, r1 ; Note: shifter carry set up b dp_logical_out ; dp_mvn mvn r1, r1 ; Note: shifter carry set up dp_mov movs r1, r1 ; Note: shifter carry set up dp_logical_out mrs r2, cpsr ; Keep the flags bl write_reg ; tst r10, #Sbit ; ldreq pc, [sp], #4 ; Return no flag alteration and r0, r10, #&0000F000 ; Rd field cmp r0, #&0000F000 ; Destination = PC? beq spsr_copy_up ; dp_logical_out1 and r2, r2, #&E0000000 ; Not the overflow flag bic r12, r12, #&E0000000 ; orr r12, r12, r2 ; ldr pc, [sp], #4 ; Return ; The following is branched to and finishes by "returning" spsr_copy_up str r12, [r9, #sim_CPSR - reg_block] ; CPSR into dustbin as default bl find_spsr ; and r2, r12, #&F ; Old mode ldr r12, [r9, r0, lsl #2] ; Load CPSR mov r0, r2 ; Register allocation poor :-( and r1, r12, #&F ; New mode ldr lr, [sp], #4 ; Pop return address b mode_reg_swap ; Update register cache & ret. ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Instruction set extensions et alia dp_reg_ext_A tst r10, #&00000060 ; Half-word transfer? bne lsrh ; and r1, r10, #&01C00000 ; mov r1, r1, lsr #21 ; ldrh r0, [pc, r1] ; Just a jump table add pc, pc, r0 ; DCW dp_mul - %f1 ; DCW undef - %f1 ; 1 DCW dp_umull - %b1 ; DCW dp_smull - %b1 ; DCW swap - %b1 ; DCW swapb - %b1 ; DCW undef - %b1 ; DCW undef - %b1 ; ALIGN ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - dp_mul dp_umull dp_smull and r0, r10, #&F ; All multiplies bl read_reg ; mov r3, r0 ; Rm mov r0, r10, lsr #8 ; bl read_reg ; mov r4, r0 ; Rs tst r10, #MUL_L_bit ; moveq r0, #12 ; Source register position "Rn" movne r0, #16 ; Ditto for RdHi tst r10, #MUL_A_bit ; moveq r0, #0 ; Accumulate nothing movne r0, r10, lsr r0 ; or something blne read_reg ; mov r1, r0 ; "Rn" or RdHi tst r10, #MUL_L_bit ; beq dp_mul_short ; tst r10, #MUL_A_bit ; Refresh condition moveq r0, #0 ; Accumulate nothing movne r0, r10, lsr #16 ; or something blne read_reg ; RdLo tst r10, #MUL_U_bit ; Signed? umlaleqs r0, r1, r3, r4 ; smlalnes r0, r1, r3, r4 ; mrs r2, cpsr ; Keep the flags and r4, r10, #&0000F000 ; RdLo cmp r4, #&0000F000 ; Test for PC strne r0, [r9, r0, lsr #10] ; Current register map ; not needed moveq r11, r0 ; or PC b dp_mul_out ; Rejoin at this address dp_mul_short mlas r1, r3, r4, r1 ; Short mult. is simpler! mrs r2, cpsr ; Keep the flags dp_mul_out and r0, r10, #&000F0000 ; "Rd" or RdHi cmp r0, #&000F0000 ; Test for PC strne r1, [r9, r0, lsr #14] ; Current register map ; not needed moveq r11, r1 ; or PC tst r10, #Sbit ; ldreq pc, [sp], #4 ; Return no flag alteration and r2, r2, #&C0000000 ; Not C or V bic r12, r12, #&C0000000 ; orr r12, r12, r2 ; ldr pc, [sp], #4 ; Return ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - dp_reg_ext_B tst r10, #&00200000 ; MSR/MSR ... beq dp_mrs ; tst r10, #&00000010 ; beq dp_msr ; ; X1 0x10 XXX xxx1 X bic r0, r10, #&F000000F ; ldr r1, BX_value ; cmp r0, r1 ; bne undef ; and r0, r10, #&0F ; bl read_reg ; bic r11, r1, #1 ; Reult into PC tst r0, #1 ; Thumb bit? ; @@@ orrne r12, r12, T_bit ; Yes, switch mode ldr pc, [sp], #4 ; Return BX_value DCD &012FFF10 ; BX instruction dp_imm_ext tst r10, #&00200000 ; beq undef ; Could test bits 15..12 also adr lr, dp_msr1 ; b immediate ; Value into R1 dp_mrs tst r10, #Rbit ; moveq r1, r12 ; CPSR blne find_spsr ; Flags preserved ldrne r1, [r9, r0, lsl #2] ; Get SPSR ldr lr, [sp], #4 ; Pop return address b write_reg ; and leave PSR_valid_bits DCD &F00000FF ; Implemented bits in PSRs dp_msr and r0, r10, #&F ; Register specifier bl read_reg ; Value returned in R0 mov r1, r0 ; dp_msr1 ldr r0, PSR_valid_bits ; See just above and r1, r1, r0 ; Mask for possible bits only tst r10, #Rbit ; bne dp_msr_spsr ; Work with an SPSR tst r10, #&00080000 ; Flags? bicne r12, r12, #&FF000000 ; Yes, lose old flags biceq r1, r1, #&FF000000 ; No, lose new bits tst r10, #&00010000 ; Status? tstne r12, #&F ; If yes, see if privileged bne msr_status ; Yup, that too! bic r1, r1, #&FF ; No status change orr r12, r12, r1 ; ldr pc, [sp], #4 ; Return msr_status and r0, r12, #&F ; Remember old mode bic r12, r12, #&000000FF ; but lose in CPSR orr r12, r12, r1 ; Insert new status and r1, r1, #&F ; Recover new mode ldr lr, [sp], #4 ; Pop return address b mode_reg_swap ; Update register cache dp_msr_spsr mov r2, r1 ; bl find_spsr ; Corrupts R0, R1 ldr r3, [r9, r0, lsl #2] ; Get SPSR tst r10, #&00080000 ; Flags? bicne r3, r3, #&FF000000 ; Yes, lose old flags biceq r2, r2, #&FF000000 ; No, lose new bits tst r10, #&00010000 ; Status? bicne r3, r3, #&FF ; Lose old status biceq r2, r2, #&FF ; No status change orr r3, r3, r2 ; str r3, [r9, r0, lsl #2] ; Save SPSR ldr pc, [sp], #4 ; Return ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - find_spsr and r0, r12, #&F ; R0 := 1/4 offset to SPSR adr r1, spsr_tab ; ldrb r0, [r1, r0] ; mov pc, lr ; ; Positions of SPSRs (sim_CPSR - reg_block)/4 = 'dustbin' spsr_tab DCB (sim_CPSR - reg_block)/4, (sim_SPSR_fiq - reg_block)/4 DCB (sim_SPSR_irq - reg_block)/4, (sim_SPSR_svc - reg_block)/4 DCB (sim_CPSR - reg_block)/4, (sim_CPSR - reg_block)/4 DCB (sim_CPSR - reg_block)/4, (sim_SPSR_abt - reg_block)/4 DCB (sim_CPSR - reg_block)/4, (sim_CPSR - reg_block)/4 DCB (sim_CPSR - reg_block)/4, (sim_SPSR_undef - reg_block)/4 DCB (sim_CPSR - reg_block)/4, (sim_CPSR - reg_block)/4 DCB (sim_CPSR - reg_block)/4, (sim_CPSR - reg_block)/4 ALIGN ;------------------------------------------------------------------------------ lsr_reg tst r10, #&00000010 ; Test for undefined bne undef ; Undefined adr lr, lsr_all ; Set return address b shifted_Rm ; Offset in R1 lsr_imm mov r1, r10, lsl #20 ; Lose all but offset mov r1, r1, lsr #20 ; lsr_all and r0, r10, #&000F0000 ; R2 := base register cmp r0, #&000F0000 ; Test for PC ldrne r2, [r9, r0, lsr #14] ; Current register map addeq r2, r11, #4 ; or PC tst r10, #Ubit ; addne r3, r2, r1 ; Up ... subeq r3, r2, r1 ; ... or down movs r1, r10, ror #25 ; P bit into carry (R1 is scrap) ; Guaranteed non-zero (ZF=0) movcs r2, r3 ; Pre-index; R2 now address tstcs r10, #Wbit ; ZF=1 if no writeback ; CS => only if pre-indexed beq lsr_imm1 ; No writeback cmp r0, #&000F0000 ; R0 -> Rn (still) strne r3, [r9, r0, lsr #14] ; Current register map ; not needed moveq r11, r3 ; or PC lsr_imm1 tst r10, #Bbit ; bne lsr_byte ; tst r10, #Ldbit ; bne lsr_imm_ldr ; mov r0, r10, lsr #12 ; bl read_reg ; ldr lr, [sp], #4 ; Return address b write_memory_w ; Data store lsr_imm_ldr bl read_memory_w ; Data load ldr lr, [sp], #4 ; Pop return address b write_reg ; and depart lsr_byte tst r10, #Ldbit ; bne lsr_imm_ldrb ; mov r0, r10, lsr #12 ; STRB code bl read_reg ; ; cmp r2, #tube ; @@@ ; beq tube_out ; cmp r2, #tube_too ; beq Wr_IO_Tube ; ldr lr, [sp], #4 ; Return address b write_memory_b ; Data write lsr_imm_ldrb bl read_memory_b ; Data load ldr lr, [sp], #4 ; Pop return address b write_reg ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lsrh tst r10, #&00400000 ; Immediate offset? bne lsrh_imm ; mov r0, r10 ; adr lr, lsrh_all ; Call with different return addr b read_reg ; More efficient! lsrh_imm bic r0, r10, #&F0 ; orr r0, r0, r0, lsr #4 ; Assemble offset and r0, r0, #&FF ; R0 := immediate offset lsrh_all and r1, r10, #&000F0000 ; R1 := base register cmp r1, #&000F0000 ; Test for PC ldrne r2, [r9, r1, lsr #14] ; Current register map addeq r2, r11, #4 ; or PC tst r10, #Ubit ; Set R3 to indexed value addne r3, r2, r0 ; Up ... subeq r3, r2, r0 ; ... or down movs r0, r10, ror #25 ; P bit into carry (R0 is scrap) ; Guaranteed non-zero (ZF=0) movcs r2, r3 ; Pre-index; R2 now address tstcs r10, #Wbit ; ZF=1 if no writeback ; CS => only if pre-indexed beq lsrh1 ; No writeback cmp r1, #&000F0000 ; R1 -> Rn (still) strne r3, [r9, r1, lsr #14] ; Current register map ; not needed moveq r11, r3 ; or PC lsrh1 tst r10, #Ldbit ; beq strh_code ; ands r0, r10, #&00000060 ; SH bits beq undef ; Really already broken, @@@ ; but impossible to reach anyway cmp r0, #&00000040 ; Middle of three values blo ldrh_start ; 00000020 beq ldrsb_start ; 00000040 b ldrsh_start ; 00000060 ; RETEST PENDING @@@ ; old stuff ldr r1, [pc, r0, lsr #3] ; ; add pc, pc, r1 ; ; ; DCD undef - %f1 ; Really already broken, ;1 ; but impossible to reach anyway ; DCD ldrh_start - %b1 ; ; DCD ldrsb_start - %b1 ; ; DCD ldrsh_start - %b1 ; ldrh_start bl read_memory_h ; Data fetch (zero extended) ldr lr, [sp], #4 ; Recover return address b write_reg ; Write and exit ldrsb_start bl read_memory_b ; Data fetch mov r1, r1, lsl #24 ; Sign extend mov r1, r1, asr #24 ; ldr lr, [sp], #4 ; Recover return address b write_reg ; Write and exit ldrsh_start bl read_memory_h ; Data fetch mov r1, r1, lsl #16 ; Sign extend mov r1, r1, asr #16 ; ldr lr, [sp], #4 ; Recover return address b write_reg ; Write and exit strh_code and r0, r10, #&00000060 ; SH bits cmp r0, #&00000020 ; Only legal pattern bne undef ; else fall into ... ; ldr r0, [pc, r0, lsr #3] ; ; add pc, pc, r0 ; ; ; DCD undef - %f1 ; Really already broken, ;1 ; but impossible to reach anyway ; DCD strh_start - %b1 ; ; DCD undef - %b1 ; Possible and should be trapped @@@ ; DCD undef - %b1 ; Possible and should be trapped @@@ ;strh_start mov r0, r10, lsr #12 ; bl read_reg ; ldr lr, [sp], #4 ; Recover return address b write_memory_h ; Write and return ; Could jump 1 instr. into "write_memory_h", but even less structured! ;------------------------------------------------------------------------------ swapb swap mov r0, r10, lsr #16 ; bl read_reg ; mov r2, r0 ; R2 := address mov r0, r10 ; bl read_reg ; R0 := store data tst r10, #Bbit ; Size appropriately bne swap_byte ; bl read_memory_w ; bl write_memory_w ; b swap1 ; swap_byte bl read_memory_b ; bl write_memory_b ; swap1 ldr lr, [sp], #4 ; Return address b write_reg ; Rd := R1 ;------------------------------------------------------------------------------ ; N.B. This performs memory cycles in either direction ; Not set up to handle data aborts lsm mov r0, r10, lsr #16 ; Fetch Rn bl read_reg ; mov r5, r0 ; Keep original value bic r2, r0, #3 ; and lose lowest bits here mov r3, #0 ; Accumulator mov r6, r10, lsl #16 ; Get bitmask mov r4, r6, lsr #16 ; Also keep a copy lsm_ngen movs r6, r6, lsl #1 ; Shift out each bit adc r3, r3, #0 ; adding into accumulator bne lsm_ngen ; until all bits gone tst r10, #Ubit ; subeq r2, r2, r3, lsl #2 ; If decrement mode eor r0, r10, r10, lsl #1 ; tst r0, #&01000000 ; "DA" or "IB"? addeq r2, r2, #4 ; Base addess bic r4, r4, #LSM_PC_bit ; Leave out PC mov r6, r9 ; Pointer to r0 mov r0, #-1 ; (Illegal) offset of register bank break tst r10, #LSM_S_bit ; or R12 + #4 & R13 bne lsm_hat ; S bit set - off main flow ; lsm_hat will indicate where (if anywhere) the paged registers start (i.e. R8 or R13) lsm_1 tst r10, #Ldbit ; Reentry after "^" checks bne ldm_loop ; Load mov r1, r0 ; Store - uses different reg. ; and fall into ... ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - stm_loop movs r4, r4, lsr #1 ; Next bit in reg. list bcc stm_1 ; clear ldr r0, [r6] ; Read register value bl write_memory_w ; add r2, r2, #4 ; Increment after stm_1 add r6, r6, #4 ; Move on one register cmp r6, r1 ; Only matches for "^" cases addeq r6, r6, #sim_R13_user - sim_R13 ; Extra offset needed here ; (Offset is same for R8/FIQ) tst r4, r4 ; bne stm_loop ; Becomes EQ when last R4 bit lost tst r10, #LSM_PC_bit ; PC? beq lsm_out ; Almost always not add r0, r11, #4 ; Synthesize PC+8 adr lr, lsm_out ; Get `return' address b write_memory_w ; and go ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ldm_loop movs r4, r4, lsr #1 ; Next bit in reg. list bcc ldm_1 ; clear bl read_memory_w ; add r2, r2, #4 ; Increment after str r1, [r6] ; ldm_1 add r6, r6, #4 ; Move on one register cmp r6, r0 ; Only matches for "^" cases addeq r6, r6, #sim_R13_user - sim_R13 ; Extra offset needed here ; (Offset is same for R8/FIQ) tst r4, r4 ; bne ldm_loop ; Becomes EQ when last R4 bit lost tst r10, #LSM_PC_bit ; PC? beq lsm_out ; bl read_memory_w ; mov r11, r1 ; Value into PC ; and fall into ... ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lsm_out tst r10, #Wbit ; See if register writeback used beq lsm_out1 ; No writeback tst r10, #Ubit ; subeq r0, r5, r3, lsl #2 ; If decrement mode addne r0, r5, r3, lsl #2 ; If increment mode mov r2, r10, lsr #16 ; Fetch Rn and r2, r2, #&F ; cmp r2, #15 ; Test for PC as base reg. strne r0, [r9, r2, lsl #2] ; Current register map ; not needed moveq r11, r0 ; or PC lsm_out1 tst r10, #LSM_S_bit ; S bit set? tstne r10, #Ldbit ; if so is it a load? tstne r10, #LSM_PC_bit ; if so is PC also set? ldreq pc, [sp], #4 ; Return if not all of these b spsr_copy_up ; else flip reg. bank (LDM PC^) ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lsm_hat tst r10, #Ldbit ; Jump here if S bit set tstne r10, #LSM_PC_bit ; LDM {PC}^ ? bne lsm_1 ; Exception return case ands r1, r12, #Mode_bits ; Z if user mode cmpne r1, #System_mode ; or if system mode beq lsm_1 ; R0 unchanged if current mode OK cmp r1, #FIQ_mode ; Where is the gap in reg. bank? moveq r0, #sim_R8 - Mon_RAM_start ; R8 if in FIQ mode movne r0, #sim_R13 - Mon_RAM_start ; R13 if not b lsm_1 ; ;------------------------------------------------------------------------------ branch tst r10, #Lbit ; strne r11, [r9, #sim_R14 - reg_block] ; Store link PC mov r0, r10, lsl #8 ; add r0, r11, r0, asr #6 ; Does sign extension add r11, r0, #4 ; Correct for PC+8 ldr pc, [sp], #4 ; Return ;------------------------------------------------------------------------------ sys tst r10, #&01000000 ; Possibly a copro op. beq sys_copro ; ; SWI adr r10, SWI_data ; R10 safe - usually IR b exception_entry ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lsc ; For now drop into undefined sys_copro ; For now drop into undefined undef adr r10, undef_data ; R10 safe - usually IR b exception_entry ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IRQ_entry str lr, [sp, #-4]! ; adr r10, IRQ_data ; R10 safe - usually IR b exception_entry ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FIQ_entry str lr, [sp, #-4]! ; adr r10, FIQ_data ; R10 safe - usually IR exception_entry ldrb r1, [r10] ; Get SPSR offset str r12, [r9, r1] ; Save current CPSR ldrb r1, [r10, #1] ; Get new mode code and r0, r12, #&F ; Current mode bl mode_reg_swap ; Trashes registers :-( not R10 ldrb r1, [r10, #2] ; Get mode mask bic r12, r12, r1 ; Lose most/all of status ldrb r1, [r10, #3] ; Get new mode bits orr r12, r12, r1 ; ldrb r1, [r10, #4] ; Get LR offset add r11, r11, r1 ; Adjust PC before saving str r11, [r9, #sim_R14 - reg_block] ; Save link (new mode) ldrb r11, [r10, #5] ; Set PC for exception ldr pc, [sp], #4 ; Return SWI_data DCB sim_SPSR_svc - reg_block; SPSR offset DCB Supervisor_mode ; DCB &BF ; Bits cleared in mode byte DCB I_bit :OR: mode32 :OR: Supervisor_mode DCB 0 ; LR offset DCB &08 ; `Vector' address undef_data DCB sim_SPSR_undef-reg_block; SPSR offset DCB Undefined_mode ; DCB &BF ; Bits cleared in mode byte DCB I_bit :OR: mode32 :OR: Undefined_mode DCB 0 ; LR offset DCB &04 ; `Vector' address IRQ_data DCB sim_SPSR_irq - reg_block; SPSR offset DCB IRQ_mode ; DCB &BF ; Bits cleared in mode byte DCB I_bit :OR: mode32 :OR: IRQ_mode DCB 4 ; LR offset DCB &18 ; `Vector' address FIQ_data DCB sim_SPSR_fiq - reg_block; SPSR offset DCB FIQ_mode ; DCB &FF ; Bits cleared in mode byte DCB I_bit :OR: F_bit :OR: mode32 :OR: FIQ_mode DCB 4 ; LR offset DCB &1C ; `Vector' address ALIGN ;------------------------------------------------------------------------------ ; Swap register map from mode R0 to mode R1 ; Needs r9 => register bank ; Corrupts r0-r6 mode_reg_swap adr r2, reg_start_tab ; Pointer to register position cmp r0, #FIQ_mode ; cmpne r1, #FIQ_mode ; bne mode_reg_swap_no_FIQ ; stmfd sp!, {r2, r7} ; Need to move 5 more registers cmp r0, #FIQ_mode ; From FIQ? addeq r2, r9, #sim_R8_FIQ - reg_block ; Yes! addne r2, r9, #sim_R8_user - reg_block; No, user add r9, r9, #sim_R8 - reg_block ; Start with R8 ldmia r9, {r3-r7} ; Out with the old stmia r2, {r3-r7} ; cmp r1, #FIQ_mode ; To FIQ? addeq r2, r9, #sim_R8_FIQ - sim_R8 ; Yes! (R9 already offset to =>R8) addne r2, r9, #sim_R8_user - sim_R8 ; No, user ldmia r2, {r3-r7} ; In with the new stmia r9, {r3-r7} ; sub r9, r9, #sim_R8 - reg_block; Put R9 correct again ldmfd sp!, {r2, r7} ; mode_reg_swap_no_FIQ ldrb r0, [r2, r0] ; Old reg. offset ldrb r1, [r2, r1] ; New reg. offset add r0, r9, r0, lsl #2 ; Old reg. base add r1, r9, r1, lsl #2 ; New reg. base add r2, r9, #sim_R13 - reg_block ; Start at R13 ldmia r2, {r3, r4} ; Out with the old stmia r0, {r3, r4} ; ldmia r1, {r3, r4} ; In with the new stmia r2, {r3, r4} ; mov pc, lr ; ; R13 offsets reg_start_tab DCB (sim_R13_user - reg_block)/4 ; User DCB (sim_R13_FIQ - reg_block)/4 ; FIQ DCB (sim_R13_IRQ - reg_block)/4 ; IRQ DCB (sim_R13_svc - reg_block)/4 ; Supervisor DCB 0 ; DCB 0 ; DCB 0 ; DCB (sim_R13_abt - reg_block)/4 ; Abort DCB 0 ; DCB 0 ; DCB 0 ; DCB (sim_R13_undef - reg_block)/4 ; Undefined DCB 0 ; DCB 0 ; DCB 0 ; DCB (sim_R13_user - reg_block)/4 ; System ALIGN ;------------------------------------------------------------------------------ ; Returns value in R1, CF = shifter_carry_out shifted_Rm and r1, r10, #&F ; cmp r1, #&F ; Test for PC ldrne r1, [r9, r1, lsl #2] ; Current register map addeq r1, r11, #4 ; or PC tst r10, #&00000FF0 ; R1 already loaded bne shifted_Rm1 ; ; Fall through if unshifted tst r12, r12, lsl #3 ; Set CF to carry mov pc, lr ; and return in most frequent case shifted_Rm1 and r0, r10, #&60 ; R0 := shift type tst r10, #&00000010 ; Register based shift? bne shifted_Rm_reg ; mov r2, r10, lsr #7 ; ands r2, r2, #&1F ; R2 := shift amount orreq r2, r2, #&20 ; 0=>32 (LSL already gone) cmpeq r0, #&60 ; RRX? bne do_shift ; tst r12, r12, lsl #3 ; Set CF to carry movs r1, r1, rrx ; mov pc, lr ; shifted_Rm_reg and r2, r10, #&00000F00 ; Rs field cmp r2, #&00000F00 ; Test for PC ldrne r2, [r9, r2, lsr #6] ; Current register map addeq r2, r11, #4 ; or PC and r2, r2, #&FF ; Bottom byte only do_shift tst r12, r12, lsl #3 ; Set CF to carry add pc, pc, r0, lsr #2 ; Index in 2 word steps nop ; Can use this word for data @@@ ; PC based on next instruction shift_lsl movs r1, r1, lsl r2 ; LSL mov pc, lr ; CF = shifter_carry_out shift_lsr movs r1, r1, lsr r2 ; LSR mov pc, lr ; CF = shifter_carry_out shift_asr movs r1, r1, asr r2 ; ASR mov pc, lr ; CF = shifter_carry_out shift_ror movs r1, r1, ror r2 ; ROR mov pc, lr ; CF = shifter_carry_out ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - immediate mov r0, r10, lsr #7 ; and r0, r0, #&1E ; Rotate amount and r1, r10, #&FF ; Immediate byte tst r12, r12, lsl #3 ; Set CF to carry movs r1, r1, ror r0 ; Carry set if required mov pc, lr ; ; CPSR carry flag is shifter_carry_out read_reg and r0, r0, #&F ; cmp r0, #&F ; Test for PC ldrne r0, [r9, r0, lsl #2] ; Current register map addeq r0, r11, #4 ; or PC mov pc, lr ; write_reg and r0, r10, #&0000F000 ; Rd cmp r0, #&0000F000 ; Test for PC strne r1, [r9, r0, lsr #10] ; Current register map moveq r11, r1 ; or PC mov pc, lr ; ;------------------------------------------------------------------------------ ; Check for breakpoint - address R2, data R1 ; Corrupts R0 (or uses it for breakpoint number) ; Returns carry set if breakpoint NOT found ; Not suitable for watchpoints yet @@@ breakpoint_check stmfd sp!, {r3-r5, lr} ; mov r3, #breakpoint_table - Mon_RAM_start mov r0, #0 brkpt_rpt ldrb r14, [r3, #BP_active - breakpoint_table] ; Look at flag cmp r14, #3 ; This one active? bne brkpt_next ; No, move along ldrh r5, [r3, #BP_type] ; Cheat, fetching halfword! tst r5, #&0400 ; 32-bit allowed? beq brkpt_next ; No, move along tst r5, #&0020 ; Read allowed? beq brkpt_next ; No, move along ; Mode is in R12 EXCEPT for watchpoints during LSM^ @@@ tst r12, #&F ; Get fetch mode moveq r4, #&80 ; In user mode movne r4, #&40 ; In kernel mode tst r5, r4 ; Check mode is legal beq brkpt_next ; Tested bit clear ldr r14, [r3, #BP_addr_A] ; Start address comparison ands r4, r5, #&000C ; Address comparison type beq brkpt_next ; 00 not defined cmp r4, #&0008 ; beq brkpt_1 ; 10 is a <= x <= b blo brkpt_next ; 01 not defined eor r4, r2, r14 ; Set unequal bits ldr r14, [r3, #BP_addr_B] ; tst r4, r14 ; Mask off don't cares bne brkpt_next ; Some bits still don't match b brkpt_2 ; Now try data ... brkpt_1 cmp r2, r14 ; blo brkpt_next ; ldr r14, [r3, #BP_addr_B] ; cmp r2, r14 ; bhi brkpt_next ; brkpt_2 ldr r14, [r3, #BP_data_A] ; Start data comparison ands r4, r5, #&0003 ; Data comparison type beq brkpt_next ; 00 not defined cmp r4, #&0002 ; beq brkpt_3 ; 10 is a <= x <= b blo brkpt_next ; 01 not defined eor r4, r1, r14 ; Set unequal bits ldr r14, [r3, #BP_data_B]; tst r4, r14 ; Mask off don't cares bne brkpt_next ; Some bits still don't match b brkpt_4 ; Found one! brkpt_3 ldr r14, [r3, #BP_data_A] ; Data comparison cmp r1, r14 ; blo brkpt_next ; ldr r14, [r3, #BP_data_B] ; cmp r1, r14 ; bhi brkpt_next ; brkpt_4 ldrb r4, [r7, #arm_state - shared_variables] strb r4, [r7, #arm_state_old - shared_variables] mov r4, #State_stop_bkpt ; Stop (Breakpoint number in R0) strb r4, [r7, #arm_state - shared_variables] ; Signal to processor movs r3, #&000FF000 ; Clear carry flag b brkpt_out ; to indicate breakpoint found brkpt_next add r3, r3, #brk_pt_rcd_length add r0, r0, #1 ; Count up until ... cmp r0, #breakpoint_max ; blo brkpt_rpt ; (i.e. carry clear) brkpt_out ldmfd sp!, {r3-r5, pc} ; Carry set if okay ;------------------------------------------------------------------------------ RAM_image_end ; New label END