; Napoleon Operating System v2.0 ;--------------------------------- ; Spring 1995 Jake Janovetz ; University of Illinois ; Advanced Digital Systems Lab ;--------------------------------- ; NOTE: "Operating System" here is used very loosely, since all the NACHOS ; really does is provide CODEC access routines and a monitor to the host. ; ; - On-board section of operating system structures and calls ; - Monitor included in this version ; ; Host Interface Vectors $24-$3a are reserved for boot code and the ; NACHOS. Host Vectors $40-$7e are available for user code use. ; nolist include 'nachos.inc' list ; Install all interrupts that NACHOS will respond to. These are: ; SWI - Catches software interrupts used for breakpoints ; Trace - Catches return from a trace instruction interrupt ; ->Trace is installed as needed since the return address changes ; SSI Tx - Transmit interrupt used for CODEC communication (does receive too) ; SSI Te - SSI transmit with error - does nothing but acknowledge error ; HSTCMD - Catches host vector $2a to enter Monitor ; ; SSI interface interrupts org p:i_ssitd ; Trap SSI transmit interrupts: movep y:(r7)+,x:m_tx ; - Transmit from y:(r7) movep x:m_rx,x:(r7) ; - Receive to x:(r7) org p:i_ssitde ; Trap SSI transmit (error): movep x:m_sr,x:m_tx ; - Clear TUE in SSI:tx to ack error ; OS routine jump table org p:jumptable jmp initcs jmp closecs ; Monitor routines interrupts org p:i_swi ; Trap SWI interrutps: jsr swiint ; - Reenter the monitor (breakpoints) org p:i_hstcm+6 ; Host Vector $2a - Enter Monitor jsr monitor ; ; Equates for NACHOS and Monitor ; LEAVE equ 0 GO equ 1 TRACE equ 2 READBP equ 3 WRITEBP equ 4 CLEARBP equ 5 READREG equ 10 WRITEREG equ 11 READREGS equ 12 WRITEREGS equ 13 READSTACK equ 14 WRITESTACK equ 15 READMEM equ 16 WRITEMEM equ 17 MAXBP equ 15 NumReg equ 40 NumStk equ 30 SWIop equ $000006 JSRop equ $0bf080 ; This is the beginning of "topcode" which is high memory set aside for ; NACHOS and the monitor. Programs should not touch this area. TOPCODE ; is defined in 'nachos.inc' ; org p:topcode ; Registers used for context saving when monitor is entered ; addrreg ; 24 Address ALU Registers reg_r0 dc 0 reg_r1 dc 0 reg_r2 dc 0 reg_r3 dc 0 reg_r4 dc 0 reg_r5 dc 0 reg_r6 dc 0 reg_r7 dc 0 reg_n0 dc 0 reg_n1 dc 0 reg_n2 dc 0 reg_n3 dc 0 reg_n4 dc 0 reg_n5 dc 0 reg_n6 dc 0 reg_n7 dc 0 reg_m0 dc $ffff reg_m1 dc $ffff reg_m2 dc $ffff reg_m3 dc $ffff reg_m4 dc $ffff reg_m5 dc $ffff reg_m6 dc $ffff reg_m7 dc $ffff datareg ; 10 words for Data ALU Registers reg_x1 dc 0 reg_x0 dc 0 reg_y1 dc 0 reg_y0 dc 0 reg_a2 dc 0 reg_a1 dc 0 reg_a0 dc 0 reg_b2 dc 0 reg_b1 dc 0 reg_b0 dc 0 progreg ; 6 Program Control Registers reg_pc dc 0 reg_sr dc 0 reg_omr dc 0 reg_sp dc 0 reg_la dc 0 reg_lc dc 0 extras ; Extras like processor state reg_bcr dc 0 reg_ipr dc 0 stack ; 30 Stack positions dup 30 ; Interleaved: SSL, SSH, SSL, SSH, ... dc 0 endm bpnum dc 0 ; Number of breakpoints set bpinfo dup MAXBP ; Save up to MAXBP breakpoints dc 0 ; Save the address of the breakpoint dc 0 ; Save the instruction at the breakpoint endm swiUSR1 dc 0 swiUSR2 dc 0 temp1 dc 0 temp2 dc 0 ; saveContext macro ;------------- ; Provides an easy way to save the user's context upon entry to a debug ; routine. The interrupt calling the routine stacks the PC and SR, so ; we must save these first, then call saveregfile to save all other ; registers and the stack ; saveContext macro move r1,p:reg_r1 move r2,p:reg_r2 move ssl,r1 ; Save the SR from the interrupt call move ssh,r2 ; Save the PC (postdecrement SP) jsr saveregfile endm ; saveregfile ;------------- ; - Expects SP and PC to be stored in R1 and R2, respectively. This is ; done in the saveContext macro ; - Then saves all registers to data space ; saveregfile move r0,p:reg_r0 move m0,p:reg_m0 move #reg_r3,r0 ; saveContext macro saves r1,r2 move #-1,m0 nop move r3,p:(r0)+ move r4,p:(r0)+ move r5,p:(r0)+ move r6,p:(r0)+ move r7,p:(r0)+ move n0,p:(r0)+ move n1,p:(r0)+ move n2,p:(r0)+ move n3,p:(r0)+ move n4,p:(r0)+ move n5,p:(r0)+ move n6,p:(r0)+ move n7,p:(r0)+ move (r0)+ ; Already saved m0 move m1,p:(r0)+ move m2,p:(r0)+ move m3,p:(r0)+ move m4,p:(r0)+ move m5,p:(r0)+ move m6,p:(r0)+ move m7,p:(r0)+ move x1,p:(r0)+ move x0,p:(r0)+ move y1,p:(r0)+ move y0,p:(r0)+ move a2,p:(r0)+ move a1,p:(r0)+ move a0,p:(r0)+ move b2,p:(r0)+ move b1,p:(r0)+ move b0,p:(r0)+ move r2,p:(r0)+ ; PC from interrupt move r1,p:(r0)+ ; SR from interrupt move omr,p:(r0)+ ; OMR move ssl,r1 ; Read caller's SR from stack move ssh,r2 ; Pop caller's PC (postdecrement SP) move sp,p:(r0)+ ; SP move la,p:(r0)+ ; LA move lc,p:(r0)+ ; LC movep x:m_bcr,p:(r0)+ ; Bus Control Register movep x:m_ipr,p:(r0)+ ; Interrupt Priority Register ; Save the user's stack ; - Top of stack is stored at 'p:stack'. ; - Stack is stored in order: L1, H1, L2, H2, L3, H3, ... where L1 is ; the top of the 'low' system stack, and H0 is the top of the 'high' ; system stack. ; move #stack,r0 _stksave movec sp,a tst a jeq _stksaveend ; If stack is empty, stop saving move ssl,p:(r0)+ ; SSLx move ssh,p:(r0)+ ; SSHx jmp _stksave _stksaveend nop ; Done saving the stack. Now, retrieve the caller's PC and SR so we can ; return to caller ; move r2,ssh ; Push caller's PC (preincrement SP) move r1,ssl ; Place caller's SR onto stack nop rts ; restoreContext macro ;---------------- ; Provides a macro to restore the user's context. Requires no previous ; setup. ; restoreContext macro jmp restregfile endm ; restregfile ;------------- ; Restores entire user context, then executes an 'rti' to start executing ; user code again. ; restregfile ; Restore user's stack first move #0,sp ; Clear stack move #stack-1,r0 move p:reg_sp,n0 nop move (r0)+n0 move (r0)+n0 ; Start at the end of the stack move p:reg_sp,a1 ; a is our counter move #>1,x0 _stkrest tst a jeq _stkrestend ; if a=0, we're done pushing to stack sub x0,a move p:(r0)-,ssh move p:(r0)-,ssl jmp _stkrest _stkrestend ; Now restore user's register context move #reg_r1,r0 nop move p:(r0)+,r1 move p:(r0)+,r2 move p:(r0)+,r3 move p:(r0)+,r4 move p:(r0)+,r5 move p:(r0)+,r6 move p:(r0)+,r7 move p:(r0)+,n0 move p:(r0)+,n1 move p:(r0)+,n2 move p:(r0)+,n3 move p:(r0)+,n4 move p:(r0)+,n5 move p:(r0)+,n6 move p:(r0)+,n7 move (r0)+ ; Don't restore M0 yet move p:(r0)+,m1 move p:(r0)+,m2 move p:(r0)+,m3 move p:(r0)+,m4 move p:(r0)+,m5 move p:(r0)+,m6 move p:(r0)+,m7 move p:(r0)+,x1 move p:(r0)+,x0 move p:(r0)+,y1 move p:(r0)+,y0 move p:(r0)+,a2 move p:(r0)+,a1 move p:(r0)+,a0 move p:(r0)+,b2 move p:(r0)+,b1 move p:(r0)+,b0 move p:(r0)+,ssh ; Push user's PC (preincrement SP) move p:(r0)+,ssl ; Push user's SR move p:(r0)+,omr move (r0)+ ; Skip SP move p:(r0)+,la move p:(r0)+,lc movep p:(r0)+,x:m_bcr movep p:(r0)+,x:m_ipr move p:reg_r0,r0 ; Now we can restore r0 and m0 move p:reg_m0,m0 rti ; Restore user's SR and jump to user's PC ;------------------------------------------------------------------------- ; Monitor - The monitor sits in high memory and waits for entry. Entry ; is performed by calling the appropriate Host Interrupt. See ; above. Then, the monitor waits for commands to appear on ; the host port. It does this in an infinite loop until the ; ExitMonitor command is given. ;------------------------------------------------------------------------- ; Entry point to the monitor. To be called from the interrupt service ; vector ONLY. ; monitor saveContext bset #m_hf3,x:m_hcr ; Set HF3 in HCR (Debug mode=ON) jsr restoreBP ; Put breakpoint instructions back monitorCmd jclr #0,x:m_hsr,* ; Wait for an incoming debug command move x:m_hrx,x0 ; Move the command into x0 move #>LEAVE,a1 cmp x0,a jeq monitorLeave move #>GO,a1 cmp x0,a jeq monitorGo move #>TRACE,a1 cmp x0,a jeq monitorTrace move #>READBP,a1 cmp x0,a jeq monitorReadBP move #>WRITEBP,a1 cmp x0,a jeq monitorSetBP move #>CLEARBP,a1 cmp x0,a jeq monitorClearBP move #>READREG,a1 cmp x0,a jeq monitorReadReg move #>WRITEREG,a1 cmp x0,a jeq monitorWriteReg move #>READREGS,a1 cmp x0,a jeq monitorReadRegs move #>WRITEREGS,a1 cmp x0,a jeq monitorWriteRegs move #>READSTACK,a1 cmp x0,a jeq monitorReadStack move #>WRITESTACK,a1 cmp x0,a jeq monitorWriteStack move #>READMEM,a1 cmp x0,a jeq monitorReadMem move #>WRITEMEM,a1 cmp x0,a jeq monitorWriteMem jmp monitorCmd ;------------------------------------------------------------------------- ; monitorReadReg ;------------------------------------------------------------------------- ; Reads the register offset number (according to the register file storage ; area) and returns the value of that register to the Host Interface. ; monitorReadReg jclr #0,x:m_hsr,* ; Wait for incoming host data movep x:m_hrx,n0 ; Read data into x0 (register number) move #addrreg,r0 ; Register table address nop movem p:(r0+n0),x0 ; Read value from user context movep x0,x:m_htx ; Write value to the HI jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorWriteReg ;------------------------------------------------------------------------- ; Reads a register number and register value from the Host Interface and ; sets the corresponding register (in the user's context) to that value. ; monitorWriteReg move #addrreg,r0 ; Register table address jclr #0,x:m_hsr,* ; Wait for incoming host data movep x:m_hrx,n0 ; Read data into x0 (register number) jclr #0,x:m_hsr,* ; Wait for incoming host data movep x:m_hrx,x0 ; Read data into x0 (register value) movem x0,p:(r0+n0) ; Change user's register file jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorReadRegs ;------------------------------------------------------------------------- ; Sends register file values through host port ; monitorReadRegs move #addrreg,r0 ; Start at register R0 do #NumReg,_l1 ; Move registers values jclr #1,x:m_hsr,* ; Wait for host to be ready movem p:(r0)+,x0 ; Read value from user context movep x0,x:m_htx ; Write value to the HI _l1 nop jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorWriteRegs ;------------------------------------------------------------------------- ; ; monitorWriteRegs jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorReadStack ;------------------------------------------------------------------------- ; ; monitorReadStack move #stack,r0 ; Start at SSL1 do #30,_l1 ; Move stack values jclr #1,x:m_hsr,* ; Wait for host to be ready movem p:(r0)+,x0 ; Read value from user context movep x0,x:m_htx ; Write value to the HI _l1 nop jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorWriteStack ;------------------------------------------------------------------------- ; ; monitorWriteStack jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorReadMem ;------------------------------------------------------------------------- ; Sends register file values through host port ; monitorReadMem jclr #0,x:m_hsr,* ; Wait for data on HI movep x:m_hrx,x0 ; Retreive the data space jclr #0,x:m_hsr,* ; Wait for data on HI movep x:m_hrx,r0 ; Retreive starting address jclr #0,x:m_hsr,* ; Wait for data on HI movep x:m_hrx,n0 ; Retreive length of data ; Call the appropriate subroutine (p, x, or y memory space) move #>0,a1 cmp x0,a jeq _readp move #>1,a1 cmp x0,a jeq _readx move #>2,a1 cmp x0,a jeq _ready jmp monitorCmd _readp do n0,_readpend jclr #1,x:m_hsr,* ; Wait for host to be ready movep p:(r0)+,x:m_htx ; Move data from X & increment addr _readpend jmp monitorCmd ; Go back to debug-wait state _readx do n0,_readxend jclr #1,x:m_hsr,* ; Wait for host to be ready movep x:(r0)+,x:m_htx ; Move data from X & increment addr _readxend jmp monitorCmd ; Go back to debug-wait state _ready do n0,_readyend jclr #1,x:m_hsr,* ; Wait for host to be ready movep y:(r0)+,x:m_htx ; Move data from Y & increment addr _readyend jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; monitorWriteMem ;------------------------------------------------------------------------- ; Sends register file values through host port ; monitorWriteMem jmp monitorCmd ; Go back to debug-wait state ;------------------------------------------------------------------------- ; Set Breakpoint ;------------------------------------------------------------------------- ; - Reads address from host port and sets a breakpoint at this ; address. ; - Abort if: ; 1- We have MAXBP breakpoints already ; 2- We already have a breakpoint at the given address ; monitorSetBP jclr #0,x:m_hsr,* ; Wait for incoming host data movep x:m_hrx,x0 ; Read data into x0 (address of breakpoint) movem p:bpnum,a move #>MAXBP,x1 ; If we exceed the maximum number of cmp x1,a ; breakpoints, then go back to wait jge monitorCmd ; for monitor commands (abort) move #bpinfo,r0 tst a ; If there are no breakpoints set yet, jeq _setbp1 ; we don't have to check for duplicates move a1,n1 ; Check the breakpoint table for repeats do n1,_setbp1 movem p:(r0)+,a ; get BP already in table cmp x0,a ; compare BP address to table address jne _cont enddo jmp monitorCmd ; Repeat breakpoint found -> (abort) _cont move (r0)+ _setbp1 movem x0,p:(r0) ; Put new breakpoint address into table movem p:bpnum,a move #>1,x0 ; We've added a breakpoint, so increment add x0,a ; the number in 'bpnum' movem a1,p:bpnum jmp monitorCmd ; Go back and wait for monitor commands ;------------------------------------------------------------------------- ; Clear Breakpoint ;------------------------------------------------------------------------- ; - Reads an address from the host port and clears the breakpoint set ; at this address ; - Aborts if there is no breakpoint set at the given address ; - When the breakpoint is not the last one in the table, it moves all ; ones after it to fill the table ; monitorClearBP jclr #0,x:m_hsr,* ; Wait for incoming host data move x:m_hrx,x0 ; Read data into x0 (address of breakpoint) movem p:bpnum,a tst a jeq monitorCmd ; If there are no breakpoints, abort move a1,n1 move #bpinfo,r0 move #2,n0 do n1,_clrbp2 movem p:(r0),a movec lc,n1 cmp x0,a jne _clrcont enddo do n1,_clrbp1 ; Move breakpoints after the deleted movem p:(r0+n0),a ; breakpoint up in the table movem a,p:(r0)+n0 _clrbp1 move p:bpnum,a ; We've removed a breakpoint, so we move #>1,x0 ; have to decrement 'bpnum' sub x0,a move a,p:bpnum jmp _clrbp2 _clrcont move (r0)+n0 _clrbp2 jmp monitorCmd ;------------------------------------------------------------------------- ; Query Breakpoints ;------------------------------------------------------------------------- ; - First, sends number of set breakpoints to host ; - Second, sends the addresses of those breakpoints to host ; monitorReadBP move p:bpnum,a jclr #1,x:m_hsr,* movep a,x:m_htx ; Send number of breakpoints first tst a jeq monitorCmd ; If there are no breakpoints, exit move a1,n1 move #bpinfo,r0 move #2,n0 do n1,_readbp1 move p:(r0)+n0,x0 ; Get breakpoint address jclr #1,x:m_hsr,* ; Wait for host to be ready movep x0,x:m_htx ; Send breakpoint address to host _readbp1 jmp monitorCmd ;------------------------------------------------------------------------- ; Leave Monitor = Exit monitor entirely ;------------------------------------------------------------------------- ; - Clears HF3 (not in monitor anymore) ; - Restores user context ; - Does NOT place SWIs in place of breakpoints ; monitorLeave clr a move a,p:bpnum ; No more breakpoints bclr #m_hf3,x:m_hcr ; Clear HF3 in HCR (Debug mode=OFF) restoreContext ; Restore context and run user code ;------------------------------------------------------------------------- ; Go = Exit monitor for execution until breakpoint ;------------------------------------------------------------------------- ; - Calls saveBP to put SWI in place of breakpoint opcodes ; - Lowers HF3 to signify monitor is not running ; - Restores user context to continue program ; ; - If user's PC points to a breakpoint: ; 1- Single step the breakpoint instruction ; 2- Go as normal ; monitorGo movem p:reg_pc,x0 movem p:bpnum,n1 move #bpinfo,r0 move #-1,m0 move #2,n0 do n1,_go1 movem p:(r0)+n0,a cmp x0,a jne _nogo enddo jmp goTrace _nogo nop _go1 monitorGo1 jsr saveBP ; Put SWI in place of breakpoints bclr #m_hf3,x:m_hcr ; Clear HF3 in HCR (Debug mode=OFF) restoreContext ; Restore context and run user code goTrace move #goTrInt,r0 ; Set Trace to return to 'goTrInt' jmp setTraceInt ; Trace one instruction goTrInt bclr #13,ssl saveContext jmp monitorGo1 ; Do a 'go' as normal ;------------------------------------------------------------------------- ; saveBP ;------------------------------------------------------------------------- ; - Used upon leaving the monitor ; - To create breakpoints, SWI opcodes must be used in place of the original ; opcode. Therefore, the original opcodes must be stored in a breakpoint ; table. ; saveBP clr a move p:bpnum,a1 tst a jeq _saveBP1 ; Skip if there are no breakpoints move a1,n0 move #bpinfo,r0 move #>SWIop,x1 do n0,_saveBP1 move p:(r0)+,r1 ; Get address of breakpoint nop move p:(r1),x0 ; Get instruction from address move x0,p:(r0)+ ; Save instruction in BP table move x1,p:(r1) ; Replace with SWI _saveBP1 rts ;------------------------------------------------------------------------- ; restoreBP ;------------------------------------------------------------------------- ; - Used upon entering the monitor ; - Since SWI opcodes were put in place of the original instruction to ; create breakpoints, those SWIs must be replaced (from the breakpoint ; table) in case instructions are read during monitoring ; restoreBP movem p:bpnum,a tst a jeq _restbp1 ; Skip if there are no breakpoints move a1,n0 move #bpinfo,r0 do n0,_restbp1 move p:(r0)+,r1 ; Get address of breakpoint move p:(r0)+,x0 ; Get original instruction opcode move x0,p:(r1) ; Place opcode back into memory _restbp1 rts ;------------------------------------------------------------------------- ; monitorTrace ;------------------------------------------------------------------------- ; - No setup required on host interface ; - Sets Trace Bit in user's SR, then restores context ; - After the trace, the monitor is entered at 'traceint' ; monitorTrace move #traceInt,r0 ; setTraceInt ; ; - Expects a return address to be in r0. ; - When the trace command is complete, the trace interrupt engages and ; starts executing a (long) interrupt at the address in r0. ; -> See 'monitorTrace' and 'goTrace' for examples ; setTraceInt move #>JSRop,y0 movem y0,p:i_trace ; Place JSR at Trace Interrupt vector movem r0,p:i_trace+1 ; Place new Trace Interrupt vector move p:reg_sr,r0 bset #13,r0 ; Set Trace Mode in MR movem r0,p:reg_sr restoreContext ; Trace Interrupt entry point ;----------------------------- ; - Instruction traces cause monitor code to be entered here ; traceInt bclr #13,ssl ; Clear the trace flag in user's SR, saveContext bset #m_hf3,x:m_hcr ; Set HF3 in HCR (Debug mode=ON) jmp monitorCmd ; Then go wait for monitor commands ; Software Interrupt entry point ;-------------------------------- ; - Breakpoints cause monitor code to be entered here ; swiint saveContext bset #m_hf3,x:m_hcr ; Set HF3 in HCR (Debug mode=ON) movem p:reg_pc,a ; After SWI is executed, PC is one more than move #>1,x0 ; it should be, so we must decrement it sub x0,a movem a,p:reg_pc jsr restoreBP ; Put breakpoint instructions back jmp monitorCmd ; Then go wait for monitor commands ;************************************************************************* ;************************ END MONITOR CODE ******************************* ;************************************************************************* ;------------------------------------------------------------------------- ; initcs ;------------------------------------------------------------------------- ; Configure Port C (SSI and LED port) ; LED0 = Port C RXD = bit 0 (GPIO) ; LED1 = Port C TXD = bit 1 (GPIO) ; LED2 = Port C SCLK = bit 2 (GPIO) ; CS PDN = Port C SC0 = bit 3 (GPIO) ; CS D/C = Port C SC1 = bit 4 (GPIO) ;Frame Sync = Port C SC2 = bit 5 ; CS SCLK = Port C SCK = bit 6 ; CS SDOUT = Port C SRD = bit 7 ; CS SDIN = Port C STD = bit 8 initcs movep #$0000,x:m_pcc ; Turn OFF SSI for now movep #$001f,x:m_pcddr ; GPIO pins are outputs bclr #3,x:m_pcd ; D/C low - enter Control mode bclr #4,x:m_pcd ; PDN low - Power up CODEC movep #$0000,x:m_pcd ; Wait 50ms after PDN goes low to send control blocks ; (1000*1000 * 2 clock cycles per NOP / 40MHz = 0.050 seconds) do #1000,_d2 do #1000,_d1 nop _d1 nop _d2 ; Setup SSI port ; movep #$4f05,x:m_cra ; 16-bit, 16 frames movep #$3b30,x:m_crb ; generate SCLK and FS for CS4216 movep #$01e0,x:m_pcc ; Port C: SSI=ON, SCI=OFF ; Now, send control blocks until CLB goes low signifying proper transition ; to Data Mode ; move #csconf,r0 move #3,m0 _wakeup jsr outblk jset #2+16,x: