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

📄 cpuarmcore.pas

📁 一个不出名的GBA模拟器
💻 PAS
📖 第 1 页 / 共 3 页
字号:
//////////////////////////////////////////////////////////////////////
//                                                                  //
// 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 + -