📄 cpugraphics.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// cpuGraphics.pas: CPU graphics subsystem //
// Implements the HV timer system and all of the rendering code //
// //
// 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: //
// The code here comes as close to emulating macros in Object //
// Pascal as you're ever likely to see, and it isn't pretty. //
// The offending code is there to implement the myriad rendering //
// combinations for sprites and backgrounds. In all, there are 40 //
// combinations for sprites, and 20 for backgrounds, a staggering //
// amount of code duplication. Instead, there are 13 gfx*.pas //
// files that implement different background or sprite types, and //
// two additional files (gfxDrawPixel.pas & gfxDrawPixelInc.pas) //
// that implement drawing an individual pixel. These are bound //
// together using a bunch of DEFINES and included into this file. //
// //
// Its nasty, its ugly, its unholy, but its *still* better than //
// the alternative. //
// //
// If I were to write this again, I'd probably play around with //
// runtime code generation like some of the newer software 3d //
// engines use, where they create a specific shader on the fly //
// from component blocks. I could do that every time the mode //
// is switched, or just generate all of them at startup. //
// //
// While we're on the subject of possibly deranged design //
// choices, why not look at the sprite system. Its designed to //
// render sprites in the correct order, supporting all of the //
// beautifully flawed scenarios that might arise, when the sprite //
// render quota runs out or multiple blendable sprites overlap. //
// The current code fufils its design criteria, but is pretty //
// inefficient in the process, with lots of array walking, and a //
// nasty amount of data translation from OAM to my own //
// TSpriteEntry format, all per scanline. //
// //
// One option that might be fruitful to look into is caching a //
// set of TSpriteEntry's every time OAM is written to, which is //
// something I've tried to avoid, but it may pay off considerably //
// in terms of speed. I haven't actually profiled the graphics //
// code in a long time tho, it may not even be worth pursuing. //
// //
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
unit cpuGraphics; ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
SysUtils, nexus, AddressSpace, cpuSound;
//////////////////////////////////////////////////////////////////////
// HBlank() is called every time the HBlankEvent timer elapses
procedure HBlank;
//////////////////////////////////////////////////////////////////////
// Exported functions ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// vmGetLaterID() returns a layer ID for the pixel rendered on the
// current scanline at offset x.
function vmGetLayerID(x: integer): byte;
// vmDrawScanline() does just as it suggests. Width can only be 240
// or 256 (other values will probably work, but its only provided for
// the VRAM observer to render a 256 swath of the screen)
function vmDrawScanline(y, width: integer): Puint16;
// vmRenderSprite() renders the y-th scanline of the i-th sprite to
// the buffer passed in as line. It is only used for the sprite
// observer currently.
procedure vmRenderSprite(i: integer; y: integer; line: Puint16);
//////////////////////////////////////////////////////////////////////
exports
vmGetLayerID,
vmDrawScanline,
vmRenderSprite;
//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
Math, cpuMemory, cpuMisc, cpuPeripherals;
//////////////////////////////////////////////////////////////////////
type
TSpriteEntry = record
a, b, c, index: uint16;
priority, width, height, run: byte;
x, y: integer;
rotscale, doublesize: boolean;
next, typ: byte;
end;
PSpriteEntry = ^TSpriteEntry;
TSpriteRenderFunc = procedure (y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
TTextRenderFunc = procedure (y: integer; line: Puint16; wins, ids: Puint8; mask: byte; CR, scrollX, scrollY: uint16);
TRSRenderFunc = procedure (y: integer; line: Puint16; wins, ids: Puint8; mask: byte; CR: uint16; params: PRotScaleSet);
//////////////////////////////////////////////////////////////////////
// Globals shared between the rendering functions ////////////////////
//////////////////////////////////////////////////////////////////////
var
// The temporary sprite tree (pretty inefficient)
spriteTable: array[0..127] of TSpriteEntry;
// the last sprite entry to draw (for the scanline limit)
lastEntry: integer;
// The chain heads for the sprite renderer (god, what was I
// thinking when I wrote this crackheaded code)
spritePoints: array[0..4] of integer;
// Stuff used for a single rendered scanline
screenData: array[0..255] of uint16;
winData: array[0..255] of byte;
idData: array[0..255] of byte;
// 240 (256 used for vram viewer)
gfxScreenWidth: integer;
//////////////////////////////////////////////////////////////////////
{$HINTS OFF}
//////////////////////////////////////////////////////////////////////
{$DEFINE SPRITE}
//////////////////////////////////////////////////////////////////////
procedure RenderRS256Color1DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D256Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure RenderRS256Color1DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D256Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure RenderRS256Color1DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D256Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure RenderRS256Color1DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D256Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure RenderRS256Color1DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D256Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure RenderRS16Color1DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D16Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure RenderRS16Color1DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D16Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure RenderRS16Color1DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D16Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure RenderRS16Color1DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D16Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure RenderRS16Color1DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS1D16Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure RenderRS256Color2DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D256Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure RenderRS256Color2DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D256Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure RenderRS256Color2DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D256Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure RenderRS256Color2DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D256Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure RenderRS256Color2DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D256Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure RenderRS16Color2DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D16Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure RenderRS16Color2DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D16Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure RenderRS16Color2DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D16Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure RenderRS16Color2DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D16Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure RenderRS16Color2DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDrawRS2D16Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure Render16Color1DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D16Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure Render16Color1DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D16Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure Render16Color1DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D16Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure Render16Color1DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D16Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure Render16Color1DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D16Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure Render256Color1DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D256Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure Render256Color1DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D256Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure Render256Color1DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D256Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure Render256Color1DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D256Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure Render256Color1DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw1D256Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure Render16Color2DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D16Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure Render16Color2DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D16Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure Render16Color2DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D16Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure Render16Color2DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D16Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure Render16Color2DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D16Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
procedure Render256Color2DSprite0(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D256Sprite.pas}
{$DEFINE BLEND}{$DEFINE ABLEND}
procedure Render256Color2DSprite1(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D256Sprite.pas}
{$UNDEF ABLEND}{$DEFINE FADEUP}
procedure Render256Color2DSprite2(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D256Sprite.pas}
{$UNDEF FADEUP}{$DEFINE FADEDOWN}
procedure Render256Color2DSprite3(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D256Sprite.pas}
{$UNDEF FADEDOWN}{$UNDEF BLEND}
{$DEFINE OBJWIN}
procedure Render256Color2DSprite4(y: integer; entry: PSpriteEntry; line: Puint16; wins, ids: Puint8; mask: byte);
{$INCLUDE gfxDraw2D256Sprite.pas}
{$UNDEF OBJWIN}
//////////////////////////////////////////////////////////////////////
{$UNDEF SPRITE}
//////////////////////////////////////////////////////////////////////
const
sprite16_1D: array[0..4] of TSpriteRenderFunc =
(Render16Color1DSprite0, Render16Color1DSprite1, Render16Color1DSprite2, Render16Color1DSprite3, Render16Color1DSprite4);
sprite16_2D: array[0..4] of TSpriteRenderFunc =
(Render16Color2DSprite0, Render16Color2DSprite1, Render16Color2DSprite2, Render16Color2DSprite3, Render16Color2DSprite4);
sprite256_1D: array[0..4] of TSpriteRenderFunc =
(Render256Color1DSprite0, Render256Color1DSprite1, Render256Color1DSprite2, Render256Color1DSprite3, Render256Color1DSprite4);
sprite256_2D: array[0..4] of TSpriteRenderFunc =
(Render256Color2DSprite0, Render256Color2DSprite1, Render256Color2DSprite2, Render256Color2DSprite3, Render256Color2DSprite4);
//////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -