📄 cpumisc.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// cpuMisc.pas: Support systems for the CPU //
// Pipeline management, mode switch code, the barrel shifter, //
// and software interrupts, traps, etc... //
// //
// 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: //
// None at present. //
// //
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
unit cpuMisc; ////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
SysUtils, nexus, AddressSpace, cpuMemory;
//////////////////////////////////////////////////////////////////////
// Pipeline management functions /////////////////////////////////////
//////////////////////////////////////////////////////////////////////
procedure FlushPipeARM;
procedure FlushPipeThumb;
//////////////////////////////////////////////////////////////////////
// Supporting hardware functions /////////////////////////////////////
//////////////////////////////////////////////////////////////////////
function BarrelShifter(number, shiftType, shift: uint32): uint32;
function cpuReadCPSR: uint32;
procedure cpuWriteCPSR(value: uint32);
//////////////////////////////////////////////////////////////////////
// Exception processing //////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
procedure cpuEnterException(newMode, newVector: uint32);
procedure TriggerIRQ(irqType: uint32);
procedure SoftwareInterrupt;
procedure UndefinedOpcode;
//////////////////////////////////////////////////////////////////////
// Misc. /////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
procedure DebugTrap;
procedure UndefinedState(error: string);
//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// leave PC at nextAddress + L
procedure FlushPipeARM;
begin
regs[R15] := regs[R15] and not 3;
regs[PIPELINE_0] := memReadWordUnc(regs[R15]); // at nextAddress
Inc(regs[R15], 4);
regs[PIPELINE_1] := memReadWordUnc(regs[R15]); // at nextAddress + L
end;
//////////////////////////////////////////////////////////////////////
// leave PC at nextAddress + L
procedure FlushPipeThumb;
begin
regs[R15] := regs[R15] and not 1;
regs[PIPELINE_0] := memReadHalfwordUnc(regs[R15]); // at nextAddress
Inc(regs[R15], 2);
regs[PIPELINE_1] := memReadHalfwordUnc(regs[R15]); // at nextAddress + L
end;
//////////////////////////////////////////////////////////////////////
// This function emulates the barrel shifter, taking a number, shift
// type, and shift amount, and returning the shifted number. It also
// sets the private barrelCarry to the carry-out of the barrel shifter
function BarrelShifter(number, shiftType, shift: uint32): uint32;
begin
case shiftType of
LSL: begin
// A logical shift left (LSL) takes the contents of Rm and
// moves each bit by the specified amount to a more significant position. The least
// significant bits of the result are filled with zeros, and the high bits of Rm which do not
// map into the result are discarded, except that the least significant discarded bit
// becomes the shifter carry output which may be latched into the C bit of the CPSR when
// the ALU operation is in the logical class (see above).
// Note LSL #0 is a special case, where the shifter carry out is the old value of the CPSR C
// flag. The contents of Rm are used directly as the second operand.
if shift = 0 then begin
Result := number;
barrelCarry := carry;
end else begin
Result := number shl shift;
barrelCarry := number and (1 shl (32-shift)) <> 0;
end;
end;
LSR: begin
// A logical shift right (LSR) is similar, but the contents of Rm are moved to less
// significant positions in the result.
//
// The form of the shift field which might be expected to correspond to LSR #0 is used to
// encode LSR #32, which has a zero result with bit 31 of Rm as the carry output. Logical
// shift right zero is redundant as it is the same as logical shift left zero, so the assembler
// will convert LSR #0 (and ASR #0 and ROR #0) into LSL #0, and allow LSR #32 to be
// specified.
if shift = 0 then begin
Result := 0;
barrelCarry := number shr 31 <> 0;
end else begin
barrelCarry := number and (1 shl (shift-1)) <> 0;
Result := number shr shift;
end;
end;
ASR: begin
// An arithmetic shift right (ASR) is similar to logical shift right, except that the high bits
// are filled with bit 31 of Rm instead of zeros. This preserves the sign in 2's complement
// notation.
//
// The form of the shift field which might be expected to give ASR #0 is used to encode
// ASR #32. Bit 31 of Rm is again used as the carry output, and each bit of operand 2 is
// also equal to bit 31 of Rm. The result is therefore all ones or all zeros, according to the
// value of bit 31 of Rm.
if (shift = 0) or (shift > 31) then begin
if number shr 31 = 0 then begin
Result := 0;
barrelCarry := false;
end else begin
Result := $FFFFFFFF;
barrelCarry := true;
end;
end else begin
barrelCarry := number and (1 shl (shift-1)) <> 0;
Result := number shr shift;
if number shr 31 <> 0 then Result := Result or ($FFFFFFFF shl (32-shift));
end;
end;
ROR: begin
// Rotate right (ROR) operations reuse the bits which overshoot in a logical shift right
// operation by reintroducing them at the high end of the result, in place of the zeros used
// to fill the high end in logical right operations.
//
// The form of the shift field which might be expected to give ROR #0 is used to encode
// a special function of the barrel shifter, rotate right extended (RRX). This is a rotate right
// by one bit position of the 33 bit quantity formed by appending the CPSR C flag to the
// most significant end of the contents of Rm.
Result := number;
if shift = 0 then begin
// Not an ROR, but a RRX, which is a ROR of one on the 33 bit register
// CRm, where carry is the 33rd bit and Rm is the 1..32 bits
Result := Result shr 1;
if carry then Result := Result or longword(1 shl 31);
barrelCarry := number and $1 <> 0;
carry := barrelCarry;
end else begin
Result := (Result shr shift) or (Result shl (32-shift));
barrelCarry := number and (1 shl (shift-1)) <> 0;
end;
end;
else
Result := 0;
end;
end;
//////////////////////////////////////////////////////////////////////
procedure UndefinedState(error: string);
var
L: uint32;
begin
if logInvalidStates then begin
if thumbMode then L := 2 else L := 4;
error := Format('[%.8x] %s at %.8x', [cpuCurrentOpcode, error, regs[R15]-L]);
LogWriteLn(error);
end;
end;
//////////////////////////////////////////////////////////////////////
procedure TriggerIRQ(irqType: uint32);
begin
if registers[IRQ_MASTER] and 1 <> 0 then begin
if Puint16(@(registers[IRQ_ENABLED]))^ and irqType <> 0 then begin
Puint16(@(registers[IRQ_FLAGS]))^ := Puint16(@(registers[IRQ_FLAGS]))^ or irqType;
irqPending := true;
end;
end;
end;
//////////////////////////////////////////////////////////////////////
procedure cpuSwitchMode(newMode: byte);
const
// topreg, numregs, spsr
modes: array[0..15, 0..2] of integer = (
(0, 0, 0),
(R14_fiq, 7, SPSR_fiq),
(R14_irq, 2, SPSR_irq),
(R14_svc, 2, SPSR_svc),
(0, 0, -1),
(0, 0, -1),
(0, 0, -1),
(R14_abt, 2, SPSR_abt),
(0, 0, -1),
(0, 0, -1),
(0, 0, -1),
(R14_und, 2, SPSR_und),
(0, 0, -1),
(0, 0, -1),
(0, 0, -1),
(0, 0, 0));
var
i: integer;
t: uint32;
begin
if cpuMode <> newMode then begin
// Cycle out the old registers
for i := 14 downto 14-modes[cpuMode and $F, 1]+1 do begin
t := regs[i+modes[cpuMode and $F, 0]-14];
regs[i+modes[cpuMode and $F, 0]-14] := regs[i];
regs[i] := t;
end;
// Cycle in the new registers
for i := 14 downto 14-modes[newMode and $F, 1]+1 do begin
t := regs[i+modes[newMode and $F, 0]-14];
regs[i+modes[newMode and $F, 0]-14] := regs[i];
regs[i] := t;
end;
// Index the SPSR register and switch modes
SPSR := modes[newMode and $F, 2];
cpuMode := newMode;
end;
end;
//////////////////////////////////////////////////////////////////////
procedure cpuEnterException(newMode, newVector: uint32);
var
oldCPSR: uint32;
begin
// Save the CPSR and switch modes
if thumbMode then Inc(regs[R15], 2);
oldCPSR := cpuReadCPSR;
cpuSwitchMode(newMode);
// Preserve the CPSR and PC in the SPSR and LR
if SPSR > 0 then regs[SPSR] := oldCPSR;
regs[R14] := regs[R15];
// Start executing at the new vector
regs[R15] := newVector;
thumbMode := false;
FlushPipeARM;
end;
//////////////////////////////////////////////////////////////////////
procedure SoftwareInterrupt;
var
swic: uint32;
begin
// The software interrupt (SWI) instruction is used to enter
// supervisor mode in a controlled fashion (similar to INT or RST,
// except with a number in the opcode and a single vector)
if thumbMode then
swic := cpuCurrentOpcode and $FF
else
swic := (cpuCurrentOpcode shr 16) and $FF;
// Throw a note in the log if needed
if logSoftwareInterrupts then begin
if swic <= LAST_SWI then
LogWriteLn(Format('BIOS call to %s from $%.8x', [swiNames[swic], regs[R15]]))
else
LogWriteLn(Format('BIOS call to unknown function $%.2x from $%.8x', [swic, regs[R15]]));
LogWriteLn(Format(' ($%.8x, $%.8x, $%.8x, $%.8x)', [regs[R0], regs[R1], regs[R2], regs[R3]]));
end;
if swic = $FF then begin
logWriteLn('VBA style dprint (SWI #0xFF) detected. Warning: This will crash on hardware');
Exit;
end;
// Switch to supervisor mode after adjusting the PC for prefetch
Dec(regs[R15], 4);
cpuEnterException(MODE_SUPERVISOR, SWI_VECTOR);
end;
//////////////////////////////////////////////////////////////////////
procedure UndefinedOpcode;
begin
// Switch to undefined opcode mode after offering the instruction to
// coprocessors for a cycle and then adjusting the PC for prefetch
Dec(quota, cycI);
Dec(regs[R14], 4);
cpuEnterException(MODE_UNDEFINED, UNDEFINED_INST_VECTOR);
end;
//////////////////////////////////////////////////////////////////////
function cpuReadCPSR: uint32;
begin
Result := cpuMode;
if thumbMode then Result := Result or SR_T;
if fiqDisabled then Result := Result or SR_F;
if irqDisabled then Result := Result or SR_I;
if overflow then Result := Result or SR_V;
if carry then Result := Result or SR_C;
if zero then Result := Result or SR_Z;
if negative then Result := Result or SR_N;
regs[CPSR] := result;
end;
//////////////////////////////////////////////////////////////////////
procedure cpuWriteCPSR(value: uint32);
begin
thumbMode := value and SR_T <> 0;
fiqDisabled := value and SR_F <> 0;
irqDisabled := value and SR_I <> 0;
overflow := value and SR_V <> 0;
carry := value and SR_C <> 0;
zero := value and SR_Z <> 0;
negative := value and SR_N <> 0;
cpuSwitchMode(value and $1F);
end;
//////////////////////////////////////////////////////////////////////
procedure DebugTrap;
var
addr, i: uint32;
st: array[0..256] of char;
begin
// r0 holds $C0DED00D
// r1 holds function
// r2..r3 hold parameters
case regs[r1] of
$0: begin
// dprintf, string in r2, max 256 characters
i := 0;
addr := regs[r2];
repeat
st[i] := char(vmReadByte(addr+i));
Inc(i);
until (i = 256) or (st[i-1] in [#0, #10, #13]);
st[i] := #0;
logWrite(PChar(@st));
end;
$3: begin
hitBreakpoint := true;
haveFlippedThumb := true;
thumbMode := not thumbMode;
end;
$4: Beep;
$42: begin
regs[r0] := $BADF00D5;
regs[r1] := $DEADF00D;
end;
end;
end;
//////////////////////////////////////////////////////////////////////
end.
//////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -