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

📄 cpugraphics.pas

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

  r, g, b: byte;
begin
  gfxScreenWidth := width;

  // Only render if the forced blank flag is cleared
  if registers[DISPLAY_CR] and DISPLAY_CR_NO_DISP = 0 then begin
    funcIndex := registers[BLEND_S1] shr 6;

    // Index the current position in the screen data array and clear it
    coeffa := registers[COEFF_Y];
    if coeffa > 15 then coeffa := 16;
    color := Puint16(@(palette[0]))^;
    if funcIndex = 2 then begin
      r := color and $1F;
      g := (color shr 5) and $1F;
      b := (color shr 10) and $1F;
      r := Min(r + ((31 - r)*coeffa) shr 4, 31);
      g := Min(g + ((31 - g)*coeffa) shr 4, 31);
      b := Min(b + ((31 - b)*coeffa) shr 4, 31);
      color := r + g shl 5 + b shl 10;
    end else if funcIndex = 3 then begin
      r := color and $1F;
      g := (color shr 5) and $1F;
      b := (color shr 10) and $1F;
      r := Max(r - (r*coeffa) shr 4, 0);
      g := Max(g - (g*coeffa) shr 4, 0);
      b := Max(b - (b*coeffa) shr 4, 0);
      color := r + g shl 5 + b shl 10;
    end;

    for i := 0 to gfxScreenWidth-1 do
      screenData[i] := color;
    FillChar(idData, gfxScreenWidth, 1 shl 5);

    // Alias the windows array and clear it
    defaultActive := (registers[DISPLAY_ACTIVE] and $1F) or $20;
    if registers[DISPLAY_ACTIVE] shr 5 <> 0 then
      FillChar(winData, gfxScreenWidth, defaultActive and registers[WIN_OUT])
    else
      FillChar(winData, gfxScreenWidth, defaultActive);

    // Process the sprites, building a traversal list for later
    BuildSpriteLists(y);

    // Draw the sprite windows
    if registers[DISPLAY_ACTIVE] and DISPLAY_ACTIVE_OBJW <> 0 then
      RenderSprites(y, 4, @screenData, @winData, @idData);

    // Plonk in the other windows onto the window array
    if registers[DISPLAY_ACTIVE] and DISPLAY_ACTIVE_WIN1 <> 0 then
      if (y >= registers[WIN1_Y0]) and (y < registers[WIN1_Y1]) then begin
        x0 := registers[WIN1_X0];
        x1 := registers[WIN1_X1];
        if x1 > x0 then FillChar(winData[x0], x1-x0+1, defaultActive and registers[WIN1_IN]);
      end;

    if registers[DISPLAY_ACTIVE] and DISPLAY_ACTIVE_WIN0 <> 0 then
      if (y >= registers[WIN0_Y0]) and (y < registers[WIN0_Y1]) then begin
        x0 := registers[WIN0_X0];
        x1 := registers[WIN0_X1];
        if x1 > x0 then FillChar(winData[x0], x1-x0+1, defaultActive and registers[WIN0_IN]);
      end;

    // Render the layers, in order of increasing priority
    case registers[DISPLAY_CR] and DISPLAY_CR_MODEMASK of
      0: begin
        for priority := 3 downto 0 do begin
          // Check text BG0..3 for a matching priority
          for bg := 3 downto 0 do begin
            CR := Puint16(@(registers[BG0_CR + bg shl 1]))^;
            if CR and $3 = priority then
              mode0funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl bg, CR,
                                       Puint16(@(registers[BG0_X0 + bg shl 2]))^ {and $1FF},
                                       Puint16(@(registers[BG0_Y0 + bg shl 2]))^ {and $1FF});
          end;

          // Check the sprite list for a matching priority
          RenderSprites(y, priority, @screenData, @winData, @idData);
        end;
      end;
      1: begin
        for priority := 3 downto 0 do begin
          // Check the R/S BG2 for a matching priority
          CR := Puint16(@(registers[BG2_CR]))^;
          if CR and $3 = priority then
            mode1funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl 2, CR, @(registers[BG2_A]));

          // Check text BG0..1 for a matching priority
          for bg := 1 downto 0 do begin
            CR := Puint16(@(registers[BG0_CR + bg shl 1]))^;
            if CR and $3 = priority then
              mode0funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl bg, CR,
                                       Puint16(@(registers[BG0_X0 + bg shl 2]))^{ and $1FF},
                                       Puint16(@(registers[BG0_Y0 + bg shl 2]))^{ and $1FF});
          end;

          // Check the sprite list for a matching priority
          RenderSprites(y, priority, @screenData, @winData, @idData);
        end;
      end;
      2: begin
        for priority := 3 downto 0 do begin
          // Check the R/S BG3 for a matching priority
          CR := Puint16(@(registers[BG3_CR]))^;
          if CR and $3 = priority then
            mode1funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl 3, CR, @(registers[BG3_A]));

          // Check the R/S BG2 for a matching priority
          CR := Puint16(@(registers[BG2_CR]))^;
          if CR and $3 = priority then
            mode1funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl 2, CR, @(registers[BG2_A]));

          // Check the sprite list for a matching priority
          RenderSprites(y, priority, @screenData, @winData, @idData);
        end;
      end;
      3: begin
        for priority := 3 downto 0 do begin
          // Check the bitmap BG2 for a matching priority
          CR := Puint16(@(registers[BG2_CR]))^;
          if CR and $3 = priority then
            mode3funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl 2, CR, @(registers[BG2_A]));

          // Check the sprite list for a matching priority
          RenderSprites(y, priority, @screenData, @winData, @idData);
        end;
      end;
      4: begin
        for priority := 3 downto 0 do begin
          // Check the bitmap BG2 for a matching priority
          CR := Puint16(@(registers[BG2_CR]))^;
          if CR and $3 = priority then
            mode4funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl 2, CR, @(registers[BG2_A]));

          // Check the sprite list for a matching priority
          RenderSprites(y, priority, @screenData, @winData, @idData);
        end;
      end;
      5: begin
        for priority := 3 downto 0 do begin
          // Check the bitmap BG2 for a matching priority
          CR := Puint16(@(registers[BG2_CR]))^;
          if CR and $3 = priority then
            mode5funcs[funcIndex](y, @screenData, @winData, @idData, 1 shl 2, CR, @(registers[BG2_A]));

          // Check the sprite list for a matching priority
          RenderSprites(y, priority, @screenData, @winData, @idData);
        end;
      end;
    else
      UndefinedState('DrawScanline: display mode > 5');
    end;
  end else begin
    // The screen is white in forced blank
    for i := 0 to gfxScreenWidth-1 do
      screenData[i] := $7FFF;
  end;

