📄 cpugraphics.pas
字号:
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 + -