Merge branch 'master' into create-orca-c-demo

This commit is contained in:
Lucas Scharenbroich 2023-03-12 15:50:22 -05:00
commit cf9759b7df
91 changed files with 19832 additions and 12310 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ emu
*_Output.txt
src/GTETestApp
*.2mg
Tool160.SHK
Tool160.SHK
src/Tool160

185
ORCACDefs/gte.h Normal file
View File

@ -0,0 +1,185 @@
/* ********************************************************************
GTE is copyright Lucas Scharenbroich and licensed under the Apache-2.0
License.
The following GTE function definitions are taken from the GTE Toolbox
documentation:
https://lscharen.github.io/iigs-game-engine/toolboxref.html
And from the GTE Macros:
https://github.com/lscharen/iigs-game-engine/blob/d7be9f1be44748b0180c930b1f90b144cda661ea/macros/GTE.Macs.s
The contents of this file are a derivite work from GTE intended to
ease the process of calling GTE / Tool 160 from ORCA/C and are believed
to be permitted under the terms of the Apache-2.0 License.
********************************************************************* */
#ifndef _GTE_HEADER_INCLUDE_
#define _GTE_HEADER_INCLUDE_
#include <types.h>
/*
GTE_IS_SYSTEM_TOOLS_INSTALL is a boolean toggle for controlling what the application assumes about the location of the GTE tool.
If GTE is installed in System:Tools, GTE_IS_SYSTEM_TOOLS_INSTALL must be defined.
Otherwise, GTE_IS_SYSTEM_TOOLS_INSTALL must be undefined.
This will control which header file is used as well as the calls used to load the tool during application startup.
*/
// #define GTE_IS_SYSTEM_TOOLS_INSTALL 1
#ifdef GTE_IS_SYSTEM_TOOLS_INSTALL
#define tool_dispatcher dispatcher
#else
#define tool_dispatcher 0xE10008L
#endif // GTE_IS_SYSTEM_TOOLS_INSTALL
typedef struct TileMapInfo {
Word width;
Word height;
Pointer tileMapPtr;
} TileMapInfo;
typedef struct ScreenInfo {
Word x;
Word y;
Word width;
Word height;
} ScreenInfo;
/* GTE Housekeeping Routines */
extern pascal void GTEBootInit(void) inline(0x01A0, tool_dispatcher);
extern pascal void GTEStartUp(Word dPageAddr, Word capFlags, Word userID) inline(0x02A0, tool_dispatcher);
extern pascal void GTEShutDown(void) inline(0x03A0, tool_dispatcher);
extern pascal Word GTEVersion(void) inline(0x04A0, tool_dispatcher);
extern pascal void GTEReset(void) inline(0x05A0, tool_dispatcher);
extern pascal Word GTEStatus(void) inline(0x06A0, tool_dispatcher);
/* GTE Sprite Routines */
extern pascal void GTECreateSpriteStamp(Word spriteDescriptor, Word vBuffAddr) inline(0x0FA0, tool_dispatcher);
extern pascal Word GTECompileSpriteStamp(Word spriteDescriptor, Word vBuffAddr) inline(0x2DA0, tool_dispatcher);
extern pascal void GTEAddSprite(Word spriteSlot, Word spriteFlags, Word vBuffAddr, Word x, Word y) inline(0x10A0, tool_dispatcher);
extern pascal void GTEMoveSprite(Word spriteSlot, Word x, Word y) inline(0x11A0, tool_dispatcher);
extern pascal void GTEUpdateSprite(Word spriteSlot, Word spriteFlags, Word vBuffAddr) inline(0x12A0, tool_dispatcher);
extern pascal void GTERemoveSprite(Word spriteSlot) inline(0x13A0, tool_dispatcher);
/* GTE Tile Routines */
extern pascal void GTELoadTileSet(Word start, Word finish, Pointer tileSetPtr) inline(0x0EA0, tool_dispatcher);
extern pascal void GTEFillTileStore(Word tileID) inline(0x25A0, tool_dispatcher);
extern pascal void GTESetTile(Word xTile, Word yTile, Word tileID) inline(0x0BA0, tool_dispatcher);
extern pascal void GTECopyTileToDynamic(Word tileID, Word dynID) inline(0x15A0, tool_dispatcher);
extern pascal Word GTEGetTileAt(Word x, Word y) inline(0x1CA0, tool_dispatcher);
extern pascal Pointer GTEGetTileDataAddr() inline(0x24A0, tool_dispatcher);
/* GTE Primary Background Routines */
extern pascal void GTESetBG0Origin(Word x, Word y) inline(0x0CA0, tool_dispatcher);
extern pascal void GTERender(Word flags) inline(0x0DA0, tool_dispatcher);
extern pascal void GTERefresh() inline(0x26A0, tool_dispatcher);
extern pascal struct TileMapInfo GTEGetBG0TileMapInfo() inline(0x19A0, tool_dispatcher);
extern pascal void GTESetBG0TileMapInfo(Word width, Word height, Pointer tileMapPtr) inline(0x1DA0, tool_dispatcher);
/* GTE Secondary Background Routines */
extern pascal void GTESetBG1Origin(Word x, Word y) inline(0x1BA0, tool_dispatcher);
extern pascal void GTECopyPicToBG1(Word width, Word height, Word stride, Pointer picPtr) inline(0x17A0, tool_dispatcher);
extern pascal void GTESetBG1TileMapInfo(Word width, Word height, Pointer tileMapPtr) inline(0x1EA0, tool_dispatcher);
/* GTE Global State Functions */
extern pascal void GTESetScreenMode(Word width, Word height) inline(0x0AA0, tool_dispatcher);
extern pascal void GTESetPalette(Word palNum, Pointer palettePtr) inline(0x16A0, tool_dispatcher);
extern pascal void GTEBindSCBArray(Pointer scbPtr) inline(0x18A0, tool_dispatcher);
extern pascal struct ScreenInfo GTEGetScreenInfo() inline(0x1AA0, tool_dispatcher);
extern pascal void GTESetBG1Displacement(Word offset) inline(0x27A0, tool_dispatcher);
extern pascal void GTESetBG1Rotation(Word rotIndex) inline(0x28A0, tool_dispatcher);
extern pascal void GTEClearBG1Buffer(Word value) inline(0x29A0, tool_dispatcher);
/* GTE Misc. Functions */
extern pascal Word GTEReadControl(void) inline(0x09A0, tool_dispatcher);
extern pascal Word GTEGetSeconds(void) inline(0x14A0, tool_dispatcher);
extern pascal Pointer GTEGetAddress(Word tableId) inline(0x2CA0, tool_dispatcher);
extern pascal void GTESetAddress(Word tableId, Pointer pointer) inline(0x2EA0, tool_dispatcher);
/* GTE Timer Functions */
extern pascal Word GTEAddTimer(Word numTicks, Pointer callback, Word flags) inline(0x1FA0, tool_dispatcher);
extern pascal Word GTERemoveTimer(Word timerID) inline(0x20A0, tool_dispatcher);
extern pascal Word GTEStartScript(Word numTicks, Pointer scriptAddr) inline(0x21A0, tool_dispatcher);
/* GTE Overlay Functions */
extern pascal Word GTESetOverlay(Word top, Word bottom, Pointer procPtr) inline(0x22A0, tool_dispatcher);
extern pascal Word GTEClearOverlay() inline(0x23A0, tool_dispatcher);
/* ReadControl return value bits */
#define PAD_BUTTON_B 0x0100
#define PAD_BUTTON_A 0x0200
#define PAD_KEY_DOWN 0x0400
/* GTE EngineMode definitions */
#define ENGINE_MODE_TWO_LAYER 0x0001
#define ENGINE_MODE_DYN_TILES 0x0002
#define ENGINE_MODE_BNK0_BUFF 0x0004
#define ENGINE_MODE_USER_TOOL 0x8000 /* Communicate if GTE is loaded as a system tool, or a user tool */
/* GTE Render Flags */
#define RENDER_ALT_BG1 0x0001
#define RENDER_BG1_HORZ_OFFSET 0x0002
#define RENDER_BG1_VERT_OFFSET 0x0004
#define RENDER_BG1_ROTATION 0x0008
#define RENDER_PER_SCANLINE 0x0010
#define RENDER_WITH_SHADOWING 0x0020
#define RENDER_SPRITES_SORTED 0x0040
/* Overlay flags */
#define OVERLAY_MASKED 0x0000 /* Overlay has a mask, so the background must be draw first */
#define OVERLAY_SOLID 0x8000 /* Overlay covers the scan line and is fully opaque */
#define OVERLAY_ABOVE 0x0000 /* Overlay is drawn above scanline sprites */
#define OVERLAY_BELOW 0x4000 /* Overlay is drawn below scanline sprites */
/* GetAddress table IDs */
#define scanlineHorzOffset 0x0001
#define scanlineHorzOffset2 0x0002
/* CopyPicToBG1 flags */
#define COPY_PIC_NORMAL 0x0000 /* Copy into BG1 buffer in "normal mode" */
#define COPY_PIC_SCANLINE 0x0001 /* Copy in a way to support BG1 + RENDER_PER_SCANLINE. */
/* GTE Tile Constants */
#define TILE_PRIORITY_BIT 0x4000 /* Put tile on top of sprite */
#define TILE_FRINGE_BIT 0x2000 /* Unused */
#define TILE_SOLID_BIT 0x1000 /* Hint bit used in TWO_LAYER_MODE to optimize rendering */
#define TILE_DYN_BIT 0x0800 /* Is this a Dynamic Tile? */
#define TILE_VFLIP_BIT 0x0400
#define TILE_HFLIP_BIT 0x0200
#define TILE_ID_MASK 0x01FF
#define TILE_CTRL_MASK 0xFE00
/* GTE Sprite Constants */
#define GTE_SPRITE_COMPILES 0x4000
#define GTE_SPRITE_HIDE 0x2000
#define GTE_SPRITE_16X16 0x1800
#define GTE_SPRITE_16X8 0x1000
#define GTE_SPRITE_8X16 0x0800
#define GTE_SPRITE_8X8 0x0000
#define GTE_SPRITE_VFLIP 0x0400
#define GTE_SPRITE_HFLIP 0x0200
/* GTE Sprint Stamp Storage Parameters */
#define GTE_VBUFF_STRIDE_BYTES (12 * 4) /* Each line has 4 slots of 16 pixels + 8 buffer pixels */
#define GTE_VBUFF_TILE_ROW_BYTES (8 * GTE_VBUFF_STRIDE_BYTES) /* Each row is comprised of 8 lines */
#define GTE_VBUFF_TILE_COL_BYTES (4)
#define GTE_VBUFF_SPRITE_STEP (GTE_VBUFF_TILE_ROW_BYTES*3) /* Allocate space for 16 rows + 8 rows of buffer */
#define GTE_VBUFF_SPRITE_START (GTE_VBUFF_TILE_ROW_BYTES+4) /* Start at an offset so $0000 can be used as an empty value */
#define GTE_VBUFF_SLOT_COUNT (48) /* Have space for this many stamps */
#endif /* _GTE_HEADER_INCLUDE_ */

View File

@ -21,7 +21,11 @@ Each demo application has a build script that also builds the toolset and copies
## Dependencies
GTE uses the [merlin32](https://brutaldeluxe.fr/products/crossdevtools/merlin/) assembler to compile its source into GS/OS OMF files and [Cadius](https://brutaldeluxe.fr/products/crossdevtools/cadius/index.html) to copy those files onto a ProDOS disk image. The paths to these tool can be set in the `package.json` file.
* node
* merlin32 (1.1.10+)
* cadius
GTE uses the [merlin32](https://brutaldeluxe.fr/products/crossdevtools/merlin/) [1.1.10](https://github.com/digarok/merlin32/releases/tag/v1.1.10) assembler to compile its source into GS/OS OMF files and [Cadius](https://brutaldeluxe.fr/products/crossdevtools/cadius/index.html) to copy those files onto a ProDOS disk image. The paths to these tool can be set in the `package.json` file.
An empty 2MG disk image is included in `emu/Target.2mg` and is used as the default location for copying demo applications. This image can be mounted in any IIgs emulator.
@ -30,7 +34,6 @@ An empty 2MG disk image is included in `emu/Target.2mg` and is used as the defau
Build of demo app in the IIgs Finder
</p>
# Documentation
Please refer to the <a href="https://lscharen.github.io/iigs-game-engine/toolboxref.html">GTE Toolbox documentation</a>.

View File

@ -1,2 +1,4 @@
GTETool.SHK=Type(E0),AuxType(8002),VersionCreate(00),MinVersion(87),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
Tool160.SHK=Type(E0),AuxType(8002),VersionCreate(00),MinVersion(B8),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
CCode.SHK=Type(E0),AuxType(8002),VersionCreate(00),MinVersion(9C),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)
ChrisV.SHK=Type(E0),AuxType(8002),VersionCreate(00),MinVersion(BC),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)

5
demos/chrisv/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
App
main
*.root
*.sym
*.a

View File

@ -0,0 +1 @@
App=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3)

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

View File

@ -0,0 +1,17 @@
echo off
REM Copy all of the assets into the ProDOS image for emulator testing
REM
REM Pass the path of the Cadius tool as the first argument (%1)
set CADIUS="%1"
set IMAGE="..\\..\\emu\\Target.2mg"
set FOLDER="/GTEDEV/ChrisV"
REM Cadius does not overwrite files, so clear the root folder first
%CADIUS% DELETEFOLDER %IMAGE% %FOLDER%
%CADIUS% CREATEFOLDER %IMAGE% %FOLDER%
REM Now copy files and folders as needed
%CADIUS% ADDFILE %IMAGE% %FOLDER% .\App
%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160

165
demos/chrisv/gte.h Normal file
View File

@ -0,0 +1,165 @@
/* ********************************************************************
GTE is copyright Lucas Scharenbroich and licensed under the Apache-2.0
License.
The following GTE function definitions are taken from the GTE Toolbox
documentation:
https://lscharen.github.io/iigs-game-engine/toolboxref.html
And from the GTE Macros:
https://github.com/lscharen/iigs-game-engine/blob/d7be9f1be44748b0180c930b1f90b144cda661ea/macros/GTE.Macs.s
The contents of this file are a derivite work from GTE intended to
ease the process of calling GTE / Tool 160 from ORCA/C and are believed
to be permitted under the terms of the Apache-2.0 License.
********************************************************************* */
#ifndef _GTE_HEADER_INCLUDE_
#define _GTE_HEADER_INCLUDE_
#include <types.h>
/*
GTE_IS_SYSTEM_TOOLS_INSTALL is a boolean toggle for controlling what the application assumes about the location of the GTE tool.
If GTE is installed in System:Tools, GTE_IS_SYSTEM_TOOLS_INSTALL must be defined.
Otherwise, GTE_IS_SYSTEM_TOOLS_INSTALL must be undefined.
This will control which header file is used as well as the calls used to load the tool during application startup.
*/
// #define GTE_IS_SYSTEM_TOOLS_INSTALL 1
#ifdef GTE_IS_SYSTEM_TOOLS_INSTALL
#define tool_dispatcher dispatcher
#else
#define tool_dispatcher 0xE10008L
#endif // GTE_IS_SYSTEM_TOOLS_INSTALL
typedef struct TileMapInfo {
Word width;
Word height;
Pointer tileMapPtr;
} TileMapInfo;
typedef struct ScreenInfo {
Word x;
Word y;
Word width;
Word height;
} ScreenInfo;
/* GTE Housekeeping Routines */
extern pascal void GTEBootInit(void) inline(0x01A0, tool_dispatcher);
extern pascal void GTEStartUp(Word dPageAddr, Word capFlags, Word userID) inline(0x02A0, tool_dispatcher);
extern pascal void GTEShutDown(void) inline(0x03A0, tool_dispatcher);
extern pascal Word GTEVersion(void) inline(0x04A0, tool_dispatcher);
extern pascal void GTEReset(void) inline(0x05A0, tool_dispatcher);
extern pascal Word GTEStatus(void) inline(0x06A0, tool_dispatcher);
/* GTE Sprite Routines */
extern pascal void GTECreateSpriteStamp(Word spriteDescriptor, Word vBuffAddr) inline(0x0FA0, tool_dispatcher);
extern pascal void GTEAddSprite(Word spriteSlot, Word spriteFlags, Word vBuffAddr, Word x, Word y) inline(0x10A0, tool_dispatcher);
extern pascal void GTEMoveSprite(Word spriteSlot, Word x, Word y) inline(0x11A0, tool_dispatcher);
extern pascal void GTEUpdateSprite(Word spriteSlot, Word spriteFlags, Word vBuffAddr) inline(0x12A0, tool_dispatcher);
extern pascal void GTERemoveSprite(Word spriteSlot) inline(0x13A0, tool_dispatcher);
/* GTE Tile Routines */
extern pascal void GTELoadTileSet(Word start, Word finish, Pointer tileSetPtr) inline(0x0EA0, tool_dispatcher);
extern pascal void GTEFillTileStore(Word tileID) inline(0x25A0, tool_dispatcher);
extern pascal void GTESetTile(Word xTile, Word yTile, Word tileID) inline(0x0BA0, tool_dispatcher);
extern pascal void GTECopyTileToDynamic(Word tileID, Word dynID) inline(0x15A0, tool_dispatcher);
extern pascal Word GTEGetTileAt(Word x, Word y) inline(0x1CA0, tool_dispatcher);
extern pascal Pointer GTEGetTileDataAddr() inline(0x24A0, tool_dispatcher);
/* GTE Primary Background Routines */
extern pascal void GTESetBG0Origin(Word x, Word y) inline(0x0CA0, tool_dispatcher);
extern pascal void GTERender(Word flags) inline(0x0DA0, tool_dispatcher);
extern pascal void GTERefresh() inline(0x26A0, tool_dispatcher);
extern pascal struct TileMapInfo GTEGetBG0TileMapInfo() inline(0x19A0, tool_dispatcher);
extern pascal void GTESetBG0TileMapInfo(Word width, Word height, Pointer tileMapPtr) inline(0x1DA0, tool_dispatcher);
/* GTE Secondary Background Routines */
extern pascal void GTESetBG1Origin(Word x, Word y) inline(0x1BA0, tool_dispatcher);
extern pascal void GTECopyPicToBG1(Word width, Word height, Word stride, Pointer picPtr) inline(0x17A0, tool_dispatcher);
extern pascal void GTESetBG1TileMapInfo(Word width, Word height, Pointer tileMapPtr) inline(0x1EA0, tool_dispatcher);
/* GTE Global State Functions */
extern pascal void GTESetScreenMode(Word width, Word height) inline(0x0AA0, tool_dispatcher);
extern pascal void GTESetPalette(Word palNum, Pointer palettePtr) inline(0x16A0, tool_dispatcher);
extern pascal void GTEBindSCBArray(Pointer scbPtr) inline(0x18A0, tool_dispatcher);
extern pascal struct ScreenInfo GTEGetScreenInfo() inline(0x1AA0, tool_dispatcher);
extern pascal void GTESetBG1Displacement(Word offset) inline(0x27A0, tool_dispatcher);
extern pascal void GTESetBG1Rotation(Word rotIndex) inline(0x28A0, tool_dispatcher);
extern pascal void GTEClearBG1Buffer(Word value) inline(0x29A0, tool_dispatcher);
/* GTE Misc. Functions */
extern pascal Word GTEReadControl(void) inline(0x09A0, tool_dispatcher);
extern pascal Word GTEGetSeconds(void) inline(0x14A0, tool_dispatcher);
/* GTE Timer Functions */
extern pascal Word GTEAddTimer(Word numTicks, Pointer callback, Word flags) inline(0x1FA0, tool_dispatcher);
extern pascal Word GTERemoveTimer(Word timerID) inline(0x20A0, tool_dispatcher);
extern pascal Word GTEStartScript(Word numTicks, Pointer scriptAddr) inline(0x21A0, tool_dispatcher);
/* GTE Overlay Functions */
extern pascal Word GTESetOverlay(Word top, Word bottom, Pointer procPtr) inline(0x22A0, tool_dispatcher);
extern pascal Word GTEClearOverlay() inline(0x23A0, tool_dispatcher);
/* ReadControl return value bits */
#define PAD_BUTTON_B 0x0100
#define PAD_BUTTON_A 0x0200
#define PAD_KEY_DOWN 0x0400
/* GTE EngineMode definitions */
#define ENGINE_MODE_TWO_LAYER 0x0001
#define ENGINE_MODE_DYN_TILES 0x0002
#define ENGINE_MODE_BNK0_BUFF 0x0004
#define ENGINE_MODE_USER_TOOL 0x8000 /* Communicate if GTE is loaded as a system tool, or a user tool */
/* GTE Render Flags */
#define RENDER_ALT_BG1 0x0001
#define RENDER_BG1_HORZ_OFFSET 0x0002
#define RENDER_BG1_VERT_OFFSET 0x0004
#define RENDER_BG1_ROTATION 0x0008
/* GTE Tile Constants */
#define TILE_PRIORITY_BIT 0x4000 /* Put tile on top of sprite */
#define TILE_FRINGE_BIT 0x2000 /* Unused */
#define TILE_SOLID_BIT 0x1000 /* Hint bit used in TWO_LAYER_MODE to optimize rendering */
#define TILE_DYN_BIT 0x0800 /* Is this a Dynamic Tile? */
#define TILE_VFLIP_BIT 0x0400
#define TILE_HFLIP_BIT 0x0200
#define TILE_ID_MASK 0x01FF
#define TILE_CTRL_MASK 0xFE00
/* GTE Sprite Constants */
#define GTE_SPRITE_HIDE 0x2000
#define GTE_SPRITE_16X16 0x1800
#define GTE_SPRITE_16X8 0x1000
#define GTE_SPRITE_8X16 0x0800
#define GTE_SPRITE_8X8 0x0000
#define GTE_SPRITE_VFLIP 0x0400
#define GTE_SPRITE_HFLIP 0x0200
/* GTE Sprint Stamp Storage Parameters */
#define GTE_VBUFF_STRIDE_BYTES (12 * 4) /* Each line has 4 slots of 16 pixels + 8 buffer pixels */
#define GTE_VBUFF_TILE_ROW_BYTES (8 * GTE_VBUFF_STRIDE_BYTES) /* Each row is comprised of 8 lines */
#define GTE_VBUFF_TILE_COL_BYTES (4)
#define GTE_VBUFF_SPRITE_STEP (GTE_VBUFF_TILE_ROW_BYTES*3) /* Allocate space for 16 rows + 8 rows of buffer */
#define GTE_VBUFF_SPRITE_START (GTE_VBUFF_TILE_ROW_BYTES+4) /* Start at an offset so $0000 can be used as an empty value */
#define GTE_VBUFF_SLOT_COUNT (48) /* Have space for this many stamps */
#endif /* _GTE_HEADER_INCLUDE_ */

25
demos/chrisv/package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "chrisv-c-demo",
"version": "1.0.0",
"description": "Using GTE from C",
"main": "index.js",
"config": {
"merlin32": "C:\\Programs\\IIgsXDev\\bin\\Merlin32-1.1.10.exe",
"cadius": "C:\\Programs\\IIgsXDev\\bin\\Cadius.exe",
"gsport": "C:\\Programs\\gsport\\gsport_0.31\\GSPort.exe",
"macros": "../../macros",
"crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe",
"png2iigs": "../../tools/png2iigs.js"
},
"scripts": {
"gsport": "%npm_package_config_gsport%",
"test:all": "npm run build && npm run build:image && %npm_package_config_gsport%",
"build": "npm run build:tool && npm run build:sys16",
"test": "npm run build:sys16 && npm run build:image && %npm_package_config_gsport%",
"build:image": "build-image.bat %npm_package_config_cadius%",
"build:sys16": "iix compile foo.c test.c && iix -DKeepType=S16 link test foo keep=App",
"build:tiles": "node %npm_package_config_png2iigs% ./assets/tileset.png --format orcac --max-tiles 160 --as-tile-data --verbose > tileData.c",
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s",
"debug": "%npm_package_config_crossrunner% ./App -Debug -CompatibilityLayer"
}
}

117
demos/chrisv/test.c Normal file
View File

@ -0,0 +1,117 @@
#include <loader.h>
#include <locator.h>
#include <memory.h>
#include <misctool.h>
#include <types.h>
/* #define GTE_IS_SYSTEM_TOOLS_INSTALL 1 */
#include "gte.h"
#ifdef GTE_IS_SYSTEM_TOOLS_INSTALL
#define ENGINE_STARTUP_MODE 0x0000
#else
#define ENGINE_STARTUP_MODE ENGINE_MODE_USER_TOOL
#endif
/* toolbox fail handler */
#define TOOLFAIL(string) if (toolerror()) SysFailMgr(toolerror(), "\p" string "\n\r Error Code -> $");
/* path to the local GTE toolset */
Str32 toolPath = {9, "1/Tool160" };
/* Helper function to load GTE as a user tool or system tool */
#ifdef GTE_IS_SYSTEM_TOOLS_INSTALL
void LoadGTEToolSet(Word unused) {
LoadOneTool(160, 0);
TOOLFAIL("Unable to load GTE toolset");
}
#else
void LoadGTEToolSet(Word userId) {
InitialLoadOutputRec loadRec;
// Load the tool from the local directory
loadRec = InitialLoad(userId, (Pointer) (&toolPath), 1);
TOOLFAIL("Unable to load Tool160 from local path");
// Install the tool using the user tool vector
SetTSPtr(0x8000, 160, loadRec.startAddr);
TOOLFAIL("Could not install tool");
}
#endif // GTE_IS_SYSTEM_TOOLS_INSTALL
#ifdef GTE_IS_SYSTEM_TOOLS_INSTALL
void UnloadGTEToolSet() {
UnloadOneTool(160);
TOOLFAIL("Unable to unload GTE toolset");
}
#else
void UnloadGTEToolSet() {
}
#endif // GTE_IS_SYSTEM_TOOLS_INSTALL
extern Byte tiles[];
extern Word tilesPalette[16];
void main(void) {
Word userId;
Word tileId;
Word controlMask, keyPress;
Handle dpHandle;
Word dpAddr;
int a, b;
TLStartUp();
TOOLFAIL("Unable to start tool locator");
userId = MMStartUp();
TOOLFAIL("Unable to start memory manager");
MTStartUp();
TOOLFAIL("Unable to start misc tools");
LoadGTEToolSet(userId);
dpHandle = NewHandle(0x200L, userId, attrBank + attrPage + attrFixed + attrLocked + attrNoCross, 0);
TOOLFAIL("Could not allocate direct page memory for GTE");
dpAddr = (Word) (*dpHandle);
GTEStartUp(dpAddr, (Word) ENGINE_STARTUP_MODE, userId);
/* GTESetScreenMode(160, 200); /* 160x200 is the default screen mode */
GTESetPalette(0, (Pointer)tilesPalette);
GTELoadTileSet(0, 160, tiles); /* Load in the tiles */
GTEFillTileStore(1);
GTERender(0);
for (a = 3; a < 18; a++) {
GTESetTile(a, a, 5);
}
GTESetTile(1, 0, 34);
GTESetTile(2, 0, 33);
GTERender(0);
GTESetTile(0, 3, 3);
GTESetTile(0, 4, 4);
for (b = 4; b < 6; b++) {
for (a = 1; a < 10; a++) {
GTESetBG0Origin(a, b);
tileId = (((b - 1) * 32) + a) | TILE_SOLID_BIT | TILE_HFLIP_BIT;
GTESetTile(a, b, tileId);
GTERender(0);
}
}
do {
controlMask = GTEReadControl();
keyPress = controlMask & 0x007F;
} while (keyPress != 'Q');
GTEShutDown();
UnloadGTEToolSet();
DisposeHandle(dpHandle);
MTShutDown();
MMShutDown(userId);
TLShutDown();
}

6129
demos/chrisv/tileData.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,18 @@ MaxGlobalX equ 16
MaxGlobalY equ 18
MaxBG0X equ 20
MaxBG0Y equ 22
frameCount equ 24
OldOneSecondCounter equ 26
appTmp0 equ 28
seg1x equ 30
seg2x equ 32
seg3x equ 34
seg4x equ 36 ; BG1 x-pos
frameCountTotal equ 38
PlayerX equ 40
PlayerY equ 42
PlayerXVel equ 44
PlayerYVel equ 46
phk
plb
@ -47,15 +57,9 @@ appTmp0 equ 28
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #0 ; Engine in Fast Mode
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_TWO_LAYER ; Engine in Fast Mode as a User Tool
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables
stz StartX
stz StartY
stz frameCount
; Initialize the graphics screen playfield
pea #160
@ -64,6 +68,8 @@ appTmp0 equ 28
; Load a tileset
pea 0
pea 256
pea #^tiledata
pea #tiledata
_GTELoadTileSet
@ -73,10 +79,38 @@ appTmp0 equ 28
pea #TileSetPalette
_GTESetPalette
pea $0
_GTEClearBG1Buffer
; Set up our level data
jsr BG0SetUp
; jsr BG0SetUp
pea 416
pea 30
pea ^App_TileMapBG0
pea App_TileMapBG0+{10*416}
_GTESetBG0TileMapInfo
stz seg1x
stz seg2x
stz seg3x
stz seg4x
jsr SetLimits
jsr DoLoadBG1
; Initialize local variables
lda #56
sta StartX
lda #0
sta StartY
stz frameCount
stz frameCountTotal
pei StartX
pei StartY
_GTESetBG0Origin
lda #193 ; Tile ID of '0'
jsr InitOverlay ; Initialize the status bar
@ -86,6 +120,59 @@ appTmp0 equ 28
sta OldOneSecondCounter
jsr UdtOverlay
; Create some sprites
lda #16
sta PlayerX
lda MaxGlobalY
sec
sbc #48 ; 32 for tiles, 16 for sprite
lda #48 ; 32 for tiles, 16 for sprite
sta PlayerY
stz PlayerXVel
stz PlayerYVel
HERO_SIZE equ {SPRITE_16X16}
HERO_FLAGS equ HERO_SIZE ; no extra H/V bits for now
HERO_FRAME_1 equ HERO_SIZE+145
HERO_VBUFF_1 equ VBUFF_SPRITE_START+0*VBUFF_SPRITE_STEP
HERO_SLOT equ 1
pea HERO_FRAME_1
pea HERO_VBUFF_1
_GTECreateSpriteStamp
pha ; Space for result
pea HERO_SIZE
pea HERO_VBUFF_1
_GTECompileSpriteStamp
pla
pea HERO_SLOT ; Put the player in slot 1
pea HERO_FLAGS+SPRITE_COMPILED ; mark this as a compiled sprite (can only use in RENDER_WITH_SHADOWING mode)
pha ; pass in the token of the compiled stamp
pei PlayerX
pei PlayerY
_GTEAddSprite
; Set up the per-scanline rendering
lda StartX
jsr InitOffsets
pea #scanlineHorzOffset
pea #^BG0Offsets
pea #BG0Offsets
_GTESetAddress
pea #scanlineHorzOffset2
pea #^BG1Offsets
pea #BG1Offsets
_GTESetAddress
pea #RENDER_WITH_SHADOWING ; one regular render to fill the screen with the tilemap
_GTERender
; Set up a very specific test. First, we draw a sprite into the sprite plane, and then
; leave it alone. We are just testing the ability to merge sprite plane data into
; the play field tiles.
@ -101,26 +188,15 @@ EvtLoop
:do_more
cmp #'d'
bne :not_d
lda StartX
cmp MaxBG0X
bcc *+5
brl :do_render
inc StartX
pei StartX
pei StartY
_GTESetBG0Origin
jsr DecRanges
jsr SetOffsets
brl :do_render
:not_d
cmp #'a'
bne :not_a
lda StartX
bne *+5
brl :do_render
dec StartX
pei StartX
pei StartY
_GTESetBG0Origin
jsr IncRanges
jsr SetOffsets
brl :do_render
:not_a
@ -148,12 +224,27 @@ EvtLoop
:not_w
:do_render
pea $0000
jsr SetBG1Animation ; Update the per-scanline BG1 offsets
jsr _GetVBLTicks
and #$00FC
lsr
lsr
sta PlayerX
pea HERO_SLOT
pei PlayerX
pei PlayerY
_GTEMoveSprite ; Move the sprite to this local position
pea #RENDER_PER_SCANLINE
; pea #0
_GTERender
; Update the performance counters
inc frameCount
inc frameCountTotal
pha
_GTEGetSeconds
pla
@ -180,6 +271,13 @@ qtRec adrl $0000
; Color palette
MyDirectPage ds 2
_GetVBLTicks
PushLong #0
_GetTick
pla
plx
rts
SetLimits
pha ; Allocate space for width (in tiles), height (in tiles), pointer
pha
@ -238,7 +336,211 @@ SetLimits
rts
frameCount equ 24
DecRanges
lda seg1x
bne *+5
lda #164
dec
sta seg1x
bit #1
bne :out
lda seg2x
bne *+5
lda #164
dec
sta seg2x
bit #1
bne :out
lda seg3x
bne *+5
lda #164
dec
sta seg3x
:out
rts
IncRanges
lda seg1x
inc
cmp #164
bcc *+5
lda #0
sta seg1x
bit #1
bne :out
lda seg2x
inc
cmp #164
bcc *+5
lda #0
sta seg2x
bit #1
bne :out
lda seg3x
inc
cmp #164
bcc *+5
lda #0
sta seg3x
bit #1
bne :out
lda seg4x
inc
cmp #164
bcc *+5
lda #0
sta seg4x
:out
rts
InitOffsets
pha
ldx #0
ldy #40
jsr _InitRange
ldx #40
ldy #80
jsr _InitRange
ldx #120
ldy #88
jsr _InitRange
jsr _InitBG1
pla
sta seg1x
jsr SetOffset1
lsr
sta seg2x
jsr SetOffset2
lsr
sta seg3x
jsr SetOffset3
jsr SetBG1Offsets
rts
SetOffsets
lda seg1x
jsr SetOffset1
lda seg2x
jsr SetOffset2
lda seg3x
jsr SetOffset3
SetBG1Offsets
pei seg4x
pea 0
_GTESetBG1Origin
rts
SetBG1Animation
pea #scanlineHorzOffset2
pea #^BG1Offsets
lda frameCountTotal
and #$000F
asl
adc #BG1Offsets
pha
_GTESetAddress
rts
SetOffset1
ldx #120
ldy #88
jmp _SetRange
SetOffset2
ldx #40
ldy #80
jmp _SetRange
SetOffset3
ldx #0
ldy #40
jmp _SetRange
_SetRange
pha
txa
asl
tax
:loop2 lda BG0Offsets,x
and #$FF00
ora 1,s
sta BG0Offsets,x
dey
beq :done
inx
inx
cpx #416
bcc :loop2
:done
pla
rts
_offsets dw 0,0,0,1,1,2,3,3,4,4,4,3,3,2,1,1
_InitBG1
ldx #0
ldy #0
:loop lda _offsets,y
sta BG1Offsets,x
iny
iny
cpy #31
bcc *+5
ldy #0
inx
inx
cpx #448
bcc :loop
rts
_InitRange
txa
asl
tax
tya
dec
and #$00FF
xba
:loop1 sta BG0Offsets,x
sec
sbc #$0100
dey
beq :done
inx
inx
cpx #416
bcc :loop1
:done
rts
; Load a binary file in the BG1 buffer
DoLoadBG1
jsr AllocBank ; Alloc 64KB for Load/Unpack
sta BankLoad ; Store "Bank Pointer"
ldx #BG1DataFile ; Load the background file into the bank
jsr LoadFile
pea #164 ; Fill everything
pea #200
pea #256
lda BankLoad
pha
pea $0000
pea COPY_PIC_SCANLINE ; Copy in a mode that supports per-scanline offsets
_GTECopyPicToBG1
rts
BG1DataFile strl '1/bg1.bin'
BG0Offsets ds 416
BG1Offsets ds 448 ; Make this a bit larger so we can just update a pointer
PUT ../StartUp.s
PUT ../../shell/Overlay.s

View File

@ -14,4 +14,5 @@ REM Cadius does not overwrite files, so clear the root folder first
REM Now copy files and folders as needed
%CADIUS% ADDFILE %IMAGE% %FOLDER% .\GTEDemo1
%CADIUS% ADDFILE %IMAGE% %FOLDER% .\gen\bg1.bin
%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\..\src\Tool160

Binary file not shown.

View File

@ -61,7 +61,7 @@ Scale equ 56
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_TWO_LAYER ; +ENGINE_MODE_DYN_TILES
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables

View File

@ -47,7 +47,7 @@ appTmp0 equ 28
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_DYN_TILES ; Engine in Fast Mode
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_DYN_TILES ; Engine in Fast Mode
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables

View File

@ -56,7 +56,7 @@ appTmp2 equ 32
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_DYN_TILES ; Engine in Fast Mode
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_DYN_TILES ; Engine in Fast Mode
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables

View File

@ -49,7 +49,7 @@ appTmp2 equ 32
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_TWO_LAYER ; +ENGINE_MODE_DYN_TILES
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables
@ -60,12 +60,14 @@ appTmp2 equ 32
; Initialize the graphics screen playfield
pea #320
pea #160
pea #200
_GTESetScreenMode
; Load a tileset
pea #0
pea #511
pea #^tiledata
pea #tiledata
_GTELoadTileSet

File diff suppressed because it is too large Load Diff

View File

@ -14,17 +14,17 @@
},
"scripts": {
"test": "npm run build && npm run build:image && npm run gsport",
"gsport": "%npm_package_config_gsport%",
"debug": "%npm_package_config_crossrunner% GTEDemo4 -Source GTEDemo4_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"gsport": "cross-var $npm_package_config_gsport",
"debug": "cross-var $npm_package_config_crossrunner GTEDemo4 -Source GTEDemo4_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"build:all": "npm run build:tiles && npm run build:map && npm run build:tool && npm run build:sys16 && npm run build:image",
"build:map": "node %npm_package_config_tiled2iigs% ./assets/tiled/yoshi-1.json --force-masked --no-gen-tiles --output-dir ./gen",
"build:tiles": "node %npm_package_config_png2iigs% ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --verbose --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:tiles:blocky": "node %npm_package_config_png2iigs% ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --verbose --force-word-alignment --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s",
"build:map": "cross-var node $npm_package_config_tiled2iigs ./assets/tiled/yoshi-1.json --force-masked --no-gen-tiles --output-dir ./gen",
"build:tiles": "cross-var node $npm_package_config_png2iigs ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --var-name tiledata --verbose --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:tiles:blocky": "cross-var node $npm_package_config_png2iigs ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --var-name tiledata --verbose --force-word-alignment --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:sys16": "cross-var $npm_package_config_merlin32 -V $npm_package_config_macros App.s",
"build": "npm run build:tool && npm run build:sys16",
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../../src/Master.s",
"build:image": "build-image.bat %npm_package_config_cadius%",
"build:background": "node %npm_package_config_png2iigs% ./assets/tilesets/bg1.png ./gen/bg1.bin --force-color-match --palette FF00FF,C14F4A,020202,00E100,C89858,216058,DCE9EE,008000,F80080,F5D56C,20308F,A0CDCC,4080A0,70B0D0"
"build:tool": "cross-var $npm_package_config_merlin32 -V $npm_package_config_macros ../../../src/Master.s",
"build:image": "cross-var build-image.bat $npm_package_config_cadius",
"build:background": "cross-var node $npm_package_config_png2iigs ./assets/tilesets/bg1.png ./gen/bg1.bin --force-color-match --palette FF00FF,C14F4A,020202,00E100,C89858,216058,DCE9EE,008000,F80080,F5D56C,20308F,A0CDCC,4080A0,70B0D0"
},
"repository": {
"type": "git",
@ -37,6 +37,7 @@
},
"homepage": "https://github.com/lscharen/iigs-game-engine#readme",
"devDependencies": {
"cross-var": "^1.1.0",
"pngjs": "^6.0.0",
"string-builder": "^0.1.8",
"watch": "latest",

View File

@ -36,6 +36,7 @@ MaxGlobalX equ 16
MaxGlobalY equ 18
MaxBG0X equ 20
MaxBG0Y equ 22
frameCount equ 24
OldOneSecondCounter equ 26
appTmp0 equ 28
PlayerX equ 30
@ -49,6 +50,10 @@ LastHFlip equ 44
SpriteFrame equ 46
SpriteToggle equ 48
SpriteCount equ 50
PlayerX1 equ 52
PlayerY1 equ 54
PlayerX2 equ 56
PlayerY2 equ 58
phk
plb
@ -59,10 +64,11 @@ SpriteCount equ 50
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #0 ; Engine in Fast Mode
lda #ENGINE_MODE_USER_TOOL ; Engine in Fast Mode
jsr GTEStartUp ; Load and install the GTE User Tool
jsr SoundStartUp
jsr StartMusic
; jsr SoundStartUp
; jsr StartMusic
; Initialize local variables
@ -81,6 +87,8 @@ SpriteCount equ 50
; Load a tileset
pea #0
pea #511
pea #^tiledata
pea #tiledata
_GTELoadTileSet
@ -108,15 +116,28 @@ SpriteCount equ 50
lda #16
sta PlayerGlobalX
sta PlayerX
sta PlayerX1
sta PlayerX2
lda MaxGlobalY
sec
sbc #48 ; 32 for tiles, 16 for sprite
lda #48 ; 32 for tiles, 16 for sprite
sta PlayerGlobalY
sta PlayerY
sta PlayerY1
sta PlayerY2
stz PlayerXVel
stz PlayerYVel
; Set the screen to the bottom-left
pea $0000
lda MaxBG0Y
pha
_GTESetBG0Origin
; Create the sprites
HERO_SIZE equ {SPRITE_16X16}
@ -131,6 +152,8 @@ HERO_FRAME_4 equ HERO_SIZE+151
HERO_VBUFF_4 equ VBUFF_SPRITE_START+3*VBUFF_SPRITE_STEP
HERO_SLOT equ 1
; Create stamps of each sprite
pea HERO_FRAME_1
pea HERO_VBUFF_1
_GTECreateSpriteStamp
@ -147,13 +170,39 @@ HERO_SLOT equ 1
pea HERO_VBUFF_4
_GTECreateSpriteStamp
; Compile the sprite stamps and hold the compilation token
pha ; Space for result
pea HERO_SIZE
pea HERO_VBUFF_1
_GTECompileSpriteStamp
pla
pea HERO_SLOT ; Put the player in slot 1
pea HERO_FLAGS
pea HERO_VBUFF_1 ; and use this stamp
pea HERO_FLAGS+SPRITE_COMPILED ; mark this as a compiled sprite (can only use in RENDER_WITH_SHADOWING mode)
pha ; pass in the token of the compiled stamp
pei PlayerX
pei PlayerY
_GTEAddSprite
; brl Exit
; Repeat for each stamp. _GTECompileSpriteStamp will return an error if it runs out of memory
pea HERO_SLOT+1 ; Put the player in slot 1
pea HERO_FLAGS
pea HERO_VBUFF_1 ; and use this stamp
pei PlayerX1
pei PlayerY1
_GTEAddSprite
pea HERO_SLOT+2 ; Put the player in slot 1
pea HERO_FLAGS
pea HERO_VBUFF_1 ; and use this stamp
pei PlayerX2
pei PlayerY2
_GTEAddSprite
EvtLoop
pha
_GTEReadControl
@ -207,7 +256,30 @@ do_render
pei PlayerY
_GTEMoveSprite ; Move the sprite to this local position
pea $0000
pea HERO_SLOT+1
lda PlayerX1
sec
sbc StartX
pha
lda PlayerY1
sec
sbc StartY
pha
_GTEMoveSprite ; Move the sprite to this local position
pea HERO_SLOT+2
lda PlayerX2
sec
sbc StartX
pha
lda PlayerY2
sec
sbc StartY
pha
_GTEMoveSprite ; Move the sprite to this local position
pea #RENDER_WITH_SHADOWING
; pea $0000
_GTERender
; Update the performance counters
@ -226,7 +298,7 @@ do_render
; Exit code
Exit
jsr SoundShutDown
; jsr SoundShutDown
_GTEShutDown
Quit
_QuitGS qtRec
@ -383,6 +455,17 @@ UpdatePlayerPos
ApplyCollisions
; Move coordinates down the list
lda PlayerX1
sta PlayerX2
lda PlayerY1
sta PlayerY2
lda PlayerGlobalX
sta PlayerX1
lda PlayerGlobalY
sta PlayerY1
; Convert global to local coordinates
lda PlayerGlobalX
@ -468,10 +551,10 @@ ApplyCollisions
tax
:frame
pea HERO_SLOT
pei LastHFlip
phx
_GTEUpdateSprite
; pea HERO_SLOT
; pei LastHFlip
; phx
; _GTEUpdateSprite
rts
@ -525,8 +608,6 @@ _GetVBLTicks
plx
rts
frameCount equ 24
MusicFile str '1/overworld.ntp'
PUT ../StartUp.s

View File

@ -49,7 +49,7 @@ appTmp2 equ 32
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables

View File

@ -60,7 +60,7 @@ SpriteCount equ 54
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_TWO_LAYER+ENGINE_MODE_DYN_TILES
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_TWO_LAYER ; +ENGINE_MODE_DYN_TILES
jsr GTEStartUp ; Load and install the GTE User Tool
; Initialize local variables
@ -75,12 +75,14 @@ SpriteCount equ 54
; Initialize the graphics screen playfield
pea #320
pea #160
pea #200
_GTESetScreenMode
; Load a tileset
pea #0
pea #511
pea #^tiledata
pea #tiledata
_GTELoadTileSet
@ -141,6 +143,8 @@ SpriteCount equ 54
; Create the sprites
HERO_SIZE equ {SPRITE_16X16}
HERO_FLAGS equ HERO_SIZE ; no extra H/V bits for now
HERO_FRAME_1 equ {SPRITE_16X16+1}
HERO_VBUFF_1 equ VBUFF_SPRITE_START+0*VBUFF_SPRITE_STEP
HERO_FRAME_2 equ {SPRITE_16X16+7}
@ -168,31 +172,71 @@ HERO_SLOT_2 equ 2
pea HERO_VBUFF_4
_GTECreateSpriteStamp
pea HERO_FRAME_1
DO 0
lda #SPRITE_16X16
sta SpriteFlags1
lda #SPRITE_16X8
sta SpriteFlags2
ELSE
lda #SPRITE_16X16+SPRITE_COMPILED
sta SpriteFlags1
lda #SPRITE_16X8+SPRITE_COMPILED
sta SpriteFlags2
pha ; Space for result
pea HERO_SIZE
pea HERO_VBUFF_1
_GTECompileSpriteStamp
pla
sta HeroFrames1+2
sta HeroFrames1+6
pha ; Space for result
pea HERO_SIZE
pea HERO_VBUFF_2
_GTECompileSpriteStamp
pla
sta HeroFrames1+0
sta HeroFrames1+4
pha ; Space for result
pea HERO_SIZE
pea HERO_VBUFF_3
_GTECompileSpriteStamp
pla
sta HeroFrames2+2
sta HeroFrames2+6
pha ; Space for result
pea HERO_SIZE
pea HERO_VBUFF_4
_GTECompileSpriteStamp
pla
sta HeroFrames2+0
sta HeroFrames2+4
FIN
pea HERO_SLOT_1 ; Put the player in slot 1
lda SpriteFlags1
pha
lda HeroFrames1
pha
pei PlayerX
pei PlayerY
pea HERO_SLOT_1 ; Put the player in slot 1
_GTEAddSprite
pea HERO_SLOT_1
pea $0000
pea HERO_VBUFF_1 ; and use this stamp
_GTEUpdateSprite
pea HERO_FRAME_2
pea HERO_SLOT_2
lda SpriteFlags2
pha
lda HeroFrames2
pha
pei PlayerX
lda PlayerY
clc
adc #16
pha
pea HERO_SLOT_2 ; Put the player in slot 1
_GTEAddSprite
pea HERO_SLOT_2
pea $0000
pea HERO_VBUFF_3 ; and use this stamp
_GTEUpdateSprite
EvtLoop
pha
_GTEReadControl
@ -298,7 +342,9 @@ do_render
pha
_GTESetBG1Origin
pea #RENDER_BG1_HORZ_OFFSET
; pea #RENDER_BG1_HORZ_OFFSET
pea #RENDER_WITH_SHADOWING
; pea #0
_GTERender
; Update the performance counters
@ -529,6 +575,10 @@ Fatal brk $00
qtRec adrl $0000
da $00
; Sprite VBUFF / Compile tokens
SpriteFlags1 ds 2
SpriteFlags2 ds 2
; Color palette
MyDirectPage ds 2

View File

@ -14,17 +14,17 @@
},
"scripts": {
"test": "npm run build && npm run build:image && npm run gsport",
"gsport": "%npm_package_config_gsport%",
"debug": "%npm_package_config_crossrunner% GTEDemo4 -Source GTEDemo4_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"gsport": "cross-var $npm_package_config_gsport",
"debug": "cross-var $npm_package_config_crossrunner GTEDemo4 -Source GTEDemo4_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"build:all": "npm run build:tiles && npm run build:map && npm run build:tool && npm run build:sys16 && npm run build:image",
"build:map": "node %npm_package_config_tiled2iigs% ./assets/tiled/yoshi-1.json --force-masked --no-gen-tiles --output-dir ./gen",
"build:tiles": "node %npm_package_config_png2iigs% ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --verbose --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:tiles:blocky": "node %npm_package_config_png2iigs% ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --verbose --force-word-alignment --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s",
"build:map": "cross-var node $npm_package_config_tiled2iigs ./assets/tiled/yoshi-1.json --force-masked --no-gen-tiles --output-dir ./gen",
"build:tiles": "cross-var node $npm_package_config_png2iigs ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --verbose --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:tiles:blocky": "cross-var node $npm_package_config_png2iigs ./assets/tilesets/smw-256x128-4bpp.png --max-tiles 360 --as-tile-data --verbose --force-word-alignment --transparent-color FF00FF --background-color 216058 > ./gen/App.TileSet.s",
"build:sys16": "cross-var $npm_package_config_merlin32 -V $npm_package_config_macros App.s",
"build": "npm run build:tool && npm run build:sys16",
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../../src/Master.s",
"build:image": "build-image.bat %npm_package_config_cadius%",
"build:background": "node %npm_package_config_png2iigs% ./assets/tilesets/bg1.png ./gen/bg1.bin --force-color-match --palette FF00FF,C14F4A,020202,00E100,C89858,216058,DCE9EE,008000,F80080,F5D56C,20308F,A0CDCC,4080A0,70B0D0"
"build:tool": "cross-var $npm_package_config_merlin32 -V $npm_package_config_macros ../../../src/Master.s",
"build:image": "cross-var build-image.bat $npm_package_config_cadius",
"build:background": "cross-var node $npm_package_config_png2iigs ./assets/tilesets/bg1.png ./gen/bg1.bin --force-color-match --palette FF00FF,C14F4A,020202,00E100,C89858,216058,DCE9EE,008000,F80080,F5D56C,20308F,A0CDCC,4080A0,70B0D0"
},
"repository": {
"type": "git",
@ -37,6 +37,7 @@
},
"homepage": "https://github.com/lscharen/iigs-game-engine#readme",
"devDependencies": {
"cross-var": "^1.1.0",
"pngjs": "^6.0.0",
"string-builder": "^0.1.8",
"watch": "latest",

View File

@ -301,9 +301,12 @@ DoLoadBG1
ldx #BG1AltDataFile
jsr LoadFile
lda altBG1Bank
jsl SetBG1Bank
ldx BankLoad
lda #0
ldy BG1AltBank
ldy BG1DataBank
jsl CopyBinToBG1
rts

View File

@ -105,7 +105,7 @@ InitOverlay
ldx #r_line+{CHAR_WIDTH*4}
jsr _DrawChar
pea $0000
pea $0000 ; logical lines for the overlay bar
pea $0008
pea #^StatusBar
pea #StatusBar
@ -174,6 +174,8 @@ oneSecondCounter ds 2
; Draw the overlay
; A = address of the left edge of the screen
; X = top line to start drawing the overlay (typically 0)
; Y = bottom line to stop drawing the overlayer (typically the overlay height set during call to _SetOverlay)
StatusBar phb ; Called via JSL
phd ; save the direct page register

1
demos/tf4/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
GTETF4

126
demos/tf4/App.Main.s Normal file
View File

@ -0,0 +1,126 @@
; Test driver to exercise graphics routines.
REL
DSK MAINSEG
use Locator.Macs
use Load.Macs
use Mem.Macs
use Misc.Macs
use Util.Macs
use EDS.GSOS.Macs
use GTE.Macs
mx %00
tiledata EXT ; tileset buffer
;TileSetPalette EXT
; Keycodes
LEFT_ARROW equ $08
RIGHT_ARROW equ $15
UP_ARROW equ $0B
DOWN_ARROW equ $0A
; Direct page space
MyUserId equ 0
BankLoad equ 2
StartX equ 4
StartY equ 6
TileMapWidth equ 8
TileMapHeight equ 10
ScreenWidth equ 12
ScreenHeight equ 14
MaxGlobalX equ 16
MaxGlobalY equ 18
MaxBG0X equ 20
MaxBG0Y equ 22
OldOneSecondCounter equ 26
appTmp0 equ 28
appTmp1 equ 30
appTmp2 equ 32
phk
plb
sta MyUserId ; GS/OS passes the memory manager user ID for the application into the program
tdc
sta MyDirectPage ; Keep a copy for the overlay callback
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_USER_TOOL+ENGINE_MODE_TWO_LAYER
jsr GTEStartUp ; Load and install the GTE User Tool
pea $0000 ; Set the first two tiles
pea $0002
pea #^TileData
pea #TileData
_GTELoadTileSet
pea $0000
pea #^TileSetPalette
pea #TileSetPalette
_GTESetPalette
; Fill in the field with a checkboard pattern
stz appTmp1
:tloop0 stz appTmp0
:tloop1 lda appTmp0 ; X
pha
pei appTmp1 ; Y
eor appTmp1
and #$0001
pha ; tile ID
_GTESetTile
inc appTmp0
lda #40
cmp appTmp0
bcs :tloop1
inc appTmp1
lda #25
cmp appTmp1
bcs :tloop0
; Set up a very specific test. First, we draw a sprite into the sprite plane, and then
; leave it alone. We are just testing the ability to merge sprite plane data into
; the play field tiles.
EvtLoop
pha
_GTEReadControl
pla
jsr HandleKeys ; Do the generic key handlers
pea #RENDER_PER_SCANLINE ; Scanline rendering
; pea $0000
_GTERender
brl EvtLoop
; Exit code
Exit
_GTEShutDown
Quit
_QuitGS qtRec
bcs Fatal
Fatal brk $00
qtRec adrl $0000
da $00
; Color palette
TileSetPalette dw $0000,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF,$0FFF
MyDirectPage ds 2
; Stub
SetLimits rts
PUT ../kfest-2022/StartUp.s
PUT Tiles.s

10
demos/tf4/App.s Normal file
View File

@ -0,0 +1,10 @@
; Thunder Force IV Demo
TYP $B3 ; S16 file
DSK GTETF4
XPL
; Segment #1 -- Main execution block
ASM App.Main.s
SNA Main

BIN
demos/tf4/GTETF4 Normal file

Binary file not shown.

3
demos/tf4/README.txt Normal file
View File

@ -0,0 +1,3 @@
Thunder Force IV scanline demo
- q to quit; arrows to scroll, numbers to select screen size
- make sure Overlay is present

41
demos/tf4/Tiles.s Normal file
View File

@ -0,0 +1,41 @@
TileData
; Reserved space (tile 0 is special...
ds 128
; Tile ID 1
; From image coordinates 0, 0
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex ffffffff
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000
hex 00000000

View File

@ -0,0 +1 @@
GTETF4=Type(B3),AuxType(0000),VersionCreate(70),MinVersion(BE),Access(E3),FolderInfo1(000000000000000000000000000000000000),FolderInfo2(000000000000000000000000000000000000)

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.5" tiledversion="1.7.2" name="App.TileSet" tilewidth="8" tileheight="8" tilecount="512" columns="32">
<transformations hflip="1" vflip="1" rotate="0" preferuntransformed="0"/>
<image source="../tilesets/smb-256-128-4bpp.png" trans="ff00ff" width="256" height="128"/>
<tile id="136">
<animation>
<frame tileid="136" duration="256"/>
<frame tileid="138" duration="256"/>
<frame tileid="140" duration="256"/>
<frame tileid="142" duration="256"/>
</animation>
</tile>
<tile id="137">
<animation>
<frame tileid="137" duration="256"/>
<frame tileid="139" duration="256"/>
<frame tileid="141" duration="256"/>
<frame tileid="143" duration="256"/>
</animation>
</tile>
<tile id="168">
<animation>
<frame tileid="168" duration="256"/>
<frame tileid="170" duration="256"/>
<frame tileid="172" duration="256"/>
<frame tileid="174" duration="256"/>
</animation>
</tile>
<tile id="169">
<animation>
<frame tileid="169" duration="256"/>
<frame tileid="171" duration="256"/>
<frame tileid="173" duration="256"/>
<frame tileid="175" duration="256"/>
</animation>
</tile>
</tileset>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.5" tiledversion="1.7.2" orientation="orthogonal" renderorder="right-down" width="416" height="30" tilewidth="8" tileheight="8" infinite="0" nextlayerid="4" nextobjectid="2">
<editorsettings>
<export target="world_1-1.json" format="json"/>
</editorsettings>
<tileset firstgid="1" source="Overworld.tsx"/>
<layer id="1" name="App.TileMapBG0" width="416" height="30">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,153,154,153,154,153,154,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,56,10,0,0,0,0,0,153,154,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,186,186,186,186,186,186,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,10,0,0,0,0,185,186,186,187,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,156,157,156,157,156,157,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,155,156,157,158,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,0,0,0,0,0,0,188,188,188,188,188,188,159,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,188,188,188,188,188,0,0,0,0,0,0,0,0,188,188,159,160,159,160,188,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,0,0,0,0,0,0,26,26,26,26,26,26,191,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,26,26,26,26,26,0,0,0,0,0,0,0,0,26,26,191,192,191,192,26,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,7,8,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,31,32,31,32,31,32,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,160,0,0,0,0,0,188,188,159,160,188,188,159,160,188,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,188,159,160,188,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,188,0,0,0,0,0,0,0,0,0,0,188,188,188,188,0,0,0,0,0,0,0,0,159,160,0,0,0,0,159,160,0,0,0,0,159,160,0,0,0,0,0,0,0,0,0,0,188,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,188,188,188,0,0,0,0,0,0,0,0,0,0,0,0,5,6,0,0,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,0,0,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,188,188,188,159,160,188,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,26,26,26,26,26,26,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,192,0,0,0,0,0,26,26,191,192,26,26,191,192,26,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,26,191,192,26,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,26,0,0,0,0,0,0,0,0,0,0,26,26,26,26,0,0,0,0,0,0,0,0,191,192,0,0,0,0,191,192,0,0,0,0,191,192,0,0,0,0,0,0,0,0,0,0,26,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,26,26,26,0,0,0,0,0,0,0,0,0,0,0,0,7,8,0,0,0,0,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,0,0,0,0,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,26,26,26,191,192,26,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,7,8,7,8,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,26,64,26,26,64,26,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,0,0,0,0,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,0,0,0,0,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,0,0,0,0,0,0,0,0,26,64,26,26,64,26,0,0,0,0,
0,0,0,0,49,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,49,50,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,0,0,0,0,7,8,7,8,0,0,0,0,0,0,0,0,49,50,0,0,0,0,0,0,7,8,7,8,7,8,0,0,0,0,7,8,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,7,8,7,8,7,8,7,8,0,0,0,0,0,0,0,0,49,50,0,0,0,0,0,0,9,10,0,0,0,0,0,0,31,32,24,25,24,25,24,25,31,32,0,0,
0,0,0,48,21,54,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,48,21,54,51,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,21,54,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,0,0,0,0,5,6,5,6,5,6,0,0,0,0,0,48,21,54,51,0,0,0,5,6,5,6,5,6,5,6,0,0,0,0,5,6,5,6,5,6,0,0,0,0,0,0,0,0,0,0,11,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,12,13,14,0,0,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,0,0,48,21,54,51,0,0,0,0,0,9,10,0,0,0,0,0,0,26,26,26,26,57,58,26,26,26,26,0,0,
0,0,48,21,21,21,21,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,2147483697,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,48,21,21,21,21,51,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,49,2147483697,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,21,21,21,21,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,2147483697,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,0,0,0,0,7,8,7,8,7,8,0,0,0,0,48,21,21,21,21,51,0,0,7,8,7,8,7,8,7,8,0,0,0,0,7,8,7,8,7,8,0,0,0,0,0,0,49,2147483697,0,0,15,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,17,18,0,0,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,0,0,0,0,0,0,48,21,21,21,21,51,0,0,0,0,9,10,0,0,0,0,0,0,26,26,26,26,64,64,26,26,26,26,0,0,
0,48,21,54,21,21,54,21,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,35,34,35,34,35,0,0,0,48,21,54,2147483696,0,0,0,0,0,0,0,0,0,0,0,34,35,0,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,0,34,35,34,35,0,0,0,0,19,20,21,22,0,48,21,54,21,21,54,21,51,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,34,35,34,35,34,35,0,0,0,48,21,54,2147483696,0,0,0,0,0,0,0,0,0,0,0,34,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,35,34,35,0,0,0,0,0,0,0,0,0,48,21,54,21,21,54,21,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,35,34,35,34,35,0,0,0,48,21,54,2147483696,0,0,0,0,0,0,0,0,0,0,0,34,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,5,6,5,6,5,6,34,35,34,35,5,6,5,6,5,6,5,6,0,48,21,54,21,21,54,21,5,6,5,6,5,6,5,6,5,6,0,0,0,0,5,6,5,6,5,6,5,6,0,0,0,48,21,54,2147483696,0,19,20,21,22,0,0,0,0,0,0,34,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,6,0,0,0,0,0,48,21,54,21,21,54,21,51,0,0,0,5,6,0,0,0,0,0,0,26,26,26,26,64,64,26,26,26,26,0,0,
48,21,21,21,21,21,21,21,21,51,0,0,0,0,0,0,0,0,0,0,0,0,0,36,37,37,37,37,37,37,38,0,48,21,21,21,21,2147483696,0,0,0,0,0,0,0,0,0,36,37,37,38,0,0,0,0,0,19,20,21,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,0,0,0,36,37,37,37,37,38,0,0,0,19,20,21,22,48,21,21,21,21,21,21,21,21,51,0,0,0,0,0,0,0,0,19,20,21,22,0,36,37,37,37,37,37,37,38,0,48,21,21,21,21,2147483696,0,0,0,0,0,0,0,0,0,36,37,37,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,37,37,37,37,38,0,0,0,0,0,0,0,48,21,21,21,21,21,21,21,21,51,0,0,0,0,0,0,0,0,0,0,0,0,0,36,37,37,37,37,37,37,38,0,48,21,21,21,21,2147483696,0,0,0,0,0,0,0,0,0,36,37,37,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,8,7,8,7,8,37,37,37,37,7,8,7,8,7,8,7,8,48,21,21,21,21,21,21,21,7,8,7,8,7,8,7,8,7,8,0,0,0,0,7,8,7,8,7,8,7,8,38,0,48,21,21,21,21,2147483696,19,20,21,22,0,0,0,0,0,36,37,37,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,20,21,22,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,0,0,0,0,48,21,21,21,21,21,21,21,21,51,0,0,7,8,0,0,0,0,0,0,26,26,26,26,64,64,26,26,26,26,0,0,
1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,
3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,
1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,
3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,0,0,0,0,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4,3,4
</data>
</layer>
</map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

17
demos/tf4/build-image.bat Normal file
View File

@ -0,0 +1,17 @@
echo off
REM Copy all of the assets into the ProDOS image for emulator testing
REM
REM Pass the path of the Cadius tool as the first argument (%1)
set CADIUS="%1"
set IMAGE="..\\..\\emu\\Target.2mg"
set FOLDER="/GTEDEV/TF4"
REM Cadius does not overwrite files, so clear the root folder first
%CADIUS% DELETEFOLDER %IMAGE% %FOLDER%
%CADIUS% CREATEFOLDER %IMAGE% %FOLDER%
REM Now copy files and folders as needed
%CADIUS% ADDFILE %IMAGE% %FOLDER% .\GTETF4
%CADIUS% ADDFILE %IMAGE% %FOLDER% ..\..\src\Tool160

44
demos/tf4/package.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "tf4-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"config": {
"merlin32": "C:\\Programs\\IIgsXDev\\bin\\Merlin32-1.1.10.exe",
"cadius": "C:\\Programs\\IIgsXDev\\bin\\Cadius.exe",
"gsport": "C:\\Programs\\gsport\\gsport_0.31\\GSPort.exe",
"macros": "../../macros",
"crossrunner": "C:\\Programs\\Crossrunner\\Crossrunner.exe",
"png2iigs": "../../tools/png2iigs.js",
"tiled2iigs": "../../tools/tiled2iigs.js"
},
"scripts": {
"test": "npm run build && npm run build:image && npm run gsport",
"gsport": "%npm_package_config_gsport%",
"debug": "%npm_package_config_crossrunner% GTETestSprites -Source GTETestSprites_S02_MAINSEG_Output.txt -Debug -CompatibilityLayer",
"build:all": "npm run build:tiles && npm run build:map && npm run build:tool && npm run build:sys16 && npm run build:image",
"build:map": "node %npm_package_config_tiled2iigs% ./assets/tiled/world_1-1.json --empty-tile 33 --no-gen-tiles --output-dir ./gen",
"build:map:masked": "node %npm_package_config_tiled2iigs% ./assets/tiled/world_1-1.json --force-masked --empty-tile 33 --no-gen-tiles --output-dir ./gen",
"build:tiles": "node %npm_package_config_png2iigs% ./assets/tilesets/smb-256-128-4bpp.png --max-tiles 360 --as-tile-data --transparent-color FF00FF --background-color 6B8CFF --verbose > ./gen/App.TileSet.s",
"build:sys16": "%npm_package_config_merlin32% -V %npm_package_config_macros% App.s",
"build": "npm run build:tool && npm run build:sys16",
"build:tool": "%npm_package_config_merlin32% -V %npm_package_config_macros% ../../src/Master.s",
"build:image": "build-image.bat %npm_package_config_cadius%"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lscharen/iigs-game-engine.git"
},
"author": "Lucas Scharenbroich",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/lscharen/iigs-game-engine/issues"
},
"homepage": "https://github.com/lscharen/iigs-game-engine#readme",
"devDependencies": {
"pngjs": "^6.0.0",
"string-builder": "^0.1.8",
"watch": "latest",
"xml2json": "^0.12.0"
}
}

View File

@ -24,40 +24,67 @@ Selected equ 10
Flips equ 12
DTile equ 14
Tmp2 equ 16
ScreenWidth equ 18
ScreenHeight equ 20
SpriteFlags equ 22
frameCount equ 24
OldOneSecondCounter equ 26
SpriteAddr equ 28
RenderMode equ 30
; Control modes
DefaultMode equ RENDER_WITH_SHADOWING
SlowSprites equ 0
; Typical init
phk
plb
sta MyUserId ; GS/OS passes the memory manager user ID for the application into the program
tdc
sta MyDirectPage ; Keep a copy for the overlay callback
_MTStartUp ; GTE requires the miscellaneous toolset to be running
lda #ENGINE_MODE_USER_TOOL ; +ENGINE_MODE_TWO_LAYER
jsr GTEStartUp ; Load and install the GTE User Tool
; Init local variables
stz frameCount
lda #DefaultMode
sta RenderMode
; Initialize the graphics screen to a 256x160 playfield
pea #320
pea #160
pea #200
_GTESetScreenMode
; Load a tileset
pea 0
pea 360
pea #^TSZelda
pea #TSZelda
_GTELoadTileSet
; Set the palette
ldx #11*2
:ploop
lda palette,x
stal $E19E00,x
dex
dex
bpl :ploop
bra sprt
pea $0000
pea #^palette
pea #palette
_GTESetPalette
palette dw $0000,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$023E
sprt
jsr SetLimits
lda #193 ; Tile ID of '0'
jsr InitOverlay ; Initialize the status bar
pha
_GTEGetSeconds
pla
sta OldOneSecondCounter
jsr UdtOverlay
; Create stamps for the sprites we are going to use
HERO_SPRITE equ SPRITE_16X16+1
@ -66,25 +93,38 @@ HERO_SPRITE equ SPRITE_16X16+1
pea VBUFF_SPRITE_START ; vbuff address
_GTECreateSpriteStamp
DO SlowSprites
lda #SPRITE_16X16
sta SpriteFlags
lda #VBUFF_SPRITE_START
sta SpriteAddr
ELSE
lda #SPRITE_16X16+SPRITE_COMPILED
sta SpriteFlags
pha ; Space for result
pea SPRITE_16X16
pea VBUFF_SPRITE_START
_GTECompileSpriteStamp
pla
sta SpriteAddr
FIN
; Create sprites
stz Tmp0
stz Tmp1
stz Tmp1 ; Slot number
ldx Tmp0
:sloop
pea HERO_SPRITE ; sprite id
pei Tmp1 ; Put the sprite in this slot
pei SpriteFlags ; with these flags (h/v flip)
pei SpriteAddr
lda PlayerX,x
pha
lda PlayerY,x
pha
pei Tmp1
_GTEAddSprite
pei Tmp1 ; update the sprite in this slot
pea $0000 ; with these flags (h/v flip)
pea VBUFF_SPRITE_START ; and use this stamp
_GTEUpdateSprite
inc Tmp1
ldx Tmp0
inx
@ -95,80 +135,13 @@ HERO_SPRITE equ SPRITE_16X16+1
; Manually fill in the 41x26 tiles of the TileStore with a test pattern of trees
; lda #TILE_DYN_BIT+TILE_PRIORITY_BIT+0 ; fill the screen the the dynamic tile slot 0
lda #TILE_DYN_BIT+0 ; fill the screen the the dynamic tile slot 0
jsr _fillTileStore
; brl :no_trees
ldx #0
ldy #0
jsr _drawTree
ldx #3
ldy #0
jsr _drawTreeH
ldx #0
ldy #3
jsr _drawTreeV
ldx #3
ldy #3
jsr _drawTreeHV
ldx #9
ldy #0
jsr _drawTree
ldx #9
ldy #3
jsr _drawTree
ldx #12
ldy #0
jsr _drawTree
ldx #12
ldy #3
jsr _drawTree
ldx #6
ldy #0
jsr _drawTreeFront
ldx #6
ldy #3
jsr _drawTreeFront
ldx #6
ldy #6
jsr _drawTreeFront
ldx #3
ldy #6
jsr _drawTreeFront
ldx #0
ldy #6
jsr _drawTreeFront
:no_trees
; Set up the dynamic tile
lda #65
sta DTile
pei DTile
pea $0000
_GTECopyTileToDynamic ; Copy DTile into the first dynamic tile slot
; Initialize the frame counter
stz FrameCount
; Set the screen coordinates
lda #128
lda #0
sta ScreenX
lda #128
lda #0
sta ScreenY
stz Selected
@ -179,309 +152,178 @@ HERO_SPRITE equ SPRITE_16X16+1
pha ; space for result, with pattern
_GTEReadControl
pla
and #$00FF
cmp #'q'
bne :2
brl :exit
:2
; cmp KeyState
; beq :evt_loop
; sta KeyState
; cmp #0
; beq :evt_loop
; cmp #' '
; bne :evt_loop ; only advance one frame at a time
; brl :next
:3
cmp #'1'
bcc :3a
cmp #'9'
bcs :3a
sec
sbc #'1'
asl
sta Selected
brl :next
:3a
cmp #'r'
bne :3b
lda Flips
clc
adc #SPRITE_HFLIP
and #SPRITE_VFLIP+SPRITE_HFLIP
sta Flips
pei Selected ; update the sprite in this slot
pei Flips ; with these flags (h/v flip)
pea VBUFF_SPRITE_START ; and use this stamp
_GTEUpdateSprite
:3b
cmp #'x'
bne :3d
ldx Selected
lda PlayerX,x
clc
adc PlayerU,x
sta PlayerX,x
lda PlayerY,x
clc
adc PlayerV,x
sta PlayerY,x
brl :next
:3d
cmp #'z'
bne :3e
ldx Selected
lda PlayerX,x
sec
sbc PlayerU,x
sta PlayerX,x
lda PlayerY,x
sec
sbc PlayerV,x
sta PlayerY,x
brl :next
:3e
cmp #'s'
bne :4
ldx Selected
inc PlayerY,x
brl :next
:4
cmp #'w'
bne :5
ldx Selected
dec PlayerY,x
brl :next
:5
cmp #'d'
bne :6
ldx Selected
inc PlayerX,x
brl :next
:6
cmp #'a'
bne :7
ldx Selected
dec PlayerX,x
brl :next
:7
cmp #$15 ; left = $08, right = $15, up = $0B, down = $0A
bne :8
inc ScreenX
bra :next
:8 cmp #$08
bne :9
dec ScreenX
brl :next
:9 cmp #$0B
bne :10
inc ScreenY
brl :next
:10 cmp #$0A
bne :11
dec ScreenY
brl :next
:11 cmp #'y'
bne :12
lda DTile
inc
jsr HandleKeys ; Do the generic key handlers
bcs :do_more
brl :do_render
:do_more
and #$007F
sta DTile
pha
pea $0000
_GTECopyTileToDynamic
brl :next
cmp #'a' ; Put in single-step advance mode
bne :skip_a
:a_loop
jsr :next_frame
:a_spin
pha ; space for result, with pattern
_GTEReadControl
pla
bit #PAD_KEY_DOWN
bne :a_spin
and #$007F
cmp #'r' ; resume?
beq :do_render
cmp #'s'
beq :toggle_sort
cmp #'a'
beq :a_loop
bra :a_spin
:toggle_sort lda RenderMode
eor #RENDER_SPRITES_SORTED
sta RenderMode
pei RenderMode
_GTERender
bra :a_spin
:skip_a
:do_render jsr :next_frame
brl :evt_loop
:12 cmp #'f'
bne :13
pea $0000
_GTEFillTileStore
brl :next
:13 cmp #'m'
bne :next
_GTERefresh
:next
; inc ScreenX
:next_frame
jsr _moveSprites
inc ScreenX
inc ScreenY
pei ScreenX
pei ScreenY
_GTESetBG0Origin
; brl no_animate
stz Tmp0
stz Tmp1
ldx Tmp0
loopX
lda PlayerX,x
clc
adc PlayerU,x
sta PlayerX,x
bpl is_posx
cmp #-15
bcs do_y
lda PlayerU,x
eor #$FFFF
inc
sta PlayerU,x
bra do_y
is_posx cmp #128
bcc do_y
lda PlayerU,x
eor #$FFFF
inc
sta PlayerU,x
do_y
lda PlayerY,x
clc
adc PlayerV,x
sta PlayerY,x
bpl is_posy
cmp #-15
bcs do_z
lda PlayerV,x
eor #$FFFF
inc
sta PlayerV,x
bra do_z
is_posy cmp #160
bcc do_z
lda PlayerV,x
eor #$FFFF
inc
sta PlayerV,x
do_z
inc Tmp1
ldx Tmp0
inx
inx
stx Tmp0
cpx #MAX_SPRITES*2
bcc loopX
no_animate
stz Tmp0
stz Tmp1
ldx Tmp0
loopY
pei Tmp1
lda PlayerX,x
pha
lda PlayerY,x
pha
_GTEMoveSprite
inc Tmp1
ldx Tmp0
inx
inx
stx Tmp0
cpx #MAX_SPRITES*2
bcc loopY
pei RenderMode
_GTERender
inc FrameCount
; Debug stuff
; Update the performance counters
inc frameCount
pha
_GTEGetSeconds
pla
cmp LastSecond
beq :no_fps
sta LastSecond
lda FrameCount
ldx #0
ldy #$FFFF
jsr DrawWord
stz FrameCount
:no_fps
; tdc
; ldx #160*32
; jsr DrawWord
brl :evt_loop
cmp OldOneSecondCounter
beq :noudt
sta OldOneSecondCounter
jsr UdtOverlay
stz frameCount
:noudt
rts
; Shut down everything
:exit
Exit
_GTEShutDown
_QuitGS qtRec
qtRec adrl $0000
da $00
; Array of sprite positions and velocities
DO 1
PlayerX dw 8,14,29,34,45,67,81,83,92,101,39,22,7,74,111,9
PlayerY dw 72,24,13,56,35,72,23,8,93,123,134,87,143,14,46,65
PlayerU dw 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4
PlayerV dw 1,1,1,1,2,2,2,4,3,3,3,3,4,4,4,4
ELSE
PlayerX dw 2,12,22,32,42,52,62,72,2,12,22,32,42,52,62,72,
PlayerY dw 24,24,24,24,24,24,24,24,44,44,44,44,44,44,44,44
PlayerU dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
PlayerV dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
FIN
_moveSprites
stz Tmp0
:loop
ldx Tmp0
; Load the GTE User Tool and install it
GTEStartUp
pea $0000
_LoaderStatus
lda PlayerX,x
clc
adc PlayerU,x
sta PlayerX,x
bpl :chk_xpos
eor #$FFFF
inc
sta PlayerX,x
bra :rev_x
:chk_xpos
cmp ScreenWidth
bcc :ok_x
sbc ScreenWidth
eor #$FFFF
inc
clc
adc ScreenWidth
sta PlayerX,x
:rev_x
lda PlayerU,x ; reverse the velocity
eor #$FFFF
inc
sta PlayerU,x
:ok_x
lda PlayerY,x
clc
adc PlayerV,x
sta PlayerY,x
bpl :chk_ypos
eor #$FFFF
inc
sta PlayerY,x
bra :rev_y
:chk_ypos
cmp ScreenHeight
bcc :ok_y
sbc ScreenHeight
eor #$FFFF
inc
clc
adc ScreenHeight
sta PlayerY,x
:rev_y
lda PlayerV,x ; reverse the velocity
eor #$FFFF
inc
sta PlayerV,x
:ok_y
txa
lsr
pha
lda PlayerX,x
pha
lda PlayerY,x
pha
_GTEMoveSprite
lda Tmp0
inc
inc
sta Tmp0
cmp #2*MAX_SPRITES
bcc :loop
rts
; Called by StartUp function callbacks when the screen size changes
SetLimits
pha ; Allocate space for x, y, width, height
pha
pha
pha
_GTEGetScreenInfo
pla
pea $0000
pea $0000
pea $0000
pea $0000
pea $0000 ; result space
lda MyUserId
pha
pea #^ToolPath
pea #ToolPath
pea $0001 ; do not load into special memory
_InitialLoad
bcc :ok1
brk $01
:ok1
ply
pla ; Address of the loaded tool
plx
ply
ply
pea $8000 ; User toolset
pea $00A0 ; Set the tool set number
phx
pha ; Address of function pointer table
_SetTSPtr
bcc :ok2
brk $02
:ok2
clc ; Give GTE a page of direct page memory
tdc
adc #$0100
pha
pea #ENGINE_MODE_DYN_TILES+ENGINE_MODE_TWO_LAYER ; Enable Dynamic Tiles and Two Layer
lda MyUserId ; Pass the userId for memory allocation
pha
_GTEStartUp
bcc :ok3
brk $03
:ok3
pla ; Discard screen corner
pla
sec
sbc #8
sta ScreenWidth ; Pre-adjust to keep sprites on the visible playfield (for compiled sprites)
pla
sec
sbc #16
sta ScreenHeight
rts
_fillTileStore
@ -490,25 +332,22 @@ _fillTileStore
:oloop
stz Tmp1
:iloop
pei Tmp1
pei Tmp0
pei Tmp2
_GTESetTile
lda Tmp2
eor #TILE_PRIORITY_BIT
sta Tmp2
ldx Tmp1
ldy Tmp0
jsr _drawTree
lda Tmp1
inc
clc
adc #2
sta Tmp1
cmp #41
cmp #40
bcc :iloop
lda Tmp0
inc
clc
adc #2
sta Tmp0
cmp #26
cmp #25
bcc :oloop
rts
@ -645,10 +484,12 @@ _drawTreeHV
_GTESetTile
rts
MyDirectPage ds 2
MyUserId ds 2
ToolPath str '1/Tool160'
FrameCount ds 2
LastSecond dw 0
palette dw $0000,$08C1,$0C41,$0F93,$0777,$0FDA,$00A0,$0000,$0D20,$0FFF,$0FD7,$0F59,$0000,$01CE,$0EDA,$0EEE
PUT App.Msg.s
PUT font.s
PUT ../kfest-2022/StartUp.s
PUT ../shell/Overlay.s
; PUT App.Msg.s
; PUT font.s

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ GEM
http_parser.rb (~> 0)
ethon (0.15.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
eventmachine (1.2.7-x64-mingw32)
execjs (2.8.1)
faraday (1.10.0)
@ -49,6 +50,7 @@ GEM
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
ffi (1.15.5)
ffi (1.15.5-x64-mingw32)
forwardable-extended (2.6.0)
gemoji (3.0.1)
@ -218,13 +220,15 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.8.0)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.15.0)
multipart-post (2.2.0)
nokogiri (1.12.5-x64-mingw32)
nokogiri (1.13.9)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (4.24.0)
faraday (>= 1, < 3)
@ -257,7 +261,7 @@ GEM
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.9)
tzinfo (1.2.10)
thread_safe (~> 0.1)
tzinfo-data (1.2022.1)
tzinfo (>= 1.0.0)
@ -270,6 +274,7 @@ GEM
PLATFORMS
x64-mingw32
x86_64-linux
DEPENDENCIES
github-pages (~> 203)

View File

@ -89,7 +89,11 @@ style: toolref
</tr>
<tr>
<td><a href="#GTECreateSpriteStamp">GTECreateSpriteStamp</a></td>
<td> Creates a sprite stamp from the tile set</td>
<td>Creates a sprite stamp from the tile set</td>
</tr>
<tr>
<td><a href="#GTECompileSpriteStamp">GTECompileSpriteStamp</a></td>
<td>Created a compiled sprite from a sprite stamp</td>
</tr>
<tr>
<td><a href="#GTEAddSprite">GTEAddSprite</a></td>
@ -794,12 +798,12 @@ style: toolref
<tr>
<td class="bot">width</td>
<td><em></em></td>
<td><em>Word</em>&mdash;Width of the playfield in pixels. Must be even.</td>
<td><em>Word</em>&mdash;Width of the playfield in bytes. Must be even.</td>
</tr>
<tr>
<td class="bot">height</td>
<td><em></em></td>
<td><em>Word</em>&mdash;Height of the playfield in pixels.</td>
<td><em>Word</em>&mdash;Height of the playfield in scanlines.</td>
</tr>
<tr>
<td class="bot"></td>
@ -1207,10 +1211,25 @@ style: toolref
<td>Offsets each column of the secondary background's vertical position. Unimplemented.</td>
</tr>
<tr>
<td>RENDER_BG1_ROTATIONT</td>
<td>$0004</td>
<td>RENDER_BG1_ROTATION</td>
<td>$0008</td>
<td>Use the internal rotation tables to render the secondary background</td>
</tr>
<tr>
<td>RENDER_PER_SCANLINE</td>
<td>$0010</td>
<td>Set individual scanline properties for the primary and secondary backgrounds from a table.</td>
</tr>
<tr>
<td>RENDER_WITH_SHADOWING</td>
<td>$0020</td>
<td>Uses a rendering mode that does not draw sprites into the tiles but uses shadowing to draw sprites on top of the rendered background and then expose the final composited image. This mode is required to use compiled sprites.</td>
</tr>
<tr>
<td>RENDER_SPRITES_SORTED</td>
<td>$0040</td>
<td>Draws the sprite in y-sorted order instead of sprite slot order.</td>
</tr>
</tbody>
</table>
@ -1234,9 +1253,18 @@ style: toolref
</p>
<p>
A tile set is an array of (up to) 512 tile definitions and each tile definition is 128 bytes. The tile definition
is comprised of four, 32-byte tiles; a normal tile, its mask, a horizontally flipped versio of the tile and its mask.
is comprised of four, 32-byte tiles; a normal tile, its mask, a horizontally flipped version of the tile and its mask.
The first 128 bytes of a tileset must be set to zero.
</p>
<p>
The <em>start</em> and <em>finish</em> parameters define a subset of tiles to be copied into the GTE memory
buffer. This is most commonly used to load a small number of tiles to avoid needing to store a full set of 512
tiles that are mostly unushed. For eample, loading in 16 tiles can be performed as <tt>GTELoadTileSet(0, 17, tilePtr)</tt>.
</p>
<p>
This function can also be used to swap out subsets of tiles on the fly. Any tiles that are replaced which may
be on-screen are not automatically refreshed.
</p>
<div class="section">
<h5>Parameters</h5>
<table class="stack">
@ -1252,6 +1280,16 @@ style: toolref
<tr>
<td class="top">previous contents</td>
</tr>
<tr>
<td class="bot">start</td>
<td></td>
<td><em>Word</em>&mdash;index of the first tile to copy</td>
</tr>
<tr>
<td class="bot">finish</td>
<td></td>
<td><em>Word</em>&mdash;terminating index. This tile is not copied.</td>
</tr>
<tr>
<td class="bot">tileSetPtr</td>
<td></td>
@ -1292,7 +1330,9 @@ style: toolref
</div>
<div class="section">
<h5>C</h5>
<p><tt>extern pascal void GTELoadTileSet(tileSetPtr)</tt></p>
<p><tt>extern pascal void GTELoadTileSet(start, finish, tileSetPtr)</tt></p>
<p><tt>Word start;</tt></p>
<p><tt>Word finish;</tt></p>
<p><tt>Pointer tileSetPtr;</tt></p>
</div>
</div>
@ -1403,6 +1443,90 @@ style: toolref
</div>
</div>
<div class="api">
<h4 class="tn">$2DXX</h4>
<h4 id="GTECompileSpriteStamp">GTECompileSpriteStamp</h4>
<p>
Creates a compiled sprite in a special compilation buffer from an existing sprite stamp. The return value
is an address token that can be passed into the AddSprite function in place of the vBuffAddr parameter as
long as the `SPRITE_COMPILED` flag is set.
</p>
<div class="section">
<h5>Parameters</h5>
<table class="stack">
<colgroup>
<col class="col-1">
<col class="col-2">
<col class="col-3">
</colgroup>
<tbody>
<tr>
<th>Stack before call</th>
</tr>
<tr>
<td class="top">previous contents</td>
</tr>
<tr>
<td class="bot">wordspace</td>
<td></td>
<td><em>Word</em>&mdash;Space for result</td>
</tr>
<tr>
<td class="bot">spriteIdent</td>
<td></td>
<td><em>Word</em>&mdash;sprite identifier word</td>
</tr>
<tr>
<td class="bot">vBuffAddr</td>
<td></td>
<td><em>Word</em>&mdash;Location in the sprite vitual buffer.</td>
</tr>
<tr>
<td class="bot"></td>
<td><em></em></td>
<td><em>SP</em></td>
</tr>
</tbody>
</table>
<table class="stack">
<colgroup>
<col class="col-1">
<col class="col-2">
<col class="col-3">
</colgroup>
<tbody>
<tr>
<th>Stack after call</th>
</tr>
<tr>
<td class="top">previous contents</td>
</tr>
<tr>
<td class="bot">addr</td>
<td></td>
<td><em>Word</em>&mdash;Location in the sprite compilation buffer.</td>
</tr>
<tr>
<td class="bot"></td>
<td><em></em></td>
<td><em>SP</em></td>
</tr>
</tbody>
</table>
</div>
<div class="section">
<h5>Errors</h5>
<p>None</p>
</div>
<div class="section">
<h5>C</h5>
<p><tt>extern pascal Word GTECompileSpriteStamp(spriteIdent, vBuffAddr)</tt></p>
<p><tt>Word spriteIdent;</tt></p>
<p><tt>Word vBuffAddr;</tt></p>
</div>
</div>
<div class="api">
<h4 class="tn">$10XX</h4>
<h4 id="GTEAddSprite">GTEAddSprite</h4>
@ -1430,9 +1554,19 @@ style: toolref
<td class="top">previous contents</td>
</tr>
<tr>
<td class="bot">spriteDescriptor</td>
<td class="bot">spriteSlot</td>
<td></td>
<td><em>Word</em>&mdash;Sprite descriptor word that is used to set the status bits.</td>
<td><em>Word</em>&mdash;The slot to assign the sprite to. There are 16 slots and sprites in lower slots are drawn above the sprites in higher slots.</td>
</tr>
<tr>
<td class="bot">spriteFlags</td>
<td></td>
<td><em>Word</em>&mdash;Sprite flags that define the sprite's properties.</td>
</tr>
<tr>
<td class="bot">vBuffAddr</td>
<td></td>
<td><em>Word</em>&mdash;Sprite address in the VBUFF space, or a compiled sprite address</td>
</tr>
<tr>
<td class="bot">x</td>
@ -1444,11 +1578,6 @@ style: toolref
<td></td>
<td><em>Word</em>&mdash;Signed vertical sprite position on the playfield.</td>
</tr>
<tr>
<td class="bot">spriteSlot</td>
<td></td>
<td><em>Word</em>&mdash;The slot to assign the sprite to. There are 16 slots and sprites in lower slots are drawn above the sprites in higher slots.</td>
</tr>
<tr>
<td class="bot"></td>
<td><em></em></td>
@ -1484,11 +1613,12 @@ style: toolref
</div>
<div class="section">
<h5>C</h5>
<p><tt>extern pascal void GTEAddSprite(spriteDescriptor, x, y, spriteSlot)</tt></p>
<p><tt>extern pascal void GTEAddSprite(spriteSlot, spriteFlags, vBuffAddr, x, y)</tt></p>
<p><tt>Word spriteSlot;</tt></p>
<p><tt>Word spriteDescriptor;</tt></p>
<p><tt>Word vBuffAddr;</tt></p>
<p><tt>Word x;</tt></p>
<p><tt>Word y;</tt></p>
<p><tt>Word spriteSlot;</tt></p>
</div>
<div class="example">
<h5>Sprite Descriptor</h5>
@ -3023,7 +3153,7 @@ style: toolref
<div class="api">
<h4 class="tn">$24XX</h4>
<h4 id="GTEGetTileDataAddr">GTEGetTileDataAddr</h4>
<p>This API is under active development at this time.</p>
<p>Returns a pointer to the bottom of the internal tile data memory. The data for each tile can be found at offset <tt>N * 128</tt></p>
<div class="section">
<h5>Parameters</h5>
<table class="stack">
@ -3039,6 +3169,11 @@ style: toolref
<tr>
<td class="top">previous contents</td>
</tr>
<tr>
<td class="bot">longspace</td>
<td></td>
<td><em>Long</em>&mdash;Space for result</td>
</tr>
<tr>
<td class="bot"></td>
<td><em></em></td>
@ -3060,6 +3195,11 @@ style: toolref
<tr>
<td class="top">previous contents</td>
</tr>
<tr>
<td class="bot">tileDataPtr</td>
<td></td>
<td><em>Long</em>&mdash;<span class="sc">pointer</span> to the tile data</td>
</tr>
<tr>
<td class="bot"></td>
<td><em></em></td>
@ -3074,6 +3214,7 @@ style: toolref
</div>
<div class="section">
<h5>C</h5>
<p><tt>extern pascal Pointer GTEGetTileDataAddr()</tt></p>
</div>
</div>

View File

@ -3,7 +3,7 @@
****************************************
_Err mac
bcc NoErr
do ]0 ; (DO if true)
do ]0 ; (DO if true) Mu
jsr PgmDeath ; this is conditionally compiled if
str ]1 ; we pass in an error statement
else ; (ELSE)
@ -182,216 +182,3 @@ ScriptStep MAC
dw ]1,]2,]3,]4
FIN
<<<
; A specialized CopyMaskedWord macro that draws a tile from a direct page workspace. Used
; to render fringe tiles and sprite tiles when BG1 is active. If there is no second background,
; then one should use the optimized functions which assumes a PEA opcode and only
; needs to copy data words
;
; ]1 : tiledata direct page address , the tilemask direct page address is tiledata + 32
; ]2 : code field offset
CopyMaskedWordD MAC
lda ]1+32 ; load the mask value
bne mixed ; a non-zero value may be mixed
; This is a solid word
lda #$00F4 ; PEA instruction
sta: ]2,y
lda ]1 ; load the tile data
sta: ]2+1,y ; PEA operand
bra next
mixed cmp #$FFFF ; All 1's in the mask is fully transparent
beq transparent
; This is the slowest path because there is a *lot* of work to do. So much that it's
; worth it to change up the environment to optimize things a bit more.
;
; Need to fill in the first 10 bytes of the JMP handler with the following code sequence
;
; lda (00),y
; and #MASK
; ora #DATA
lda #$004C ; JMP instruction
sta: ]2,y
ldx _X_REG ; Get the addressing offset
ldal JTableOffset,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
adc #{]2&$F000} ; adjust for the current row offset
sta: ]2+1,y
tay ; This becomes the new address that we use to patch in
txa ; Get the offset and render a LDA (dp),y instruction
sep #$20
sta: $0001,y ; LDA (00),y operand
lda #$B1
sta: $0000,y ; LDA (00),y opcode
lda #$29
sta: $0002,y ; AND #$0000 opcode
lda #$09
sta: $0005,y ; ORA #$0000 opcode
rep #$20
lda ]1+32 ; insert the tile mask and data into the exception
sta: $0003,y ; handler.
lda ]1
sta: $0006,y
lda #$0D80 ; branch to the prologue (BRA *+15)
sta: $0008,y
ldy _Y_REG ; restore original y-register value and move on
bra next
; This is a transparent word, so just show the second background layer
transparent
lda #$00B1 ; LDA (dp),y instruction
sta: ]2,y
lda _X_REG ; X is the logical tile offset (0, 2, 4, ... 82) left-to-right
ora #$4800 ; put a PHA after the offset
sta: ]2+1,y
next
eom
; Macros to use in the Masked Tile renderer
;
; ]1 : tiledata offset
; ]2 : tilemask offset
; ]3 : code field offset
CopyMaskedWord MAC
ldal ]2,x ; load the mask value
bne mixed ; a non-zero value may be mixed
; This is a solid word
lda #$00F4 ; PEA instruction
sta: ]3,y
ldal ]1,x ; load the tile data
sta: ]3+1,y ; PEA operand
bra next
mixed cmp #$FFFF ; All 1's in the mask is fully transparent
beq transparent
; This is the slowest path because there is a *lot* of work to do. So much that it's
; worth it to change up the environment to optimize things a bit more.
;
; Need to fill in the first 8 bytes of the JMP handler with the following code sequence
;
; lda (00),y
; and #MASK
; ora #DATA
lda #$004C ; JMP instruction
sta: ]3,y
ldx _X_REG ; Get the addressing offset
ldal JTableOffset,x ; Get the address offset and add to the base address
ora _BASE_ADDR ; of the current code field row (2 rows per bank) $0000 or $8000
ora #{]3&$7000} ; adjust for the current line offset within the row
sta: ]3+1,y
tay ; This becomes the new address that we use to patch in
txa ; Get the offset and render a LDA (dp),y instruction
sep #$20
sta: $0001,y ; LDA (00),y operand
lda #$B1
sta: $0000,y ; LDA (00),y opcode
lda #$29
sta: $0002,y ; AND #$0000 opcode
lda #$09
sta: $0005,y ; ORA #$0000 opcode
rep #$20
ldx _T_PTR ; restore the original x-register value
ldal ]2,x ; insert the tile mask and data into the exception
sta: $0003,y ; handler.
ldal ]1,x
sta: $0006,y
; Copy the top 9 bytes down. We have 23 bytes of space and are only using 8. Since 9 + 8 = 17 < 23, we
; can save 3 cycles per word by eliminating the BRA instruction
; lda #$0D80 ; branch to the prologue (BRA *+15)
; sta: $0008,y
lda: $0017,y
sta: $0008,y
lda: $0019,y
sta: $000A,y
lda: $001B,y
sta: $000C,y
lda: $001D,y
sta: $000E,y
lda: $001E,y
sta: $000F,y
ldy _Y_REG ; restore original y-register value and move on
bra next
; This is a transparent word, so just show the second background layer
transparent
lda #$00B1 ; LDA (dp),y instruction
sta: ]3,y
lda _X_REG ; X is the logical tile offset (0, 2, 4, ... 82) left-to-right
ora #$4800 ; put a PHA after the offset
sta: ]3+1,y
next
eom
; Large code blocks that can be used in sprite blitters
; ]1: line number
OneSpriteToCodeField mac
lda blttmp+{]1*4}
andl spritemask+{]1*SPRITE_PLANE_SPAN},x
oral spritedata+{]1*SPRITE_PLANE_SPAN},x
sta: $0004+{]1*$1000},y
lda blttmp+{]1*4}+2
andl spritemask+{]1*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]1*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]1*$1000},y
eom
TwoSpritesToCodeField mac
ldy #{]1*SPRITE_PLANE_SPAN}
lda blttmp+{]1*4}
andl [spritemask_1],y
oral [spritedata_1],y
andl [spritemask_0],y
oral [spritedata_0],y
sta: $0004+{]1*$1000},x
ldy #{]1*SPRITE_PLANE_SPAN}+2
lda blttmp+{]1*4}+2
andl [spritemask_1],y
oral [spritedata_1],y
andl [spritemask_0],y
oral [spritedata_0],y
sta: $0001+{]1*$1000},x
eom
ThreeSpritesToCodeField mac
ldy #{]1*SPRITE_PLANE_SPAN}
lda blttmp+{]1*4}
andl [spritemask_2],y
oral [spritedata_2],y
andl [spritemask_1],y
oral [spritedata_1],y
andl [spritemask_0],y
oral [spritedata_0],y
sta: $0004+{]1*$1000},x
ldy #{]1*SPRITE_PLANE_SPAN}+2
lda blttmp+{]1*4}+2
andl [spritemask_2],y
oral [spritedata_2],y
andl [spritemask_1],y
oral [spritedata_1],y
andl [spritemask_0],y
oral [spritedata_0],y
sta: $0001+{]1*$1000},x
eom

View File

@ -126,6 +126,15 @@ _GTEClearBG1Buffer MAC
_GTESetBG1Scale MAC
UserTool $2B00+GTEToolNum
<<<
_GTEGetAddress MAC
UserTool $2C00+GTEToolNum
<<<
_GTECompileSpriteStamp MAC
UserTool $2D00+GTEToolNum
<<<
_GTESetAddress MAC
UserTool $2E00+GTEToolNum
<<<
; EngineMode definitions
; Script definition
@ -146,12 +155,30 @@ PAD_KEY_DOWN equ $0400
ENGINE_MODE_TWO_LAYER equ $0001
ENGINE_MODE_DYN_TILES equ $0002
ENGINE_MODE_BNK0_BUFF equ $0004
ENGINE_MODE_USER_TOOL equ $8000 ; Communicate if GTE is loaded as a system tool, or a user tool
; Render flags
RENDER_ALT_BG1 equ $0001
RENDER_BG1_HORZ_OFFSET equ $0002
RENDER_BG1_VERT_OFFSET equ $0004
RENDER_BG1_ROTATION equ $0008
RENDER_PER_SCANLINE equ $0010
RENDER_WITH_SHADOWING equ $0020
RENDER_SPRITES_SORTED equ $0040
; Overlay flags
OVERLAY_MASKED equ $0000 ; Overlay has a mask, so the background must be draw first
OVERLAY_SOLID equ $8000 ; Overlay covers the scan line and is fully opaque
OVERLAY_ABOVE equ $0000 ; Overlay is drawn above scanline sprites
OVERLAY_BELOW equ $4000 ; Overlay is drawn below scanline sprites
; GetAddress table IDs
scanlineHorzOffset equ $0001
scanlineHorzOffset2 equ $0002
; CopyPicToBG1 flags
COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode"
COPY_PIC_SCANLINE equ $0001 ; Copy in a way to support BG1 + RENDER_PER_SCANLINE.
; Tile constants
; TILE_RESERVED_BIT equ $8000
@ -164,8 +191,8 @@ TILE_HFLIP_BIT equ $0200
TILE_ID_MASK equ $01FF
TILE_CTRL_MASK equ $FE00
; Sprite constants
SPRITE_COMPILED equ $4000 ; This is a compiled sprite
SPRITE_HIDE equ $2000
SPRITE_16X16 equ $1800
SPRITE_16X8 equ $1000

186
package-lock.json generated
View File

@ -1,8 +1,192 @@
{
"name": "generic-tile-engine",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "generic-tile-engine",
"version": "1.0.0",
"license": "Apache-2.0",
"devDependencies": {
"pngjs": "^6.0.0",
"string-builder": "^0.1.8",
"watch": "latest",
"xml2json": "^0.12.0"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/exec-sh": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz",
"integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==",
"dev": true,
"dependencies": {
"merge": "^1.2.0"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true
},
"node_modules/hoek": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
"deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/isemail": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz",
"integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==",
"dev": true,
"dependencies": {
"punycode": "2.x.x"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/joi": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz",
"integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==",
"deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).",
"dev": true,
"dependencies": {
"hoek": "5.x.x",
"isemail": "3.x.x",
"topo": "3.x.x"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/joi/node_modules/hoek": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz",
"integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==",
"deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).",
"dev": true,
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/merge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==",
"dev": true
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true
},
"node_modules/nan": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
"integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
"dev": true
},
"node_modules/node-expat": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.4.0.tgz",
"integrity": "sha512-X8Y/Zk/izfNgfayeOeUGqze7KlaOwVJ9SDTjHUMKd0hu0aFTRpLlLCBwmx79cTPiQWD24I1YOafF+U+rTvEMfQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.5.0",
"nan": "^2.13.2"
}
},
"node_modules/pngjs": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz",
"integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==",
"dev": true,
"engines": {
"node": ">=12.13.0"
}
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/string-builder": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/string-builder/-/string-builder-0.1.8.tgz",
"integrity": "sha512-0pUtikmhChLaf+uLqzYTgzTCQc4jAjaWHolxPGq3D77SgSoTqkOlv0RVF3XwDxMR9x/y1WPPwkTNalZCA9DGnQ==",
"dev": true
},
"node_modules/topo": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz",
"integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==",
"deprecated": "This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.",
"dev": true,
"dependencies": {
"hoek": "6.x.x"
}
},
"node_modules/topo/node_modules/hoek": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz",
"integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==",
"deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.",
"dev": true
},
"node_modules/watch": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz",
"integrity": "sha512-1u+Z5n9Jc1E2c7qDO8SinPoZuHj7FgbgU1olSFoyaklduDvvtX7GMMtlE6OC9FTXq4KvNAOfj6Zu4vI1e9bAKA==",
"dev": true,
"dependencies": {
"exec-sh": "^0.2.0",
"minimist": "^1.2.0"
},
"bin": {
"watch": "cli.js"
},
"engines": {
"node": ">=0.1.95"
}
},
"node_modules/xml2json": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.12.0.tgz",
"integrity": "sha512-EPJHRWJnJUYbJlzR4pBhZODwWdi2IaYGtDdteJi0JpZ4OD31IplWALuit8r73dJuM4iHZdDVKY1tLqY2UICejg==",
"dev": true,
"dependencies": {
"hoek": "^4.2.1",
"joi": "^13.1.2",
"node-expat": "^2.3.18"
},
"bin": {
"xml2json": "bin/xml2json"
}
}
},
"dependencies": {
"bindings": {
"version": "1.5.0",

View File

@ -1,126 +0,0 @@
use Util.Macs.s
use Load.Macs.s
use Locator.Macs.s
use Mem.Macs.s
use Misc.Macs.s
use Tool222.MACS.s
use Core.MACS.s
use .\Defs.s
EngineStartUp ENT
phb
phk
plb
jsr ToolStartUp ; Start up the toolbox tools we rely on
jsr _CoreStartUp
jsr SoundStartUp ; Start up any sound/music tools
plb
rtl
EngineShutDown ENT
phb
phk
plb
jsr SoundShutDown
jsr _CoreShutDown
jsr ToolShutDown
plb
rtl
ToolStartUp
_TLStartUp ; normal tool initialization
pha
_MMStartUp
_Err ; should never happen
pla
sta MasterId ; our master handle references the memory allocated to us
ora #$0100 ; set auxID = $01 (valid values $01-0f)
sta UserId ; any memory we request must use our own id
_MTStartUp
rts
MasterId ds 2
; Fatal error handler invoked by the _Err macro
PgmDeath tax
pla
inc
phx
phk
pha
bra ContDeath
PgmDeath0 pha
pea $0000
pea $0000
ContDeath ldx #$1503
jsl $E10000
; Use Tool222 (NinjaTrackerPlus) for music playback
SoundStartUp
lda #NO_MUSIC
bne :no_music
pea $00DE
pea $0000
_LoadOneTool
_Err
lda UserId
pha
_NTPStartUp
:no_music
rts
SoundShutDown
lda #NO_MUSIC
bne :no_music
_NTPShutDown
:no_music
rts
ToolShutDown
rts
put CoreImpl.s
put blitter/Template.s
put Memory.s
put Graphics.s
put Sprite.s
put blitter/Tiles.s
put Sprite2.s
put SpriteRender.s
put Render.s
put Timer.s
put Script.s
put blitter/Blitter.s
put blitter/Horz.s
put blitter/PEISlammer.s
put blitter/Tables.s
put blitter/Tiles00000.s ; normal tiles
put blitter/Tiles00001.s ; dynamic tiles
put blitter/Tiles00010.s ; normal masked tiles
put blitter/Tiles00011.s ; dynamic masked tiles
put blitter/Tiles10000.s ; normal tiles + sprites
put blitter/Tiles10001.s ; dynamic tiles + sprites
put blitter/Tiles10010.s ; normal masked tiles + sprites
put blitter/Tiles10011.s ; dynamic masked tiles + sprites
put blitter/Tiles11000.s ; normal high priority tiles + sprites
put blitter/Tiles11001.s ; dynamic high priority tiles + sprites
put blitter/Tiles11010.s ; normal high priority masked tiles + sprites
put blitter/Tiles11011.s ; dynamic high priority masked tiles + sprites
put blitter/TilesBG1.s
put blitter/Vert.s
put blitter/BG0.s
put blitter/BG1.s
put blitter/SCB.s
put TileMap.s

View File

@ -225,6 +225,8 @@ EngineReset
stz BG1TileMapPtr
stz BG1TileMapPtr+2
stz CompileBankTop
stz SCBArrayPtr
stz SCBArrayPtr+2
@ -238,6 +240,7 @@ EngineReset
sta tmp15
stz tmp14
; Rebuild all of the bank blitters
:loop
ldx #BlitBuff
lda #^BlitBuff
@ -252,6 +255,15 @@ EngineReset
dec tmp15
bne :loop
; Set the scanline tables to reasonable default values
; ldx #{416*2}-2
; lda #0
;:sxm_loop
; sta StartXMod164Arr,x
; dex
; dex
; bpl :sxm_loop
rts

View File

@ -40,12 +40,12 @@ EngineMode equ 20 ; Defined the mode/capabilities that ar
; bit 2: 0 = No static buffer, 1 = Allocation Bank 00 space for a static screen buffer
DirtyBits equ 22 ; Identify values that have changed between frames
BG1DataBank equ 24 ; Data bank that holds BG1 layer data
BG1AltBank equ 26 ; Alternate BG1 bank
CompileBank0 equ 24 ; Always zero to allow [CompileBank0],y addressing
CompileBank equ 26 ; Data bank that holds compiled sprite code
BlitterDP equ 28 ; Direct page address the holder blitter data
BlitterDP equ 28 ; Direct page address that holds blitter data
OldStartX equ 30
OldStartX equ 30 ; Used to track deltas between frames
OldStartY equ 32
LastPatchOffset equ 34 ; Offset into code field that was patched with BRA instructions
@ -61,7 +61,7 @@ BG1StartYMod208 equ 46
OldBG1StartX equ 48
OldBG1StartY equ 50
BG1OffsetIndex equ 52
BG1OffsetIndex equ 52 ; Utility index for scanline effect in BG1
BG0TileOriginX equ 54 ; Coordinate in the tile map that corresponds to the top-left corner
BG0TileOriginY equ 56
@ -73,22 +73,22 @@ BG1TileOriginY equ 64
OldBG1TileOriginX equ 66
OldBG1TileOriginY equ 68
TileMapWidth equ 70
TileMapWidth equ 70 ; Pointer to memory holding the tile map for the primary background
TileMapHeight equ 72
TileMapPtr equ 74
FringeMapPtr equ 78
BG1TileMapWidth equ 82
BG1TileMapHeight equ 84
BG1TileMapPtr equ 86
BG1TileMapPtr equ 86 ; Pointer to memory holding the tile map for the secondary background
SCBArrayPtr equ 90 ; Used for palette binding
SpriteBanks equ 94 ; Bank bytes for the sprite data and sprite mask
LastRender equ 96 ; Record which render function was last executed
; gap
CompileBankTop equ 98 ; First free byte i nthe compile bank. Grows upward in memeory.
SpriteMap equ 100 ; Bitmap of open sprite slots.
ActiveSpriteCount equ 102
BankLoad equ 104
BG1DataBank equ 104 ; Data bank that holds BG1 layer data
TileStoreBankAndBank01 equ 106
TileStoreBankAndTileDataBank equ 108
TileStoreBankDoubled equ 110
@ -102,7 +102,9 @@ RenderFlags equ 124 ; Flags passed to the Render() function
BG1Scaling equ 126
activeSpriteList equ 128 ; 32 bytes for the active sprite list (can persist across frames)
; tiletmp equ 178 ; 16 bytes of temp storage for the tile renderers
; Free space from 160 to 192
blttmp equ 192 ; 32 bytes of local cache/scratch space for blitter
tmp8 equ 224 ; another 16 bytes of temporary space to be used as scratch
@ -166,12 +168,22 @@ SPRITE_VBUFF_PTR equ 224 ; 32 bytes of adjusted pointers to VBuf
ENGINE_MODE_TWO_LAYER equ $0001
ENGINE_MODE_DYN_TILES equ $0002
ENGINE_MODE_BNK0_BUFF equ $0004
ENGINE_MODE_USER_TOOL equ $8000 ; Communicate if GTE is loaded as a system tool, or a user tool
; Render flags
RENDER_ALT_BG1 equ $0001
RENDER_BG1_HORZ_OFFSET equ $0002
RENDER_BG1_VERT_OFFSET equ $0004
RENDER_BG1_ROTATION equ $0008
RENDER_PER_SCANLINE equ $0010
RENDER_WITH_SHADOWING equ $0020
RENDER_SPRITES_SORTED equ $0040 ; Draw the sprites in y-sorted order. Otherwise, use the index.
; Overlay flags
OVERLAY_MASKED equ $0000 ; Overlay has a mask, so the background must be draw first
OVERLAY_SOLID equ $8000 ; Overlay covers the scan line and is fully opaque
OVERLAY_ABOVE equ $0000 ; Overlay is drawn above scanline sprites
OVERLAY_BELOW equ $4000 ; Overlay is drawn below scanline sprites
; DirtyBits definitions
DIRTY_BIT_BG0_X equ $0001
@ -182,6 +194,14 @@ DIRTY_BIT_BG0_REFRESH equ $0010
DIRTY_BIT_BG1_REFRESH equ $0020
DIRTY_BIT_SPRITE_ARRAY equ $0040
; GetAddress table IDs
scanlineHorzOffset equ $0001 ; Table of 416 words, a double-array of scanline offset values. Values must be in range [0, 163]
scanlineHorzOffset2 equ $0002 ; Table of 416 words, a double-array of scanline offset values. Values must be in range [0, 163]
; CopyPicToBG1 flags
COPY_PIC_NORMAL equ $0000 ; Copy into BG1 buffer in "normal mode" treating the buffer as a 164x208 pixmap with stride of 256
COPY_PIC_SCANLINE equ $0001 ; Copy in a way to support BG1 + RENDER_PER_SCANLINE. Pixmap is double-width, 327x200 with stride of 327
; Script definition
YIELD equ $8000
JUMP equ $4000
@ -197,25 +217,27 @@ PAD_BUTTON_A equ $0200
PAD_KEY_DOWN equ $0400
; Tile constants
; TILE_RESERVED_BIT equ $8000
TILE_PRIORITY_BIT equ $4000 ; Put tile on top of sprite
TILE_DAMAGED_BIT equ $8000 ; Mark a tile as damaged (internal only)
TILE_PRIORITY_BIT equ $4000 ; Put tile on top of sprite (unimplemented)
TILE_FRINGE_BIT equ $2000 ; Unused
TILE_SOLID_BIT equ $1000 ; Hint bit used in TWO_LAYER_MODE to optimize rendering
TILE_DYN_BIT equ $0800 ; Is this a Dynamic Tile?
TILE_VFLIP_BIT equ $0400
TILE_HFLIP_BIT equ $0200
TILE_ID_MASK equ $01FF
TILE_CTRL_MASK equ $FE00
; TILE_PROC_MASK equ $F800 ; Select tile proc for rendering
TILE_CTRL_MASK equ $7E00
; TILE_PROC_MASK equ $7800 ; Select tile proc for rendering
; Sprite constants
SPRITE_HIDE equ $2000
SPRITE_OVERLAY equ $8000 ; This is an overlay record. Stored as a sprite for render ordering purposes
SPRITE_COMPILED equ $4000 ; This is a compiled sprite (SPRITE_DISP points to a routine in the compiled cache bank)
SPRITE_HIDE equ $2000 ; Do not render the sprite
SPRITE_16X16 equ $1800 ; 16 pixels wide x 16 pixels tall
SPRITE_16X8 equ $1000 ; 16 pixels wide x 8 pixels tall
SPRITE_8X16 equ $0800 ; 8 pixels wide x 16 pixels tall
SPRITE_8X8 equ $0000 ; 8 pixels wide x 8 pixels tall
SPRITE_VFLIP equ $0400
SPRITE_HFLIP equ $0200
SPRITE_VFLIP equ $0400 ; Flip the sprite vertically
SPRITE_HFLIP equ $0200 ; Flip the sprite horizontally
; Stamp storage parameters
VBUFF_STRIDE_BYTES equ {12*4} ; Each line has 4 slots of 16 pixels + 8 buffer pixels
@ -263,11 +285,26 @@ VBuffArray EXT
_stamp_step EXT
VBuffVertTableSelect EXT
VBuffHorzTableSelect EXT
Overlays EXT
; Overlays EXT
BG1YCache EXT
ScalingTables EXT
NumHandles EXT
Handles EXT
;StartXMod164Arr EXT
;LastPatchOffsetArr EXT
_SortedHead EXT
_ShadowListCount EXT
_ShadowListTop EXT
_ShadowListBottom EXT
_DirectListCount EXT
_DirectListTop EXT
_DirectListBottom EXT
StartXMod164Tbl EXT
LastOffsetTbl EXT
BG1StartXMod164Tbl EXT
; Tool error codes
NO_TIMERS_AVAILABLE equ 10

271
src/FastCopies.s Normal file
View File

@ -0,0 +1,271 @@
; Large, unrolled loops for setting values in the code field that would be used by the Horz.s
; and Vert.s code.
;
; The utility of these functions is that they do not need to do any sort of bank switching and
; can update all of the play field lines in a single call. The downside is that they take up
; significantly more space, need large auxiliary tables, and must be patched after the code
; field memory is allocated.
;
; Probably still worth it....
BlitBuff EXT
; Patch the fast copy routines with the allocated memory addresses
InitFastCopies
; Fist, patch the cttc routine
ldy #0
ldx #0
:loop1
lda BlitBuff+2,y ; Get the bank of each in the accumulatow low byte
sep #$20
]line equ 0
lup 16
stal cttc_start+{]line*7}+4,x
stal cttc_start+{{]line+208}*7}+4,x
]line equ ]line+1
--^
rep #$20
txa
clc
adc #7*16
tax
tya
clc
adc #4
tay
cpy #13*4
bcs *+5
brl :loop1
; Next, patch the two store routines
ldy #0
ldx #0
:loop2
lda BlitBuff+2,y ; Get the bank of each in the accumulatow low byte
sep #$20
]line equ 0
lup 16
stal store_start+{]line*4}+1,x
stal store_start+{{]line+208}*4}+1,x
]line equ ]line+1
--^
rep #$20
txa
clc
adc #4*16
tax
tya
clc
adc #4
tay
cpy #13*4
bcs *+5
brl :loop2
rtl
; Function to load data from an array and store in the code field. Assume that the
; bank register is already set to the bank of the srcAddr data
srcAddr equ 0
destOffset equ 2
CopyTblToCode
ldal entry_7,x ; This is the entry point
stal cttc_jump+1
txa ; Set the Y register to srcAddr - 2*start to compensate for the
eor #$FFFF ; offset in the code. This does mean that the array we are copying
sec ; cannot by near the beginning of the bank
adc srcAddr
tyx ; put the ending offset in X
tay
ldal entry_7,x
tax
lda #$0060
stal {cttc_start&$FF0000}+3,x ; patch at the next STAL instruction because the high byte is always zero
ldx destOffset ; byte offset within each line
cttc_jump jsr $0000
lda #$009F ; restore the STAL opcode
stal {cttc_start&$FF0000}+3,x
rtl
; Define the 416 addresses for each copy
entry_7
]line equ 0
lup 416
da cttc_start+{]line*7}
]line equ ]line+1
--^
; Generate the code that performs the copy.
cttc_unit mac
lda: {]1*32}+{]2*2},y
stal $000000+{]2*$1000},x
<<<
cttc_start
]bank equ 0
lup 26
cttc_unit ]bank;0
cttc_unit ]bank;1
cttc_unit ]bank;2
cttc_unit ]bank;3
cttc_unit ]bank;4
cttc_unit ]bank;5
cttc_unit ]bank;6
cttc_unit ]bank;7
cttc_unit ]bank;8
cttc_unit ]bank;9
cttc_unit ]bank;10
cttc_unit ]bank;11
cttc_unit ]bank;12
cttc_unit ]bank;13
cttc_unit ]bank;14
cttc_unit ]bank;15
]bank equ ]bank+1
--^
rts
Store8Bits
txa
asl
adc #store_start
stal s8b_jump+1
tya
asl
tax
lda #$0060
stal {store_start&$FF0000},x
ldx destOffset ; byte offset within each line
lda srcAddr
sep #$20
s8b_jump jsr $0000
lda #$9F ; restore the STAL opcode
stal {store_start&$FF0000},x
rep #$20
rtl
Store16Bits
txa
asl
adc #store_start
stal s16b_jump+1
tya
asl
tax
lda #$0060
stal {store_start&$FF0000},x
ldx destOffset ; byte offset within each line
lda srcAddr
s16b_jump jsr $0000
lda #$009F ; restore the STAL opcode
stal {store_start&$FF0000},x
rtl
store_start
lup 26
stal $000000,x
stal $001000,x
stal $002000,x
stal $003000,x
stal $004000,x
stal $005000,x
stal $006000,x
stal $007000,x
stal $008000,x
stal $009000,x
stal $00A000,x
stal $00B000,x
stal $00C000,x
stal $00D000,x
stal $00E000,x
stal $00F000,x
--^
rts
CodeCopy8
txa
asl
adc #store_start
stal cc8_jump+1
tya
asl
tax
lda #$0060
stal {store8_start&$FF0000},x
ldx destOffset ; byte offset within each line
lda srcAddr
cc8_jump jsr $0000
lda #$009F ; restore the STAL opcode
stal {store8_start&$FF0000},x
rtl
store8_start
lup 26
pea $0000
plb
plb
lda $0000,y
stal $000000,x
lda $0000,y
stal $001000,x
lda $0000,y
stal $002000,x
lda $0000,y
stal $003000,x
lda $0000,y
stal $004000,x
lda $0000,y
stal $005000,x
lda $0000,y
stal $006000,x
lda $0000,y
stal $007000,x
lda $0000,y
stal $008000,x
lda $0000,y
stal $009000,x
lda $0000,y
stal $00A000,x
lda $0000,y
stal $00B000,x
lda $0000,y
stal $00C000,x
lda $C000,y
stal $00D000,x
lda $E000,y
stal $00E000,x
lda $F000,y
stal $00F000,x
--^
rts

