📄 cputhumbcore.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -