⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpumisc.pas

📁 一个不出名的GBA模拟器
💻 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 + -