ecs: data <field>, 48 pixel decoder

This commit is contained in:
Steven Hugg 2022-02-09 11:45:40 -06:00
parent 5081307d81
commit 999aed9cb4
4 changed files with 96 additions and 31 deletions

View File

@ -266,3 +266,16 @@ LINESD12 = 16
SLEEP cycles SLEEP cycles
.endif .endif
.endmacro .endmacro
;-----------------------------------------------------------
; SLEEPH - sleep macro that uses PHA/PLA for 12 cycle delays
.macro SLEEPH cycles
.if cycles >= 9 || cycles = 7
pha
pla
SLEEPH (cycles-7)
.else
SLEEP cycles
.endif
.endmacro

View File

@ -103,6 +103,34 @@ export class VCSVersatilePlayfieldDecoder extends LineDecoder {
} }
} }
export class VCSBitmap48Decoder extends LineDecoder {
parse() {
let height = this.lines.length;
let bitmap0 = new Uint8Array(height);
let bitmap1 = new Uint8Array(height);
let bitmap2 = new Uint8Array(height);
let bitmap3 = new Uint8Array(height);
let bitmap4 = new Uint8Array(height);
let bitmap5 = new Uint8Array(height);
for (let i=0; i<height; i++) {
let toks = this.lines[height - 1 - i];
this.assertTokens(toks, 1);
bitmap0[i] = this.decodeBits(toks[0].slice(0,8), 8, true);
bitmap1[i] = this.decodeBits(toks[0].slice(8,16), 8, true);
bitmap2[i] = this.decodeBits(toks[0].slice(16,24), 8, true);
bitmap3[i] = this.decodeBits(toks[0].slice(24,32), 8, true);
bitmap4[i] = this.decodeBits(toks[0].slice(32,40), 8, true);
bitmap5[i] = this.decodeBits(toks[0].slice(40,48), 8, true);
}
return {
properties: {
bitmap0, bitmap1, bitmap2, bitmap3, bitmap4, bitmap5,
height: height-1
}
}
}
}
export function newDecoder(name: string, text: string) : LineDecoder | undefined { export function newDecoder(name: string, text: string) : LineDecoder | undefined {
let cons = (DECODERS as any)[name]; let cons = (DECODERS as any)[name];
if (cons) return new cons(text); if (cons) return new cons(text);
@ -111,4 +139,5 @@ export function newDecoder(name: string, text: string) : LineDecoder | undefined
const DECODERS = { const DECODERS = {
'vcs_sprite': VCSSpriteDecoder, 'vcs_sprite': VCSSpriteDecoder,
'vcs_versatile': VCSVersatilePlayfieldDecoder, 'vcs_versatile': VCSVersatilePlayfieldDecoder,
'vcs_bitmap48': VCSBitmap48Decoder,
} }

View File

@ -55,7 +55,10 @@ only worry about intersection when non-contiguous ranges?
crazy idea -- full expansion, then relooper crazy idea -- full expansion, then relooper
how to avoid cycle crossing for critical code and data? 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
*/ */
@ -150,7 +153,7 @@ export interface ActionWithJoin extends ActionWithQuery {
export type Action = ActionWithQuery | ActionWithJoin | ActionOnce; export type Action = ActionWithQuery | ActionWithJoin | ActionOnce;
export type DataValue = number | boolean | Uint8Array | Uint16Array; export type DataValue = number | boolean | Uint8Array | Uint16Array | Uint32Array;
export type DataField = { name: string } & DataType; export type DataField = { name: string } & DataType;
@ -206,7 +209,7 @@ export class Dialect_CA65 {
{{%code}} {{%code}}
inx inx
cpx #{{%ecount}} cpx #{{%ecount}}
bne @__each jne @__each
@__exit: @__exit:
`; `;
@ -215,7 +218,7 @@ export class Dialect_CA65 {
@__each: @__each:
{{%code}} {{%code}}
dex dex
bpl @__each jpl @__each
@__exit: @__exit:
`; `;
@ -226,7 +229,7 @@ export class Dialect_CA65 {
{{%code}} {{%code}}
iny iny
cpy #{{%ecount}} cpy #{{%ecount}}
bne @__each jne @__each
@__exit: @__exit:
`; `;
@ -236,20 +239,20 @@ export class Dialect_CA65 {
ldx {{%joinfield}},y ldx {{%joinfield}},y
{{%code}} {{%code}}
dey dey
bpl @__each jpl @__each
@__exit: @__exit:
`; `;
ASM_FILTER_RANGE_LO_X = ` ASM_FILTER_RANGE_LO_X = `
cpx #{{%xofs}} cpx #{{%xofs}}
bcc @__skipxlo jcc @__skipxlo
{{%code}} {{%code}}
@__skipxlo: @__skipxlo:
` `
ASM_FILTER_RANGE_HI_X = ` ASM_FILTER_RANGE_HI_X = `
cpx #{{%xofs}}+{{%ecount}} cpx #{{%xofs}}+{{%ecount}}
bcs @__skipxhi jcs @__skipxhi
{{%code}} {{%code}}
@__skipxhi: @__skipxhi:
` `
@ -259,7 +262,7 @@ export class Dialect_CA65 {
txa txa
pha pha
lda {{%mapping}},x lda {{%mapping}},x
bmi @__mapskip jmi @__mapskip
tax tax
{{%code}} {{%code}}
@__mapskip: @__mapskip:
@ -295,8 +298,8 @@ export class Dialect_CA65 {
fieldsymbol(component: ComponentType, field: DataField, bitofs: number) { fieldsymbol(component: ComponentType, field: DataField, bitofs: number) {
return `${component.name}_${field.name}_b${bitofs}`; return `${component.name}_${field.name}_b${bitofs}`;
} }
datasymbol(component: ComponentType, field: DataField, eid: number) { datasymbol(component: ComponentType, field: DataField, eid: number, bitofs: number) {
return `${component.name}_${field.name}_e${eid}`; return `${component.name}_${field.name}_e${eid}_b${bitofs}`;
} }
code() { code() {
return `.code\n`; return `.code\n`;
@ -522,6 +525,7 @@ class EntitySet {
// todo: generalize // todo: generalize
class ActionCPUState { class ActionCPUState {
loops: EntitySet[] = [];
x: EntitySet | null = null; x: EntitySet | null = null;
y: EntitySet | null = null; y: EntitySet | null = null;
xofs: number = 0; xofs: number = 0;
@ -561,12 +565,14 @@ class ActionEval {
if (state.x && state.y) throw new ECSError('no more index registers', this.action); if (state.x && state.y) throw new ECSError('no more index registers', this.action);
if (state.x) state.y = this.qr; if (state.x) state.y = this.qr;
else state.x = this.qr; else state.x = this.qr;
state.loops = state.loops.concat([this.qr]);
break; break;
case 'join': case 'join':
if (state.x || state.y) throw new ECSError('no free index registers for join', this.action); if (state.x || state.y) throw new ECSError('no free index registers for join', this.action);
this.jr = new EntitySet(this.scope, (this.action as ActionWithJoin).join); this.jr = new EntitySet(this.scope, (this.action as ActionWithJoin).join);
state.y = this.qr; state.y = this.qr;
state.x = this.jr; state.x = this.jr;
state.loops = state.loops.concat([this.qr]);
break; break;
case 'if': case 'if':
case 'with': case 'with':
@ -625,11 +631,16 @@ class ActionEval {
// select subset of entities // select subset of entities
let fullEntityCount = this.qr.entities.length; //entities.length.toString(); let fullEntityCount = this.qr.entities.length; //entities.length.toString();
let entities = this.entities; let entities = this.entities;
let loops = this.scope.state.loops;
let loopents = loops[loops.length-1]?.entities;
// TODO: let loopreduce = !loopents || entities.length < loopents.length;
//console.log(action.event, entities.length, loopents.length);
// filter entities from loop? // filter entities from loop?
if (action.select == 'with' && entities.length > 1) { // TODO: when to ignore if entities.length == 1 and not in for loop?
if (action.select == 'with') {
code = this.wrapCodeInFilter(code); code = this.wrapCodeInFilter(code);
} }
if (action.select == 'if' && entities.length > 1) { if (action.select == 'if') {
code = this.wrapCodeInFilter(code); code = this.wrapCodeInFilter(code);
} }
if (action.select == 'foreach' && entities.length > 1) { if (action.select == 'foreach' && entities.length > 1) {
@ -678,15 +689,24 @@ class ActionEval {
let bitofs = parseInt(args[1] || '0'); let bitofs = parseInt(args[1] || '0');
return this.generateCodeForField(fieldName, bitofs, canwrite); return this.generateCodeForField(fieldName, bitofs, canwrite);
} }
__base(args: string[]) { parseFieldArgs(args: string[]) {
// TODO: refactor into generateCode..
let fieldName = args[0]; let fieldName = args[0];
let bitofs = parseInt(args[1] || '0'); let bitofs = parseInt(args[1] || '0');
let component = this.em.singleComponentWithFieldName(this.qr.atypes, fieldName, this.action); let component = this.em.singleComponentWithFieldName(this.qr.atypes, fieldName, this.action);
let field = component.fields.find(f => f.name == fieldName); let field = component.fields.find(f => f.name == fieldName);
if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, this.action); if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, this.action);
return { component, field, bitofs };
}
__base(args: string[]) {
let { component, field, bitofs } = this.parseFieldArgs(args);
return this.dialect.fieldsymbol(component, field, bitofs); return this.dialect.fieldsymbol(component, field, bitofs);
} }
__data(args: string[]) {
let { component, field, bitofs } = this.parseFieldArgs(args);
if (this.qr.entities.length != 1) throw new ECSError(`data command operates on exactly one entity`); // TODO?
let eid = this.qr.entities[0].id; // TODO?
return this.dialect.datasymbol(component, field, eid, bitofs);
}
__index(args: string[]) { __index(args: string[]) {
// TODO: check select type and if we actually have an index... // TODO: check select type and if we actually have an index...
let ident = args[0]; let ident = args[0];
@ -798,6 +818,7 @@ class ActionEval {
if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO
// TODO: dialect // TODO: dialect
let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative?
// TODO: array field baseoffset?
if (baseLookup) { if (baseLookup) {
return this.dialect.absolute(ident); return this.dialect.absolute(ident);
} else if (entities.length == 1) { } else if (entities.length == 1) {
@ -983,11 +1004,13 @@ export class EntityScope implements SourceLocated {
// this is not a constant // this is not a constant
// is it a bounded array? (TODO) // is it a bounded array? (TODO)
if (f.dtype == 'array' && f.index) { if (f.dtype == 'array' && f.index) {
let datasym = this.dialect.datasymbol(c, f, e.id); let datasym = this.dialect.datasymbol(c, f, e.id, 0);
let databytes = getFieldLength(f.index); let databytes = getFieldLength(f.index);
let offset = this.bss.allocateBytes(datasym, databytes); let offset = this.bss.allocateBytes(datasym, databytes);
// TODO? this.allocatePointerArray(c, f, datasym, entcount);
let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrlosym = this.dialect.fieldsymbol(c, f, 0);
let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let ptrhisym = this.dialect.fieldsymbol(c, f, 8);
// TODO: what if we don't need a pointer array?
let loofs = segment.allocateBytes(ptrlosym, entcount); let loofs = segment.allocateBytes(ptrlosym, entcount);
let hiofs = segment.allocateBytes(ptrhisym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount);
if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`;
@ -997,8 +1020,9 @@ export class EntityScope implements SourceLocated {
} else { } else {
// this is a constant // this is a constant
// is it a byte array? // is it a byte array?
//TODO? if (ArrayBuffer.isView(v) && f.dtype == 'array') {
if (v instanceof Uint8Array && f.dtype == 'array') { if (v instanceof Uint8Array && f.dtype == 'array') {
let datasym = this.dialect.datasymbol(c, f, e.id); let datasym = this.dialect.datasymbol(c, f, e.id, 0);
segment.allocateInitData(datasym, v); segment.allocateInitData(datasym, v);
let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrlosym = this.dialect.fieldsymbol(c, f, 0);
let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let ptrhisym = this.dialect.fieldsymbol(c, f, 8);
@ -1007,7 +1031,6 @@ export class EntityScope implements SourceLocated {
if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`;
segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 };
segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 };
// TODO: } else if (v instanceof Uint16Array) {
} else if (typeof v === 'number') { } else if (typeof v === 'number') {
// more than 1 entity, add an array // more than 1 entity, add an array
if (entcount > 1) { if (entcount > 1) {
@ -1020,7 +1043,8 @@ export class EntityScope implements SourceLocated {
} }
// TODO: what if mix of var, const, and init values? // TODO: what if mix of var, const, and init values?
} else { } else {
throw new ECSError(`unhandled constant ${e.id}:${cfname}`); // TODO: bad error message - should say "wrong type, should be array"
throw new ECSError(`unhandled constant ${e.id}:${cfname} -- ${typeof v}`);
} }
} }
} }
@ -1044,8 +1068,8 @@ export class EntityScope implements SourceLocated {
initbytes[offset] = (initvalue >> a.bit) & ((1 << a.width) - 1); initbytes[offset] = (initvalue >> a.bit) & ((1 << a.width) - 1);
} }
} else if (initvalue instanceof Uint8Array) { } else if (initvalue instanceof Uint8Array) {
// TODO??? // TODO: 16/32...
let datasym = this.dialect.datasymbol(c, f, e.id); let datasym = this.dialect.datasymbol(c, f, e.id, 0);
let ofs = this.bss.symbols[datasym]; let ofs = this.bss.symbols[datasym];
initbytes.set(initvalue, ofs); initbytes.set(initvalue, ofs);
} else { } else {
@ -1095,13 +1119,12 @@ export class EntityScope implements SourceLocated {
//s += `\n; event ${event}\n`; //s += `\n; event ${event}\n`;
systems = systems.filter(s => this.systems.includes(s)); systems = systems.filter(s => this.systems.includes(s));
for (let sys of systems) { for (let sys of systems) {
let tmplabel = this.dialect.tempLabel(sys);
for (let action of sys.actions) { for (let action of sys.actions) {
if (action.event == event) { if (action.event == event) {
// TODO: use Tokenizer so error msgs are better // TODO: use Tokenizer so error msgs are better
// TODO: keep event tree // TODO: keep event tree
let codeeval = new ActionEval(this, sys, action); let codeeval = new ActionEval(this, sys, action);
codeeval.tmplabel = tmplabel; codeeval.tmplabel = this.dialect.tempLabel(sys);
codeeval.begin(); codeeval.begin();
s += this.dialect.comment(`start action ${sys.name} ${event}`); // TODO s += this.dialect.comment(`start action ${sys.name} ${event}`); // TODO
s += codeeval.codeToString(); s += codeeval.codeToString();

View File

@ -83,10 +83,10 @@ FrameLoop__start__2__NextFrame:
;;; start action StaticKernel kernelsetup ;;; start action StaticKernel kernelsetup
cpx #0+2 cpx #0+2
bcs StaticKernel__kernelsetup__5____skipxhi jcs StaticKernel__kernelsetup__5____skipxhi
cpx #0 cpx #0
bcc StaticKernel__kernelsetup__5____skipxlo jcc StaticKernel__kernelsetup__5____skipxlo
lda PFColor_pfcolor_b0,x lda PFColor_pfcolor_b0,x
sta COLUPF sta COLUPF
@ -100,10 +100,10 @@ StaticKernel__kernelsetup__5____skipxhi:
;;; start action StaticKernel kernelsetup ;;; start action StaticKernel kernelsetup
cpx #0+4 cpx #0+4
bcs StaticKernel__kernelsetup__6____skipxhi jcs StaticKernel__kernelsetup__6____skipxhi
cpx #0 cpx #0
bcc StaticKernel__kernelsetup__6____skipxlo jcc StaticKernel__kernelsetup__6____skipxlo
lda Playfield_pf_b0,x lda Playfield_pf_b0,x
sta PF0 sta PF0
@ -142,10 +142,10 @@ StaticKernel__kernel__7____each:
;;; start action StaticKernel kernelsetup ;;; start action StaticKernel kernelsetup
cpx #5+2 cpx #5+2
bcs StaticKernel__kernelsetup__9____skipxhi jcs StaticKernel__kernelsetup__9____skipxhi
cpx #5 cpx #5
bcc StaticKernel__kernelsetup__9____skipxlo jcc StaticKernel__kernelsetup__9____skipxlo
lda PFColor_pfcolor_b0-5,x lda PFColor_pfcolor_b0-5,x
sta COLUPF sta COLUPF
@ -159,7 +159,7 @@ StaticKernel__kernelsetup__9____skipxhi:
;;; start action StaticKernel kernelsetup ;;; start action StaticKernel kernelsetup
cpx #4 cpx #4
bcc StaticKernel__kernelsetup__10____skipxlo jcc StaticKernel__kernelsetup__10____skipxlo
lda Playfield_pf_b0-4,x lda Playfield_pf_b0-4,x
sta PF0 sta PF0
@ -182,7 +182,7 @@ StaticKernel__kernel__7__loop:
inx inx
cpx #8 cpx #8
bne StaticKernel__kernel__7____each jne StaticKernel__kernel__7____each
StaticKernel__kernel__7____exit: StaticKernel__kernel__7____exit:
;;; end action StaticKernel kernel ;;; end action StaticKernel kernel