////////////////////////////////////////////////////////////////////// // // // cpuThumbCore.pas: Thumb instruction set decoder and executer // // Decodes and executes Thumb instructions // // // // The contents of this file are subject to the Bottled Light // // Public License Version 1.0 (the "License"); you may not use this // // file except in compliance with the License. You may obtain a // // copy of the License at http://www.bottledlight.com/BLPL/ // // // // Software distributed under the License is distributed on an // // "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or // // implied. See the License for the specific language governing // // rights and limitations under the License. // // // // The Original Code is the Mappy VM Core, released April 1st, 2003 // // The Initial Developer of the Original Code is Bottled Light, // // Inc. Portions created by Bottled Light, Inc. are Copyright // // (C) 2001 - 2003 Bottled Light, Inc. All Rights Reserved. // // // // Author(s): // // Michael Noland (joat), michael@bottledlight.com // // // // Changelog: // // 1.0: First public release (April 1st, 2003) // // // // Notes: // // This operates differently to a real ARM7T processor, since // // it directly executes Thumb opcodes. The ARM7T converts them // // to equivelant ARM instructions at no speed penalty, but that // // would require two complete decodes and a conversion if done // // in software. // // // // There are still a few bugs either here or in the ARM core, as // // evidenced by the glitches in the BIOS display sequence. It // // *could* be a result of a bug in the graphics code, but it is // // pretty unlikely. // // // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// unit cpuThumbCore; /////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // // // CPU - Thumb instruction set decoder: // // Decodes and executes thumb opcodes // // // // All code contained unless explicitly stated is (C) Copyright // // January 8th 2001 to Present by Michael Noland, part of Mappy VM // // // ////////////////////////////////////////////////////////////////////// interface //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // EmulateThumb() emulates thumb opcodes until one of the following // conditions is met: // the cycle quota is exhausted // the CPU switches into ARM mode // a breakpoint is encountered procedure EmulateThumb; ////////////////////////////////////////////////////////////////////// implementation /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// uses SysUtils, nexus, AddressSpace, cpuMemory, cpuMisc, cpuARMCore; ////////////////////////////////////////////////////////////////////// procedure EmulateThumb; var t: uint16; rs, rd, c: byte; cond: boolean; i, test: uint32; neg1, neg2, negr: boolean; operand1, operand2: uint32; shift, shiftType: byte; begin repeat if irqPending and not irqDisabled then Exit; {$IFDEF SIGNATURES} // Add a thumb signature to the address of the current opcode operand2 := regs[R15]-2; if operand2 >= $08000000 then begin operand2 := (operand2 shr 1) and $FFFFFF; sigs[operand2] := sigs[operand2] or THUMB_READ; end; {$ENDIF} // Grab an opcode from the pipeline and read in a new opcode t := regs[PIPELINE_0]; regs[PIPELINE_0] := regs[PIPELINE_1]; // at currentAddress + L Inc(regs[R15], 2); regs[PIPELINE_1] := memReadHalfwordUnc(regs[R15]); // at currentAddress + 2L // Do a coarse search based on the top 8 bits of each opcode c := t shr 8; case c of $00..$07: begin // Format 1: Move shifted register, [lsl Rd, Rs, #Offset5] test := BarrelShifter(regs[(t shr 3) and 7], LSL, (t shr 6) and 31); carry := barrelCarry; negative := test shr 31 <> 0; zero := test = 0; regs[t and 7] := test; end; $08..$0F: begin // Format 1: Move shifted register, [lsr Rd, Rs, #Offset5] test := BarrelShifter(regs[(t shr 3) and 7], LSR, (t shr 6) and 31); carry := barrelCarry; negative := test shr 31 <> 0; zero := test = 0; regs[t and 7] := test; end; $10..$17: begin // Format 1: Move shifted register, [asr Rd, Rs, #Offset5] test := BarrelShifter(regs[(t shr 3) and 7], ASR, (t shr 6) and 31); carry := barrelCarry; negative := test shr 31 <> 0; zero := test = 0; regs[t and 7] := test; end; $18..$19: begin // Format 2: add, [add Rd, Rs, Rn] operand1 := regs[(t shr 3) and 7]; operand2 := regs[(t shr 6) and 7]; test := operand1 + operand2; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negative := test shr 31 <> 0; overflow := (neg1 = neg2) and (neg1 <> negative); carry := (neg1 and neg2) or ((neg1 or neg2) and not negative); zero := test = 0; regs[t and 7] := test; end; $1A..$1B: begin // Format 2: subtract, [sub Rd, Rs, Rn] operand1 := regs[(t shr 3) and 7]; operand2 := regs[(t shr 6) and 7]; test := operand1 - operand2; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negative := test shr 31 <> 0; overflow := (neg1 and not neg2 and not negative) or (not neg1 and neg2 and negative); carry := (neg1 and not neg2) or (neg1 and not negative) or (not neg2 and not negative); zero := test = 0; regs[t and 7] := test; end; $1C..$1D: begin // Format 2: add, [add Rd, Rs, #Offset3] operand1 := regs[(t shr 3) and 7]; test := operand1 + (t shr 6) and 7; neg1 := operand1 shr 31 <> 0; negative := test shr 31 <> 0; overflow := negative and not neg1; carry := neg1 and not negative; zero := test = 0; regs[t and 7] := test; end; $1E..$1F: begin // Format 2: subtract, [sub Rd, Rs, #Offset3] operand1 := regs[(t shr 3) and 7]; test := operand1 - (t shr 6) and 7; neg1 := operand1 shr 31 <> 0; negative := test shr 31 <> 0; overflow := neg1 and not negative; carry := neg1 or not negative; zero := test = 0; regs[t and 7] := test; end; $20..$27: begin // Format 3: move immediate, [mov Rd, #Offset8] test := t and $FF; negative := test shr 31 <> 0; zero := test = 0; regs[c and 7] := test; end; $28..$2F: begin // Format 3: compare immediate, [cmp Rd, #Offset8] operand1 := regs[c and 7]; test := operand1 - t and $FF; neg1 := operand1 shr 31 <> 0; negative := test shr 31 <> 0; overflow := neg1 and not negative; carry := neg1 or not negative; zero := test = 0; end; $30..$37: begin // Format 3: add immediate, [add Rd, #Offset8] operand1 := regs[c and 7]; test := operand1 + t and $FF; neg1 := operand1 shr 31 <> 0; negative := test shr 31 <> 0; overflow := negative and not neg1; carry := neg1 and not negative; zero := test = 0; regs[c and 7] := test; end; $38..$3F: begin // Format 3: subtract immediate, [sub Rd, #Offset8] operand1 := regs[c and 7]; test := operand1 - t and $FF; neg1 := operand1 shr 31 <> 0; negative := test shr 31 = $1; overflow := neg1 and not negative; carry := neg1 or not negative; negative := test shr 31 <> 0; zero := test = 0; regs[c and 7] := test; end; $40..$43: begin // Format 4: ALU Operations case (t shr 6) and $F of $0: begin // and Rd, Rs if (t = $4000) and (regs[0] = $c0ded00d) then DebugTrap; test := regs[t and 7] and regs[(t shr 3) and 7]; regs[t and 7] := test; end; $1: begin // eor Rd, Rs test := regs[t and 7] xor regs[(t shr 3) and 7]; regs[t and 7] := test; end; $2: begin // lsl Rd, Rs shift := regs[(t shr 3) and 7] and $FF; if shift = 0 then test := regs[t and 7] else if shift > 31 then begin test := 0; carry := (shift = 32) and (regs[t and 7] and (1 shl 0) <> 0); end else begin test := BarrelShifter(regs[t and 7], LSL, shift); carry := barrelCarry; end; regs[t and 7] := test; end; $3: begin // lsr Rd, Rs shift := regs[(t shr 3) and 7] and $FF; if shift = 0 then test := regs[t and 7] else if shift > 31 then begin test := 0; carry := (shift = 32) and (regs[t and 7] shr 31 <> 0); end else begin test := BarrelShifter(regs[t and 7], LSR, shift); carry := barrelCarry; end; regs[t and 7] := test; end; $4: begin // asr Rd, Rs shift := regs[(t shr 3) and 7] and $FF; test := regs[t and 7]; if shift <> 0 then begin test := BarrelShifter(test, ASR, shift); carry := barrelCarry; regs[t and 7] := test; end; end; $5: begin // adc Rd, Rs operand1 := regs[t and 7]; operand2 := regs[(t shr 3) and 7]; test := operand1 + operand2; if carry then Inc(test); regs[t and 7] := test; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negr := test shr 31 <> 0; overflow := (neg1 = neg2) and (neg1 <> negr); carry := (neg1 and neg2) or ((neg1 or neg2) and not negr); end; $6: begin // sbc Rd, Rs operand1 := regs[t and 7]; operand2 := regs[(t shr 3) and 7]; test := operand1 - operand2; if not carry then Dec(test); regs[t and 7] := test; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negr := test shr 31 <> 0; carry := (neg1 and not neg2) or (neg1 and not negr) or (not neg2 and not negr); overflow := (neg1 and not neg2 and not negr) or (not neg1 and neg2 and negr); end; $7: begin // ror Rd, Rs test := BarrelShifter(regs[t and 7], ROR, regs[(t shr 3) and 7]); regs[t and 7] := test; carry := barrelCarry; end; $8: begin // tst Rd, Rs test := regs[t and 7] and regs[(t shr 3) and 7]; end; $9: begin // neg Rd, Rs operand1 := regs[(t shr 3) and 7]; test := -operand1; regs[t and 7] := test; overflow := (test shr 31 <> 0) and (operand1 shr 31 <> 0); carry := not overflow; end; $A: begin // cmp Rd, Rs operand1 := regs[t and 7]; operand2 := regs[(t shr 3) and 7]; test := operand1 - operand2; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negr := test shr 31 <> 0; carry := (neg1 and not neg2) or (neg1 and not negr) or (not neg2 and not negr); overflow := (neg1 and not neg2 and not negr) or (not neg1 and neg2 and negr); end; $B: begin // cmn Rd, Rs operand1 := regs[t and 7]; operand2 := regs[(t shr 3) and 7]; test := operand1 + operand2; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negr := test shr 31 <> 0; overflow := (neg1 = neg2) and (neg1 <> negr); carry := (neg1 and neg2) or ((neg1 or neg2) and not negr); end; $C: begin // orr Rd, Rs test := regs[t and 7] or regs[(t shr 3) and 7]; regs[t and 7] := test; end; $D: begin // mul Rd, Rs // Instruction cycle times // MUL takes 1S + mI and MLA 1S + (m+1)I cycles to execute // m is the number of 8 bit multiplier array cycles required to complete the // multiply, which is controlled by the value of the multiplier operand // specified by Rs. Its possible values are as follows: // 1 if bits [32:8] of the multiplier operand are all zero or all one. // 2 if bits [32:16] of the multiplier operand are all zero or all one. // 3 if bits [32:24] of the multiplier operand are all zero or all one. // 4 in all other cases. test := regs[(t shr 3) and 7]; if test shr 31 <> 0 then test := not test; if test and $FFFFFF00 = 0 then c := 1 else if test and $FFFF0000 = 0 then c := 2 else if test and $FF000000 = 0 then c := 3 else c := 4; Dec(quota, c*cycI); // Do the actual multiply test := regs[(t shr 3) and 7] * regs[t and 7]; regs[t and 7] := test; // Test flags negative := test shr 31 <> 0; zero := test = 0; end; $E: begin // bic Rd, Rs test := regs[t and 7] and not regs[(t shr 3) and 7]; regs[t and 7] := test; end; $F: begin // mvn Rd, Rs test := not regs[(t shr 3) and 7]; regs[t and 7] := test; end; else test := 0; // shaddup end; negative := test shr 31 <> 0; zero := test = 0; end; $44: begin // Format 5: Hi register add, [add Rd/Hd, Hs/Rs] (both low is undefined) case (t shr 6) and 3 of 1: begin operand1 := t and 7; regs[operand1] := regs[operand1] + regs[(t shr 3) and 7 + 8]; end; 2: begin operand1 := t and 7 + 8; regs[operand1] := regs[operand1] + regs[(t shr 3) and 7]; if operand1 = 15 then FlushPipeThumb; end; 3: begin operand1 := t and 7 + 8; regs[operand1] := regs[operand1] + regs[(t shr 3) and 7 + 8]; if operand1 = 15 then FlushPipeThumb; end; end; end; $45: begin // Format 5: Hi register compare, [cmp Rd/Hd, Hs/Rs] (both low is undefined) case (t shr 6) and 3 of 1: begin operand1 := regs[t and 7]; operand2 := regs[(t shr 3) and 7 + 8]; end; 2: begin operand1 := regs[t and 7 + 8]; operand2 := regs[(t shr 3) and 7]; end; 3: begin operand1 := regs[t and 7 + 8]; operand2 := regs[(t shr 3) and 7 + 8]; end; else UndefinedState('Thumb format 5: H1 and H2 = 0'); operand1 := 0; operand2 := 0; end; test := operand1 - operand2; neg1 := operand1 shr 31 <> 0; neg2 := operand2 shr 31 <> 0; negative := test shr 31 <> 0; carry := (neg1 and not neg2) or (neg1 and not negative) or (not neg2 and not negative); overflow := (neg1 and not neg2 and not negative) or (not neg1 and neg2 and negative); zero := test = 0; end; $46: begin // Format 5: Hi register move, [mov Rd/Hd, Hs/Rs] (both low is undefined) case (t shr 6) and 3 of 1: regs[t and 7] := regs[(t shr 3) and 7 + 8]; 2: begin Rd := t and 7 + 8; regs[Rd] := regs[(t shr 3) and 7]; if Rd = 15 then begin regs[R15] := regs[R15] and not 1; FlushPipeThumb; end; end; 3: begin Rd := t and 7 + 8; regs[Rd] := regs[(t shr 3) and 7 + 8]; if Rd = 15 then begin regs[R15] := regs[R15] and not 1; FlushPipeThumb; end; end; end; end; $47: begin // Format 5: Branch and exchange, [bx Rs/Hs] (H1=1 is undefined) case (t shr 6) and 3 of 0: Rd := (t shr 3) and 7; 1: Rd := (t shr 3) and 7 + 8; else Rd := 0; end; // Using R15 as an operand thumbMode := regs[Rd] and 1 <> 0; regs[R15] := regs[Rd] and not 1; if thumbMode then FlushPipeThumb else FlushPipeARM; end; $48..$4F: begin // Format 6: PC-relative load, [ldr Rd, [PC, #Imm]] regs[c and 7] := memReadWord((regs[R15] and $FFFFFFFC) + (t and $FF) shl 2); Dec(quota, cycI); end; $50..$51: begin // Format 7: Store with register offset, [str Rd, [Rb, Ro]] memWriteWord(regs[(t shr 3) and 7] + regs[(t shr 6) and 7], regs[t and 7]); end; $52..$53: begin // Format 8: Store halfword, [strh Rd, [Rb, Ro]] memWriteHalfWord(regs[(t shr 3) and 7] + regs[(t shr 6) and 7], regs[t and 7]); end; $54..$55: begin // Format 7: Store byte with register offset, [strb Rd, [Rb, Ro]] memWriteByte(regs[(t shr 3) and 7] + regs[(t shr 6) and 7], regs[t and 7]); end; $56..$57: begin // Format 8: Load signed byte, [ldrsb Rd, [Rb, Ro]] test := memReadByte(regs[(t shr 3) and 7] + regs[(t shr 6) and 7]); if test shr 7 <> 0 then test := test or $FFFFFF00; regs[t and 7] := test; Dec(quota, cycI); end; $58..$59: begin // Format 7: Load with register offset, [ldr Rd, [Rb, Ro]] regs[t and 7] := memReadWord(regs[(t shr 3) and 7] + regs[(t shr 6) and 7]); Dec(quota, cycI); end; $5A..$5B: begin // Format 8: Load halfword, [ldrh Rd, [Rb, Ro]] regs[t and 7] := memReadHalfWord(regs[(t shr 3) and 7] + regs[(t shr 6) and 7]); Dec(quota, cycI); end; $5C..$5D: begin // Format 7: Load byte with register offset, [ldrb Rd, [Rb, Ro]] regs[t and 7] := memReadByte(regs[(t shr 3) and 7] + regs[(t shr 6) and 7]); Dec(quota, cycI); end; $5E..$5F: begin // Format 8: Load signed halfword, [ldrsh Rd, [Rb, Ro]] test := memReadHalfWord(regs[(t shr 3) and 7] + regs[(t shr 6) and 7]); if test shr 15 <> 0 then test := test or $FFFF0000; regs[t and 7] := test; Dec(quota, cycI); end; $60..$67: begin // Format 9: Store with immediate offset, [str Rd, [Rb, #Imm]] memWriteWord(regs[(t shr 3) and 7] + ((t shr 6) and $1F) shl 2, regs[t and 7]); end; $68..$6F: begin // Format 9: Load with immediate offset, [ldr Rd, [Rb, #Imm]] regs[t and 7] := memReadWord(regs[(t shr 3) and 7] + ((t shr 6) and $1F) shl 2); Dec(quota, cycI); end; $70..$77: begin // Format 9: Store byte with immediate offset, [strb Rd, [Rb, #Imm]] memWriteByte(regs[(t shr 3) and 7] + ((t shr 6) and $1F), regs[t and 7]); end; $78..$7F: begin // Format 9: Load byte with immediate offset, [ldrb Rd, [Rb, #Imm]] regs[t and 7] := memReadByte(regs[(t shr 3) and 7] + ((t shr 6) and $1F)); Dec(quota, cycI); end; $80..$87: begin // Format 10: Store halfword, [strh Rd, [Rb, #Imm]] memWriteHalfWord(regs[(t shr 3) and 7] + ((t shr 6) and $1F) shl 1, regs[t and 7]); end; $88..$8F: begin // Format 10: Load halfword, [ldrh Rd, [Rb, #Imm]] regs[t and 7] := memReadHalfWord(regs[(t shr 3) and 7] + ((t shr 6) and $1F) shl 1); Dec(quota, cycI); end; $90..$97: begin // Format 11: SP-relative store, [str Rd, [SP, #Imm]] memWriteWord(regs[R13] + (t and $FF) shl 2, regs[c and 7]); end; $98..$9F: begin // Format 11: SP-relative load, [ldr Rd, [SP, #Imm]] regs[c and 7] := memReadWord(regs[R13] + (t and $FF) shl 2); Dec(quota, cycI); end; $A0..$A7: begin // Format 12: Load address, [add Rd, PC, #Imm] regs[c and 7] := (regs[R15] and $FFFFFFFC) + (t and $FF) shl 2; end; $A8..$AF: begin // Format 12: Load address, [add Rd, SP, #Imm] regs[c and 7] := regs[R13] + (t and $FF) shl 2; end; $B0: begin // Format 13: Add offset to stack pointer [add SP, #+/-Imm] test := (t and $7F) shl 2; if t and (1 shl 7) <> 0 then Dec(regs[R13], test) else Inc(regs[R13], test); end; $B1..$B3, $B6..$BB, $BE..$BF, $E8..$EF: begin // Undefined Opcode UndefinedOpcode; end; $B4: begin // Format 14: push {rlist} for i := 7 downto 0 do if t and (1 shl i) <> 0 then begin Dec(regs[R13], 4); memWriteWord(regs[R13], regs[i]); end; end; $B5: begin // Format 14: push {rlist,LR} Dec(regs[R13], 4); memWriteWord(regs[R13], regs[R14]); for i := 7 downto 0 do if t and (1 shl i) <> 0 then begin Dec(regs[R13], 4); memWriteWord(regs[R13], regs[i]); end; end; $BC: begin // Format 14: pop {rlist} for i := 0 to 7 do if t and (1 shl i) <> 0 then begin regs[i] := memReadWord(regs[R13]); Inc(regs[R13], 4); end; Dec(quota, cycI); end; $BD: begin // Format 14: pop {rlist, PC} for i := 0 to 7 do if t and (1 shl i) <> 0 then begin regs[i] := memReadWord(regs[R13]); Inc(regs[R13], 4); end; regs[R15] := memReadWord(regs[R13]); Inc(regs[R13], 4); FlushPipeThumb; Dec(quota, cycI); end; $C0..$C7: begin // Format 15: stmia Rb!, {rlist} for i := 0 to 7 do if t and (1 shl i) <> 0 then begin memWriteWord(regs[c and 7], regs[i]); Inc(regs[c and 7], 4); end; end; $C8..$CF: begin // Format 15: ldmia Rb!, {rlist} for i := 0 to 7 do if t and (1 shl i) <> 0 then begin regs[i] := memReadWord(regs[c and 7]); Inc(regs[c and 7], 4); end; Dec(quota, cycI); end; $D0..$DE: begin // Format 16: Conditionial branch, [bXX label] rs := c and $F; {$I incTestCond.pas} if cond then begin test := t and $FF; if test shr 7 <> 0 then test := test or $FFFFFF00; Inc(regs[R15], test shl 1); FlushPipeThumb; end; end; $DF: begin // Format 17: Software Interrupt, [swi value8] cpuCurrentOpcode := t; SoftwareInterrupt; end; $E0..$E7: begin // Format 18: Unconditional branch, [b label] test := t and $7FF; if test shr 10 <> 0 then test := test or $FFFFF800; Inc(regs[R15], test shl 1); FlushPipeThumb; end; $F0..$F7: begin // Format 19: Long branch with link (hw1), [part one of bl label] test := t and $7FF; if test shr 10 <> 0 then test := test or $FFFFF800; regs[R14] := regs[R15] + test shl 12; end; $F8..$FF: begin // Format 19: Long branch with link (hw2), [part two of bl label] test := t and $7FF; operand1 := regs[R15] - 2; regs[R15] := regs[R14] + test shl 1; regs[R14] := operand1 or 1; FlushPipeThumb; end; end; // Check for a breakpoint if memStopAtAddy(regs[R15] - 2) then begin hitBreakpoint := true; Exit; end; until (quota <= 0) or not thumbMode; // Check for a breakpoint on the next opcode if (not thumbMode) and not haveFlippedThumb then hitBreakpoint := memStopAtAddy(regs[R15] - 4); end; ////////////////////////////////////////////////////////////////////// end. //////////////////////////////////////////////////////////////////////