View File

@ -4,13 +4,15 @@ InitGraphics
jsr _ShadowOn
jsr _GrafOn
lda #0
jsr _ClearToColor
lda #0
jsr _SetSCBs
ldx #DefaultPalette
lda #0
jsr _SetPalette
ldx #SHR_LINE_WIDTH
ldy #SHR_SCREEN_HEIGHT
jsr _SetScreenMode
jsr _InitBG0 ; Initialize the background layer
lda EngineMode

View File

@ -11,28 +11,28 @@
; 64KB Tile Memory
ASM static\TileData.s
ASM static/TileData.s
KND #$1001 ; Type and Attributes ($10=Static,$01=Data)
ALI BANK
SNA TDATA
; 64KB Sprite Plane Data
ASM static\SprData.s
ASM static/SprData.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
ALI BANK
SNA SDATA
; 64KB Sprite Mask Data
ASM static\SprMask.s
ASM static/SprMask.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
ALI BANK
SNA SMASK
; 64KB Tile Store
ASM static\TileStore.s
ASM static/TileStore.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
ALI BANK
SNA TSTORE
@ -43,3 +43,10 @@
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
ALI BANK
SNA ROTDATA
; Additional code
ASM FastCopies.s
KND #$1001 ; Type and Attributes ($11=Static+Bank Relative,$01=Data)
ALI BANK
SNA FASTCPY

View File

@ -28,9 +28,7 @@ InitMemory lda EngineMode
_TrackHandle ; returns LONG Handle on stack
plx ; base address of the new handle
pla ; high address 00XX of the new handle (bank)
; _Deref
; stx Buff00
; sta Buff00+2
:no_bnk0_buff
PushLong #0 ; space for result
@ -41,9 +39,6 @@ InitMemory lda EngineMode
_TrackHandle ; returns LONG Handle on stack
plx ; base address of the new handle
pla ; high address 00XX of the new handle (bank)
; _Deref
; stx Buff01
; sta Buff01+2
PushLong #0 ; space for result
@ -64,16 +59,20 @@ InitMemory lda EngineMode
_Deref
stx BlitterDP
; Allocate banks of memory for BG1
; Allocate banks of memory for BG1. If the user wants to swap between multiple BG1 banks, then they need to be allocated
; outside of GTE and selected using the GTESetBG1Bank() function. Passing a zero for that function's argument will
; always set the bank to the allocated bank number. Bank 00 and Bank 01 are illegal values.
lda EngineMode
bit #ENGINE_MODE_TWO_LAYER
beq :no_bg1
jsr AllocOneBank2
sta BG1DataBank
:no_bg1
jsr AllocOneBank2
sta BG1AltBank
:no_bg1
sta CompileBank
stz CompileBank0
; Allocate the 13 banks of memory we need and store in double-length array
]step equ 0

View File

@ -8,8 +8,15 @@
; and internal data structure to properly render the play field. Then the update pipeline is
; executed.
;
; Everything is composited into the tiles in the playfield and then the screen is rendered in
; a single pass.
; There are two major rendering modes: a composited mode and a scanline mode. The composited mode
; will render all of the sprites into the playfield tiles, and then perform a single blit to update
; the entire playfield. The scanline mode utilized shadowing and blits the background scanlines
; on sprite lines first, then draws the sprites and finally exposes the updated scanlines.
;
; The composited mode has the advantages of being able to render sprites behind tile data as well
; as avoiding most overdraw. The scanline mode is able to draw sprites correctly even when scanline
; effect are used on the background and has lower overhead, which can make it faster in some cases,
; even with the additional overdraw.
;
; TODO -- actually check the dirty bits and be selective on what gets updated. For example, if
; only the Y position changes, then we should only need to set new values on the
@ -51,9 +58,10 @@ _Render
jsr _UpdateBG0TileMap ; and the tile maps. These subroutines build up a list of tiles
; jsr _UpdateBG1TileMap ; that need to be updated in the code field
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode
lda #RENDER_BG1_ROTATION
bit RenderFlags
bne :skip_bg1_x
@ -63,7 +71,7 @@ _Render
; The code fields are locked in now and ready to be rendered. See if there is an overlay or any
; other reason to render with shadowing off. Otherwise, just do things quickly.
lda Overlays
lda Overlays+OVERLAY_ID
beq :no_ovrly
jsr _ShadowOff
@ -72,8 +80,9 @@ _Render
; optimization that can be done here is that the lines can be rendered in any order
; since it is not shown on-screen yet.
ldx Overlays+2 ; Blit the full virtual buffer to the screen
ldy Overlays+4
ldx Overlays+OVERLAY_TOP ; Blit the full virtual buffer to the screen
ldy Overlays+OVERLAY_BOTTOM
iny
jsr _BltRange
; Turn shadowing back on
@ -82,14 +91,10 @@ _Render
; Now render all of the remaining lines in top-to-bottom (or bottom-to-top) order
ldx #0
ldy Overlays+2
beq :skip
jsr _BltRange
:skip
jsr _DoOverlay
ldx Overlays+4
ldx Overlays+OVERLAY_BOTTOM
inx
cpx ScreenHeight
beq :done
ldy ScreenHeight
@ -131,14 +136,14 @@ _Render
rts
_DoOverlay
lda Overlays+6
lda Overlays+OVERLAY_PROC
stal :disp+1
lda Overlays+7
lda Overlays+OVERLAY_PROC+1
stal :disp+2
lda ScreenY0 ; pass the address of the first line of the overlay
clc
adc Overlays+2
adc Overlays+OVERLAY_TOP
asl
tax
lda ScreenAddr,x
@ -147,6 +152,61 @@ _DoOverlay
:disp jsl $000000
rts
; Use the per-scanline tables to set the screen. This is really meant to be used without the built-in tilemap
; support and is more of a low-level way to control the background rendering
_RenderScanlines
lda BG1YTable ; Make sure we're in the right mode (0 = scanline mode, $1800 = normal mode)
beq :ytbl_ok
lda #1
jsr _ResetBG1YTable
:ytbl_ok
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
jsr _ApplyScanlineBG1YPos ; Set the y-register values of the blitter
; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and
; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data.
jsr _ApplyBG0XPosPre
jsr _ApplyBG1XPosPre
jsr _ApplyScanlineBG0XPos ; Patch the code field instructions with exit BRA opcode
jsr _ApplyScanlineBG1XPos
jsr _BuildShadowList ; Create the rages based on the sorted sprite y-values
jsr _ShadowOff ; Turn off shadowing and draw all the scanlines with sprites on them
jsr _DrawShadowList
jsr _DrawDirectSprites ; Draw the sprites directly to the Bank $01 graphics buffer (skipping the render-to-tile step)
jsr _ShadowOn ; Turn shadowing back on
jsr _DrawFinalPass
lda StartYMod208 ; Restore the fields back to their original state
ldx ScreenHeight
jsr _RestoreScanlineBG0Opcodes
lda StartY
sta OldStartY
lda StartX
sta OldStartX
lda BG1StartY
sta OldBG1StartY
lda BG1StartX
sta OldBG1StartX
stz DirtyBits
stz LastRender ; Mark that a full render was just performed
lda SpriteRemovedFlag ; If any sprite was removed, set the rebuild flag
beq :no_removal
lda #DIRTY_BIT_SPRITE_ARRAY
sta DirtyBits
:no_removal
rts
; Run through all of the tiles on the DirtyTile list and render them
_ApplyTiles
ldx DirtyTileCount
@ -213,3 +273,561 @@ _ApplyDirtyTiles
stz DirtyTileCount ; Reset the dirty tile count
rts
; This rendering mode turns off shadowing and draws all of the relevant background lines and then
; draws sprites on top of the background before turning shadowing on and exposing the lines to the
; screen. Even though entire lines are drawn twice, it's so efficient that it is often faster
; than using all of the logic to draw/erase tiles in the TileBuffer, even though less visible words
; are touched.
;
; This mode is also necessary if per-scanling rendering it used since sprites would not look correct
; if each line had independent offsets.
_RenderWithShadowing
sta RenderFlags
jsr _DoTimers ; Run any pending timer tasks
jsr _ApplyBG0YPos ; Set stack addresses for the virtual lines to the physical screen
jsr _ApplyBG1YPos ; Set the y-register values of the blitter
; _ApplyBG0Xpos need to be split because we have to set the offsets, then draw in any updated tiles, and
; finally patch out the code field. Right now, the BRA operand is getting overwritten by tile data.
jsr _ApplyBG0XPosPre
jsr _ApplyBG1XPosPre
jsr _UpdateBG0TileMap ; and the tile maps. These subroutines build up a list of tiles
; jsr _UpdateBG1TileMap ; that need to be updated in the code field
jsr _ApplyTiles ; This function actually draws the new tiles into the code field
jsr _ApplyBG0XPos ; Patch the code field instructions with exit BRA opcode
jsr _ApplyBG1XPos ; Update the direct page value based on the horizontal position
; At this point, everything in the background has been rendered into the code field. Next, we need
; to create priority lists of scanline ranges.
jsr _BuildShadowList ; Create the rages based on the sorted sprite y-values
jsr _ShadowOff ; Turn off shadowing and draw all the scanlines with sprites on them
jsr _DrawShadowList
jsr _DrawDirectSprites ; Draw the sprites directly to the Bank $01 graphics buffer (skipping the render-to-tile step)
jsr _ShadowOn ; Turn shadowing back on
; jsr _DrawComplementList ; Alternate drawing scanlines and PEI slam to expose the full fram
jsr _DrawFinalPass
;
; The objects that need to be reasoned about are
;
; 1. Sprites
; 2. Overlays
; a. Solid High Priority
; b. Solid Low Priority
; c. Masked High Priority
; d. Masked Low Priority
; 3. Background
;
; Notes:
;
; A High Priority overlay is rendered above the sprites
; A Low Priority overlay is rendered below the sprites
; A Solid High Priority overlay obscured everything and if the only thing drawn on the scanline
;
; The order of draw oprations is:
;
; 1. Turn off shadowing
; 2. Draw the background for scanlines with (Sprites OR a Masked Low Priority overlay) AND NOT a Solid Low Priority overlay
; 3. Draw the Solid Low Priority overlays
; 4. Draw the Sprites
; 5. Draw the Masked Low Priority overlays
; 6. Turn on shadowing
; 7. Draw, in top-to-bottom order
; a. Background lines not drawn yet
; b. PEI Slam lines with (Sprites OR a Masked Low Priority Overlay) AND NOT a High Priority overlay
; c. High Priority overlays
;
; The work of this routine is to quickly build a sorted list of scanline ranges that can the appropriate
; sub-renderer
; jsr BuildShadowSegments
;
; The trick is to create a bit-field mapping for the different actions to define
; lda Overlays
; beq :no_ovrly
;
; jsr _ShadowOff
; Shadowing is turned off. Render all of the scan lines that need a second pass. One
; optimization that can be done here is that the lines can be rendered in any order
; since it is not shown on-screen yet.
; ldx Overlays+OVERLAY_TOP ; Blit the full virtual buffer to the screen
; ldy Overlays+OVERLAY_BOTTOM
; jsr _BltRange
; Turn shadowing back on
; jsr _ShadowOn
; Now render all of the remaining lines in top-to-bottom (or bottom-to-top) order
; ldx #0
; ldy Overlays+OVERLAY_TOP
; beq :skip
; jsr _BltRange
;:skip
; jsr _DoOverlay
; ldx Overlays+OVERLAY_BOTTOM
; cpx ScreenHeight
; beq :done
; ldy ScreenHeight
; jsr _BltRange
; bra :done
;:no_ovrly
; ldx #0 ; Blit the full virtual buffer to the screen
; ldy ScreenHeight
; jsr _BltRange
;:done
; ldx #0
; ldy ScreenHeight
; jsr _BltSCB
lda StartYMod208 ; Restore the fields back to their original state
ldx ScreenHeight
jsr _RestoreBG0Opcodes
lda StartY
sta OldStartY
lda StartX
sta OldStartX
lda BG1StartY
sta OldBG1StartY
lda BG1StartX
sta OldBG1StartX
stz DirtyBits
stz LastRender ; Mark that a full render was just performed
lda SpriteRemovedFlag ; If any sprite was removed, set the rebuild flag
beq :no_removal
lda #DIRTY_BIT_SPRITE_ARRAY
sta DirtyBits
:no_removal
rts
; Look at the overlay list and the sprite list and figure out which scanline ranges need to be
; blitted in what order. We try to build all of the scan line segments lists because that
; saves the work of re-scanning the lists.
;
; The semgent list definitions are:
;
; BLIT_W_SHADOW_OF
BuildShadowSegments
; ldx _SortedHead
; bmi :no_sprite
;:loop
; lda _Sprites+CLIP_TOP,x
; lda _Sprites+SORTED_NEXT,x
; tax
; bpl :loop
;
; lda #0 ; Start at the top of the
rts
; Function to iterate through the sprite list and build a merged scanline list of sprites. Once this is
; done, we re-scan the list to build the complement for scanlines that do not need shadowing.
_BuildShadowList
ldy #0 ; This is the index into the list of shadow segments
ldx _SortedHead
bmi :empty
bra :insert
; Start of loop
:advance
iny
iny
:insert
lda _Sprites+SPRITE_CLIP_TOP,x ; Load the sprite's top line
sta _ShadowListTop,y ; Set the top entry of the list to the sprite top
lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Optimistically set the end of the segment to the bottom of this sprite
inc ; Clip values are on the scanline, so add one to make it a proper interval
:replace
sta _ShadowListBottom,y
:skip
lda _Sprites+SORTED_NEXT,x ; If there another sprite in the list?
bmi :no_more_sprites ; If not, we can finish up
tax
lda _ShadowListBottom,y ; If the bottom of the current sprite is _less than_ the top of the next
cmp _Sprites+SPRITE_CLIP_TOP,x ; sprite, then there is a gap and we create a new entry
bcc :advance
lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Get the bottom value of the next sprite.
inc
cmp _ShadowListBottom,y ; If it extends the segment then replace the value, otherwise skip
bcc :skip
bra :replace
:no_more_sprites
iny ; Set the list count to N * 2
iny
:empty
sty _ShadowListCount
rts
; Run through the shadow list and make a complementary list, e.g
; [[0, 7], [12, 19]] -> [[7, 12], [19, end]]
; [[2, 10], [20, 40]] -> [[0, 2], [10, 20], [40, end]]
_ComplementList
ldy #0
tyx
lda _ShadowListCount
beq :empty_list
lda _ShadowListTop
beq :loop
stz _DirectListTop
sta _DirectListBottom
inx
inx
:loop
lda _ShadowListBottom,y
sta _DirectListTop,x
iny ; Move to the next shadow list record
iny
cpy _ShadowListCount ; Are there any other segments to process
bcs :eol
lda _ShadowListTop,y
sta _DirectListBottom,x ; Finish the direct list entry
inx
inx
bra :loop
:eol
lda ScreenHeight
sta _DirectListBottom,x
inx ; Set the count to N * 2
inx
stx _DirectListCount
rts
:empty_list
lda #1
sta _DirectListCount
stz _DirectListTop
lda ScreenHeight
sta _DirectListBottom
rts
; Iterate through the shadow list and call _BltRange on each
_DrawShadowList
ldx #0
bra :start
:loop
phx ; Save the index
lda _ShadowListTop,x
ldy _ShadowListBottom,x
tax
jsr _BltRange
plx
inx
inx
:start
cpx _ShadowListCount
bcc :loop
rts
; Run through the list of sprites that are not IS_OFFSCREEN and not OVERLAYS and draw them directly to the graphics screen. We can use
; compiled sprites here, with limitations.
_DrawDirectSprites
lda RenderFlags
bit #RENDER_SPRITES_SORTED
bne :sorted
; Shift through the sprites
lda SpriteMap
beq :empty
sta tmp15
ldx #0
:iloop
lsr tmp15
bcc :next
jsr :render
:next inx
inx
lda tmp15
bne :iloop
rts
:sorted
ldx _SortedHead
bmi :empty
:loop
jsr :render
lda _Sprites+SORTED_NEXT,x ; If there another sprite in the list?
tax
bpl :loop
:empty
rts
:render
lda _Sprites+SPRITE_ID,x
bit #SPRITE_OVERLAY
beq *+3
rts
lda _Sprites+SPRITE_STATUS,x
bit #SPRITE_STATUS_HIDDEN
beq *+3
rts
phx
jsr _DrawStampToScreen
plx
rts
; Run through the sorted list and perform a final render the jumps between calling _PEISlam for shadowed lines,
; _BltRange for clean backgrounds and Overlays as needed.
;
; The trick here is to merge runs of shared render types.
;
; Loop invariant: X-register is the current object index, Y-register is the next object index
;
; TODO: This does not yet handle the case of a narrow overlay in the middle of a sprite. The second half of the sprite will not be exposed
; by a PEISlam.
;
; e.g. |--- Overlay ---|
; |-------------- Sprite ----------------|
;
; Output Should be |-- PEI --||--- Overlay ---||--- PEI --|
; But currently is |-- PEI --||--- Overlay ---|
_DrawFinalPass
:curr_top equ tmp0
:curr_bottom equ tmp1
:curr_type equ tmp2
ldx _SortedHead
bmi :empty
lda _Sprites+SPRITE_CLIP_TOP,x ; Load the first object's top edge
beq :loop ; If it's at the top edge of the screen, proceed. Othrewise _BltRange the top range
ldx #0
tay
jsr _BltRange
ldx _SortedHead ; Reload the register
:loop
lda _Sprites+SPRITE_ID,x ; Save the type of the current segment. Do this first because it can be skipped
and #SPRITE_OVERLAY ; when merging ranges of the same type
sta :curr_type
lda _Sprites+SPRITE_CLIP_TOP,x
sta :curr_top
lda _Sprites+SPRITE_CLIP_BOTTOM,x ; Optimistically set the end of the segment to the bottom of this object
inc ; Clip values are on the scanline, so add one to make it a proper interval
:update
sta :curr_bottom
:skip
ldy _Sprites+SORTED_NEXT,x ; If there another object in the list?
bmi :no_more ; If not, we can finish up
lda :curr_bottom ; If the bottom of the current object is _less than_ the top of the next
cmp _Sprites+SPRITE_CLIP_TOP,y ; sprite, then there is a gap and we can draw the current object and a
bcc :advance ; _BltRange up to the next one
; Here, we've established that there is another object segment that starts at or within the bounds of the current
; object. If they are of the same type, then we can merge them and look at the next object in the list; treating
; the merges range as a larger, single object range.
;
; If they are different, then clip the current object range to the top of the next one, render the current object
; range and then take the new object as the current one.
;
; If the first object extends past the second, we are going to miss the remainder of that object. We really need a
; stack to put it on so that it can eventually be processed later.
lda _Sprites+SPRITE_ID,y
and #SPRITE_OVERLAY
cmp :curr_type
bne :no_merge
tyx ; Move the next index into the current
lda _Sprites+SPRITE_CLIP_BOTTOM,y ; Get the bottom value of the next sprite.
inc
cmp :curr_bottom ; If it extends the segment then replace the bottom value, otherwise skip. In
bcc :skip ; either case, the type and top value remain the same
bra :update
; This is a simpler version of the 'advance' below. In this case there are overlapping ranges, so we just need to draw a
; clipped version of the top range and then restart the loop with the next range.
:no_merge
lda _Sprites+SPRITE_CLIP_TOP,y ; Get the top of the next segment
sta :curr_bottom ; Use it as the bottom of the current segment
phy ; Save the next index...
jsr :PEIOrOverlay ; Draw the current segment type
plx ; ...and restore as the current
bra :loop ; Start again
:advance
phy
jsr :PEIOrOverlay ; Draw the appropriate filler
lda 1,s
tax
ldy _Sprites+SPRITE_CLIP_TOP,x ; Draw the background in between
ldx :curr_bottom
; brk $34
jsr _BltRange
plx
bra :loop
; List is empty, so just do one big _BltRange with a tail call
:empty
ldx #0
:no_more2
ldy ScreenHeight
jmp _BltRange
; Found the end of the list. Draw current object and then blit the rest of the screen
:no_more
jsr :PEIOrOverlay
ldx :curr_bottom
cpx ScreenHeight
bcc :no_more2
rts
; Help to select between calling an Overlay or PEISlam routine
:PEIOrOverlay
lda :curr_type
bne :overlay
ldx :curr_top
ldy :curr_bottom
jmp _PEISlam
:overlay
lda _Sprites+OVERLAY_PROC,x
stal :disp+1
lda _Sprites+OVERLAY_PROC+1,x
stal :disp+2
lda ScreenY0 ; pass the address of the first line of the overlay
clc
adc _Sprites+OVERLAY_TOP,x
asl
tax
lda ScreenAddr,x
clc
adc ScreenX0
ldx :curr_top
ldy :curr_bottom
; brk $33
:disp jsl $000000
rts
_DrawComplementList
ldx #0
lda _DirectListCount ; Skip empty lists
beq :out
lda _DirectListTop ; If the first segment starts at 0, begin with _BltRange
beq :blt_range
lda #0
bra :pei_first
:blt_range
phx
lda _DirectListTop,x
ldy _DirectListBottom,x
tax
jsr _BltRange
plx
lda _DirectListBottom,x ; Grab a copy of the bottom of the blit range
inx
inx ; Advance to the next entry
cpx _DirectListCount
bcs :last ; Done, so check if there is any remaining part of the screen to slam
:pei_first
phx
ldy _DirectListTop,x
tax
jsr _PEISlam
plx
bra :blt_range
:last
cmp ScreenHeight ; If the bottom on the last segment didn't come to the bottom of the
bcs :out ; screen, then expose that range
tax
ldy ScreenHeight
jsr _PEISlam
:out
rts
; Helper to set a palette index on a range of SCBs to help show which actions are applied to which lines
DebugSCBs
phx
phy
sep #$30 ; short m/x
pha ; save the SCB value
phx
tya
sec
sbc 1,s
tay ; number of scanlines
pla
clc
adc ScreenY0
tax ; physical line index
pla
:loop
stal SHR_SCB,x
inx
dey
bne :loop
rep #$30
ply
plx
rts

View File

@ -150,7 +150,7 @@ ROW_BYTES equ 384 ; VBUFF_TILE_ROW_BYTES
; a. If it is not marked in the DirtyTile list
; * Clear its bit from the TileStore's TS_SPRITE_FLAG
; * Add the tile to the DirtyTile list
;t
;
; 2. If a sprite is marked as SPRITE_STATUS_REMOVED, then
; A. Clear its bit from the SpriteBits bitmap
; B. For each tile the sprite overlaps with:
@ -223,10 +223,11 @@ _DoPhase1
trb SpriteMap
lda #SPRITE_STATUS_EMPTY ; Mark as empty so no error if we try to Add a sprite here again
sta _Sprites+SPRITE_STATUS,y
jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done
tyx
jsr _DeleteSprite ; Remove sprite from linked list
txy ; Restore y-register
:hidden
jmp _ClearSpriteFromTileStore
jmp _ClearSpriteFromTileStore ; Clear the tile flags, add to the dirty tile list and done
:no_clear
@ -330,7 +331,7 @@ phase1 dw :phase1_0
; the stamp every time. So this allows users to create stamps in advance and then
; assign them to the sprites as needed.
;
; Note that the user had full freedom to create a stamp at any VBUFF address, however,
; Note that the user has full freedom to create a stamp at any VBUFF address, however,
; without leaving a buffer around each stamp, graphical corruption will occur. It is
; recommended that the defines for VBUFF_SPRITE_START, VBUFF_TILE_ROW_BYTES and
; VBUFF_TILE_COL_BYTES to calculate tile-aligned corner locations to lay out the
@ -361,8 +362,8 @@ _CreateSpriteStamp
; 01 - 8x16 (1x2 tiles)
; 10 - 16x8 (2x1 tiles)
; 11 - 16x16 (2x2 tiles)
; Bit 13 : Show/Hid sprite
; Bit 14 : Reserved. Must be zero.
; Bit 13 : Show/Hide sprite during rendering
; Bit 14 : Mark sprite as a compile sprite. SPRITE_DISP is treated as a compilation token.
; Bit 15 : Reserved. Must be zero.
; TBD: Bit 15 : Low Sprite priority. Draws behind high priority tiles.
;
@ -370,7 +371,7 @@ _CreateSpriteStamp
; the vertical tiles are taken from tileId + 32. This is why tile sheets should be saved
; with a width of 256 pixels.
;
; A = vbuffAddress
; A = Sprite ID / Flags
; Y = High Byte = x-pos, Low Byte = y-pos
; X = Sprite Slot (0 - 15)
_AddSprite
@ -383,10 +384,11 @@ _AddSprite
sta _Sprites+SPRITE_ID,x ; Keep a copy of the full descriptor
lda #SPRITE_STATUS_ADDED
lda #SPRITE_STATUS_ADDED ; Used to initialize the SPRITE_STATUS
sta _Sprites+SPRITE_STATUS,x
stz _Sprites+VBUFF_ADDR,x ; Clear the VBUFF address, just to initialize it
lda #$FFFF
sta _Sprites+VBUFF_ADDR,x ; Clear the VBUFF address, just to initialize it
phy
tya
@ -397,8 +399,6 @@ _AddSprite
and #$00FF
sta _Sprites+SPRITE_X,x ; X coordinate
jsr _PrecalcAllSpriteInfo ; Cache sprite property values (simple stuff)
; Mark the dirty bit to indicate that the active sprite list needs to be rebuilt in the next
; render call
@ -408,8 +408,292 @@ _AddSprite
lda _SpriteBits,x ; Get the bit flag for this sprite slot
tsb SpriteMap ; Mark it in the sprite map bit field
rts
jsr _PrecalcSpriteSize ; Cache sprite property values
jsr _PrecalcSpriteBounds
jsr _InsertSprite ; Insert it into the sorted list
jmp _Validate
; _SortSprite
;
; Given a sprite's index, i, update the sprite permutation array such that p[j] = i where
; the sprite is the j.th sprite ordered by the SPRITE_CLIP_TOP value. It is important to
; note that the sorted sprite order does not impact rendering order (that is determined by
; the sprite index position), but is only used to calculate region of the screen to update
; and, in the future, may be useful for isometric perspectives where sorting order *is*
; determined by y-position
;
; X = current sprite index
;
; The sorting strategy is to
;
; a) check if the current slot's y-pos is greater than the next item. If yes, then search forward
; b) check if the current slot's y-pos is less than the prev item. If yes, then search in reverse
; c) sprite is in the correct location
;
; The heuristic in play here is that, usually sprites will only move one position in the sorted order
; between frames, if at all.
_SortSprite
lda _Sprites+SPRITE_CLIP_TOP,x
ldy _Sprites+SORTED_PREV,x
bmi :chk_fwd
cmp _Sprites+SPRITE_CLIP_TOP,y
bcc :scan_bkwd ; The current node needs to move to an lower position
:chk_fwd
ldy _Sprites+SORTED_NEXT,x ; If there is nothing ahead of the current node, we're done
bmi :early_out
cmp _Sprites+SPRITE_CLIP_TOP,y ; If the current node is <= the next node, we're done
bcc :early_out
bne :scan_fwd
:early_out
rts
; Look to move the sprite into a later position
:scan_fwd
lda _Sprites+SORTED_NEXT,y ; Need to step forward; if we're at the end, then insert here
bmi :insert_end
tay
lda _Sprites+SPRITE_CLIP_TOP,y ; Check against the next node. If it's less that current, keep going
cmp _Sprites+SPRITE_CLIP_TOP,x
bcc :scan_fwd
; Put X before Y
;
; Change
; a <=> x <=> b
; c <=> y <=> d
;
; Into
; a <=> b and c <=> x <=> y <=> d
:insert_before
jsr _ReleaseNode
tya
sta _Sprites+SORTED_NEXT,x ; Link X to Y
lda _Sprites+SORTED_PREV,y
sta _Sprites+SORTED_PREV,x ; Link X to C
txa ; Link Y to X
sta _Sprites+SORTED_PREV,y
ldy _Sprites+SORTED_PREV,x ; Link C to X
sta _Sprites+SORTED_NEXT,y
rts
; Move X to the end of the list. Y point to the last element
;
; ; Change
; a <=> x <=> b
; y -> nil
;
; Into
; a <=> b and y <=> x -> nil
:insert_end
jsr _ReleaseNode
lda #$FFFF
sta _Sprites+SORTED_NEXT,x
tya
sta _Sprites+SORTED_PREV,x
txa
sta _Sprites+SORTED_NEXT,y
rts
; Look to move the sprite into an earlier position
:scan_bkwd
lda _Sprites+SORTED_PREV,y ; Need to step backward; if we're at the beginning, then insert here
bmi :insert_front
tay
lda _Sprites+SPRITE_CLIP_TOP,x ; Check against the next node. If it's less that current, keep going
cmp _Sprites+SPRITE_CLIP_TOP,y
bcc :scan_bkwd
; Put X after Y
;
; Change
; a <=> x <=> b
; c <=> y <=> d
;
; Into
; a <=> b and c <=> y <=> x <=> d
:insert_after
jsr _ReleaseNode
tya
sta _Sprites+SORTED_PREV,x ; c <=> y <-- x --- d
lda _Sprites+SORTED_NEXT,y ; c <=> y <-- x --> d
sta _Sprites+SORTED_NEXT,x
txa
ldx _Sprites+SORTED_NEXT,y
sta _Sprites+SORTED_NEXT,y ; c <=> y <=> x --> d
sta _Sprites+SORTED_PREV,x ; c <=> y <=> x <=> d
rts
; Move X to the front of the list. Y points to the first element
;
; ; Change
; a <=> x <=> b
; head -> y
;
; Into
; a <=> b and head -> x <=> y
:insert_front
jsr _ReleaseNode
stx _SortedHead
txa
sta _Sprites+SORTED_PREV,y
lda #$FFFF
sta _Sprites+SORTED_PREV,x
tya
sta _Sprites+SORTED_NEXT,x
:done
rts
; Take the node pointed at X and remove it from the doubly-linked list.
_ReleaseNode
phy
jsr _DeleteSprite
ply
rts
; Add a new sprite into the sorted double-linked list
_InsertSprite
lda _SortedHead ; If the list is empty, just insert the sprite index
bmi :empty
tay ; Check the first item
lda _Sprites+SPRITE_CLIP_TOP,x
cmp _Sprites+SPRITE_CLIP_TOP,y
bcc :insert_head
:next
lda _Sprites+SORTED_NEXT,y
bmi :insert_tail
tay
lda _Sprites+SPRITE_CLIP_TOP,x
cmp _Sprites+SPRITE_CLIP_TOP,y
bcs :next
lda _Sprites+SORTED_PREV,y
sta _Sprites+SORTED_PREV,x ; [p] <-- [c] [n]
tya
sta _Sprites+SORTED_NEXT,x ; [p] <-- [c] --> [n]
txa
ldx _Sprites+SORTED_PREV,y ; get ref to the [p]revious node
sta _Sprites+SORTED_PREV,y ; [p] <-- [c] <=> [n]
sta _Sprites+SORTED_NEXT,x ; [p] <=> [c] <=> [n]
rts
:insert_head
stx _SortedHead
lda #$FFFF
sta _Sprites+SORTED_PREV,x
tya
sta _Sprites+SORTED_NEXT,x
txa
sta _Sprites+SORTED_PREV,y
rts
:insert_tail
txa
sta _Sprites+SORTED_NEXT,y
tya
sta _Sprites+SORTED_PREV,x
lda #$FFFF
sta _Sprites+SORTED_NEXT,x
rts
:empty
sta _Sprites+SORTED_NEXT,x
sta _Sprites+SORTED_PREV,x
stx _SortedHead
rts
; Remove a sprite from the double-linked list
_DeleteSprite
ldy _Sprites+SORTED_NEXT,x
bmi :remove_tail
cpx _SortedHead
beq :remove_head
lda _Sprites+SORTED_PREV,x
sta _Sprites+SORTED_PREV,y
tay
lda _Sprites+SORTED_NEXT,x
sta _Sprites+SORTED_NEXT,y
rts
:remove_head
sty _SortedHead
lda #$FFFF
sta _Sprites+SORTED_PREV,y
rts
:remove_tail
ldy _Sprites+SORTED_PREV,x
bmi :make_empty
lda #$FFFF
sta _Sprites+SORTED_NEXT,y
rts
:make_empty
lda #$FFFF
sta _SortedHead
rts
; Validate the integrity of the linked list
_Validate
:prev equ tmp0
:curr equ tmp1
ldy #$FFFF
ldx _SortedHead
bmi :done
:loop
sty :prev
stx :curr
lda _Sprites+SORTED_PREV,x
cmp :prev
beq *+4
brk $08
cpy #$FFFF
beq :skip
lda _Sprites+SORTED_NEXT,y
cmp :curr
beq *+4
brk $06
lda _Sprites+SPRITE_CLIP_TOP,x
cmp _Sprites+SPRITE_CLIP_TOP,y
bcs *+4
brk $0A
:skip
txy
lda _Sprites+SORTED_NEXT,x
tax
bpl :loop
:done
rts
; Macro to make the unrolled loop more concise
;
; 1. Load the tile store address from a fixed offset
@ -643,25 +927,131 @@ _CacheSpriteBanks
rts
; Precalculate some cached values for a sprite. These are *only* to make other part of code,
; Precalculate some cached values for a sprite. These are *only* to make other parts of code,
; specifically the draw/erase routines more efficient.
;
; There are variations of this routine based on whether we are adding a new sprite, updating
; it's tile information, or changing its position.
;
; X = sprite index
_PrecalcAllSpriteInfo
lda _Sprites+SPRITE_ID,x
; and #$3E00
_PrecalcSpriteVBuff
lda _Sprites+SPRITE_ID,x ; Compiled sprites use the SPRITE_DISP as a fixed address to compiled code
bit #SPRITE_COMPILED
bne :compiled
xba
and #$0006
tay
lda _Sprites+VBUFF_ADDR,x
clc
adc _stamp_step,y
sta _Sprites+SPRITE_DISP,x
sta _Sprites+SPRITE_DISP,x ; Interpreted as an address in the VBUFF bank
rts
:compiled
xba
and #$0006 ; Pick the address from the table of 4 values. Can use this value directly
clc ; as an index
adc _Sprites+VBUFF_ADDR,x
tay
lda [CompileBank0],y
sta _Sprites+SPRITE_DISP,x ; Interpreted as an address in the CompileBank
rts
; Compile the four stamps and keep a reference to the addresses. We take the current CompileBankTop address and allocate 8 bytes
; of memory. Then compile each stamp and save the compilation address in the header area. Finally, the DISP_ADDR is set
; to that value and the SPRITE_COMPILED bit is set in the SPRITE_ID word.
;
; A = sprite Id
; X = vbuff base address
_CompileStampSet
:height equ tmp8
:width equ tmp9
:base equ tmp10
:output equ tmp11
:addrs equ tmp12 ; 4 words (tmp12, tmp13, tmp14 and tmp15)
; Save the base address
stx :base
; Initialize the height and width based on the sprite flags
ldy #8
sty :height
ldx #4
stx :width
bit #$1000 ; wide flag
beq :skinny
ldx #8
stx :width
:skinny
bit #$0800 ; tall flag
beq :short
ldy #16
sty :height
:short
lda CompileBankTop
sta :output ; Save the current address as the return value
clc
adc #8
sta CompileBankTop ; Allocate space for the 4 addresses return by _CompileStamp
; ldy :height ; X and Y are already set for the first call
; ldx :width
lda :base
jsr _CompileStamp ; Compile into the bank
sta :addrs ; Save the address temporarily
ldy :height
ldx :width
clc
lda :base
adc _stamp_step+2
jsr _CompileStamp
sta :addrs+2
ldy :height
ldx :width
clc
lda :base
adc _stamp_step+4
jsr _CompileStamp
sta :addrs+4
ldy :height
ldx :width
clc
lda :base
adc _stamp_step+6
jsr _CompileStamp
sta :addrs+6
; Now the sprite stamps are all compiled. Set the bank to the compilation bank and fill in the header
phb
ldy :output
pei CompileBank
plb
lda :addrs
sta: 0,y
lda :addrs+2
sta: 2,y
lda :addrs+4
sta: 4,y
lda :addrs+6
sta: 6,y
plb
plb
tya ; Put the output value into the accumulator
clc ; No error
rts
_PrecalcSpriteSize
; Set the sprite's width and height
lda #4
sta _Sprites+SPRITE_WIDTH,x
@ -681,10 +1071,11 @@ _PrecalcAllSpriteInfo
lda #16
sta _Sprites+SPRITE_HEIGHT,x
:height_8
rts
; Clip the sprite's bounding box to the play field size and also set a flag if the sprite
; is fully off-screen or not
_PrecalcSpriteBounds
lda _Sprites+SPRITE_X,x
bpl :pos_x
lda #0
@ -760,14 +1151,14 @@ _RemoveSprite
ora #SPRITE_STATUS_REMOVED
sta _Sprites+SPRITE_STATUS,x
rts
rts ; The _DeleteSprite call is made in _DoPhase1 during the next render
; Update the sprite's flags. We do not allow the size of a sprite to be changed. That requires
; the sprite to be removed and re-added.
;
; A = Sprite slot
; X = New Sprite Flags
; Y = New Sprite Stamp Address
; Y = New Sprite Stamp Address | New Compiled Sprite Token
_UpdateSprite
cmp #MAX_SPRITES
bcc :ok
@ -813,7 +1204,7 @@ _UpdateSprite
ora #SPRITE_STATUS_UPDATED
sta _Sprites+SPRITE_STATUS,x
jmp _PrecalcAllSpriteInfo ; Cache stuff and return
jmp _PrecalcSpriteVBuff ; Cache stuff and return
; Move a sprite to a new location. If the tile ID of the sprite needs to be changed, then
; a full remove/add cycle needs to happen
@ -849,4 +1240,6 @@ _MoveSprite
ora #SPRITE_STATUS_MOVED
sta _Sprites+SPRITE_STATUS,x
jmp _PrecalcAllSpriteInfo ; Can be specialized to only update (x,y) values
jsr _PrecalcSpriteBounds ; Can be specialized to only update (x,y) values
jsr _SortSprite ; Update the sprite's sorted position
jmp _Validate

View File

@ -1,3 +1,278 @@
; Compile a stamp into a compilation cache
;
; A = vbuff address
; X = width (in bytes)
; Y = height (in scanlines)
_CompileStamp
:lines equ tmp0
:sprwidth equ tmp1
:cntwidth equ tmp2
:baseAddr equ tmp3
:destAddr equ tmp4
:vbuffAddr equ tmp5
:rtnval equ tmp6
LDA_IMM_OPCODE equ $A9
LDA_ABS_X_OPCODE equ $BD
AND_IMM_OPCODE equ $29
ORA_IMM_OPCODE equ $09
STA_ABS_X_OPCODE equ $9D
STZ_ABS_X_OPCODE equ $9E
RTL_OPCODE equ $6B
sta :vbuffAddr
sty :lines
txa
lsr
sta :sprwidth
; Get ready to build the sprite
ldy CompileBankTop ; First free byte in the compilation bank
sty :rtnval ; Save it as the return value
phb
pei CompileBank
plb ; Set the bank to the compilation cache
stz :baseAddr
stz :destAddr
:oloop
lda :sprwidth
sta :cntwidth
ldx :vbuffAddr
:iloop
ldal spritemask,x
beq :no_mask ; If Mask == $0000, then it's a solid word
cmp #$FFFF
beq :next ; If Mask == $FFFF, then it's transparent
; Mask with the screen data
lda #LDA_ABS_X_OPCODE
sta: 0,y
lda :destAddr
sta: 1,y
lda #AND_IMM_OPCODE
sta: 3,y
ldal spritemask,x
sta: 4,y
lda #ORA_IMM_OPCODE
sta: 6,y
ldal spritedata,x
sta: 7,y
lda #STA_ABS_X_OPCODE
sta: 9,y
lda :destAddr
sta: 10,y
tya
clc
adc #12
tay
bra :next
; Just store the data
:no_mask lda #LDA_IMM_OPCODE
sta: 0,y
ldal spritedata,x
beq :zero
sta: 1,y
lda #STA_ABS_X_OPCODE
sta: 3,y
lda :destAddr
sta: 4,y
tya
clc
adc #6
tay
bra :next
:zero lda #STZ_ABS_X_OPCODE
sta: 0,y
lda :destAddr
sta: 1,y
iny
iny
iny
:next
inx
inx
inc :destAddr ; Move to the next word
inc :destAddr
dec :cntwidth
bne :iloop
lda :vbuffAddr
clc
adc #SPRITE_PLANE_SPAN
sta :vbuffAddr
lda :baseAddr ; Move to the next line
clc
adc #160
sta :baseAddr
sta :destAddr
dec :lines
beq :out
brl :oloop
:out
lda #RTL_OPCODE ; Finish up the subroutine
sta: 0,y
iny
sty CompileBankTop
plb
plb
lda :rtnval ; Address in the compile memory
rts
; Draw a sprite directly to the graphics screen. If sprite is clipped at all, do not draw.
;
; X = sprite record index
_DSTSOut
rts
_DrawStampToScreen
lda _Sprites+IS_OFF_SCREEN,x ; If the sprite is off-screen, don't draw it
bne _DSTSOut
lda _Sprites+SPRITE_CLIP_WIDTH,x ; If the sprite is clipped to the playfield, don't draw it
cmp _Sprites+SPRITE_WIDTH,x
bne _DSTSOut
lda _Sprites+SPRITE_CLIP_HEIGHT,x
cmp _Sprites+SPRITE_HEIGHT,x
bne _DSTSOut
clc
lda _Sprites+SPRITE_Y,x
adc ScreenY0
asl
asl
asl
asl
asl
sta tmp0
asl
asl
clc
adc tmp0
clc
adc #$2000
clc
adc ScreenX0
adc _Sprites+SPRITE_X,x ; Move to the horizontal address
tay ; This is the on-screen address
lda _Sprites+SPRITE_ID,x ; If this is a compiled sprite, call the routine in the compilation bank
bit #SPRITE_COMPILED
beq *+5
brl :compiled
lda _Sprites+SPRITE_HEIGHT,x
sta tmp0
; Sprite is either 8 or 16 pixels wide, so select the entry point
lda _Sprites+SPRITE_WIDTH,x
cmp #4
beq :skinny
lda _Sprites+SPRITE_DISP,x ; This is the VBUFF address with the correct sprite frame
tax
phb
pea $0101
plb
plb
bra :entry16
:loop16
clc
txa
adc #SPRITE_PLANE_SPAN
tax
tya
adc #SHR_LINE_WIDTH
tay
:entry16
lda: 6,y
andl spritemask+6,x
oral spritedata+6,x
sta: 6,y
lda: 4,y
andl spritemask+4,x
oral spritedata+4,x
sta: 4,y
lda: 2,y
andl spritemask+2,x
oral spritedata+2,x
sta: 2,y
lda: 0,y
andl spritemask+0,x
oral spritedata+0,x
sta: 0,y
dec tmp0
bne :loop16
plb
rts
:skinny
lda _Sprites+SPRITE_DISP,x ; This is the VBUFF address with the correct sprite frame
tax
phb
pea $0101
plb
plb
bra :entry8
:loop8
clc
txa
adc #SPRITE_PLANE_SPAN
tax
tya
adc #SHR_LINE_WIDTH
tay
:entry8
lda: 2,y
andl spritemask+2,x
oral spritedata+2,x
sta: 2,y
lda: 0,y
andl spritemask+0,x
oral spritedata+0,x
sta: 0,y
dec tmp0
bne :loop8
plb
rts
:compiled
lda CompileBank-1 ; Load the bank into the high byte
stal :patch+2 ; Put it into the 3rd address bytes (2nd byte is garbage)
lda _Sprites+SPRITE_DISP,x ; Address in the compile bank
stal :patch+1 ; Set 1st and 2nd address bytes
tyx ; Put on-screen address in X-register
phb ; Compiled sprites assume bank register is $01
pea $0101
plb
plb
:patch jsl $000000 ; Dispatch
plb
rts
; Alternate entry point that takes arguments in registers instead of using a _Sprite
; record
;

View File

@ -1,226 +0,0 @@
; Old code the was in Version 1, but is not needed. May be adapted for Verions 2.
; Y = _Sprites array offset
_EraseSpriteY
lda _Sprites+OLD_VBUFF_ADDR,y
beq :noerase
ldx _Sprites+SPRITE_DISP,y ; get the dispatch index for this sprite (32 values)
jmp (:do_erase,x)
:noerase rts
:do_erase dw _EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8
dw _EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16
dw _EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8
dw _EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16
dw _EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8,_EraseTileSprite8x8
dw _EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16,_EraseTileSprite8x16
dw _EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8,_EraseTileSprite16x8
dw _EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16,_EraseTileSprite16x16
; A = bank address
_EraseTileSprite8x8
tax
phb ; Save the bank to switch to the sprite plane
pei SpriteBanks
plb ; pop the data bank (low byte)
]line equ 0
lup 8
stz: {]line*SPRITE_PLANE_SPAN}+0,x
stz: {]line*SPRITE_PLANE_SPAN}+2,x
]line equ ]line+1
--^
plb ; pop the mask bank (high byte)
lda #$FFFF
]line equ 0
lup 8
sta: {]line*SPRITE_PLANE_SPAN}+0,x
sta: {]line*SPRITE_PLANE_SPAN}+2,x
]line equ ]line+1
--^
plb
rts
_EraseTileSprite8x16
tax
phb ; Save the bank to switch to the sprite plane
pei SpriteBanks
plb ; pop the data bank (low byte)
]line equ 0
lup 16
stz: {]line*SPRITE_PLANE_SPAN}+0,x
stz: {]line*SPRITE_PLANE_SPAN}+2,x
]line equ ]line+1
--^
plb ; pop the mask bank (high byte)
lda #$FFFF
]line equ 0
lup 16
sta: {]line*SPRITE_PLANE_SPAN}+0,x
sta: {]line*SPRITE_PLANE_SPAN}+2,x
]line equ ]line+1
--^
plb
rts
_EraseTileSprite16x8
tax
phb ; Save the bank to switch to the sprite plane
pei SpriteBanks
plb ; pop the data bank (low byte)
]line equ 0
lup 8
stz: {]line*SPRITE_PLANE_SPAN}+0,x
stz: {]line*SPRITE_PLANE_SPAN}+2,x
stz: {]line*SPRITE_PLANE_SPAN}+4,x
stz: {]line*SPRITE_PLANE_SPAN}+6,x
]line equ ]line+1
--^
plb ; pop the mask bank (high byte)
lda #$FFFF
]line equ 0
lup 8
sta: {]line*SPRITE_PLANE_SPAN}+0,x
sta: {]line*SPRITE_PLANE_SPAN}+2,x
sta: {]line*SPRITE_PLANE_SPAN}+4,x
sta: {]line*SPRITE_PLANE_SPAN}+6,x
]line equ ]line+1
--^
plb
rts
_EraseTileSprite16x16
tax
phb ; Save the bank to switch to the sprite plane
pei SpriteBanks
plb ; pop the data bank (low byte)
]line equ 0
lup 16
stz: {]line*SPRITE_PLANE_SPAN}+0,x
stz: {]line*SPRITE_PLANE_SPAN}+2,x
stz: {]line*SPRITE_PLANE_SPAN}+4,x
stz: {]line*SPRITE_PLANE_SPAN}+6,x
]line equ ]line+1
--^
plb ; pop the mask bank (high byte)
lda #$FFFF
]line equ 0
lup 16
sta: {]line*SPRITE_PLANE_SPAN}+0,x
sta: {]line*SPRITE_PLANE_SPAN}+2,x
sta: {]line*SPRITE_PLANE_SPAN}+4,x
sta: {]line*SPRITE_PLANE_SPAN}+6,x
]line equ ]line+1
--^
plb
rts
; First, if there is only one sprite, then we can skip any overhead and do a single lda/and/ora/sta to put the
; sprite data on the screen.
;
; Second, if there are 4 or less, then we "stack" the sprite data using an unrolled loop that allows each
; sprite to just be a single and/ora pair and the final result is not written to any intermediate memory buffer.
;
; Third, if there are 5 or more sprites, then we assume that the sprites are "dense" and that there will be a
; non-trivial amount of overdraw. In this case we do a series of optimized copies of the sprite data *and*
; masks into a direct page buffer in *reverse order*. Once a mask value becomes zero, then nothing else can
; show through and that value can be skipped. Once all of the mask values are zero, then the render is terminated
; and the data buffer copied to the final destination.
;
; Note that these rendering algorithms impose a priority ordering on the sprites where lower sprite IDs are drawn
; underneath higher sprite IDs.
RenderActiveSpriteTiles
cmp #0 ; Is there only one active sprite? If so optimise
bne :many
ldx vbuff ; load the address to the (adjusted) sprite tile
lda TileStore+TS_SCREEN_ADDR,y
tay
lda tiledata+0,y
andl spritemask,x
oral spritedata,x
sta 00,s
lda tiledata+2,y
andl spritemask+2,x
oral spritedata+2,x
sta 02,s
...
tsc
adc #320
tcs
...
lda tiledata+{line*4},y
andl spritemask+{line*SPAN},x
oral spritedata+{line*SPAN},x
sta 160,s
lda tiledata+{line*4}+2,y
andl spritemask+{line*SPAN}+2,x
oral spritedata+{line*SPAN}+2,x
sta 162,s
rts
:many
lda TileStore+TS_SCREEN_ADDR,y
tcs
lda TileStore+TS_TILE_ADDR,y
tay
ldx count
jmp (:arr,x)
lda tiledata+0,y
ldx vbuff
andl spritemask,x
oral spritedata,x
ldx vbuff+2
andl spritemask,x
oral spritedata,x
ldx vbuff+4
andl spritemask,x
oral spritedata,x
...
sta 00,s
ldx count
jmp (:arr,x)
lda tiledata+0,y
ldx vbuff
andl spritemask,x
oral spritedata,x
ldx vbuff+2
andl spritemask,x
oral spritedata,x
ldx vbuff+4
andl spritemask,x
oral spritedata,x
...
sta 02,s
sta 160,s
sta 162,s
tsc
adc #320

View File

@ -1,20 +1,29 @@
; Basic tile functions
; Copy tileset data from a pointer in memory to the tiledata back
; X = high word
; A = low word
;
; tmp0 = Pointer to tile data
; X = first tile
; Y = last tile
;
; To copy in three tiles starting at tile 5, for example, X = 5 and Y = 9
_LoadTileSet
sta tmp0
stx tmp1
txa
_Mul128 ; Jump to the target location
tax
tya
_Mul128
sta tmp2 ; This is the terminating byte
ldy #0
tyx
:loop lda [tmp0],y
stal tiledata,x
dex
dex
dey
dey
bne :loop
inx
inx
iny
iny
cpx tmp2
bne :loop ; Use BNE so when Y=512 => $0000, we wait for wrap-around
rts
@ -259,7 +268,7 @@ _SetNormalTileProcs
brl :pickDynProc
:pickTwoLyrProc ldy #TwoLyrProcs
pla ; pull of the proc index
pla ; pull off the proc index
jmp _SetTileProcs
; Specialized check for when the engine is in "Fast" mode. If is a simple decision tree based on whether
@ -436,16 +445,16 @@ _SetTileProcs
; TileProcTables
;
; Tables of tuples used to populate the K_TS_* dispatch arrays for different combinations. This is
; easier to maintain than a bunch of conditional code. Each etry hold three addresses.
; easier to maintain than a bunch of conditional code. Each entry holds three addresses.
;
; First address: Draw a tile directly into the code buffer (no sprites)
; Second address: Draw a tile merged with sprite data from the direct page
; Third address: Specialize routine to draw a tile merged with one sprite
;
; There are unique tuples of routines for all of the different combinations of tile properties
; and engine modes. This is an extesive number of combinations, but it simplified the development
; and maintainence of the rendering subroutines. Also, the difference subroutines can be written
; in any way and can make use of their on subroutines to reduce code size.
; and engine modes. This is an extensive number of combinations, but it simplifies the development
; and maintainence of the rendering subroutines. Also, the different subroutines can be written
; in any way and can make use of their own subroutines to reduce code size.
;
; Properties:
;
@ -497,6 +506,12 @@ DynUnder dw CopyDynamicTile,DynamicUnder,OneSpriteDynamicUnder
; the TILE_SOLID_BIT hint bit can be set to indicate that a tile
; has no transparency. This allows one of the faster routines
; to be selected from the other Proc tables
;
; FUTURE: An optimization that can be done is to have the snippets
; code layout fixed based on the EngineFlags and then the Two Layer
; routines should only need to update the DATA and MASK operands in
; the snippet at a fixed location rather than rebuild the ~20 bytes
; of data.
TwoLyrProcs
TwoLyrOverZA dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr
TwoLyrOverZV dw Tile0TwoLyr,SpriteOver0TwoLyr,OneSpriteOver0TwoLyr

View File

@ -96,6 +96,11 @@ _CallTable
adrl _TSClearBG1Buffer-1
adrl _TSSetBG1Scale-1
adrl _TSGetAddress-1
adrl _TSCompileSpriteStamp-1
adrl _TSSetAddress-1
_CTEnd
_GTEAddSprite MAC
UserTool $1000+GTEToolNum
@ -162,7 +167,9 @@ zpToUse = userId+4
; SetWAP(userOrSystem, tsNum, waptPtr)
pea #$8000 ; $8000 = user tool set
lda EngineMode ; $0000 = system tool, $8000 = user tool set
and #$8000
pha
pei ToolNum ; Push the tool number from the direct page
pea $0000 ; High word of WAP is zero (bank 0)
phd ; Low word of WAP is the direct page
@ -187,7 +194,9 @@ _TSShutDown
jsr _CoreShutDown ; Shut down the library
plb
pea $8000
lda EngineMode ; $0000 = system tool, $8000 = user tool set
and #$8000
pha
pei ToolNum
pea $0000 ; Set WAP to null
pea $0000
@ -233,14 +242,14 @@ _TSReserved
; SetScreenMode(width, height)
_TSSetScreenMode
height equ FirstParam
width equ FirstParam+2
:height equ FirstParam
:width equ FirstParam+2
_TSEntry
lda height,s
lda :height,s
tay
lda width,s
lda :width,s
tax
jsr _SetScreenMode
@ -294,7 +303,21 @@ _TSRender
_TSEntry
lda :flags,s
bit #RENDER_WITH_SHADOWING
beq :no_shadowing
jsr _RenderWithShadowing
bra :done
:no_shadowing
bit #RENDER_PER_SCANLINE
beq :no_scanline
jsr _RenderScanlines
bra :done
:no_scanline
jsr _Render
:done
_TSExit #0;#2
@ -307,18 +330,27 @@ _TSRenderDirty
jsr _RenderDirty
_TSExit #0;#2
; LoadTileSet(Pointer)
; LoadTileSet(Start, Finish, Pointer)
_TSLoadTileSet
TSPtr equ FirstParam
:TSPtr equ FirstParam
:finish equ FirstParam+4
:start equ FirstParam+6
_TSEntry
lda TSPtr+2,s
lda :TSPtr+2,s ; stuff the pointer in the direct page
sta tmp1
lda :TSPtr,s
sta tmp0
lda :start,s ; put the range in the registers
tax
lda TSPtr,s
lda :finish,s
tay
jsr _LoadTileSet
_TSExit #0;#4
_TSExit #0;#8
; CreateSpriteStamp(spriteDescriptor: Word, vbuffAddr: Word)
_TSCreateSpriteStamp
@ -334,7 +366,7 @@ _TSCreateSpriteStamp
_TSExit #0;#4
; AddSprite(spriteSlot, spriteFlags, vbuff, spriteX, spriteY)
; AddSprite(spriteSlot, spriteFlags, vbuff | cbuff, spriteX, spriteY)
_TSAddSprite
:spriteY equ FirstParam+0
:spriteX equ FirstParam+2
@ -344,13 +376,13 @@ _TSAddSprite
_TSEntry
lda :spriteY,s
and #$00FF
xba
sta :spriteY,s
lda :spriteX,s
and #$00FF
ora :spriteY,s
xba
sta :spriteX,s
lda :spriteY,s
and #$00FF
ora :spriteX,s
tay
lda :spriteSlot,s
@ -474,6 +506,7 @@ _TSCopyPicToBG1
:src_width equ tmp6
:src_height equ tmp7
:src_stride equ tmp8
:src_flags equ tmp9
lda :width,s
sta :src_width
@ -484,9 +517,10 @@ _TSCopyPicToBG1
ldy BG1DataBank ; Pick the target data bank
lda :flags,s
bit #$0001
beq *+4
ldy BG1AltBank
sta :src_flags
; bit #$0001
; beq *+4
; ldy BG1AltBank
lda :ptr+2,s
tax
@ -679,6 +713,11 @@ _TSStartScript
_TSExit #0;#6
; SetOverlay(top, bottom, proc)
;
; Overlays are handled as quasi-sprites. They need to be included in the y-sorted list of "stuff", but they are not drawn like
; sprites. As such, they set a special flag in the SPRITE_ID field which allows them to be ignored for other purposes. Also,
; they are not added into the "normal" sprite range on 0 - 15, but are stored in locations 16 and up to further seggregate them from
; the rest of the system. A lot of the SPRITE_* locations are repurposed for Overlay-specific information.
_TSSetOverlay
:proc equ FirstParam+0
:bottom equ FirstParam+4
@ -686,16 +725,48 @@ _TSSetOverlay
_TSEntry
lda #1
sta Overlays
ldx #0 ; Always just use the first spot
lda #SPRITE_OVERLAY
sta Overlays+OVERLAY_ID,x
stz Overlays+OVERLAY_FLAGS,x
lda :top,s
sta Overlays+2
sta Overlays+OVERLAY_TOP,x
lda :bottom,s
sta Overlays+4
dec
sta Overlays+OVERLAY_BOTTOM,x
sec
sbc :top,s
inc
sta Overlays+OVERLAY_HEIGHT,x
lda :proc,s
sta Overlays+6
sta Overlays+OVERLAY_PROC,x
lda :proc+2,s
sta Overlays+8
sta Overlays+OVERLAY_PROC+2,x
ldx #{MAX_SPRITES+0}*2 ; Adjust to call the generic routings
jsr _InsertSprite
_TSExit #0;#8
_TSUpdateOverlay
:proc equ FirstParam+0
:bottom equ FirstParam+4
:top equ FirstParam+6
_TSEntry
ldx #0
stz Overlays+OVERLAY_FLAGS,x
lda :top,s
sta Overlays+OVERLAY_TOP
lda :bottom,s
sta Overlays+OVERLAY_BOTTOM
lda :proc,s
sta Overlays+OVERLAY_PROC
lda :proc+2,s
sta Overlays+OVERLAY_PROC+2
_TSExit #0;#8
@ -809,6 +880,83 @@ _TSSetBG1Scale
sta BG1Scaling
_TSExit #0;#2
; Pointer GetAddress(tblId)
_TSGetAddress
:tblId equ FirstParam+0
:output equ FirstParam+2
_TSEntry
lda #0
sta :output,s
sta :output+2,s
lda :tblId,s
cmp #scanlineHorzOffset
bne :next_1
lda StartXMod164Tbl
sta :output,s
lda StartXMod164Tbl+2
sta :output+2,s
bra :out
:next_1 cmp #scanlineHorzOffset2
bne :next_2
lda BG1StartXMod164Tbl
sta :output,s
lda BG1StartXMod164Tbl+2
sta :output+2,s
bra :out
:next_2
:out
_TSExit #0;#2
; SetAddress(tblId, Pointer)
_TSSetAddress
:ptr equ FirstParam+0
:tblId equ FirstParam+4
_TSEntry
lda :tblId,s
cmp #scanlineHorzOffset
bne :next_1
lda :ptr,s
sta StartXMod164Tbl
lda :ptr+2,s
sta StartXMod164Tbl+2
bra :out
:next_1
cmp #scanlineHorzOffset2
bne :next_2
lda :ptr,s
sta BG1StartXMod164Tbl
lda :ptr+2,s
sta BG1StartXMod164Tbl+2
bra :out
:next_2
:out
_TSExit #0;#6
; CompileSpriteStamp(spriteId, vbuffAddr)
_TSCompileSpriteStamp
:vbuff equ FirstParam
:spriteId equ FirstParam+2
:output equ FirstParam+4
_TSEntry
lda :vbuff,s
tax
lda :spriteId,s
jsr _CompileStampSet
sta :output,s
_TSExit #0;#4
; Insert the GTE code
put Math.s
@ -823,6 +971,7 @@ _TSSetBG1Scale
put Sprite2.s
put SpriteRender.s
put Render.s
; put blitter/Scanline.s
put render/Render.s
put render/Fast.s
put render/Slow.s
@ -841,3 +990,4 @@ _TSSetBG1Scale
put blitter/Template.s
put blitter/TemplateUtils.s
put blitter/Blitter.s
put blitter/PEISlammer.s

View File

@ -12,6 +12,7 @@ _InitBG1
_CopyBinToBG1
:src_width equ tmp6
:src_height equ tmp7
:src_flags equ tmp9
clc
adc #8 ; Advance over the header
@ -21,10 +22,47 @@ _CopyBinToBG1
sta :src_width
lda #208
sta :src_height
lda #COPY_PIC_NORMAL
sta :src_flags
pla
jmp _CopyToBG1
; Reset the BG1 Y-table depending on the rendering mode
;
; A = mode
; 0 = default (base = $1800, stride = 256)
; 1 = scanline (base = $0, stride = 327)
_ResetBG1YTable
:base equ tmp0
:stride equ tmp1
cmp #1 ; scanline mode?
bne :default
lda #0
sta :base
lda #327
sta :stride
bra :begin
:default
lda #$1800
sta :base
lda #256
sta :stride
:begin
ldx #0
lda :base
:loop
sta BG1YTable,x
sta BG1YTable+{208*2},x
clc
adc :stride
inx
inx
cpx #{208*2}
bcc :loop
rts
; Copy a IIgs $C1 picture into BG1. Assumes the file is the correct size (320 x 200)
;
@ -35,6 +73,7 @@ _CopyPicToBG1
:src_width equ tmp6
:src_height equ tmp7
:src_stride equ tmp8
:src_flags equ tmp9
pha
lda #160
@ -42,6 +81,8 @@ _CopyPicToBG1
sta :src_stride
lda #200
sta :src_height
lda #COPY_PIC_NORMAL
sta :src_flags
pla
jmp _CopyToBG1
@ -54,24 +95,64 @@ _CopyToBG1
:src_width equ tmp6
:src_height equ tmp7
:src_stride equ tmp8
:src_flags equ tmp9
:dstptr2 equ tmp10
; scanline mode is tricky -- there's not enough space to make two full copies of a 328x200 bitmap buffer, but we can
; *barely* fit a (164 + 163) x 200 buffer. And, since the zero offset could use either end, this covers all of the cases.
sta :srcptr
stx :srcptr+2
sty :dstptr+2 ; Everything goes into this bank
sty :dstptr2+2
; "Normal" BG1 mode as a stride of 164 bytes and mirrors the BG0 size (328 x 208)
; In "Scanline" mode, the BG1 is treated as a 328x200 bitfield with each horizontal line doubled
lda :src_flags
cmp #COPY_PIC_NORMAL
bne *+5
jmp :_CopyToBG1Normal
cmp #COPY_PIC_SCANLINE
bne *+5
jmp :_CopyToBG1SCanline
rts ; Flag do not match a known copy mode
:_CopyToBG1SCanline
lda #0 ; Start a byte 1 because odd offsets might go back 1 byte and don't want to wrap around
sta :dstptr
clc
adc #164 ; The first part is 1-byte short, the second part is a full 164 bytes
sta :dstptr2
lda :src_width
min #164
sta :src_width
lda :src_height
min #200
sta :src_height
stz :line_cnt
:rloop
lda :line_cnt ; get the pointer to the code field line
asl
tax
lda BG1YTable,x
sta :dstptr
ldy #0 ; move forward in the image data and image data
; Handle first word as a special case
lda [:srcptr],y
sta [:dstptr2],y ; copy directly into the 164-byte buffer
iny
xba
sep #$20
sta [:dstptr],y ; only copy the high byte because the previous line occupies the low byte
rep #$20
iny
:cloop
lda [:srcptr],y
sta [:dstptr],y
sta [:dstptr2],y
iny
iny
@ -79,14 +160,12 @@ _CopyToBG1
cpy :src_width
bcc :cloop
ldy #164
lda [:srcptr] ; Duplicate the last couple of words in the extra space at the end of the line
sta [:dstptr],y
ldy #2
lda [:srcptr],y
ldy #166
sta [:dstptr],y
lda :dstptr
clc
adc #327
sta :dstptr
adc #164
sta :dstptr2
lda :srcptr
clc
@ -99,6 +178,49 @@ _CopyToBG1
bcc :rloop
rts
:_CopyToBG1Normal
stz :line_cnt
:rloop2
lda :line_cnt ; get the pointer to the code field line
asl
tax
lda BG1YTable,x
sta :dstptr
clc
adc #164
sta :dstptr2
ldy #0 ; move forward in the image data and image data
:cloop2
lda [:srcptr],y
sta [:dstptr],y
iny
iny
cpy :src_width
bcc :cloop2
ldy #0
lda [:dstptr],y ; Duplicate the last couple of words in the extra space at the end of the line
sta [:dstptr2],y
ldy #2
lda [:dstptr],y
sta [:dstptr2],y
lda :srcptr
clc
adc :src_stride
sta :srcptr
inc :line_cnt
lda :line_cnt
cmp :src_height
bcc :rloop2
rts
_SetBG1XPos
cmp BG1StartX
beq :out ; Easy, if nothing changed, then nothing changes
@ -140,6 +262,35 @@ _ApplyBG1XPosPre
sta BG1StartXMod164
rts
; Save as _ApplyBG1XPos, but we pretend that StartXMod164 is always zero and deal with the per-line offset adjustment in
; _ApplyScanlineBG1YPos. The tweak here is that the buffer is only 160 bytes wide in scanine mode, instead of 164 bytes wide
_ApplyScanlineBG1XPos
lda BG1StartXMod164 ; How far into the BG1 buffer is the left edge?
tay
phd ; save the direct page because we are going to switch to the
lda BlitterDP ; blitter direct page space and fill in the addresses
tcd
ldx #0
; tya
lda #0
:loop
sta 00,x ; store the value
inc
inc
cmp #164
bcc *+5
sbc #164
inx
inx
cpx #164
bcc :loop
pld
rts
_ApplyBG1XPos
lda #162
sec
@ -196,6 +347,163 @@ _ClearBG1Buffer
plb
rts
; Variation to take care of horizontal adjustments within the BG1 buffer to compensate for the
; per-scanline BG0 displacement. It is up to the caller to manage the memeory layout to make
; this visually work.
;
; In the scanline mode we have to be able to adjust the base address of each BG1 line up to
; a full screen, so scanline mode treats bank as a 640x200 pixel bitmap (64000 byte).
;
; This is just a limitation of scanline displacement mode that there is no extra vertical space.
_ApplyScanlineBG1YPos
:stk_save equ tmp0
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:ytbl_idx_x2 equ tmp4
:shift_value equ tmp5
; Avoid local var collision
:ptr2 equ tmp8
:ytbl_idx_pos_x2 equ tmp10
:virt_line_pos_x2 equ tmp11
:total_left_x2 equ tmp12
:current_count_x2 equ tmp13
:ptr equ tmp14
lda StartXMod164Tbl
sta :ptr
lda StartXMod164Tbl+2
sta :ptr+2
lda BG1StartXMod164Tbl
sta :ptr2
lda BG1StartXMod164Tbl+2
sta :ptr2+2
ora :ptr2
lda BG1StartY
jsr Mod208
sta BG1StartYMod208
asl
sta :ytbl_idx_pos_x2 ; Start copying from the first entry in the table
lda StartYMod208 ; This is the base line of the virtual screen
asl
sta :virt_line_pos_x2
tay
lda ScreenHeight
asl
sta :total_left_x2
:loop0
lda [:ptr],y
tax
and #$FF00 ; Determine how many sequential lines have this mod value
xba
inc
asl
min :total_left_x2 ; Don't draw more than the number of lines that are left to process
sta :current_count_x2 ; Save a copy for later
sta :lines_left_x2 ; Set the parameter
lda :ytbl_idx_pos_x2 ; Set the parameter
sta :ytbl_idx_x2
sty :virt_line_x2 ; Set the parameter
txa ; Put the X mod 164 value in the offset value
and #$00FF
sta :shift_value
jsr :_ApplyConstBG1YPos ; Shift this range by a constant amount
clc
lda :virt_line_pos_x2
adc :current_count_x2
cmp #208*2 ; Do the modulo check in this loop
bcc *+5
sbc #208*2
sta :virt_line_pos_x2
tay
clc
lda :ytbl_idx_pos_x2
adc :current_count_x2
sta :ytbl_idx_pos_x2
lda :total_left_x2
sec
sbc :current_count_x2
sta :total_left_x2
bne :loop0
rts
:_ApplyConstBG1YPos
lda #164
sec
sbc :shift_value
clc
adc BG1StartXMod164
cmp #164+1
bcc *+5
sbc #164
sta :shift_value ; Base shift value
cmp #165
bcc *+4
brk $04
phb ; Save the existing bank
tsc
sta :stk_save
:loop
ldx :virt_line_x2
ldal BTableHigh,x ; Get the bank
pha
plb
ldal BTableLow,x ; Get the address of the first code field line
tay
txa ; Calculate number of lines to draw on this iteration
and #$001E
eor #$FFFF
sec
adc #32
min :lines_left_x2
sta :draw_count_x2
tax
lda :ytbl_idx_x2 ; Read from this location in the BG1YTable
; clc
; CopyBG1YTableToBG1Addr3 :shift_value
CopyBG1YTableToBG1Addr4 :shift_value;blttmp
lda :virt_line_x2 ; advance to the virtual line after
adc :draw_count_x2 ; filled in
sta :virt_line_x2
lda :ytbl_idx_x2
adc :draw_count_x2
sta :ytbl_idx_x2
lda :lines_left_x2 ; subtract the number of lines we just completed
sec
sbc :draw_count_x2
sta :lines_left_x2
jne :loop
lda :stk_save
tcs
plb
rts
; Everytime either BG1 or BG0 Y-position changes, we have to update the Y-register
; value in all of the code fields (within the visible screen)
_ApplyBG1YPos
@ -353,8 +661,138 @@ CopyBG1YTableToBG1Addr
sta: BG1_ADDR+$0000,y
:none rts
; Unrolled copy routine to move BG1YTable entries into BG1_ADDR position
; with a constant shift applied
;
; A = index into the BG1YTable array (x2)
; Y = starting line * $1000
; X = number of lines (x2)
; ]1 = offset
CopyBG1YTableToBG1Addr3 mac
jmp (tbl,x)
tbl da none
da do01,do02,do03,do04
da do05,do06,do07,do08
da do09,do10,do11,do12
da do13,do14,do15,do16
do15 tax
jmp x15
do14 tax
jmp x14
do13 tax
jmp x13
do12 tax
jmp x12
do11 tax
jmp x11
do10 tax
jmp x10
do09 tax
jmp x09
do08 tax
jmp x08
do07 tax
jmp x07
do06 tax
jmp x06
do05 tax
jmp x05
do04 tax
jmp x04
do03 tax
jmp x03
do02 tax
jmp x02
do01 tax
jmp x01
do16 tax
ldal BG1YTable+30,x
adc ]1
sta BG1_ADDR+$F000,y
x15 ldal BG1YTable+28,x
adc ]1
sta BG1_ADDR+$E000,y
x14 ldal BG1YTable+26,x
adc ]1
sta BG1_ADDR+$D000,y
x13 ldal BG1YTable+24,x
adc ]1
sta BG1_ADDR+$C000,y
x12 ldal BG1YTable+22,x
adc ]1
sta BG1_ADDR+$B000,y
x11 ldal BG1YTable+20,x
adc ]1
sta BG1_ADDR+$A000,y
x10 ldal BG1YTable+18,x
adc ]1
sta BG1_ADDR+$9000,y
x09 ldal BG1YTable+16,x
adc ]1
sta BG1_ADDR+$8000,y
x08 ldal BG1YTable+14,x
adc ]1
sta BG1_ADDR+$7000,y
x07 ldal BG1YTable+12,x
adc ]1
sta BG1_ADDR+$6000,y
x06 ldal BG1YTable+10,x
adc ]1
sta BG1_ADDR+$5000,y
x05 ldal BG1YTable+08,x
adc ]1
sta: BG1_ADDR+$4000,y
x04 ldal BG1YTable+06,x
adc ]1
sta BG1_ADDR+$3000,y
x03 ldal BG1YTable+04,x
adc ]1
sta BG1_ADDR+$2000,y
x02 ldal BG1YTable+02,x
adc ]1
sta BG1_ADDR+$1000,y
x01 ldal BG1YTable+00,x
adc ]1
sta: BG1_ADDR+$0000,y
none <<<
; Copy routine to move BG1YTable entries into BG1_ADDR position with an additional
; shift on every line form the user-provided BG1StartXMod164Tbl.
;
; A = index into the BG1YTable array (x2)
; Y = starting line * $1000
; X = number of lines (x2)
;
; ]1 = constant shift value
; ]2 = temp storage space
CopyBG1YTableToBG1Addr4 mac
phy ; save the registers
phx
phb
pha
jsr _SetDataBank ; Set to toolbox data bank
clc
lda 1,s ; virtual_index_x2
adc BG1StartXMod164Tbl ; Get the starting address in the array for this chunk
tay
lda BG1StartXMod164Tbl+1 ; Set the bank to the array location
pha
plb
plb
pla ; POp back the original value
ApplyBG1ModXToShift ]1;]2 ; Copy the array into direct page storage and apply the shift
plb ; Restore the code field bank
plx ; x is used directly in this routine
ply
ApplyBG1ShiftToCode ]2
<<<
; Unrolled copy routine to move BG1YTable entries into BG1_ADDR position with an additional
; shift. This has to be split into two
; shift on every line. This has to be split into two
;
; A = index into the BG1YTable array (x2)
; Y = starting line * $1000
@ -504,3 +942,105 @@ ApplyBG1OffsetValues
:do01 ldal BG1YCache+00
sta: BG1_ADDR+$0000,y
:none rts
; Apply the per-scanline shift from the BG1StartXMod164Tbl pointer. Save the relevant values to the
; direct page since the array could be in any bank.
;
; This does bounds checking to make sure the shift value remains in the valid range
;
; ]1 = source array offset
; ]2 = constant shift value
; ]3 = destination address
_BG1ShiftTemplate mac
lda ]1,y ; Load the value from the array
adc ]2 ; Add to the direct page shift_value
cmp #165 ; If below this value, we're good
bcc *+6
sbc #164
clc
adcl BG1YTable+]1,x
sta ]3+]1
<<<
AToX mac
tax
brl ]1
<<<
ApplyBG1ModXToShift mac
jmp (agmxts_tbl,x)
agmxts_tbl da none
da do01,do02,do03,do04
da do05,do06,do07,do08
da do09,do10,do11,do12
da do13,do14,do15,do16
do16 AToX o16
do15 AToX o15
do14 AToX o14
do13 AToX o13
do12 AToX o12
do11 AToX o11
do10 AToX o10
do09 AToX o09
do08 AToX o08
do07 AToX o07
do06 AToX o06
do05 AToX o05
do04 AToX o04
do03 AToX o03
do02 AToX o02
do01 AToX o01
o16 _BG1ShiftTemplate 30;]1;]2
o15 _BG1ShiftTemplate 28;]1;]2
o14 _BG1ShiftTemplate 26;]1;]2
o13 _BG1ShiftTemplate 24;]1;]2
o12 _BG1ShiftTemplate 22;]1;]2
o11 _BG1ShiftTemplate 20;]1;]2
o10 _BG1ShiftTemplate 18;]1;]2
o09 _BG1ShiftTemplate 16;]1;]2
o08 _BG1ShiftTemplate 14;]1;]2
o07 _BG1ShiftTemplate 12;]1;]2
o06 _BG1ShiftTemplate 10;]1;]2
o05 _BG1ShiftTemplate 8;]1;]2
o04 _BG1ShiftTemplate 6;]1;]2
o03 _BG1ShiftTemplate 4;]1;]2
o02 _BG1ShiftTemplate 2;]1;]2
o01 _BG1ShiftTemplate 0;]1;]2
none <<<
; After the values are saved, the data bank can be repointed at the current code bank and the values copied in
; from the direct page
;
; ]1 = offset in temp buffer
; ]2 = address of temp buffer
; ]3 = 4k offset in code buffer
_BG1ShiftToCodeTemplate mac
lda ]2+]1
sta: BG1_ADDR+]3,y
<<<
ApplyBG1ShiftToCode mac
jmp (abstc_tbl,x)
abstc_tbl da none
da do01,do02,do03,do04
da do05,do06,do07,do08
da do09,do10,do11,do12
da do13,do14,do15,do16
do16 _BG1ShiftToCodeTemplate 30;]1;$F000
do15 _BG1ShiftToCodeTemplate 28;]1;$E000
do14 _BG1ShiftToCodeTemplate 26;]1;$D000
do13 _BG1ShiftToCodeTemplate 24;]1;$C000
do12 _BG1ShiftToCodeTemplate 22;]1;$B000
do11 _BG1ShiftToCodeTemplate 20;]1;$A000
do10 _BG1ShiftToCodeTemplate 18;]1;$9000
do09 _BG1ShiftToCodeTemplate 16;]1;$8000
do08 _BG1ShiftToCodeTemplate 14;]1;$7000
do07 _BG1ShiftToCodeTemplate 12;]1;$6000
do06 _BG1ShiftToCodeTemplate 10;]1;$5000
do05 _BG1ShiftToCodeTemplate 8;]1;$4000
do04 _BG1ShiftToCodeTemplate 6;]1;$3000
do03 _BG1ShiftToCodeTemplate 4;]1;$2000
do02 _BG1ShiftToCodeTemplate 2;]1;$1000
do01 _BG1ShiftToCodeTemplate 0;]1;$0000
none <<<

View File

@ -57,16 +57,19 @@ _BltRange
lda EngineMode
bit #ENGINE_MODE_TWO_LAYER
beq :skip_bank
lda RenderFlags
bit #RENDER_ALT_BG1
beq :primary
lda BG1AltBank
bra :alt
:primary lda BG1DataBank
:alt
; TODO: Switch to loading the selected BG1 bank. No special "Alt" bank
;
; lda RenderFlags
; bit #RENDER_ALT_BG1
; beq :primary
;
; lda BG1AltBank
; bra :alt
;
;:primary lda BG1DataBank
;:alt
lda BG1DataBank
pha
plb
@ -81,7 +84,7 @@ _BltRange
_R0W1
tsc ; save the stack pointer
stal stk_save+1
blt_entry jml $000000 ; Jump into the blitter code $XX/YY00
blt_return _R0W0

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,20 @@
; slammer, note that page-aligned addresses repeat every 8 scan lines and some lines would need
; to be split into two slams to keep the direct page aligned.
;
; At best, this saves 1 cycles per word, or 80 cycles for a full screen -- which is only about
; At best, this saves 1 cycles per word, or 80 cycles for a full scanline -- which is only about
; 12 additional instructions, so this is an optimization that is unlikely to lead to a net
; improvement.
;
; X = first line (inclusive), valid range of 0 to 199
; Y = last line (exclusive), valid range >X up to 200
_PEISlam
cpx #201
bcc *+4
brk $A9
cpy #201
bcc *+4
brk $A8
lda ScreenWidth
dec
stal :screen_width_1 ; save the width-1 outside of the direct page

256
src/blitter/Scanline.s Normal file
View File

@ -0,0 +1,256 @@
; This support an alternate engine mode. When in scanline mode the renderer does not use the
; global StartX and StartY parameters to set up the code field. Instead, an array of scanline
; parameters must be provided to the blitter.
;
; This is a low-level mode and it is assumed that the arrays will contain valid values. This
; process is quite a bit slower that the normal setup because it must calculate code field
; entry points for each line, instead of once for the entire frame.
_ScanlineBG0XPos
:stk_save equ tmp0
:virt_line_x2 equ tmp1
:last_line_x2 equ tmp2
:src_bank equ tmp3
:exit_offset equ tmp4
:entry_offset equ tmp5
:exit_bra equ tmp6
:exit_address equ tmp7
:base_address equ tmp8
:opcode equ tmp9
:odd_entry_offset equ tmp10
lda StartYMod208 ; This is the base line of the virtual screen
asl
sta :virt_line_x2 ; Keep track of it
lda ScreenHeight
asl
clc
adc :virt_line_x2
sta :last_line_x2
phb
phb
pla
and #$FF00
sta :src_bank
:loop
ldx :virt_line_x2
lda StartXMod164Arr,x ; Get the offset for this line
dec ; The exit point is one byte sooner
bpl *+5
lda #163
bit #$0001 ; if odd, then original number was even
beq :odd_exit ; if even, the original number was odd
; This is the even code path
and #$FFFE
tay
lda CodeFieldEvenBRA,y
sta :exit_bra
lda Col2CodeOffset,y
sta :exit_offset
sta LastPatchOffsetArr,x ; Cache afor later
bra :do_entry
; This is the odd code path
:odd_exit tay
lda CodeFieldOddBRA,y
sta :exit_bra
lda Col2CodeOffset,y
sta :exit_offset
sta LastPatchOffsetArr,x
; Handle the entry point calculations
:do_entry
lda StartXMod164Arr,x
clc
adc ScreenWidth ; move to the right edge and back up a byte
dec ; to get the index of the first on-screen byte
cmp #164 ; Keep the value in range
bcc *+5
sbc #164
; Same logic as before
bit #$0001
beq :odd_entry
and #$FFFE
tay
lda Col2CodeOffset,y
sta :entry_offset
lda #$004C ; set the entry_jmp opcode to JMP
sta :opcode
stz :odd_entry_offset ; mark as an even case
bra :prep_complete
:odd_entry
tay
lda Col2CodeOffset,y
sta :entry_offset ; Will be used to load the data
lda Col2CodeOffset-2,y
sta :odd_entry_offset ; will the the actual location to jump to
lda #$00AF ; set the entry_jmp opcode to LDAL
sta :opcode
:prep_complete
; Now patch in the code field line
ldy BTableLow,x ; Get the address of the first code field line
clc
adc :exit_offset ; Add some offsets to get the base address in the code field line
sta :exit_address
sty :base_address
lda BTableHigh,x
ora :src_bank
pha
plb
; First step is to set the BRA instruction to exit the code field at the proper location. There
; are two sub-steps to do here; we need to save the 16-bit value that exists at the location and
; then overwrite it with the branch instruction.
; SaveOpcode
; y is already set to :base_address
ldx :exit_address ; Save from this location
lda: $0000,x
sta: OPCODE_SAVE+$0000,y
;SetConst
; txy ; ldy :exit_address -- starting at this address
lda :exit_bra ; Copy this value into all of the lines
sta: $0000,x
; Next, patch in the CODE_ENTRY value, which is the low byte of a JMP instruction. This is an
; 8-bit operation and, since the PEA code is bank aligned, we use the entry_offset value directly
sep #$20
; SetCodeEntry
lda :entry_offset
; ldy :base_address
sta: CODE_ENTRY+$0000,y
; SetCodeEntryOpcode
lda :opcode
sta: CODE_ENTRY_OPCODE+$0000,y
; If this is an odd entry, also set the odd_entry low byte and save the operand high byte
lda :odd_entry_offset
beq :not_odd
; SetOddCodeEntry
sta: ODD_ENTRY+$0000,y
; SaveHighOperand
; ldx :exit_address
lda: $0002,x
sta: OPCODE_HIGH_SAVE+$0000,y
:not_odd
rep #$20 ; clear the carry
; Do the end of the loop -- update the virtual line counter and reduce the number
; of lines left to render
plb ; restore the bank
lda :virt_line_x2
inc
inc
sta :virt_line_x2
cmp :last_line_x2
jne :loop
rts
_RestoreScanlineBG0Opcodes
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:src_bank equ tmp6
asl
sta :virt_line_x2 ; Keep track of it
phb
phb
pla
and #$FF00
sta :src_bank
txa
asl
sta :lines_left_x2
:loop
ldx :virt_line_x2
lda BTableHigh,x
ora :src_bank
pha
lda BTableLow,x ; Get the address of the first code field line
clc
adc LastPatchOffsetArr,x
tax
plb
lda: OPCODE_SAVE+$0000,y
sta: $0000,x
; Do the end of the loop -- update the virtual line counter and reduce the number
; of lines left to render
plb ; restore the bank
lda :virt_line_x2
inc
inc
sta :virt_line_x2
cmp :last_line_x2
jne :loop
stz LastPatchOffset ; Clear the value once completed
rts
; Unrolled copy routine to move BankTable entries into BNK_ADDR position. This is a bit different than the
; other routines, because we don't need to put values into the code fields, but just copy one-byte values
; into an internal array in bank 00 space. The reason for this is because the code sequence
;
; lda #ADDR
; tcs
; plb
;
; Take only 9 cycles, but the alternative is slower
;
; pea #$BBBB
; plb
; plb = 13 cycles
;
; If for some reason it becomes important to preserve the accumulator, or save the 208 bytes of
; bank 00 memory, then we can change it. The advantage right now is that updating the array can
; be done 16-bits at a time and without having to chunk up the writes across multiple banks. This
; is quite a bit faster than the other routines.
CopyTableToBankBytes
tsx ; save the stack
sei
jmp $0000
lda: 2,y
pha
lda: 0,y
pha
bottom
txs ; restore the stack
cli ; turn interrupts back on
rts

View File

@ -5,10 +5,12 @@
DP_ADDR equ entry_1-base+1 ; offset to patch in the direct page for dynamic tiles
BG1_ADDR equ entry_2-base+1 ; offset to patch in the Y-reg for BG1 (dp),y addressing
STK_ADDR equ entry_3-base+1 ; offset to patch in the stack (SHR) right edge address
; BNK_ADDR equ entry_0-base+1 ; offset to patch in the address of a Bank 0 memory location to load the bank register
DP_ENTRY equ entry_1-base
TWO_LYR_ENTRY equ entry_2-base
ONE_LYR_ENTRY equ entry_3-base
; BANK_ENTRY equ entry_0-base
CODE_ENTRY_OPCODE equ entry_jmp-base
CODE_ENTRY equ entry_jmp-base+1 ; low byte of the page-aligned jump address
@ -16,19 +18,26 @@ ODD_ENTRY equ odd_entry-base+1
CODE_TOP equ loop-base
CODE_LEN equ top-base
CODE_EXIT equ even_exit-base
OPCODE_SAVE equ odd_save-base ; spot to save the code field opcode when patching exit BRA
OPCODE_HIGH_SAVE equ odd_save-base+2 ; save the third byte
OPCODE_SAVE equ odd_low_save-base ; spot to save the code field opcode when patching exit BRA
OPCODE_HIGH_SAVE equ odd_high_save-base ; save the second and third byte
FULL_RETURN equ full_return-base ; offset that returns from the blitter
ENABLE_INT equ enable_int-base ; offset that re-enable interrupts and continues
LINES_PER_BANK equ 16
SNIPPET_BASE equ snippets-base
; offsets from each snippet base address for the different entry points
SNIPPET_ENTRY_1 equ 0 ; two layer + dynamic tile + sprite
SNIPPET_ENTRY_2 equ 4 ; (two layer | dynamic tile) + sprite
SNIPPET_ENTRY_3 equ 18 ; sprite under dynamic tile
SNIPPET_ENTRY_4 equ 19 ; two layer + dynamic tile (no sprite)
; Locations that need the page offset added
PagePatches da {long_0-base+2}
da {long_1-base+2}
da {long_2-base+2}
da {long_3-base+2}
da {long_4-base+2}
; da {long_4-base+2}
da {long_5-base+2}
da {long_6-base+2}
da {odd_entry-base+2}
@ -41,9 +50,9 @@ PagePatches da {long_0-base+2}
; da {jmp_rtn_2-base+2}
]index equ 0
lup 82 ; All the snippet addresses. The two JMP
da {snippets-base+{]index*32}+31} ; instructions are at the end of each of
da {snippets-base+{]index*32}+28} ; the 32-byte buffers
lup 82 ; Patch anything that needs updating within the snippets
da {snippets-base+{]index*32}+17}
da {snippets-base+{]index*32}+29}
]index equ ]index+1
--^
PagePatchNum equ *-PagePatches
@ -53,7 +62,7 @@ BankPatches da {long_0-base+3}
da {long_1-base+3}
da {long_2-base+3}
da {long_3-base+3}
da {long_4-base+3}
; da {long_4-base+3}
da {long_5-base+3}
da {long_6-base+3}
BankPatchNum equ *-BankPatches
@ -66,6 +75,9 @@ BankPatchNum equ *-BankPatches
; the code is assembled on a page boundary to help with alignment
ds \,$00 ; pad to the next page boundary
base
;entry_0 lda #0000 ; Used to set per-scanline bank register
; tcs
; plb
entry_1 ldx #0000 ; Used for LDA 00,x addressing (Dynamic Tiles)
entry_2 ldy #0000 ; Used for LDA (00),y addressing (Second Layer; BG1)
entry_3 lda #0000 ; Sets screen address (right edge)
@ -82,7 +94,7 @@ entry_jmp jmp $0100
; update the low-byte of the address, the means it takes only
; an amortized 4-cycles per line to set the entry point break
right_odd bit #$000B ; Check the bottom nibble to quickly identify a PEA instruction
bit #$000B ; Check the bottom nibble to quickly identify a PEA instruction
bne r_is_not_pea ; This costs 5 cycles in the fast-path
xba ; fast code for PEA
@ -166,38 +178,39 @@ loop lup 82 ; +6 Set up 82 PEA instruc
loop_back jmp loop-base ; +252 Ensure execution continues to loop around
loop_exit_3 jmp even_exit-base ; +255
long_5
odd_exit ldal l_is_jmp+1-base
bit #$000B
odd_exit sep #$21 ; 8-bit mode and set the carry just in case we get to a snippet JMP
long_5 ldal OPCODE_SAVE ; Load the opcode that was saved
bit #$0B
bne :chk_jmp
sep #$20
long_6 ldal l_is_jmp+3-base ; get the high byte of the PEA operand
long_6 ldal OPCODE_HIGH_SAVE+1 ; get the high byte of the PEA operand
; Fall-through when we have to push a byte on the left edge. Must be 8-bit on entry. Optimized
; for the PEA $0000 case -- only 19 cycles to handle the edge, so pretty good
:left_byte
; for the PEA $0000 case -- only 17 cycles to handle the edge, so pretty good
pha
rep #$20
rep #$21
; JMP opcode = $4C, JML opcode = $5C
even_exit jmp $1000 ; Jump to the next line.
ds 1 ; space so that the last line in a bank can be patched into a JML
:chk_jmp
bit #$0040
:chk_jmp mx %10 ; 8-bit accumulator / 16-bit registers
bit #$40
bne l_is_jmp
long_4 stal *+4-base
dfb $00,$00
rep #$20 ; saved 3 cycles using 8-bit mode, but give it back here.
odd_low_save dfb $00,$00 ; save the first and second bytes of the code field. Works for LDA dp,x and LDA (0),y
l_jmp_rtn xba
sep #$20
pha
rep #$61 ; Clear everything C, V and M
bra even_exit
l_is_jmp sec ; Set the C flag (V is always cleared at this point) which tells a snippet to push only the high byte
odd_save dfb $00,$00,$00 ; The odd exit 3-byte sequence is always stashed here
l_is_jmp
rep #$20 ; Back to 16-bit mode (carry was set above)
; sec ; Set the C flag (V is always cleared at this point) which tells a snippet to push only the high byte
dfb $4C ; Expect a JMP instruction
odd_high_save dfb $00,$00 ; The high 2 bytes of the 3-byte code field sequence is always stashed here
; Special epilogue: skip a number of bytes and jump back into the code field. This is useful for
; large, floating panels in the attract mode of a game, or to overlay solid
@ -227,73 +240,95 @@ epilogue_1 tsc
; its passed state, because having the carry bit clear prevents evaluation of
; the V bit.
;
; Snippet Samples:
; Version 2: In order to improve performance, especially for two-layer tiles + sprites, the
; snippet code was revised to have a fixed structure so that the constant DATA and
; MASK values always exist in the same location, regarless of the tile type. The
; tradeoff is that there is a different entry point into the snippet based on the
; tile type, but that is significantly cheaper to lookup and patch into the code
; field JMP instruction than it is to rebuild 20+ bytes of code each time.
;
; Standard Two-level Mix (23 bytes)
; There are different snippet templates + offset tables based on the EngineMode
;
; Optimal = 18 cycles (LDA/AND/ORA/PHA/JMP)
; 16-bit write = 20 cycles
; 8-bit low = 28 cycles
; 8-bit high = 27 cycles
; EngineMode
;
; start lda (00),y ; 6
; and #MASK ; 3
; ora #DATA ; 3 = 12 cycles to load the data
; bcs alt_exit ; 2/3
; pha ; 4
; out jmp next ; 3 Fast-path completes in 5 additional cycles
; alt_exit jmp jmp_rtn ; 3
; ENGINE_MODE_TWO_LAYER NO
; ENGINE_MODE_DYN_TILES NO
;
; Snippet Template
; None.
;
; For dynamic masked tiles, we re-write bytes 2 - 8 as this, which mostly
; avoids an execution speed pentaly for having to fill in the two extra bytes
; with an instruction
; ENGINE_MODE_TWO_LAYER YES
; ENGINE_MODE_DYN_TILES NO
;
; start lda (00),y ; 6
; and $80,x ; 5
; ora $00,x ; 5 = 16 cycles to load the data
; bcs alt_exit ; 2/3
; pha
; ...
; Snippet Template
;
; A theoretical exception handler that performed a full 3-level blend would be
; ds 4
; lda (00),y <-- Single Entry Point
; and #MASK <-- Mask is always at byte 8
; ora #DATA <-- Data is always at byte 11
; bcs _alt
; pha
; jmp NEXT
; _alt jmp RTN
;
; start lda 0,s
; and [00],y
; ora (00),y
; and $80,x
; ora $00,x
; and #MASK
; ora #DATA
; bcs alt_exit
; pha ; 4
; out brl next ; 4 Fast-path completes in 5 additional cycles
; ENGINE_MODE_TWO_LAYER NO
; ENGINE_MODE_DYN_TILES YES
;
; alt_exit bvs r_edge ; 2/3
; clc ; 2
; brl l_jmp_rtn ; 3
; r_edge rep #$41
; brl r_jmp_rtn ; 3
; Snippet Template
;
; ds 4
; lda 00,x <-- Single Entry Point
; and #MASK
; ora #DATA
; bcs _alt
; pha
; jmp NEXT
; _alt jmp RTN
;
; ENGINE_MODE_TWO_LAYER YES
; ENGINE_MODE_DYN_TILES YES
;
; Snippet Template
;
; lda (00),y <-- Entry Point 1
; and $80,x
; ora $00,x <-- Entry Point 2 (Change this word to "lda (00),y" or "lda 00,x", or "ora 00,x" depending on combination)
; and #MASK
; ora #DATA
; bcs _alt
; _16bit pha
; jmp NEXT
; db 1 <--- Entry Point 3 (opcode for an LDA #DATA instruction)
; lda (00),y <--- Entry Point 4 (sneak this in here to avoid extra branch)
; and $80,x
; ora $00,x
; bcc _16bit
; _alt jmp RTN (29 bytes)
;
; Note that the code that's assembled in these snippets is just a template. Every routine that utilizes
; an exception handler *MUST* patch up the routines. There are different routines based on the Engine Mode.
;
; The LDA (00),y opcodes have a fixed operand, but the dynamic tile instructions are determined by the
; dynamic tile id and must be set each time.
; Each snippet is provided 32 bytes of space. The constant code is filled in from the end and
; it is the responsibility of the code that fills in the hander to create valid program in the
; first 23 bytes are available to be manipulated.
;
; Note that the code that's assembled in the first bytes of these snippets is just an example. Every
; routine that created an exception handler *MUST* write a full set of instructions since there is
; no guarantee of what was written previously.
ds \,$00 ; pad to the next page boundary
]index equ 0
snippets lup 82
ds 2 ; space for all exception handlers
and #$0000 ; the mask operand will be set when the tile is drawn
ora #$0000 ; the data operand will be set when the tile is drawn
ds 15 ; extra padding
bcs :byte ; if C = 0, just push the data and return
pha ; 1 byte
jmp loop+3+{3*]index}-base ; 3 bytes
:byte jmp jmp_rtn-base ; 3 bytes
lda ({{81-]index}*2}),y ; 0: Pre-set the LDA (XX),y instructions
and $80,x ; 2: The direct page instructions are placeholders and get overwritten
ora $00,x ; 4: This gets patched out often
and #$0000 ; 6: the mask operand will be set when the tile is drawn
ora #$0000 ; 9: the data operand will be set when the tile is drawn
bcs :byte ; 12: if C = 0, just push the data and return
:word pha ; 14:
jmp loop+3+{3*]index}-base ; 15: Return address offset within the code field
db $A9 ; 18: LDA #DATA opcode
lda ({{81-]index}*2}),y ; 19: Pre-set the LDA (XX),y instructions
and $80,x ; 21:
ora $00,x ; 23:
bcc :word ; 25:
:byte jmp jmp_rtn-base ; 27:
ds 2 ; 30: Padding to make a full 32 bytes
]index equ ]index+1
--^
top

View File

@ -72,7 +72,7 @@ Counter equ tmp3
rts
; Patch an 8-bit or 16-bit valueS into the bank. These are a set up unrolled loops to
; Patch an 8-bit or 16-bit valueS into the bank. These are set up as unrolled loops to
; quickly patch in a constant value, or a value from an array into a given set of
; templates.
;
@ -87,14 +87,14 @@ Counter equ tmp3
; A = value
;
; Set M to 0 or 1
SetConst ; Need a blank line here, otherwise the :tbl local variable resolveds backwards
jmp (:tbl,x)
:tbl da :bottom-00,:bottom-03,:bottom-06,:bottom-09
da :bottom-12,:bottom-15,:bottom-18,:bottom-21
da :bottom-24,:bottom-27,:bottom-30,:bottom-33
da :bottom-36,:bottom-39,:bottom-42,:bottom-45
da :bottom-48
:top sta $F000,y
SetConst mac
jmp (dispTbl,x)
dispTbl da bottom-00,bottom-03,bottom-06,bottom-09
da bottom-12,bottom-15,bottom-18,bottom-21
da bottom-24,bottom-27,bottom-30,bottom-33
da bottom-36,bottom-39,bottom-42,bottom-45
da bottom-48
sta $F000,y
sta $E000,y
sta $D000,y
sta $C000,y
@ -110,7 +110,8 @@ SetConst ; Need a blank line here, ot
sta $2000,y
sta $1000,y
sta: $0000,y
:bottom rts
bottom
<<<
; SetDPAddrs
;
@ -244,8 +245,8 @@ BuildBank
plb
plb
; Change the patched value to one of DP_ENTRY, TWO_LYR_ENTRY or ONE_LYR_ENTRY based on the capabilities
; that the engine needs.
; Change the patched value to one of BANK_ENTRY, DP_ENTRY, TWO_LYR_ENTRY or ONE_LYR_ENTRY based
; on the capabilities that the engine needs.
lda #DP_ENTRY
sta :entryOffset

View File

@ -1,121 +0,0 @@
; A simple helper function that fills in all of the opcodes of a tile with the PEA opcode. This is
; a separate functino because we can often just update the tile data if we know the opcodes are already
; set. When we have to fill the opcodes, this function is used
_TBFillPEAOpcode
sep #$20
lda #$F4
]line equ 0
lup 8
sta: $0000+{]line*$1000},y
sta: $0003+{]line*$1000},y
]line equ ]line+1
--^
rep #$20
rts
; Copy tile data into the direct page compositing buffer. The main reason to do this in full passes is
; because we can avoid needing to use both the X and Y registers during the compositing process and
; reserve Y to hold the code field address.
;
; Also, we can get away with not setting the bank register, this is a wash in terms of speed, but results
; in simpler, more composable subroutines
_TBCopyTileDataAndMaskToCBuff
jsr _TBCopyTileDataToCBuff
jmp _TBCopyTileMaskToCBuff
_TBCopyTileDataAndMaskToCBuffV
jsr _TBCopyTileDataToCBuffV
jmp _TBCopyTileMaskToCBuffV
_TBCopyTileDataToCBuff
]line equ 0
lup 8
ldal tiledata+{]line*4},x
sta blttmp+{]line*4}
ldal tiledata+{]line*4}+2,x
sta blttmp+{]line*4}+2
]line equ ]line+1
--^
rts
_TBCopyTileDataToCBuffV
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
sta blttmp+{]dest*4}
ldal tiledata+{]src*4}+2,x
sta blttmp+{]dest*4}+2
]src equ ]src-1
]dest equ ]dest+1
--^
rts
; Copy tile mask data into the direct page compositing buffer.
_TBCopyTileMaskToCBuff
]line equ 0
lup 8
ldal tiledata+{]line*4}+32,x
sta blttmp+{]line*4}+32
ldal tiledata+{]line*4}+32+2,x
sta blttmp+{]line*4}+32+2
]line equ ]line+1
--^
rts
_TBCopyTileMaskToCBuffV
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4}+32,x
sta blttmp+{]dest*4}+32
ldal tiledata+{]src*4}+32+2,x
sta blttmp+{]dest*4}+32+2
]src equ ]src-1
]dest equ ]dest+1
--^
rts
; Tile 0 specializations
; _TBConstTile
;
; A specialized routine that fills in a tile with a single constant value. It's intended to be used to
; fill in solid colors, so there are no specialized horizontal or verical flipped variants
_TBConstTileX
lda #0
sta: $0001,y
sta: $0004,y
sta $1001,y
sta $1004,y
sta $2001,y
sta $2004,y
sta $3001,y
sta $3004,y
sta $4001,y
sta $4004,y
sta $5001,y
sta $5004,y
sta $6001,y
sta $6004,y
sta $7001,y
sta $7004,y
plb
rts
_TBConstTileSlow0
tax
jsr _TBFillPEAOpcode
jmp _TBConstTileX
_TBConstTileDataToDP2
]line equ 0
lup 8
stz tmp_tile_data+{]line*4}
stz tmp_tile_data+{]line*4}+2
]line equ ]line+1
--^
rts

View File

@ -1,52 +0,0 @@
; _TBSolidTile
;
; Define the addresses of the subroutines that draw the normal and flipped variants of the tiles, both
; in the optimized (no second background) and normal cases.
;
; On entry, the following register values need to be set
;
; X : address of base tile in the tiledata bank (tileId * 128)
; Y : address of the top-left corder of the tile location in the code field
; B : set to the code field bank
_TBSolidTile_00
jsr _TBCopyData
jmp _TBFillPEAOpcode
_TBSolidTile_0H
jsr _TBCopyData
jmp _TBFillPEAOpcode
_TBSolidTile_V0
jsr _TBCopyDataV
jmp _TBFillPEAOpcode
_TBSolidTile_VH
jsr _TBCopyDataV
jmp _TBFillPEAOpcode
; Old routines
_TBCopyData
]line equ 0
lup 8
ldal tiledata+{]line*4},x
sta: $0004+{]line*$1000},y
ldal tiledata+{]line*4}+2,x
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts
_TBCopyDataV
]src equ 7
]dest equ 0
lup 8
ldal tiledata+{]src*4},x
sta: $0004+{]dest*$1000},y
ldal tiledata+{]src*4}+2,x
sta: $0001+{]dest*$1000},y
]src equ ]src-1
]dest equ ]dest+1
--^
rts

View File

@ -1,104 +0,0 @@
; _TBDynamicTile
;
; These subroutines fill in the code field with the instructions to render data from the dynamic
; code buffer. This is a bit different, because no tile data is manipulated. It is the
; responsibiliy of the user of the API to use the CopyTileToDyn subroutine to get data
; into the correct location.
;
; This tile type does not explicitly support horizontal or vertical flipping. An appropriate tile
; descriptor should be passed into CopyTileToDyn to put the horizontally or vertically flipped source
; data into the dynamic tile buffer
_TBDynamicTile_00
jsr _TBDynamicData
jmp _TBFillLdaDpOpcode
_TBDynamic
ldal TileStore+TS_TILE_ID,x
and #$007F
ora #$4800
]line equ 0 ; render the first column
lup 8
sta: $0004+{]line*$1000},y
]line equ ]line+1
--^
inc ; advance to the next word
inc
]line equ 0 ; render the second column
lup 8
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
sep #$20
lda #$B5
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
plb
rts
; Primitive to render a dynamic tile
;
; LDA 00,x / PHA where the operand is fixed when the tile is rendered
; $B5 $00 $48
_TBDynamicData
lda _TILE_ID ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$4800 ; insert the PHA instruction
]line equ 0 ; render the first column
lup 8
sta: $0004+{]line*$1000},y
]line equ ]line+1
--^
inc ; advance to the next word
inc
]line equ 0 ; render the second column
lup 8
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts
; A simple helper function that fill in all of the opcodes of a tile with the LDA dp,x opcode.
_TBFillLdaDpOpcode
sep #$20
lda #$B5
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts

View File

@ -1,131 +0,0 @@
; _TBMaskedTile
;
; These tile renderes are for "normal" tiles that also apply their mask data. If the case of the second
; background being disabled, the optimized variants are the same as Tile00000
;
; Y register = address of code field tile
; X register = tile address
; Accumulator = logical word offset of the tile (0, 2, 4, ..., 82)
;
; Need to slightly remap these register inputs to save into the direct page cached values
_TBMaskedTile_00
_TBMaskedTile_0H
sta _X_REG ; Save these values as we will need to reload them
sty _Y_REG ; at certain points
stx _T_PTR
; Do the left column first
CopyMaskedWord tiledata+0;tiledata+32+0;$0003
CopyMaskedWord tiledata+4;tiledata+32+4;$1003
CopyMaskedWord tiledata+8;tiledata+32+8;$2003
CopyMaskedWord tiledata+12;tiledata+32+12;$3003
CopyMaskedWord tiledata+16;tiledata+32+16;$4003
CopyMaskedWord tiledata+20;tiledata+32+20;$5003
CopyMaskedWord tiledata+24;tiledata+32+24;$6003
CopyMaskedWord tiledata+28;tiledata+32+28;$7003
; Move the index for the JTableOffset array. This is the same index used for transparent words,
; so, if _X_REG is zero, then we would be patching out the last word in the code field with LDA (0),y
; and then increment _X_REG by two to patch the next-to-last word in the code field with LDA (2),y
inc _X_REG
inc _X_REG
; Do the right column
CopyMaskedWord tiledata+2;tiledata+32+2;$0000
CopyMaskedWord tiledata+6;tiledata+32+6;$1000
CopyMaskedWord tiledata+10;tiledata+32+10;$2000
CopyMaskedWord tiledata+14;tiledata+32+14;$3000
CopyMaskedWord tiledata+18;tiledata+32+18;$4000
CopyMaskedWord tiledata+22;tiledata+32+22;$5000
CopyMaskedWord tiledata+26;tiledata+32+26;$6000
CopyMaskedWord tiledata+30;tiledata+32+30;$7000
rts
;_TBMaskedTile_0H
; sta _X_REG
; sty _Y_REG
; stx _T_PTR
;
; CopyMaskedWord tiledata+64+0;tiledata+64+32+0;$0003
; CopyMaskedWord tiledata+64+4;tiledata+64+32+4;$1003
; CopyMaskedWord tiledata+64+8;tiledata+64+32+8;$2003
; CopyMaskedWord tiledata+64+12;tiledata+64+32+12;$3003
; CopyMaskedWord tiledata+64+16;tiledata+64+32+16;$4003
; CopyMaskedWord tiledata+64+20;tiledata+64+32+20;$5003
; CopyMaskedWord tiledata+64+24;tiledata+64+32+24;$6003
; CopyMaskedWord tiledata+64+28;tiledata+64+32+28;$7003
;
; inc _X_REG
; inc _X_REG
;
; CopyMaskedWord tiledata+64+2;tiledata+64+32+2;$0000
; CopyMaskedWord tiledata+64+6;tiledata+64+32+6;$1000
; CopyMaskedWord tiledata+64+10;tiledata+64+32+10;$2000
; CopyMaskedWord tiledata+64+14;tiledata+64+32+14;$3000
; CopyMaskedWord tiledata+64+18;tiledata+64+32+18;$4000
; CopyMaskedWord tiledata+64+22;tiledata+64+32+22;$5000
; CopyMaskedWord tiledata+64+26;tiledata+64+32+26;$6000
; CopyMaskedWord tiledata+64+30;tiledata+64+32+30;$7000
;
; rts
_TBMaskedTile_V0
_TBMaskedTile_VH
sta _X_REG
sty _Y_REG
stx _T_PTR
CopyMaskedWord tiledata+0;tiledata+32+0;$7003
CopyMaskedWord tiledata+4;tiledata+32+4;$6003
CopyMaskedWord tiledata+8;tiledata+32+8;$5003
CopyMaskedWord tiledata+12;tiledata+32+12;$4003
CopyMaskedWord tiledata+16;tiledata+32+16;$3003
CopyMaskedWord tiledata+20;tiledata+32+20;$2003
CopyMaskedWord tiledata+24;tiledata+32+24;$1003
CopyMaskedWord tiledata+28;tiledata+32+28;$0003
inc _X_REG
inc _X_REG
CopyMaskedWord tiledata+2;tiledata+32+2;$7000
CopyMaskedWord tiledata+6;tiledata+32+6;$6000
CopyMaskedWord tiledata+10;tiledata+32+10;$5000
CopyMaskedWord tiledata+14;tiledata+32+14;$4000
CopyMaskedWord tiledata+18;tiledata+32+18;$3000
CopyMaskedWord tiledata+22;tiledata+32+22;$2000
CopyMaskedWord tiledata+26;tiledata+32+26;$1000
CopyMaskedWord tiledata+30;tiledata+32+30;$0000
rts
;_TBMaskedTile_VH
; sta _X_REG
; sty _Y_REG
; stx _T_PTR
;
; CopyMaskedWord tiledata+64+0;tiledata+64+32+0;$7003
; CopyMaskedWord tiledata+64+4;tiledata+64+32+4;$6003
; CopyMaskedWord tiledata+64+8;tiledata+64+32+8;$5003
; CopyMaskedWord tiledata+64+12;tiledata+64+32+12;$4003
; CopyMaskedWord tiledata+64+16;tiledata+64+32+16;$3003
; CopyMaskedWord tiledata+64+20;tiledata+64+32+20;$2003
; CopyMaskedWord tiledata+64+24;tiledata+64+32+24;$1003
; CopyMaskedWord tiledata+64+28;tiledata+64+32+28;$0003
;
; inc _X_REG
; inc _X_REG
;
; CopyMaskedWord tiledata+64+2;tiledata+64+32+2;$7000
; CopyMaskedWord tiledata+64+6;tiledata+64+32+6;$6000
; CopyMaskedWord tiledata+64+10;tiledata+64+32+10;$5000
; CopyMaskedWord tiledata+64+14;tiledata+64+32+14;$4000
; CopyMaskedWord tiledata+64+18;tiledata+64+32+18;$3000
; CopyMaskedWord tiledata+64+22;tiledata+64+32+22;$2000
; CopyMaskedWord tiledata+64+26;tiledata+64+32+26;$1000
; CopyMaskedWord tiledata+64+30;tiledata+64+32+30;$0000
;
; rts

View File

@ -1,122 +0,0 @@
; _TBDynamicMaskTile
;
; Insert a code sequence to mask the dynamic tile against the background. This is quite a slow process because
; every word needs to be handled with a JMP exception; but it looks good!
_TBDynamicMaskTile_00
jsr _TBDynamicDataAndMask
jmp _TBFillJMPOpcode
; A = dynamic tile id (must be <32)
_TBDynamicDataAndMask
sta _X_REG ; Cache some column values derived from _X_REG
tax
ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand
xba
sta _OP_CACHE
clc
ldal JTableOffset,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
; We need to do an AND dp|$80,x / ORA dp,x. The opcode values are $35 and $15, respectively.
; We pre-calculate the AND opcode with the high bit of the operand set and then, in the macro
; perform and EOR #$2080 to covert the opcode and operand in one instruction
lda _TILE_ID ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$3580 ; Pre-calc the AND $80,x opcode + operand
xba
sta _T_PTR ; This is an op to load the dynamic tile data
CopyMaskedDWord $0003
CopyMaskedDWord $1003
CopyMaskedDWord $2003
CopyMaskedDWord $3003
CopyMaskedDWord $4003
CopyMaskedDWord $5003
CopyMaskedDWord $6003
CopyMaskedDWord $7003
ldx _X_REG
clc
ldal JTableOffset+2,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _OP_CACHE
adc #$0200
sta _OP_CACHE
lda _T_PTR
adc #$0200
sta _T_PTR
CopyMaskedDWord $0000
CopyMaskedDWord $1000
CopyMaskedDWord $2000
CopyMaskedDWord $3000
CopyMaskedDWord $4000
CopyMaskedDWord $5000
CopyMaskedDWord $6000
CopyMaskedDWord $7000
rts
; A simple helper function that fill in all of the opcodes of a tile with the JMP opcode.
_TBFillJMPOpcode
sep #$20
lda #$4C
sta: $0000,y
sta: $0003,y
sta $1000,y
sta $1003,y
sta $2000,y
sta $2003,y
sta $3000,y
sta $3003,y
sta $4000,y
sta $4003,y
sta $5000,y
sta $5003,y
sta $6000,y
sta $6003,y
sta $7000,y
sta $7003,y
rep #$20
rts
; Masked renderer for a dynamic tile. What's interesting about this renderer is that the mask
; value is not used directly, but simply indicates if we can use a LDA 0,x / PHA sequence,
; a LDA (00),y / PHA, or a JMP to a blended render
;
; If a dynamic tile is animated, there is the possibility to create a special mask that marks
; words of the tile that a front / back / mixed across all frames.
;
; ]1 : code field offset
;
; This macro does not set the opcode since they will all be JMP instructions, they can be
; filled more efficiently in a separate routine.
CopyMaskedDWord MAC
; Need to fill in the first 6 bytes of the JMP handler with the following code sequence
;
; lda (00),y
; and $80,x
; ora $00,x
; bra *+17
lda _JTBL_CACHE
ora #{]1&$F000} ; adjust for the current row offset
sta: ]1+1,y
tax ; This becomes the new address that we use to patch in
lda _OP_CACHE
sta: $0000,x ; LDA (00),y
lda _T_PTR
sta: $0002,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,x ; ORA $00,x
lda #$0F80 ; branch to the prologue (BRA *+17)
sta: $0006,x
eom

View File

@ -1,6 +0,0 @@
; _TBPriorityTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00000.s implementation
_TBPriorityTile dw _TBSolidTile_00,_TBSolidTile_0H,_TBSolidTile_V0,_TBSolidTile_VH
dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH

View File

@ -1,6 +0,0 @@
; _TBPriorityDynamicTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00001.s implementation
_TBPriorityDynamicTile dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00
dw _TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00,_TBDynamicTile_00

View File

@ -1,19 +0,0 @@
; _TBMaskedPriorityTile
;
; The priority bit allows the tile to be rendered in front of sprites. If there's no sprite
; in this tile area, then just fallback to the Tile00000.s implementation
_TBMaskedPriorityTile dw _TBMaskedTile_00,_TBMaskedTile_0H,_TBMaskedTile_V0,_TBMaskedTile_VH
dw _TBCopyData,_TBCopyDataH,_TBCopyDataV,_TBCopyDataVH
; NOTE: Eventually, we want a way to support this use-case
;
; When the high-priority bit is set for a tile, then the BG0 tile will be rendered behind the BG1 data. In
; order to support this, the optional BG1 mask buffer needs to be enabled and *every* word in the tile
; becomes a JMP handler (similar to masked dynamic tiles)
;
; The 8 bytes of code that is generated in the JMP handler is
;
; lda #tiledata
; and [dp],y
; ora (dp),y
; nop

View File

@ -1,99 +0,0 @@
; _TBSolidSpriteTile
;
; Renders solid tiles with sprites layered on top of the tile data. Because we need to combine
; data from the sprite plane, tile data and write to the code field (which are all in different banks),
; there is no way to do everything inline, so a composite tile is created on the fly and written to
; a direct page buffer. This direct page buffer is then used to render the tile.
_TBSolidSpriteTile_00
_TBSolidSpriteTile_0H
jsr _TBCopyTileDataToCBuff ; Copy the tile into the compositing buffer (using correct x-register)
jsr _TBApplySpriteData ; Overlay the data from the sprite plane (and copy into the code field)
jmp _TBFillPEAOpcode ; Fill in the code field opcodes
_TBSolidSpriteTile_V0
_TBSolidSpriteTile_VH
jsr _TBCopyTileDataToCBuffV
jsr _TBApplySpriteData
jmp _TBFillPEAOpcode
; Fast variation that does not need to set the opcode
_TBFastSpriteTile_00
_TBFastSpriteTile_0H
jsr _TBCopyTileDataToCBuff ; Copy the tile into the compositing buffer
jmp _TBApplySpriteData ; Overlay the data form the sprite plane (and copy into the code field)
_TBFastSpriteTile_V0
_TBFastSpriteTile_VH
jsr _TBCopyTileDataToCBuffV
jmp _TBApplySpriteData
; Need to update the X-register before calling this
_TBApplySpriteData
ldx _SPR_X_REG ; set to the unaligned tile block address in the sprite plane
]line equ 0
lup 8
lda blttmp+{]line*4}
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts
_TBApplySpriteDataOne
ldx spriteIdx
]line equ 0
lup 8
lda blttmp+{]line*4}
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts
_TBApplySpriteDataTwo
]line equ 0
lup 8
lda blttmp+{]line*4}
ldx spriteIdx+2
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
ldx spriteIdx
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
ldx spriteIdx+2
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
ldx spriteIdx
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts
; Copy just the data into the code field from the composite buffer
_TBSolidComposite
]line equ 0
lup 8
lda blttmp+{]line*4}
sta: $0004+{]line*$1000},y
lda blttmp+{]line*4}+2
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
rts

View File

@ -1,211 +0,0 @@
; _TBDynamicSpriteTile
;
; This tile type does not explicitly support horizontal or vertical flipping. An appropriate tile
; descriptor should be passed into CopyTileToDyn to put the horizontally or vertically flipped source
; data into the dynamic tile buffer
_TBDynamicSpriteTile
sta _X_REG
ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
sta _JTBL_CACHE
ldal TileStore+TS_TILE_ID,x ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$B500
xba
sta _OP_CACHE ; This is the 2-byte opcode for to load the data
CopyDynWord 0;$0003
CopyDynWord 4;$1003
CopyDynWord 8;$2003
CopyDynWord 12;$3003
CopyDynWord 16;$4003
CopyDynWord 20;$5003
CopyDynWord 24;$6003
CopyDynWord 28;$7003
clc
lda _JTBL_CACHE
adc #32 ; All the snippets are 32 bytes wide and, since we're
sta _JTBL_CACHE ; within one tile, the second column is consecutive
lda _OP_CACHE
adc #$0200 ; Advance to the next word
sta _OP_CACHE
CopyDynWord 2;$0000
CopyDynWord 6;$1000
CopyDynWord 10;$2000
CopyDynWord 14;$3000
CopyDynWord 18;$4000
CopyDynWord 22;$5000
CopyDynWord 26;$6000
CopyDynWord 30;$7000
plb
rts
_TBDynamicSpriteTile_00
sty _Y_REG ; This is restored in the macro
sta _X_REG ; Cache some column values derived from _X_REG
tax
clc
ldal JTableOffset,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _TILE_ID ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$B500
xba
sta _OP_CACHE ; This is the 2-byte opcode for to load the data
ldx _SPR_X_REG
CopyDynSpriteWord {0*SPRITE_PLANE_SPAN};$0003
CopyDynSpriteWord {1*SPRITE_PLANE_SPAN};$1003
CopyDynSpriteWord {2*SPRITE_PLANE_SPAN};$2003
CopyDynSpriteWord {3*SPRITE_PLANE_SPAN};$3003
CopyDynSpriteWord {4*SPRITE_PLANE_SPAN};$4003
CopyDynSpriteWord {5*SPRITE_PLANE_SPAN};$5003
CopyDynSpriteWord {6*SPRITE_PLANE_SPAN};$6003
CopyDynSpriteWord {7*SPRITE_PLANE_SPAN};$7003
ldx _X_REG
clc
ldal JTableOffset+2,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _OP_CACHE
adc #$0200
sta _OP_CACHE
ldx _SPR_X_REG
CopyDynSpriteWord {0*SPRITE_PLANE_SPAN}+2;$0000
CopyDynSpriteWord {1*SPRITE_PLANE_SPAN}+2;$1000
CopyDynSpriteWord {2*SPRITE_PLANE_SPAN}+2;$2000
CopyDynSpriteWord {3*SPRITE_PLANE_SPAN}+2;$3000
CopyDynSpriteWord {4*SPRITE_PLANE_SPAN}+2;$4000
CopyDynSpriteWord {5*SPRITE_PLANE_SPAN}+2;$5000
CopyDynSpriteWord {6*SPRITE_PLANE_SPAN}+2;$6000
CopyDynSpriteWord {7*SPRITE_PLANE_SPAN}+2;$7000
rts
; Create a masked render based on data in the direct page temporary buffer
;
; ]1 : sprite buffer offset
; ]2 : code field offset
CopyDynWord mac
lda tmp_sprite_mask+{]1} ; load the mask value
bne mixed ; a non-zero value may be mixed
; This is a solid word
lda #$00F4 ; PEA instruction
sta: ]2,y
lda tmp_sprite_data+{]1} ; load the sprite data
sta: ]2+1,y ; PEA operand
bra next
mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent
lda #$004C ; JMP to handler
sta: {]2},y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$F000} ; adjust for the current row offset
sta: {]2}+1,y
tax ; This becomes the new address that we use to patch in
lda _OP_CACHE ; Get the LDA dp,x instruction for this column
sta: $0000,x
lda #$0029 ; AND #SPRITE_MASK
sta: $0002,x
lda tmp_sprite_mask+{]1}
sta: $0003,x
lda #$0009 ; ORA #SPRITE_DATA
sta: $0005,x
lda tmp_sprite_data+{]1}
sta: $0006,x
lda #$0D80 ; branch to the prologue (BRA *+15)
sta: $0008,x
bra next
; This is a transparent word, so just show the dynamic data
transparent
lda #$4800 ; Put the PHA in the third byte
sta: {]2}+1,y
lda _OP_CACHE ; Store the LDA dp,x instruction with operand
sta: {]2},y
next
<<<
; Masked renderer for a dynamic tile with sprite data overlaid.
;
; ]1 : sprite plane offset
; ]2 : code field offset
CopyDynSpriteWord MAC
; Need to fill in the first 10 bytes of the JMP handler with the following code sequence where
; the data and mask from from the sprite plane
;
; lda $00,x
; and #MASK
; ora #DATA
; bra *+15
;
; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile
; code.
ldal spritemask+{]1},x ; load the mask value
bne mixed ; a non-zero value may be mixed
; This is a solid word
lda #$00F4 ; PEA instruction
sta: ]2,y
ldal spritedata+{]1},x ; load the sprite data
sta: ]2+1,y ; PEA operand
bra next
mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent
lda #$004C ; JMP to handler
sta: ]2,y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$F000} ; adjust for the current row offset
sta: ]2+1,y
tay ; This becomes the new address that we use to patch in
lda _OP_CACHE ; Get the LDA dp,x instruction for this column
sta: $0000,y
lda #$0029 ; AND #SPRITE_MASK
sta: $0002,y
ldal spritemask+{]1},x
sta: $0003,y
lda #$0009 ; ORA #SPRITE_DATA
sta: $0005,y
ldal spritedata+{]1},x
sta: $0006,y
lda #$0D80 ; branch to the prologue (BRA *+15)
sta: $0008,y
ldy _Y_REG ; restore original y-register value and move on
bra next
; This is a transparent word, so just show the dynamic data
transparent
lda #$4800 ; Put the PHA in the third byte
sta: ]2+1,y
lda _OP_CACHE ; Store the LDA dp,x instruction with operand
sta: ]2,y
next
eom

View File

@ -1,88 +0,0 @@
; _TBMaskedSpriteTile
;
; Renders a composited tile with masking to the code field.
_TBMaskedSpriteTile_00
_TBMaskedSpriteTile_0H
sta _X_REG ; Immedately stash the parameters
sty _Y_REG
jsr _TBCopyTileDataToCBuff ; Copy the tile data into the compositing buffer (using correct x-register)
jsr _TBCopyTileMaskToCBuff ; Copy the tile mask into the compositing buffer (using correct x-register)
jsr _TBMergeSpriteDataAndMask ; Overlay the data and mask from the sprite plane into the compositing buffer
jmp _TBMaskedCBuff ; Render the masked tile from the compositing buffer into the code field
;_TBMaskedSpriteTile_0H
; sta _X_REG
; sty _Y_REG
; jsr _TBCopyTileDataToCBuffH
; jsr _TBCopyTileMaskToCBuffH
; jsr _TBMergeSpriteDataAndMask
; jmp _TBMaskedCBuff
_TBMaskedSpriteTile_V0
_TBMaskedSpriteTile_VH
sta _X_REG
sty _Y_REG
jsr _TBCopyTileDataToCBuffV
jsr _TBCopyTileMaskToCBuffV
jsr _TBMergeSpriteDataAndMask
jmp _TBMaskedCBuff
;_TBMaskedSpriteTile_VH
; sta _X_REG
; sty _Y_REG
; jsr _TBCopyTileDataToCBuffVH
; jsr _TBCopyTileMaskToCBuffVH
; jsr _TBMergeSpriteDataAndMask
; jmp _TBMaskedCBuff
_TBMergeSpriteDataAndMask
ldx _SPR_X_REG ; set to the unaligned tile block address in the sprite plane
]line equ 0
lup 8
lda blttmp+{]line*4}
andl spritemask+{]line*SPRITE_PLANE_SPAN},x
oral spritedata+{]line*SPRITE_PLANE_SPAN},x
sta blttmp+{]line*4}
ldal spritemask+{]line*SPRITE_PLANE_SPAN},x
and blttmp+{]line*4}+32
sta blttmp+{]line*4}+32
lda blttmp+{]line*4}+2
andl spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
oral spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
sta blttmp+{]line*4}+2
ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
and blttmp+{]line*4}+32+2
sta blttmp+{]line*4}+32+2
]line equ ]line+1
--^
rts
; See the Tiles00010.s blitter for additional details
_TBMaskedCBuff
CopyMaskedWordD blttmp+0;$0003
CopyMaskedWordD blttmp+4;$1003
CopyMaskedWordD blttmp+8;$2003
CopyMaskedWordD blttmp+12;$3003
CopyMaskedWordD blttmp+16;$4003
CopyMaskedWordD blttmp+20;$5003
CopyMaskedWordD blttmp+24;$6003
CopyMaskedWordD blttmp+28;$7003
inc _X_REG
inc _X_REG
CopyMaskedWordD blttmp+2;$0000
CopyMaskedWordD blttmp+6;$1000
CopyMaskedWordD blttmp+10;$2000
CopyMaskedWordD blttmp+14;$3000
CopyMaskedWordD blttmp+18;$4000
CopyMaskedWordD blttmp+22;$5000
CopyMaskedWordD blttmp+26;$6000
CopyMaskedWordD blttmp+30;$7000
rts

View File

@ -1,134 +0,0 @@
; _TBDynamicMaskedSpriteTile
;
; This tile type does not explicitly support horizontal or vertical flipping. An appropriate tile
; descriptor should be passed into CopyTileToDyn to put the horizontally or vertically flipped source
; data into the dynamic tile buffer
;
; When rendering, the background, via lda (dp),y, is shown behind the animate sprite
_TBDynamicMaskedSpriteTile_00
sty _Y_REG ; This is restored in the macro
sta _X_REG ; Cache some column values derived from _X_REG
tax
ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand
xba
sta _OP_CACHE
clc
ldal JTableOffset,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _TILE_ID ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$3580 ; Pre-calc the AND $80,x opcode + operand
xba
sta _T_PTR ; This is an op to load the dynamic tile data
ldx _SPR_X_REG
CopyDynMaskedSpriteWord {0*SPRITE_PLANE_SPAN};$0003
CopyDynMaskedSpriteWord {1*SPRITE_PLANE_SPAN};$1003
CopyDynMaskedSpriteWord {2*SPRITE_PLANE_SPAN};$2003
CopyDynMaskedSpriteWord {3*SPRITE_PLANE_SPAN};$3003
CopyDynMaskedSpriteWord {4*SPRITE_PLANE_SPAN};$4003
CopyDynMaskedSpriteWord {5*SPRITE_PLANE_SPAN};$5003
CopyDynMaskedSpriteWord {6*SPRITE_PLANE_SPAN};$6003
CopyDynMaskedSpriteWord {7*SPRITE_PLANE_SPAN};$7003
ldx _X_REG
clc
ldal JTableOffset+2,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _OP_CACHE
adc #$0200
sta _OP_CACHE
lda _T_PTR
adc #$0200
sta _T_PTR
ldx _SPR_X_REG
CopyDynMaskedSpriteWord {0*SPRITE_PLANE_SPAN}+2;$0000
CopyDynMaskedSpriteWord {1*SPRITE_PLANE_SPAN}+2;$1000
CopyDynMaskedSpriteWord {2*SPRITE_PLANE_SPAN}+2;$2000
CopyDynMaskedSpriteWord {3*SPRITE_PLANE_SPAN}+2;$3000
CopyDynMaskedSpriteWord {4*SPRITE_PLANE_SPAN}+2;$4000
CopyDynMaskedSpriteWord {5*SPRITE_PLANE_SPAN}+2;$5000
CopyDynMaskedSpriteWord {6*SPRITE_PLANE_SPAN}+2;$6000
CopyDynMaskedSpriteWord {7*SPRITE_PLANE_SPAN}+2;$7000
rts
; Masked renderer for a masked dynamic tile with sprite data overlaid.
;
; ]1 : sprite plane offset
; ]2 : code field offset
CopyDynMaskedSpriteWord MAC
; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where
; the data and mask from from the sprite plane
;
; lda ($00),y
; and $80,x
; ora $00,x
; and #MASK
; ora #DATA
; bra *+15
;
; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile
; code and eliminate the constanct AND/ORA instructions.
ldal spritemask+{]1},x ; load the mask value
bne mixed ; a non-zero value may be mixed
; This is a solid word
lda #$00F4 ; PEA instruction
sta: ]2,y
ldal spritedata+{]1},x ; load the sprite data
sta: ]2+1,y ; PEA operand
bra next
; We will always do a JMP to the eception handler, so set that up, then check for sprite
; transparency
mixed
lda #$004C ; JMP to handler
sta: ]2,y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$F000} ; adjust for the current row offset
sta: {]2}+1,y
tay ; This becomes the new address that we use to patch in
lda _OP_CACHE
sta: $0000,y ; LDA (00),y
lda _T_PTR
sta: $0002,y ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,y ; ORA $00,x
lda #$0029 ; AND #SPRITE_MASK
sta: $0006,y
ldal spritemask+{]1},x
cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent ; so we can use the Tile00011 method
sta: $0007,y
lda #$0009 ; ORA #SPRITE_DATA
sta: $0009,y
ldal spritedata+{]1},x
sta: $000A,y
lda #$0980 ; branch to the prologue (BRA *+11)
sta: $000C,y
bra next
; This is a transparent word, so just show the dynamic data
transparent
lda #$0F80 ; branch to the epilogue (BRA *+17)
sta: $0006,y
next
ldy _Y_REG ; restore original y-register value and move on
eom

View File

@ -1,50 +0,0 @@
; _TBSolidPrioritySpriteTile
;
; When the sprite is composited with the tile data, the tile mask is used to place the tile data on top of
; any sprite data
_TBSolidPrioritySpriteTile_00
_TBSolidPrioritySpriteTile_0H
jsr _TBCopyTileDataToCBuff ; Copy the tile data into the compositing buffer (using correct x-register)
jsr _TBCopyTileMaskToCBuff ; Copy the tile mask into the compositing buffer (using correct x-register)
jsr _TBApplyPrioritySpriteData ; Underlay the data fromthe sprite plane (and copy into the code field)
jmp _TBFillPEAOpcode ; Fill in the code field opcodes
;_TBSolidPrioritySpriteTile_0H
; jsr _TBCopyTileDataToCBuffH
; jsr _TBCopyTileMaskToCBuffH
; jsr _TBApplyPrioritySpriteData
; jmp _TBFillPEAOpcode
_TBSolidPrioritySpriteTile_V0
_TBSolidPrioritySpriteTile_VH
jsr _TBCopyTileDataToCBuffV
jsr _TBCopyTileMaskToCBuffV
jsr _TBApplyPrioritySpriteData
jmp _TBFillPEAOpcode
;_TBSolidPrioritySpriteTile_VH
; jsr _TBCopyTileDataToCBuffVH
; jsr _TBCopyTileMaskToCBuffVH
; jsr _TBApplyPrioritySpriteData
; jmp _TBFillPEAOpcode
; Need to update the X-register before calling this
_TBApplyPrioritySpriteData
ldx _SPR_X_REG ; set to the unaligned tile block address in the sprite plane
]line equ 0
lup 8
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
and blttmp+{]line*4}+32
ora blttmp+{]line*4}
sta: $0004+{]line*$1000},y
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
and blttmp+{]line*4}+32+2
ora blttmp+{]line*4}+2
sta: $0001+{]line*$1000},y
]line equ ]line+1
--^
ldx _X_REG ; restore the original value
rts

View File

@ -1,92 +0,0 @@
; _TBDynamicPrioritySpriteTile
;
; Variant of _TBDynamicSpriteTile (Tile10001), but draw the sprite data behind the dynamic tile
_TBDynamicPrioritySpriteTile_00
jsr _TBDynamicPriorityDataAndMask
jmp _TBFillJMPOpcode
_TBDynamicPriorityDataAndMask
sty _Y_REG ; This is restored in the macro
sta _X_REG ; Cache some column values derived from _X_REG
tax
clc
ldal JTableOffset,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _TILE_ID ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$3580 ; Pre-calc the AND $80,x opcode + operand
xba
sta _OP_CACHE ; This is an op to load the dynamic tile data
ldx _SPR_X_REG
CopyDynPriSpriteWord {0*SPRITE_PLANE_SPAN};$0003
CopyDynPriSpriteWord {1*SPRITE_PLANE_SPAN};$1003
CopyDynPriSpriteWord {2*SPRITE_PLANE_SPAN};$2003
CopyDynPriSpriteWord {3*SPRITE_PLANE_SPAN};$3003
CopyDynPriSpriteWord {4*SPRITE_PLANE_SPAN};$4003
CopyDynPriSpriteWord {5*SPRITE_PLANE_SPAN};$5003
CopyDynPriSpriteWord {6*SPRITE_PLANE_SPAN};$6003
CopyDynPriSpriteWord {7*SPRITE_PLANE_SPAN};$7003
ldx _X_REG
clc
ldal JTableOffset+2,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _OP_CACHE
adc #$0200
sta _OP_CACHE
ldx _SPR_X_REG
CopyDynPriSpriteWord {0*SPRITE_PLANE_SPAN}+2;$0000
CopyDynPriSpriteWord {1*SPRITE_PLANE_SPAN}+2;$1000
CopyDynPriSpriteWord {2*SPRITE_PLANE_SPAN}+2;$2000
CopyDynPriSpriteWord {3*SPRITE_PLANE_SPAN}+2;$3000
CopyDynPriSpriteWord {4*SPRITE_PLANE_SPAN}+2;$4000
CopyDynPriSpriteWord {5*SPRITE_PLANE_SPAN}+2;$5000
CopyDynPriSpriteWord {6*SPRITE_PLANE_SPAN}+2;$6000
CopyDynPriSpriteWord {7*SPRITE_PLANE_SPAN}+2;$7000
rts
; Masked renderer for a dynamic tile with sprite data overlaid.
;
; ]1 : sprite plane offset
; ]2 : code field offset
CopyDynPriSpriteWord MAC
; Need to fill in the first 9 bytes of the JMP handler with the following code sequence where
; the data and mask from from the sprite plane
;
; lda #DATA
; and $80,x
; ora $00,x
; bra *+16
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$F000} ; adjust for the current row offset
sta: ]2+1,y
tay ; This becomes the new address that we use to patch in
lda #$00A9 ; LDA #DATA
sta: $0000,y
ldal spritedata+{]1},x
sta: $0001,y
lda _OP_CACHE
sta: $0003,y ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0005,y ; ORA $00,x
lda #$0E80 ; branch to the prologue (BRA *+16)
sta: $0007,y
ldy _Y_REG ; restore original y-register value and move on
eom

View File

@ -1,63 +0,0 @@
; _TBMaskedPrioritySpriteTile
;
; Renders a composited tile with masking to the code field. The sprite is underlaid
_TBMaskedPrioritySpriteTile_00
_TBMaskedPrioritySpriteTile_0H
sta _X_REG ; Immedately stash the parameters
sty _Y_REG
jsr _TBCopyTileDataToCBuff ; Copy the tile data into the compositing buffer (using correct x-register)
jsr _TBCopyTileMaskToCBuff ; Copy the tile mask into the compositing buffer (using correct x-register)
jsr _TBUnderlaySpriteDataAndMask ; Underlay the data and mask from the sprite plane into the compositing buffer
jmp _TBMaskedCBuff ; Render the masked tile from the compositing buffer into the code field
;_TBMaskedPrioritySpriteTile_0H
; sta _X_REG
; sty _Y_REG
; jsr _TBCopyTileDataToCBuffH
; jsr _TBCopyTileMaskToCBuffH
; jsr _TBUnderlaySpriteDataAndMask
; jmp _TBMaskedCBuff
_TBMaskedPrioritySpriteTile_V0
_TBMaskedPrioritySpriteTile_VH
sta _X_REG
sty _Y_REG
jsr _TBCopyTileDataToCBuffV
jsr _TBCopyTileMaskToCBuffV
jsr _TBUnderlaySpriteDataAndMask
jmp _TBMaskedCBuff
;_TBMaskedPrioritySpriteTile_VH
; sta _X_REG
; sty _Y_REG
; jsr _TBCopyTileDataToCBuffVH
; jsr _TBCopyTileMaskToCBuffVH
; jsr _TBUnderlaySpriteDataAndMask
; jmp _TBMaskedCBuff
_TBUnderlaySpriteDataAndMask
ldx _SPR_X_REG ; set to the unaligned tile block address in the sprite plane
]line equ 0
lup 8
ldal spritedata+{]line*SPRITE_PLANE_SPAN},x
and blttmp+{]line*4}+32
ora blttmp+{]line*4} ; Maybe this can be a TSB???
sta blttmp+{]line*4}
ldal spritemask+{]line*SPRITE_PLANE_SPAN},x
and blttmp+{]line*4}+32
sta blttmp+{]line*4}+32
ldal spritedata+{]line*SPRITE_PLANE_SPAN}+2,x
and blttmp+{]line*4}+32+2
ora blttmp+{]line*4}+2
sta blttmp+{]line*4}+2
ldal spritemask+{]line*SPRITE_PLANE_SPAN}+2,x
and blttmp+{]line*4}+32+2
sta blttmp+{]line*4}+32+2
]line equ ]line+1
--^
rts

View File

@ -1,123 +0,0 @@
; _TBDynamicMaskedPrioritySpriteTile
;
; This tile type does not explicitly support horizontal or vertical flipping. An appropriate tile
; descriptor should be passed into CopyTileToDyn to put the horizontally or vertically flipped source
; data into the dynamic tile buffer
_TBDynamicMaskedPrioritySpriteTile_00
sty _Y_REG ; This is restored in the macro
sta _X_REG ; Cache some column values derived from _X_REG
tax
ora #$B100 ; Pre-calc the LDA (dp),y opcode + operand
xba
sta _OP_CACHE
clc
ldal JTableOffset,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _TILE_ID ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
ora #$3580 ; Pre-calc the AND $80,x opcode + operand
xba
sta _T_PTR ; This is an op to load the dynamic tile data
ldx _SPR_X_REG
CopyDynPrioMaskedSpriteWord {0*SPRITE_PLANE_SPAN};$0003
CopyDynPrioMaskedSpriteWord {1*SPRITE_PLANE_SPAN};$1003
CopyDynPrioMaskedSpriteWord {2*SPRITE_PLANE_SPAN};$2003
CopyDynPrioMaskedSpriteWord {3*SPRITE_PLANE_SPAN};$3003
CopyDynPrioMaskedSpriteWord {4*SPRITE_PLANE_SPAN};$4003
CopyDynPrioMaskedSpriteWord {5*SPRITE_PLANE_SPAN};$5003
CopyDynPrioMaskedSpriteWord {6*SPRITE_PLANE_SPAN};$6003
CopyDynPrioMaskedSpriteWord {7*SPRITE_PLANE_SPAN};$7003
ldx _X_REG
clc
ldal JTableOffset+2,x ; Get the address offset and add to the base address
adc _BASE_ADDR ; of the current code field line
sta _JTBL_CACHE
lda _OP_CACHE
adc #$0200
sta _OP_CACHE
lda _T_PTR
adc #$0200
sta _T_PTR
ldx _SPR_X_REG
CopyDynPrioMaskedSpriteWord {0*SPRITE_PLANE_SPAN}+2;$0000
CopyDynPrioMaskedSpriteWord {1*SPRITE_PLANE_SPAN}+2;$1000
CopyDynPrioMaskedSpriteWord {2*SPRITE_PLANE_SPAN}+2;$2000
CopyDynPrioMaskedSpriteWord {3*SPRITE_PLANE_SPAN}+2;$3000
CopyDynPrioMaskedSpriteWord {4*SPRITE_PLANE_SPAN}+2;$4000
CopyDynPrioMaskedSpriteWord {5*SPRITE_PLANE_SPAN}+2;$5000
CopyDynPrioMaskedSpriteWord {6*SPRITE_PLANE_SPAN}+2;$6000
CopyDynPrioMaskedSpriteWord {7*SPRITE_PLANE_SPAN}+2;$7000
rts
; Masked renderer for a masked dynamic tile with sprite data underlaid.
;
; ]1 : sprite plane offset
; ]2 : code field offset
CopyDynPrioMaskedSpriteWord MAC
; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where
; the data and mask from from the sprite plane
;
; lda ($00),y
; and #MASK
; ora #DATA
; and $80,x
; ora $00,x
; bra *+15
lda #$004C ; JMP to handler
sta: ]2,y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$F000} ; adjust for the current row offset
sta: ]2+1,y
tay ; This becomes the new address that we use to patch in
lda _OP_CACHE
sta: $0000,y ; LDA (00),y
lda #$0029 ; AND #SPRITE_MASK
sta: $0002,y
ldal spritemask+{]1},x
cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent ; so we can use the Tile00011 method
sta: $0003,y
lda #$0009 ; ORA #SPRITE_DATA
sta: $0005,y
ldal spritedata+{]1},x
sta: $0006,y
lda _T_PTR
sta: $0008,y ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $000A,y ; ORA $00,x
lda #$0980 ; branch to the prologue (BRA *+11)
sta: $000C,y
bra next
; This is a transparent word, so just show the dynamic data
transparent
lda _T_PTR
sta: $0002,y ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,y ; ORA $00,x
lda #$0F80 ; branch to the epilogue (BRA *+17)
sta: $0006,y
next
ldy _Y_REG ; restore original y-register value and move on
eom

View File

@ -5,31 +5,38 @@
; Based on the current value of StartY in the direct page. Set up the dispatch
; information so that the BltRange driver will render the correct code field
; lines in the correct order
_ApplyBG0YPos
_ApplyBG0YPosOld
:rtbl_idx equ tmp0
:virt_line equ tmp1
:lines_left equ tmp2
:draw_count equ tmp3
:rtbl_idx_x2 equ tmp0
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:stk_save equ tmp4
; First task is to fill in the STK_ADDR values by copying them from the RTable array. We
; copy from RTable[i] into BlitField[StartY+i]. As with all of this code, the difficult part
; is decomposing the update across banks
stz :rtbl_idx ; Start copying from the first entry in the table
stz :rtbl_idx_x2 ; Start copying from the first entry in the table
lda StartY ; This is the base line of the virtual screen
jsr Mod208
sta StartYMod208
sta :virt_line ; Keep track of it
asl
sta :virt_line_x2 ; Keep track of it
phb ; Save the current bank
tsc ; we intentionally leak one byte of stack in each loop
sta :stk_save ; iteration, so save the stack to repair at the end
; copy a range of address from the table into the destination bank. If we restrict ourselves to
; rectangular playfields, this can be optimized to just subtracting a constant value. See the
; Templates::SetScreenAddrs subroutine.
lda ScreenHeight
sta :lines_left
asl
sta :lines_left_x2
; This is the verbose part -- figure out how many lines to draw. We don't want to artificially limit
; the height of the visible screen (for example, doing an animated wipe while scrolling), so the screen
@ -38,137 +45,280 @@ _ApplyBG0YPos
; For larger values, we want to break things up on 16-line boundaries based on the virt_line value. So,
;
; draw_count = min(lines_left, (16 - (virt_line % 16))
;
; Note that almost everything in this loop can be done with 8-bit operations sincc the values are
; all under 200. The one exception is the virt_line value which could exceed 256. This will be
; a later optimization and might save around 10 cycles per iteration, or up to ~120 cycles per frame
; and ~2,500 per secord. This is ~1% of our total CPU budget and is *just* enough cycles to be
; interesting.... Another 8 cycles could be removed by doing all calculatinos pre-multiplied by 2
; to avoid several 'asl' instructions
phb
:loop
lda :virt_line
asl
tax
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
tay
sep #$20
ldal BTableHigh,x
ldal BTableHigh,x ; Target bank in low byte
pha
plb ; This is the bank that will receive the updates
rep #$20
lda :virt_line
and #$000F
txa
and #$001E
eor #$FFFF
inc
clc
adc #16
min :lines_left
sec
adc #32
min :lines_left_x2
sta :draw_count ; Do this many lines
asl
sta :draw_count_x2 ; Do this many lines
tax
lda :rtbl_idx ; Read from this location in the RTable
asl
clc ; pre-advance virt_line_2 because we have the value
adc :virt_line_x2
sta :virt_line_x2
jsr CopyRTableToStkAddr
plb
jsr _CopyRTableToStkAddr
; CopyRTableToStkAddr :rtbl_idx_x2 ; X = rtbl_idx_x2 on return
lda :virt_line ; advance to the virtual line after the segment we just
clc ; filled in
adc :draw_count
sta :virt_line
txa ; carry flag is unchanged
adc :draw_count_x2 ; advance the index into the RTable
sta :rtbl_idx_x2
lda :rtbl_idx ; advance the index into the RTable
adc :draw_count
sta :rtbl_idx
lda :lines_left ; subtract the number of lines we just completed
lda :lines_left_x2 ; subtract the number of lines we just completed
sec
sbc :draw_count
sta :lines_left
sbc :draw_count_x2
sta :lines_left_x2
jne :loop
plb
:out
lda :stk_save
tcs
plb
rts
; This is an optimized version of _ApplyBG0YPos. We pre-compute the breakdown across the bank
; boundries in order to eliminate the the minimum calculation and some loop variable updates
; from the inner loop.
_ApplyBG0YPos
:rtbl_idx_x2 equ tmp0
:virt_line_x2 equ tmp1
:lines_left_x2 equ tmp2
:draw_count_x2 equ tmp3
:stk_save equ tmp4
:line_count equ tmp5
; First task is to fill in the STK_ADDR values by copying them from the RTable array. We
; copy from RTable[i] into BlitField[StartY+i]. As with all of this code, the difficult part
; is decomposing the update across banks
stz :rtbl_idx_x2 ; Start copying from the first entry in the table
lda StartY ; This is the base line of the virtual screen
jsr Mod208
sta StartYMod208
asl
sta :virt_line_x2 ; Keep track of it
phb ; Save the current bank
tsc ; we intentionally leak one byte of stack in each loop
sta :stk_save ; iteration, so save the stack to repair at the end
; copy a range of address from the table into the destination bank. If we restrict ourselves to
; rectangular playfields, this can be optimized to just subtracting a constant value. See the
; Templates::SetScreenAddrs subroutine.
lda ScreenHeight
asl
sta :lines_left_x2
; This is the verbose part -- figure out how many lines to draw. We don't want to artificially limit
; the height of the visible screen (for example, doing an animated wipe while scrolling), so the screen
; height could be anything from 1 to 200.
;
; For larger values, we want to break things up on 16-line boundaries based on the virt_line value. So,
;
; draw_count = min(lines_left, (16 - (virt_line % 16))
; Pre-loop: Calculate the number of lines to copy to get the loop into a bank-aligned state
;
; lines_in_bank = 16 - (virt_line % 16)
:pre
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
tay
ldal BTableHigh,x ; Target bank in low byte
pha
txa
and #$001E
eor #$FFFF
sec
adc #32
min :lines_left_x2
sta :draw_count_x2 ; Do this many lines
tax
clc ; pre-advance virt_line_2 because we have the value
adc :virt_line_x2
sta :virt_line_x2
plb
jsr _CopyRTableToStkAddr
txa ; carry flag is unchanged
adc :draw_count_x2 ; advance the index into the RTable
sta :rtbl_idx_x2
lda :lines_left_x2 ; subtract the number of lines we just completed
sec
sbc :draw_count_x2
sta :lines_left_x2
jeq :done ; if there are no lines left, we're done!
cmp #33
jcc :post ; if there are 16 lines or less left, jump to post
; Now we are in the main loop. We know that the virt_line is a multiple of 16, but the number
; of remaining lines could be any number greater than 0. we test to see if the lines_left are
; less than 16. If so, we can jump straight to the post-loop update. Otherwise we caculate
; the number of 16-line iterations and but that in an auxiliary count variable and simplify
; the loop update.
tax
and #$001E ; this is the number of lines in post
sta :lines_left_x2
txa
lsr
lsr
lsr
lsr
lsr
sta :line_count ; single byte count, saves 9 cycles per loop iteration
:loop
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
tay
ldal BTableHigh,x ; Target bank in low byte
pha
lda #32 ; Do this many lines (x2)
tax
clc ; pre-advance virt_line_2 because we have the value
adc :virt_line_x2
sta :virt_line_x2
plb
CopyRTableToStkAddr :rtbl_idx_x2
txa ; carry flag is unchanged
adc #32 ; advance the index into the RTable
sta :rtbl_idx_x2
dec :line_count
jne :loop
lda :lines_left_x2
beq :done
; Draw some number of lines that are less that 16. No need to update loop variabls because we
; know we are in the last iteration
:post
ldx :virt_line_x2
ldal BTableLow,x ; Get the address of the first code field line
tay
ldal BTableHigh,x ; Target bank in low byte
pha
ldx :lines_left_x2 ; Do this many lines
plb
jsr _CopyRTableToStkAddr
:done
lda :stk_save
tcs
plb
rts
; Unrolled copy routine to move RTable intries into STK_ADDR position.
;
; A = intect into the RTable array (x2)
; A = index into the RTable array (x2)
; Y = starting line * $1000
; X = number of lines (x2)
CopyRTableToStkAddr
jmp (:tbl,x)
:tbl da :none
da :do01,:do02,:do03,:do04
da :do05,:do06,:do07,:do08
da :do09,:do10,:do11,:do12
da :do13,:do14,:do15,:do16
:do15 tax
bra :x15
:do14 tax
bra :x14
:do13 tax
bra :x13
:do12 tax
bra :x12
:do11 tax
bra :x11
:do10 tax
bra :x10
:do09 tax
bra :x09
:do08 tax
bra :x08
:do07 tax
bra :x07
:do06 tax
bra :x06
:do05 tax
bra :x05
:do04 tax
bra :x04
:do03 tax
bra :x03
:do02 tax
bra :x02
:do01 tax
bra :x01
:do16 tax
CopyRTableToStkAddr mac
jmp (dispTbl,x)
dispTbl da bottom
da do01,do02,do03,do04
da do05,do06,do07,do08
da do09,do10,do11,do12
da do13,do14,do15,do16
do15 ldx ]1
bra x15
do14 ldx ]1
bra x14
do13 ldx ]1
bra x13
do12 ldx ]1
bra x12
do11 ldx ]1
bra x11
do10 ldx ]1
bra x10
do09 ldx ]1
bra x09
do08 ldx ]1
bra x08
do07 ldx ]1
bra x07
do06 ldx ]1
bra x06
do05 ldx ]1
bra x05
do04 ldx ]1
bra x04
do03 ldx ]1
bra x03
do02 ldx ]1
bra x02
do01 ldx ]1
bra x01
do16 ldx ]1
ldal RTable+30,x
sta STK_ADDR+$F000,y
:x15 ldal RTable+28,x
x15 ldal RTable+28,x
sta STK_ADDR+$E000,y
:x14 ldal RTable+26,x
x14 ldal RTable+26,x
sta STK_ADDR+$D000,y
:x13 ldal RTable+24,x
x13 ldal RTable+24,x
sta STK_ADDR+$C000,y
:x12 ldal RTable+22,x
x12 ldal RTable+22,x
sta STK_ADDR+$B000,y
:x11 ldal RTable+20,x
x11 ldal RTable+20,x
sta STK_ADDR+$A000,y
:x10 ldal RTable+18,x
x10 ldal RTable+18,x
sta STK_ADDR+$9000,y
:x09 ldal RTable+16,x
x09 ldal RTable+16,x
sta STK_ADDR+$8000,y
:x08 ldal RTable+14,x
x08 ldal RTable+14,x
sta STK_ADDR+$7000,y
:x07 ldal RTable+12,x
x07 ldal RTable+12,x
sta STK_ADDR+$6000,y
:x06 ldal RTable+10,x
x06 ldal RTable+10,x
sta STK_ADDR+$5000,y
:x05 ldal RTable+08,x
x05 ldal RTable+08,x
sta STK_ADDR+$4000,y
:x04 ldal RTable+06,x
x04 ldal RTable+06,x
sta STK_ADDR+$3000,y
:x03 ldal RTable+04,x
x03 ldal RTable+04,x
sta STK_ADDR+$2000,y
:x02 ldal RTable+02,x
x02 ldal RTable+02,x
sta STK_ADDR+$1000,y
:x01 ldal RTable+00,x
x01 ldal RTable+00,x
sta: STK_ADDR+$0000,y
:none rts
bottom
<<<
_CopyRTableToStkAddr
CopyRTableToStkAddr tmp0
rts

View File

@ -45,13 +45,14 @@ CopyDynamicTile
; populating the code field and snippet space
DynamicOver
lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_2
sta _JTBL_CACHE
lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor
and #$007F ; clamp to < (32 * 4)
and #$007F ; clamp to < (32 * 4)
ora #$B500
xba
sta _OP_CACHE ; This is the 2-byte opcode for to load the data
sta _OP_CACHE ; This is the 2-byte opcode for to load the data
lda TileStore+TS_CODE_ADDR_HIGH,x
pha
@ -91,6 +92,7 @@ DynamicOver
DynamicUnder
lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_3
sta _JTBL_CACHE
lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor
@ -164,6 +166,7 @@ _DynFillJmpOpcode
CopyDynamicTileTwoLyr
ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_4
sta _JTBL_CACHE
ldal TileStore+TS_WORD_OFFSET,x
@ -215,9 +218,10 @@ CopyDynamicTileTwoLyr
jmp _DynFillJmpOpcode
; Render a sprite on top of a dyamic tile with transparent areas shwing the second background
; Render a sprite on top of a dyamic tile with transparent areas showing the second background
DynamicOverTwoLyr
lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_1
sta _JTBL_CACHE
lda TileStore+TS_WORD_OFFSET,x
@ -275,9 +279,19 @@ DynamicOverTwoLyr
plb
rts
; Render a sprite on top of a dyamic tile with transparent areas shwing the second background
; Render a sprite under a dyamic tile with transparent areas showing the second background
;
; This is a special case where we cannot fit the code into the fixed snippet structure. As such,
; any tile drawn with this routine will set a DAMAGED flag on the TileStore flags. If another
; tile blitter in the TwoLayer function set sees that a tile is marked as DAMAGED, it must
; restore the original code structure before proceeding.
;
; The damages area is not too bad -- just the 10 bytes from [2, 10] are overwritten and must be
; restored. This is actually less work than a lot of the snippet macros were doing before
; applying the fixed snippet optimization.
DynamicUnderTwoLyr
lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_1
sta _JTBL_CACHE
lda TileStore+TS_WORD_OFFSET,x
@ -290,6 +304,8 @@ DynamicUnderTwoLyr
; perform and EOR #$2080 to covert the opcode and operand in one instruction
lda TileStore+TS_TILE_ID,x ; Get the original tile descriptor
ora #TILE_DAMAGED_BIT ; Set the DAMAGED bit here since we have to load TILE_ID anyway
sta TileStore+TS_TILE_ID,x
and #$007F ; clamp to < (32 * 4)
ora #$3580 ; Pre-calc the AND $80,x opcode + operand
xba
@ -368,18 +384,12 @@ mixed cmp #$FFFF ; All 1's in the mask is a fully transparent sp
lda _OP_CACHE ; Get the LDA dp,x instruction for this column
sta: $0000,x
lda #$0029 ; AND #SPRITE_MASK
sta: $0002,x
lda tmp_sprite_mask+{]1}
sta: $0003,x
lda #$0009 ; ORA #SPRITE_DATA
sta: $0005,x
lda tmp_sprite_data+{]1}
sta: $0006,x
lda #$0D80 ; branch to the prologue (BRA *+15)
sta: $0008,x
bra next
; This is a transparent word, so just show the dynamic data
@ -410,10 +420,8 @@ CopyDynUnder MAC
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$7000} ; adjust for the current row offset
sta: {]2}+1,y
tax ; This becomes the new address that we use to patch in
tax ; This becomes the new address that we use to patch in
lda #$00A9 ; LDA #DATA
sta: $0000,x
lda tmp_sprite_data+{]1}
sta: $0001,x
@ -421,9 +429,6 @@ CopyDynUnder MAC
sta: $0003,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0005,x ; ORA $00,x
lda #$0E80 ; branch to the prologue (BRA *+16)
sta: $0007,x
eom
; Masked renderer for a dynamic tile. What's interesting about this renderer is that the mask
@ -449,37 +454,31 @@ CopyMaskedDWord MAC
lda _JTBL_CACHE
ora #{{]1}&$7000} ; adjust for the current row offset
sta: {]1}+1,y
tax ; This becomes the new address that we use to patch in
lda _OP_CACHE
sta: $0000,x ; LDA (00),y
lda _OP_CACHE2
sta: $0002,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,x ; ORA $00,x
lda #$0F80 ; branch to the prologue (BRA *+17)
sta: $0006,x
eom
; Masked renderer for a masked dynamic tile with sprite data overlaid.
; Version 2 will set the JMP to Entry Point 1 and set the Opcode at Entry Point 2 to a ora $00,x. Also
; the mask transparency check can be performed earlier.
;
; ]1 : sprite plane offset
; ]2 : code field offset
; lda #$004C ; JMP to handler
; sta: {]2},y
; lda _JTBL_CACHE ; Get the offset to the exception handler for this column
; ora #{]2&$7000} ; adjust for the current row offset
; sta: {]2}+1,y
; tax ; This becomes the new address that we use to patch in
; lda OP_CACHE_2 ; switch from AND to ORA instruction cached in setup
; sta: $0004,x ; ORA $00,x
CopyDynMaskedSpriteWord MAC
; Need to fill in the first 14 bytes of the JMP handler with the following code sequence where
; the data and mask from from the sprite plane
;
; lda ($00),y
; and $80,x
; ora $00,x
; and #MASK
; ora #DATA
; bra *+15
;
; If MASK == 0, then we can do a PEA. If MASK == $FFFF, then fall back to the simple Dynamic Tile
; code and eliminate the constanct AND/ORA instructions.
; code and eliminate the constant AND/ORA instructions.
lda tmp_sprite_mask+{]1} ; load the mask value
bne mixed ; a non-zero value may be mixed
@ -491,9 +490,12 @@ CopyDynMaskedSpriteWord MAC
sta: {]2}+1,y ; PEA operand
bra next
; We will always do a JMP to the exception handler, so set that up, then check for sprite
; transparency
; We will always do a JMP to the exception handler, but the entry point changes depending on
; whether the mask is transparent or not
mixed
cmp #$FFFF
beq transparent
lda #$004C ; JMP to handler
sta: {]2},y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
@ -501,37 +503,37 @@ mixed
sta: {]2}+1,y
tax ; This becomes the new address that we use to patch in
lda _OP_CACHE2
sta: $0002,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,x ; ORA $00,x
lda tmp_sprite_mask+{]1}
sta: $0007,x
lda tmp_sprite_data+{]1}
sta: $000A,x
bra next
; This is a transparent word, so just show the dynamic data overlaid on layer 2
transparent
lda #$004C ; JMP to handler
sta: {]2},y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$7000}.SNIPPET_ENTRY_4 ; adjust for the current row offset and OR in the offset since snippets are 32-byte aligned
sta: {]2}+1,y
tax
lda _OP_CACHE
sta: $0000,x ; LDA (00),y
lda _OP_CACHE2
sta: $0002,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,x ; ORA $00,x
lda #$0029 ; AND #SPRITE_MASK
sta: $0006,x
lda tmp_sprite_mask+{]1}
cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent
sta: $0007,x
lda #$0009 ; ORA #SPRITE_DATA
sta: $0009,x
lda tmp_sprite_data+{]1}
sta: $000A,x
lda #$0980 ; branch to the prologue (BRA *+11)
sta: $000C,x
bra next
; This is a transparent word, so just show the dynamic data
transparent
lda #$0F80 ; branch to the epilogue (BRA *+17)
sta: $0006,x
next
eom
; Masked renderer for a masked dynamic tile with sprite data underlaid.
;
; ]1 : sprite plane offset
@ -546,7 +548,12 @@ CopyDynPrioMaskedSpriteWord MAC
; ora #DATA
; and $80,x
; ora $00,x
; bra *+15
; This macro has different targets based on the transparency
lda tmp_sprite_mask+{]1}
cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent
lda #$004C ; JMP to handler
sta: {]2},y
@ -555,15 +562,10 @@ CopyDynPrioMaskedSpriteWord MAC
sta: {]2}+1,y
tax ; This becomes the new address that we use to patch in
lda _OP_CACHE
sta: $0000,x ; LDA (00),y
lda #$0029 ; AND #SPRITE_MASK
sta: $0002,x
lda tmp_sprite_mask+{]1}
cmp #$FFFF ; All 1's in the mask is a fully transparent sprite word
beq transparent ; so we can use the Tile00011 method
sta: $0003,x
lda #$0009 ; ORA #SPRITE_DATA
@ -576,19 +578,22 @@ CopyDynPrioMaskedSpriteWord MAC
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $000A,x ; ORA $00,x
lda #$0980 ; branch to the prologue (BRA *+11)
sta: $000C,x
bra next
; This is a transparent word, so just show the dynamic data
transparent
lda _OP_CACHE2
sta: $0002,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,x ; ORA $00,x
; This is a transparent word, so just show the dynamic data
lda #$004C ; JMP to handler
sta: {]2},y
lda _JTBL_CACHE ; Get the offset to the exception handler for this column
ora #{]2&$7000}.SNIPPET_ENTRY_4 ; adjust for the current row offset and OR in the offset since snippets are 32-byte aligned
sta: {]2}+1,y
tax
lda #$0F80 ; branch to the epilogue (BRA *+17)
sta: $0006,x
lda _OP_CACHE
sta: $0000,x ; LDA (00),y
lda _OP_CACHE2
sta: $0002,x ; AND $80,x
eor #$8020 ; Switch the opcode to an ORA and remove the high bit of the operand
sta: $0004,x ; ORA $00,x
next
eom

View File

@ -7,8 +7,8 @@
; GenericUnderSlow : Places the TileStore's tile on top of tmp_sprite_data
ConstTile0Slow
jsr FillPEAOpcode
jmp ConstTile0Fast
jsr FillPEAOpcode ; Could these be slightly faster to do PEA ConstTile0Fast-1 ; JMP FillPEAOpcode?
jmp ConstTile0Fast ; Currently it's 6 + 6 + 3 + 6, improved would be 5 + 3 + 6 + 6 = 1 cycle saved. :(
SpriteOverASlow
lda TileStore+TS_CODE_ADDR_HIGH,x ; load the bank of the target code field line
@ -79,12 +79,6 @@ FillPEAOpcode
rep #$20
rts
; This is a stub; will be removed eventually
_FillPEAOpcode
jsr FillPEAOpcode
plb ; Restore the TileStore bank
rts
CopyTileASlow
tax
jsr FillPEAOpcode

View File

@ -50,6 +50,7 @@ Tile0TwoLyr
SpriteOver0TwoLyr
lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_2 ; Offset into the second entry point
sta _JTBL_CACHE
lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2)
@ -95,6 +96,7 @@ SpriteOver0TwoLyr
TmpTileDataToCodeField
lda TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_2
sta _JTBL_CACHE
lda TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2)
@ -143,6 +145,7 @@ _TmpTileDataToCodeField
; Copy a tile into the tile data buffer and then render to the code field
CopyTileATwoLyr
ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_2
sta _JTBL_CACHE
ldal TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2)
@ -170,6 +173,7 @@ CopyTileATwoLyr
CopyTileVTwoLyr
ldal TileStore+TS_JMP_ADDR,x ; Get the address of the exception handler
ora #SNIPPET_ENTRY_2
sta _JTBL_CACHE
ldal TileStore+TS_WORD_OFFSET,x ; Load the word offset of this tile (0 to 82 in steps of 2)
@ -348,22 +352,14 @@ mixed cmp #$FFFF ; All 1's in the mask is fully transparent
sta: {]2}+1,y
tax ; This becomes the new address that we use to patch in
lda #$29
sta: $0002,x ; AND #$0000 opcode
lda #$09
sta: $0005,x ; ORA #$0000 opcode
lda _OP_CACHE ; Get the LDA (dp),y instruction for this column
sta: $0000,x
lda {]1}+32 ; insert the tile mask and data into the exception
sta: $0003,x ; handler.
lda {]1}+32 ; insert the tile mask and data into the exception
sta: $0003,x ; handler.
lda {]1}
sta: $0006,x
lda #$0D80 ; branch to the prologue (BRA *+15)
sta: $0008,x
bra next
; This is a transparent word, so just show the second background layer

View File

@ -27,7 +27,7 @@ DirtyTiles ENT
ds \,$00 ; pad to the next page boundary
_Sprites ENT
ds SPRITE_REC_SIZE*MAX_SPRITES
ds SPRITE_REC_SIZE*MAX_ELEMENTS
;-------------------------------------------------------------------------------------
;
@ -108,6 +108,10 @@ TileStoreLookup ENT
Col2CodeOffset ENT
lup 82
dw CODE_TOP+{{81-]step}*PER_TILE_SIZE}
]step equ ]step+1
--^
lup 82 ; Make is a double-length table so we can add the ScreenWidth without testing for wrap-around
dw CODE_TOP+{{81-]step}*PER_TILE_SIZE}
]step equ ]step+1
--^
dw CODE_TOP+{81*PER_TILE_SIZE}
@ -129,6 +133,7 @@ JTableOffset ENT
;
; These tables are reversed to be parallel with the JTableOffset and Col2CodeOffset tables above. The
; physical word index that each instruction is intended to be placed at is in the comment.
bra *-3 ; wrap around
CodeFieldEvenBRA ENT
bra *+6 ; 81 -- need to skip over the JMP loop that passed control back
bra *+9 ; 80
@ -213,6 +218,7 @@ CodeFieldEvenBRA ENT
bra *-6 ; 1
bra *-3 ; 0
bra *-6 ; wrap around
CodeFieldOddBRA ENT
bra *+9 ; 81 -- need to skip over two JMP instructions
bra *+12 ; 80
@ -382,6 +388,17 @@ BG1YOffsetTable ENT
; dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
--^
; Pointer to per-scanline offsets for BG0
StartXMod164Tbl ENT
dw 0,0
LastOffsetTbl ENT
ds 416
; Pointer to per-scanline offsets for BG1
BG1StartXMod164Tbl ENT
dw 0,0
; Other Toolset variables
OneSecondCounter ENT
dw 0
@ -389,9 +406,10 @@ OldOneSecVec ENT
ds 4
Timers ENT
ds TIMER_REC_SIZE*MAX_TIMERS
Overlays ENT
dw 0 ; count
ds 8 ; only support one or now (start_line, end_line, function call)
;Overlays ENT
; dw 0 ; count
; ds 10 ; only support one for now (flags, start_line, end_line, function call)
; From the IIgs ref
DefaultPalette ENT
@ -503,6 +521,29 @@ _SpriteBits ENT
_SpriteBitsNot ENT
dw $FFFE,$FFFD,$FFFB,$FFF7,$FFEF,$FFDF,$FFBF,$FF7F,$FEFF,$FDFF,$FBFF,$F7FF,$EFFF,$DFFF,$BFFF,$7FFF
; Doubly linked list that allows the sprites to be traversed in SPRITE_CLIP_TOP order. The prev/next
; index links are stored in the parallel _Sprites structure; just the extra head and tail index values
; are stored here. A negative value is used as a setinel
_SortedHead ENT
dw $FFFF
; Array of screen ranges covered by the sprites. Adjacent sprites are merged. Used in the shadowing renderer
_ShadowListCount ENT
ds 2
_ShadowListTop ENT
ds {2*{MAX_ELEMENTS}} ; space for all of the sprites + overlay range
_ShadowListBottom ENT
ds {2*{MAX_ELEMENTS}}
; Complement of the Shadow List. Can have one more segment than that list
_DirectListCount ENT
ds 2
_DirectListTop ENT
ds {2*{MAX_ELEMENTS+1}}
_DirectListBottom ENT
ds {2*{MAX_ELEMENTS+1}}
; Steps to the different sprite stamps
_stamp_step ENT
dw 0,12,24,36
@ -541,4 +582,9 @@ Handles ENT
ds 4*32
blt_return
stk_save
stk_save
;StartXMod164Arr ENT
; ds 416*2
;LastPatchOffsetArr ENT
; ds 416*2

View File

@ -12,7 +12,6 @@ TS_TILE_ADDR equ {TILE_STORE_SIZE*3} ; cached value, the address
TS_CODE_ADDR_LOW equ {TILE_STORE_SIZE*4} ; const value, address of this tile in the code fields
TS_CODE_ADDR_HIGH equ {TILE_STORE_SIZE*5}
TS_WORD_OFFSET equ {TILE_STORE_SIZE*6} ; const value, word offset value for this tile if LDA (dp),y instructions re used
;TS_BASE_ADDR equ {TILE_STORE_SIZE*7} ; const value, because there are two rows of tiles per bank, this is set to $0000 or $8000.
TS_JMP_ADDR equ {TILE_STORE_SIZE*7} ; const value, address of the 32-byte snippet space for this tile
TS_SCREEN_ADDR equ {TILE_STORE_SIZE*8} ; cached value of on-screen location of tile. Used for DirtyRender.
@ -29,6 +28,9 @@ TILE_STORE_NUM equ 12 ; Need this many parallel arra
MAX_SPRITES equ 16
SPRITE_REC_SIZE equ 42
MAX_OVERLAYS equ 2
MAX_ELEMENTS equ {MAX_SPRITES+MAX_OVERLAYS}
; Mark each sprite as ADDED, UPDATED, MOVED, REMOVED depending on the actions applied to it
; on this frame. Quick note, the same Sprite ID cannot be removed and added in the same frame.
; A REMOVED sprite if removed from the sprite list during the Render call, so it's ID is not
@ -42,27 +44,39 @@ SPRITE_STATUS_REMOVED equ $0008 ; Sprite has been removed.
SPRITE_STATUS_HIDDEN equ $0010 ; Sprite is in a hidden state
; These values are set by the user
SPRITE_STATUS equ {MAX_SPRITES*0}
SPRITE_ID equ {MAX_SPRITES*2}
SPRITE_X equ {MAX_SPRITES*4}
SPRITE_Y equ {MAX_SPRITES*6}
VBUFF_ADDR equ {MAX_SPRITES*8} ; Base address of the sprite's stamp in the data/mask banks
SPRITE_STATUS equ {MAX_ELEMENTS*0}
SPRITE_ID equ {MAX_ELEMENTS*2}
SPRITE_X equ {MAX_ELEMENTS*4}
SPRITE_Y equ {MAX_ELEMENTS*6}
VBUFF_ADDR equ {MAX_ELEMENTS*8} ; Base address of the sprite's stamp in the data/mask banks
; These values are cached / calculated during the rendering process
TS_LOOKUP_INDEX equ {MAX_SPRITES*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite
TS_COVERAGE_SIZE equ {MAX_SPRITES*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite
SPRITE_DISP equ {MAX_SPRITES*14} ; Cached address of the specific stamp based on sprite flags
SPRITE_CLIP_LEFT equ {MAX_SPRITES*16}
SPRITE_CLIP_RIGHT equ {MAX_SPRITES*18}
SPRITE_CLIP_TOP equ {MAX_SPRITES*20}
SPRITE_CLIP_BOTTOM equ {MAX_SPRITES*22}
IS_OFF_SCREEN equ {MAX_SPRITES*24}
SPRITE_WIDTH equ {MAX_SPRITES*26}
SPRITE_HEIGHT equ {MAX_SPRITES*28}
SPRITE_CLIP_WIDTH equ {MAX_SPRITES*30}
SPRITE_CLIP_HEIGHT equ {MAX_SPRITES*32}
TS_VBUFF_BASE equ {MAX_SPRITES*34} ; Finalized VBUFF address based on the sprite position and tile offsets
VBUFF_ARRAY_ADDR equ {MAX_SPRITES*36} ; Fixed address where this sprite's VBUFF addresses are stores. The array is the same shape as TileStore, but much smaller
TS_LOOKUP_INDEX equ {MAX_ELEMENTS*10} ; The index from the TileStoreLookup table that corresponds to the top-left corner of the sprite
TS_COVERAGE_SIZE equ {MAX_ELEMENTS*12} ; Representation of how many TileStore tiles (NxM) are covered by this sprite
SPRITE_DISP equ {MAX_ELEMENTS*14} ; Cached address of the specific stamp based on sprite flags
SPRITE_CLIP_LEFT equ {MAX_ELEMENTS*16}
SPRITE_CLIP_RIGHT equ {MAX_ELEMENTS*18}
SPRITE_CLIP_TOP equ {MAX_ELEMENTS*20}
SPRITE_CLIP_BOTTOM equ {MAX_ELEMENTS*22}
IS_OFF_SCREEN equ {MAX_ELEMENTS*24}
SPRITE_WIDTH equ {MAX_ELEMENTS*26}
SPRITE_HEIGHT equ {MAX_ELEMENTS*28}
SPRITE_CLIP_WIDTH equ {MAX_ELEMENTS*30}
SPRITE_CLIP_HEIGHT equ {MAX_ELEMENTS*32}
TS_VBUFF_BASE equ {MAX_ELEMENTS*34} ; Finalized VBUFF address based on the sprite position and tile offsets
SORTED_PREV equ {MAX_ELEMENTS*36} ; Doubly-Linked List that maintains the sprites in sorted order based on SPRITE_Y
SORTED_NEXT equ {MAX_ELEMENTS*38}
; The Overlays are part of the _Sprites memory space and come after the maximum number of sprites
Overlays equ {_Sprites+{MAX_SPRITES*2}}
; Aliases of SPRITE_* memory locations that are used for Overlay info when the SPRITE_OVERLAY bit is set on SPRITE_ID
OVERLAY_ID equ SPRITE_ID
OVERLAY_FLAGS equ SPRITE_STATUS
OVERLAY_TOP equ SPRITE_CLIP_TOP ; This is important because SPRITE_CLIP_TOP is used for sorting
OVERLAY_BOTTOM equ SPRITE_CLIP_BOTTOM
OVERLAY_HEIGHT equ SPRITE_HEIGHT
OVERLAY_PROC equ VBUFF_ADDR
; 52 rows by 82 columns + 2 extra rows and columns for sprite sizes
;

View File

@ -146,6 +146,8 @@ function getOptions(argv) {
options.targetPalette = getArg(argv, '--palette', x => x.split(',').map(c => hexStringToPalette(c)), null);
options.forceMatch = getArg(argv, '--force-color-match', x => true, false);
options.forceWordAlignment = getArg(argv, '--force-word-alignment', x => true, false);
options.format = getArg(argv, '--format', x => x, 'asm65816'); // asm65816 or orcac or rez
options.varName = getArg(argv, '--var-name', x => x, 'tiles'); // language-specific label to reference tile data
return options;
}
@ -222,25 +224,60 @@ function getPaletteMap(options, png) {
};
}
function writeComment(options, message, logger=console.log) {
switch (options.format) {
case 'orcac':
logger(`/* ${message} */`);
break;
default:
logger(`; ${message}`);
}
}
function writePaletteArray(options, palette, logger=console.log) {
switch (options.format) {
case 'orcac': {
const hexCodes = palette.map(c => '0x' + paletteToIIgs(c));
if (options.backgroundColor !== null) {
hexCodes[0] = '0x' + paletteToIIgs(hexStringToPalette(options.backgroundColor));
}
logger('#include <types.h>');
logger('');
logger(`Word ${options.varName}Palette[16] = {`);
logger(` ${hexCodes.join(',')}`);
logger(`};`);
break;
}
default: {
const hexCodes = palette.map(c => '$' + paletteToIIgs(c));
// The transparent color is always mapped into color 0, so if a background color is set it goes into index 0
if (options.backgroundColor !== null) {
hexCodes[0] = '$' + paletteToIIgs(hexStringToPalette(options.backgroundColor));
}
logger('TileSetPalette ENT');
logger(' dw ', hexCodes.join(','));
}
}
}
async function main(argv) {
// try {
const png = await readPNG(argv[0]);
const options = getOptions(argv);
console.info(`; startIndex = ${options.startIndex}`);
writeComment(options, `startIndex = ${options.startIndex}`);
if (png.colorType !== 3) {
console.warn('; PNG must be in palette color type');
writeComment(options, `PNG must be in palette color type`, logger.warn);
return;
}
if (png.palette.length > 16) {
console.warn('; Too many colors. Must be 16 or less');
writeComment(options, `Too many colors. Must be 16 or less`, logger.warn);
return;
}
if (options.palette && options.palette.length > 16) {
console.warn('; Too many colors on command line. Must be 16 or less');
writeComment(options, `Too many colors on command line. Must be 16 or less`, logger.warn);
return;
}
@ -249,22 +286,16 @@ async function main(argv) {
options.paletteMap = paletteMap;
// Dump the palette in IIgs hex format
console.log('; Palette');
const hexCodes = targetPalette.map(c => '$' + paletteToIIgs(c));
// The transparent color is always mapped into color 0, so if a background color is set it goes into index 0
if (options.backgroundColor !== null) {
hexCodes[0] = '$' + paletteToIIgs(hexStringToPalette(options.backgroundColor));
}
console.log('TileSetPalette ENT');
console.log(' dw ', hexCodes.join(','));
writeComment(options, `Palette`);
writePaletteArray(options, targetPalette);
// Just convert a paletted PNG to IIgs memory format. We make sure that only a few widths
// are supported
let buff = null;
let mask = null;
console.log('; Converting to BG0 format...');
console.log('');
writeComment(options, `Converting to BG0 format...`);
[buff, mask] = pngToIIgsBuff(options, png);
if (buff && argv[1]) {
@ -272,7 +303,7 @@ async function main(argv) {
writeToTileDataSource(options, buff, mask, png.width / 2);
}
else {
console.log(`; Writing to output file ${argv[1]}`);
writeComment(options, `Writing to output file ${argv[1]}`);
await writeBinayOutput(options, argv[1], buff);
}
}
@ -288,7 +319,7 @@ function reverse(str) {
}
function toHex(h) {
return h.toString(16).padStart(2, '0');
return h.toString(16).padStart(2, '0').toUpperCase();
}
function swap(hex) {
@ -429,21 +460,69 @@ function writeTileToStream(stream, data) {
}
}
function writeTileToStreamORCAC(stream, data) {
// Output the tile data
for (const row of data) {
const hex = row.map(d => '0x' + toHex(d)).join(', ');
stream.write(' ' + hex + ',\n');
}
stream.write('\n');
}
function writeTilesToStream(options, stream, tiles, label='tiledata') {
stream.write(`${label} ENT\n`);
switch (options.format) {
case 'orcac':
writeTilesToStreamORCAC(options, stream, tiles, label);
break;
case 'asm65816':
writeTilesToStreamASM65816(options, stream, tiles, label);
break;
default:
throw `Unknown output format: ${options.format}`;
}
}
function writeTilesToStreamORCAC(options, stream, tiles, label='tiledata') {
stream.write(`Byte ${options.varName}[] = {\n`);
stream.write('/* Reserved space (tile 0 is special...) */\n');
for (let j = 0; j < 4; j += 1) {
for (let i = 0; i < 8; i += 1) {
stream.write(' 0x00, 0x00, 0x00, 0x00,\n');
}
stream.write('\n');
}
let count = 0;
for (const tile of tiles.slice(0, options.maxTiles)) {
stream.write(`/* Tile ID ${count + 1}, isSolid: ${tile.isSolid} */\n`);
writeTileToStreamORCAC(stream, tile.normal.data);
writeTileToStreamORCAC(stream, tile.normal.mask);
writeTileToStreamORCAC(stream, tile.flipped.data);
writeTileToStreamORCAC(stream, tile.flipped.mask);
stream.write('\n');
count += 1;
}
stream.write('};\n');
stream.write('\n');
}
function writeTilesToStreamASM65816(options, stream, tiles, label='tiledata') {
stream.write(`${options.varName} ENT\n`);
stream.write('');
stream.write('; Reserved space (tile 0 is special...)\n');
stream.write(' ds 128\n');
let count = 0;
for (const tile of tiles.slice(0, options.maxTiles)) {
console.log(`Writing tile ${count + 1}`);
stream.write(`; Tile ID ${count + 1}, isSolid: ${tile.isSolid}\n`);
writeTileToStream(stream, tile.normal.data);
writeTileToStream(stream, tile.normal.mask);
writeTileToStream(stream, tile.flipped.data);
writeTileToStream(stream, tile.flipped.mask);
stream.write('');
stream.write('\n');
count += 1;
}
@ -485,51 +564,13 @@ function buildMerlinCodeForTiles(options, tiles, label='tiledata') {
}
function writeToTileDataSource(options, buff, mask, width) {
console.log('tiledata ENT');
console.log();
console.log('; Reserved space (tile 0 is special...');
console.log(' ds 128');
const stream = process.stdout;
let count = 0;
for (let y = 0; ; y += 8) {
for (let x = 0; x < width; x += 4, count += 1) {
if (count >= options.maxTiles) {
return;
}
console.log('; Tile ID ' + (count + 1));
console.log('; From image coordinates ' + (x * 2) + ', ' + y);
// Build the tiles
const tiles = buildTiles(options, buff, mask, width);
const tile = buildTile(options, buff, mask, width, x, y);
// Output the tile data
for (const row of tile.normal.data) {
const hex = row.map(d => toHex(d)).join('');
console.log(' hex ' + hex);
}
console.log();
// Output the tile mask
for (const row of tile.normal.mask) {
const hex = row.map(d => toHex(d)).join('');
console.log(' hex ' + hex);
}
console.log();
// Output the flipped tile data
for (const row of tile.flipped.data) {
const hex = row.map(d => toHex(d)).join('');
console.log(' hex ' + hex);
}
console.log();
// Output the flipped tile data
for (const row of tile.flipped.mask) {
const hex = row.map(d => toHex(d)).join('');
console.log(' hex ' + hex);
}
console.log();
}
}
// Write them to the default output stream
writeTilesToStream(options, stream, tiles, options.varName);
}
async function writeBinayOutput(options, filename, buff) {

105
tools/scanline-test.py Normal file
View File

@ -0,0 +1,105 @@
# Playground for working through scanline decomposition algorithm
# 1. Shadow Off
# 2. Draw Playfield (sprite OR overlay) AND NOT solid_overlay
# 3. Draw sprites
sprites = [
{ 'top': 10, 'bottom': 17 },
{ 'top': 14, 'bottom': 29 }
]
overlays = [
{ 'top': 0, 'bottom': 7 }
]
MAX_HEIGHT = 199
# Combine a list on ranges into a minimal list by merging overlapping ranges
def simplify(a):
b = []
if len(a) > 0:
last = a[0]
start = last['top']
for r in a[1:]:
if r['top'] <= last['bottom']:
last = r
continue
b.append([start, last['bottom']])
last = r
start = r['top']
b.append([start, last['bottom']])
return b
# Given two sorted lists, merge them together into a minimal set of ranges. This could
# be done as a list merge and then a combine step, but we want to be more efficient and
# do the merge-and-combine at the same time
def merge(a, b):
if len(a) == 0:
return simplify(b)
if len(b) == 0:
return simplify(a)
c = []
i = j = 0
while i < len(a) and j < len(b):
if a[i]['top'] <= b[j]['top']:
c.append(a[i])
i += 1
else:
c.append(b[j])
j += 1
if i < len(a):
c.extend(a[i:])
if j < len(b):
c.extend(b[j:])
return simplify(c)
# Find the lines that need to be drawn with shadowing off
def get_shadow_off_bg(sprites):
ranges = []
if len(sprites) > 0:
last = sprites[0]
start = last['top']
for sprite in sprites[1:]:
if sprite['top'] <= last['bottom']:
last = sprite
continue
ranges.push([start, last['bottom']])
start = sprite['top']
last = sprite
ranges.append([start, last['bottom']])
return ranges
def complement(ranges):
comp = []
if len(ranges) > 0:
if ranges[0][0] > 0:
comp.append([0, ranges[0][0]])
for i in range(1, len(ranges)):
comp.append([ranges[i-1][1]+1, ranges[i][0]])
last = ranges[len(ranges)-1]
if last[1] < MAX_HEIGHT:
comp.append([last[1]+1, MAX_HEIGHT])
return comp
else:
return [0, MAX_HEIGHT]
r = get_shadow_off_bg(sprites)
print(r)
print(complement(r))
print(merge(sprites, overlays))