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

📄 cpuarmcore.pas

📁 一个不出名的GBA模拟器
💻 PAS
📖 第 1 页 / 共 3 页
字号:

  // 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 + -