{ funkyness ahead:
  i := y and 1;
  while i < gfxScreenWidth do begin
    screenData[i] := not screenData[i];
    Inc(i, 2);
  end;            }

  Result := @screenData;
end;

//////////////////////////////////////////////////////////////////////

function vmGetLayerID(x: integer): byte;
var
  test: byte;
begin
  Result := 0;
  test := idData[x];
  repeat
    test := test shr 1;
    Inc(Result);
  until (test = 1) or (result > 5);
end;

//////////////////////////////////////////////////////////////////////

// Called when entering vertical blank (see ExitHBlank)
procedure EnterVBlank;
begin
  // Set the vertical blank status bit
  registers[DISPLAY_SR] := registers[DISPLAY_SR] or DISPLAY_SR_IN_VBL;

  // Check for DMA triggers
  if (Puint16(@(registers[DMA0_CR]))^ shr 12) and $59 = $49 then InitiateDMATransfer(0);
  if (Puint16(@(registers[DMA1_CR]))^ shr 12) and $59 = $49 then InitiateDMATransfer(1);
  if (Puint16(@(registers[DMA2_CR]))^ shr 12) and $59 = $49 then InitiateDMATransfer(2);
  if (Puint16(@(registers[DMA3_CR]))^ shr 12) and $59 = $49 then InitiateDMATransfer(3);

  // Try to trigger a vertical blank interrupt
  if registers[DISPLAY_SR] and DISPLAY_SR_VBL_IRQ <> 0 then TriggerIRQ(IRQ_VBLANK);
end;

//////////////////////////////////////////////////////////////////////

// Called when exiting vertical blank (see ExitHBlank)
procedure ExitVBlank;
begin
  // Clear the vertical blank bit in the display status register
  registers[DISPLAY_SR] := registers[DISPLAY_SR] and not DISPLAY_SR_IN_VBL;

  // Reset the rotation registers
  Puint32(@(registers[BG2_X]))^ := Puint32(@(registers[BG2_X_LATCH]))^;
  Puint32(@(registers[BG2_Y]))^ := Puint32(@(registers[BG2_Y_LATCH]))^;
  Puint32(@(registers[BG3_X]))^ := Puint32(@(registers[BG3_X_LATCH]))^;
  Puint32(@(registers[BG3_Y]))^ := Puint32(@(registers[BG3_Y_LATCH]))^;
end;

