📄 cpuarmcore.pas
字号:
// In the case of post-indexed addressing, the write back bit is
// redundant and is always set to zero, since post-indexed data
// transfers always write back the modified base.
if not preIncrement then regs[Rn] := index + offset;
end;
//////////////////////////////////////////////////////////////////////
procedure BlockDataTransfer;
const
LOAD_BIT = 1 shl 20;
WRITEBACK_BIT = 1 shl 21;
PSR_FORCE_BIT = 1 shl 22;
UP_BIT = 1 shl 23;
PRE_INCREMENT_BIT = 1 shl 24;
var
bits, base: byte;
i: integer;
address, temp: uint32;
begin
// Block Data Transfer (LDM, STM)
// The instruction can transfer any registers in the current bank
// (and also to and from the user bank in non-user modes). The
// register list is a 16 bit field in the instruction, where each
// bit corresponds to a register.
//
// Addressing modes
// The transfer addresses are determined by the contents of the base
// register (Rn), the pre/post bit (P) and the up/down bit (U). The
// registers are transferred in the order lowest to highest, so R15
// (if in the list) will always be transferred last. The lowest
// register also gets transferred to/from the lowest memory address.
//
// Address alignment
// The address should normally be a word aligned quantity and non
// word aligned addresses do not affect the instruction. However,
// the bottom 2 bits of the address will appear on A[1:0] and might
// be interpreted by the memory system.
//
// R15 should not be used as the base register.
base := (cpuCurrentOpcode shr 16) and $F;
address := regs[base];
// Count the number of bits in the list
bits := 0;
for i := 0 to 15 do Inc(bits, (cpuCurrentOpcode shr i) and 1);
if cpuCurrentOpcode and LOAD_BIT <> 0 then begin
// LDM - Load multiple registers from memory
// Compute the start address and calculate the writeback value
if cpuCurrentOpcode and UP_BIT = 0 then begin
Dec(address, 4*bits);
// Since we start from the bottom and increment instead
// of from the top and decrementing, the order we increment
// must change as well.
cpuCurrentOpcode := cpuCurrentOpcode xor PRE_INCREMENT_BIT;
// Write the modified value back if needed
if cpuCurrentOpcode and WRITEBACK_BIT <> 0 then
regs[base] := address;
end else begin
if cpuCurrentOpcode and WRITEBACK_BIT <> 0 then
regs[base] := address + 4*bits;
end;
// Read any registers in the list
if cpuCurrentOpcode and PRE_INCREMENT_BIT <> 0 then begin
for i := 0 to 15 do
if (cpuCurrentOpcode shr i) and 1 <> 0 then begin
Inc(address, 4);
regs[i] := memReadWord(address);
end;
end else begin
for i := 0 to 15 do
if (cpuCurrentOpcode shr i) and 1 <> 0 then begin
regs[i] := memReadWord(address);
Inc(address, 4);
end;
end;
if (cpuCurrentOpcode shr 15) and 1 <> 0 then begin
// If a LDM has R15 in its transfer list and the S bit set, a
// mode change will take place where SPSR_<mode> is transferred
// to CPSR at the same time R15 is loaded and the pipe flushed.
if (cpuCurrentOpcode and PSR_FORCE_BIT <> 0) and (SPSR <> 0) then cpuWriteCPSR(regs[SPSR]);
if thumbMode then FlushPipeThumb else FlushPipeARM;
end else begin
// User bank transfers occur when the S bit is set and R15 is
// not in the list. The user bank registers are transferred
// instead of the banked registers of the current mode. Base
// write-back should not be used when this mechanism is
// employed. For LDM, care must be taken not to read from a
// banked register during the following cycle
if cpuCurrentOpcode and PSR_FORCE_BIT <> 0 then
LogWriteLn(Format('Unimplemented opcode at $%8.8x: LDM with forced user bank', [regs[R15]-8]));
end;
// LDR has a trailing I cycle that gets merged with the next
// opcode fetch normally
Dec(quota, cycI);
end else begin
// STM - Store multiple registers to memory
// Whenever R15 is stored to memory the stored value is the
// address of the STM instruction plus 12.
Inc(regs[R15], 4);
// Compute the start address and calculate the writeback value
if cpuCurrentOpcode and UP_BIT = 0 then begin
Dec(address, 4*bits);
// Since we start from the bottom and increment instead
// of from the top and decrementing, the order we increment
// must change as well.
cpuCurrentOpcode := cpuCurrentOpcode xor PRE_INCREMENT_BIT;
// Write the modified value back if needed
if cpuCurrentOpcode and WRITEBACK_BIT <> 0 then begin
temp := regs[base];
regs[base] := address;
if cpuCurrentOpcode and (1 shl base) <> 0 then begin
// stm base!, {base, ...} with base as the lowest register
// stores the original value of base, not the computed
// final value, damn you ARM
if cpuCurrentOpcode and not ($FFFFFFFF shl base) = 0 then begin
cpuCurrentOpcode := cpuCurrentOpcode xor (1 shl base);
if cpuCurrentOpcode and PRE_INCREMENT_BIT = 0 then begin
memWriteWord(address, temp);
Inc(address, 4);
end else begin
Inc(address, 4);
memWriteWord(address, temp);
end;
end;
end;
end;
end else begin
if cpuCurrentOpcode and WRITEBACK_BIT <> 0 then
regs[base] := address + 4*bits;
end;
// Write any registers in the list
if cpuCurrentOpcode and PRE_INCREMENT_BIT <> 0 then begin
for i := 0 to 15 do
if (cpuCurrentOpcode shr i) and 1 <> 0 then begin
Inc(address, 4);
memWriteWord(address, regs[i]);
end;
end else begin
for i := 0 to 15 do
if (cpuCurrentOpcode shr i) and 1 <> 0 then begin
memWriteWord(address, regs[i]);
Inc(address, 4);
end;
end;
// Since the encapsulating code takes care of the advancement,
// we need to repair R15 before returning
Dec(regs[R15], 4);
// User bank transfers occur when the S bit is set and R15 is not
// in the list. The user bank registers are transferred instead
// of the banked registers of the current mode. Base write-back
// should not be used when this mechanism is employed.
if cpuCurrentOpcode and PSR_FORCE_BIT <> 0 then
LogWriteLn(Format('Unimplemented opcode at $%8.8x: STM with forced user bank', [regs[R15]-8]));
end;
end;
//////////////////////////////////////////////////////////////////////
procedure SingleDataSwap;
const
AS_BYTE_BIT = 1 shl 22;
var
Rn, Rd, Rm: byte;
address, source: uint32;
begin
// Single Data Swap (SWP)
// The data swap instruction is used to swap a byte or word between
// a register and external memory. This instruction is implemented
// as a memory read followed by a memory write which are atomic.
// Do not use R15 as an operand (Rd, Rn or Rm) in a SWP instruction.
// Parse the operands
Rn := (cpuCurrentOpcode shr 16) and $F;
Rd := (cpuCurrentOpcode shr 12) and $F;
Rm := cpuCurrentOpcode and $F;
address := regs[Rn];
// The swap address is determined by the base register (Rn). The
// processor first reads the contents of the swap address, then it
// writes the contents of the source register (Rm) to the swap
// address, and stores the old memory contents in the destination
// register (Rd). The same register may be specified as both the
// source and destination.
source := regs[Rm];
if cpuCurrentOpcode and AS_BYTE_BIT <> 0 then begin
regs[Rd] := memReadByte(address);
memWriteByte(address, source);
end else begin
regs[Rd] := memLoadWord(address);
memWriteWord(address, source);
end;
// Swap instructions have a trailing I cycle
Dec(quota, cycI);
end;
//////////////////////////////////////////////////////////////////////
procedure EmulateArm;
var
shift: uint32;
Rs, Rm: byte;
operand2: uint32;
cond: boolean;
begin
repeat
if irqPending and not irqDisabled then Exit;
{$IFDEF SIGNATURES}
// Add an ARM signature to the address of the current opcode
operand2 := regs[R15]-4;
if operand2 >= $08000000 then begin
operand2 := (operand2 shr 1) and $FFFFFF;
sigs[operand2] := sigs[operand2] or ARM_READ;
end;
{$ENDIF}
// Read the current instruction
// leave PC at nextAddress + L
cpuCurrentOpcode := regs[PIPELINE_0];
regs[PIPELINE_0] := regs[PIPELINE_1]; // at currentAddress + L
Inc(regs[R15], 4);
regs[PIPELINE_1] := memReadWordUnc(regs[R15]); // at currentAddress + 2L
// Process the rest of the instruction if the condition is true
rs := cpuCurrentOpcode shr 28;
{$I incTestCond.pas}
if cond then begin
if cpuCurrentOpcode and BX_MASK = BX_SIG then begin
BranchAndExchange;
end else if cpuCurrentOpcode and BRANCH_MASK = BRANCH_SIG then begin
Branch;
end else if cpuCurrentOpcode and MULTIPLY_MASK = MULTIPLY_SIG then begin
Multiply;
end else if cpuCurrentOpcode and MULTIPLY_LONG_MASK = MULTIPLY_LONG_SIG then begin
LongMultiply;
end else if cpuCurrentOpcode and ALU_SHIFT_BY_IMM_MASK = ALU_SHIFT_BY_IMM_SIG then begin
// Instruction specified shift amount: the shift amount is
// specified in the instruction as a 5 bit field
operand2 := BarrelShifter(regs[cpuCurrentOpcode and $F], (cpuCurrentOpcode shr 5) and $3, (cpuCurrentOpcode shr 7) and $1F);
// Perform the operation
DataProcessing(false, operand2);
end else if cpuCurrentOpcode and ALU_SHIFT_BY_REG_MASK = ALU_SHIFT_BY_REG_SIG then begin
// Register specified shift amount
Rm := cpuCurrentOpcode and $F;
Rs := (cpuCurrentOpcode shr 8) and $F;
// Only the least significant byte of the contents of Rs is
// used to determine the shift amount. Rs can be any general
// register other than R15.
shift := regs[Rs] and $FF;
if shift = 0 then begin
// If this byte is zero, the unchanged contents of Rm will
// be used as the second operand, and the old value of the
// CPSR C flag will be passed on as the shifter carry out.
operand2 := regs[Rm];
barrelCarry := carry;
end else if shift > 31 then begin
// If the value in the byte is 32 or more, the result will
// be a logical extension of the shift described above:
// LSL by 32: Result zero, carry out equal to Rm[0]
// LSL by more than 32: Result zero, carry out zero
// LSR by 32: Result zero, carry out equal to Rm[31]
// LSR by more than 32: Result zero, carry out zero
// ASR by 32 or more: Result filled with and carry out
// equal to bit 31 of Rm.
// ROR by 32: Result equal to Rm, carry out is Rm[31]
// ROR by n where n is greater than 32: Gives the same
// result and carry out as ROR by n-32
operand2 := 0;
case ((cpuCurrentOpcode shr 5) and $3) of
LSL: barrelCarry := (shift = 32) and (regs[Rm] and (1 shl 0) <> 0);
LSR: barrelCarry := (shift = 32) and (regs[Rm] shr 31 <> 0);
ASR: operand2 := BarrelShifter(regs[Rm], (cpuCurrentOpcode shr 5) and $3, shift);
ROR: begin
if shift and 31 = 0 then begin
operand2 := regs[Rm];
barrelCarry := regs[Rm] shr 31 <> 0;
end else
operand2 := BarrelShifter(regs[Rm], (cpuCurrentOpcode shr 5) and $3, shift and 31);
end;
end;
end else
// If the byte has a value between 1 and 31, the shifted
// result will exactly match that of a specified shift with
// the same value and shift operation.
operand2 := BarrelShifter(regs[Rm], (cpuCurrentOpcode shr 5) and $3, shift);
// Perform the data processing operation
DataProcessing(true, operand2);
// Register specified shift data processing opcodes have a
// trailing I cycle
Dec(quota, cycI);
end else if cpuCurrentOpcode and ALU_IMM_ROT_MASK = ALU_IMM_ROT_SIG then begin
// The immediate operand rotate field is a 4 bit unsigned
// integer which specifies a shift operation on the 8 bit
// immediate value. This value is zero extended to 32 bits,
// and then subject to a rotate right by twice the value in
// the rotate field.
shift := ((cpuCurrentOpcode shr 8) and $F) shl 1;
operand2 := cpuCurrentOpcode and $FF;
barrelCarry := operand2 and (1 shl (shift-1)) <> 0;
operand2 := (operand2 shr shift) or (operand2 shl (32-shift));
// Perform the data processing operation
DataProcessing(false, operand2);
end else if cpuCurrentOpcode and SINGLE_DATA_SWAP_MASK = SINGLE_DATA_SWAP_SIG then
SingleDataSwap
else if cpuCurrentOpcode and HW_XFER_REGOFS_MASK = HW_XFER_REGOFS_SIG then
HalfwordXfer
else if cpuCurrentOpcode and HW_XFER_IMMOFS_MASK = HW_XFER_IMMOFS_SIG then
HalfwordXfer
else if cpuCurrentOpcode and UNDEFINED_MASK = UNDEFINED_SIG then
UndefinedOpcode
else if cpuCurrentOpcode and SINGLE_DATA_XFER_MASK = SINGLE_DATA_XFER_SIG then
SingleDataTransfer
else if cpuCurrentOpcode and BLOCK_DATA_XFER_MASK = BLOCK_DATA_XFER_SIG then
BlockDataTransfer
else if cpuCurrentOpcode and SWI_MASK = SWI_SIG then
SoftwareInterrupt
else if cpuCurrentOpcode and COPRO_DATA_XFER_MASK = COPRO_DATA_XFER_SIG then
UndefinedOpcode
else if cpuCurrentOpcode and COPRO_DATA_OP_MASK = COPRO_DATA_OP_SIG then
UndefinedOpcode
else if cpuCurrentOpcode and COPRO_REG_XFER_MASK = COPRO_REG_XFER_SIG then
UndefinedOpcode
else
UndefinedState('ARM Decoder: Does not match any known instruction class');
end;
if memStopAtAddy(regs[R15] - 4) then begin
hitBreakpoint := true;
Exit;
end;
until (quota <= 0) or thumbMode;
if thumbMode and not haveFlippedThumb then
hitBreakpoint := memStopAtAddy(regs[R15] - 2);
end;
//////////////////////////////////////////////////////////////////////
end.
//////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -