mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-12-21 21:29:17 +00:00
ecs: forward entity refs, deferred components, %binary
This commit is contained in:
parent
999aed9cb4
commit
fc0a43b9af
@ -19,6 +19,8 @@ npm i
|
||||
npm run build
|
||||
```
|
||||
|
||||
To use GitHub integration locally, download the Firebase config file, e.g. https://8bitworkshop.com/v[version]/config.js
|
||||
|
||||
### Start Server
|
||||
|
||||
Start a web server on http://localhost:8000/ while TypeScript compiles in the background:
|
||||
@ -102,3 +104,4 @@ The IDE uses custom forks for many of these, found at https://github.com/sehugg?
|
||||
* https://github.com/sehugg/8bitworkshop-compilers
|
||||
* https://github.com/sehugg/8bit-tools
|
||||
* https://github.com/sehugg/awesome-8bitgamedev
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
|
||||
import { mergeLocs, Tokenizer, TokenType } from "../tokenizer";
|
||||
import { SourceLocated } from "../workertypes";
|
||||
import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer";
|
||||
import { SourceLocated, SourceLocation } from "../workertypes";
|
||||
import { newDecoder } from "./decoder";
|
||||
import { Action, ActionWithJoin, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System } from "./ecs";
|
||||
import { Action, ActionOwner, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System } from "./ecs";
|
||||
|
||||
export enum ECSTokenType {
|
||||
Ellipsis = 'ellipsis',
|
||||
@ -10,11 +10,18 @@ export enum ECSTokenType {
|
||||
QuotedString = 'quoted-string',
|
||||
Integer = 'integer',
|
||||
CodeFragment = 'code-fragment',
|
||||
Placeholder = 'placeholder',
|
||||
}
|
||||
|
||||
interface ForwardRef {
|
||||
reftype: RefType | undefined
|
||||
token: Token
|
||||
}
|
||||
|
||||
export class ECSCompiler extends Tokenizer {
|
||||
|
||||
currentScope: EntityScope | null = null;
|
||||
currentContext: ActionOwner | null = null;
|
||||
debuginfo = false;
|
||||
|
||||
constructor(
|
||||
@ -26,9 +33,10 @@ export class ECSCompiler extends Tokenizer {
|
||||
{ type: ECSTokenType.Ellipsis, regex: /\.\./ },
|
||||
{ type: ECSTokenType.QuotedString, regex: /".*?"/ },
|
||||
{ type: ECSTokenType.CodeFragment, regex: /---.*?---/ },
|
||||
{ type: ECSTokenType.Integer, regex: /[-]?0x[A-Fa-f0-9]+/ },
|
||||
{ type: ECSTokenType.Integer, regex: /[-]?0[xX][A-Fa-f0-9]+/ },
|
||||
{ type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ },
|
||||
{ type: ECSTokenType.Integer, regex: /[-]?\d+/ },
|
||||
{ type: ECSTokenType.Integer, regex: /[%][01]+/ },
|
||||
{ type: ECSTokenType.Operator, regex: /[#=,:(){}\[\]\-]/ },
|
||||
{ type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ },
|
||||
{ type: TokenType.Ignore, regex: /\/\/.*?[\n\r]/ },
|
||||
@ -54,6 +62,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
this.annotate(() => t); // TODO? typescript bug?
|
||||
}
|
||||
}
|
||||
this.runDeferred();
|
||||
}
|
||||
|
||||
getImportFile: (path: string) => string;
|
||||
@ -108,6 +117,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
parseComponentDefinition(): ComponentType {
|
||||
let name = this.expectIdent().str;
|
||||
let fields = [];
|
||||
this.em.deferComponent(name);
|
||||
while (this.peekToken().str != 'end') {
|
||||
fields.push(this.parseComponentField());
|
||||
}
|
||||
@ -148,7 +158,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
this.compileError(`I expected a data type here.`); throw new Error();
|
||||
}
|
||||
|
||||
parseDataValue(field: DataField) : DataValue {
|
||||
parseDataValue(field: DataField) : DataValue | ForwardRef {
|
||||
let tok = this.peekToken();
|
||||
if (tok.type == 'integer') {
|
||||
return this.expectInteger();
|
||||
@ -158,20 +168,10 @@ export class ECSCompiler extends Tokenizer {
|
||||
return new Uint8Array(this.parseDataArray());
|
||||
}
|
||||
if (tok.str == '#') {
|
||||
this.consumeToken();
|
||||
let reftype = field.dtype == 'ref' ? field as RefType : undefined;
|
||||
let e = this.parseEntityRef();
|
||||
let id = e.id;
|
||||
if (reftype) {
|
||||
// TODO: make this a function? elo ehi etc?
|
||||
if (!this.currentScope) {
|
||||
this.compileError("This type can only exist inside of a scope."); throw new Error()
|
||||
};
|
||||
let atypes = this.em.archetypesMatching(reftype.query);
|
||||
let entities = this.currentScope.entitiesMatching(atypes);
|
||||
if (entities.length == 0) this.compileError(`This entitiy doesn't seem to fit the reference type.`);
|
||||
id -= entities[0].id;
|
||||
}
|
||||
return id;
|
||||
let token = this.expectIdent();
|
||||
return { reftype, token };
|
||||
}
|
||||
this.compileError(`I expected a ${field.dtype} here.`); throw new Error();
|
||||
}
|
||||
@ -185,8 +185,13 @@ export class ECSCompiler extends Tokenizer {
|
||||
|
||||
expectInteger(): number {
|
||||
let s = this.consumeToken().str;
|
||||
if (s.startsWith('$')) s = '0x' + s.substring(1);
|
||||
let i = parseInt(s);
|
||||
let i : number;
|
||||
if (s.startsWith('$'))
|
||||
i = parseInt(s.substring(1), 16); // hex $...
|
||||
else if (s.startsWith('%'))
|
||||
i = parseInt(s.substring(1), 2); // binary %...
|
||||
else
|
||||
i = parseInt(s); // default base 10 or 16 (0x...)
|
||||
if (isNaN(i)) this.compileError('There should be an integer here.');
|
||||
return i;
|
||||
}
|
||||
@ -198,7 +203,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
let cmd;
|
||||
while ((cmd = this.expectTokens(['on','locals','end']).str) != 'end') {
|
||||
if (cmd == 'on') {
|
||||
let action = this.annotate(() => this.parseAction());
|
||||
let action = this.annotate(() => this.parseAction(system));
|
||||
actions.push(action);
|
||||
} else if (cmd == 'locals') {
|
||||
system.tempbytes = this.expectInteger();
|
||||
@ -216,13 +221,16 @@ export class ECSCompiler extends Tokenizer {
|
||||
this.consumeToken();
|
||||
tempbytes = this.expectInteger();
|
||||
}
|
||||
let text = this.parseCode();
|
||||
let system : System = { name, tempbytes, actions: [] };
|
||||
let context : ActionOwner = { scope: null, system };
|
||||
let text = this.parseCode(context);
|
||||
let select : SelectType = 'once';
|
||||
let action : Action = { text, event: name, select };
|
||||
return { name, tempbytes, actions: [action] };
|
||||
system.actions.push(action);
|
||||
return system;
|
||||
}
|
||||
|
||||
parseAction(): Action {
|
||||
parseAction(system: System): Action {
|
||||
// TODO: unused events?
|
||||
const event = this.expectIdent().str;
|
||||
this.expectToken('do');
|
||||
@ -245,7 +253,8 @@ export class ECSCompiler extends Tokenizer {
|
||||
if (!query) { this.compileError(`A "${select}" query can't include a limit.`); }
|
||||
else query.limit = this.expectInteger();
|
||||
}
|
||||
let text = this.parseCode();
|
||||
let context : ActionOwner = { scope: null, system };
|
||||
let text = this.parseCode(context);
|
||||
let direction = undefined;
|
||||
if (modifiers['asc']) direction = 'asc';
|
||||
else if (modifiers['desc']) direction = 'desc';
|
||||
@ -287,13 +296,20 @@ export class ECSCompiler extends Tokenizer {
|
||||
return this.parseList(this.parseEventName, ",");
|
||||
}
|
||||
|
||||
parseCode(): string {
|
||||
parseCode(context: ActionOwner): string { // TODOActionNode[] {
|
||||
// TODO: add $loc
|
||||
let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
|
||||
let code = tok.str.substring(3, tok.str.length-3);
|
||||
/*
|
||||
let lines = code.split('\n');
|
||||
// TODO: add after parsing maybe?
|
||||
if (this.debuginfo) this.addDebugInfo(lines, tok.$loc.line);
|
||||
return lines.join('\n');
|
||||
code = lines.join('\n');
|
||||
*/
|
||||
let acomp = new ECSActionCompiler(context);
|
||||
let nodes = acomp.parseFile(code, this.path);
|
||||
// TODO: return nodes
|
||||
return code;
|
||||
}
|
||||
|
||||
addDebugInfo(lines: string[], startline: number) {
|
||||
@ -342,6 +358,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
|
||||
parseEntity() : Entity {
|
||||
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
||||
const scope = this.currentScope;
|
||||
let entname = '';
|
||||
if (this.peekToken().type == TokenType.Ident) {
|
||||
entname = this.expectIdent().str;
|
||||
@ -349,17 +366,30 @@ export class ECSCompiler extends Tokenizer {
|
||||
let etype = this.parseEntityArchetype();
|
||||
let entity = this.currentScope.newEntity(etype);
|
||||
entity.name = entname;
|
||||
let cmd;
|
||||
let cmd2 : string;
|
||||
// TODO: remove init?
|
||||
while ((cmd = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') {
|
||||
while ((cmd2 = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') {
|
||||
let cmd = cmd2; // put in scope
|
||||
if (cmd == 'var') cmd = 'init';
|
||||
if (cmd == 'init' || cmd == 'const') {
|
||||
// TODO: check data types
|
||||
let name = this.expectIdent().str;
|
||||
this.setEntityProperty(entity, name, cmd, (field) : DataValue => {
|
||||
this.expectToken('=');
|
||||
return this.parseDataValue(field);
|
||||
});
|
||||
let { component, field } = this.getEntityField(entity, name);
|
||||
let symtype = this.currentScope.isConstOrInit(component, name);
|
||||
if (symtype && symtype != cmd)
|
||||
this.compileError(`I can't mix const and init values for a given field in a scope.`);
|
||||
this.expectToken('=');
|
||||
let valueOrRef = this.parseDataValue(field);
|
||||
if ((valueOrRef as ForwardRef).token != null) {
|
||||
this.deferred.push(() => {
|
||||
let refvalue = this.resolveEntityRef(scope, valueOrRef as ForwardRef);
|
||||
if (cmd == 'const') scope.setConstValue(entity, component, name, refvalue);
|
||||
if (cmd == 'init') scope.setInitValue(entity, component, name, refvalue);
|
||||
});
|
||||
} else {
|
||||
if (cmd == 'const') scope.setConstValue(entity, component, name, valueOrRef as DataValue);
|
||||
if (cmd == 'init') scope.setInitValue(entity, component, name, valueOrRef as DataValue);
|
||||
}
|
||||
} else if (cmd == 'decode') {
|
||||
let decoderid = this.expectIdent().str;
|
||||
let code = this.expectTokenTypes([ECSTokenType.CodeFragment]).str;
|
||||
@ -368,28 +398,23 @@ export class ECSCompiler extends Tokenizer {
|
||||
if (!decoder) { this.compileError(`I can't find a "${decoderid}" decoder.`); throw new Error() }
|
||||
let result = decoder.parse();
|
||||
for (let entry of Object.entries(result.properties)) {
|
||||
this.setEntityProperty(entity, entry[0], 'const', (field) : DataValue => {
|
||||
return entry[1];
|
||||
});
|
||||
let { component, field } = this.getEntityField(entity, entry[0]);
|
||||
scope.setConstValue(entity, component, field.name, entry[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
setEntityProperty(e: Entity, name: string, cmd: 'init' | 'const', valuefn: (field: DataField) => DataValue) {
|
||||
getEntityField(e: Entity, name: string) {
|
||||
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
||||
let comps = this.em.componentsWithFieldName([e.etype], name);
|
||||
if (comps.length == 0) this.compileError(`I couldn't find a field named "${name}" for this entity.`)
|
||||
if (comps.length > 1) this.compileError(`I found more than one field named "${name}" for this entity.`)
|
||||
let field = comps[0].fields.find(f => f.name == name);
|
||||
let component = comps[0];
|
||||
let field = component.fields.find(f => f.name == name);
|
||||
if (!field) { this.internalError(); throw new Error(); }
|
||||
let value = valuefn(field);
|
||||
let symtype = this.currentScope.isConstOrInit(comps[0], name);
|
||||
if (symtype && symtype != cmd)
|
||||
this.compileError(`I can't mix const and init values for a given field in a scope.`);
|
||||
if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value);
|
||||
if (cmd == 'init') this.currentScope.setInitValue(e, comps[0], name, value);
|
||||
return { component, field };
|
||||
}
|
||||
|
||||
parseEntityArchetype() : EntityArchetype {
|
||||
@ -406,18 +431,25 @@ export class ECSCompiler extends Tokenizer {
|
||||
return cref;
|
||||
}
|
||||
|
||||
parseEntityRef(reftype?: RefType) : Entity {
|
||||
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
||||
this.expectToken('#');
|
||||
let name = this.expectIdent().str;
|
||||
let eref = this.currentScope.entities.find(e => e.name == name);
|
||||
resolveEntityRef(scope: EntityScope, ref: ForwardRef) : number {
|
||||
let name = ref.token.str;
|
||||
let eref = scope.entities.find(e => e.name == name);
|
||||
if (!eref) {
|
||||
this.compileError(`I couldn't find an entity named "${name}" in this scope.`)
|
||||
this.compileError(`I couldn't find an entity named "${name}" in this scope.`, ref.token.$loc)
|
||||
throw new Error();
|
||||
}
|
||||
return eref;
|
||||
let id = eref.id;
|
||||
if (ref.reftype) {
|
||||
// TODO: make this a function? elo ehi etc?
|
||||
let atypes = this.em.archetypesMatching(ref.reftype.query);
|
||||
let entities = scope.entitiesMatching(atypes);
|
||||
if (entities.length == 0)
|
||||
this.compileError(`This entity doesn't seem to fit the reference type.`, ref.token.$loc);
|
||||
id -= entities[0].id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
parseSystemRef() : System {
|
||||
let name = this.expectIdent().str;
|
||||
let sys = this.em.getSystemByName(name);
|
||||
@ -438,3 +470,31 @@ export class ECSCompiler extends Tokenizer {
|
||||
return src.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export class ECSActionCompiler extends Tokenizer {
|
||||
constructor(
|
||||
public readonly context: ActionOwner)
|
||||
{
|
||||
super();
|
||||
this.setTokenRules([
|
||||
{ type: ECSTokenType.Placeholder, regex: /\{\{.*?\}\}/ },
|
||||
{ type: TokenType.CatchAll, regex: /[^{\n]+\n*/ },
|
||||
]);
|
||||
this.errorOnCatchAll = false;
|
||||
}
|
||||
|
||||
parseFile(text: string, path: string) {
|
||||
this.tokenizeFile(text, path);
|
||||
let nodes = [];
|
||||
while (!this.isEOF()) {
|
||||
let tok = this.consumeToken();
|
||||
if (tok.type == ECSTokenType.Placeholder) {
|
||||
let args = tok.str.substring(2, tok.str.length-2).split(/\s+/);
|
||||
nodes.push(new CodePlaceholderNode(this.context, tok.$loc, args));
|
||||
} else if (tok.type == TokenType.CatchAll) {
|
||||
nodes.push(new CodeLiteralNode(this.context, tok.$loc, tok.str));
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +1,3 @@
|
||||
/*
|
||||
|
||||
entity scopes contain entities, and are nested
|
||||
also contain segments (code, bss, rodata)
|
||||
components and systems are global
|
||||
component fields are stored in arrays, range of entities, can be bit-packed
|
||||
some values can be constant, are stored in rodata (or loaded immediate)
|
||||
optional components? on or off
|
||||
union components? either X or Y or Z...
|
||||
|
||||
systems receive and send events, execute code on entities
|
||||
systems are generated on a per-scope basis
|
||||
system queries can only contain entities from self and parent scopes
|
||||
starting from the 'init' event walk the event tree
|
||||
include systems that have at least 1 entity in scope (except init?)
|
||||
|
||||
when entering scope, entities are initialized (zero or init w/ data)
|
||||
to change scope, fire event w/ scope name
|
||||
- how to handle bank-switching?
|
||||
|
||||
helps with:
|
||||
- rapid prototyping w/ reasonable defaults
|
||||
- deconstructing objects into arrays
|
||||
- packing/unpacking bitfields
|
||||
- initializing objects
|
||||
- building lookup tables
|
||||
- selecting and iterating objects
|
||||
- managing events
|
||||
- managing memory and scope
|
||||
- converting assets to native formats?
|
||||
- removing unused data
|
||||
|
||||
it's more convenient to have loops be zero-indexed
|
||||
for page cross, temp storage, etc
|
||||
should references be zero-indexed to a field, or global?
|
||||
should we limit # of entities passed to systems? min-max
|
||||
join thru a reference? load both x and y
|
||||
|
||||
code fragments can be parameterized like macros
|
||||
if two fragments are identical, do a JSR
|
||||
(do we have to look for labels?)
|
||||
should events have parameters? e.g. addscore X Y Z
|
||||
how are Z80 arrays working?
|
||||
https://forums.nesdev.org/viewtopic.php?f=20&t=14691
|
||||
https://www.cpcwiki.eu/forum/programming/trying-not-to-use-ix/msg133416/#msg133416
|
||||
|
||||
how to select two between two entities with once? like scoreboard
|
||||
maybe stack-based interpreter?
|
||||
|
||||
can you query specific entities? merge with existing queries?
|
||||
bigints?
|
||||
source/if query?
|
||||
|
||||
only worry about intersection when non-contiguous ranges?
|
||||
|
||||
crazy idea -- full expansion, then relooper
|
||||
|
||||
how to avoid cycle crossing for critical code and data? bin packing
|
||||
|
||||
system define order, action order, entity order, using order?
|
||||
what happens when a system must be nested inside another? like display kernels
|
||||
|
||||
*/
|
||||
|
||||
import { SourceLocated, SourceLocation } from "../workertypes";
|
||||
import { Bin, Packer } from "./binpack";
|
||||
@ -130,6 +67,39 @@ export class ActionStats {
|
||||
callcount: number = 0;
|
||||
}
|
||||
|
||||
export interface ActionOwner {
|
||||
system: System
|
||||
scope: EntityScope | null
|
||||
}
|
||||
|
||||
export class ActionNode implements SourceLocated {
|
||||
constructor(
|
||||
public readonly owner: ActionOwner,
|
||||
public readonly $loc: SourceLocation
|
||||
) { }
|
||||
}
|
||||
|
||||
export class CodeLiteralNode extends ActionNode {
|
||||
constructor(
|
||||
owner: ActionOwner,
|
||||
$loc: SourceLocation,
|
||||
public readonly text: string
|
||||
) {
|
||||
super(owner, $loc);
|
||||
}
|
||||
}
|
||||
|
||||
export class CodePlaceholderNode extends ActionNode {
|
||||
constructor(
|
||||
owner: ActionOwner,
|
||||
$loc: SourceLocation,
|
||||
public readonly args: string[]
|
||||
) {
|
||||
super(owner, $loc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface ActionBase extends SourceLocated {
|
||||
select: SelectType;
|
||||
event: string;
|
||||
@ -532,6 +502,10 @@ class ActionCPUState {
|
||||
yofs: number = 0;
|
||||
}
|
||||
|
||||
class ActionContext {
|
||||
|
||||
}
|
||||
|
||||
class ActionEval {
|
||||
em : EntityManager;
|
||||
dialect : Dialect_CA65;
|
||||
@ -1272,15 +1246,24 @@ export class EntityManager {
|
||||
if (!parent) this.topScopes[name] = scope;
|
||||
return scope;
|
||||
}
|
||||
deferComponent(name: string) {
|
||||
this.components[name] = { name, fields: [] };
|
||||
}
|
||||
defineComponent(ctype: ComponentType) {
|
||||
let existing = this.components[ctype.name];
|
||||
if (existing) throw new ECSError(`component ${ctype.name} already defined`, existing);
|
||||
if (existing && existing.fields.length > 0)
|
||||
throw new ECSError(`component ${ctype.name} already defined`, existing);
|
||||
for (let field of ctype.fields) {
|
||||
let list = this.name2cfpairs[field.name];
|
||||
if (!list) list = this.name2cfpairs[field.name] = [];
|
||||
list.push({ c: ctype, f: field });
|
||||
}
|
||||
return this.components[ctype.name] = ctype;
|
||||
if (existing) {
|
||||
existing.fields = ctype.fields;
|
||||
return existing;
|
||||
} else {
|
||||
return this.components[ctype.name] = ctype;
|
||||
}
|
||||
}
|
||||
defineSystem(system: System) {
|
||||
let existing = this.systems[system.name];
|
||||
|
@ -69,6 +69,7 @@ export class Tokenizer {
|
||||
curlabel: string;
|
||||
eof: Token;
|
||||
errorOnCatchAll = false;
|
||||
deferred: (() => void)[] = [];
|
||||
|
||||
constructor() {
|
||||
this.errors = [];
|
||||
@ -230,4 +231,9 @@ export class Tokenizer {
|
||||
this.pushbackToken(sep);
|
||||
return list;
|
||||
}
|
||||
runDeferred() {
|
||||
while (this.deferred.length) {
|
||||
this.deferred.shift()();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { execFileSync, spawnSync } from "child_process";
|
||||
import { readdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { describe } from "mocha";
|
||||
import { Bin, BoxConstraints, Packer } from "../common/ecs/binpack";
|
||||
import { ECSCompiler } from "../common/ecs/compiler";
|
||||
import { ECSActionCompiler, ECSCompiler } from "../common/ecs/compiler";
|
||||
import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs";
|
||||
|
||||
const TEMPLATE1 = `
|
||||
@ -344,6 +344,7 @@ end
|
||||
c.exportToFile(src);
|
||||
// TODO: test?
|
||||
//console.log(src.toString());
|
||||
return em;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
for (let err of c.errors) {
|
||||
|
400
test/ecs/sprites1.ecs
Normal file
400
test/ecs/sprites1.ecs
Normal file
@ -0,0 +1,400 @@
|
||||
|
||||
//#resource "vcs-ca65.h"
|
||||
|
||||
import "vcslib.ecs"
|
||||
|
||||
component Bitmap
|
||||
bitmapdata: array of 0..255 baseoffset 31
|
||||
height: 0..255
|
||||
end
|
||||
|
||||
component HasBitmap
|
||||
bitmap: [Bitmap]
|
||||
end
|
||||
|
||||
component Colormap
|
||||
colormapdata: array of 0..255 baseoffset 31
|
||||
end
|
||||
|
||||
component HasColormap
|
||||
colormap: [Colormap]
|
||||
end
|
||||
|
||||
component Sprite
|
||||
plyrflags: 0..63
|
||||
end
|
||||
|
||||
component HasXpos
|
||||
xpos: 0..255
|
||||
end
|
||||
|
||||
component HasYpos
|
||||
ypos: 0..255
|
||||
end
|
||||
|
||||
component SpriteSlot
|
||||
sprite: [Sprite,HasBitmap,HasColormap,HasYpos]
|
||||
end
|
||||
|
||||
component Missile
|
||||
end
|
||||
|
||||
system Kernel2Sprite
|
||||
locals 13
|
||||
on preframe do with [KernelSection]
|
||||
---
|
||||
.define KLINES {{<lines}}
|
||||
.define KPAD 32
|
||||
---
|
||||
on preframe do join
|
||||
[SpriteSlot] with
|
||||
[Sprite,HasBitmap,HasColormap,HasYpos] limit 2
|
||||
---
|
||||
; set player object flags
|
||||
lda {{<plyrflags}}
|
||||
sta NUSIZ0,y
|
||||
sta REFP0,y
|
||||
; calculate screen height - ypos
|
||||
lda KLINES+KPAD
|
||||
sec
|
||||
sbc {{<ypos}}
|
||||
sta {{$11}}
|
||||
; calculate bitmap pointer
|
||||
stx {{$12}} ; save X (Sprite index)
|
||||
lda {{<bitmap}} ; deref bitmap
|
||||
tax
|
||||
lda {{<Bitmap:bitmapdata}},x
|
||||
sec
|
||||
sbc {{$11}}
|
||||
sta {{$0}},y ; Y = sprite slot index
|
||||
lda {{>Bitmap:bitmapdata}},x
|
||||
sbc #0
|
||||
sta {{$2}},y
|
||||
; get bitmap height
|
||||
lda {{<Bitmap:height}},x
|
||||
sta {{$8}},y
|
||||
; calculate colormap pointer
|
||||
ldx {{$12}} ; restore X
|
||||
lda {{<colormap}} ; deref colormap
|
||||
tax
|
||||
lda {{<Colormap:colormapdata}},x
|
||||
sec
|
||||
sbc {{$11}}
|
||||
sta {{$4}},y
|
||||
lda {{>Colormap:colormapdata}},x
|
||||
sbc #0
|
||||
sta {{$6}},y
|
||||
; save ypos
|
||||
ldx {{$12}} ; restore X
|
||||
lda {{<ypos}}
|
||||
sta {{$10}},y
|
||||
---
|
||||
on preframe do once
|
||||
---
|
||||
; L0 L1 H0 H1 -> L0 H0 L1 H1
|
||||
lda {{$1}}
|
||||
ldy {{$2}}
|
||||
sty {{$1}}
|
||||
sta {{$2}}
|
||||
lda {{$5}}
|
||||
ldy {{$6}}
|
||||
sty {{$5}}
|
||||
sta {{$6}}
|
||||
---
|
||||
on preframe do if [BGColor]
|
||||
---
|
||||
lda {{<bgcolor}}
|
||||
sta COLUBK
|
||||
---
|
||||
on preframe do if [Missile,HasYpos]
|
||||
---
|
||||
lda KLINES
|
||||
sec
|
||||
sbc {{<ypos}}
|
||||
sta {{$12}}
|
||||
---
|
||||
on kernel do with [KernelSection]
|
||||
---
|
||||
ldy #0
|
||||
sty VDELP0
|
||||
iny
|
||||
sta VDELP1
|
||||
---
|
||||
on kernel do with [KernelSection]
|
||||
---
|
||||
; define macro for each line
|
||||
.macro @DrawLine do_wsync
|
||||
.local DoDraw1
|
||||
.local DoDraw2
|
||||
; draw player 0
|
||||
lda {{$8}} ; height
|
||||
dcp {{$10}} ; ypos
|
||||
bcs DoDraw1
|
||||
lda #0
|
||||
.byte $2C
|
||||
DoDraw1:
|
||||
lda ({{$0}}),y
|
||||
.if do_wsync
|
||||
sta WSYNC
|
||||
.endif
|
||||
sta GRP0
|
||||
lda ({{$4}}),y
|
||||
sta COLUP0
|
||||
; draw player 1
|
||||
lda {{$9}} ; height
|
||||
dcp {{$11}} ; ypos
|
||||
bcs DoDraw2
|
||||
lda #0
|
||||
.byte $2C
|
||||
DoDraw2:
|
||||
lda ({{$2}}),y
|
||||
sta GRP1
|
||||
lda ({{$6}}),y
|
||||
sta COLUP1
|
||||
.endmacro
|
||||
|
||||
ldy {{<lines}}
|
||||
@LVScan:
|
||||
{{!scanline1}}
|
||||
@DrawLine 1 ; macro: draw scanline w/ WSYNC
|
||||
dey ; next scanline
|
||||
{{!scanline2}}
|
||||
@DrawLine 0 ; macro: draw scanline no WSYNC
|
||||
dey ; next scanline
|
||||
bne @LVScan ; repeat until out of lines
|
||||
---
|
||||
on kernel do with [KernelSection]
|
||||
---
|
||||
lda #0
|
||||
sta GRP0
|
||||
sta GRP1
|
||||
sta GRP0
|
||||
sta GRP1
|
||||
---
|
||||
on scanline1 do if [Missile,HasYpos]
|
||||
---
|
||||
cpy {{$12}}
|
||||
php
|
||||
pla
|
||||
sta ENAM0
|
||||
---
|
||||
end
|
||||
|
||||
system VersatilePlayfield
|
||||
locals 2
|
||||
on preframe do with [VersatilePlayfield]
|
||||
---
|
||||
lda {{<data}}
|
||||
sta {{$0}}
|
||||
lda {{>data}}
|
||||
sta {{$1}}
|
||||
---
|
||||
on scanline1 do with [VersatilePlayfield]
|
||||
---
|
||||
lda ({{local 0}}),y
|
||||
tax
|
||||
---
|
||||
on scanline2 do with [VersatilePlayfield]
|
||||
---
|
||||
lda ({{local 0}}),y
|
||||
sta $00,x
|
||||
---
|
||||
end
|
||||
|
||||
system SetXPos
|
||||
on preframe do once
|
||||
---
|
||||
sta HMCLR
|
||||
---
|
||||
on preframe do join [SpriteSlot] with [HasXpos]
|
||||
limit 2
|
||||
---
|
||||
lda {{<xpos}}
|
||||
{{!SetHorizPos}}
|
||||
---
|
||||
on preframe do if [Missile,HasXpos]
|
||||
---
|
||||
lda {{<xpos}}
|
||||
ldx #2
|
||||
{{!SetHorizPos}}
|
||||
---
|
||||
on preframe do once
|
||||
---
|
||||
sta WSYNC
|
||||
sta HMOVE
|
||||
---
|
||||
end
|
||||
|
||||
system MoveJoyX
|
||||
on joyleft do with [HasXpos]
|
||||
---
|
||||
lda {{<xpos}}
|
||||
sec
|
||||
sbc #1
|
||||
bcc @nomove
|
||||
sta {{<xpos}}
|
||||
@nomove:
|
||||
---
|
||||
on joyright do with [HasXpos]
|
||||
---
|
||||
lda {{<xpos}}
|
||||
clc
|
||||
adc #1
|
||||
cmp #152
|
||||
bcs @nomove
|
||||
sta {{<xpos}}
|
||||
@nomove:
|
||||
---
|
||||
end
|
||||
|
||||
system MoveJoyY
|
||||
on joyup do with [HasYpos]
|
||||
---
|
||||
lda {{<ypos}}
|
||||
sec
|
||||
sbc #1
|
||||
bcc @nomove
|
||||
sta {{<ypos}}
|
||||
@nomove:
|
||||
---
|
||||
on joydown do with [HasYpos]
|
||||
---
|
||||
lda {{<ypos}}
|
||||
clc
|
||||
adc #1
|
||||
cmp #220
|
||||
bcs @nomove
|
||||
sta {{<ypos}}
|
||||
@nomove:
|
||||
---
|
||||
end
|
||||
|
||||
system SpriteShuffler
|
||||
locals 2
|
||||
on postframe do select [SpriteSlot]
|
||||
---
|
||||
; load two sprite slots at left side of array
|
||||
lda {{<SpriteSlot:sprite}}
|
||||
sta {{$0}}
|
||||
lda {{<SpriteSlot:sprite}}+1
|
||||
sta {{$1}}
|
||||
; move two slots to the left
|
||||
ldx #0
|
||||
@loop:
|
||||
lda {{<SpriteSlot:sprite}}+2,x
|
||||
sta {{<SpriteSlot:sprite}},x
|
||||
inx
|
||||
cpx #{{%ecount}}-2
|
||||
bne @loop
|
||||
; store two sprite slots at right side of array
|
||||
lda {{$0}}
|
||||
sta {{<SpriteSlot:sprite}}+{{%ecount}}-2
|
||||
lda {{$1}}
|
||||
sta {{<SpriteSlot:sprite}}+{{%ecount}}-1
|
||||
---
|
||||
end
|
||||
|
||||
system SpriteHider
|
||||
locals 1
|
||||
on postframe do select [SpriteSlot]
|
||||
---
|
||||
lda #{{%efullcount}}-1
|
||||
sta {{$0}}
|
||||
---
|
||||
on postframe do
|
||||
join [SpriteSlot]
|
||||
with [Sprite,HasYpos]
|
||||
limit 2
|
||||
---
|
||||
lda {{<ypos}}
|
||||
cmp #192
|
||||
bcc @skip
|
||||
; swap this sprite slot with slot at end of array
|
||||
lda {{<SpriteSlot:sprite}},y
|
||||
pha
|
||||
ldx {{$0}} ; clobbers X, but no longer used
|
||||
lda {{<SpriteSlot:sprite}},x
|
||||
sta {{<SpriteSlot:sprite}},y
|
||||
pla
|
||||
sta {{<SpriteSlot:sprite}},x
|
||||
dec {{$0}}
|
||||
@skip:
|
||||
---
|
||||
end
|
||||
|
||||
///
|
||||
|
||||
demo Main
|
||||
|
||||
using FrameLoop, Kernel2Sprite
|
||||
using Joystick, MoveJoyX, MoveJoyY
|
||||
using SetXPos, SetHorizPos
|
||||
using SpriteShuffler, SpriteHider
|
||||
|
||||
entity Kernel [KernelSection, BGColor]
|
||||
const lines = 192
|
||||
const bgcolor = 0xa2
|
||||
end
|
||||
|
||||
entity Bitmap1 [Bitmap]
|
||||
const bitmapdata = [1, 1, 3, 7, 15, 31, 63, 127]
|
||||
const height = 8
|
||||
end
|
||||
|
||||
entity Bitmap2 [Bitmap]
|
||||
const bitmapdata = [$18,$3e,$ff,$ff,$ff,$ff,$3e,$18]
|
||||
const height = 8
|
||||
end
|
||||
|
||||
entity Colormap1 [Colormap]
|
||||
const colormapdata = [6, 3, 6, 9, 12, 14, 31, 63]
|
||||
end
|
||||
|
||||
entity Sprite0 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
|
||||
init xpos = 50
|
||||
init ypos = 150
|
||||
init bitmap = #Bitmap2
|
||||
const plyrflags = 0
|
||||
end
|
||||
|
||||
entity Sprite1 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
|
||||
init xpos = 100
|
||||
init ypos = 60
|
||||
init bitmap = #Bitmap1
|
||||
const plyrflags = 3
|
||||
end
|
||||
|
||||
entity Sprite2 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
|
||||
init xpos = 80
|
||||
init ypos = 90
|
||||
init bitmap = #Bitmap2
|
||||
const plyrflags = 0
|
||||
end
|
||||
|
||||
entity Sprite3 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
|
||||
init xpos = 40
|
||||
init ypos = 150
|
||||
init bitmap = #Bitmap1
|
||||
const plyrflags = 0
|
||||
end
|
||||
/*
|
||||
entity [Missile,HasXpos,HasYpos]
|
||||
init xpos = 70
|
||||
init ypos = 70
|
||||
end
|
||||
*/
|
||||
entity Slot0 [SpriteSlot]
|
||||
init sprite = #Sprite0
|
||||
end
|
||||
entity Slot1 [SpriteSlot]
|
||||
init sprite = #Sprite1
|
||||
end
|
||||
entity Slot2 [SpriteSlot]
|
||||
init sprite = #Sprite2
|
||||
end
|
||||
entity Slot3 [SpriteSlot]
|
||||
init sprite = #Sprite3
|
||||
end
|
||||
|
||||
end
|
||||
|
515
test/ecs/sprites1.txt
Normal file
515
test/ecs/sprites1.txt
Normal file
@ -0,0 +1,515 @@
|
||||
.scope Main
|
||||
.zeropage
|
||||
SpriteSlot_sprite_b0:
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
HasYpos_ypos_b0:
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
HasXpos_xpos_b0:
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
HasColormap_colormap_b0:
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
HasBitmap_bitmap_b0:
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
TEMP:
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
.res 1
|
||||
Kernel2Sprite__tmp = TEMP+0
|
||||
Joystick__tmp = TEMP+0
|
||||
SpriteShuffler__tmp = TEMP+1
|
||||
SpriteHider__tmp = TEMP+3
|
||||
.code
|
||||
Bitmap_bitmapdata_e1_b0:
|
||||
.byte 1
|
||||
.byte 1
|
||||
.byte 3
|
||||
.byte 7
|
||||
.byte 15
|
||||
.byte 31
|
||||
.byte 63
|
||||
.byte 127
|
||||
Bitmap_bitmapdata_b0:
|
||||
.byte <(Bitmap_bitmapdata_e1_b0+31)
|
||||
.byte <(Bitmap_bitmapdata_e2_b0+31)
|
||||
Bitmap_bitmapdata_b8:
|
||||
.byte >(Bitmap_bitmapdata_e1_b0+31)
|
||||
.byte >(Bitmap_bitmapdata_e2_b0+31)
|
||||
Bitmap_height_b0:
|
||||
.byte 8
|
||||
.byte 8
|
||||
Bitmap_bitmapdata_e2_b0:
|
||||
.byte 24
|
||||
.byte 62
|
||||
.byte 255
|
||||
.byte 255
|
||||
.byte 255
|
||||
.byte 255
|
||||
.byte 62
|
||||
.byte 24
|
||||
Colormap_colormapdata_e3_b0:
|
||||
.byte 6
|
||||
.byte 3
|
||||
.byte 6
|
||||
.byte 9
|
||||
.byte 12
|
||||
.byte 14
|
||||
.byte 31
|
||||
.byte 63
|
||||
Colormap_colormapdata_b0:
|
||||
.byte <(Colormap_colormapdata_e3_b0+31)
|
||||
Colormap_colormapdata_b8:
|
||||
.byte >(Colormap_colormapdata_e3_b0+31)
|
||||
Sprite_plyrflags_b0:
|
||||
.byte 0
|
||||
.byte 3
|
||||
.byte 0
|
||||
.byte 0
|
||||
Main__INITDATA:
|
||||
.byte 0
|
||||
.byte 1
|
||||
.byte 2
|
||||
.byte 3
|
||||
.byte 150
|
||||
.byte 60
|
||||
.byte 90
|
||||
.byte 150
|
||||
.byte 50
|
||||
.byte 100
|
||||
.byte 80
|
||||
.byte 40
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 1
|
||||
.byte 0
|
||||
.byte 1
|
||||
.byte 0
|
||||
__Start:
|
||||
.code
|
||||
|
||||
;;; start action Init main_init
|
||||
|
||||
.include "vcs-ca65.h"
|
||||
.macpack longbranch
|
||||
.define PAL 0
|
||||
__NMI:
|
||||
__Reset:
|
||||
__BRK:
|
||||
CLEAN_START
|
||||
|
||||
ldy #20
|
||||
: lda Main__INITDATA-1,y
|
||||
sta SpriteSlot_sprite_b0-1,y
|
||||
dey
|
||||
bne :-
|
||||
; initialize data segment
|
||||
.code
|
||||
|
||||
;;; start action FrameLoop start
|
||||
|
||||
FrameLoop__start__2__NextFrame:
|
||||
FRAME_START
|
||||
.code
|
||||
|
||||
;;; start action Kernel2Sprite preframe
|
||||
|
||||
.define KLINES #192
|
||||
.define KPAD 32
|
||||
|
||||
;;; end action Kernel2Sprite preframe
|
||||
|
||||
;;; start action Kernel2Sprite preframe
|
||||
|
||||
ldy #0
|
||||
Kernel2Sprite__preframe__4____each:
|
||||
ldx SpriteSlot_sprite_b0,y
|
||||
|
||||
; set player object flags
|
||||
lda Sprite_plyrflags_b0,x
|
||||
sta NUSIZ0,y
|
||||
sta REFP0,y
|
||||
; calculate screen height - ypos
|
||||
lda KLINES+KPAD
|
||||
sec
|
||||
sbc HasYpos_ypos_b0,x
|
||||
sta Kernel2Sprite__tmp+11
|
||||
; calculate bitmap pointer
|
||||
stx Kernel2Sprite__tmp+12 ; save X (Sprite index)
|
||||
lda HasBitmap_bitmap_b0,x ; deref bitmap
|
||||
tax
|
||||
lda Bitmap_bitmapdata_b0,x
|
||||
sec
|
||||
sbc Kernel2Sprite__tmp+11
|
||||
sta Kernel2Sprite__tmp+0,y ; Y = sprite slot index
|
||||
lda Bitmap_bitmapdata_b8,x
|
||||
sbc #0
|
||||
sta Kernel2Sprite__tmp+2,y
|
||||
; get bitmap height
|
||||
lda Bitmap_height_b0,x
|
||||
sta Kernel2Sprite__tmp+8,y
|
||||
; calculate colormap pointer
|
||||
ldx Kernel2Sprite__tmp+12 ; restore X
|
||||
lda HasColormap_colormap_b0,x ; deref colormap
|
||||
tax
|
||||
lda Colormap_colormapdata_b0,x
|
||||
sec
|
||||
sbc Kernel2Sprite__tmp+11
|
||||
sta Kernel2Sprite__tmp+4,y
|
||||
lda Colormap_colormapdata_b8,x
|
||||
sbc #0
|
||||
sta Kernel2Sprite__tmp+6,y
|
||||
; save ypos
|
||||
ldx Kernel2Sprite__tmp+12 ; restore X
|
||||
lda HasYpos_ypos_b0,x
|
||||
sta Kernel2Sprite__tmp+10,y
|
||||
|
||||
iny
|
||||
cpy #2
|
||||
jne Kernel2Sprite__preframe__4____each
|
||||
Kernel2Sprite__preframe__4____exit:
|
||||
|
||||
;;; end action Kernel2Sprite preframe
|
||||
|
||||
;;; start action Kernel2Sprite preframe
|
||||
|
||||
; L0 L1 H0 H1 -> L0 H0 L1 H1
|
||||
lda Kernel2Sprite__tmp+1
|
||||
ldy Kernel2Sprite__tmp+2
|
||||
sty Kernel2Sprite__tmp+1
|
||||
sta Kernel2Sprite__tmp+2
|
||||
lda Kernel2Sprite__tmp+5
|
||||
ldy Kernel2Sprite__tmp+6
|
||||
sty Kernel2Sprite__tmp+5
|
||||
sta Kernel2Sprite__tmp+6
|
||||
|
||||
;;; end action Kernel2Sprite preframe
|
||||
|
||||
;;; start action Kernel2Sprite preframe
|
||||
|
||||
lda #162
|
||||
sta COLUBK
|
||||
|
||||
;;; end action Kernel2Sprite preframe
|
||||
|
||||
;;; start action Kernel2Sprite preframe
|
||||
|
||||
;;; end action Kernel2Sprite preframe
|
||||
|
||||
;;; start action SetXPos preframe
|
||||
|
||||
sta HMCLR
|
||||
|
||||
;;; end action SetXPos preframe
|
||||
|
||||
;;; start action SetXPos preframe
|
||||
|
||||
ldy #0
|
||||
SetXPos__preframe__8____each:
|
||||
ldx SpriteSlot_sprite_b0,y
|
||||
|
||||
lda HasXpos_xpos_b0,x
|
||||
.code
|
||||
|
||||
;;; start action SetHorizPos SetHorizPos
|
||||
|
||||
; SetHorizPos routine
|
||||
; A = X coordinate
|
||||
; Y = player number (0 or 1)
|
||||
sta WSYNC ; start a new line
|
||||
sec ; set carry flag
|
||||
nop
|
||||
SetHorizPos__SetHorizPos__9__DivideLoop:
|
||||
sbc #15 ; subtract 15
|
||||
bcs SetHorizPos__SetHorizPos__9__DivideLoop ; branch until negative
|
||||
eor #7 ; calculate fine offset
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta RESP0,y ; fix coarse position
|
||||
sta HMP0,y ; set fine offset
|
||||
|
||||
;;; end action SetHorizPos SetHorizPos
|
||||
|
||||
|
||||
iny
|
||||
cpy #2
|
||||
jne SetXPos__preframe__8____each
|
||||
SetXPos__preframe__8____exit:
|
||||
|
||||
;;; end action SetXPos preframe
|
||||
|
||||
;;; start action SetXPos preframe
|
||||
|
||||
;;; end action SetXPos preframe
|
||||
|
||||
;;; start action SetXPos preframe
|
||||
|
||||
sta WSYNC
|
||||
sta HMOVE
|
||||
|
||||
;;; end action SetXPos preframe
|
||||
|
||||
KERNEL_START
|
||||
.code
|
||||
|
||||
;;; start action Kernel2Sprite kernel
|
||||
|
||||
ldy #0
|
||||
sty VDELP0
|
||||
iny
|
||||
sta VDELP1
|
||||
|
||||
;;; end action Kernel2Sprite kernel
|
||||
|
||||
;;; start action Kernel2Sprite kernel
|
||||
|
||||
; define macro for each line
|
||||
.macro Kernel2Sprite__kernel__12__DrawLine do_wsync
|
||||
.local DoDraw1
|
||||
.local DoDraw2
|
||||
; draw player 0
|
||||
lda Kernel2Sprite__tmp+8 ; height
|
||||
dcp Kernel2Sprite__tmp+10 ; ypos
|
||||
bcs DoDraw1
|
||||
lda #0
|
||||
.byte $2C
|
||||
DoDraw1:
|
||||
lda (Kernel2Sprite__tmp+0),y
|
||||
.if do_wsync
|
||||
sta WSYNC
|
||||
.endif
|
||||
sta GRP0
|
||||
lda (Kernel2Sprite__tmp+4),y
|
||||
sta COLUP0
|
||||
; draw player 1
|
||||
lda Kernel2Sprite__tmp+9 ; height
|
||||
dcp Kernel2Sprite__tmp+11 ; ypos
|
||||
bcs DoDraw2
|
||||
lda #0
|
||||
.byte $2C
|
||||
DoDraw2:
|
||||
lda (Kernel2Sprite__tmp+2),y
|
||||
sta GRP1
|
||||
lda (Kernel2Sprite__tmp+6),y
|
||||
sta COLUP1
|
||||
.endmacro
|
||||
|
||||
ldy #192
|
||||
Kernel2Sprite__kernel__12__LVScan:
|
||||
.code
|
||||
|
||||
;;; start action Kernel2Sprite scanline1
|
||||
|
||||
;;; end action Kernel2Sprite scanline1
|
||||
|
||||
Kernel2Sprite__kernel__12__DrawLine 1 ; macro: draw scanline w/ WSYNC
|
||||
dey ; next scanline
|
||||
.code
|
||||
|
||||
Kernel2Sprite__kernel__12__DrawLine 0 ; macro: draw scanline no WSYNC
|
||||
dey ; next scanline
|
||||
bne Kernel2Sprite__kernel__12__LVScan ; repeat until out of lines
|
||||
|
||||
;;; end action Kernel2Sprite kernel
|
||||
|
||||
;;; start action Kernel2Sprite kernel
|
||||
|
||||
lda #0
|
||||
sta GRP0
|
||||
sta GRP1
|
||||
sta GRP0
|
||||
sta GRP1
|
||||
|
||||
;;; end action Kernel2Sprite kernel
|
||||
|
||||
KERNEL_END
|
||||
.code
|
||||
|
||||
;;; start action Joystick postframe
|
||||
|
||||
; 2 control inputs share a single byte, 4 bits each
|
||||
lda SWCHA
|
||||
sta Joystick__tmp+0
|
||||
|
||||
;;; end action Joystick postframe
|
||||
|
||||
;;; start action Joystick postframe
|
||||
|
||||
ldx #0
|
||||
Joystick__postframe__15____each:
|
||||
|
||||
asl Joystick__tmp+0
|
||||
bcs Joystick__postframe__15__SkipMoveRight
|
||||
.code
|
||||
|
||||
;;; start action MoveJoyX joyright
|
||||
|
||||
lda HasXpos_xpos_b0,x
|
||||
clc
|
||||
adc #1
|
||||
cmp #152
|
||||
bcs MoveJoyX__joyright__16__nomove
|
||||
sta HasXpos_xpos_b0,x
|
||||
MoveJoyX__joyright__16__nomove:
|
||||
|
||||
;;; end action MoveJoyX joyright
|
||||
|
||||
Joystick__postframe__15__SkipMoveRight:
|
||||
asl Joystick__tmp+0
|
||||
bcs Joystick__postframe__15__SkipMoveLeft
|
||||
.code
|
||||
|
||||
;;; start action MoveJoyX joyleft
|
||||
|
||||
lda HasXpos_xpos_b0,x
|
||||
sec
|
||||
sbc #1
|
||||
bcc MoveJoyX__joyleft__17__nomove
|
||||
sta HasXpos_xpos_b0,x
|
||||
MoveJoyX__joyleft__17__nomove:
|
||||
|
||||
;;; end action MoveJoyX joyleft
|
||||
|
||||
Joystick__postframe__15__SkipMoveLeft:
|
||||
asl Joystick__tmp+0
|
||||
bcs Joystick__postframe__15__SkipMoveDown
|
||||
.code
|
||||
|
||||
;;; start action MoveJoyY joydown
|
||||
|
||||
lda HasYpos_ypos_b0,x
|
||||
clc
|
||||
adc #1
|
||||
cmp #220
|
||||
bcs MoveJoyY__joydown__18__nomove
|
||||
sta HasYpos_ypos_b0,x
|
||||
MoveJoyY__joydown__18__nomove:
|
||||
|
||||
;;; end action MoveJoyY joydown
|
||||
|
||||
Joystick__postframe__15__SkipMoveDown:
|
||||
asl Joystick__tmp+0
|
||||
bcs Joystick__postframe__15__SkipMoveUp
|
||||
.code
|
||||
|
||||
;;; start action MoveJoyY joyup
|
||||
|
||||
lda HasYpos_ypos_b0,x
|
||||
sec
|
||||
sbc #1
|
||||
bcc MoveJoyY__joyup__19__nomove
|
||||
sta HasYpos_ypos_b0,x
|
||||
MoveJoyY__joyup__19__nomove:
|
||||
|
||||
;;; end action MoveJoyY joyup
|
||||
|
||||
Joystick__postframe__15__SkipMoveUp:
|
||||
|
||||
inx
|
||||
cpx #4
|
||||
jne Joystick__postframe__15____each
|
||||
Joystick__postframe__15____exit:
|
||||
|
||||
;;; end action Joystick postframe
|
||||
|
||||
;;; start action SpriteShuffler postframe
|
||||
|
||||
; load two sprite slots at left side of array
|
||||
lda SpriteSlot_sprite_b0
|
||||
sta SpriteShuffler__tmp+0
|
||||
lda SpriteSlot_sprite_b0+1
|
||||
sta SpriteShuffler__tmp+1
|
||||
; move two slots to the left
|
||||
ldx #0
|
||||
SpriteShuffler__postframe__20__loop:
|
||||
lda SpriteSlot_sprite_b0+2,x
|
||||
sta SpriteSlot_sprite_b0,x
|
||||
inx
|
||||
cpx #4-2
|
||||
bne SpriteShuffler__postframe__20__loop
|
||||
; store two sprite slots at right side of array
|
||||
lda SpriteShuffler__tmp+0
|
||||
sta SpriteSlot_sprite_b0+4-2
|
||||
lda SpriteShuffler__tmp+1
|
||||
sta SpriteSlot_sprite_b0+4-1
|
||||
|
||||
;;; end action SpriteShuffler postframe
|
||||
|
||||
;;; start action SpriteHider postframe
|
||||
|
||||
lda #4-1
|
||||
sta SpriteHider__tmp+0
|
||||
|
||||
;;; end action SpriteHider postframe
|
||||
|
||||
;;; start action SpriteHider postframe
|
||||
|
||||
ldy #0
|
||||
SpriteHider__postframe__22____each:
|
||||
ldx SpriteSlot_sprite_b0,y
|
||||
|
||||
lda HasYpos_ypos_b0,x
|
||||
cmp #192
|
||||
bcc SpriteHider__postframe__22__skip
|
||||
; swap this sprite slot with slot at end of array
|
||||
lda SpriteSlot_sprite_b0,y
|
||||
pha
|
||||
ldx SpriteHider__tmp+0 ; clobbers X, but no longer used
|
||||
lda SpriteSlot_sprite_b0,x
|
||||
sta SpriteSlot_sprite_b0,y
|
||||
pla
|
||||
sta SpriteSlot_sprite_b0,x
|
||||
dec SpriteHider__tmp+0
|
||||
SpriteHider__postframe__22__skip:
|
||||
|
||||
iny
|
||||
cpy #2
|
||||
jne SpriteHider__postframe__22____each
|
||||
SpriteHider__postframe__22____exit:
|
||||
|
||||
;;; end action SpriteHider postframe
|
||||
|
||||
FRAME_END
|
||||
.code
|
||||
|
||||
jmp FrameLoop__start__2__NextFrame ; loop to next frame
|
||||
|
||||
;;; end action FrameLoop start
|
||||
; start main routine
|
||||
.segment "VECTORS"
|
||||
Return: .word $6060
|
||||
VecNMI:
|
||||
VecReset: .word Main::__Reset
|
||||
VecBRK: .word Main::__BRK
|
||||
|
||||
;;; end action Init main_init
|
||||
|
||||
.endscope
|
||||
Main__Start = Main::__Start
|
240
test/ecs/vcslib.ecs
Normal file
240
test/ecs/vcslib.ecs
Normal file
@ -0,0 +1,240 @@
|
||||
|
||||
//#resource "vcs-ca65.h"
|
||||
|
||||
system Init
|
||||
on main_init do once
|
||||
---
|
||||
.include "vcs-ca65.h"
|
||||
.macpack longbranch
|
||||
.define PAL 0
|
||||
__NMI:
|
||||
__Reset:
|
||||
__BRK:
|
||||
CLEAN_START
|
||||
{{bss_init}} ; initialize data segment
|
||||
{{!start}} ; start main routine
|
||||
.segment "VECTORS"
|
||||
Return: .word $6060
|
||||
VecNMI:
|
||||
VecReset: .word Main::__Reset
|
||||
VecBRK: .word Main::__BRK
|
||||
---
|
||||
end
|
||||
|
||||
component Player
|
||||
end
|
||||
|
||||
component KernelSection
|
||||
lines: 1..255
|
||||
end
|
||||
|
||||
component BGColor
|
||||
bgcolor: 0..255
|
||||
end
|
||||
|
||||
component PFColor
|
||||
pfcolor: 0..255
|
||||
end
|
||||
|
||||
component Playfield
|
||||
pf: 0..0xffffff
|
||||
end
|
||||
|
||||
component AsymPlayfield
|
||||
pfleft: 0..0xffffff
|
||||
pfright: 0..0xffffff
|
||||
end
|
||||
|
||||
component VersatilePlayfield
|
||||
data: array of 0..255 baseoffset -1
|
||||
end
|
||||
|
||||
system FrameLoop
|
||||
on start do once
|
||||
---
|
||||
@NextFrame:
|
||||
FRAME_START
|
||||
{{emit preframe}}
|
||||
KERNEL_START
|
||||
{{emit kernel}}
|
||||
KERNEL_END
|
||||
{{emit postframe}}
|
||||
FRAME_END
|
||||
{{emit nextframe}}
|
||||
jmp @NextFrame ; loop to next frame
|
||||
---
|
||||
end
|
||||
|
||||
system ResetSwitch
|
||||
on nextframe do once
|
||||
---
|
||||
lsr SWCHB ; test Game Reset switch
|
||||
bcs @NoStart
|
||||
{{!resetswitch}}
|
||||
@NoStart:
|
||||
---
|
||||
end
|
||||
|
||||
system ResetConsole
|
||||
on resetswitch do once
|
||||
---
|
||||
jmp Main::__Reset ; jump to Reset handler
|
||||
---
|
||||
end
|
||||
|
||||
system JoyButton
|
||||
on postframe do foreach [Player]
|
||||
---
|
||||
lda {{index INPT4}} ;read button input
|
||||
bmi @NotPressed
|
||||
{{emit joybutton}}
|
||||
@NotPressed:
|
||||
---
|
||||
end
|
||||
|
||||
system Joystick
|
||||
locals 1
|
||||
on postframe do once
|
||||
---
|
||||
; 2 control inputs share a single byte, 4 bits each
|
||||
lda SWCHA
|
||||
sta {{$0}}
|
||||
---
|
||||
on postframe do foreach [Player]
|
||||
---
|
||||
asl {{$0}}
|
||||
bcs @SkipMoveRight
|
||||
{{!joyright}}
|
||||
@SkipMoveRight:
|
||||
asl {{$0}}
|
||||
bcs @SkipMoveLeft
|
||||
{{!joyleft}}
|
||||
@SkipMoveLeft:
|
||||
asl {{$0}}
|
||||
bcs @SkipMoveDown
|
||||
{{!joydown}}
|
||||
@SkipMoveDown:
|
||||
asl {{$0}}
|
||||
bcs @SkipMoveUp
|
||||
{{!joyup}}
|
||||
@SkipMoveUp:
|
||||
---
|
||||
end
|
||||
|
||||
system SetHorizPos
|
||||
on SetHorizPos do once
|
||||
---
|
||||
; SetHorizPos routine
|
||||
; A = X coordinate
|
||||
; Y = player number (0 or 1)
|
||||
sta WSYNC ; start a new line
|
||||
sec ; set carry flag
|
||||
nop
|
||||
@DivideLoop:
|
||||
sbc #15 ; subtract 15
|
||||
bcs @DivideLoop ; branch until negative
|
||||
eor #7 ; calculate fine offset
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta RESP0,y ; fix coarse position
|
||||
sta HMP0,y ; set fine offset
|
||||
---
|
||||
end
|
||||
|
||||
|
||||
system StaticKernel
|
||||
on preframe do foreach [KernelSection] limit 1
|
||||
---
|
||||
{{!kernelsetup}}
|
||||
---
|
||||
on kernel do foreach [KernelSection]
|
||||
---
|
||||
sta WSYNC
|
||||
{{!kernelsetup}}
|
||||
{{!kerneldraw}}
|
||||
{{!kerneldone}}
|
||||
---
|
||||
on kerneldraw do with [KernelSection]
|
||||
---
|
||||
ldy {{<lines}}
|
||||
@loop:
|
||||
sta WSYNC
|
||||
{{!scanline}}
|
||||
dey
|
||||
bne @loop
|
||||
---
|
||||
on kernelsetup do if [BGColor]
|
||||
---
|
||||
lda {{<bgcolor}}
|
||||
sta COLUBK
|
||||
---
|
||||
on kernelsetup do if [PFColor]
|
||||
---
|
||||
lda {{get pfcolor}}
|
||||
sta COLUPF
|
||||
---
|
||||
on kernelsetup do if [Playfield]
|
||||
---
|
||||
lda {{get pf 0}}
|
||||
sta PF0
|
||||
lda {{get pf 8}}
|
||||
sta PF1
|
||||
lda {{get pf 16}}
|
||||
sta PF2
|
||||
---
|
||||
end
|
||||
|
||||
///
|
||||
|
||||
demo Main
|
||||
using FrameLoop, ResetSwitch, ResetConsole
|
||||
using StaticKernel, JoyButton
|
||||
entity [Player]
|
||||
end
|
||||
entity [KernelSection,BGColor]
|
||||
const lines = 2
|
||||
const bgcolor = $18
|
||||
end
|
||||
entity [KernelSection,BGColor]
|
||||
const lines = 2
|
||||
const bgcolor = $16
|
||||
end
|
||||
entity [KernelSection,BGColor]
|
||||
const lines = 2
|
||||
const bgcolor = $14
|
||||
end
|
||||
entity [KernelSection,BGColor]
|
||||
const lines = 2
|
||||
const bgcolor = $12
|
||||
end
|
||||
entity [KernelSection,BGColor,Playfield]
|
||||
const lines = 10
|
||||
const bgcolor = $14
|
||||
const pf = 0x125244
|
||||
end
|
||||
entity Trees [KernelSection,BGColor,PFColor,Playfield]
|
||||
const lines = 50
|
||||
const bgcolor = $14
|
||||
const pf = 0x112244
|
||||
end
|
||||
entity [KernelSection,BGColor,PFColor,Playfield]
|
||||
const lines = 50
|
||||
const bgcolor = $16
|
||||
const pf = 0x124
|
||||
end
|
||||
entity [KernelSection,BGColor,Playfield]
|
||||
const lines = 10
|
||||
const bgcolor = $18
|
||||
const pf = 0
|
||||
end
|
||||
|
||||
system Local
|
||||
locals 1
|
||||
on joybutton do foreach [PFColor] limit 1 ---
|
||||
inc {{$0}}
|
||||
inc {{set Trees.pfcolor}}
|
||||
---
|
||||
end
|
||||
end
|
250
test/ecs/vcslib.txt
Normal file
250
test/ecs/vcslib.txt
Normal file
@ -0,0 +1,250 @@
|
||||
.scope Main
|
||||
.zeropage
|
||||
PFColor_pfcolor_b0:
|
||||
.res 1
|
||||
.res 1
|
||||
TEMP:
|
||||
.res 1
|
||||
Local__tmp = TEMP+0
|
||||
.code
|
||||
KernelSection_lines_b0:
|
||||
.byte 2
|
||||
.byte 2
|
||||
.byte 2
|
||||
.byte 2
|
||||
.byte 10
|
||||
.byte 50
|
||||
.byte 50
|
||||
.byte 10
|
||||
BGColor_bgcolor_b0:
|
||||
.byte 24
|
||||
.byte 22
|
||||
.byte 20
|
||||
.byte 18
|
||||
.byte 20
|
||||
.byte 20
|
||||
.byte 22
|
||||
.byte 24
|
||||
Playfield_pf_b0:
|
||||
.byte 68
|
||||
.byte 68
|
||||
.byte 36
|
||||
.byte 0
|
||||
Playfield_pf_b8:
|
||||
.byte 82
|
||||
.byte 34
|
||||
.byte 1
|
||||
.byte 0
|
||||
Playfield_pf_b16:
|
||||
.byte 18
|
||||
.byte 17
|
||||
.byte 0
|
||||
.byte 0
|
||||
Main__INITDATA:
|
||||
.byte 0
|
||||
.byte 0
|
||||
__Start:
|
||||
.code
|
||||
|
||||
;;; start action Init main_init
|
||||
|
||||
.include "vcs-ca65.h"
|
||||
.macpack longbranch
|
||||
.define PAL 0
|
||||
__NMI:
|
||||
__Reset:
|
||||
__BRK:
|
||||
CLEAN_START
|
||||
|
||||
ldy #2
|
||||
: lda Main__INITDATA-1,y
|
||||
sta PFColor_pfcolor_b0-1,y
|
||||
dey
|
||||
bne :-
|
||||
; initialize data segment
|
||||
.code
|
||||
|
||||
;;; start action FrameLoop start
|
||||
|
||||
FrameLoop__start__2__NextFrame:
|
||||
FRAME_START
|
||||
.code
|
||||
|
||||
;;; start action StaticKernel preframe
|
||||
|
||||
.code
|
||||
|
||||
;;; start action StaticKernel kernelsetup
|
||||
|
||||
lda #24
|
||||
sta COLUBK
|
||||
|
||||
;;; end action StaticKernel kernelsetup
|
||||
|
||||
;;; start action StaticKernel kernelsetup
|
||||
|
||||
cpx #0+2
|
||||
jcs StaticKernel__kernelsetup__5____skipxhi
|
||||
|
||||
cpx #0
|
||||
jcc StaticKernel__kernelsetup__5____skipxlo
|
||||
|
||||
lda PFColor_pfcolor_b0,x
|
||||
sta COLUPF
|
||||
|
||||
StaticKernel__kernelsetup__5____skipxlo:
|
||||
|
||||
StaticKernel__kernelsetup__5____skipxhi:
|
||||
|
||||
;;; end action StaticKernel kernelsetup
|
||||
|
||||
;;; start action StaticKernel kernelsetup
|
||||
|
||||
cpx #0+4
|
||||
jcs StaticKernel__kernelsetup__6____skipxhi
|
||||
|
||||
cpx #0
|
||||
jcc StaticKernel__kernelsetup__6____skipxlo
|
||||
|
||||
lda Playfield_pf_b0,x
|
||||
sta PF0
|
||||
lda Playfield_pf_b8,x
|
||||
sta PF1
|
||||
lda Playfield_pf_b16,x
|
||||
sta PF2
|
||||
|
||||
StaticKernel__kernelsetup__6____skipxlo:
|
||||
|
||||
StaticKernel__kernelsetup__6____skipxhi:
|
||||
|
||||
;;; end action StaticKernel kernelsetup
|
||||
|
||||
|
||||
;;; end action StaticKernel preframe
|
||||
|
||||
KERNEL_START
|
||||
.code
|
||||
|
||||
;;; start action StaticKernel kernel
|
||||
|
||||
ldx #0
|
||||
StaticKernel__kernel__7____each:
|
||||
|
||||
sta WSYNC
|
||||
.code
|
||||
|
||||
;;; start action StaticKernel kernelsetup
|
||||
|
||||
lda BGColor_bgcolor_b0,x
|
||||
sta COLUBK
|
||||
|
||||
;;; end action StaticKernel kernelsetup
|
||||
|
||||
;;; start action StaticKernel kernelsetup
|
||||
|
||||
cpx #5+2
|
||||
jcs StaticKernel__kernelsetup__9____skipxhi
|
||||
|
||||
cpx #5
|
||||
jcc StaticKernel__kernelsetup__9____skipxlo
|
||||
|
||||
lda PFColor_pfcolor_b0-5,x
|
||||
sta COLUPF
|
||||
|
||||
StaticKernel__kernelsetup__9____skipxlo:
|
||||
|
||||
StaticKernel__kernelsetup__9____skipxhi:
|
||||
|
||||
;;; end action StaticKernel kernelsetup
|
||||
|
||||
;;; start action StaticKernel kernelsetup
|
||||
|
||||
cpx #4
|
||||
jcc StaticKernel__kernelsetup__10____skipxlo
|
||||
|
||||
lda Playfield_pf_b0-4,x
|
||||
sta PF0
|
||||
lda Playfield_pf_b8-4,x
|
||||
sta PF1
|
||||
lda Playfield_pf_b16-4,x
|
||||
sta PF2
|
||||
|
||||
StaticKernel__kernelsetup__10____skipxlo:
|
||||
|
||||
;;; end action StaticKernel kernelsetup
|
||||
|
||||
.code
|
||||
|
||||
;;; start action StaticKernel kerneldraw
|
||||
|
||||
ldy KernelSection_lines_b0,x
|
||||
StaticKernel__kerneldraw__11__loop:
|
||||
sta WSYNC
|
||||
|
||||
dey
|
||||
bne StaticKernel__kerneldraw__11__loop
|
||||
|
||||
;;; end action StaticKernel kerneldraw
|
||||
|
||||
|
||||
|
||||
inx
|
||||
cpx #8
|
||||
jne StaticKernel__kernel__7____each
|
||||
StaticKernel__kernel__7____exit:
|
||||
|
||||
;;; end action StaticKernel kernel
|
||||
|
||||
KERNEL_END
|
||||
.code
|
||||
|
||||
;;; start action JoyButton postframe
|
||||
|
||||
lda INPT4 ;read button input
|
||||
bmi JoyButton__postframe__12__NotPressed
|
||||
.code
|
||||
|
||||
;;; start action Local joybutton
|
||||
|
||||
inc Local__tmp+0
|
||||
inc PFColor_pfcolor_b0
|
||||
|
||||
;;; end action Local joybutton
|
||||
|
||||
JoyButton__postframe__12__NotPressed:
|
||||
|
||||
;;; end action JoyButton postframe
|
||||
|
||||
FRAME_END
|
||||
.code
|
||||
|
||||
;;; start action ResetSwitch nextframe
|
||||
|
||||
lsr SWCHB ; test Game Reset switch
|
||||
bcs ResetSwitch__nextframe__14__NoStart
|
||||
.code
|
||||
|
||||
;;; start action ResetConsole resetswitch
|
||||
|
||||
jmp Main::__Reset ; jump to Reset handler
|
||||
|
||||
;;; end action ResetConsole resetswitch
|
||||
|
||||
ResetSwitch__nextframe__14__NoStart:
|
||||
|
||||
;;; end action ResetSwitch nextframe
|
||||
|
||||
jmp FrameLoop__start__2__NextFrame ; loop to next frame
|
||||
|
||||
;;; end action FrameLoop start
|
||||
; start main routine
|
||||
.segment "VECTORS"
|
||||
Return: .word $6060
|
||||
VecNMI:
|
||||
VecReset: .word Main::__Reset
|
||||
VecBRK: .word Main::__BRK
|
||||
|
||||
;;; end action Init main_init
|
||||
|
||||
.endscope
|
||||
Main__Start = Main::__Start
|
Loading…
Reference in New Issue
Block a user