//////////////////////////////////////////////////////////////////////

// Called when entering horizontal blank (see HBlank)
procedure EnterHBlank;
var
  y: uint32;
begin
  y := registers[DISPLAY_Y];

  // Check for the y-line trigger
  if registers[DISPLAY_YTRIG] = y then begin
    registers[DISPLAY_SR] := registers[DISPLAY_SR] or DISPLAY_SR_TRIGGERED;
    if registers[DISPLAY_SR] and DISPLAY_SR_Y_TRIGGER_IRQ <> 0 then TriggerIRQ(IRQ_YLINE);
  end else
    registers[DISPLAY_SR] := registers[DISPLAY_SR] and not DISPLAY_SR_TRIGGERED;

  // See if we are doing visible lines, entering v-blank, or what
  registers[DISPLAY_SR] := registers[DISPLAY_SR] or DISPLAY_SR_IN_HBL;

  // Check for DMA triggers
  if y < 160 then begin
    if (Puint16(@(registers[DMA0_CR]))^ shr 9) and $59 = $51 then InitiateDMATransfer(0);
    if (Puint16(@(registers[DMA1_CR]))^ shr 9) and $59 = $51 then InitiateDMATransfer(1);
    if (Puint16(@(registers[DMA2_CR]))^ shr 9) and $59 = $51 then InitiateDMATransfer(2);
    if (Puint16(@(registers[DMA3_CR]))^ shr 9) and $59 = $51 then InitiateDMATransfer(3);

    // Try to trigger a horizontal blank interrupt
    if registers[DISPLAY_SR] and DISPLAY_SR_HBL_IRQ <> 0 then TriggerIRQ(IRQ_HBLANK);
  end;
end;

//////////////////////////////////////////////////////////////////////

// Called when exiting horizontal blank (see HBlank)
procedure ExitHBlank;
var
  y: uint32;
  b, d: uint32;
begin
  // Update the rot/scale registers
  if registers[BG2_X_DIRTY] = 0 then begin
    b := Puint16(@(registers[BG2_B]))^;
    if b and (1 shl 15) <> 0 then b := b or $FFFF0000;
    Inc(Puint32(@(registers[BG2_X]))^, b);
  end;

  if registers[BG2_Y_DIRTY] = 0 then begin
    d := Puint16(@(registers[BG2_D]))^;
    if d and (1 shl 15) <> 0 then d := d or $FFFF0000;
    Inc(Puint32(@(registers[BG2_Y]))^, d);
  end;

  if registers[BG3_X_DIRTY] = 0 then begin
    b := Puint16(@(registers[BG3_B]))^;
    if b and (1 shl 15) <> 0 then b := b or $FFFF0000;
    Inc(Puint32(@(registers[BG3_X]))^, b);
  end;

  if registers[BG3_Y_DIRTY] = 0 then begin
    d := Puint16(@(registers[BG3_D]))^;
    if d and (1 shl 15) <> 0 then d := d or $FFFF0000;
    Inc(Puint32(@(registers[BG3_Y]))^, d);
  end;

  // Reset the rotation registers
  registers[BG2_X_DIRTY] := 0;
  registers[BG2_Y_DIRTY] := 0;
  registers[BG3_X_DIRTY] := 0;
  registers[BG3_Y_DIRTY] := 0;

  // Increment the y-counter
  y := registers[DISPLAY_Y] + 1;
  if y = 228 then y := 0;
  registers[DISPLAY_Y] := y;

  // Clear the H blank bit and check for V blank
  registers[DISPLAY_SR] := registers[DISPLAY_SR] and not DISPLAY_SR_IN_HBL;
  if y = 160 then
    EnterVBlank
  else if y = 0 then begin
    ExitVBlank;
    OnVideoReady(registers[DISPLAY_Y], vmDrawScanline(registers[DISPLAY_Y], 240));
  end else if y < 160 then begin
    OnVideoReady(registers[DISPLAY_Y], vmDrawScanline(registers[DISPLAY_Y], 240));
  end;
end;

//////////////////////////////////////////////////////////////////////

// HBlank() is called twice per scanline, at 960 and 1232 cycles.
// It handles all of the state changes needed for entering and exiting
// horizontal and vertical blank
procedure HBlank;
begin
  if enteringHBlank then begin
    EnterHBlank;
    Inc(HBlankEvent, 272);
  end else begin
    ExitHBlank;
    Inc(HBlankEvent, 960);
  end;
  enteringHBlank := not enteringHBlank;
end;

//////////////////////////////////////////////////////////////////////

end.

//////////////////////////////////////////////////////////////////////

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -