📄 cpuarmcore.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// cpuARMCore.pas: ARM instruction set decoder and executer //
// Decodes and executes ARM 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: //
// There are still a few bugs either here or in the Thumb 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 cpuARMCore; /////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// EmulateARM() emulates ARM opcodes until one of the following
// conditions is met:
// the cycle quota is exhausted
// the CPU switches into thumb mode
// a breakpoint is encountered
procedure EmulateArm;
//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
SysUtils, nexus, AddressSpace, cpuMemory, cpuMisc,
cpuGraphics, cpuPeripherals;
//////////////////////////////////////////////////////////////////////
// Branch and Exchange (BX)
procedure BranchAndExchange;
var
Rn: uint32;
begin
// This instruction performs a branch by copying the contents of a
// register Rn into the PC. The branch causes a pipeline flush and
// refill from the address specified by Rn, and Rn[0] determines
// whether the instruction stream will be decoded as ARM or Thumb.
Rn := regs[cpuCurrentOpcode and $F];
regs[R15] := Rn and not 1;
thumbMode := Rn and 1 <> 0;
if thumbMode then FlushPipeThumb else FlushPipeARM;
end;
//////////////////////////////////////////////////////////////////////
// Branch and Branch with Link (B, BL)
procedure Branch;
const
LINK_BIT = 1 shl 24;
var
dest: uint32;
begin
// Branch with Link (BL) writes the address of the instruction
// following the BL instruction into the LR of the current mode.
// Note that the CPSR is not saved and R14[1:0] are always cleared.
if cpuCurrentOpcode and LINK_BIT <> 0 then regs[R14] := (regs[R15] - 4) and not 3;
// Branch instructions contain a 2's complement 24 bit offset. This
// is shifted left two bits, sign extended to 32 bits, and added to
// the PC. Thus, the instruction can specify a branch of +/- 32 MB.
dest := (cpuCurrentOpcode and $00FFFFFF) shl 2;
if dest and (1 shl 25) <> 0 then dest := $FC000000 or dest;
regs[R15] := regs[R15] + dest;
FlushPipeARM;
end;
//////////////////////////////////////////////////////////////////////
procedure DataProcessing(rsShift: boolean; operand2: uint32);
const
SET_FLAGS_BIT = 1 shl 20; // S bit
var
Rn, Rd, Rm: byte;
operand1, test: uint32;
neg1, neg2, negr: boolean;
begin
// Data Processing
// The instruction produces a result by performing a ALU operation
// on one or two operands. The first operand is always a register
// (Rn). The second operand may be a shifted register (Rm) or a
// rotated 8 bit immediate value (Imm) according to the value of
// the I bit. The CPSR condition codes can be optionally updated
// as a result of this instruction, depending on the the S bit.
//
// Due to instruction prefetching, the PC will be 8 bytes ahead if
// the shift amount is specified in the instruction. If a register
// is used to specify the shift, the PC will be 12 bytes ahead.
if rsShift then Inc(regs[R15], 4);
// Parse the operands
Rn := (cpuCurrentOpcode shr 16) and $F;
Rd := (cpuCurrentOpcode shr 12) and $F;
operand1 := regs[Rn];
// Do the actual processing
if cpuCurrentOpcode and SET_FLAGS_BIT <> 0 then begin
// CPSR flags
// The logical operations (AND, EOR, TST, TEQ, ORR, MOV, BIC, MVN)
// perform the logical action on all corresponding bits of the
// operand or operands to produce the result. If the S bit is set
// the V flag in the CPSR will be unaffected and the C flag will
// be set to the carry out from the barrel shifter (or preserved
// if the shift operation is LSL #0).
//
// The arithmetic operations (SUB, RSB, ADD, ADC, SBC, RSC, CMP,
// CMN) treat each operand as a 32 bit integer. If the S bit is
// set, the V flag in the CPSR will be set if an overflow occurs
// into bit 31 of the result. The C flag will be set to the carry
// out of bit 31 of the ALU.
case ((cpuCurrentOpcode shr 21) and $F) of
AND_OPCODE: begin
test := operand1 and operand2;
carry := barrelCarry;
regs[Rd] := test;
end;
EOR_OPCODE: begin
test := operand1 xor operand2;
carry := barrelCarry;
regs[Rd] := test;
end;
SUB_OPCODE: begin
test := operand1 - operand2;
neg1 := operand1 shr 31 <> 0;
neg2 := operand2 shr 31 <> 0;
negr := test shr 31 <> 0;
overflow := (neg1 and not neg2 and not negr) or (not neg1 and neg2 and negr);
carry := (neg1 and not neg2) or (neg1 and not negr) or (not neg2 and not negr);
regs[Rd] := test;
end;
RSB_OPCODE: begin
test := operand2 - operand1;
neg1 := operand2 shr 31 <> 0;
neg2 := operand1 shr 31 <> 0;
negr := test shr 31 <> 0;
overflow := (neg1 and not (neg2 or negr)) or ((neg2 and negr) and not neg1);
carry := (neg1 and not neg2) or (neg1 and not negr) or (not neg2 and not negr);
regs[Rd] := test;
end;
ADD_OPCODE: begin
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);
regs[Rd] := test;
end;
ADC_OPCODE: begin
test := operand1 + operand2;
if carry then Inc(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);
regs[Rd] := test;
end;
SBC_OPCODE: begin
test := operand1 - operand2;
if not carry then Dec(test);
neg1 := operand1 shr 31 <> 0;
neg2 := operand2 shr 31 <> 0;
negr := test shr 31 <> 0;
overflow := (neg1 and not (neg2 or negr)) or ((neg2 and negr) and not neg1);
carry := (neg1 and not neg2) or (neg1 and not negr) or (not neg2 and not negr);
regs[Rd] := test;
end;
RSC_OPCODE: begin
test := operand2 - operand1;
if not carry then Dec(test);
neg1 := operand2 shr 31 <> 0;
neg2 := operand1 shr 31 <> 0;
negr := test shr 31 <> 0;
overflow := (neg1 and not (neg2 or negr)) or ((neg2 and negr) and not neg1);
carry := (neg1 and not neg2) or (neg1 and not negr) or (not neg2 and not negr);
regs[Rd] := test;
end;
TST_OPCODE: begin
test := operand1 and operand2;
carry := barrelCarry;
end;
TEQ_OPCODE: begin
test := operand1 xor operand2;
carry := barrelCarry;
end;
CMP_OPCODE: begin
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;
CMN_OPCODE: begin
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;
ORR_OPCODE: begin
test := operand1 or operand2;
carry := barrelCarry;
regs[Rd] := test;
end;
MOV_OPCODE: begin
test := operand2;
carry := barrelCarry;
regs[Rd] := test;
end;
BIC_OPCODE: begin
test := operand1 and not operand2;
carry := barrelCarry;
regs[Rd] := test;
end;
MVN_OPCODE: begin
test := not operand2;
carry := barrelCarry;
regs[Rd] := test;
end;
else
test := 0; // shut up delphi!
end;
// The Z flag will be set if the result is zero, and the N flag
// will be set to the value of bit 31 of the result.
zero := test = 0;
negative := test shr 31 <> 0;
// When Rd is R15 and the S flag is set, the result of the is
// placed in R15 and the SPSR corresponding to the current mode
// is moved to the CPSR. This allows state changes which
// atomically restore both PC and CPSR. This form of instruction
// should not be used in User mode.
if Rd = R15 then begin
rsShift := false; // make sure we don't do the PC fixup hack
if SPSR <> 0 then cpuWriteCPSR(regs[SPSR]);
if thumbMode then FlushPipeThumb else FlushPipeARM;
end;
end else begin
// S bit is clear, ignore changes to the flags
case ((cpuCurrentOpcode shr 21) and $F) of
AND_OPCODE: begin
regs[Rd] := operand1 and operand2;
// As if by magik
if (Rd = 0) and (Rn = 0) and (cpuCurrentOpcode and $FFF = 0) and (operand1 = $c0ded00d) then DebugTrap;
end;
EOR_OPCODE: regs[Rd] := operand1 xor operand2;
SUB_OPCODE: regs[Rd] := operand1 - operand2;
RSB_OPCODE: regs[Rd] := operand2 - operand1;
ADD_OPCODE: regs[Rd] := operand1 + operand2;
ADC_OPCODE: begin
test := operand1 + operand2;
if carry then Inc(test);
regs[Rd] := test;
end;
SBC_OPCODE: begin
test := operand1 - operand2;
if not carry then Dec(test);
regs[Rd] := test;
end;
RSC_OPCODE: begin
test := operand2 - operand1;
if not carry then Dec(test);
regs[Rd] := test;
end;
// PSR Transfer (MRS, MSR)
// The instruction is only executed if the condition is true.
// The MRS and MSR instructions are formed from a subset of the Data Processing
// operations and are implemented using the TEQ, TST, CMN and CMP instructions
// without the S flag set.
//
// These instructions allow access to the CPSR and SPSR registers. The MRS
// instruction allows the contents of the CPSR or SPSR_<mode> to be moved to a
// general register. The MSR instruction allows the contents of a general register to be
// moved to the CPSR or SPSR_<mode> register.
//
// The MSR instruction also allows an immediate value or register contents to be
// transferred to the condition code flags (N,Z,C and V) of CPSR or SPSR_<mode>
// without affecting the control bits. In this case, the top four bits of the specified register
// contents or 32 bit immediate value are written to the top four bits of the relevant PSR.
//
// Operand restrictions
// o In User mode, the control bits of the CPSR are protected from change, so only
// the condition code flags of the CPSR can be changed. In other (privileged)
// modes the entire CPSR can be changed.
//
// Note that the software must never change the state of the T bit in the CPSR.
// If this happens, the processor will enter an unpredictable state. fixme: not caught!
// You must not specify R15 as the source or destination register.
// Do not attempt to access an SPSR in User mode, since no such register exists.
TST_OPCODE: begin
// MRS Rd, CPSR (transfer PSR contents to a register)
// cond 00 0 10P0 0 1111 [Rd] 0000 0000 0000
if cpuCurrentOpcode and $F0FFF = $F0000 then
regs[Rd] := cpuReadCPSR
else
UndefinedState('MRS Rd, CPSR found that does not decode properly');
end;
TEQ_OPCODE: begin
Rm := cpuCurrentOpcode and $F;
if cpuCurrentOpcode and $FFFF0 = $9F000 then begin
// MSR CPSR, Rm cond 00 0 10P1 0 1001 1111 0000 0000 [Rm]
test := regs[Rm];
if cpuMode = MODE_USER then begin
// You can only set the flag bits in user mode
negative := test and SR_N <> 0;
zero := test and SR_Z <> 0;
carry := test and SR_C <> 0;
overflow := test and SR_V <> 0;
end else
cpuWriteCPSR(test);
end else if cpuCurrentOpcode and $FF000 = $8F000 then begin
// MSR CPSR, Rm cond 00 0 10P1 010001111 0000 0000 [Rm]
// MSR CPSR, operand2 cond 00 1 10P1 010001111 Rotate Immedia
// 0 1001 010001111 0000 0000 0100
if cpuCurrentOpcode and (1 shl 25) = 0 then begin
if Rm = R15 then UndefinedState('MSR CPSR_flg, r15');
if cpuCurrentOpcode and $FF0 <> 0 then UndefinedState('MSR CPSR_flg, Rm using the barrel shifter');
end;
negative := operand2 and SR_N <> 0;
zero := operand2 and SR_Z <> 0;
carry := operand2 and SR_C <> 0;
overflow := operand2 and SR_V <> 0;
end else begin
UndefinedState('MSR (TEQ-type) found that does not decode properly');
end;
// keep pc from changing
Rd := 0;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -