From 3f87b5dbb8f413f85f8ad1cf7200fd839c4fb311 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 26 Jan 2022 10:54:57 -0600 Subject: [PATCH 001/104] ecs branch --- src/common/ecs/ecs.ts | 672 +++++++++++++++++++++++++++++++++++ src/common/ecs/tsconfig.json | 9 + 2 files changed, 681 insertions(+) create mode 100644 src/common/ecs/ecs.ts create mode 100644 src/common/ecs/tsconfig.json diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts new file mode 100644 index 00000000..53174c55 --- /dev/null +++ b/src/common/ecs/ecs.ts @@ -0,0 +1,672 @@ + +// 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) +// +// 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? + +export interface Entity { + id: number; + etype: EntityArchetype; + consts: {[name: string]: DataValue}; +} + +export interface EntityConst { + component: ComponentType; + name: string; + value: DataValue; +} + +export interface EntityArchetype { + components: ComponentType[]; +} + +export interface ComponentType { + name: string; + fields: DataField[]; + optional?: boolean; +} + +export interface Query { + include: string[]; + listen?: string[]; + exclude?: string[]; + updates?: string[]; +} + +export interface System { + name: string; + query: Query; + tempbytes?: number; + actions: CodeFragment[]; + emits?: string[]; + live?: EntityArchetype[] | null; +} + +export interface CodeFragment { + text: string; + event: string; + iterate: 'once' | 'each' +} + +export type DataValue = number | boolean | Uint8Array; + +export type DataField = { name: string } & DataType; + +export type DataType = IntType | ArrayType | RefType; + +export interface IntType { + dtype: 'int' + lo: number + hi: number +} + +export interface ArrayType { + dtype: 'array' + elem: DataType + index?: DataType +} + +export interface RefType { + dtype: 'ref' + query: Query +} + +interface FieldArray { + component: ComponentType; + field: DataField; + elo: number; + ehi: number; + access?: FieldAccess[]; +} + +interface FieldAccess { + symbol: string; + bit: number; + width: number; +} + +interface ConstByte { + symbol: string; + bitofs: number; +} + +class SourceFileExport { + lines : string[] = []; + + comment(text: string) { + this.lines.push(';' + text); + } + segment(seg: string, segtype: 'rodata' | 'bss') { + if (segtype == 'bss') + this.lines.push(` seg.u ${seg}`); + else + this.lines.push(` seg ${seg}`); + } + label(sym: string) { + this.lines.push(`${sym}:`); + } + byte(b: number | ConstByte | undefined) { + if (b === undefined) { + this.lines.push(` .ds 1`); + } else if (typeof b === 'number') { + if (b < 0 || b > 255) throw new Error(`out of range byte ${b}`); + this.lines.push(` .byte ${b}`) + } else { + this.lines.push(` .byte (${b.symbol} >> ${b.bitofs}) & 0xff`) + } + } + toString() { + return this.lines.join('\n'); + } +} + +class Segment { + symbols: {[sym: string]: number} = {}; + ofs2sym = new Map(); + fieldranges: {[cfname: string]: FieldArray} = {}; + size: number = 0; + initdata: (number | ConstByte | undefined)[] = []; + + allocateBytes(name: string, bytes: number) { + let ofs = this.size; + this.symbols[name] = ofs; + if (!this.ofs2sym.has(ofs)) + this.ofs2sym.set(ofs, []); + this.ofs2sym.get(ofs)?.push(name); + this.size += bytes; + return ofs; + } + // TODO: optimize shared data + allocateInitData(name: string, bytes: Uint8Array) { + let ofs = this.allocateBytes(name, bytes.length); + for (let i=0; i 256 entities? + } + return 0; +} + +const ASM_ITERATE_EACH = ` + ldx #%{elo} +.loop: + %{action} + inx + cpx #%{ehi}+1 + bne .loop +`; + +export class EntityScope { + childScopes : EntityScope[] = []; + entities : Entity[] = []; + bss = new Segment(); + rodata = new Segment(); + code = new Segment(); + componentsInScope = new Set(); + + constructor( + public readonly em: EntityManager, + public readonly name: string, + public readonly parent: EntityScope | undefined + ) { + parent?.childScopes.push(this); + } + newEntity(etype: EntityArchetype) : Entity { + // TODO: add parent ID? lock parent scope? + let id = this.entities.length; + let entity : Entity = {id, etype, consts:{}}; + this.em.archtypes.add(etype); + for (let c of etype.components) { + this.componentsInScope.add(c.name); + } + this.entities.push(entity); + return entity; + } + *iterateFields() { + for (let i=0; i (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); + let f; + while (f = fields.pop()) { + let name = f.component.name + "_" + f.field.name; + // TODO: doesn't work for packed arrays too well + let bits = getPackedFieldSize(f.field); + // variable size? make it a pointer + if (bits == 0) bits = 16; + let rangelen = (f.ehi - f.elo + 1); + let bytesperelem = Math.ceil(bits/8) * rangelen; + // TODO: packing bits + // TODO: split arrays + f.access = []; + for (let i=0; i fieldrange.elo) { + // more than one element, add an array + // TODO + } + //console.log(cfname, i, v, fieldrange); + //segment.allocateInitData(cfname, ); + } + } + //console.log(segment.initdata) + } + setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { + e.consts[component.name + '.' + fieldName] = value; + } + dump(file: SourceFileExport) { + file.segment(`${this.name}_CODE`, 'rodata'); + this.rodata.dump(file); + file.segment(`${this.name}_DATA`, 'bss'); + this.bss.dump(file); + } + generateCode() { + let code = this.generateCodeForEvent('init'); + console.log(code); + } + generateCodeForEvent(event: string): string { + // find systems that respond to event + // and have entities in this scope + let systems = this.getSystems([event]); + if (systems.length == 0) { + console.log(`no system responds to ${event}`); // TODO: warning + } + let s = ''; + //s += `\n; event ${event}\n`; + let emitcode : {[event: string] : string} = {}; + for (let sys of systems) { + if (sys.emits) { + for (let emit of sys.emits) { + //console.log('>', emit); + // TODO: cycles + emitcode[emit] = this.generateCodeForEvent(emit); + //console.log('<', emit, emitcode[emit].length); + } + } + for (let action of sys.actions) { + let code = action.text; + if (action.event == event) { + // TODO: find loops + if (action.iterate == 'each') { + let ents = this.entitiesMatching(sys.query); + console.log(sys.name, action.event, ents); + //frag = this.iterateCode(frag); + } + // TODO: better parser of ${} + for (let [k,v] of Object.entries(emitcode)) { + let frag = v; + code = code.replace(`%{${k}}`, frag); + } + // anything not replaced? + let unused = /\%\{.+?\}/.exec(code); + if (unused) { + //throw new Error(`${sys.name}:${action.event} did not replace ${unused[0]}`); + } + // TODO: check that this happens once? + s += `\n; action ${sys.name}:${action.event}\n`; + s += code; + } + } + } + return s; + } + entitiesMatching(q: Query) { + let result = []; + let atypes = this.em.archetypesMatching(q); + for (let e of this.entities) { + for (let a of atypes) { + if (e.etype == a.etype) + result.push(e); + } + } + return result; + } + getSystems(events: string[]) { + let result = []; + for (let sys of Object.values(this.em.systems)) { + if (this.systemListensTo(sys, events)) { + result.push(sys); + } + } + return result; + } + systemListensTo(sys: System, events: string[]) { + for (let action of sys.actions) { + if (action.event != null && events.includes(action.event)) { + let archs = this.em.archetypesMatching(sys.query); + for (let arch of archs) { + for (let ctype of arch.cmatch) { + if (this.hasComponent(ctype)) { + return true; + } + } + } + } + } + } + hasComponent(ctype: ComponentType) { + return this.componentsInScope.has(ctype.name); + } +} + +export class EntityManager { + archtypes = new Set(); + components : {[name: string]: ComponentType} = {}; + systems : {[name: string]: System} = {}; + scopes : {[name: string]: EntityScope} = {}; + + newScope(name: string, parent?: EntityScope) { + let scope = new EntityScope(this, name, parent); + if (this.scopes[name]) throw new Error(`scope ${name} already defined`); + this.scopes[name] = scope; + return scope; + } + defineComponent(ctype: ComponentType) { + if (this.components[ctype.name]) throw new Error(`component ${name} already defined`); + return this.components[ctype.name] = ctype; + } + defineSystem(system: System) { + if (this.systems[system.name]) throw new Error(`system ${name} already defined`); + this.systems[system.name] = system; + } + componentsMatching(q: Query, etype: EntityArchetype) { + let list = []; + for (let c of etype.components) { + let cname = c.name; + // TODO: 0 includes == all entities? + if (q.include.length == 0 || q.include.includes(cname)) { + if (!q.exclude?.includes(cname)) { + list.push(c); + } + } + } + return list; + } + archetypesMatching(q: Query) { + let result : {etype: EntityArchetype, cmatch: ComponentType[]}[] = []; + this.archtypes.forEach(etype => { + let cmatch = this.componentsMatching(q, etype); + if (cmatch.length > 0) { + result.push({etype, cmatch}); + } + }); + return result; + } + emitCode(root: System) { + } +} + +/// + +const TEMPLATE_INIT = ` +Start: + CLEAN_START + %{start} +` + +const TEMPLATE1 = ` +.NextFrame: + lsr SWCHB ; test Game Reset switch + bcc Start ; reset? + VERTICAL_SYNC + TIMER_SETUP 37 + %{preframe} + TIMER_WAIT + TIMER_SETUP 192 + %{kernel} + TIMER_WAIT + TIMER_SETUP 29 + %{postframe} + TIMER_WAIT + jmp .NextFrame +`; + +const TEMPLATE2 = ` +#ifdef EVENT_joyup + lda #%00100000 ;Up? + bit SWCHA + bne SkipMoveUp + %{joyup} +.SkipMoveUp +#endif +`; + +const TEMPLATE3 = ` + lda %{ypos},x + sec + sbc #1 + bcc .noclip + sta %{ypos},x +.noclip +`; + +const TEMPLATE4_S = ` + lda %{@sprite.bitmap+0} ; bitmap address + sta temp+0 ; temp space + lda %{@sprite.bitmap+1} ; bitmap address + sta temp+1 ; temp space +` + +const TEMPLATE4_K = ` + ldx %{kernel.numlines} ; lines in kernel +LVScan + txa ; X -> A + sec ; set carry for subtract + sbc YPos ; local coordinate + cmp %{sprite.height} ; in sprite? + bcc InSprite ; yes, skip over next + lda #0 ; not in sprite, load 0 +InSprite + tay ; local coord -> Y + lda (temp+0),y ; lookup color + sta WSYNC ; sync w/ scanline + sta GRP0 ; store bitmap + lda (temp+2),y ; lookup color + sta COLUP0 ; store color + dex ; decrement X + bne LVScan ; repeat until 192 lines +`; + +const SET_XPOS = ` + lda %{sprite.xpos} + ldx %(sprite.plyrindex} + jsr SetHorizPos +` + +const SETHORIZPOS = ` +SetHorizPos: subroutine + sta WSYNC ; start a new line + SLEEP 3 + sec ; set carry flag +SetHorizPosLoop: + sbc #15 ; subtract 15 + bcs SetHorizPosLoop ; branch until negative +SetHorizPosAfter: + ASSERT_SAME_PAGE SetHorizPosLoop, SetHorizPosAfter + eor #7 ; calculate fine offset + asl + asl + asl + asl + sta RESP0,x ; fix coarse position + sta HMP0,x ; set fine offset + sta WSYNC +Return: ; for SLEEP macro, etc. + rts ; return to caller +` + +function test() { + let em = new EntityManager(); + + let c_kernel = em.defineComponent({name:'kernel', fields:[ + {name:'lines', dtype:'int', lo:0, hi:255} + ]}) + let c_sprite = em.defineComponent({name:'sprite', fields:[ + {name:'height', dtype:'int', lo:0, hi:255}, + {name:'plyrindex', dtype:'int', lo:0, hi:1}, + {name:'flags', dtype:'int', lo:0, hi:255}, + ]}) + let c_player = em.defineComponent({name:'player', fields:[ + //TODO: optional? + ]}) + let c_hasbitmap = em.defineComponent({name:'hasbitmap', fields:[ + {name:'bitmap', dtype:'ref', query:{include:['bitmap']}}, + ]}) + let c_hascolormap = em.defineComponent({name:'hascolormap', fields:[ + {name:'colormap', dtype:'ref', query:{include:['colormap']}}, + ]}) + let c_bitmap = em.defineComponent({name:'bitmap', fields:[ + {name:'data', dtype:'array', elem:{ dtype:'int', lo:0, hi:255 }} + ]}) + let c_colormap = em.defineComponent({name:'colormap', fields:[ + {name:'data', dtype:'array', elem:{ dtype:'int', lo:0, hi:255 }} + ]}) + let c_xpos = em.defineComponent({name:'xpos', fields:[ + {name:'xpos', dtype:'int', lo:0, hi:255} + ]}) + let c_ypos = em.defineComponent({name:'ypos', fields:[ + {name:'ypos', dtype:'int', lo:0, hi:255} + ]}) + let c_xyvel = em.defineComponent({name:'xyvel', fields:[ + {name:'xvel', dtype:'int', lo:-8, hi:7}, + {name:'yvel', dtype:'int', lo:-8, hi:7} + ]}) + + // init -> [start] -> frameloop + // frameloop -> [preframe] [kernel] [postframe] + + // TODO: where is kernel numlines? + // temp between preframe + frame? + em.defineSystem({ + name:'kernel-simple', + tempbytes:4, + query:{ + include:['sprite','bitmap','colormap','ypos'], + }, + actions:[ + { text:TEMPLATE4_S, event:'preframe', iterate:'once' }, + { text:TEMPLATE4_K, event:'kernel', iterate:'once' }, + ] + }) + em.defineSystem({ + name:'set-xpos', + query:{ + include:['sprite','xpos'] + }, + actions:[ + { text:SET_XPOS, event:'preframe', iterate:'each' }, + //{ text:SETHORIZPOS }, + ] + }) + // TODO: how to have subsystems? maybe need Scopes + // TODO: easy stagger of system update? + // TODO: easy lookup tables + // TODO: how to init? + // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html + em.defineSystem({ + name:'init', + emits:['start'], + query:{ + include:[], // ??? + }, + actions:[ + { text:TEMPLATE_INIT, event:'init', iterate:'once' } + ] + }) + em.defineSystem({ + name:'frameloop', + emits:['preframe','kernel','postframe'], + query:{ + include:['kernel'], // ??? + }, + actions:[ + { text:TEMPLATE1, event:'start', iterate:'once' } + ] + }) + em.defineSystem({ + name:'joyread', + query:{ + include:['player'] + }, + emits:['joyup','joydown','joyleft','joyright','joybutton'], + actions:[ + { text:TEMPLATE2, event:'postframe', iterate:'each' } + ] + }); + em.defineSystem({ + name:'simple-move', + query:{ + include:['player','xpos','ypos'] + }, + actions:[ + { text:TEMPLATE3, event:'joyup', iterate:'each' } + ] + }); + + let root = em.newScope("Root"); + let scene = em.newScope("Scene", root); + let e_ekernel = root.newEntity({components:[c_kernel]}); + root.setConstValue(e_ekernel, c_kernel, 'lines', 192); + + let e_bitmap0 = root.newEntity({components:[c_bitmap]}); + // TODO: store array sizes? + root.setConstValue(e_bitmap0, c_bitmap, 'data', new Uint8Array([1,2,3,4,5])); + + let e_colormap0 = root.newEntity({components:[c_colormap]}); + root.setConstValue(e_colormap0, c_colormap, 'data', new Uint8Array([1,2,3,4,5])); + + let ea_playerSprite = {components:[c_sprite,c_hasbitmap,c_hascolormap,c_xpos,c_ypos,c_player]}; + let e_player0 = root.newEntity(ea_playerSprite); + let e_player1 = root.newEntity(ea_playerSprite); + + let src = new SourceFileExport(); + root.analyzeEntities(); + root.generateCode(); + root.dump(src); + console.log(src.toString()); +} + +test(); diff --git a/src/common/ecs/tsconfig.json b/src/common/ecs/tsconfig.json new file mode 100644 index 00000000..44c1e47b --- /dev/null +++ b/src/common/ecs/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "noImplicitThis": true, + "noImplicitAny": true, + "preserveConstEnums": true, + "alwaysStrict": true, + "strictNullChecks": true, + } +} From b00d43d1ea52bea748424e62f006b35d77b139df Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 26 Jan 2022 19:12:49 -0600 Subject: [PATCH 002/104] ecs 1st compile --- src/common/ecs/ecs.ts | 436 ++++++++++++++++++++++++++++++------------ 1 file changed, 312 insertions(+), 124 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 53174c55..06e46f16 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -15,6 +15,10 @@ // to change scope, fire event w/ scope name // - how to handle bank-switching? +function mksymbol(c: ComponentType, fieldName: string) { + return c.name + '_' + fieldName; +} + export interface Entity { id: number; etype: EntityArchetype; @@ -46,9 +50,9 @@ export interface Query { export interface System { name: string; + actions: CodeFragment[]; query: Query; tempbytes?: number; - actions: CodeFragment[]; emits?: string[]; live?: EntityArchetype[] | null; } @@ -101,6 +105,11 @@ interface ConstByte { bitofs: number; } +interface ArchetypeMatch { + etype: EntityArchetype; + cmatch: ComponentType[]; +} + class SourceFileExport { lines : string[] = []; @@ -108,10 +117,13 @@ class SourceFileExport { this.lines.push(';' + text); } segment(seg: string, segtype: 'rodata' | 'bss') { - if (segtype == 'bss') + if (segtype == 'bss') { this.lines.push(` seg.u ${seg}`); - else + this.lines.push(` org $80`); // TODO + } else { this.lines.push(` seg ${seg}`); + this.lines.push(` org $f000`); // TODO + } } label(sym: string) { this.lines.push(`${sym}:`); @@ -123,9 +135,13 @@ class SourceFileExport { if (b < 0 || b > 255) throw new Error(`out of range byte ${b}`); this.lines.push(` .byte ${b}`) } else { - this.lines.push(` .byte (${b.symbol} >> ${b.bitofs}) & 0xff`) + this.lines.push(` .byte (${b.symbol} >> ${b.bitofs})`) } } + text(s: string) { + for (let l of s.split('\n')) + this.lines.push(l); + } toString() { return this.lines.join('\n'); } @@ -137,8 +153,13 @@ class Segment { fieldranges: {[cfname: string]: FieldArray} = {}; size: number = 0; initdata: (number | ConstByte | undefined)[] = []; + codefrags : string[] = []; + addCodeFragment(code: string) { + this.codefrags.push(code); + } allocateBytes(name: string, bytes: number) { + if (this.symbols[name]) return this.symbols[name]; // TODO: check size let ofs = this.size; this.symbols[name] = ofs; if (!this.ofs2sym.has(ofs)) @@ -155,6 +176,9 @@ class Segment { } } dump(file: SourceFileExport) { + for (let code of this.codefrags) { + file.text(code); + } for (let i=0; i(); constructor( public readonly em: EntityManager, @@ -223,23 +254,17 @@ export class EntityScope { let e = this.entities[i]; for (let c of e.etype.components) { for (let f of c.fields) { - yield {i, e, c, f, v:e.consts[c.name + '.' + f.name]}; + yield {i, e, c, f, v:e.consts[mksymbol(c, f.name)]}; } } } } - analyzeEntities() { - this.buildSegments(); - this.allocateSegment(this.bss, false); - this.allocateSegment(this.rodata, true); - this.allocateROData(this.rodata); - } buildSegments() { let iter = this.iterateFields(); for (var o=iter.next(); o.value; o=iter.next()) { let {i,e,c,f,v} = o.value; let segment = v === undefined ? this.bss : this.rodata; - let cfname = c.name + '.' + f.name; + let cfname = mksymbol(c, f.name); let array = segment.fieldranges[cfname]; if (!array) { array = segment.fieldranges[cfname] = {component:c, field:f, elo:i, ehi:i}; @@ -254,11 +279,11 @@ export class EntityScope { fields.sort((a,b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); let f; while (f = fields.pop()) { - let name = f.component.name + "_" + f.field.name; + let name = mksymbol(f.component, f.field.name); // TODO: doesn't work for packed arrays too well let bits = getPackedFieldSize(f.field); // variable size? make it a pointer - if (bits == 0) bits = 16; + if (bits == 0) bits = 16; // TODO? let rangelen = (f.ehi - f.elo + 1); let bytesperelem = Math.ceil(bits/8) * rangelen; // TODO: packing bits @@ -277,13 +302,20 @@ export class EntityScope { let iter = this.iterateFields(); for (var o=iter.next(); o.value; o=iter.next()) { let {i,e,c,f,v} = o.value; - let cfname = c.name + '.' + f.name; + let cfname = mksymbol(c, f.name); let fieldrange = segment.fieldranges[cfname]; if (v !== undefined) { // is it a byte array? if (v instanceof Uint8Array) { - let sym = c.name + '_' + f.name; - segment.allocateInitData(sym, v); + let datasym = `${c.name}_${f.name}_e${e.id}`; + let ptrlosym = `${c.name}_${f.name}_b0`; + let ptrhisym = `${c.name}_${f.name}_b8`; + let entcount = fieldrange.ehi - fieldrange.elo + 1; + segment.allocateInitData(datasym, v); + let loofs = segment.allocateBytes(ptrlosym, entcount); + let hiofs = segment.allocateBytes(ptrhisym, entcount); + segment.initdata[loofs + e.id - fieldrange.elo] = {symbol:datasym, bitofs:0}; + segment.initdata[hiofs + e.id - fieldrange.elo] = {symbol:datasym, bitofs:8}; } else if (fieldrange.ehi > fieldrange.elo) { // more than one element, add an array // TODO @@ -295,71 +327,124 @@ export class EntityScope { //console.log(segment.initdata) } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - e.consts[component.name + '.' + fieldName] = value; - } - dump(file: SourceFileExport) { - file.segment(`${this.name}_CODE`, 'rodata'); - this.rodata.dump(file); - file.segment(`${this.name}_DATA`, 'bss'); - this.bss.dump(file); - } - generateCode() { - let code = this.generateCodeForEvent('init'); - console.log(code); + // TODO: check to make sure component exists + e.consts[mksymbol(component, fieldName)] = value; } generateCodeForEvent(event: string): string { // find systems that respond to event // and have entities in this scope let systems = this.getSystems([event]); if (systems.length == 0) { - console.log(`no system responds to ${event}`); // TODO: warning + console.log(`; warning: no system responds to ${event}`); // TODO: warning } let s = ''; //s += `\n; event ${event}\n`; let emitcode : {[event: string] : string} = {}; for (let sys of systems) { + if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); if (sys.emits) { for (let emit of sys.emits) { + if (emitcode[emit]) { + console.log(`already emitted for ${sys.name}:${event}`); + } //console.log('>', emit); // TODO: cycles emitcode[emit] = this.generateCodeForEvent(emit); //console.log('<', emit, emitcode[emit].length); } } + if (sys.tempbytes) this.allocateTempBytes(-sys.tempbytes); for (let action of sys.actions) { - let code = action.text; if (action.event == event) { - // TODO: find loops - if (action.iterate == 'each') { - let ents = this.entitiesMatching(sys.query); - console.log(sys.name, action.event, ents); - //frag = this.iterateCode(frag); - } - // TODO: better parser of ${} - for (let [k,v] of Object.entries(emitcode)) { - let frag = v; - code = code.replace(`%{${k}}`, frag); - } - // anything not replaced? - let unused = /\%\{.+?\}/.exec(code); - if (unused) { - //throw new Error(`${sys.name}:${action.event} did not replace ${unused[0]}`); - } - // TODO: check that this happens once? - s += `\n; action ${sys.name}:${action.event}\n`; + let code = this.replaceCode(action.text, sys, action); + s += `\n; \n`; s += code; + s += `\n; \n`; + // TODO: check that this happens once? } - } + } } return s; } - entitiesMatching(q: Query) { + allocateTempBytes(n: number) { + this.tempOffset += n; + this.maxTempBytes = Math.max(this.tempOffset, this.maxTempBytes); + } + replaceCode(code: string, sys: System, action: CodeFragment): string { + const re = /\%\{(.+?)\}/g; + let label = sys.name + '_' + action.event; + let atypes = this.em.archetypesMatching(sys.query); + let entities = this.entitiesMatching(atypes); + // TODO: find loops + if (action.iterate == 'each') { + code = this.wrapCodeInLoop(code, sys, action, entities); + //console.log(sys.name, action.event, ents); + //frag = this.iterateCode(frag); + } + return code.replace(re, (entire, group: string) => { + let cmd = group.charAt(0); + let rest = group.substring(1); + switch (cmd) { + case '!': // emit event + return this.generateCodeForEvent(rest); + case '.': // auto label + return `.${label}_${rest}`; + case '$': // temp byte + return `TEMP+${this.tempOffset}+${rest}`; + case '<': // low byte + return this.generateCodeForField(sys, action, atypes, entities, rest, 0); + case '>': // high byte + return this.generateCodeForField(sys, action, atypes, entities, rest, 8); + case '^': // reference + return this.includeSubroutine(rest); + default: + //throw new Error(`unrecognized command ${cmd} in ${entire}`); + console.log(`unrecognized command ${cmd} in ${entire}`); + return entire; + } + }); + } + includeSubroutine(symbol: string): string { + this.subroutines.add(symbol); + return symbol; + } + wrapCodeInLoop(code: string, sys: System, action: CodeFragment, ents: Entity[]): string { + // TODO: check ents + // TODO: check segment bounds + let s = ASM_ITERATE_EACH; + s = s.replace('%{elo}', ents[0].id.toString()); + s = s.replace('%{ehi}', ents[ents.length-1].id.toString()); + s = s.replace('%{ecount}', ents.length.toString()); + s = s.replace('%{code}', code); + return s; + } + generateCodeForField(sys: System, action: CodeFragment, + atypes: ArchetypeMatch[], entities: Entity[], + fieldName: string, bitofs: number): string { + // find archetypes + let component = this.em.componentWithFieldName(atypes, fieldName); + if (!component) { + throw new Error(`cannot find component with field "${fieldName}" in ${sys.name}:${action.event}`); + } + // see if all entities have the same constant value + let constValues = new Set(); + for (let e of entities) { + let constVal = e.consts[fieldName]; + if (constVal) constValues.add(constVal); + } + // TODO: offset > 0? + //let range = this.bss.getFieldRange(component, fieldName); + return `${component.name}_${fieldName}_b${bitofs},x` + } + entitiesMatching(atypes: ArchetypeMatch[]) { let result = []; - let atypes = this.em.archetypesMatching(q); for (let e of this.entities) { for (let a of atypes) { - if (e.etype == a.etype) + // TODO: what about subclasses? + if (e.etype == a.etype) { result.push(e); + break; + } } } return result; @@ -390,6 +475,31 @@ export class EntityScope { hasComponent(ctype: ComponentType) { return this.componentsInScope.has(ctype.name); } + analyzeEntities() { + this.buildSegments(); + this.allocateSegment(this.bss, false); + this.allocateSegment(this.rodata, true); + this.allocateROData(this.rodata); + } + generateCode() { + this.tempOffset = this.maxTempBytes = 0; + let init = this.generateCodeForEvent('init'); + this.code.addCodeFragment(init); + for (let sub of Array.from(this.subroutines.values())) { + let code = this.generateCodeForEvent(sub); + this.code.addCodeFragment(code); + } + } + dump(file: SourceFileExport) { + file.text(HEADER); // TODO + file.segment(`${this.name}_DATA`, 'bss'); + if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); + this.bss.dump(file); + file.segment(`${this.name}_CODE`, 'rodata'); + this.rodata.dump(file); + this.code.dump(file); + file.text(FOOTER); // TODO + } } export class EntityManager { @@ -426,7 +536,7 @@ export class EntityManager { return list; } archetypesMatching(q: Query) { - let result : {etype: EntityArchetype, cmatch: ComponentType[]}[] = []; + let result : ArchetypeMatch[] = []; this.archtypes.forEach(etype => { let cmatch = this.componentsMatching(q, etype); if (cmatch.length > 0) { @@ -435,107 +545,176 @@ export class EntityManager { }); return result; } - emitCode(root: System) { + componentWithFieldName(atypes: ArchetypeMatch[], fieldName: string) { + // TODO??? + for (let at of atypes) { + for (let c of at.cmatch) { + for (let f of c.fields) { + if (f.name == fieldName) + return c; + } + } + } } } /// +const HEADER = ` + processor 6502 + include "vcs.h" + include "macro.h" + include "xmacro.h" +` + +const FOOTER = ` + org $fffc + .word Start ; reset vector + .word Start ; BRK vector +` + const TEMPLATE_INIT = ` Start: CLEAN_START - %{start} + %{!start} ` const TEMPLATE1 = ` .NextFrame: - lsr SWCHB ; test Game Reset switch - bcc Start ; reset? - VERTICAL_SYNC - TIMER_SETUP 37 - %{preframe} + VERTICAL_SYNC + sta CXCLR ; clear collision register + IFCONST PAL + TIMER_SETUP 44 + ELSE + TIMER_SETUP 36 + ENDIF + + %{!preframe} + TIMER_WAIT - TIMER_SETUP 192 - %{kernel} - TIMER_WAIT - TIMER_SETUP 29 - %{postframe} + lda #0 + sta VBLANK + IFNCONST PAL + TIMER_SETUP 194 + ENDIF + + %{!kernel} + + IFNCONST PAL + TIMER_WAIT + ENDIF + lda #2 + sta VBLANK + IFCONST PAL + TIMER_SETUP 36 + ELSE + TIMER_SETUP 28 + ENDIF + + %{!postframe} + TIMER_WAIT + lsr SWCHB ; test Game Reset switch + bcs .NoStart ; reset? + jmp Start +.NoStart: jmp .NextFrame `; +// TODO: two sticks? const TEMPLATE2 = ` -#ifdef EVENT_joyup - lda #%00100000 ;Up? +;#ifdef EVENT_joyleft + lda #%01000000 ;Left? bit SWCHA - bne SkipMoveUp - %{joyup} -.SkipMoveUp -#endif + bne %{.SkipMoveLeft} + %{!joyleft} +%{.SkipMoveLeft} +;#endif `; -const TEMPLATE3 = ` - lda %{ypos},x +const TEMPLATE3_L = ` + lda %{ A sec ; set carry for subtract - sbc YPos ; local coordinate - cmp %{sprite.height} ; in sprite? + sbc %{$5} ; local coordinate + cmp %{$4} ; in sprite? (height) bcc InSprite ; yes, skip over next lda #0 ; not in sprite, load 0 InSprite tay ; local coord -> Y - lda (temp+0),y ; lookup color + lda (%{$0}),y ; lookup color sta WSYNC ; sync w/ scanline sta GRP0 ; store bitmap - lda (temp+2),y ; lookup color + lda (%{$2}),y ; lookup color sta COLUP0 ; store color dex ; decrement X bne LVScan ; repeat until 192 lines `; const SET_XPOS = ` - lda %{sprite.xpos} - ldx %(sprite.plyrindex} - jsr SetHorizPos + lda %{ Date: Wed, 26 Jan 2022 21:12:11 -0600 Subject: [PATCH 003/104] ecs init values --- src/common/ecs/ecs.ts | 230 ++++++++++++++++++++++++++++++++---------- 1 file changed, 174 insertions(+), 56 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 06e46f16..eea87343 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -18,11 +18,15 @@ function mksymbol(c: ComponentType, fieldName: string) { return c.name + '_' + fieldName; } +function mkscopesymbol(s: EntityScope, c: ComponentType, fieldName: string) { + return s.name + '_' + c.name + '_' + fieldName; +} export interface Entity { id: number; etype: EntityArchetype; - consts: {[name: string]: DataValue}; + consts: {[component_field: string]: DataValue}; + inits: {[scope_component_field: string]: DataValue}; } export interface EntityConst { @@ -191,6 +195,21 @@ class Segment { getFieldRange(component: ComponentType, fieldName: string) { return this.fieldranges[mksymbol(component, fieldName)]; } + getSegmentByteOffset(component: ComponentType, fieldName: string, bitofs: number, entityID: number) { + let range = this.getFieldRange(component, fieldName); + if (range && range.access) { + let a = range.access[0]; // TODO: bitofs + let ofs = this.symbols[a.symbol]; + if (ofs !== undefined) { + return ofs + entityID - range.elo; + } + } + } + getOriginSymbol() { + let a = this.ofs2sym.get(0); + if (!a) throw new Error('getOriginSymbol'); + return a[0]; + } } function getFieldBits(f: IntType) { @@ -241,7 +260,7 @@ export class EntityScope { newEntity(etype: EntityArchetype) : Entity { // TODO: add parent ID? lock parent scope? let id = this.entities.length; - let entity : Entity = {id, etype, consts:{}}; + let entity : Entity = {id, etype, consts:{}, inits:{}}; this.em.archtypes.add(etype); for (let c of etype.components) { this.componentsInScope.add(c.name); @@ -326,10 +345,41 @@ export class EntityScope { } //console.log(segment.initdata) } + allocateInitData(segment: Segment) { + let initbytes = new Uint8Array(segment.size); + let iter = this.iterateFields(); + for (var o=iter.next(); o.value; o=iter.next()) { + let {i,e,c,f,v} = o.value; + let scfname = mkscopesymbol(this, c, f.name); + let initvalue = e.inits[scfname]; + if (initvalue !== undefined) { + let offset = segment.getSegmentByteOffset(c, f.name, 0, e.id); + if (offset !== undefined && typeof initvalue === 'number') { + initbytes[offset] = initvalue; // TODO: > 8 bits? + } else { + throw new Error(`cannot access ${scfname}`); + } + } + } + // build the final init buffer + // TODO: compress 0s? + let bufsym = this.name + '__INITDATA'; + let bufofs = this.rodata.allocateInitData(bufsym, initbytes); + let code = INITFROMARRAY; + //TODO: function to repalce from dict? + code = code.replace('%{nbytes}', initbytes.length.toString()) + code = code.replace('%{src}', bufsym); + code = code.replace('%{dest}', segment.getOriginSymbol()); + return code; + } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { // TODO: check to make sure component exists e.consts[mksymbol(component, fieldName)] = value; } + setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { + // TODO: check to make sure component exists + e.inits[mkscopesymbol(this, component, fieldName)] = value; + } generateCodeForEvent(event: string): string { // find systems that respond to event // and have entities in this scope @@ -341,6 +391,7 @@ export class EntityScope { //s += `\n; event ${event}\n`; let emitcode : {[event: string] : string} = {}; for (let sys of systems) { + // TODO: does this work if multiple actions? if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); if (sys.emits) { for (let emit of sys.emits) { @@ -434,7 +485,7 @@ export class EntityScope { } // TODO: offset > 0? //let range = this.bss.getFieldRange(component, fieldName); - return `${component.name}_${fieldName}_b${bitofs},x` + return `${component.name}_${fieldName}_b${bitofs},x` // TODO? ,x? } entitiesMatching(atypes: ArchetypeMatch[]) { let result = []; @@ -483,8 +534,11 @@ export class EntityScope { } generateCode() { this.tempOffset = this.maxTempBytes = 0; - let init = this.generateCodeForEvent('init'); - this.code.addCodeFragment(init); + this.code.addCodeFragment(TEMPLATE_INIT); + let initcode = this.allocateInitData(this.bss); + this.code.addCodeFragment(initcode); + let start = this.generateCodeForEvent('start'); + this.code.addCodeFragment(start); for (let sub of Array.from(this.subroutines.values())) { let code = this.generateCodeForEvent(sub); this.code.addCodeFragment(code); @@ -576,7 +630,6 @@ const FOOTER = ` const TEMPLATE_INIT = ` Start: CLEAN_START - %{!start} ` const TEMPLATE1 = ` @@ -622,23 +675,36 @@ const TEMPLATE1 = ` `; // TODO: two sticks? -const TEMPLATE2 = ` -;#ifdef EVENT_joyleft - lda #%01000000 ;Left? - bit SWCHA - bne %{.SkipMoveLeft} +const TEMPLATE2_a = ` + lda SWCHA + sta %{$0} +` +const TEMPLATE2_b = ` + asl %{$0} + bcs %{.SkipMoveRight} + %{!joyright} +%{.SkipMoveRight} + asl %{$0} + bcs %{.SkipMoveLeft} %{!joyleft} %{.SkipMoveLeft} -;#endif + asl %{$0} + bcs %{.SkipMoveDown} + %{!joydown} +%{.SkipMoveDown} + asl %{$0} + bcs %{.SkipMoveUp} + %{!joyup} +%{.SkipMoveUp} `; const TEMPLATE3_L = ` lda %{ A - sec ; set carry for subtract - sbc %{$5} ; local coordinate - cmp %{$4} ; in sprite? (height) - bcc InSprite ; yes, skip over next - lda #0 ; not in sprite, load 0 + txa ; X -> A + sec ; set carry for subtract + sbc %{$5} ; local coordinate + cmp %{$4} ; in sprite? (height) + bcc InSprite ; yes, skip over next + lda #0 ; not in sprite, load 0 InSprite - tay ; local coord -> Y - lda (%{$0}),y ; lookup color - sta WSYNC ; sync w/ scanline - sta GRP0 ; store bitmap - lda (%{$2}),y ; lookup color - sta COLUP0 ; store color - dex ; decrement X - bne LVScan ; repeat until 192 lines + tay ; local coord -> Y + lda (%{$0}),y ; lookup color + sta WSYNC ; sync w/ scanline + sta GRP0 ; store bitmap + lda (%{$2}),y ; lookup color + sta COLUP0 ; store color + dex ; decrement X + bne LVScan ; repeat until 192 lines `; const SET_XPOS = ` @@ -714,9 +795,43 @@ DivideLoop asl sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset + sta WSYNC + sta HMOVE rts ; return to caller ` +const INITFROMSPARSE = ` +MemSrc equ $80 +MemDest equ $82 +InitMemory + ldy #0 + lda (MemSrc),y + beq .done + tax + iny + lda (MemSrc),y + sta MemDest + iny + lda (MemSrc),y + sta MemDest+1 +.loop + iny + lda (MemSrc),y + sta (MemDest),y + dex + bne .loop +.done rts +` + +const INITFROMARRAY = ` + ldy #%{nbytes} +.loop + lda %{src}-1,y + sta %{dest}-1,y + dey + bne .loop +` + function test() { let em = new EntityManager(); @@ -767,7 +882,7 @@ function test() { include:['sprite','hasbitmap','hascolormap','ypos'], }, actions:[ - { text:TEMPLATE4_S, event:'preframe', iterate:'each' }, + { text:TEMPLATE4_S, event:'preframe', iterate:'once' }, { text:TEMPLATE4_K, event:'kernel', iterate:'once' }, ] }) @@ -785,17 +900,8 @@ function test() { // TODO: easy stagger of system update? // TODO: easy lookup tables // TODO: how to init? + // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html - em.defineSystem({ - name:'init', - emits:['start'], - query:{ - include:[], // ??? - }, - actions:[ - { text:TEMPLATE_INIT, event:'init', iterate:'once' } - ] - }) em.defineSystem({ name:'frameloop', emits:['preframe','kernel','postframe'], @@ -811,9 +917,11 @@ function test() { query:{ include:['player'] }, + tempbytes:1, emits:['joyup','joydown','joyleft','joyright','joybutton'], actions:[ - { text:TEMPLATE2, event:'postframe', iterate:'each' } + { text:TEMPLATE2_a, event:'postframe', iterate:'once' }, + { text:TEMPLATE2_b, event:'postframe', iterate:'each' } ] }); em.defineSystem({ @@ -828,7 +936,7 @@ function test() { }); em.defineSystem({ name:'SetHorizPos', - query:{ include:[] }, + query:{ include:[] }, // TODO? actions:[ { text:SETHORIZPOS, event:'SetHorizPos', iterate:'once' }, // TODO: event source? ] @@ -848,7 +956,15 @@ function test() { let ea_playerSprite = {components:[c_sprite,c_hasbitmap,c_hascolormap,c_xpos,c_ypos,c_player]}; let e_player0 = root.newEntity(ea_playerSprite); + root.setInitValue(e_player0, c_sprite, 'plyrindex', 0); + root.setInitValue(e_player0, c_sprite, 'height', 8); + root.setInitValue(e_player0, c_xpos, 'xpos', 50); + root.setInitValue(e_player0, c_ypos, 'ypos', 50); let e_player1 = root.newEntity(ea_playerSprite); + root.setInitValue(e_player1, c_sprite, 'plyrindex', 1); + root.setInitValue(e_player1, c_sprite, 'height', 8); + root.setInitValue(e_player1, c_xpos, 'xpos', 100); + root.setInitValue(e_player1, c_ypos, 'ypos', 60); let src = new SourceFileExport(); root.analyzeEntities(); @@ -857,4 +973,6 @@ function test() { console.log(src.toString()); } +// TODO: files in markdown? + test(); From 4c2442dd90ccd168eb8baf50d7988fae54c26509 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 27 Jan 2022 09:54:12 -0600 Subject: [PATCH 004/104] ecs: use ca65 syntax --- src/common/ecs/ecs.ts | 244 +++++++++++++++++++++++------------------- 1 file changed, 131 insertions(+), 113 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index eea87343..95a26e8b 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -122,11 +122,9 @@ class SourceFileExport { } segment(seg: string, segtype: 'rodata' | 'bss') { if (segtype == 'bss') { - this.lines.push(` seg.u ${seg}`); - this.lines.push(` org $80`); // TODO + this.lines.push(`.zeropage`); // TODO } else { - this.lines.push(` seg ${seg}`); - this.lines.push(` org $f000`); // TODO + this.lines.push(`.segment "CODE"`); // TODO } } label(sym: string) { @@ -134,12 +132,14 @@ class SourceFileExport { } byte(b: number | ConstByte | undefined) { if (b === undefined) { - this.lines.push(` .ds 1`); + this.lines.push(` .res 1`); } else if (typeof b === 'number') { if (b < 0 || b > 255) throw new Error(`out of range byte ${b}`); this.lines.push(` .byte ${b}`) } else { - this.lines.push(` .byte (${b.symbol} >> ${b.bitofs})`) + if (b.bitofs == 0) this.lines.push(` .byte <${b.symbol}`) + else if (b.bitofs == 8) this.lines.push(` .byte >${b.symbol}`) + else this.lines.push(` .byte (${b.symbol} >> ${b.bitofs})`) // TODO? } } text(s: string) { @@ -232,11 +232,11 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { const ASM_ITERATE_EACH = ` ldx #0 -%{.loop}: +%{@loop}: %{code} inx cpx #%{ecount} - bne %{.loop} + bne %{@loop} `; export class EntityScope { @@ -439,7 +439,8 @@ export class EntityScope { case '!': // emit event return this.generateCodeForEvent(rest); case '.': // auto label - return `.${label}_${rest}`; + case '@': // auto label + return `${label}_${rest}`; case '$': // temp byte return `TEMP+${this.tempOffset}+${rest}`; case '<': // low byte @@ -615,16 +616,14 @@ export class EntityManager { /// const HEADER = ` - processor 6502 - include "vcs.h" - include "macro.h" - include "xmacro.h" +.include "vcs-ca65.h" ` const FOOTER = ` - org $fffc - .word Start ; reset vector - .word Start ; BRK vector +.segment "VECTORS" +VecNMI: .word Start +VecReset: .word Start +VecBRK: .word Start ` const TEMPLATE_INIT = ` @@ -633,45 +632,19 @@ Start: ` const TEMPLATE1 = ` -.NextFrame: - VERTICAL_SYNC - sta CXCLR ; clear collision register - IFCONST PAL - TIMER_SETUP 44 - ELSE - TIMER_SETUP 36 - ENDIF - +%{@NextFrame}: + FRAME_START %{!preframe} - - TIMER_WAIT - lda #0 - sta VBLANK - IFNCONST PAL - TIMER_SETUP 194 - ENDIF - + KERNEL_START %{!kernel} - - IFNCONST PAL - TIMER_WAIT - ENDIF - lda #2 - sta VBLANK - IFCONST PAL - TIMER_SETUP 36 - ELSE - TIMER_SETUP 28 - ENDIF - + KERNEL_END %{!postframe} - - TIMER_WAIT + FRAME_END lsr SWCHB ; test Game Reset switch - bcs .NoStart ; reset? + bcs @NoStart jmp Start -.NoStart: - jmp .NextFrame +@NoStart: + jmp %{@NextFrame} `; // TODO: two sticks? @@ -681,100 +654,133 @@ const TEMPLATE2_a = ` ` const TEMPLATE2_b = ` asl %{$0} - bcs %{.SkipMoveRight} + bcs %{@SkipMoveRight} %{!joyright} -%{.SkipMoveRight} +%{@SkipMoveRight}: asl %{$0} - bcs %{.SkipMoveLeft} + bcs %{@SkipMoveLeft} %{!joyleft} -%{.SkipMoveLeft} +%{@SkipMoveLeft}: asl %{$0} - bcs %{.SkipMoveDown} + bcs %{@SkipMoveDown} %{!joydown} -%{.SkipMoveDown} +%{@SkipMoveDown}: asl %{$0} - bcs %{.SkipMoveUp} + bcs %{@SkipMoveUp} %{!joyup} -%{.SkipMoveUp} +%{@SkipMoveUp}: `; const TEMPLATE3_L = ` lda %{ A - sec ; set carry for subtract - sbc %{$5} ; local coordinate - cmp %{$4} ; in sprite? (height) - bcc InSprite ; yes, skip over next - lda #0 ; not in sprite, load 0 -InSprite - tay ; local coord -> Y - lda (%{$0}),y ; lookup color - sta WSYNC ; sync w/ scanline - sta GRP0 ; store bitmap - lda (%{$2}),y ; lookup color - sta COLUP0 ; store color - dex ; decrement X - bne LVScan ; repeat until 192 lines + ldy #192 ; lines in kernel +@LVScan: + lda %{$4} ; height + dcp %{$5} + bcs @DoDraw1 + lda #0 + .byte $2C +@DoDraw1: + lda (%{$0}),y + sta WSYNC + sta GRP0 + lda (%{$2}),y + sta COLUP0 + + lda %{$10} ; height + dcp %{$11} + bcs @DoDraw2 + lda #0 + .byte $2C +@DoDraw2: + lda (%{$6}),y + sta GRP1 + lda (%{$8}),y + sta COLUP1 + + dey ; decrement + bne @LVScan ; repeat until 192 lines `; const SET_XPOS = ` lda %{ Date: Thu, 27 Jan 2022 12:43:27 -0600 Subject: [PATCH 005/104] ecs: actions have queries --- src/common/ecs/ecs.ts | 282 ++++++++++++++++++++++-------------------- 1 file changed, 148 insertions(+), 134 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 95a26e8b..7f41e6d3 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -25,8 +25,8 @@ function mkscopesymbol(s: EntityScope, c: ComponentType, fieldName: string) { export interface Entity { id: number; etype: EntityArchetype; - consts: {[component_field: string]: DataValue}; - inits: {[scope_component_field: string]: DataValue}; + consts: { [component_field: string]: DataValue }; + inits: { [scope_component_field: string]: DataValue }; } export interface EntityConst { @@ -54,17 +54,16 @@ export interface Query { export interface System { name: string; - actions: CodeFragment[]; - query: Query; + actions: Action[]; tempbytes?: number; emits?: string[]; - live?: EntityArchetype[] | null; } -export interface CodeFragment { +export interface Action { text: string; event: string; - iterate: 'once' | 'each' + query: Query; + select: 'once' | 'each' | 'source' } export type DataValue = number | boolean | Uint8Array; @@ -115,7 +114,7 @@ interface ArchetypeMatch { } class SourceFileExport { - lines : string[] = []; + lines: string[] = []; comment(text: string) { this.lines.push(';' + text); @@ -152,12 +151,12 @@ class SourceFileExport { } class Segment { - symbols: {[sym: string]: number} = {}; - ofs2sym = new Map(); - fieldranges: {[cfname: string]: FieldArray} = {}; + symbols: { [sym: string]: number } = {}; + ofs2sym = new Map(); + fieldranges: { [cfname: string]: FieldArray } = {}; size: number = 0; initdata: (number | ConstByte | undefined)[] = []; - codefrags : string[] = []; + codefrags: string[] = []; addCodeFragment(code: string) { this.codefrags.push(code); @@ -175,7 +174,7 @@ class Segment { // TODO: optimize shared data allocateInitData(name: string, bytes: Uint8Array) { let ofs = this.allocateBytes(name, bytes.length); - for (let i=0; i (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); + fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); let f; while (f = fields.pop()) { let name = mksymbol(f.component, f.field.name); @@ -304,13 +303,13 @@ export class EntityScope { // variable size? make it a pointer if (bits == 0) bits = 16; // TODO? let rangelen = (f.ehi - f.elo + 1); - let bytesperelem = Math.ceil(bits/8) * rangelen; + let bytesperelem = Math.ceil(bits / 8) * rangelen; // TODO: packing bits // TODO: split arrays f.access = []; - for (let i=0; i fieldrange.elo) { // more than one element, add an array // TODO @@ -348,8 +347,8 @@ export class EntityScope { allocateInitData(segment: Segment) { let initbytes = new Uint8Array(segment.size); let iter = this.iterateFields(); - for (var o=iter.next(); o.value; o=iter.next()) { - let {i,e,c,f,v} = o.value; + for (var o = iter.next(); o.value; o = iter.next()) { + let { i, e, c, f, v } = o.value; let scfname = mkscopesymbol(this, c, f.name); let initvalue = e.inits[scfname]; if (initvalue !== undefined) { @@ -389,7 +388,7 @@ export class EntityScope { } let s = ''; //s += `\n; event ${event}\n`; - let emitcode : {[event: string] : string} = {}; + let emitcode: { [event: string]: string } = {}; for (let sys of systems) { // TODO: does this work if multiple actions? if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); @@ -421,13 +420,14 @@ export class EntityScope { this.tempOffset += n; this.maxTempBytes = Math.max(this.tempOffset, this.maxTempBytes); } - replaceCode(code: string, sys: System, action: CodeFragment): string { + replaceCode(code: string, sys: System, action: Action): string { const re = /\%\{(.+?)\}/g; let label = sys.name + '_' + action.event; - let atypes = this.em.archetypesMatching(sys.query); + let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); - // TODO: find loops - if (action.iterate == 'each') { + // TODO: detect cycles + // TODO: "source"? + if (action.select == 'each') { code = this.wrapCodeInLoop(code, sys, action, entities); //console.log(sys.name, action.event, ents); //frag = this.iterateCode(frag); @@ -460,17 +460,17 @@ export class EntityScope { this.subroutines.add(symbol); return symbol; } - wrapCodeInLoop(code: string, sys: System, action: CodeFragment, ents: Entity[]): string { + wrapCodeInLoop(code: string, sys: System, action: Action, ents: Entity[]): string { // TODO: check ents // TODO: check segment bounds let s = ASM_ITERATE_EACH; s = s.replace('%{elo}', ents[0].id.toString()); - s = s.replace('%{ehi}', ents[ents.length-1].id.toString()); + s = s.replace('%{ehi}', ents[ents.length - 1].id.toString()); s = s.replace('%{ecount}', ents.length.toString()); s = s.replace('%{code}', code); return s; } - generateCodeForField(sys: System, action: CodeFragment, + generateCodeForField(sys: System, action: Action, atypes: ArchetypeMatch[], entities: Entity[], fieldName: string, bitofs: number): string { // find archetypes @@ -513,7 +513,7 @@ export class EntityScope { systemListensTo(sys: System, events: string[]) { for (let action of sys.actions) { if (action.event != null && events.includes(action.event)) { - let archs = this.em.archetypesMatching(sys.query); + let archs = this.em.archetypesMatching(action.query); for (let arch of archs) { for (let ctype of arch.cmatch) { if (this.hasComponent(ctype)) { @@ -559,9 +559,9 @@ export class EntityScope { export class EntityManager { archtypes = new Set(); - components : {[name: string]: ComponentType} = {}; - systems : {[name: string]: System} = {}; - scopes : {[name: string]: EntityScope} = {}; + components: { [name: string]: ComponentType } = {}; + systems: { [name: string]: System } = {}; + scopes: { [name: string]: EntityScope } = {}; newScope(name: string, parent?: EntityScope) { let scope = new EntityScope(this, name, parent); @@ -591,11 +591,11 @@ export class EntityManager { return list; } archetypesMatching(q: Query) { - let result : ArchetypeMatch[] = []; + let result: ArchetypeMatch[] = []; this.archtypes.forEach(etype => { let cmatch = this.componentsMatching(q, etype); if (cmatch.length > 0) { - result.push({etype, cmatch}); + result.push({ etype, cmatch }); } }); return result; @@ -843,39 +843,60 @@ const INITFROMARRAY = ` function test() { let em = new EntityManager(); - let c_kernel = em.defineComponent({name:'kernel', fields:[ - {name:'lines', dtype:'int', lo:0, hi:255} - ]}) - let c_sprite = em.defineComponent({name:'sprite', fields:[ - {name:'height', dtype:'int', lo:0, hi:255}, - {name:'plyrindex', dtype:'int', lo:0, hi:1}, - {name:'flags', dtype:'int', lo:0, hi:255}, - ]}) - let c_player = em.defineComponent({name:'player', fields:[ - //TODO: optional? - ]}) - let c_hasbitmap = em.defineComponent({name:'hasbitmap', fields:[ - {name:'bitmap', dtype:'ref', query:{include:['bitmap']}}, - ]}) - let c_hascolormap = em.defineComponent({name:'hascolormap', fields:[ - {name:'colormap', dtype:'ref', query:{include:['colormap']}}, - ]}) - let c_bitmap = em.defineComponent({name:'bitmap', fields:[ - {name:'bitmapdata', dtype:'array', elem:{ dtype:'int', lo:0, hi:255 }} - ]}) - let c_colormap = em.defineComponent({name:'colormap', fields:[ - {name:'colormapdata', dtype:'array', elem:{ dtype:'int', lo:0, hi:255 }} - ]}) - let c_xpos = em.defineComponent({name:'xpos', fields:[ - {name:'xpos', dtype:'int', lo:0, hi:255} - ]}) - let c_ypos = em.defineComponent({name:'ypos', fields:[ - {name:'ypos', dtype:'int', lo:0, hi:255} - ]}) - let c_xyvel = em.defineComponent({name:'xyvel', fields:[ - {name:'xvel', dtype:'int', lo:-8, hi:7}, - {name:'yvel', dtype:'int', lo:-8, hi:7} - ]}) + let c_kernel = em.defineComponent({ + name: 'kernel', fields: [ + { name: 'lines', dtype: 'int', lo: 0, hi: 255 }, + { name: 'bgcolor', dtype: 'int', lo: 0, hi: 255 }, + ] + }) + let c_sprite = em.defineComponent({ + name: 'sprite', fields: [ + { name: 'height', dtype: 'int', lo: 0, hi: 255 }, + { name: 'plyrindex', dtype: 'int', lo: 0, hi: 1 }, + { name: 'flags', dtype: 'int', lo: 0, hi: 255 }, + ] + }) + let c_player = em.defineComponent({ + name: 'player', fields: [ + //TODO: optional? + ] + }) + let c_hasbitmap = em.defineComponent({ + name: 'hasbitmap', fields: [ + { name: 'bitmap', dtype: 'ref', query: { include: ['bitmap'] } }, + ] + }) + let c_hascolormap = em.defineComponent({ + name: 'hascolormap', fields: [ + { name: 'colormap', dtype: 'ref', query: { include: ['colormap'] } }, + ] + }) + let c_bitmap = em.defineComponent({ + name: 'bitmap', fields: [ + { name: 'bitmapdata', dtype: 'array', elem: { dtype: 'int', lo: 0, hi: 255 } } + ] + }) + let c_colormap = em.defineComponent({ + name: 'colormap', fields: [ + { name: 'colormapdata', dtype: 'array', elem: { dtype: 'int', lo: 0, hi: 255 } } + ] + }) + let c_xpos = em.defineComponent({ + name: 'xpos', fields: [ + { name: 'xpos', dtype: 'int', lo: 0, hi: 255 } + ] + }) + let c_ypos = em.defineComponent({ + name: 'ypos', fields: [ + { name: 'ypos', dtype: 'int', lo: 0, hi: 255 } + ] + }) + let c_xyvel = em.defineComponent({ + name: 'xyvel', fields: [ + { name: 'xvel', dtype: 'int', lo: -8, hi: 7 }, + { name: 'yvel', dtype: 'int', lo: -8, hi: 7 } + ] + }) // init -> [start] -> frameloop // frameloop -> [preframe] [kernel] [postframe] @@ -884,23 +905,29 @@ function test() { // temp between preframe + frame? // TODO: check names for identifierness em.defineSystem({ - name:'kernel_simple', - tempbytes:8, - query:{ - include:['sprite','hasbitmap','hascolormap','ypos'], - }, - actions:[ - { text:TEMPLATE4_S, event:'preframe', iterate:'once' }, - { text:TEMPLATE4_K, event:'kernel', iterate:'once' }, + name: 'kernel_simple', + tempbytes: 8, + actions: [ + { + text: TEMPLATE4_S, event: 'preframe', select: 'once', query: { + include: ['sprite', 'hasbitmap', 'hascolormap', 'ypos'], + }, + }, + { + text: TEMPLATE4_K, event: 'kernel', select: 'once', query: { + include: ['kernel'] + } + }, ] }) em.defineSystem({ - name:'set_xpos', - query:{ - include:['sprite','xpos'] - }, - actions:[ - { text:SET_XPOS, event:'preframe', iterate:'each' }, + name: 'set_xpos', + actions: [ + { + text: SET_XPOS, event: 'preframe', select: 'each', query: { + include: ['sprite', 'xpos'] + }, + }, //{ text:SETHORIZPOS }, ] }) @@ -911,68 +938,55 @@ function test() { // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html em.defineSystem({ - name:'frameloop', - emits:['preframe','kernel','postframe'], - query:{ - include:['kernel'], // ??? - }, - actions:[ - { text:TEMPLATE1, event:'start', iterate:'once' } + name: 'frameloop', + emits: ['preframe', 'kernel', 'postframe'], + actions: [ + { text: TEMPLATE1, event: 'start', select: 'once', query: { include: [] } } // TODO: []? ] }) em.defineSystem({ - name:'joyread', - query:{ - include:['player'] - }, - tempbytes:1, - emits:['joyup','joydown','joyleft','joyright','joybutton'], - actions:[ - { text:TEMPLATE2_a, event:'postframe', iterate:'once' }, - { text:TEMPLATE2_b, event:'postframe', iterate:'each' } + name: 'joyread', + tempbytes: 1, + emits: ['joyup', 'joydown', 'joyleft', 'joyright', 'joybutton'], + actions: [ + { text: TEMPLATE2_a, event: 'postframe', select: 'once', query: { include: ['player'] } }, + { text: TEMPLATE2_b, event: 'postframe', select: 'each', query: { include: ['player'] } } ] }); em.defineSystem({ - name:'move_x', - query:{ - include:['player','xpos'] - }, - actions:[ - { text:TEMPLATE3_L, event:'joyleft', iterate:'once' }, // TODO: event source? - { text:TEMPLATE3_R, event:'joyright', iterate:'once' }, // TODO: event source? + name: 'move_x', + actions: [ + { text: TEMPLATE3_L, event: 'joyleft', select: 'source', query: { include: ['player', 'xpos'] }, }, + { text: TEMPLATE3_R, event: 'joyright', select: 'source', query: { include: ['player', 'xpos'] }, }, ] }); em.defineSystem({ - name:'move_y', - query:{ - include:['player','ypos'] - }, - actions:[ - { text:TEMPLATE3_U, event:'joyup', iterate:'once' }, // TODO: event source? - { text:TEMPLATE3_D, event:'joydown', iterate:'once' }, // TODO: event source? + name: 'move_y', + actions: [ + { text: TEMPLATE3_U, event: 'joyup', select: 'source', query: { include: ['player', 'ypos'] } }, + { text: TEMPLATE3_D, event: 'joydown', select: 'source', query: { include: ['player', 'ypos'] } }, ] }); em.defineSystem({ - name:'SetHorizPos', - query:{ include:[] }, // TODO? - actions:[ - { text:SETHORIZPOS, event:'SetHorizPos', iterate:'once' }, // TODO: event source? + name: 'SetHorizPos', + actions: [ + { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: [] } }, // TODO: []? ] }); let root = em.newScope("Root"); let scene = em.newScope("Scene", root); - let e_ekernel = root.newEntity({components:[c_kernel]}); + let e_ekernel = root.newEntity({ components: [c_kernel] }); root.setConstValue(e_ekernel, c_kernel, 'lines', 192); - let e_bitmap0 = root.newEntity({components:[c_bitmap]}); + let e_bitmap0 = root.newEntity({ components: [c_bitmap] }); // TODO: store array sizes? - root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([0,1,3,7,15,31,0])); + root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([0, 1, 3, 7, 15, 31, 0])); - let e_colormap0 = root.newEntity({components:[c_colormap]}); - root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([0,3,6,9,12,14,0])); + let e_colormap0 = root.newEntity({ components: [c_colormap] }); + root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([0, 3, 6, 9, 12, 14, 0])); - let ea_playerSprite = {components:[c_sprite,c_hasbitmap,c_hascolormap,c_xpos,c_ypos,c_player]}; + let ea_playerSprite = { components: [c_sprite, c_hasbitmap, c_hascolormap, c_xpos, c_ypos, c_player] }; let e_player0 = root.newEntity(ea_playerSprite); root.setInitValue(e_player0, c_sprite, 'plyrindex', 0); root.setInitValue(e_player0, c_sprite, 'height', 8); From c022c16dc26800f750c0ed6f90b68c59dd7686fc Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 27 Jan 2022 13:48:45 -0600 Subject: [PATCH 006/104] ecs: const values --- src/common/ecs/ecs.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 7f41e6d3..ac3b3970 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -481,8 +481,17 @@ export class EntityScope { // see if all entities have the same constant value let constValues = new Set(); for (let e of entities) { - let constVal = e.consts[fieldName]; - if (constVal) constValues.add(constVal); + let constVal = e.consts[mksymbol(component, fieldName)]; + constValues.add(constVal); // constVal === undefined is allowed + } + // is it a constant? + if (constValues.size == 1) { + let value = constValues.values().next().value; + if (value !== undefined) { + if (bitofs == 0) return `#<${value}`; + if (bitofs == 8) return `#>${value}`; + // TODO: bitofs? + } } // TODO: offset > 0? //let range = this.bss.getFieldRange(component, fieldName); @@ -711,7 +720,7 @@ const TEMPLATE3_D = ` const TEMPLATE4_S = ` .macro %{@KernelSetup} ent,ofs - lda #192 + lda #192 ; TODO: numlines sec sbc ypos_ypos_b0+ent sta %{$5}+ofs @@ -748,7 +757,8 @@ const TEMPLATE4_S = ` // https://atariage.com/forums/topic/129683-advice-on-a-masking-kernel/ // https://atariage.com/forums/topic/128147-having-trouble-with-2-free-floating-player-graphics/?tab=comments#comment-1547059 const TEMPLATE4_K = ` - ldy #192 ; lines in kernel + lda %{ Date: Thu, 27 Jan 2022 14:07:13 -0600 Subject: [PATCH 007/104] ecs: all includes[] must be present --- src/common/ecs/ecs.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index ac3b3970..bac5b1c5 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -443,6 +443,8 @@ export class EntityScope { return `${label}_${rest}`; case '$': // temp byte return `TEMP+${this.tempOffset}+${rest}`; + case '=': + // TODO? case '<': // low byte return this.generateCodeForField(sys, action, atypes, entities, rest, 0); case '>': // high byte @@ -579,25 +581,26 @@ export class EntityManager { return scope; } defineComponent(ctype: ComponentType) { - if (this.components[ctype.name]) throw new Error(`component ${name} already defined`); + if (this.components[ctype.name]) throw new Error(`component ${ctype.name} already defined`); return this.components[ctype.name] = ctype; } defineSystem(system: System) { - if (this.systems[system.name]) throw new Error(`system ${name} already defined`); + if (this.systems[system.name]) throw new Error(`system ${system.name} already defined`); this.systems[system.name] = system; } componentsMatching(q: Query, etype: EntityArchetype) { let list = []; for (let c of etype.components) { let cname = c.name; + if (q.exclude?.includes(cname)) { + return []; + } // TODO: 0 includes == all entities? if (q.include.length == 0 || q.include.includes(cname)) { - if (!q.exclude?.includes(cname)) { - list.push(c); - } + list.push(c); } } - return list; + return list.length == q.include.length ? list : []; } archetypesMatching(q: Query) { let result: ArchetypeMatch[] = []; @@ -720,7 +723,7 @@ const TEMPLATE3_D = ` const TEMPLATE4_S = ` .macro %{@KernelSetup} ent,ofs - lda #192 ; TODO: numlines + lda #192 ; TODO: numlinesgit sec sbc ypos_ypos_b0+ent sta %{$5}+ofs @@ -758,6 +761,7 @@ const TEMPLATE4_S = ` // https://atariage.com/forums/topic/128147-having-trouble-with-2-free-floating-player-graphics/?tab=comments#comment-1547059 const TEMPLATE4_K = ` lda %{ Date: Thu, 27 Jan 2022 14:39:37 -0600 Subject: [PATCH 008/104] ecs: allocate init data table --- src/common/ecs/ecs.ts | 86 ++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index bac5b1c5..e1da1b6c 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -14,6 +14,18 @@ // 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 function mksymbol(c: ComponentType, fieldName: string) { return c.name + '_' + fieldName; @@ -66,7 +78,7 @@ export interface Action { select: 'once' | 'each' | 'source' } -export type DataValue = number | boolean | Uint8Array; +export type DataValue = number | boolean | Uint8Array | Uint16Array; export type DataField = { name: string } & DataType; @@ -121,7 +133,7 @@ class SourceFileExport { } segment(seg: string, segtype: 'rodata' | 'bss') { if (segtype == 'bss') { - this.lines.push(`.zeropage`); // TODO + this.lines.push(`.segment "ZEROPAGE"`); // TODO } else { this.lines.push(`.segment "CODE"`); // TODO } @@ -229,13 +241,14 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { return 0; } +// TODO: language dialects? const ASM_ITERATE_EACH = ` ldx #0 -%{@loop}: +%{@__each}: %{code} inx cpx #%{ecount} - bne %{@loop} + bne %{@__each} `; export class EntityScope { @@ -323,23 +336,30 @@ export class EntityScope { let cfname = mksymbol(c, f.name); let fieldrange = segment.fieldranges[cfname]; if (v !== undefined) { + let entcount = fieldrange.ehi - fieldrange.elo + 1; // is it a byte array? if (v instanceof Uint8Array) { let datasym = `${c.name}_${f.name}_e${e.id}`; let ptrlosym = `${c.name}_${f.name}_b0`; let ptrhisym = `${c.name}_${f.name}_b8`; - let entcount = fieldrange.ehi - fieldrange.elo + 1; segment.allocateInitData(datasym, v); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); segment.initdata[loofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 8 }; - } else if (fieldrange.ehi > fieldrange.elo) { - // more than one element, add an array - // TODO + // TODO: } else if (v instanceof Uint16Array) { + } else if (typeof v === 'number') { + // more than 1 entity, add an array + // TODO: what if > 8 bits? + // TODO: what if mix of var, const, and init values? + if (fieldrange.ehi > fieldrange.elo) { + let datasym = `${c.name}_${f.name}_b0`; + let base = segment.allocateBytes(datasym, entcount); + segment.initdata[base + e.id - fieldrange.elo] = v; + } + } else { + throw new Error(`unhandled constant ${e.id}:${cfname}`); } - //console.log(cfname, i, v, fieldrange); - //segment.allocateInitData(cfname, ); } } //console.log(segment.initdata) @@ -497,7 +517,13 @@ export class EntityScope { } // TODO: offset > 0? //let range = this.bss.getFieldRange(component, fieldName); - return `${component.name}_${fieldName}_b${bitofs},x` // TODO? ,x? + if (action.select == 'once') { + if (entities.length != 1) + throw new Error(`can't choose multiple entities for ${fieldName} with select=once`); + return `${component.name}_${fieldName}_b${bitofs}` // TODO? check there's only 1 entity? + } else { + return `${component.name}_${fieldName}_b${bitofs},x` // TODO? ,x? + } } entitiesMatching(atypes: ArchetypeMatch[]) { let result = []; @@ -721,9 +747,9 @@ const TEMPLATE3_D = ` %{@nomove}: `; -const TEMPLATE4_S = ` +const TEMPLATE4_S1 = ` .macro %{@KernelSetup} ent,ofs - lda #192 ; TODO: numlinesgit + lda #192 ; TODO: numlines sec sbc ypos_ypos_b0+ent sta %{$5}+ofs @@ -751,7 +777,8 @@ const TEMPLATE4_S = ` lda ypos_ypos_b0+ent sta %{$5}+ofs .endmacro - +` +const TEMPLATE4_S2 = ` %{@KernelSetup} 0,0 %{@KernelSetup} 1,6 ` @@ -867,7 +894,7 @@ function test() { name: 'sprite', fields: [ { name: 'height', dtype: 'int', lo: 0, hi: 255 }, { name: 'plyrindex', dtype: 'int', lo: 0, hi: 1 }, - { name: 'flags', dtype: 'int', lo: 0, hi: 255 }, + { name: 'nusiz', dtype: 'int', lo: 0, hi: 15 }, ] }) let c_player = em.defineComponent({ @@ -915,7 +942,6 @@ function test() { // init -> [start] -> frameloop // frameloop -> [preframe] [kernel] [postframe] - // TODO: where is kernel numlines? // temp between preframe + frame? // TODO: check names for identifierness em.defineSystem({ @@ -923,7 +949,13 @@ function test() { tempbytes: 8, actions: [ { - text: TEMPLATE4_S, event: 'preframe', select: 'once', query: { + text: TEMPLATE4_S1, event: 'preframe', select: 'once', query: { + include: ['kernel'] + } + }, + { + // TODO: should include kernel for numlines + text: TEMPLATE4_S2, event: 'preframe', select: 'once', query: { include: ['sprite', 'hasbitmap', 'hascolormap', 'ypos'], }, }, @@ -945,17 +977,12 @@ function test() { //{ text:SETHORIZPOS }, ] }) - // TODO: how to have subsystems? maybe need Scopes - // TODO: easy stagger of system update? - // TODO: easy lookup tables - // TODO: how to init? - // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html em.defineSystem({ name: 'frameloop', emits: ['preframe', 'kernel', 'postframe'], actions: [ - { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] } } // TODO: []? + { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] } } ] }) em.defineSystem({ @@ -984,7 +1011,7 @@ function test() { em.defineSystem({ name: 'SetHorizPos', actions: [ - { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: ['xpos'] } }, // TODO: []? + { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: ['xpos'] } }, ] }); @@ -992,23 +1019,24 @@ function test() { let scene = em.newScope("Scene", root); let e_ekernel = root.newEntity({ components: [c_kernel] }); root.setConstValue(e_ekernel, c_kernel, 'lines', 192); - root.setConstValue(e_ekernel, c_kernel, 'bgcolor', 0x92); + //root.setConstValue(e_ekernel, c_kernel, 'bgcolor', 0x92); + root.setInitValue(e_ekernel, c_kernel, 'bgcolor', 0x92); let e_bitmap0 = root.newEntity({ components: [c_bitmap] }); // TODO: store array sizes? - root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([0, 1, 3, 7, 15, 31, 0])); + root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([1, 1, 3, 7, 15, 31, 63, 127])); let e_colormap0 = root.newEntity({ components: [c_colormap] }); - root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([0, 3, 6, 9, 12, 14, 0])); + root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([6, 3, 6, 9, 12, 14, 31, 63])); let ea_playerSprite = { components: [c_sprite, c_hasbitmap, c_hascolormap, c_xpos, c_ypos, c_player] }; let e_player0 = root.newEntity(ea_playerSprite); - root.setInitValue(e_player0, c_sprite, 'plyrindex', 0); + root.setConstValue(e_player0, c_sprite, 'plyrindex', 0); root.setInitValue(e_player0, c_sprite, 'height', 8); root.setInitValue(e_player0, c_xpos, 'xpos', 50); root.setInitValue(e_player0, c_ypos, 'ypos', 50); let e_player1 = root.newEntity(ea_playerSprite); - root.setInitValue(e_player1, c_sprite, 'plyrindex', 1); + root.setConstValue(e_player1, c_sprite, 'plyrindex', 1); root.setInitValue(e_player1, c_sprite, 'height', 8); root.setInitValue(e_player1, c_xpos, 'xpos', 100); root.setInitValue(e_player1, c_ypos, 'ypos', 60); From b0810a2f665144d1f9eed07bbf43d1ad4b113a26 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 27 Jan 2022 18:02:37 -0600 Subject: [PATCH 009/104] ecs: yaml? --- package-lock.json | 168 +++++++++++++++++++++++++++--------------- package.json | 2 + src/common/ecs/ecs.ts | 18 ++++- 3 files changed, 127 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4be23fe3..fcc30a30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,16 +11,17 @@ "dependencies": { "@types/chroma-js": "^2.1.3", "@types/emscripten": "^1.39.5", + "@types/js-yaml": "^4.0.5", "@wasmer/wasi": "^0.12.0", "@wasmer/wasmfs": "^0.12.0", "binaryen": "^101.0.0", "chroma-js": "^2.1.2", - "chromedriver": "^97.0.1", "clipboard": "^2.0.6", "error-stack-parser": "^2.0.6", "fast-png": "^5.0.4", "file-saver": "^2.0.5", "jquery": "^3.6.0", + "js-yaml": "^4.1.0", "jszip": "^3.7.0", "localforage": "^1.9.0", "mousetrap": "^1.6.5", @@ -399,6 +400,11 @@ "@types/sizzle": "*" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" + }, "node_modules/@types/node": { "version": "14.18.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", @@ -589,13 +595,9 @@ "optional": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "optional": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-equal": { "version": "1.0.0", @@ -3152,13 +3154,11 @@ "optional": true }, "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "optional": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -3826,12 +3826,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/mocha/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -4003,18 +3997,6 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/mocha/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4333,6 +4315,15 @@ "node": ">=4" } }, + "node_modules/nightwatch/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "optional": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/nightwatch/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4369,6 +4360,19 @@ "ms": "^2.1.1" } }, + "node_modules/nightwatch/node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "optional": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/nightwatch/node_modules/log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -4677,6 +4681,28 @@ "node": ">=6" } }, + "node_modules/nyc/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "optional": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/nyc/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "optional": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/nyc/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -7212,6 +7238,11 @@ "@types/sizzle": "*" } }, + "@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" + }, "@types/node": { "version": "14.18.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", @@ -7368,13 +7399,9 @@ "optional": true }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "optional": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-equal": { "version": "1.0.0", @@ -9329,13 +9356,11 @@ "optional": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "optional": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "jsbn": { @@ -9898,12 +9923,6 @@ "color-convert": "^2.0.1" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -10023,15 +10042,6 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10262,6 +10272,15 @@ "color-convert": "^1.9.0" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "optional": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -10293,6 +10312,16 @@ "ms": "^2.1.1" } }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "optional": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -10549,6 +10578,25 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "optional": true }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "optional": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "optional": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", diff --git a/package.json b/package.json index ee9c24a6..13f2e815 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@types/chroma-js": "^2.1.3", "@types/emscripten": "^1.39.5", + "@types/js-yaml": "^4.0.5", "@wasmer/wasi": "^0.12.0", "@wasmer/wasmfs": "^0.12.0", "binaryen": "^101.0.0", @@ -21,6 +22,7 @@ "fast-png": "^5.0.4", "file-saver": "^2.0.5", "jquery": "^3.6.0", + "js-yaml": "^4.1.0", "jszip": "^3.7.0", "localforage": "^1.9.0", "mousetrap": "^1.6.5", diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index e1da1b6c..22904595 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1,9 +1,13 @@ +import * as YAML from "js-yaml"; + // 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 @@ -649,6 +653,12 @@ export class EntityManager { } } } + toYAML() { + return YAML.dump({ + components: this.components, + systems: this.systems, + }) + } } /// @@ -894,7 +904,11 @@ function test() { name: 'sprite', fields: [ { name: 'height', dtype: 'int', lo: 0, hi: 255 }, { name: 'plyrindex', dtype: 'int', lo: 0, hi: 1 }, - { name: 'nusiz', dtype: 'int', lo: 0, hi: 15 }, + ] + }) + let c_plyrflags = em.defineComponent({ + name: 'nusizable', fields: [ + { name: 'plyrflags', dtype: 'int', lo: 0, hi: 63 }, ] }) let c_player = em.defineComponent({ @@ -1048,8 +1062,10 @@ function test() { root.generateCode(); root.dump(src); console.log(src.toString()); + //console.log(em.toYAML()); } // TODO: files in markdown? test(); + From dd5a95688f9093ab99692a462bd61cd35342631a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 28 Jan 2022 07:20:02 -0600 Subject: [PATCH 010/104] ecs: dialect --- src/common/ecs/ecs.ts | 91 ++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 22904595..5c29d247 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -129,6 +129,38 @@ interface ArchetypeMatch { cmatch: ComponentType[]; } +class Dialect_CA65 { + readonly ASM_ITERATE_EACH = ` + ldx #0 +%{@__each}: + %{code} + inx + cpx #%{ecount} + bne %{@__each} +`; + readonly INIT_FROM_ARRAY = ` + ldy #%{nbytes} +: + lda %{src}-1,y + sta %{dest}-1,y + dey + bne - +` + readonly HEADER = ` +.include "vcs-ca65.h" +` + readonly FOOTER = ` +.segment "VECTORS" +VecNMI: .word Start +VecReset: .word Start +VecBRK: .word Start +` + readonly TEMPLATE_INIT = ` +Start: + CLEAN_START +` +} + class SourceFileExport { lines: string[] = []; @@ -245,16 +277,6 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { return 0; } -// TODO: language dialects? -const ASM_ITERATE_EACH = ` - ldx #0 -%{@__each}: - %{code} - inx - cpx #%{ecount} - bne %{@__each} -`; - export class EntityScope { childScopes: EntityScope[] = []; entities: Entity[] = []; @@ -268,6 +290,7 @@ export class EntityScope { constructor( public readonly em: EntityManager, + public readonly dialect: Dialect_CA65, public readonly name: string, public readonly parent: EntityScope | undefined ) { @@ -388,7 +411,7 @@ export class EntityScope { // TODO: compress 0s? let bufsym = this.name + '__INITDATA'; let bufofs = this.rodata.allocateInitData(bufsym, initbytes); - let code = INITFROMARRAY; + let code = this.dialect.INIT_FROM_ARRAY; //TODO: function to repalce from dict? code = code.replace('%{nbytes}', initbytes.length.toString()) code = code.replace('%{src}', bufsym); @@ -489,7 +512,7 @@ export class EntityScope { wrapCodeInLoop(code: string, sys: System, action: Action, ents: Entity[]): string { // TODO: check ents // TODO: check segment bounds - let s = ASM_ITERATE_EACH; + let s = this.dialect.ASM_ITERATE_EACH; s = s.replace('%{elo}', ents[0].id.toString()); s = s.replace('%{ehi}', ents[ents.length - 1].id.toString()); s = s.replace('%{ecount}', ents.length.toString()); @@ -505,15 +528,17 @@ export class EntityScope { throw new Error(`cannot find component with field "${fieldName}" in ${sys.name}:${action.event}`); } // see if all entities have the same constant value - let constValues = new Set(); + let constValues = new Set(); for (let e of entities) { let constVal = e.consts[mksymbol(component, fieldName)]; constValues.add(constVal); // constVal === undefined is allowed } // is it a constant? if (constValues.size == 1) { - let value = constValues.values().next().value; - if (value !== undefined) { + let value = constValues.values().next().value as DataValue; + // TODO: what about symbols? + // TODO: use dialect + if (typeof value === 'number') { if (bitofs == 0) return `#<${value}`; if (bitofs == 8) return `#>${value}`; // TODO: bitofs? @@ -521,6 +546,7 @@ export class EntityScope { } // TODO: offset > 0? //let range = this.bss.getFieldRange(component, fieldName); + // TODO: dialect if (action.select == 'once') { if (entities.length != 1) throw new Error(`can't choose multiple entities for ${fieldName} with select=once`); @@ -576,7 +602,7 @@ export class EntityScope { } generateCode() { this.tempOffset = this.maxTempBytes = 0; - this.code.addCodeFragment(TEMPLATE_INIT); + this.code.addCodeFragment(this.dialect.TEMPLATE_INIT); let initcode = this.allocateInitData(this.bss); this.code.addCodeFragment(initcode); let start = this.generateCodeForEvent('start'); @@ -587,25 +613,26 @@ export class EntityScope { } } dump(file: SourceFileExport) { - file.text(HEADER); // TODO + file.text(this.dialect.HEADER); // TODO file.segment(`${this.name}_DATA`, 'bss'); if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); this.bss.dump(file); file.segment(`${this.name}_CODE`, 'rodata'); this.rodata.dump(file); this.code.dump(file); - file.text(FOOTER); // TODO + file.text(this.dialect.FOOTER); // TODO } } export class EntityManager { + dialect = new Dialect_CA65(); archtypes = new Set(); components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; newScope(name: string, parent?: EntityScope) { - let scope = new EntityScope(this, name, parent); + let scope = new EntityScope(this, this.dialect, name, parent); if (this.scopes[name]) throw new Error(`scope ${name} already defined`); this.scopes[name] = scope; return scope; @@ -663,22 +690,6 @@ export class EntityManager { /// -const HEADER = ` -.include "vcs-ca65.h" -` - -const FOOTER = ` -.segment "VECTORS" -VecNMI: .word Start -VecReset: .word Start -VecBRK: .word Start -` - -const TEMPLATE_INIT = ` -Start: - CLEAN_START -` - const TEMPLATE1 = ` %{@NextFrame}: FRAME_START @@ -881,15 +892,6 @@ InitMemory: .done rts ` -// TODO: @loop -const INITFROMARRAY = ` - ldy #%{nbytes} -@loop: - lda %{src}-1,y - sta %{dest}-1,y - dey - bne @loop -` function test() { let em = new EntityManager(); @@ -1066,6 +1068,7 @@ function test() { } // TODO: files in markdown? +// TODO: jsr OperModeExecutionTree? test(); From 46f60639290e09ec8ecb91eb2974d2b7fb78aa01 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 28 Jan 2022 10:04:09 -0600 Subject: [PATCH 011/104] typescript unit tests in src/test --- package-lock.json | 946 +++++++++++++++++++++++++++++++++++++++++-- package.json | 6 +- src/test/testutil.ts | 136 +++++++ test/cli/testutil.js | 138 ------- 4 files changed, 1060 insertions(+), 166 deletions(-) create mode 100644 src/test/testutil.ts delete mode 100644 test/cli/testutil.js diff --git a/package-lock.json b/package-lock.json index fcc30a30..7d53b8fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,8 +33,10 @@ "devDependencies": { "@types/bootbox": "^5.1.3", "@types/bootstrap": "^3.4.0", + "@types/expect": "^24.3.0", "@types/file-saver": "^2.0.3", "@types/jquery": "^3.5.5", + "@types/mocha": "^9.1.0", "@types/node": "^14.14.20", "atob": "^2.1.x", "bootstrap": "^3.4.1", @@ -63,7 +65,7 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/highlight": "^7.16.7" }, @@ -160,7 +162,7 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "optional": true, + "devOptional": true, "engines": { "node": ">=6.9.0" } @@ -169,7 +171,7 @@ "version": "7.16.10", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "optional": true, + "devOptional": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", @@ -183,7 +185,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "optional": true, + "devOptional": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -195,7 +197,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "optional": true, + "devOptional": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -209,7 +211,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "optional": true, + "devOptional": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -277,6 +279,92 @@ "node": ">=6.9.0" } }, + "node_modules/@jest/types": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", + "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -385,12 +473,46 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "optional": true }, + "node_modules/@types/expect": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-24.3.0.tgz", + "integrity": "sha512-aq5Z+YFBz5o2b6Sp1jigx5nsmoZMK5Ceurjwy6PZmRv7dEi1jLtkARfvB1ME+OXJUG+7TZUDcv3WoCr/aor6dQ==", + "deprecated": "This is a stub types definition. expect provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "expect": "*" + } + }, "node_modules/@types/file-saver": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", "dev": true }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/jquery": { "version": "3.5.13", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.13.tgz", @@ -405,6 +527,12 @@ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, + "node_modules/@types/mocha": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "dev": true + }, "node_modules/@types/node": { "version": "14.18.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", @@ -422,6 +550,27 @@ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, "node_modules/@types/yauzl": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", @@ -1129,7 +1278,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "optional": true, + "devOptional": true, "dependencies": { "color-name": "1.1.3" } @@ -1138,7 +1287,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "optional": true + "devOptional": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -1450,6 +1599,15 @@ "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", + "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1769,6 +1927,21 @@ "node": ">=0.10.0" } }, + "node_modules/expect": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz", + "integrity": "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "jest-get-type": "^27.4.0", + "jest-matcher-utils": "^27.4.6", + "jest-message-util": "^27.4.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2286,7 +2459,7 @@ "version": "4.2.9", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "optional": true + "devOptional": true }, "node_modules/graceful-readlink": { "version": "1.0.1", @@ -2363,7 +2536,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "optional": true, + "devOptional": true, "engines": { "node": ">=4" } @@ -3142,6 +3315,275 @@ "node": ">=4" } }, + "node_modules/jest-diff": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz", + "integrity": "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.4.0", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-get-type": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", + "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz", + "integrity": "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.4.6", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz", + "integrity": "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.4.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.4.6", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jquery": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", @@ -3151,7 +3593,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "optional": true + "devOptional": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -3663,7 +4105,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "optional": true, + "devOptional": true, "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" @@ -5310,6 +5752,41 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", + "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -5447,6 +5924,12 @@ "node": ">= 0.8" } }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -5878,7 +6361,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "optional": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -6041,6 +6524,27 @@ "node": ">=0.10.0" } }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/stackframe": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", @@ -6963,7 +7467,7 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "optional": true, + "devOptional": true, "requires": { "@babel/highlight": "^7.16.7" } @@ -7038,13 +7542,13 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "optional": true + "devOptional": true }, "@babel/highlight": { "version": "7.16.10", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "optional": true, + "devOptional": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", @@ -7055,7 +7559,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "optional": true, + "devOptional": true, "requires": { "color-convert": "^1.9.0" } @@ -7064,7 +7568,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "optional": true, + "devOptional": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -7075,7 +7579,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "optional": true, + "devOptional": true, "requires": { "has-flag": "^3.0.0" } @@ -7127,6 +7631,70 @@ "to-fast-properties": "^2.0.0" } }, + "@jest/types": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", + "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7223,12 +7791,45 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "optional": true }, + "@types/expect": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-24.3.0.tgz", + "integrity": "sha512-aq5Z+YFBz5o2b6Sp1jigx5nsmoZMK5Ceurjwy6PZmRv7dEi1jLtkARfvB1ME+OXJUG+7TZUDcv3WoCr/aor6dQ==", + "dev": true, + "requires": { + "expect": "*" + } + }, "@types/file-saver": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", "dev": true }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "@types/jquery": { "version": "3.5.13", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.13.tgz", @@ -7243,6 +7844,12 @@ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, + "@types/mocha": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "dev": true + }, "@types/node": { "version": "14.18.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", @@ -7260,6 +7867,27 @@ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, "@types/yauzl": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", @@ -7804,7 +8432,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "optional": true, + "devOptional": true, "requires": { "color-name": "1.1.3" } @@ -7813,7 +8441,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "optional": true + "devOptional": true }, "combined-stream": { "version": "1.0.8", @@ -8062,6 +8690,12 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "optional": true }, + "diff-sequences": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", + "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -8310,6 +8944,18 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "devOptional": true }, + "expect": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz", + "integrity": "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "jest-get-type": "^27.4.0", + "jest-matcher-utils": "^27.4.6", + "jest-message-util": "^27.4.6" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -8732,7 +9378,7 @@ "version": "4.2.9", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "optional": true + "devOptional": true }, "graceful-readlink": { "version": "1.0.1", @@ -8790,7 +9436,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "optional": true + "devOptional": true }, "has-symbols": { "version": "1.0.2", @@ -9344,6 +9990,206 @@ } } }, + "jest-diff": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz", + "integrity": "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.4.0", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-get-type": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", + "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", + "dev": true + }, + "jest-matcher-utils": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz", + "integrity": "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.4.6", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz", + "integrity": "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.4.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.4.6", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "jquery": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", @@ -9353,7 +10199,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "optional": true + "devOptional": true }, "js-yaml": { "version": "4.1.0", @@ -9813,7 +10659,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "optional": true, + "devOptional": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.2.3" @@ -11055,6 +11901,31 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "devOptional": true }, + "pretty-format": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", + "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -11160,6 +12031,12 @@ "unpipe": "1.0.0" } }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -11490,7 +12367,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "optional": true + "devOptional": true }, "smart-buffer": { "version": "4.2.0", @@ -11624,6 +12501,23 @@ "tweetnacl": "~0.14.0" } }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, "stackframe": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", diff --git a/package.json b/package.json index 13f2e815..5cb29d87 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,10 @@ "devDependencies": { "@types/bootbox": "^5.1.3", "@types/bootstrap": "^3.4.0", + "@types/expect": "^24.3.0", "@types/file-saver": "^2.0.3", "@types/jquery": "^3.5.5", + "@types/mocha": "^9.1.0", "@types/node": "^14.14.20", "atob": "^2.1.x", "bootstrap": "^3.4.1", @@ -68,8 +70,8 @@ "esbuild-worker": "esbuild src/worker/workermain.ts --bundle --sourcemap --target=es2017 --outfile=./gen/worker/bundle.js", "esbuild-ui": "esbuild src/ide/ui.ts src/ide/embedui.ts --splitting --format=esm --bundle --minify --sourcemap --target=es2017 --outdir=./gen/ --external:path --external:fs", "test-one": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000", - "test-node": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 test/cli", - "test-profile": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 --prof test/cli", + "test-node": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 test/cli gen/test", + "test-profile": "NODE_PATH=$(pwd) mocha --recursive --timeout 60000 --prof test/cli gen/test", "test-worker": "NODE_PATH=$(pwd) mocha --timeout 60000 test/cli/testworker.js", "test-platforms": "NODE_PATH=$(pwd) mocha --timeout 60000 test/cli/testplatforms.js", "test-verilog": "NODE_PATH=$(pwd) mocha --timeout 60000 --reporter mocha-simple-html-reporter --reporter-options output=test/output/verilog.html test/verilog/testverilog.js", diff --git a/src/test/testutil.ts b/src/test/testutil.ts new file mode 100644 index 00000000..b95210ec --- /dev/null +++ b/src/test/testutil.ts @@ -0,0 +1,136 @@ + +import assert from "assert"; +import { describe } from "mocha"; +import { EmuHalt } from "../../src/common/emu" +import { lzgmini, isProbablyBinary } from "../../src/common/util"; + +var NES_CONIO_ROM_LZG = [ + 76,90,71,0,0,160,16,0,0,11,158,107,131,223,83,1,9,17,21,22,78,69,83,26,2,1,3,0,22,6,120,216, + 162,0,134,112,134,114,134,113,134,115,154,169,32,157,0,2,157,0,3,157,0,4,232,208,244,32,134,130,32,85,129,169, + 0,162,8,133,2,134,3,32,93,128,32,50,129,32,73,129,76,0,128,72,152,72,138,72,169,1,133,112,230,107,208,2, + 230,108,32,232,129,169,32,141,6,32,169,0,22,129,141,5,22,66,104,170,104,168,104,64,160,0,240,7,169,105,162,128, + 76,4,96,96,162,0,21,23,0,32,22,195,1,22,194,63,21,37,21,134,22,197,41,21,27,173,41,96,201,4,32,169, + 129,240,3,76,158,128,76,188,128,169,184,162,130,24,109,41,96,144,1,232,160,0,32,130,129,141,7,21,36,238,41,96, + 21,32,76,140,128,21,47,33,21,246,201,17,14,61,15,21,253,227,128,76,1,129,169,169,17,24,61,209,21,125,17,2, + 180,17,10,130,5,22,201,128,17,4,172,30,141,1,32,76,46,129,22,65,96,173,0,96,174,1,96,32,112,130,173,2, + 96,174,3,21,65,160,4,76,105,128,17,3,228,188,162,130,17,2,228,169,188,133,10,169,130,133,11,169,0,133,12,169, + 96,133,13,162,214,169,255,133,18,160,0,232,240,13,177,10,145,12,200,208,246,230,11,230,13,208,240,230,18,208,239,96, + 133,10,134,11,162,0,177,10,96,208,42,162,0,138,96,240,36,22,163,30,48,28,22,227,2,16,20,22,227,14,144,12, + 21,200,176,4,22,226,162,0,169,1,96,165,115,208,252,96,169,255,197,115,240,252,96,133,118,132,116,134,117,32,193,129, + 164,113,165,116,153,0,2,165,117,153,0,3,165,118,153,0,4,200,132,113,230,115,96,164,115,208,1,96,166,114,169,14, + 141,42,96,189,0,2,141,6,32,189,0,3,22,163,4,141,7,32,232,136,240,93,17,19,14,71,17,19,14,49,17,19, + 14,27,17,19,14,5,206,42,96,208,141,134,114,132,115,96,169,0,162,0,72,165,2,56,233,2,133,2,176,2,198,3, + 160,1,138,145,2,104,136,145,2,96,169,41,133,10,169,96,17,34,41,168,162,0,240,10,145,10,200,208,251,230,11,202, + 208,246,192,2,240,5,21,70,247,96,78,111,32,99,97,114,116,32,108,111,97,100,101,100,0,1,0,16,32,17,66,184, + 141,18,96,142,19,96,141,25,96,142,26,96,136,185,255,255,141,35,22,196,34,96,140,37,96,32,255,255,160,255,208,232, + 96,17,71,230,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,10,53,128,0,128,92,128, + 17,14,14,204,204,51,51,22,106,0,24,60,126,24,22,1,22,231,16,48,127,127,48,16,0,22,230,12,18,48,124,48, + 98,252,22,231,0,0,3,62,118,54,54,22,231,127,127,17,4,80,22,230,224,224,96,22,3,22,230,24,24,24,248,248, + 21,16,22,230,204,153,51,102,22,106,51,153,204,22,107,21,27,255,255,17,4,67,22,227,3,22,13,17,6,188,22,230, + 17,2,172,22,13,31,31,22,236,255,255,22,236,31,31,17,4,136,22,227,22,1,248,248,21,5,22,233,17,14,123,17, + 3,64,22,230,17,3,64,21,248,17,8,29,21,216,17,6,88,17,3,64,22,230,240,22,13,21,233,21,243,22,230,17, + 6,16,22,226,192,192,48,48,22,106,15,22,1,21,84,22,230,17,10,4,22,226,17,10,52,22,230,17,6,16,17,10, + 44,22,6,17,35,220,0,24,22,231,102,102,17,34,107,0,22,233,255,22,33,102,22,231,24,62,96,60,6,124,21,40, + 22,229,0,102,12,24,48,102,70,22,231,60,102,60,56,103,102,63,22,231,6,12,17,36,59,22,230,21,30,48,48,24, + 12,22,231,22,97,12,21,4,22,231,0,102,60,255,60,17,2,115,22,230,24,24,126,17,35,70,22,230,17,4,173,21, + 33,22,231,126,21,205,22,231,21,80,22,232,3,6,12,24,48,96,22,231,60,102,110,118,102,102,60,22,231,24,24,56, + 24,24,24,126,22,231,60,102,6,12,48,96,22,235,28,6,21,168,22,228,6,14,30,102,127,6,6,22,231,126,96,124, + 6,21,80,22,230,60,102,96,124,17,4,88,22,228,126,102,12,17,35,83,22,230,60,21,13,21,216,22,231,62,21,240, + 22,228,17,34,124,22,66,22,236,17,2,224,22,228,14,24,48,96,48,24,14,0,22,230,17,2,239,17,4,241,22,228, + 112,24,12,6,12,24,112,22,231,17,2,192,24,21,52,22,232,110,110,96,98,17,3,248,22,227,24,60,102,126,17,34, + 228,22,230,124,102,102,22,66,22,231,60,102,96,96,96,17,4,200,22,227,120,108,21,30,108,120,22,231,126,96,96,120, + 96,96,126,22,237,96,22,231,21,48,110,17,37,8,22,227,21,46,17,3,96,22,230,60,17,99,19,21,24,22,229,30, + 12,22,1,108,56,22,231,102,108,120,112,120,108,21,40,22,229,17,132,62,126,22,231,99,119,127,107,99,99,99,22,231, + 102,118,126,126,110,17,2,88,22,229,60,102,22,2,17,35,88,22,227,17,2,205,21,49,22,231,21,144,60,14,22,231, + 21,80,17,2,96,22,230,60,102,96,60,17,37,208,22,227,17,163,13,17,34,200,22,229,21,111,17,5,208,22,232,60, + 17,5,16,22,225,99,99,99,107,127,119,99,22,231,21,77,60,17,3,248,22,230,21,1,17,4,64,22,227,126,17,67, + 159,126,22,231,60,48,22,2,60,22,231,96,48,24,12,6,3,0,22,231,60,17,34,32,12,21,24,22,229,17,34,193, + 17,68,244,22,229,22,3,17,165,133,22,225,17,134,203,22,230,21,58,6,62,102,62,22,232,96,17,66,176,124,22,232, + 0,60,96,96,96,17,66,144,22,229,6,21,31,21,96,22,230,0,60,102,126,21,216,22,228,14,24,62,17,3,84,22, + 230,0,21,95,6,124,22,231,17,3,80,102,17,5,88,22,225,24,0,56,17,34,240,22,231,6,0,6,22,1,60,22, + 231,96,96,108,17,34,128,22,231,21,30,21,160,22,230,0,102,127,127,107,99,22,233,17,2,79,21,32,22,231,17,34, + 210,17,4,152,22,228,17,36,242,22,232,17,3,144,6,22,232,124,17,66,226,21,160,22,228,17,131,225,22,232,17,130, + 127,17,98,112,22,230,17,35,226,17,34,0,22,233,60,17,2,240,22,230,99,107,127,62,17,226,24,22,230,17,35,241, + 22,234,21,47,12,120,22,232,126,12,24,48,17,98,194,22,228,28,48,24,112,24,48,28,22,231,17,164,159,22,3,22, + 227,56,12,24,14,24,12,56,0,22,230,51,255,204,17,35,206,22,230,22,14,17,194,92,22,10,17,236,246,204,204,255, + 231,195,129,231,22,1,22,231,239,207,128,128,207,239,255,22,230,243,237,207,131,207,157,3,22,231,255,255,252,193,137,201, + 201,22,231,128,128,17,4,80,22,230,31,31,159,22,3,22,230,231,231,231,7,7,21,16,22,230,17,236,246,204,17,237, + 246,51,153,17,227,11,17,4,67,22,227,252,22,13,17,6,188,22,230,17,2,172,22,13,224,224,22,236,0,0,22,236, + 224,224,17,4,136,22,227,22,1,7,7,21,5,22,233,17,14,123,17,3,64,22,230,17,3,64,21,248,17,8,29,21, + 216,17,6,88,17,3,64,22,230,17,226,124,22,10,17,238,244,22,226,17,6,16,22,226,63,63,207,207,22,106,17,226, + 192,21,84,22,230,17,10,4,17,230,220,17,14,60,17,234,252,17,6,44,22,6,17,35,220,255,231,22,231,153,153,17, + 34,107,255,22,233,0,22,33,153,22,231,231,193,159,195,249,131,21,40,22,229,255,153,243,231,207,153,185,22,231,195,153, + 195,199,152,153,192,22,231,249,243,17,36,59,22,230,21,30,207,207,231,243,22,231,22,97,243,21,4,22,231,255,153,195, + 0,195,17,2,115,22,230,231,231,129,17,35,70,22,230,17,4,173,21,33,22,231,129,21,205,22,231,21,80,22,232,252, + 249,243,231,207,159,22,231,195,153,145,137,153,153,195,22,231,231,231,199,231,231,231,129,22,231,195,153,249,243,207,159,22, + 235,227,249,21,168,22,228,249,241,225,153,128,249,249,22,231,129,159,131,249,21,80,22,230,195,153,159,131,17,4,88,22, + 228,129,153,243,17,35,83,22,230,195,21,13,21,216,22,231,193,21,240,22,228,17,34,124,22,66,22,236,17,2,224,22, + 228,241,231,207,159,207,231,241,255,22,230,17,2,239,17,4,241,22,228,143,231,243,249,243,231,143,22,231,17,2,192,231, + 21,52,22,232,145,145,159,157,17,3,248,22,227,231,195,153,129,17,34,228,22,230,131,153,153,22,66,22,231,195,153,159, + 159,159,17,4,200,22,227,135,147,21,30,147,135,22,231,129,159,159,135,159,159,129,22,237,159,22,231,21,48,145,17,37, + 8,22,227,21,46,17,3,96,22,230,195,17,99,19,21,24,22,229,225,243,22,1,147,199,22,231,153,147,135,143,135,147, + 21,40,22,229,17,132,62,129,22,231,156,136,128,148,156,156,156,22,231,153,137,129,129,145,17,2,88,22,229,195,153,22, + 2,17,35,88,22,227,17,2,205,21,49,22,231,21,144,195,241,22,231,21,80,17,2,96,22,230,195,153,159,195,17,37, + 208,22,227,17,163,13,17,34,200,22,229,21,111,17,5,208,22,232,195,17,5,16,22,225,156,156,156,148,128,136,156,22, + 231,21,77,195,17,3,248,22,230,21,1,17,4,64,22,227,129,17,67,159,129,22,231,195,207,22,2,195,22,231,159,207, + 231,243,249,252,255,22,231,195,17,34,32,243,21,24,22,229,17,34,193,17,68,244,22,229,22,3,17,165,133,22,225,17, + 134,203,22,230,21,58,249,193,153,193,22,232,159,17,66,176,131,22,232,255,195,159,159,159,17,66,144,22,229,249,21,31, + 21,96,22,230,255,195,153,129,21,216,22,228,241,231,193,17,3,84,22,230,255,21,95,249,131,22,231,17,3,80,153,17, + 5,88,22,225,231,255,199,17,34,240,22,231,249,255,249,22,1,195,22,231,159,159,147,17,34,128,22,231,21,30,21,160, + 22,230,255,153,128,128,148,156,22,233,17,2,79,21,32,22,231,17,34,210,17,4,152,22,228,17,36,242,22,232,17,3, + 144,249,22,232,131,17,66,226,21,160,22,228,17,131,225,22,232,17,130,127,17,98,112,22,230,17,35,226,17,34,0,22, + 233,195,17,2,240,22,230,156,148,128,193,17,226,24,22,230,17,35,241,22,234,21,47,243,135,22,232,129,243,231,207,17, + 98,194,22,228,227,207,231,143,231,207,227,22,231,17,164,159,22,3,22,227,199,243,231,241,231,243,199,255,22,230,204,0, + 51,17,35,206,22,230,22,14,9,19,0,13,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, + 22,31,22,31,22,31,22,31,22,31,22,30,22,28 + ]; + + describe('LZG', function() { + it('Should decode LZG', function() { + var rom = new Uint8Array(new lzgmini().decode(NES_CONIO_ROM_LZG)); + assert.equal(40976, rom.length); + }); + }); + + describe('string functions', function() { + it('Should detect binary', function() { + assert.ok(!isProbablyBinary(null, [32,32,10,13,9,32,32,10,13])); + assert.ok(isProbablyBinary(null, [32,32,0x80])); + assert.ok(!isProbablyBinary(null, [32,32,0xc1,0x81,32,32,10,13])); + assert.ok(isProbablyBinary(null, NES_CONIO_ROM_LZG)); + assert.ok(isProbablyBinary('test.bin')); + assert.ok(isProbablyBinary('test.chr')); + assert.ok(!isProbablyBinary('test.txt')); + assert.ok(isProbablyBinary('test.dat')); + assert.ok(!isProbablyBinary(null, [0x20,0xa9,0x20,0x31,0x39,0x38,0x32])); + assert.ok(isProbablyBinary(null, [0x00,0x00])); // 00 is binary + assert.ok(isProbablyBinary(null, [0x9f])); // 9f is binary + assert.ok(isProbablyBinary(null, [0xff])); // FF is binary + assert.ok(isProbablyBinary(null, [0xf0,0x12])); // ran out of data + }); + }); + + describe('EmuHalt', function() { + it('Should detect emuhalt', function() { + var e = new EmuHalt("?"); + assert.ok(e instanceof EmuHalt); + assert.ok(e.hasOwnProperty('$loc')); + }); + }); + \ No newline at end of file diff --git a/test/cli/testutil.js b/test/cli/testutil.js deleted file mode 100644 index cfa912a1..00000000 --- a/test/cli/testutil.js +++ /dev/null @@ -1,138 +0,0 @@ - -var vm = require('vm'); -var fs = require('fs'); -var assert = require('assert'); - -var emu = require("gen/common/emu.js"); -var util = require("gen/common/util.js"); -var nes = require("gen/platform/nes.js"); - -var NES_CONIO_ROM_LZG = [ - 76,90,71,0,0,160,16,0,0,11,158,107,131,223,83,1,9,17,21,22,78,69,83,26,2,1,3,0,22,6,120,216, - 162,0,134,112,134,114,134,113,134,115,154,169,32,157,0,2,157,0,3,157,0,4,232,208,244,32,134,130,32,85,129,169, - 0,162,8,133,2,134,3,32,93,128,32,50,129,32,73,129,76,0,128,72,152,72,138,72,169,1,133,112,230,107,208,2, - 230,108,32,232,129,169,32,141,6,32,169,0,22,129,141,5,22,66,104,170,104,168,104,64,160,0,240,7,169,105,162,128, - 76,4,96,96,162,0,21,23,0,32,22,195,1,22,194,63,21,37,21,134,22,197,41,21,27,173,41,96,201,4,32,169, - 129,240,3,76,158,128,76,188,128,169,184,162,130,24,109,41,96,144,1,232,160,0,32,130,129,141,7,21,36,238,41,96, - 21,32,76,140,128,21,47,33,21,246,201,17,14,61,15,21,253,227,128,76,1,129,169,169,17,24,61,209,21,125,17,2, - 180,17,10,130,5,22,201,128,17,4,172,30,141,1,32,76,46,129,22,65,96,173,0,96,174,1,96,32,112,130,173,2, - 96,174,3,21,65,160,4,76,105,128,17,3,228,188,162,130,17,2,228,169,188,133,10,169,130,133,11,169,0,133,12,169, - 96,133,13,162,214,169,255,133,18,160,0,232,240,13,177,10,145,12,200,208,246,230,11,230,13,208,240,230,18,208,239,96, - 133,10,134,11,162,0,177,10,96,208,42,162,0,138,96,240,36,22,163,30,48,28,22,227,2,16,20,22,227,14,144,12, - 21,200,176,4,22,226,162,0,169,1,96,165,115,208,252,96,169,255,197,115,240,252,96,133,118,132,116,134,117,32,193,129, - 164,113,165,116,153,0,2,165,117,153,0,3,165,118,153,0,4,200,132,113,230,115,96,164,115,208,1,96,166,114,169,14, - 141,42,96,189,0,2,141,6,32,189,0,3,22,163,4,141,7,32,232,136,240,93,17,19,14,71,17,19,14,49,17,19, - 14,27,17,19,14,5,206,42,96,208,141,134,114,132,115,96,169,0,162,0,72,165,2,56,233,2,133,2,176,2,198,3, - 160,1,138,145,2,104,136,145,2,96,169,41,133,10,169,96,17,34,41,168,162,0,240,10,145,10,200,208,251,230,11,202, - 208,246,192,2,240,5,21,70,247,96,78,111,32,99,97,114,116,32,108,111,97,100,101,100,0,1,0,16,32,17,66,184, - 141,18,96,142,19,96,141,25,96,142,26,96,136,185,255,255,141,35,22,196,34,96,140,37,96,32,255,255,160,255,208,232, - 96,17,71,230,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,10,53,128,0,128,92,128, - 17,14,14,204,204,51,51,22,106,0,24,60,126,24,22,1,22,231,16,48,127,127,48,16,0,22,230,12,18,48,124,48, - 98,252,22,231,0,0,3,62,118,54,54,22,231,127,127,17,4,80,22,230,224,224,96,22,3,22,230,24,24,24,248,248, - 21,16,22,230,204,153,51,102,22,106,51,153,204,22,107,21,27,255,255,17,4,67,22,227,3,22,13,17,6,188,22,230, - 17,2,172,22,13,31,31,22,236,255,255,22,236,31,31,17,4,136,22,227,22,1,248,248,21,5,22,233,17,14,123,17, - 3,64,22,230,17,3,64,21,248,17,8,29,21,216,17,6,88,17,3,64,22,230,240,22,13,21,233,21,243,22,230,17, - 6,16,22,226,192,192,48,48,22,106,15,22,1,21,84,22,230,17,10,4,22,226,17,10,52,22,230,17,6,16,17,10, - 44,22,6,17,35,220,0,24,22,231,102,102,17,34,107,0,22,233,255,22,33,102,22,231,24,62,96,60,6,124,21,40, - 22,229,0,102,12,24,48,102,70,22,231,60,102,60,56,103,102,63,22,231,6,12,17,36,59,22,230,21,30,48,48,24, - 12,22,231,22,97,12,21,4,22,231,0,102,60,255,60,17,2,115,22,230,24,24,126,17,35,70,22,230,17,4,173,21, - 33,22,231,126,21,205,22,231,21,80,22,232,3,6,12,24,48,96,22,231,60,102,110,118,102,102,60,22,231,24,24,56, - 24,24,24,126,22,231,60,102,6,12,48,96,22,235,28,6,21,168,22,228,6,14,30,102,127,6,6,22,231,126,96,124, - 6,21,80,22,230,60,102,96,124,17,4,88,22,228,126,102,12,17,35,83,22,230,60,21,13,21,216,22,231,62,21,240, - 22,228,17,34,124,22,66,22,236,17,2,224,22,228,14,24,48,96,48,24,14,0,22,230,17,2,239,17,4,241,22,228, - 112,24,12,6,12,24,112,22,231,17,2,192,24,21,52,22,232,110,110,96,98,17,3,248,22,227,24,60,102,126,17,34, - 228,22,230,124,102,102,22,66,22,231,60,102,96,96,96,17,4,200,22,227,120,108,21,30,108,120,22,231,126,96,96,120, - 96,96,126,22,237,96,22,231,21,48,110,17,37,8,22,227,21,46,17,3,96,22,230,60,17,99,19,21,24,22,229,30, - 12,22,1,108,56,22,231,102,108,120,112,120,108,21,40,22,229,17,132,62,126,22,231,99,119,127,107,99,99,99,22,231, - 102,118,126,126,110,17,2,88,22,229,60,102,22,2,17,35,88,22,227,17,2,205,21,49,22,231,21,144,60,14,22,231, - 21,80,17,2,96,22,230,60,102,96,60,17,37,208,22,227,17,163,13,17,34,200,22,229,21,111,17,5,208,22,232,60, - 17,5,16,22,225,99,99,99,107,127,119,99,22,231,21,77,60,17,3,248,22,230,21,1,17,4,64,22,227,126,17,67, - 159,126,22,231,60,48,22,2,60,22,231,96,48,24,12,6,3,0,22,231,60,17,34,32,12,21,24,22,229,17,34,193, - 17,68,244,22,229,22,3,17,165,133,22,225,17,134,203,22,230,21,58,6,62,102,62,22,232,96,17,66,176,124,22,232, - 0,60,96,96,96,17,66,144,22,229,6,21,31,21,96,22,230,0,60,102,126,21,216,22,228,14,24,62,17,3,84,22, - 230,0,21,95,6,124,22,231,17,3,80,102,17,5,88,22,225,24,0,56,17,34,240,22,231,6,0,6,22,1,60,22, - 231,96,96,108,17,34,128,22,231,21,30,21,160,22,230,0,102,127,127,107,99,22,233,17,2,79,21,32,22,231,17,34, - 210,17,4,152,22,228,17,36,242,22,232,17,3,144,6,22,232,124,17,66,226,21,160,22,228,17,131,225,22,232,17,130, - 127,17,98,112,22,230,17,35,226,17,34,0,22,233,60,17,2,240,22,230,99,107,127,62,17,226,24,22,230,17,35,241, - 22,234,21,47,12,120,22,232,126,12,24,48,17,98,194,22,228,28,48,24,112,24,48,28,22,231,17,164,159,22,3,22, - 227,56,12,24,14,24,12,56,0,22,230,51,255,204,17,35,206,22,230,22,14,17,194,92,22,10,17,236,246,204,204,255, - 231,195,129,231,22,1,22,231,239,207,128,128,207,239,255,22,230,243,237,207,131,207,157,3,22,231,255,255,252,193,137,201, - 201,22,231,128,128,17,4,80,22,230,31,31,159,22,3,22,230,231,231,231,7,7,21,16,22,230,17,236,246,204,17,237, - 246,51,153,17,227,11,17,4,67,22,227,252,22,13,17,6,188,22,230,17,2,172,22,13,224,224,22,236,0,0,22,236, - 224,224,17,4,136,22,227,22,1,7,7,21,5,22,233,17,14,123,17,3,64,22,230,17,3,64,21,248,17,8,29,21, - 216,17,6,88,17,3,64,22,230,17,226,124,22,10,17,238,244,22,226,17,6,16,22,226,63,63,207,207,22,106,17,226, - 192,21,84,22,230,17,10,4,17,230,220,17,14,60,17,234,252,17,6,44,22,6,17,35,220,255,231,22,231,153,153,17, - 34,107,255,22,233,0,22,33,153,22,231,231,193,159,195,249,131,21,40,22,229,255,153,243,231,207,153,185,22,231,195,153, - 195,199,152,153,192,22,231,249,243,17,36,59,22,230,21,30,207,207,231,243,22,231,22,97,243,21,4,22,231,255,153,195, - 0,195,17,2,115,22,230,231,231,129,17,35,70,22,230,17,4,173,21,33,22,231,129,21,205,22,231,21,80,22,232,252, - 249,243,231,207,159,22,231,195,153,145,137,153,153,195,22,231,231,231,199,231,231,231,129,22,231,195,153,249,243,207,159,22, - 235,227,249,21,168,22,228,249,241,225,153,128,249,249,22,231,129,159,131,249,21,80,22,230,195,153,159,131,17,4,88,22, - 228,129,153,243,17,35,83,22,230,195,21,13,21,216,22,231,193,21,240,22,228,17,34,124,22,66,22,236,17,2,224,22, - 228,241,231,207,159,207,231,241,255,22,230,17,2,239,17,4,241,22,228,143,231,243,249,243,231,143,22,231,17,2,192,231, - 21,52,22,232,145,145,159,157,17,3,248,22,227,231,195,153,129,17,34,228,22,230,131,153,153,22,66,22,231,195,153,159, - 159,159,17,4,200,22,227,135,147,21,30,147,135,22,231,129,159,159,135,159,159,129,22,237,159,22,231,21,48,145,17,37, - 8,22,227,21,46,17,3,96,22,230,195,17,99,19,21,24,22,229,225,243,22,1,147,199,22,231,153,147,135,143,135,147, - 21,40,22,229,17,132,62,129,22,231,156,136,128,148,156,156,156,22,231,153,137,129,129,145,17,2,88,22,229,195,153,22, - 2,17,35,88,22,227,17,2,205,21,49,22,231,21,144,195,241,22,231,21,80,17,2,96,22,230,195,153,159,195,17,37, - 208,22,227,17,163,13,17,34,200,22,229,21,111,17,5,208,22,232,195,17,5,16,22,225,156,156,156,148,128,136,156,22, - 231,21,77,195,17,3,248,22,230,21,1,17,4,64,22,227,129,17,67,159,129,22,231,195,207,22,2,195,22,231,159,207, - 231,243,249,252,255,22,231,195,17,34,32,243,21,24,22,229,17,34,193,17,68,244,22,229,22,3,17,165,133,22,225,17, - 134,203,22,230,21,58,249,193,153,193,22,232,159,17,66,176,131,22,232,255,195,159,159,159,17,66,144,22,229,249,21,31, - 21,96,22,230,255,195,153,129,21,216,22,228,241,231,193,17,3,84,22,230,255,21,95,249,131,22,231,17,3,80,153,17, - 5,88,22,225,231,255,199,17,34,240,22,231,249,255,249,22,1,195,22,231,159,159,147,17,34,128,22,231,21,30,21,160, - 22,230,255,153,128,128,148,156,22,233,17,2,79,21,32,22,231,17,34,210,17,4,152,22,228,17,36,242,22,232,17,3, - 144,249,22,232,131,17,66,226,21,160,22,228,17,131,225,22,232,17,130,127,17,98,112,22,230,17,35,226,17,34,0,22, - 233,195,17,2,240,22,230,156,148,128,193,17,226,24,22,230,17,35,241,22,234,21,47,243,135,22,232,129,243,231,207,17, - 98,194,22,228,227,207,231,143,231,207,227,22,231,17,164,159,22,3,22,227,199,243,231,241,231,243,199,255,22,230,204,0, - 51,17,35,206,22,230,22,14,9,19,0,13,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,30,22,28 -]; - -describe('LZG', function() { - it('Should decode LZG', function() { - var rom = new Uint8Array(new util.lzgmini().decode(NES_CONIO_ROM_LZG)); - assert.equal(40976, rom.length); - }); -}); - -describe('string functions', function() { - it('Should detect binary', function() { - assert.ok(!util.isProbablyBinary(null, [32,32,10,13,9,32,32,10,13])); - assert.ok(util.isProbablyBinary(null, [32,32,0x80])); - assert.ok(!util.isProbablyBinary(null, [32,32,0xc1,0x81,32,32,10,13])); - assert.ok(util.isProbablyBinary(null, NES_CONIO_ROM_LZG)); - assert.ok(util.isProbablyBinary('test.bin')); - assert.ok(util.isProbablyBinary('test.chr')); - assert.ok(!util.isProbablyBinary('test.txt')); - assert.ok(util.isProbablyBinary('test.dat')); - assert.ok(!util.isProbablyBinary(null, [0x20,0xa9,0x20,0x31,0x39,0x38,0x32])); - assert.ok(util.isProbablyBinary(null, [0x00,0x00])); // 00 is binary - assert.ok(util.isProbablyBinary(null, [0x9f])); // 9f is binary - assert.ok(util.isProbablyBinary(null, [0xff])); // FF is binary - assert.ok(util.isProbablyBinary(null, [0xf0,0x12])); // ran out of data - }); -}); - -describe('EmuHalt', function() { - it('Should detect emuhalt', function() { - var e = new emu.EmuHalt(); - assert.ok(e instanceof emu.EmuHalt); - assert.ok(e.hasOwnProperty('$loc')); - }); -}); From d2caee8cc3bbb3ab983584fd4f5722be46266a19 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 28 Jan 2022 11:22:59 -0600 Subject: [PATCH 012/104] use {{ }} delimiters, tokenizer, testecs, compiler --- package-lock.json | 9 +- package.json | 1 - src/common/ecs/compiler.ts | 212 +++++++++++++++++ src/common/ecs/ecs.ts | 471 ++++--------------------------------- src/common/ecs/main.ts | 30 +++ src/common/tokenizer.ts | 239 +++++++++++++++++++ src/test/testecs.ts | 449 +++++++++++++++++++++++++++++++++++ src/test/testutil.ts | 41 +++- 8 files changed, 1026 insertions(+), 426 deletions(-) create mode 100644 src/common/ecs/compiler.ts create mode 100644 src/common/ecs/main.ts create mode 100644 src/common/tokenizer.ts create mode 100644 src/test/testecs.ts diff --git a/package-lock.json b/package-lock.json index 7d53b8fa..1c715b31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,6 @@ "fast-png": "^5.0.4", "file-saver": "^2.0.5", "jquery": "^3.6.0", - "js-yaml": "^4.1.0", "jszip": "^3.7.0", "localforage": "^1.9.0", "mousetrap": "^1.6.5", @@ -746,7 +745,8 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-equal": { "version": "1.0.0", @@ -3599,6 +3599,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -8029,7 +8030,8 @@ "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "array-equal": { "version": "1.0.0", @@ -10205,6 +10207,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "requires": { "argparse": "^2.0.1" } diff --git a/package.json b/package.json index 5cb29d87..adc8c9db 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "fast-png": "^5.0.4", "file-saver": "^2.0.5", "jquery": "^3.6.0", - "js-yaml": "^4.1.0", "jszip": "^3.7.0", "localforage": "^1.9.0", "mousetrap": "^1.6.5", diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts new file mode 100644 index 00000000..4954125f --- /dev/null +++ b/src/common/ecs/compiler.ts @@ -0,0 +1,212 @@ + +import { Tokenizer, TokenType } from "../tokenizer"; +import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Dialect_CA65, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; + +export enum ECSTokenType { + Ellipsis = 'ellipsis', + Operator = 'delimiter', + QuotedString = 'quoted-string', + Integer = 'integer', +} + +export class ECSCompiler extends Tokenizer { + + em = new EntityManager(new Dialect_CA65()); // TODO + currentScope: EntityScope | null = null; + + constructor() { + super(); + //this.includeEOL = true; + this.setTokenRules([ + { type: TokenType.Ident, regex: /[$A-Za-z_][A-Za-z0-9_-]*/ }, + { type: TokenType.CodeFragment, regex: /---/ }, + { type: ECSTokenType.Ellipsis, regex: /\.\./ }, + { type: ECSTokenType.Operator, regex: /[=,:(){}\[\]]/ }, + { type: ECSTokenType.QuotedString, regex: /".*?"/ }, + { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, + { type: TokenType.Ignore, regex: /\s+/ }, + ]); + this.errorOnCatchAll = true; + } + + parseFile(text: string, path: string) { + this.tokenizeFile(text, path); + while (!this.isEOF()) { + this.parseTopLevel(); + } + } + + parseTopLevel() { + //this.skipBlankLines(); + let tok = this.expectTokens(['component', 'system', 'scope', 'comment']); + if (tok.str == 'component') { + return this.em.defineComponent(this.parseComponentDefinition()); + } + if (tok.str == 'system') { + return this.em.defineSystem(this.parseSystem()); + } + if (tok.str == 'scope') { + return this.parseScope(); + } + if (tok.str == 'comment') { + this.expectTokenTypes([TokenType.CodeFragment]); + return; + } + this.compileError(`Unexpected top-level keyword: ${tok.str}`); + } + + parseComponentDefinition(): ComponentType { + let name = this.expectIdent().str; + let fields = []; + while (this.peekToken().str != 'end') { + fields.push(this.parseComponentField()); + } + this.expectToken('end'); + return { name, fields }; + } + + parseComponentField(): DataField { + let name = this.expectIdent(); + this.expectToken(':'); + let type = this.parseDataType(); + return { name: name.str, ...type }; + } + + parseDataType(): DataType { + if (this.peekToken().type == 'integer') { + let lo = this.expectInteger(); + this.expectToken('..'); + let hi = this.expectInteger(); + return { dtype: 'int', lo, hi } as IntType; + } + if (this.peekToken().str == '[') { + return { dtype: 'ref', query: this.parseQuery() } as RefType; + } + if (this.peekToken().str == 'array') { + this.expectToken('array'); + this.expectToken('of'); + return { dtype: 'array', elem: this.parseDataType() } as ArrayType; + } + this.compileError(`Unknown data type`); // TODO + } + + parseDataValue() : DataValue { + if (this.peekToken().type == 'integer') { + return this.expectInteger(); + } + this.compileError(`Unknown data value`); // TODO + } + + expectInteger(): number { + let i = parseInt(this.consumeToken().str); + if (isNaN(i)) this.compileError('There should be an integer here.'); + return i; + } + + parseSystem(): System { + let name = this.expectIdent().str; + let actions: Action[] = []; + let system: System = { name, actions }; + let cmd; + while ((cmd = this.consumeToken().str) != 'end') { + if (cmd == 'on') { + actions.push(this.parseAction()); + } else if (cmd == 'locals') { + system.tempbytes = this.expectInteger(); + } else { + this.compileError(`Unexpected system keyword: ${cmd}`); + } + } + return system; + } + + parseAction(): Action { + let event = this.expectIdent().str; + this.expectToken('do'); + let select = this.expectTokens(['once', 'each']).str as SelectType; // TODO: type check? + let query = this.parseQuery(); + let text = this.parseCode(); + return { text, event, query, select }; + } + + parseQuery() { + let q: Query = { include: [] }; + this.expectToken('['); + q.include = this.parseList(this.parseComponentRef, ',').map(c => c.name); + // TODO: other params + this.expectToken(']'); + return q; + } + + parseEvent() { + return this.expectIdent().str; + } + + parseCode(): string { + return this.expectTokenTypes([TokenType.CodeFragment]).str; + } + + parseScope() : EntityScope { + let name = this.expectIdent().str; + let scope = this.em.newScope(name, this.currentScope); + this.currentScope = scope; + let cmd; + while ((cmd = this.consumeToken().str) != 'end') { + if (cmd == 'entity') { + this.parseEntity(); + } else { + this.compileError(`Unexpected scope keyword: ${cmd}`); + } + } + this.currentScope = scope.parent; + return scope; + } + + parseEntity() : Entity { + let name = ''; + if (this.peekToken().type == TokenType.Ident) { + name = this.expectIdent().str; + } + let etype = this.parseEntityArchetype(); + let e = this.currentScope.newEntity(etype); + e.name = name; + let cmd; + while ((cmd = this.consumeToken().str) != 'end') { + // TODO: check data types + if (cmd == 'const') { + let name = this.expectIdent().str; + this.expectToken('='); + e.consts[name] = this.parseDataValue(); + } else if (cmd == 'init') { + let name = this.expectIdent().str; + this.expectToken('='); + e.inits[name] = this.parseDataValue(); + } else { + this.compileError(`Unexpected scope keyword: ${cmd}`); + } + } + return e; + } + + parseEntityArchetype() : EntityArchetype { + this.expectToken('['); + let components = this.parseList(this.parseComponentRef, ','); + this.expectToken(']'); + return {components}; + } + + parseComponentRef() : ComponentType { + let name = this.expectIdent().str; + let cref = this.em.getComponentByName(name); + if (!cref) this.compileError(`I couldn't find a component named "${name}".`) + return cref; + } + + exportToFile(src: SourceFileExport) { + for (let scope of Object.values(this.em.scopes)) { + scope.analyzeEntities(); + scope.generateCode(); + scope.dump(src); + } + } +} diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 5c29d247..8f822dc4 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1,6 +1,4 @@ -import * as YAML from "js-yaml"; - // entity scopes contain entities, and are nested // also contain segments (code, bss, rodata) // components and systems are global @@ -31,6 +29,8 @@ import * as YAML from "js-yaml"; // - converting assets to native formats? // - removing unused data +import { Token } from "../tokenizer"; + function mksymbol(c: ComponentType, fieldName: string) { return c.name + '_' + fieldName; } @@ -40,6 +40,7 @@ function mkscopesymbol(s: EntityScope, c: ComponentType, fieldName: string) { export interface Entity { id: number; + name?: string; etype: EntityArchetype; consts: { [component_field: string]: DataValue }; inits: { [scope_component_field: string]: DataValue }; @@ -62,7 +63,7 @@ export interface ComponentType { } export interface Query { - include: string[]; + include: string[]; // TODO: make ComponentType listen?: string[]; exclude?: string[]; updates?: string[]; @@ -79,9 +80,11 @@ export interface Action { text: string; event: string; query: Query; - select: 'once' | 'each' | 'source' + select: SelectType } +export type SelectType = 'once' | 'each' | 'source'; + export type DataValue = number | boolean | Uint8Array | Uint16Array; export type DataField = { name: string } & DataType; @@ -129,22 +132,21 @@ interface ArchetypeMatch { cmatch: ComponentType[]; } -class Dialect_CA65 { +export class Dialect_CA65 { readonly ASM_ITERATE_EACH = ` ldx #0 -%{@__each}: - %{code} +{{@__each}}: + {{code}} inx - cpx #%{ecount} - bne %{@__each} + cpx #{{ecount}} + bne {{@__each}} `; readonly INIT_FROM_ARRAY = ` - ldy #%{nbytes} -: - lda %{src}-1,y - sta %{dest}-1,y + ldy #{{nbytes}} +: lda {{src}}-1,y + sta {{dest}}-1,y dey - bne - + bne :- ` readonly HEADER = ` .include "vcs-ca65.h" @@ -159,9 +161,19 @@ VecBRK: .word Start Start: CLEAN_START ` + + comment(s: string) { + return `\n;;; ${s}\n`; + } + absolute(ident: string) { + return ident; + } + indexed_x(ident: string) { + return ident + ',x'; + } } -class SourceFileExport { +export class SourceFileExport { lines: string[] = []; comment(text: string) { @@ -254,7 +266,7 @@ class Segment { } getOriginSymbol() { let a = this.ofs2sym.get(0); - if (!a) throw new Error('getOriginSymbol'); + if (!a) throw new Error('getOriginSymbol(): no symbol at offset 0'); // TODO return a[0]; } } @@ -334,7 +346,7 @@ export class EntityScope { } allocateSegment(segment: Segment, readonly: boolean) { let fields = Object.values(segment.fieldranges); - fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); + // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); let f; while (f = fields.pop()) { let name = mksymbol(f.component, f.field.name); @@ -413,9 +425,9 @@ export class EntityScope { let bufofs = this.rodata.allocateInitData(bufsym, initbytes); let code = this.dialect.INIT_FROM_ARRAY; //TODO: function to repalce from dict? - code = code.replace('%{nbytes}', initbytes.length.toString()) - code = code.replace('%{src}', bufsym); - code = code.replace('%{dest}', segment.getOriginSymbol()); + code = code.replace('{{nbytes}}', initbytes.length.toString()) + code = code.replace('{{src}}', bufsym); + code = code.replace('{{dest}}', segment.getOriginSymbol()); return code; } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { @@ -454,9 +466,9 @@ export class EntityScope { for (let action of sys.actions) { if (action.event == event) { let code = this.replaceCode(action.text, sys, action); - s += `\n; \n`; + s += this.dialect.comment(``); s += code; - s += `\n; \n`; + s += this.dialect.comment(``); // TODO: check that this happens once? } } @@ -468,12 +480,13 @@ export class EntityScope { this.maxTempBytes = Math.max(this.tempOffset, this.maxTempBytes); } replaceCode(code: string, sys: System, action: Action): string { - const re = /\%\{(.+?)\}/g; + const re = /\{\{(.+?)\}\}/g; let label = sys.name + '_' + action.event; let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); // TODO: detect cycles // TODO: "source"? + // TODO: what if only 1 item? if (action.select == 'each') { code = this.wrapCodeInLoop(code, sys, action, entities); //console.log(sys.name, action.event, ents); @@ -513,10 +526,10 @@ export class EntityScope { // TODO: check ents // TODO: check segment bounds let s = this.dialect.ASM_ITERATE_EACH; - s = s.replace('%{elo}', ents[0].id.toString()); - s = s.replace('%{ehi}', ents[ents.length - 1].id.toString()); - s = s.replace('%{ecount}', ents.length.toString()); - s = s.replace('%{code}', code); + s = s.replace('{{elo}}', ents[0].id.toString()); + s = s.replace('{{ehi}}', ents[ents.length - 1].id.toString()); + s = s.replace('{{ecount}}', ents.length.toString()); + s = s.replace('{{code}}', code); return s; } generateCodeForField(sys: System, action: Action, @@ -547,12 +560,13 @@ export class EntityScope { // TODO: offset > 0? //let range = this.bss.getFieldRange(component, fieldName); // TODO: dialect + let ident = `${component.name}_${fieldName}_b${bitofs}`; if (action.select == 'once') { if (entities.length != 1) throw new Error(`can't choose multiple entities for ${fieldName} with select=once`); - return `${component.name}_${fieldName}_b${bitofs}` // TODO? check there's only 1 entity? + return this.dialect.absolute(ident) // TODO? check there's only 1 entity? } else { - return `${component.name}_${fieldName}_b${bitofs},x` // TODO? ,x? + return this.dialect.indexed_x(ident) } } entitiesMatching(atypes: ArchetypeMatch[]) { @@ -625,12 +639,13 @@ export class EntityScope { } export class EntityManager { - dialect = new Dialect_CA65(); archtypes = new Set(); components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; + constructor(public readonly dialect: Dialect_CA65) { + } newScope(name: string, parent?: EntityScope) { let scope = new EntityScope(this, this.dialect, name, parent); if (this.scopes[name]) throw new Error(`scope ${name} already defined`); @@ -643,7 +658,7 @@ export class EntityManager { } defineSystem(system: System) { if (this.systems[system.name]) throw new Error(`system ${system.name} already defined`); - this.systems[system.name] = system; + return this.systems[system.name] = system; } componentsMatching(q: Query, etype: EntityArchetype) { let list = []; @@ -680,395 +695,13 @@ export class EntityManager { } } } - toYAML() { - return YAML.dump({ + getComponentByName(name: string): ComponentType { + return this.components[name]; + } + toJSON() { + return JSON.stringify({ components: this.components, - systems: this.systems, + systems: this.systems }) } } - -/// - -const TEMPLATE1 = ` -%{@NextFrame}: - FRAME_START - %{!preframe} - KERNEL_START - %{!kernel} - KERNEL_END - %{!postframe} - FRAME_END - lsr SWCHB ; test Game Reset switch - bcs @NoStart - jmp Start -@NoStart: - jmp %{@NextFrame} -`; - -// TODO: two sticks? -const TEMPLATE2_a = ` - lda SWCHA - sta %{$0} -` -const TEMPLATE2_b = ` - asl %{$0} - bcs %{@SkipMoveRight} - %{!joyright} -%{@SkipMoveRight}: - asl %{$0} - bcs %{@SkipMoveLeft} - %{!joyleft} -%{@SkipMoveLeft}: - asl %{$0} - bcs %{@SkipMoveDown} - %{!joydown} -%{@SkipMoveDown}: - asl %{$0} - bcs %{@SkipMoveUp} - %{!joyup} -%{@SkipMoveUp}: -`; - -const TEMPLATE3_L = ` - lda %{ [start] -> frameloop - // frameloop -> [preframe] [kernel] [postframe] - - // temp between preframe + frame? - // TODO: check names for identifierness - em.defineSystem({ - name: 'kernel_simple', - tempbytes: 8, - actions: [ - { - text: TEMPLATE4_S1, event: 'preframe', select: 'once', query: { - include: ['kernel'] - } - }, - { - // TODO: should include kernel for numlines - text: TEMPLATE4_S2, event: 'preframe', select: 'once', query: { - include: ['sprite', 'hasbitmap', 'hascolormap', 'ypos'], - }, - }, - { - text: TEMPLATE4_K, event: 'kernel', select: 'once', query: { - include: ['kernel'] - } - }, - ] - }) - em.defineSystem({ - name: 'set_xpos', - actions: [ - { - text: SET_XPOS, event: 'preframe', select: 'each', query: { - include: ['sprite', 'xpos'] - }, - }, - //{ text:SETHORIZPOS }, - ] - }) - // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html - em.defineSystem({ - name: 'frameloop', - emits: ['preframe', 'kernel', 'postframe'], - actions: [ - { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] } } - ] - }) - em.defineSystem({ - name: 'joyread', - tempbytes: 1, - emits: ['joyup', 'joydown', 'joyleft', 'joyright', 'joybutton'], - actions: [ - { text: TEMPLATE2_a, event: 'postframe', select: 'once', query: { include: ['player'] } }, - { text: TEMPLATE2_b, event: 'postframe', select: 'each', query: { include: ['player'] } } - ] - }); - em.defineSystem({ - name: 'move_x', - actions: [ - { text: TEMPLATE3_L, event: 'joyleft', select: 'source', query: { include: ['player', 'xpos'] }, }, - { text: TEMPLATE3_R, event: 'joyright', select: 'source', query: { include: ['player', 'xpos'] }, }, - ] - }); - em.defineSystem({ - name: 'move_y', - actions: [ - { text: TEMPLATE3_U, event: 'joyup', select: 'source', query: { include: ['player', 'ypos'] } }, - { text: TEMPLATE3_D, event: 'joydown', select: 'source', query: { include: ['player', 'ypos'] } }, - ] - }); - em.defineSystem({ - name: 'SetHorizPos', - actions: [ - { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: ['xpos'] } }, - ] - }); - - let root = em.newScope("Root"); - let scene = em.newScope("Scene", root); - let e_ekernel = root.newEntity({ components: [c_kernel] }); - root.setConstValue(e_ekernel, c_kernel, 'lines', 192); - //root.setConstValue(e_ekernel, c_kernel, 'bgcolor', 0x92); - root.setInitValue(e_ekernel, c_kernel, 'bgcolor', 0x92); - - let e_bitmap0 = root.newEntity({ components: [c_bitmap] }); - // TODO: store array sizes? - root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([1, 1, 3, 7, 15, 31, 63, 127])); - - let e_colormap0 = root.newEntity({ components: [c_colormap] }); - root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([6, 3, 6, 9, 12, 14, 31, 63])); - - let ea_playerSprite = { components: [c_sprite, c_hasbitmap, c_hascolormap, c_xpos, c_ypos, c_player] }; - let e_player0 = root.newEntity(ea_playerSprite); - root.setConstValue(e_player0, c_sprite, 'plyrindex', 0); - root.setInitValue(e_player0, c_sprite, 'height', 8); - root.setInitValue(e_player0, c_xpos, 'xpos', 50); - root.setInitValue(e_player0, c_ypos, 'ypos', 50); - let e_player1 = root.newEntity(ea_playerSprite); - root.setConstValue(e_player1, c_sprite, 'plyrindex', 1); - root.setInitValue(e_player1, c_sprite, 'height', 8); - root.setInitValue(e_player1, c_xpos, 'xpos', 100); - root.setInitValue(e_player1, c_ypos, 'ypos', 60); - - //console.log(em.archetypesMatching({ include:['xpos','ypos']})[0]) - - let src = new SourceFileExport(); - root.analyzeEntities(); - root.generateCode(); - root.dump(src); - console.log(src.toString()); - //console.log(em.toYAML()); -} - -// TODO: files in markdown? -// TODO: jsr OperModeExecutionTree? - -test(); - diff --git a/src/common/ecs/main.ts b/src/common/ecs/main.ts new file mode 100644 index 00000000..e035ed53 --- /dev/null +++ b/src/common/ecs/main.ts @@ -0,0 +1,30 @@ +import { readFileSync } from "fs"; +import { ECSCompiler } from "./compiler"; +import { SourceFileExport } from "./ecs"; + +class ECSMain { + + compiler = new ECSCompiler(); + + constructor(readonly args: string[]) { + } + + run() { + for (let path of this.args) { + let text = readFileSync(path, 'utf-8'); + try { + this.compiler.parseFile(text, path); + } catch (e) { + console.log(e); + for (let err of this.compiler.errors) { + console.log(`${err.path}:${err.line}:${err.start}: ${err.msg}`); + } + } + } + let file = new SourceFileExport(); + this.compiler.exportToFile(file); + console.log(file.toString()); + } +} + +new ECSMain(process.argv.slice(2)).run(); diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts new file mode 100644 index 00000000..c603ae7e --- /dev/null +++ b/src/common/tokenizer.ts @@ -0,0 +1,239 @@ + +import type { SourceLocation, SourceLine, WorkerError } from "./workertypes"; + +// objects that have source code position info +export interface SourceLocated { + $loc?: SourceLocation; +} +// statements also have the 'offset' (pc) field from SourceLine +export interface SourceLineLocated { + $loc?: SourceLine; +} + +export class CompileError extends Error { + $loc: SourceLocation; + constructor(msg: string, loc: SourceLocation) { + super(msg); + Object.setPrototypeOf(this, CompileError.prototype); + this.$loc = loc; + } +} + +export function mergeLocs(a: SourceLocation, b: SourceLocation): SourceLocation { + return { + line: Math.min(a.line, b.line), + start: Math.min(a.start, b.start), + end: Math.max(a.end, b.end), + label: a.label || b.label, + path: a.path || b.path, + } +} + +export enum TokenType { + EOF = 'eof', + EOL = 'eol', + Ident = 'ident', + Comment = 'comment', + Ignore = 'ignore', + CodeFragment = 'code-fragment', + CatchAll = 'catch-all', +} + +export class Token implements SourceLocated { + str: string; + type: string; + $loc: SourceLocation; +} + +export class TokenRule { + type: string; + regex: RegExp; +} + +const CATCH_ALL_RULES: TokenRule[] = [ + { type: TokenType.CatchAll, regex: /.+?/ } +] + +function re_escape(rule: TokenRule): string { + return `(${rule.regex.source})`; +} + +export class Tokenizer { + rules: TokenRule[]; + regex: RegExp; + path: string; + lineno: number; + tokens: Token[]; + lasttoken: Token; + errors: WorkerError[]; + curlabel: string; + eol: Token; + includeEOL = false; + errorOnCatchAll = false; + codeFragment : string | null = null; + + constructor() { + this.lineno = 0; + this.errors = []; + } + setTokenRules(rules: TokenRule[]) { + this.rules = rules.concat(CATCH_ALL_RULES); + var pattern = this.rules.map(re_escape).join('|'); + this.regex = new RegExp(pattern, "g"); + } + tokenizeFile(contents: string, path: string) { + this.path = path; + this.tokens = []; // can't have errors until this is set + let txtlines = contents.split(/\n|\r\n?/); + txtlines.forEach((line) => this._tokenize(line)); + this._pushToken({ type: TokenType.EOF, str: "", $loc: { path: this.path, line: this.lineno } }); + } + tokenizeLine(line: string) : void { + this.lineno++; + this._tokenize(line); + } + _tokenize(line: string): void { + this.lineno++; + this.eol = { type: TokenType.EOL, str: "", $loc: { path: this.path, line: this.lineno, start: line.length } }; + // iterate over each token via re_toks regex + let m: RegExpMatchArray; + while (m = this.regex.exec(line)) { + let found = false; + // find out which capture group was matched, and thus token type + for (let i = 0; i < this.rules.length; i++) { + let s: string = m[i + 1]; + if (s != null) { + found = true; + let loc = { path: this.path, line: this.lineno, start: m.index, end: m.index + s.length }; + let rule = this.rules[i]; + // add token to list + switch (rule.type) { + case TokenType.CodeFragment: + if (this.codeFragment) { + this._pushToken({ str: this.codeFragment, type: rule.type, $loc: loc }); //TODO: merge start/end + this.codeFragment = null; + } else { + this.codeFragment = ''; + return; // don't add any more tokens (TODO: check for trash?) + } + break; + case TokenType.CatchAll: + if (this.errorOnCatchAll && this.codeFragment == null) { + this.compileError(`I didn't expect the character "${m[0]}" here.`); + } + default: + if (this.codeFragment == null) { + this._pushToken({ str: s, type: rule.type, $loc: loc }); + } + case TokenType.Ignore: + break; + } + break; + } + } + if (!found) { + this.compileError(`Could not parse token: <<${m[0]}>>`) + } + } + if (this.includeEOL) { + this._pushToken(this.eol); + } + if (this.codeFragment != null) { + this.codeFragment += line + '\n'; + } + } + _pushToken(token: Token) { + this.tokens.push(token); + } + addError(msg: string, loc?: SourceLocation) { + let tok = this.lasttoken || this.peekToken(); + if (!loc) loc = tok.$loc; + this.errors.push({ path: loc.path, line: loc.line, label: this.curlabel, start: loc.start, end: loc.end, msg: msg }); + } + internalError() { + this.compileError("Internal error."); + } + notImplementedError() { + this.compileError("Not yet implemented."); + } + compileError(msg: string, loc?: SourceLocation, loc2?: SourceLocation) { + this.addError(msg, loc); + //if (loc2 != null) this.addError(`...`, loc2); + throw new CompileError(msg, loc); + } + peekToken(lookahead?: number): Token { + let tok = this.tokens[lookahead || 0]; + return tok ? tok : this.eol; + } + consumeToken(): Token { + let tok = this.lasttoken = (this.tokens.shift() || this.eol); + return tok; + } + expectToken(str: string, msg?: string): Token { + let tok = this.consumeToken(); + let tokstr = tok.str; + if (str != tokstr) { + this.compileError(msg || `There should be a "${str}" here.`); + } + return tok; + } + expectTokens(strlist: string[], msg?: string): Token { + let tok = this.consumeToken(); + let tokstr = tok.str; + if (strlist.indexOf(tokstr) < 0) { + this.compileError(msg || `There should be a ${strlist.map((s) => `"${s}"`).join(' or ')} here, not "${tokstr}.`); + } + return tok; + } + parseModifiers(modifiers: string[]): { [modifier: string]: boolean } { + let result = {}; + do { + var tok = this.peekToken(); + if (modifiers.indexOf(tok.str) < 0) + return result; + this.consumeToken(); + result[tok.str] = true; + } while (tok != null); + } + expectIdent(msg?: string): Token { + let tok = this.consumeToken(); + if (tok.type != TokenType.Ident) + this.compileError(msg || `There should be an identifier here.`); + return tok; + } + pushbackToken(tok: Token) { + this.tokens.unshift(tok); + } + isEOF() { + return this.tokens.length == 0 || this.peekToken().type == 'eof'; // TODO? + } + expectEOL(msg?: string) { + let tok = this.consumeToken(); + if (tok.type != TokenType.EOL) + this.compileError(msg || `There's too much stuff on this line.`); + } + skipBlankLines() { + this.skipTokenTypes(['eol']); + } + skipTokenTypes(types: string[]) { + while (types.includes(this.peekToken().type)) + this.consumeToken(); + } + expectTokenTypes(types: string[], msg?: string) { + let tok = this.consumeToken(); + if (!types.includes(tok.type)) + this.compileError(msg || `There should be a ${types.map((s) => `"${s}"`).join(' or ')} here. not a "${tok.type}".`); + return tok; + } + parseList(parseFunc:()=>T, delim:string): T[] { + var sep; + var list = []; + do { + var el = parseFunc.bind(this)(); // call parse function + if (el != null) list.push(el); // add parsed element to list + sep = this.consumeToken(); // consume seperator token + } while (sep.str == delim); + this.pushbackToken(sep); + return list; + } +} diff --git a/src/test/testecs.ts b/src/test/testecs.ts new file mode 100644 index 00000000..6709a9a1 --- /dev/null +++ b/src/test/testecs.ts @@ -0,0 +1,449 @@ +import { describe } from "mocha"; +import { ECSCompiler } from "../common/ecs/compiler"; +import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; + +const TEMPLATE1 = ` +{{@NextFrame}}: + FRAME_START + {{!preframe}} + KERNEL_START + {{!kernel}} + KERNEL_END + {{!postframe}} + FRAME_END + lsr SWCHB ; test Game Reset switch + bcs @NoStart + jmp Start +@NoStart: + jmp {{@NextFrame}} +`; + +// TODO: two sticks? +const TEMPLATE2_a = ` + lda SWCHA + sta {{$0}} +` +const TEMPLATE2_b = ` + asl {{$0}} + bcs {{@SkipMoveRight}} + {{!joyright}} +{{@SkipMoveRight}}: + asl {{$0}} + bcs {{@SkipMoveLeft}} + {{!joyleft}} +{{@SkipMoveLeft}}: + asl {{$0}} + bcs {{@SkipMoveDown}} + {{!joydown}} +{{@SkipMoveDown}}: + asl {{$0}} + bcs {{@SkipMoveUp}} + {{!joyup}} +{{@SkipMoveUp}}: +`; + +const TEMPLATE3_L = ` + lda {{ [start] -> frameloop + // frameloop -> [preframe] [kernel] [postframe] + + // temp between preframe + frame? + // TODO: check names for identifierness + em.defineSystem({ + name: 'kernel_simple', + tempbytes: 8, + actions: [ + { + text: TEMPLATE4_S1, event: 'preframe', select: 'once', query: { + include: ['kernel'] + } + }, + { + // TODO: should include kernel for numlines + text: TEMPLATE4_S2, event: 'preframe', select: 'once', query: { + include: ['sprite', 'hasbitmap', 'hascolormap', 'ypos'], + }, + }, + { + text: TEMPLATE4_K, event: 'kernel', select: 'once', query: { + include: ['kernel'] + } + }, + ] + }) + em.defineSystem({ + name: 'set_xpos', + actions: [ + { + text: SET_XPOS, event: 'preframe', select: 'each', query: { + include: ['sprite', 'xpos'] + }, + }, + //{ text:SETHORIZPOS }, + ] + }) + // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html + em.defineSystem({ + name: 'frameloop', + emits: ['preframe', 'kernel', 'postframe'], + actions: [ + { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] } } + ] + }) + em.defineSystem({ + name: 'joyread', + tempbytes: 1, + emits: ['joyup', 'joydown', 'joyleft', 'joyright', 'joybutton'], + actions: [ + { text: TEMPLATE2_a, event: 'postframe', select: 'once', query: { include: ['player'] } }, + { text: TEMPLATE2_b, event: 'postframe', select: 'each', query: { include: ['player'] } } + ] + }); + em.defineSystem({ + name: 'move_x', + actions: [ + { text: TEMPLATE3_L, event: 'joyleft', select: 'source', query: { include: ['player', 'xpos'] }, }, + { text: TEMPLATE3_R, event: 'joyright', select: 'source', query: { include: ['player', 'xpos'] }, }, + ] + }); + em.defineSystem({ + name: 'move_y', + actions: [ + { text: TEMPLATE3_U, event: 'joyup', select: 'source', query: { include: ['player', 'ypos'] } }, + { text: TEMPLATE3_D, event: 'joydown', select: 'source', query: { include: ['player', 'ypos'] } }, + ] + }); + em.defineSystem({ + name: 'SetHorizPos', + actions: [ + { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: ['xpos'] } }, + ] + }); + + let root = em.newScope("Root"); + let scene = em.newScope("Scene", root); + let e_ekernel = root.newEntity({ components: [c_kernel] }); + root.setConstValue(e_ekernel, c_kernel, 'lines', 192); + //root.setConstValue(e_ekernel, c_kernel, 'bgcolor', 0x92); + root.setInitValue(e_ekernel, c_kernel, 'bgcolor', 0x92); + + let e_bitmap0 = root.newEntity({ components: [c_bitmap] }); + // TODO: store array sizes? + root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([1, 1, 3, 7, 15, 31, 63, 127])); + + let e_colormap0 = root.newEntity({ components: [c_colormap] }); + root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([6, 3, 6, 9, 12, 14, 31, 63])); + + let ea_playerSprite = { components: [c_sprite, c_hasbitmap, c_hascolormap, c_xpos, c_ypos, c_player] }; + let e_player0 = root.newEntity(ea_playerSprite); + root.setConstValue(e_player0, c_sprite, 'plyrindex', 0); + root.setInitValue(e_player0, c_sprite, 'height', 8); + root.setInitValue(e_player0, c_xpos, 'xpos', 50); + root.setInitValue(e_player0, c_ypos, 'ypos', 50); + let e_player1 = root.newEntity(ea_playerSprite); + root.setConstValue(e_player1, c_sprite, 'plyrindex', 1); + root.setInitValue(e_player1, c_sprite, 'height', 8); + root.setInitValue(e_player1, c_xpos, 'xpos', 100); + root.setInitValue(e_player1, c_ypos, 'ypos', 60); + + //console.log(em.archetypesMatching({ include:['xpos','ypos']})[0]) + + root.analyzeEntities(); + root.generateCode(); + let src = new SourceFileExport(); + root.dump(src); + //console.log(src.toString()); + //console.log(em.toYAML()); +} + +function testCompiler() { + let c = new ECSCompiler(); + try { + c.parseFile(` + +component Kernel + lines: 0..255 + bgcolor: 0..255 +end + +component Bitmap + data: array of 0..255 +end + +component HasBitmap + bitmap: [Bitmap] +end + +system SimpleKernel +locals 8 +on preframe do once [Kernel] --- JUNK_AT_END + lda #5 + sta #6 +Label: +--- +end + +scope Root + + entity kernel [Kernel] + const lines = 100 + end + + entity player1 [HasBitmap] + const plyrindex = 0 + init height = 8 + init xpos = 100 + init ypos = 100 + end + +end + +`, 'foo.txt'); + console.log('json', c.em.toJSON()); + let src = new SourceFileExport(); + c.exportToFile(src); + // TODO: test? + //console.log(src.toString()); + } catch (e) { + console.log(e); + for (let err of c.errors) { + console.log(err); + } + console.log(c.tokens); + } +} + +// TODO: files in markdown? +// TODO: jsr OperModeExecutionTree? + +describe('Tokenizer', function() { + it('Should use API', function() { + testECS(); + }); + it('Should use Compiler', function() { + testCompiler(); + }); +}); diff --git a/src/test/testutil.ts b/src/test/testutil.ts index b95210ec..9e340a69 100644 --- a/src/test/testutil.ts +++ b/src/test/testutil.ts @@ -1,8 +1,9 @@ import assert from "assert"; import { describe } from "mocha"; -import { EmuHalt } from "../../src/common/emu" -import { lzgmini, isProbablyBinary } from "../../src/common/util"; +import { EmuHalt } from "../common/emu" +import { lzgmini, isProbablyBinary } from "../common/util"; +import { Tokenizer, TokenType } from "../common/tokenizer"; var NES_CONIO_ROM_LZG = [ 76,90,71,0,0,160,16,0,0,11,158,107,131,223,83,1,9,17,21,22,78,69,83,26,2,1,3,0,22,6,120,216, @@ -133,4 +134,38 @@ var NES_CONIO_ROM_LZG = [ assert.ok(e.hasOwnProperty('$loc')); }); }); - \ No newline at end of file + +describe('Tokenizer', function() { + it('Should tokenize', function() { + var t = new Tokenizer(); + t.setTokenRules([ + { type: 'ident', regex: /[$A-Za-z_][A-Za-z0-9_-]*/ }, + { type: 'delim', regex: /[\(\)\{\}\[\]]/ }, + { type: 'qstring', regex: /".*?"/ }, + { type: 'integer', regex: /[-]?\d+/ }, + { type: 'ignore', regex: /\s+/ }, + { type: TokenType.CodeFragment, regex: /---/ }, + ]); + t.tokenizeFile("\n{\"key\" value\n \"number\" 531\n \"f\" (fn [x] (+ x 2))}\n", "test.file"); + assert.strictEqual(t.tokens.map(t => t.type).join(' '), + 'delim qstring ident qstring integer qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eof'); + assert.strictEqual(t.tokens.map(t => t.str).join(' '), + '{ "key" value "number" 531 "f" ( fn [ x ] ( + x 2 ) ) } '); + assert.strictEqual(19, t.tokens.length); + assert.strictEqual('{', t.peekToken().str); + assert.strictEqual('{', t.expectToken('{').str); + t.pushbackToken(t.consumeToken()); + assert.strictEqual('"key"', t.consumeToken().str); + assert.deepStrictEqual({'value':true}, t.parseModifiers(['foo','value','bar'])); + assert.deepStrictEqual([], t.errors); + t.includeEOL = true; + t.tokenizeFile("\n{\"key\" value\n \"number\" 531\n \"f\" (fn [x] (+ x 2))}\n", "test.file"); + assert.strictEqual(24, t.tokens.length); + assert.strictEqual(t.tokens.map(t => t.type).join(' '), + 'eol delim qstring ident eol qstring integer eol qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eol eol eof'); + t.includeEOL = false; + t.tokenizeFile("key value ---\nthis is\na fragment\n--- foo", "test.file"); + assert.strictEqual(t.tokens.map(t => t.type).join(' '), + 'ident ident code-fragment ident eof'); + }); +}); From 310ce87bcc59286f59caafac56eb6e6cfadc584c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 28 Jan 2022 21:13:33 -0600 Subject: [PATCH 013/104] ecs: actions emit? --- ecsroot/kernel.txt | 90 ++++++++++++ ecsroot/vcs/kernel.ecs | 279 +++++++++++++++++++++++++++++++++++++ src/common/ecs/compiler.ts | 65 +++++++-- src/common/ecs/ecs.ts | 75 ++++++---- src/common/ecs/main.ts | 16 ++- src/common/tokenizer.ts | 2 +- src/test/testecs.ts | 22 +-- 7 files changed, 490 insertions(+), 59 deletions(-) create mode 100644 ecsroot/kernel.txt create mode 100644 ecsroot/vcs/kernel.ecs diff --git a/ecsroot/kernel.txt b/ecsroot/kernel.txt new file mode 100644 index 00000000..f5696d2a --- /dev/null +++ b/ecsroot/kernel.txt @@ -0,0 +1,90 @@ +# Kernel +# A Kernel draws a set of scanlines to the screen. + +component Kernel +lines: 0..255 "Height of region in scanlines" +bgcolor: 0..255 "Background color" +end + +system SimpleKernel +tempbytes 8 +on preframe do once [Kernel] --- + lda #192 ; TODO: numlines + sec + sbc ypos_ypos_b0+ent + sta {{$5}}+ofs + + ldy hasbitmap_bitmap_b0+ent + lda bitmap_bitmapdata_b0,y + sec + sbc {{$5}}+ofs + sta {{$0}}+ofs + lda bitmap_bitmapdata_b8,y + sbc #0 + sta {{$1}}+ofs + + ldy hascolormap_colormap_b0+ent + lda colormap_colormapdata_b0,y + sec + sbc {{$5}}+ofs + sta {{$2}}+ofs + lda colormap_colormapdata_b8,y + sbc #0 + sta {{$3}}+ofs + + lda sprite_height_b0+ent + sta {{$4}}+ofs + lda ypos_ypos_b0+ent + sta {{$5}}+ofs +--- +on preframe do once [Sprite,HasBitmap,HasColormap,HasYpos] -- + {{@KernelSetup}} 0,0 + {{@KernelSetup}} 1,6 +-- +on kernel do once [Kernel]: + lda %{ L0 H0 L1 H1 + lda {{$1}} + ldy {{$2}} + sty {{$1}} + sta {{$2}} + lda {{$5}} + ldy {{$6}} + sty {{$5}} + sta {{$6}} +--- + on kernel do once [Kernel] +--- + lda {{ 1) this.compileError(`I found more than one field named "${name}" for this entity.`) + let value = this.parseDataValue(); + if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value); + if (cmd == 'init') this.currentScope.setInitValue(e, comps[0], name, value); } else { this.compileError(`Unexpected scope keyword: ${cmd}`); } @@ -202,6 +235,14 @@ export class ECSCompiler extends Tokenizer { return cref; } + parseEntityRef() : Entity { + this.expectToken('#'); + let name = this.expectIdent().str; + let eref = this.currentScope.entities.find(e => e.name == name); + if (!eref) this.compileError(`I couldn't find an entity named "${name}" in this scope.`) + return eref; + } + exportToFile(src: SourceFileExport) { for (let scope of Object.values(this.em.scopes)) { scope.analyzeEntities(); diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 8f822dc4..f95bc694 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -29,8 +29,6 @@ // - converting assets to native formats? // - removing unused data -import { Token } from "../tokenizer"; - function mksymbol(c: ComponentType, fieldName: string) { return c.name + '_' + fieldName; } @@ -73,7 +71,6 @@ export interface System { name: string; actions: Action[]; tempbytes?: number; - emits?: string[]; } export interface Action { @@ -81,6 +78,7 @@ export interface Action { event: string; query: Query; select: SelectType + emits?: string[]; } export type SelectType = 'once' | 'each' | 'source'; @@ -132,6 +130,11 @@ interface ArchetypeMatch { cmatch: ComponentType[]; } +interface ComponentFieldPair { + c: ComponentType; + f: DataField; +} + export class Dialect_CA65 { readonly ASM_ITERATE_EACH = ` ldx #0 @@ -297,6 +300,7 @@ export class EntityScope { code = new Segment(); componentsInScope = new Set(); tempOffset = 0; + tempSize = 0; maxTempBytes = 0; subroutines = new Set(); @@ -415,7 +419,8 @@ export class EntityScope { if (offset !== undefined && typeof initvalue === 'number') { initbytes[offset] = initvalue; // TODO: > 8 bits? } else { - throw new Error(`cannot access ${scfname}`); + // TODO: init arrays? + throw new Error(`cannot initialize ${scfname}: ${offset} ${initvalue}`); // TODO?? } } } @@ -431,11 +436,11 @@ export class EntityScope { return code; } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - // TODO: check to make sure component exists + let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); e.consts[mksymbol(component, fieldName)] = value; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - // TODO: check to make sure component exists + let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); e.inits[mkscopesymbol(this, component, fieldName)] = value; } generateCodeForEvent(event: string): string { @@ -450,21 +455,21 @@ export class EntityScope { let emitcode: { [event: string]: string } = {}; for (let sys of systems) { // TODO: does this work if multiple actions? + // TODO: should 'emits' be on action? if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); - if (sys.emits) { - for (let emit of sys.emits) { - if (emitcode[emit]) { - console.log(`already emitted for ${sys.name}:${event}`); - } - //console.log('>', emit); - // TODO: cycles - emitcode[emit] = this.generateCodeForEvent(emit); - //console.log('<', emit, emitcode[emit].length); - } - } - if (sys.tempbytes) this.allocateTempBytes(-sys.tempbytes); for (let action of sys.actions) { if (action.event == event) { + if (action.emits) { + for (let emit of action.emits) { + if (emitcode[emit]) { + console.log(`already emitted for ${sys.name}:${event}`); + } + //console.log('>', emit); + // TODO: cycles + emitcode[emit] = this.generateCodeForEvent(emit); + //console.log('<', emit, emitcode[emit].length); + } + } let code = this.replaceCode(action.text, sys, action); s += this.dialect.comment(``); s += code; @@ -472,12 +477,15 @@ export class EntityScope { // TODO: check that this happens once? } } + if (sys.tempbytes) this.allocateTempBytes(-sys.tempbytes); } return s; } allocateTempBytes(n: number) { - this.tempOffset += n; - this.maxTempBytes = Math.max(this.tempOffset, this.maxTempBytes); + if (n > 0) this.tempOffset = this.tempSize; + this.tempSize += n; + this.maxTempBytes = Math.max(this.tempSize, this.maxTempBytes); + if (n < 0) this.tempOffset = this.tempSize; } replaceCode(code: string, sys: System, action: Action): string { const re = /\{\{(.+?)\}\}/g; @@ -512,9 +520,7 @@ export class EntityScope { case '^': // reference return this.includeSubroutine(rest); default: - //throw new Error(`unrecognized command ${cmd} in ${entire}`); - console.log(`unrecognized command ${cmd} in ${entire}`); - return entire; + throw new Error(`unrecognized command ${cmd} in ${entire}`); } }); } @@ -536,10 +542,7 @@ export class EntityScope { atypes: ArchetypeMatch[], entities: Entity[], fieldName: string, bitofs: number): string { // find archetypes - let component = this.em.componentWithFieldName(atypes, fieldName); - if (!component) { - throw new Error(`cannot find component with field "${fieldName}" in ${sys.name}:${action.event}`); - } + let component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); // see if all entities have the same constant value let constValues = new Set(); for (let e of entities) { @@ -583,7 +586,7 @@ export class EntityScope { return result; } getSystems(events: string[]) { - let result = []; + let result : System[] = []; for (let sys of Object.values(this.em.systems)) { if (this.systemListensTo(sys, events)) { result.push(sys); @@ -684,20 +687,32 @@ export class EntityManager { }); return result; } - componentWithFieldName(atypes: ArchetypeMatch[], fieldName: string) { + componentsWithFieldName(atypes: ArchetypeMatch[], fieldName: string) { // TODO??? + let comps = new Set(); for (let at of atypes) { for (let c of at.cmatch) { for (let f of c.fields) { if (f.name == fieldName) - return c; + comps.add(c); } } } + return Array.from(comps); } getComponentByName(name: string): ComponentType { return this.components[name]; } + singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: string) { + let components = this.componentsWithFieldName(atypes, fieldName); + if (components.length == 0) { + throw new Error(`cannot find component with field "${fieldName}" in ${where}`); + } + if (components.length > 1) { + throw new Error(`ambiguous field name "${fieldName}" in ${where}`); + } + return components[0]; + } toJSON() { return JSON.stringify({ components: this.components, diff --git a/src/common/ecs/main.ts b/src/common/ecs/main.ts index e035ed53..573505dc 100644 --- a/src/common/ecs/main.ts +++ b/src/common/ecs/main.ts @@ -14,16 +14,18 @@ class ECSMain { let text = readFileSync(path, 'utf-8'); try { this.compiler.parseFile(text, path); - } catch (e) { - console.log(e); - for (let err of this.compiler.errors) { - console.log(`${err.path}:${err.line}:${err.start}: ${err.msg}`); + if (this.compiler.errors.length == 0) { + let file = new SourceFileExport(); + this.compiler.exportToFile(file); + console.log(file.toString()); } + } catch (e) { + console.error(e); + } + for (let err of this.compiler.errors) { + console.error(`${err.path}:${err.line}:${err.start}: ${err.msg}`); } } - let file = new SourceFileExport(); - this.compiler.exportToFile(file); - console.log(file.toString()); } } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index c603ae7e..91114f71 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -181,7 +181,7 @@ export class Tokenizer { let tok = this.consumeToken(); let tokstr = tok.str; if (strlist.indexOf(tokstr) < 0) { - this.compileError(msg || `There should be a ${strlist.map((s) => `"${s}"`).join(' or ')} here, not "${tokstr}.`); + this.compileError(msg || `There should be a ${strlist.map((s) => `"${s}"`).join(' or ')} here, not "${tokstr}".`); } return tok; } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 6709a9a1..492a76a3 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -23,6 +23,7 @@ const TEMPLATE2_a = ` lda SWCHA sta {{$0}} ` + const TEMPLATE2_b = ` asl {{$0}} bcs {{@SkipMoveRight}} @@ -308,18 +309,18 @@ function testECS() { // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html em.defineSystem({ name: 'frameloop', - emits: ['preframe', 'kernel', 'postframe'], actions: [ - { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] } } + { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] }, + emits: ['preframe', 'kernel', 'postframe'] } ] }) em.defineSystem({ name: 'joyread', tempbytes: 1, - emits: ['joyup', 'joydown', 'joyleft', 'joyright', 'joybutton'], actions: [ { text: TEMPLATE2_a, event: 'postframe', select: 'once', query: { include: ['player'] } }, - { text: TEMPLATE2_b, event: 'postframe', select: 'each', query: { include: ['player'] } } + { text: TEMPLATE2_b, event: 'postframe', select: 'each', query: { include: ['player'] }, + emits: ['joyup', 'joydown', 'joyleft', 'joyright', 'joybutton'] } ] }); em.defineSystem({ @@ -406,17 +407,19 @@ Label: --- end +comment --- + +--- + scope Root entity kernel [Kernel] - const lines = 100 + const lines = 0xc0 + const lines = $c0 end entity player1 [HasBitmap] - const plyrindex = 0 - init height = 8 - init xpos = 100 - init ypos = 100 + init bitmap = 1 end end @@ -433,6 +436,7 @@ end console.log(err); } console.log(c.tokens); + throw e; } } From 8b96c746a23dcf54919b507bca229c81b5b06ee8 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 29 Jan 2022 09:15:44 -0600 Subject: [PATCH 014/104] ecs: ref types? --- ecsroot/vcs/kernel.ecs | 12 +++---- src/common/ecs/compiler.ts | 20 ++++++++--- src/common/ecs/ecs.ts | 71 ++++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/ecsroot/vcs/kernel.ecs b/ecsroot/vcs/kernel.ecs index fa39e113..67674fd6 100644 --- a/ecsroot/vcs/kernel.ecs +++ b/ecsroot/vcs/kernel.ecs @@ -71,21 +71,21 @@ system SimpleKernel sbc {{Bitmap.bitmapdata}},y sbc #0 sta {{$2}},x ldy {{Colormap.colormapdata}},y sbc #0 sta {{$6}},x @@ -273,7 +273,7 @@ scope Main init height = 8 init xpos = 100 init ypos = 60 - init bitmap = 1 + init bitmap = #Bitmap2 end end diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 434ff00c..ec6aa5fc 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -92,7 +92,7 @@ export class ECSCompiler extends Tokenizer { this.compileError(`Unknown data type`); // TODO } - parseDataValue() : DataValue { + parseDataValue(field: DataField) : DataValue { let tok = this.peekToken(); if (tok.type == 'integer') { return this.expectInteger(); @@ -102,9 +102,17 @@ export class ECSCompiler extends Tokenizer { return new Uint8Array(this.parseDataArray()); } if (tok.str == '#') { + let reftype = field.dtype == 'ref' ? field as RefType : undefined; let e = this.parseEntityRef(); - // TODO: entity ref types by query? - return e.id; + let id = e.id; + if (reftype) { + // TODO: make this a function? elo ehi etc? + 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; } this.compileError(`Unknown data value`); // TODO } @@ -211,7 +219,9 @@ export class ECSCompiler extends Tokenizer { let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], 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 value = this.parseDataValue(); + let field = comps[0].fields.find(f => f.name == name); + if (!field) this.internalError(); + let value = this.parseDataValue(field); if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value); if (cmd == 'init') this.currentScope.setInitValue(e, comps[0], name, value); } else { @@ -235,7 +245,7 @@ export class ECSCompiler extends Tokenizer { return cref; } - parseEntityRef() : Entity { + parseEntityRef(reftype?: RefType) : Entity { this.expectToken('#'); let name = this.expectIdent().str; let eref = this.currentScope.entities.find(e => e.name == name); diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index f95bc694..b8ebf165 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -28,6 +28,11 @@ // - 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 function mksymbol(c: ComponentType, fieldName: string) { return c.name + '_' + fieldName; @@ -174,8 +179,15 @@ Start: indexed_x(ident: string) { return ident + ',x'; } + fieldsymbol(component: ComponentType, field: DataField, bitofs: number) { + return `${component.name}_${field.name}_b${bitofs}`; + } + datasymbol(component: ComponentType, field: DataField, eid: number) { + return `${component.name}_${field.name}_e${eid}`; + } } +// TODO: merge with Dialect? export class SourceFileExport { lines: string[] = []; @@ -364,7 +376,7 @@ export class EntityScope { // TODO: split arrays f.access = []; for (let i = 0; i < bits; i += 8) { - let symbol = name + '_b' + i; + let symbol = this.dialect.fieldsymbol(f.component, f.field, i); f.access.push({ symbol, bit: 0, width: 8 }); // TODO if (!readonly) { segment.allocateBytes(symbol, rangelen * bytesperelem); // TODO @@ -382,9 +394,9 @@ export class EntityScope { let entcount = fieldrange.ehi - fieldrange.elo + 1; // is it a byte array? if (v instanceof Uint8Array) { - let datasym = `${c.name}_${f.name}_e${e.id}`; - let ptrlosym = `${c.name}_${f.name}_b0`; - let ptrhisym = `${c.name}_${f.name}_b8`; + let datasym = this.dialect.datasymbol(c, f, e.id); + let ptrlosym = this.dialect.fieldsymbol(c, f, 0); + let ptrhisym = this.dialect.fieldsymbol(c, f, 8); segment.allocateInitData(datasym, v); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); @@ -396,7 +408,7 @@ export class EntityScope { // TODO: what if > 8 bits? // TODO: what if mix of var, const, and init values? if (fieldrange.ehi > fieldrange.elo) { - let datasym = `${c.name}_${f.name}_b0`; + let datasym = this.dialect.fieldsymbol(c, f, 0); let base = segment.allocateBytes(datasym, entcount); segment.initdata[base + e.id - fieldrange.elo] = v; } @@ -438,10 +450,16 @@ export class EntityScope { setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); e.consts[mksymbol(component, fieldName)] = value; + if (this.em.symbols[mksymbol(component, fieldName)] == 'init') + throw new Error(`Can't mix const and init values for a component field`); + this.em.symbols[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); + let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setInitValue"); e.inits[mkscopesymbol(this, component, fieldName)] = value; + if (this.em.symbols[mksymbol(component, fieldName)] == 'const') + throw new Error(`Can't mix const and init values for a component field`); + this.em.symbols[mksymbol(component, fieldName)] = 'init'; } generateCodeForEvent(event: string): string { // find systems that respond to event @@ -517,7 +535,7 @@ export class EntityScope { return this.generateCodeForField(sys, action, atypes, entities, rest, 0); case '>': // high byte return this.generateCodeForField(sys, action, atypes, entities, rest, 8); - case '^': // reference + case '^': // subroutine reference return this.includeSubroutine(rest); default: throw new Error(`unrecognized command ${cmd} in ${entire}`); @@ -541,8 +559,22 @@ export class EntityScope { generateCodeForField(sys: System, action: Action, atypes: ArchetypeMatch[], entities: Entity[], fieldName: string, bitofs: number): string { + + var component : ComponentType; + var qualified = false; + // is qualified field? + if (fieldName.indexOf('.') > 0) { + let [cname,fname] = fieldName.split('.'); + component = this.em.getComponentByName(cname); + fieldName = fname; + qualified = true; + if (component == null) throw new Error(`no component named "${cname}"`) + } else { + component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); + } // find archetypes - let component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); + let field = component.fields.find(f => f.name == fieldName); + if (field == null) throw new Error(`no field named "${fieldName}" in component`) // see if all entities have the same constant value let constValues = new Set(); for (let e of entities) { @@ -561,19 +593,29 @@ export class EntityScope { } } // TODO: offset > 0? - //let range = this.bss.getFieldRange(component, fieldName); + // TODO: don't mix const and init data + let range = this.bss.getFieldRange(component, fieldName) || this.rodata.getFieldRange(component, fieldName); + let eidofs = range.elo - entities[0].id; // TODO: dialect - let ident = `${component.name}_${fieldName}_b${bitofs}`; - if (action.select == 'once') { + let ident = this.dialect.fieldsymbol(component, field, bitofs); + if (qualified) { + return this.dialect.absolute(ident); + } else if (action.select == 'once') { if (entities.length != 1) throw new Error(`can't choose multiple entities for ${fieldName} with select=once`); - return this.dialect.absolute(ident) // TODO? check there's only 1 entity? + return this.dialect.absolute(ident); } else { - return this.dialect.indexed_x(ident) + // TODO: right direction? + if (eidofs > 0) { + ident += '+' + eidofs; + } else if (eidofs < 0) { + ident += '' + eidofs; + } + return this.dialect.indexed_x(ident); } } entitiesMatching(atypes: ArchetypeMatch[]) { - let result = []; + let result : Entity[] = []; for (let e of this.entities) { for (let a of atypes) { // TODO: what about subclasses? @@ -646,6 +688,7 @@ export class EntityManager { components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; + symbols: { [name: string] : 'init' | 'const' } = {}; constructor(public readonly dialect: Dialect_CA65) { } From 3f241bfcc2eaa8419769dae52c0bea52972c0706 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 29 Jan 2022 11:34:26 -0600 Subject: [PATCH 015/104] ecs: worker tool --- src/common/baseplatform.ts | 1 + src/common/ecs/compiler.ts | 8 +++++++- src/platform/vcs.ts | 1 + src/worker/tools/ecs.ts | 30 ++++++++++++++++++++++++++++++ src/worker/workermain.ts | 5 ++++- 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/worker/tools/ecs.ts diff --git a/src/common/baseplatform.ts b/src/common/baseplatform.ts index e7af32e6..c3a6754d 100644 --- a/src/common/baseplatform.ts +++ b/src/common/baseplatform.ts @@ -431,6 +431,7 @@ export function getToolForFilename_6502(fn:string) : string { if (fn.endsWith(".dasm")) return "dasm"; if (fn.endsWith(".acme")) return "acme"; if (fn.endsWith(".wiz")) return "wiz"; + if (fn.endsWith(".ecs")) return "ecs"; return "dasm"; // .a } diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index ec6aa5fc..5b35a017 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -69,7 +69,7 @@ export class ECSCompiler extends Tokenizer { parseComponentField(): DataField { let name = this.expectIdent(); - this.expectToken(':'); + this.expectToken(':', 'I expected either a ":" or "end" here.'); // TODO let type = this.parseDataType(); return { name: name.str, ...type }; } @@ -260,4 +260,10 @@ export class ECSCompiler extends Tokenizer { scope.dump(src); } } + + export() { + let src = new SourceFileExport(); + this.exportToFile(src); + return src.toString(); + } } diff --git a/src/platform/vcs.ts b/src/platform/vcs.ts index cd42dbf2..9be588ab 100644 --- a/src/platform/vcs.ts +++ b/src/platform/vcs.ts @@ -60,6 +60,7 @@ function getToolForFilename_vcs(fn: string) { if (fn.endsWith(".wiz")) return "wiz"; if (fn.endsWith(".bb") || fn.endsWith(".bas")) return "bataribasic"; if (fn.endsWith(".ca65")) return "ca65"; + if (fn.endsWith(".ecs")) return "ecs"; return "dasm"; } diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts new file mode 100644 index 00000000..cfb01d80 --- /dev/null +++ b/src/worker/tools/ecs.ts @@ -0,0 +1,30 @@ +import { ECSCompiler } from "../../common/ecs/compiler"; +import { CompileError } from "../../common/tokenizer"; +import { CodeListingMap } from "../../common/workertypes"; +import { BuildStep, BuildStepResult, gatherFiles, getWorkFileAsString, putWorkFile, staleFiles } from "../workermain"; + +export function assembleECS(step: BuildStep): BuildStepResult { + let compiler = new ECSCompiler(); + gatherFiles(step, { mainFilePath: "main.ecs" }); + var destpath = step.prefix + '.ca65'; + if (staleFiles(step, [destpath])) { + let code = getWorkFileAsString(step.path); + try { + compiler.parseFile(code, step.path); + } catch (e) { + if (e instanceof CompileError) { + return { errors: compiler.errors }; + } else { + throw e; + } + } + //var listings: CodeListingMap = {}; + putWorkFile(destpath, compiler.export().toString()); + } + return { + nexttool: "ca65", + path: destpath, + args: [destpath], + files: [destpath, 'vcs-ca65.h'], //TODO + }; +} diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index ffde5d72..be050701 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -1063,6 +1063,7 @@ import * as z80 from './tools/z80' import * as x86 from './tools/x86' import * as arm from './tools/arm' import * as script from './tools/script' +import * as ecs from './tools/ecs' var TOOLS = { 'dasm': dasm.assembleDASM, @@ -1098,7 +1099,8 @@ var TOOLS = { 'wiz': misc.compileWiz, 'armips': arm.assembleARMIPS, 'vasmarm': arm.assembleVASMARM, - 'js': script.runJavascript, + //'js': script.runJavascript, + 'ecs': ecs.assembleECS, } var TOOL_PRELOADFS = { @@ -1125,6 +1127,7 @@ var TOOL_PRELOADFS = { 'fastbasic': '65-atari8', 'silice': 'Silice', 'wiz': 'wiz', + 'ecs': '65-none', // TODO: support multiple platforms } //const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay)); // for testing From 9b6f7974517e346ec2aa91ae451eebf83a692e8b Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 29 Jan 2022 12:24:38 -0600 Subject: [PATCH 016/104] ecs: debug info --- src/common/ecs/compiler.ts | 9 ++++++++- src/common/ecs/ecs.ts | 6 ++++++ src/common/tokenizer.ts | 7 ++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 5b35a017..49761fc9 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -183,7 +183,13 @@ export class ECSCompiler extends Tokenizer { } parseCode(): string { - return this.expectTokenTypes([TokenType.CodeFragment]).str; + let tok = this.expectTokenTypes([TokenType.CodeFragment]); + let code = tok.str; + let lines = code.split('\n'); + for (let i=0; i Date: Sat, 29 Jan 2022 13:07:21 -0600 Subject: [PATCH 017/104] moved SourceLocated to workertypes --- src/common/basic/compiler.ts | 11 +--------- src/common/ecs/ecs.ts | 42 +++++++++++++++++++++++------------- src/common/tokenizer.ts | 11 +--------- src/common/workertypes.ts | 9 ++++++++ 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/common/basic/compiler.ts b/src/common/basic/compiler.ts index f0770bb7..9f9a8a05 100644 --- a/src/common/basic/compiler.ts +++ b/src/common/basic/compiler.ts @@ -1,4 +1,4 @@ -import { WorkerError, CodeListingMap, SourceLocation, SourceLine } from "../workertypes"; +import { WorkerError, CodeListingMap, SourceLocation, SourceLine, SourceLocated, SourceLineLocated } from "../workertypes"; export interface BASICOptions { dialectName : string; // use this to select the dialect @@ -50,15 +50,6 @@ export interface BASICOptions { maxArrayElements? : number; // max array elements (all dimensions) } -// objects that have source code position info -export interface SourceLocated { - $loc?: SourceLocation; -} -// statements also have the 'offset' (pc) field from SourceLine -export interface SourceLineLocated { - $loc?: SourceLine; -} - export class CompileError extends Error { $loc : SourceLocation; constructor(msg: string, loc: SourceLocation) { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index a441d32c..5bfb3fd3 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -34,6 +34,17 @@ // should references be zero-indexed to a field, or global? // should we limit # of entities passed to systems? min-max +import { SourceLocated, SourceLocation } from "../workertypes"; + +export class ECSError extends Error { + $loc: SourceLocation; + constructor(msg: string, obj?: SourceLocation | SourceLocated) { + super(msg); + Object.setPrototypeOf(this, ECSError.prototype); + if (obj) this.$loc = (obj as SourceLocated).$loc || (obj as SourceLocation); + } +} + function mksymbol(c: ComponentType, fieldName: string) { return c.name + '_' + fieldName; } @@ -208,7 +219,7 @@ export class SourceFileExport { if (b === undefined) { this.lines.push(` .res 1`); } else if (typeof b === 'number') { - if (b < 0 || b > 255) throw new Error(`out of range byte ${b}`); + if (b < 0 || b > 255) throw new ECSError(`out of range byte ${b}`); this.lines.push(` .byte ${b}`) } else { if (b.bitofs == 0) this.lines.push(` .byte <${b.symbol}`) @@ -287,7 +298,7 @@ class Segment { } getOriginSymbol() { let a = this.ofs2sym.get(0); - if (!a) throw new Error('getOriginSymbol(): no symbol at offset 0'); // TODO + if (!a) throw new ECSError('getOriginSymbol(): no symbol at offset 0'); // TODO return a[0]; } } @@ -419,7 +430,7 @@ export class EntityScope { segment.initdata[base + e.id - fieldrange.elo] = v; } } else { - throw new Error(`unhandled constant ${e.id}:${cfname}`); + throw new ECSError(`unhandled constant ${e.id}:${cfname}`); } } } @@ -438,7 +449,7 @@ export class EntityScope { initbytes[offset] = initvalue; // TODO: > 8 bits? } else { // TODO: init arrays? - throw new Error(`cannot initialize ${scfname}: ${offset} ${initvalue}`); // TODO?? + throw new ECSError(`cannot initialize ${scfname}: ${offset} ${initvalue}`); // TODO?? } } } @@ -457,14 +468,14 @@ export class EntityScope { let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); e.consts[mksymbol(component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'init') - throw new Error(`Can't mix const and init values for a component field`); + throw new ECSError(`Can't mix const and init values for a component field`); this.em.symbols[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setInitValue"); e.inits[mkscopesymbol(this, component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'const') - throw new Error(`Can't mix const and init values for a component field`); + throw new ECSError(`Can't mix const and init values for a component field`); this.em.symbols[mksymbol(component, fieldName)] = 'init'; } generateCodeForEvent(event: string): string { @@ -544,7 +555,7 @@ export class EntityScope { case '^': // subroutine reference return this.includeSubroutine(rest); default: - throw new Error(`unrecognized command ${cmd} in ${entire}`); + throw new ECSError(`unrecognized command ${cmd} in ${entire}`); } }); } @@ -574,13 +585,13 @@ export class EntityScope { component = this.em.getComponentByName(cname); fieldName = fname; qualified = true; - if (component == null) throw new Error(`no component named "${cname}"`) + if (component == null) throw new ECSError(`no component named "${cname}"`) } else { component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); } // find archetypes let field = component.fields.find(f => f.name == fieldName); - if (field == null) throw new Error(`no field named "${fieldName}" in component`) + if (field == null) throw new ECSError(`no field named "${fieldName}" in component`) // see if all entities have the same constant value let constValues = new Set(); for (let e of entities) { @@ -601,6 +612,7 @@ export class EntityScope { // TODO: offset > 0? // TODO: don't mix const and init data let range = this.bss.getFieldRange(component, fieldName) || this.rodata.getFieldRange(component, fieldName); + if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO let eidofs = range.elo - entities[0].id; // TODO: dialect let ident = this.dialect.fieldsymbol(component, field, bitofs); @@ -608,7 +620,7 @@ export class EntityScope { return this.dialect.absolute(ident); } else if (action.select == 'once') { if (entities.length != 1) - throw new Error(`can't choose multiple entities for ${fieldName} with select=once`); + throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`); return this.dialect.absolute(ident); } else { // TODO: right direction? @@ -700,16 +712,16 @@ export class EntityManager { } newScope(name: string, parent?: EntityScope) { let scope = new EntityScope(this, this.dialect, name, parent); - if (this.scopes[name]) throw new Error(`scope ${name} already defined`); + if (this.scopes[name]) throw new ECSError(`scope ${name} already defined`); this.scopes[name] = scope; return scope; } defineComponent(ctype: ComponentType) { - if (this.components[ctype.name]) throw new Error(`component ${ctype.name} already defined`); + if (this.components[ctype.name]) throw new ECSError(`component ${ctype.name} already defined`); return this.components[ctype.name] = ctype; } defineSystem(system: System) { - if (this.systems[system.name]) throw new Error(`system ${system.name} already defined`); + if (this.systems[system.name]) throw new ECSError(`system ${system.name} already defined`); return this.systems[system.name] = system; } componentsMatching(q: Query, etype: EntityArchetype) { @@ -755,10 +767,10 @@ export class EntityManager { singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: string) { let components = this.componentsWithFieldName(atypes, fieldName); if (components.length == 0) { - throw new Error(`cannot find component with field "${fieldName}" in ${where}`); + throw new ECSError(`cannot find component with field "${fieldName}" in ${where}`); } if (components.length > 1) { - throw new Error(`ambiguous field name "${fieldName}" in ${where}`); + throw new ECSError(`ambiguous field name "${fieldName}" in ${where}`); } return components[0]; } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 07c6e809..1eb6d49d 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -1,14 +1,5 @@ -import type { SourceLocation, SourceLine, WorkerError } from "./workertypes"; - -// objects that have source code position info -export interface SourceLocated { - $loc?: SourceLocation; -} -// statements also have the 'offset' (pc) field from SourceLine -export interface SourceLineLocated { - $loc?: SourceLine; -} +import type { SourceLocation, SourceLine, WorkerError, SourceLocated } from "./workertypes"; export class CompileError extends Error { $loc: SourceLocation; diff --git a/src/common/workertypes.ts b/src/common/workertypes.ts index 07fbc2ff..fe53426c 100644 --- a/src/common/workertypes.ts +++ b/src/common/workertypes.ts @@ -17,6 +17,15 @@ export interface SourceLine extends SourceLocation { cycles?:number; } +// objects that have source code position info +export interface SourceLocated { + $loc?: SourceLocation; +} +// statements also have the 'offset' (pc) field from SourceLine +export interface SourceLineLocated { + $loc?: SourceLine; +} + export class SourceFile { lines: SourceLine[]; text: string; From 69cd28aa0ee144479e3cb39da1aea056050b0347 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 29 Jan 2022 18:21:38 -0600 Subject: [PATCH 018/104] codemirror ecs mode --- ecsroot/vcs/kernel.ecs | 279 ------------------------------------- index.html | 1 + src/codemirror/ecs.js | 103 ++++++++++++++ src/common/ecs/compiler.ts | 3 +- src/common/ecs/ecs.ts | 34 +++-- src/ide/ui.ts | 3 +- src/ide/views/editors.ts | 1 + src/test/testecs.ts | 103 +------------- 8 files changed, 135 insertions(+), 392 deletions(-) delete mode 100644 ecsroot/vcs/kernel.ecs create mode 100644 src/codemirror/ecs.js diff --git a/ecsroot/vcs/kernel.ecs b/ecsroot/vcs/kernel.ecs deleted file mode 100644 index 67674fd6..00000000 --- a/ecsroot/vcs/kernel.ecs +++ /dev/null @@ -1,279 +0,0 @@ - -component Kernel - lines: 0..255 - bgcolor: 0..255 -end - -component Bitmap - bitmapdata: array of 0..255 -end - -component HasBitmap - bitmap: [Bitmap] -end - -component Colormap - colormapdata: array of 0..255 -end - -component HasColormap - colormap: [Colormap] -end - -component Sprite - height: 0..255 - plyrindex: 0..1 -end - -component Player end - -component PlayerFlags - plyrflags: 0..63 -end - -component HasXpos - xpos: 0..255 -end - -component HasYpos - ypos: 0..255 -end - -component HasXYVel - xyvel: 0..255 -end - -system FrameLoop - on start do once [Kernel] emit (preframe, kernel, postframe) ---- -{{@NextFrame}}: - FRAME_START - {{!preframe}} - KERNEL_START - {{!kernel}} - KERNEL_END - {{!postframe}} - FRAME_END - lsr SWCHB ; test Game Reset switch - bcs {{@NoStart}} - jmp Start -{{@NoStart}}: - jmp {{@NextFrame}} ---- -end - -system SimpleKernel - locals 12 - on preframe do each [Sprite,HasBitmap,HasColormap,HasYpos] ---- - lda #192 ; TODO: numlines - sec - sbc {{Bitmap.bitmapdata}},y - sbc #0 - sta {{$2}},x - - ldy {{Colormap.colormapdata}},y - sbc #0 - sta {{$6}},x - - lda {{ L0 H0 L1 H1 - lda {{$1}} - ldy {{$2}} - sty {{$1}} - sta {{$2}} - lda {{$5}} - ldy {{$6}} - sty {{$5}} - sta {{$6}} ---- - on kernel do once [Kernel] ---- - lda {{ + diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js new file mode 100644 index 00000000..eb149db1 --- /dev/null +++ b/src/codemirror/ecs.js @@ -0,0 +1,103 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function (mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function (CodeMirror) { + "use strict"; + + // 6502 DASM syntax + + CodeMirror.defineMode('ecs', function (_config, parserConfig) { + var keywords1, keywords2; + + var directives_list = [ + 'component', 'system', 'entity', 'scope', 'end', + 'const', 'init', 'locals', + 'on', 'do', 'emit', + 'once', 'foreach', 'source', + ]; + var keywords_list = [ + 'processor', + 'byte', 'word', 'long', + 'include', 'seg', 'dc', 'ds', 'dv', 'hex', 'err', 'org', 'rorg', 'echo', 'rend', + 'align', 'subroutine', 'equ', 'eqm', 'set', 'mac', 'endm', 'mexit', 'ifconst', + 'ifnconst', 'if', 'else', 'endif', 'eif', 'repeat', 'repend' + ]; + + var directives = new Map(); + directives_list.forEach(function (s) { directives.set(s, 'def'); }); + keywords_list.forEach(function (s) { directives.set(s, 'keyword'); }); + + var opcodes = /^[a-z][a-z][a-z]\b/i; + var numbers = /^(0x[\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; + var tags = /^\{\{.*\}\}/; + var mlcomment = /^---.+?---\b/i; + + return { + startState: function () { + return { + context: 0 + }; + }, + token: function (stream, state) { + if (stream.eatSpace()) + return null; + + if (stream.match(tags)) { + return 'meta'; + } + + var w; + if (stream.eatWhile(/\w/)) { + w = stream.current(); + var cur = w.toLowerCase(); + var style = directives.get(cur); + if (style) + return style; + + if (opcodes.test(w)) { + return 'keyword'; + } else if (numbers.test(w)) { + return 'number'; + } else if (w == 'comment') { + stream.match(mlcomment); + return 'comment'; + } + } else if (stream.eat(';')) { + stream.skipToEnd(); + return 'comment'; + } else if (stream.eat('"')) { + while (w = stream.next()) { + if (w == '"') + break; + + if (w == '\\') + stream.next(); + } + return 'string'; + } else if (stream.eat('\'')) { + if (stream.match(/\\?.'/)) + return 'number'; + } else if (stream.eat('$') || stream.eat('#')) { + if (stream.eatWhile(/[^;]/i)) + return 'number'; + } else if (stream.eat('%')) { + if (stream.eatWhile(/[01]/)) + return 'number'; + } else { + stream.next(); + } + return null; + } + }; + }); + + CodeMirror.defineMIME("text/x-ecs", "ecs"); + +}); diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 49761fc9..ba3677c7 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -152,7 +152,7 @@ export class ECSCompiler extends Tokenizer { parseAction(): Action { let event = this.expectIdent().str; this.expectToken('do'); - let select = this.expectTokens(['once', 'each', 'source']).str as SelectType; // TODO: type check? + let select = this.expectTokens(['once', 'foreach', 'source']).str as SelectType; // TODO: type check? let query = this.parseQuery(); let emits; if (this.peekToken().str == 'emit') { @@ -183,6 +183,7 @@ export class ECSCompiler extends Tokenizer { } parseCode(): string { + // TODO: add $loc let tok = this.expectTokenTypes([TokenType.CodeFragment]); let code = tok.str; let lines = code.split('\n'); diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 5bfb3fd3..019452c2 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -97,7 +97,7 @@ export interface Action { emits?: string[]; } -export type SelectType = 'once' | 'each' | 'source'; +export type SelectType = 'once' | 'foreach' | 'source'; export type DataValue = number | boolean | Uint8Array | Uint16Array; @@ -154,11 +154,11 @@ interface ComponentFieldPair { export class Dialect_CA65 { readonly ASM_ITERATE_EACH = ` ldx #0 -{{@__each}}: +@__each: {{code}} inx cpx #{{ecount}} - bne {{@__each}} + bne @__each `; readonly INIT_FROM_ARRAY = ` ldy #{{nbytes}} @@ -254,13 +254,15 @@ class Segment { this.codefrags.push(code); } allocateBytes(name: string, bytes: number) { - if (this.symbols[name]) return this.symbols[name]; // TODO: check size - let ofs = this.size; - this.symbols[name] = ofs; - if (!this.ofs2sym.has(ofs)) - this.ofs2sym.set(ofs, []); - this.ofs2sym.get(ofs)?.push(name); - this.size += bytes; + let ofs = this.symbols[name]; + if (ofs == null) { + ofs = this.size; + this.symbols[name] = ofs; + if (!this.ofs2sym.has(ofs)) + this.ofs2sym.set(ofs, []); + this.ofs2sym.get(ofs)?.push(name); + this.size += bytes; + } return ofs; } // TODO: optimize shared data @@ -388,7 +390,7 @@ export class EntityScope { // variable size? make it a pointer if (bits == 0) bits = 16; // TODO? let rangelen = (f.ehi - f.elo + 1); - let bytesperelem = Math.ceil(bits / 8) * rangelen; + let bytesperelem = Math.ceil(bits / 8); // TODO: packing bits // TODO: split arrays f.access = []; @@ -419,7 +421,7 @@ export class EntityScope { let hiofs = segment.allocateBytes(ptrhisym, entcount); segment.initdata[loofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 8 }; - // TODO: } else if (v instanceof Uint16Array) { + // TODO: } else if (v instanceof Uint16Array) { } else if (typeof v === 'number') { // more than 1 entity, add an array // TODO: what if > 8 bits? @@ -428,6 +430,7 @@ export class EntityScope { let datasym = this.dialect.fieldsymbol(c, f, 0); let base = segment.allocateBytes(datasym, entcount); segment.initdata[base + e.id - fieldrange.elo] = v; + //console.error(cfname, datasym, base, e.id, fieldrange.elo, entcount, v); } } else { throw new ECSError(`unhandled constant ${e.id}:${cfname}`); @@ -524,17 +527,20 @@ export class EntityScope { } replaceCode(code: string, sys: System, action: Action): string { const re = /\{\{(.+?)\}\}/g; - let label = sys.name + '_' + action.event; + let label = `${sys.name}__${action.event}`; let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); // TODO: detect cycles // TODO: "source"? // TODO: what if only 1 item? - if (action.select == 'each') { + if (action.select == 'foreach') { code = this.wrapCodeInLoop(code, sys, action, entities); //console.log(sys.name, action.event, ents); //frag = this.iterateCode(frag); } + // replace @labels + code = code.replace(/@(\w+)\b/g, (s: string, a: string) => `${label}__${a}`); + // replace {{...}} tags return code.replace(re, (entire, group: string) => { let cmd = group.charAt(0); let rest = group.substring(1); diff --git a/src/ide/ui.ts b/src/ide/ui.ts index 2c958ba3..c64edde0 100644 --- a/src/ide/ui.ts +++ b/src/ide/ui.ts @@ -114,7 +114,8 @@ const TOOL_TO_SOURCE_STYLE = { 'silice': 'verilog', 'wiz': 'text/x-wiz', 'vasmarm': 'vasm', - 'armips': 'vasm' + 'armips': 'vasm', + 'ecs': 'ecs', } const TOOL_TO_HELPURL = { diff --git a/src/ide/views/editors.ts b/src/ide/views/editors.ts index d5a81d28..d594ac10 100644 --- a/src/ide/views/editors.ts +++ b/src/ide/views/editors.ts @@ -36,6 +36,7 @@ const MODEDEFS = { markdown: { lineWrap: true }, fastbasic: { noGutters: true }, basic: { noLineNumbers: true, noGutters: true }, // TODO: not used? + ecs: { theme: 'mbo', isAsm: false }, } export var textMapFunctions = { diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 492a76a3..3c3b4438 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -3,7 +3,7 @@ import { ECSCompiler } from "../common/ecs/compiler"; import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; const TEMPLATE1 = ` -{{@NextFrame}}: +@NextFrame: FRAME_START {{!preframe}} KERNEL_START @@ -15,74 +15,12 @@ const TEMPLATE1 = ` bcs @NoStart jmp Start @NoStart: - jmp {{@NextFrame}} + jmp @NextFrame `; -// TODO: two sticks? -const TEMPLATE2_a = ` - lda SWCHA - sta {{$0}} -` - -const TEMPLATE2_b = ` - asl {{$0}} - bcs {{@SkipMoveRight}} - {{!joyright}} -{{@SkipMoveRight}}: - asl {{$0}} - bcs {{@SkipMoveLeft}} - {{!joyleft}} -{{@SkipMoveLeft}}: - asl {{$0}} - bcs {{@SkipMoveDown}} - {{!joydown}} -{{@SkipMoveDown}}: - asl {{$0}} - bcs {{@SkipMoveUp}} - {{!joyup}} -{{@SkipMoveUp}}: -`; - -const TEMPLATE3_L = ` - lda {{ [start] -> frameloop // frameloop -> [preframe] [kernel] [postframe] @@ -299,7 +231,7 @@ function testECS() { name: 'set_xpos', actions: [ { - text: SET_XPOS, event: 'preframe', select: 'each', query: { + text: SET_XPOS, event: 'preframe', select: 'foreach', query: { include: ['sprite', 'xpos'] }, }, @@ -314,29 +246,6 @@ function testECS() { emits: ['preframe', 'kernel', 'postframe'] } ] }) - em.defineSystem({ - name: 'joyread', - tempbytes: 1, - actions: [ - { text: TEMPLATE2_a, event: 'postframe', select: 'once', query: { include: ['player'] } }, - { text: TEMPLATE2_b, event: 'postframe', select: 'each', query: { include: ['player'] }, - emits: ['joyup', 'joydown', 'joyleft', 'joyright', 'joybutton'] } - ] - }); - em.defineSystem({ - name: 'move_x', - actions: [ - { text: TEMPLATE3_L, event: 'joyleft', select: 'source', query: { include: ['player', 'xpos'] }, }, - { text: TEMPLATE3_R, event: 'joyright', select: 'source', query: { include: ['player', 'xpos'] }, }, - ] - }); - em.defineSystem({ - name: 'move_y', - actions: [ - { text: TEMPLATE3_U, event: 'joyup', select: 'source', query: { include: ['player', 'ypos'] } }, - { text: TEMPLATE3_D, event: 'joydown', select: 'source', query: { include: ['player', 'ypos'] } }, - ] - }); em.defineSystem({ name: 'SetHorizPos', actions: [ From 249d4735ebcdb138be539242aff3c0b8380c384e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 30 Jan 2022 09:01:55 -0600 Subject: [PATCH 019/104] ecs: added join, limit, fixed some things, took out ca65 listing for now --- src/codemirror/ecs.js | 4 +- src/common/ecs/compiler.ts | 13 ++- src/common/ecs/ecs.ts | 192 +++++++++++++++++++++++++------------ src/ide/views/editors.ts | 2 +- src/worker/tools/cc65.ts | 2 +- src/worker/tools/ecs.ts | 7 +- 6 files changed, 149 insertions(+), 71 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index eb149db1..947b0806 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -19,8 +19,8 @@ var directives_list = [ 'component', 'system', 'entity', 'scope', 'end', 'const', 'init', 'locals', - 'on', 'do', 'emit', - 'once', 'foreach', 'source', + 'on', 'do', 'emit', 'limit', + 'once', 'foreach', 'source', 'join' ]; var keywords_list = [ 'processor', diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index ba3677c7..1e06aa46 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -152,9 +152,15 @@ export class ECSCompiler extends Tokenizer { parseAction(): Action { let event = this.expectIdent().str; this.expectToken('do'); - let select = this.expectTokens(['once', 'foreach', 'source']).str as SelectType; // TODO: type check? + let select = this.expectTokens(['once', 'foreach', 'source', 'join']).str as SelectType; // TODO: type check? let query = this.parseQuery(); + let join = select == 'join' && this.parseQuery(); let emits; + let limit; + if (this.peekToken().str == 'limit') { + this.consumeToken(); + limit = this.expectInteger(); + } if (this.peekToken().str == 'emit') { this.consumeToken(); this.expectToken('('); @@ -162,7 +168,8 @@ export class ECSCompiler extends Tokenizer { this.expectToken(')'); } let text = this.parseCode(); - return { text, event, query, select }; + let action = { text, event, query, join, select, limit }; + return action; } parseQuery() { @@ -222,12 +229,12 @@ export class ECSCompiler extends Tokenizer { // TODO: check data types if (cmd == 'const' || cmd == 'init') { let name = this.expectIdent().str; - this.expectToken('='); let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], 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); if (!field) this.internalError(); + this.expectToken('='); let value = this.parseDataValue(field); if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value); if (cmd == 'init') this.currentScope.setInitValue(e, comps[0], name, value); diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 019452c2..caeee962 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -33,6 +33,7 @@ // 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 import { SourceLocated, SourceLocation } from "../workertypes"; @@ -92,12 +93,14 @@ export interface System { export interface Action { text: string; event: string; - query: Query; select: SelectType + query: Query; + join?: Query; + limit?: number; emits?: string[]; } -export type SelectType = 'once' | 'foreach' | 'source'; +export type SelectType = 'once' | 'foreach' | 'source' | 'join'; export type DataValue = number | boolean | Uint8Array | Uint16Array; @@ -160,6 +163,17 @@ export class Dialect_CA65 { cpx #{{ecount}} bne @__each `; + +readonly ASM_ITERATE_JOIN = ` + ldy #0 +@__each: + ldx {{joinfield}},y + {{code}} + iny + cpy #{{ecount}} + bne @__each +`; + readonly INIT_FROM_ARRAY = ` ldy #{{nbytes}} : lda {{src}}-1,y @@ -345,18 +359,19 @@ export class EntityScope { } newEntity(etype: EntityArchetype): Entity { // TODO: add parent ID? lock parent scope? + // TODO: name identical check? let id = this.entities.length; + etype = this.em.addArchetype(etype); let entity: Entity = { id, etype, consts: {}, inits: {} }; - this.em.archtypes.add(etype); for (let c of etype.components) { this.componentsInScope.add(c.name); } this.entities.push(entity); return entity; } - *iterateFields() { - for (let i = 0; i < this.entities.length; i++) { - let e = this.entities[i]; + *iterateEntityFields(entities: Entity[]) { + for (let i = 0; i < entities.length; i++) { + let e = entities[i]; for (let c of e.etype.components) { for (let f of c.fields) { yield { i, e, c, f, v: e.consts[mksymbol(c, f.name)] }; @@ -364,8 +379,77 @@ export class EntityScope { } } } + *iterateArchetypeFields(arch: ArchetypeMatch[], filter?: (c:ComponentType,f:DataField) => boolean) { + for (let i = 0; i < arch.length; i++) { + let a = arch[i]; + for (let c of a.etype.components) { + for (let f of c.fields) { + if (!filter || filter(c,f)) + yield { i, c, f }; + } + } + } + } + entitiesMatching(atypes: ArchetypeMatch[]) { + let result : Entity[] = []; + for (let e of this.entities) { + for (let a of atypes) { + // TODO: what about subclasses? + // TODO: very scary identity ocmpare + if (e.etype === a.etype) { + result.push(e); + break; + } + } + } + return result; + } + getSystems(events: string[]) { + let result : System[] = []; + for (let sys of Object.values(this.em.systems)) { + if (this.systemListensTo(sys, events)) { + result.push(sys); + } + } + return result; + } + systemListensTo(sys: System, events: string[]) { + for (let action of sys.actions) { + if (action.event != null && events.includes(action.event)) { + let archs = this.em.archetypesMatching(action.query); + for (let arch of archs) { + for (let ctype of arch.cmatch) { + if (this.hasComponent(ctype)) { + return true; + } + } + } + } + } + } + hasComponent(ctype: ComponentType) { + return this.componentsInScope.has(ctype.name); + } + getJoinField(atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]) : ComponentFieldPair { + let refs = Array.from(this.iterateArchetypeFields(atypes, (c,f) => f.dtype == 'ref')); + if (refs.length == 0) throw new ECSError(`cannot find join fields`); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields`); + // TODO: check to make sure join works + return refs[0]; // TODO + /* TODO + let match = refs.map(ref => this.em.archetypesMatching((ref.f as RefType).query)); + for (let ref of refs) { + let m = this.em.archetypesMatching((ref.f as RefType).query); + for (let a of m) { + if (jtypes.includes(a.etype)) { + console.log(a,m); + } + } + } + */ + } buildSegments() { - let iter = this.iterateFields(); + let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; let segment = v === undefined ? this.bss : this.rodata; @@ -404,7 +488,7 @@ export class EntityScope { } } allocateROData(segment: Segment) { - let iter = this.iterateFields(); + let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; let cfname = mksymbol(c, f.name); @@ -441,7 +525,7 @@ export class EntityScope { } allocateInitData(segment: Segment) { let initbytes = new Uint8Array(segment.size); - let iter = this.iterateFields(); + let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; let scfname = mkscopesymbol(this, c, f.name); @@ -526,7 +610,7 @@ export class EntityScope { if (n < 0) this.tempOffset = this.tempSize; } replaceCode(code: string, sys: System, action: Action): string { - const re = /\{\{(.+?)\}\}/g; + const tag_re = /\{\{(.+?)\}\}/g; let label = `${sys.name}__${action.event}`; let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); @@ -534,14 +618,22 @@ export class EntityScope { // TODO: "source"? // TODO: what if only 1 item? if (action.select == 'foreach') { - code = this.wrapCodeInLoop(code, sys, action, entities); - //console.log(sys.name, action.event, ents); - //frag = this.iterateCode(frag); + code = this.wrapCodeInLoop(code, action, entities); } + if (action.select == 'join' && action.join) { + let jtypes = this.em.archetypesMatching(action.join); + let jentities = this.entitiesMatching(jtypes); + let joinfield = this.getJoinField(atypes, jtypes); + // TODO: what if only 1 item? + code = this.wrapCodeInLoop(code, action, entities, joinfield); + atypes = jtypes; + entities = jentities; + } + if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`); // replace @labels code = code.replace(/@(\w+)\b/g, (s: string, a: string) => `${label}__${a}`); // replace {{...}} tags - return code.replace(re, (entire, group: string) => { + return code.replace(tag_re, (entire, group: string) => { let cmd = group.charAt(0); let rest = group.substring(1); switch (cmd) { @@ -569,14 +661,22 @@ export class EntityScope { this.subroutines.add(symbol); return symbol; } - wrapCodeInLoop(code: string, sys: System, action: Action, ents: Entity[]): string { + wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { // TODO: check ents // TODO: check segment bounds + // TODO: what if 0 or 1 entitites? let s = this.dialect.ASM_ITERATE_EACH; - s = s.replace('{{elo}}', ents[0].id.toString()); - s = s.replace('{{ehi}}', ents[ents.length - 1].id.toString()); - s = s.replace('{{ecount}}', ents.length.toString()); + if (joinfield) s = this.dialect.ASM_ITERATE_JOIN; + if (action.limit) { + ents = ents.slice(0, action.limit); + } + s = s.replace('{{elo}}', () => ents[0].id.toString()); + s = s.replace('{{ehi}}', () => ents[ents.length - 1].id.toString()); + s = s.replace('{{ecount}}', () => ents.length.toString()); s = s.replace('{{code}}', code); + if (joinfield) { + s = s.replace('{{joinfield}}', () => this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0)); + } return s; } generateCodeForField(sys: System, action: Action, @@ -586,8 +686,8 @@ export class EntityScope { var component : ComponentType; var qualified = false; // is qualified field? - if (fieldName.indexOf('.') > 0) { - let [cname,fname] = fieldName.split('.'); + if (fieldName.indexOf(':') > 0) { + let [cname,fname] = fieldName.split(':'); component = this.em.getComponentByName(cname); fieldName = fname; qualified = true; @@ -638,45 +738,6 @@ export class EntityScope { return this.dialect.indexed_x(ident); } } - entitiesMatching(atypes: ArchetypeMatch[]) { - let result : Entity[] = []; - for (let e of this.entities) { - for (let a of atypes) { - // TODO: what about subclasses? - if (e.etype == a.etype) { - result.push(e); - break; - } - } - } - return result; - } - getSystems(events: string[]) { - let result : System[] = []; - for (let sys of Object.values(this.em.systems)) { - if (this.systemListensTo(sys, events)) { - result.push(sys); - } - } - return result; - } - systemListensTo(sys: System, events: string[]) { - for (let action of sys.actions) { - if (action.event != null && events.includes(action.event)) { - let archs = this.em.archetypesMatching(action.query); - for (let arch of archs) { - for (let ctype of arch.cmatch) { - if (this.hasComponent(ctype)) { - return true; - } - } - } - } - } - } - hasComponent(ctype: ComponentType) { - return this.componentsInScope.has(ctype.name); - } analyzeEntities() { this.buildSegments(); this.allocateSegment(this.bss, false); @@ -708,7 +769,7 @@ export class EntityScope { } export class EntityManager { - archtypes = new Set(); + archetypes: { [key: string]: EntityArchetype } = {}; components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; @@ -730,6 +791,13 @@ export class EntityManager { if (this.systems[system.name]) throw new ECSError(`system ${system.name} already defined`); return this.systems[system.name] = system; } + addArchetype(atype: EntityArchetype) : EntityArchetype { + let key = atype.components.map(c => c.name).join(','); + if (this.archetypes[key]) + return this.archetypes[key]; + else + return this.archetypes[key] = atype; + } componentsMatching(q: Query, etype: EntityArchetype) { let list = []; for (let c of etype.components) { @@ -746,12 +814,12 @@ export class EntityManager { } archetypesMatching(q: Query) { let result: ArchetypeMatch[] = []; - this.archtypes.forEach(etype => { + for (let etype of Object.values(this.archetypes)) { let cmatch = this.componentsMatching(q, etype); if (cmatch.length > 0) { result.push({ etype, cmatch }); } - }); + } return result; } componentsWithFieldName(atypes: ArchetypeMatch[], fieldName: string) { diff --git a/src/ide/views/editors.ts b/src/ide/views/editors.ts index d594ac10..b9b5dd38 100644 --- a/src/ide/views/editors.ts +++ b/src/ide/views/editors.ts @@ -36,7 +36,7 @@ const MODEDEFS = { markdown: { lineWrap: true }, fastbasic: { noGutters: true }, basic: { noLineNumbers: true, noGutters: true }, // TODO: not used? - ecs: { theme: 'mbo', isAsm: false }, + ecs: { theme: 'mbo', isAsm: true }, } export var textMapFunctions = { diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index 5f1f2a71..77b40837 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -216,7 +216,7 @@ export function linkLD65(step: BuildStep): BuildStepResult { if (fn.endsWith('.lst')) { var lstout = FS.readFile(fn, { encoding: 'utf8' }); lstout = lstout.split('\n\n')[1] || lstout; // remove header - var asmlines = parseCA65Listing(lstout, symbolmap, params, false); + var asmlines = []; // TODO: parseCA65Listing(lstout, symbolmap, params, false); var srclines = parseCA65Listing(lstout, symbolmap, params, true); putWorkFile(fn, lstout); // TODO: you have to get rid of all source lines to get asm listing diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index cfb01d80..c130d8a5 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -18,13 +18,16 @@ export function assembleECS(step: BuildStep): BuildStepResult { throw e; } } - //var listings: CodeListingMap = {}; - putWorkFile(destpath, compiler.export().toString()); + let outtext = compiler.export().toString(); + putWorkFile(destpath, outtext); + var listings: CodeListingMap = {}; + listings[destpath] = {lines:[], text:outtext} // TODO } return { nexttool: "ca65", path: destpath, args: [destpath], files: [destpath, 'vcs-ca65.h'], //TODO + listings }; } From c54b6a115063cf3df7f0d0ff5cb110c92e3ad98e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 30 Jan 2022 10:48:56 -0600 Subject: [PATCH 020/104] ecs: {{%props}}, ecs errors w/ $loc --- src/common/ecs/compiler.ts | 54 ++++++++++++++---------- src/common/ecs/ecs.ts | 86 ++++++++++++++++++++++---------------- src/common/tokenizer.ts | 6 ++- src/worker/tools/ecs.ts | 14 ++++--- 4 files changed, 93 insertions(+), 67 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 1e06aa46..904e4348 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,5 +1,6 @@ import { Tokenizer, TokenType } from "../tokenizer"; +import { SourceLocated } from "../workertypes"; import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Dialect_CA65, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { @@ -18,7 +19,6 @@ export class ECSCompiler extends Tokenizer { super(); //this.includeEOL = true; this.setTokenRules([ - { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, { type: TokenType.CodeFragment, regex: /---/ }, { type: ECSTokenType.Ellipsis, regex: /\.\./ }, { type: ECSTokenType.Operator, regex: /[#=,:(){}\[\]]/ }, @@ -26,15 +26,23 @@ export class ECSCompiler extends Tokenizer { { type: ECSTokenType.Integer, regex: /[-]?0x[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, + { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, { type: TokenType.Ignore, regex: /\s+/ }, ]); this.errorOnCatchAll = true; } + + annotate(fn: () => T) { + let tok = this.peekToken(); + let obj = fn(); + if (obj) (obj as SourceLocated).$loc = tok.$loc; + return obj; + } parseFile(text: string, path: string) { this.tokenizeFile(text, path); while (!this.isEOF()) { - this.parseTopLevel(); + this.annotate(() => this.parseTopLevel()); } } @@ -137,9 +145,10 @@ export class ECSCompiler extends Tokenizer { let actions: Action[] = []; let system: System = { name, actions }; let cmd; - while ((cmd = this.consumeToken().str) != 'end') { + while ((cmd = this.expectTokens(['on','locals','end']).str) != 'end') { if (cmd == 'on') { - actions.push(this.parseAction()); + let action = this.annotate(() => this.parseAction()); + actions.push(action); } else if (cmd == 'locals') { system.tempbytes = this.expectInteger(); } else { @@ -150,6 +159,7 @@ export class ECSCompiler extends Tokenizer { } parseAction(): Action { + // TODO: unused events? let event = this.expectIdent().str; this.expectToken('do'); let select = this.expectTokens(['once', 'foreach', 'source', 'join']).str as SelectType; // TODO: type check? @@ -159,6 +169,7 @@ export class ECSCompiler extends Tokenizer { let limit; if (this.peekToken().str == 'limit') { this.consumeToken(); + if (!['foreach', 'join'].includes(select)) this.compileError(`A "${select}" query can't include a limit.`); limit = this.expectInteger(); } if (this.peekToken().str == 'emit') { @@ -205,11 +216,12 @@ export class ECSCompiler extends Tokenizer { let scope = this.em.newScope(name, this.currentScope); this.currentScope = scope; let cmd; - while ((cmd = this.consumeToken().str) != 'end') { + while ((cmd = this.expectTokens(['entity', 'comment', 'end']).str) != 'end') { if (cmd == 'entity') { - this.parseEntity(); - } else { - this.compileError(`Unexpected scope keyword: ${cmd}`); + this.annotate(() => this.parseEntity()); + } + if (cmd == 'comment') { + this.expectTokenTypes([TokenType.CodeFragment]); } } this.currentScope = scope.parent; @@ -225,22 +237,18 @@ export class ECSCompiler extends Tokenizer { let e = this.currentScope.newEntity(etype); e.name = name; let cmd; - while ((cmd = this.consumeToken().str) != 'end') { + while ((cmd = this.expectTokens(['const', 'init', 'end']).str) != 'end') { // TODO: check data types - if (cmd == 'const' || cmd == 'init') { - let name = this.expectIdent().str; - let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], 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); - if (!field) this.internalError(); - this.expectToken('='); - let value = this.parseDataValue(field); - if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value); - if (cmd == 'init') this.currentScope.setInitValue(e, comps[0], name, value); - } else { - this.compileError(`Unexpected scope keyword: ${cmd}`); - } + let name = this.expectIdent().str; + let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], 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); + if (!field) this.internalError(); + this.expectToken('='); + let value = this.parseDataValue(field); + if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value); + if (cmd == 'init') this.currentScope.setInitValue(e, comps[0], name, value); } return e; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index caeee962..78c90de5 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -53,7 +53,7 @@ function mkscopesymbol(s: EntityScope, c: ComponentType, fieldName: string) { return s.name + '_' + c.name + '_' + fieldName; } -export interface Entity { +export interface Entity extends SourceLocated { id: number; name?: string; etype: EntityArchetype; @@ -71,7 +71,7 @@ export interface EntityArchetype { components: ComponentType[]; } -export interface ComponentType { +export interface ComponentType extends SourceLocated { name: string; fields: DataField[]; optional?: boolean; @@ -84,13 +84,13 @@ export interface Query { updates?: string[]; } -export interface System { +export interface System extends SourceLocated { name: string; actions: Action[]; tempbytes?: number; } -export interface Action { +export interface Action extends SourceLocated { text: string; event: string; select: SelectType @@ -158,26 +158,28 @@ export class Dialect_CA65 { readonly ASM_ITERATE_EACH = ` ldx #0 @__each: - {{code}} + {{%code}} inx - cpx #{{ecount}} + cpx #{{%ecount}} bne @__each +@__exit: `; readonly ASM_ITERATE_JOIN = ` ldy #0 @__each: - ldx {{joinfield}},y - {{code}} + ldx {{%joinfield}},y + {{%code}} iny - cpy #{{ecount}} + cpy #{{%ecount}} bne @__each +@__exit: `; readonly INIT_FROM_ARRAY = ` - ldy #{{nbytes}} -: lda {{src}}-1,y - sta {{dest}}-1,y + ldy #{{%nbytes}} +: lda {{%src}}-1,y + sta {{%dest}}-1,y dey bne :- ` @@ -337,7 +339,8 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { return 0; } -export class EntityScope { +export class EntityScope implements SourceLocated { + $loc: SourceLocation; childScopes: EntityScope[] = []; entities: Entity[] = []; bss = new Segment(); @@ -430,10 +433,11 @@ export class EntityScope { hasComponent(ctype: ComponentType) { return this.componentsInScope.has(ctype.name); } - getJoinField(atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]) : ComponentFieldPair { + getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]) : ComponentFieldPair { let refs = Array.from(this.iterateArchetypeFields(atypes, (c,f) => f.dtype == 'ref')); - if (refs.length == 0) throw new ECSError(`cannot find join fields`); - if (refs.length > 1) throw new ECSError(`cannot join multiple fields`); + // TODO: better error message + if (refs.length == 0) throw new ECSError(`cannot find join fields`, action); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action); // TODO: check to make sure join works return refs[0]; // TODO /* TODO @@ -546,23 +550,23 @@ export class EntityScope { let bufofs = this.rodata.allocateInitData(bufsym, initbytes); let code = this.dialect.INIT_FROM_ARRAY; //TODO: function to repalce from dict? - code = code.replace('{{nbytes}}', initbytes.length.toString()) - code = code.replace('{{src}}', bufsym); - code = code.replace('{{dest}}', segment.getOriginSymbol()); + code = code.replace('{{%nbytes}}', initbytes.length.toString()) + code = code.replace('{{%src}}', bufsym); + code = code.replace('{{%dest}}', segment.getOriginSymbol()); return code; } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); e.consts[mksymbol(component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'init') - throw new ECSError(`Can't mix const and init values for a component field`); + throw new ECSError(`Can't mix const and init values for a component field`, e); this.em.symbols[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setInitValue"); e.inits[mkscopesymbol(this, component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'const') - throw new ECSError(`Can't mix const and init values for a component field`); + throw new ECSError(`Can't mix const and init values for a component field`, e); this.em.symbols[mksymbol(component, fieldName)] = 'init'; } generateCodeForEvent(event: string): string { @@ -611,29 +615,43 @@ export class EntityScope { } replaceCode(code: string, sys: System, action: Action): string { const tag_re = /\{\{(.+?)\}\}/g; + const label_re = /@(\w+)\b/g; + let label = `${sys.name}__${action.event}`; let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); + if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`, action); // TODO // TODO: detect cycles // TODO: "source"? // TODO: what if only 1 item? + let props : {[name: string] : string} = {}; if (action.select == 'foreach') { code = this.wrapCodeInLoop(code, action, entities); } if (action.select == 'join' && action.join) { let jtypes = this.em.archetypesMatching(action.join); let jentities = this.entitiesMatching(jtypes); - let joinfield = this.getJoinField(atypes, jtypes); + let joinfield = this.getJoinField(action, atypes, jtypes); // TODO: what if only 1 item? + // TODO: should be able to access fields via Y reg code = this.wrapCodeInLoop(code, action, entities, joinfield); atypes = jtypes; entities = jentities; + props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); } - if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`); + props['%efullcount'] = entities.length.toString(); + if (action.limit) { + entities = entities.slice(0, action.limit); + } + if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`); // TODO + // define properties + props['%elo'] = entities[0].id.toString(); + props['%ehi'] = entities[entities.length - 1].id.toString(); + props['%ecount'] = entities.length.toString(); // replace @labels - code = code.replace(/@(\w+)\b/g, (s: string, a: string) => `${label}__${a}`); + code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); // replace {{...}} tags - return code.replace(tag_re, (entire, group: string) => { + code = code.replace(tag_re, (entire, group: string) => { let cmd = group.charAt(0); let rest = group.substring(1); switch (cmd) { @@ -642,7 +660,7 @@ export class EntityScope { case '.': // auto label case '@': // auto label return `${label}_${rest}`; - case '$': // temp byte + case '$': // temp byte (TODO: check to make sure not overflowing) return `TEMP+${this.tempOffset}+${rest}`; case '=': // TODO? @@ -653,9 +671,12 @@ export class EntityScope { case '^': // subroutine reference return this.includeSubroutine(rest); default: - throw new ECSError(`unrecognized command ${cmd} in ${entire}`); + let value = props[group]; + if (value) return value; + else throw new ECSError(`unrecognized command {{${group}}} in ${entire}`); } }); + return code; } includeSubroutine(symbol: string): string { this.subroutines.add(symbol); @@ -667,16 +688,7 @@ export class EntityScope { // TODO: what if 0 or 1 entitites? let s = this.dialect.ASM_ITERATE_EACH; if (joinfield) s = this.dialect.ASM_ITERATE_JOIN; - if (action.limit) { - ents = ents.slice(0, action.limit); - } - s = s.replace('{{elo}}', () => ents[0].id.toString()); - s = s.replace('{{ehi}}', () => ents[ents.length - 1].id.toString()); - s = s.replace('{{ecount}}', () => ents.length.toString()); - s = s.replace('{{code}}', code); - if (joinfield) { - s = s.replace('{{joinfield}}', () => this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0)); - } + s = s.replace('{{%code}}', code); return s; } generateCodeForField(sys: System, action: Action, diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 1eb6d49d..0615b2e5 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -101,9 +101,10 @@ export class Tokenizer { // add token to list switch (rule.type) { case TokenType.CodeFragment: - if (this.codeFragment) { + // TODO: empty code fragment doesn't work + if (this.codeFragment != null) { let codeLoc = mergeLocs(this.codeFragmentStart, loc); - this._pushToken({ str: this.codeFragment, type: rule.type, $loc: codeLoc }); //TODO: merge start/end + this._pushToken({ str: this.codeFragment, type: rule.type, $loc: codeLoc }); this.codeFragmentStart = null; this.codeFragment = null; } else { @@ -121,6 +122,7 @@ export class Tokenizer { if (this.codeFragment == null) { this._pushToken({ str: s, type: rule.type, $loc: loc }); } + case TokenType.Comment: case TokenType.Ignore: break; } diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index c130d8a5..37a1eef7 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -1,4 +1,5 @@ import { ECSCompiler } from "../../common/ecs/compiler"; +import { ECSError } from "../../common/ecs/ecs"; import { CompileError } from "../../common/tokenizer"; import { CodeListingMap } from "../../common/workertypes"; import { BuildStep, BuildStepResult, gatherFiles, getWorkFileAsString, putWorkFile, staleFiles } from "../workermain"; @@ -11,17 +12,20 @@ export function assembleECS(step: BuildStep): BuildStepResult { let code = getWorkFileAsString(step.path); try { compiler.parseFile(code, step.path); + let outtext = compiler.export().toString(); + putWorkFile(destpath, outtext); + var listings: CodeListingMap = {}; + listings[destpath] = {lines:[], text:outtext} // TODO } catch (e) { - if (e instanceof CompileError) { + if (e instanceof ECSError) { + compiler.addError(e.message, e.$loc); + return { errors: compiler.errors }; + } else if (e instanceof CompileError) { return { errors: compiler.errors }; } else { throw e; } } - let outtext = compiler.export().toString(); - putWorkFile(destpath, outtext); - var listings: CodeListingMap = {}; - listings[destpath] = {lines:[], text:outtext} // TODO } return { nexttool: "ca65", From 87eb8e2f05a66bf0ad445b5d4a09cdd45997ea51 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 31 Jan 2022 09:17:40 -0600 Subject: [PATCH 021/104] ecs: event2system --- src/codemirror/ecs.js | 4 ++++ src/common/ecs/compiler.ts | 8 ++++--- src/common/ecs/ecs.ts | 46 +++++++++++++------------------------- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 947b0806..e1b180c2 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -37,6 +37,7 @@ var opcodes = /^[a-z][a-z][a-z]\b/i; var numbers = /^(0x[\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; var tags = /^\{\{.*\}\}/; + var comment = /\/\/.*/; var mlcomment = /^---.+?---\b/i; return { @@ -52,6 +53,9 @@ if (stream.match(tags)) { return 'meta'; } + if (stream.match(comment)) { + return 'comment'; + } var w; if (stream.eatWhile(/\w/)) { diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 904e4348..e738cd7f 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,5 +1,5 @@ -import { Tokenizer, TokenType } from "../tokenizer"; +import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Dialect_CA65, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; @@ -27,6 +27,7 @@ export class ECSCompiler extends Tokenizer { { type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, + { type: TokenType.Ignore, regex: /\/\/.*/ }, { type: TokenType.Ignore, regex: /\s+/ }, ]); this.errorOnCatchAll = true; @@ -185,10 +186,11 @@ export class ECSCompiler extends Tokenizer { parseQuery() { let q: Query = { include: [] }; - this.expectToken('['); + let start = this.expectToken('['); q.include = this.parseList(this.parseComponentRef, ',').map(c => c.name); // TODO: other params - this.expectToken(']'); + let end = this.expectToken(']'); + q.$loc = mergeLocs(start.$loc, end.$loc); return q; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 78c90de5..73781b5c 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -77,7 +77,7 @@ export interface ComponentType extends SourceLocated { optional?: boolean; } -export interface Query { +export interface Query extends SourceLocated { include: string[]; // TODO: make ComponentType listen?: string[]; exclude?: string[]; @@ -407,37 +407,14 @@ export class EntityScope implements SourceLocated { } return result; } - getSystems(events: string[]) { - let result : System[] = []; - for (let sys of Object.values(this.em.systems)) { - if (this.systemListensTo(sys, events)) { - result.push(sys); - } - } - return result; - } - systemListensTo(sys: System, events: string[]) { - for (let action of sys.actions) { - if (action.event != null && events.includes(action.event)) { - let archs = this.em.archetypesMatching(action.query); - for (let arch of archs) { - for (let ctype of arch.cmatch) { - if (this.hasComponent(ctype)) { - return true; - } - } - } - } - } - } hasComponent(ctype: ComponentType) { return this.componentsInScope.has(ctype.name); } getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]) : ComponentFieldPair { let refs = Array.from(this.iterateArchetypeFields(atypes, (c,f) => f.dtype == 'ref')); // TODO: better error message - if (refs.length == 0) throw new ECSError(`cannot find join fields`, action); - if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action); + if (refs.length == 0) throw new ECSError(`cannot find join fields`, action.query); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action.query); // TODO: check to make sure join works return refs[0]; // TODO /* TODO @@ -572,8 +549,8 @@ export class EntityScope implements SourceLocated { generateCodeForEvent(event: string): string { // find systems that respond to event // and have entities in this scope - let systems = this.getSystems([event]); - if (systems.length == 0) { + let systems = this.em.event2system[event]; + if (!systems || systems.length == 0) { console.log(`; warning: no system responds to ${event}`); // TODO: warning } let s = ''; @@ -620,7 +597,6 @@ export class EntityScope implements SourceLocated { let label = `${sys.name}__${action.event}`; let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); - if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`, action); // TODO // TODO: detect cycles // TODO: "source"? // TODO: what if only 1 item? @@ -631,6 +607,8 @@ export class EntityScope implements SourceLocated { if (action.select == 'join' && action.join) { let jtypes = this.em.archetypesMatching(action.join); let jentities = this.entitiesMatching(jtypes); + if (jentities.length == 0) + throw new ECSError(`join query for ${label} doesn't match any entities`, action.join); // TODO let joinfield = this.getJoinField(action, atypes, jtypes); // TODO: what if only 1 item? // TODO: should be able to access fields via Y reg @@ -643,7 +621,8 @@ export class EntityScope implements SourceLocated { if (action.limit) { entities = entities.slice(0, action.limit); } - if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`); // TODO + if (entities.length == 0) + throw new ECSError(`query for ${label} doesn't match any entities`, action.query); // TODO // define properties props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); @@ -786,6 +765,7 @@ export class EntityManager { systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; symbols: { [name: string] : 'init' | 'const' } = {}; + event2system: { [name: string]: System[] } = {}; constructor(public readonly dialect: Dialect_CA65) { } @@ -801,6 +781,12 @@ export class EntityManager { } defineSystem(system: System) { if (this.systems[system.name]) throw new ECSError(`system ${system.name} already defined`); + for (let a of system.actions) { + let event = a.event; + let list = this.event2system[event]; + if (list == null) list = this.event2system[event] = []; + if (!list.includes(system)) list.push(system); + } return this.systems[system.name] = system; } addArchetype(atype: EntityArchetype) : EntityArchetype { From ec73e41f8c1392755b20fe0b4331039108b73e39 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 31 Jan 2022 12:11:50 -0600 Subject: [PATCH 022/104] ecs: resources --- src/common/ecs/compiler.ts | 25 +++++++++++++++++++++++-- src/common/ecs/ecs.ts | 28 +++++++++++++++++++--------- src/ide/views/editors.ts | 2 +- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index e738cd7f..de8dc173 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -49,7 +49,7 @@ export class ECSCompiler extends Tokenizer { parseTopLevel() { //this.skipBlankLines(); - let tok = this.expectTokens(['component', 'system', 'scope', 'comment']); + let tok = this.expectTokens(['component', 'system', 'scope', 'resource', 'comment']); if (tok.str == 'component') { return this.em.defineComponent(this.parseComponentDefinition()); } @@ -59,6 +59,9 @@ export class ECSCompiler extends Tokenizer { if (tok.str == 'scope') { return this.parseScope(); } + if (tok.str == 'resource') { + return this.em.defineSystem(this.parseResource()); + } if (tok.str == 'comment') { this.expectTokenTypes([TokenType.CodeFragment]); return; @@ -94,9 +97,13 @@ export class ECSCompiler extends Tokenizer { return { dtype: 'ref', query: this.parseQuery() } as RefType; } if (this.peekToken().str == 'array') { + let range : IntType; this.expectToken('array'); + if (this.peekToken().type == ECSTokenType.Integer) { + range = this.parseDataType() as IntType; + } this.expectToken('of'); - return { dtype: 'array', elem: this.parseDataType() } as ArrayType; + return { dtype: 'array', range, elem: this.parseDataType() } as ArrayType; } this.compileError(`Unknown data type`); // TODO } @@ -159,6 +166,20 @@ export class ECSCompiler extends Tokenizer { return system; } + parseResource(): System { + let name = this.expectIdent().str; + let query = this.parseQuery(); + let tempbytes; + if (this.peekToken().str == 'locals') { + this.consumeToken(); + tempbytes = this.expectInteger(); + } + let text = this.parseCode(); + let select : SelectType = 'once'; + let action : Action = { text, event: name, query, select }; + return { name, tempbytes, actions: [action] }; + } + parseAction(): Action { // TODO: unused events? let event = this.expectIdent().str; diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 73781b5c..8df28f61 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -155,6 +155,7 @@ interface ComponentFieldPair { } export class Dialect_CA65 { + readonly ASM_ITERATE_EACH = ` ldx #0 @__each: @@ -212,6 +213,12 @@ Start: datasymbol(component: ComponentType, field: DataField, eid: number) { return `${component.name}_${field.name}_e${eid}`; } + code() { + return `.code\n`; + } + bss() { + return `.bss\n`; + } } // TODO: merge with Dialect? @@ -350,7 +357,7 @@ export class EntityScope implements SourceLocated { tempOffset = 0; tempSize = 0; maxTempBytes = 0; - subroutines = new Set(); + resources = new Set(); constructor( public readonly em: EntityManager, @@ -505,6 +512,7 @@ export class EntityScope implements SourceLocated { //console.log(segment.initdata) } allocateInitData(segment: Segment) { + if (segment.size == 0) return ''; // TODO: warning for no init data? let initbytes = new Uint8Array(segment.size); let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { @@ -551,9 +559,11 @@ export class EntityScope implements SourceLocated { // and have entities in this scope let systems = this.em.event2system[event]; if (!systems || systems.length == 0) { - console.log(`; warning: no system responds to ${event}`); // TODO: warning + // TODO: error or warning? + console.log(`warning: no system responds to "${event}"`); return ''; + //throw new ECSError(`warning: no system responds to "${event}"`); } - let s = ''; + let s = this.dialect.code(); //s += `\n; event ${event}\n`; let emitcode: { [event: string]: string } = {}; for (let sys of systems) { @@ -594,7 +604,7 @@ export class EntityScope implements SourceLocated { const tag_re = /\{\{(.+?)\}\}/g; const label_re = /@(\w+)\b/g; - let label = `${sys.name}__${action.event}`; + let label = `${sys.name}__${action.event}`; // TODO: better label that won't conflict (seq?) let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); // TODO: detect cycles @@ -647,8 +657,8 @@ export class EntityScope implements SourceLocated { return this.generateCodeForField(sys, action, atypes, entities, rest, 0); case '>': // high byte return this.generateCodeForField(sys, action, atypes, entities, rest, 8); - case '^': // subroutine reference - return this.includeSubroutine(rest); + case '^': // resource reference + return this.includeResource(rest); default: let value = props[group]; if (value) return value; @@ -657,8 +667,8 @@ export class EntityScope implements SourceLocated { }); return code; } - includeSubroutine(symbol: string): string { - this.subroutines.add(symbol); + includeResource(symbol: string): string { + this.resources.add(symbol); return symbol; } wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { @@ -742,7 +752,7 @@ export class EntityScope implements SourceLocated { this.code.addCodeFragment(initcode); let start = this.generateCodeForEvent('start'); this.code.addCodeFragment(start); - for (let sub of Array.from(this.subroutines.values())) { + for (let sub of Array.from(this.resources.values())) { let code = this.generateCodeForEvent(sub); this.code.addCodeFragment(code); } diff --git a/src/ide/views/editors.ts b/src/ide/views/editors.ts index b9b5dd38..7f922d73 100644 --- a/src/ide/views/editors.ts +++ b/src/ide/views/editors.ts @@ -208,7 +208,7 @@ export class SourceEditor implements ProjectView { if (!info.path || this.path.endsWith(info.path)) { var numLines = this.editor.lineCount(); var line = info.line-1; - if (line < 0 || line >= numLines) line = 0; + if (isNaN(line) || line < 0 || line >= numLines) line = 0; this.addErrorMarker(line, info.msg); if (info.start != null) { var markOpts = {className:"mark-error", inclusiveLeft:true}; From ce1c44490068b1676305928ed2fdd19162fa805a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 31 Jan 2022 13:28:55 -0600 Subject: [PATCH 023/104] ecs: trying to do arrays, scores --- src/common/ecs/compiler.ts | 6 +- src/common/ecs/ecs.ts | 157 ++++++++++++++++++++++++------------- 2 files changed, 106 insertions(+), 57 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index de8dc173..77dc1368 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -97,13 +97,13 @@ export class ECSCompiler extends Tokenizer { return { dtype: 'ref', query: this.parseQuery() } as RefType; } if (this.peekToken().str == 'array') { - let range : IntType; + let index : IntType; this.expectToken('array'); if (this.peekToken().type == ECSTokenType.Integer) { - range = this.parseDataType() as IntType; + index = this.parseDataType() as IntType; } this.expectToken('of'); - return { dtype: 'array', range, elem: this.parseDataType() } as ArrayType; + return { dtype: 'array', index, elem: this.parseDataType() } as ArrayType; } this.compileError(`Unknown data type`); // TODO } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 8df28f61..f05854dd 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1,39 +1,44 @@ +/* +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 + + + +*/ -// 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 import { SourceLocated, SourceLocation } from "../workertypes"; @@ -230,9 +235,9 @@ export class SourceFileExport { } segment(seg: string, segtype: 'rodata' | 'bss') { if (segtype == 'bss') { - this.lines.push(`.segment "ZEROPAGE"`); // TODO + this.lines.push(`.zeropage`); // TODO } else { - this.lines.push(`.segment "CODE"`); // TODO + this.lines.push(`.code`); // TODO } } label(sym: string) { @@ -333,11 +338,19 @@ function getFieldBits(f: IntType) { return Math.ceil(Math.log2(n)); } +function getFieldLength(f: DataType) { + if (f.dtype == 'int') { + return f.hi - f.lo + 1; + } else { + return 1; //TODO? + } +} + function getPackedFieldSize(f: DataType, constValue?: DataValue): number { if (f.dtype == 'int') { return getFieldBits(f); } if (f.dtype == 'array' && f.index) { - return getPackedFieldSize(f.index) * getPackedFieldSize(f.elem); + return 0; // TODO? getFieldLength(f.index) * getPackedFieldSize(f.elem); } if (f.dtype == 'array' && constValue != null && Array.isArray(constValue)) { return constValue.length * getPackedFieldSize(f.elem); } if (f.dtype == 'ref') { @@ -437,11 +450,15 @@ export class EntityScope implements SourceLocated { */ } buildSegments() { + // build FieldArray for each component/field pair + // they will be different for bss/rodata segments let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; - let segment = v === undefined ? this.bss : this.rodata; + // constants and array pointers go into rodata + let segment = v === undefined && f.dtype != 'array' ? this.bss : this.rodata; let cfname = mksymbol(c, f.name); + // determine range of indices for entities let array = segment.fieldranges[cfname]; if (!array) { array = segment.fieldranges[cfname] = { component: c, field: f, elo: i, ehi: i }; @@ -452,27 +469,28 @@ export class EntityScope implements SourceLocated { } } allocateSegment(segment: Segment, readonly: boolean) { - let fields = Object.values(segment.fieldranges); + let fields : FieldArray[] = Object.values(segment.fieldranges); // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); - let f; + let f : FieldArray | undefined; while (f = fields.pop()) { - let name = mksymbol(f.component, f.field.name); + let rangelen = (f.ehi - f.elo + 1); + let alloc = !readonly; // TODO: doesn't work for packed arrays too well let bits = getPackedFieldSize(f.field); // variable size? make it a pointer if (bits == 0) bits = 16; // TODO? - let rangelen = (f.ehi - f.elo + 1); let bytesperelem = Math.ceil(bits / 8); // TODO: packing bits // TODO: split arrays - f.access = []; + let access = []; for (let i = 0; i < bits; i += 8) { let symbol = this.dialect.fieldsymbol(f.component, f.field, i); - f.access.push({ symbol, bit: 0, width: 8 }); // TODO - if (!readonly) { + access.push({ symbol, bit: 0, width: 8 }); // TODO + if (alloc) { segment.allocateBytes(symbol, rangelen * bytesperelem); // TODO } } + f.access = access; } } allocateROData(segment: Segment) { @@ -481,14 +499,28 @@ export class EntityScope implements SourceLocated { let { i, e, c, f, v } = o.value; let cfname = mksymbol(c, f.name); let fieldrange = segment.fieldranges[cfname]; - if (v !== undefined) { - let entcount = fieldrange.ehi - fieldrange.elo + 1; + let entcount = fieldrange ? fieldrange.ehi - fieldrange.elo + 1 : 0; + if (v === undefined) { + // this is not a constant + // is it an array? + if (f.dtype == 'array' && f.index) { + let datasym = this.dialect.datasymbol(c, f, e.id); + let offset = this.bss.allocateBytes(datasym, getFieldLength(f.index)); + let ptrlosym = this.dialect.fieldsymbol(c, f, 0); + let ptrhisym = this.dialect.fieldsymbol(c, f, 8); + let loofs = segment.allocateBytes(ptrlosym, entcount); + let hiofs = segment.allocateBytes(ptrhisym, entcount); + segment.initdata[loofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 0 }; + segment.initdata[hiofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 8 }; + } + } else { + // this is a constant // is it a byte array? if (v instanceof Uint8Array) { let datasym = this.dialect.datasymbol(c, f, e.id); + segment.allocateInitData(datasym, v); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); - segment.allocateInitData(datasym, v); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); segment.initdata[loofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 0 }; @@ -523,6 +555,11 @@ export class EntityScope implements SourceLocated { let offset = segment.getSegmentByteOffset(c, f.name, 0, e.id); if (offset !== undefined && typeof initvalue === 'number') { initbytes[offset] = initvalue; // TODO: > 8 bits? + } else if (initvalue instanceof Uint8Array) { + // TODO??? + let datasym = this.dialect.datasymbol(c, f, e.id); + let ofs = this.bss.symbols[datasym]; + initbytes.set(initvalue, ofs); } else { // TODO: init arrays? throw new ECSError(`cannot initialize ${scfname}: ${offset} ${initvalue}`); // TODO?? @@ -557,7 +594,7 @@ export class EntityScope implements SourceLocated { generateCodeForEvent(event: string): string { // find systems that respond to event // and have entities in this scope - let systems = this.em.event2system[event]; + let systems = this.em.event2systems[event]; if (!systems || systems.length == 0) { // TODO: error or warning? console.log(`warning: no system responds to "${event}"`); return ''; @@ -583,6 +620,7 @@ export class EntityScope implements SourceLocated { //console.log('<', emit, emitcode[emit].length); } } + // TODO: use Tokenizer so error msgs are better let code = this.replaceCode(action.text, sys, action); s += this.dialect.comment(``); s += code; @@ -650,7 +688,11 @@ export class EntityScope implements SourceLocated { case '@': // auto label return `${label}_${rest}`; case '$': // temp byte (TODO: check to make sure not overflowing) - return `TEMP+${this.tempOffset}+${rest}`; + let tempinc = parseInt(rest); + if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, action); + if (!sys.tempbytes) throw new ECSError(`this system has no locals`, action); + if (tempinc < 0 || tempinc >= sys.tempbytes) throw new ECSError(`this system only has ${sys.tempbytes} locals`, action); + return `TEMP+${this.tempOffset}+${tempinc}`; case '=': // TODO? case '<': // low byte @@ -775,7 +817,8 @@ export class EntityManager { systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; symbols: { [name: string] : 'init' | 'const' } = {}; - event2system: { [name: string]: System[] } = {}; + event2systems: { [name: string]: System[] } = {}; + name2cfpairs: { [name: string]: ComponentFieldPair[]} = {}; constructor(public readonly dialect: Dialect_CA65) { } @@ -787,14 +830,19 @@ export class EntityManager { } defineComponent(ctype: ComponentType) { if (this.components[ctype.name]) throw new ECSError(`component ${ctype.name} already defined`); + 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; } defineSystem(system: System) { if (this.systems[system.name]) throw new ECSError(`system ${system.name} already defined`); for (let a of system.actions) { let event = a.event; - let list = this.event2system[event]; - if (list == null) list = this.event2system[event] = []; + let list = this.event2systems[event]; + if (list == null) list = this.event2systems[event] = []; if (!list.includes(system)) list.push(system); } return this.systems[system.name] = system; @@ -848,6 +896,7 @@ export class EntityManager { } singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: string) { let components = this.componentsWithFieldName(atypes, fieldName); + // TODO: use name2cfpairs? if (components.length == 0) { throw new ECSError(`cannot find component with field "${fieldName}" in ${where}`); } From 0803b80485d4e630de6ca8624618347bf652c89e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 31 Jan 2022 21:16:40 -0600 Subject: [PATCH 024/104] ecs: fixed init 16-bit values --- src/common/ecs/ecs.ts | 78 +++++++++++++++++++++++++++---------------- src/test/testecs.ts | 2 +- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index f05854dd..e2317a88 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -316,15 +316,28 @@ class Segment { getFieldRange(component: ComponentType, fieldName: string) { return this.fieldranges[mksymbol(component, fieldName)]; } - getSegmentByteOffset(component: ComponentType, fieldName: string, bitofs: number, entityID: number) { + getByteOffset(range: FieldArray, access: FieldAccess, entityID: number) { + let ofs = this.symbols[access.symbol]; + if (ofs !== undefined) { + return ofs + entityID - range.elo; + } + // TODO: show entity name? + throw new ECSError(`cannot find field access for ${access.symbol}`); + } + getSegmentByteOffset(component: ComponentType, fieldName: string, entityID: number, bitofs: number, width: number) { let range = this.getFieldRange(component, fieldName); if (range && range.access) { - let a = range.access[0]; // TODO: bitofs - let ofs = this.symbols[a.symbol]; - if (ofs !== undefined) { - return ofs + entityID - range.elo; + for (let a of range.access) { + if (a.bit == bitofs && a.width == width) { + let ofs = this.symbols[a.symbol]; + if (ofs !== undefined) { + return ofs + entityID - range.elo; + } + } } } + // TODO: show entity name? + throw new ECSError(`cannot find field offset for ${component.name}:${fieldName} entity #${entityID} bits ${bitofs} ${width}`) } getOriginSymbol() { let a = this.ofs2sym.get(0); @@ -485,9 +498,9 @@ export class EntityScope implements SourceLocated { let access = []; for (let i = 0; i < bits; i += 8) { let symbol = this.dialect.fieldsymbol(f.component, f.field, i); - access.push({ symbol, bit: 0, width: 8 }); // TODO + access.push({ symbol, bit: i, width: 8 }); // TODO if (alloc) { - segment.allocateBytes(symbol, rangelen * bytesperelem); // TODO + segment.allocateBytes(symbol, rangelen); // TODO } } f.access = access; @@ -498,11 +511,12 @@ export class EntityScope implements SourceLocated { for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; let cfname = mksymbol(c, f.name); - let fieldrange = segment.fieldranges[cfname]; - let entcount = fieldrange ? fieldrange.ehi - fieldrange.elo + 1 : 0; + let range = segment.fieldranges[cfname]; + let entcount = range ? range.ehi - range.elo + 1 : 0; + // is this a constant value? if (v === undefined) { // this is not a constant - // is it an array? + // is it a bounded array? (TODO) if (f.dtype == 'array' && f.index) { let datasym = this.dialect.datasymbol(c, f, e.id); let offset = this.bss.allocateBytes(datasym, getFieldLength(f.index)); @@ -510,8 +524,8 @@ export class EntityScope implements SourceLocated { let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); - segment.initdata[loofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 0 }; - segment.initdata[hiofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 8 }; + segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; + segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; } } else { // this is a constant @@ -523,19 +537,20 @@ export class EntityScope implements SourceLocated { let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); - segment.initdata[loofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 0 }; - segment.initdata[hiofs + e.id - fieldrange.elo] = { symbol: datasym, bitofs: 8 }; + segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; + segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; // TODO: } else if (v instanceof Uint16Array) { } else if (typeof v === 'number') { // more than 1 entity, add an array - // TODO: what if > 8 bits? - // TODO: what if mix of var, const, and init values? - if (fieldrange.ehi > fieldrange.elo) { - let datasym = this.dialect.fieldsymbol(c, f, 0); - let base = segment.allocateBytes(datasym, entcount); - segment.initdata[base + e.id - fieldrange.elo] = v; - //console.error(cfname, datasym, base, e.id, fieldrange.elo, entcount, v); + if (range.ehi > range.elo) { + if (!range.access) throw new ECSError(`no access for field ${cfname}`) + let base = segment.allocateBytes(range.access[0].symbol, range.ehi-range.elo+1); + for (let a of range.access) { + let ofs = segment.getByteOffset(range, a, e.id); + segment.initdata[ofs] = v; + } } + // TODO: what if mix of var, const, and init values? } else { throw new ECSError(`unhandled constant ${e.id}:${cfname}`); } @@ -552,9 +567,14 @@ export class EntityScope implements SourceLocated { let scfname = mkscopesymbol(this, c, f.name); let initvalue = e.inits[scfname]; if (initvalue !== undefined) { - let offset = segment.getSegmentByteOffset(c, f.name, 0, e.id); - if (offset !== undefined && typeof initvalue === 'number') { - initbytes[offset] = initvalue; // TODO: > 8 bits? + let range = segment.getFieldRange(c, f.name); + if (!range) throw new ECSError(`no range`, e); + if (!range.access) throw new ECSError(`no range access`, e); + if (typeof initvalue === 'number') { + for (let a of range.access) { + let offset = segment.getByteOffset(range, a, e.id); + initbytes[offset] = (initvalue >> a.bit) & ((1 << a.width)-1); + } } else if (initvalue instanceof Uint8Array) { // TODO??? let datasym = this.dialect.datasymbol(c, f, e.id); @@ -562,7 +582,7 @@ export class EntityScope implements SourceLocated { initbytes.set(initvalue, ofs); } else { // TODO: init arrays? - throw new ECSError(`cannot initialize ${scfname}: ${offset} ${initvalue}`); // TODO?? + throw new ECSError(`cannot initialize ${scfname} = ${initvalue}`); // TODO?? } } } @@ -699,6 +719,10 @@ export class EntityScope implements SourceLocated { return this.generateCodeForField(sys, action, atypes, entities, rest, 0); case '>': // high byte return this.generateCodeForField(sys, action, atypes, entities, rest, 8); + case '(': // higher byte (TODO) + return this.generateCodeForField(sys, action, atypes, entities, rest, 16); + case ')': // higher byte (TODO) + return this.generateCodeForField(sys, action, atypes, entities, rest, 24); case '^': // resource reference return this.includeResource(rest); default: @@ -753,9 +777,7 @@ export class EntityScope implements SourceLocated { // TODO: what about symbols? // TODO: use dialect if (typeof value === 'number') { - if (bitofs == 0) return `#<${value}`; - if (bitofs == 8) return `#>${value}`; - // TODO: bitofs? + return `#${(value >> bitofs) & 0xff}`; } } // TODO: offset > 0? diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 3c3b4438..12ce31ac 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -207,7 +207,7 @@ function testECS() { // TODO: check names for identifierness em.defineSystem({ name: 'kernel_simple', - tempbytes: 8, + tempbytes: 12, actions: [ { text: TEMPLATE4_S1, event: 'preframe', select: 'once', query: { From 10784585d97aa77f2b9aaea45244b23a3cf6a0e5 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 1 Feb 2022 08:07:33 -0600 Subject: [PATCH 025/104] ecs: codesegment vs data --- src/common/ecs/ecs.ts | 47 ++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index e2317a88..f67d8e55 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -35,7 +35,9 @@ 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?) */ @@ -233,11 +235,11 @@ export class SourceFileExport { comment(text: string) { this.lines.push(';' + text); } - segment(seg: string, segtype: 'rodata' | 'bss') { + segment(seg: string, segtype: 'rodata' | 'bss' | 'code') { if (segtype == 'bss') { this.lines.push(`.zeropage`); // TODO } else { - this.lines.push(`.code`); // TODO + this.lines.push(`.code`); } } label(sym: string) { @@ -270,17 +272,26 @@ export class SourceFileExport { } } -class Segment { - symbols: { [sym: string]: number } = {}; - ofs2sym = new Map(); - fieldranges: { [cfname: string]: FieldArray } = {}; - size: number = 0; - initdata: (number | ConstByte | undefined)[] = []; +class CodeSegment { codefrags: string[] = []; addCodeFragment(code: string) { this.codefrags.push(code); } + dump(file: SourceFileExport) { + for (let code of this.codefrags) { + file.text(code); + } + } +} + +class DataSegment { + symbols: { [sym: string]: number } = {}; + ofs2sym = new Map(); + fieldranges: { [cfname: string]: FieldArray } = {}; + size: number = 0; + initdata: (number | ConstByte | undefined)[] = []; + allocateBytes(name: string, bytes: number) { let ofs = this.symbols[name]; if (ofs == null) { @@ -301,9 +312,6 @@ class Segment { } } dump(file: SourceFileExport) { - for (let code of this.codefrags) { - file.text(code); - } for (let i = 0; i < this.size; i++) { let syms = this.ofs2sym.get(i); if (syms) { @@ -376,9 +384,9 @@ export class EntityScope implements SourceLocated { $loc: SourceLocation; childScopes: EntityScope[] = []; entities: Entity[] = []; - bss = new Segment(); - rodata = new Segment(); - code = new Segment(); + bss = new DataSegment(); + rodata = new DataSegment(); + code = new CodeSegment(); componentsInScope = new Set(); tempOffset = 0; tempSize = 0; @@ -481,7 +489,7 @@ export class EntityScope implements SourceLocated { //console.log(i,array,cfname); } } - allocateSegment(segment: Segment, readonly: boolean) { + allocateSegment(segment: DataSegment, readonly: boolean) { let fields : FieldArray[] = Object.values(segment.fieldranges); // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); let f : FieldArray | undefined; @@ -506,7 +514,7 @@ export class EntityScope implements SourceLocated { f.access = access; } } - allocateROData(segment: Segment) { + allocateROData(segment: DataSegment) { let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; @@ -558,7 +566,7 @@ export class EntityScope implements SourceLocated { } //console.log(segment.initdata) } - allocateInitData(segment: Segment) { + allocateInitData(segment: DataSegment) { if (segment.size == 0) return ''; // TODO: warning for no init data? let initbytes = new Uint8Array(segment.size); let iter = this.iterateEntityFields(this.entities); @@ -826,8 +834,9 @@ export class EntityScope implements SourceLocated { file.segment(`${this.name}_DATA`, 'bss'); if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); this.bss.dump(file); - file.segment(`${this.name}_CODE`, 'rodata'); + file.segment(`${this.name}_RODATA`, 'rodata'); this.rodata.dump(file); + //file.segment(`${this.name}_CODE`, 'code'); this.code.dump(file); file.text(this.dialect.FOOTER); // TODO } From 40cc5ee118b84427cc4891eff563f93f50ed10a4 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 1 Feb 2022 08:16:53 -0600 Subject: [PATCH 026/104] ecs: refactor into ActionEval --- src/common/ecs/ecs.ts | 370 ++++++++++++++++++++++-------------------- 1 file changed, 193 insertions(+), 177 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index f67d8e55..fb15f8b2 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -173,7 +173,7 @@ export class Dialect_CA65 { @__exit: `; -readonly ASM_ITERATE_JOIN = ` + readonly ASM_ITERATE_JOIN = ` ldy #0 @__each: ldx {{%joinfield}},y @@ -380,6 +380,182 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { return 0; } +class ActionEval { + em; + dialect; + constructor( + readonly scope: EntityScope, + readonly sys: System, + readonly action: Action) { + this.em = scope.em; + this.dialect = scope.em.dialect; + } + codeToString(): string { + const tag_re = /\{\{(.+?)\}\}/g; + const label_re = /@(\w+)\b/g; + + let action = this.action; + let sys = this.sys; + let code = action.text; + + let label = `${sys.name}__${action.event}`; // TODO: better label that won't conflict (seq?) + let atypes = this.em.archetypesMatching(action.query); + let entities = this.scope.entitiesMatching(atypes); + // TODO: detect cycles + // TODO: "source"? + // TODO: what if only 1 item? + let props: { [name: string]: string } = {}; + if (action.select == 'foreach') { + code = this.wrapCodeInLoop(code, action, entities); + } + if (action.select == 'join' && action.join) { + let jtypes = this.em.archetypesMatching(action.join); + let jentities = this.scope.entitiesMatching(jtypes); + if (jentities.length == 0) + throw new ECSError(`join query for ${label} doesn't match any entities`, action.join); // TODO + let joinfield = this.getJoinField(action, atypes, jtypes); + // TODO: what if only 1 item? + // TODO: should be able to access fields via Y reg + code = this.wrapCodeInLoop(code, action, entities, joinfield); + atypes = jtypes; + entities = jentities; + props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); + } + props['%efullcount'] = entities.length.toString(); + if (action.limit) { + entities = entities.slice(0, action.limit); + } + if (entities.length == 0) + throw new ECSError(`query for ${label} doesn't match any entities`, action.query); // TODO + // define properties + props['%elo'] = entities[0].id.toString(); + props['%ehi'] = entities[entities.length - 1].id.toString(); + props['%ecount'] = entities.length.toString(); + // replace @labels + code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); + // replace {{...}} tags + code = code.replace(tag_re, (entire, group: string) => { + let cmd = group.charAt(0); + let rest = group.substring(1); + switch (cmd) { + case '!': // emit event + return this.scope.generateCodeForEvent(rest); + case '.': // auto label + case '@': // auto label + return `${label}_${rest}`; + case '$': // temp byte (TODO: check to make sure not overflowing) + let tempinc = parseInt(rest); + if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, action); + if (!sys.tempbytes) throw new ECSError(`this system has no locals`, action); + if (tempinc < 0 || tempinc >= sys.tempbytes) throw new ECSError(`this system only has ${sys.tempbytes} locals`, action); + return `TEMP+${this.scope.tempOffset}+${tempinc}`; + case '=': + // TODO? + case '<': // low byte + return this.generateCodeForField(sys, action, atypes, entities, rest, 0); + case '>': // high byte + return this.generateCodeForField(sys, action, atypes, entities, rest, 8); + case '(': // higher byte (TODO) + return this.generateCodeForField(sys, action, atypes, entities, rest, 16); + case ')': // higher byte (TODO) + return this.generateCodeForField(sys, action, atypes, entities, rest, 24); + case '^': // resource reference + return this.scope.includeResource(rest); + default: + let value = props[group]; + if (value) return value; + else throw new ECSError(`unrecognized command {{${group}}} in ${entire}`); + } + }); + return code; + } + wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { + // TODO: check ents + // TODO: check segment bounds + // TODO: what if 0 or 1 entitites? + let s = this.dialect.ASM_ITERATE_EACH; + if (joinfield) s = this.dialect.ASM_ITERATE_JOIN; + s = s.replace('{{%code}}', code); + return s; + } + generateCodeForField(sys: System, action: Action, + atypes: ArchetypeMatch[], entities: Entity[], + fieldName: string, bitofs: number): string { + + var component: ComponentType; + var qualified = false; + // is qualified field? + if (fieldName.indexOf(':') > 0) { + let [cname, fname] = fieldName.split(':'); + component = this.em.getComponentByName(cname); + fieldName = fname; + qualified = true; + if (component == null) throw new ECSError(`no component named "${cname}"`) + } else { + component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); + } + // find archetypes + let field = component.fields.find(f => f.name == fieldName); + if (field == null) throw new ECSError(`no field named "${fieldName}" in component`) + // see if all entities have the same constant value + let constValues = new Set(); + for (let e of entities) { + let constVal = e.consts[mksymbol(component, fieldName)]; + constValues.add(constVal); // constVal === undefined is allowed + } + // is it a constant? + if (constValues.size == 1) { + let value = constValues.values().next().value as DataValue; + // TODO: what about symbols? + // TODO: use dialect + if (typeof value === 'number') { + return `#${(value >> bitofs) & 0xff}`; + } + } + // TODO: offset > 0? + // TODO: don't mix const and init data + let range = this.scope.bss.getFieldRange(component, fieldName) || this.scope.rodata.getFieldRange(component, fieldName); + if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO + let eidofs = range.elo - entities[0].id; + // TODO: dialect + let ident = this.dialect.fieldsymbol(component, field, bitofs); + if (qualified) { + return this.dialect.absolute(ident); + } else if (action.select == 'once') { + if (entities.length != 1) + throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`); + return this.dialect.absolute(ident); + } else { + // TODO: right direction? + if (eidofs > 0) { + ident += '+' + eidofs; + } else if (eidofs < 0) { + ident += '' + eidofs; + } + return this.dialect.indexed_x(ident); + } + } + getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]): ComponentFieldPair { + let refs = Array.from(this.scope.iterateArchetypeFields(atypes, (c, f) => f.dtype == 'ref')); + // TODO: better error message + if (refs.length == 0) throw new ECSError(`cannot find join fields`, action.query); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action.query); + // TODO: check to make sure join works + return refs[0]; // TODO + /* TODO + let match = refs.map(ref => this.em.archetypesMatching((ref.f as RefType).query)); + for (let ref of refs) { + let m = this.em.archetypesMatching((ref.f as RefType).query); + for (let a of m) { + if (jtypes.includes(a.etype)) { + console.log(a,m); + } + } + } + */ + } +} + export class EntityScope implements SourceLocated { $loc: SourceLocation; childScopes: EntityScope[] = []; @@ -423,19 +599,19 @@ export class EntityScope implements SourceLocated { } } } - *iterateArchetypeFields(arch: ArchetypeMatch[], filter?: (c:ComponentType,f:DataField) => boolean) { + *iterateArchetypeFields(arch: ArchetypeMatch[], filter?: (c: ComponentType, f: DataField) => boolean) { for (let i = 0; i < arch.length; i++) { let a = arch[i]; for (let c of a.etype.components) { for (let f of c.fields) { - if (!filter || filter(c,f)) + if (!filter || filter(c, f)) yield { i, c, f }; } } } } entitiesMatching(atypes: ArchetypeMatch[]) { - let result : Entity[] = []; + let result: Entity[] = []; for (let e of this.entities) { for (let a of atypes) { // TODO: what about subclasses? @@ -451,25 +627,6 @@ export class EntityScope implements SourceLocated { hasComponent(ctype: ComponentType) { return this.componentsInScope.has(ctype.name); } - getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]) : ComponentFieldPair { - let refs = Array.from(this.iterateArchetypeFields(atypes, (c,f) => f.dtype == 'ref')); - // TODO: better error message - if (refs.length == 0) throw new ECSError(`cannot find join fields`, action.query); - if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action.query); - // TODO: check to make sure join works - return refs[0]; // TODO - /* TODO - let match = refs.map(ref => this.em.archetypesMatching((ref.f as RefType).query)); - for (let ref of refs) { - let m = this.em.archetypesMatching((ref.f as RefType).query); - for (let a of m) { - if (jtypes.includes(a.etype)) { - console.log(a,m); - } - } - } - */ - } buildSegments() { // build FieldArray for each component/field pair // they will be different for bss/rodata segments @@ -490,9 +647,9 @@ export class EntityScope implements SourceLocated { } } allocateSegment(segment: DataSegment, readonly: boolean) { - let fields : FieldArray[] = Object.values(segment.fieldranges); + let fields: FieldArray[] = Object.values(segment.fieldranges); // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); - let f : FieldArray | undefined; + let f: FieldArray | undefined; while (f = fields.pop()) { let rangelen = (f.ehi - f.elo + 1); let alloc = !readonly; @@ -552,7 +709,7 @@ export class EntityScope implements SourceLocated { // more than 1 entity, add an array if (range.ehi > range.elo) { if (!range.access) throw new ECSError(`no access for field ${cfname}`) - let base = segment.allocateBytes(range.access[0].symbol, range.ehi-range.elo+1); + let base = segment.allocateBytes(range.access[0].symbol, range.ehi - range.elo + 1); for (let a of range.access) { let ofs = segment.getByteOffset(range, a, e.id); segment.initdata[ofs] = v; @@ -581,7 +738,7 @@ export class EntityScope implements SourceLocated { if (typeof initvalue === 'number') { for (let a of range.access) { let offset = segment.getByteOffset(range, a, e.id); - initbytes[offset] = (initvalue >> a.bit) & ((1 << a.width)-1); + initbytes[offset] = (initvalue >> a.bit) & ((1 << a.width) - 1); } } else if (initvalue instanceof Uint8Array) { // TODO??? @@ -606,14 +763,14 @@ export class EntityScope implements SourceLocated { return code; } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setConstValue"); + let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, "setConstValue"); e.consts[mksymbol(component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'init') throw new ECSError(`Can't mix const and init values for a component field`, e); this.em.symbols[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{etype: e.etype, cmatch:[component]}], fieldName, "setInitValue"); + let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, "setInitValue"); e.inits[mkscopesymbol(this, component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'const') throw new ECSError(`Can't mix const and init values for a component field`, e); @@ -649,9 +806,9 @@ export class EntityScope implements SourceLocated { } } // TODO: use Tokenizer so error msgs are better - let code = this.replaceCode(action.text, sys, action); - s += this.dialect.comment(``); - s += code; + let codeeval = new ActionEval(this, sys, action); + s += this.dialect.comment(``); // TODO + s += codeeval.codeToString(); s += this.dialect.comment(``); // TODO: check that this happens once? } @@ -666,151 +823,10 @@ export class EntityScope implements SourceLocated { this.maxTempBytes = Math.max(this.tempSize, this.maxTempBytes); if (n < 0) this.tempOffset = this.tempSize; } - replaceCode(code: string, sys: System, action: Action): string { - const tag_re = /\{\{(.+?)\}\}/g; - const label_re = /@(\w+)\b/g; - - let label = `${sys.name}__${action.event}`; // TODO: better label that won't conflict (seq?) - let atypes = this.em.archetypesMatching(action.query); - let entities = this.entitiesMatching(atypes); - // TODO: detect cycles - // TODO: "source"? - // TODO: what if only 1 item? - let props : {[name: string] : string} = {}; - if (action.select == 'foreach') { - code = this.wrapCodeInLoop(code, action, entities); - } - if (action.select == 'join' && action.join) { - let jtypes = this.em.archetypesMatching(action.join); - let jentities = this.entitiesMatching(jtypes); - if (jentities.length == 0) - throw new ECSError(`join query for ${label} doesn't match any entities`, action.join); // TODO - let joinfield = this.getJoinField(action, atypes, jtypes); - // TODO: what if only 1 item? - // TODO: should be able to access fields via Y reg - code = this.wrapCodeInLoop(code, action, entities, joinfield); - atypes = jtypes; - entities = jentities; - props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); - } - props['%efullcount'] = entities.length.toString(); - if (action.limit) { - entities = entities.slice(0, action.limit); - } - if (entities.length == 0) - throw new ECSError(`query for ${label} doesn't match any entities`, action.query); // TODO - // define properties - props['%elo'] = entities[0].id.toString(); - props['%ehi'] = entities[entities.length - 1].id.toString(); - props['%ecount'] = entities.length.toString(); - // replace @labels - code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); - // replace {{...}} tags - code = code.replace(tag_re, (entire, group: string) => { - let cmd = group.charAt(0); - let rest = group.substring(1); - switch (cmd) { - case '!': // emit event - return this.generateCodeForEvent(rest); - case '.': // auto label - case '@': // auto label - return `${label}_${rest}`; - case '$': // temp byte (TODO: check to make sure not overflowing) - let tempinc = parseInt(rest); - if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, action); - if (!sys.tempbytes) throw new ECSError(`this system has no locals`, action); - if (tempinc < 0 || tempinc >= sys.tempbytes) throw new ECSError(`this system only has ${sys.tempbytes} locals`, action); - return `TEMP+${this.tempOffset}+${tempinc}`; - case '=': - // TODO? - case '<': // low byte - return this.generateCodeForField(sys, action, atypes, entities, rest, 0); - case '>': // high byte - return this.generateCodeForField(sys, action, atypes, entities, rest, 8); - case '(': // higher byte (TODO) - return this.generateCodeForField(sys, action, atypes, entities, rest, 16); - case ')': // higher byte (TODO) - return this.generateCodeForField(sys, action, atypes, entities, rest, 24); - case '^': // resource reference - return this.includeResource(rest); - default: - let value = props[group]; - if (value) return value; - else throw new ECSError(`unrecognized command {{${group}}} in ${entire}`); - } - }); - return code; - } includeResource(symbol: string): string { this.resources.add(symbol); return symbol; } - wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { - // TODO: check ents - // TODO: check segment bounds - // TODO: what if 0 or 1 entitites? - let s = this.dialect.ASM_ITERATE_EACH; - if (joinfield) s = this.dialect.ASM_ITERATE_JOIN; - s = s.replace('{{%code}}', code); - return s; - } - generateCodeForField(sys: System, action: Action, - atypes: ArchetypeMatch[], entities: Entity[], - fieldName: string, bitofs: number): string { - - var component : ComponentType; - var qualified = false; - // is qualified field? - if (fieldName.indexOf(':') > 0) { - let [cname,fname] = fieldName.split(':'); - component = this.em.getComponentByName(cname); - fieldName = fname; - qualified = true; - if (component == null) throw new ECSError(`no component named "${cname}"`) - } else { - component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); - } - // find archetypes - let field = component.fields.find(f => f.name == fieldName); - if (field == null) throw new ECSError(`no field named "${fieldName}" in component`) - // see if all entities have the same constant value - let constValues = new Set(); - for (let e of entities) { - let constVal = e.consts[mksymbol(component, fieldName)]; - constValues.add(constVal); // constVal === undefined is allowed - } - // is it a constant? - if (constValues.size == 1) { - let value = constValues.values().next().value as DataValue; - // TODO: what about symbols? - // TODO: use dialect - if (typeof value === 'number') { - return `#${(value >> bitofs) & 0xff}`; - } - } - // TODO: offset > 0? - // TODO: don't mix const and init data - let range = this.bss.getFieldRange(component, fieldName) || this.rodata.getFieldRange(component, fieldName); - if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO - let eidofs = range.elo - entities[0].id; - // TODO: dialect - let ident = this.dialect.fieldsymbol(component, field, bitofs); - if (qualified) { - return this.dialect.absolute(ident); - } else if (action.select == 'once') { - if (entities.length != 1) - throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`); - return this.dialect.absolute(ident); - } else { - // TODO: right direction? - if (eidofs > 0) { - ident += '+' + eidofs; - } else if (eidofs < 0) { - ident += '' + eidofs; - } - return this.dialect.indexed_x(ident); - } - } analyzeEntities() { this.buildSegments(); this.allocateSegment(this.bss, false); @@ -847,9 +863,9 @@ export class EntityManager { components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; - symbols: { [name: string] : 'init' | 'const' } = {}; + symbols: { [name: string]: 'init' | 'const' } = {}; event2systems: { [name: string]: System[] } = {}; - name2cfpairs: { [name: string]: ComponentFieldPair[]} = {}; + name2cfpairs: { [name: string]: ComponentFieldPair[] } = {}; constructor(public readonly dialect: Dialect_CA65) { } @@ -864,7 +880,7 @@ export class EntityManager { 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}); + list.push({ c: ctype, f: field }); } return this.components[ctype.name] = ctype; } @@ -878,7 +894,7 @@ export class EntityManager { } return this.systems[system.name] = system; } - addArchetype(atype: EntityArchetype) : EntityArchetype { + addArchetype(atype: EntityArchetype): EntityArchetype { let key = atype.components.map(c => c.name).join(','); if (this.archetypes[key]) return this.archetypes[key]; From 4e5beb6c74d632e48784d8c4425a43484f1613de Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 1 Feb 2022 09:13:37 -0600 Subject: [PATCH 027/104] tokenizer is no longer line-based --- src/codemirror/ecs.js | 5 +- src/common/ecs/compiler.ts | 45 +++-- src/common/tokenizer.ts | 94 +++++------ src/test/testecs.ts | 5 +- src/test/testutil.ts | 325 +++++++++++++++++++------------------ 5 files changed, 245 insertions(+), 229 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index e1b180c2..c418cc03 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -38,7 +38,7 @@ var numbers = /^(0x[\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; var tags = /^\{\{.*\}\}/; var comment = /\/\/.*/; - var mlcomment = /^---.+?---\b/i; + var mlcomment = /\/\*.*?\*\//s; // TODO return { startState: function () { @@ -56,6 +56,9 @@ if (stream.match(comment)) { return 'comment'; } + if (stream.match(mlcomment)) { + return 'comment'; + } var w; if (stream.eatWhile(/\w/)) { diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 77dc1368..61407a10 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -8,6 +8,7 @@ export enum ECSTokenType { Operator = 'delimiter', QuotedString = 'quoted-string', Integer = 'integer', + CodeFragment = 'code-fragment', } export class ECSCompiler extends Tokenizer { @@ -19,15 +20,16 @@ export class ECSCompiler extends Tokenizer { super(); //this.includeEOL = true; this.setTokenRules([ - { type: TokenType.CodeFragment, regex: /---/ }, { type: ECSTokenType.Ellipsis, regex: /\.\./ }, { type: ECSTokenType.Operator, regex: /[#=,:(){}\[\]]/ }, { type: ECSTokenType.QuotedString, regex: /".*?"/ }, + { type: ECSTokenType.CodeFragment, regex: /---.*?---/ }, { type: ECSTokenType.Integer, regex: /[-]?0x[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, - { type: TokenType.Ignore, regex: /\/\/.*/ }, + { type: TokenType.Ignore, regex: /\/\/.*?[\n\r]/ }, + { type: TokenType.Ignore, regex: /\/\*.*?\*\// }, { type: TokenType.Ignore, regex: /\s+/ }, ]); this.errorOnCatchAll = true; @@ -43,7 +45,11 @@ export class ECSCompiler extends Tokenizer { parseFile(text: string, path: string) { this.tokenizeFile(text, path); while (!this.isEOF()) { - this.annotate(() => this.parseTopLevel()); + let top = this.parseTopLevel(); + if (top) { + let t = top; + this.annotate(() => t); // TODO? typescript bug? + } } } @@ -63,7 +69,7 @@ export class ECSCompiler extends Tokenizer { return this.em.defineSystem(this.parseResource()); } if (tok.str == 'comment') { - this.expectTokenTypes([TokenType.CodeFragment]); + this.expectTokenTypes([ECSTokenType.CodeFragment]); return; } this.compileError(`Unexpected top-level keyword: ${tok.str}`); @@ -97,7 +103,7 @@ export class ECSCompiler extends Tokenizer { return { dtype: 'ref', query: this.parseQuery() } as RefType; } if (this.peekToken().str == 'array') { - let index : IntType; + let index : IntType | undefined = undefined; this.expectToken('array'); if (this.peekToken().type == ECSTokenType.Integer) { index = this.parseDataType() as IntType; @@ -105,7 +111,7 @@ export class ECSCompiler extends Tokenizer { this.expectToken('of'); return { dtype: 'array', index, elem: this.parseDataType() } as ArrayType; } - this.compileError(`Unknown data type`); // TODO + this.internalError(); throw new Error(); } parseDataValue(field: DataField) : DataValue { @@ -123,6 +129,9 @@ export class ECSCompiler extends Tokenizer { 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.`); @@ -130,7 +139,7 @@ export class ECSCompiler extends Tokenizer { } return id; } - this.compileError(`Unknown data value`); // TODO + this.internalError(); throw new Error(); } parseDataArray() { @@ -186,7 +195,8 @@ export class ECSCompiler extends Tokenizer { this.expectToken('do'); let select = this.expectTokens(['once', 'foreach', 'source', 'join']).str as SelectType; // TODO: type check? let query = this.parseQuery(); - let join = select == 'join' && this.parseQuery(); + let join = undefined; + if (select == 'join') join = this.parseQuery(); let emits; let limit; if (this.peekToken().str == 'limit') { @@ -225,8 +235,8 @@ export class ECSCompiler extends Tokenizer { parseCode(): string { // TODO: add $loc - let tok = this.expectTokenTypes([TokenType.CodeFragment]); - let code = tok.str; + let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); + let code = tok.str.substring(3, tok.str.length-3); let lines = code.split('\n'); for (let i=0; i this.parseEntity()); } if (cmd == 'comment') { - this.expectTokenTypes([TokenType.CodeFragment]); + this.expectTokenTypes([ECSTokenType.CodeFragment]); } } - this.currentScope = scope.parent; + this.currentScope = scope.parent || null; return scope; } parseEntity() : Entity { + if (!this.currentScope) { this.internalError(); throw new Error(); } let name = ''; if (this.peekToken().type == TokenType.Ident) { name = this.expectIdent().str; @@ -267,7 +278,7 @@ export class ECSCompiler extends Tokenizer { 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); - if (!field) this.internalError(); + if (!field) { this.internalError(); throw new Error(); } this.expectToken('='); let value = this.parseDataValue(field); if (cmd == 'const') this.currentScope.setConstValue(e, comps[0], name, value); @@ -291,10 +302,14 @@ export class ECSCompiler extends Tokenizer { } 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); - if (!eref) this.compileError(`I couldn't find an entity named "${name}" in this scope.`) + if (!eref) { + this.compileError(`I couldn't find an entity named "${name}" in this scope.`) + throw new Error(); + } return eref; } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 0615b2e5..d62aae82 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -26,7 +26,6 @@ export enum TokenType { Ident = 'ident', Comment = 'comment', Ignore = 'ignore', - CodeFragment = 'code-fragment', CatchAll = 'catch-all', } @@ -49,79 +48,78 @@ function re_escape(rule: TokenRule): string { return `(${rule.regex.source})`; } -export class Tokenizer { +export class TokenizerRuleSet { rules: TokenRule[]; regex: RegExp; + constructor(rules: TokenRule[]) { + this.rules = rules.concat(CATCH_ALL_RULES); + var pattern = this.rules.map(re_escape).join('|'); + this.regex = new RegExp(pattern, "gs"); // global, dotall + } +} + +export class Tokenizer { + ruleset: TokenizerRuleSet; + lineindex: number[]; path: string; lineno: number; tokens: Token[]; lasttoken: Token; errors: WorkerError[]; curlabel: string; - eol: Token; - includeEOL = false; + eof: Token; errorOnCatchAll = false; - codeFragment : string | null = null; - codeFragmentStart : SourceLocation | null = null; constructor() { - this.lineno = 0; this.errors = []; + this.lineno = 0; + this.lineindex = []; + this.tokens = []; + } + setTokenRuleSet(ruleset: TokenizerRuleSet) { + this.ruleset = ruleset; } setTokenRules(rules: TokenRule[]) { - this.rules = rules.concat(CATCH_ALL_RULES); - var pattern = this.rules.map(re_escape).join('|'); - this.regex = new RegExp(pattern, "g"); + this.setTokenRuleSet(new TokenizerRuleSet(rules)); } tokenizeFile(contents: string, path: string) { this.path = path; - this.tokens = []; // can't have errors until this is set - let txtlines = contents.split(/\n|\r\n?/); - txtlines.forEach((line) => this._tokenize(line)); - this._pushToken({ type: TokenType.EOF, str: "", $loc: { path: this.path, line: this.lineno } }); + let m; + let re = /\n|\r\n?/g; + this.lineindex.push(0); + while (m = re.exec(contents)) { + this.lineindex.push(m.index); + } + this._tokenize(contents); + this.eof = { type: TokenType.EOF, str: "", $loc: { path: this.path, line: this.lineno } }; + this.pushToken(this.eof); } - tokenizeLine(line: string) : void { - this.lineno++; - this._tokenize(line); - } - _tokenize(line: string): void { - this.lineno++; - this.eol = { type: TokenType.EOL, str: "", $loc: { path: this.path, line: this.lineno, start: line.length } }; + _tokenize(text: string): void { // iterate over each token via re_toks regex let m: RegExpMatchArray; - while (m = this.regex.exec(line)) { + this.lineno = 0; + while (m = this.ruleset.regex.exec(text)) { let found = false; + // find line # + while (m.index >= this.lineindex[this.lineno]) { + this.lineno++; + } // find out which capture group was matched, and thus token type - for (let i = 0; i < this.rules.length; i++) { + let rules = this.ruleset.rules; + for (let i = 0; i < rules.length; i++) { let s: string = m[i + 1]; if (s != null) { found = true; let loc = { path: this.path, line: this.lineno, start: m.index, end: m.index + s.length }; - let rule = this.rules[i]; + let rule = rules[i]; // add token to list switch (rule.type) { - case TokenType.CodeFragment: - // TODO: empty code fragment doesn't work - if (this.codeFragment != null) { - let codeLoc = mergeLocs(this.codeFragmentStart, loc); - this._pushToken({ str: this.codeFragment, type: rule.type, $loc: codeLoc }); - this.codeFragmentStart = null; - this.codeFragment = null; - } else { - loc.line++; - this.codeFragmentStart = loc; - this.codeFragment = ''; - return; // don't add any more tokens (TODO: check for trash?) - } - break; case TokenType.CatchAll: - if (this.errorOnCatchAll && this.codeFragment == null) { + if (this.errorOnCatchAll) { this.compileError(`I didn't expect the character "${m[0]}" here.`); } default: - if (this.codeFragment == null) { - this._pushToken({ str: s, type: rule.type, $loc: loc }); - } + this.pushToken({ str: s, type: rule.type, $loc: loc }); case TokenType.Comment: case TokenType.Ignore: break; @@ -133,14 +131,8 @@ export class Tokenizer { this.compileError(`Could not parse token: <<${m[0]}>>`) } } - if (this.includeEOL) { - this._pushToken(this.eol); - } - if (this.codeFragment != null) { - this.codeFragment += line + '\n'; - } } - _pushToken(token: Token) { + pushToken(token: Token) { this.tokens.push(token); } addError(msg: string, loc?: SourceLocation) { @@ -161,10 +153,10 @@ export class Tokenizer { } peekToken(lookahead?: number): Token { let tok = this.tokens[lookahead || 0]; - return tok ? tok : this.eol; + return tok ? tok : this.eof; } consumeToken(): Token { - let tok = this.lasttoken = (this.tokens.shift() || this.eol); + let tok = this.lasttoken = (this.tokens.shift() || this.eof); return tok; } expectToken(str: string, msg?: string): Token { diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 12ce31ac..fb8b2e63 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -293,7 +293,10 @@ function testCompiler() { let c = new ECSCompiler(); try { c.parseFile(` - + // comment + /* + mju,fjeqowfjqewiofjqe + */ component Kernel lines: 0..255 bgcolor: 0..255 diff --git a/src/test/testutil.ts b/src/test/testutil.ts index 9e340a69..a1f7a75c 100644 --- a/src/test/testutil.ts +++ b/src/test/testutil.ts @@ -6,166 +6,169 @@ import { lzgmini, isProbablyBinary } from "../common/util"; import { Tokenizer, TokenType } from "../common/tokenizer"; var NES_CONIO_ROM_LZG = [ - 76,90,71,0,0,160,16,0,0,11,158,107,131,223,83,1,9,17,21,22,78,69,83,26,2,1,3,0,22,6,120,216, - 162,0,134,112,134,114,134,113,134,115,154,169,32,157,0,2,157,0,3,157,0,4,232,208,244,32,134,130,32,85,129,169, - 0,162,8,133,2,134,3,32,93,128,32,50,129,32,73,129,76,0,128,72,152,72,138,72,169,1,133,112,230,107,208,2, - 230,108,32,232,129,169,32,141,6,32,169,0,22,129,141,5,22,66,104,170,104,168,104,64,160,0,240,7,169,105,162,128, - 76,4,96,96,162,0,21,23,0,32,22,195,1,22,194,63,21,37,21,134,22,197,41,21,27,173,41,96,201,4,32,169, - 129,240,3,76,158,128,76,188,128,169,184,162,130,24,109,41,96,144,1,232,160,0,32,130,129,141,7,21,36,238,41,96, - 21,32,76,140,128,21,47,33,21,246,201,17,14,61,15,21,253,227,128,76,1,129,169,169,17,24,61,209,21,125,17,2, - 180,17,10,130,5,22,201,128,17,4,172,30,141,1,32,76,46,129,22,65,96,173,0,96,174,1,96,32,112,130,173,2, - 96,174,3,21,65,160,4,76,105,128,17,3,228,188,162,130,17,2,228,169,188,133,10,169,130,133,11,169,0,133,12,169, - 96,133,13,162,214,169,255,133,18,160,0,232,240,13,177,10,145,12,200,208,246,230,11,230,13,208,240,230,18,208,239,96, - 133,10,134,11,162,0,177,10,96,208,42,162,0,138,96,240,36,22,163,30,48,28,22,227,2,16,20,22,227,14,144,12, - 21,200,176,4,22,226,162,0,169,1,96,165,115,208,252,96,169,255,197,115,240,252,96,133,118,132,116,134,117,32,193,129, - 164,113,165,116,153,0,2,165,117,153,0,3,165,118,153,0,4,200,132,113,230,115,96,164,115,208,1,96,166,114,169,14, - 141,42,96,189,0,2,141,6,32,189,0,3,22,163,4,141,7,32,232,136,240,93,17,19,14,71,17,19,14,49,17,19, - 14,27,17,19,14,5,206,42,96,208,141,134,114,132,115,96,169,0,162,0,72,165,2,56,233,2,133,2,176,2,198,3, - 160,1,138,145,2,104,136,145,2,96,169,41,133,10,169,96,17,34,41,168,162,0,240,10,145,10,200,208,251,230,11,202, - 208,246,192,2,240,5,21,70,247,96,78,111,32,99,97,114,116,32,108,111,97,100,101,100,0,1,0,16,32,17,66,184, - 141,18,96,142,19,96,141,25,96,142,26,96,136,185,255,255,141,35,22,196,34,96,140,37,96,32,255,255,160,255,208,232, - 96,17,71,230,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,10,53,128,0,128,92,128, - 17,14,14,204,204,51,51,22,106,0,24,60,126,24,22,1,22,231,16,48,127,127,48,16,0,22,230,12,18,48,124,48, - 98,252,22,231,0,0,3,62,118,54,54,22,231,127,127,17,4,80,22,230,224,224,96,22,3,22,230,24,24,24,248,248, - 21,16,22,230,204,153,51,102,22,106,51,153,204,22,107,21,27,255,255,17,4,67,22,227,3,22,13,17,6,188,22,230, - 17,2,172,22,13,31,31,22,236,255,255,22,236,31,31,17,4,136,22,227,22,1,248,248,21,5,22,233,17,14,123,17, - 3,64,22,230,17,3,64,21,248,17,8,29,21,216,17,6,88,17,3,64,22,230,240,22,13,21,233,21,243,22,230,17, - 6,16,22,226,192,192,48,48,22,106,15,22,1,21,84,22,230,17,10,4,22,226,17,10,52,22,230,17,6,16,17,10, - 44,22,6,17,35,220,0,24,22,231,102,102,17,34,107,0,22,233,255,22,33,102,22,231,24,62,96,60,6,124,21,40, - 22,229,0,102,12,24,48,102,70,22,231,60,102,60,56,103,102,63,22,231,6,12,17,36,59,22,230,21,30,48,48,24, - 12,22,231,22,97,12,21,4,22,231,0,102,60,255,60,17,2,115,22,230,24,24,126,17,35,70,22,230,17,4,173,21, - 33,22,231,126,21,205,22,231,21,80,22,232,3,6,12,24,48,96,22,231,60,102,110,118,102,102,60,22,231,24,24,56, - 24,24,24,126,22,231,60,102,6,12,48,96,22,235,28,6,21,168,22,228,6,14,30,102,127,6,6,22,231,126,96,124, - 6,21,80,22,230,60,102,96,124,17,4,88,22,228,126,102,12,17,35,83,22,230,60,21,13,21,216,22,231,62,21,240, - 22,228,17,34,124,22,66,22,236,17,2,224,22,228,14,24,48,96,48,24,14,0,22,230,17,2,239,17,4,241,22,228, - 112,24,12,6,12,24,112,22,231,17,2,192,24,21,52,22,232,110,110,96,98,17,3,248,22,227,24,60,102,126,17,34, - 228,22,230,124,102,102,22,66,22,231,60,102,96,96,96,17,4,200,22,227,120,108,21,30,108,120,22,231,126,96,96,120, - 96,96,126,22,237,96,22,231,21,48,110,17,37,8,22,227,21,46,17,3,96,22,230,60,17,99,19,21,24,22,229,30, - 12,22,1,108,56,22,231,102,108,120,112,120,108,21,40,22,229,17,132,62,126,22,231,99,119,127,107,99,99,99,22,231, - 102,118,126,126,110,17,2,88,22,229,60,102,22,2,17,35,88,22,227,17,2,205,21,49,22,231,21,144,60,14,22,231, - 21,80,17,2,96,22,230,60,102,96,60,17,37,208,22,227,17,163,13,17,34,200,22,229,21,111,17,5,208,22,232,60, - 17,5,16,22,225,99,99,99,107,127,119,99,22,231,21,77,60,17,3,248,22,230,21,1,17,4,64,22,227,126,17,67, - 159,126,22,231,60,48,22,2,60,22,231,96,48,24,12,6,3,0,22,231,60,17,34,32,12,21,24,22,229,17,34,193, - 17,68,244,22,229,22,3,17,165,133,22,225,17,134,203,22,230,21,58,6,62,102,62,22,232,96,17,66,176,124,22,232, - 0,60,96,96,96,17,66,144,22,229,6,21,31,21,96,22,230,0,60,102,126,21,216,22,228,14,24,62,17,3,84,22, - 230,0,21,95,6,124,22,231,17,3,80,102,17,5,88,22,225,24,0,56,17,34,240,22,231,6,0,6,22,1,60,22, - 231,96,96,108,17,34,128,22,231,21,30,21,160,22,230,0,102,127,127,107,99,22,233,17,2,79,21,32,22,231,17,34, - 210,17,4,152,22,228,17,36,242,22,232,17,3,144,6,22,232,124,17,66,226,21,160,22,228,17,131,225,22,232,17,130, - 127,17,98,112,22,230,17,35,226,17,34,0,22,233,60,17,2,240,22,230,99,107,127,62,17,226,24,22,230,17,35,241, - 22,234,21,47,12,120,22,232,126,12,24,48,17,98,194,22,228,28,48,24,112,24,48,28,22,231,17,164,159,22,3,22, - 227,56,12,24,14,24,12,56,0,22,230,51,255,204,17,35,206,22,230,22,14,17,194,92,22,10,17,236,246,204,204,255, - 231,195,129,231,22,1,22,231,239,207,128,128,207,239,255,22,230,243,237,207,131,207,157,3,22,231,255,255,252,193,137,201, - 201,22,231,128,128,17,4,80,22,230,31,31,159,22,3,22,230,231,231,231,7,7,21,16,22,230,17,236,246,204,17,237, - 246,51,153,17,227,11,17,4,67,22,227,252,22,13,17,6,188,22,230,17,2,172,22,13,224,224,22,236,0,0,22,236, - 224,224,17,4,136,22,227,22,1,7,7,21,5,22,233,17,14,123,17,3,64,22,230,17,3,64,21,248,17,8,29,21, - 216,17,6,88,17,3,64,22,230,17,226,124,22,10,17,238,244,22,226,17,6,16,22,226,63,63,207,207,22,106,17,226, - 192,21,84,22,230,17,10,4,17,230,220,17,14,60,17,234,252,17,6,44,22,6,17,35,220,255,231,22,231,153,153,17, - 34,107,255,22,233,0,22,33,153,22,231,231,193,159,195,249,131,21,40,22,229,255,153,243,231,207,153,185,22,231,195,153, - 195,199,152,153,192,22,231,249,243,17,36,59,22,230,21,30,207,207,231,243,22,231,22,97,243,21,4,22,231,255,153,195, - 0,195,17,2,115,22,230,231,231,129,17,35,70,22,230,17,4,173,21,33,22,231,129,21,205,22,231,21,80,22,232,252, - 249,243,231,207,159,22,231,195,153,145,137,153,153,195,22,231,231,231,199,231,231,231,129,22,231,195,153,249,243,207,159,22, - 235,227,249,21,168,22,228,249,241,225,153,128,249,249,22,231,129,159,131,249,21,80,22,230,195,153,159,131,17,4,88,22, - 228,129,153,243,17,35,83,22,230,195,21,13,21,216,22,231,193,21,240,22,228,17,34,124,22,66,22,236,17,2,224,22, - 228,241,231,207,159,207,231,241,255,22,230,17,2,239,17,4,241,22,228,143,231,243,249,243,231,143,22,231,17,2,192,231, - 21,52,22,232,145,145,159,157,17,3,248,22,227,231,195,153,129,17,34,228,22,230,131,153,153,22,66,22,231,195,153,159, - 159,159,17,4,200,22,227,135,147,21,30,147,135,22,231,129,159,159,135,159,159,129,22,237,159,22,231,21,48,145,17,37, - 8,22,227,21,46,17,3,96,22,230,195,17,99,19,21,24,22,229,225,243,22,1,147,199,22,231,153,147,135,143,135,147, - 21,40,22,229,17,132,62,129,22,231,156,136,128,148,156,156,156,22,231,153,137,129,129,145,17,2,88,22,229,195,153,22, - 2,17,35,88,22,227,17,2,205,21,49,22,231,21,144,195,241,22,231,21,80,17,2,96,22,230,195,153,159,195,17,37, - 208,22,227,17,163,13,17,34,200,22,229,21,111,17,5,208,22,232,195,17,5,16,22,225,156,156,156,148,128,136,156,22, - 231,21,77,195,17,3,248,22,230,21,1,17,4,64,22,227,129,17,67,159,129,22,231,195,207,22,2,195,22,231,159,207, - 231,243,249,252,255,22,231,195,17,34,32,243,21,24,22,229,17,34,193,17,68,244,22,229,22,3,17,165,133,22,225,17, - 134,203,22,230,21,58,249,193,153,193,22,232,159,17,66,176,131,22,232,255,195,159,159,159,17,66,144,22,229,249,21,31, - 21,96,22,230,255,195,153,129,21,216,22,228,241,231,193,17,3,84,22,230,255,21,95,249,131,22,231,17,3,80,153,17, - 5,88,22,225,231,255,199,17,34,240,22,231,249,255,249,22,1,195,22,231,159,159,147,17,34,128,22,231,21,30,21,160, - 22,230,255,153,128,128,148,156,22,233,17,2,79,21,32,22,231,17,34,210,17,4,152,22,228,17,36,242,22,232,17,3, - 144,249,22,232,131,17,66,226,21,160,22,228,17,131,225,22,232,17,130,127,17,98,112,22,230,17,35,226,17,34,0,22, - 233,195,17,2,240,22,230,156,148,128,193,17,226,24,22,230,17,35,241,22,234,21,47,243,135,22,232,129,243,231,207,17, - 98,194,22,228,227,207,231,143,231,207,227,22,231,17,164,159,22,3,22,227,199,243,231,241,231,243,199,255,22,230,204,0, - 51,17,35,206,22,230,22,14,9,19,0,13,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31,22,31, - 22,31,22,31,22,31,22,31,22,31,22,30,22,28 - ]; - - describe('LZG', function() { - it('Should decode LZG', function() { - var rom = new Uint8Array(new lzgmini().decode(NES_CONIO_ROM_LZG)); - assert.equal(40976, rom.length); - }); + 76, 90, 71, 0, 0, 160, 16, 0, 0, 11, 158, 107, 131, 223, 83, 1, 9, 17, 21, 22, 78, 69, 83, 26, 2, 1, 3, 0, 22, 6, 120, 216, + 162, 0, 134, 112, 134, 114, 134, 113, 134, 115, 154, 169, 32, 157, 0, 2, 157, 0, 3, 157, 0, 4, 232, 208, 244, 32, 134, 130, 32, 85, 129, 169, + 0, 162, 8, 133, 2, 134, 3, 32, 93, 128, 32, 50, 129, 32, 73, 129, 76, 0, 128, 72, 152, 72, 138, 72, 169, 1, 133, 112, 230, 107, 208, 2, + 230, 108, 32, 232, 129, 169, 32, 141, 6, 32, 169, 0, 22, 129, 141, 5, 22, 66, 104, 170, 104, 168, 104, 64, 160, 0, 240, 7, 169, 105, 162, 128, + 76, 4, 96, 96, 162, 0, 21, 23, 0, 32, 22, 195, 1, 22, 194, 63, 21, 37, 21, 134, 22, 197, 41, 21, 27, 173, 41, 96, 201, 4, 32, 169, + 129, 240, 3, 76, 158, 128, 76, 188, 128, 169, 184, 162, 130, 24, 109, 41, 96, 144, 1, 232, 160, 0, 32, 130, 129, 141, 7, 21, 36, 238, 41, 96, + 21, 32, 76, 140, 128, 21, 47, 33, 21, 246, 201, 17, 14, 61, 15, 21, 253, 227, 128, 76, 1, 129, 169, 169, 17, 24, 61, 209, 21, 125, 17, 2, + 180, 17, 10, 130, 5, 22, 201, 128, 17, 4, 172, 30, 141, 1, 32, 76, 46, 129, 22, 65, 96, 173, 0, 96, 174, 1, 96, 32, 112, 130, 173, 2, + 96, 174, 3, 21, 65, 160, 4, 76, 105, 128, 17, 3, 228, 188, 162, 130, 17, 2, 228, 169, 188, 133, 10, 169, 130, 133, 11, 169, 0, 133, 12, 169, + 96, 133, 13, 162, 214, 169, 255, 133, 18, 160, 0, 232, 240, 13, 177, 10, 145, 12, 200, 208, 246, 230, 11, 230, 13, 208, 240, 230, 18, 208, 239, 96, + 133, 10, 134, 11, 162, 0, 177, 10, 96, 208, 42, 162, 0, 138, 96, 240, 36, 22, 163, 30, 48, 28, 22, 227, 2, 16, 20, 22, 227, 14, 144, 12, + 21, 200, 176, 4, 22, 226, 162, 0, 169, 1, 96, 165, 115, 208, 252, 96, 169, 255, 197, 115, 240, 252, 96, 133, 118, 132, 116, 134, 117, 32, 193, 129, + 164, 113, 165, 116, 153, 0, 2, 165, 117, 153, 0, 3, 165, 118, 153, 0, 4, 200, 132, 113, 230, 115, 96, 164, 115, 208, 1, 96, 166, 114, 169, 14, + 141, 42, 96, 189, 0, 2, 141, 6, 32, 189, 0, 3, 22, 163, 4, 141, 7, 32, 232, 136, 240, 93, 17, 19, 14, 71, 17, 19, 14, 49, 17, 19, + 14, 27, 17, 19, 14, 5, 206, 42, 96, 208, 141, 134, 114, 132, 115, 96, 169, 0, 162, 0, 72, 165, 2, 56, 233, 2, 133, 2, 176, 2, 198, 3, + 160, 1, 138, 145, 2, 104, 136, 145, 2, 96, 169, 41, 133, 10, 169, 96, 17, 34, 41, 168, 162, 0, 240, 10, 145, 10, 200, 208, 251, 230, 11, 202, + 208, 246, 192, 2, 240, 5, 21, 70, 247, 96, 78, 111, 32, 99, 97, 114, 116, 32, 108, 111, 97, 100, 101, 100, 0, 1, 0, 16, 32, 17, 66, 184, + 141, 18, 96, 142, 19, 96, 141, 25, 96, 142, 26, 96, 136, 185, 255, 255, 141, 35, 22, 196, 34, 96, 140, 37, 96, 32, 255, 255, 160, 255, 208, 232, + 96, 17, 71, 230, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 10, 53, 128, 0, 128, 92, 128, + 17, 14, 14, 204, 204, 51, 51, 22, 106, 0, 24, 60, 126, 24, 22, 1, 22, 231, 16, 48, 127, 127, 48, 16, 0, 22, 230, 12, 18, 48, 124, 48, + 98, 252, 22, 231, 0, 0, 3, 62, 118, 54, 54, 22, 231, 127, 127, 17, 4, 80, 22, 230, 224, 224, 96, 22, 3, 22, 230, 24, 24, 24, 248, 248, + 21, 16, 22, 230, 204, 153, 51, 102, 22, 106, 51, 153, 204, 22, 107, 21, 27, 255, 255, 17, 4, 67, 22, 227, 3, 22, 13, 17, 6, 188, 22, 230, + 17, 2, 172, 22, 13, 31, 31, 22, 236, 255, 255, 22, 236, 31, 31, 17, 4, 136, 22, 227, 22, 1, 248, 248, 21, 5, 22, 233, 17, 14, 123, 17, + 3, 64, 22, 230, 17, 3, 64, 21, 248, 17, 8, 29, 21, 216, 17, 6, 88, 17, 3, 64, 22, 230, 240, 22, 13, 21, 233, 21, 243, 22, 230, 17, + 6, 16, 22, 226, 192, 192, 48, 48, 22, 106, 15, 22, 1, 21, 84, 22, 230, 17, 10, 4, 22, 226, 17, 10, 52, 22, 230, 17, 6, 16, 17, 10, + 44, 22, 6, 17, 35, 220, 0, 24, 22, 231, 102, 102, 17, 34, 107, 0, 22, 233, 255, 22, 33, 102, 22, 231, 24, 62, 96, 60, 6, 124, 21, 40, + 22, 229, 0, 102, 12, 24, 48, 102, 70, 22, 231, 60, 102, 60, 56, 103, 102, 63, 22, 231, 6, 12, 17, 36, 59, 22, 230, 21, 30, 48, 48, 24, + 12, 22, 231, 22, 97, 12, 21, 4, 22, 231, 0, 102, 60, 255, 60, 17, 2, 115, 22, 230, 24, 24, 126, 17, 35, 70, 22, 230, 17, 4, 173, 21, + 33, 22, 231, 126, 21, 205, 22, 231, 21, 80, 22, 232, 3, 6, 12, 24, 48, 96, 22, 231, 60, 102, 110, 118, 102, 102, 60, 22, 231, 24, 24, 56, + 24, 24, 24, 126, 22, 231, 60, 102, 6, 12, 48, 96, 22, 235, 28, 6, 21, 168, 22, 228, 6, 14, 30, 102, 127, 6, 6, 22, 231, 126, 96, 124, + 6, 21, 80, 22, 230, 60, 102, 96, 124, 17, 4, 88, 22, 228, 126, 102, 12, 17, 35, 83, 22, 230, 60, 21, 13, 21, 216, 22, 231, 62, 21, 240, + 22, 228, 17, 34, 124, 22, 66, 22, 236, 17, 2, 224, 22, 228, 14, 24, 48, 96, 48, 24, 14, 0, 22, 230, 17, 2, 239, 17, 4, 241, 22, 228, + 112, 24, 12, 6, 12, 24, 112, 22, 231, 17, 2, 192, 24, 21, 52, 22, 232, 110, 110, 96, 98, 17, 3, 248, 22, 227, 24, 60, 102, 126, 17, 34, + 228, 22, 230, 124, 102, 102, 22, 66, 22, 231, 60, 102, 96, 96, 96, 17, 4, 200, 22, 227, 120, 108, 21, 30, 108, 120, 22, 231, 126, 96, 96, 120, + 96, 96, 126, 22, 237, 96, 22, 231, 21, 48, 110, 17, 37, 8, 22, 227, 21, 46, 17, 3, 96, 22, 230, 60, 17, 99, 19, 21, 24, 22, 229, 30, + 12, 22, 1, 108, 56, 22, 231, 102, 108, 120, 112, 120, 108, 21, 40, 22, 229, 17, 132, 62, 126, 22, 231, 99, 119, 127, 107, 99, 99, 99, 22, 231, + 102, 118, 126, 126, 110, 17, 2, 88, 22, 229, 60, 102, 22, 2, 17, 35, 88, 22, 227, 17, 2, 205, 21, 49, 22, 231, 21, 144, 60, 14, 22, 231, + 21, 80, 17, 2, 96, 22, 230, 60, 102, 96, 60, 17, 37, 208, 22, 227, 17, 163, 13, 17, 34, 200, 22, 229, 21, 111, 17, 5, 208, 22, 232, 60, + 17, 5, 16, 22, 225, 99, 99, 99, 107, 127, 119, 99, 22, 231, 21, 77, 60, 17, 3, 248, 22, 230, 21, 1, 17, 4, 64, 22, 227, 126, 17, 67, + 159, 126, 22, 231, 60, 48, 22, 2, 60, 22, 231, 96, 48, 24, 12, 6, 3, 0, 22, 231, 60, 17, 34, 32, 12, 21, 24, 22, 229, 17, 34, 193, + 17, 68, 244, 22, 229, 22, 3, 17, 165, 133, 22, 225, 17, 134, 203, 22, 230, 21, 58, 6, 62, 102, 62, 22, 232, 96, 17, 66, 176, 124, 22, 232, + 0, 60, 96, 96, 96, 17, 66, 144, 22, 229, 6, 21, 31, 21, 96, 22, 230, 0, 60, 102, 126, 21, 216, 22, 228, 14, 24, 62, 17, 3, 84, 22, + 230, 0, 21, 95, 6, 124, 22, 231, 17, 3, 80, 102, 17, 5, 88, 22, 225, 24, 0, 56, 17, 34, 240, 22, 231, 6, 0, 6, 22, 1, 60, 22, + 231, 96, 96, 108, 17, 34, 128, 22, 231, 21, 30, 21, 160, 22, 230, 0, 102, 127, 127, 107, 99, 22, 233, 17, 2, 79, 21, 32, 22, 231, 17, 34, + 210, 17, 4, 152, 22, 228, 17, 36, 242, 22, 232, 17, 3, 144, 6, 22, 232, 124, 17, 66, 226, 21, 160, 22, 228, 17, 131, 225, 22, 232, 17, 130, + 127, 17, 98, 112, 22, 230, 17, 35, 226, 17, 34, 0, 22, 233, 60, 17, 2, 240, 22, 230, 99, 107, 127, 62, 17, 226, 24, 22, 230, 17, 35, 241, + 22, 234, 21, 47, 12, 120, 22, 232, 126, 12, 24, 48, 17, 98, 194, 22, 228, 28, 48, 24, 112, 24, 48, 28, 22, 231, 17, 164, 159, 22, 3, 22, + 227, 56, 12, 24, 14, 24, 12, 56, 0, 22, 230, 51, 255, 204, 17, 35, 206, 22, 230, 22, 14, 17, 194, 92, 22, 10, 17, 236, 246, 204, 204, 255, + 231, 195, 129, 231, 22, 1, 22, 231, 239, 207, 128, 128, 207, 239, 255, 22, 230, 243, 237, 207, 131, 207, 157, 3, 22, 231, 255, 255, 252, 193, 137, 201, + 201, 22, 231, 128, 128, 17, 4, 80, 22, 230, 31, 31, 159, 22, 3, 22, 230, 231, 231, 231, 7, 7, 21, 16, 22, 230, 17, 236, 246, 204, 17, 237, + 246, 51, 153, 17, 227, 11, 17, 4, 67, 22, 227, 252, 22, 13, 17, 6, 188, 22, 230, 17, 2, 172, 22, 13, 224, 224, 22, 236, 0, 0, 22, 236, + 224, 224, 17, 4, 136, 22, 227, 22, 1, 7, 7, 21, 5, 22, 233, 17, 14, 123, 17, 3, 64, 22, 230, 17, 3, 64, 21, 248, 17, 8, 29, 21, + 216, 17, 6, 88, 17, 3, 64, 22, 230, 17, 226, 124, 22, 10, 17, 238, 244, 22, 226, 17, 6, 16, 22, 226, 63, 63, 207, 207, 22, 106, 17, 226, + 192, 21, 84, 22, 230, 17, 10, 4, 17, 230, 220, 17, 14, 60, 17, 234, 252, 17, 6, 44, 22, 6, 17, 35, 220, 255, 231, 22, 231, 153, 153, 17, + 34, 107, 255, 22, 233, 0, 22, 33, 153, 22, 231, 231, 193, 159, 195, 249, 131, 21, 40, 22, 229, 255, 153, 243, 231, 207, 153, 185, 22, 231, 195, 153, + 195, 199, 152, 153, 192, 22, 231, 249, 243, 17, 36, 59, 22, 230, 21, 30, 207, 207, 231, 243, 22, 231, 22, 97, 243, 21, 4, 22, 231, 255, 153, 195, + 0, 195, 17, 2, 115, 22, 230, 231, 231, 129, 17, 35, 70, 22, 230, 17, 4, 173, 21, 33, 22, 231, 129, 21, 205, 22, 231, 21, 80, 22, 232, 252, + 249, 243, 231, 207, 159, 22, 231, 195, 153, 145, 137, 153, 153, 195, 22, 231, 231, 231, 199, 231, 231, 231, 129, 22, 231, 195, 153, 249, 243, 207, 159, 22, + 235, 227, 249, 21, 168, 22, 228, 249, 241, 225, 153, 128, 249, 249, 22, 231, 129, 159, 131, 249, 21, 80, 22, 230, 195, 153, 159, 131, 17, 4, 88, 22, + 228, 129, 153, 243, 17, 35, 83, 22, 230, 195, 21, 13, 21, 216, 22, 231, 193, 21, 240, 22, 228, 17, 34, 124, 22, 66, 22, 236, 17, 2, 224, 22, + 228, 241, 231, 207, 159, 207, 231, 241, 255, 22, 230, 17, 2, 239, 17, 4, 241, 22, 228, 143, 231, 243, 249, 243, 231, 143, 22, 231, 17, 2, 192, 231, + 21, 52, 22, 232, 145, 145, 159, 157, 17, 3, 248, 22, 227, 231, 195, 153, 129, 17, 34, 228, 22, 230, 131, 153, 153, 22, 66, 22, 231, 195, 153, 159, + 159, 159, 17, 4, 200, 22, 227, 135, 147, 21, 30, 147, 135, 22, 231, 129, 159, 159, 135, 159, 159, 129, 22, 237, 159, 22, 231, 21, 48, 145, 17, 37, + 8, 22, 227, 21, 46, 17, 3, 96, 22, 230, 195, 17, 99, 19, 21, 24, 22, 229, 225, 243, 22, 1, 147, 199, 22, 231, 153, 147, 135, 143, 135, 147, + 21, 40, 22, 229, 17, 132, 62, 129, 22, 231, 156, 136, 128, 148, 156, 156, 156, 22, 231, 153, 137, 129, 129, 145, 17, 2, 88, 22, 229, 195, 153, 22, + 2, 17, 35, 88, 22, 227, 17, 2, 205, 21, 49, 22, 231, 21, 144, 195, 241, 22, 231, 21, 80, 17, 2, 96, 22, 230, 195, 153, 159, 195, 17, 37, + 208, 22, 227, 17, 163, 13, 17, 34, 200, 22, 229, 21, 111, 17, 5, 208, 22, 232, 195, 17, 5, 16, 22, 225, 156, 156, 156, 148, 128, 136, 156, 22, + 231, 21, 77, 195, 17, 3, 248, 22, 230, 21, 1, 17, 4, 64, 22, 227, 129, 17, 67, 159, 129, 22, 231, 195, 207, 22, 2, 195, 22, 231, 159, 207, + 231, 243, 249, 252, 255, 22, 231, 195, 17, 34, 32, 243, 21, 24, 22, 229, 17, 34, 193, 17, 68, 244, 22, 229, 22, 3, 17, 165, 133, 22, 225, 17, + 134, 203, 22, 230, 21, 58, 249, 193, 153, 193, 22, 232, 159, 17, 66, 176, 131, 22, 232, 255, 195, 159, 159, 159, 17, 66, 144, 22, 229, 249, 21, 31, + 21, 96, 22, 230, 255, 195, 153, 129, 21, 216, 22, 228, 241, 231, 193, 17, 3, 84, 22, 230, 255, 21, 95, 249, 131, 22, 231, 17, 3, 80, 153, 17, + 5, 88, 22, 225, 231, 255, 199, 17, 34, 240, 22, 231, 249, 255, 249, 22, 1, 195, 22, 231, 159, 159, 147, 17, 34, 128, 22, 231, 21, 30, 21, 160, + 22, 230, 255, 153, 128, 128, 148, 156, 22, 233, 17, 2, 79, 21, 32, 22, 231, 17, 34, 210, 17, 4, 152, 22, 228, 17, 36, 242, 22, 232, 17, 3, + 144, 249, 22, 232, 131, 17, 66, 226, 21, 160, 22, 228, 17, 131, 225, 22, 232, 17, 130, 127, 17, 98, 112, 22, 230, 17, 35, 226, 17, 34, 0, 22, + 233, 195, 17, 2, 240, 22, 230, 156, 148, 128, 193, 17, 226, 24, 22, 230, 17, 35, 241, 22, 234, 21, 47, 243, 135, 22, 232, 129, 243, 231, 207, 17, + 98, 194, 22, 228, 227, 207, 231, 143, 231, 207, 227, 22, 231, 17, 164, 159, 22, 3, 22, 227, 199, 243, 231, 241, 231, 243, 199, 255, 22, 230, 204, 0, + 51, 17, 35, 206, 22, 230, 22, 14, 9, 19, 0, 13, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, + 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 30, 22, 28 +]; + +describe('LZG', function () { + it('Should decode LZG', function () { + var rom = new Uint8Array(new lzgmini().decode(NES_CONIO_ROM_LZG)); + assert.equal(40976, rom.length); + }); +}); + +describe('string functions', function () { + it('Should detect binary', function () { + assert.ok(!isProbablyBinary(null, [32, 32, 10, 13, 9, 32, 32, 10, 13])); + assert.ok(isProbablyBinary(null, [32, 32, 0x80])); + assert.ok(!isProbablyBinary(null, [32, 32, 0xc1, 0x81, 32, 32, 10, 13])); + assert.ok(isProbablyBinary(null, NES_CONIO_ROM_LZG)); + assert.ok(isProbablyBinary('test.bin')); + assert.ok(isProbablyBinary('test.chr')); + assert.ok(!isProbablyBinary('test.txt')); + assert.ok(isProbablyBinary('test.dat')); + assert.ok(!isProbablyBinary(null, [0x20, 0xa9, 0x20, 0x31, 0x39, 0x38, 0x32])); + assert.ok(isProbablyBinary(null, [0x00, 0x00])); // 00 is binary + assert.ok(isProbablyBinary(null, [0x9f])); // 9f is binary + assert.ok(isProbablyBinary(null, [0xff])); // FF is binary + assert.ok(isProbablyBinary(null, [0xf0, 0x12])); // ran out of data + }); +}); + +describe('EmuHalt', function () { + it('Should detect emuhalt', function () { + var e = new EmuHalt("?"); + assert.ok(e instanceof EmuHalt); + assert.ok(e.hasOwnProperty('$loc')); + }); +}); + +describe('Tokenizer', function () { + it('Should tokenize', function () { + var t = new Tokenizer(); + t.setTokenRules([ + { type: 'ident', regex: /[$A-Za-z_][A-Za-z0-9_-]*/ }, + { type: 'delim', regex: /[\(\)\{\}\[\]]/ }, + { type: 'qstring', regex: /".*?"/ }, + { type: 'integer', regex: /[-]?\d+/ }, + { type: 'ignore', regex: /\s+/ }, + ]); + t.tokenizeFile("a\n{\"key\" value\n \"number\" 531\n\n \"f\" (fn [x] (+ x 2))}\n", "test.file"); + assert.strictEqual(t.tokens.map(t => t.type).join(' '), + 'ident delim qstring ident qstring integer qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eof'); + assert.strictEqual(t.tokens.map(t => t.str).join(' '), + 'a { "key" value "number" 531 "f" ( fn [ x ] ( + x 2 ) ) } '); + assert.strictEqual(t.tokens.map(t => t.$loc.line).join(' '), + '1 2 2 2 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 6'); + assert.strictEqual(20, t.tokens.length); + assert.strictEqual('a', t.peekToken().str); + assert.strictEqual('a', t.expectToken('a').str); + t.expectTokens(['foo', '{']); + t.pushbackToken(t.consumeToken()); + assert.strictEqual('"key"', t.consumeToken().str); + assert.deepStrictEqual({ 'value': true }, t.parseModifiers(['foo', 'value', 'bar'])); + assert.deepStrictEqual([], t.errors); + t = new Tokenizer(); + t.setTokenRules([ + { type: 'ident', regex: /[$A-Za-z_][A-Za-z0-9_-]*/ }, + { type: 'delim', regex: /[\(\)\{\}\[\]]/ }, + { type: 'code-fragment', regex: /---[^-]*---/ }, + { type: 'ignore', regex: /\s+/ }, + ]); + t.tokenizeFile("key value ---\nthis is\na fragment\n--- foo", "test.file"); + assert.strictEqual(t.tokens.map(t => t.type).join(' '), + 'ident ident code-fragment ident eof'); }); - - describe('string functions', function() { - it('Should detect binary', function() { - assert.ok(!isProbablyBinary(null, [32,32,10,13,9,32,32,10,13])); - assert.ok(isProbablyBinary(null, [32,32,0x80])); - assert.ok(!isProbablyBinary(null, [32,32,0xc1,0x81,32,32,10,13])); - assert.ok(isProbablyBinary(null, NES_CONIO_ROM_LZG)); - assert.ok(isProbablyBinary('test.bin')); - assert.ok(isProbablyBinary('test.chr')); - assert.ok(!isProbablyBinary('test.txt')); - assert.ok(isProbablyBinary('test.dat')); - assert.ok(!isProbablyBinary(null, [0x20,0xa9,0x20,0x31,0x39,0x38,0x32])); - assert.ok(isProbablyBinary(null, [0x00,0x00])); // 00 is binary - assert.ok(isProbablyBinary(null, [0x9f])); // 9f is binary - assert.ok(isProbablyBinary(null, [0xff])); // FF is binary - assert.ok(isProbablyBinary(null, [0xf0,0x12])); // ran out of data - }); - }); - - describe('EmuHalt', function() { - it('Should detect emuhalt', function() { - var e = new EmuHalt("?"); - assert.ok(e instanceof EmuHalt); - assert.ok(e.hasOwnProperty('$loc')); - }); - }); - -describe('Tokenizer', function() { - it('Should tokenize', function() { - var t = new Tokenizer(); - t.setTokenRules([ - { type: 'ident', regex: /[$A-Za-z_][A-Za-z0-9_-]*/ }, - { type: 'delim', regex: /[\(\)\{\}\[\]]/ }, - { type: 'qstring', regex: /".*?"/ }, - { type: 'integer', regex: /[-]?\d+/ }, - { type: 'ignore', regex: /\s+/ }, - { type: TokenType.CodeFragment, regex: /---/ }, - ]); - t.tokenizeFile("\n{\"key\" value\n \"number\" 531\n \"f\" (fn [x] (+ x 2))}\n", "test.file"); - assert.strictEqual(t.tokens.map(t => t.type).join(' '), - 'delim qstring ident qstring integer qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eof'); - assert.strictEqual(t.tokens.map(t => t.str).join(' '), - '{ "key" value "number" 531 "f" ( fn [ x ] ( + x 2 ) ) } '); - assert.strictEqual(19, t.tokens.length); - assert.strictEqual('{', t.peekToken().str); - assert.strictEqual('{', t.expectToken('{').str); - t.pushbackToken(t.consumeToken()); - assert.strictEqual('"key"', t.consumeToken().str); - assert.deepStrictEqual({'value':true}, t.parseModifiers(['foo','value','bar'])); - assert.deepStrictEqual([], t.errors); - t.includeEOL = true; - t.tokenizeFile("\n{\"key\" value\n \"number\" 531\n \"f\" (fn [x] (+ x 2))}\n", "test.file"); - assert.strictEqual(24, t.tokens.length); - assert.strictEqual(t.tokens.map(t => t.type).join(' '), - 'eol delim qstring ident eol qstring integer eol qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eol eol eof'); - t.includeEOL = false; - t.tokenizeFile("key value ---\nthis is\na fragment\n--- foo", "test.file"); - assert.strictEqual(t.tokens.map(t => t.type).join(' '), - 'ident ident code-fragment ident eof'); - }); }); From 7759cc52f144cd30266f8fabed5824ce72ef4c50 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 1 Feb 2022 10:38:54 -0600 Subject: [PATCH 028/104] ecs: can use {{cmd args...}} format --- src/common/ecs/ecs.ts | 92 ++++++++++++++++++++++++------------------- src/test/testecs.ts | 14 +++---- 2 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index fb15f8b2..0cd3739a 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -38,6 +38,13 @@ 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? */ @@ -383,12 +390,16 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { class ActionEval { em; dialect; + atypes; + entities; constructor( readonly scope: EntityScope, readonly sys: System, readonly action: Action) { this.em = scope.em; this.dialect = scope.em.dialect; + this.atypes = this.em.archetypesMatching(action.query); + this.entities = this.scope.entitiesMatching(this.atypes); } codeToString(): string { const tag_re = /\{\{(.+?)\}\}/g; @@ -397,78 +408,79 @@ class ActionEval { let action = this.action; let sys = this.sys; let code = action.text; - let label = `${sys.name}__${action.event}`; // TODO: better label that won't conflict (seq?) - let atypes = this.em.archetypesMatching(action.query); - let entities = this.scope.entitiesMatching(atypes); // TODO: detect cycles // TODO: "source"? // TODO: what if only 1 item? let props: { [name: string]: string } = {}; if (action.select == 'foreach') { - code = this.wrapCodeInLoop(code, action, entities); + code = this.wrapCodeInLoop(code, action, this.entities); } if (action.select == 'join' && action.join) { let jtypes = this.em.archetypesMatching(action.join); let jentities = this.scope.entitiesMatching(jtypes); if (jentities.length == 0) throw new ECSError(`join query for ${label} doesn't match any entities`, action.join); // TODO - let joinfield = this.getJoinField(action, atypes, jtypes); + let joinfield = this.getJoinField(action, this.atypes, jtypes); // TODO: what if only 1 item? // TODO: should be able to access fields via Y reg - code = this.wrapCodeInLoop(code, action, entities, joinfield); - atypes = jtypes; - entities = jentities; + code = this.wrapCodeInLoop(code, action, this.entities, joinfield); + this.atypes = jtypes; + this.entities = jentities; props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); } - props['%efullcount'] = entities.length.toString(); + props['%efullcount'] = this.entities.length.toString(); if (action.limit) { - entities = entities.slice(0, action.limit); + this.entities = this.entities.slice(0, action.limit); } - if (entities.length == 0) + if (this.entities.length == 0) throw new ECSError(`query for ${label} doesn't match any entities`, action.query); // TODO // define properties - props['%elo'] = entities[0].id.toString(); - props['%ehi'] = entities[entities.length - 1].id.toString(); - props['%ecount'] = entities.length.toString(); + props['%elo'] = this.entities[0].id.toString(); + props['%ehi'] = this.entities[this.entities.length - 1].id.toString(); + props['%ecount'] = this.entities.length.toString(); // replace @labels code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); // replace {{...}} tags code = code.replace(tag_re, (entire, group: string) => { + let toks = group.split(/\s+/); + if (toks.length == 0) throw new ECSError(`empty command`, action); let cmd = group.charAt(0); let rest = group.substring(1); switch (cmd) { - case '!': // emit event - return this.scope.generateCodeForEvent(rest); - case '.': // auto label - case '@': // auto label - return `${label}_${rest}`; - case '$': // temp byte (TODO: check to make sure not overflowing) - let tempinc = parseInt(rest); - if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, action); - if (!sys.tempbytes) throw new ECSError(`this system has no locals`, action); - if (tempinc < 0 || tempinc >= sys.tempbytes) throw new ECSError(`this system only has ${sys.tempbytes} locals`, action); - return `TEMP+${this.scope.tempOffset}+${tempinc}`; - case '=': - // TODO? - case '<': // low byte - return this.generateCodeForField(sys, action, atypes, entities, rest, 0); - case '>': // high byte - return this.generateCodeForField(sys, action, atypes, entities, rest, 8); - case '(': // higher byte (TODO) - return this.generateCodeForField(sys, action, atypes, entities, rest, 16); - case ')': // higher byte (TODO) - return this.generateCodeForField(sys, action, atypes, entities, rest, 24); - case '^': // resource reference - return this.scope.includeResource(rest); + case '!': return this.__emit([rest]); + case '$': return this.__local([rest]); + case '^': return this.__use([rest]); + case '<': return this.__byte([rest, '0']); + case '>': return this.__byte([rest, '8']); default: - let value = props[group]; + let value = props[toks[0]]; if (value) return value; - else throw new ECSError(`unrecognized command {{${group}}} in ${entire}`); + let fn = (this as any)['__' + toks[0]]; + if (fn) return fn.bind(this)(toks.slice(1)); + throw new ECSError(`unrecognized command {{${toks[0]}}}`, action); } }); return code; } + __byte(args: string[]) { + let fieldName = args[0]; + let bitofs = parseInt(args[1] || '0'); + return this.generateCodeForField(this.sys, this.action, this.atypes, this.entities, fieldName, bitofs); + } + __use(args: string[]) { + return this.scope.includeResource(args[0]); + } + __emit(args: string[]) { + return this.scope.generateCodeForEvent(args[0]); + } + __local(args: string[]) { + let tempinc = parseInt(args[0]); + if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, this.action); + if (!this.sys.tempbytes) throw new ECSError(`this system has no locals`, this.action); + if (tempinc < 0 || tempinc >= this.sys.tempbytes) throw new ECSError(`this system only has ${this.sys.tempbytes} locals`, this.action); + return `TEMP+${this.scope.tempOffset}+${tempinc}`; + } wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { // TODO: check ents // TODO: check segment bounds @@ -523,7 +535,7 @@ class ActionEval { return this.dialect.absolute(ident); } else if (action.select == 'once') { if (entities.length != 1) - throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`); + throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`, action); return this.dialect.absolute(ident); } else { // TODO: right direction? diff --git a/src/test/testecs.ts b/src/test/testecs.ts index fb8b2e63..1f40ac45 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -5,7 +5,7 @@ import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs const TEMPLATE1 = ` @NextFrame: FRAME_START - {{!preframe}} + {{emit preframe}} KERNEL_START {{!kernel}} KERNEL_END @@ -24,7 +24,7 @@ const TEMPLATE4_S1 = ` lda #192 ; TODO: numlines sec sbc ypos_ypos_b0+ent - sta {{$5}}+ofs + sta {{local 5}}+ofs ldy hasbitmap_bitmap_b0+ent lda bitmap_bitmapdata_b0,y @@ -59,9 +59,9 @@ const TEMPLATE4_S2 = ` // https://atariage.com/forums/topic/129683-advice-on-a-masking-kernel/ // https://atariage.com/forums/topic/128147-having-trouble-with-2-free-floating-player-graphics/?tab=comments#comment-1547059 const TEMPLATE4_K = ` - lda {{ Date: Wed, 2 Feb 2022 11:34:55 -0600 Subject: [PATCH 029/104] ecs: refactor, QueryResult, filter --- src/common/ecs/compiler.ts | 14 ++- src/common/ecs/ecs.ts | 234 +++++++++++++++++++++++++++++-------- src/test/testecs.ts | 32 ++--- 3 files changed, 211 insertions(+), 69 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 61407a10..e456a332 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -21,12 +21,12 @@ export class ECSCompiler extends Tokenizer { //this.includeEOL = true; this.setTokenRules([ { type: ECSTokenType.Ellipsis, regex: /\.\./ }, - { type: ECSTokenType.Operator, regex: /[#=,:(){}\[\]]/ }, { type: ECSTokenType.QuotedString, regex: /".*?"/ }, { type: ECSTokenType.CodeFragment, regex: /---.*?---/ }, { type: ECSTokenType.Integer, regex: /[-]?0x[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, + { type: ECSTokenType.Operator, regex: /[#=,:(){}\[\]\-]/ }, { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, { type: TokenType.Ignore, regex: /\/\/.*?[\n\r]/ }, { type: TokenType.Ignore, regex: /\/\*.*?\*\// }, @@ -218,10 +218,16 @@ export class ECSCompiler extends Tokenizer { parseQuery() { let q: Query = { include: [] }; let start = this.expectToken('['); - q.include = this.parseList(this.parseComponentRef, ',').map(c => c.name); + q.include = this.parseList(this.parseComponentRef, ','); + this.expectToken(']'); + if (this.peekToken().str == '-') { + this.consumeToken(); + this.expectToken('['); + q.exclude = this.parseList(this.parseComponentRef, ','); + this.expectToken(']'); + } // TODO: other params - let end = this.expectToken(']'); - q.$loc = mergeLocs(start.$loc, end.$loc); + q.$loc = mergeLocs(start.$loc, this.lasttoken.$loc); return q; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 0cd3739a..db1aa0c7 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1,4 +1,5 @@ /* + entity scopes contain entities, and are nested also contain segments (code, bss, rodata) components and systems are global @@ -46,6 +47,12 @@ https://www.cpcwiki.eu/forum/programming/trying-not-to-use-ix/msg133416/#msg1334 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? + +crazy idea -- full expansion, then relooper + */ @@ -92,10 +99,10 @@ export interface ComponentType extends SourceLocated { } export interface Query extends SourceLocated { - include: string[]; // TODO: make ComponentType - listen?: string[]; - exclude?: string[]; - updates?: string[]; + include: ComponentType[]; // TODO: make ComponentType + listen?: ComponentType[]; + exclude?: ComponentType[]; + updates?: ComponentType[]; } export interface System extends SourceLocated { @@ -191,6 +198,29 @@ export class Dialect_CA65 { @__exit: `; +// TODO: lo/hi side of range? + readonly ASM_FILTER_RANGE_X = ` + cpx #{{%xofs}} + bcc @__skip + cpx #{{%xofs}}+{{%ecount}} + bcs @__skip + {{%code}} +@__skip: +` + +// TODO + readonly ASM_MAP_RANGES = ` + txa + pha + lda {{%mapping}},x + bmi @__mapskip + tax + {{%code}} +@__mapskip: + pla + tax +`; + readonly INIT_FROM_ARRAY = ` ldy #{{%nbytes}} : lda {{%src}}-1,y @@ -218,8 +248,16 @@ Start: absolute(ident: string) { return ident; } - indexed_x(ident: string) { - return ident + ',x'; + addOffset(ident: string, offset: number) { + if (offset > 0) return `${ident}+${offset}`; + if (offset < 0) return `${ident}-${-offset}`; + return ident; + } + indexed_x(ident: string, offset: number) { + return this.addOffset(ident, offset) + ',x'; + } + indexed_y(ident: string, offset: number) { + return this.addOffset(ident, offset) + ',y'; } fieldsymbol(component: ComponentType, field: DataField, bitofs: number) { return `${component.name}_${field.name}_b${bitofs}`; @@ -387,19 +425,92 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { return 0; } +class QueryResult { + atypes: ArchetypeMatch[]; + entities: Entity[]; + scope; + + constructor(scope: EntityScope, query?: Query, a?: ArchetypeMatch[], e?: Entity[]) { + this.scope = scope; + if (query) { + this.atypes = scope.em.archetypesMatching(query); + this.entities = scope.entitiesMatching(this.atypes); + } else if (a && e) { + this.atypes = a; + this.entities = e; + } + } + contains(c: ComponentType, f: DataField, where: SourceLocated) { + // TODO: action for error msg + return this.scope.em.singleComponentWithFieldName(this.atypes, f.name, where); + } + intersection(qr: QueryResult) { + let ents = this.entities.filter(e => qr.entities.includes(e)); + let atypes = this.atypes.filter(a1 => qr.atypes.find(a2 => a2.etype == a1.etype)); + return new QueryResult(this.scope, undefined, atypes, ents); + } + isContiguous() { + if (this.entities.length == 0) return true; + let id = this.entities[0].id; + for (let i=1; i `${label}__${a}`); // replace {{...}} tags @@ -466,7 +583,7 @@ class ActionEval { __byte(args: string[]) { let fieldName = args[0]; let bitofs = parseInt(args[1] || '0'); - return this.generateCodeForField(this.sys, this.action, this.atypes, this.entities, fieldName, bitofs); + return this.generateCodeForField(this.sys, this.action, this.qr, fieldName, bitofs); } __use(args: string[]) { return this.scope.includeResource(args[0]); @@ -490,8 +607,13 @@ class ActionEval { s = s.replace('{{%code}}', code); return s; } - generateCodeForField(sys: System, action: Action, - atypes: ArchetypeMatch[], entities: Entity[], + wrapCodeInFilter(code: string) { + // TODO: what if not needed? + let s = this.dialect.ASM_FILTER_RANGE_X; + s = s.replace('{{%code}}', code); + return s; + } + generateCodeForField(sys: System, action: Action, qr: QueryResult, fieldName: string, bitofs: number): string { var component: ComponentType; @@ -504,14 +626,15 @@ class ActionEval { qualified = true; if (component == null) throw new ECSError(`no component named "${cname}"`) } else { - component = this.em.singleComponentWithFieldName(atypes, fieldName, `${sys.name}:${action.event}`); + component = this.em.singleComponentWithFieldName(qr.atypes, fieldName, action); } // find archetypes let field = component.fields.find(f => f.name == fieldName); if (field == null) throw new ECSError(`no field named "${fieldName}" in component`) // see if all entities have the same constant value + // TODO: should be done somewhere else? let constValues = new Set(); - for (let e of entities) { + for (let e of qr.entities) { let constVal = e.consts[mksymbol(component, fieldName)]; constValues.add(constVal); // constVal === undefined is allowed } @@ -528,23 +651,34 @@ class ActionEval { // TODO: don't mix const and init data let range = this.scope.bss.getFieldRange(component, fieldName) || this.scope.rodata.getFieldRange(component, fieldName); if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO - let eidofs = range.elo - entities[0].id; + let eidofs = range.elo - qr.entities[0].id; // TODO // TODO: dialect let ident = this.dialect.fieldsymbol(component, field, bitofs); if (qualified) { return this.dialect.absolute(ident); } else if (action.select == 'once') { - if (entities.length != 1) + if (qr.entities.length != 1) throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`, action); return this.dialect.absolute(ident); } else { - // TODO: right direction? - if (eidofs > 0) { - ident += '+' + eidofs; - } else if (eidofs < 0) { - ident += '' + eidofs; + // TODO: eidofs? + let ir; + if (this.scope.state.x?.intersection(this.qr)) { + ir = this.scope.state.x; + eidofs -= this.scope.state.xofs; } - return this.dialect.indexed_x(ident); + else if (this.scope.state.y?.intersection(this.qr)) { + ir = this.scope.state.y; + eidofs -= this.scope.state.yofs; + } + if (!ir) throw new ECSError(`no intersection for index register`, action); + if (ir.entities.length == 0) throw new ECSError(`no common entities for index register`, action); + if (!ir.isContiguous()) throw new ECSError(`entities in query are not contiguous`, action); + if (ir == this.scope.state.x) + return this.dialect.indexed_x(ident, eidofs); + if (ir == this.scope.state.y) + return this.dialect.indexed_y(ident, eidofs); + throw new ECSError(`cannot find "${component.name}:${field.name}" in state`, action); } } getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]): ComponentFieldPair { @@ -580,6 +714,7 @@ export class EntityScope implements SourceLocated { tempSize = 0; maxTempBytes = 0; resources = new Set(); + state = new ActionState(); constructor( public readonly em: EntityManager, @@ -719,12 +854,12 @@ export class EntityScope implements SourceLocated { // TODO: } else if (v instanceof Uint16Array) { } else if (typeof v === 'number') { // more than 1 entity, add an array - if (range.ehi > range.elo) { + if (entcount > 1) { if (!range.access) throw new ECSError(`no access for field ${cfname}`) - let base = segment.allocateBytes(range.access[0].symbol, range.ehi - range.elo + 1); for (let a of range.access) { + segment.allocateBytes(a.symbol, entcount); let ofs = segment.getByteOffset(range, a, e.id); - segment.initdata[ofs] = v; + segment.initdata[ofs] = (v >> a.bit) & 0xff; } } // TODO: what if mix of var, const, and init values? @@ -775,14 +910,14 @@ export class EntityScope implements SourceLocated { return code; } setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, "setConstValue"); + let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); e.consts[mksymbol(component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'init') throw new ECSError(`Can't mix const and init values for a component field`, e); this.em.symbols[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, "setInitValue"); + let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); e.inits[mkscopesymbol(this, component, fieldName)] = value; if (this.em.symbols[mksymbol(component, fieldName)] == 'const') throw new ECSError(`Can't mix const and init values for a component field`, e); @@ -819,10 +954,12 @@ export class EntityScope implements SourceLocated { } // TODO: use Tokenizer so error msgs are better let codeeval = new ActionEval(this, sys, action); + codeeval.begin(); s += this.dialect.comment(``); // TODO s += codeeval.codeToString(); s += this.dialect.comment(``); // TODO: check that this happens once? + codeeval.end(); } } if (sys.tempbytes) this.allocateTempBytes(-sys.tempbytes); @@ -916,12 +1053,11 @@ export class EntityManager { componentsMatching(q: Query, etype: EntityArchetype) { let list = []; for (let c of etype.components) { - let cname = c.name; - if (q.exclude?.includes(cname)) { + if (q.exclude?.includes(c)) { return []; } // TODO: 0 includes == all entities? - if (q.include.length == 0 || q.include.includes(cname)) { + if (q.include.length == 0 || q.include.includes(c)) { list.push(c); } } @@ -953,14 +1089,14 @@ export class EntityManager { getComponentByName(name: string): ComponentType { return this.components[name]; } - singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: string) { + singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: SourceLocated) { let components = this.componentsWithFieldName(atypes, fieldName); // TODO: use name2cfpairs? if (components.length == 0) { - throw new ECSError(`cannot find component with field "${fieldName}" in ${where}`); + throw new ECSError(`cannot find component with field "${fieldName}"`, where); } if (components.length > 1) { - throw new ECSError(`ambiguous field name "${fieldName}" in ${where}`); + throw new ECSError(`ambiguous field name "${fieldName}"`, where); } return components[0]; } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 1f40ac45..5e505300 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -169,16 +169,6 @@ function testECS() { //TODO: optional? ] }) - let c_hasbitmap = em.defineComponent({ - name: 'hasbitmap', fields: [ - { name: 'bitmap', dtype: 'ref', query: { include: ['bitmap'] } }, - ] - }) - let c_hascolormap = em.defineComponent({ - name: 'hascolormap', fields: [ - { name: 'colormap', dtype: 'ref', query: { include: ['colormap'] } }, - ] - }) let c_bitmap = em.defineComponent({ name: 'bitmap', fields: [ { name: 'bitmapdata', dtype: 'array', elem: { dtype: 'int', lo: 0, hi: 255 } } @@ -189,6 +179,16 @@ function testECS() { { name: 'colormapdata', dtype: 'array', elem: { dtype: 'int', lo: 0, hi: 255 } } ] }) + let c_hasbitmap = em.defineComponent({ + name: 'hasbitmap', fields: [ + { name: 'bitmap', dtype: 'ref', query: { include: [c_bitmap] } }, + ] + }) + let c_hascolormap = em.defineComponent({ + name: 'hascolormap', fields: [ + { name: 'colormap', dtype: 'ref', query: { include: [c_colormap] } }, + ] + }) let c_xpos = em.defineComponent({ name: 'xpos', fields: [ { name: 'xpos', dtype: 'int', lo: 0, hi: 255 } @@ -211,18 +211,18 @@ function testECS() { actions: [ { text: TEMPLATE4_S1, event: 'preframe', select: 'once', query: { - include: ['kernel'] + include: [c_kernel] } }, { // TODO: should include kernel for numlines text: TEMPLATE4_S2, event: 'preframe', select: 'once', query: { - include: ['sprite', 'hasbitmap', 'hascolormap', 'ypos'], + include: [c_sprite, c_hasbitmap, c_hascolormap, c_ypos], }, }, { text: TEMPLATE4_K, event: 'kernel', select: 'once', query: { - include: ['kernel'] + include: [c_kernel] } }, ] @@ -232,7 +232,7 @@ function testECS() { actions: [ { text: SET_XPOS, event: 'preframe', select: 'foreach', query: { - include: ['sprite', 'xpos'] + include: [c_sprite, c_xpos] }, }, //{ text:SETHORIZPOS }, @@ -242,14 +242,14 @@ function testECS() { em.defineSystem({ name: 'frameloop', actions: [ - { text: TEMPLATE1, event: 'start', select: 'once', query: { include: ['kernel'] }, + { text: TEMPLATE1, event: 'start', select: 'once', query: { include: [c_kernel] }, emits: ['preframe', 'kernel', 'postframe'] } ] }) em.defineSystem({ name: 'SetHorizPos', actions: [ - { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: ['xpos'] } }, + { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: [c_xpos] } }, ] }); From b28a4b5be4867be80490bdd4a9dc430dc5faf371 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 2 Feb 2022 16:32:04 -0600 Subject: [PATCH 030/104] ecs: using, fixed tokenizer start/end column --- src/codemirror/ecs.js | 2 +- src/common/ecs/compiler.ts | 25 +++++++++++++++++++------ src/common/ecs/ecs.ts | 30 ++++++++++++++++++++++++------ src/common/tokenizer.ts | 3 ++- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index c418cc03..a54a7e91 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -17,7 +17,7 @@ var keywords1, keywords2; var directives_list = [ - 'component', 'system', 'entity', 'scope', 'end', + 'end', 'component', 'system', 'entity', 'scope', 'using', 'const', 'init', 'locals', 'on', 'do', 'emit', 'limit', 'once', 'foreach', 'source', 'join' diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index e456a332..7d530730 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -255,7 +255,10 @@ export class ECSCompiler extends Tokenizer { let scope = this.em.newScope(name, this.currentScope || undefined); this.currentScope = scope; let cmd; - while ((cmd = this.expectTokens(['entity', 'comment', 'end']).str) != 'end') { + while ((cmd = this.expectTokens(['using', 'entity', 'comment', 'end']).str) != 'end') { + if (cmd == 'using') { + this.parseScopeUsing(); + } if (cmd == 'entity') { this.annotate(() => this.parseEntity()); } @@ -267,6 +270,13 @@ export class ECSCompiler extends Tokenizer { return scope; } + parseScopeUsing() { + let syslist = this.parseList(this.parseSystemRef, ','); + for (let sys of syslist) { + this.currentScope?.systems.push(sys); + } + } + parseEntity() : Entity { if (!this.currentScope) { this.internalError(); throw new Error(); } let name = ''; @@ -319,12 +329,15 @@ export class ECSCompiler extends Tokenizer { return eref; } + parseSystemRef() : System { + let name = this.expectIdent().str; + let sys = this.em.getSystemByName(name); + if (!sys) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc); + return sys; + } + exportToFile(src: SourceFileExport) { - for (let scope of Object.values(this.em.scopes)) { - scope.analyzeEntities(); - scope.generateCode(); - scope.dump(src); - } + this.em.exportToFile(src); } export() { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index db1aa0c7..f90d16f8 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -230,6 +230,7 @@ export class Dialect_CA65 { ` readonly HEADER = ` .include "vcs-ca65.h" +.code ` readonly FOOTER = ` .segment "VECTORS" @@ -237,7 +238,7 @@ VecNMI: .word Start VecReset: .word Start VecBRK: .word Start ` - readonly TEMPLATE_INIT = ` + readonly TEMPLATE_INIT_MAIN = ` Start: CLEAN_START ` @@ -502,6 +503,7 @@ class ActionEval { case 'source': if (!state.x) throw new ECSError('expected index register', this.action); let int = state.x.intersection(this.qr); + // TODO: what if we filter 0 entities? if (int.entities.length == 0) throw new ECSError('queries do not intersect', this.action); let indofs = int.entities[0].id - state.x.entities[0].id; state.xofs += indofs; @@ -705,6 +707,7 @@ class ActionEval { export class EntityScope implements SourceLocated { $loc: SourceLocation; childScopes: EntityScope[] = []; + systems: System[] = []; entities: Entity[] = []; bss = new DataSegment(); rodata = new DataSegment(); @@ -909,6 +912,7 @@ export class EntityScope implements SourceLocated { code = code.replace('{{%dest}}', segment.getOriginSymbol()); return code; } + // TODO: check type/range of value setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); e.consts[mksymbol(component, fieldName)] = value; @@ -935,6 +939,7 @@ export class EntityScope implements SourceLocated { let s = this.dialect.code(); //s += `\n; event ${event}\n`; let emitcode: { [event: string]: string } = {}; + systems = systems.filter(s => this.systems.includes(s)); for (let sys of systems) { // TODO: does this work if multiple actions? // TODO: should 'emits' be on action? @@ -984,7 +989,10 @@ export class EntityScope implements SourceLocated { } generateCode() { this.tempOffset = this.maxTempBytes = 0; - this.code.addCodeFragment(this.dialect.TEMPLATE_INIT); + // TODO: main scope? + if (this.name.toLowerCase() == 'main') { + this.code.addCodeFragment(this.dialect.TEMPLATE_INIT_MAIN); + } let initcode = this.allocateInitData(this.bss); this.code.addCodeFragment(initcode); let start = this.generateCodeForEvent('start'); @@ -995,7 +1003,6 @@ export class EntityScope implements SourceLocated { } } dump(file: SourceFileExport) { - file.text(this.dialect.HEADER); // TODO file.segment(`${this.name}_DATA`, 'bss'); if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); this.bss.dump(file); @@ -1003,7 +1010,6 @@ export class EntityScope implements SourceLocated { this.rodata.dump(file); //file.segment(`${this.name}_CODE`, 'code'); this.code.dump(file); - file.text(this.dialect.FOOTER); // TODO } } @@ -1013,8 +1019,8 @@ export class EntityManager { systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; symbols: { [name: string]: 'init' | 'const' } = {}; - event2systems: { [name: string]: System[] } = {}; - name2cfpairs: { [name: string]: ComponentFieldPair[] } = {}; + event2systems: { [event: string]: System[] } = {}; + name2cfpairs: { [cfname: string]: ComponentFieldPair[] } = {}; constructor(public readonly dialect: Dialect_CA65) { } @@ -1089,6 +1095,9 @@ export class EntityManager { getComponentByName(name: string): ComponentType { return this.components[name]; } + getSystemByName(name: string): System { + return this.systems[name]; + } singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: SourceLocated) { let components = this.componentsWithFieldName(atypes, fieldName); // TODO: use name2cfpairs? @@ -1106,4 +1115,13 @@ export class EntityManager { systems: this.systems }) } + exportToFile(file: SourceFileExport) { + file.text(this.dialect.HEADER); // TODO + for (let scope of Object.values(this.scopes)) { + scope.analyzeEntities(); + scope.generateCode(); + scope.dump(file); + } + file.text(this.dialect.FOOTER); // TODO + } } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index d62aae82..4fad3494 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -110,7 +110,8 @@ export class Tokenizer { let s: string = m[i + 1]; if (s != null) { found = true; - let loc = { path: this.path, line: this.lineno, start: m.index, end: m.index + s.length }; + let col = m.index - (this.lineindex[this.lineno-1] || -1) - 1; + let loc = { path: this.path, line: this.lineno, start: col, end: col + s.length }; let rule = rules[i]; // add token to list switch (rule.type) { From 83238f30f1e990f2f5ceb26f63effa19be43654f Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 2 Feb 2022 17:03:22 -0600 Subject: [PATCH 031/104] ecs: scoping, start labels, subscopes (need unions for data) --- src/common/ecs/compiler.ts | 5 ++++- src/common/ecs/ecs.ts | 39 ++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 7d530730..33061c3f 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -255,13 +255,16 @@ export class ECSCompiler extends Tokenizer { let scope = this.em.newScope(name, this.currentScope || undefined); this.currentScope = scope; let cmd; - while ((cmd = this.expectTokens(['using', 'entity', 'comment', 'end']).str) != 'end') { + while ((cmd = this.expectTokens(['using', 'entity', 'scope', 'comment', 'end']).str) != 'end') { if (cmd == 'using') { this.parseScopeUsing(); } if (cmd == 'entity') { this.annotate(() => this.parseEntity()); } + if (cmd == 'scope') { + this.annotate(() => this.parseScope()); + } if (cmd == 'comment') { this.expectTokenTypes([ECSTokenType.CodeFragment]); } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index f90d16f8..461c86aa 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -56,6 +56,7 @@ crazy idea -- full expansion, then relooper */ +import { throws } from "assert"; import { SourceLocated, SourceLocation } from "../workertypes"; export class ECSError extends Error { @@ -230,16 +231,19 @@ export class Dialect_CA65 { ` readonly HEADER = ` .include "vcs-ca65.h" +.define PAL 0 .code ` readonly FOOTER = ` .segment "VECTORS" -VecNMI: .word Start -VecReset: .word Start -VecBRK: .word Start +VecNMI: .word Main::__NMI +VecReset: .word Main::__Reset +VecBRK: .word Main::__BRK ` readonly TEMPLATE_INIT_MAIN = ` -Start: +__NMI: +__Reset: +__BRK: CLEAN_START ` @@ -313,6 +317,14 @@ export class SourceFileExport { debug_line(path: string, line: number) { this.lines.push(` .dbg line, "${path}", ${line}`); } + startScope(name: string) { + this.lines.push(` .scope ${name}`) + } + endScope(name: string) { + this.lines.push(` .endscope`) + this.lines.push(`${name}__Start = ${name}::__Start`) + // TODO: scope__start = scope::start + } toString() { return this.lines.join('\n'); } @@ -1003,13 +1015,22 @@ export class EntityScope implements SourceLocated { } } dump(file: SourceFileExport) { + this.analyzeEntities(); + this.generateCode(); + file.startScope(this.name); file.segment(`${this.name}_DATA`, 'bss'); if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); this.bss.dump(file); file.segment(`${this.name}_RODATA`, 'rodata'); this.rodata.dump(file); //file.segment(`${this.name}_CODE`, 'code'); + file.label('__Start'); this.code.dump(file); + for (let subscope of this.childScopes) { + // TODO: overlay child BSS segments + subscope.dump(file); + } + file.endScope(this.name); } } @@ -1017,7 +1038,7 @@ export class EntityManager { archetypes: { [key: string]: EntityArchetype } = {}; components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; - scopes: { [name: string]: EntityScope } = {}; + topScopes: { [name: string]: EntityScope } = {}; symbols: { [name: string]: 'init' | 'const' } = {}; event2systems: { [event: string]: System[] } = {}; name2cfpairs: { [cfname: string]: ComponentFieldPair[] } = {}; @@ -1026,8 +1047,8 @@ export class EntityManager { } newScope(name: string, parent?: EntityScope) { let scope = new EntityScope(this, this.dialect, name, parent); - if (this.scopes[name]) throw new ECSError(`scope ${name} already defined`); - this.scopes[name] = scope; + if (this.topScopes[name]) throw new ECSError(`scope ${name} already defined`); + if (!parent) this.topScopes[name] = scope; return scope; } defineComponent(ctype: ComponentType) { @@ -1117,9 +1138,7 @@ export class EntityManager { } exportToFile(file: SourceFileExport) { file.text(this.dialect.HEADER); // TODO - for (let scope of Object.values(this.scopes)) { - scope.analyzeEntities(); - scope.generateCode(); + for (let scope of Object.values(this.topScopes)) { scope.dump(file); } file.text(this.dialect.FOOTER); // TODO From dcb11a70801074249b7449791bea8c3a434246a6 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 2 Feb 2022 20:06:44 -0600 Subject: [PATCH 032/104] ecs: import --- src/common/ecs/compiler.ts | 26 +++++++++++++++++++++++--- src/common/ecs/ecs.ts | 8 ++++++-- src/common/tokenizer.ts | 2 +- src/ide/project.ts | 8 +++++++- src/test/testecs.ts | 3 ++- src/worker/tools/ecs.ts | 8 ++++++-- 6 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 33061c3f..922f0f37 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -13,10 +13,11 @@ export enum ECSTokenType { export class ECSCompiler extends Tokenizer { - em = new EntityManager(new Dialect_CA65()); // TODO currentScope: EntityScope | null = null; - constructor() { + constructor( + public readonly em: EntityManager) + { super(); //this.includeEOL = true; this.setTokenRules([ @@ -53,9 +54,17 @@ export class ECSCompiler extends Tokenizer { } } + getImportFile: (path: string) => string; + + importFile(path: string) { + let text = this.getImportFile && this.getImportFile(path); + if (!text) this.compileError(`I can't find the import file "${path}".`); + new ECSCompiler(this.em).parseFile(path, text); + } + parseTopLevel() { //this.skipBlankLines(); - let tok = this.expectTokens(['component', 'system', 'scope', 'resource', 'comment']); + let tok = this.expectTokens(['component', 'system', 'scope', 'resource', 'import', 'demo', 'comment']); if (tok.str == 'component') { return this.em.defineComponent(this.parseComponentDefinition()); } @@ -68,6 +77,16 @@ export class ECSCompiler extends Tokenizer { if (tok.str == 'resource') { return this.em.defineSystem(this.parseResource()); } + if (tok.str == 'import') { + let tok = this.expectTokenTypes([ECSTokenType.QuotedString]); + let path = tok.str.substring(1, tok.str.length-1); + return this.importFile(path); + } + if (tok.str == 'demo') { + let scope = this.parseScope(); + scope.isDemo = true; + return scope; + } if (tok.str == 'comment') { this.expectTokenTypes([ECSTokenType.CodeFragment]); return; @@ -253,6 +272,7 @@ export class ECSCompiler extends Tokenizer { parseScope() : EntityScope { let name = this.expectIdent().str; let scope = this.em.newScope(name, this.currentScope || undefined); + scope.filePath = this.path; this.currentScope = scope; let cmd; while ((cmd = this.expectTokens(['using', 'entity', 'scope', 'comment', 'end']).str) != 'end') { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 461c86aa..dc1e3af9 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -56,7 +56,6 @@ crazy idea -- full expansion, then relooper */ -import { throws } from "assert"; import { SourceLocated, SourceLocation } from "../workertypes"; export class ECSError extends Error { @@ -730,6 +729,8 @@ export class EntityScope implements SourceLocated { maxTempBytes = 0; resources = new Set(); state = new ActionState(); + isDemo = false; + filePath = ''; constructor( public readonly em: EntityManager, @@ -1139,7 +1140,10 @@ export class EntityManager { exportToFile(file: SourceFileExport) { file.text(this.dialect.HEADER); // TODO for (let scope of Object.values(this.topScopes)) { - scope.dump(file); + // TODO: demos + if (!scope.isDemo) { + scope.dump(file); + } } file.text(this.dialect.FOOTER); // TODO } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 4fad3494..9c7b2ec7 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -171,7 +171,7 @@ export class Tokenizer { expectTokens(strlist: string[], msg?: string): Token { let tok = this.consumeToken(); let tokstr = tok.str; - if (strlist.indexOf(tokstr) < 0) { + if (!strlist.includes(tokstr)) { this.compileError(msg || `There should be a ${strlist.map((s) => `"${s}"`).join(' or ')} here, not "${tokstr}".`); } return tok; diff --git a/src/ide/project.ts b/src/ide/project.ts index a399ef33..0c11974a 100644 --- a/src/ide/project.ts +++ b/src/ide/project.ts @@ -150,6 +150,7 @@ export class CodeProject { files.push(dir + '/' + fn); } + // TODO: use tool id to parse files, not platform parseIncludeDependencies(text:string):string[] { let files = []; let m; @@ -199,13 +200,18 @@ export class CodeProject { this.pushAllFiles(files, m[2]); } // for wiz - let re5 = /^\s*(import|embed)\s+"(.+?)";/gmi; + let re5 = /^\s*(import|embed)\s*"(.+?)";/gmi; while (m = re5.exec(text)) { if (m[1] == 'import') this.pushAllFiles(files, m[2] + ".wiz"); else this.pushAllFiles(files, m[2]); } + // for ecs + let re6 = /^\s*(import)\s*"(.+?)"/gmi; + while (m = re6.exec(text)) { + this.pushAllFiles(files, m[2]); + } } return files; } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 5e505300..1b626032 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -290,7 +290,8 @@ function testECS() { } function testCompiler() { - let c = new ECSCompiler(); + let em = new EntityManager(new Dialect_CA65()); // TODO + let c = new ECSCompiler(em); try { c.parseFile(` // comment diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 37a1eef7..3600ae1a 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -1,11 +1,15 @@ import { ECSCompiler } from "../../common/ecs/compiler"; -import { ECSError } from "../../common/ecs/ecs"; +import { Dialect_CA65, ECSError, EntityManager } from "../../common/ecs/ecs"; import { CompileError } from "../../common/tokenizer"; import { CodeListingMap } from "../../common/workertypes"; import { BuildStep, BuildStepResult, gatherFiles, getWorkFileAsString, putWorkFile, staleFiles } from "../workermain"; export function assembleECS(step: BuildStep): BuildStepResult { - let compiler = new ECSCompiler(); + let em = new EntityManager(new Dialect_CA65()); // TODO + let compiler = new ECSCompiler(em); + compiler.getImportFile = (path: string) => { + return getWorkFileAsString(path); + } gatherFiles(step, { mainFilePath: "main.ecs" }); var destpath = step.prefix + '.ca65'; if (staleFiles(step, [destpath])) { From c299bcddae26d675b566c03ec7c9176169e595f9 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 07:44:47 -0600 Subject: [PATCH 033/104] ecs: with, select queries --- src/codemirror/ecs.js | 4 +- src/common/ecs/compiler.ts | 20 ++++-- src/common/ecs/ecs.ts | 144 +++++++++++++++++++++---------------- src/test/testecs.ts | 10 +-- src/worker/tools/ecs.ts | 1 + 5 files changed, 103 insertions(+), 76 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index a54a7e91..10b835a2 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -17,10 +17,10 @@ var keywords1, keywords2; var directives_list = [ - 'end', 'component', 'system', 'entity', 'scope', 'using', + 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'const', 'init', 'locals', 'on', 'do', 'emit', 'limit', - 'once', 'foreach', 'source', 'join' + 'once', 'foreach', 'with', 'join' ]; var keywords_list = [ 'processor', diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 922f0f37..f187d428 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,7 +1,7 @@ import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; -import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Dialect_CA65, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; +import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -196,7 +196,6 @@ export class ECSCompiler extends Tokenizer { parseResource(): System { let name = this.expectIdent().str; - let query = this.parseQuery(); let tempbytes; if (this.peekToken().str == 'locals') { this.consumeToken(); @@ -204,7 +203,7 @@ export class ECSCompiler extends Tokenizer { } let text = this.parseCode(); let select : SelectType = 'once'; - let action : Action = { text, event: name, query, select }; + let action : Action = { text, event: name, select }; return { name, tempbytes, actions: [action] }; } @@ -212,10 +211,17 @@ export class ECSCompiler extends Tokenizer { // TODO: unused events? let event = this.expectIdent().str; this.expectToken('do'); - let select = this.expectTokens(['once', 'foreach', 'source', 'join']).str as SelectType; // TODO: type check? - let query = this.parseQuery(); + let select = this.expectTokens( + ['once', 'foreach', 'join', 'with', 'select']).str as SelectType; // TODO: type check? + let query = undefined; let join = undefined; - if (select == 'join') join = this.parseQuery(); + if (select != 'once') { + query = this.parseQuery(); + } + if (select == 'join') { + this.expectToken('with'); + join = this.parseQuery(); + } let emits; let limit; if (this.peekToken().str == 'limit') { @@ -231,7 +237,7 @@ export class ECSCompiler extends Tokenizer { } let text = this.parseCode(); let action = { text, event, query, join, select, limit }; - return action; + return action as Action; } parseQuery() { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index dc1e3af9..454173ad 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -111,17 +111,31 @@ export interface System extends SourceLocated { tempbytes?: number; } -export interface Action extends SourceLocated { - text: string; +export type SelectType = 'once' | 'foreach' | 'join' | 'with' | 'select'; + +export interface ActionBase extends SourceLocated { + select: SelectType; event: string; - select: SelectType - query: Query; - join?: Query; - limit?: number; + text: string; emits?: string[]; } -export type SelectType = 'once' | 'foreach' | 'source' | 'join'; +export interface ActionOnce extends ActionBase { + select: 'once' +} + +export interface ActionWithQuery extends ActionBase { + select: 'foreach' | 'join' | 'with' | 'select' + query: Query + limit?: number +} + +export interface ActionWithJoin extends ActionWithQuery { + select: 'join' + join?: Query +} + +export type Action = ActionWithQuery | ActionWithJoin | ActionOnce; export type DataValue = number | boolean | Uint8Array | Uint16Array; @@ -491,8 +505,10 @@ class ActionEval { readonly action: Action) { this.em = scope.em; this.dialect = scope.em.dialect; - this.qr = new QueryResult(scope, action.query); this.oldState = scope.state; + let q = (action as ActionWithQuery).query; + if (q) this.qr = new QueryResult(scope, q); + else this.qr = new QueryResult(scope, undefined, [], []); } begin() { let state = this.scope.state = Object.assign({}, this.scope.state); @@ -506,19 +522,21 @@ class ActionEval { case 'join': if (state.x || state.y) throw new ECSError('no free index registers for join', this.action); state.y = this.qr; - if (this.action.join) { - this.jr = new QueryResult(this.scope, this.action.join); - state.x = this.jr; - } + this.jr = new QueryResult(this.scope, (this.action as ActionWithJoin).join); + state.x = this.jr; break; - case 'source': - if (!state.x) throw new ECSError('expected index register', this.action); - let int = state.x.intersection(this.qr); - // TODO: what if we filter 0 entities? - if (int.entities.length == 0) throw new ECSError('queries do not intersect', this.action); - let indofs = int.entities[0].id - state.x.entities[0].id; - state.xofs += indofs; - state.x = int; + case 'with': + if (state.x) { + let int = state.x.intersection(this.qr); + // TODO: what if we filter 0 entities? + if (int.entities.length == 0) throw new ECSError('queries do not intersect', this.action); + let indofs = int.entities[0].id - state.x.entities[0].id; + state.xofs += indofs; + state.x = int; + } else { + if (this.qr.entities.length != 1) + throw new ECSError(`query outside of loop must match exactly one entity`, this.action); + } break; } } @@ -532,43 +550,46 @@ class ActionEval { let action = this.action; let sys = this.sys; let code = action.text; - let label = `${sys.name}__${action.event}`; // TODO: better label that won't conflict (seq?) - // TODO: detect cycles - // TODO: "source"? - // TODO: what if only 1 item? + let label = `${sys.name}__${action.event}__${this.em.seq++}`; // TODO: better label that won't conflict (seq?) let props: { [name: string]: string } = {}; - if (action.select == 'foreach') { - code = this.wrapCodeInLoop(code, action, this.qr.entities); - } - if (action.select == 'join' && this.jr) { - let jentities = this.jr.entities; - if (jentities.length == 0) - throw new ECSError(`join query for ${label} doesn't match any entities`, action.join); // TODO - let joinfield = this.getJoinField(action, this.qr.atypes, this.jr.atypes); + if (action.select != 'once') { + // TODO: detect cycles + // TODO: "source"? // TODO: what if only 1 item? - // TODO: should be able to access fields via Y reg - code = this.wrapCodeInLoop(code, action, this.qr.entities, joinfield); - props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); //TODO? - this.qr = this.jr; // TODO? + if (action.select == 'foreach') { + code = this.wrapCodeInLoop(code, action, this.qr.entities); + } + if (action.select == 'join' && this.jr) { + let jentities = this.jr.entities; + if (jentities.length == 0) + throw new ECSError(`join query doesn't match any entities`, action); // TODO + let joinfield = this.getJoinField(action, this.qr.atypes, this.jr.atypes); + // TODO: what if only 1 item? + // TODO: should be able to access fields via Y reg + code = this.wrapCodeInLoop(code, action, this.qr.entities, joinfield); + props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); //TODO? + this.qr = this.jr; // TODO? + } + let entities = this.qr.entities; + props['%efullcount'] = entities.length.toString(); + if (action.limit) { + entities = entities.slice(0, action.limit); + } + if (entities.length == 0) + throw new ECSError(`query doesn't match any entities`, action.query); // TODO + // filter entities from loop? + if (action.select == 'with' && entities.length > 1) { + // TODO: what if not needed + code = this.wrapCodeInFilter(code); + } + // define properties + props['%elo'] = entities[0].id.toString(); + props['%ehi'] = entities[entities.length - 1].id.toString(); + props['%ecount'] = entities.length.toString(); + props['%xofs'] = this.scope.state.xofs.toString(); + props['%yofs'] = this.scope.state.yofs.toString(); + this.qr.entities = entities; } - if (action.select == 'source') { - // TODO: what if not needed - code = this.wrapCodeInFilter(code); - } - let entities = this.qr.entities; - props['%efullcount'] = entities.length.toString(); - if (action.limit) { - entities = entities.slice(0, action.limit); - } - if (entities.length == 0) - throw new ECSError(`query for ${label} doesn't match any entities`, action.query); // TODO - // define properties - props['%elo'] = entities[0].id.toString(); - props['%ehi'] = entities[entities.length - 1].id.toString(); - props['%ecount'] = entities.length.toString(); - props['%xofs'] = this.scope.state.xofs.toString(); - props['%yofs'] = this.scope.state.yofs.toString(); - this.qr.entities = entities; // replace @labels code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); // replace {{...}} tags @@ -664,16 +685,14 @@ class ActionEval { // TODO: don't mix const and init data let range = this.scope.bss.getFieldRange(component, fieldName) || this.scope.rodata.getFieldRange(component, fieldName); if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO - let eidofs = range.elo - qr.entities[0].id; // TODO // TODO: dialect let ident = this.dialect.fieldsymbol(component, field, bitofs); if (qualified) { return this.dialect.absolute(ident); - } else if (action.select == 'once') { - if (qr.entities.length != 1) - throw new ECSError(`can't choose multiple entities for ${fieldName} with select=once`, action); + } else if (qr.entities.length == 1) { return this.dialect.absolute(ident); } else { + let eidofs = range.elo - qr.entities[0].id; // TODO // TODO: eidofs? let ir; if (this.scope.state.x?.intersection(this.qr)) { @@ -697,8 +716,8 @@ class ActionEval { getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]): ComponentFieldPair { let refs = Array.from(this.scope.iterateArchetypeFields(atypes, (c, f) => f.dtype == 'ref')); // TODO: better error message - if (refs.length == 0) throw new ECSError(`cannot find join fields`, action.query); - if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action.query); + if (refs.length == 0) throw new ECSError(`cannot find join fields`, action); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action); // TODO: check to make sure join works return refs[0]; // TODO /* TODO @@ -1043,6 +1062,8 @@ export class EntityManager { symbols: { [name: string]: 'init' | 'const' } = {}; event2systems: { [event: string]: System[] } = {}; name2cfpairs: { [cfname: string]: ComponentFieldPair[] } = {}; + mainPath: string = ''; + seq = 1; constructor(public readonly dialect: Dialect_CA65) { } @@ -1140,8 +1161,7 @@ export class EntityManager { exportToFile(file: SourceFileExport) { file.text(this.dialect.HEADER); // TODO for (let scope of Object.values(this.topScopes)) { - // TODO: demos - if (!scope.isDemo) { + if (!scope.isDemo || scope.filePath == this.mainPath) { scope.dump(file); } } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 1b626032..0af4fede 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -210,18 +210,18 @@ function testECS() { tempbytes: 12, actions: [ { - text: TEMPLATE4_S1, event: 'preframe', select: 'once', query: { + text: TEMPLATE4_S1, event: 'preframe', select: 'with', query: { include: [c_kernel] } }, { // TODO: should include kernel for numlines - text: TEMPLATE4_S2, event: 'preframe', select: 'once', query: { + text: TEMPLATE4_S2, event: 'preframe', select: 'with', query: { include: [c_sprite, c_hasbitmap, c_hascolormap, c_ypos], }, }, { - text: TEMPLATE4_K, event: 'kernel', select: 'once', query: { + text: TEMPLATE4_K, event: 'kernel', select: 'with', query: { include: [c_kernel] } }, @@ -242,14 +242,14 @@ function testECS() { em.defineSystem({ name: 'frameloop', actions: [ - { text: TEMPLATE1, event: 'start', select: 'once', query: { include: [c_kernel] }, + { text: TEMPLATE1, event: 'start', select: 'with', query: { include: [c_kernel] }, emits: ['preframe', 'kernel', 'postframe'] } ] }) em.defineSystem({ name: 'SetHorizPos', actions: [ - { text: SETHORIZPOS, event: 'SetHorizPos', select: 'once', query: { include: [c_xpos] } }, + { text: SETHORIZPOS, event: 'SetHorizPos', select: 'with', query: { include: [c_xpos] } }, ] }); diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 3600ae1a..4d2de328 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -11,6 +11,7 @@ export function assembleECS(step: BuildStep): BuildStepResult { return getWorkFileAsString(path); } gatherFiles(step, { mainFilePath: "main.ecs" }); + if (step.mainfile) em.mainPath = step.path; var destpath = step.prefix + '.ca65'; if (staleFiles(step, [destpath])) { let code = getWorkFileAsString(step.path); From a7f708a3036ff38e6746a63bc42ca3639e6dfd63 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 08:51:28 -0600 Subject: [PATCH 034/104] ecs: better with filter --- src/common/ecs/ecs.ts | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 454173ad..38f6f466 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -212,14 +212,18 @@ export class Dialect_CA65 { @__exit: `; -// TODO: lo/hi side of range? - readonly ASM_FILTER_RANGE_X = ` + readonly ASM_FILTER_RANGE_LO_X = ` cpx #{{%xofs}} - bcc @__skip - cpx #{{%xofs}}+{{%ecount}} - bcs @__skip + bcc @__skipxlo {{%code}} -@__skip: +@__skipxlo: +` + + readonly ASM_FILTER_RANGE_HI_X = ` + cpx #{{%xofs}}+{{%ecount}} + bcs @__skipxhi + {{%code}} +@__skipxhi: ` // TODO @@ -531,7 +535,7 @@ class ActionEval { // TODO: what if we filter 0 entities? if (int.entities.length == 0) throw new ECSError('queries do not intersect', this.action); let indofs = int.entities[0].id - state.x.entities[0].id; - state.xofs += indofs; + state.xofs += indofs; // TODO: should merge with filter state.x = int; } else { if (this.qr.entities.length != 1) @@ -570,25 +574,26 @@ class ActionEval { props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); //TODO? this.qr = this.jr; // TODO? } + // select subset of entities let entities = this.qr.entities; - props['%efullcount'] = entities.length.toString(); + let fullEntityCount = entities.length.toString(); if (action.limit) { entities = entities.slice(0, action.limit); } if (entities.length == 0) throw new ECSError(`query doesn't match any entities`, action.query); // TODO + this.qr.entities = entities; // filter entities from loop? if (action.select == 'with' && entities.length > 1) { - // TODO: what if not needed code = this.wrapCodeInFilter(code); } // define properties props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); props['%ecount'] = entities.length.toString(); + props['%efullcount'] = fullEntityCount; props['%xofs'] = this.scope.state.xofs.toString(); props['%yofs'] = this.scope.state.yofs.toString(); - this.qr.entities = entities; } // replace @labels code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); @@ -642,10 +647,20 @@ class ActionEval { return s; } wrapCodeInFilter(code: string) { - // TODO: what if not needed? - let s = this.dialect.ASM_FILTER_RANGE_X; - s = s.replace('{{%code}}', code); - return s; + // TODO: :-p + let ents = this.qr.entities; + let ents2 = this.oldState.x?.entities; + if (ents && ents2) { + let lo = ents[0].id; + let hi = ents[ents.length-1].id; + let lo2 = ents2[0].id; + let hi2 = ents2[ents2.length-1].id; + if (lo != lo2) + code = this.dialect.ASM_FILTER_RANGE_LO_X.replace('{{%code}}', code); + if (hi != hi2) + code = this.dialect.ASM_FILTER_RANGE_HI_X.replace('{{%code}}', code); + } + return code; } generateCodeForField(sys: System, action: Action, qr: QueryResult, fieldName: string, bitofs: number): string { From bc3b2cd81d5237fb0628bf5ecce1ef7ac4f8b4ec Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 09:24:00 -0600 Subject: [PATCH 035/104] ecs: with/if --- src/codemirror/ecs.js | 4 ++-- src/common/ecs/compiler.ts | 2 +- src/common/ecs/ecs.ts | 27 +++++++++++++++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 10b835a2..869f9eab 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -20,14 +20,14 @@ 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'const', 'init', 'locals', 'on', 'do', 'emit', 'limit', - 'once', 'foreach', 'with', 'join' + 'once', 'foreach', 'with', 'join', 'if', ]; var keywords_list = [ 'processor', 'byte', 'word', 'long', 'include', 'seg', 'dc', 'ds', 'dv', 'hex', 'err', 'org', 'rorg', 'echo', 'rend', 'align', 'subroutine', 'equ', 'eqm', 'set', 'mac', 'endm', 'mexit', 'ifconst', - 'ifnconst', 'if', 'else', 'endif', 'eif', 'repeat', 'repend' + 'ifnconst', 'else', 'endif', 'eif', 'repeat', 'repend' ]; var directives = new Map(); diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index f187d428..79823b65 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -212,7 +212,7 @@ export class ECSCompiler extends Tokenizer { let event = this.expectIdent().str; this.expectToken('do'); let select = this.expectTokens( - ['once', 'foreach', 'join', 'with', 'select']).str as SelectType; // TODO: type check? + ['once', 'foreach', 'join', 'with', 'if', 'select']).str as SelectType; // TODO: type check? let query = undefined; let join = undefined; if (select != 'once') { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 38f6f466..18147ecb 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -111,7 +111,7 @@ export interface System extends SourceLocated { tempbytes?: number; } -export type SelectType = 'once' | 'foreach' | 'join' | 'with' | 'select'; +export type SelectType = 'once' | 'foreach' | 'join' | 'with' | 'if' | 'select'; export interface ActionBase extends SourceLocated { select: SelectType; @@ -125,7 +125,7 @@ export interface ActionOnce extends ActionBase { } export interface ActionWithQuery extends ActionBase { - select: 'foreach' | 'join' | 'with' | 'select' + select: 'foreach' | 'join' | 'with' | 'if' | 'select' query: Query limit?: number } @@ -529,14 +529,20 @@ class ActionEval { this.jr = new QueryResult(this.scope, (this.action as ActionWithJoin).join); state.x = this.jr; break; + case 'if': case 'with': if (state.x) { let int = state.x.intersection(this.qr); - // TODO: what if we filter 0 entities? - if (int.entities.length == 0) throw new ECSError('queries do not intersect', this.action); - let indofs = int.entities[0].id - state.x.entities[0].id; - state.xofs += indofs; // TODO: should merge with filter - state.x = int; + if (int.entities.length == 0) { + if (this.action.select == 'with') + throw new ECSError('queries do not intersect', this.action); + else + break; + } else { + let indofs = int.entities[0].id - state.x.entities[0].id; + state.xofs += indofs; // TODO: should merge with filter + state.x = int; + } } else { if (this.qr.entities.length != 1) throw new ECSError(`query outside of loop must match exactly one entity`, this.action); @@ -566,7 +572,7 @@ class ActionEval { if (action.select == 'join' && this.jr) { let jentities = this.jr.entities; if (jentities.length == 0) - throw new ECSError(`join query doesn't match any entities`, action); // TODO + throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO let joinfield = this.getJoinField(action, this.qr.atypes, this.jr.atypes); // TODO: what if only 1 item? // TODO: should be able to access fields via Y reg @@ -580,6 +586,8 @@ class ActionEval { if (action.limit) { entities = entities.slice(0, action.limit); } + if (entities.length == 0 && action.select == 'if') + return ''; if (entities.length == 0) throw new ECSError(`query doesn't match any entities`, action.query); // TODO this.qr.entities = entities; @@ -587,6 +595,9 @@ class ActionEval { if (action.select == 'with' && entities.length > 1) { code = this.wrapCodeInFilter(code); } + if (action.select == 'if' && entities.length > 1) { + code = this.wrapCodeInFilter(code); + } // define properties props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); From 5d3cae480461c29ab36a02943b89348eca1d051d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 10:44:29 -0600 Subject: [PATCH 036/104] ecs: -query, get/set, imported --- src/common/ecs/compiler.ts | 33 +++++++++++++++++-------- src/common/ecs/ecs.ts | 49 +++++++++++++++++++++++--------------- src/common/tokenizer.ts | 2 +- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 79823b65..82fcb835 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -57,9 +57,12 @@ export class ECSCompiler extends Tokenizer { getImportFile: (path: string) => string; importFile(path: string) { - let text = this.getImportFile && this.getImportFile(path); - if (!text) this.compileError(`I can't find the import file "${path}".`); - new ECSCompiler(this.em).parseFile(path, text); + if (!this.em.imported[path]) { // already imported? + let text = this.getImportFile && this.getImportFile(path); + if (!text) this.compileError(`I can't find the import file "${path}".`); + new ECSCompiler(this.em).parseFile(path, text); + this.em.imported[path] = true; + } } parseTopLevel() { @@ -243,19 +246,29 @@ export class ECSCompiler extends Tokenizer { parseQuery() { let q: Query = { include: [] }; let start = this.expectToken('['); - q.include = this.parseList(this.parseComponentRef, ','); + this.parseList(() => this.parseQueryItem(q), ','); this.expectToken(']'); - if (this.peekToken().str == '-') { - this.consumeToken(); - this.expectToken('['); - q.exclude = this.parseList(this.parseComponentRef, ','); - this.expectToken(']'); - } // TODO: other params q.$loc = mergeLocs(start.$loc, this.lasttoken.$loc); return q; } + parseQueryItem(q: Query) { + let prefix = this.peekToken(); + if (prefix.type != TokenType.Ident) { + this.consumeToken(); + } + let cref = this.parseComponentRef(); + if (prefix.type == TokenType.Ident) { + q.include.push(cref); + } else if (prefix.str == '-') { + if (!q.exclude) q.exclude = []; + q.exclude.push(cref); + } else { + this.compileError(`Query components may be preceded only by a '-'.`); + } + } + parseEvent() { return this.expectIdent().str; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 18147ecb..32e4d34a 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -226,7 +226,7 @@ export class Dialect_CA65 { @__skipxhi: ` -// TODO + // TODO readonly ASM_MAP_RANGES = ` txa pha @@ -482,7 +482,7 @@ class QueryResult { isContiguous() { if (this.entities.length == 0) return true; let id = this.entities[0].id; - for (let i=1; i 1) { code = this.wrapCodeInFilter(code); } + if (action.select == 'foreach' && entities.length > 1) { + code = this.wrapCodeInLoop(code, action, this.qr.entities); + } // define properties props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); @@ -613,13 +614,13 @@ class ActionEval { let toks = group.split(/\s+/); if (toks.length == 0) throw new ECSError(`empty command`, action); let cmd = group.charAt(0); - let rest = group.substring(1); + let rest = group.substring(1).trim(); switch (cmd) { case '!': return this.__emit([rest]); case '$': return this.__local([rest]); case '^': return this.__use([rest]); - case '<': return this.__byte([rest, '0']); - case '>': return this.__byte([rest, '8']); + case '<': return this.__get([rest, '0']); + case '>': return this.__get([rest, '8']); default: let value = props[toks[0]]; if (value) return value; @@ -630,10 +631,16 @@ class ActionEval { }); return code; } - __byte(args: string[]) { + __get(args: string[]) { + return this.getset(args, false); + } + __set(args: string[]) { + return this.getset(args, true); + } + getset(args: string[], canwrite: boolean) { let fieldName = args[0]; let bitofs = parseInt(args[1] || '0'); - return this.generateCodeForField(this.sys, this.action, this.qr, fieldName, bitofs); + return this.generateCodeForField(this.sys, this.action, this.qr, fieldName, bitofs, canwrite); } __use(args: string[]) { return this.scope.includeResource(args[0]); @@ -663,9 +670,9 @@ class ActionEval { let ents2 = this.oldState.x?.entities; if (ents && ents2) { let lo = ents[0].id; - let hi = ents[ents.length-1].id; + let hi = ents[ents.length - 1].id; let lo2 = ents2[0].id; - let hi2 = ents2[ents2.length-1].id; + let hi2 = ents2[ents2.length - 1].id; if (lo != lo2) code = this.dialect.ASM_FILTER_RANGE_LO_X.replace('{{%code}}', code); if (hi != hi2) @@ -674,7 +681,7 @@ class ActionEval { return code; } generateCodeForField(sys: System, action: Action, qr: QueryResult, - fieldName: string, bitofs: number): string { + fieldName: string, bitofs: number, canwrite: boolean): string { var component: ComponentType; var qualified = false; @@ -684,13 +691,13 @@ class ActionEval { component = this.em.getComponentByName(cname); fieldName = fname; qualified = true; - if (component == null) throw new ECSError(`no component named "${cname}"`) + if (component == null) throw new ECSError(`no component named "${cname}"`, action) } else { component = this.em.singleComponentWithFieldName(qr.atypes, fieldName, action); } // find archetypes let field = component.fields.find(f => f.name == fieldName); - if (field == null) throw new ECSError(`no field named "${fieldName}" in component`) + if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, action); // see if all entities have the same constant value // TODO: should be done somewhere else? let constValues = new Set(); @@ -698,6 +705,9 @@ class ActionEval { let constVal = e.consts[mksymbol(component, fieldName)]; constValues.add(constVal); // constVal === undefined is allowed } + // can't write to constant + if (constValues.size > 0 && canwrite) + throw new ECSError(`can't write to constant field ${fieldName}`, action); // is it a constant? if (constValues.size == 1) { let value = constValues.values().next().value as DataValue; @@ -1089,6 +1099,7 @@ export class EntityManager { event2systems: { [event: string]: System[] } = {}; name2cfpairs: { [cfname: string]: ComponentFieldPair[] } = {}; mainPath: string = ''; + imported: { [path: string]: boolean } = {}; seq = 1; constructor(public readonly dialect: Dialect_CA65) { diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 9c7b2ec7..bb52f568 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -172,7 +172,7 @@ export class Tokenizer { let tok = this.consumeToken(); let tokstr = tok.str; if (!strlist.includes(tokstr)) { - this.compileError(msg || `There should be a ${strlist.map((s) => `"${s}"`).join(' or ')} here, not "${tokstr}".`); + this.compileError(msg || `These keywords are valid here: ${strlist.join(', ')}`); } return tok; } From 167dff50a54b5bc9e686d21f3dcc38af8ef7e45d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 14:46:10 -0600 Subject: [PATCH 037/104] ecs: refactored limit, loops, but needs more --- src/common/ecs/compiler.ts | 6 +-- src/common/ecs/ecs.ts | 77 +++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 82fcb835..65394149 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -229,8 +229,8 @@ export class ECSCompiler extends Tokenizer { let limit; if (this.peekToken().str == 'limit') { this.consumeToken(); - if (!['foreach', 'join'].includes(select)) this.compileError(`A "${select}" query can't include a limit.`); - limit = this.expectInteger(); + if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } + else query.limit = this.expectInteger(); } if (this.peekToken().str == 'emit') { this.consumeToken(); @@ -239,7 +239,7 @@ export class ECSCompiler extends Tokenizer { this.expectToken(')'); } let text = this.parseCode(); - let action = { text, event, query, join, select, limit }; + let action = { text, event, query, join, select }; return action as Action; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 32e4d34a..1fb707dd 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -103,6 +103,7 @@ export interface Query extends SourceLocated { listen?: ComponentType[]; exclude?: ComponentType[]; updates?: ComponentType[]; + limit?: number; } export interface System extends SourceLocated { @@ -127,7 +128,6 @@ export interface ActionOnce extends ActionBase { export interface ActionWithQuery extends ActionBase { select: 'foreach' | 'join' | 'with' | 'if' | 'select' query: Query - limit?: number } export interface ActionWithJoin extends ActionWithQuery { @@ -455,7 +455,7 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { return 0; } -class QueryResult { +class EntitySet { atypes: ArchetypeMatch[]; entities: Entity[]; scope; @@ -465,6 +465,9 @@ class QueryResult { if (query) { this.atypes = scope.em.archetypesMatching(query); this.entities = scope.entitiesMatching(this.atypes); + if (query.limit) { + this.entities = this.entities.slice(0, query.limit); + } } else if (a && e) { this.atypes = a; this.entities = e; @@ -474,10 +477,10 @@ class QueryResult { // TODO: action for error msg return this.scope.em.singleComponentWithFieldName(this.atypes, f.name, where); } - intersection(qr: QueryResult) { + intersection(qr: EntitySet) { let ents = this.entities.filter(e => qr.entities.includes(e)); let atypes = this.atypes.filter(a1 => qr.atypes.find(a2 => a2.etype == a1.etype)); - return new QueryResult(this.scope, undefined, atypes, ents); + return new EntitySet(this.scope, undefined, atypes, ents); } isContiguous() { if (this.entities.length == 0) return true; @@ -489,9 +492,10 @@ class QueryResult { } } -class ActionState { - x: QueryResult | null = null; - y: QueryResult | null = null; +// todo: generalize +class ActionCPUState { + x: EntitySet | null = null; + y: EntitySet | null = null; xofs: number = 0; yofs: number = 0; } @@ -499,9 +503,10 @@ class ActionState { class ActionEval { em; dialect; - qr: QueryResult; - jr: QueryResult | undefined; - oldState; + qr: EntitySet; + jr: EntitySet | undefined; + oldState : ActionCPUState; + entities : Entity[]; constructor( readonly scope: EntityScope, @@ -512,8 +517,12 @@ class ActionEval { this.dialect = scope.em.dialect; this.oldState = scope.state; let q = (action as ActionWithQuery).query; - if (q) this.qr = new QueryResult(scope, q); - else this.qr = new QueryResult(scope, undefined, [], []); + if (q) this.qr = new EntitySet(scope, q); + else this.qr = new EntitySet(scope, undefined, [], []); + this.entities = this.qr.entities; + let query = (this.action as ActionWithQuery).query; + if (query && this.entities.length == 0) + throw new ECSError(`query doesn't match any entities`, query); // TODO } begin() { let state = this.scope.state = Object.assign({}, this.scope.state); @@ -526,12 +535,13 @@ class ActionEval { break; case 'join': 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); state.y = this.qr; - this.jr = new QueryResult(this.scope, (this.action as ActionWithJoin).join); state.x = this.jr; break; case 'if': case 'with': + // TODO: what if not in X because 1 element? if (state.x) { let int = state.x.intersection(this.qr); if (int.entities.length == 0) { @@ -543,6 +553,7 @@ class ActionEval { let indofs = int.entities[0].id - state.x.entities[0].id; state.xofs += indofs; // TODO: should merge with filter state.x = int; + this.entities = int.entities; // TODO? } } else { if (this.qr.entities.length != 1) @@ -558,6 +569,9 @@ class ActionEval { const tag_re = /\{\{(.+?)\}\}/g; const label_re = /@(\w+)\b/g; + if (this.entities.length == 0 && this.action.select == 'if') + return ''; + let action = this.action; let sys = this.sys; let code = action.text; @@ -576,19 +590,10 @@ class ActionEval { // TODO: should be able to access fields via Y reg code = this.wrapCodeInLoop(code, action, this.qr.entities, joinfield); props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); //TODO? - this.qr = this.jr; // TODO? } // select subset of entities - let entities = this.qr.entities; - let fullEntityCount = entities.length.toString(); - if (action.limit) { - entities = entities.slice(0, action.limit); - } - if (entities.length == 0 && action.select == 'if') - return ''; - if (entities.length == 0) - throw new ECSError(`query doesn't match any entities`, action.query); // TODO - this.qr.entities = entities; + let fullEntityCount = this.qr.entities.length; //entities.length.toString(); + let entities = this.entities; // filter entities from loop? if (action.select == 'with' && entities.length > 1) { code = this.wrapCodeInFilter(code); @@ -603,7 +608,7 @@ class ActionEval { props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); props['%ecount'] = entities.length.toString(); - props['%efullcount'] = fullEntityCount; + props['%efullcount'] = fullEntityCount.toString(); props['%xofs'] = this.scope.state.xofs.toString(); props['%yofs'] = this.scope.state.yofs.toString(); } @@ -640,7 +645,7 @@ class ActionEval { getset(args: string[], canwrite: boolean) { let fieldName = args[0]; let bitofs = parseInt(args[1] || '0'); - return this.generateCodeForField(this.sys, this.action, this.qr, fieldName, bitofs, canwrite); + return this.generateCodeForField(fieldName, bitofs, canwrite); } __use(args: string[]) { return this.scope.includeResource(args[0]); @@ -666,8 +671,8 @@ class ActionEval { } wrapCodeInFilter(code: string) { // TODO: :-p - let ents = this.qr.entities; - let ents2 = this.oldState.x?.entities; + const ents = this.entities; + const ents2 = this.oldState.x?.entities; if (ents && ents2) { let lo = ents[0].id; let hi = ents[ents.length - 1].id; @@ -680,8 +685,9 @@ class ActionEval { } return code; } - generateCodeForField(sys: System, action: Action, qr: QueryResult, - fieldName: string, bitofs: number, canwrite: boolean): string { + generateCodeForField(fieldName: string, bitofs: number, canwrite: boolean): string { + const action = this.action; + const qr = this.jr || this.qr; // TODO: why not both! var component: ComponentType; var qualified = false; @@ -701,7 +707,7 @@ class ActionEval { // see if all entities have the same constant value // TODO: should be done somewhere else? let constValues = new Set(); - for (let e of qr.entities) { + for (let e of this.entities) { let constVal = e.consts[mksymbol(component, fieldName)]; constValues.add(constVal); // constVal === undefined is allowed } @@ -725,17 +731,17 @@ class ActionEval { let ident = this.dialect.fieldsymbol(component, field, bitofs); if (qualified) { return this.dialect.absolute(ident); - } else if (qr.entities.length == 1) { + } else if (this.entities.length == 1) { return this.dialect.absolute(ident); } else { let eidofs = range.elo - qr.entities[0].id; // TODO // TODO: eidofs? let ir; - if (this.scope.state.x?.intersection(this.qr)) { + if (this.scope.state.x?.intersection(qr)) { ir = this.scope.state.x; eidofs -= this.scope.state.xofs; } - else if (this.scope.state.y?.intersection(this.qr)) { + else if (this.scope.state.y?.intersection(qr)) { ir = this.scope.state.y; eidofs -= this.scope.state.yofs; } @@ -783,7 +789,7 @@ export class EntityScope implements SourceLocated { tempSize = 0; maxTempBytes = 0; resources = new Set(); - state = new ActionState(); + state = new ActionCPUState(); isDemo = false; filePath = ''; @@ -864,6 +870,7 @@ export class EntityScope implements SourceLocated { //console.log(i,array,cfname); } } + // TODO: cull unused entity fields allocateSegment(segment: DataSegment, readonly: boolean) { let fields: FieldArray[] = Object.values(segment.fieldranges); // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); From a66d060c082b3d5ac771d281f6d78b1b1f82c47d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 16:04:48 -0600 Subject: [PATCH 038/104] ecs: scope events --- src/common/ecs/compiler.ts | 7 +++++-- src/common/ecs/ecs.ts | 28 ++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 65394149..7e20c26f 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -294,7 +294,7 @@ export class ECSCompiler extends Tokenizer { scope.filePath = this.path; this.currentScope = scope; let cmd; - while ((cmd = this.expectTokens(['using', 'entity', 'scope', 'comment', 'end']).str) != 'end') { + while ((cmd = this.expectTokens(['end', 'using', 'entity', 'scope', 'comment', 'on']).str) != 'end') { if (cmd == 'using') { this.parseScopeUsing(); } @@ -307,6 +307,9 @@ export class ECSCompiler extends Tokenizer { if (cmd == 'comment') { this.expectTokenTypes([ECSTokenType.CodeFragment]); } + if (cmd == 'on') { + this.currentScope.addAction(this.annotate(() => this.parseAction())); + } } this.currentScope = scope.parent || null; return scope; @@ -315,7 +318,7 @@ export class ECSCompiler extends Tokenizer { parseScopeUsing() { let syslist = this.parseList(this.parseSystemRef, ','); for (let sys of syslist) { - this.currentScope?.systems.push(sys); + this.currentScope?.addUsingSystem(sys); } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 1fb707dd..05e49631 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -647,6 +647,14 @@ class ActionEval { let bitofs = parseInt(args[1] || '0'); return this.generateCodeForField(fieldName, bitofs, canwrite); } + __index(args: string[]) { + let ident = args[0]; // TODO? + if (this.entities.length == 1) { + return this.dialect.absolute(ident); + } else { + return this.dialect.indexed_x(ident, 0); //TODO? + } + } __use(args: string[]) { return this.scope.includeResource(args[0]); } @@ -685,7 +693,7 @@ class ActionEval { } return code; } - generateCodeForField(fieldName: string, bitofs: number, canwrite: boolean): string { + generateCodeForField(fieldName: string, bitofs: number, canWrite: boolean): string { const action = this.action; const qr = this.jr || this.qr; // TODO: why not both! @@ -707,12 +715,14 @@ class ActionEval { // see if all entities have the same constant value // TODO: should be done somewhere else? let constValues = new Set(); + let isConst = false; for (let e of this.entities) { let constVal = e.consts[mksymbol(component, fieldName)]; + if (constVal !== undefined) isConst = true; constValues.add(constVal); // constVal === undefined is allowed } // can't write to constant - if (constValues.size > 0 && canwrite) + if (isConst && canWrite) throw new ECSError(`can't write to constant field ${fieldName}`, action); // is it a constant? if (constValues.size == 1) { @@ -781,6 +791,7 @@ export class EntityScope implements SourceLocated { childScopes: EntityScope[] = []; systems: System[] = []; entities: Entity[] = []; + events: Action[] = []; bss = new DataSegment(); rodata = new DataSegment(); code = new CodeSegment(); @@ -813,6 +824,12 @@ export class EntityScope implements SourceLocated { this.entities.push(entity); return entity; } + addUsingSystem(system: System) { + this.systems.push(system); + } + addAction(action: Action) { + this.events.push(action); + } *iterateEntityFields(entities: Entity[]) { for (let i = 0; i < entities.length; i++) { let e = entities[i]; @@ -1057,11 +1074,18 @@ export class EntityScope implements SourceLocated { return symbol; } analyzeEntities() { + this.buildLocalSystem(); this.buildSegments(); this.allocateSegment(this.bss, false); this.allocateSegment(this.rodata, true); this.allocateROData(this.rodata); } + buildLocalSystem() { + if (this.events.length) { + let sys : System = { name: this.name, actions: this.events }; + this.addUsingSystem(this.em.defineSystem(sys)); + } + } generateCode() { this.tempOffset = this.maxTempBytes = 0; // TODO: main scope? From 51f0f6ffea2268cefb58abcb819eeb43d904fd06 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 16:44:43 -0600 Subject: [PATCH 039/104] ecs: entity.lookup --- src/common/ecs/ecs.ts | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 05e49631..7e489bc2 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -267,8 +267,8 @@ __BRK: comment(s: string) { return `\n;;; ${s}\n`; } - absolute(ident: string) { - return ident; + absolute(ident: string, offset?: number) { + return this.addOffset(ident, offset || 0); } addOffset(ident: string, offset: number) { if (offset > 0) return `${ident}+${offset}`; @@ -698,16 +698,26 @@ class ActionEval { const qr = this.jr || this.qr; // TODO: why not both! var component: ComponentType; - var qualified = false; + var baseLookup = false; + let entities: Entity[]; // is qualified field? - if (fieldName.indexOf(':') > 0) { + if (fieldName.indexOf('.') > 0) { + let [entname, fname] = fieldName.split('.'); + let ent = this.scope.getEntityByName(entname); + if (ent == null) throw new ECSError(`no entity named "${entname}" in this scope`, action); + component = this.em.singleComponentWithFieldName(this.qr.atypes, fname, action); + fieldName = fname; + entities = [ent]; + } else if (fieldName.indexOf(':') > 0) { let [cname, fname] = fieldName.split(':'); component = this.em.getComponentByName(cname); - fieldName = fname; - qualified = true; if (component == null) throw new ECSError(`no component named "${cname}"`, action) + entities = this.entities; + fieldName = fname; + baseLookup = true; } else { component = this.em.singleComponentWithFieldName(qr.atypes, fieldName, action); + entities = this.entities; } // find archetypes let field = component.fields.find(f => f.name == fieldName); @@ -716,7 +726,7 @@ class ActionEval { // TODO: should be done somewhere else? let constValues = new Set(); let isConst = false; - for (let e of this.entities) { + for (let e of entities) { let constVal = e.consts[mksymbol(component, fieldName)]; if (constVal !== undefined) isConst = true; constValues.add(constVal); // constVal === undefined is allowed @@ -739,10 +749,10 @@ class ActionEval { if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO // TODO: dialect let ident = this.dialect.fieldsymbol(component, field, bitofs); - if (qualified) { - return this.dialect.absolute(ident); - } else if (this.entities.length == 1) { + if (baseLookup) { return this.dialect.absolute(ident); + } else if (entities.length == 1) { + return this.dialect.absolute(ident, entities[0].id - qr.entities[0].id); } else { let eidofs = range.elo - qr.entities[0].id; // TODO // TODO: eidofs? @@ -830,6 +840,9 @@ export class EntityScope implements SourceLocated { addAction(action: Action) { this.events.push(action); } + getEntityByName(name: string) { + return this.entities.find(e => e.name == name); + } *iterateEntityFields(entities: Entity[]) { for (let i = 0; i < entities.length; i++) { let e = entities[i]; From 01de3b8d6b585fd119640539545cbf6e5a937e1b Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 17:14:03 -0600 Subject: [PATCH 040/104] ecs: local system --- src/common/ecs/compiler.ts | 8 +++++--- src/common/ecs/ecs.ts | 11 ----------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 7e20c26f..b2220f0b 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -294,7 +294,7 @@ export class ECSCompiler extends Tokenizer { scope.filePath = this.path; this.currentScope = scope; let cmd; - while ((cmd = this.expectTokens(['end', 'using', 'entity', 'scope', 'comment', 'on']).str) != 'end') { + while ((cmd = this.expectTokens(['end', 'using', 'entity', 'scope', 'comment', 'system']).str) != 'end') { if (cmd == 'using') { this.parseScopeUsing(); } @@ -307,8 +307,10 @@ export class ECSCompiler extends Tokenizer { if (cmd == 'comment') { this.expectTokenTypes([ECSTokenType.CodeFragment]); } - if (cmd == 'on') { - this.currentScope.addAction(this.annotate(() => this.parseAction())); + if (cmd == 'system') { + let sys = this.annotate(() => this.parseSystem()); + this.em.defineSystem(sys); + this.currentScope.addUsingSystem(sys); } } this.currentScope = scope.parent || null; diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 7e489bc2..a5d1519e 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -801,7 +801,6 @@ export class EntityScope implements SourceLocated { childScopes: EntityScope[] = []; systems: System[] = []; entities: Entity[] = []; - events: Action[] = []; bss = new DataSegment(); rodata = new DataSegment(); code = new CodeSegment(); @@ -837,9 +836,6 @@ export class EntityScope implements SourceLocated { addUsingSystem(system: System) { this.systems.push(system); } - addAction(action: Action) { - this.events.push(action); - } getEntityByName(name: string) { return this.entities.find(e => e.name == name); } @@ -1087,18 +1083,11 @@ export class EntityScope implements SourceLocated { return symbol; } analyzeEntities() { - this.buildLocalSystem(); this.buildSegments(); this.allocateSegment(this.bss, false); this.allocateSegment(this.rodata, true); this.allocateROData(this.rodata); } - buildLocalSystem() { - if (this.events.length) { - let sys : System = { name: this.name, actions: this.events }; - this.addUsingSystem(this.em.defineSystem(sys)); - } - } generateCode() { this.tempOffset = this.maxTempBytes = 0; // TODO: main scope? From 513fb85b8ec26645a3a804d89c44e9019834060d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 18:25:36 -0600 Subject: [PATCH 041/104] ecs: fixed import --- src/common/ecs/compiler.ts | 10 +++++++++- src/common/ecs/ecs.ts | 10 +++++++--- src/common/ecs/main.ts | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index b2220f0b..4f74380f 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -60,8 +60,14 @@ export class ECSCompiler extends Tokenizer { if (!this.em.imported[path]) { // already imported? let text = this.getImportFile && this.getImportFile(path); if (!text) this.compileError(`I can't find the import file "${path}".`); - new ECSCompiler(this.em).parseFile(path, text); this.em.imported[path] = true; + let comp = new ECSCompiler(this.em); + try { + comp.parseFile(text, path); + } catch (e) { + for (e of comp.errors) this.errors.push(e); + throw e; + } } } @@ -390,6 +396,8 @@ export class ECSCompiler extends Tokenizer { export() { let src = new SourceFileExport(); src.debug_file(this.path); + for (let path of Object.keys(this.em.imported)) + src.debug_file(path); this.exportToFile(src); return src.toString(); } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index a5d1519e..f3ac0c21 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1138,13 +1138,16 @@ export class EntityManager { constructor(public readonly dialect: Dialect_CA65) { } newScope(name: string, parent?: EntityScope) { + let existing = this.topScopes[name]; + if (existing && !existing.isDemo) + throw new ECSError(`scope ${name} already defined`, existing); let scope = new EntityScope(this, this.dialect, name, parent); - if (this.topScopes[name]) throw new ECSError(`scope ${name} already defined`); if (!parent) this.topScopes[name] = scope; return scope; } defineComponent(ctype: ComponentType) { - if (this.components[ctype.name]) throw new ECSError(`component ${ctype.name} already defined`); + let existing = this.components[ctype.name]; + if (existing) 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] = []; @@ -1153,7 +1156,8 @@ export class EntityManager { return this.components[ctype.name] = ctype; } defineSystem(system: System) { - if (this.systems[system.name]) throw new ECSError(`system ${system.name} already defined`); + let existing = this.systems[system.name]; + if (existing) throw new ECSError(`system ${system.name} already defined`, existing); for (let a of system.actions) { let event = a.event; let list = this.event2systems[event]; diff --git a/src/common/ecs/main.ts b/src/common/ecs/main.ts index 573505dc..20057603 100644 --- a/src/common/ecs/main.ts +++ b/src/common/ecs/main.ts @@ -1,10 +1,10 @@ import { readFileSync } from "fs"; import { ECSCompiler } from "./compiler"; -import { SourceFileExport } from "./ecs"; +import { Dialect_CA65, EntityManager, SourceFileExport } from "./ecs"; class ECSMain { - compiler = new ECSCompiler(); + compiler = new ECSCompiler(new EntityManager(new Dialect_CA65())); // TODO constructor(readonly args: string[]) { } From dbc84c94b209cf48ff0350b2b20f7e2a7de0ebef Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 19:38:35 -0600 Subject: [PATCH 042/104] ecs: no error if select is empty --- src/common/ecs/compiler.ts | 4 +++- src/common/ecs/ecs.ts | 29 ++++++++++++++--------------- src/worker/tools/cc65.ts | 8 ++++++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 4f74380f..1bd61d6a 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -232,7 +232,6 @@ export class ECSCompiler extends Tokenizer { join = this.parseQuery(); } let emits; - let limit; if (this.peekToken().str == 'limit') { this.consumeToken(); if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } @@ -350,6 +349,9 @@ export class ECSCompiler extends Tokenizer { if (!field) { this.internalError(); throw new Error(); } this.expectToken('='); let value = this.parseDataValue(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); } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index f3ac0c21..dc2ecf57 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -100,9 +100,7 @@ export interface ComponentType extends SourceLocated { export interface Query extends SourceLocated { include: ComponentType[]; // TODO: make ComponentType - listen?: ComponentType[]; exclude?: ComponentType[]; - updates?: ComponentType[]; limit?: number; } @@ -520,9 +518,9 @@ class ActionEval { if (q) this.qr = new EntitySet(scope, q); else this.qr = new EntitySet(scope, undefined, [], []); this.entities = this.qr.entities; - let query = (this.action as ActionWithQuery).query; - if (query && this.entities.length == 0) - throw new ECSError(`query doesn't match any entities`, query); // TODO + //let query = (this.action as ActionWithQuery).query; + //TODO? if (query && this.entities.length == 0) + //throw new ECSError(`query doesn't match any entities`, query); // TODO } begin() { let state = this.scope.state = Object.assign({}, this.scope.state); @@ -569,7 +567,8 @@ class ActionEval { const tag_re = /\{\{(.+?)\}\}/g; const label_re = /@(\w+)\b/g; - if (this.entities.length == 0 && this.action.select == 'if') + const allowEmpty = ['if','foreach','join']; + if (this.entities.length == 0 && allowEmpty.includes(this.action.select)) return ''; let action = this.action; @@ -581,10 +580,11 @@ class ActionEval { // TODO: detect cycles // TODO: "source"? // TODO: what if only 1 item? + // TODO: what if join is subset of items? if (action.select == 'join' && this.jr) { let jentities = this.jr.entities; - if (jentities.length == 0) - throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO + if (jentities.length == 0) return ''; + // TODO? throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO let joinfield = this.getJoinField(action, this.qr.atypes, this.jr.atypes); // TODO: what if only 1 item? // TODO: should be able to access fields via Y reg @@ -801,6 +801,7 @@ export class EntityScope implements SourceLocated { childScopes: EntityScope[] = []; systems: System[] = []; entities: Entity[] = []; + fieldtypes: { [name: string]: 'init' | 'const' } = {}; bss = new DataSegment(); rodata = new DataSegment(); code = new CodeSegment(); @@ -1017,16 +1018,15 @@ export class EntityScope implements SourceLocated { setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); e.consts[mksymbol(component, fieldName)] = value; - if (this.em.symbols[mksymbol(component, fieldName)] == 'init') - throw new ECSError(`Can't mix const and init values for a component field`, e); - this.em.symbols[mksymbol(component, fieldName)] = 'const'; + this.fieldtypes[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); e.inits[mkscopesymbol(this, component, fieldName)] = value; - if (this.em.symbols[mksymbol(component, fieldName)] == 'const') - throw new ECSError(`Can't mix const and init values for a component field`, e); - this.em.symbols[mksymbol(component, fieldName)] = 'init'; + this.fieldtypes[mksymbol(component, fieldName)] = 'init'; + } + isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' { + return this.fieldtypes[mksymbol(component, fieldName)]; } generateCodeForEvent(event: string): string { // find systems that respond to event @@ -1128,7 +1128,6 @@ export class EntityManager { components: { [name: string]: ComponentType } = {}; systems: { [name: string]: System } = {}; topScopes: { [name: string]: EntityScope } = {}; - symbols: { [name: string]: 'init' | 'const' } = {}; event2systems: { [event: string]: System[] } = {}; name2cfpairs: { [cfname: string]: ComponentFieldPair[] } = {}; mainPath: string = ''; diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index 77b40837..d90bbba4 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -27,12 +27,14 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) { var segMatch = /[.]segment\s+"(\w+)"/i; var lines = []; var linenum = 0; + let curpath = ''; // TODO: only does .c functions, not all .s files for (var line of code.split(re_crlf)) { var dbgm = dbgLineMatch.exec(line); if (dbgm && dbgm[1]) { var dbgtype = dbgm[4]; offset = parseInt(dbgm[1], 16); + curpath = dbgm[5]; if (dbgtype == 'func') { var funcm = funcLineMatch.exec(dbgm[6]); if (funcm) { @@ -45,9 +47,9 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) { } } if (dbg && dbgm && dbgtype == 'line') { - //console.log(dbgm[6], offset, segofs); + //console.log(dbgm[5], dbgm[6], offset, segofs); lines.push({ - // TODO: sourcefile + path: dbgm[5], line: parseInt(dbgm[6]), offset: offset + segofs, insns: null @@ -65,6 +67,7 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) { linenum--; } else if (!dbg) { lines.push({ + path: curpath, line: linenum, offset: offset + segofs, insns: insns, @@ -219,6 +222,7 @@ export function linkLD65(step: BuildStep): BuildStepResult { var asmlines = []; // TODO: parseCA65Listing(lstout, symbolmap, params, false); var srclines = parseCA65Listing(lstout, symbolmap, params, true); putWorkFile(fn, lstout); + // TODO: multiple source files // TODO: you have to get rid of all source lines to get asm listing listings[fn] = { asmlines: srclines.length ? asmlines : null, From 40ca02fc6b27cd5596fba1ea00820cbb0422b606 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 3 Feb 2022 22:38:35 -0600 Subject: [PATCH 043/104] ecs: separate temp regions for now --- src/common/ecs/compiler.ts | 3 +-- src/common/ecs/ecs.ts | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 1bd61d6a..bfb8b3b0 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -65,8 +65,7 @@ export class ECSCompiler extends Tokenizer { try { comp.parseFile(text, path); } catch (e) { - for (e of comp.errors) this.errors.push(e); - throw e; + for (var err of comp.errors) this.errors.push(err); } } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index dc2ecf57..fd654973 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -251,7 +251,8 @@ export class Dialect_CA65 { ` readonly FOOTER = ` .segment "VECTORS" -VecNMI: .word Main::__NMI +Return: .word $6060 +VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK ` @@ -505,6 +506,7 @@ class ActionEval { jr: EntitySet | undefined; oldState : ActionCPUState; entities : Entity[]; + tmplabel = ''; constructor( readonly scope: EntityScope, @@ -666,7 +668,8 @@ class ActionEval { if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, this.action); if (!this.sys.tempbytes) throw new ECSError(`this system has no locals`, this.action); if (tempinc < 0 || tempinc >= this.sys.tempbytes) throw new ECSError(`this system only has ${this.sys.tempbytes} locals`, this.action); - return `TEMP+${this.scope.tempOffset}+${tempinc}`; + return `${this.tmplabel}+${tempinc}`; + //return `TEMP+${this.scope.tempOffset}+${tempinc}`; } wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { // TODO: check ents @@ -1044,7 +1047,12 @@ export class EntityScope implements SourceLocated { for (let sys of systems) { // TODO: does this work if multiple actions? // TODO: should 'emits' be on action? - if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); + // TODO: share storage + //if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); + let tmplabel = `${sys.name}_tmp`; + if (sys.tempbytes) this.bss.allocateBytes(tmplabel, sys.tempbytes); + //this.allocateTempBytes(1); + let numActions = 0; for (let action of sys.actions) { if (action.event == event) { if (action.emits) { @@ -1060,15 +1068,17 @@ export class EntityScope implements SourceLocated { } // TODO: use Tokenizer so error msgs are better let codeeval = new ActionEval(this, sys, action); + codeeval.tmplabel = tmplabel; codeeval.begin(); s += this.dialect.comment(``); // TODO s += codeeval.codeToString(); s += this.dialect.comment(``); // TODO: check that this happens once? codeeval.end(); + numActions++; } } - if (sys.tempbytes) this.allocateTempBytes(-sys.tempbytes); + // TODO: if (sys.tempbytes && numActions) this.allocateTempBytes(-sys.tempbytes); } return s; } From 186baf8c13e4ac1b60926c2e41137c68126fdfbd Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 4 Feb 2022 08:25:31 -0600 Subject: [PATCH 044/104] fixed ca65 listings --- src/ide/project.ts | 4 ++++ src/ide/views/editors.ts | 4 +++- src/worker/tools/cc65.ts | 22 +++++++++++++--------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/ide/project.ts b/src/ide/project.ts index 0c11974a..5bd7737d 100644 --- a/src/ide/project.ts +++ b/src/ide/project.ts @@ -407,6 +407,10 @@ export class CodeProject { var fnprefix = getFilenamePrefix(this.stripLocalPath(path)); var listings = this.getListings(); var onlyfile = null; + for (var lstfn in listings) { + if (lstfn == path) + return listings[lstfn]; + } for (var lstfn in listings) { onlyfile = lstfn; if (getFilenamePrefix(lstfn) == fnprefix) { diff --git a/src/ide/views/editors.ts b/src/ide/views/editors.ts index 7f922d73..513702c1 100644 --- a/src/ide/views/editors.ts +++ b/src/ide/views/editors.ts @@ -273,6 +273,7 @@ export class SourceEditor implements ProjectView { this.editor.clearGutter("gutter-clock"); var lstlines = this.sourcefile.lines || []; for (var info of lstlines) { + //if (info.path && info.path != this.path) continue; if (info.offset >= 0) { this.setGutter("gutter-offset", info.line-1, hex(info.offset&0xffff,4)); } @@ -574,7 +575,7 @@ export class ListingView extends DisassemblerView implements ProjectView { refreshListing() { // lookup corresponding assemblyfile for this file, using listing var lst = current_project.getListingForFile(this.path); - // TODO? + // TODO? this.assemblyfile = lst && (lst.assemblyfile || lst.sourcefile); } @@ -584,6 +585,7 @@ export class ListingView extends DisassemblerView implements ProjectView { if (!this.assemblyfile) return; var asmtext = this.assemblyfile.text; var disasmview = this.getDisasmView(); + // TODO: sometimes it picks one without a text file disasmview.setValue(asmtext); // go to PC if (!platform.saveState) return; diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index d90bbba4..8aff1e0d 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -1,5 +1,5 @@ -import { getRootBasePlatform } from "../../common/util"; +import { getFilenamePrefix, getRootBasePlatform } from "../../common/util"; import { CodeListingMap, WorkerError } from "../../common/workertypes"; import { re_crlf, BuildStepResult, anyTargetChanged, execMain, gatherFiles, msvcErrorMatcher, populateEntry, populateExtraFiles, populateFiles, print_fn, putWorkFile, setupFS, staleFiles, BuildStep, emglobal, loadNative, moduleInstFn, fixParamsWithDefines, store, makeErrorMatcher } from "../workermain"; import { EmscriptenModule } from "../workermain" @@ -18,14 +18,15 @@ import { EmscriptenModule } from "../workermain" 00B726 1 xx xx IBSECSZ: .res 2 00BA2F 1 2A 2B E8 2C HEX "2A2BE82C2D2E2F303132F0F133343536" */ -function parseCA65Listing(code: string, symbols, params, dbg: boolean) { +function parseCA65Listing(code: string, symbols, params, dbg: boolean, listings?: CodeListingMap) { var segofs = 0; var offset = 0; var dbgLineMatch = /^([0-9A-F]+)([r]?)\s+(\d+)\s+[.]dbg\s+(\w+), "([^"]+)", (.+)/; var funcLineMatch = /"(\w+)", (\w+), "(\w+)"/; var insnLineMatch = /^([0-9A-F]+)([r]?)\s{1,2}(\d+)\s{1,2}([0-9A-Frx ]{11})\s+(.*)/; var segMatch = /[.]segment\s+"(\w+)"/i; - var lines = []; + var origlines = []; + var lines = origlines; var linenum = 0; let curpath = ''; // TODO: only does .c functions, not all .s files @@ -35,6 +36,12 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) { var dbgtype = dbgm[4]; offset = parseInt(dbgm[1], 16); curpath = dbgm[5]; + // new file? + if (curpath && listings) { + let l = listings[curpath]; + if (!l) l = listings[curpath] = {lines:[]}; + lines = l.lines; + } if (dbgtype == 'func') { var funcm = funcLineMatch.exec(dbgm[6]); if (funcm) { @@ -93,7 +100,7 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) { } } } - return lines; + return origlines; } export function assembleCA65(step: BuildStep): BuildStepResult { @@ -220,13 +227,10 @@ export function linkLD65(step: BuildStep): BuildStepResult { var lstout = FS.readFile(fn, { encoding: 'utf8' }); lstout = lstout.split('\n\n')[1] || lstout; // remove header var asmlines = []; // TODO: parseCA65Listing(lstout, symbolmap, params, false); - var srclines = parseCA65Listing(lstout, symbolmap, params, true); + var srclines = parseCA65Listing(lstout, symbolmap, params, true, listings); putWorkFile(fn, lstout); - // TODO: multiple source files - // TODO: you have to get rid of all source lines to get asm listing listings[fn] = { - asmlines: srclines.length ? asmlines : null, - lines: srclines.length ? srclines : asmlines, + lines: [], text: lstout }; } From 2d5eec8ba319608551b8d0683e33f83858c49b32 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 4 Feb 2022 11:45:14 -0600 Subject: [PATCH 045/104] ecs: moved everything to dialect --- src/common/ecs/compiler.ts | 12 +++-- src/common/ecs/ecs.ts | 97 +++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index bfb8b3b0..6d9a63af 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -286,8 +286,10 @@ export class ECSCompiler extends Tokenizer { let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); let code = tok.str.substring(3, tok.str.length-3); let lines = code.split('\n'); + let re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines for (let i=0; i 255) throw new ECSError(`out of range byte ${b}`); + return `.byte ${b}` + } else { + if (b.bitofs == 0) return `.byte <${b.symbol}` + else if (b.bitofs == 8) return `.byte >${b.symbol}` + else return `.byte (${b.symbol} >> ${b.bitofs})` // TODO? + } + } } // TODO: merge with Dialect? export class SourceFileExport { lines: string[] = []; - comment(text: string) { - this.lines.push(';' + text); - } - segment(seg: string, segtype: 'rodata' | 'bss' | 'code') { - if (segtype == 'bss') { - this.lines.push(`.zeropage`); // TODO - } else { - this.lines.push(`.code`); - } - } - label(sym: string) { - this.lines.push(`${sym}:`); - } - byte(b: number | ConstByte | undefined) { - if (b === undefined) { - this.lines.push(` .res 1`); - } else if (typeof b === 'number') { - if (b < 0 || b > 255) throw new ECSError(`out of range byte ${b}`); - this.lines.push(` .byte ${b}`) - } else { - if (b.bitofs == 0) this.lines.push(` .byte <${b.symbol}`) - else if (b.bitofs == 8) this.lines.push(` .byte >${b.symbol}`) - else this.lines.push(` .byte (${b.symbol} >> ${b.bitofs})`) // TODO? - } + line(s: string) { + this.text(s); } text(s: string) { for (let l of s.split('\n')) this.lines.push(l); } - debug_file(path: string) { - this.lines.push(` .dbg file, "${path}", 0, 0`); - } - debug_line(path: string, line: number) { - this.lines.push(` .dbg line, "${path}", ${line}`); - } - startScope(name: string) { - this.lines.push(` .scope ${name}`) - } - endScope(name: string) { - this.lines.push(` .endscope`) - this.lines.push(`${name}__Start = ${name}::__Start`) - // TODO: scope__start = scope::start - } toString() { return this.lines.join('\n'); } @@ -385,13 +384,14 @@ class DataSegment { this.initdata[ofs + i] = bytes[i]; } } - dump(file: SourceFileExport) { + dump(file: SourceFileExport, dialect: Dialect_CA65) { for (let i = 0; i < this.size; i++) { let syms = this.ofs2sym.get(i); if (syms) { - for (let sym of syms) file.label(sym); + for (let sym of syms) + file.line(dialect.label(sym)); } - file.byte(this.initdata[i]); + file.line(dialect.byte(this.initdata[i])); } } // TODO: move cfname functions in here too @@ -1116,20 +1116,21 @@ export class EntityScope implements SourceLocated { dump(file: SourceFileExport) { this.analyzeEntities(); this.generateCode(); - file.startScope(this.name); - file.segment(`${this.name}_DATA`, 'bss'); + let dialect = this.dialect; + file.line(dialect.startScope(this.name)); + file.line(dialect.segment(`${this.name}_DATA`, 'bss')); if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); - this.bss.dump(file); - file.segment(`${this.name}_RODATA`, 'rodata'); - this.rodata.dump(file); + this.bss.dump(file, dialect); + file.line(dialect.segment(`${this.name}_RODATA`, 'rodata')); + this.rodata.dump(file, dialect); //file.segment(`${this.name}_CODE`, 'code'); - file.label('__Start'); + file.line(dialect.label('__Start')); this.code.dump(file); for (let subscope of this.childScopes) { // TODO: overlay child BSS segments subscope.dump(file); } - file.endScope(this.name); + file.line(dialect.endScope(this.name)); } } From 1f64a623aa372c8bf898597c2d9dc8c2eb8bc120 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 4 Feb 2022 14:51:53 -0600 Subject: [PATCH 046/104] ecs: fixed eidofs --- src/common/ecs/ecs.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 0ab797c8..fdaec5d6 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -53,6 +53,8 @@ source/if query? crazy idea -- full expansion, then relooper +how to avoid cycle crossing for critical code and data? + */ @@ -555,7 +557,7 @@ class ActionEval { state.x = int; this.entities = int.entities; // TODO? } - } else { + } else if (this.action.select == 'with') { if (this.qr.entities.length != 1) throw new ECSError(`query outside of loop must match exactly one entity`, this.action); } @@ -752,13 +754,12 @@ class ActionEval { if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO // TODO: dialect let ident = this.dialect.fieldsymbol(component, field, bitofs); + let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? if (baseLookup) { return this.dialect.absolute(ident); } else if (entities.length == 1) { - return this.dialect.absolute(ident, entities[0].id - qr.entities[0].id); + return this.dialect.absolute(ident, eidofs); } else { - let eidofs = range.elo - qr.entities[0].id; // TODO - // TODO: eidofs? let ir; if (this.scope.state.x?.intersection(qr)) { ir = this.scope.state.x; From 3a77e31e4754e5d8456ab4862912b638bfa62d8d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 4 Feb 2022 19:48:08 -0600 Subject: [PATCH 047/104] ecs: baseoffset --- src/common/ecs/compiler.ts | 12 ++++++++---- src/common/ecs/ecs.ts | 23 +++++++++++++++++++---- src/common/tokenizer.ts | 3 +++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 6d9a63af..b3b96948 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,7 +1,7 @@ import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; -import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; +import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -129,14 +129,18 @@ export class ECSCompiler extends Tokenizer { if (this.peekToken().str == '[') { return { dtype: 'ref', query: this.parseQuery() } as RefType; } - if (this.peekToken().str == 'array') { + if (this.ifToken('array')) { let index : IntType | undefined = undefined; - this.expectToken('array'); if (this.peekToken().type == ECSTokenType.Integer) { index = this.parseDataType() as IntType; } this.expectToken('of'); - return { dtype: 'array', index, elem: this.parseDataType() } as ArrayType; + let elem = this.parseDataType(); + let baseoffset; + if (this.ifToken('baseoffset')) { + baseoffset = this.expectInteger(); + } + return { dtype: 'array', index, elem, baseoffset } as ArrayType; } this.internalError(); throw new Error(); } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index fdaec5d6..83ee871d 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -58,6 +58,7 @@ how to avoid cycle crossing for critical code and data? */ +import { data } from "jquery"; import { SourceLocated, SourceLocation } from "../workertypes"; export class ECSError extends Error { @@ -153,6 +154,7 @@ export interface ArrayType { dtype: 'array' elem: DataType index?: DataType + baseoffset?: number } export interface RefType { @@ -651,8 +653,18 @@ class ActionEval { let bitofs = parseInt(args[1] || '0'); return this.generateCodeForField(fieldName, bitofs, canwrite); } + __base(args: string[]) { + // TODO: refactor into generateCode.. + let fieldName = args[0]; + let bitofs = parseInt(args[1] || '0'); + let component = this.em.singleComponentWithFieldName(this.qr.atypes, fieldName, this.action); + let field = component.fields.find(f => f.name == fieldName); + if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, this.action); + return this.dialect.fieldsymbol(component, field, bitofs); + } __index(args: string[]) { - let ident = args[0]; // TODO? + // TODO: check select type and if we actually have an index... + let ident = args[0]; if (this.entities.length == 1) { return this.dialect.absolute(ident); } else { @@ -727,6 +739,7 @@ class ActionEval { // find archetypes let field = component.fields.find(f => f.name == fieldName); if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, action); + let ident = this.dialect.fieldsymbol(component, field, bitofs); // see if all entities have the same constant value // TODO: should be done somewhere else? let constValues = new Set(); @@ -753,7 +766,6 @@ class ActionEval { let range = this.scope.bss.getFieldRange(component, fieldName) || this.scope.rodata.getFieldRange(component, fieldName); if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO // TODO: dialect - let ident = this.dialect.fieldsymbol(component, field, bitofs); let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? if (baseLookup) { return this.dialect.absolute(ident); @@ -940,24 +952,27 @@ export class EntityScope implements SourceLocated { // is it a bounded array? (TODO) if (f.dtype == 'array' && f.index) { let datasym = this.dialect.datasymbol(c, f, e.id); - let offset = this.bss.allocateBytes(datasym, getFieldLength(f.index)); + let databytes = getFieldLength(f.index); + let offset = this.bss.allocateBytes(datasym, databytes); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); + if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; } } else { // this is a constant // is it a byte array? - if (v instanceof Uint8Array) { + if (v instanceof Uint8Array && f.dtype == 'array') { let datasym = this.dialect.datasymbol(c, f, e.id); segment.allocateInitData(datasym, v); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); + if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; // TODO: } else if (v instanceof Uint16Array) { diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index bb52f568..1dd1c5ba 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -160,6 +160,9 @@ export class Tokenizer { let tok = this.lasttoken = (this.tokens.shift() || this.eof); return tok; } + ifToken(match: string): Token | undefined { + if (this.peekToken().str == match) return this.consumeToken(); + } expectToken(str: string, msg?: string): Token { let tok = this.consumeToken(); let tokstr = tok.str; From 0904e83bf61106cf3957e5cc1e6cbb469e18de77 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 4 Feb 2022 23:56:43 -0600 Subject: [PATCH 048/104] ecs: decoders --- src/codemirror/ecs.js | 6 +- src/common/ecs/compiler.ts | 77 +++++++++++++++---------- src/common/ecs/decoder.ts | 114 +++++++++++++++++++++++++++++++++++++ src/common/ecs/ecs.ts | 20 ++----- src/test/testecs.ts | 5 +- 5 files changed, 171 insertions(+), 51 deletions(-) create mode 100644 src/common/ecs/decoder.ts diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 869f9eab..bf3696f9 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -17,8 +17,8 @@ var keywords1, keywords2; var directives_list = [ - 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', - 'const', 'init', 'locals', + 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', + 'const', 'locals', 'var', 'on', 'do', 'emit', 'limit', 'once', 'foreach', 'with', 'join', 'if', ]; @@ -34,7 +34,7 @@ directives_list.forEach(function (s) { directives.set(s, 'def'); }); keywords_list.forEach(function (s) { directives.set(s, 'keyword'); }); - var opcodes = /^[a-z][a-z][a-z]\b/i; + var opcodes = /^\s[a-z][a-z][a-z]\s/i; var numbers = /^(0x[\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; var tags = /^\{\{.*\}\}/; var comment = /\/\/.*/; diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index b3b96948..d4e96c3a 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,6 +1,7 @@ import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; +import { newDecoder } from "./decoder"; import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { @@ -142,7 +143,7 @@ export class ECSCompiler extends Tokenizer { } return { dtype: 'array', index, elem, baseoffset } as ArrayType; } - this.internalError(); throw new Error(); + this.compileError(`I expected a data type here.`); throw new Error(); } parseDataValue(field: DataField) : DataValue { @@ -170,7 +171,7 @@ export class ECSCompiler extends Tokenizer { } return id; } - this.internalError(); throw new Error(); + this.compileError(`I expected a ${field.dtype} here.`); throw new Error(); } parseDataArray() { @@ -234,17 +235,12 @@ export class ECSCompiler extends Tokenizer { this.expectToken('with'); join = this.parseQuery(); } - let emits; - if (this.peekToken().str == 'limit') { - this.consumeToken(); + if (this.ifToken('limit')) { if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } else query.limit = this.expectInteger(); } - if (this.peekToken().str == 'emit') { - this.consumeToken(); - this.expectToken('('); - emits = this.parseEventList(); - this.expectToken(')'); + if (this.ifToken('cyclecritical')) { + // TODO } let text = this.parseCode(); let action = { text, event, query, join, select }; @@ -336,33 +332,54 @@ export class ECSCompiler extends Tokenizer { parseEntity() : Entity { if (!this.currentScope) { this.internalError(); throw new Error(); } - let name = ''; + let entname = ''; if (this.peekToken().type == TokenType.Ident) { - name = this.expectIdent().str; + entname = this.expectIdent().str; } let etype = this.parseEntityArchetype(); - let e = this.currentScope.newEntity(etype); - e.name = name; + let entity = this.currentScope.newEntity(etype); + entity.name = entname; let cmd; // TODO: remove init? - while ((cmd = this.expectTokens(['const', 'init', 'var', 'end']).str) != 'end') { + while ((cmd = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') { if (cmd == 'var') cmd = 'init'; - // TODO: check data types - let name = this.expectIdent().str; - let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], 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); - if (!field) { this.internalError(); throw new Error(); } - this.expectToken('='); - let value = this.parseDataValue(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); + 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); + }); + } else if (cmd == 'decode') { + let decoderid = this.expectIdent().str; + let code = this.expectTokenTypes([ECSTokenType.CodeFragment]).str; + code = code.substring(3, code.length-3); + let decoder = newDecoder(decoderid, code); + 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]; + }); + } + } } - return e; + return entity; + } + + setEntityProperty(e: Entity, name: string, cmd: 'init' | 'const', valuefn: (field: DataField) => DataValue) { + if (!this.currentScope) { this.internalError(); throw new Error(); } + let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], 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); + 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); } parseEntityArchetype() : EntityArchetype { diff --git a/src/common/ecs/decoder.ts b/src/common/ecs/decoder.ts new file mode 100644 index 00000000..8f2a8617 --- /dev/null +++ b/src/common/ecs/decoder.ts @@ -0,0 +1,114 @@ +import { threadId } from "worker_threads"; +import { DataValue, ECSError } from "./ecs"; + +export interface DecoderResult { + properties: {[name: string] : DataValue} +} + +abstract class LineDecoder { + lines : string[][]; + + constructor( + text: string + ) { + // split the text into lines and into tokens + this.lines = text.split('\n').map(s => s.trim()).filter(s => !!s).map(s => s.split(/\s+/)); + } + + decodeBits(s: string, n: number, msbfirst: boolean) { + if (s.length != n) throw new ECSError(`Expected ${n} characters`); + let b = 0; + for (let i=0; i 1) { + console.log(changed, cur, prev); + throw new ECSError(`More than one register change in line ${i+1}: [${changed}]`); + } + let chgidx = changed.length ? changed[0] : regs.length-1; + data[height*2 - i*2 - 1] = regs[chgidx]; + data[height*2 - i*2 - 2] = cur[chgidx]; + prev[chgidx] = cur[chgidx]; + } + return { + properties: { + data + } + } + } +} + +export function newDecoder(name: string, text: string) : LineDecoder | undefined { + let cons = (DECODERS as any)[name]; + if (cons) return new cons(text); +} + +const DECODERS = { + 'vcs_sprite': VCSSpriteDecoder, + 'vcs_versatile': VCSVersatilePlayfieldDecoder, +} diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 83ee871d..fb447351 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -51,6 +51,8 @@ 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? @@ -119,7 +121,6 @@ export interface ActionBase extends SourceLocated { select: SelectType; event: string; text: string; - emits?: string[]; } export interface ActionOnce extends ActionBase { @@ -675,7 +676,8 @@ class ActionEval { return this.scope.includeResource(args[0]); } __emit(args: string[]) { - return this.scope.generateCodeForEvent(args[0]); + let event = args[0]; + return this.scope.generateCodeForEvent(event); } __local(args: string[]) { let tempinc = parseInt(args[0]); @@ -1058,11 +1060,9 @@ export class EntityScope implements SourceLocated { } let s = this.dialect.code(); //s += `\n; event ${event}\n`; - let emitcode: { [event: string]: string } = {}; systems = systems.filter(s => this.systems.includes(s)); for (let sys of systems) { // TODO: does this work if multiple actions? - // TODO: should 'emits' be on action? // TODO: share storage //if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); let tmplabel = `${sys.name}_tmp`; @@ -1071,18 +1071,8 @@ export class EntityScope implements SourceLocated { let numActions = 0; for (let action of sys.actions) { if (action.event == event) { - if (action.emits) { - for (let emit of action.emits) { - if (emitcode[emit]) { - console.log(`already emitted for ${sys.name}:${event}`); - } - //console.log('>', emit); - // TODO: cycles - emitcode[emit] = this.generateCodeForEvent(emit); - //console.log('<', emit, emitcode[emit].length); - } - } // TODO: use Tokenizer so error msgs are better + // TODO: keep event tree let codeeval = new ActionEval(this, sys, action); codeeval.tmplabel = tmplabel; codeeval.begin(); diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 0af4fede..05d78187 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -242,8 +242,7 @@ function testECS() { em.defineSystem({ name: 'frameloop', actions: [ - { text: TEMPLATE1, event: 'start', select: 'with', query: { include: [c_kernel] }, - emits: ['preframe', 'kernel', 'postframe'] } + { text: TEMPLATE1, event: 'start', select: 'with', query: { include: [c_kernel] } } ] }) em.defineSystem({ @@ -313,7 +312,7 @@ end system SimpleKernel locals 8 -on preframe do once [Kernel] --- JUNK_AT_END +on preframe do with [Kernel] --- JUNK_AT_END lda #5 sta #6 Label: From d986cda3b3b838757d5cd9d6107668a7ec4f67db Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 5 Feb 2022 20:43:45 -0600 Subject: [PATCH 049/104] ecs: start tests --- src/common/ecs/ecs.ts | 28 +++++++++++++++++----------- src/test/testecs.ts | 18 ++++++++++++++++++ test/ecs/basic1.ecs | 16 ++++++++++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 test/ecs/basic1.ecs diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index fb447351..6a06cc91 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -194,7 +194,7 @@ interface ComponentFieldPair { export class Dialect_CA65 { - readonly ASM_ITERATE_EACH = ` + ASM_ITERATE_EACH = ` ldx #0 @__each: {{%code}} @@ -204,7 +204,7 @@ export class Dialect_CA65 { @__exit: `; - readonly ASM_ITERATE_JOIN = ` + ASM_ITERATE_JOIN = ` ldy #0 @__each: ldx {{%joinfield}},y @@ -215,14 +215,14 @@ export class Dialect_CA65 { @__exit: `; - readonly ASM_FILTER_RANGE_LO_X = ` + ASM_FILTER_RANGE_LO_X = ` cpx #{{%xofs}} bcc @__skipxlo {{%code}} @__skipxlo: ` - readonly ASM_FILTER_RANGE_HI_X = ` + ASM_FILTER_RANGE_HI_X = ` cpx #{{%xofs}}+{{%ecount}} bcs @__skipxhi {{%code}} @@ -230,7 +230,7 @@ export class Dialect_CA65 { ` // TODO - readonly ASM_MAP_RANGES = ` + ASM_MAP_RANGES = ` txa pha lda {{%mapping}},x @@ -242,26 +242,26 @@ export class Dialect_CA65 { tax `; - readonly INIT_FROM_ARRAY = ` + INIT_FROM_ARRAY = ` ldy #{{%nbytes}} : lda {{%src}}-1,y sta {{%dest}}-1,y dey bne :- ` - readonly HEADER = ` + HEADER = ` .include "vcs-ca65.h" .define PAL 0 .code ` - readonly FOOTER = ` + FOOTER = ` .segment "VECTORS" Return: .word $6060 VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK ` - readonly TEMPLATE_INIT_MAIN = ` + TEMPLATE_INIT_MAIN = ` __NMI: __Reset: __BRK: @@ -433,6 +433,12 @@ class DataSegment { } } +class UninitDataSegment extends DataSegment { +} + +class ConstDataSegment extends DataSegment { +} + function getFieldBits(f: IntType) { let n = f.hi - f.lo + 1; return Math.ceil(Math.log2(n)); @@ -820,8 +826,8 @@ export class EntityScope implements SourceLocated { systems: System[] = []; entities: Entity[] = []; fieldtypes: { [name: string]: 'init' | 'const' } = {}; - bss = new DataSegment(); - rodata = new DataSegment(); + bss = new UninitDataSegment(); + rodata = new ConstDataSegment(); code = new CodeSegment(); componentsInScope = new Set(); tempOffset = 0; diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 05d78187..8bef77f4 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -1,3 +1,4 @@ +import { readdirSync, readFileSync } from "fs"; import { describe } from "mocha"; import { ECSCompiler } from "../common/ecs/compiler"; import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; @@ -363,3 +364,20 @@ describe('Tokenizer', function() { testCompiler(); }); }); + +describe('Compiler', function() { + let testdir = './test/ecs/'; + let files = readdirSync(testdir).filter(f => f.endsWith('.ecs')); + files.forEach((ecspath) => { + let dialect = new Dialect_CA65(); + dialect.HEADER = ''; + dialect.FOOTER = ''; + let em = new EntityManager(dialect); + let compiler = new ECSCompiler(em); + let code = readFileSync(testdir + ecspath, 'utf-8'); + compiler.parseFile(code, ecspath); + let out = new SourceFileExport(); + em.exportToFile(out); + console.log(out.toString()); + }); +}); diff --git a/test/ecs/basic1.ecs b/test/ecs/basic1.ecs new file mode 100644 index 00000000..2221f030 --- /dev/null +++ b/test/ecs/basic1.ecs @@ -0,0 +1,16 @@ + // comment + /* + mju,fjeqowfjqewiofjqe + */ +component Kernel + lines: 0..255 + bgcolor: 0..255 +end + +component Bitmap + data: array of 0..255 +end + +component HasBitmap + bitmap: [Bitmap] +end From e3d54b9150dadb98e8d593e7f050b7579956cc07 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 6 Feb 2022 11:27:08 -0600 Subject: [PATCH 050/104] ecs: don't need archetypematch? --- src/common/ecs/compiler.ts | 2 +- src/common/ecs/ecs.ts | 37 ++++++++++++++++--------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index d4e96c3a..122cce35 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -369,7 +369,7 @@ export class ECSCompiler extends Tokenizer { setEntityProperty(e: Entity, name: string, cmd: 'init' | 'const', valuefn: (field: DataField) => DataValue) { if (!this.currentScope) { this.internalError(); throw new Error(); } - let comps = this.em.componentsWithFieldName([{etype: e.etype, cmatch:e.etype.components}], name); + 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); diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 6a06cc91..a33b58b8 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -182,11 +182,6 @@ interface ConstByte { bitofs: number; } -interface ArchetypeMatch { - etype: EntityArchetype; - cmatch: ComponentType[]; -} - interface ComponentFieldPair { c: ComponentType; f: DataField; @@ -466,11 +461,11 @@ function getPackedFieldSize(f: DataType, constValue?: DataValue): number { } class EntitySet { - atypes: ArchetypeMatch[]; + atypes: EntityArchetype[]; entities: Entity[]; scope; - constructor(scope: EntityScope, query?: Query, a?: ArchetypeMatch[], e?: Entity[]) { + constructor(scope: EntityScope, query?: Query, a?: EntityArchetype[], e?: Entity[]) { this.scope = scope; if (query) { this.atypes = scope.em.archetypesMatching(query); @@ -489,7 +484,7 @@ class EntitySet { } intersection(qr: EntitySet) { let ents = this.entities.filter(e => qr.entities.includes(e)); - let atypes = this.atypes.filter(a1 => qr.atypes.find(a2 => a2.etype == a1.etype)); + let atypes = this.atypes.filter(a1 => qr.atypes.find(a2 => a2 == a1)); return new EntitySet(this.scope, undefined, atypes, ents); } isContiguous() { @@ -799,7 +794,7 @@ class ActionEval { throw new ECSError(`cannot find "${component.name}:${field.name}" in state`, action); } } - getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]): ComponentFieldPair { + getJoinField(action: Action, atypes: EntityArchetype[], jtypes: EntityArchetype[]): ComponentFieldPair { let refs = Array.from(this.scope.iterateArchetypeFields(atypes, (c, f) => f.dtype == 'ref')); // TODO: better error message if (refs.length == 0) throw new ECSError(`cannot find join fields`, action); @@ -874,10 +869,10 @@ export class EntityScope implements SourceLocated { } } } - *iterateArchetypeFields(arch: ArchetypeMatch[], filter?: (c: ComponentType, f: DataField) => boolean) { + *iterateArchetypeFields(arch: EntityArchetype[], filter?: (c: ComponentType, f: DataField) => boolean) { for (let i = 0; i < arch.length; i++) { let a = arch[i]; - for (let c of a.etype.components) { + for (let c of a.components) { for (let f of c.fields) { if (!filter || filter(c, f)) yield { i, c, f }; @@ -885,13 +880,13 @@ export class EntityScope implements SourceLocated { } } } - entitiesMatching(atypes: ArchetypeMatch[]) { + entitiesMatching(atypes: EntityArchetype[]) { let result: Entity[] = []; for (let e of this.entities) { for (let a of atypes) { // TODO: what about subclasses? // TODO: very scary identity ocmpare - if (e.etype === a.etype) { + if (e.etype === a) { result.push(e); break; } @@ -1043,12 +1038,12 @@ export class EntityScope implements SourceLocated { } // TODO: check type/range of value setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); + let c = this.em.singleComponentWithFieldName([e.etype], fieldName, e); e.consts[mksymbol(component, fieldName)] = value; this.fieldtypes[mksymbol(component, fieldName)] = 'const'; } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([{ etype: e.etype, cmatch: [component] }], fieldName, e); + let c = this.em.singleComponentWithFieldName([e.etype], fieldName, e); e.inits[mkscopesymbol(this, component, fieldName)] = value; this.fieldtypes[mksymbol(component, fieldName)] = 'init'; } @@ -1209,20 +1204,20 @@ export class EntityManager { return list.length == q.include.length ? list : []; } archetypesMatching(q: Query) { - let result: ArchetypeMatch[] = []; + let result = new Set(); for (let etype of Object.values(this.archetypes)) { let cmatch = this.componentsMatching(q, etype); if (cmatch.length > 0) { - result.push({ etype, cmatch }); + result.add(etype); } } - return result; + return Array.from(result.values()); } - componentsWithFieldName(atypes: ArchetypeMatch[], fieldName: string) { + componentsWithFieldName(atypes: EntityArchetype[], fieldName: string) { // TODO??? let comps = new Set(); for (let at of atypes) { - for (let c of at.cmatch) { + for (let c of at.components) { for (let f of c.fields) { if (f.name == fieldName) comps.add(c); @@ -1237,7 +1232,7 @@ export class EntityManager { getSystemByName(name: string): System { return this.systems[name]; } - singleComponentWithFieldName(atypes: ArchetypeMatch[], fieldName: string, where: SourceLocated) { + singleComponentWithFieldName(atypes: EntityArchetype[], fieldName: string, where: SourceLocated) { let components = this.componentsWithFieldName(atypes, fieldName); // TODO: use name2cfpairs? if (components.length == 0) { From 109e60ae10416c52dada1633f488efa264f3702c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 6 Feb 2022 11:47:51 -0600 Subject: [PATCH 051/104] ecs: Init, main_init, bss_init --- src/common/ecs/ecs.ts | 39 +++++++++++++-------------------------- src/test/testecs.ts | 2 -- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index a33b58b8..e1c99434 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -244,24 +244,6 @@ export class Dialect_CA65 { dey bne :- ` - HEADER = ` -.include "vcs-ca65.h" -.define PAL 0 -.code -` - FOOTER = ` -.segment "VECTORS" -Return: .word $6060 -VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK -` - TEMPLATE_INIT_MAIN = ` -__NMI: -__Reset: -__BRK: - CLEAN_START -` comment(s: string) { return `\n;;; ${s}\n`; @@ -688,6 +670,9 @@ class ActionEval { return `${this.tmplabel}+${tempinc}`; //return `TEMP+${this.scope.tempOffset}+${tempinc}`; } + __bss_init(args: string[]) { + return this.scope.allocateInitData(this.scope.bss); + } wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { // TODO: check ents // TODO: check segment bounds @@ -1106,14 +1091,15 @@ export class EntityScope implements SourceLocated { this.allocateROData(this.rodata); } generateCode() { + let isMainScope = this.parent == null; this.tempOffset = this.maxTempBytes = 0; - // TODO: main scope? - if (this.name.toLowerCase() == 'main') { - this.code.addCodeFragment(this.dialect.TEMPLATE_INIT_MAIN); + let start; + if (isMainScope) { + this.addUsingSystem(this.em.getSystemByName('Init')); //TODO: what if none? + start = this.generateCodeForEvent('main_init'); + } else { + start = this.generateCodeForEvent('start'); } - let initcode = this.allocateInitData(this.bss); - this.code.addCodeFragment(initcode); - let start = this.generateCodeForEvent('start'); this.code.addCodeFragment(start); for (let sub of Array.from(this.resources.values())) { let code = this.generateCodeForEvent(sub); @@ -1123,6 +1109,9 @@ export class EntityScope implements SourceLocated { dump(file: SourceFileExport) { this.analyzeEntities(); this.generateCode(); + this.dumpCodeTo(file); + } + private dumpCodeTo(file: SourceFileExport) { let dialect = this.dialect; file.line(dialect.startScope(this.name)); file.line(dialect.segment(`${this.name}_DATA`, 'bss')); @@ -1250,12 +1239,10 @@ export class EntityManager { }) } exportToFile(file: SourceFileExport) { - file.text(this.dialect.HEADER); // TODO for (let scope of Object.values(this.topScopes)) { if (!scope.isDemo || scope.filePath == this.mainPath) { scope.dump(file); } } - file.text(this.dialect.FOOTER); // TODO } } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 8bef77f4..59d98f5e 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -370,8 +370,6 @@ describe('Compiler', function() { let files = readdirSync(testdir).filter(f => f.endsWith('.ecs')); files.forEach((ecspath) => { let dialect = new Dialect_CA65(); - dialect.HEADER = ''; - dialect.FOOTER = ''; let em = new EntityManager(dialect); let compiler = new ECSCompiler(em); let code = readFileSync(testdir + ecspath, 'utf-8'); From 2dfe65932be727355fa2004c369b25a0f256907a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 7 Feb 2022 14:58:03 -0600 Subject: [PATCH 052/104] ecs: test files --- src/common/ecs/compiler.ts | 25 ++-- src/common/ecs/ecs.ts | 8 +- src/common/tokenizer.ts | 2 +- src/test/testecs.ts | 27 ++++- src/worker/tools/ecs.ts | 1 + test/ecs/basic1.txt | 0 test/ecs/vcs1.ecs | 235 ++++++++++++++++++++++++++++++++++++ test/ecs/vcs1.txt | 241 +++++++++++++++++++++++++++++++++++++ 8 files changed, 520 insertions(+), 19 deletions(-) create mode 100644 test/ecs/basic1.txt create mode 100644 test/ecs/vcs1.ecs create mode 100644 test/ecs/vcs1.txt diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 122cce35..3e452930 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -2,7 +2,7 @@ import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; import { newDecoder } from "./decoder"; -import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; +import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -15,6 +15,7 @@ export enum ECSTokenType { export class ECSCompiler extends Tokenizer { currentScope: EntityScope | null = null; + debuginfo = false; constructor( public readonly em: EntityManager) @@ -63,6 +64,7 @@ export class ECSCompiler extends Tokenizer { if (!text) this.compileError(`I can't find the import file "${path}".`); this.em.imported[path] = true; let comp = new ECSCompiler(this.em); + comp.debuginfo = this.debuginfo; // TODO: clone compiler try { comp.parseFile(text, path); } catch (e) { @@ -224,11 +226,12 @@ export class ECSCompiler extends Tokenizer { // TODO: unused events? let event = this.expectIdent().str; this.expectToken('do'); - let select = this.expectTokens( - ['once', 'foreach', 'join', 'with', 'if', 'select']).str as SelectType; // TODO: type check? + let select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? let query = undefined; let join = undefined; - if (select != 'once') { + if (select == 'once') { + if (this.peekToken().str == '[') this.compileError(`A "${select}" query can't include a query.`) + } else { query = this.parseQuery(); } if (select == 'join') { @@ -273,12 +276,12 @@ export class ECSCompiler extends Tokenizer { } } - parseEvent() { + parseEventName() { return this.expectIdent().str; } parseEventList() { - return this.parseList(this.parseEvent, ","); + return this.parseList(this.parseEventName, ","); } parseCode(): string { @@ -286,12 +289,16 @@ export class ECSCompiler extends Tokenizer { let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); let code = tok.str.substring(3, tok.str.length-3); let lines = code.split('\n'); - let re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines + if (this.debuginfo) this.addDebugInfo(lines, tok.$loc.line); + return lines.join('\n'); + } + + addDebugInfo(lines: string[], startline: number) { + const re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines for (let i=0; i`); // TODO + s += this.dialect.comment(`start action ${sys.name} ${event}`); // TODO s += codeeval.codeToString(); - s += this.dialect.comment(``); + s += this.dialect.comment(`end action ${sys.name} ${event}`); // TODO: check that this happens once? codeeval.end(); numActions++; diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 1dd1c5ba..1b7c3fc9 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -171,7 +171,7 @@ export class Tokenizer { } return tok; } - expectTokens(strlist: string[], msg?: string): Token { + expectTokens(strlist: readonly string[], msg?: string): Token { let tok = this.consumeToken(); let tokstr = tok.str; if (!strlist.includes(tokstr)) { diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 59d98f5e..2498062d 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -1,4 +1,6 @@ -import { readdirSync, readFileSync } from "fs"; +import assert from "assert"; +import { execFileSync } from "child_process"; +import { readdirSync, readFileSync, writeFileSync } from "fs"; import { describe } from "mocha"; import { ECSCompiler } from "../common/ecs/compiler"; import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; @@ -338,7 +340,7 @@ scope Root end `, 'foo.txt'); - console.log('json', c.em.toJSON()); + //console.log('json', c.em.toJSON()); let src = new SourceFileExport(); c.exportToFile(src); // TODO: test? @@ -368,14 +370,27 @@ describe('Tokenizer', function() { describe('Compiler', function() { let testdir = './test/ecs/'; let files = readdirSync(testdir).filter(f => f.endsWith('.ecs')); - files.forEach((ecspath) => { + files.forEach((ecsfn) => { + let goodfn = ecsfn.replace('.ecs','.txt') + let srcpath = testdir + ecsfn; + let destpath = testdir + goodfn; let dialect = new Dialect_CA65(); let em = new EntityManager(dialect); + em.mainPath = srcpath; let compiler = new ECSCompiler(em); - let code = readFileSync(testdir + ecspath, 'utf-8'); - compiler.parseFile(code, ecspath); + compiler.getImportFile = (path: string) => { + return readFileSync(testdir + path, 'utf-8'); + } + let code = readFileSync(srcpath, 'utf-8'); + compiler.parseFile(code, srcpath); let out = new SourceFileExport(); em.exportToFile(out); - console.log(out.toString()); + let outtxt = out.toString(); + let goodtxt = readFileSync(destpath, 'utf-8'); + if (outtxt.trim() != goodtxt.trim()) { + writeFileSync('/tmp/' + goodfn, outtxt, 'utf-8'); + execFileSync('/usr/bin/diff', [srcpath, destpath]); + throw new Error(ecsfn + ' did not match test file'); + } }); }); diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 4d2de328..f537cc05 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -16,6 +16,7 @@ export function assembleECS(step: BuildStep): BuildStepResult { if (staleFiles(step, [destpath])) { let code = getWorkFileAsString(step.path); try { + compiler.debuginfo = true; compiler.parseFile(code, step.path); let outtext = compiler.export().toString(); putWorkFile(destpath, outtext); diff --git a/test/ecs/basic1.txt b/test/ecs/basic1.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/ecs/vcs1.ecs b/test/ecs/vcs1.ecs new file mode 100644 index 00000000..0044cad0 --- /dev/null +++ b/test/ecs/vcs1.ecs @@ -0,0 +1,235 @@ + +//#resource "vcs-ca65.h" + +system Init + on main_init do once +--- +.include "vcs-ca65.h" +.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}} + ldy {{ Date: Tue, 8 Feb 2022 06:18:28 -0600 Subject: [PATCH 053/104] ecs: asc desc --- src/common/ecs/compiler.ts | 21 ++++++++++++--------- src/common/ecs/ecs.ts | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 3e452930..30b95465 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -2,7 +2,7 @@ import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; import { newDecoder } from "./decoder"; -import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System } from "./ecs"; +import { Action, ActionWithJoin, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -224,13 +224,16 @@ export class ECSCompiler extends Tokenizer { parseAction(): Action { // TODO: unused events? - let event = this.expectIdent().str; + const event = this.expectIdent().str; this.expectToken('do'); - let select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? + const all_modifiers = ['cyclecritical','asc','desc']; // TODO + const modifiers = this.parseModifiers(all_modifiers); + // TODO: include modifiers in error msg + const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? let query = undefined; let join = undefined; if (select == 'once') { - if (this.peekToken().str == '[') this.compileError(`A "${select}" query can't include a query.`) + if (this.peekToken().str == '[') this.compileError(`A "${select}" action can't include a query.`) } else { query = this.parseQuery(); } @@ -242,12 +245,12 @@ export class ECSCompiler extends Tokenizer { if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } else query.limit = this.expectInteger(); } - if (this.ifToken('cyclecritical')) { - // TODO - } let text = this.parseCode(); - let action = { text, event, query, join, select }; - return action as Action; + let direction = undefined; + if (modifiers['asc']) direction = 'asc'; + else if (modifiers['desc']) direction = 'desc'; + let action = { text, event, query, join, select, direction }; + return action as ActionWithJoin; } parseQuery() { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index d7604f48..40e1a31f 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -59,8 +59,6 @@ how to avoid cycle crossing for critical code and data? */ - -import { data } from "jquery"; import { SourceLocated, SourceLocation } from "../workertypes"; export class ECSError extends Error { @@ -132,6 +130,7 @@ export interface ActionOnce extends ActionBase { export interface ActionWithQuery extends ActionBase { select: 'foreach' | 'join' | 'with' | 'if' | 'select' query: Query + direction?: 'asc' | 'desc' } export interface ActionWithJoin extends ActionWithQuery { @@ -191,7 +190,7 @@ interface ComponentFieldPair { export class Dialect_CA65 { - ASM_ITERATE_EACH = ` + ASM_ITERATE_EACH_ASC = ` ldx #0 @__each: {{%code}} @@ -201,7 +200,16 @@ export class Dialect_CA65 { @__exit: `; - ASM_ITERATE_JOIN = ` + ASM_ITERATE_EACH_DESC = ` + ldx #{{%ecount}}-1 +@__each: + {{%code}} + dex + bpl @__each +@__exit: +`; + + ASM_ITERATE_JOIN_ASC = ` ldy #0 @__each: ldx {{%joinfield}},y @@ -210,6 +218,16 @@ export class Dialect_CA65 { cpy #{{%ecount}} bne @__each @__exit: +`; + + ASM_ITERATE_JOIN_DESC = ` + ldy #{{%ecount}}-1 +@__each: + ldx {{%joinfield}},y + {{%code}} + dey + bpl @__each +@__exit: `; ASM_FILTER_RANGE_LO_X = ` @@ -490,8 +508,8 @@ class ActionCPUState { } class ActionEval { - em; - dialect; + em : EntityManager; + dialect : Dialect_CA65; qr: EntitySet; jr: EntitySet | undefined; oldState : ActionCPUState; @@ -675,12 +693,14 @@ class ActionEval { __bss_init(args: string[]) { return this.scope.allocateInitData(this.scope.bss); } - wrapCodeInLoop(code: string, action: Action, ents: Entity[], joinfield?: ComponentFieldPair): string { + wrapCodeInLoop(code: string, action: ActionWithQuery, ents: Entity[], joinfield?: ComponentFieldPair): string { // TODO: check ents // TODO: check segment bounds // TODO: what if 0 or 1 entitites? - let s = this.dialect.ASM_ITERATE_EACH; - if (joinfield) s = this.dialect.ASM_ITERATE_JOIN; + // TODO: check > 127 or > 255 + let dir = action.direction; + let s = dir == 'desc' ? this.dialect.ASM_ITERATE_EACH_DESC : this.dialect.ASM_ITERATE_EACH_ASC; + if (joinfield) s = dir == 'desc' ? this.dialect.ASM_ITERATE_JOIN_DESC : this.dialect.ASM_ITERATE_JOIN_ASC; s = s.replace('{{%code}}', code); return s; } From 01056c66a870d05a5b8df168041f63bf8a1d386f Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 8 Feb 2022 16:30:11 -0600 Subject: [PATCH 054/104] ecs: sys, action stats --- src/common/ecs/binpack.ts | 170 ++++++++++++++++++++++++++++++++++++++ src/common/ecs/ecs.ts | 60 +++++++++++++- src/test/testecs.ts | 21 ++++- 3 files changed, 246 insertions(+), 5 deletions(-) create mode 100644 src/common/ecs/binpack.ts diff --git a/src/common/ecs/binpack.ts b/src/common/ecs/binpack.ts new file mode 100644 index 00000000..a0662cc9 --- /dev/null +++ b/src/common/ecs/binpack.ts @@ -0,0 +1,170 @@ + +var debug = true; + +export interface BoxConstraints { + left?: number; + top?: number; + width: number; + height: number; + box?: PlacedBox; +} + +enum BoxPlacement { + TopLeft=0, TopRight=1, BottomLeft=2, BottomRight=3 +} + +export interface Box { + left: number; + top: number; + right: number; + bottom: number; +} + +export interface PlacedBox extends Box { + bin: Bin; + parent: Box; + place: BoxPlacement; +} + +function boxesIntersect(a: Box, b: Box) : boolean { + return !(b.left >= a.right || b.right <= a.left || b.top >= a.bottom || b.bottom <= a.top); +} + +function getBoxPlacements(b: PlacedBox) { + let posns : BoxPlacement[]; + let snugw = b.right - b.left == b.parent.right - b.parent.left; + let snugh = b.bottom - b.top == b.parent.bottom - b.parent.top; + if (snugw && snugh) { + posns = [BoxPlacement.TopLeft]; + } else if (snugw && !snugh) { + posns = [BoxPlacement.TopLeft, BoxPlacement.BottomLeft]; + } else if (!snugw && snugh) { + posns = [BoxPlacement.TopLeft, BoxPlacement.TopRight]; + } else { + posns = [BoxPlacement.TopLeft, BoxPlacement.TopRight, + BoxPlacement.BottomLeft, BoxPlacement.BottomRight]; + } + return posns; +} + +export class Bin { + boxes: Box[] = []; + free: Box[] = []; + + constructor(public readonly binbounds: Box) { + this.free.push(binbounds); + } + getBoxes(bounds: Box, limit: number) : Box[] { + let result = []; + for (let box of this.boxes) { + //console.log(bounds, box, boxesIntersect(bounds, box)) + if (boxesIntersect(bounds, box)) { + result.push(box); + if (result.length >= limit) break; + } + } + return result; + } + fits(b: Box) { + if (!boxesIntersect(this.binbounds, b)) return false; + if (this.getBoxes(b, 1).length > 0) return false; + return true; + } + bestFit(b: Box) : Box | null { + let bestscore = 0; + let best = null; + for (let f of this.free) { + let dx = (f.right - f.left) - (b.right - b.left); + let dy = (f.bottom - f.top) - (b.bottom - b.top); + if (dx >= 0 && dy >= 0) { + let score = 1 / (1 + dx + dy); + if (score > bestscore) { + best = f; + } + } + } + return best; + } + add(b: PlacedBox) { + if (debug) console.log('added',b.left,b.top,b.right,b.bottom); + if (!this.fits(b)) { + //console.log('collided with', this.getBoxes(b, 1)); + throw new Error(`bad fit ${b.left} ${b.top} ${b.right} ${b.bottom}`) + } + // add box to list + this.boxes.push(b); + // delete bin + let i = this.free.indexOf(b.parent); + if (i < 0) throw new Error('cannot find parent'); + if (debug) console.log('removed',b.parent.left,b.parent.top,b.parent.right,b.parent.bottom); + this.free.splice(i, 1); + // split into new bins + switch (b.place) { + case BoxPlacement.TopLeft: + this.addFree( { top: b.top, left: b.right, bottom: b.bottom, right: b.parent.right } ); + this.addFree( { top: b.bottom, left: b.parent.left, bottom: b.parent.bottom, right: b.parent.right } ); + break; + case BoxPlacement.TopRight: + this.addFree( { top: b.top, left: b.parent.left, bottom: b.bottom, right: b.left } ); + this.addFree( { top: b.bottom, left: b.parent.left, bottom: b.parent.bottom, right: b.parent.right } ); + break; + case BoxPlacement.BottomLeft: + this.addFree( { top: b.parent.top, left: b.parent.left, bottom: b.top, right: b.parent.right } ); + this.addFree( { top: b.top, left: b.right, bottom: b.parent.bottom, right: b.parent.right } ); + break; + case BoxPlacement.BottomRight: + this.addFree( { top: b.parent.top, left: b.parent.left, bottom: b.top, right: b.parent.right } ); + this.addFree( { top: b.top, left: b.parent.left, bottom: b.parent.bottom, right: b.left } ); + break; + } + } + addFree(b: Box) { + if (b.bottom > b.top && b.right > b.left) { + if (debug) console.log('free',b.left,b.top,b.right,b.bottom); + this.free.push(b); + } + // TODO: merge free boxes + } +} + +export class Packer { + bins : Bin[] = []; + boxes : BoxConstraints[] = []; + + pack() : boolean { + for (let bc of this.boxes) { + let box = this.bestPlacement(bc); + if (!box) return false; + box.bin.add(box); + bc.box = box; + } + return true; + } + bestPlacement(b: BoxConstraints) : PlacedBox | null { + let left = b.left != null ? b.left : 0; + let top = b.top != null ? b.top : 0; + let right = left + b.width; + let bottom = top + b.height; + for (let bin of this.bins) { + let place : BoxPlacement = BoxPlacement.TopLeft; //TODO + let box = { left, top, right, bottom }; + let parent = bin.bestFit(box); + if (parent) { + box.left = parent.left; + box.top = parent.top; + box.right = parent.left + b.width; + box.bottom = parent.top + b.height; + /* + if (place == BoxPlacement.BottomLeft || place == BoxPlacement.BottomRight) { + box.top = parent.bottom - (box.bottom - box.top); + } + if (place == BoxPlacement.TopRight || place == BoxPlacement.BottomRight) { + box.left = parent.right - (box.right - box.left); + } + */ + return { parent, place, bin, ...box }; + } + } + return null; + } +} diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 40e1a31f..a810d2b6 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -107,6 +107,11 @@ export interface Query extends SourceLocated { limit?: number; } +export class SystemStats { + tempstartseq: number | undefined; + tempendseq: number | undefined; +} + export interface System extends SourceLocated { name: string; actions: Action[]; @@ -117,6 +122,10 @@ export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select'] a export type SelectType = typeof SELECT_TYPE[number]; +export class ActionStats { + callcount: number = 0; +} + export interface ActionBase extends SourceLocated { select: SelectType; event: string; @@ -687,6 +696,7 @@ class ActionEval { if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, this.action); if (!this.sys.tempbytes) throw new ECSError(`this system has no locals`, this.action); if (tempinc < 0 || tempinc >= this.sys.tempbytes) throw new ECSError(`this system only has ${this.sys.tempbytes} locals`, this.action); + this.scope.updateTempLiveness(this.sys); return `${this.tmplabel}+${tempinc}`; //return `TEMP+${this.scope.tempOffset}+${tempinc}`; } @@ -828,10 +838,13 @@ export class EntityScope implements SourceLocated { systems: System[] = []; entities: Entity[] = []; fieldtypes: { [name: string]: 'init' | 'const' } = {}; + sysstats = new Map(); + actionstats = new Map(); bss = new UninitDataSegment(); rodata = new ConstDataSegment(); code = new CodeSegment(); componentsInScope = new Set(); + eventSeq = 0; tempOffset = 0; tempSize = 0; maxTempBytes = 0; @@ -861,6 +874,7 @@ export class EntityScope implements SourceLocated { return entity; } addUsingSystem(system: System) { + if (!system) throw new Error(); this.systems.push(system); } getEntityByName(name: string) { @@ -1063,9 +1077,12 @@ export class EntityScope implements SourceLocated { let systems = this.em.event2systems[event]; if (!systems || systems.length == 0) { // TODO: error or warning? - console.log(`warning: no system responds to "${event}"`); return ''; //throw new ECSError(`warning: no system responds to "${event}"`); + console.log(`warning: no system responds to "${event}"`); + return ''; } + this.eventSeq++; + // generate code let s = this.dialect.code(); //s += `\n; event ${event}\n`; systems = systems.filter(s => this.systems.includes(s)); @@ -1089,6 +1106,7 @@ export class EntityScope implements SourceLocated { s += this.dialect.comment(`end action ${sys.name} ${event}`); // TODO: check that this happens once? codeeval.end(); + this.getActionStats(action).callcount++; numActions++; } } @@ -1102,6 +1120,32 @@ export class EntityScope implements SourceLocated { this.maxTempBytes = Math.max(this.tempSize, this.maxTempBytes); if (n < 0) this.tempOffset = this.tempSize; } + getSystemStats(sys: System) : SystemStats { + let stats = this.sysstats.get(sys); + if (!stats) { + stats = new SystemStats(); + this.sysstats.set(sys, stats); + } + return stats; + } + getActionStats(action: Action) : ActionStats { + let stats = this.actionstats.get(action); + if (!stats) { + stats = new ActionStats(); + this.actionstats.set(action, stats); + } + return stats; + } + updateTempLiveness(sys: System) { + let stats = this.getSystemStats(sys); + let n = this.eventSeq; + if (stats.tempstartseq && stats.tempendseq) { + stats.tempstartseq = Math.min(stats.tempstartseq, n); + stats.tempendseq = Math.max(stats.tempendseq, n); + } else { + stats.tempstartseq = stats.tempendseq = n; + } + } includeResource(symbol: string): string { this.resources.add(symbol); return symbol; @@ -1116,8 +1160,9 @@ export class EntityScope implements SourceLocated { let isMainScope = this.parent == null; this.tempOffset = this.maxTempBytes = 0; let start; - if (isMainScope) { - this.addUsingSystem(this.em.getSystemByName('Init')); //TODO: what if none? + let initsys = this.em.getSystemByName('Init'); + if (isMainScope && initsys) { + this.addUsingSystem(initsys); //TODO: what if none? start = this.generateCodeForEvent('main_init'); } else { start = this.generateCodeForEvent('start'); @@ -1127,6 +1172,15 @@ export class EntityScope implements SourceLocated { let code = this.generateCodeForEvent(sub); this.code.addCodeFragment(code); } + //this.showStats(); + } + showStats() { + for (let sys of this.systems) { + console.log(sys.name, this.getSystemStats(sys)); + } + for (let action of Array.from(this.actionstats.keys())) { + console.log(action.event, this.getActionStats(action)); + } } dump(file: SourceFileExport) { this.analyzeEntities(); diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 2498062d..fcc31534 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -1,7 +1,8 @@ import assert from "assert"; -import { execFileSync } from "child_process"; +import { execFileSync, spawnSync } from "child_process"; import { readdirSync, readFileSync, writeFileSync } from "fs"; import { describe } from "mocha"; +import { Bin, Packer } from "../common/ecs/binpack"; import { ECSCompiler } from "../common/ecs/compiler"; import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; @@ -389,8 +390,24 @@ describe('Compiler', function() { let goodtxt = readFileSync(destpath, 'utf-8'); if (outtxt.trim() != goodtxt.trim()) { writeFileSync('/tmp/' + goodfn, outtxt, 'utf-8'); - execFileSync('/usr/bin/diff', [srcpath, destpath]); + console.log(spawnSync('/usr/bin/diff', [srcpath, destpath], {encoding:'utf-8'}).stdout); throw new Error(ecsfn + ' did not match test file'); } }); }); + +describe('Box Packer', function() { + it('Should pack boxes', function() { + let packer = new Packer(); + let bin1 = new Bin({ left:0, top:0, right:10, bottom:10 }); + packer.bins.push(bin1); + packer.boxes.push({ width: 5, height: 5 }); + packer.boxes.push({ width: 5, height: 5 }); + packer.boxes.push({ width: 5, height: 5 }); + packer.boxes.push({ width: 5, height: 5 }); + if (!packer.pack()) throw new Error('cannot pack') + console.log(packer.boxes); + console.log(packer.bins[0].free) + }); +}); + From bc7cd6f1a355f7ff1818ff0fa05fcb3fb87caa58 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 8 Feb 2022 22:21:23 -0600 Subject: [PATCH 055/104] ecs: temp vars --- src/common/ecs/binpack.ts | 71 ++++++++++++++++++++++++++++++--------- src/common/ecs/ecs.ts | 25 ++++++++++++++ src/test/testecs.ts | 41 ++++++++++++++++------ 3 files changed, 110 insertions(+), 27 deletions(-) diff --git a/src/common/ecs/binpack.ts b/src/common/ecs/binpack.ts index a0662cc9..d070d9e0 100644 --- a/src/common/ecs/binpack.ts +++ b/src/common/ecs/binpack.ts @@ -66,16 +66,26 @@ export class Bin { return result; } fits(b: Box) { - if (!boxesIntersect(this.binbounds, b)) return false; - if (this.getBoxes(b, 1).length > 0) return false; + if (!boxesIntersect(this.binbounds, b)) { + if (debug) console.log('out of bounds!', b.left,b.top,b.right,b.bottom); + return false; + } + if (this.getBoxes(b, 1).length > 0) { + if (debug) console.log('intersect!', b.left,b.top,b.right,b.bottom); + return false; + } return true; } - bestFit(b: Box) : Box | null { + bestFit(b: BoxConstraints) : Box | null { let bestscore = 0; let best = null; for (let f of this.free) { - let dx = (f.right - f.left) - (b.right - b.left); - let dy = (f.bottom - f.top) - (b.bottom - b.top); + if (b.left != null && b.left < f.left) continue; + if (b.left != null && b.left + b.width > f.right) continue; + if (b.top != null && b.top < f.top) continue; + if (b.top != null && b.top + b.height > f.bottom) continue; + let dx = (f.right - f.left) - b.width; + let dy = (f.bottom - f.top) - b.height; if (dx >= 0 && dy >= 0) { let score = 1 / (1 + dx + dy); if (score > bestscore) { @@ -85,8 +95,26 @@ export class Bin { } return best; } + anyFit(b: BoxConstraints) : Box | null { + let bestscore = 0; + let best = null; + for (let f of this.free) { + let box : Box = { + left: b.left != null ? b.left : f.left, + right: f.left + b.width, + top: b.top != null ? b.top : f.top, + bottom: f.top + b.height }; + if (this.fits(box)) { + let score = 1 / (1 + box.left + box.top); + if (score > bestscore) { + best = f; + } + } + } + return best; + } add(b: PlacedBox) { - if (debug) console.log('added',b.left,b.top,b.right,b.bottom); + if (debug) console.log('added', b.left,b.top,b.right,b.bottom); if (!this.fits(b)) { //console.log('collided with', this.getBoxes(b, 1)); throw new Error(`bad fit ${b.left} ${b.top} ${b.right} ${b.bottom}`) @@ -141,19 +169,29 @@ export class Packer { return true; } bestPlacement(b: BoxConstraints) : PlacedBox | null { - let left = b.left != null ? b.left : 0; - let top = b.top != null ? b.top : 0; - let right = left + b.width; - let bottom = top + b.height; for (let bin of this.bins) { let place : BoxPlacement = BoxPlacement.TopLeft; //TODO - let box = { left, top, right, bottom }; - let parent = bin.bestFit(box); + let parent = bin.bestFit(b); + if (!parent) { + parent = bin.anyFit(b); + if (debug) console.log('anyfit',parent?.left,parent?.top); + } if (parent) { - box.left = parent.left; - box.top = parent.top; - box.right = parent.left + b.width; - box.bottom = parent.top + b.height; + let box = { + left: parent.left, + top: parent.top, + right: parent.left + b.width, + bottom: parent.top + b.height + }; + if (b.left != null) { + box.left = b.left; + box.right = b.left + b.width; + } + if (b.top != null) { + box.top = b.top; + box.bottom = b.top + b.height; + } + if (debug) console.log('place',box.left,box.top,box.right,box.bottom,parent?.left,parent?.top); /* if (place == BoxPlacement.BottomLeft || place == BoxPlacement.BottomRight) { box.top = parent.bottom - (box.bottom - box.top); @@ -165,6 +203,7 @@ export class Packer { return { parent, place, bin, ...box }; } } + if (debug) console.log('cannot place!', b.left,b.top,b.width,b.height); return null; } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index a810d2b6..77441c5d 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -60,6 +60,7 @@ how to avoid cycle crossing for critical code and data? */ import { SourceLocated, SourceLocation } from "../workertypes"; +import { Bin, Packer } from "./binpack"; export class ECSError extends Error { $loc: SourceLocation; @@ -1150,6 +1151,29 @@ export class EntityScope implements SourceLocated { this.resources.add(symbol); return symbol; } + allocateTempVars() { + let pack = new Packer(); + let maxTempBytes = 128; // TODO + pack.bins.push(new Bin({ left:0, top:0, bottom: this.eventSeq+1, right: maxTempBytes })); + for (let sys of this.systems) { + let stats = this.getSystemStats(sys); + if (sys.tempbytes && stats.tempstartseq && stats.tempendseq) { + let v = { + sys, + top: stats.tempstartseq, + bottom: stats.tempendseq+1, + width: sys.tempbytes, + height: stats.tempendseq - stats.tempstartseq + 1, + }; + pack.boxes.push(v); + } + } + if (!pack.pack()) console.log('cannot pack temporary local vars'); // TODO + console.log('tempvars', pack); + for (let b of pack.boxes) { + console.log((b as any).sys.name, b.box); + } + } analyzeEntities() { this.buildSegments(); this.allocateSegment(this.bss, false); @@ -1173,6 +1197,7 @@ export class EntityScope implements SourceLocated { this.code.addCodeFragment(code); } //this.showStats(); + this.allocateTempVars(); } showStats() { for (let sys of this.systems) { diff --git a/src/test/testecs.ts b/src/test/testecs.ts index fcc31534..62feb4b3 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -2,7 +2,7 @@ import assert from "assert"; import { execFileSync, spawnSync } from "child_process"; import { readdirSync, readFileSync, writeFileSync } from "fs"; import { describe } from "mocha"; -import { Bin, Packer } from "../common/ecs/binpack"; +import { Bin, BoxConstraints, Packer } from "../common/ecs/binpack"; import { ECSCompiler } from "../common/ecs/compiler"; import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; @@ -396,18 +396,37 @@ describe('Compiler', function() { }); }); +function testPack(bins: Bin[], boxes: BoxConstraints[]) { + let packer = new Packer(); + for (let bin of bins) packer.bins.push(bin); + for (let bc of boxes) packer.boxes.push(bc); + if (!packer.pack()) throw new Error('cannot pack') + console.log(packer.boxes); + console.log(packer.bins[0].free) +} + describe('Box Packer', function() { it('Should pack boxes', function() { - let packer = new Packer(); - let bin1 = new Bin({ left:0, top:0, right:10, bottom:10 }); - packer.bins.push(bin1); - packer.boxes.push({ width: 5, height: 5 }); - packer.boxes.push({ width: 5, height: 5 }); - packer.boxes.push({ width: 5, height: 5 }); - packer.boxes.push({ width: 5, height: 5 }); - if (!packer.pack()) throw new Error('cannot pack') - console.log(packer.boxes); - console.log(packer.bins[0].free) + testPack( + [ + new Bin({ left:0, top:0, right:10, bottom:10 }) + ], [ + { width: 5, height: 5 }, + { width: 5, height: 5 }, + { width: 5, height: 5 }, + { width: 5, height: 5 }, + ] + ); + }); + it('Should pack temp vars', function() { + testPack( + [ + new Bin({ left:0, top:0, right:10, bottom:10 }) + ], [ + { width: 3, height: 7, top: 0 }, + { width: 3, height: 7, top: 1 }, + ] + ); }); }); From 5081307d81c3c616148a22a9de82b55969d16684 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 9 Feb 2022 07:39:41 -0600 Subject: [PATCH 056/104] ecs: bin pack temp vars --- src/common/ecs/binpack.ts | 96 +++++++++++++++++--------------------- src/common/ecs/compiler.ts | 4 +- src/common/ecs/ecs.ts | 65 ++++++++++++-------------- src/test/testecs.ts | 43 +++++++++++------ test/ecs/vcs1.txt | 5 +- 5 files changed, 110 insertions(+), 103 deletions(-) diff --git a/src/common/ecs/binpack.ts b/src/common/ecs/binpack.ts index d070d9e0..8b92517c 100644 --- a/src/common/ecs/binpack.ts +++ b/src/common/ecs/binpack.ts @@ -22,7 +22,7 @@ export interface Box { export interface PlacedBox extends Box { bin: Bin; - parent: Box; + parents: Box[]; place: BoxPlacement; } @@ -30,33 +30,22 @@ function boxesIntersect(a: Box, b: Box) : boolean { return !(b.left >= a.right || b.right <= a.left || b.top >= a.bottom || b.bottom <= a.top); } -function getBoxPlacements(b: PlacedBox) { - let posns : BoxPlacement[]; - let snugw = b.right - b.left == b.parent.right - b.parent.left; - let snugh = b.bottom - b.top == b.parent.bottom - b.parent.top; - if (snugw && snugh) { - posns = [BoxPlacement.TopLeft]; - } else if (snugw && !snugh) { - posns = [BoxPlacement.TopLeft, BoxPlacement.BottomLeft]; - } else if (!snugw && snugh) { - posns = [BoxPlacement.TopLeft, BoxPlacement.TopRight]; - } else { - posns = [BoxPlacement.TopLeft, BoxPlacement.TopRight, - BoxPlacement.BottomLeft, BoxPlacement.BottomRight]; - } - return posns; +function boxesContain(a: Box, b: Box) : boolean { + return b.left >= a.left && b.top >= a.top && b.right <= a.right && b.bottom <= a.bottom; } export class Bin { boxes: Box[] = []; free: Box[] = []; + extents: Box = {left:0,top:0,right:0,bottom:0}; constructor(public readonly binbounds: Box) { this.free.push(binbounds); } - getBoxes(bounds: Box, limit: number) : Box[] { + getBoxes(bounds: Box, limit: number, boxes?: Box[]) : Box[] { let result = []; - for (let box of this.boxes) { + if (!boxes) boxes = this.boxes; + for (let box of boxes) { //console.log(bounds, box, boxesIntersect(bounds, box)) if (boxesIntersect(bounds, box)) { result.push(box); @@ -66,7 +55,7 @@ export class Bin { return result; } fits(b: Box) { - if (!boxesIntersect(this.binbounds, b)) { + if (!boxesContain(this.binbounds, b)) { if (debug) console.log('out of bounds!', b.left,b.top,b.right,b.bottom); return false; } @@ -90,6 +79,7 @@ export class Bin { let score = 1 / (1 + dx + dy); if (score > bestscore) { best = f; + if (score == 1) break; } } } @@ -108,56 +98,51 @@ export class Bin { let score = 1 / (1 + box.left + box.top); if (score > bestscore) { best = f; + if (score == 1) break; } } } return best; } add(b: PlacedBox) { - if (debug) console.log('added', b.left,b.top,b.right,b.bottom); + if (debug) console.log('add', b.left,b.top,b.right,b.bottom); if (!this.fits(b)) { //console.log('collided with', this.getBoxes(b, 1)); throw new Error(`bad fit ${b.left} ${b.top} ${b.right} ${b.bottom}`) } // add box to list this.boxes.push(b); + this.extents.right = Math.max(this.extents.right, b.right); + this.extents.bottom = Math.max(this.extents.bottom, b.bottom); // delete bin - let i = this.free.indexOf(b.parent); - if (i < 0) throw new Error('cannot find parent'); - if (debug) console.log('removed',b.parent.left,b.parent.top,b.parent.right,b.parent.bottom); - this.free.splice(i, 1); - // split into new bins - switch (b.place) { - case BoxPlacement.TopLeft: - this.addFree( { top: b.top, left: b.right, bottom: b.bottom, right: b.parent.right } ); - this.addFree( { top: b.bottom, left: b.parent.left, bottom: b.parent.bottom, right: b.parent.right } ); - break; - case BoxPlacement.TopRight: - this.addFree( { top: b.top, left: b.parent.left, bottom: b.bottom, right: b.left } ); - this.addFree( { top: b.bottom, left: b.parent.left, bottom: b.parent.bottom, right: b.parent.right } ); - break; - case BoxPlacement.BottomLeft: - this.addFree( { top: b.parent.top, left: b.parent.left, bottom: b.top, right: b.parent.right } ); - this.addFree( { top: b.top, left: b.right, bottom: b.parent.bottom, right: b.parent.right } ); - break; - case BoxPlacement.BottomRight: - this.addFree( { top: b.parent.top, left: b.parent.left, bottom: b.top, right: b.parent.right } ); - this.addFree( { top: b.top, left: b.parent.left, bottom: b.parent.bottom, right: b.left } ); - break; + for (let p of b.parents) { + let i = this.free.indexOf(p); + if (i < 0) throw new Error('cannot find parent'); + if (debug) console.log('removed',p.left,p.top,p.right,p.bottom); + this.free.splice(i, 1); + // split into new bins + // make long columns + this.addFree(p.left, p.top, b.left, p.bottom); + this.addFree(b.right, p.top, p.right, p.bottom); + // make top caps + this.addFree(b.left, p.top, b.right, b.top); + this.addFree(b.left, b.bottom, b.right, p.bottom); } } - addFree(b: Box) { - if (b.bottom > b.top && b.right > b.left) { + addFree(left: number, top: number, right: number, bottom: number) { + if (bottom > top && right > left) { + let b = { left, top, right, bottom }; if (debug) console.log('free',b.left,b.top,b.right,b.bottom); this.free.push(b); } - // TODO: merge free boxes + // TODO: merge free boxes? } } export class Packer { bins : Bin[] = []; boxes : BoxConstraints[] = []; + defaultPlacement : BoxPlacement = BoxPlacement.TopLeft; //TODO pack() : boolean { for (let bc of this.boxes) { @@ -170,13 +155,15 @@ export class Packer { } bestPlacement(b: BoxConstraints) : PlacedBox | null { for (let bin of this.bins) { - let place : BoxPlacement = BoxPlacement.TopLeft; //TODO let parent = bin.bestFit(b); + let approx = false; if (!parent) { parent = bin.anyFit(b); + approx = true; if (debug) console.log('anyfit',parent?.left,parent?.top); } if (parent) { + let place = this.defaultPlacement; let box = { left: parent.left, top: parent.top, @@ -191,16 +178,21 @@ export class Packer { box.top = b.top; box.bottom = b.top + b.height; } - if (debug) console.log('place',box.left,box.top,box.right,box.bottom,parent?.left,parent?.top); - /* if (place == BoxPlacement.BottomLeft || place == BoxPlacement.BottomRight) { - box.top = parent.bottom - (box.bottom - box.top); + let h = box.bottom - box.top; + box.top = parent.bottom - h; + box.bottom = parent.bottom; } if (place == BoxPlacement.TopRight || place == BoxPlacement.BottomRight) { - box.left = parent.right - (box.right - box.left); + let w = box.right - box.left; + box.left = parent.right - w; + box.right = parent.right; } - */ - return { parent, place, bin, ...box }; + if (debug) console.log('place',box.left,box.top,box.right,box.bottom,parent?.left,parent?.top); + let parents = [parent]; + // if approx match, might overlap multiple free boxes + if (approx) parents = bin.getBoxes(box, 100, bin.free); + return { parents, place, bin, ...box }; } } if (debug) console.log('cannot place!', b.left,b.top,b.width,b.height); diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 30b95465..5089dac8 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -226,10 +226,10 @@ export class ECSCompiler extends Tokenizer { // TODO: unused events? const event = this.expectIdent().str; this.expectToken('do'); - const all_modifiers = ['cyclecritical','asc','desc']; // TODO - const modifiers = this.parseModifiers(all_modifiers); // TODO: include modifiers in error msg const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? + const all_modifiers = ['cyclecritical','asc','desc']; // TODO + const modifiers = this.parseModifiers(all_modifiers); let query = undefined; let join = undefined; if (select == 'once') { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 77441c5d..f37e4f80 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -339,6 +339,12 @@ export class Dialect_CA65 { else return `.byte (${b.symbol} >> ${b.bitofs})` // TODO? } } + tempLabel(sys: System) { + return `${sys.name}__tmp`; + } + equate(symbol: string, value: string): string { + return `${symbol} = ${value}`; + } } // TODO: merge with Dialect? @@ -372,6 +378,7 @@ class CodeSegment { class DataSegment { symbols: { [sym: string]: number } = {}; + equates: { [sym: string]: string } = {}; ofs2sym = new Map(); fieldranges: { [cfname: string]: FieldArray } = {}; size: number = 0; @@ -397,6 +404,7 @@ class DataSegment { } } dump(file: SourceFileExport, dialect: Dialect_CA65) { + // TODO: fewer lines for (let i = 0; i < this.size; i++) { let syms = this.ofs2sym.get(i); if (syms) { @@ -405,6 +413,9 @@ class DataSegment { } file.line(dialect.byte(this.initdata[i])); } + for (let [symbol,value] of Object.entries(this.equates)) { + file.line(dialect.equate(symbol, value)); + } } // TODO: move cfname functions in here too getFieldRange(component: ComponentType, fieldName: string) { @@ -699,7 +710,6 @@ class ActionEval { if (tempinc < 0 || tempinc >= this.sys.tempbytes) throw new ECSError(`this system only has ${this.sys.tempbytes} locals`, this.action); this.scope.updateTempLiveness(this.sys); return `${this.tmplabel}+${tempinc}`; - //return `TEMP+${this.scope.tempOffset}+${tempinc}`; } __bss_init(args: string[]) { return this.scope.allocateInitData(this.scope.bss); @@ -846,9 +856,6 @@ export class EntityScope implements SourceLocated { code = new CodeSegment(); componentsInScope = new Set(); eventSeq = 0; - tempOffset = 0; - tempSize = 0; - maxTempBytes = 0; resources = new Set(); state = new ActionCPUState(); isDemo = false; @@ -1088,13 +1095,7 @@ export class EntityScope implements SourceLocated { //s += `\n; event ${event}\n`; systems = systems.filter(s => this.systems.includes(s)); for (let sys of systems) { - // TODO: does this work if multiple actions? - // TODO: share storage - //if (sys.tempbytes) this.allocateTempBytes(sys.tempbytes); - let tmplabel = `${sys.name}_tmp`; - if (sys.tempbytes) this.bss.allocateBytes(tmplabel, sys.tempbytes); - //this.allocateTempBytes(1); - let numActions = 0; + let tmplabel = this.dialect.tempLabel(sys); for (let action of sys.actions) { if (action.event == event) { // TODO: use Tokenizer so error msgs are better @@ -1108,19 +1109,11 @@ export class EntityScope implements SourceLocated { // TODO: check that this happens once? codeeval.end(); this.getActionStats(action).callcount++; - numActions++; } } - // TODO: if (sys.tempbytes && numActions) this.allocateTempBytes(-sys.tempbytes); } return s; } - allocateTempBytes(n: number) { - if (n > 0) this.tempOffset = this.tempSize; - this.tempSize += n; - this.maxTempBytes = Math.max(this.tempSize, this.maxTempBytes); - if (n < 0) this.tempOffset = this.tempSize; - } getSystemStats(sys: System) : SystemStats { let stats = this.sysstats.get(sys); if (!stats) { @@ -1151,10 +1144,11 @@ export class EntityScope implements SourceLocated { this.resources.add(symbol); return symbol; } - allocateTempVars() { + private allocateTempVars() { let pack = new Packer(); - let maxTempBytes = 128; // TODO - pack.bins.push(new Bin({ left:0, top:0, bottom: this.eventSeq+1, right: maxTempBytes })); + let maxTempBytes = 128 - this.bss.size; // TODO: multiple data segs + let bssbin = new Bin({ left:0, top:0, bottom: this.eventSeq+1, right: maxTempBytes }); + pack.bins.push(bssbin); for (let sys of this.systems) { let stats = this.getSystemStats(sys); if (sys.tempbytes && stats.tempstartseq && stats.tempendseq) { @@ -1170,19 +1164,23 @@ export class EntityScope implements SourceLocated { } if (!pack.pack()) console.log('cannot pack temporary local vars'); // TODO console.log('tempvars', pack); - for (let b of pack.boxes) { - console.log((b as any).sys.name, b.box); + if (bssbin.extents.right > 0) { + this.bss.allocateBytes('TEMP', bssbin.extents.right); + for (let b of pack.boxes) { + let sys : System = (b as any).sys; + console.log(sys.name, b.box?.left); + this.bss.equates[this.dialect.tempLabel(sys)] = `TEMP+${b.box?.left}`; + } } } - analyzeEntities() { + private analyzeEntities() { this.buildSegments(); this.allocateSegment(this.bss, false); this.allocateSegment(this.rodata, true); this.allocateROData(this.rodata); } - generateCode() { + private generateCode() { let isMainScope = this.parent == null; - this.tempOffset = this.maxTempBytes = 0; let start; let initsys = this.em.getSystemByName('Init'); if (isMainScope && initsys) { @@ -1197,7 +1195,6 @@ export class EntityScope implements SourceLocated { this.code.addCodeFragment(code); } //this.showStats(); - this.allocateTempVars(); } showStats() { for (let sys of this.systems) { @@ -1207,16 +1204,10 @@ export class EntityScope implements SourceLocated { console.log(action.event, this.getActionStats(action)); } } - dump(file: SourceFileExport) { - this.analyzeEntities(); - this.generateCode(); - this.dumpCodeTo(file); - } private dumpCodeTo(file: SourceFileExport) { let dialect = this.dialect; file.line(dialect.startScope(this.name)); file.line(dialect.segment(`${this.name}_DATA`, 'bss')); - if (this.maxTempBytes) this.bss.allocateBytes('TEMP', this.maxTempBytes); this.bss.dump(file, dialect); file.line(dialect.segment(`${this.name}_RODATA`, 'rodata')); this.rodata.dump(file, dialect); @@ -1229,6 +1220,12 @@ export class EntityScope implements SourceLocated { } file.line(dialect.endScope(this.name)); } + dump(file: SourceFileExport) { + this.analyzeEntities(); + this.generateCode(); + this.allocateTempVars(); + this.dumpCodeTo(file); + } } export class EntityManager { diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 62feb4b3..35649752 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -284,8 +284,6 @@ function testECS() { //console.log(em.archetypesMatching({ include:['xpos','ypos']})[0]) - root.analyzeEntities(); - root.generateCode(); let src = new SourceFileExport(); root.dump(src); //console.log(src.toString()); @@ -372,26 +370,28 @@ describe('Compiler', function() { let testdir = './test/ecs/'; let files = readdirSync(testdir).filter(f => f.endsWith('.ecs')); files.forEach((ecsfn) => { - let goodfn = ecsfn.replace('.ecs','.txt') - let srcpath = testdir + ecsfn; - let destpath = testdir + goodfn; + let asmfn = ecsfn.replace('.ecs','.asm'); + let goodfn = ecsfn.replace('.ecs','.txt'); + let ecspath = testdir + ecsfn; + let goodpath = testdir + goodfn; let dialect = new Dialect_CA65(); let em = new EntityManager(dialect); - em.mainPath = srcpath; + em.mainPath = ecspath; let compiler = new ECSCompiler(em); compiler.getImportFile = (path: string) => { return readFileSync(testdir + path, 'utf-8'); } - let code = readFileSync(srcpath, 'utf-8'); - compiler.parseFile(code, srcpath); + let code = readFileSync(ecspath, 'utf-8'); + compiler.parseFile(code, ecspath); let out = new SourceFileExport(); em.exportToFile(out); let outtxt = out.toString(); - let goodtxt = readFileSync(destpath, 'utf-8'); + let goodtxt = readFileSync(goodpath, 'utf-8'); if (outtxt.trim() != goodtxt.trim()) { - writeFileSync('/tmp/' + goodfn, outtxt, 'utf-8'); - console.log(spawnSync('/usr/bin/diff', [srcpath, destpath], {encoding:'utf-8'}).stdout); - throw new Error(ecsfn + ' did not match test file'); + let asmpath = '/tmp/' + asmfn; + writeFileSync(asmpath, outtxt, 'utf-8'); + console.log(spawnSync('/usr/bin/diff', [asmpath, goodpath], {encoding:'utf-8'}).stdout); + throw new Error(`files different; to fix: cp ${asmpath} ${goodpath}`); } }); }); @@ -418,13 +418,30 @@ describe('Box Packer', function() { ] ); }); - it('Should pack temp vars', function() { + it('Should pack top-aligned boxes', function() { + testPack( + [ + new Bin({ left:0, top:0, right:10, bottom:10 }) + ], [ + { width: 5, height: 7, top: 0 }, + { width: 5, height: 7, top: 1 }, + { width: 5, height: 1 }, + { width: 5, height: 1 }, + { width: 5, height: 3 }, + { width: 5, height: 1 }, + ] + ); + }); + it('Should pack top-aligned boxes', function() { testPack( [ new Bin({ left:0, top:0, right:10, bottom:10 }) ], [ { width: 3, height: 7, top: 0 }, { width: 3, height: 7, top: 1 }, + { width: 3, height: 7, top: 2 }, + { width: 5, height: 1 }, + { width: 3, height: 1 }, ] ); }); diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index d4008144..aba14e40 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -3,8 +3,9 @@ PFColor_pfcolor_b0: .res 1 .res 1 -Local_tmp: +TEMP: .res 1 +Local__tmp = TEMP+0 .code KernelSection_lines_b0: .byte 2 @@ -197,7 +198,7 @@ StaticKernel__kernel__7____exit: ;;; start action Local joybutton - inc Local_tmp+0 + inc Local__tmp+0 inc PFColor_pfcolor_b0 ;;; end action Local joybutton From 999aed9cb4ba092b357d673b09307860f265561a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 9 Feb 2022 11:45:40 -0600 Subject: [PATCH 057/104] ecs: data , 48 pixel decoder --- presets/vcs/vcs-ca65.h | 13 ++++++++ src/common/ecs/decoder.ts | 29 ++++++++++++++++ src/common/ecs/ecs.ts | 69 ++++++++++++++++++++++++++------------- test/ecs/vcs1.txt | 16 ++++----- 4 files changed, 96 insertions(+), 31 deletions(-) diff --git a/presets/vcs/vcs-ca65.h b/presets/vcs/vcs-ca65.h index 87a4b7d5..9a8fc374 100644 --- a/presets/vcs/vcs-ca65.h +++ b/presets/vcs/vcs-ca65.h @@ -266,3 +266,16 @@ LINESD12 = 16 SLEEP cycles .endif .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 diff --git a/src/common/ecs/decoder.ts b/src/common/ecs/decoder.ts index 8f2a8617..7af1feef 100644 --- a/src/common/ecs/decoder.ts +++ b/src/common/ecs/decoder.ts @@ -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 1) { + // TODO: when to ignore if entities.length == 1 and not in for loop? + if (action.select == 'with') { code = this.wrapCodeInFilter(code); } - if (action.select == 'if' && entities.length > 1) { + if (action.select == 'if') { code = this.wrapCodeInFilter(code); } if (action.select == 'foreach' && entities.length > 1) { @@ -678,15 +689,24 @@ class ActionEval { let bitofs = parseInt(args[1] || '0'); return this.generateCodeForField(fieldName, bitofs, canwrite); } - __base(args: string[]) { - // TODO: refactor into generateCode.. + parseFieldArgs(args: string[]) { let fieldName = args[0]; let bitofs = parseInt(args[1] || '0'); let component = this.em.singleComponentWithFieldName(this.qr.atypes, fieldName, this.action); let field = component.fields.find(f => f.name == fieldName); 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); } + __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[]) { // TODO: check select type and if we actually have an index... 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 // TODO: dialect let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? + // TODO: array field baseoffset? if (baseLookup) { return this.dialect.absolute(ident); } else if (entities.length == 1) { @@ -983,11 +1004,13 @@ export class EntityScope implements SourceLocated { // this is not a constant // is it a bounded array? (TODO) 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 offset = this.bss.allocateBytes(datasym, databytes); + // TODO? this.allocatePointerArray(c, f, datasym, entcount); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); 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 hiofs = segment.allocateBytes(ptrhisym, entcount); if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; @@ -997,8 +1020,9 @@ export class EntityScope implements SourceLocated { } else { // this is a constant // is it a byte array? + //TODO? if (ArrayBuffer.isView(v) && 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); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); @@ -1007,7 +1031,6 @@ export class EntityScope implements SourceLocated { if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; - // TODO: } else if (v instanceof Uint16Array) { } else if (typeof v === 'number') { // more than 1 entity, add an array if (entcount > 1) { @@ -1020,7 +1043,8 @@ export class EntityScope implements SourceLocated { } // TODO: what if mix of var, const, and init values? } 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); } } else if (initvalue instanceof Uint8Array) { - // TODO??? - let datasym = this.dialect.datasymbol(c, f, e.id); + // TODO: 16/32... + let datasym = this.dialect.datasymbol(c, f, e.id, 0); let ofs = this.bss.symbols[datasym]; initbytes.set(initvalue, ofs); } else { @@ -1095,13 +1119,12 @@ export class EntityScope implements SourceLocated { //s += `\n; event ${event}\n`; systems = systems.filter(s => this.systems.includes(s)); for (let sys of systems) { - let tmplabel = this.dialect.tempLabel(sys); for (let action of sys.actions) { if (action.event == event) { // TODO: use Tokenizer so error msgs are better // TODO: keep event tree let codeeval = new ActionEval(this, sys, action); - codeeval.tmplabel = tmplabel; + codeeval.tmplabel = this.dialect.tempLabel(sys); codeeval.begin(); s += this.dialect.comment(`start action ${sys.name} ${event}`); // TODO s += codeeval.codeToString(); diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index aba14e40..082244e5 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -83,10 +83,10 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel kernelsetup cpx #0+2 - bcs StaticKernel__kernelsetup__5____skipxhi + jcs StaticKernel__kernelsetup__5____skipxhi cpx #0 - bcc StaticKernel__kernelsetup__5____skipxlo + jcc StaticKernel__kernelsetup__5____skipxlo lda PFColor_pfcolor_b0,x sta COLUPF @@ -100,10 +100,10 @@ StaticKernel__kernelsetup__5____skipxhi: ;;; start action StaticKernel kernelsetup cpx #0+4 - bcs StaticKernel__kernelsetup__6____skipxhi + jcs StaticKernel__kernelsetup__6____skipxhi cpx #0 - bcc StaticKernel__kernelsetup__6____skipxlo + jcc StaticKernel__kernelsetup__6____skipxlo lda Playfield_pf_b0,x sta PF0 @@ -142,10 +142,10 @@ StaticKernel__kernel__7____each: ;;; start action StaticKernel kernelsetup cpx #5+2 - bcs StaticKernel__kernelsetup__9____skipxhi + jcs StaticKernel__kernelsetup__9____skipxhi cpx #5 - bcc StaticKernel__kernelsetup__9____skipxlo + jcc StaticKernel__kernelsetup__9____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF @@ -159,7 +159,7 @@ StaticKernel__kernelsetup__9____skipxhi: ;;; start action StaticKernel kernelsetup cpx #4 - bcc StaticKernel__kernelsetup__10____skipxlo + jcc StaticKernel__kernelsetup__10____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -182,7 +182,7 @@ StaticKernel__kernel__7__loop: inx cpx #8 - bne StaticKernel__kernel__7____each + jne StaticKernel__kernel__7____each StaticKernel__kernel__7____exit: ;;; end action StaticKernel kernel From fc0a43b9aff68a56a92218807e311bf1cd0bb136 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 10 Feb 2022 09:21:24 -0600 Subject: [PATCH 058/104] ecs: forward entity refs, deferred components, %binary --- README.md | 3 + src/common/ecs/compiler.ts | 164 ++++++++---- src/common/ecs/ecs.ts | 113 ++++---- src/common/tokenizer.ts | 6 + src/test/testecs.ts | 3 +- test/ecs/sprites1.ecs | 400 ++++++++++++++++++++++++++++ test/ecs/sprites1.txt | 515 +++++++++++++++++++++++++++++++++++++ test/ecs/vcslib.ecs | 240 +++++++++++++++++ test/ecs/vcslib.txt | 250 ++++++++++++++++++ 9 files changed, 1576 insertions(+), 118 deletions(-) create mode 100644 test/ecs/sprites1.ecs create mode 100644 test/ecs/sprites1.txt create mode 100644 test/ecs/vcslib.ecs create mode 100644 test/ecs/vcslib.txt diff --git a/README.md b/README.md index 86389797..ef77a928 100644 --- a/README.md +++ b/README.md @@ -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 + diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 5089dac8..c600224f 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -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; + } +} diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index ca1a4ee5..bb53cd83 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -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]; diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 1b7c3fc9..502cee15 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -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()(); + } + } } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 35649752..a8dbc712 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -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) { diff --git a/test/ecs/sprites1.ecs b/test/ecs/sprites1.ecs new file mode 100644 index 00000000..382bd3cf --- /dev/null +++ b/test/ecs/sprites1.ecs @@ -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 {{Bitmap:bitmapdata}},x + sbc #0 + sta {{$2}},y +; get bitmap height + lda {{Colormap:colormapdata}},x + sbc #0 + sta {{$6}},y +; save ypos + ldx {{$12}} ; restore X + lda {{ 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 {{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 {{(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 \ No newline at end of file diff --git a/test/ecs/vcslib.ecs b/test/ecs/vcslib.ecs new file mode 100644 index 00000000..ac74cf00 --- /dev/null +++ b/test/ecs/vcslib.ecs @@ -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 {{ Date: Thu, 10 Feb 2022 12:55:36 -0600 Subject: [PATCH 059/104] ecs: var/init/const allocation --- src/common/ecs/ecs.ts | 47 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index bb53cd83..13e9bf70 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -941,29 +941,29 @@ export class EntityScope implements SourceLocated { } } // TODO: cull unused entity fields - allocateSegment(segment: DataSegment, readonly: boolean) { + allocateSegment(segment: DataSegment, alloc: boolean, type: 'init' | 'const' | undefined) { let fields: FieldArray[] = Object.values(segment.fieldranges); // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); - let f: FieldArray | undefined; - while (f = fields.pop()) { - let rangelen = (f.ehi - f.elo + 1); - let alloc = !readonly; - // TODO: doesn't work for packed arrays too well - let bits = getPackedFieldSize(f.field); - // variable size? make it a pointer - if (bits == 0) bits = 16; // TODO? - let bytesperelem = Math.ceil(bits / 8); - // TODO: packing bits - // TODO: split arrays - let access = []; - for (let i = 0; i < bits; i += 8) { - let symbol = this.dialect.fieldsymbol(f.component, f.field, i); - access.push({ symbol, bit: i, width: 8 }); // TODO - if (alloc) { - segment.allocateBytes(symbol, rangelen); // TODO + for (let f of fields) { + if (this.fieldtypes[mksymbol(f.component, f.field.name)] == type) { + let rangelen = (f.ehi - f.elo + 1); + // TODO: doesn't work for packed arrays too well + let bits = getPackedFieldSize(f.field); + // variable size? make it a pointer + if (bits == 0) bits = 16; // TODO? + let bytesperelem = Math.ceil(bits / 8); + // TODO: packing bits + // TODO: split arrays + let access = []; + for (let i = 0; i < bits; i += 8) { + let symbol = this.dialect.fieldsymbol(f.component, f.field, i); + access.push({ symbol, bit: i, width: 8 }); // TODO + if (alloc) { + segment.allocateBytes(symbol, rangelen); // TODO + } } + f.access = access; } - f.access = access; } } allocateROData(segment: DataSegment) { @@ -1034,8 +1034,8 @@ export class EntityScope implements SourceLocated { let initvalue = e.inits[scfname]; if (initvalue !== undefined) { let range = segment.getFieldRange(c, f.name); - if (!range) throw new ECSError(`no range`, e); - if (!range.access) throw new ECSError(`no range access`, e); + if (!range) throw new ECSError(`no init range for ${scfname}`, e); + if (!range.access) throw new ECSError(`no init range access for ${scfname}`, e); if (typeof initvalue === 'number') { for (let a of range.access) { let offset = segment.getByteOffset(range, a, e.id); @@ -1172,8 +1172,9 @@ export class EntityScope implements SourceLocated { } private analyzeEntities() { this.buildSegments(); - this.allocateSegment(this.bss, false); - this.allocateSegment(this.rodata, true); + this.allocateSegment(this.bss, true, undefined); // uninitialized vars + this.allocateSegment(this.bss, true, 'init'); // initialized vars + this.allocateSegment(this.rodata, false, 'const'); // constants this.allocateROData(this.rodata); } private generateCode() { From acb50ac4b9cdd20bd9b45b4c9a4ab6eae13ac8e3 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 10 Feb 2022 15:33:27 -0600 Subject: [PATCH 060/104] ecs: changed constant array declaraiton --- src/common/ecs/ecs.ts | 52 ++++++++++++++++++++++--------------------- test/ecs/sprites1.txt | 48 +++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 13e9bf70..daa9ddad 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -566,7 +566,7 @@ class ActionEval { } } else if (this.action.select == 'with') { if (this.qr.entities.length != 1) - throw new ECSError(`query outside of loop must match exactly one entity`, this.action); + throw new ECSError(`${this.sys.name} query outside of loop must match exactly one entity`, this.action); //TODO } break; } @@ -946,6 +946,7 @@ export class EntityScope implements SourceLocated { // TODO: fields.sort((a, b) => (a.ehi - a.elo + 1) * getPackedFieldSize(a.field)); for (let f of fields) { if (this.fieldtypes[mksymbol(f.component, f.field.name)] == type) { + //console.log(f.component.name, f.field.name, type); let rangelen = (f.ehi - f.elo + 1); // TODO: doesn't work for packed arrays too well let bits = getPackedFieldSize(f.field); @@ -971,27 +972,14 @@ export class EntityScope implements SourceLocated { for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; let cfname = mksymbol(c, f.name); - let range = segment.fieldranges[cfname]; - let entcount = range ? range.ehi - range.elo + 1 : 0; - // is this a constant value? - if (v === undefined) { - // this is not a constant - // is it a bounded array? (TODO) - if (f.dtype == 'array' && f.index) { - let datasym = this.dialect.datasymbol(c, f, e.id, 0); - let databytes = getFieldLength(f.index); - let offset = this.bss.allocateBytes(datasym, databytes); - // TODO? this.allocatePointerArray(c, f, datasym, entcount); - let ptrlosym = this.dialect.fieldsymbol(c, f, 0); - 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 hiofs = segment.allocateBytes(ptrhisym, entcount); - if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; - segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; - segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; - } - } else { + // TODO: what if mix of var, const, and init values? + if (this.fieldtypes[cfname] == 'const') { + let range = segment.fieldranges[cfname]; + let entcount = range ? range.ehi - range.elo + 1 : 0; + if (v == null && f.dtype == 'int') v = 0; + if (v == null && f.dtype == 'ref') v = 0; + if (v == null && f.dtype == 'array') throw new ECSError(`no default value for array ${cfname}`) + //console.log(c.name, f.name, '#'+e.id, '=', v); // this is a constant // is it a byte array? //TODO? if (ArrayBuffer.isView(v) && f.dtype == 'array') { @@ -1007,7 +995,8 @@ export class EntityScope implements SourceLocated { segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; } else if (typeof v === 'number') { // more than 1 entity, add an array - if (entcount > 1) { + // TODO: infer need for array by usage + /*if (entcount > 1)*/ { if (!range.access) throw new ECSError(`no access for field ${cfname}`) for (let a of range.access) { segment.allocateBytes(a.symbol, entcount); @@ -1015,7 +1004,20 @@ export class EntityScope implements SourceLocated { segment.initdata[ofs] = (v >> a.bit) & 0xff; } } - // TODO: what if mix of var, const, and init values? + } else if (v == null && f.dtype == 'array' && f.index) { + // TODO + let datasym = this.dialect.datasymbol(c, f, e.id, 0); + let databytes = getFieldLength(f.index); + let offset = this.bss.allocateBytes(datasym, databytes); + // TODO? this.allocatePointerArray(c, f, datasym, entcount); + let ptrlosym = this.dialect.fieldsymbol(c, f, 0); + 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 hiofs = segment.allocateBytes(ptrhisym, entcount); + if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; + segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; + segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; } else { // TODO: bad error message - should say "wrong type, should be array" throw new ECSError(`unhandled constant ${e.id}:${cfname} -- ${typeof v}`); @@ -1172,8 +1174,8 @@ export class EntityScope implements SourceLocated { } private analyzeEntities() { this.buildSegments(); - this.allocateSegment(this.bss, true, undefined); // uninitialized vars this.allocateSegment(this.bss, true, 'init'); // initialized vars + this.allocateSegment(this.bss, true, undefined); // uninitialized vars this.allocateSegment(this.rodata, false, 'const'); // constants this.allocateROData(this.rodata); } diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 920795d8..164ccdb5 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -1,11 +1,6 @@ .scope Main .zeropage -SpriteSlot_sprite_b0: -.res 1 -.res 1 -.res 1 -.res 1 -HasYpos_ypos_b0: +HasBitmap_bitmap_b0: .res 1 .res 1 .res 1 @@ -15,16 +10,21 @@ HasXpos_xpos_b0: .res 1 .res 1 .res 1 +HasYpos_ypos_b0: +.res 1 +.res 1 +.res 1 +.res 1 +SpriteSlot_sprite_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 @@ -44,6 +44,10 @@ Joystick__tmp = TEMP+0 SpriteShuffler__tmp = TEMP+1 SpriteHider__tmp = TEMP+3 .code +KernelSection_lines_b0: +.byte 192 +BGColor_bgcolor_b0: +.byte 162 Bitmap_bitmapdata_e1_b0: .byte 1 .byte 1 @@ -90,25 +94,25 @@ Sprite_plyrflags_b0: .byte 0 .byte 0 Main__INITDATA: +.byte 1 .byte 0 .byte 1 -.byte 2 -.byte 3 -.byte 150 -.byte 60 -.byte 90 -.byte 150 +.byte 0 .byte 50 .byte 100 .byte 80 .byte 40 -.byte 0 -.byte 0 -.byte 0 +.byte 150 +.byte 60 +.byte 90 +.byte 150 .byte 0 .byte 1 +.byte 2 +.byte 3 +.byte 0 +.byte 0 .byte 0 -.byte 1 .byte 0 __Start: .code @@ -125,7 +129,7 @@ __BRK: ldy #20 : lda Main__INITDATA-1,y - sta SpriteSlot_sprite_b0-1,y + sta HasBitmap_bitmap_b0-1,y dey bne :- ; initialize data segment From 4584cc778f7607987b5c6395142d22448d04e2e9 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 10 Feb 2022 15:51:03 -0600 Subject: [PATCH 061/104] ecs: system instance, with #entity.field --- src/common/ecs/compiler.ts | 77 +++++++++------ src/common/ecs/ecs.ts | 127 +++++++++++++++++-------- src/common/tokenizer.ts | 2 +- test/ecs/sprites1.txt | 188 ++++++++++++++++++------------------- test/ecs/vcs1.txt | 60 ++++++------ test/ecs/vcslib.txt | 64 ++++++------- 6 files changed, 295 insertions(+), 223 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index c600224f..9865a547 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -2,7 +2,7 @@ import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { newDecoder } from "./decoder"; -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"; +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, SystemInstance, SystemInstanceParameters, ComponentFieldPair } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -37,7 +37,7 @@ export class ECSCompiler extends Tokenizer { { type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, { type: ECSTokenType.Integer, regex: /[%][01]+/ }, - { type: ECSTokenType.Operator, regex: /[#=,:(){}\[\]\-]/ }, + { type: ECSTokenType.Operator, regex: /[.#=,:(){}\[\]\-]/ }, { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, { type: TokenType.Ignore, regex: /\/\/.*?[\n\r]/ }, { type: TokenType.Ignore, regex: /\/\*.*?\*\// }, @@ -300,12 +300,11 @@ export class ECSCompiler extends Tokenizer { // 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? + let lines = code.split('\n'); if (this.debuginfo) this.addDebugInfo(lines, tok.$loc.line); code = lines.join('\n'); - */ + let acomp = new ECSActionCompiler(context); let nodes = acomp.parseFile(code, this.path); // TODO: return nodes @@ -342,7 +341,7 @@ export class ECSCompiler extends Tokenizer { if (cmd == 'system') { let sys = this.annotate(() => this.parseSystem()); this.em.defineSystem(sys); - this.currentScope.addUsingSystem(sys); + this.currentScope.newSystemInstanceWithDefaults(sys); } } this.currentScope = scope.parent || null; @@ -350,9 +349,9 @@ export class ECSCompiler extends Tokenizer { } parseScopeUsing() { - let syslist = this.parseList(this.parseSystemRef, ','); - for (let sys of syslist) { - this.currentScope?.addUsingSystem(sys); + let instlist = this.parseList(this.parseSystemInstanceRef, ','); + for (let inst of instlist) { + this.currentScope?.newSystemInstance(inst); } } @@ -374,21 +373,21 @@ export class ECSCompiler extends Tokenizer { if (cmd == 'init' || cmd == 'const') { // TODO: check data types let name = this.expectIdent().str; - let { component, field } = this.getEntityField(entity, name); - let symtype = this.currentScope.isConstOrInit(component, name); + let { c, f } = this.getEntityField(entity, name); + let symtype = this.currentScope.isConstOrInit(c, 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); + let valueOrRef = this.parseDataValue(f); 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); + if (cmd == 'const') scope.setConstValue(entity, c, name, refvalue); + if (cmd == 'init') scope.setInitValue(entity, c, 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); + if (cmd == 'const') scope.setConstValue(entity, c, name, valueOrRef as DataValue); + if (cmd == 'init') scope.setInitValue(entity, c, name, valueOrRef as DataValue); } } else if (cmd == 'decode') { let decoderid = this.expectIdent().str; @@ -398,15 +397,15 @@ 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)) { - let { component, field } = this.getEntityField(entity, entry[0]); - scope.setConstValue(entity, component, field.name, entry[1]); + let { c, f } = this.getEntityField(entity, entry[0]); + scope.setConstValue(entity, c, f.name, entry[1]); } } } return entity; } - getEntityField(e: Entity, name: string) { + getEntityField(e: Entity, name: string) : ComponentFieldPair { 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.`) @@ -414,7 +413,7 @@ export class ECSCompiler extends Tokenizer { let component = comps[0]; let field = component.fields.find(f => f.name == name); if (!field) { this.internalError(); throw new Error(); } - return { component, field }; + return { c: component, f: field }; } parseEntityArchetype() : EntityArchetype { @@ -431,14 +430,18 @@ export class ECSCompiler extends Tokenizer { return cref; } - resolveEntityRef(scope: EntityScope, ref: ForwardRef) : number { - let name = ref.token.str; + findEntityByName(scope: EntityScope, token: Token) { + let name = 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.`, ref.token.$loc) + this.compileError(`I couldn't find an entity named "${name}" in this scope.`, token.$loc) throw new Error(); } - let id = eref.id; + return eref; + } + + resolveEntityRef(scope: EntityScope, ref: ForwardRef) : number { + let id = this.findEntityByName(scope, ref.token).id; if (ref.reftype) { // TODO: make this a function? elo ehi etc? let atypes = this.em.archetypesMatching(ref.reftype.query); @@ -450,11 +453,29 @@ export class ECSCompiler extends Tokenizer { return id; } - parseSystemRef() : System { + parseSystemInstanceRef() : SystemInstance { let name = this.expectIdent().str; - let sys = this.em.getSystemByName(name); - if (!sys) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc); - return sys; + let system = this.em.getSystemByName(name); + if (!system) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc); + let params = {}; + if (this.peekToken().str == 'with') { + this.consumeToken(); + params = this.parseSystemInstanceParameters(); + } + let inst = { system, params, id: 0 }; + return inst; + } + + parseSystemInstanceParameters() : SystemInstanceParameters { + let scope = this.currentScope; + if (scope == null) throw new Error(); + this.expectToken('#'); + let entname = this.expectIdent(); + this.expectToken('.'); + let fieldname = this.expectIdent(); + let entity = this.findEntityByName(scope, entname); + let cf = this.getEntityField(entity, fieldname.str); + return { refEntity: entity, refField: cf }; } exportToFile(src: SourceFileExport) { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index daa9ddad..c47a6e47 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -59,6 +59,17 @@ export interface System extends SourceLocated { tempbytes?: number; } +export interface SystemInstanceParameters { + refEntity?: Entity; + refField?: ComponentFieldPair; +} + +export interface SystemInstance extends SourceLocated { + system: System; + params: SystemInstanceParameters; + id: number; +} + export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select'] as const; export type SelectType = typeof SELECT_TYPE[number]; @@ -166,7 +177,7 @@ interface ConstByte { bitofs: number; } -interface ComponentFieldPair { +export interface ComponentFieldPair { c: ComponentType; f: DataField; } @@ -227,6 +238,11 @@ export class Dialect_CA65 { @__skipxhi: ` + ASM_LOOKUP_REF_X = ` + ldx {{%reffield}} + {{%code}} +` + // TODO ASM_MAP_RANGES = ` txa @@ -312,8 +328,8 @@ export class Dialect_CA65 { else return `.byte (${b.symbol} >> ${b.bitofs})` // TODO? } } - tempLabel(sys: System) { - return `${sys.name}__tmp`; + tempLabel(inst: SystemInstance) { + return `${inst.system.name}__${inst.id}__tmp`; // TODO: multiple instances? } equate(symbol: string, value: string): string { return `${symbol} = ${value}`; @@ -517,15 +533,25 @@ class ActionEval { constructor( readonly scope: EntityScope, - readonly sys: System, + readonly instance: SystemInstance, readonly action: Action) { this.em = scope.em; this.dialect = scope.em.dialect; this.oldState = scope.state; + this.tmplabel = this.dialect.tempLabel(this.instance); let q = (action as ActionWithQuery).query; if (q) this.qr = new EntitySet(scope, q); else this.qr = new EntitySet(scope, undefined, [], []); + // TODO? error if none? + if (instance.params.refEntity && instance.params.refField) { + let rf = instance.params.refField; + if (rf.f.dtype == 'ref') { + let rq = rf.f.query; + this.qr = this.qr.intersection(new EntitySet(scope, rq)); + //console.log('with', instance.params, rq, this.qr); + } + } this.entities = this.qr.entities; //let query = (this.action as ActionWithQuery).query; //TODO? if (query && this.entities.length == 0) @@ -565,8 +591,11 @@ class ActionEval { this.entities = int.entities; // TODO? } } else if (this.action.select == 'with') { - if (this.qr.entities.length != 1) - throw new ECSError(`${this.sys.name} query outside of loop must match exactly one entity`, this.action); //TODO + if (this.instance.params.refEntity && this.instance.params.refField) { + state.x = this.qr; + // ??? + } else if (this.qr.entities.length != 1) + throw new ECSError(`${this.instance.system.name} query outside of loop must match exactly one entity`, this.action); //TODO } break; } @@ -583,7 +612,7 @@ class ActionEval { return ''; let action = this.action; - let sys = this.sys; + let sys = this.instance.system; let code = action.text; let label = `${sys.name}__${action.event}__${this.em.seq++}`; // TODO: better label that won't conflict (seq?) let props: { [name: string]: string } = {}; @@ -612,7 +641,18 @@ class ActionEval { // filter entities from loop? // TODO: when to ignore if entities.length == 1 and not in for loop? if (action.select == 'with') { - code = this.wrapCodeInFilter(code); + // TODO? when to load x? + if (this.instance.params.refEntity && this.instance.params.refField) { + let re = this.instance.params.refEntity; + let rf = this.instance.params.refField; + code = this.wrapCodeInRefLookup(code); + // TODO: only fetches 1st entity in list, need offset + let range = this.scope.bss.getFieldRange(rf.c, rf.f.name) || this.scope.rodata.getFieldRange(rf.c, rf.f.name); + let eidofs = re.id - range.elo; + props['%reffield'] = `${this.dialect.fieldsymbol(rf.c, rf.f, 0)}+${eidofs}`; + } else { + code = this.wrapCodeInFilter(code); + } } if (action.select == 'if') { code = this.wrapCodeInFilter(code); @@ -699,10 +739,11 @@ class ActionEval { } __local(args: string[]) { let tempinc = parseInt(args[0]); + let tempbytes = this.instance.system.tempbytes; if (isNaN(tempinc)) throw new ECSError(`bad temporary offset`, this.action); - if (!this.sys.tempbytes) throw new ECSError(`this system has no locals`, this.action); - if (tempinc < 0 || tempinc >= this.sys.tempbytes) throw new ECSError(`this system only has ${this.sys.tempbytes} locals`, this.action); - this.scope.updateTempLiveness(this.sys); + if (!tempbytes) throw new ECSError(`this system has no locals`, this.action); + if (tempinc < 0 || tempinc >= tempbytes) throw new ECSError(`this system only has ${tempbytes} locals`, this.action); + this.scope.updateTempLiveness(this.instance); return `${this.tmplabel}+${tempinc}`; } __bss_init(args: string[]) { @@ -735,6 +776,10 @@ class ActionEval { } return code; } + wrapCodeInRefLookup(code: string) { + code = this.dialect.ASM_LOOKUP_REF_X.replace('{{%code}}', code); + return code; + } generateCodeForField(fieldName: string, bitofs: number, canWrite: boolean): string { const action = this.action; const qr = this.jr || this.qr; // TODO: why not both! @@ -841,10 +886,10 @@ class ActionEval { export class EntityScope implements SourceLocated { $loc: SourceLocation; childScopes: EntityScope[] = []; - systems: System[] = []; + instances: SystemInstance[] = []; entities: Entity[] = []; fieldtypes: { [name: string]: 'init' | 'const' } = {}; - sysstats = new Map(); + sysstats = new Map(); actionstats = new Map(); bss = new UninitDataSegment(); rodata = new ConstDataSegment(); @@ -876,9 +921,14 @@ export class EntityScope implements SourceLocated { this.entities.push(entity); return entity; } - addUsingSystem(system: System) { - if (!system) throw new Error(); - this.systems.push(system); + newSystemInstance(inst: SystemInstance) { + if (!inst) throw new Error(); + inst.id = this.instances.length+1; + this.instances.push(inst); + return inst; + } + newSystemInstanceWithDefaults(system: System) { + return this.newSystemInstance({ system, params: {}, id:0 }); } getEntityByName(name: string) { return this.entities.find(e => e.name == name); @@ -1093,18 +1143,18 @@ export class EntityScope implements SourceLocated { // generate code let s = this.dialect.code(); //s += `\n; event ${event}\n`; - systems = systems.filter(s => this.systems.includes(s)); - for (let sys of systems) { + let instances = this.instances.filter(inst => systems.includes(inst.system)); + for (let inst of instances) { + let sys = inst.system; for (let action of sys.actions) { if (action.event == event) { // TODO: use Tokenizer so error msgs are better // TODO: keep event tree - let codeeval = new ActionEval(this, sys, action); - codeeval.tmplabel = this.dialect.tempLabel(sys); + let codeeval = new ActionEval(this, inst, action); codeeval.begin(); - s += this.dialect.comment(`start action ${sys.name} ${event}`); // TODO + s += this.dialect.comment(`start action ${sys.name} ${inst.id} ${event}`); // TODO s += codeeval.codeToString(); - s += this.dialect.comment(`end action ${sys.name} ${event}`); + s += this.dialect.comment(`end action ${sys.name} ${inst.id} ${event}`); // TODO: check that this happens once? codeeval.end(); this.getActionStats(action).callcount++; @@ -1113,11 +1163,11 @@ export class EntityScope implements SourceLocated { } return s; } - getSystemStats(sys: System) : SystemStats { - let stats = this.sysstats.get(sys); + getSystemStats(inst: SystemInstance) : SystemStats { + let stats = this.sysstats.get(inst); if (!stats) { stats = new SystemStats(); - this.sysstats.set(sys, stats); + this.sysstats.set(inst, stats); } return stats; } @@ -1129,8 +1179,8 @@ export class EntityScope implements SourceLocated { } return stats; } - updateTempLiveness(sys: System) { - let stats = this.getSystemStats(sys); + updateTempLiveness(inst: SystemInstance) { + let stats = this.getSystemStats(inst); let n = this.eventSeq; if (stats.tempstartseq && stats.tempendseq) { stats.tempstartseq = Math.min(stats.tempstartseq, n); @@ -1148,14 +1198,14 @@ export class EntityScope implements SourceLocated { let maxTempBytes = 128 - this.bss.size; // TODO: multiple data segs let bssbin = new Bin({ left:0, top:0, bottom: this.eventSeq+1, right: maxTempBytes }); pack.bins.push(bssbin); - for (let sys of this.systems) { - let stats = this.getSystemStats(sys); - if (sys.tempbytes && stats.tempstartseq && stats.tempendseq) { + for (let instance of this.instances) { + let stats = this.getSystemStats(instance); + if (instance.system.tempbytes && stats.tempstartseq && stats.tempendseq) { let v = { - sys, + inst: instance, top: stats.tempstartseq, bottom: stats.tempendseq+1, - width: sys.tempbytes, + width: instance.system.tempbytes, height: stats.tempendseq - stats.tempstartseq + 1, }; pack.boxes.push(v); @@ -1166,9 +1216,9 @@ export class EntityScope implements SourceLocated { if (bssbin.extents.right > 0) { this.bss.allocateBytes('TEMP', bssbin.extents.right); for (let b of pack.boxes) { - let sys : System = (b as any).sys; - console.log(sys.name, b.box?.left); - this.bss.equates[this.dialect.tempLabel(sys)] = `TEMP+${b.box?.left}`; + let inst : SystemInstance = (b as any).inst; + console.log(inst.system.name, b.box?.left); + this.bss.equates[this.dialect.tempLabel(inst)] = `TEMP+${b.box?.left}`; } } } @@ -1184,7 +1234,7 @@ export class EntityScope implements SourceLocated { let start; let initsys = this.em.getSystemByName('Init'); if (isMainScope && initsys) { - this.addUsingSystem(initsys); //TODO: what if none? + this.newSystemInstanceWithDefaults(initsys); //TODO: what if none? start = this.generateCodeForEvent('main_init'); } else { start = this.generateCodeForEvent('start'); @@ -1197,8 +1247,9 @@ export class EntityScope implements SourceLocated { //this.showStats(); } showStats() { - for (let sys of this.systems) { - console.log(sys.name, this.getSystemStats(sys)); + for (let inst of this.instances) { + // TODO? + console.log(inst.system.name, this.getSystemStats(inst)); } for (let action of Array.from(this.actionstats.keys())) { console.log(action.event, this.getActionStats(action)); diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 502cee15..567e38d3 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -118,7 +118,7 @@ export class Tokenizer { switch (rule.type) { case TokenType.CatchAll: if (this.errorOnCatchAll) { - this.compileError(`I didn't expect the character "${m[0]}" here.`); + this.compileError(`I didn't expect the character "${m[0]}" here.`, loc); } default: this.pushToken({ str: s, type: rule.type, $loc: loc }); diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 164ccdb5..2c0a333d 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -39,10 +39,10 @@ TEMP: .res 1 .res 1 .res 1 -Kernel2Sprite__tmp = TEMP+0 -Joystick__tmp = TEMP+0 -SpriteShuffler__tmp = TEMP+1 -SpriteHider__tmp = TEMP+3 +Kernel2Sprite__2__tmp = TEMP+0 +Joystick__3__tmp = TEMP+0 +SpriteShuffler__8__tmp = TEMP+1 +SpriteHider__9__tmp = TEMP+3 .code KernelSection_lines_b0: .byte 192 @@ -117,7 +117,7 @@ Main__INITDATA: __Start: .code -;;; start action Init main_init +;;; start action Init 10 main_init .include "vcs-ca65.h" .macpack longbranch @@ -135,20 +135,20 @@ __BRK: ; initialize data segment .code -;;; start action FrameLoop start +;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code -;;; start action Kernel2Sprite preframe +;;; start action Kernel2Sprite 2 preframe .define KLINES #192 .define KPAD 32 -;;; end action Kernel2Sprite preframe +;;; end action Kernel2Sprite 2 preframe -;;; start action Kernel2Sprite preframe +;;; start action Kernel2Sprite 2 preframe ldy #0 Kernel2Sprite__preframe__4____each: @@ -162,76 +162,76 @@ Kernel2Sprite__preframe__4____each: lda KLINES+KPAD sec sbc HasYpos_ypos_b0,x - sta Kernel2Sprite__tmp+11 + sta Kernel2Sprite__2__tmp+11 ; calculate bitmap pointer - stx Kernel2Sprite__tmp+12 ; save X (Sprite index) + stx Kernel2Sprite__2__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 + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index lda Bitmap_bitmapdata_b8,x sbc #0 - sta Kernel2Sprite__tmp+2,y + sta Kernel2Sprite__2__tmp+2,y ; get bitmap height lda Bitmap_height_b0,x - sta Kernel2Sprite__tmp+8,y + sta Kernel2Sprite__2__tmp+8,y ; calculate colormap pointer - ldx Kernel2Sprite__tmp+12 ; restore X + ldx Kernel2Sprite__2__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 + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+4,y lda Colormap_colormapdata_b8,x sbc #0 - sta Kernel2Sprite__tmp+6,y + sta Kernel2Sprite__2__tmp+6,y ; save ypos - ldx Kernel2Sprite__tmp+12 ; restore X + ldx Kernel2Sprite__2__tmp+12 ; restore X lda HasYpos_ypos_b0,x - sta Kernel2Sprite__tmp+10,y + sta Kernel2Sprite__2__tmp+10,y iny cpy #2 jne Kernel2Sprite__preframe__4____each Kernel2Sprite__preframe__4____exit: -;;; end action Kernel2Sprite preframe +;;; end action Kernel2Sprite 2 preframe -;;; start action Kernel2Sprite preframe +;;; start action Kernel2Sprite 2 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 + lda Kernel2Sprite__2__tmp+1 + ldy Kernel2Sprite__2__tmp+2 + sty Kernel2Sprite__2__tmp+1 + sta Kernel2Sprite__2__tmp+2 + lda Kernel2Sprite__2__tmp+5 + ldy Kernel2Sprite__2__tmp+6 + sty Kernel2Sprite__2__tmp+5 + sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite preframe +;;; end action Kernel2Sprite 2 preframe -;;; start action Kernel2Sprite preframe +;;; start action Kernel2Sprite 2 preframe lda #162 sta COLUBK -;;; end action Kernel2Sprite preframe +;;; end action Kernel2Sprite 2 preframe -;;; start action Kernel2Sprite preframe +;;; start action Kernel2Sprite 2 preframe -;;; end action Kernel2Sprite preframe +;;; end action Kernel2Sprite 2 preframe -;;; start action SetXPos preframe +;;; start action SetXPos 6 preframe sta HMCLR -;;; end action SetXPos preframe +;;; end action SetXPos 6 preframe -;;; start action SetXPos preframe +;;; start action SetXPos 6 preframe ldy #0 SetXPos__preframe__8____each: @@ -240,7 +240,7 @@ SetXPos__preframe__8____each: lda HasXpos_xpos_b0,x .code -;;; start action SetHorizPos SetHorizPos +;;; start action SetHorizPos 7 SetHorizPos ; SetHorizPos routine ; A = X coordinate @@ -259,7 +259,7 @@ SetHorizPos__SetHorizPos__9__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos SetHorizPos +;;; end action SetHorizPos 7 SetHorizPos iny @@ -267,61 +267,61 @@ SetHorizPos__SetHorizPos__9__DivideLoop: jne SetXPos__preframe__8____each SetXPos__preframe__8____exit: -;;; end action SetXPos preframe +;;; end action SetXPos 6 preframe -;;; start action SetXPos preframe +;;; start action SetXPos 6 preframe -;;; end action SetXPos preframe +;;; end action SetXPos 6 preframe -;;; start action SetXPos preframe +;;; start action SetXPos 6 preframe sta WSYNC sta HMOVE -;;; end action SetXPos preframe +;;; end action SetXPos 6 preframe KERNEL_START .code -;;; start action Kernel2Sprite kernel +;;; start action Kernel2Sprite 2 kernel ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite kernel +;;; end action Kernel2Sprite 2 kernel -;;; start action Kernel2Sprite kernel +;;; start action Kernel2Sprite 2 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 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos bcs DoDraw1 lda #0 .byte $2C DoDraw1: - lda (Kernel2Sprite__tmp+0),y + lda (Kernel2Sprite__2__tmp+0),y .if do_wsync sta WSYNC .endif sta GRP0 - lda (Kernel2Sprite__tmp+4),y + lda (Kernel2Sprite__2__tmp+4),y sta COLUP0 ; draw player 1 - lda Kernel2Sprite__tmp+9 ; height - dcp Kernel2Sprite__tmp+11 ; ypos + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos bcs DoDraw2 lda #0 .byte $2C DoDraw2: - lda (Kernel2Sprite__tmp+2),y + lda (Kernel2Sprite__2__tmp+2),y sta GRP1 - lda (Kernel2Sprite__tmp+6),y + lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 .endmacro @@ -329,9 +329,9 @@ DoDraw2: Kernel2Sprite__kernel__12__LVScan: .code -;;; start action Kernel2Sprite scanline1 +;;; start action Kernel2Sprite 2 scanline1 -;;; end action Kernel2Sprite scanline1 +;;; end action Kernel2Sprite 2 scanline1 Kernel2Sprite__kernel__12__DrawLine 1 ; macro: draw scanline w/ WSYNC dey ; next scanline @@ -341,9 +341,9 @@ Kernel2Sprite__kernel__12__LVScan: dey ; next scanline bne Kernel2Sprite__kernel__12__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite kernel +;;; end action Kernel2Sprite 2 kernel -;;; start action Kernel2Sprite kernel +;;; start action Kernel2Sprite 2 kernel lda #0 sta GRP0 @@ -351,29 +351,29 @@ Kernel2Sprite__kernel__12__LVScan: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite kernel +;;; end action Kernel2Sprite 2 kernel KERNEL_END .code -;;; start action Joystick postframe +;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each lda SWCHA - sta Joystick__tmp+0 + sta Joystick__3__tmp+0 -;;; end action Joystick postframe +;;; end action Joystick 3 postframe -;;; start action Joystick postframe +;;; start action Joystick 3 postframe ldx #0 Joystick__postframe__15____each: - asl Joystick__tmp+0 + asl Joystick__3__tmp+0 bcs Joystick__postframe__15__SkipMoveRight .code -;;; start action MoveJoyX joyright +;;; start action MoveJoyX 4 joyright lda HasXpos_xpos_b0,x clc @@ -383,14 +383,14 @@ Joystick__postframe__15____each: sta HasXpos_xpos_b0,x MoveJoyX__joyright__16__nomove: -;;; end action MoveJoyX joyright +;;; end action MoveJoyX 4 joyright Joystick__postframe__15__SkipMoveRight: - asl Joystick__tmp+0 + asl Joystick__3__tmp+0 bcs Joystick__postframe__15__SkipMoveLeft .code -;;; start action MoveJoyX joyleft +;;; start action MoveJoyX 4 joyleft lda HasXpos_xpos_b0,x sec @@ -399,14 +399,14 @@ Joystick__postframe__15__SkipMoveRight: sta HasXpos_xpos_b0,x MoveJoyX__joyleft__17__nomove: -;;; end action MoveJoyX joyleft +;;; end action MoveJoyX 4 joyleft Joystick__postframe__15__SkipMoveLeft: - asl Joystick__tmp+0 + asl Joystick__3__tmp+0 bcs Joystick__postframe__15__SkipMoveDown .code -;;; start action MoveJoyY joydown +;;; start action MoveJoyY 5 joydown lda HasYpos_ypos_b0,x clc @@ -416,14 +416,14 @@ Joystick__postframe__15__SkipMoveLeft: sta HasYpos_ypos_b0,x MoveJoyY__joydown__18__nomove: -;;; end action MoveJoyY joydown +;;; end action MoveJoyY 5 joydown Joystick__postframe__15__SkipMoveDown: - asl Joystick__tmp+0 + asl Joystick__3__tmp+0 bcs Joystick__postframe__15__SkipMoveUp .code -;;; start action MoveJoyY joyup +;;; start action MoveJoyY 5 joyup lda HasYpos_ypos_b0,x sec @@ -432,7 +432,7 @@ Joystick__postframe__15__SkipMoveDown: sta HasYpos_ypos_b0,x MoveJoyY__joyup__19__nomove: -;;; end action MoveJoyY joyup +;;; end action MoveJoyY 5 joyup Joystick__postframe__15__SkipMoveUp: @@ -441,15 +441,15 @@ Joystick__postframe__15__SkipMoveUp: jne Joystick__postframe__15____each Joystick__postframe__15____exit: -;;; end action Joystick postframe +;;; end action Joystick 3 postframe -;;; start action SpriteShuffler postframe +;;; start action SpriteShuffler 8 postframe ; load two sprite slots at left side of array lda SpriteSlot_sprite_b0 - sta SpriteShuffler__tmp+0 + sta SpriteShuffler__8__tmp+0 lda SpriteSlot_sprite_b0+1 - sta SpriteShuffler__tmp+1 + sta SpriteShuffler__8__tmp+1 ; move two slots to the left ldx #0 SpriteShuffler__postframe__20__loop: @@ -459,21 +459,21 @@ SpriteShuffler__postframe__20__loop: cpx #4-2 bne SpriteShuffler__postframe__20__loop ; store two sprite slots at right side of array - lda SpriteShuffler__tmp+0 + lda SpriteShuffler__8__tmp+0 sta SpriteSlot_sprite_b0+4-2 - lda SpriteShuffler__tmp+1 + lda SpriteShuffler__8__tmp+1 sta SpriteSlot_sprite_b0+4-1 -;;; end action SpriteShuffler postframe +;;; end action SpriteShuffler 8 postframe -;;; start action SpriteHider postframe +;;; start action SpriteHider 9 postframe lda #4-1 - sta SpriteHider__tmp+0 + sta SpriteHider__9__tmp+0 -;;; end action SpriteHider postframe +;;; end action SpriteHider 9 postframe -;;; start action SpriteHider postframe +;;; start action SpriteHider 9 postframe ldy #0 SpriteHider__postframe__22____each: @@ -485,12 +485,12 @@ SpriteHider__postframe__22____each: ; 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 + ldx SpriteHider__9__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 + dec SpriteHider__9__tmp+0 SpriteHider__postframe__22__skip: iny @@ -498,14 +498,14 @@ SpriteHider__postframe__22__skip: jne SpriteHider__postframe__22____each SpriteHider__postframe__22____exit: -;;; end action SpriteHider postframe +;;; end action SpriteHider 9 postframe FRAME_END .code jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop start +;;; end action FrameLoop 1 start ; start main routine .segment "VECTORS" Return: .word $6060 @@ -513,7 +513,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init main_init +;;; end action Init 10 main_init .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index 082244e5..c13f7bb2 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -5,7 +5,7 @@ PFColor_pfcolor_b0: .res 1 TEMP: .res 1 -Local__tmp = TEMP+0 +Local__6__tmp = TEMP+0 .code KernelSection_lines_b0: .byte 2 @@ -46,7 +46,7 @@ Main__INITDATA: __Start: .code -;;; start action Init main_init +;;; start action Init 7 main_init .include "vcs-ca65.h" .define PAL 0 @@ -63,24 +63,24 @@ __BRK: ; initialize data segment .code -;;; start action FrameLoop start +;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code -;;; start action StaticKernel preframe +;;; start action StaticKernel 4 preframe .code -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup lda #24 sta COLUBK -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #0+2 jcs StaticKernel__kernelsetup__5____skipxhi @@ -95,9 +95,9 @@ StaticKernel__kernelsetup__5____skipxlo: StaticKernel__kernelsetup__5____skipxhi: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #0+4 jcs StaticKernel__kernelsetup__6____skipxhi @@ -116,15 +116,15 @@ StaticKernel__kernelsetup__6____skipxlo: StaticKernel__kernelsetup__6____skipxhi: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; end action StaticKernel preframe +;;; end action StaticKernel 4 preframe KERNEL_START .code -;;; start action StaticKernel kernel +;;; start action StaticKernel 4 kernel ldx #0 StaticKernel__kernel__7____each: @@ -132,14 +132,14 @@ StaticKernel__kernel__7____each: sta WSYNC .code -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #5+2 jcs StaticKernel__kernelsetup__9____skipxhi @@ -154,9 +154,9 @@ StaticKernel__kernelsetup__9____skipxlo: StaticKernel__kernelsetup__9____skipxhi: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #4 jcc StaticKernel__kernelsetup__10____skipxlo @@ -170,7 +170,7 @@ StaticKernel__kernelsetup__9____skipxhi: StaticKernel__kernelsetup__10____skipxlo: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup ldy KernelSection_lines_b0,x StaticKernel__kernel__7__loop: @@ -185,50 +185,50 @@ StaticKernel__kernel__7__loop: jne StaticKernel__kernel__7____each StaticKernel__kernel__7____exit: -;;; end action StaticKernel kernel +;;; end action StaticKernel 4 kernel KERNEL_END .code -;;; start action JoyButton postframe +;;; start action JoyButton 5 postframe lda INPT4 ;read button input bmi JoyButton__postframe__11__NotPressed .code -;;; start action Local joybutton +;;; start action Local 6 joybutton - inc Local__tmp+0 + inc Local__6__tmp+0 inc PFColor_pfcolor_b0 -;;; end action Local joybutton +;;; end action Local 6 joybutton JoyButton__postframe__11__NotPressed: -;;; end action JoyButton postframe +;;; end action JoyButton 5 postframe FRAME_END .code -;;; start action ResetSwitch nextframe +;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__13__NoStart .code -;;; start action ResetConsole resetswitch +;;; start action ResetConsole 3 resetswitch jmp Main::__Reset ; jump to Reset handler -;;; end action ResetConsole resetswitch +;;; end action ResetConsole 3 resetswitch ResetSwitch__nextframe__13__NoStart: -;;; end action ResetSwitch nextframe +;;; end action ResetSwitch 2 nextframe jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop start +;;; end action FrameLoop 1 start ; start main routine .segment "VECTORS" Return: .word $6060 @@ -236,7 +236,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init main_init +;;; end action Init 7 main_init .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index b92fdc60..38fa892b 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -5,7 +5,7 @@ PFColor_pfcolor_b0: .res 1 TEMP: .res 1 -Local__tmp = TEMP+0 +Local__6__tmp = TEMP+0 .code KernelSection_lines_b0: .byte 2 @@ -46,7 +46,7 @@ Main__INITDATA: __Start: .code -;;; start action Init main_init +;;; start action Init 7 main_init .include "vcs-ca65.h" .macpack longbranch @@ -64,24 +64,24 @@ __BRK: ; initialize data segment .code -;;; start action FrameLoop start +;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code -;;; start action StaticKernel preframe +;;; start action StaticKernel 4 preframe .code -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup lda #24 sta COLUBK -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #0+2 jcs StaticKernel__kernelsetup__5____skipxhi @@ -96,9 +96,9 @@ StaticKernel__kernelsetup__5____skipxlo: StaticKernel__kernelsetup__5____skipxhi: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #0+4 jcs StaticKernel__kernelsetup__6____skipxhi @@ -117,15 +117,15 @@ StaticKernel__kernelsetup__6____skipxlo: StaticKernel__kernelsetup__6____skipxhi: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; end action StaticKernel preframe +;;; end action StaticKernel 4 preframe KERNEL_START .code -;;; start action StaticKernel kernel +;;; start action StaticKernel 4 kernel ldx #0 StaticKernel__kernel__7____each: @@ -133,14 +133,14 @@ StaticKernel__kernel__7____each: sta WSYNC .code -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #5+2 jcs StaticKernel__kernelsetup__9____skipxhi @@ -155,9 +155,9 @@ StaticKernel__kernelsetup__9____skipxlo: StaticKernel__kernelsetup__9____skipxhi: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup -;;; start action StaticKernel kernelsetup +;;; start action StaticKernel 4 kernelsetup cpx #4 jcc StaticKernel__kernelsetup__10____skipxlo @@ -171,11 +171,11 @@ StaticKernel__kernelsetup__9____skipxhi: StaticKernel__kernelsetup__10____skipxlo: -;;; end action StaticKernel kernelsetup +;;; end action StaticKernel 4 kernelsetup .code -;;; start action StaticKernel kerneldraw +;;; start action StaticKernel 4 kerneldraw ldy KernelSection_lines_b0,x StaticKernel__kerneldraw__11__loop: @@ -184,7 +184,7 @@ StaticKernel__kerneldraw__11__loop: dey bne StaticKernel__kerneldraw__11__loop -;;; end action StaticKernel kerneldraw +;;; end action StaticKernel 4 kerneldraw @@ -193,50 +193,50 @@ StaticKernel__kerneldraw__11__loop: jne StaticKernel__kernel__7____each StaticKernel__kernel__7____exit: -;;; end action StaticKernel kernel +;;; end action StaticKernel 4 kernel KERNEL_END .code -;;; start action JoyButton postframe +;;; start action JoyButton 5 postframe lda INPT4 ;read button input bmi JoyButton__postframe__12__NotPressed .code -;;; start action Local joybutton +;;; start action Local 6 joybutton - inc Local__tmp+0 + inc Local__6__tmp+0 inc PFColor_pfcolor_b0 -;;; end action Local joybutton +;;; end action Local 6 joybutton JoyButton__postframe__12__NotPressed: -;;; end action JoyButton postframe +;;; end action JoyButton 5 postframe FRAME_END .code -;;; start action ResetSwitch nextframe +;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__14__NoStart .code -;;; start action ResetConsole resetswitch +;;; start action ResetConsole 3 resetswitch jmp Main::__Reset ; jump to Reset handler -;;; end action ResetConsole resetswitch +;;; end action ResetConsole 3 resetswitch ResetSwitch__nextframe__14__NoStart: -;;; end action ResetSwitch nextframe +;;; end action ResetSwitch 2 nextframe jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop start +;;; end action FrameLoop 1 start ; start main routine .segment "VECTORS" Return: .word $6060 @@ -244,7 +244,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init main_init +;;; end action Init 7 main_init .endscope Main__Start = Main::__Start \ No newline at end of file From e461ae41d90d5729693932df10d0fef886218360 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 10 Feb 2022 22:01:05 -0600 Subject: [PATCH 062/104] ecs: no actionstats --- src/common/ecs/compiler.ts | 12 +- src/common/ecs/ecs.ts | 46 +++--- src/test/testecs.ts | 293 +------------------------------------ 3 files changed, 28 insertions(+), 323 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 9865a547..ae522ba1 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -2,7 +2,7 @@ import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { newDecoder } from "./decoder"; -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, SystemInstance, SystemInstanceParameters, ComponentFieldPair } from "./ecs"; +import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -21,7 +21,7 @@ interface ForwardRef { export class ECSCompiler extends Tokenizer { currentScope: EntityScope | null = null; - currentContext: ActionOwner | null = null; + currentContext: ActionContext | null = null; debuginfo = false; constructor( @@ -222,7 +222,7 @@ export class ECSCompiler extends Tokenizer { tempbytes = this.expectInteger(); } let system : System = { name, tempbytes, actions: [] }; - let context : ActionOwner = { scope: null, system }; + let context : ActionContext = { scope: null, system }; let text = this.parseCode(context); let select : SelectType = 'once'; let action : Action = { text, event: name, select }; @@ -253,7 +253,7 @@ export class ECSCompiler extends Tokenizer { if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } else query.limit = this.expectInteger(); } - let context : ActionOwner = { scope: null, system }; + let context : ActionContext = { scope: null, system }; let text = this.parseCode(context); let direction = undefined; if (modifiers['asc']) direction = 'asc'; @@ -296,7 +296,7 @@ export class ECSCompiler extends Tokenizer { return this.parseList(this.parseEventName, ","); } - parseCode(context: ActionOwner): string { // TODOActionNode[] { + parseCode(context: ActionContext): string { // TODOActionNode[] { // TODO: add $loc let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); let code = tok.str.substring(3, tok.str.length-3); @@ -494,7 +494,7 @@ export class ECSCompiler extends Tokenizer { export class ECSActionCompiler extends Tokenizer { constructor( - public readonly context: ActionOwner) + public readonly context: ActionContext) { super(); this.setTokenRules([ diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index c47a6e47..15bb7ff2 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -74,25 +74,23 @@ export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select'] a export type SelectType = typeof SELECT_TYPE[number]; -export class ActionStats { - callcount: number = 0; -} - -export interface ActionOwner { +export interface ActionContext { system: System scope: EntityScope | null } export class ActionNode implements SourceLocated { constructor( - public readonly owner: ActionOwner, + public readonly owner: ActionContext, public readonly $loc: SourceLocation ) { } + + children?: ActionNode[]; } export class CodeLiteralNode extends ActionNode { constructor( - owner: ActionOwner, + owner: ActionContext, $loc: SourceLocation, public readonly text: string ) { @@ -102,7 +100,7 @@ export class CodeLiteralNode extends ActionNode { export class CodePlaceholderNode extends ActionNode { constructor( - owner: ActionOwner, + owner: ActionContext, $loc: SourceLocation, public readonly args: string[] ) { @@ -110,6 +108,15 @@ export class CodePlaceholderNode extends ActionNode { } } +class QueryNode extends ActionNode { +} + +class WrapperNode extends ActionNode { +} + +class LoopNode extends ActionNode { +} + export interface ActionBase extends SourceLocated { select: SelectType; @@ -518,10 +525,6 @@ class ActionCPUState { yofs: number = 0; } -class ActionContext { - -} - class ActionEval { em : EntityManager; dialect : Dialect_CA65; @@ -541,8 +544,10 @@ class ActionEval { this.oldState = scope.state; this.tmplabel = this.dialect.tempLabel(this.instance); let q = (action as ActionWithQuery).query; - if (q) this.qr = new EntitySet(scope, q); - else this.qr = new EntitySet(scope, undefined, [], []); + if (q) + this.qr = new EntitySet(scope, q); + else + this.qr = new EntitySet(scope, undefined, [], []); // TODO? error if none? if (instance.params.refEntity && instance.params.refField) { let rf = instance.params.refField; @@ -890,7 +895,6 @@ export class EntityScope implements SourceLocated { entities: Entity[] = []; fieldtypes: { [name: string]: 'init' | 'const' } = {}; sysstats = new Map(); - actionstats = new Map(); bss = new UninitDataSegment(); rodata = new ConstDataSegment(); code = new CodeSegment(); @@ -1157,7 +1161,6 @@ export class EntityScope implements SourceLocated { s += this.dialect.comment(`end action ${sys.name} ${inst.id} ${event}`); // TODO: check that this happens once? codeeval.end(); - this.getActionStats(action).callcount++; } } } @@ -1171,14 +1174,6 @@ export class EntityScope implements SourceLocated { } return stats; } - getActionStats(action: Action) : ActionStats { - let stats = this.actionstats.get(action); - if (!stats) { - stats = new ActionStats(); - this.actionstats.set(action, stats); - } - return stats; - } updateTempLiveness(inst: SystemInstance) { let stats = this.getSystemStats(inst); let n = this.eventSeq; @@ -1251,9 +1246,6 @@ export class EntityScope implements SourceLocated { // TODO? console.log(inst.system.name, this.getSystemStats(inst)); } - for (let action of Array.from(this.actionstats.keys())) { - console.log(action.event, this.getActionStats(action)); - } } private dumpCodeTo(file: SourceFileExport) { let dialect = this.dialect; diff --git a/src/test/testecs.ts b/src/test/testecs.ts index a8dbc712..aac04e1a 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -1,295 +1,11 @@ -import assert from "assert"; -import { execFileSync, spawnSync } from "child_process"; + +import { spawnSync } from "child_process"; import { readdirSync, readFileSync, writeFileSync } from "fs"; import { describe } from "mocha"; import { Bin, BoxConstraints, Packer } from "../common/ecs/binpack"; -import { ECSActionCompiler, ECSCompiler } from "../common/ecs/compiler"; +import { ECSCompiler } from "../common/ecs/compiler"; import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs"; -const TEMPLATE1 = ` -@NextFrame: - FRAME_START - {{emit preframe}} - KERNEL_START - {{!kernel}} - KERNEL_END - {{!postframe}} - FRAME_END - lsr SWCHB ; test Game Reset switch - bcs @NoStart - jmp Start -@NoStart: - jmp @NextFrame -`; - - -const TEMPLATE4_S1 = ` -.macro @KernelSetup ent,ofs - lda #192 ; TODO: numlines - sec - sbc ypos_ypos_b0+ent - sta {{local 5}}+ofs - - ldy hasbitmap_bitmap_b0+ent - lda bitmap_bitmapdata_b0,y - sec - sbc {{$5}}+ofs - sta {{$0}}+ofs - lda bitmap_bitmapdata_b8,y - sbc #0 - sta {{$1}}+ofs - - ldy hascolormap_colormap_b0+ent - lda colormap_colormapdata_b0,y - sec - sbc {{$5}}+ofs - sta {{$2}}+ofs - lda colormap_colormapdata_b8,y - sbc #0 - sta {{$3}}+ofs - - lda sprite_height_b0+ent - sta {{$4}}+ofs - lda ypos_ypos_b0+ent - sta {{$5}}+ofs -.endmacro -` -const TEMPLATE4_S2 = ` - @KernelSetup 0,0 - @KernelSetup 1,6 -` - -// https://atariage.com/forums/topic/75982-skipdraw-and-graphics/?tab=comments#comment-928232 -// https://atariage.com/forums/topic/129683-advice-on-a-masking-kernel/ -// https://atariage.com/forums/topic/128147-having-trouble-with-2-free-floating-player-graphics/?tab=comments#comment-1547059 -const TEMPLATE4_K = ` - lda {{byte bgcolor}} - sta COLUBK - ldy {{byte lines}} -@LVScan: - lda {{$4}} ; height - dcp {{$5}} - bcs @DoDraw1 - lda #0 - .byte $2C -@DoDraw1: - lda ({{$0}}),y - sta WSYNC - sta GRP0 - lda ({{$2}}),y - sta COLUP0 - - lda {{$10}} ; height - dcp {{$11}} - bcs @DoDraw2 - lda #0 - .byte $2C -@DoDraw2: - lda ({{$6}}),y - sta GRP1 - lda ({{$8}}),y - sta COLUP1 - - dey ; decrement - bne @LVScan ; repeat until 192 lines -`; - -const SET_XPOS = ` - lda {{byte xpos}} - ldy {{byte plyrindex}} - sta HMCLR - jsr {{use SetHorizPos}} -` - -const SETHORIZPOS = ` -; SetHorizPos routine -; A = X coordinate -; Y = player number (0 or 1) -SetHorizPos: - 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 - sta WSYNC - sta HMOVE - rts ; return to caller -` - -const INITFROMSPARSE = ` -MemSrc equ $80 -MemDest equ $82 -InitMemory: - ldy #0 - lda (MemSrc),y - beq .done - tax - iny - lda (MemSrc),y - sta MemDest - iny - lda (MemSrc),y - sta MemDest+1 -.loop - iny - lda (MemSrc),y - sta (MemDest),y - dex - bne .loop -.done rts -` - - -function testECS() { - let em = new EntityManager(new Dialect_CA65()); - - let c_kernel = em.defineComponent({ - name: 'kernel', fields: [ - { name: 'lines', dtype: 'int', lo: 0, hi: 255 }, - { name: 'bgcolor', dtype: 'int', lo: 0, hi: 255 }, - ] - }) - let c_sprite = em.defineComponent({ - name: 'sprite', fields: [ - { name: 'height', dtype: 'int', lo: 0, hi: 255 }, - { name: 'plyrindex', dtype: 'int', lo: 0, hi: 1 }, - ] - }) - let c_plyrflags = em.defineComponent({ - name: 'nusizable', fields: [ - { name: 'plyrflags', dtype: 'int', lo: 0, hi: 63 }, - ] - }) - let c_player = em.defineComponent({ - name: 'player', fields: [ - //TODO: optional? - ] - }) - let c_bitmap = em.defineComponent({ - name: 'bitmap', fields: [ - { name: 'bitmapdata', dtype: 'array', elem: { dtype: 'int', lo: 0, hi: 255 } } - ] - }) - let c_colormap = em.defineComponent({ - name: 'colormap', fields: [ - { name: 'colormapdata', dtype: 'array', elem: { dtype: 'int', lo: 0, hi: 255 } } - ] - }) - let c_hasbitmap = em.defineComponent({ - name: 'hasbitmap', fields: [ - { name: 'bitmap', dtype: 'ref', query: { include: [c_bitmap] } }, - ] - }) - let c_hascolormap = em.defineComponent({ - name: 'hascolormap', fields: [ - { name: 'colormap', dtype: 'ref', query: { include: [c_colormap] } }, - ] - }) - let c_xpos = em.defineComponent({ - name: 'xpos', fields: [ - { name: 'xpos', dtype: 'int', lo: 0, hi: 255 } - ] - }) - let c_ypos = em.defineComponent({ - name: 'ypos', fields: [ - { name: 'ypos', dtype: 'int', lo: 0, hi: 255 } - ] - }) - - // init -> [start] -> frameloop - // frameloop -> [preframe] [kernel] [postframe] - - // temp between preframe + frame? - // TODO: check names for identifierness - em.defineSystem({ - name: 'kernel_simple', - tempbytes: 12, - actions: [ - { - text: TEMPLATE4_S1, event: 'preframe', select: 'with', query: { - include: [c_kernel] - } - }, - { - // TODO: should include kernel for numlines - text: TEMPLATE4_S2, event: 'preframe', select: 'with', query: { - include: [c_sprite, c_hasbitmap, c_hascolormap, c_ypos], - }, - }, - { - text: TEMPLATE4_K, event: 'kernel', select: 'with', query: { - include: [c_kernel] - } - }, - ] - }) - em.defineSystem({ - name: 'set_xpos', - actions: [ - { - text: SET_XPOS, event: 'preframe', select: 'foreach', query: { - include: [c_sprite, c_xpos] - }, - }, - //{ text:SETHORIZPOS }, - ] - }) - // https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_systems.html - em.defineSystem({ - name: 'frameloop', - actions: [ - { text: TEMPLATE1, event: 'start', select: 'with', query: { include: [c_kernel] } } - ] - }) - em.defineSystem({ - name: 'SetHorizPos', - actions: [ - { text: SETHORIZPOS, event: 'SetHorizPos', select: 'with', query: { include: [c_xpos] } }, - ] - }); - - let root = em.newScope("Root"); - let scene = em.newScope("Scene", root); - let e_ekernel = root.newEntity({ components: [c_kernel] }); - root.setConstValue(e_ekernel, c_kernel, 'lines', 192); - //root.setConstValue(e_ekernel, c_kernel, 'bgcolor', 0x92); - root.setInitValue(e_ekernel, c_kernel, 'bgcolor', 0x92); - - let e_bitmap0 = root.newEntity({ components: [c_bitmap] }); - // TODO: store array sizes? - root.setConstValue(e_bitmap0, c_bitmap, 'bitmapdata', new Uint8Array([1, 1, 3, 7, 15, 31, 63, 127])); - - let e_colormap0 = root.newEntity({ components: [c_colormap] }); - root.setConstValue(e_colormap0, c_colormap, 'colormapdata', new Uint8Array([6, 3, 6, 9, 12, 14, 31, 63])); - - let ea_playerSprite = { components: [c_sprite, c_hasbitmap, c_hascolormap, c_xpos, c_ypos, c_player] }; - let e_player0 = root.newEntity(ea_playerSprite); - root.setConstValue(e_player0, c_sprite, 'plyrindex', 0); - root.setInitValue(e_player0, c_sprite, 'height', 8); - root.setInitValue(e_player0, c_xpos, 'xpos', 50); - root.setInitValue(e_player0, c_ypos, 'ypos', 50); - let e_player1 = root.newEntity(ea_playerSprite); - root.setConstValue(e_player1, c_sprite, 'plyrindex', 1); - root.setInitValue(e_player1, c_sprite, 'height', 8); - root.setInitValue(e_player1, c_xpos, 'xpos', 100); - root.setInitValue(e_player1, c_ypos, 'ypos', 60); - - //console.log(em.archetypesMatching({ include:['xpos','ypos']})[0]) - - let src = new SourceFileExport(); - root.dump(src); - //console.log(src.toString()); - //console.log(em.toYAML()); -} - function testCompiler() { let em = new EntityManager(new Dialect_CA65()); // TODO let c = new ECSCompiler(em); @@ -359,9 +75,6 @@ end // TODO: jsr OperModeExecutionTree? describe('Tokenizer', function() { - it('Should use API', function() { - testECS(); - }); it('Should use Compiler', function() { testCompiler(); }); From fbe07c33d21711a4b5246025363e9ad92192c942 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 14 Feb 2022 18:09:14 -0600 Subject: [PATCH 063/104] ecs: playfield decoder --- src/common/ecs/decoder.ts | 22 ++++++++++++++++++++++ src/common/ecs/ecs.ts | 3 +++ 2 files changed, 25 insertions(+) diff --git a/src/common/ecs/decoder.ts b/src/common/ecs/decoder.ts index 7af1feef..fa2cfcd5 100644 --- a/src/common/ecs/decoder.ts +++ b/src/common/ecs/decoder.ts @@ -63,6 +63,27 @@ export class VCSSpriteDecoder extends LineDecoder { } } +export class VCSPlayfieldDecoder extends LineDecoder { + parse() { + let height = this.lines.length; + let pf = new Uint32Array(height); + for (let i=0; i> a.bit) & 0xff; } } From 10baf3abc622ec86293040f457a3bdc8693e624c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 15 Feb 2022 14:01:46 -0600 Subject: [PATCH 064/104] vcs: scanline io viewer logs i/o only --- src/ide/views/debugviews.ts | 2 +- src/platform/vcs.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ide/views/debugviews.ts b/src/ide/views/debugviews.ts index 8fec2c1f..256e82ab 100644 --- a/src/ide/views/debugviews.ts +++ b/src/ide/views/debugviews.ts @@ -704,7 +704,7 @@ export class ScanlineIOView extends ProbeViewBaseBase { var sym = platform.debugSymbols.addr2symbol[addr]; if (sym) line[-1] = sym; break; - case ProbeFlags.MEM_WRITE: + //case ProbeFlags.MEM_WRITE: case ProbeFlags.IO_READ: case ProbeFlags.IO_WRITE: case ProbeFlags.VRAM_READ: diff --git a/src/platform/vcs.ts b/src/platform/vcs.ts index 9be588ab..c38d4b23 100644 --- a/src/platform/vcs.ts +++ b/src/platform/vcs.ts @@ -386,13 +386,17 @@ class VCSPlatform extends BasePlatform { bus.oldRead = bus.read; bus.read = function(a) { var v = this.oldRead(a); - probe.logRead(a,v); + if (a < 0x80) probe.logIORead(a,v); + else if (a > 0x280 && a < 0x300) probe.logIORead(a,v); + else probe.logRead(a,v); return v; } bus.oldWrite = bus.write; bus.write = function(a,v) { this.oldWrite(a,v); - probe.logWrite(a,v); + if (a < 0x80) probe.logIOWrite(a,v); + else if (a > 0x280 && a < 0x300) probe.logIOWrite(a,v); + else probe.logWrite(a,v); } } return rec; From 32689256380ee8895118a57c53cb320769e3725e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 15 Feb 2022 11:25:00 -0600 Subject: [PATCH 065/104] ecs: tests, refactor, event args, fixed c.f --- src/common/ecs/README.md | 146 ++++++++++++++++++++++++++++++++++ src/common/ecs/binpack.ts | 2 +- src/common/ecs/compiler.ts | 16 ++-- src/common/ecs/decoder.ts | 36 ++++++--- src/common/ecs/ecs.ts | 159 ++++++++++++++++++++++++++----------- src/test/testecs.ts | 26 +++--- test/ecs/narrow1.ecs | 28 +++++++ test/ecs/narrow1.txt | 27 +++++++ test/ecs/noconstvalues.ecs | 42 ++++++++++ test/ecs/noconstvalues.txt | 25 ++++++ test/ecs/sprites1.txt | 20 ++--- 11 files changed, 442 insertions(+), 85 deletions(-) create mode 100644 src/common/ecs/README.md create mode 100644 test/ecs/narrow1.ecs create mode 100644 test/ecs/narrow1.txt create mode 100644 test/ecs/noconstvalues.ecs create mode 100644 test/ecs/noconstvalues.txt diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md new file mode 100644 index 00000000..04800ec4 --- /dev/null +++ b/src/common/ecs/README.md @@ -0,0 +1,146 @@ + +# NOTES + +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 + +constants? (NTSC vs PAL) + +set operations: + +E = select entities from scope +A intersect B +A join B +loop over E limit N asc/desc +select Nth from E +run if A intersects B (if) + + +virtual machine +- entityset stack +- register states + + +entity Foo[Bar] { } + +system FrameLoop + +end + +class StaticKernel + locals 12 + func init --- + lda {{$0}} + sta {{$1}} + --- + func display --- + lda {{$0}} + sta {{$1}} + --- +end + +Game { + superman: [Sprite, ...] { + var xpos=12 + } + FrameLoop { + a: StaticKernel(lines=30,bgcolor=$30) + b: Kernel48(...) + c: StaticKernel(...) + + on preframe { + } + on display { + } + on postframe { + } + } +} + + +systems and scope same thing? +nested systems? +systems allocated in blocks +entities allocated in arrays, take up 1 or more blocks +mid-level abstraction for scopes/macros/(banks?) + + + +Init +with FrameLoop do + with StaticKernel do + end + ResetSwitch: + on reset do ResetConsole + end + StaticKernel: + end + JoyButton: + end + end +end + + +scopes are banks! +banks need to duplicate code and/or rodata +- don't split critical code across banks +need bank trampoline macro + +events might need args diff --git a/src/common/ecs/binpack.ts b/src/common/ecs/binpack.ts index 8b92517c..61a576cb 100644 --- a/src/common/ecs/binpack.ts +++ b/src/common/ecs/binpack.ts @@ -1,5 +1,5 @@ -var debug = true; +var debug = false; export interface BoxConstraints { left?: number; diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index ae522ba1..d3ddbd98 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -129,7 +129,7 @@ export class ECSCompiler extends Tokenizer { let name = this.expectIdent(); this.expectToken(':', 'I expected either a ":" or "end" here.'); // TODO let type = this.parseDataType(); - return { name: name.str, ...type }; + return { name: name.str, $loc: name.$loc, ...type }; } parseDataType(): DataType { @@ -369,7 +369,7 @@ export class ECSCompiler extends Tokenizer { // TODO: remove init? while ((cmd2 = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') { let cmd = cmd2; // put in scope - if (cmd == 'var') cmd = 'init'; + if (cmd == 'var') cmd = 'init'; // TODO: remove? if (cmd == 'init' || cmd == 'const') { // TODO: check data types let name = this.expectIdent().str; @@ -391,14 +391,20 @@ export class ECSCompiler extends Tokenizer { } } else if (cmd == 'decode') { let decoderid = this.expectIdent().str; - let code = this.expectTokenTypes([ECSTokenType.CodeFragment]).str; + let codetok = this.expectTokenTypes([ECSTokenType.CodeFragment]); + let code = codetok.str; code = code.substring(3, code.length-3); let decoder = newDecoder(decoderid, code); if (!decoder) { this.compileError(`I can't find a "${decoderid}" decoder.`); throw new Error() } - let result = decoder.parse(); + let result; + try { + result = decoder.parse(); + } catch (e) { + throw new ECSError(e.message, decoder.getErrorLocation(codetok.$loc)); + } for (let entry of Object.entries(result.properties)) { let { c, f } = this.getEntityField(entity, entry[0]); - scope.setConstValue(entity, c, f.name, entry[1]); + scope.setConstValue(entity, c, f.name, entry[1] as DataValue); } } } diff --git a/src/common/ecs/decoder.ts b/src/common/ecs/decoder.ts index fa2cfcd5..628a193f 100644 --- a/src/common/ecs/decoder.ts +++ b/src/common/ecs/decoder.ts @@ -1,4 +1,5 @@ -import { threadId } from "worker_threads"; + +import { SourceLocation } from "../workertypes"; import { DataValue, ECSError } from "./ecs"; export interface DecoderResult { @@ -6,7 +7,8 @@ export interface DecoderResult { } abstract class LineDecoder { - lines : string[][]; + curline: number = 0; // for debugging, zero-indexed + lines : string[][]; // array of token arrays constructor( text: string @@ -41,6 +43,12 @@ abstract class LineDecoder { return v; } + getErrorLocation($loc: SourceLocation): SourceLocation { + // TODO: blank lines mess this up + $loc.line += this.curline + 1; + return $loc; + } + abstract parse() : DecoderResult; } @@ -50,7 +58,8 @@ export class VCSSpriteDecoder extends LineDecoder { let bitmapdata = new Uint8Array(height); let colormapdata = new Uint8Array(height); for (let i=0; i range.ehi) throw new ECSError(`entity ID ${entityID} too high for ${access.symbol}`); let ofs = this.symbols[access.symbol]; if (ofs !== undefined) { return ofs + entityID - range.elo; @@ -425,21 +428,6 @@ class DataSegment { // TODO: show entity name? throw new ECSError(`cannot find field access for ${access.symbol}`); } - getSegmentByteOffset(component: ComponentType, fieldName: string, entityID: number, bitofs: number, width: number) { - let range = this.getFieldRange(component, fieldName); - if (range && range.access) { - for (let a of range.access) { - if (a.bit == bitofs && a.width == width) { - let ofs = this.symbols[a.symbol]; - if (ofs !== undefined) { - return ofs + entityID - range.elo; - } - } - } - } - // TODO: show entity name? - throw new ECSError(`cannot find field offset for ${component.name}:${fieldName} entity #${entityID} bits ${bitofs} ${width}`) - } getOriginSymbol() { let a = this.ofs2sym.get(0); if (!a) throw new ECSError('getOriginSymbol(): no symbol at offset 0'); // TODO @@ -516,13 +504,59 @@ class EntitySet { } } +class IndexRegister { + lo: number | null; + hi: number | null; + offset = 0; + elo: number; + ehi: number; + eset: EntitySet | undefined; + + constructor( + public readonly scope: EntityScope, + eset?: EntitySet + ) { + this.elo = 0; + this.ehi = scope.entities.length - 1; + this.lo = null; + this.hi = null; + if (eset) { this.narrowInPlace(eset); } + } + entityCount() { + return this.ehi - this.elo + 1; + } + narrow(eset: EntitySet): IndexRegister { + let i = new IndexRegister(this.scope); + i.narrowInPlace(eset); + return i; + } + narrowInPlace(eset: EntitySet) { + if (this.scope != eset.scope) throw new ECSError(`scope mismatch`); + if (!eset.isContiguous()) throw new ECSError(`entities are not contiguous`); + let newelo = eset.entities[0].id; + let newehi = eset.entities[eset.entities.length - 1].id; + if (this.elo > newelo) throw new ECSError(`cannot narrow to new range, elo`); + if (this.ehi < newehi) throw new ECSError(`cannot narrow to new range, ehi`); + if (this.lo == null || this.hi == null) { + this.lo = 0; + this.hi = newehi - newelo + 1; + } else { + this.offset += newelo - this.elo; + } + this.elo = newelo; + this.ehi = newehi; + this.eset = eset; + } +} + // todo: generalize class ActionCPUState { - loops: EntitySet[] = []; x: EntitySet | null = null; y: EntitySet | null = null; xofs: number = 0; yofs: number = 0; + xreg: IndexRegister | null = null; + yreg: IndexRegister | null = null; } class ActionEval { @@ -537,7 +571,8 @@ class ActionEval { constructor( readonly scope: EntityScope, readonly instance: SystemInstance, - readonly action: Action) + readonly action: Action, + readonly eventargs: string[]) { this.em = scope.em; this.dialect = scope.em.dialect; @@ -570,14 +605,12 @@ class ActionEval { if (state.x && state.y) throw new ECSError('no more index registers', this.action); if (state.x) state.y = this.qr; else state.x = this.qr; - state.loops = state.loops.concat([this.qr]); break; case 'join': 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); state.y = this.qr; state.x = this.jr; - state.loops = state.loops.concat([this.qr]); break; case 'if': case 'with': @@ -639,8 +672,6 @@ class ActionEval { // select subset of entities let fullEntityCount = this.qr.entities.length; //entities.length.toString(); 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? @@ -652,7 +683,7 @@ class ActionEval { let rf = this.instance.params.refField; code = this.wrapCodeInRefLookup(code); // TODO: only fetches 1st entity in list, need offset - let range = this.scope.bss.getFieldRange(rf.c, rf.f.name) || this.scope.rodata.getFieldRange(rf.c, rf.f.name); + let range = this.scope.getFieldRange(rf.c, rf.f.name); let eidofs = re.id - range.elo; props['%reffield'] = `${this.dialect.fieldsymbol(rf.c, rf.f, 0)}+${eidofs}`; } else { @@ -680,13 +711,15 @@ class ActionEval { let toks = group.split(/\s+/); if (toks.length == 0) throw new ECSError(`empty command`, action); let cmd = group.charAt(0); - let rest = group.substring(1).trim(); + let arg0 = toks[0].substring(1).trim(); + let args = [arg0].concat(toks.slice(1)); switch (cmd) { - case '!': return this.__emit([rest]); - case '$': return this.__local([rest]); - case '^': return this.__use([rest]); - case '<': return this.__get([rest, '0']); - case '>': return this.__get([rest, '8']); + case '!': return this.__emit(args); + case '$': return this.__local(args); + case '^': return this.__use(args); + case '#': return this.__arg(args); + case '<': return this.__get([arg0, '0']); + case '>': return this.__get([arg0, '8']); default: let value = props[toks[0]]; if (value) return value; @@ -740,7 +773,8 @@ class ActionEval { } __emit(args: string[]) { let event = args[0]; - return this.scope.generateCodeForEvent(event); + let eventargs = args.slice(1); + return this.scope.generateCodeForEvent(event, eventargs); } __local(args: string[]) { let tempinc = parseInt(args[0]); @@ -751,6 +785,10 @@ class ActionEval { this.scope.updateTempLiveness(this.instance); return `${this.tmplabel}+${tempinc}`; } + __arg(args: string[]) { + let index = parseInt(args[0] || '0'); + return this.eventargs[index] || ''; + } __bss_init(args: string[]) { return this.scope.allocateInitData(this.scope.bss); } @@ -791,15 +829,17 @@ class ActionEval { var component: ComponentType; var baseLookup = false; + var entityLookup = false; let entities: Entity[]; // is qualified field? if (fieldName.indexOf('.') > 0) { let [entname, fname] = fieldName.split('.'); let ent = this.scope.getEntityByName(entname); if (ent == null) throw new ECSError(`no entity named "${entname}" in this scope`, action); - component = this.em.singleComponentWithFieldName(this.qr.atypes, fname, action); + component = this.em.singleComponentWithFieldName([ent.etype], fname, action); fieldName = fname; entities = [ent]; + entityLookup = true; } else if (fieldName.indexOf(':') > 0) { let [cname, fname] = fieldName.split(':'); component = this.em.getComponentByName(cname); @@ -838,10 +878,13 @@ class ActionEval { } // TODO: offset > 0? // TODO: don't mix const and init data - let range = this.scope.bss.getFieldRange(component, fieldName) || this.scope.rodata.getFieldRange(component, fieldName); + let range = this.scope.getFieldRange(component, field.name); if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO // TODO: dialect + // TODO: doesnt work for entity.field let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? + if (entityLookup) + eidofs = entities[0].id - range.elo; // TODO: array field baseoffset? if (baseLookup) { return this.dialect.absolute(ident); @@ -982,8 +1025,11 @@ export class EntityScope implements SourceLocated { for (var o = iter.next(); o.value; o = iter.next()) { let { i, e, c, f, v } = o.value; // constants and array pointers go into rodata - let segment = v === undefined && f.dtype != 'array' ? this.bss : this.rodata; let cfname = mksymbol(c, f.name); + let ftype = this.fieldtypes[cfname]; + let segment = ftype == 'const' ? this.rodata : this.bss; + if (v === undefined && ftype == 'const') + throw new ECSError(`no value for const field ${cfname}`, e); // determine range of indices for entities let array = segment.fieldranges[cfname]; if (!array) { @@ -991,7 +1037,7 @@ export class EntityScope implements SourceLocated { } else { array.ehi = i; } - //console.log(i,array,cfname); + //console.log(i,e.name,array,cfname); } } // TODO: cull unused entity fields @@ -1032,18 +1078,20 @@ export class EntityScope implements SourceLocated { let entcount = range ? range.ehi - range.elo + 1 : 0; if (v == null && f.dtype == 'int') v = 0; if (v == null && f.dtype == 'ref') v = 0; - if (v == null && f.dtype == 'array') throw new ECSError(`no default value for array ${cfname}`) + if (v == null && f.dtype == 'array') + throw new ECSError(`no default value for array ${cfname}`, e); //console.log(c.name, f.name, '#'+e.id, '=', v); // this is a constant // is it a byte array? //TODO? if (ArrayBuffer.isView(v) && f.dtype == 'array') { if (v instanceof Uint8Array && f.dtype == 'array') { - let datasym = this.dialect.datasymbol(c, f, e.id, 0); - segment.allocateInitData(datasym, v); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); + let datasym = this.dialect.datasymbol(c, f, e.id, 0); + // TODO: share shared data + segment.allocateInitData(datasym, v); if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; @@ -1056,8 +1104,8 @@ export class EntityScope implements SourceLocated { segment.allocateBytes(a.symbol, entcount); let ofs = segment.getByteOffset(range, a, e.id); // TODO: this happens if you forget a const field on an object? - if (e.id < range.elo) throw new ECSError(c.name + ' ' + f.name); - if (typeof segment.initdata[ofs] !== 'undefined') throw new ECSError(ofs+""); + if (e.id < range.elo) throw new ECSError('entity out of range ' + c.name + ' ' + f.name, e); + if (segment.initdata[ofs] !== undefined) throw new ECSError('initdata already set ' + ofs), e; segment.initdata[ofs] = (v >> a.bit) & 0xff; } } @@ -1122,21 +1170,31 @@ export class EntityScope implements SourceLocated { code = code.replace('{{%dest}}', segment.getOriginSymbol()); return code; } + getFieldRange(c: ComponentType, fn: string) { + return this.bss.getFieldRange(c, fn) || this.rodata.getFieldRange(c, fn); + } // TODO: check type/range of value setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([e.etype], fieldName, e); - e.consts[mksymbol(component, fieldName)] = value; - this.fieldtypes[mksymbol(component, fieldName)] = 'const'; + this.setConstInitValue(e, component, fieldName, value, 'const'); } setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) { - let c = this.em.singleComponentWithFieldName([e.etype], fieldName, e); - e.inits[mkscopesymbol(this, component, fieldName)] = value; - this.fieldtypes[mksymbol(component, fieldName)] = 'init'; + this.setConstInitValue(e, component, fieldName, value, 'init'); + } + setConstInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue, + type: 'const'|'init') { + this.em.singleComponentWithFieldName([e.etype], fieldName, e); + let cfname = mksymbol(component, fieldName); + let ecfname = mkscopesymbol(this, component, fieldName); + if (e.consts[cfname] !== undefined) throw new ECSError(`"${fieldName}" is already defined as a constant`, e); + if (e.inits[ecfname] !== undefined) throw new ECSError(`"${fieldName}" is already defined as a`, e); + if (type == 'const') e.consts[cfname] = value; + if (type == 'init') e.inits[ecfname] = value; + this.fieldtypes[cfname] = type; } isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' { return this.fieldtypes[mksymbol(component, fieldName)]; } - generateCodeForEvent(event: string): string { + generateCodeForEvent(event: string, args?: string[]): string { // find systems that respond to event // and have entities in this scope let systems = this.em.event2systems[event]; @@ -1150,14 +1208,16 @@ export class EntityScope implements SourceLocated { // generate code let s = this.dialect.code(); //s += `\n; event ${event}\n`; + let eventCount = 0; let instances = this.instances.filter(inst => systems.includes(inst.system)); for (let inst of instances) { let sys = inst.system; for (let action of sys.actions) { if (action.event == event) { + eventCount++; // TODO: use Tokenizer so error msgs are better // TODO: keep event tree - let codeeval = new ActionEval(this, inst, action); + let codeeval = new ActionEval(this, inst, action, args || []); codeeval.begin(); s += this.dialect.comment(`start action ${sys.name} ${inst.id} ${event}`); // TODO s += codeeval.codeToString(); @@ -1167,6 +1227,9 @@ export class EntityScope implements SourceLocated { } } } + if (eventCount == 0) { + console.log(`warning: event ${event} not handled`); + } return s; } getSystemStats(inst: SystemInstance) : SystemStats { @@ -1210,7 +1273,7 @@ export class EntityScope implements SourceLocated { } } if (!pack.pack()) console.log('cannot pack temporary local vars'); // TODO - console.log('tempvars', pack); + //console.log('tempvars', pack); if (bssbin.extents.right > 0) { this.bss.allocateBytes('TEMP', bssbin.extents.right); for (let b of pack.boxes) { diff --git a/src/test/testecs.ts b/src/test/testecs.ts index aac04e1a..4b07d0cf 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -1,6 +1,6 @@ import { spawnSync } from "child_process"; -import { readdirSync, readFileSync, writeFileSync } from "fs"; +import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs"; import { describe } from "mocha"; import { Bin, BoxConstraints, Packer } from "../common/ecs/binpack"; import { ECSCompiler } from "../common/ecs/compiler"; @@ -19,11 +19,9 @@ component Kernel lines: 0..255 bgcolor: 0..255 end - component Bitmap data: array of 0..255 end - component HasBitmap bitmap: [Bitmap] end @@ -42,16 +40,19 @@ comment --- --- scope Root - entity kernel [Kernel] const lines = 0xc0 - const lines = $c0 + //const lines = $c0 + end + entity nobmp [Bitmap] + const data = [4] + end + entity bmp [Bitmap] + const data = [1,2,3] end - entity player1 [HasBitmap] - init bitmap = 1 + init bitmap = #bmp end - end `, 'foo.txt'); @@ -59,7 +60,7 @@ end let src = new SourceFileExport(); c.exportToFile(src); // TODO: test? - //console.log(src.toString()); + console.log(src.toString()); return em; } catch (e) { console.log(e); @@ -97,10 +98,11 @@ describe('Compiler', function() { } let code = readFileSync(ecspath, 'utf-8'); compiler.parseFile(code, ecspath); + // TODO: errors let out = new SourceFileExport(); em.exportToFile(out); let outtxt = out.toString(); - let goodtxt = readFileSync(goodpath, 'utf-8'); + let goodtxt = existsSync(goodpath) ? readFileSync(goodpath, 'utf-8') : ''; if (outtxt.trim() != goodtxt.trim()) { let asmpath = '/tmp/' + asmfn; writeFileSync(asmpath, outtxt, 'utf-8'); @@ -115,8 +117,8 @@ function testPack(bins: Bin[], boxes: BoxConstraints[]) { for (let bin of bins) packer.bins.push(bin); for (let bc of boxes) packer.boxes.push(bc); if (!packer.pack()) throw new Error('cannot pack') - console.log(packer.boxes); - console.log(packer.bins[0].free) + //console.log(packer.boxes); + //console.log(packer.bins[0].free) } describe('Box Packer', function() { diff --git a/test/ecs/narrow1.ecs b/test/ecs/narrow1.ecs new file mode 100644 index 00000000..164c9bde --- /dev/null +++ b/test/ecs/narrow1.ecs @@ -0,0 +1,28 @@ + +component Xpos + x: 0..255 +end +component Player +end +component Enemy +end + +scope Main + entity foo [Xpos] + end + entity p [Xpos,Player] + init x = 50 + end + entity e1 [Xpos,Enemy] + init x = 100 + end + entity e2 [Xpos,Enemy] + init x = 150 + end + system move + on start do foreach [Enemy] + --- + lda {{(Bitmap_bitmapdata_e1_b0+31) +.byte >(Bitmap_bitmapdata_e2_b0+31) Bitmap_bitmapdata_e1_b0: .byte 1 .byte 1 @@ -57,12 +63,6 @@ Bitmap_bitmapdata_e1_b0: .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 @@ -75,6 +75,10 @@ Bitmap_bitmapdata_e2_b0: .byte 255 .byte 62 .byte 24 +Colormap_colormapdata_b0: +.byte <(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_b8: +.byte >(Colormap_colormapdata_e3_b0+31) Colormap_colormapdata_e3_b0: .byte 6 .byte 3 @@ -84,10 +88,6 @@ Colormap_colormapdata_e3_b0: .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 From a425012b2b5826e4806bcd668be00926f87baa5a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 16 Feb 2022 11:54:44 -0600 Subject: [PATCH 066/104] ecs: started w/ new IndexRegister --- src/common/ecs/ecs.ts | 110 ++++++++++++++++++++++++------------------ src/test/testecs.ts | 2 +- test/ecs/vcs1.txt | 60 ++++++----------------- test/ecs/vcslib.txt | 60 ++++++----------------- 4 files changed, 93 insertions(+), 139 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 0edc5e5f..eaae6441 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -525,36 +525,43 @@ class IndexRegister { entityCount() { return this.ehi - this.elo + 1; } - narrow(eset: EntitySet): IndexRegister { - let i = new IndexRegister(this.scope); - i.narrowInPlace(eset); - return i; + clone() { + return Object.assign(new IndexRegister(this.scope), this); } - narrowInPlace(eset: EntitySet) { - if (this.scope != eset.scope) throw new ECSError(`scope mismatch`); - if (!eset.isContiguous()) throw new ECSError(`entities are not contiguous`); - let newelo = eset.entities[0].id; - let newehi = eset.entities[eset.entities.length - 1].id; - if (this.elo > newelo) throw new ECSError(`cannot narrow to new range, elo`); - if (this.ehi < newehi) throw new ECSError(`cannot narrow to new range, ehi`); - if (this.lo == null || this.hi == null) { - this.lo = 0; - this.hi = newehi - newelo + 1; + narrow(eset: EntitySet, action?: SourceLocated) { + let i = this.clone(); + return i.narrowInPlace(eset, action) ? i : null; + } + narrowInPlace(eset: EntitySet, action?: SourceLocated): boolean { + if (this.scope != eset.scope) throw new ECSError(`scope mismatch`, action); + if (!eset.isContiguous()) throw new ECSError(`entities are not contiguous`, action); + if (this.eset) { + this.eset = this.eset.intersection(eset); } else { + this.eset = eset; + } + if (this.eset.entities.length == 0) { + return false; + } + let newelo = this.eset.entities[0].id; + let newehi = this.eset.entities[this.eset.entities.length - 1].id; + if (this.lo === null || this.hi === null) { + this.lo = 0; + this.hi = newehi - newelo; + this.offset = 0; + } else { + if (action) console.log((action as any).event, this.offset, newelo, this.elo); this.offset += newelo - this.elo; } this.elo = newelo; this.ehi = newehi; - this.eset = eset; + if (action) console.log((action as any).event, this.offset, this.elo, this.ehi, newelo, newehi); + return true; } } // todo: generalize class ActionCPUState { - x: EntitySet | null = null; - y: EntitySet | null = null; - xofs: number = 0; - yofs: number = 0; xreg: IndexRegister | null = null; yreg: IndexRegister | null = null; } @@ -602,35 +609,37 @@ class ActionEval { // TODO: generalize to other cpus/langs switch (this.action.select) { case 'foreach': - if (state.x && state.y) throw new ECSError('no more index registers', this.action); - if (state.x) state.y = this.qr; - else state.x = this.qr; + if (state.xreg && state.yreg) throw new ECSError('no more index registers', this.action); + if (state.xreg) state.yreg = new IndexRegister(this.scope, this.qr); + else state.xreg = new IndexRegister(this.scope, this.qr); break; case 'join': - if (state.x || state.y) throw new ECSError('no free index registers for join', this.action); + if (state.xreg || state.yreg) throw new ECSError('no free index registers for join', this.action); this.jr = new EntitySet(this.scope, (this.action as ActionWithJoin).join); - state.y = this.qr; - state.x = this.jr; + state.xreg = new IndexRegister(this.scope, this.jr); + state.yreg = new IndexRegister(this.scope, this.qr); break; case 'if': case 'with': // TODO: what if not in X because 1 element? - if (state.x) { - let int = state.x.intersection(this.qr); - if (int.entities.length == 0) { - if (this.action.select == 'with') - throw new ECSError('no entities match this query', this.action); - else - break; + if (state.xreg && state.xreg.eset) { + state.xreg = state.xreg.narrow(this.qr, this.action); + if (state.xreg == null || state.xreg.eset?.entities == null) { + if (this.action.select == 'if') { + this.entities = []; + break; // "if" failed + } else { + throw new ECSError(`no entities in statement`, this.action); + } } else { - let indofs = int.entities[0].id - state.x.entities[0].id; - state.xofs += indofs; // TODO: should merge with filter - state.x = int; - this.entities = int.entities; // TODO? + this.entities = state.xreg.eset.entities; // TODO? } } else if (this.action.select == 'with') { if (this.instance.params.refEntity && this.instance.params.refField) { - state.x = this.qr; + if (state.xreg) + state.xreg.eset = this.qr; + else + state.xreg = new IndexRegister(this.scope, this.qr); // ??? } else if (this.qr.entities.length != 1) throw new ECSError(`${this.instance.system.name} query outside of loop must match exactly one entity`, this.action); //TODO @@ -701,8 +710,9 @@ class ActionEval { props['%ehi'] = entities[entities.length - 1].id.toString(); props['%ecount'] = entities.length.toString(); props['%efullcount'] = fullEntityCount.toString(); - props['%xofs'] = this.scope.state.xofs.toString(); - props['%yofs'] = this.scope.state.yofs.toString(); + // TODO + props['%xofs'] = (this.scope.state.xreg?.offset || 0).toString(); + props['%yofs'] = (this.scope.state.yreg?.offset || 0).toString(); } // replace @labels code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); @@ -806,7 +816,7 @@ class ActionEval { wrapCodeInFilter(code: string) { // TODO: :-p const ents = this.entities; - const ents2 = this.oldState.x?.entities; + const ents2 = this.oldState.xreg?.eset?.entities; if (ents && ents2) { let lo = ents[0].id; let hi = ents[ents.length - 1].id; @@ -892,20 +902,24 @@ class ActionEval { return this.dialect.absolute(ident, eidofs); } else { let ir; - if (this.scope.state.x?.intersection(qr)) { - ir = this.scope.state.x; - eidofs -= this.scope.state.xofs; + let int; + let xreg = this.scope.state.xreg; + let yreg = this.scope.state.yreg; + if (xreg && (int = xreg.eset?.intersection(qr))) { + //console.log('x',qr.entities[0].id,xreg.elo,int.entities[0].id,xreg.offset); + ir = xreg.eset; + eidofs -= xreg.offset; } - else if (this.scope.state.y?.intersection(qr)) { - ir = this.scope.state.y; - eidofs -= this.scope.state.yofs; + else if (yreg && (int = yreg.eset?.intersection(qr))) { + ir = yreg.eset; + eidofs -= yreg.offset; } if (!ir) throw new ECSError(`no intersection for index register`, action); if (ir.entities.length == 0) throw new ECSError(`no common entities for index register`, action); if (!ir.isContiguous()) throw new ECSError(`entities in query are not contiguous`, action); - if (ir == this.scope.state.x) + if (ir == this.scope.state.xreg?.eset) return this.dialect.indexed_x(ident, eidofs); - if (ir == this.scope.state.y) + if (ir == this.scope.state.yreg?.eset) return this.dialect.indexed_y(ident, eidofs); throw new ECSError(`cannot find "${component.name}:${field.name}" in state`, action); } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 4b07d0cf..eb4af3ed 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -106,7 +106,7 @@ describe('Compiler', function() { if (outtxt.trim() != goodtxt.trim()) { let asmpath = '/tmp/' + asmfn; writeFileSync(asmpath, outtxt, 'utf-8'); - console.log(spawnSync('/usr/bin/diff', [asmpath, goodpath], {encoding:'utf-8'}).stdout); + console.log(spawnSync('/usr/bin/diff', [goodpath, asmpath], {encoding:'utf-8'}).stdout); throw new Error(`files different; to fix: cp ${asmpath} ${goodpath}`); } }); diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index c13f7bb2..2f038c3f 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -82,40 +82,10 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel 4 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 4 kernelsetup ;;; start action StaticKernel 4 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 4 kernelsetup @@ -127,7 +97,7 @@ StaticKernel__kernelsetup__6____skipxhi: ;;; start action StaticKernel 4 kernel ldx #0 -StaticKernel__kernel__7____each: +StaticKernel__kernel__5____each: sta WSYNC .code @@ -142,24 +112,24 @@ StaticKernel__kernel__7____each: ;;; start action StaticKernel 4 kernelsetup cpx #5+2 - jcs StaticKernel__kernelsetup__9____skipxhi + jcs StaticKernel__kernelsetup__7____skipxhi cpx #5 - jcc StaticKernel__kernelsetup__9____skipxlo + jcc StaticKernel__kernelsetup__7____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF -StaticKernel__kernelsetup__9____skipxlo: +StaticKernel__kernelsetup__7____skipxlo: -StaticKernel__kernelsetup__9____skipxhi: +StaticKernel__kernelsetup__7____skipxhi: ;;; end action StaticKernel 4 kernelsetup ;;; start action StaticKernel 4 kernelsetup cpx #4 - jcc StaticKernel__kernelsetup__10____skipxlo + jcc StaticKernel__kernelsetup__8____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -168,22 +138,22 @@ StaticKernel__kernelsetup__9____skipxhi: lda Playfield_pf_b16-4,x sta PF2 -StaticKernel__kernelsetup__10____skipxlo: +StaticKernel__kernelsetup__8____skipxlo: ;;; end action StaticKernel 4 kernelsetup ldy KernelSection_lines_b0,x -StaticKernel__kernel__7__loop: +StaticKernel__kernel__5__loop: sta WSYNC dey - bne StaticKernel__kernel__7__loop + bne StaticKernel__kernel__5__loop inx cpx #8 - jne StaticKernel__kernel__7____each -StaticKernel__kernel__7____exit: + jne StaticKernel__kernel__5____each +StaticKernel__kernel__5____exit: ;;; end action StaticKernel 4 kernel @@ -193,7 +163,7 @@ StaticKernel__kernel__7____exit: ;;; start action JoyButton 5 postframe lda INPT4 ;read button input - bmi JoyButton__postframe__11__NotPressed + bmi JoyButton__postframe__9__NotPressed .code ;;; start action Local 6 joybutton @@ -203,7 +173,7 @@ StaticKernel__kernel__7____exit: ;;; end action Local 6 joybutton -JoyButton__postframe__11__NotPressed: +JoyButton__postframe__9__NotPressed: ;;; end action JoyButton 5 postframe @@ -213,7 +183,7 @@ JoyButton__postframe__11__NotPressed: ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch - bcs ResetSwitch__nextframe__13__NoStart + bcs ResetSwitch__nextframe__11__NoStart .code ;;; start action ResetConsole 3 resetswitch @@ -222,7 +192,7 @@ JoyButton__postframe__11__NotPressed: ;;; end action ResetConsole 3 resetswitch -ResetSwitch__nextframe__13__NoStart: +ResetSwitch__nextframe__11__NoStart: ;;; end action ResetSwitch 2 nextframe diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 38fa892b..c317ee78 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -83,40 +83,10 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel 4 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 4 kernelsetup ;;; start action StaticKernel 4 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 4 kernelsetup @@ -128,7 +98,7 @@ StaticKernel__kernelsetup__6____skipxhi: ;;; start action StaticKernel 4 kernel ldx #0 -StaticKernel__kernel__7____each: +StaticKernel__kernel__5____each: sta WSYNC .code @@ -143,24 +113,24 @@ StaticKernel__kernel__7____each: ;;; start action StaticKernel 4 kernelsetup cpx #5+2 - jcs StaticKernel__kernelsetup__9____skipxhi + jcs StaticKernel__kernelsetup__7____skipxhi cpx #5 - jcc StaticKernel__kernelsetup__9____skipxlo + jcc StaticKernel__kernelsetup__7____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF -StaticKernel__kernelsetup__9____skipxlo: +StaticKernel__kernelsetup__7____skipxlo: -StaticKernel__kernelsetup__9____skipxhi: +StaticKernel__kernelsetup__7____skipxhi: ;;; end action StaticKernel 4 kernelsetup ;;; start action StaticKernel 4 kernelsetup cpx #4 - jcc StaticKernel__kernelsetup__10____skipxlo + jcc StaticKernel__kernelsetup__8____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -169,7 +139,7 @@ StaticKernel__kernelsetup__9____skipxhi: lda Playfield_pf_b16-4,x sta PF2 -StaticKernel__kernelsetup__10____skipxlo: +StaticKernel__kernelsetup__8____skipxlo: ;;; end action StaticKernel 4 kernelsetup @@ -178,11 +148,11 @@ StaticKernel__kernelsetup__10____skipxlo: ;;; start action StaticKernel 4 kerneldraw ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__11__loop: +StaticKernel__kerneldraw__9__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__11__loop + bne StaticKernel__kerneldraw__9__loop ;;; end action StaticKernel 4 kerneldraw @@ -190,8 +160,8 @@ StaticKernel__kerneldraw__11__loop: inx cpx #8 - jne StaticKernel__kernel__7____each -StaticKernel__kernel__7____exit: + jne StaticKernel__kernel__5____each +StaticKernel__kernel__5____exit: ;;; end action StaticKernel 4 kernel @@ -201,7 +171,7 @@ StaticKernel__kernel__7____exit: ;;; start action JoyButton 5 postframe lda INPT4 ;read button input - bmi JoyButton__postframe__12__NotPressed + bmi JoyButton__postframe__10__NotPressed .code ;;; start action Local 6 joybutton @@ -211,7 +181,7 @@ StaticKernel__kernel__7____exit: ;;; end action Local 6 joybutton -JoyButton__postframe__12__NotPressed: +JoyButton__postframe__10__NotPressed: ;;; end action JoyButton 5 postframe @@ -221,7 +191,7 @@ JoyButton__postframe__12__NotPressed: ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch - bcs ResetSwitch__nextframe__14__NoStart + bcs ResetSwitch__nextframe__12__NoStart .code ;;; start action ResetConsole 3 resetswitch @@ -230,7 +200,7 @@ JoyButton__postframe__12__NotPressed: ;;; end action ResetConsole 3 resetswitch -ResetSwitch__nextframe__14__NoStart: +ResetSwitch__nextframe__12__NoStart: ;;; end action ResetSwitch 2 nextframe From 0451e8ea33e8d6ff6e22060b64311ca92a01838d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 16 Feb 2022 14:50:48 -0600 Subject: [PATCH 067/104] ecs: fixed indexregister offset, for now --- src/common/ecs/ecs.ts | 53 +- src/test/testecs.ts | 50 +- test/ecs/score.ecs | 299 ++++++++++ test/ecs/score.txt | 423 +++++++++++++++ test/ecs/sprites.ecs | 440 +++++++++++++++ test/ecs/sprites.txt | 565 +++++++++++++++++++ test/ecs/superman.ecs | 524 ++++++++++++++++++ test/ecs/superman.txt | 1199 +++++++++++++++++++++++++++++++++++++++++ test/ecs/titles.ecs | 131 +++++ test/ecs/titles.txt | 369 +++++++++++++ 10 files changed, 4008 insertions(+), 45 deletions(-) create mode 100644 test/ecs/score.ecs create mode 100644 test/ecs/score.txt create mode 100644 test/ecs/sprites.ecs create mode 100644 test/ecs/sprites.txt create mode 100644 test/ecs/superman.ecs create mode 100644 test/ecs/superman.txt create mode 100644 test/ecs/titles.ecs create mode 100644 test/ecs/titles.txt diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index eaae6441..2b50c45a 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -507,7 +507,6 @@ class EntitySet { class IndexRegister { lo: number | null; hi: number | null; - offset = 0; elo: number; ehi: number; eset: EntitySet | undefined; @@ -548,16 +547,19 @@ class IndexRegister { if (this.lo === null || this.hi === null) { this.lo = 0; this.hi = newehi - newelo; - this.offset = 0; + this.elo = newelo; + this.ehi = newehi; } else { - if (action) console.log((action as any).event, this.offset, newelo, this.elo); - this.offset += newelo - this.elo; + //if (action) console.log((action as any).event, this.elo, '-', this.ehi, '->', newelo, '..', newehi); + this.lo += newelo - this.elo; + this.hi += newehi - this.ehi; } - this.elo = newelo; - this.ehi = newehi; - if (action) console.log((action as any).event, this.offset, this.elo, this.ehi, newelo, newehi); return true; } + // TODO: removegi + offset() { + return this.lo || 0; + } } // todo: generalize @@ -711,8 +713,8 @@ class ActionEval { props['%ecount'] = entities.length.toString(); props['%efullcount'] = fullEntityCount.toString(); // TODO - props['%xofs'] = (this.scope.state.xreg?.offset || 0).toString(); - props['%yofs'] = (this.scope.state.yreg?.offset || 0).toString(); + props['%xofs'] = (this.scope.state.xreg?.offset() || 0).toString(); + props['%yofs'] = (this.scope.state.yreg?.offset() || 0).toString(); } // replace @labels code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); @@ -772,10 +774,11 @@ class ActionEval { __index(args: string[]) { // TODO: check select type and if we actually have an index... let ident = args[0]; + let index = parseInt(args[1] || '0'); if (this.entities.length == 1) { return this.dialect.absolute(ident); } else { - return this.dialect.indexed_x(ident, 0); //TODO? + return this.dialect.indexed_x(ident, index); //TODO? } } __use(args: string[]) { @@ -814,7 +817,7 @@ class ActionEval { return s; } wrapCodeInFilter(code: string) { - // TODO: :-p + // TODO: :-p filters too often? const ents = this.entities; const ents2 = this.oldState.xreg?.eset?.entities; if (ents && ents2) { @@ -892,29 +895,37 @@ class ActionEval { if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO // TODO: dialect // TODO: doesnt work for entity.field - let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? - if (entityLookup) - eidofs = entities[0].id - range.elo; // TODO: array field baseoffset? if (baseLookup) { return this.dialect.absolute(ident); } else if (entities.length == 1) { + let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? + if (entityLookup) + eidofs = entities[0].id - range.elo; return this.dialect.absolute(ident, eidofs); } else { let ir; let int; + let eidofs; let xreg = this.scope.state.xreg; let yreg = this.scope.state.yreg; if (xreg && (int = xreg.eset?.intersection(qr))) { - //console.log('x',qr.entities[0].id,xreg.elo,int.entities[0].id,xreg.offset); + //console.log(eidofs,'x',qr.entities[0].id,xreg.elo,int.entities[0].id,xreg.offset(),range.elo); ir = xreg.eset; - eidofs -= xreg.offset; - } - else if (yreg && (int = yreg.eset?.intersection(qr))) { + //eidofs -= xreg.offset(); + //eidofs -= int.entities[0].id - xreg.elo; + eidofs = xreg.elo - range.elo; + } else if (yreg && (int = yreg.eset?.intersection(qr))) { ir = yreg.eset; - eidofs -= yreg.offset; + //eidofs -= yreg.offset(); + eidofs = yreg.elo - range.elo; + } else { + ir = null; + eidofs = 0; + } + if (!ir) { + throw new ECSError(`no intersection for index register`, action); } - if (!ir) throw new ECSError(`no intersection for index register`, action); if (ir.entities.length == 0) throw new ECSError(`no common entities for index register`, action); if (!ir.isContiguous()) throw new ECSError(`entities in query are not contiguous`, action); if (ir == this.scope.state.xreg?.eset) @@ -1292,7 +1303,7 @@ export class EntityScope implements SourceLocated { this.bss.allocateBytes('TEMP', bssbin.extents.right); for (let b of pack.boxes) { let inst : SystemInstance = (b as any).inst; - console.log(inst.system.name, b.box?.left); + //console.log(inst.system.name, b.box?.left); this.bss.equates[this.dialect.tempLabel(inst)] = `TEMP+${b.box?.left}`; } } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index eb4af3ed..3402813f 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -85,30 +85,32 @@ describe('Compiler', function() { let testdir = './test/ecs/'; let files = readdirSync(testdir).filter(f => f.endsWith('.ecs')); files.forEach((ecsfn) => { - let asmfn = ecsfn.replace('.ecs','.asm'); - let goodfn = ecsfn.replace('.ecs','.txt'); - let ecspath = testdir + ecsfn; - let goodpath = testdir + goodfn; - let dialect = new Dialect_CA65(); - let em = new EntityManager(dialect); - em.mainPath = ecspath; - let compiler = new ECSCompiler(em); - compiler.getImportFile = (path: string) => { - return readFileSync(testdir + path, 'utf-8'); - } - let code = readFileSync(ecspath, 'utf-8'); - compiler.parseFile(code, ecspath); - // TODO: errors - let out = new SourceFileExport(); - em.exportToFile(out); - let outtxt = out.toString(); - let goodtxt = existsSync(goodpath) ? readFileSync(goodpath, 'utf-8') : ''; - if (outtxt.trim() != goodtxt.trim()) { - let asmpath = '/tmp/' + asmfn; - writeFileSync(asmpath, outtxt, 'utf-8'); - console.log(spawnSync('/usr/bin/diff', [goodpath, asmpath], {encoding:'utf-8'}).stdout); - throw new Error(`files different; to fix: cp ${asmpath} ${goodpath}`); - } + it('Should compile ' + ecsfn, function() { + let asmfn = ecsfn.replace('.ecs','.asm'); + let goodfn = ecsfn.replace('.ecs','.txt'); + let ecspath = testdir + ecsfn; + let goodpath = testdir + goodfn; + let dialect = new Dialect_CA65(); + let em = new EntityManager(dialect); + em.mainPath = ecspath; + let compiler = new ECSCompiler(em); + compiler.getImportFile = (path: string) => { + return readFileSync(testdir + path, 'utf-8'); + } + let code = readFileSync(ecspath, 'utf-8'); + compiler.parseFile(code, ecspath); + // TODO: errors + let out = new SourceFileExport(); + em.exportToFile(out); + let outtxt = out.toString(); + let goodtxt = existsSync(goodpath) ? readFileSync(goodpath, 'utf-8') : ''; + if (outtxt.trim() != goodtxt.trim()) { + let asmpath = '/tmp/' + asmfn; + writeFileSync(asmpath, outtxt, 'utf-8'); + console.log(spawnSync('/usr/bin/diff', [goodpath, asmpath], {encoding:'utf-8'}).stdout); + throw new Error(`files different; to fix: cp ${asmpath} ${goodpath}`); + } + }); }); }); diff --git a/test/ecs/score.ecs b/test/ecs/score.ecs new file mode 100644 index 00000000..b3ae2924 --- /dev/null +++ b/test/ecs/score.ecs @@ -0,0 +1,299 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" + +component BCDScore2 + digits: 0..0xff +end + +component BCDScore4 + digits: 0..0xffff +end + +component BCDScore6 + digits: 0..0xffffff +end + +system Kernel6Digit + locals 15 + on preframe do with [BCDScore6] +--- +Digit0 = {{$0}} +Digit1 = {{$2}} +Digit2 = {{$4}} +Digit3 = {{$6}} +Digit4 = {{$8}} +Digit5 = {{$10}} +@BCD0 = {{$12}} +@BCD1 = {{$13}} +@BCD2 = {{$14}} + + lda {{get digits 0}} + sta @BCD0 + lda {{get digits 8}} + sta @BCD1 + lda {{get digits 16}} + sta @BCD2 + ldx #0 ; leftmost bitmap + ldy #2 ; start from most-sigificant BCD value +@Loop: + lda @BCD0,y ; get BCD value + and #$f0 ; isolate high nibble (* 16) + lsr ; shift right 1 bit (* 8) + clc + adc #<{{^FontTable}} + sta Digit0,x ; store pointer lo byte + lda #>{{^FontTable}} + adc #0 + sta Digit0+1,x ; store pointer hi byte + inx + inx ; next bitmap pointer + lda @BCD0,y ; get BCD value (again) + and #$f ; isolate low nibble + asl + asl + asl ; * 8 + clc + adc #<{{^FontTable}} + sta Digit0,x ; store pointer lo byte + lda #>{{^FontTable}} + adc #0 + sta Digit0+1,x ; store pointer hi byte + inx + inx ; next bitmap pointer + dey ; next BCD value + bpl @Loop ; repeat until < 0 +--- + on kernel do with [BCDScore6,PFColor] +--- + lda {{ [GRP0] + lda (Digit1),y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + sta WSYNC ; sync to next scanline + lda (Digit2),y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda (Digit5),y ; load B5 -> A + sta @Temp ; B5 -> temp + lda (Digit4),y ; load B4 + tax ; -> X + lda (Digit3),y ; load B3 -> A + ldy @Temp ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec @LoopCount ; go to next line + bpl @BigLoop ; repeat until < 0 + + lda #0 ; clear the sprite registers + sta WSYNC + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + sta COLUBK +--- +end + + +resource FontTable --- +; Font table for digits 0-9 (8x8 pixels) +FontTable: +;;{w:8,h:8,count:10,brev:1,flip:1};; + .byte $00,$3c,$66,$66,$76,$6e,$66,$3c,$00,$7e,$18,$18,$18,$38,$18,$18 + .byte $00,$7e,$60,$30,$0c,$06,$66,$3c,$00,$3c,$66,$06,$1c,$06,$66,$3c + .byte $00,$06,$06,$7f,$66,$1e,$0e,$06,$00,$3c,$66,$06,$06,$7c,$60,$7e + .byte $00,$3c,$66,$66,$7c,$60,$66,$3c,$00,$18,$18,$18,$18,$0c,$66,$7e + .byte $00,$3c,$66,$66,$3c,$66,$66,$3c,$00,$3c,$66,$06,$3e,$66,$66,$3c +--- + +system Kernel2Digit + locals 3 + on kernel do select [BCDScore2,PFColor] +--- + lda #$02 + sta CTRLPF +; TODO: should be constants +; and it's wrong, too! + lda {{{{arg 0}} +.endif +; Adds value to 6-BCD-digit score. +; A = 1st BCD digit +; Y = 2nd BCD digit + sed ; enter BCD mode + clc ; clear carry + adc {{get digits}} + sta {{set digits}} + tya + adc {{get digits 8}} + sta {{set digits 8}} + lda {{get digits 16}} + adc #0 + sta {{set digits 16}} + cld ; exit BCD mode +--- +end + +demo Main + + using FrameLoop + using Kernel6Digit, FontTable + using Kernel2Digit, FontTablePF + using JoyButton, BCDMath + + entity [Player,BCDScore6,PFColor,BGColor] + init digits = 0x123456 + init pfcolor = $3c + init bgcolor = $02 + end + + entity [BCDScore2,PFColor] + init digits = 0x24 + init pfcolor = $ce + end + entity [BCDScore2,PFColor] + init digits = 0x56 + init pfcolor = $3e + end + + system IncScore + on joybutton do with [Player,BCDScore6] +--- + {{!AddBCD4 $0210}} +--- + end +end diff --git a/test/ecs/score.txt b/test/ecs/score.txt new file mode 100644 index 00000000..173c7927 --- /dev/null +++ b/test/ecs/score.txt @@ -0,0 +1,423 @@ +.scope Main +.zeropage +BCDScore6_digits_b0: +.res 1 +BCDScore6_digits_b8: +.res 1 +BCDScore6_digits_b16: +.res 1 +PFColor_pfcolor_b0: +.res 1 +.res 1 +.res 1 +BGColor_bgcolor_b0: +.res 1 +BCDScore2_digits_b0: +.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 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +Kernel6Digit__2__tmp = TEMP+0 +Kernel2Digit__4__tmp = TEMP+15 +.code +Main__INITDATA: +.byte 86 +.byte 52 +.byte 18 +.byte 60 +.byte 206 +.byte 62 +.byte 2 +.byte 36 +.byte 86 +__Start: +.code + +;;; start action Init 9 main_init + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #9 +: lda Main__INITDATA-1,y + sta BCDScore6_digits_b0-1,y + dey + bne :- + ; initialize data segment +.code + +;;; start action FrameLoop 1 start + +FrameLoop__start__2__NextFrame: + FRAME_START + .code + +;;; start action Kernel6Digit 2 preframe + +Digit0 = Kernel6Digit__2__tmp+0 +Digit1 = Kernel6Digit__2__tmp+2 +Digit2 = Kernel6Digit__2__tmp+4 +Digit3 = Kernel6Digit__2__tmp+6 +Digit4 = Kernel6Digit__2__tmp+8 +Digit5 = Kernel6Digit__2__tmp+10 +Kernel6Digit__preframe__3__BCD0 = Kernel6Digit__2__tmp+12 +Kernel6Digit__preframe__3__BCD1 = Kernel6Digit__2__tmp+13 +Kernel6Digit__preframe__3__BCD2 = Kernel6Digit__2__tmp+14 + + lda BCDScore6_digits_b0 + sta Kernel6Digit__preframe__3__BCD0 + lda BCDScore6_digits_b8 + sta Kernel6Digit__preframe__3__BCD1 + lda BCDScore6_digits_b16 + sta Kernel6Digit__preframe__3__BCD2 + ldx #0 ; leftmost bitmap + ldy #2 ; start from most-sigificant BCD value +Kernel6Digit__preframe__3__Loop: + lda Kernel6Digit__preframe__3__BCD0,y ; get BCD value + and #$f0 ; isolate high nibble (* 16) + lsr ; shift right 1 bit (* 8) + clc + adc #FontTable + adc #0 + sta Digit0+1,x ; store pointer hi byte + inx + inx ; next bitmap pointer + lda Kernel6Digit__preframe__3__BCD0,y ; get BCD value (again) + and #$f ; isolate low nibble + asl + asl + asl ; * 8 + clc + adc #FontTable + adc #0 + sta Digit0+1,x ; store pointer hi byte + inx + inx ; next bitmap pointer + dey ; next BCD value + bpl Kernel6Digit__preframe__3__Loop ; repeat until < 0 + +;;; end action Kernel6Digit 2 preframe + + KERNEL_START + .code + +;;; start action Kernel6Digit 2 kernel + + lda PFColor_pfcolor_b0 + sta COLUP0 + sta COLUP1 + lda #3 + sta NUSIZ0 + sta NUSIZ1 +; set horizontal position of player objects + sta WSYNC + sta HMCLR + SLEEPR 24 + sta RESP0 + sta RESP1 + lda #$10 + sta HMP1 + sta WSYNC + sta HMOVE + SLEEPR 24 ; wait 24 cycles between write to HMOVE and HMxxx + sta HMCLR + lda #1 + sta VDELP0 + sta VDELP1 + +;;; end action Kernel6Digit 2 kernel + +;;; start action Kernel6Digit 2 kernel + +; Display the resulting 48x8 bitmap +; using the Digit0-5 pointers. +Kernel6Digit__kernel__5__LoopCount = Kernel6Digit__2__tmp+12 +Kernel6Digit__kernel__5__Temp = Kernel6Digit__2__tmp+13 + + lda BGColor_bgcolor_b0 + sta WSYNC + sta COLUBK + lda #7 + sta Kernel6Digit__kernel__5__LoopCount + SLEEPR 20 ; TODO? +Kernel6Digit__kernel__5__BigLoop: + ldy Kernel6Digit__kernel__5__LoopCount ; counts backwards + lda (Digit0),y ; load B0 (1st sprite byte) + sta GRP0 ; B0 -> [GRP0] + lda (Digit1),y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + sta WSYNC ; sync to next scanline + lda (Digit2),y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda (Digit5),y ; load B5 -> A + sta Kernel6Digit__kernel__5__Temp ; B5 -> temp + lda (Digit4),y ; load B4 + tax ; -> X + lda (Digit3),y ; load B3 -> A + ldy Kernel6Digit__kernel__5__Temp ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec Kernel6Digit__kernel__5__LoopCount ; go to next line + bpl Kernel6Digit__kernel__5__BigLoop ; repeat until < 0 + + lda #0 ; clear the sprite registers + sta WSYNC + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + sta COLUBK + +;;; end action Kernel6Digit 2 kernel + +;;; start action Kernel2Digit 4 kernel + + lda #$02 + sta CTRLPF +; TODO: should be constants +; and it's wrong, too! + lda PFColor_pfcolor_b0+0 + sta COLUP0 + lda PFColor_pfcolor_b0+1 + sta COLUP1 + +;;; end action Kernel2Digit 4 kernel + +;;; start action Kernel2Digit 4 kernel + + lda #7 + sta Kernel2Digit__4__tmp+0 +Kernel2Digit__kernel__7__Loop: + ldx #0 + sta WSYNC + .code + +;;; start action Kernel2Digit 4 compute2digit + + lda Kernel2Digit__4__tmp+1 ; load 1st pf + sta PF1 ; store 1st pf +; first digit + lda BCDScore2_digits_b0 + 0 + pha + and #$0f + asl + asl + asl + .code + +;;; start action Kernel2Digit 4 fetchdigit + + adc Kernel2Digit__4__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit 4 fetchdigit + + and #$0f + ldy Kernel2Digit__4__tmp+2 ; load 2nd pf + sta Kernel2Digit__4__tmp+1 + 0 +; second digit + pla + and #$f0 + lsr + sty PF1 ; store 2nd pf + .code + +;;; start action Kernel2Digit 4 fetchdigit + + adc Kernel2Digit__4__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit 4 fetchdigit + + and #$f0 + ora Kernel2Digit__4__tmp+1 + 0 + sta Kernel2Digit__4__tmp+1 + 0 + +;;; end action Kernel2Digit 4 compute2digit + + inx + .code + +;;; start action Kernel2Digit 4 compute2digit + + lda Kernel2Digit__4__tmp+1 ; load 1st pf + sta PF1 ; store 1st pf +; first digit + lda BCDScore2_digits_b0 + 1 + pha + and #$0f + asl + asl + asl + .code + +;;; start action Kernel2Digit 4 fetchdigit + + adc Kernel2Digit__4__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit 4 fetchdigit + + and #$0f + ldy Kernel2Digit__4__tmp+2 ; load 2nd pf + sta Kernel2Digit__4__tmp+1 + 1 +; second digit + pla + and #$f0 + lsr + sty PF1 ; store 2nd pf + .code + +;;; start action Kernel2Digit 4 fetchdigit + + adc Kernel2Digit__4__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit 4 fetchdigit + + and #$f0 + ora Kernel2Digit__4__tmp+1 + 1 + sta Kernel2Digit__4__tmp+1 + 1 + +;;; end action Kernel2Digit 4 compute2digit + +; playfield + dec Kernel2Digit__4__tmp+0 + bpl Kernel2Digit__kernel__7__Loop +; dex +; stx PF1 + +;;; end action Kernel2Digit 4 kernel + + KERNEL_END + .code + +;;; start action JoyButton 6 postframe + + lda INPT4 ;read button input + bmi JoyButton__postframe__14__NotPressed + .code + +;;; start action IncScore 8 joybutton + + .code + +;;; start action BCDMath 7 AddBCD4 + +.ifnblank $0210 + lda #<$0210 + ldy #>$0210 +.endif +; Adds value to 6-BCD-digit score. +; A = 1st BCD digit +; Y = 2nd BCD digit + sed ; enter BCD mode + clc ; clear carry + adc BCDScore6_digits_b0 + sta BCDScore6_digits_b0 + tya + adc BCDScore6_digits_b8 + sta BCDScore6_digits_b8 + lda BCDScore6_digits_b16 + adc #0 + sta BCDScore6_digits_b16 + cld ; exit BCD mode + +;;; end action BCDMath 7 AddBCD4 + + +;;; end action IncScore 8 joybutton + +JoyButton__postframe__14__NotPressed: + +;;; end action JoyButton 6 postframe + + FRAME_END + .code + + jmp FrameLoop__start__2__NextFrame ; loop to next frame + +;;; end action FrameLoop 1 start + ; start main routine +.segment "VECTORS" +Return: .word $6060 +VecNMI: +VecReset: .word Main::__Reset +VecBRK: .word Main::__BRK + +;;; end action Init 9 main_init + +.code + +;;; start action FontTable 3 FontTable + +; Font table for digits 0-9 (8x8 pixels) +FontTable: +;;{w:8,h:8,count:10,brev:1,flip:1};; + .byte $00,$3c,$66,$66,$76,$6e,$66,$3c,$00,$7e,$18,$18,$18,$38,$18,$18 + .byte $00,$7e,$60,$30,$0c,$06,$66,$3c,$00,$3c,$66,$06,$1c,$06,$66,$3c + .byte $00,$06,$06,$7f,$66,$1e,$0e,$06,$00,$3c,$66,$06,$06,$7c,$60,$7e + .byte $00,$3c,$66,$66,$7c,$60,$66,$3c,$00,$18,$18,$18,$18,$0c,$66,$7e + .byte $00,$3c,$66,$66,$3c,$66,$66,$3c,$00,$3c,$66,$06,$3e,$66,$66,$3c + +;;; end action FontTable 3 FontTable + +.code + +;;; start action FontTablePF 5 FontTablePF + +; Font table for digits 0-9 (4x8 pixels) +FontTablePF: +;;{w:8,h:8,count:10,brev:1,flip:1};; + .byte $00,$00,$EE,$AA,$AA,$AA,$EE,$00 + .byte $00,$00,$22,$22,$22,$22,$22,$00 + .byte $00,$00,$EE,$88,$EE,$22,$EE,$00 + .byte $00,$00,$EE,$22,$66,$22,$EE,$00 + .byte $00,$00,$22,$22,$EE,$AA,$AA,$00 + .byte $00,$00,$EE,$22,$EE,$88,$EE,$00 + .byte $00,$00,$EE,$AA,$EE,$88,$EE,$00 + .byte $00,$00,$22,$22,$22,$22,$EE,$00 + .byte $00,$00,$EE,$AA,$EE,$AA,$EE,$00 + .byte $00,$00,$EE,$22,$EE,$AA,$EE,$00 +;; + +;;; end action FontTablePF 5 FontTablePF + +.endscope +Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/sprites.ecs b/test/ecs/sprites.ecs new file mode 100644 index 00000000..aa28b6ab --- /dev/null +++ b/test/ecs/sprites.ecs @@ -0,0 +1,440 @@ + +//#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 {{Bitmap:bitmapdata}},x + sbc #0 + sta {{$2}},y +; get bitmap height + lda {{Colormap:colormapdata}},x + sbc #0 + sta {{$6}},y +; save ypos + ldx {{$12}} ; restore X + lda {{ 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 {{data}} + sta {{$1}} +--- + on scanline do once +--- +.if {{arg 0}} = 0 + lda ({{local 0}}),y + tax +.endif +--- + on scanline do once +--- +.if {{arg 0}} = 1 + lda ({{local 0}}),y + sta $00,x +.endif +--- + on postframe do once +--- + lda #0 + sta PF0 + sta PF1 + sta PF2 +--- +end + +system SetXPos + on preframe do once +--- + sta HMCLR +--- + on preframe do join [SpriteSlot] with [HasXpos] + limit 2 +--- + lda {{(Bitmap_bitmapdata_e1_b0+31) +.byte >(Bitmap_bitmapdata_e2_b0+31) +Bitmap_bitmapdata_e1_b0: +.byte 1 +.byte 1 +.byte 3 +.byte 7 +.byte 15 +.byte 31 +.byte 63 +.byte 127 +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_b0: +.byte <(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_b8: +.byte >(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_e3_b0: +.byte 6 +.byte 3 +.byte 6 +.byte 9 +.byte 12 +.byte 14 +.byte 31 +.byte 63 +Sprite_plyrflags_b0: +.byte 0 +.byte 3 +.byte 2 +.byte 0 +Main__INITDATA: +.byte 1 +.byte 0 +.byte 1 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 50 +.byte 100 +.byte 80 +.byte 40 +.byte 150 +.byte 60 +.byte 90 +.byte 150 +.byte 0 +.byte 1 +.byte 2 +.byte 3 +__Start: +.code + +;;; start action Init 10 main_init + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #20 +: lda Main__INITDATA-1,y + sta HasBitmap_bitmap_b0-1,y + dey + bne :- + ; initialize data segment +.code + +;;; start action FrameLoop 1 start + +FrameLoop__start__2__NextFrame: + FRAME_START + .code + +;;; start action Kernel2Sprite 2 preframe + +.define KLINES #192 +.define KPAD 32 +; set height to zero in case no sprites + lda #0 + sta Kernel2Sprite__2__tmp+8 + sta Kernel2Sprite__2__tmp+9 +; set temp value so we don't read bitmap from h/w registers + lda #$F0 + sta Kernel2Sprite__2__tmp+2 + sta Kernel2Sprite__2__tmp+3 + sta Kernel2Sprite__2__tmp+6 + sta Kernel2Sprite__2__tmp+7 + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + + ldy #0 +Kernel2Sprite__preframe__4____each: + ldx SpriteSlot_sprite_b0,y + +; flags set according to sprite slot value +; skip sprite if negative + jmi Kernel2Sprite__preframe__4__nosprite +; 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__2__tmp+11 +; calculate bitmap pointer + stx Kernel2Sprite__2__tmp+12 ; save X (Sprite index) + lda HasBitmap_bitmap_b0,x ; deref bitmap + tax + lda Bitmap_bitmapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index + lda Bitmap_bitmapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+2,y +; get bitmap height + lda Bitmap_height_b0,x + sta Kernel2Sprite__2__tmp+8,y +; calculate colormap pointer + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasColormap_colormap_b0,x ; deref colormap + tax + lda Colormap_colormapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+4,y + lda Colormap_colormapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+6,y +; save ypos + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasYpos_ypos_b0,x + sta Kernel2Sprite__2__tmp+10,y +Kernel2Sprite__preframe__4__nosprite: + + iny + cpy #2 + jne Kernel2Sprite__preframe__4____each +Kernel2Sprite__preframe__4____exit: + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + +; shuffle pointers into (MSB, LSB) byte order +; L0 L1 H0 H1 -> L0 H0 L1 H1 + lda Kernel2Sprite__2__tmp+1 + ldy Kernel2Sprite__2__tmp+2 + sty Kernel2Sprite__2__tmp+1 + sta Kernel2Sprite__2__tmp+2 + lda Kernel2Sprite__2__tmp+5 + ldy Kernel2Sprite__2__tmp+6 + sty Kernel2Sprite__2__tmp+5 + sta Kernel2Sprite__2__tmp+6 + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + + lda #162 + sta COLUBK + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + +;;; end action Kernel2Sprite 2 preframe + +;;; start action SetXPos 6 preframe + + sta HMCLR + +;;; end action SetXPos 6 preframe + +;;; start action SetXPos 6 preframe + + ldy #0 +SetXPos__preframe__8____each: + ldx SpriteSlot_sprite_b0,y + + lda HasXpos_xpos_b0,x + .code + +;;; start action SetHorizPos 7 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 7 SetHorizPos + + + iny + cpy #2 + jne SetXPos__preframe__8____each +SetXPos__preframe__8____exit: + +;;; end action SetXPos 6 preframe + +;;; start action SetXPos 6 preframe + +;;; end action SetXPos 6 preframe + +;;; start action SetXPos 6 preframe + + sta WSYNC + sta HMOVE + +;;; end action SetXPos 6 preframe + + KERNEL_START + .code + +;;; start action Kernel2Sprite 2 kernel + + ldy #0 + sty VDELP0 + iny + sta VDELP1 + +;;; end action Kernel2Sprite 2 kernel + +;;; start action Kernel2Sprite 2 kernel + + ldy #192 +Kernel2Sprite__kernel__12__LVScan: + .code + +;;; start action Kernel2Sprite 2 scanline + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__13__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__13__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y + .if 0 = 0 + sta WSYNC + .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__13__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__13__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite 2 scanline + +;;; start action Kernel2Sprite 2 scanline + +;;; end action Kernel2Sprite 2 scanline + + dey ; next scanline + .code + +;;; start action Kernel2Sprite 2 scanline + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__14__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__14__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y + .if 1 = 0 + sta WSYNC + .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__14__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__14__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite 2 scanline + +;;; start action Kernel2Sprite 2 scanline + +;;; end action Kernel2Sprite 2 scanline + + dey ; next scanline + bne Kernel2Sprite__kernel__12__LVScan ; repeat until out of lines + +;;; end action Kernel2Sprite 2 kernel + +;;; start action Kernel2Sprite 2 kernel + + lda #0 + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + +;;; end action Kernel2Sprite 2 kernel + + KERNEL_END + .code + +;;; start action Joystick 3 postframe + +; 2 control inputs share a single byte, 4 bits each + lda SWCHA + sta Joystick__3__tmp+0 + +;;; end action Joystick 3 postframe + +;;; start action Joystick 3 postframe + + ldx #0 +Joystick__postframe__17____each: + + asl Joystick__3__tmp+0 + bcs Joystick__postframe__17__SkipMoveRight + .code + +;;; start action MoveJoyX 4 joyright + + lda HasXpos_xpos_b0,x + clc + adc #1 + cmp #152 + bcs MoveJoyX__joyright__18__nomove + sta HasXpos_xpos_b0,x +MoveJoyX__joyright__18__nomove: + +;;; end action MoveJoyX 4 joyright + +Joystick__postframe__17__SkipMoveRight: + asl Joystick__3__tmp+0 + bcs Joystick__postframe__17__SkipMoveLeft + .code + +;;; start action MoveJoyX 4 joyleft + + lda HasXpos_xpos_b0,x + sec + sbc #1 + bcc MoveJoyX__joyleft__19__nomove + sta HasXpos_xpos_b0,x +MoveJoyX__joyleft__19__nomove: + +;;; end action MoveJoyX 4 joyleft + +Joystick__postframe__17__SkipMoveLeft: + asl Joystick__3__tmp+0 + bcs Joystick__postframe__17__SkipMoveDown + .code + +;;; start action MoveJoyY 5 joydown + + lda HasYpos_ypos_b0,x + clc + adc #1 + cmp #220 + bcs MoveJoyY__joydown__20__nomove + sta HasYpos_ypos_b0,x +MoveJoyY__joydown__20__nomove: + +;;; end action MoveJoyY 5 joydown + +Joystick__postframe__17__SkipMoveDown: + asl Joystick__3__tmp+0 + bcs Joystick__postframe__17__SkipMoveUp + .code + +;;; start action MoveJoyY 5 joyup + + lda HasYpos_ypos_b0,x + sec + sbc #1 + bcc MoveJoyY__joyup__21__nomove + sta HasYpos_ypos_b0,x +MoveJoyY__joyup__21__nomove: + +;;; end action MoveJoyY 5 joyup + +Joystick__postframe__17__SkipMoveUp: + + inx + cpx #4 + jne Joystick__postframe__17____each +Joystick__postframe__17____exit: + +;;; end action Joystick 3 postframe + +;;; start action SpriteShuffler 8 postframe + +; load two sprite slots at left side of array + lda SpriteSlot_sprite_b0 + sta SpriteShuffler__8__tmp+0 + lda SpriteSlot_sprite_b0+1 + sta SpriteShuffler__8__tmp+1 +; move two slots to the left + ldx #0 +SpriteShuffler__postframe__22__loop: + lda SpriteSlot_sprite_b0+2,x + sta SpriteSlot_sprite_b0,x + inx + cpx #4-2 + bne SpriteShuffler__postframe__22__loop +; store two sprite slots at right side of array + lda SpriteShuffler__8__tmp+0 + sta SpriteSlot_sprite_b0+4-2 + lda SpriteShuffler__8__tmp+1 + sta SpriteSlot_sprite_b0+4-1 + +;;; end action SpriteShuffler 8 postframe + +;;; start action SpriteHider 9 postframe + + lda #4-1 + sta SpriteHider__9__tmp+0 + +;;; end action SpriteHider 9 postframe + +;;; start action SpriteHider 9 postframe + + ldy #0 +SpriteHider__postframe__24____each: + ldx SpriteSlot_sprite_b0,y + + lda HasYpos_ypos_b0,x + cmp #192 + bcc SpriteHider__postframe__24__skip +; swap this sprite slot with slot at end of array + lda SpriteSlot_sprite_b0,y + pha + ldx SpriteHider__9__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__9__tmp+0 +SpriteHider__postframe__24__skip: + + iny + cpy #2 + jne SpriteHider__postframe__24____each +SpriteHider__postframe__24____exit: + +;;; end action SpriteHider 9 postframe + + FRAME_END + .code + + jmp FrameLoop__start__2__NextFrame ; loop to next frame + +;;; end action FrameLoop 1 start + ; start main routine +.segment "VECTORS" +Return: .word $6060 +VecNMI: +VecReset: .word Main::__Reset +VecBRK: .word Main::__BRK + +;;; end action Init 10 main_init + +.endscope +Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/superman.ecs b/test/ecs/superman.ecs new file mode 100644 index 00000000..137bc1ba --- /dev/null +++ b/test/ecs/superman.ecs @@ -0,0 +1,524 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" +import "sprites.ecs" + +// https://csanyk.com/2014/02/topology-metropolis-superman-atari-2600/ + +component RoomGraphics + graphics: array 0..8 of 0..255 +end + +component Room + fgcolor: 0..255 + bgcolor: 0..255 + gfx: [VersatilePlayfield] + north: [Room] + east: [Room] + south: [Room] + west: [Room] +end + +component Location + room: [Room] +end + +component Enemy +end + +component Moving + speed: 1..15 +end + + +system SuperFly + on gowest do with [Location] +--- + ldy {{(Bitmap_bitmapdata_e1_b0+31) +.byte >(Bitmap_bitmapdata_e2_b0+31) +.byte >(Bitmap_bitmapdata_e3_b0+31) +Bitmap_bitmapdata_e1_b0: +Bitmap_height_b0: +.byte 255 +.byte 17 +.byte 27 +Colormap_colormapdata_b0: +.byte <(Colormap_colormapdata_e1_b0+31) +.byte <(Colormap_colormapdata_e2_b0+31) +.byte <(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_b8: +.byte >(Colormap_colormapdata_e1_b0+31) +.byte >(Colormap_colormapdata_e2_b0+31) +.byte >(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_e1_b0: +Bitmap_bitmapdata_e2_b0: +.byte 128 +.byte 192 +.byte 192 +.byte 224 +.byte 224 +.byte 112 +.byte 116 +.byte 62 +.byte 61 +.byte 28 +.byte 28 +.byte 12 +.byte 22 +.byte 39 +.byte 70 +.byte 134 +.byte 6 +.byte 3 +Colormap_colormapdata_e2_b0: +.byte 70 +.byte 70 +.byte 134 +.byte 134 +.byte 134 +.byte 136 +.byte 134 +.byte 134 +.byte 134 +.byte 134 +.byte 136 +.byte 70 +.byte 72 +.byte 72 +.byte 72 +.byte 72 +.byte 84 +.byte 82 +Bitmap_bitmapdata_e3_b0: +.byte 14 +.byte 14 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 14 +.byte 30 +.byte 31 +.byte 63 +.byte 63 +.byte 63 +.byte 62 +.byte 62 +.byte 44 +.byte 46 +.byte 46 +.byte 47 +.byte 46 +.byte 46 +.byte 38 +.byte 32 +.byte 40 +.byte 48 +.byte 96 +.byte 128 +Colormap_colormapdata_e3_b0: +.byte 4 +.byte 4 +.byte 198 +.byte 198 +.byte 198 +.byte 200 +.byte 200 +.byte 200 +.byte 38 +.byte 40 +.byte 40 +.byte 40 +.byte 40 +.byte 40 +.byte 42 +.byte 42 +.byte 70 +.byte 72 +.byte 72 +.byte 72 +.byte 72 +.byte 72 +.byte 70 +.byte 4 +.byte 4 +.byte 4 +.byte 4 +.byte 4 +Room_fgcolor_b0: +.byte 0 +.byte 12 +.byte 12 +Room_bgcolor_b0: +.byte 0 +.byte 18 +.byte 18 +Room_north_b0: +.byte 0 +.byte 1 +.byte 1 +Room_east_b0: +.byte 0 +.byte 2 +.byte 1 +Room_south_b0: +.byte 0 +.byte 1 +.byte 1 +Room_west_b0: +.byte 0 +.byte 2 +.byte 1 +VersatilePlayfield_data_b0: +.byte <(VersatilePlayfield_data_e4_b0+-1) +.byte <(VersatilePlayfield_data_e5_b0+-1) +.byte <(VersatilePlayfield_data_e6_b0+-1) +VersatilePlayfield_data_b8: +.byte >(VersatilePlayfield_data_e4_b0+-1) +.byte >(VersatilePlayfield_data_e5_b0+-1) +.byte >(VersatilePlayfield_data_e6_b0+-1) +VersatilePlayfield_data_e4_b0: +.byte 0 +VersatilePlayfield_data_e5_b0: +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 255 +.byte 14 +.byte 85 +.byte 14 +.byte 24 +.byte 9 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 6 +.byte 8 +.byte 0 +.byte 63 +.byte 255 +.byte 15 +.byte 247 +.byte 15 +.byte 128 +.byte 15 +.byte 0 +.byte 63 +.byte 193 +.byte 15 +.byte 227 +.byte 15 +.byte 0 +.byte 63 +.byte 247 +.byte 15 +.byte 200 +.byte 9 +.byte 0 +.byte 10 +.byte 255 +.byte 15 +.byte 255 +.byte 14 +.byte 240 +.byte 13 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 56 +.byte 9 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 247 +.byte 15 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 246 +.byte 15 +.byte 0 +.byte 63 +.byte 70 +.byte 9 +.byte 240 +.byte 15 +.byte 0 +.byte 63 +.byte 176 +.byte 15 +.byte 127 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 107 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 68 +.byte 9 +.byte 240 +.byte 15 +.byte 0 +.byte 63 +.byte 176 +.byte 15 +.byte 127 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 107 +.byte 14 +.byte 240 +.byte 15 +.byte 80 +.byte 9 +.byte 176 +.byte 15 +.byte 127 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 107 +.byte 14 +.byte 240 +.byte 15 +.byte 112 +.byte 15 +.byte 48 +.byte 15 +.byte 16 +.byte 15 +.byte 144 +.byte 15 +.byte 96 +.byte 13 +.byte 127 +.byte 14 +.byte 96 +.byte 9 +.byte 0 +.byte 63 +.byte 208 +.byte 15 +.byte 144 +.byte 15 +.byte 16 +.byte 15 +.byte 48 +.byte 15 +.byte 112 +.byte 15 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 240 +.byte 15 +.byte 224 +.byte 15 +.byte 120 +.byte 14 +.byte 192 +.byte 15 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 8 +.byte 8 +.byte 1 +.byte 10 +.byte 192 +.byte 9 +VersatilePlayfield_data_e6_b0: +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 63 +.byte 6 +.byte 9 +.byte 24 +.byte 9 +.byte 0 +.byte 63 +.byte 85 +.byte 15 +.byte 170 +.byte 14 +.byte 80 +.byte 13 +.byte 6 +.byte 9 +.byte 6 +.byte 8 +.byte 8 +.byte 8 +.byte 255 +.byte 14 +.byte 247 +.byte 14 +.byte 128 +.byte 14 +.byte 0 +.byte 63 +.byte 193 +.byte 14 +.byte 227 +.byte 14 +.byte 0 +.byte 63 +.byte 247 +.byte 14 +.byte 200 +.byte 9 +.byte 0 +.byte 10 +.byte 255 +.byte 15 +.byte 255 +.byte 14 +.byte 240 +.byte 13 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 56 +.byte 9 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 247 +.byte 15 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 246 +.byte 15 +.byte 0 +.byte 63 +.byte 70 +.byte 9 +.byte 240 +.byte 15 +.byte 0 +.byte 63 +.byte 176 +.byte 15 +.byte 127 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 107 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 68 +.byte 9 +.byte 240 +.byte 15 +.byte 0 +.byte 63 +.byte 176 +.byte 15 +.byte 127 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 107 +.byte 14 +.byte 240 +.byte 15 +.byte 80 +.byte 9 +.byte 176 +.byte 15 +.byte 127 +.byte 14 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 107 +.byte 14 +.byte 240 +.byte 15 +.byte 112 +.byte 15 +.byte 48 +.byte 15 +.byte 16 +.byte 15 +.byte 144 +.byte 15 +.byte 96 +.byte 13 +.byte 127 +.byte 14 +.byte 96 +.byte 9 +.byte 0 +.byte 63 +.byte 208 +.byte 15 +.byte 144 +.byte 15 +.byte 16 +.byte 15 +.byte 48 +.byte 15 +.byte 112 +.byte 15 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 240 +.byte 15 +.byte 224 +.byte 15 +.byte 120 +.byte 14 +.byte 192 +.byte 15 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 0 +.byte 63 +.byte 168 +.byte 8 +.byte 1 +.byte 10 +.byte 112 +.byte 9 +HasBitmap_bitmap_b0: +.byte 1 +.byte 2 +.byte 2 +.byte 2 +HasColormap_colormap_b0: +.byte 1 +.byte 2 +.byte 2 +.byte 2 +Moving_speed_b0: +.byte 2 +.byte 1 +.byte 1 +.byte 1 +Main__INITDATA: +.byte 2 +.byte 2 +.byte 2 +.byte 1 +.byte 5 +.byte 0 +.byte 0 +.byte 0 +.byte 40 +.byte 100 +.byte 100 +.byte 50 +.byte 30 +.byte 30 +.byte 60 +.byte 90 +.byte 0 +.byte 255 +.byte 0 +.byte 0 +.byte 0 +__Start: +.code + +;;; start action Init 11 main_init + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #21 +: lda Main__INITDATA-1,y + sta Location_room_b0-1,y + dey + bne :- + ; initialize data segment +.code + +;;; start action FrameLoop 1 start + +FrameLoop__start__2__NextFrame: + FRAME_START + .code + +;;; start action Kernel2Sprite 2 preframe + +.define KLINES #192 +.define KPAD 32 +; set height to zero in case no sprites + lda #0 + sta Kernel2Sprite__2__tmp+8 + sta Kernel2Sprite__2__tmp+9 +; set temp value so we don't read bitmap from h/w registers + lda #$F0 + sta Kernel2Sprite__2__tmp+2 + sta Kernel2Sprite__2__tmp+3 + sta Kernel2Sprite__2__tmp+6 + sta Kernel2Sprite__2__tmp+7 + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + + ldy #0 +Kernel2Sprite__preframe__4____each: + ldx SpriteSlot_sprite_b0,y + +; flags set according to sprite slot value +; skip sprite if negative + jmi Kernel2Sprite__preframe__4__nosprite +; 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__2__tmp+11 +; calculate bitmap pointer + stx Kernel2Sprite__2__tmp+12 ; save X (Sprite index) + lda HasBitmap_bitmap_b0,x ; deref bitmap + tax + lda Bitmap_bitmapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index + lda Bitmap_bitmapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+2,y +; get bitmap height + lda Bitmap_height_b0,x + sta Kernel2Sprite__2__tmp+8,y +; calculate colormap pointer + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasColormap_colormap_b0,x ; deref colormap + tax + lda Colormap_colormapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+4,y + lda Colormap_colormapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+6,y +; save ypos + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasYpos_ypos_b0,x + sta Kernel2Sprite__2__tmp+10,y +Kernel2Sprite__preframe__4__nosprite: + + iny + cpy #2 + jne Kernel2Sprite__preframe__4____each +Kernel2Sprite__preframe__4____exit: + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + +; shuffle pointers into (MSB, LSB) byte order +; L0 L1 H0 H1 -> L0 H0 L1 H1 + lda Kernel2Sprite__2__tmp+1 + ldy Kernel2Sprite__2__tmp+2 + sty Kernel2Sprite__2__tmp+1 + sta Kernel2Sprite__2__tmp+2 + lda Kernel2Sprite__2__tmp+5 + ldy Kernel2Sprite__2__tmp+6 + sty Kernel2Sprite__2__tmp+5 + sta Kernel2Sprite__2__tmp+6 + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + + lda #162 + sta COLUBK + +;;; end action Kernel2Sprite 2 preframe + +;;; start action Kernel2Sprite 2 preframe + +;;; end action Kernel2Sprite 2 preframe + +;;; start action SetXPos 8 preframe + + sta HMCLR + +;;; end action SetXPos 8 preframe + +;;; start action SetXPos 8 preframe + + ldy #0 +SetXPos__preframe__8____each: + ldx SpriteSlot_sprite_b0,y + + lda HasXpos_xpos_b0,x + .code + +;;; start action SetHorizPos 9 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 9 SetHorizPos + + + iny + cpy #2 + jne SetXPos__preframe__8____each +SetXPos__preframe__8____exit: + +;;; end action SetXPos 8 preframe + +;;; start action SetXPos 8 preframe + +;;; end action SetXPos 8 preframe + +;;; start action SetXPos 8 preframe + + sta WSYNC + sta HMOVE + +;;; end action SetXPos 8 preframe + +;;; start action VersatilePlayfield 10 preframe + + ldx Location_room_b0+0 + + lda VersatilePlayfield_data_b0,x + sta VersatilePlayfield__10__tmp+0 + lda VersatilePlayfield_data_b8,x + sta VersatilePlayfield__10__tmp+1 + + +;;; end action VersatilePlayfield 10 preframe + + KERNEL_START + .code + +;;; start action Kernel2Sprite 2 kernel + + ldy #0 + sty VDELP0 + iny + sta VDELP1 + +;;; end action Kernel2Sprite 2 kernel + +;;; start action Kernel2Sprite 2 kernel + + ldy #192 +Kernel2Sprite__kernel__13__LVScan: + .code + +;;; start action Kernel2Sprite 2 scanline + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__14__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__14__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y + .if 0 = 0 + sta WSYNC + .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__14__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__14__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite 2 scanline + +;;; start action Kernel2Sprite 2 scanline + +;;; end action Kernel2Sprite 2 scanline + +;;; start action VersatilePlayfield 10 scanline + +.if 0 = 0 + lda (VersatilePlayfield__10__tmp+0),y + tax +.endif + +;;; end action VersatilePlayfield 10 scanline + +;;; start action VersatilePlayfield 10 scanline + +.if 0 = 1 + lda (VersatilePlayfield__10__tmp+0),y + sta $00,x +.endif + +;;; end action VersatilePlayfield 10 scanline + + dey ; next scanline + .code + +;;; start action Kernel2Sprite 2 scanline + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__17__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__17__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y + .if 1 = 0 + sta WSYNC + .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__17__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__17__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite 2 scanline + +;;; start action Kernel2Sprite 2 scanline + +;;; end action Kernel2Sprite 2 scanline + +;;; start action VersatilePlayfield 10 scanline + +.if 1 = 0 + lda (VersatilePlayfield__10__tmp+0),y + tax +.endif + +;;; end action VersatilePlayfield 10 scanline + +;;; start action VersatilePlayfield 10 scanline + +.if 1 = 1 + lda (VersatilePlayfield__10__tmp+0),y + sta $00,x +.endif + +;;; end action VersatilePlayfield 10 scanline + + dey ; next scanline + bne Kernel2Sprite__kernel__13__LVScan ; repeat until out of lines + +;;; end action Kernel2Sprite 2 kernel + +;;; start action Kernel2Sprite 2 kernel + + lda #0 + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + +;;; end action Kernel2Sprite 2 kernel + + KERNEL_END + .code + +;;; start action Joystick 3 postframe + +; 2 control inputs share a single byte, 4 bits each + lda SWCHA + sta Joystick__3__tmp+0 + +;;; end action Joystick 3 postframe + +;;; start action Joystick 3 postframe + + asl Joystick__3__tmp+0 + bcs Joystick__postframe__22__SkipMoveRight + .code + +;;; start action JoyFaceDirection 4 joyright + + lda Sprite_plyrflags_b0 + and #$f7 + sta Sprite_plyrflags_b0 + +;;; end action JoyFaceDirection 4 joyright + +;;; start action SuperFly 5 joyright + + lda HasXpos_xpos_b0 + clc + adc #2 + cmp #142 + jcc SuperFly__joyright__24__nomove + .code + +;;; start action SuperFly 5 goeast + + ldy Location_room_b0 + lda Room_east_b0,y + sta Location_room_b0 + +;;; end action SuperFly 5 goeast + + lda #2 +SuperFly__joyright__24__nomove: + sta HasXpos_xpos_b0 + +;;; end action SuperFly 5 joyright + +Joystick__postframe__22__SkipMoveRight: + asl Joystick__3__tmp+0 + bcs Joystick__postframe__22__SkipMoveLeft + .code + +;;; start action JoyFaceDirection 4 joyleft + + lda Sprite_plyrflags_b0 + ora #$08 + sta Sprite_plyrflags_b0 + +;;; end action JoyFaceDirection 4 joyleft + +;;; start action SuperFly 5 joyleft + + lda HasXpos_xpos_b0 + sec + sbc #2 + jcs SuperFly__joyleft__27__nomove + .code + +;;; start action SuperFly 5 gowest + + ldy Location_room_b0 + lda Room_west_b0,y + sta Location_room_b0 + +;;; end action SuperFly 5 gowest + + lda #142 +SuperFly__joyleft__27__nomove: + sta HasXpos_xpos_b0 + +;;; end action SuperFly 5 joyleft + +Joystick__postframe__22__SkipMoveLeft: + asl Joystick__3__tmp+0 + bcs Joystick__postframe__22__SkipMoveDown + .code + +;;; start action SuperFly 5 joydown + + lda HasYpos_ypos_b0 + clc + adc #2 + cmp #220 + jcc SuperFly__joydown__29__nomove + .code + +;;; start action SuperFly 5 gosouth + + ldy Location_room_b0 + lda Room_south_b0,y + sta Location_room_b0 + +;;; end action SuperFly 5 gosouth + + lda #2 +SuperFly__joydown__29__nomove: + sta HasYpos_ypos_b0 + +;;; end action SuperFly 5 joydown + +Joystick__postframe__22__SkipMoveDown: + asl Joystick__3__tmp+0 + bcs Joystick__postframe__22__SkipMoveUp + .code + +;;; start action SuperFly 5 joyup + + lda HasYpos_ypos_b0 + sec + sbc #2 + jcs SuperFly__joyup__31__nomove + .code + +;;; start action SuperFly 5 gonorth + + ldy Location_room_b0 + lda Room_north_b0,y + sta Location_room_b0 + +;;; end action SuperFly 5 gonorth + + lda #200 +SuperFly__joyup__31__nomove: + sta HasYpos_ypos_b0 + +;;; end action SuperFly 5 joyup + +Joystick__postframe__22__SkipMoveUp: + +;;; end action Joystick 3 postframe + +;;; start action BadMove 6 postframe + + ldx #0 +BadMove__postframe__33____each: + + .code + +;;; start action JoyFaceDirection 4 joyright + + lda Sprite_plyrflags_b0+1,x + and #$f7 + sta Sprite_plyrflags_b0+1,x + +;;; end action JoyFaceDirection 4 joyright + +;;; start action SuperFly 5 joyright + + lda HasXpos_xpos_b0+1,x + clc + adc #1 + cmp #142 + jcc SuperFly__joyright__35__nomove + .code + +;;; start action SuperFly 5 goeast + + ldy Location_room_b0+1,x + lda Room_east_b0,y + sta Location_room_b0+1,x + +;;; end action SuperFly 5 goeast + + lda #2 +SuperFly__joyright__35__nomove: + sta HasXpos_xpos_b0+1,x + +;;; end action SuperFly 5 joyright + + + inx + cpx #3 + jne BadMove__postframe__33____each +BadMove__postframe__33____exit: + +;;; end action BadMove 6 postframe + +;;; start action RoomShuffle 7 postframe + + ldy 4 + ldx SpriteSlot_sprite_b0+1 + bmi RoomShuffle__postframe__37__empty ; empty slot, load 1st entry +RoomShuffle__postframe__37__loop: + inx + cpx 4 + bcc RoomShuffle__postframe__37__norecycle +; TODO: need to get index of specific entity +RoomShuffle__postframe__37__empty: + ldx #1 ; skip null sprite and super dude? +RoomShuffle__postframe__37__norecycle: + lda Location_room_b0,x + cmp Location_room_b0 + beq RoomShuffle__postframe__37__exit + dey + bne RoomShuffle__postframe__37__loop + ldx #$ff +RoomShuffle__postframe__37__exit: + stx SpriteSlot_sprite_b0+1 + +;;; end action RoomShuffle 7 postframe + +;;; start action VersatilePlayfield 10 postframe + + lda #0 + sta PF0 + sta PF1 + sta PF2 + +;;; end action VersatilePlayfield 10 postframe + + FRAME_END + .code + + jmp FrameLoop__start__2__NextFrame ; loop to next frame + +;;; end action FrameLoop 1 start + ; start main routine +.segment "VECTORS" +Return: .word $6060 +VecNMI: +VecReset: .word Main::__Reset +VecBRK: .word Main::__BRK + +;;; end action Init 11 main_init + +.endscope +Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/titles.ecs b/test/ecs/titles.ecs new file mode 100644 index 00000000..b42f6389 --- /dev/null +++ b/test/ecs/titles.ecs @@ -0,0 +1,131 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" + +component Bitmap48 + bitmap0: array of 0..0xff + bitmap1: array of 0..0xff + bitmap2: array of 0..0xff + bitmap3: array of 0..0xff + bitmap4: array of 0..0xff + bitmap5: array of 0..0xff + height: 0..255 +end + +system Kernel48Pixel + locals 2 + on kernelsetup do if [Bitmap48] --- + lda {{ [GRP0] + lda {{data bitmap1}},y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + lda {{data bitmap2}},y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda {{data bitmap5}},y ; load B5 -> A + sta {{$1}} ; B5 -> temp + ldx {{data bitmap4}},y ; load B4 -> X + lda {{data bitmap3}},y ; load B3 -> A + ldy {{$1}} ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec {{$0}} ; go to next line + bpl @Loop ; repeat until < 0 + pla + tax +--- +end + +demo Main + + //using FrameLoop, StaticKernel, Kernel48Pixel + using FrameLoop, Kernel48Pixel, StaticKernel + + // TODO + + entity [KernelSection, BGColor] + const lines = 10 + const bgcolor = $a0 + end + + entity [KernelSection, BGColor] + const lines = 10 + const bgcolor = $a2 + end + + entity [KernelSection, BGColor, PFColor, Bitmap48] + const lines = 2 + const bgcolor = 0xa4 + const pfcolor = 0xfc + decode vcs_bitmap48 --- +x.............................................xx +xx.........................................xxx.x +xxxx....................................xxx....x +xx..xxxx..............................xx.......x +xx......xx...........................x.........x +xx...x....x............xx............x.........x +xx...x....x...........x..x.........xx..........x +xx...x..x..x....x......xx.....x...x............x +xx...xxxx...x...xxx...x..x..xxx..x.............x +xx...x..x..x......x....xx...x.....x............x +xx...x....x...........x..x.........xx..........x +xx......xx.............xx............x.........x +xx..xxxx..............................xx.......x +xxxx....................................xxxxxxxx +................................................ +--- + end + + entity [KernelSection, BGColor] + const lines = 10 + const bgcolor = $a2 + end + + entity [KernelSection, BGColor] + const lines = 10 + const bgcolor = $a0 + end + +end diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt new file mode 100644 index 00000000..7e030e86 --- /dev/null +++ b/test/ecs/titles.txt @@ -0,0 +1,369 @@ +.scope Main +.zeropage +TEMP: +.res 1 +.res 1 +Kernel48Pixel__2__tmp = TEMP+0 +.code +KernelSection_lines_b0: +.byte 10 +.byte 10 +.byte 2 +.byte 10 +.byte 10 +BGColor_bgcolor_b0: +.byte 160 +.byte 162 +.byte 164 +.byte 162 +.byte 160 +PFColor_pfcolor_b0: +.byte 252 +Bitmap48_bitmap0_b0: +.byte Bitmap48_bitmap0_e2_b0 +Bitmap48_bitmap0_e2_b0: +.byte 0 +.byte 240 +.byte 207 +.byte 192 +.byte 196 +.byte 196 +.byte 199 +.byte 196 +.byte 196 +.byte 196 +.byte 192 +.byte 207 +.byte 240 +.byte 192 +.byte 128 +Bitmap48_bitmap1_b0: +.byte Bitmap48_bitmap1_e2_b0 +Bitmap48_bitmap1_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 192 +.byte 32 +.byte 144 +.byte 136 +.byte 144 +.byte 32 +.byte 32 +.byte 192 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap2_b0: +.byte Bitmap48_bitmap2_e2_b0 +Bitmap48_bitmap2_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 1 +.byte 2 +.byte 33 +.byte 226 +.byte 129 +.byte 2 +.byte 1 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap3_b0: +.byte Bitmap48_bitmap3_e2_b0 +Bitmap48_bitmap3_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 128 +.byte 64 +.byte 136 +.byte 78 +.byte 130 +.byte 64 +.byte 128 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap4_b0: +.byte Bitmap48_bitmap4_e2_b0 +Bitmap48_bitmap4_e2_b0: +.byte 0 +.byte 0 +.byte 3 +.byte 4 +.byte 24 +.byte 32 +.byte 64 +.byte 32 +.byte 24 +.byte 4 +.byte 4 +.byte 3 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap5_b0: +.byte Bitmap48_bitmap5_e2_b0 +Bitmap48_bitmap5_e2_b0: +.byte 0 +.byte 255 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 1 +.byte 225 +.byte 29 +.byte 3 +Bitmap48_height_b0: +.byte 14 +__Start: +.code + +;;; start action Init 4 main_init + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + ; initialize data segment +.code + +;;; start action FrameLoop 1 start + +FrameLoop__start__2__NextFrame: + FRAME_START + .code + +;;; start action StaticKernel 3 preframe + + .code + +;;; start action Kernel48Pixel 2 kernelsetup + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action Kernel48Pixel 2 kernelsetup + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + + lda #160 + sta COLUBK + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + +;;; end action StaticKernel 3 kernelsetup + + +;;; end action StaticKernel 3 preframe + + KERNEL_START + .code + +;;; start action StaticKernel 3 kernel + + ldx #0 +StaticKernel__kernel__5____each: + + sta WSYNC + .code + +;;; start action Kernel48Pixel 2 kernelsetup + + cpx #2+1 + jcs Kernel48Pixel__kernelsetup__6____skipxhi + + cpx #2 + jcc Kernel48Pixel__kernelsetup__6____skipxlo + + lda #14 + sta Kernel48Pixel__2__tmp+0 ; scanline counter + lda #$22 + sta COLUP0 ; show how players alternate + lda #$12 + sta COLUP1 ; by having different colors + lda #3 + sta NUSIZ0 + sta NUSIZ1 ; both players have 3 copies + sta WSYNC + SLEEPH 34 + sta RESP0 ; position 1st player + sta RESP1 ; ...and 2nd player + lda #$10 + sta HMP1 ; 1 pixel to the left + sta WSYNC + sta HMOVE ; apply HMOVE + SLEEPH 24 ; sleep 24 cycles + sta HMCLR ; clear HMOVE registers + lda #1 + sta VDELP0 ; we need the VDEL registers + sta VDELP1 ; so we can do our 4-store trick + +Kernel48Pixel__kernelsetup__6____skipxlo: + +Kernel48Pixel__kernelsetup__6____skipxhi: + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action Kernel48Pixel 2 kernelsetup + + cpx #2+1 + jcs Kernel48Pixel__kernelsetup__7____skipxhi + + cpx #2 + jcc Kernel48Pixel__kernelsetup__7____skipxlo + + lda #252 + sta COLUP0 + sta COLUP1 + +Kernel48Pixel__kernelsetup__7____skipxlo: + +Kernel48Pixel__kernelsetup__7____skipxhi: + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + + lda BGColor_bgcolor_b0,x + sta COLUBK + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + + cpx #2+1 + jcs StaticKernel__kernelsetup__9____skipxhi + + cpx #2 + jcc StaticKernel__kernelsetup__9____skipxlo + + lda #252 + sta COLUPF + +StaticKernel__kernelsetup__9____skipxlo: + +StaticKernel__kernelsetup__9____skipxhi: + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + +;;; end action StaticKernel 3 kernelsetup + + .code + +;;; start action Kernel48Pixel 2 kerneldraw + + cpx #2+1 + jcs Kernel48Pixel__kerneldraw__10____skipxhi + + cpx #2 + jcc Kernel48Pixel__kerneldraw__10____skipxlo + + txa + pha +Kernel48Pixel__kerneldraw__10__Loop: + ldy Kernel48Pixel__2__tmp+0 ; counts backwards + sta WSYNC ; sync to next scanline + lda Bitmap48_bitmap0_e2_b0,y ; load B0 (1st sprite byte) + sta GRP0 ; B0 -> [GRP0] + lda Bitmap48_bitmap1_e2_b0,y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + lda Bitmap48_bitmap2_e2_b0,y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda Bitmap48_bitmap5_e2_b0,y ; load B5 -> A + sta Kernel48Pixel__2__tmp+1 ; B5 -> temp + ldx Bitmap48_bitmap4_e2_b0,y ; load B4 -> X + lda Bitmap48_bitmap3_e2_b0,y ; load B3 -> A + ldy Kernel48Pixel__2__tmp+1 ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec Kernel48Pixel__2__tmp+0 ; go to next line + bpl Kernel48Pixel__kerneldraw__10__Loop ; repeat until < 0 + pla + tax + +Kernel48Pixel__kerneldraw__10____skipxlo: + +Kernel48Pixel__kerneldraw__10____skipxhi: + +;;; end action Kernel48Pixel 2 kerneldraw + +;;; start action StaticKernel 3 kerneldraw + + ldy KernelSection_lines_b0,x +StaticKernel__kerneldraw__11__loop: + sta WSYNC + + dey + bne StaticKernel__kerneldraw__11__loop + +;;; end action StaticKernel 3 kerneldraw + + + + inx + cpx #5 + jne StaticKernel__kernel__5____each +StaticKernel__kernel__5____exit: + +;;; end action StaticKernel 3 kernel + + KERNEL_END + .code + + FRAME_END + .code + + jmp FrameLoop__start__2__NextFrame ; loop to next frame + +;;; end action FrameLoop 1 start + ; start main routine +.segment "VECTORS" +Return: .word $6060 +VecNMI: +VecReset: .word Main::__Reset +VecBRK: .word Main::__BRK + +;;; end action Init 4 main_init + +.endscope +Main__Start = Main::__Start \ No newline at end of file From 75f36d95dc908dcc3d1b619093049122e8a20d23 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 17 Feb 2022 09:48:50 -0600 Subject: [PATCH 068/104] ecs: const, multiple sys instances for params --- src/common/ecs/compiler.ts | 14 ++++++++++---- src/common/ecs/ecs.ts | 13 ++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index d3ddbd98..64e912ec 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -338,6 +338,7 @@ export class ECSCompiler extends Tokenizer { if (cmd == 'comment') { this.expectTokenTypes([ECSTokenType.CodeFragment]); } + // TODO: need to make these local names, otherwise we get "duplicate name" if (cmd == 'system') { let sys = this.annotate(() => this.parseSystem()); this.em.defineSystem(sys); @@ -350,7 +351,13 @@ export class ECSCompiler extends Tokenizer { parseScopeUsing() { let instlist = this.parseList(this.parseSystemInstanceRef, ','); + let params = {}; + if (this.peekToken().str == 'with') { + this.consumeToken(); + params = this.parseSystemInstanceParameters(); + } for (let inst of instlist) { + inst.params = params; this.currentScope?.newSystemInstance(inst); } } @@ -464,10 +471,6 @@ export class ECSCompiler extends Tokenizer { let system = this.em.getSystemByName(name); if (!system) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc); let params = {}; - if (this.peekToken().str == 'with') { - this.consumeToken(); - params = this.parseSystemInstanceParameters(); - } let inst = { system, params, id: 0 }; return inst; } @@ -475,6 +478,9 @@ export class ECSCompiler extends Tokenizer { parseSystemInstanceParameters() : SystemInstanceParameters { let scope = this.currentScope; if (scope == null) throw new Error(); + if (this.peekToken().str == '[') { + return { query: this.parseQuery() }; + } this.expectToken('#'); let entname = this.expectIdent(); this.expectToken('.'); diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 2b50c45a..1129251f 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -61,6 +61,7 @@ export interface System extends SourceLocated { } export interface SystemInstanceParameters { + query?: Query; refEntity?: Entity; refField?: ComponentFieldPair; } @@ -600,6 +601,8 @@ class ActionEval { this.qr = this.qr.intersection(new EntitySet(scope, rq)); //console.log('with', instance.params, rq, this.qr); } + } else if (instance.params.query) { + this.qr = this.qr.intersection(new EntitySet(scope, instance.params.query)); } this.entities = this.qr.entities; //let query = (this.action as ActionWithQuery).query; @@ -767,10 +770,18 @@ class ActionEval { } __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? + if (this.qr.entities.length != 1) throw new ECSError(`data operates on exactly one entity`, this.action); // TODO? let eid = this.qr.entities[0].id; // TODO? return this.dialect.datasymbol(component, field, eid, bitofs); } + __const(args: string[]) { + let { component, field, bitofs } = this.parseFieldArgs(args); + if (this.qr.entities.length != 1) throw new ECSError(`const operates on exactly one entity`, this.action); // TODO? + let constVal = this.qr.entities[0].consts[mksymbol(component, field.name)]; + if (constVal === undefined) throw new ECSError(`field is not constant`, this.action); // TODO? + if (typeof constVal !== 'number') throw new ECSError(`field is not numeric`, this.action); // TODO? + return constVal << bitofs; + } __index(args: string[]) { // TODO: check select type and if we actually have an index... let ident = args[0]; From b4dbcb83eb537b95f212dd3cce7b2e66758ff0e9 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 17 Feb 2022 09:58:13 -0600 Subject: [PATCH 069/104] ecs: temp symbols, not equates --- src/common/ecs/ecs.ts | 16 ++++++++++------ test/ecs/score.txt | 4 ++-- test/ecs/sprites.txt | 14 +++++++------- test/ecs/sprites1.txt | 14 +++++++------- test/ecs/superman.txt | 6 +++--- test/ecs/titles.txt | 2 +- test/ecs/vcs1.txt | 2 +- test/ecs/vcslib.txt | 2 +- 8 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 1129251f..974778f2 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -386,14 +386,17 @@ class DataSegment { let ofs = this.symbols[name]; if (ofs == null) { ofs = this.size; - this.symbols[name] = ofs; - if (!this.ofs2sym.has(ofs)) - this.ofs2sym.set(ofs, []); - this.ofs2sym.get(ofs)?.push(name); + this.declareSymbol(name, ofs); this.size += bytes; } return ofs; } + declareSymbol(name: string, ofs: number) { + this.symbols[name] = ofs; + if (!this.ofs2sym.has(ofs)) + this.ofs2sym.set(ofs, []); + this.ofs2sym.get(ofs)?.push(name); + } // TODO: optimize shared data allocateInitData(name: string, bytes: Uint8Array) { let ofs = this.allocateBytes(name, bytes.length); @@ -1311,11 +1314,12 @@ export class EntityScope implements SourceLocated { if (!pack.pack()) console.log('cannot pack temporary local vars'); // TODO //console.log('tempvars', pack); if (bssbin.extents.right > 0) { - this.bss.allocateBytes('TEMP', bssbin.extents.right); + let tempofs = this.bss.allocateBytes('TEMP', bssbin.extents.right); for (let b of pack.boxes) { let inst : SystemInstance = (b as any).inst; //console.log(inst.system.name, b.box?.left); - this.bss.equates[this.dialect.tempLabel(inst)] = `TEMP+${b.box?.left}`; + if (b.box) this.bss.declareSymbol(this.dialect.tempLabel(inst), tempofs + b.box.left); + //this.bss.equates[this.dialect.tempLabel(inst)] = `TEMP+${b.box?.left}`; } } } diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 173c7927..8bd03d7f 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -16,6 +16,7 @@ BCDScore2_digits_b0: .res 1 .res 1 TEMP: +Kernel6Digit__2__tmp: .res 1 .res 1 .res 1 @@ -31,11 +32,10 @@ TEMP: .res 1 .res 1 .res 1 +Kernel2Digit__4__tmp: .res 1 .res 1 .res 1 -Kernel6Digit__2__tmp = TEMP+0 -Kernel2Digit__4__tmp = TEMP+15 .code Main__INITDATA: .byte 86 diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index d9f5d0b8..7b49ceef 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -26,6 +26,13 @@ SpriteSlot_sprite_b0: .res 1 .res 1 TEMP: +Kernel2Sprite__2__tmp: +Joystick__3__tmp: +.res 1 +SpriteShuffler__8__tmp: +.res 1 +.res 1 +SpriteHider__9__tmp: .res 1 .res 1 .res 1 @@ -36,13 +43,6 @@ TEMP: .res 1 .res 1 .res 1 -.res 1 -.res 1 -.res 1 -Kernel2Sprite__2__tmp = TEMP+0 -Joystick__3__tmp = TEMP+0 -SpriteShuffler__8__tmp = TEMP+1 -SpriteHider__9__tmp = TEMP+3 .code KernelSection_lines_b0: .byte 192 diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 5d8a5ac2..d67ece2f 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -26,6 +26,13 @@ HasColormap_colormap_b0: .res 1 .res 1 TEMP: +Kernel2Sprite__2__tmp: +Joystick__3__tmp: +.res 1 +SpriteShuffler__8__tmp: +.res 1 +.res 1 +SpriteHider__9__tmp: .res 1 .res 1 .res 1 @@ -36,13 +43,6 @@ TEMP: .res 1 .res 1 .res 1 -.res 1 -.res 1 -.res 1 -Kernel2Sprite__2__tmp = TEMP+0 -Joystick__3__tmp = TEMP+0 -SpriteShuffler__8__tmp = TEMP+1 -SpriteHider__9__tmp = TEMP+3 .code KernelSection_lines_b0: .byte 192 diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index d3a6d9c1..af4c5257 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -28,6 +28,8 @@ Room_gfx_b0: .res 1 .res 1 TEMP: +Kernel2Sprite__2__tmp: +Joystick__3__tmp: .res 1 .res 1 .res 1 @@ -41,11 +43,9 @@ TEMP: .res 1 .res 1 .res 1 +VersatilePlayfield__10__tmp: .res 1 .res 1 -Kernel2Sprite__2__tmp = TEMP+0 -Joystick__3__tmp = TEMP+0 -VersatilePlayfield__10__tmp = TEMP+13 .code KernelSection_lines_b0: .byte 192 diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index 7e030e86..67482d52 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -1,9 +1,9 @@ .scope Main .zeropage TEMP: +Kernel48Pixel__2__tmp: .res 1 .res 1 -Kernel48Pixel__2__tmp = TEMP+0 .code KernelSection_lines_b0: .byte 10 diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index 2f038c3f..d5908d83 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -4,8 +4,8 @@ PFColor_pfcolor_b0: .res 1 .res 1 TEMP: +Local__6__tmp: .res 1 -Local__6__tmp = TEMP+0 .code KernelSection_lines_b0: .byte 2 diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index c317ee78..9a7c50e6 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -4,8 +4,8 @@ PFColor_pfcolor_b0: .res 1 .res 1 TEMP: +Local__6__tmp: .res 1 -Local__6__tmp = TEMP+0 .code KernelSection_lines_b0: .byte 2 From 4749e18b3530fb1f8f83c2dd7aff9e9a2eeb1cd3 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 17 Feb 2022 12:52:13 -0600 Subject: [PATCH 070/104] ecs: critical code, sorta --- src/common/ecs/compiler.ts | 3 +- src/common/ecs/ecs.ts | 80 ++++++++++++++++++++++++++-------- src/common/util.ts | 2 +- src/test/testecs.ts | 17 +++++++- test/ecs/sprites.txt | 82 +++++++++++++++++------------------ test/ecs/sprites1.txt | 72 +++++++++++++++---------------- test/ecs/superman.txt | 88 +++++++++++++++++++------------------- test/ecs/titles.txt | 46 ++++++++++---------- test/ecs/vcs1.txt | 30 ++++++------- test/ecs/vcslib.txt | 30 ++++++------- 10 files changed, 254 insertions(+), 196 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 64e912ec..aaaf510a 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -236,7 +236,7 @@ export class ECSCompiler extends Tokenizer { this.expectToken('do'); // TODO: include modifiers in error msg const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? - const all_modifiers = ['cyclecritical','asc','desc']; // TODO + const all_modifiers = ['critical','asc','desc']; // TODO const modifiers = this.parseModifiers(all_modifiers); let query = undefined; let join = undefined; @@ -259,6 +259,7 @@ export class ECSCompiler extends Tokenizer { if (modifiers['asc']) direction = 'asc'; else if (modifiers['desc']) direction = 'desc'; let action = { text, event, query, join, select, direction }; + if (modifiers['critical']) (action as ActionWithJoin).critical = true; return action as ActionWithJoin; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 974778f2..6ffbce44 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -124,6 +124,7 @@ export interface ActionBase extends SourceLocated { select: SelectType; event: string; text: string; + critical?: boolean; } export interface ActionOnce extends ActionBase { @@ -315,9 +316,11 @@ export class Dialect_CA65 { return `.endscope\n${name}__Start = ${name}::__Start` // TODO: scope__start = scope::start } - segment(seg: string, segtype: 'rodata' | 'bss' | 'code') { + segment(segtype: 'rodata' | 'bss' | 'code') { if (segtype == 'bss') { - return `.zeropage`; // TODO + return `.zeropage`; + } else if (segtype == 'rodata') { + return '.rodata'; // TODO? } else { return `.code`; } @@ -343,6 +346,12 @@ export class Dialect_CA65 { equate(symbol: string, value: string): string { return `${symbol} = ${value}`; } + call(symbol: string) { + return ` jsr ${symbol}`; + } + return() { + return ' rts'; + } } // TODO: merge with Dialect? @@ -580,6 +589,7 @@ class ActionEval { oldState : ActionCPUState; entities : Entity[]; tmplabel = ''; + label : string; constructor( readonly scope: EntityScope, @@ -611,6 +621,7 @@ class ActionEval { //let query = (this.action as ActionWithQuery).query; //TODO? if (query && this.entities.length == 0) //throw new ECSError(`query doesn't match any entities`, query); // TODO + this.label = `${this.instance.system.name}__${action.event}__${this.em.seq++}`; } begin() { let state = this.scope.state = Object.assign({}, this.scope.state); @@ -659,17 +670,22 @@ class ActionEval { this.scope.state = this.oldState; } codeToString(): string { - const tag_re = /\{\{(.+?)\}\}/g; - const label_re = /@(\w+)\b/g; - const allowEmpty = ['if','foreach','join']; if (this.entities.length == 0 && allowEmpty.includes(this.action.select)) return ''; let action = this.action; let sys = this.instance.system; + let { code, props } = this.getCodeAndProps(action); + // replace @labels + code = this.replaceLabels(code, this.label); + // replace {{...}} tags + // TODO: use nodes instead + code = this.replaceTags(code, action, props); + return code; + } + private getCodeAndProps(action: Action) { let code = action.text; - let label = `${sys.name}__${action.event}__${this.em.seq++}`; // TODO: better label that won't conflict (seq?) let props: { [name: string]: string } = {}; if (action.select != 'once') { // TODO: detect cycles @@ -677,8 +693,8 @@ class ActionEval { // TODO: what if only 1 item? // TODO: what if join is subset of items? if (action.select == 'join' && this.jr) { - let jentities = this.jr.entities; - if (jentities.length == 0) return ''; + //let jentities = this.jr.entities; + // TODO? if (jentities.length == 0) return ''; // TODO? throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO let joinfield = this.getJoinField(action, this.qr.atypes, this.jr.atypes); // TODO: what if only 1 item? @@ -722,12 +738,14 @@ class ActionEval { props['%xofs'] = (this.scope.state.xreg?.offset() || 0).toString(); props['%yofs'] = (this.scope.state.yreg?.offset() || 0).toString(); } - // replace @labels - code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); - // replace {{...}} tags + return { code, props }; + } + private replaceTags(code: string, action: Action, props: { [name: string]: string; }) { + const tag_re = /\{\{(.+?)\}\}/g; code = code.replace(tag_re, (entire, group: string) => { let toks = group.split(/\s+/); - if (toks.length == 0) throw new ECSError(`empty command`, action); + if (toks.length == 0) + throw new ECSError(`empty command`, action); let cmd = group.charAt(0); let arg0 = toks[0].substring(1).trim(); let args = [arg0].concat(toks.slice(1)); @@ -740,14 +758,22 @@ class ActionEval { case '>': return this.__get([arg0, '8']); default: let value = props[toks[0]]; - if (value) return value; + if (value) + return value; let fn = (this as any)['__' + toks[0]]; - if (fn) return fn.bind(this)(toks.slice(1)); + if (fn) + return fn.bind(this)(toks.slice(1)); throw new ECSError(`unrecognized command {{${toks[0]}}}`, action); } }); return code; } + private replaceLabels(code: string, label: string) { + const label_re = /@(\w+)\b/g; + code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); + return code; + } + __get(args: string[]) { return this.getset(args, false); } @@ -1245,7 +1271,7 @@ export class EntityScope implements SourceLocated { } this.eventSeq++; // generate code - let s = this.dialect.code(); + let code = this.dialect.code(); //s += `\n; event ${event}\n`; let eventCount = 0; let instances = this.instances.filter(inst => systems.includes(inst.system)); @@ -1258,9 +1284,25 @@ export class EntityScope implements SourceLocated { // TODO: keep event tree let codeeval = new ActionEval(this, inst, action, args || []); codeeval.begin(); + let s = ''; s += this.dialect.comment(`start action ${sys.name} ${inst.id} ${event}`); // TODO - s += codeeval.codeToString(); + if (action.critical) { + // TODO: bin-packing, share identical code + let sublabel = `${codeeval.label}__sub`; + let lines = [ + this.dialect.segment('rodata'), + this.dialect.label(sublabel), + codeeval.codeToString(), + this.dialect.return(), + this.dialect.code(), + this.dialect.call(sublabel) + ]; + s += lines.join('\n'); + } else { + s += codeeval.codeToString(); + } s += this.dialect.comment(`end action ${sys.name} ${inst.id} ${event}`); + code += s; // TODO: check that this happens once? codeeval.end(); } @@ -1269,7 +1311,7 @@ export class EntityScope implements SourceLocated { if (eventCount == 0) { console.log(`warning: event ${event} not handled`); } - return s; + return code; } getSystemStats(inst: SystemInstance) : SystemStats { let stats = this.sysstats.get(inst); @@ -1356,9 +1398,9 @@ export class EntityScope implements SourceLocated { private dumpCodeTo(file: SourceFileExport) { let dialect = this.dialect; file.line(dialect.startScope(this.name)); - file.line(dialect.segment(`${this.name}_DATA`, 'bss')); + file.line(dialect.segment('bss')); this.bss.dump(file, dialect); - file.line(dialect.segment(`${this.name}_RODATA`, 'rodata')); + file.line(dialect.segment('code')); // TODO: rodata for aligned? this.rodata.dump(file, dialect); //file.segment(`${this.name}_CODE`, 'code'); file.line(dialect.label('__Start')); diff --git a/src/common/util.ts b/src/common/util.ts index 47c854ab..d2687958 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -482,7 +482,7 @@ export function getBasePlatform(platform : string) : string { } // get platform ID without - specialization -export function getRootPlatform(platform : string) : string { +function getRootPlatform(platform : string) : string { return platform.split('-')[0]; } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 3402813f..bd93b72d 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -150,7 +150,7 @@ describe('Box Packer', function() { ] ); }); - it('Should pack top-aligned boxes', function() { + it('Should pack unaligned boxes', function() { testPack( [ new Bin({ left:0, top:0, right:10, bottom:10 }) @@ -163,5 +163,20 @@ describe('Box Packer', function() { ] ); }); + it('Should pack multiple bins', function() { + testPack( + [ + new Bin({ left:0, top:0, right:10, bottom:10 }), + new Bin({ left:0, top:0, right:10, bottom:10 }) + ], [ + + { width: 5, height: 10 }, + { width: 5, height: 10 }, + { width: 5, height: 5 }, + { width: 5, height: 10 }, + { width: 5, height: 5 }, + ] + ); + }); }); diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index 7b49ceef..04821ad8 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -249,7 +249,7 @@ Kernel2Sprite__preframe__4____exit: ;;; start action SetXPos 6 preframe ldy #0 -SetXPos__preframe__8____each: +SetXPos__preframe__9____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x @@ -263,9 +263,9 @@ SetXPos__preframe__8____each: sta WSYNC ; start a new line sec ; set carry flag nop -SetHorizPos__SetHorizPos__9__DivideLoop: +SetHorizPos__SetHorizPos__10__DivideLoop: sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__9__DivideLoop ; branch until negative + bcs SetHorizPos__SetHorizPos__10__DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl @@ -279,8 +279,8 @@ SetHorizPos__SetHorizPos__9__DivideLoop: iny cpy #2 - jne SetXPos__preframe__8____each -SetXPos__preframe__8____exit: + jne SetXPos__preframe__9____each +SetXPos__preframe__9____exit: ;;; end action SetXPos 6 preframe @@ -310,7 +310,7 @@ SetXPos__preframe__8____exit: ;;; start action Kernel2Sprite 2 kernel ldy #192 -Kernel2Sprite__kernel__12__LVScan: +Kernel2Sprite__kernel__14__LVScan: .code ;;; start action Kernel2Sprite 2 scanline @@ -318,10 +318,10 @@ Kernel2Sprite__kernel__12__LVScan: ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__13__DoDraw1 + bcs Kernel2Sprite__scanline__15__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__13__DoDraw1: +Kernel2Sprite__scanline__15__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 0 = 0 sta WSYNC @@ -332,10 +332,10 @@ Kernel2Sprite__scanline__13__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__13__DoDraw2 + bcs Kernel2Sprite__scanline__15__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__13__DoDraw2: +Kernel2Sprite__scanline__15__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y @@ -355,10 +355,10 @@ Kernel2Sprite__scanline__13__DoDraw2: ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__14__DoDraw1 + bcs Kernel2Sprite__scanline__17__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__14__DoDraw1: +Kernel2Sprite__scanline__17__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 1 = 0 sta WSYNC @@ -369,10 +369,10 @@ Kernel2Sprite__scanline__14__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__14__DoDraw2 + bcs Kernel2Sprite__scanline__17__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__14__DoDraw2: +Kernel2Sprite__scanline__17__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y @@ -385,7 +385,7 @@ Kernel2Sprite__scanline__14__DoDraw2: ;;; end action Kernel2Sprite 2 scanline dey ; next scanline - bne Kernel2Sprite__kernel__12__LVScan ; repeat until out of lines + bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines ;;; end action Kernel2Sprite 2 kernel @@ -413,10 +413,10 @@ Kernel2Sprite__scanline__14__DoDraw2: ;;; start action Joystick 3 postframe ldx #0 -Joystick__postframe__17____each: +Joystick__postframe__21____each: asl Joystick__3__tmp+0 - bcs Joystick__postframe__17__SkipMoveRight + bcs Joystick__postframe__21__SkipMoveRight .code ;;; start action MoveJoyX 4 joyright @@ -425,15 +425,15 @@ Joystick__postframe__17____each: clc adc #1 cmp #152 - bcs MoveJoyX__joyright__18__nomove + bcs MoveJoyX__joyright__22__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyright__18__nomove: +MoveJoyX__joyright__22__nomove: ;;; end action MoveJoyX 4 joyright -Joystick__postframe__17__SkipMoveRight: +Joystick__postframe__21__SkipMoveRight: asl Joystick__3__tmp+0 - bcs Joystick__postframe__17__SkipMoveLeft + bcs Joystick__postframe__21__SkipMoveLeft .code ;;; start action MoveJoyX 4 joyleft @@ -441,15 +441,15 @@ Joystick__postframe__17__SkipMoveRight: lda HasXpos_xpos_b0,x sec sbc #1 - bcc MoveJoyX__joyleft__19__nomove + bcc MoveJoyX__joyleft__23__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyleft__19__nomove: +MoveJoyX__joyleft__23__nomove: ;;; end action MoveJoyX 4 joyleft -Joystick__postframe__17__SkipMoveLeft: +Joystick__postframe__21__SkipMoveLeft: asl Joystick__3__tmp+0 - bcs Joystick__postframe__17__SkipMoveDown + bcs Joystick__postframe__21__SkipMoveDown .code ;;; start action MoveJoyY 5 joydown @@ -458,15 +458,15 @@ Joystick__postframe__17__SkipMoveLeft: clc adc #1 cmp #220 - bcs MoveJoyY__joydown__20__nomove + bcs MoveJoyY__joydown__24__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joydown__20__nomove: +MoveJoyY__joydown__24__nomove: ;;; end action MoveJoyY 5 joydown -Joystick__postframe__17__SkipMoveDown: +Joystick__postframe__21__SkipMoveDown: asl Joystick__3__tmp+0 - bcs Joystick__postframe__17__SkipMoveUp + bcs Joystick__postframe__21__SkipMoveUp .code ;;; start action MoveJoyY 5 joyup @@ -474,18 +474,18 @@ Joystick__postframe__17__SkipMoveDown: lda HasYpos_ypos_b0,x sec sbc #1 - bcc MoveJoyY__joyup__21__nomove + bcc MoveJoyY__joyup__25__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joyup__21__nomove: +MoveJoyY__joyup__25__nomove: ;;; end action MoveJoyY 5 joyup -Joystick__postframe__17__SkipMoveUp: +Joystick__postframe__21__SkipMoveUp: inx cpx #4 - jne Joystick__postframe__17____each -Joystick__postframe__17____exit: + jne Joystick__postframe__21____each +Joystick__postframe__21____exit: ;;; end action Joystick 3 postframe @@ -498,12 +498,12 @@ Joystick__postframe__17____exit: sta SpriteShuffler__8__tmp+1 ; move two slots to the left ldx #0 -SpriteShuffler__postframe__22__loop: +SpriteShuffler__postframe__26__loop: lda SpriteSlot_sprite_b0+2,x sta SpriteSlot_sprite_b0,x inx cpx #4-2 - bne SpriteShuffler__postframe__22__loop + bne SpriteShuffler__postframe__26__loop ; store two sprite slots at right side of array lda SpriteShuffler__8__tmp+0 sta SpriteSlot_sprite_b0+4-2 @@ -522,12 +522,12 @@ SpriteShuffler__postframe__22__loop: ;;; start action SpriteHider 9 postframe ldy #0 -SpriteHider__postframe__24____each: +SpriteHider__postframe__28____each: ldx SpriteSlot_sprite_b0,y lda HasYpos_ypos_b0,x cmp #192 - bcc SpriteHider__postframe__24__skip + bcc SpriteHider__postframe__28__skip ; swap this sprite slot with slot at end of array lda SpriteSlot_sprite_b0,y pha @@ -537,12 +537,12 @@ SpriteHider__postframe__24____each: pla sta SpriteSlot_sprite_b0,x dec SpriteHider__9__tmp+0 -SpriteHider__postframe__24__skip: +SpriteHider__postframe__28__skip: iny cpy #2 - jne SpriteHider__postframe__24____each -SpriteHider__postframe__24____exit: + jne SpriteHider__postframe__28____each +SpriteHider__postframe__28____exit: ;;; end action SpriteHider 9 postframe diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index d67ece2f..0022430d 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -234,7 +234,7 @@ Kernel2Sprite__preframe__4____exit: ;;; start action SetXPos 6 preframe ldy #0 -SetXPos__preframe__8____each: +SetXPos__preframe__9____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x @@ -248,9 +248,9 @@ SetXPos__preframe__8____each: sta WSYNC ; start a new line sec ; set carry flag nop -SetHorizPos__SetHorizPos__9__DivideLoop: +SetHorizPos__SetHorizPos__10__DivideLoop: sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__9__DivideLoop ; branch until negative + bcs SetHorizPos__SetHorizPos__10__DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl @@ -264,8 +264,8 @@ SetHorizPos__SetHorizPos__9__DivideLoop: iny cpy #2 - jne SetXPos__preframe__8____each -SetXPos__preframe__8____exit: + jne SetXPos__preframe__9____each +SetXPos__preframe__9____exit: ;;; end action SetXPos 6 preframe @@ -295,7 +295,7 @@ SetXPos__preframe__8____exit: ;;; start action Kernel2Sprite 2 kernel ; define macro for each line - .macro Kernel2Sprite__kernel__12__DrawLine do_wsync + .macro Kernel2Sprite__kernel__14__DrawLine do_wsync .local DoDraw1 .local DoDraw2 ; draw player 0 @@ -326,20 +326,20 @@ DoDraw2: .endmacro ldy #192 -Kernel2Sprite__kernel__12__LVScan: +Kernel2Sprite__kernel__14__LVScan: .code ;;; start action Kernel2Sprite 2 scanline1 ;;; end action Kernel2Sprite 2 scanline1 - Kernel2Sprite__kernel__12__DrawLine 1 ; macro: draw scanline w/ WSYNC + Kernel2Sprite__kernel__14__DrawLine 1 ; macro: draw scanline w/ WSYNC dey ; next scanline .code - Kernel2Sprite__kernel__12__DrawLine 0 ; macro: draw scanline no WSYNC + Kernel2Sprite__kernel__14__DrawLine 0 ; macro: draw scanline no WSYNC dey ; next scanline - bne Kernel2Sprite__kernel__12__LVScan ; repeat until out of lines + bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines ;;; end action Kernel2Sprite 2 kernel @@ -367,10 +367,10 @@ Kernel2Sprite__kernel__12__LVScan: ;;; start action Joystick 3 postframe ldx #0 -Joystick__postframe__15____each: +Joystick__postframe__18____each: asl Joystick__3__tmp+0 - bcs Joystick__postframe__15__SkipMoveRight + bcs Joystick__postframe__18__SkipMoveRight .code ;;; start action MoveJoyX 4 joyright @@ -379,15 +379,15 @@ Joystick__postframe__15____each: clc adc #1 cmp #152 - bcs MoveJoyX__joyright__16__nomove + bcs MoveJoyX__joyright__19__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyright__16__nomove: +MoveJoyX__joyright__19__nomove: ;;; end action MoveJoyX 4 joyright -Joystick__postframe__15__SkipMoveRight: +Joystick__postframe__18__SkipMoveRight: asl Joystick__3__tmp+0 - bcs Joystick__postframe__15__SkipMoveLeft + bcs Joystick__postframe__18__SkipMoveLeft .code ;;; start action MoveJoyX 4 joyleft @@ -395,15 +395,15 @@ Joystick__postframe__15__SkipMoveRight: lda HasXpos_xpos_b0,x sec sbc #1 - bcc MoveJoyX__joyleft__17__nomove + bcc MoveJoyX__joyleft__20__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyleft__17__nomove: +MoveJoyX__joyleft__20__nomove: ;;; end action MoveJoyX 4 joyleft -Joystick__postframe__15__SkipMoveLeft: +Joystick__postframe__18__SkipMoveLeft: asl Joystick__3__tmp+0 - bcs Joystick__postframe__15__SkipMoveDown + bcs Joystick__postframe__18__SkipMoveDown .code ;;; start action MoveJoyY 5 joydown @@ -412,15 +412,15 @@ Joystick__postframe__15__SkipMoveLeft: clc adc #1 cmp #220 - bcs MoveJoyY__joydown__18__nomove + bcs MoveJoyY__joydown__21__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joydown__18__nomove: +MoveJoyY__joydown__21__nomove: ;;; end action MoveJoyY 5 joydown -Joystick__postframe__15__SkipMoveDown: +Joystick__postframe__18__SkipMoveDown: asl Joystick__3__tmp+0 - bcs Joystick__postframe__15__SkipMoveUp + bcs Joystick__postframe__18__SkipMoveUp .code ;;; start action MoveJoyY 5 joyup @@ -428,18 +428,18 @@ Joystick__postframe__15__SkipMoveDown: lda HasYpos_ypos_b0,x sec sbc #1 - bcc MoveJoyY__joyup__19__nomove + bcc MoveJoyY__joyup__22__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joyup__19__nomove: +MoveJoyY__joyup__22__nomove: ;;; end action MoveJoyY 5 joyup -Joystick__postframe__15__SkipMoveUp: +Joystick__postframe__18__SkipMoveUp: inx cpx #4 - jne Joystick__postframe__15____each -Joystick__postframe__15____exit: + jne Joystick__postframe__18____each +Joystick__postframe__18____exit: ;;; end action Joystick 3 postframe @@ -452,12 +452,12 @@ Joystick__postframe__15____exit: sta SpriteShuffler__8__tmp+1 ; move two slots to the left ldx #0 -SpriteShuffler__postframe__20__loop: +SpriteShuffler__postframe__23__loop: lda SpriteSlot_sprite_b0+2,x sta SpriteSlot_sprite_b0,x inx cpx #4-2 - bne SpriteShuffler__postframe__20__loop + bne SpriteShuffler__postframe__23__loop ; store two sprite slots at right side of array lda SpriteShuffler__8__tmp+0 sta SpriteSlot_sprite_b0+4-2 @@ -476,12 +476,12 @@ SpriteShuffler__postframe__20__loop: ;;; start action SpriteHider 9 postframe ldy #0 -SpriteHider__postframe__22____each: +SpriteHider__postframe__25____each: ldx SpriteSlot_sprite_b0,y lda HasYpos_ypos_b0,x cmp #192 - bcc SpriteHider__postframe__22__skip + bcc SpriteHider__postframe__25__skip ; swap this sprite slot with slot at end of array lda SpriteSlot_sprite_b0,y pha @@ -491,12 +491,12 @@ SpriteHider__postframe__22____each: pla sta SpriteSlot_sprite_b0,x dec SpriteHider__9__tmp+0 -SpriteHider__postframe__22__skip: +SpriteHider__postframe__25__skip: iny cpy #2 - jne SpriteHider__postframe__22____each -SpriteHider__postframe__22____exit: + jne SpriteHider__postframe__25____each +SpriteHider__postframe__25____exit: ;;; end action SpriteHider 9 postframe diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index af4c5257..1f41dd4a 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -761,7 +761,7 @@ Kernel2Sprite__preframe__4____exit: ;;; start action SetXPos 8 preframe ldy #0 -SetXPos__preframe__8____each: +SetXPos__preframe__9____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x @@ -775,9 +775,9 @@ SetXPos__preframe__8____each: sta WSYNC ; start a new line sec ; set carry flag nop -SetHorizPos__SetHorizPos__9__DivideLoop: +SetHorizPos__SetHorizPos__10__DivideLoop: sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__9__DivideLoop ; branch until negative + bcs SetHorizPos__SetHorizPos__10__DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl @@ -791,8 +791,8 @@ SetHorizPos__SetHorizPos__9__DivideLoop: iny cpy #2 - jne SetXPos__preframe__8____each -SetXPos__preframe__8____exit: + jne SetXPos__preframe__9____each +SetXPos__preframe__9____exit: ;;; end action SetXPos 8 preframe @@ -834,7 +834,7 @@ SetXPos__preframe__8____exit: ;;; start action Kernel2Sprite 2 kernel ldy #192 -Kernel2Sprite__kernel__13__LVScan: +Kernel2Sprite__kernel__15__LVScan: .code ;;; start action Kernel2Sprite 2 scanline @@ -842,10 +842,10 @@ Kernel2Sprite__kernel__13__LVScan: ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__14__DoDraw1 + bcs Kernel2Sprite__scanline__16__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__14__DoDraw1: +Kernel2Sprite__scanline__16__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 0 = 0 sta WSYNC @@ -856,10 +856,10 @@ Kernel2Sprite__scanline__14__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__14__DoDraw2 + bcs Kernel2Sprite__scanline__16__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__14__DoDraw2: +Kernel2Sprite__scanline__16__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y @@ -897,10 +897,10 @@ Kernel2Sprite__scanline__14__DoDraw2: ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__17__DoDraw1 + bcs Kernel2Sprite__scanline__20__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__17__DoDraw1: +Kernel2Sprite__scanline__20__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 1 = 0 sta WSYNC @@ -911,10 +911,10 @@ Kernel2Sprite__scanline__17__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__17__DoDraw2 + bcs Kernel2Sprite__scanline__20__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__17__DoDraw2: +Kernel2Sprite__scanline__20__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y @@ -945,7 +945,7 @@ Kernel2Sprite__scanline__17__DoDraw2: ;;; end action VersatilePlayfield 10 scanline dey ; next scanline - bne Kernel2Sprite__kernel__13__LVScan ; repeat until out of lines + bne Kernel2Sprite__kernel__15__LVScan ; repeat until out of lines ;;; end action Kernel2Sprite 2 kernel @@ -973,7 +973,7 @@ Kernel2Sprite__scanline__17__DoDraw2: ;;; start action Joystick 3 postframe asl Joystick__3__tmp+0 - bcs Joystick__postframe__22__SkipMoveRight + bcs Joystick__postframe__26__SkipMoveRight .code ;;; start action JoyFaceDirection 4 joyright @@ -990,7 +990,7 @@ Kernel2Sprite__scanline__17__DoDraw2: clc adc #2 cmp #142 - jcc SuperFly__joyright__24__nomove + jcc SuperFly__joyright__28__nomove .code ;;; start action SuperFly 5 goeast @@ -1002,14 +1002,14 @@ Kernel2Sprite__scanline__17__DoDraw2: ;;; end action SuperFly 5 goeast lda #2 -SuperFly__joyright__24__nomove: +SuperFly__joyright__28__nomove: sta HasXpos_xpos_b0 ;;; end action SuperFly 5 joyright -Joystick__postframe__22__SkipMoveRight: +Joystick__postframe__26__SkipMoveRight: asl Joystick__3__tmp+0 - bcs Joystick__postframe__22__SkipMoveLeft + bcs Joystick__postframe__26__SkipMoveLeft .code ;;; start action JoyFaceDirection 4 joyleft @@ -1025,7 +1025,7 @@ Joystick__postframe__22__SkipMoveRight: lda HasXpos_xpos_b0 sec sbc #2 - jcs SuperFly__joyleft__27__nomove + jcs SuperFly__joyleft__31__nomove .code ;;; start action SuperFly 5 gowest @@ -1037,14 +1037,14 @@ Joystick__postframe__22__SkipMoveRight: ;;; end action SuperFly 5 gowest lda #142 -SuperFly__joyleft__27__nomove: +SuperFly__joyleft__31__nomove: sta HasXpos_xpos_b0 ;;; end action SuperFly 5 joyleft -Joystick__postframe__22__SkipMoveLeft: +Joystick__postframe__26__SkipMoveLeft: asl Joystick__3__tmp+0 - bcs Joystick__postframe__22__SkipMoveDown + bcs Joystick__postframe__26__SkipMoveDown .code ;;; start action SuperFly 5 joydown @@ -1053,7 +1053,7 @@ Joystick__postframe__22__SkipMoveLeft: clc adc #2 cmp #220 - jcc SuperFly__joydown__29__nomove + jcc SuperFly__joydown__33__nomove .code ;;; start action SuperFly 5 gosouth @@ -1065,14 +1065,14 @@ Joystick__postframe__22__SkipMoveLeft: ;;; end action SuperFly 5 gosouth lda #2 -SuperFly__joydown__29__nomove: +SuperFly__joydown__33__nomove: sta HasYpos_ypos_b0 ;;; end action SuperFly 5 joydown -Joystick__postframe__22__SkipMoveDown: +Joystick__postframe__26__SkipMoveDown: asl Joystick__3__tmp+0 - bcs Joystick__postframe__22__SkipMoveUp + bcs Joystick__postframe__26__SkipMoveUp .code ;;; start action SuperFly 5 joyup @@ -1080,7 +1080,7 @@ Joystick__postframe__22__SkipMoveDown: lda HasYpos_ypos_b0 sec sbc #2 - jcs SuperFly__joyup__31__nomove + jcs SuperFly__joyup__35__nomove .code ;;; start action SuperFly 5 gonorth @@ -1092,19 +1092,19 @@ Joystick__postframe__22__SkipMoveDown: ;;; end action SuperFly 5 gonorth lda #200 -SuperFly__joyup__31__nomove: +SuperFly__joyup__35__nomove: sta HasYpos_ypos_b0 ;;; end action SuperFly 5 joyup -Joystick__postframe__22__SkipMoveUp: +Joystick__postframe__26__SkipMoveUp: ;;; end action Joystick 3 postframe ;;; start action BadMove 6 postframe ldx #0 -BadMove__postframe__33____each: +BadMove__postframe__37____each: .code @@ -1122,7 +1122,7 @@ BadMove__postframe__33____each: clc adc #1 cmp #142 - jcc SuperFly__joyright__35__nomove + jcc SuperFly__joyright__39__nomove .code ;;; start action SuperFly 5 goeast @@ -1134,7 +1134,7 @@ BadMove__postframe__33____each: ;;; end action SuperFly 5 goeast lda #2 -SuperFly__joyright__35__nomove: +SuperFly__joyright__39__nomove: sta HasXpos_xpos_b0+1,x ;;; end action SuperFly 5 joyright @@ -1142,8 +1142,8 @@ SuperFly__joyright__35__nomove: inx cpx #3 - jne BadMove__postframe__33____each -BadMove__postframe__33____exit: + jne BadMove__postframe__37____each +BadMove__postframe__37____exit: ;;; end action BadMove 6 postframe @@ -1151,22 +1151,22 @@ BadMove__postframe__33____exit: ldy 4 ldx SpriteSlot_sprite_b0+1 - bmi RoomShuffle__postframe__37__empty ; empty slot, load 1st entry -RoomShuffle__postframe__37__loop: + bmi RoomShuffle__postframe__41__empty ; empty slot, load 1st entry +RoomShuffle__postframe__41__loop: inx cpx 4 - bcc RoomShuffle__postframe__37__norecycle + bcc RoomShuffle__postframe__41__norecycle ; TODO: need to get index of specific entity -RoomShuffle__postframe__37__empty: +RoomShuffle__postframe__41__empty: ldx #1 ; skip null sprite and super dude? -RoomShuffle__postframe__37__norecycle: +RoomShuffle__postframe__41__norecycle: lda Location_room_b0,x cmp Location_room_b0 - beq RoomShuffle__postframe__37__exit + beq RoomShuffle__postframe__41__exit dey - bne RoomShuffle__postframe__37__loop + bne RoomShuffle__postframe__41__loop ldx #$ff -RoomShuffle__postframe__37__exit: +RoomShuffle__postframe__41__exit: stx SpriteSlot_sprite_b0+1 ;;; end action RoomShuffle 7 postframe diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index 67482d52..11290f5d 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -198,7 +198,7 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel 3 kernel ldx #0 -StaticKernel__kernel__5____each: +StaticKernel__kernel__9____each: sta WSYNC .code @@ -206,10 +206,10 @@ StaticKernel__kernel__5____each: ;;; start action Kernel48Pixel 2 kernelsetup cpx #2+1 - jcs Kernel48Pixel__kernelsetup__6____skipxhi + jcs Kernel48Pixel__kernelsetup__10____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__6____skipxlo + jcc Kernel48Pixel__kernelsetup__10____skipxlo lda #14 sta Kernel48Pixel__2__tmp+0 ; scanline counter @@ -234,27 +234,27 @@ StaticKernel__kernel__5____each: sta VDELP0 ; we need the VDEL registers sta VDELP1 ; so we can do our 4-store trick -Kernel48Pixel__kernelsetup__6____skipxlo: +Kernel48Pixel__kernelsetup__10____skipxlo: -Kernel48Pixel__kernelsetup__6____skipxhi: +Kernel48Pixel__kernelsetup__10____skipxhi: ;;; end action Kernel48Pixel 2 kernelsetup ;;; start action Kernel48Pixel 2 kernelsetup cpx #2+1 - jcs Kernel48Pixel__kernelsetup__7____skipxhi + jcs Kernel48Pixel__kernelsetup__11____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__7____skipxlo + jcc Kernel48Pixel__kernelsetup__11____skipxlo lda #252 sta COLUP0 sta COLUP1 -Kernel48Pixel__kernelsetup__7____skipxlo: +Kernel48Pixel__kernelsetup__11____skipxlo: -Kernel48Pixel__kernelsetup__7____skipxhi: +Kernel48Pixel__kernelsetup__11____skipxhi: ;;; end action Kernel48Pixel 2 kernelsetup @@ -268,17 +268,17 @@ Kernel48Pixel__kernelsetup__7____skipxhi: ;;; start action StaticKernel 3 kernelsetup cpx #2+1 - jcs StaticKernel__kernelsetup__9____skipxhi + jcs StaticKernel__kernelsetup__13____skipxhi cpx #2 - jcc StaticKernel__kernelsetup__9____skipxlo + jcc StaticKernel__kernelsetup__13____skipxlo lda #252 sta COLUPF -StaticKernel__kernelsetup__9____skipxlo: +StaticKernel__kernelsetup__13____skipxlo: -StaticKernel__kernelsetup__9____skipxhi: +StaticKernel__kernelsetup__13____skipxhi: ;;; end action StaticKernel 3 kernelsetup @@ -291,14 +291,14 @@ StaticKernel__kernelsetup__9____skipxhi: ;;; start action Kernel48Pixel 2 kerneldraw cpx #2+1 - jcs Kernel48Pixel__kerneldraw__10____skipxhi + jcs Kernel48Pixel__kerneldraw__15____skipxhi cpx #2 - jcc Kernel48Pixel__kerneldraw__10____skipxlo + jcc Kernel48Pixel__kerneldraw__15____skipxlo txa pha -Kernel48Pixel__kerneldraw__10__Loop: +Kernel48Pixel__kerneldraw__15__Loop: ldy Kernel48Pixel__2__tmp+0 ; counts backwards sta WSYNC ; sync to next scanline lda Bitmap48_bitmap0_e2_b0,y ; load B0 (1st sprite byte) @@ -317,24 +317,24 @@ Kernel48Pixel__kerneldraw__10__Loop: sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec Kernel48Pixel__2__tmp+0 ; go to next line - bpl Kernel48Pixel__kerneldraw__10__Loop ; repeat until < 0 + bpl Kernel48Pixel__kerneldraw__15__Loop ; repeat until < 0 pla tax -Kernel48Pixel__kerneldraw__10____skipxlo: +Kernel48Pixel__kerneldraw__15____skipxlo: -Kernel48Pixel__kerneldraw__10____skipxhi: +Kernel48Pixel__kerneldraw__15____skipxhi: ;;; end action Kernel48Pixel 2 kerneldraw ;;; start action StaticKernel 3 kerneldraw ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__11__loop: +StaticKernel__kerneldraw__16__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__11__loop + bne StaticKernel__kerneldraw__16__loop ;;; end action StaticKernel 3 kerneldraw @@ -342,8 +342,8 @@ StaticKernel__kerneldraw__11__loop: inx cpx #5 - jne StaticKernel__kernel__5____each -StaticKernel__kernel__5____exit: + jne StaticKernel__kernel__9____each +StaticKernel__kernel__9____exit: ;;; end action StaticKernel 3 kernel diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index d5908d83..db7d7db8 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -97,7 +97,7 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel 4 kernel ldx #0 -StaticKernel__kernel__5____each: +StaticKernel__kernel__7____each: sta WSYNC .code @@ -112,24 +112,24 @@ StaticKernel__kernel__5____each: ;;; start action StaticKernel 4 kernelsetup cpx #5+2 - jcs StaticKernel__kernelsetup__7____skipxhi + jcs StaticKernel__kernelsetup__9____skipxhi cpx #5 - jcc StaticKernel__kernelsetup__7____skipxlo + jcc StaticKernel__kernelsetup__9____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF -StaticKernel__kernelsetup__7____skipxlo: +StaticKernel__kernelsetup__9____skipxlo: -StaticKernel__kernelsetup__7____skipxhi: +StaticKernel__kernelsetup__9____skipxhi: ;;; end action StaticKernel 4 kernelsetup ;;; start action StaticKernel 4 kernelsetup cpx #4 - jcc StaticKernel__kernelsetup__8____skipxlo + jcc StaticKernel__kernelsetup__10____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -138,22 +138,22 @@ StaticKernel__kernelsetup__7____skipxhi: lda Playfield_pf_b16-4,x sta PF2 -StaticKernel__kernelsetup__8____skipxlo: +StaticKernel__kernelsetup__10____skipxlo: ;;; end action StaticKernel 4 kernelsetup ldy KernelSection_lines_b0,x -StaticKernel__kernel__5__loop: +StaticKernel__kernel__7__loop: sta WSYNC dey - bne StaticKernel__kernel__5__loop + bne StaticKernel__kernel__7__loop inx cpx #8 - jne StaticKernel__kernel__5____each -StaticKernel__kernel__5____exit: + jne StaticKernel__kernel__7____each +StaticKernel__kernel__7____exit: ;;; end action StaticKernel 4 kernel @@ -163,7 +163,7 @@ StaticKernel__kernel__5____exit: ;;; start action JoyButton 5 postframe lda INPT4 ;read button input - bmi JoyButton__postframe__9__NotPressed + bmi JoyButton__postframe__11__NotPressed .code ;;; start action Local 6 joybutton @@ -173,7 +173,7 @@ StaticKernel__kernel__5____exit: ;;; end action Local 6 joybutton -JoyButton__postframe__9__NotPressed: +JoyButton__postframe__11__NotPressed: ;;; end action JoyButton 5 postframe @@ -183,7 +183,7 @@ JoyButton__postframe__9__NotPressed: ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch - bcs ResetSwitch__nextframe__11__NoStart + bcs ResetSwitch__nextframe__13__NoStart .code ;;; start action ResetConsole 3 resetswitch @@ -192,7 +192,7 @@ JoyButton__postframe__9__NotPressed: ;;; end action ResetConsole 3 resetswitch -ResetSwitch__nextframe__11__NoStart: +ResetSwitch__nextframe__13__NoStart: ;;; end action ResetSwitch 2 nextframe diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 9a7c50e6..1f58b892 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -98,7 +98,7 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel 4 kernel ldx #0 -StaticKernel__kernel__5____each: +StaticKernel__kernel__7____each: sta WSYNC .code @@ -113,24 +113,24 @@ StaticKernel__kernel__5____each: ;;; start action StaticKernel 4 kernelsetup cpx #5+2 - jcs StaticKernel__kernelsetup__7____skipxhi + jcs StaticKernel__kernelsetup__9____skipxhi cpx #5 - jcc StaticKernel__kernelsetup__7____skipxlo + jcc StaticKernel__kernelsetup__9____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF -StaticKernel__kernelsetup__7____skipxlo: +StaticKernel__kernelsetup__9____skipxlo: -StaticKernel__kernelsetup__7____skipxhi: +StaticKernel__kernelsetup__9____skipxhi: ;;; end action StaticKernel 4 kernelsetup ;;; start action StaticKernel 4 kernelsetup cpx #4 - jcc StaticKernel__kernelsetup__8____skipxlo + jcc StaticKernel__kernelsetup__10____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -139,7 +139,7 @@ StaticKernel__kernelsetup__7____skipxhi: lda Playfield_pf_b16-4,x sta PF2 -StaticKernel__kernelsetup__8____skipxlo: +StaticKernel__kernelsetup__10____skipxlo: ;;; end action StaticKernel 4 kernelsetup @@ -148,11 +148,11 @@ StaticKernel__kernelsetup__8____skipxlo: ;;; start action StaticKernel 4 kerneldraw ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__9__loop: +StaticKernel__kerneldraw__11__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__9__loop + bne StaticKernel__kerneldraw__11__loop ;;; end action StaticKernel 4 kerneldraw @@ -160,8 +160,8 @@ StaticKernel__kerneldraw__9__loop: inx cpx #8 - jne StaticKernel__kernel__5____each -StaticKernel__kernel__5____exit: + jne StaticKernel__kernel__7____each +StaticKernel__kernel__7____exit: ;;; end action StaticKernel 4 kernel @@ -171,7 +171,7 @@ StaticKernel__kernel__5____exit: ;;; start action JoyButton 5 postframe lda INPT4 ;read button input - bmi JoyButton__postframe__10__NotPressed + bmi JoyButton__postframe__12__NotPressed .code ;;; start action Local 6 joybutton @@ -181,7 +181,7 @@ StaticKernel__kernel__5____exit: ;;; end action Local 6 joybutton -JoyButton__postframe__10__NotPressed: +JoyButton__postframe__12__NotPressed: ;;; end action JoyButton 5 postframe @@ -191,7 +191,7 @@ JoyButton__postframe__10__NotPressed: ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch - bcs ResetSwitch__nextframe__12__NoStart + bcs ResetSwitch__nextframe__14__NoStart .code ;;; start action ResetConsole 3 resetswitch @@ -200,7 +200,7 @@ JoyButton__postframe__10__NotPressed: ;;; end action ResetConsole 3 resetswitch -ResetSwitch__nextframe__12__NoStart: +ResetSwitch__nextframe__14__NoStart: ;;; end action ResetSwitch 2 nextframe From d61e2391ce2fe42c30575ef88bec7304e87fa668 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 18 Feb 2022 12:27:13 -0600 Subject: [PATCH 071/104] ecs: highlighting changes --- src/codemirror/ecs.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index bf3696f9..8e504c53 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -31,14 +31,15 @@ ]; var directives = new Map(); - directives_list.forEach(function (s) { directives.set(s, 'def'); }); - keywords_list.forEach(function (s) { directives.set(s, 'keyword'); }); + directives_list.forEach(function (s) { directives.set(s, 'keyword'); }); + keywords_list.forEach(function (s) { directives.set(s, 'def'); }); var opcodes = /^\s[a-z][a-z][a-z]\s/i; - var numbers = /^(0x[\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; + var numbers = /^(0x[\da-f]+|[$][\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; var tags = /^\{\{.*\}\}/; - var comment = /\/\/.*/; - var mlcomment = /\/\*.*?\*\//s; // TODO + var comment = /^\/\/.*$/; + var mlcomment = /^\/\*.*?\*\//s; // TODO + var codedelim = /^---/; return { startState: function () { @@ -59,6 +60,10 @@ if (stream.match(mlcomment)) { return 'comment'; } + if (stream.match(codedelim)) { + state.context = state.context ^ 1; + return null; + } var w; if (stream.eatWhile(/\w/)) { @@ -68,13 +73,13 @@ if (style) return style; - if (opcodes.test(w)) { - return 'keyword'; - } else if (numbers.test(w)) { + if (numbers.test(w)) { return 'number'; } else if (w == 'comment') { stream.match(mlcomment); return 'comment'; + } else { + return state.context ? 'variable-2' : null; } } else if (stream.eat(';')) { stream.skipToEnd(); From a87dbf03cf544a7878a0ee658b38494614ccaf8a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 02:37:19 -0600 Subject: [PATCH 072/104] ecs: resources have their own labels, auto-find resources --- src/codemirror/ecs.js | 2 +- src/common/ecs/ecs.ts | 32 +++++++++++++++++++++----------- test/ecs/narrow1.txt | 1 + test/ecs/score.ecs | 3 --- test/ecs/score.txt | 20 ++++++++++++++++++-- test/ecs/sprites.txt | 13 +++++++++++++ test/ecs/sprites1.txt | 13 +++++++++++++ test/ecs/superman.txt | 19 +++++++++++++++++++ test/ecs/titles.txt | 9 +++++++++ test/ecs/vcs1.txt | 10 ++++++++++ test/ecs/vcslib.txt | 11 +++++++++++ 11 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 8e504c53..1a893b4c 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -17,7 +17,7 @@ var keywords1, keywords2; var directives_list = [ - 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', + 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', 'resource', 'const', 'locals', 'var', 'on', 'do', 'emit', 'limit', 'once', 'foreach', 'with', 'join', 'if', diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 6ffbce44..8324d94b 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -624,9 +624,12 @@ class ActionEval { this.label = `${this.instance.system.name}__${action.event}__${this.em.seq++}`; } begin() { - let state = this.scope.state = Object.assign({}, this.scope.state); + let state = this.scope.state = Object.assign(new ActionCPUState(), this.scope.state); // TODO: generalize to other cpus/langs switch (this.action.select) { + case 'once': + state.xreg = state.yreg = null; + break; case 'foreach': if (state.xreg && state.yreg) throw new ECSError('no more index registers', this.action); if (state.xreg) state.yreg = new IndexRegister(this.scope, this.qr); @@ -674,14 +677,12 @@ class ActionEval { if (this.entities.length == 0 && allowEmpty.includes(this.action.select)) return ''; - let action = this.action; - let sys = this.instance.system; - let { code, props } = this.getCodeAndProps(action); + let { code, props } = this.getCodeAndProps(this.action); // replace @labels code = this.replaceLabels(code, this.label); // replace {{...}} tags // TODO: use nodes instead - code = this.replaceTags(code, action, props); + code = this.replaceTags(code, this.action, props); return code; } private getCodeAndProps(action: Action) { @@ -839,8 +840,9 @@ class ActionEval { return `${this.tmplabel}+${tempinc}`; } __arg(args: string[]) { - let index = parseInt(args[0] || '0'); - return this.eventargs[index] || ''; + let argindex = parseInt(args[0] || '0'); + let argvalue = this.eventargs[argindex] || ''; + return argvalue; } __bss_init(args: string[]) { return this.scope.allocateInitData(this.scope.bss); @@ -1042,6 +1044,9 @@ export class EntityScope implements SourceLocated { newSystemInstanceWithDefaults(system: System) { return this.newSystemInstance({ system, params: {}, id:0 }); } + getSystemInstanceNamed(name: string) { + return this.instances.find(sys => sys.system.name == name); + } getEntityByName(name: string) { return this.entities.find(e => e.name == name); } @@ -1259,7 +1264,7 @@ export class EntityScope implements SourceLocated { isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' { return this.fieldtypes[mksymbol(component, fieldName)]; } - generateCodeForEvent(event: string, args?: string[]): string { + generateCodeForEvent(event: string, args?: string[], codelabel?: string): string { // find systems that respond to event // and have entities in this scope let systems = this.em.event2systems[event]; @@ -1271,8 +1276,8 @@ export class EntityScope implements SourceLocated { } this.eventSeq++; // generate code - let code = this.dialect.code(); - //s += `\n; event ${event}\n`; + let code = this.dialect.code() + '\n'; + if (codelabel) { code += this.dialect.label(codelabel) + '\n'; } let eventCount = 0; let instances = this.instances.filter(inst => systems.includes(inst.system)); for (let inst of instances) { @@ -1384,7 +1389,12 @@ export class EntityScope implements SourceLocated { } this.code.addCodeFragment(start); for (let sub of Array.from(this.resources.values())) { - let code = this.generateCodeForEvent(sub); + if (!this.getSystemInstanceNamed(sub)) { + let sys = this.em.getSystemByName(sub); + if (!sys) throw new ECSError(`cannot find resource named "${sub}"`); + this.newSystemInstanceWithDefaults(sys); + } + let code = this.generateCodeForEvent(sub, [], sub); this.code.addCodeFragment(code); } //this.showStats(); diff --git a/test/ecs/narrow1.txt b/test/ecs/narrow1.txt index 0f91204b..4aef92c2 100644 --- a/test/ecs/narrow1.txt +++ b/test/ecs/narrow1.txt @@ -9,6 +9,7 @@ Xpos_x_b0: __Start: .code + ;;; start action move 1 start ldx #0 diff --git a/test/ecs/score.ecs b/test/ecs/score.ecs index b3ae2924..ec467f77 100644 --- a/test/ecs/score.ecs +++ b/test/ecs/score.ecs @@ -137,7 +137,6 @@ end resource FontTable --- ; Font table for digits 0-9 (8x8 pixels) -FontTable: ;;{w:8,h:8,count:10,brev:1,flip:1};; .byte $00,$3c,$66,$66,$76,$6e,$66,$3c,$00,$7e,$18,$18,$18,$38,$18,$18 .byte $00,$7e,$60,$30,$0c,$06,$66,$3c,$00,$3c,$66,$06,$1c,$06,$66,$3c @@ -211,7 +210,6 @@ end resource FontTablePF --- ; Font table for digits 0-9 (4x8 pixels) -FontTablePF: ;;{w:8,h:8,count:10,brev:1,flip:1};; .byte $00,$00,$EE,$AA,$AA,$AA,$EE,$00 .byte $00,$00,$22,$22,$22,$22,$22,$00 @@ -228,7 +226,6 @@ FontTablePF: resource FontTablePFFancy --- ; Font table for digits 0-9 (4x8 pixels) -FontTablePFFancy: ;;{w:8,h:8,count:10,brev:1,flip:1};; .byte $00,$44,$AA,$AA,$AA,$AA,$AA,$44 .byte $00,$EE,$44,$44,$44,$44,$CC,$44 diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 8bd03d7f..3151d0b1 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -50,6 +50,7 @@ Main__INITDATA: __Start: .code + ;;; start action Init 9 main_init .include "vcs-ca65.h" @@ -68,12 +69,14 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action Kernel6Digit 2 preframe Digit0 = Kernel6Digit__2__tmp+0 @@ -127,6 +130,7 @@ Kernel6Digit__preframe__3__Loop: KERNEL_START .code + ;;; start action Kernel6Digit 2 kernel lda PFColor_pfcolor_b0 @@ -220,6 +224,7 @@ Kernel2Digit__kernel__7__Loop: sta WSYNC .code + ;;; start action Kernel2Digit 4 compute2digit lda Kernel2Digit__4__tmp+1 ; load 1st pf @@ -233,6 +238,7 @@ Kernel2Digit__kernel__7__Loop: asl .code + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -252,6 +258,7 @@ Kernel2Digit__kernel__7__Loop: sty PF1 ; store 2nd pf .code + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -270,6 +277,7 @@ Kernel2Digit__kernel__7__Loop: inx .code + ;;; start action Kernel2Digit 4 compute2digit lda Kernel2Digit__4__tmp+1 ; load 1st pf @@ -283,6 +291,7 @@ Kernel2Digit__kernel__7__Loop: asl .code + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -302,6 +311,7 @@ Kernel2Digit__kernel__7__Loop: sty PF1 ; store 2nd pf .code + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -328,16 +338,19 @@ Kernel2Digit__kernel__7__Loop: KERNEL_END .code + ;;; start action JoyButton 6 postframe lda INPT4 ;read button input bmi JoyButton__postframe__14__NotPressed .code + ;;; start action IncScore 8 joybutton .code + ;;; start action BCDMath 7 AddBCD4 .ifnblank $0210 @@ -371,6 +384,7 @@ JoyButton__postframe__14__NotPressed: FRAME_END .code + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start @@ -385,10 +399,11 @@ VecBRK: .word Main::__BRK .code +FontTable: + ;;; start action FontTable 3 FontTable ; Font table for digits 0-9 (8x8 pixels) -FontTable: ;;{w:8,h:8,count:10,brev:1,flip:1};; .byte $00,$3c,$66,$66,$76,$6e,$66,$3c,$00,$7e,$18,$18,$18,$38,$18,$18 .byte $00,$7e,$60,$30,$0c,$06,$66,$3c,$00,$3c,$66,$06,$1c,$06,$66,$3c @@ -400,10 +415,11 @@ FontTable: .code +FontTablePF: + ;;; start action FontTablePF 5 FontTablePF ; Font table for digits 0-9 (4x8 pixels) -FontTablePF: ;;{w:8,h:8,count:10,brev:1,flip:1};; .byte $00,$00,$EE,$AA,$AA,$AA,$EE,$00 .byte $00,$00,$22,$22,$22,$22,$22,$00 diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index 04821ad8..a75688af 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -117,6 +117,7 @@ Main__INITDATA: __Start: .code + ;;; start action Init 10 main_init .include "vcs-ca65.h" @@ -135,12 +136,14 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action Kernel2Sprite 2 preframe .define KLINES #192 @@ -255,6 +258,7 @@ SetXPos__preframe__9____each: lda HasXpos_xpos_b0,x .code + ;;; start action SetHorizPos 7 SetHorizPos ; SetHorizPos routine @@ -298,6 +302,7 @@ SetXPos__preframe__9____exit: KERNEL_START .code + ;;; start action Kernel2Sprite 2 kernel ldy #0 @@ -313,6 +318,7 @@ SetXPos__preframe__9____exit: Kernel2Sprite__kernel__14__LVScan: .code + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -350,6 +356,7 @@ Kernel2Sprite__scanline__15__DoDraw2: dey ; next scanline .code + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -402,6 +409,7 @@ Kernel2Sprite__scanline__17__DoDraw2: KERNEL_END .code + ;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each @@ -419,6 +427,7 @@ Joystick__postframe__21____each: bcs Joystick__postframe__21__SkipMoveRight .code + ;;; start action MoveJoyX 4 joyright lda HasXpos_xpos_b0,x @@ -436,6 +445,7 @@ Joystick__postframe__21__SkipMoveRight: bcs Joystick__postframe__21__SkipMoveLeft .code + ;;; start action MoveJoyX 4 joyleft lda HasXpos_xpos_b0,x @@ -452,6 +462,7 @@ Joystick__postframe__21__SkipMoveLeft: bcs Joystick__postframe__21__SkipMoveDown .code + ;;; start action MoveJoyY 5 joydown lda HasYpos_ypos_b0,x @@ -469,6 +480,7 @@ Joystick__postframe__21__SkipMoveDown: bcs Joystick__postframe__21__SkipMoveUp .code + ;;; start action MoveJoyY 5 joyup lda HasYpos_ypos_b0,x @@ -549,6 +561,7 @@ SpriteHider__postframe__28____exit: FRAME_END .code + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 0022430d..fec16295 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -117,6 +117,7 @@ Main__INITDATA: __Start: .code + ;;; start action Init 10 main_init .include "vcs-ca65.h" @@ -135,12 +136,14 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action Kernel2Sprite 2 preframe .define KLINES #192 @@ -240,6 +243,7 @@ SetXPos__preframe__9____each: lda HasXpos_xpos_b0,x .code + ;;; start action SetHorizPos 7 SetHorizPos ; SetHorizPos routine @@ -283,6 +287,7 @@ SetXPos__preframe__9____exit: KERNEL_START .code + ;;; start action Kernel2Sprite 2 kernel ldy #0 @@ -329,6 +334,7 @@ DoDraw2: Kernel2Sprite__kernel__14__LVScan: .code + ;;; start action Kernel2Sprite 2 scanline1 ;;; end action Kernel2Sprite 2 scanline1 @@ -337,6 +343,7 @@ Kernel2Sprite__kernel__14__LVScan: dey ; next scanline .code + Kernel2Sprite__kernel__14__DrawLine 0 ; macro: draw scanline no WSYNC dey ; next scanline bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines @@ -356,6 +363,7 @@ Kernel2Sprite__kernel__14__LVScan: KERNEL_END .code + ;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each @@ -373,6 +381,7 @@ Joystick__postframe__18____each: bcs Joystick__postframe__18__SkipMoveRight .code + ;;; start action MoveJoyX 4 joyright lda HasXpos_xpos_b0,x @@ -390,6 +399,7 @@ Joystick__postframe__18__SkipMoveRight: bcs Joystick__postframe__18__SkipMoveLeft .code + ;;; start action MoveJoyX 4 joyleft lda HasXpos_xpos_b0,x @@ -406,6 +416,7 @@ Joystick__postframe__18__SkipMoveLeft: bcs Joystick__postframe__18__SkipMoveDown .code + ;;; start action MoveJoyY 5 joydown lda HasYpos_ypos_b0,x @@ -423,6 +434,7 @@ Joystick__postframe__18__SkipMoveDown: bcs Joystick__postframe__18__SkipMoveUp .code + ;;; start action MoveJoyY 5 joyup lda HasYpos_ypos_b0,x @@ -503,6 +515,7 @@ SpriteHider__postframe__25____exit: FRAME_END .code + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index 1f41dd4a..c236fb13 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -629,6 +629,7 @@ Main__INITDATA: __Start: .code + ;;; start action Init 11 main_init .include "vcs-ca65.h" @@ -647,12 +648,14 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action Kernel2Sprite 2 preframe .define KLINES #192 @@ -767,6 +770,7 @@ SetXPos__preframe__9____each: lda HasXpos_xpos_b0,x .code + ;;; start action SetHorizPos 9 SetHorizPos ; SetHorizPos routine @@ -822,6 +826,7 @@ SetXPos__preframe__9____exit: KERNEL_START .code + ;;; start action Kernel2Sprite 2 kernel ldy #0 @@ -837,6 +842,7 @@ SetXPos__preframe__9____exit: Kernel2Sprite__kernel__15__LVScan: .code + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -892,6 +898,7 @@ Kernel2Sprite__scanline__16__DoDraw2: dey ; next scanline .code + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -962,6 +969,7 @@ Kernel2Sprite__scanline__20__DoDraw2: KERNEL_END .code + ;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each @@ -976,6 +984,7 @@ Kernel2Sprite__scanline__20__DoDraw2: bcs Joystick__postframe__26__SkipMoveRight .code + ;;; start action JoyFaceDirection 4 joyright lda Sprite_plyrflags_b0 @@ -993,6 +1002,7 @@ Kernel2Sprite__scanline__20__DoDraw2: jcc SuperFly__joyright__28__nomove .code + ;;; start action SuperFly 5 goeast ldy Location_room_b0 @@ -1012,6 +1022,7 @@ Joystick__postframe__26__SkipMoveRight: bcs Joystick__postframe__26__SkipMoveLeft .code + ;;; start action JoyFaceDirection 4 joyleft lda Sprite_plyrflags_b0 @@ -1028,6 +1039,7 @@ Joystick__postframe__26__SkipMoveRight: jcs SuperFly__joyleft__31__nomove .code + ;;; start action SuperFly 5 gowest ldy Location_room_b0 @@ -1047,6 +1059,7 @@ Joystick__postframe__26__SkipMoveLeft: bcs Joystick__postframe__26__SkipMoveDown .code + ;;; start action SuperFly 5 joydown lda HasYpos_ypos_b0 @@ -1056,6 +1069,7 @@ Joystick__postframe__26__SkipMoveLeft: jcc SuperFly__joydown__33__nomove .code + ;;; start action SuperFly 5 gosouth ldy Location_room_b0 @@ -1075,6 +1089,7 @@ Joystick__postframe__26__SkipMoveDown: bcs Joystick__postframe__26__SkipMoveUp .code + ;;; start action SuperFly 5 joyup lda HasYpos_ypos_b0 @@ -1083,6 +1098,7 @@ Joystick__postframe__26__SkipMoveDown: jcs SuperFly__joyup__35__nomove .code + ;;; start action SuperFly 5 gonorth ldy Location_room_b0 @@ -1108,6 +1124,7 @@ BadMove__postframe__37____each: .code + ;;; start action JoyFaceDirection 4 joyright lda Sprite_plyrflags_b0+1,x @@ -1125,6 +1142,7 @@ BadMove__postframe__37____each: jcc SuperFly__joyright__39__nomove .code + ;;; start action SuperFly 5 goeast ldy Location_room_b0+1,x @@ -1183,6 +1201,7 @@ RoomShuffle__postframe__41__exit: FRAME_END .code + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index 11290f5d..f3011e68 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -144,6 +144,7 @@ Bitmap48_height_b0: __Start: .code + ;;; start action Init 4 main_init .include "vcs-ca65.h" @@ -156,16 +157,19 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action StaticKernel 3 preframe .code + ;;; start action Kernel48Pixel 2 kernelsetup ;;; end action Kernel48Pixel 2 kernelsetup @@ -195,6 +199,7 @@ FrameLoop__start__2__NextFrame: KERNEL_START .code + ;;; start action StaticKernel 3 kernel ldx #0 @@ -203,6 +208,7 @@ StaticKernel__kernel__9____each: sta WSYNC .code + ;;; start action Kernel48Pixel 2 kernelsetup cpx #2+1 @@ -288,6 +294,7 @@ StaticKernel__kernelsetup__13____skipxhi: .code + ;;; start action Kernel48Pixel 2 kerneldraw cpx #2+1 @@ -350,9 +357,11 @@ StaticKernel__kernel__9____exit: KERNEL_END .code + FRAME_END .code + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index db7d7db8..3dc89aae 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -46,6 +46,7 @@ Main__INITDATA: __Start: .code + ;;; start action Init 7 main_init .include "vcs-ca65.h" @@ -63,16 +64,19 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action StaticKernel 4 preframe .code + ;;; start action StaticKernel 4 kernelsetup lda #24 @@ -94,6 +98,7 @@ FrameLoop__start__2__NextFrame: KERNEL_START .code + ;;; start action StaticKernel 4 kernel ldx #0 @@ -102,6 +107,7 @@ StaticKernel__kernel__7____each: sta WSYNC .code + ;;; start action StaticKernel 4 kernelsetup lda BGColor_bgcolor_b0,x @@ -160,12 +166,14 @@ StaticKernel__kernel__7____exit: KERNEL_END .code + ;;; start action JoyButton 5 postframe lda INPT4 ;read button input bmi JoyButton__postframe__11__NotPressed .code + ;;; start action Local 6 joybutton inc Local__6__tmp+0 @@ -180,12 +188,14 @@ JoyButton__postframe__11__NotPressed: FRAME_END .code + ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__13__NoStart .code + ;;; start action ResetConsole 3 resetswitch jmp Main::__Reset ; jump to Reset handler diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 1f58b892..4b73be8e 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -46,6 +46,7 @@ Main__INITDATA: __Start: .code + ;;; start action Init 7 main_init .include "vcs-ca65.h" @@ -64,16 +65,19 @@ __BRK: ; initialize data segment .code + ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START .code + ;;; start action StaticKernel 4 preframe .code + ;;; start action StaticKernel 4 kernelsetup lda #24 @@ -95,6 +99,7 @@ FrameLoop__start__2__NextFrame: KERNEL_START .code + ;;; start action StaticKernel 4 kernel ldx #0 @@ -103,6 +108,7 @@ StaticKernel__kernel__7____each: sta WSYNC .code + ;;; start action StaticKernel 4 kernelsetup lda BGColor_bgcolor_b0,x @@ -145,6 +151,7 @@ StaticKernel__kernelsetup__10____skipxlo: .code + ;;; start action StaticKernel 4 kerneldraw ldy KernelSection_lines_b0,x @@ -168,12 +175,14 @@ StaticKernel__kernel__7____exit: KERNEL_END .code + ;;; start action JoyButton 5 postframe lda INPT4 ;read button input bmi JoyButton__postframe__12__NotPressed .code + ;;; start action Local 6 joybutton inc Local__6__tmp+0 @@ -188,12 +197,14 @@ JoyButton__postframe__12__NotPressed: FRAME_END .code + ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__14__NoStart .code + ;;; start action ResetConsole 3 resetswitch jmp Main::__Reset ; jump to Reset handler From 349c45d7c088ec6d1bf3e9d6d8f81ad213d02a1b Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 04:06:25 -0600 Subject: [PATCH 073/104] ecs: fold common subroutines, align/page crossing --- src/common/ecs/compiler.ts | 9 ++- src/common/ecs/ecs.ts | 112 +++++++++++++++++++++++++++++-------- test/ecs/narrow1.txt | 2 - test/ecs/score.txt | 56 ++++--------------- test/ecs/sprites.txt | 48 ++++------------ test/ecs/sprites1.txt | 48 ++++------------ test/ecs/superman.txt | 72 ++++++------------------ test/ecs/titles.txt | 32 +++-------- test/ecs/vcs1.txt | 36 +++--------- test/ecs/vcslib.txt | 40 +++---------- 10 files changed, 170 insertions(+), 285 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index aaaf510a..9d743758 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -237,7 +237,6 @@ export class ECSCompiler extends Tokenizer { // TODO: include modifiers in error msg const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? const all_modifiers = ['critical','asc','desc']; // TODO - const modifiers = this.parseModifiers(all_modifiers); let query = undefined; let join = undefined; if (select == 'once') { @@ -253,12 +252,18 @@ export class ECSCompiler extends Tokenizer { if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } else query.limit = this.expectInteger(); } + const modifiers = this.parseModifiers(all_modifiers); + let fitbytes = undefined; + if (this.ifToken('fit')) { + fitbytes = this.expectInteger(); + } let context : ActionContext = { scope: null, system }; + // parse --- code --- let text = this.parseCode(context); let direction = undefined; if (modifiers['asc']) direction = 'asc'; else if (modifiers['desc']) direction = 'desc'; - let action = { text, event, query, join, select, direction }; + let action = { text, event, query, join, select, direction, fitbytes }; if (modifiers['critical']) (action as ActionWithJoin).critical = true; return action as ActionWithJoin; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 8324d94b..5816e91e 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -125,6 +125,7 @@ export interface ActionBase extends SourceLocated { event: string; text: string; critical?: boolean; + fitbytes?: number; } export interface ActionOnce extends ActionBase { @@ -297,12 +298,6 @@ export class Dialect_CA65 { datasymbol(component: ComponentType, field: DataField, eid: number, bitofs: number) { return `${component.name}_${field.name}_e${eid}_b${bitofs}`; } - code() { - return `.code\n`; - } - bss() { - return `.bss\n`; - } debug_file(path: string) { return `.dbg file, "${path}", 0, 0` } @@ -316,6 +311,22 @@ export class Dialect_CA65 { return `.endscope\n${name}__Start = ${name}::__Start` // TODO: scope__start = scope::start } + align(value: number) { + return `.align ${value}`; + } + alignSegmentStart() { + return this.label('__ALIGNORIGIN'); + } + warningIfMoreThan(bytes: number, startlabel: string) { + return ` +.assert (* - ${startlabel}) <= ${bytes}, error, "${startlabel} does not fit in ${bytes} bytes!"` + } + alignIfLessThan(bytes: number) { + return ` +.if <(* - __ALIGNORIGIN) > 256-${bytes} +.align $100 +.endif` + } segment(segtype: 'rodata' | 'bss' | 'code') { if (segtype == 'bss') { return `.zeropage`; @@ -590,6 +601,7 @@ class ActionEval { entities : Entity[]; tmplabel = ''; label : string; + //used = new Set(); // TODO constructor( readonly scope: EntityScope, @@ -842,6 +854,7 @@ class ActionEval { __arg(args: string[]) { let argindex = parseInt(args[0] || '0'); let argvalue = this.eventargs[argindex] || ''; + //this.used.add(`arg_${argindex}_${argvalue}`); return argvalue; } __bss_init(args: string[]) { @@ -996,6 +1009,22 @@ class ActionEval { } */ } + isSubroutineSized(code: string) { + // TODO? + if (code.length > 20000) return false; + if (code.split('.dbg line').length >= 4) return true; + return false; + } +} + +class EventCodeStats { + constructor( + public readonly inst: SystemInstance, + public readonly action: Action, + public readonly code: string, + public readonly symbol: string, + ) { } + count = 0; } export class EntityScope implements SourceLocated { @@ -1009,12 +1038,15 @@ export class EntityScope implements SourceLocated { rodata = new ConstDataSegment(); code = new CodeSegment(); componentsInScope = new Set(); - eventSeq = 0; resources = new Set(); state = new ActionCPUState(); isDemo = false; filePath = ''; + eventSeq : number; + eventStats : { [key:string] : EventCodeStats }; + inCritical = 0; + constructor( public readonly em: EntityManager, public readonly dialect: Dialect_CA65, @@ -1276,7 +1308,7 @@ export class EntityScope implements SourceLocated { } this.eventSeq++; // generate code - let code = this.dialect.code() + '\n'; + let code = ''; if (codelabel) { code += this.dialect.label(codelabel) + '\n'; } let eventCount = 0; let instances = this.instances.filter(inst => systems.includes(inst.system)); @@ -1289,23 +1321,22 @@ export class EntityScope implements SourceLocated { // TODO: keep event tree let codeeval = new ActionEval(this, inst, action, args || []); codeeval.begin(); + if (action.critical) this.inCritical++; + let eventcode = codeeval.codeToString(); + if (action.critical) this.inCritical--; + if (!this.inCritical && codeeval.isSubroutineSized(eventcode)) { + // TODO: label rewriting messes this up + let estats = this.eventStats[eventcode]; + if (!estats) { + estats = this.eventStats[eventcode] = new EventCodeStats( + inst, action, eventcode, codeeval.label); + } + estats.count++; + if (action.critical) estats.count++; // always make critical event subroutines + } let s = ''; s += this.dialect.comment(`start action ${sys.name} ${inst.id} ${event}`); // TODO - if (action.critical) { - // TODO: bin-packing, share identical code - let sublabel = `${codeeval.label}__sub`; - let lines = [ - this.dialect.segment('rodata'), - this.dialect.label(sublabel), - codeeval.codeToString(), - this.dialect.return(), - this.dialect.code(), - this.dialect.call(sublabel) - ]; - s += lines.join('\n'); - } else { - s += codeeval.codeToString(); - } + s += eventcode; s += this.dialect.comment(`end action ${sys.name} ${inst.id} ${event}`); code += s; // TODO: check that this happens once? @@ -1378,6 +1409,8 @@ export class EntityScope implements SourceLocated { this.allocateROData(this.rodata); } private generateCode() { + this.eventSeq = 0; + this.eventStats = {}; let isMainScope = this.parent == null; let start; let initsys = this.em.getSystemByName('Init'); @@ -1387,6 +1420,7 @@ export class EntityScope implements SourceLocated { } else { start = this.generateCodeForEvent('start'); } + start = this.replaceSubroutines(start); this.code.addCodeFragment(start); for (let sub of Array.from(this.resources.values())) { if (!this.getSystemInstanceNamed(sub)) { @@ -1395,10 +1429,40 @@ export class EntityScope implements SourceLocated { this.newSystemInstanceWithDefaults(sys); } let code = this.generateCodeForEvent(sub, [], sub); - this.code.addCodeFragment(code); + this.code.addCodeFragment(code); // TODO: should be rodata? } //this.showStats(); } + replaceSubroutines(code: string) { + // TODO: bin-packing for critical code + let allsubs : string[] = []; + for (let stats of Object.values(this.eventStats)) { + if (stats.count > 1) { + if (allsubs.length == 0) { + allsubs = [ + this.dialect.segment('rodata'), + this.dialect.alignSegmentStart() + ] + } else if (stats.action.fitbytes) { + allsubs.push(this.dialect.alignIfLessThan(stats.action.fitbytes)); + } + code = (code as any).replaceAll(stats.code, this.dialect.call(stats.symbol)); + let substart = stats.symbol; + let sublines = [ + this.dialect.segment('rodata'), + this.dialect.label(substart), + stats.code, + this.dialect.return(), + ]; + if (stats.action.fitbytes) { + sublines.push(this.dialect.warningIfMoreThan(stats.action.fitbytes, substart)); + } + allsubs = allsubs.concat(sublines); + } + } + code += allsubs.join('\n'); + return code; + } showStats() { for (let inst of this.instances) { // TODO? diff --git a/test/ecs/narrow1.txt b/test/ecs/narrow1.txt index 4aef92c2..607f1529 100644 --- a/test/ecs/narrow1.txt +++ b/test/ecs/narrow1.txt @@ -7,8 +7,6 @@ Xpos_x_b0: .res 1 .code __Start: -.code - ;;; start action move 1 start diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 3151d0b1..93907dbb 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -48,8 +48,6 @@ Main__INITDATA: .byte 36 .byte 86 __Start: -.code - ;;; start action Init 9 main_init @@ -67,16 +65,12 @@ __BRK: dey bne :- ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action Kernel6Digit 2 preframe Digit0 = Kernel6Digit__2__tmp+0 @@ -128,9 +122,7 @@ Kernel6Digit__preframe__3__Loop: ;;; end action Kernel6Digit 2 preframe KERNEL_START - .code - - + ;;; start action Kernel6Digit 2 kernel lda PFColor_pfcolor_b0 @@ -222,9 +214,7 @@ Kernel6Digit__kernel__5__BigLoop: Kernel2Digit__kernel__7__Loop: ldx #0 sta WSYNC - .code - - + ;;; start action Kernel2Digit 4 compute2digit lda Kernel2Digit__4__tmp+1 ; load 1st pf @@ -236,9 +226,7 @@ Kernel2Digit__kernel__7__Loop: asl asl asl - .code - - + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -256,9 +244,7 @@ Kernel2Digit__kernel__7__Loop: and #$f0 lsr sty PF1 ; store 2nd pf - .code - - + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -275,9 +261,7 @@ Kernel2Digit__kernel__7__Loop: ;;; end action Kernel2Digit 4 compute2digit inx - .code - - + ;;; start action Kernel2Digit 4 compute2digit lda Kernel2Digit__4__tmp+1 ; load 1st pf @@ -289,9 +273,7 @@ Kernel2Digit__kernel__7__Loop: asl asl asl - .code - - + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -309,9 +291,7 @@ Kernel2Digit__kernel__7__Loop: and #$f0 lsr sty PF1 ; store 2nd pf - .code - - + ;;; start action Kernel2Digit 4 fetchdigit adc Kernel2Digit__4__tmp+0 @@ -336,21 +316,15 @@ Kernel2Digit__kernel__7__Loop: ;;; end action Kernel2Digit 4 kernel KERNEL_END - .code - - + ;;; start action JoyButton 6 postframe lda INPT4 ;read button input bmi JoyButton__postframe__14__NotPressed - .code - - + ;;; start action IncScore 8 joybutton - .code - - + ;;; start action BCDMath 7 AddBCD4 .ifnblank $0210 @@ -382,9 +356,7 @@ JoyButton__postframe__14__NotPressed: ;;; end action JoyButton 6 postframe FRAME_END - .code - - + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start @@ -397,8 +369,6 @@ VecBRK: .word Main::__BRK ;;; end action Init 9 main_init -.code - FontTable: ;;; start action FontTable 3 FontTable @@ -413,8 +383,6 @@ FontTable: ;;; end action FontTable 3 FontTable -.code - FontTablePF: ;;; start action FontTablePF 5 FontTablePF diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index a75688af..c56905b2 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -115,8 +115,6 @@ Main__INITDATA: .byte 2 .byte 3 __Start: -.code - ;;; start action Init 10 main_init @@ -134,16 +132,12 @@ __BRK: dey bne :- ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action Kernel2Sprite 2 preframe .define KLINES #192 @@ -256,9 +250,7 @@ SetXPos__preframe__9____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x - .code - - + ;;; start action SetHorizPos 7 SetHorizPos ; SetHorizPos routine @@ -300,9 +292,7 @@ SetXPos__preframe__9____exit: ;;; end action SetXPos 6 preframe KERNEL_START - .code - - + ;;; start action Kernel2Sprite 2 kernel ldy #0 @@ -316,9 +306,7 @@ SetXPos__preframe__9____exit: ldy #192 Kernel2Sprite__kernel__14__LVScan: - .code - - + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -354,9 +342,7 @@ Kernel2Sprite__scanline__15__DoDraw2: ;;; end action Kernel2Sprite 2 scanline dey ; next scanline - .code - - + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -407,9 +393,7 @@ Kernel2Sprite__scanline__17__DoDraw2: ;;; end action Kernel2Sprite 2 kernel KERNEL_END - .code - - + ;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each @@ -425,9 +409,7 @@ Joystick__postframe__21____each: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveRight - .code - - + ;;; start action MoveJoyX 4 joyright lda HasXpos_xpos_b0,x @@ -443,9 +425,7 @@ MoveJoyX__joyright__22__nomove: Joystick__postframe__21__SkipMoveRight: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveLeft - .code - - + ;;; start action MoveJoyX 4 joyleft lda HasXpos_xpos_b0,x @@ -460,9 +440,7 @@ MoveJoyX__joyleft__23__nomove: Joystick__postframe__21__SkipMoveLeft: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveDown - .code - - + ;;; start action MoveJoyY 5 joydown lda HasYpos_ypos_b0,x @@ -478,9 +456,7 @@ MoveJoyY__joydown__24__nomove: Joystick__postframe__21__SkipMoveDown: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveUp - .code - - + ;;; start action MoveJoyY 5 joyup lda HasYpos_ypos_b0,x @@ -559,9 +535,7 @@ SpriteHider__postframe__28____exit: ;;; end action SpriteHider 9 postframe FRAME_END - .code - - + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index fec16295..db5a6be4 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -115,8 +115,6 @@ Main__INITDATA: .byte 0 .byte 0 __Start: -.code - ;;; start action Init 10 main_init @@ -134,16 +132,12 @@ __BRK: dey bne :- ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action Kernel2Sprite 2 preframe .define KLINES #192 @@ -241,9 +235,7 @@ SetXPos__preframe__9____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x - .code - - + ;;; start action SetHorizPos 7 SetHorizPos ; SetHorizPos routine @@ -285,9 +277,7 @@ SetXPos__preframe__9____exit: ;;; end action SetXPos 6 preframe KERNEL_START - .code - - + ;;; start action Kernel2Sprite 2 kernel ldy #0 @@ -332,18 +322,14 @@ DoDraw2: ldy #192 Kernel2Sprite__kernel__14__LVScan: - .code - - + ;;; start action Kernel2Sprite 2 scanline1 ;;; end action Kernel2Sprite 2 scanline1 Kernel2Sprite__kernel__14__DrawLine 1 ; macro: draw scanline w/ WSYNC dey ; next scanline - .code - - + Kernel2Sprite__kernel__14__DrawLine 0 ; macro: draw scanline no WSYNC dey ; next scanline bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines @@ -361,9 +347,7 @@ Kernel2Sprite__kernel__14__LVScan: ;;; end action Kernel2Sprite 2 kernel KERNEL_END - .code - - + ;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each @@ -379,9 +363,7 @@ Joystick__postframe__18____each: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveRight - .code - - + ;;; start action MoveJoyX 4 joyright lda HasXpos_xpos_b0,x @@ -397,9 +379,7 @@ MoveJoyX__joyright__19__nomove: Joystick__postframe__18__SkipMoveRight: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveLeft - .code - - + ;;; start action MoveJoyX 4 joyleft lda HasXpos_xpos_b0,x @@ -414,9 +394,7 @@ MoveJoyX__joyleft__20__nomove: Joystick__postframe__18__SkipMoveLeft: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveDown - .code - - + ;;; start action MoveJoyY 5 joydown lda HasYpos_ypos_b0,x @@ -432,9 +410,7 @@ MoveJoyY__joydown__21__nomove: Joystick__postframe__18__SkipMoveDown: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveUp - .code - - + ;;; start action MoveJoyY 5 joyup lda HasYpos_ypos_b0,x @@ -513,9 +489,7 @@ SpriteHider__postframe__25____exit: ;;; end action SpriteHider 9 postframe FRAME_END - .code - - + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index c236fb13..5028993e 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -627,8 +627,6 @@ Main__INITDATA: .byte 0 .byte 0 __Start: -.code - ;;; start action Init 11 main_init @@ -646,16 +644,12 @@ __BRK: dey bne :- ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action Kernel2Sprite 2 preframe .define KLINES #192 @@ -768,9 +762,7 @@ SetXPos__preframe__9____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x - .code - - + ;;; start action SetHorizPos 9 SetHorizPos ; SetHorizPos routine @@ -824,9 +816,7 @@ SetXPos__preframe__9____exit: ;;; end action VersatilePlayfield 10 preframe KERNEL_START - .code - - + ;;; start action Kernel2Sprite 2 kernel ldy #0 @@ -840,9 +830,7 @@ SetXPos__preframe__9____exit: ldy #192 Kernel2Sprite__kernel__15__LVScan: - .code - - + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -896,9 +884,7 @@ Kernel2Sprite__scanline__16__DoDraw2: ;;; end action VersatilePlayfield 10 scanline dey ; next scanline - .code - - + ;;; start action Kernel2Sprite 2 scanline ; draw player 0 @@ -967,9 +953,7 @@ Kernel2Sprite__scanline__20__DoDraw2: ;;; end action Kernel2Sprite 2 kernel KERNEL_END - .code - - + ;;; start action Joystick 3 postframe ; 2 control inputs share a single byte, 4 bits each @@ -982,9 +966,7 @@ Kernel2Sprite__scanline__20__DoDraw2: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveRight - .code - - + ;;; start action JoyFaceDirection 4 joyright lda Sprite_plyrflags_b0 @@ -1000,9 +982,7 @@ Kernel2Sprite__scanline__20__DoDraw2: adc #2 cmp #142 jcc SuperFly__joyright__28__nomove - .code - - + ;;; start action SuperFly 5 goeast ldy Location_room_b0 @@ -1020,9 +1000,7 @@ SuperFly__joyright__28__nomove: Joystick__postframe__26__SkipMoveRight: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveLeft - .code - - + ;;; start action JoyFaceDirection 4 joyleft lda Sprite_plyrflags_b0 @@ -1037,9 +1015,7 @@ Joystick__postframe__26__SkipMoveRight: sec sbc #2 jcs SuperFly__joyleft__31__nomove - .code - - + ;;; start action SuperFly 5 gowest ldy Location_room_b0 @@ -1057,9 +1033,7 @@ SuperFly__joyleft__31__nomove: Joystick__postframe__26__SkipMoveLeft: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveDown - .code - - + ;;; start action SuperFly 5 joydown lda HasYpos_ypos_b0 @@ -1067,9 +1041,7 @@ Joystick__postframe__26__SkipMoveLeft: adc #2 cmp #220 jcc SuperFly__joydown__33__nomove - .code - - + ;;; start action SuperFly 5 gosouth ldy Location_room_b0 @@ -1087,18 +1059,14 @@ SuperFly__joydown__33__nomove: Joystick__postframe__26__SkipMoveDown: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveUp - .code - - + ;;; start action SuperFly 5 joyup lda HasYpos_ypos_b0 sec sbc #2 jcs SuperFly__joyup__35__nomove - .code - - + ;;; start action SuperFly 5 gonorth ldy Location_room_b0 @@ -1122,9 +1090,7 @@ Joystick__postframe__26__SkipMoveUp: ldx #0 BadMove__postframe__37____each: - .code - - + ;;; start action JoyFaceDirection 4 joyright lda Sprite_plyrflags_b0+1,x @@ -1140,9 +1106,7 @@ BadMove__postframe__37____each: adc #1 cmp #142 jcc SuperFly__joyright__39__nomove - .code - - + ;;; start action SuperFly 5 goeast ldy Location_room_b0+1,x @@ -1199,9 +1163,7 @@ RoomShuffle__postframe__41__exit: ;;; end action VersatilePlayfield 10 postframe FRAME_END - .code - - + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index f3011e68..5ebdc9ae 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -142,8 +142,6 @@ Bitmap48_bitmap5_e2_b0: Bitmap48_height_b0: .byte 14 __Start: -.code - ;;; start action Init 4 main_init @@ -155,21 +153,15 @@ __Reset: __BRK: CLEAN_START ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action StaticKernel 3 preframe - .code - - + ;;; start action Kernel48Pixel 2 kernelsetup ;;; end action Kernel48Pixel 2 kernelsetup @@ -197,18 +189,14 @@ FrameLoop__start__2__NextFrame: ;;; end action StaticKernel 3 preframe KERNEL_START - .code - - + ;;; start action StaticKernel 3 kernel ldx #0 StaticKernel__kernel__9____each: sta WSYNC - .code - - + ;;; start action Kernel48Pixel 2 kernelsetup cpx #2+1 @@ -292,9 +280,7 @@ StaticKernel__kernelsetup__13____skipxhi: ;;; end action StaticKernel 3 kernelsetup - .code - - + ;;; start action Kernel48Pixel 2 kerneldraw cpx #2+1 @@ -355,13 +341,9 @@ StaticKernel__kernel__9____exit: ;;; end action StaticKernel 3 kernel KERNEL_END - .code - - + FRAME_END - .code - - + jmp FrameLoop__start__2__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index 3dc89aae..88e608b2 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -44,8 +44,6 @@ Main__INITDATA: .byte 0 .byte 0 __Start: -.code - ;;; start action Init 7 main_init @@ -62,21 +60,15 @@ __BRK: dey bne :- ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action StaticKernel 4 preframe - .code - - + ;;; start action StaticKernel 4 kernelsetup lda #24 @@ -96,18 +88,14 @@ FrameLoop__start__2__NextFrame: ;;; end action StaticKernel 4 preframe KERNEL_START - .code - - + ;;; start action StaticKernel 4 kernel ldx #0 StaticKernel__kernel__7____each: sta WSYNC - .code - - + ;;; start action StaticKernel 4 kernelsetup lda BGColor_bgcolor_b0,x @@ -164,16 +152,12 @@ StaticKernel__kernel__7____exit: ;;; end action StaticKernel 4 kernel KERNEL_END - .code - - + ;;; start action JoyButton 5 postframe lda INPT4 ;read button input bmi JoyButton__postframe__11__NotPressed - .code - - + ;;; start action Local 6 joybutton inc Local__6__tmp+0 @@ -186,16 +170,12 @@ JoyButton__postframe__11__NotPressed: ;;; end action JoyButton 5 postframe FRAME_END - .code - - + ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__13__NoStart - .code - - + ;;; start action ResetConsole 3 resetswitch jmp Main::__Reset ; jump to Reset handler diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 4b73be8e..1233c265 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -44,8 +44,6 @@ Main__INITDATA: .byte 0 .byte 0 __Start: -.code - ;;; start action Init 7 main_init @@ -63,21 +61,15 @@ __BRK: dey bne :- ; initialize data segment -.code - ;;; start action FrameLoop 1 start FrameLoop__start__2__NextFrame: FRAME_START - .code - - + ;;; start action StaticKernel 4 preframe - .code - - + ;;; start action StaticKernel 4 kernelsetup lda #24 @@ -97,18 +89,14 @@ FrameLoop__start__2__NextFrame: ;;; end action StaticKernel 4 preframe KERNEL_START - .code - - + ;;; start action StaticKernel 4 kernel ldx #0 StaticKernel__kernel__7____each: sta WSYNC - .code - - + ;;; start action StaticKernel 4 kernelsetup lda BGColor_bgcolor_b0,x @@ -149,9 +137,7 @@ StaticKernel__kernelsetup__10____skipxlo: ;;; end action StaticKernel 4 kernelsetup - .code - - + ;;; start action StaticKernel 4 kerneldraw ldy KernelSection_lines_b0,x @@ -173,16 +159,12 @@ StaticKernel__kernel__7____exit: ;;; end action StaticKernel 4 kernel KERNEL_END - .code - - + ;;; start action JoyButton 5 postframe lda INPT4 ;read button input bmi JoyButton__postframe__12__NotPressed - .code - - + ;;; start action Local 6 joybutton inc Local__6__tmp+0 @@ -195,16 +177,12 @@ JoyButton__postframe__12__NotPressed: ;;; end action JoyButton 5 postframe FRAME_END - .code - - + ;;; start action ResetSwitch 2 nextframe lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__14__NoStart - .code - - + ;;; start action ResetConsole 3 resetswitch jmp Main::__Reset ; jump to Reset handler From 38920c6dce9b1bfd768596d2a278d7d9963a7384 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 11:35:30 -0600 Subject: [PATCH 074/104] ca65: return listings if errors --- src/worker/tools/cc65.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index 8aff1e0d..fe324a58 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -1,7 +1,7 @@ import { getFilenamePrefix, getRootBasePlatform } from "../../common/util"; import { CodeListingMap, WorkerError } from "../../common/workertypes"; -import { re_crlf, BuildStepResult, anyTargetChanged, execMain, gatherFiles, msvcErrorMatcher, populateEntry, populateExtraFiles, populateFiles, print_fn, putWorkFile, setupFS, staleFiles, BuildStep, emglobal, loadNative, moduleInstFn, fixParamsWithDefines, store, makeErrorMatcher } from "../workermain"; +import { re_crlf, BuildStepResult, anyTargetChanged, execMain, gatherFiles, msvcErrorMatcher, populateEntry, populateExtraFiles, populateFiles, print_fn, putWorkFile, setupFS, staleFiles, BuildStep, emglobal, loadNative, moduleInstFn, fixParamsWithDefines, store, makeErrorMatcher, getWorkFileAsString } from "../workermain"; import { EmscriptenModule } from "../workermain" @@ -128,8 +128,12 @@ export function assembleCA65(step: BuildStep): BuildStepResult { args.unshift.apply(args, ["-D", "__MAIN__=1"]); } execMain(step, CA65, args); - if (errors.length) - return { errors: errors }; + if (errors.length) { + // TODO? + let listings : CodeListingMap = {}; + listings[step.path] = { lines:[], text:getWorkFileAsString(step.path) }; + return { errors, listings }; + } objout = FS.readFile(objpath, { encoding: 'binary' }); lstout = FS.readFile(lstpath, { encoding: 'utf8' }); putWorkFile(objpath, objout); From 600e62f0c68561db0f24b51023db59447fe5a328 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 11:43:40 -0600 Subject: [PATCH 075/104] worker: can return listings with error result --- src/ide/project.ts | 14 ++++++++++++-- src/ide/ui.ts | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/ide/project.ts b/src/ide/project.ts index 5bd7737d..8db8f63f 100644 --- a/src/ide/project.ts +++ b/src/ide/project.ts @@ -1,5 +1,5 @@ -import { FileData, Dependency, SourceLine, SourceFile, CodeListing, CodeListingMap, WorkerError, Segment, WorkerResult, WorkerOutputResult, isUnchanged, isOutputResult, WorkerMessage, WorkerItemUpdate } from "../common/workertypes"; +import { FileData, Dependency, SourceLine, SourceFile, CodeListing, CodeListingMap, WorkerError, Segment, WorkerResult, WorkerOutputResult, isUnchanged, isOutputResult, WorkerMessage, WorkerItemUpdate, WorkerErrorResult, isErrorResult } from "../common/workertypes"; import { getFilenamePrefix, getFolderForPath, isProbablyBinary, getBasePlatform, getWithBinary } from "../common/util"; import { Platform } from "../common/baseplatform"; import localforage from "localforage"; @@ -129,6 +129,8 @@ export class CodeProject { } if (data && isOutputResult(data)) { this.processBuildResult(data); + } else if (isErrorResult(data)) { + this.processBuildListings(data); } this.callbackBuildResult(data); } @@ -376,7 +378,7 @@ export class CodeProject { this.sendBuild(); } - processBuildResult(data: WorkerOutputResult) { + processBuildListings(data: WorkerOutputResult | WorkerErrorResult) { // TODO: link listings with source files if (data.listings) { this.listings = data.listings; @@ -388,6 +390,14 @@ export class CodeProject { lst.assemblyfile = new SourceFile(lst.asmlines, lst.text); } } + } + + processBuildResult(data: WorkerOutputResult) { + this.processBuildListings(data); + this.processBuildSegments(data); + } + + processBuildSegments(data: WorkerOutputResult) { // save and sort segment list var segs = (this.platform.getMemoryMap && this.platform.getMemoryMap()["main"]) || []; if (data.segments) { segs = segs.concat(data.segments || []); } diff --git a/src/ide/ui.ts b/src/ide/ui.ts index b7f2607d..727b0e13 100644 --- a/src/ide/ui.ts +++ b/src/ide/ui.ts @@ -323,7 +323,7 @@ function refreshWindowList() { for (var lstfn in listings) { var lst = listings[lstfn]; // add listing if source/assembly file exists and has text - if ((lst.assemblyfile && lst.assemblyfile.text) || (lst.sourcefile && lst.sourcefile.text)) { + if ((lst.assemblyfile && lst.assemblyfile.text) || (lst.sourcefile && lst.sourcefile.text) || lst.text) { addWindowItem(lstfn, getFilenameForPath(lstfn), (path) => { return new ListingView(path); }); From c73c5ee185f76156119b01caf305a9fba6e4bbbc Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 11:51:48 -0600 Subject: [PATCH 076/104] ecs: check page boundary for critical subs --- src/common/ecs/ecs.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 5816e91e..2fdfa13a 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -317,6 +317,10 @@ export class Dialect_CA65 { alignSegmentStart() { return this.label('__ALIGNORIGIN'); } + warningIfPageCrossed(startlabel: string) { + return ` +.assert >(${startlabel}) = >(*), error, "${startlabel} crosses a page boundary!"` + } warningIfMoreThan(bytes: number, startlabel: string) { return ` .assert (* - ${startlabel}) <= ${bytes}, error, "${startlabel} does not fit in ${bytes} bytes!"` @@ -1454,6 +1458,9 @@ export class EntityScope implements SourceLocated { stats.code, this.dialect.return(), ]; + if (stats.action.critical) { + sublines.push(this.dialect.warningIfPageCrossed(substart)); + } if (stats.action.fitbytes) { sublines.push(this.dialect.warningIfMoreThan(stats.action.fitbytes, substart)); } From 3fe2a151da69bd54bb66d6719b22e47783896bad Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 12:38:16 -0600 Subject: [PATCH 077/104] ecs: show svg of bin packing, fixed bug --- src/common/ecs/binpack.ts | 42 +++++++++++++++++++++++++++++++++++++-- src/common/ecs/ecs.ts | 3 +++ test/ecs/sprites.txt | 2 +- test/ecs/sprites1.txt | 2 +- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/common/ecs/binpack.ts b/src/common/ecs/binpack.ts index 61a576cb..163f4c6f 100644 --- a/src/common/ecs/binpack.ts +++ b/src/common/ecs/binpack.ts @@ -7,6 +7,7 @@ export interface BoxConstraints { width: number; height: number; box?: PlacedBox; + label?: string; } enum BoxPlacement { @@ -76,9 +77,10 @@ export class Bin { let dx = (f.right - f.left) - b.width; let dy = (f.bottom - f.top) - b.height; if (dx >= 0 && dy >= 0) { - let score = 1 / (1 + dx + dy); + let score = 1 / (1 + dx + dy + f.left * 0.001); if (score > bestscore) { best = f; + bestscore = score; if (score == 1) break; } } @@ -188,7 +190,7 @@ export class Packer { box.left = parent.right - w; box.right = parent.right; } - if (debug) console.log('place',box.left,box.top,box.right,box.bottom,parent?.left,parent?.top); + if (debug) console.log('place',b.label,box.left,box.top,box.right,box.bottom,parent?.left,parent?.top); let parents = [parent]; // if approx match, might overlap multiple free boxes if (approx) parents = bin.getBoxes(box, 100, bin.free); @@ -198,4 +200,40 @@ export class Packer { if (debug) console.log('cannot place!', b.left,b.top,b.width,b.height); return null; } + toSVG() { + let s = ''; + let r = {width:100,height:70} + for (let bin of this.bins) { + r.width = Math.max(r.width, bin.binbounds.right); + r.height = Math.max(r.height, bin.binbounds.bottom); + } + s += ``; + for (let bin of this.bins) { + let be = bin.extents; + s += '' + s += ``; + let textx = be.right+1; + let texty = 0; + for (let box of this.boxes) { + let b = box.box; + if (b) { + if (b.bin == bin) s += ``; + if (b.top == texty) textx += 10; else textx = be.right+1; + texty = b.top; + if (box.label) s += `${box.label}`; + } + } + /* + for (let b of bin.free) { + s += ``; + } + */ + s += '' + } + s += ``; + return s; + } + toSVGUrl() { + return `data:image/svg+xml;base64,${btoa(this.toSVG())}`; + } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 2fdfa13a..c09dd9e1 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1389,6 +1389,7 @@ export class EntityScope implements SourceLocated { bottom: stats.tempendseq+1, width: instance.system.tempbytes, height: stats.tempendseq - stats.tempstartseq + 1, + label: instance.system.name }; pack.boxes.push(v); } @@ -1404,6 +1405,7 @@ export class EntityScope implements SourceLocated { //this.bss.equates[this.dialect.tempLabel(inst)] = `TEMP+${b.box?.left}`; } } + console.log(pack.toSVGUrl()); } private analyzeEntities() { this.buildSegments(); @@ -1439,6 +1441,7 @@ export class EntityScope implements SourceLocated { } replaceSubroutines(code: string) { // TODO: bin-packing for critical code + // TODO: doesn't work with nested subroutines? let allsubs : string[] = []; for (let stats of Object.values(this.eventStats)) { if (stats.count > 1) { diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index c56905b2..0f32e4f4 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -28,11 +28,11 @@ SpriteSlot_sprite_b0: TEMP: Kernel2Sprite__2__tmp: Joystick__3__tmp: +SpriteHider__9__tmp: .res 1 SpriteShuffler__8__tmp: .res 1 .res 1 -SpriteHider__9__tmp: .res 1 .res 1 .res 1 diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index db5a6be4..254293c0 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -28,11 +28,11 @@ HasColormap_colormap_b0: TEMP: Kernel2Sprite__2__tmp: Joystick__3__tmp: +SpriteHider__9__tmp: .res 1 SpriteShuffler__8__tmp: .res 1 .res 1 -SpriteHider__9__tmp: .res 1 .res 1 .res 1 From 479c2f24b8881674ac5833c470db4b310de06e88 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 15:39:44 -0600 Subject: [PATCH 078/104] ecs: unroll keyword --- src/common/ecs/ecs.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index c09dd9e1..c0138814 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -72,7 +72,7 @@ export interface SystemInstance extends SourceLocated { id: number; } -export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select'] as const; +export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select', 'unroll'] as const; export type SelectType = typeof SELECT_TYPE[number]; @@ -133,7 +133,7 @@ export interface ActionOnce extends ActionBase { } export interface ActionWithQuery extends ActionBase { - select: 'foreach' | 'join' | 'with' | 'if' | 'select' + select: 'foreach' | 'join' | 'with' | 'if' | 'select' | 'unroll' query: Query direction?: 'asc' | 'desc' } @@ -647,6 +647,7 @@ class ActionEval { state.xreg = state.yreg = null; break; case 'foreach': + case 'unroll': if (state.xreg && state.yreg) throw new ECSError('no more index registers', this.action); if (state.xreg) state.yreg = new IndexRegister(this.scope, this.qr); else state.xreg = new IndexRegister(this.scope, this.qr); @@ -689,7 +690,7 @@ class ActionEval { this.scope.state = this.oldState; } codeToString(): string { - const allowEmpty = ['if','foreach','join']; + const allowEmpty = ['if','foreach','unroll','join']; if (this.entities.length == 0 && allowEmpty.includes(this.action.select)) return ''; @@ -746,6 +747,9 @@ class ActionEval { if (action.select == 'foreach' && entities.length > 1) { code = this.wrapCodeInLoop(code, action, this.qr.entities); } + if (action.select == 'unroll' && entities.length > 1) { + throw new ECSError('unroll is not yet implemented'); + } // define properties props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); From d96296675b1f4a17907b5f22f6556ffd1c0a9691 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 16:19:16 -0600 Subject: [PATCH 079/104] ecs: accept 0/1 for bitmaps --- src/common/ecs/decoder.ts | 7 +- src/common/ecs/ecs.ts | 2 +- test/ecs/titles.ecs | 176 +++++-- test/ecs/titles.txt | 1000 +++++++++++++++++++++++++++++++------ 4 files changed, 990 insertions(+), 195 deletions(-) diff --git a/src/common/ecs/decoder.ts b/src/common/ecs/decoder.ts index 628a193f..7b2e91e0 100644 --- a/src/common/ecs/decoder.ts +++ b/src/common/ecs/decoder.ts @@ -22,9 +22,10 @@ abstract class LineDecoder { let b = 0; for (let i=0; iBitmap48_bitmap0_e2_b0 -Bitmap48_bitmap0_e2_b0: +.byte >Bitmap48_bitmap0_e3_b0 +Bitmap48_bitmap0_e3_b0: .byte 0 -.byte 240 -.byte 207 -.byte 192 -.byte 196 -.byte 196 -.byte 199 -.byte 196 -.byte 196 -.byte 196 -.byte 192 -.byte 207 -.byte 240 -.byte 192 -.byte 128 +.byte 0 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 3 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 3 +.byte 15 +.byte 12 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 28 +.byte 31 +.byte 27 +.byte 24 +.byte 24 +.byte 12 +.byte 15 +.byte 3 Bitmap48_bitmap1_b0: -.byte Bitmap48_bitmap1_e2_b0 -Bitmap48_bitmap1_e2_b0: +.byte >Bitmap48_bitmap1_e3_b0 +Bitmap48_bitmap1_e3_b0: .byte 0 .byte 0 -.byte 0 -.byte 192 -.byte 32 -.byte 144 -.byte 136 -.byte 144 -.byte 32 -.byte 32 -.byte 192 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -Bitmap48_bitmap2_b0: -.byte Bitmap48_bitmap2_e2_b0 -Bitmap48_bitmap2_e2_b0: -.byte 0 -.byte 0 -.byte 0 -.byte 1 -.byte 2 -.byte 33 -.byte 226 -.byte 129 -.byte 2 -.byte 1 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -Bitmap48_bitmap3_b0: -.byte Bitmap48_bitmap3_e2_b0 -Bitmap48_bitmap3_e2_b0: -.byte 0 -.byte 0 -.byte 0 -.byte 128 -.byte 64 -.byte 136 -.byte 78 -.byte 130 -.byte 64 -.byte 128 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -Bitmap48_bitmap4_b0: -.byte Bitmap48_bitmap4_e2_b0 -Bitmap48_bitmap4_e2_b0: -.byte 0 -.byte 0 -.byte 3 -.byte 4 -.byte 24 -.byte 32 -.byte 64 -.byte 32 -.byte 24 -.byte 4 -.byte 4 -.byte 3 -.byte 0 -.byte 0 -.byte 0 -Bitmap48_bitmap5_b0: -.byte Bitmap48_bitmap5_e2_b0 -Bitmap48_bitmap5_e2_b0: -.byte 0 .byte 255 -.byte 1 -.byte 1 -.byte 1 -.byte 1 -.byte 1 -.byte 1 -.byte 1 -.byte 1 -.byte 1 -.byte 1 +.byte 255 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 254 +.byte 254 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 255 +.byte 255 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 192 .byte 225 -.byte 29 +.byte 51 +.byte 51 +.byte 48 +.byte 48 +.byte 48 +.byte 112 +.byte 225 +.byte 193 +.byte 1 +.byte 49 +.byte 49 +.byte 225 +.byte 193 +Bitmap48_bitmap2_b0: +.byte Bitmap48_bitmap2_e3_b0 +Bitmap48_bitmap2_e3_b0: +.byte 0 +.byte 0 .byte 3 -Bitmap48_height_b0: +.byte 15 +.byte 28 +.byte 24 +.byte 56 +.byte 48 +.byte 48 +.byte 48 +.byte 48 +.byte 48 +.byte 56 +.byte 24 +.byte 28 +.byte 15 +.byte 3 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 248 +.byte 252 .byte 14 +.byte 6 +.byte 6 +.byte 6 +.byte 6 +.byte 14 +.byte 252 +.byte 248 +.byte 128 +.byte 128 +.byte 128 +.byte 254 +.byte 254 +Bitmap48_bitmap3_b0: +.byte Bitmap48_bitmap3_e3_b0 +Bitmap48_bitmap3_e3_b0: +.byte 0 +.byte 0 +.byte 224 +.byte 248 +.byte 28 +.byte 12 +.byte 14 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 4 +.byte 12 +.byte 28 +.byte 248 +.byte 224 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 31 +.byte 63 +.byte 49 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 49 +.byte 63 +.byte 15 +Bitmap48_bitmap4_b0: +.byte Bitmap48_bitmap4_e3_b0 +Bitmap48_bitmap4_e3_b0: +.byte 0 +.byte 0 +.byte 31 +.byte 127 +.byte 96 +.byte 192 +.byte 192 +.byte 0 +.byte 1 +.byte 31 +.byte 62 +.byte 112 +.byte 192 +.byte 192 +.byte 96 +.byte 127 +.byte 31 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 15 +.byte 143 +.byte 142 +.byte 199 +.byte 195 +.byte 193 +.byte 192 +.byte 192 +.byte 192 +.byte 192 +.byte 192 +.byte 204 +.byte 142 +.byte 135 +.byte 3 +Bitmap48_bitmap5_b0: +.byte Bitmap48_bitmap5_e3_b0 +Bitmap48_bitmap5_e3_b0: +.byte 0 +.byte 0 +.byte 128 +.byte 192 +.byte 224 +.byte 96 +.byte 96 +.byte 96 +.byte 224 +.byte 192 +.byte 0 +.byte 0 +.byte 0 +.byte 96 +.byte 224 +.byte 192 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 248 +.byte 248 +.byte 0 +.byte 0 +.byte 128 +.byte 192 +.byte 224 +.byte 112 +.byte 48 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 240 +.byte 224 +Bitmap48_height_b0: +.byte 36 __Start: -;;; start action Init 4 main_init - -.include "vcs-ca65.h" -.macpack longbranch -.define PAL 0 -__NMI: -__Reset: -__BRK: - CLEAN_START - ; initialize data segment - ;;; start action FrameLoop 1 start -FrameLoop__start__2__NextFrame: +FrameLoop__start__3__NextFrame: FRAME_START ;;; start action StaticKernel 3 preframe @@ -193,19 +344,19 @@ FrameLoop__start__2__NextFrame: ;;; start action StaticKernel 3 kernel ldx #0 -StaticKernel__kernel__9____each: +StaticKernel__kernel__10____each: sta WSYNC ;;; start action Kernel48Pixel 2 kernelsetup cpx #2+1 - jcs Kernel48Pixel__kernelsetup__10____skipxhi + jcs Kernel48Pixel__kernelsetup__11____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__10____skipxlo + jcc Kernel48Pixel__kernelsetup__11____skipxlo - lda #14 + lda #36 sta Kernel48Pixel__2__tmp+0 ; scanline counter lda #$22 sta COLUP0 ; show how players alternate @@ -215,40 +366,40 @@ StaticKernel__kernel__9____each: sta NUSIZ0 sta NUSIZ1 ; both players have 3 copies sta WSYNC - SLEEPH 34 + SLEEPH 35 sta RESP0 ; position 1st player sta RESP1 ; ...and 2nd player lda #$10 sta HMP1 ; 1 pixel to the left sta WSYNC sta HMOVE ; apply HMOVE - SLEEPH 24 ; sleep 24 cycles - sta HMCLR ; clear HMOVE registers lda #1 sta VDELP0 ; we need the VDEL registers sta VDELP1 ; so we can do our 4-store trick + SLEEPH 24-8 ; sleep 24 cycles + sta HMCLR ; clear HMOVE registers -Kernel48Pixel__kernelsetup__10____skipxlo: +Kernel48Pixel__kernelsetup__11____skipxlo: -Kernel48Pixel__kernelsetup__10____skipxhi: +Kernel48Pixel__kernelsetup__11____skipxhi: ;;; end action Kernel48Pixel 2 kernelsetup ;;; start action Kernel48Pixel 2 kernelsetup cpx #2+1 - jcs Kernel48Pixel__kernelsetup__11____skipxhi + jcs Kernel48Pixel__kernelsetup__12____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__11____skipxlo + jcc Kernel48Pixel__kernelsetup__12____skipxlo lda #252 sta COLUP0 sta COLUP1 -Kernel48Pixel__kernelsetup__11____skipxlo: +Kernel48Pixel__kernelsetup__12____skipxlo: -Kernel48Pixel__kernelsetup__11____skipxhi: +Kernel48Pixel__kernelsetup__12____skipxhi: ;;; end action Kernel48Pixel 2 kernelsetup @@ -262,17 +413,17 @@ Kernel48Pixel__kernelsetup__11____skipxhi: ;;; start action StaticKernel 3 kernelsetup cpx #2+1 - jcs StaticKernel__kernelsetup__13____skipxhi + jcs StaticKernel__kernelsetup__14____skipxhi cpx #2 - jcc StaticKernel__kernelsetup__13____skipxlo + jcc StaticKernel__kernelsetup__14____skipxlo lda #252 sta COLUPF -StaticKernel__kernelsetup__13____skipxlo: +StaticKernel__kernelsetup__14____skipxlo: -StaticKernel__kernelsetup__13____skipxhi: +StaticKernel__kernelsetup__14____skipxhi: ;;; end action StaticKernel 3 kernelsetup @@ -284,14 +435,545 @@ StaticKernel__kernelsetup__13____skipxhi: ;;; start action Kernel48Pixel 2 kerneldraw cpx #2+1 - jcs Kernel48Pixel__kerneldraw__15____skipxhi + jcs Kernel48Pixel__kerneldraw__16____skipxhi cpx #2 - jcc Kernel48Pixel__kerneldraw__15____skipxlo + jcc Kernel48Pixel__kerneldraw__16____skipxlo txa pha -Kernel48Pixel__kerneldraw__15__Loop: +Kernel48Pixel__kerneldraw__16__Loop: + ldy Kernel48Pixel__2__tmp+0 ; counts backwards + sta WSYNC ; sync to next scanline + lda Bitmap48_bitmap0_e3_b0,y ; load B0 (1st sprite byte) + sta GRP0 ; B0 -> [GRP0] + lda Bitmap48_bitmap1_e3_b0,y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + lda Bitmap48_bitmap2_e3_b0,y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda Bitmap48_bitmap5_e3_b0,y ; load B5 -> A + sta Kernel48Pixel__2__tmp+1 ; B5 -> temp + ldx Bitmap48_bitmap4_e3_b0,y ; load B4 -> X + lda Bitmap48_bitmap3_e3_b0,y ; load B3 -> A + ldy Kernel48Pixel__2__tmp+1 ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec Kernel48Pixel__2__tmp+0 ; go to next line + bpl Kernel48Pixel__kerneldraw__16__Loop ; repeat until < 0 + pla + tax + +Kernel48Pixel__kerneldraw__16____skipxlo: + +Kernel48Pixel__kerneldraw__16____skipxhi: + +;;; end action Kernel48Pixel 2 kerneldraw + +;;; start action StaticKernel 3 kerneldraw + + ldy KernelSection_lines_b0,x +StaticKernel__kerneldraw__17__loop: + sta WSYNC + + dey + bne StaticKernel__kerneldraw__17__loop + +;;; end action StaticKernel 3 kerneldraw + + + + inx + cpx #5 + jne StaticKernel__kernel__10____each +StaticKernel__kernel__10____exit: + +;;; end action StaticKernel 3 kernel + + KERNEL_END + +;;; start action JoyButton 4 postframe + + lda INPT4 ;read button input + bmi JoyButton__postframe__18__NotPressed + +;;; start action Advance 5 joybutton + + FRAME_END + jmp Title2__Start + +;;; end action Advance 5 joybutton + +JoyButton__postframe__18__NotPressed: + +;;; end action JoyButton 4 postframe + + FRAME_END + + jmp FrameLoop__start__3__NextFrame ; loop to next frame + +;;; end action FrameLoop 1 start + +.endscope +Title__Start = Title::__Start +.scope Title2 +.zeropage +TEMP: +Kernel48Pixel__2__tmp: +.res 1 +.res 1 +.code +KernelSection_lines_b0: +.byte 60 +.byte 10 +.byte 2 +.byte 10 +.byte 10 +BGColor_bgcolor_b0: +.byte 80 +.byte 82 +.byte 84 +.byte 82 +.byte 80 +PFColor_pfcolor_b0: +.byte 236 +Bitmap48_bitmap0_b0: +.byte Bitmap48_bitmap0_e2_b0 +Bitmap48_bitmap0_e2_b0: +.byte 0 +.byte 1 +.byte 1 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 7 +.byte 31 +.byte 24 +.byte 0 +.byte 7 +.byte 15 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 15 +.byte 7 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap1_b0: +.byte Bitmap48_bitmap1_e2_b0 +Bitmap48_bitmap1_e2_b0: +.byte 0 +.byte 192 +.byte 224 +.byte 96 +.byte 96 +.byte 97 +.byte 99 +.byte 102 +.byte 102 +.byte 102 +.byte 102 +.byte 102 +.byte 102 +.byte 103 +.byte 99 +.byte 97 +.byte 0 +.byte 0 +.byte 96 +.byte 96 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 192 +.byte 224 +.byte 96 +.byte 48 +.byte 176 +.byte 241 +.byte 115 +.byte 51 +.byte 51 +.byte 51 +.byte 51 +.byte 51 +.byte 115 +.byte 241 +.byte 176 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap2_b0: +.byte Bitmap48_bitmap2_e2_b0 +Bitmap48_bitmap2_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 240 +.byte 248 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 248 +.byte 240 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 248 +.byte 252 +.byte 6 +.byte 6 +.byte 6 +.byte 6 +.byte 6 +.byte 6 +.byte 14 +.byte 252 +.byte 248 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap3_b0: +.byte Bitmap48_bitmap3_e2_b0 +Bitmap48_bitmap3_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 222 +.byte 243 +.byte 225 +.byte 193 +.byte 193 +.byte 193 +.byte 193 +.byte 193 +.byte 225 +.byte 255 +.byte 222 +.byte 192 +.byte 192 +.byte 192 +.byte 192 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 31 +.byte 63 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 96 +.byte 63 +.byte 31 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap4_b0: +.byte Bitmap48_bitmap4_e2_b0 +Bitmap48_bitmap4_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 12 +.byte 12 +.byte 128 +.byte 128 +.byte 140 +.byte 140 +.byte 140 +.byte 140 +.byte 140 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 12 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 3 +.byte 135 +.byte 204 +.byte 204 +.byte 204 +.byte 204 +.byte 204 +.byte 204 +.byte 204 +.byte 135 +.byte 3 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +Bitmap48_bitmap5_b0: +.byte Bitmap48_bitmap5_e2_b0 +Bitmap48_bitmap5_e2_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 216 +.byte 248 +.byte 56 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +.byte 56 +.byte 248 +.byte 216 +.byte 24 +.byte 24 +.byte 24 +.byte 24 +Bitmap48_height_b0: +.byte 43 +__Start: + +;;; start action FrameLoop 1 start + +FrameLoop__start__20__NextFrame: + FRAME_START + +;;; start action StaticKernel 3 preframe + + +;;; start action Kernel48Pixel 2 kernelsetup + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action Kernel48Pixel 2 kernelsetup + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + + lda #80 + sta COLUBK + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + +;;; end action StaticKernel 3 kernelsetup + + +;;; end action StaticKernel 3 preframe + + KERNEL_START + +;;; start action StaticKernel 3 kernel + + ldx #0 +StaticKernel__kernel__27____each: + + sta WSYNC + +;;; start action Kernel48Pixel 2 kernelsetup + + cpx #2+1 + jcs Kernel48Pixel__kernelsetup__28____skipxhi + + cpx #2 + jcc Kernel48Pixel__kernelsetup__28____skipxlo + + lda #43 + sta Kernel48Pixel__2__tmp+0 ; scanline counter + lda #$22 + sta COLUP0 ; show how players alternate + lda #$12 + sta COLUP1 ; by having different colors + lda #3 + sta NUSIZ0 + sta NUSIZ1 ; both players have 3 copies + sta WSYNC + SLEEPH 35 + sta RESP0 ; position 1st player + sta RESP1 ; ...and 2nd player + lda #$10 + sta HMP1 ; 1 pixel to the left + sta WSYNC + sta HMOVE ; apply HMOVE + lda #1 + sta VDELP0 ; we need the VDEL registers + sta VDELP1 ; so we can do our 4-store trick + SLEEPH 24-8 ; sleep 24 cycles + sta HMCLR ; clear HMOVE registers + +Kernel48Pixel__kernelsetup__28____skipxlo: + +Kernel48Pixel__kernelsetup__28____skipxhi: + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action Kernel48Pixel 2 kernelsetup + + cpx #2+1 + jcs Kernel48Pixel__kernelsetup__29____skipxhi + + cpx #2 + jcc Kernel48Pixel__kernelsetup__29____skipxlo + + lda #236 + sta COLUP0 + sta COLUP1 + +Kernel48Pixel__kernelsetup__29____skipxlo: + +Kernel48Pixel__kernelsetup__29____skipxhi: + +;;; end action Kernel48Pixel 2 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + + lda BGColor_bgcolor_b0,x + sta COLUBK + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + + cpx #2+1 + jcs StaticKernel__kernelsetup__31____skipxhi + + cpx #2 + jcc StaticKernel__kernelsetup__31____skipxlo + + lda #236 + sta COLUPF + +StaticKernel__kernelsetup__31____skipxlo: + +StaticKernel__kernelsetup__31____skipxhi: + +;;; end action StaticKernel 3 kernelsetup + +;;; start action StaticKernel 3 kernelsetup + +;;; end action StaticKernel 3 kernelsetup + + +;;; start action Kernel48Pixel 2 kerneldraw + + cpx #2+1 + jcs Kernel48Pixel__kerneldraw__33____skipxhi + + cpx #2 + jcc Kernel48Pixel__kerneldraw__33____skipxlo + + txa + pha +Kernel48Pixel__kerneldraw__33__Loop: ldy Kernel48Pixel__2__tmp+0 ; counts backwards sta WSYNC ; sync to next scanline lda Bitmap48_bitmap0_e2_b0,y ; load B0 (1st sprite byte) @@ -310,24 +992,24 @@ Kernel48Pixel__kerneldraw__15__Loop: sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec Kernel48Pixel__2__tmp+0 ; go to next line - bpl Kernel48Pixel__kerneldraw__15__Loop ; repeat until < 0 + bpl Kernel48Pixel__kerneldraw__33__Loop ; repeat until < 0 pla tax -Kernel48Pixel__kerneldraw__15____skipxlo: +Kernel48Pixel__kerneldraw__33____skipxlo: -Kernel48Pixel__kerneldraw__15____skipxhi: +Kernel48Pixel__kerneldraw__33____skipxhi: ;;; end action Kernel48Pixel 2 kerneldraw ;;; start action StaticKernel 3 kerneldraw ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__16__loop: +StaticKernel__kerneldraw__34__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__16__loop + bne StaticKernel__kerneldraw__34__loop ;;; end action StaticKernel 3 kerneldraw @@ -335,8 +1017,8 @@ StaticKernel__kerneldraw__16__loop: inx cpx #5 - jne StaticKernel__kernel__9____each -StaticKernel__kernel__9____exit: + jne StaticKernel__kernel__27____each +StaticKernel__kernel__27____exit: ;;; end action StaticKernel 3 kernel @@ -344,17 +1026,11 @@ StaticKernel__kernel__9____exit: FRAME_END - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__20__NextFrame ; loop to next frame ;;; end action FrameLoop 1 start - ; start main routine -.segment "VECTORS" -Return: .word $6060 -VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK - -;;; end action Init 4 main_init .endscope -Main__Start = Main::__Start \ No newline at end of file +Title2__Start = Title2::__Start +.endscope +TitleDemo__Start = TitleDemo::__Start \ No newline at end of file From eb30e5cf857af0b7fb68a574d5226a5fe8587064 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 16:53:19 -0600 Subject: [PATCH 080/104] ecs: automatically init bss when 'start' event --- src/common/ecs/ecs.ts | 10 ++++++--- test/ecs/narrow1.txt | 11 ++++++++++ test/ecs/score.txt | 1 - test/ecs/sprites.txt | 1 - test/ecs/sprites1.txt | 1 - test/ecs/superman.txt | 1 - test/ecs/titles.ecs | 24 +++++++++++++++------- test/ecs/titles.txt | 47 +++++++++++++++++++++++++++++++------------ test/ecs/vcs1.ecs | 1 - test/ecs/vcs1.txt | 1 - test/ecs/vcslib.ecs | 1 - test/ecs/vcslib.txt | 1 - 12 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 7dd3226a..cd1e4cf6 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -865,9 +865,6 @@ class ActionEval { //this.used.add(`arg_${argindex}_${argvalue}`); return argvalue; } - __bss_init(args: string[]) { - return this.scope.allocateInitData(this.scope.bss); - } wrapCodeInLoop(code: string, action: ActionWithQuery, ents: Entity[], joinfield?: ComponentFieldPair): string { // TODO: check ents // TODO: check segment bounds @@ -1317,7 +1314,13 @@ export class EntityScope implements SourceLocated { this.eventSeq++; // generate code let code = ''; + // is there a label? generate it first if (codelabel) { code += this.dialect.label(codelabel) + '\n'; } + // if "start" event, initialize data segment + if (event == 'start') { + code += this.allocateInitData(this.bss); + } + // iterate all instances and generate matching events let eventCount = 0; let instances = this.instances.filter(inst => systems.includes(inst.system)); for (let inst of instances) { @@ -1446,6 +1449,7 @@ export class EntityScope implements SourceLocated { replaceSubroutines(code: string) { // TODO: bin-packing for critical code // TODO: doesn't work with nested subroutines? + // TODO: doesn't work between scopes let allsubs : string[] = []; for (let stats of Object.values(this.eventStats)) { if (stats.count > 1) { diff --git a/test/ecs/narrow1.txt b/test/ecs/narrow1.txt index 607f1529..3073830d 100644 --- a/test/ecs/narrow1.txt +++ b/test/ecs/narrow1.txt @@ -6,8 +6,19 @@ Xpos_x_b0: .res 1 .res 1 .code +Main__INITDATA: +.byte 0 +.byte 50 +.byte 100 +.byte 150 __Start: + ldy #4 +: lda Main__INITDATA-1,y + sta Xpos_x_b0-1,y + dey + bne :- + ;;; start action move 1 start ldx #0 diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 93907dbb..a9470893 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -64,7 +64,6 @@ __BRK: sta BCDScore6_digits_b0-1,y dey bne :- - ; initialize data segment ;;; start action FrameLoop 1 start diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index 0f32e4f4..8a5f0186 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -131,7 +131,6 @@ __BRK: sta HasBitmap_bitmap_b0-1,y dey bne :- - ; initialize data segment ;;; start action FrameLoop 1 start diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 254293c0..62363c54 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -131,7 +131,6 @@ __BRK: sta HasBitmap_bitmap_b0-1,y dey bne :- - ; initialize data segment ;;; start action FrameLoop 1 start diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index 5028993e..e798d8af 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -643,7 +643,6 @@ __BRK: sta Location_room_b0-1,y dey bne :- - ; initialize data segment ;;; start action FrameLoop 1 start diff --git a/test/ecs/titles.ecs b/test/ecs/titles.ecs index 6aa20261..70ab5e7a 100644 --- a/test/ecs/titles.ecs +++ b/test/ecs/titles.ecs @@ -86,7 +86,6 @@ demo TitleDemo system Advance on joybutton do once --- - FRAME_END jmp Title2__Start --- end @@ -166,19 +165,19 @@ demo TitleDemo entity [KernelSection, BGColor] const lines = 60 - const bgcolor = $50 + var bgcolor = 0x10 end entity [KernelSection, BGColor] const lines = 10 - const bgcolor = $52 + var bgcolor = 0x30 end // convert -size 48x -gravity center label:"6502\nECS" pbm: | pnmtopnm -plain entity [KernelSection, BGColor, PFColor, Bitmap48] const lines = 2 - const bgcolor = 0x54 - const pfcolor = 0xec + var pfcolor = 0xec + var bgcolor = 0x30 decode vcs_bitmap48 --- 000000000000000000000000000000000000000000011000 000000000000000000000000000000000000000000011000 @@ -229,14 +228,25 @@ demo TitleDemo entity [KernelSection, BGColor] const lines = 10 - const bgcolor = $52 + var bgcolor = 0x30 end entity [KernelSection, BGColor] const lines = 10 - const bgcolor = $50 + var bgcolor = 0x50 end +system Colors + on postframe do foreach [PFColor] +--- + inc {{set pfcolor}} + bne :+ + inc {{set bgcolor}} + inc {{set bgcolor}} +: +--- +end + end system Demo diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index fb3d4079..77aab5f8 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -12,7 +12,6 @@ __NMI: __Reset: __BRK: CLEAN_START - ; initialize data segment ;;; start action Demo 1 start @@ -500,7 +499,6 @@ StaticKernel__kernel__10____exit: ;;; start action Advance 5 joybutton - FRAME_END jmp Title2__Start ;;; end action Advance 5 joybutton @@ -519,6 +517,14 @@ JoyButton__postframe__18__NotPressed: Title__Start = Title::__Start .scope Title2 .zeropage +BGColor_bgcolor_b0: +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +PFColor_pfcolor_b0: +.res 1 TEMP: Kernel48Pixel__2__tmp: .res 1 @@ -530,14 +536,6 @@ KernelSection_lines_b0: .byte 2 .byte 10 .byte 10 -BGColor_bgcolor_b0: -.byte 80 -.byte 82 -.byte 84 -.byte 82 -.byte 80 -PFColor_pfcolor_b0: -.byte 236 Bitmap48_bitmap0_b0: .byte Date: Sat, 19 Feb 2022 17:52:24 -0600 Subject: [PATCH 081/104] ecs: added eid cmd --- src/common/ecs/ecs.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index cd1e4cf6..563588cc 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -775,6 +775,7 @@ class ActionEval { case '$': return this.__local(args); case '^': return this.__use(args); case '#': return this.__arg(args); + case '&': return this.__eid(args); case '<': return this.__get([arg0, '0']); case '>': return this.__get([arg0, '8']); default: @@ -842,6 +843,11 @@ class ActionEval { return this.dialect.indexed_x(ident, index); //TODO? } } + __eid(args: string[]) { + let e = this.scope.getEntityByName(args[0] || '?'); + if (!e) throw new ECSError(`can't find entity named "${args[0]}"`, this.action); + return e.id.toString(); + } __use(args: string[]) { return this.scope.includeResource(args[0]); } From 77676085950bf70f6993c18a6a6d9e7dcc4771f5 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Feb 2022 18:20:03 -0600 Subject: [PATCH 082/104] ecs: query takes [#entity] list --- src/common/ecs/compiler.ts | 20 +++++++++++++++++--- src/common/ecs/ecs.ts | 23 +++++++++++++++++------ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 9d743758..db2274ac 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -170,12 +170,16 @@ export class ECSCompiler extends Tokenizer { if (tok.str == '#') { this.consumeToken(); let reftype = field.dtype == 'ref' ? field as RefType : undefined; - let token = this.expectIdent(); - return { reftype, token }; + return this.parseEntityForwardRef(reftype); } this.compileError(`I expected a ${field.dtype} here.`); throw new Error(); } + parseEntityForwardRef(reftype?: RefType) : ForwardRef { + let token = this.expectIdent(); + return { reftype, token }; + } + parseDataArray() { this.expectToken('['); let arr = this.parseList(this.expectInteger, ','); @@ -283,12 +287,22 @@ export class ECSCompiler extends Tokenizer { if (prefix.type != TokenType.Ident) { this.consumeToken(); } - let cref = this.parseComponentRef(); if (prefix.type == TokenType.Ident) { + let cref = this.parseComponentRef(); q.include.push(cref); } else if (prefix.str == '-') { + let cref = this.parseComponentRef(); if (!q.exclude) q.exclude = []; q.exclude.push(cref); + } else if (prefix.str == '#') { + const scope = this.currentScope; + if (scope == null) { this.internalError(); throw new Error(); } + let eref = this.parseEntityForwardRef(); + this.deferred.push(() => { + let refvalue = this.resolveEntityRef(scope, eref); + if (!q.entities) q.entities = []; + q.entities.push(scope.entities[refvalue]); + }); } else { this.compileError(`Query components may be preceded only by a '-'.`); } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 563588cc..2a1c9448 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -46,6 +46,7 @@ export interface ComponentType extends SourceLocated { export interface Query extends SourceLocated { include: ComponentType[]; // TODO: make ComponentType exclude?: ComponentType[]; + entities?: Entity[]; limit?: number; } @@ -503,10 +504,18 @@ class EntitySet { constructor(scope: EntityScope, query?: Query, a?: EntityArchetype[], e?: Entity[]) { this.scope = scope; if (query) { - this.atypes = scope.em.archetypesMatching(query); - this.entities = scope.entitiesMatching(this.atypes); - if (query.limit) { - this.entities = this.entities.slice(0, query.limit); + if (query.entities) { + this.entities = query.entities.slice(0); + this.atypes = []; + for (let e of this.entities) + if (!this.atypes.includes(e.etype)) + this.atypes.push(e.etype); + } else { + this.atypes = scope.em.archetypesMatching(query); + this.entities = scope.entitiesMatching(this.atypes); + if (query.limit) { + this.entities = this.entities.slice(0, query.limit); + } } } else if (a && e) { this.atypes = a; @@ -751,8 +760,10 @@ class ActionEval { throw new ECSError('unroll is not yet implemented'); } // define properties - props['%elo'] = entities[0].id.toString(); - props['%ehi'] = entities[entities.length - 1].id.toString(); + if (entities.length) { + props['%elo'] = entities[0].id.toString(); + props['%ehi'] = entities[entities.length - 1].id.toString(); + } props['%ecount'] = entities.length.toString(); props['%efullcount'] = fullEntityCount.toString(); // TODO From a444de693b13674e6e8f312e6b1d7e1cf4d0bcc9 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 20 Feb 2022 07:37:51 -0600 Subject: [PATCH 083/104] ecs: field, ref type checking, start cmd --- .gitignore | 14 ++-- src/common/ecs/README.md | 8 ++ src/common/ecs/compiler.ts | 150 ++++++++++++++++++++----------------- src/common/ecs/ecs.ts | 53 ++++++++++--- test/ecs/superman.ecs | 3 +- test/ecs/superman.txt | 6 +- 6 files changed, 143 insertions(+), 91 deletions(-) diff --git a/.gitignore b/.gitignore index 0998cb12..3be955f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,13 @@ *~ node_modules -./local/ -./tests_output/ -./test/output/ +/local/ +/tests_output/ +/test/output/ .DS_Store -./tmp/ -./web/ -./release/ -./gen/ +/tmp/ +/web/ +/release/ +/gen/ config.js chromedriver.log nightwatch.conf.js diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md index 907da05a..73db0182 100644 --- a/src/common/ecs/README.md +++ b/src/common/ecs/README.md @@ -143,3 +143,11 @@ banks need to duplicate code and/or rodata - don't split critical code across banks need bank trampoline macro nested scopes for game modes? (title / demo / play) + +critical data fields +if accessed in critical section, make critical +ignore arrays that aren't referenced + +use DASM for multipass? + +default values for component fields diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index db2274ac..8ca73c01 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -25,8 +25,7 @@ export class ECSCompiler extends Tokenizer { debuginfo = false; constructor( - public readonly em: EntityManager) - { + public readonly em: EntityManager) { super(); //this.includeEOL = true; this.setTokenRules([ @@ -52,7 +51,7 @@ export class ECSCompiler extends Tokenizer { if (obj) (obj as SourceLocated).$loc = tok.$loc; return obj; } - + parseFile(text: string, path: string) { this.tokenizeFile(text, path); while (!this.isEOF()) { @@ -78,6 +77,7 @@ export class ECSCompiler extends Tokenizer { comp.parseFile(text, path); } catch (e) { for (var err of comp.errors) this.errors.push(err); + throw e; } } } @@ -99,12 +99,14 @@ export class ECSCompiler extends Tokenizer { } if (tok.str == 'import') { let tok = this.expectTokenTypes([ECSTokenType.QuotedString]); - let path = tok.str.substring(1, tok.str.length-1); + let path = tok.str.substring(1, tok.str.length - 1); return this.importFile(path); } if (tok.str == 'demo') { let scope = this.parseScope(); scope.isDemo = true; + // TODO: make required + if (this.peekToken().str == 'demo') this.expectToken('demo'); return scope; } if (tok.str == 'comment') { @@ -143,7 +145,7 @@ export class ECSCompiler extends Tokenizer { return { dtype: 'ref', query: this.parseQuery() } as RefType; } if (this.ifToken('array')) { - let index : IntType | undefined = undefined; + let index: IntType | undefined = undefined; if (this.peekToken().type == ECSTokenType.Integer) { index = this.parseDataType() as IntType; } @@ -158,7 +160,7 @@ export class ECSCompiler extends Tokenizer { this.compileError(`I expected a data type here.`); throw new Error(); } - parseDataValue(field: DataField) : DataValue | ForwardRef { + parseDataValue(field: DataField): DataValue | ForwardRef { let tok = this.peekToken(); if (tok.type == 'integer') { return this.expectInteger(); @@ -175,7 +177,7 @@ export class ECSCompiler extends Tokenizer { this.compileError(`I expected a ${field.dtype} here.`); throw new Error(); } - parseEntityForwardRef(reftype?: RefType) : ForwardRef { + parseEntityForwardRef(reftype?: RefType): ForwardRef { let token = this.expectIdent(); return { reftype, token }; } @@ -189,7 +191,7 @@ export class ECSCompiler extends Tokenizer { expectInteger(): number { let s = this.consumeToken().str; - let i : number; + let i: number; if (s.startsWith('$')) i = parseInt(s.substring(1), 16); // hex $... else if (s.startsWith('%')) @@ -205,7 +207,7 @@ export class ECSCompiler extends Tokenizer { let actions: Action[] = []; let system: System = { name, actions }; let cmd; - while ((cmd = this.expectTokens(['on','locals','end']).str) != 'end') { + while ((cmd = this.expectTokens(['on', 'locals', 'end']).str) != 'end') { if (cmd == 'on') { let action = this.annotate(() => this.parseAction(system)); actions.push(action); @@ -225,11 +227,11 @@ export class ECSCompiler extends Tokenizer { this.consumeToken(); tempbytes = this.expectInteger(); } - let system : System = { name, tempbytes, actions: [] }; - let context : ActionContext = { scope: null, system }; + let system: System = { name, tempbytes, actions: [] }; + let context: ActionContext = { scope: null, system }; let text = this.parseCode(context); - let select : SelectType = 'once'; - let action : Action = { text, event: name, select }; + let select: SelectType = 'once'; + let action: Action = { text, event: name, select }; system.actions.push(action); return system; } @@ -240,7 +242,7 @@ export class ECSCompiler extends Tokenizer { this.expectToken('do'); // TODO: include modifiers in error msg const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? - const all_modifiers = ['critical','asc','desc']; // TODO + const all_modifiers = ['critical', 'asc', 'desc']; // TODO let query = undefined; let join = undefined; if (select == 'once') { @@ -261,7 +263,7 @@ export class ECSCompiler extends Tokenizer { if (this.ifToken('fit')) { fitbytes = this.expectInteger(); } - let context : ActionContext = { scope: null, system }; + let context: ActionContext = { scope: null, system }; // parse --- code --- let text = this.parseCode(context); let direction = undefined; @@ -271,7 +273,7 @@ export class ECSCompiler extends Tokenizer { if (modifiers['critical']) (action as ActionWithJoin).critical = true; return action as ActionWithJoin; } - + parseQuery() { let q: Query = { include: [] }; let start = this.expectToken('['); @@ -319,12 +321,12 @@ export class ECSCompiler extends Tokenizer { parseCode(context: ActionContext): string { // TODOActionNode[] { // TODO: add $loc let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); - let code = tok.str.substring(3, tok.str.length-3); + let code = tok.str.substring(3, tok.str.length - 3); // TODO: add after parsing maybe? let lines = code.split('\n'); if (this.debuginfo) this.addDebugInfo(lines, tok.$loc.line); code = lines.join('\n'); - + let acomp = new ECSActionCompiler(context); let nodes = acomp.parseFile(code, this.path); // TODO: return nodes @@ -333,13 +335,13 @@ export class ECSCompiler extends Tokenizer { addDebugInfo(lines: string[], startline: number) { const re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines - for (let i=0; i field.hi) + throw new ECSError(`This "${field.name}" value is out of range, should be between ${field.lo} and ${field.hi}.`); + } else if (field.dtype == 'ref') { + // TODO: allow override if number + let eset = new EntitySet(this, field.query); + if (value < 0 || value >= eset.entities.length) + throw new ECSError(`This "${field.name}" value is out of range for this ref type.`); + } + } + } generateCodeForEvent(event: string, args?: string[], codelabel?: string): string { // find systems that respond to event // and have entities in this scope diff --git a/test/ecs/superman.ecs b/test/ecs/superman.ecs index 137bc1ba..db50c3c7 100644 --- a/test/ecs/superman.ecs +++ b/test/ecs/superman.ecs @@ -159,6 +159,7 @@ end entity NullShape [Bitmap,Colormap] decode vcs_sprite --- +........ 00 --- end @@ -504,7 +505,7 @@ x.x.x.x.x.x.x.x.x.x. .. 06 .. var sprite = #Superdude end entity Slot1 [SpriteSlot] - var sprite = $ff + var sprite = 0 // $ff end using VersatilePlayfield with #Superdude.room diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index e798d8af..e26502e7 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -60,8 +60,9 @@ Bitmap_bitmapdata_b8: .byte >(Bitmap_bitmapdata_e2_b0+31) .byte >(Bitmap_bitmapdata_e3_b0+31) Bitmap_bitmapdata_e1_b0: +.byte 0 Bitmap_height_b0: -.byte 255 +.byte 0 .byte 17 .byte 27 Colormap_colormapdata_b0: @@ -73,6 +74,7 @@ Colormap_colormapdata_b8: .byte >(Colormap_colormapdata_e2_b0+31) .byte >(Colormap_colormapdata_e3_b0+31) Colormap_colormapdata_e1_b0: +.byte 0 Bitmap_bitmapdata_e2_b0: .byte 128 .byte 192 @@ -622,7 +624,7 @@ Main__INITDATA: .byte 60 .byte 90 .byte 0 -.byte 255 +.byte 0 .byte 0 .byte 0 .byte 0 From 07c0ae2f9c454d6d8c7f9d4da8cca58ff7f3ca5e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 20 Feb 2022 10:15:16 -0600 Subject: [PATCH 084/104] ecs: default field values --- src/common/ecs/README.md | 7 ++++++- src/common/ecs/compiler.ts | 10 +++++++--- src/common/ecs/decoder.ts | 19 +++++++++++++++++++ src/common/ecs/ecs.ts | 17 ++++++++++++++--- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md index 73db0182..7dc1c759 100644 --- a/src/common/ecs/README.md +++ b/src/common/ecs/README.md @@ -143,6 +143,7 @@ banks need to duplicate code and/or rodata - don't split critical code across banks need bank trampoline macro nested scopes for game modes? (title / demo / play) +access parent data from child scope critical data fields if accessed in critical section, make critical @@ -150,4 +151,8 @@ ignore arrays that aren't referenced use DASM for multipass? -default values for component fields +processes +take up at least one byte if stateful +might need free list +need jump table? +you'd like to change "mode" from any event diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 8ca73c01..45957533 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -139,7 +139,12 @@ export class ECSCompiler extends Tokenizer { let lo = this.expectInteger(); this.expectToken('..'); let hi = this.expectInteger(); - return { dtype: 'int', lo, hi } as IntType; + // TODO: use default value? + let defvalue; + if (this.ifToken('default')) { + defvalue = this.expectInteger(); + } + return { dtype: 'int', lo, hi, defvalue } as IntType; } if (this.peekToken().str == '[') { return { dtype: 'ref', query: this.parseQuery() } as RefType; @@ -392,8 +397,7 @@ export class ECSCompiler extends Tokenizer { entname = this.expectIdent().str; } let etype = this.parseEntityArchetype(); - let entity = this.currentScope.newEntity(etype); - entity.name = entname; + let entity = this.currentScope.newEntity(etype, entname); let cmd2: string; // TODO: remove init? while ((cmd2 = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') { diff --git a/src/common/ecs/decoder.ts b/src/common/ecs/decoder.ts index 7b2e91e0..51f5beb2 100644 --- a/src/common/ecs/decoder.ts +++ b/src/common/ecs/decoder.ts @@ -73,6 +73,24 @@ export class VCSSpriteDecoder extends LineDecoder { } } +export class VCSBitmapDecoder extends LineDecoder { + parse() { + let height = this.lines.length; + let bitmapdata = new Uint8Array(height); + for (let i=0; i Date: Mon, 21 Feb 2022 09:24:03 -0600 Subject: [PATCH 085/104] ecs: set up for nes --- src/common/ecs/README.md | 28 ++++++++++++++++++++++++++++ src/worker/tools/ecs.ts | 5 +++-- src/worker/workermain.ts | 3 ++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md index 7dc1c759..e461202d 100644 --- a/src/common/ecs/README.md +++ b/src/common/ecs/README.md @@ -156,3 +156,31 @@ take up at least one byte if stateful might need free list need jump table? you'd like to change "mode" from any event + +need constant folding, set arrays from other exprs + +a = [Sprite,-Player] +foreach a do begin + xpos = ypos +end + + on gowest do with x:[Location] +--- + ldy {{ new Promise(resolve => setTimeout(resolve, delay)); // for testing From 37edd8268a9f7a559db94460662511df912ad24f Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 22 Feb 2022 10:35:41 -0600 Subject: [PATCH 086/104] ecs: shared init data --- src/common/ecs/ecs.ts | 54 +++++++++++++++++++++---------------------- test/ecs/superman.txt | 6 ++--- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 6c6bee9a..374d11f0 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -44,7 +44,7 @@ export interface ComponentType extends SourceLocated { } export interface Query extends SourceLocated { - include: ComponentType[]; // TODO: make ComponentType + include: ComponentType[]; exclude?: ComponentType[]; entities?: Entity[]; limit?: number; @@ -256,19 +256,6 @@ export class Dialect_CA65 { {{%code}} ` - // TODO - ASM_MAP_RANGES = ` - txa - pha - lda {{%mapping}},x - jmi @__mapskip - tax - {{%code}} -@__mapskip: - pla - tax -`; - INIT_FROM_ARRAY = ` ldy #{{%nbytes}} : lda {{%src}}-1,y @@ -311,7 +298,6 @@ export class Dialect_CA65 { } endScope(name: string) { return `.endscope\n${this.scopeSymbol(name)} = ${name}::__Start` - // TODO: scope__start = scope::start } scopeSymbol(name: string) { return `${name}__Start`; @@ -340,7 +326,7 @@ export class Dialect_CA65 { if (segtype == 'bss') { return `.zeropage`; } else if (segtype == 'rodata') { - return '.rodata'; // TODO? + return '.rodata'; } else { return `.code`; } @@ -357,11 +343,11 @@ export class Dialect_CA65 { } else { if (b.bitofs == 0) return `.byte <${b.symbol}` else if (b.bitofs == 8) return `.byte >${b.symbol}` - else return `.byte (${b.symbol} >> ${b.bitofs})` // TODO? + else return `.byte ((${b.symbol} >> ${b.bitofs})&255)` } } tempLabel(inst: SystemInstance) { - return `${inst.system.name}__${inst.id}__tmp`; // TODO: multiple instances? + return `${inst.system.name}__${inst.id}__tmp`; } equate(symbol: string, value: string): string { return `${symbol} = ${value}`; @@ -429,11 +415,24 @@ class DataSegment { this.ofs2sym.set(ofs, []); this.ofs2sym.get(ofs)?.push(name); } - // TODO: optimize shared data + findExistingInitData(bytes: Uint8Array) { + for (let i=0; i= 0) { + this.declareSymbol(name, ofs); + } else { + ofs = this.allocateBytes(name, bytes.length); + for (let i = 0; i < bytes.length; i++) { + this.initdata[ofs + i] = bytes[i]; + } } } dump(file: SourceFileExport, dialect: Dialect_CA65) { @@ -461,7 +460,6 @@ class DataSegment { if (ofs !== undefined) { return ofs + entityID - range.elo; } - // TODO: show entity name? throw new ECSError(`cannot find field access for ${access.symbol}`); } getOriginSymbol() { @@ -1175,6 +1173,8 @@ export class EntityScope implements SourceLocated { array = segment.fieldranges[cfname] = { component: c, field: f, elo: i, ehi: i }; } else { array.ehi = i; + if (array.ehi - array.elo + 1 >= 256) + throw new ECSError(`too many entities have field ${cfname}, limit is 256`); } // set default values for entity/field if (ftype == 'init') { @@ -1236,7 +1236,6 @@ export class EntityScope implements SourceLocated { let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); let datasym = this.dialect.datasymbol(c, f, e.id, 0); - // TODO: share shared data segment.allocateInitData(datasym, v); if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; @@ -1278,7 +1277,7 @@ export class EntityScope implements SourceLocated { //console.log(segment.initdata) } allocateInitData(segment: DataSegment) { - if (segment.size == 0) return ''; // TODO: warning for no init data? + if (segment.size == 0) return ''; let initbytes = new Uint8Array(segment.size); let iter = this.iterateEntityFields(this.entities); for (var o = iter.next(); o.value; o = iter.next()) { @@ -1319,7 +1318,6 @@ export class EntityScope implements SourceLocated { getFieldRange(c: ComponentType, fn: string) { return this.bss.getFieldRange(c, fn) || this.rodata.getFieldRange(c, fn); } - // TODO: check type/range of value setConstValue(e: Entity, component: ComponentType, field: DataField, value: DataValue) { this.setConstInitValue(e, component, field, value, 'const'); } @@ -1329,7 +1327,7 @@ export class EntityScope implements SourceLocated { setConstInitValue(e: Entity, component: ComponentType, field: DataField, value: DataValue, type: 'const'|'init') { - this.checkValueType(field, value); + this.checkFieldValue(field, value); let fieldName = field.name; let cfname = mksymbol(component, fieldName); let ecfname = mkscopesymbol(this, component, fieldName); @@ -1342,7 +1340,7 @@ export class EntityScope implements SourceLocated { isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' { return this.fieldtypes[mksymbol(component, fieldName)]; } - checkValueType(field: DataField, value: DataValue) { + checkFieldValue(field: DataField, value: DataValue) { if (field.dtype == 'array') { if (!(value instanceof Uint8Array)) throw new ECSError(`This "${field.name}" value should be an array.`); diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index e26502e7..7446ae34 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -60,6 +60,8 @@ Bitmap_bitmapdata_b8: .byte >(Bitmap_bitmapdata_e2_b0+31) .byte >(Bitmap_bitmapdata_e3_b0+31) Bitmap_bitmapdata_e1_b0: +Colormap_colormapdata_e1_b0: +VersatilePlayfield_data_e4_b0: .byte 0 Bitmap_height_b0: .byte 0 @@ -73,8 +75,6 @@ Colormap_colormapdata_b8: .byte >(Colormap_colormapdata_e1_b0+31) .byte >(Colormap_colormapdata_e2_b0+31) .byte >(Colormap_colormapdata_e3_b0+31) -Colormap_colormapdata_e1_b0: -.byte 0 Bitmap_bitmapdata_e2_b0: .byte 128 .byte 192 @@ -203,8 +203,6 @@ VersatilePlayfield_data_b8: .byte >(VersatilePlayfield_data_e4_b0+-1) .byte >(VersatilePlayfield_data_e5_b0+-1) .byte >(VersatilePlayfield_data_e6_b0+-1) -VersatilePlayfield_data_e4_b0: -.byte 0 VersatilePlayfield_data_e5_b0: .byte 0 .byte 63 From 3d05c0ed1ae7786bde75f41eafc4491013ac66f4 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 22 Feb 2022 10:59:40 -0600 Subject: [PATCH 087/104] ecs: use name2cfpairs, fixed identity thing, test errors --- src/common/ecs/ecs.ts | 23 ++++++++++++----------- src/test/testecs.ts | 18 +++++++++++++----- test/ecs/errors1.ecs | 31 +++++++++++++++++++++++++++++++ test/ecs/errors1.txt | 1 + 4 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 test/ecs/errors1.ecs create mode 100644 test/ecs/errors1.txt diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 374d11f0..530d04e3 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1594,19 +1594,20 @@ export class EntityManager { } defineComponent(ctype: ComponentType) { let existing = this.components[ctype.name]; + // we can defer component definitions, just declare a component with 0 fields? if (existing && existing.fields.length > 0) throw new ECSError(`component ${ctype.name} already defined`, existing); + if (existing) { + existing.fields = ctype.fields; + ctype = 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 }); } - if (existing) { - existing.fields = ctype.fields; - return existing; - } else { - return this.components[ctype.name] = ctype; - } + this.components[ctype.name] = ctype; + return ctype; } defineSystem(system: System) { let existing = this.systems[system.name]; @@ -1669,15 +1670,15 @@ export class EntityManager { return this.systems[name]; } singleComponentWithFieldName(atypes: EntityArchetype[], fieldName: string, where: SourceLocated) { - let components = this.componentsWithFieldName(atypes, fieldName); - // TODO: use name2cfpairs? - if (components.length == 0) { + let cfpairs = this.name2cfpairs[fieldName]; + let filtered = cfpairs.filter(cf => atypes.find(a => a.components.includes(cf.c))); + if (filtered.length == 0) { throw new ECSError(`cannot find component with field "${fieldName}"`, where); } - if (components.length > 1) { + if (filtered.length > 1) { throw new ECSError(`ambiguous field name "${fieldName}"`, where); } - return components[0]; + return filtered[0].c; } toJSON() { return JSON.stringify({ diff --git a/src/test/testecs.ts b/src/test/testecs.ts index bd93b72d..6936fe0c 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -98,11 +98,19 @@ describe('Compiler', function() { return readFileSync(testdir + path, 'utf-8'); } let code = readFileSync(ecspath, 'utf-8'); - compiler.parseFile(code, ecspath); - // TODO: errors - let out = new SourceFileExport(); - em.exportToFile(out); - let outtxt = out.toString(); + var outtxt = ''; + try { + compiler.parseFile(code, ecspath); + // TODO: errors + let out = new SourceFileExport(); + em.exportToFile(out); + outtxt = out.toString(); + } catch (e) { + outtxt = e.toString(); + console.log(e); + } + if (compiler.errors.length) + outtxt = compiler.errors.map(e => `${e.line}:${e.msg}`).join('\n'); let goodtxt = existsSync(goodpath) ? readFileSync(goodpath, 'utf-8') : ''; if (outtxt.trim() != goodtxt.trim()) { let asmpath = '/tmp/' + asmfn; diff --git a/test/ecs/errors1.ecs b/test/ecs/errors1.ecs new file mode 100644 index 00000000..b0ad8cb7 --- /dev/null +++ b/test/ecs/errors1.ecs @@ -0,0 +1,31 @@ + +component KernelSection + lines: 1..255 +end + +component BGColor + color: 0..255 +end + +component FGColor + color: 0..255 +end + +demo Main + entity [KernelSection,BGColor] + const lines = 2 + const color = $18 + end + entity [KernelSection,BGColor,FGColor] + const lines = 2 + const color = $16 + end + entity [KernelSection,FGColor] + const lines = 2 + const color = $16 + end + entity [KernelSection,FGColor] + const lines = 2 + const color = $16 + end +end diff --git a/test/ecs/errors1.txt b/test/ecs/errors1.txt new file mode 100644 index 00000000..29b87eac --- /dev/null +++ b/test/ecs/errors1.txt @@ -0,0 +1 @@ +21:I found more than one field named "color" for this entity. \ No newline at end of file From f7099b8e8f0a2187b22f2af9596487d320162619 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 22 Feb 2022 12:33:57 -0600 Subject: [PATCH 088/104] ecs: enum keyword, entity.field constants --- src/codemirror/ecs.js | 1 + src/common/ecs/compiler.ts | 71 ++++++++++++++++++++++++++++++++++++-- src/common/ecs/ecs.ts | 12 ++++++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 1a893b4c..9d8bcad2 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -19,6 +19,7 @@ var directives_list = [ 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', 'resource', 'const', 'locals', 'var', + 'enum', 'default', 'array', 'baseoffset', 'critical', 'on', 'do', 'emit', 'limit', 'once', 'foreach', 'with', 'join', 'if', ]; diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 45957533..f7bbba57 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -139,11 +139,16 @@ export class ECSCompiler extends Tokenizer { let lo = this.expectInteger(); this.expectToken('..'); let hi = this.expectInteger(); + this.checkLowerLimit(lo, -0x80000000, "lower int range"); + this.checkUpperLimit(hi, 0x7fffffff, "upper int range"); + this.checkUpperLimit(hi-lo, 0xffffffff, "int range"); + this.checkLowerLimit(hi, lo, "int range"); // TODO: use default value? let defvalue; if (this.ifToken('default')) { defvalue = this.expectInteger(); } + // TODO: check types return { dtype: 'int', lo, hi, defvalue } as IntType; } if (this.peekToken().str == '[') { @@ -159,17 +164,67 @@ export class ECSCompiler extends Tokenizer { let baseoffset; if (this.ifToken('baseoffset')) { baseoffset = this.expectInteger(); + this.checkLowerLimit(baseoffset, -32768, "base offset"); + this.checkUpperLimit(baseoffset, 32767, "base offset"); } return { dtype: 'array', index, elem, baseoffset } as ArrayType; } + if (this.ifToken('enum')) { + this.expectToken('['); + let enumtoks = this.parseList(this.parseEnumIdent, ','); + this.expectToken(']'); + if (enumtoks.length == 0) this.compileError(`must define at least one enum`); + let lo = 0; + let hi = enumtoks.length-1; + this.checkLowerLimit(hi, 0, "enum count"); + this.checkUpperLimit(hi, 255, "enum count"); + let enums : {[name:string]:number} = {}; + for (let i=0; i<=hi; i++) + enums[enumtoks[i].str] = i; + // TODO: use default value? + let defvalue; + if (this.ifToken('default')) { + defvalue = this.expectInteger(); + } + return { dtype: 'int', lo, hi, defvalue, enums } as IntType; + } this.compileError(`I expected a data type here.`); throw new Error(); } + parseEnumIdent() { + let tok = this.expectTokenTypes([TokenType.Ident]); + return tok; + } + parseEnumValue(tok: Token, field: IntType) { + if (!field.enums) throw new ECSError(`field is not an enum`); + let value = field.enums[tok.str]; + if (value == null) throw new ECSError(`unknown enum "${tok.str}"`); + return value; + } + parseDataValue(field: DataField): DataValue | ForwardRef { let tok = this.peekToken(); - if (tok.type == 'integer') { + if (tok.type == ECSTokenType.Integer) { return this.expectInteger(); } + if (tok.type == TokenType.Ident && field.dtype == 'int') { + return this.parseEnumValue(this.consumeToken(), field); + } + if (tok.type == TokenType.Ident) { + let entity = this.currentScope?.getEntityByName(tok.str); + if (!entity) + this.compileError('no entity named "${tok.str}"'); + else { + this.consumeToken(); + this.expectToken('.'); + let fieldName = this.expectIdent().str; + let constValue = this.currentScope?.getConstValue(entity, fieldName); + if (constValue == null) + throw new ECSError(`"${fieldName}" is not defined as a constant`, entity); + else + return constValue; + } + } if (tok.str == '[') { // TODO: 16-bit? return new Uint8Array(this.parseDataArray()); @@ -303,7 +358,10 @@ export class ECSCompiler extends Tokenizer { q.exclude.push(cref); } else if (prefix.str == '#') { const scope = this.currentScope; - if (scope == null) { this.internalError(); throw new Error(); } + if (scope == null) { + this.compileError('You can only reference specific entities inside of a scope.'); + throw new Error(); + } let eref = this.parseEntityForwardRef(); this.deferred.push(() => { let refvalue = this.resolveEntityRef(scope, eref); @@ -537,8 +595,17 @@ export class ECSCompiler extends Tokenizer { this.exportToFile(src); return src.toString(); } + + checkUpperLimit(value: number, upper: number, what: string) { + if (value > upper) this.compileError(`This ${what} is too high; must be ${upper} or less`); + } + checkLowerLimit(value: number, lower: number, what: string) { + if (value < lower) this.compileError(`This ${what} is too low; must be ${lower} or more`); + } } +/// + export class ECSActionCompiler extends Tokenizer { constructor( public readonly context: ActionContext) { diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 530d04e3..bf92ebba 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -157,6 +157,7 @@ export interface IntType { lo: number hi: number defvalue?: number + enums?: { [name: string] : number } } export interface ArrayType { @@ -415,6 +416,7 @@ class DataSegment { this.ofs2sym.set(ofs, []); this.ofs2sym.get(ofs)?.push(name); } + // TODO: ordering should not matter, but it does findExistingInitData(bytes: Uint8Array) { for (let i=0; i atypes.find(a => a.components.includes(cf.c))); if (filtered.length == 0) { - throw new ECSError(`cannot find component with field "${fieldName}"`, where); + throw new ECSError(`cannot find component with field "${fieldName}" in this context`, where); } if (filtered.length > 1) { throw new ECSError(`ambiguous field name "${fieldName}"`, where); From 5afd3cf498af315aa1acf2333909472085139536 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 23 Feb 2022 08:43:19 -0600 Subject: [PATCH 089/104] ecs: better subroutine finding --- src/common/ecs/ecs.ts | 51 +++++--- test/ecs/narrow1.txt | 4 +- test/ecs/score.txt | 72 +++++------ test/ecs/sprites.txt | 112 ++++++++--------- test/ecs/sprites1.txt | 100 +++++++-------- test/ecs/superman.txt | 168 ++++++++++++------------- test/ecs/titles.txt | 284 ++++++++++++++++++++++-------------------- test/ecs/vcs1.txt | 56 ++++----- test/ecs/vcslib.txt | 60 ++++----- 9 files changed, 470 insertions(+), 437 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index bf92ebba..038685f3 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -623,6 +623,7 @@ class ActionEval { entities : Entity[]; tmplabel = ''; label : string; + seq : number; //used = new Set(); // TODO constructor( @@ -655,7 +656,8 @@ class ActionEval { //let query = (this.action as ActionWithQuery).query; //TODO? if (query && this.entities.length == 0) //throw new ECSError(`query doesn't match any entities`, query); // TODO - this.label = `${this.instance.system.name}__${action.event}__${this.em.seq++}`; + this.seq = this.em.seq++; + this.label = `${this.instance.system.name}__${action.event}__${this.seq}`; } begin() { let state = this.scope.state = Object.assign(new ActionCPUState(), this.scope.state); @@ -1048,7 +1050,7 @@ class ActionEval { isSubroutineSized(code: string) { // TODO? if (code.length > 20000) return false; - if (code.split('.dbg line').length >= 4) return true; + if (code.split('\n ').length >= 4) return true; // TODO: :^/ return false; } } @@ -1057,10 +1059,10 @@ class EventCodeStats { constructor( public readonly inst: SystemInstance, public readonly action: Action, - public readonly code: string, - public readonly symbol: string, + public readonly eventcode: string ) { } - count = 0; + labels : string[] = []; + count : number = 0; } export class EntityScope implements SourceLocated { @@ -1080,7 +1082,7 @@ export class EntityScope implements SourceLocated { filePath = ''; eventSeq : number; - eventStats : { [key:string] : EventCodeStats }; + eventCodeStats : { [code:string] : EventCodeStats }; inCritical = 0; constructor( @@ -1402,19 +1404,21 @@ export class EntityScope implements SourceLocated { let eventcode = codeeval.codeToString(); if (action.critical) this.inCritical--; if (!this.inCritical && codeeval.isSubroutineSized(eventcode)) { + let normcode = this.normalizeCode(eventcode, action); // TODO: label rewriting messes this up - let estats = this.eventStats[eventcode]; + let estats = this.eventCodeStats[normcode]; if (!estats) { - estats = this.eventStats[eventcode] = new EventCodeStats( - inst, action, eventcode, codeeval.label); + estats = this.eventCodeStats[normcode] = new EventCodeStats( + inst, action, eventcode); } + estats.labels.push(codeeval.label); estats.count++; if (action.critical) estats.count++; // always make critical event subroutines } let s = ''; - s += this.dialect.comment(`start action ${sys.name} ${inst.id} ${event}`); // TODO + s += this.dialect.comment(`start action ${codeeval.label}`); s += eventcode; - s += this.dialect.comment(`end action ${sys.name} ${inst.id} ${event}`); + s += this.dialect.comment(`end action ${codeeval.label}`); code += s; // TODO: check that this happens once? codeeval.end(); @@ -1426,6 +1430,11 @@ export class EntityScope implements SourceLocated { } return code; } + normalizeCode(code: string, action: Action) { + // TODO: use dialect to help with this + code = code.replace(/(\w+__\w+__)(\d+)(\w+)/g, (z,a,b,c) => a+c); + return code; + } getSystemStats(inst: SystemInstance) : SystemStats { let stats = this.sysstats.get(inst); if (!stats) { @@ -1489,7 +1498,7 @@ export class EntityScope implements SourceLocated { } private generateCode() { this.eventSeq = 0; - this.eventStats = {}; + this.eventCodeStats = {}; let isMainScope = this.parent == null; let start; let initsys = this.em.getSystemByName('Init'); @@ -1517,7 +1526,7 @@ export class EntityScope implements SourceLocated { // TODO: doesn't work with nested subroutines? // TODO: doesn't work between scopes let allsubs : string[] = []; - for (let stats of Object.values(this.eventStats)) { + for (let stats of Object.values(this.eventCodeStats)) { if (stats.count > 1) { if (allsubs.length == 0) { allsubs = [ @@ -1527,12 +1536,22 @@ export class EntityScope implements SourceLocated { } else if (stats.action.fitbytes) { allsubs.push(this.dialect.alignIfLessThan(stats.action.fitbytes)); } - code = (code as any).replaceAll(stats.code, this.dialect.call(stats.symbol)); - let substart = stats.symbol; + let subcall = this.dialect.call(stats.labels[0]); + for (let label of stats.labels) { + // TODO: use dialect + let startdelim = `;;; start action ${label}` + let enddelim = `;;; end action ${label}` + let istart = code.indexOf(startdelim); + let iend = code.indexOf(enddelim, istart); + if (istart >= 0 && iend > istart) { + code = code.substring(0, istart) + subcall + code.substring(iend + enddelim.length); + } + } + let substart = stats.labels[0]; let sublines = [ this.dialect.segment('rodata'), this.dialect.label(substart), - stats.code, + stats.eventcode, this.dialect.return(), ]; if (stats.action.critical) { diff --git a/test/ecs/narrow1.txt b/test/ecs/narrow1.txt index 3073830d..f21762c1 100644 --- a/test/ecs/narrow1.txt +++ b/test/ecs/narrow1.txt @@ -19,7 +19,7 @@ __Start: dey bne :- -;;; start action move 1 start +;;; start action move__start__1 ldx #0 move__start__1____each: @@ -31,7 +31,7 @@ move__start__1____each: jne move__start__1____each move__start__1____exit: -;;; end action move 1 start +;;; end action move__start__1 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/score.txt b/test/ecs/score.txt index a9470893..9a90a1a1 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -49,7 +49,7 @@ Main__INITDATA: .byte 86 __Start: -;;; start action Init 9 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .macpack longbranch @@ -65,12 +65,12 @@ __BRK: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__2 FrameLoop__start__2__NextFrame: FRAME_START -;;; start action Kernel6Digit 2 preframe +;;; start action Kernel6Digit__preframe__3 Digit0 = Kernel6Digit__2__tmp+0 Digit1 = Kernel6Digit__2__tmp+2 @@ -118,11 +118,11 @@ Kernel6Digit__preframe__3__Loop: dey ; next BCD value bpl Kernel6Digit__preframe__3__Loop ; repeat until < 0 -;;; end action Kernel6Digit 2 preframe +;;; end action Kernel6Digit__preframe__3 KERNEL_START -;;; start action Kernel6Digit 2 kernel +;;; start action Kernel6Digit__kernel__4 lda PFColor_pfcolor_b0 sta COLUP0 @@ -146,9 +146,9 @@ Kernel6Digit__preframe__3__Loop: sta VDELP0 sta VDELP1 -;;; end action Kernel6Digit 2 kernel +;;; end action Kernel6Digit__kernel__4 -;;; start action Kernel6Digit 2 kernel +;;; start action Kernel6Digit__kernel__5 ; Display the resulting 48x8 bitmap ; using the Digit0-5 pointers. @@ -191,9 +191,9 @@ Kernel6Digit__kernel__5__BigLoop: sta GRP1 sta COLUBK -;;; end action Kernel6Digit 2 kernel +;;; end action Kernel6Digit__kernel__5 -;;; start action Kernel2Digit 4 kernel +;;; start action Kernel2Digit__kernel__6 lda #$02 sta CTRLPF @@ -204,9 +204,9 @@ Kernel6Digit__kernel__5__BigLoop: lda PFColor_pfcolor_b0+1 sta COLUP1 -;;; end action Kernel2Digit 4 kernel +;;; end action Kernel2Digit__kernel__6 -;;; start action Kernel2Digit 4 kernel +;;; start action Kernel2Digit__kernel__7 lda #7 sta Kernel2Digit__4__tmp+0 @@ -214,7 +214,7 @@ Kernel2Digit__kernel__7__Loop: ldx #0 sta WSYNC -;;; start action Kernel2Digit 4 compute2digit +;;; start action Kernel2Digit__compute2digit__8 lda Kernel2Digit__4__tmp+1 ; load 1st pf sta PF1 ; store 1st pf @@ -226,14 +226,14 @@ Kernel2Digit__kernel__7__Loop: asl asl -;;; start action Kernel2Digit 4 fetchdigit +;;; start action Kernel2Digit__fetchdigit__9 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit 4 fetchdigit +;;; end action Kernel2Digit__fetchdigit__9 and #$0f ldy Kernel2Digit__4__tmp+2 ; load 2nd pf @@ -244,24 +244,24 @@ Kernel2Digit__kernel__7__Loop: lsr sty PF1 ; store 2nd pf -;;; start action Kernel2Digit 4 fetchdigit +;;; start action Kernel2Digit__fetchdigit__10 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit 4 fetchdigit +;;; end action Kernel2Digit__fetchdigit__10 and #$f0 ora Kernel2Digit__4__tmp+1 + 0 sta Kernel2Digit__4__tmp+1 + 0 -;;; end action Kernel2Digit 4 compute2digit +;;; end action Kernel2Digit__compute2digit__8 inx -;;; start action Kernel2Digit 4 compute2digit +;;; start action Kernel2Digit__compute2digit__11 lda Kernel2Digit__4__tmp+1 ; load 1st pf sta PF1 ; store 1st pf @@ -273,14 +273,14 @@ Kernel2Digit__kernel__7__Loop: asl asl -;;; start action Kernel2Digit 4 fetchdigit +;;; start action Kernel2Digit__fetchdigit__12 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit 4 fetchdigit +;;; end action Kernel2Digit__fetchdigit__12 and #$0f ldy Kernel2Digit__4__tmp+2 ; load 2nd pf @@ -291,20 +291,20 @@ Kernel2Digit__kernel__7__Loop: lsr sty PF1 ; store 2nd pf -;;; start action Kernel2Digit 4 fetchdigit +;;; start action Kernel2Digit__fetchdigit__13 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit 4 fetchdigit +;;; end action Kernel2Digit__fetchdigit__13 and #$f0 ora Kernel2Digit__4__tmp+1 + 1 sta Kernel2Digit__4__tmp+1 + 1 -;;; end action Kernel2Digit 4 compute2digit +;;; end action Kernel2Digit__compute2digit__11 ; playfield dec Kernel2Digit__4__tmp+0 @@ -312,19 +312,19 @@ Kernel2Digit__kernel__7__Loop: ; dex ; stx PF1 -;;; end action Kernel2Digit 4 kernel +;;; end action Kernel2Digit__kernel__7 KERNEL_END -;;; start action JoyButton 6 postframe +;;; start action JoyButton__postframe__14 lda INPT4 ;read button input bmi JoyButton__postframe__14__NotPressed -;;; start action IncScore 8 joybutton +;;; start action IncScore__joybutton__15 -;;; start action BCDMath 7 AddBCD4 +;;; start action BCDMath__AddBCD4__16 .ifnblank $0210 lda #<$0210 @@ -345,20 +345,20 @@ Kernel2Digit__kernel__7__Loop: sta BCDScore6_digits_b16 cld ; exit BCD mode -;;; end action BCDMath 7 AddBCD4 +;;; end action BCDMath__AddBCD4__16 -;;; end action IncScore 8 joybutton +;;; end action IncScore__joybutton__15 JoyButton__postframe__14__NotPressed: -;;; end action JoyButton 6 postframe +;;; end action JoyButton__postframe__14 FRAME_END jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop 1 start +;;; end action FrameLoop__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -366,11 +366,11 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 9 main_init +;;; end action Init__main_init__1 FontTable: -;;; start action FontTable 3 FontTable +;;; start action FontTable__FontTable__17 ; Font table for digits 0-9 (8x8 pixels) ;;{w:8,h:8,count:10,brev:1,flip:1};; @@ -380,11 +380,11 @@ FontTable: .byte $00,$3c,$66,$66,$7c,$60,$66,$3c,$00,$18,$18,$18,$18,$0c,$66,$7e .byte $00,$3c,$66,$66,$3c,$66,$66,$3c,$00,$3c,$66,$06,$3e,$66,$66,$3c -;;; end action FontTable 3 FontTable +;;; end action FontTable__FontTable__17 FontTablePF: -;;; start action FontTablePF 5 FontTablePF +;;; start action FontTablePF__FontTablePF__18 ; Font table for digits 0-9 (4x8 pixels) ;;{w:8,h:8,count:10,brev:1,flip:1};; @@ -400,7 +400,7 @@ FontTablePF: .byte $00,$00,$EE,$22,$EE,$AA,$EE,$00 ;; -;;; end action FontTablePF 5 FontTablePF +;;; end action FontTablePF__FontTablePF__18 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index 8a5f0186..6095ad23 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -116,7 +116,7 @@ Main__INITDATA: .byte 3 __Start: -;;; start action Init 10 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .macpack longbranch @@ -132,12 +132,12 @@ __BRK: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__2 FrameLoop__start__2__NextFrame: FRAME_START -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__3 .define KLINES #192 .define KPAD 32 @@ -152,9 +152,9 @@ FrameLoop__start__2__NextFrame: sta Kernel2Sprite__2__tmp+6 sta Kernel2Sprite__2__tmp+7 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__3 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__4 ldy #0 Kernel2Sprite__preframe__4____each: @@ -208,9 +208,9 @@ Kernel2Sprite__preframe__4__nosprite: jne Kernel2Sprite__preframe__4____each Kernel2Sprite__preframe__4____exit: -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__4 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__5 ; shuffle pointers into (MSB, LSB) byte order ; L0 L1 H0 H1 -> L0 H0 L1 H1 @@ -223,26 +223,26 @@ Kernel2Sprite__preframe__4____exit: sty Kernel2Sprite__2__tmp+5 sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__5 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__6 lda #162 sta COLUBK -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__6 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__7 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__7 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__8 sta HMCLR -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__8 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__9 ldy #0 SetXPos__preframe__9____each: @@ -250,7 +250,7 @@ SetXPos__preframe__9____each: lda HasXpos_xpos_b0,x -;;; start action SetHorizPos 7 SetHorizPos +;;; start action SetHorizPos__SetHorizPos__10 ; SetHorizPos routine ; A = X coordinate @@ -269,7 +269,7 @@ SetHorizPos__SetHorizPos__10__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos 7 SetHorizPos +;;; end action SetHorizPos__SetHorizPos__10 iny @@ -277,36 +277,36 @@ SetHorizPos__SetHorizPos__10__DivideLoop: jne SetXPos__preframe__9____each SetXPos__preframe__9____exit: -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__9 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__11 -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__11 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__12 sta WSYNC sta HMOVE -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__12 KERNEL_START -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__13 ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__13 -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__14 ldy #192 Kernel2Sprite__kernel__14__LVScan: -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__15 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height @@ -334,15 +334,15 @@ Kernel2Sprite__scanline__15__DoDraw2: lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__15 -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__16 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__16 dey ; next scanline -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__17 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height @@ -370,18 +370,18 @@ Kernel2Sprite__scanline__17__DoDraw2: lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__17 -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__18 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__18 dey ; next scanline bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__14 -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__19 lda #0 sta GRP0 @@ -389,19 +389,19 @@ Kernel2Sprite__scanline__17__DoDraw2: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__19 KERNEL_END -;;; start action Joystick 3 postframe +;;; start action Joystick__postframe__20 ; 2 control inputs share a single byte, 4 bits each lda SWCHA sta Joystick__3__tmp+0 -;;; end action Joystick 3 postframe +;;; end action Joystick__postframe__20 -;;; start action Joystick 3 postframe +;;; start action Joystick__postframe__21 ldx #0 Joystick__postframe__21____each: @@ -409,7 +409,7 @@ Joystick__postframe__21____each: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveRight -;;; start action MoveJoyX 4 joyright +;;; start action MoveJoyX__joyright__22 lda HasXpos_xpos_b0,x clc @@ -419,13 +419,13 @@ Joystick__postframe__21____each: sta HasXpos_xpos_b0,x MoveJoyX__joyright__22__nomove: -;;; end action MoveJoyX 4 joyright +;;; end action MoveJoyX__joyright__22 Joystick__postframe__21__SkipMoveRight: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveLeft -;;; start action MoveJoyX 4 joyleft +;;; start action MoveJoyX__joyleft__23 lda HasXpos_xpos_b0,x sec @@ -434,13 +434,13 @@ Joystick__postframe__21__SkipMoveRight: sta HasXpos_xpos_b0,x MoveJoyX__joyleft__23__nomove: -;;; end action MoveJoyX 4 joyleft +;;; end action MoveJoyX__joyleft__23 Joystick__postframe__21__SkipMoveLeft: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveDown -;;; start action MoveJoyY 5 joydown +;;; start action MoveJoyY__joydown__24 lda HasYpos_ypos_b0,x clc @@ -450,13 +450,13 @@ Joystick__postframe__21__SkipMoveLeft: sta HasYpos_ypos_b0,x MoveJoyY__joydown__24__nomove: -;;; end action MoveJoyY 5 joydown +;;; end action MoveJoyY__joydown__24 Joystick__postframe__21__SkipMoveDown: asl Joystick__3__tmp+0 bcs Joystick__postframe__21__SkipMoveUp -;;; start action MoveJoyY 5 joyup +;;; start action MoveJoyY__joyup__25 lda HasYpos_ypos_b0,x sec @@ -465,7 +465,7 @@ Joystick__postframe__21__SkipMoveDown: sta HasYpos_ypos_b0,x MoveJoyY__joyup__25__nomove: -;;; end action MoveJoyY 5 joyup +;;; end action MoveJoyY__joyup__25 Joystick__postframe__21__SkipMoveUp: @@ -474,9 +474,9 @@ Joystick__postframe__21__SkipMoveUp: jne Joystick__postframe__21____each Joystick__postframe__21____exit: -;;; end action Joystick 3 postframe +;;; end action Joystick__postframe__21 -;;; start action SpriteShuffler 8 postframe +;;; start action SpriteShuffler__postframe__26 ; load two sprite slots at left side of array lda SpriteSlot_sprite_b0 @@ -497,16 +497,16 @@ SpriteShuffler__postframe__26__loop: lda SpriteShuffler__8__tmp+1 sta SpriteSlot_sprite_b0+4-1 -;;; end action SpriteShuffler 8 postframe +;;; end action SpriteShuffler__postframe__26 -;;; start action SpriteHider 9 postframe +;;; start action SpriteHider__postframe__27 lda #4-1 sta SpriteHider__9__tmp+0 -;;; end action SpriteHider 9 postframe +;;; end action SpriteHider__postframe__27 -;;; start action SpriteHider 9 postframe +;;; start action SpriteHider__postframe__28 ldy #0 SpriteHider__postframe__28____each: @@ -531,13 +531,13 @@ SpriteHider__postframe__28__skip: jne SpriteHider__postframe__28____each SpriteHider__postframe__28____exit: -;;; end action SpriteHider 9 postframe +;;; end action SpriteHider__postframe__28 FRAME_END jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop 1 start +;;; end action FrameLoop__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -545,7 +545,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 10 main_init +;;; end action Init__main_init__1 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 62363c54..fb6ce8c8 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -116,7 +116,7 @@ Main__INITDATA: .byte 0 __Start: -;;; start action Init 10 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .macpack longbranch @@ -132,19 +132,19 @@ __BRK: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__2 FrameLoop__start__2__NextFrame: FRAME_START -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__3 .define KLINES #192 .define KPAD 32 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__3 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__4 ldy #0 Kernel2Sprite__preframe__4____each: @@ -194,9 +194,9 @@ Kernel2Sprite__preframe__4____each: jne Kernel2Sprite__preframe__4____each Kernel2Sprite__preframe__4____exit: -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__4 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__5 ; L0 L1 H0 H1 -> L0 H0 L1 H1 lda Kernel2Sprite__2__tmp+1 @@ -208,26 +208,26 @@ Kernel2Sprite__preframe__4____exit: sty Kernel2Sprite__2__tmp+5 sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__5 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__6 lda #162 sta COLUBK -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__6 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__7 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__7 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__8 sta HMCLR -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__8 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__9 ldy #0 SetXPos__preframe__9____each: @@ -235,7 +235,7 @@ SetXPos__preframe__9____each: lda HasXpos_xpos_b0,x -;;; start action SetHorizPos 7 SetHorizPos +;;; start action SetHorizPos__SetHorizPos__10 ; SetHorizPos routine ; A = X coordinate @@ -254,7 +254,7 @@ SetHorizPos__SetHorizPos__10__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos 7 SetHorizPos +;;; end action SetHorizPos__SetHorizPos__10 iny @@ -262,31 +262,31 @@ SetHorizPos__SetHorizPos__10__DivideLoop: jne SetXPos__preframe__9____each SetXPos__preframe__9____exit: -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__9 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__11 -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__11 -;;; start action SetXPos 6 preframe +;;; start action SetXPos__preframe__12 sta WSYNC sta HMOVE -;;; end action SetXPos 6 preframe +;;; end action SetXPos__preframe__12 KERNEL_START -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__13 ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__13 -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__14 ; define macro for each line .macro Kernel2Sprite__kernel__14__DrawLine do_wsync @@ -322,9 +322,9 @@ DoDraw2: ldy #192 Kernel2Sprite__kernel__14__LVScan: -;;; start action Kernel2Sprite 2 scanline1 +;;; start action Kernel2Sprite__scanline1__15 -;;; end action Kernel2Sprite 2 scanline1 +;;; end action Kernel2Sprite__scanline1__15 Kernel2Sprite__kernel__14__DrawLine 1 ; macro: draw scanline w/ WSYNC dey ; next scanline @@ -333,9 +333,9 @@ Kernel2Sprite__kernel__14__LVScan: dey ; next scanline bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__14 -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__16 lda #0 sta GRP0 @@ -343,19 +343,19 @@ Kernel2Sprite__kernel__14__LVScan: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__16 KERNEL_END -;;; start action Joystick 3 postframe +;;; start action Joystick__postframe__17 ; 2 control inputs share a single byte, 4 bits each lda SWCHA sta Joystick__3__tmp+0 -;;; end action Joystick 3 postframe +;;; end action Joystick__postframe__17 -;;; start action Joystick 3 postframe +;;; start action Joystick__postframe__18 ldx #0 Joystick__postframe__18____each: @@ -363,7 +363,7 @@ Joystick__postframe__18____each: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveRight -;;; start action MoveJoyX 4 joyright +;;; start action MoveJoyX__joyright__19 lda HasXpos_xpos_b0,x clc @@ -373,13 +373,13 @@ Joystick__postframe__18____each: sta HasXpos_xpos_b0,x MoveJoyX__joyright__19__nomove: -;;; end action MoveJoyX 4 joyright +;;; end action MoveJoyX__joyright__19 Joystick__postframe__18__SkipMoveRight: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveLeft -;;; start action MoveJoyX 4 joyleft +;;; start action MoveJoyX__joyleft__20 lda HasXpos_xpos_b0,x sec @@ -388,13 +388,13 @@ Joystick__postframe__18__SkipMoveRight: sta HasXpos_xpos_b0,x MoveJoyX__joyleft__20__nomove: -;;; end action MoveJoyX 4 joyleft +;;; end action MoveJoyX__joyleft__20 Joystick__postframe__18__SkipMoveLeft: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveDown -;;; start action MoveJoyY 5 joydown +;;; start action MoveJoyY__joydown__21 lda HasYpos_ypos_b0,x clc @@ -404,13 +404,13 @@ Joystick__postframe__18__SkipMoveLeft: sta HasYpos_ypos_b0,x MoveJoyY__joydown__21__nomove: -;;; end action MoveJoyY 5 joydown +;;; end action MoveJoyY__joydown__21 Joystick__postframe__18__SkipMoveDown: asl Joystick__3__tmp+0 bcs Joystick__postframe__18__SkipMoveUp -;;; start action MoveJoyY 5 joyup +;;; start action MoveJoyY__joyup__22 lda HasYpos_ypos_b0,x sec @@ -419,7 +419,7 @@ Joystick__postframe__18__SkipMoveDown: sta HasYpos_ypos_b0,x MoveJoyY__joyup__22__nomove: -;;; end action MoveJoyY 5 joyup +;;; end action MoveJoyY__joyup__22 Joystick__postframe__18__SkipMoveUp: @@ -428,9 +428,9 @@ Joystick__postframe__18__SkipMoveUp: jne Joystick__postframe__18____each Joystick__postframe__18____exit: -;;; end action Joystick 3 postframe +;;; end action Joystick__postframe__18 -;;; start action SpriteShuffler 8 postframe +;;; start action SpriteShuffler__postframe__23 ; load two sprite slots at left side of array lda SpriteSlot_sprite_b0 @@ -451,16 +451,16 @@ SpriteShuffler__postframe__23__loop: lda SpriteShuffler__8__tmp+1 sta SpriteSlot_sprite_b0+4-1 -;;; end action SpriteShuffler 8 postframe +;;; end action SpriteShuffler__postframe__23 -;;; start action SpriteHider 9 postframe +;;; start action SpriteHider__postframe__24 lda #4-1 sta SpriteHider__9__tmp+0 -;;; end action SpriteHider 9 postframe +;;; end action SpriteHider__postframe__24 -;;; start action SpriteHider 9 postframe +;;; start action SpriteHider__postframe__25 ldy #0 SpriteHider__postframe__25____each: @@ -485,13 +485,13 @@ SpriteHider__postframe__25__skip: jne SpriteHider__postframe__25____each SpriteHider__postframe__25____exit: -;;; end action SpriteHider 9 postframe +;;; end action SpriteHider__postframe__25 FRAME_END jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop 1 start +;;; end action FrameLoop__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -499,7 +499,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 10 main_init +;;; end action Init__main_init__1 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index 7446ae34..07f8b8e4 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -628,7 +628,7 @@ Main__INITDATA: .byte 0 __Start: -;;; start action Init 11 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .macpack longbranch @@ -644,12 +644,12 @@ __BRK: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__2 FrameLoop__start__2__NextFrame: FRAME_START -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__3 .define KLINES #192 .define KPAD 32 @@ -664,9 +664,9 @@ FrameLoop__start__2__NextFrame: sta Kernel2Sprite__2__tmp+6 sta Kernel2Sprite__2__tmp+7 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__3 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__4 ldy #0 Kernel2Sprite__preframe__4____each: @@ -720,9 +720,9 @@ Kernel2Sprite__preframe__4__nosprite: jne Kernel2Sprite__preframe__4____each Kernel2Sprite__preframe__4____exit: -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__4 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__5 ; shuffle pointers into (MSB, LSB) byte order ; L0 L1 H0 H1 -> L0 H0 L1 H1 @@ -735,26 +735,26 @@ Kernel2Sprite__preframe__4____exit: sty Kernel2Sprite__2__tmp+5 sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__5 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__6 lda #162 sta COLUBK -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__6 -;;; start action Kernel2Sprite 2 preframe +;;; start action Kernel2Sprite__preframe__7 -;;; end action Kernel2Sprite 2 preframe +;;; end action Kernel2Sprite__preframe__7 -;;; start action SetXPos 8 preframe +;;; start action SetXPos__preframe__8 sta HMCLR -;;; end action SetXPos 8 preframe +;;; end action SetXPos__preframe__8 -;;; start action SetXPos 8 preframe +;;; start action SetXPos__preframe__9 ldy #0 SetXPos__preframe__9____each: @@ -762,7 +762,7 @@ SetXPos__preframe__9____each: lda HasXpos_xpos_b0,x -;;; start action SetHorizPos 9 SetHorizPos +;;; start action SetHorizPos__SetHorizPos__10 ; SetHorizPos routine ; A = X coordinate @@ -781,7 +781,7 @@ SetHorizPos__SetHorizPos__10__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos 9 SetHorizPos +;;; end action SetHorizPos__SetHorizPos__10 iny @@ -789,20 +789,20 @@ SetHorizPos__SetHorizPos__10__DivideLoop: jne SetXPos__preframe__9____each SetXPos__preframe__9____exit: -;;; end action SetXPos 8 preframe +;;; end action SetXPos__preframe__9 -;;; start action SetXPos 8 preframe +;;; start action SetXPos__preframe__11 -;;; end action SetXPos 8 preframe +;;; end action SetXPos__preframe__11 -;;; start action SetXPos 8 preframe +;;; start action SetXPos__preframe__12 sta WSYNC sta HMOVE -;;; end action SetXPos 8 preframe +;;; end action SetXPos__preframe__12 -;;; start action VersatilePlayfield 10 preframe +;;; start action VersatilePlayfield__preframe__13 ldx Location_room_b0+0 @@ -812,25 +812,25 @@ SetXPos__preframe__9____exit: sta VersatilePlayfield__10__tmp+1 -;;; end action VersatilePlayfield 10 preframe +;;; end action VersatilePlayfield__preframe__13 KERNEL_START -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__14 ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__14 -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__15 ldy #192 Kernel2Sprite__kernel__15__LVScan: -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__16 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height @@ -858,33 +858,33 @@ Kernel2Sprite__scanline__16__DoDraw2: lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__16 -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__17 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__17 -;;; start action VersatilePlayfield 10 scanline +;;; start action VersatilePlayfield__scanline__18 .if 0 = 0 lda (VersatilePlayfield__10__tmp+0),y tax .endif -;;; end action VersatilePlayfield 10 scanline +;;; end action VersatilePlayfield__scanline__18 -;;; start action VersatilePlayfield 10 scanline +;;; start action VersatilePlayfield__scanline__19 .if 0 = 1 lda (VersatilePlayfield__10__tmp+0),y sta $00,x .endif -;;; end action VersatilePlayfield 10 scanline +;;; end action VersatilePlayfield__scanline__19 dey ; next scanline -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__20 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height @@ -912,36 +912,36 @@ Kernel2Sprite__scanline__20__DoDraw2: lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__20 -;;; start action Kernel2Sprite 2 scanline +;;; start action Kernel2Sprite__scanline__21 -;;; end action Kernel2Sprite 2 scanline +;;; end action Kernel2Sprite__scanline__21 -;;; start action VersatilePlayfield 10 scanline +;;; start action VersatilePlayfield__scanline__22 .if 1 = 0 lda (VersatilePlayfield__10__tmp+0),y tax .endif -;;; end action VersatilePlayfield 10 scanline +;;; end action VersatilePlayfield__scanline__22 -;;; start action VersatilePlayfield 10 scanline +;;; start action VersatilePlayfield__scanline__23 .if 1 = 1 lda (VersatilePlayfield__10__tmp+0),y sta $00,x .endif -;;; end action VersatilePlayfield 10 scanline +;;; end action VersatilePlayfield__scanline__23 dey ; next scanline bne Kernel2Sprite__kernel__15__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__15 -;;; start action Kernel2Sprite 2 kernel +;;; start action Kernel2Sprite__kernel__24 lda #0 sta GRP0 @@ -949,32 +949,32 @@ Kernel2Sprite__scanline__20__DoDraw2: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite 2 kernel +;;; end action Kernel2Sprite__kernel__24 KERNEL_END -;;; start action Joystick 3 postframe +;;; start action Joystick__postframe__25 ; 2 control inputs share a single byte, 4 bits each lda SWCHA sta Joystick__3__tmp+0 -;;; end action Joystick 3 postframe +;;; end action Joystick__postframe__25 -;;; start action Joystick 3 postframe +;;; start action Joystick__postframe__26 asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveRight -;;; start action JoyFaceDirection 4 joyright +;;; start action JoyFaceDirection__joyright__27 lda Sprite_plyrflags_b0 and #$f7 sta Sprite_plyrflags_b0 -;;; end action JoyFaceDirection 4 joyright +;;; end action JoyFaceDirection__joyright__27 -;;; start action SuperFly 5 joyright +;;; start action SuperFly__joyright__28 lda HasXpos_xpos_b0 clc @@ -982,58 +982,58 @@ Kernel2Sprite__scanline__20__DoDraw2: cmp #142 jcc SuperFly__joyright__28__nomove -;;; start action SuperFly 5 goeast +;;; start action SuperFly__goeast__29 ldy Location_room_b0 lda Room_east_b0,y sta Location_room_b0 -;;; end action SuperFly 5 goeast +;;; end action SuperFly__goeast__29 lda #2 SuperFly__joyright__28__nomove: sta HasXpos_xpos_b0 -;;; end action SuperFly 5 joyright +;;; end action SuperFly__joyright__28 Joystick__postframe__26__SkipMoveRight: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveLeft -;;; start action JoyFaceDirection 4 joyleft +;;; start action JoyFaceDirection__joyleft__30 lda Sprite_plyrflags_b0 ora #$08 sta Sprite_plyrflags_b0 -;;; end action JoyFaceDirection 4 joyleft +;;; end action JoyFaceDirection__joyleft__30 -;;; start action SuperFly 5 joyleft +;;; start action SuperFly__joyleft__31 lda HasXpos_xpos_b0 sec sbc #2 jcs SuperFly__joyleft__31__nomove -;;; start action SuperFly 5 gowest +;;; start action SuperFly__gowest__32 ldy Location_room_b0 lda Room_west_b0,y sta Location_room_b0 -;;; end action SuperFly 5 gowest +;;; end action SuperFly__gowest__32 lda #142 SuperFly__joyleft__31__nomove: sta HasXpos_xpos_b0 -;;; end action SuperFly 5 joyleft +;;; end action SuperFly__joyleft__31 Joystick__postframe__26__SkipMoveLeft: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveDown -;;; start action SuperFly 5 joydown +;;; start action SuperFly__joydown__33 lda HasYpos_ypos_b0 clc @@ -1041,64 +1041,64 @@ Joystick__postframe__26__SkipMoveLeft: cmp #220 jcc SuperFly__joydown__33__nomove -;;; start action SuperFly 5 gosouth +;;; start action SuperFly__gosouth__34 ldy Location_room_b0 lda Room_south_b0,y sta Location_room_b0 -;;; end action SuperFly 5 gosouth +;;; end action SuperFly__gosouth__34 lda #2 SuperFly__joydown__33__nomove: sta HasYpos_ypos_b0 -;;; end action SuperFly 5 joydown +;;; end action SuperFly__joydown__33 Joystick__postframe__26__SkipMoveDown: asl Joystick__3__tmp+0 bcs Joystick__postframe__26__SkipMoveUp -;;; start action SuperFly 5 joyup +;;; start action SuperFly__joyup__35 lda HasYpos_ypos_b0 sec sbc #2 jcs SuperFly__joyup__35__nomove -;;; start action SuperFly 5 gonorth +;;; start action SuperFly__gonorth__36 ldy Location_room_b0 lda Room_north_b0,y sta Location_room_b0 -;;; end action SuperFly 5 gonorth +;;; end action SuperFly__gonorth__36 lda #200 SuperFly__joyup__35__nomove: sta HasYpos_ypos_b0 -;;; end action SuperFly 5 joyup +;;; end action SuperFly__joyup__35 Joystick__postframe__26__SkipMoveUp: -;;; end action Joystick 3 postframe +;;; end action Joystick__postframe__26 -;;; start action BadMove 6 postframe +;;; start action BadMove__postframe__37 ldx #0 BadMove__postframe__37____each: -;;; start action JoyFaceDirection 4 joyright +;;; start action JoyFaceDirection__joyright__38 lda Sprite_plyrflags_b0+1,x and #$f7 sta Sprite_plyrflags_b0+1,x -;;; end action JoyFaceDirection 4 joyright +;;; end action JoyFaceDirection__joyright__38 -;;; start action SuperFly 5 joyright +;;; start action SuperFly__joyright__39 lda HasXpos_xpos_b0+1,x clc @@ -1106,19 +1106,19 @@ BadMove__postframe__37____each: cmp #142 jcc SuperFly__joyright__39__nomove -;;; start action SuperFly 5 goeast +;;; start action SuperFly__goeast__40 ldy Location_room_b0+1,x lda Room_east_b0,y sta Location_room_b0+1,x -;;; end action SuperFly 5 goeast +;;; end action SuperFly__goeast__40 lda #2 SuperFly__joyright__39__nomove: sta HasXpos_xpos_b0+1,x -;;; end action SuperFly 5 joyright +;;; end action SuperFly__joyright__39 inx @@ -1126,9 +1126,9 @@ SuperFly__joyright__39__nomove: jne BadMove__postframe__37____each BadMove__postframe__37____exit: -;;; end action BadMove 6 postframe +;;; end action BadMove__postframe__37 -;;; start action RoomShuffle 7 postframe +;;; start action RoomShuffle__postframe__41 ldy 4 ldx SpriteSlot_sprite_b0+1 @@ -1150,22 +1150,22 @@ RoomShuffle__postframe__41__norecycle: RoomShuffle__postframe__41__exit: stx SpriteSlot_sprite_b0+1 -;;; end action RoomShuffle 7 postframe +;;; end action RoomShuffle__postframe__41 -;;; start action VersatilePlayfield 10 postframe +;;; start action VersatilePlayfield__postframe__42 lda #0 sta PF0 sta PF1 sta PF2 -;;; end action VersatilePlayfield 10 postframe +;;; end action VersatilePlayfield__postframe__42 FRAME_END jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop 1 start +;;; end action FrameLoop__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -1173,7 +1173,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 11 main_init +;;; end action Init__main_init__1 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index 77aab5f8..6083a1ad 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -3,7 +3,7 @@ .code __Start: -;;; start action Init 2 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .macpack longbranch @@ -13,11 +13,11 @@ __Reset: __BRK: CLEAN_START -;;; start action Demo 1 start +;;; start action Demo__start__2 jmp Title__Start -;;; end action Demo 1 start +;;; end action Demo__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -25,7 +25,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 2 main_init +;;; end action Init__main_init__1 .scope Title .zeropage @@ -304,50 +304,50 @@ Bitmap48_height_b0: .byte 36 __Start: -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__3 FrameLoop__start__3__NextFrame: FRAME_START -;;; start action StaticKernel 3 preframe +;;; start action StaticKernel__preframe__4 -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__5 -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__5 -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__6 -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__6 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__7 lda #160 sta COLUBK -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__7 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__8 -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__8 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__9 -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__9 -;;; end action StaticKernel 3 preframe +;;; end action StaticKernel__preframe__4 KERNEL_START -;;; start action StaticKernel 3 kernel +;;; start action StaticKernel__kernel__10 ldx #0 StaticKernel__kernel__10____each: sta WSYNC -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__11 cpx #2+1 jcs Kernel48Pixel__kernelsetup__11____skipxhi @@ -382,9 +382,9 @@ Kernel48Pixel__kernelsetup__11____skipxlo: Kernel48Pixel__kernelsetup__11____skipxhi: -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__11 -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__12 cpx #2+1 jcs Kernel48Pixel__kernelsetup__12____skipxhi @@ -400,16 +400,16 @@ Kernel48Pixel__kernelsetup__12____skipxlo: Kernel48Pixel__kernelsetup__12____skipxhi: -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__12 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__13 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__13 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__14 cpx #2+1 jcs StaticKernel__kernelsetup__14____skipxhi @@ -424,14 +424,61 @@ StaticKernel__kernelsetup__14____skipxlo: StaticKernel__kernelsetup__14____skipxhi: -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__14 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__15 -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__15 -;;; start action Kernel48Pixel 2 kerneldraw + jsr Kernel48Pixel__kerneldraw__16 + +;;; start action StaticKernel__kerneldraw__17 + + ldy KernelSection_lines_b0,x +StaticKernel__kerneldraw__17__loop: + sta WSYNC + + dey + bne StaticKernel__kerneldraw__17__loop + +;;; end action StaticKernel__kerneldraw__17 + + + + inx + cpx #5 + jne StaticKernel__kernel__10____each +StaticKernel__kernel__10____exit: + +;;; end action StaticKernel__kernel__10 + + KERNEL_END + +;;; start action JoyButton__postframe__18 + + lda INPT4 ;read button input + bmi JoyButton__postframe__18__NotPressed + +;;; start action Advance__joybutton__19 + + jmp Title2__Start + +;;; end action Advance__joybutton__19 + +JoyButton__postframe__18__NotPressed: + +;;; end action JoyButton__postframe__18 + + FRAME_END + + jmp FrameLoop__start__3__NextFrame ; loop to next frame + +;;; end action FrameLoop__start__3 +.rodata +__ALIGNORIGIN: +.rodata +Kernel48Pixel__kerneldraw__16: cpx #2+1 jcs Kernel48Pixel__kerneldraw__16____skipxhi @@ -468,51 +515,11 @@ Kernel48Pixel__kerneldraw__16____skipxlo: Kernel48Pixel__kerneldraw__16____skipxhi: -;;; end action Kernel48Pixel 2 kerneldraw + rts -;;; start action StaticKernel 3 kerneldraw - - ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__17__loop: - sta WSYNC - - dey - bne StaticKernel__kerneldraw__17__loop - -;;; end action StaticKernel 3 kerneldraw - - - - inx - cpx #5 - jne StaticKernel__kernel__10____each -StaticKernel__kernel__10____exit: - -;;; end action StaticKernel 3 kernel - - KERNEL_END - -;;; start action JoyButton 4 postframe - - lda INPT4 ;read button input - bmi JoyButton__postframe__18__NotPressed - -;;; start action Advance 5 joybutton - - jmp Title2__Start - -;;; end action Advance 5 joybutton - -JoyButton__postframe__18__NotPressed: - -;;; end action JoyButton 4 postframe - - FRAME_END - - jmp FrameLoop__start__3__NextFrame ; loop to next frame - -;;; end action FrameLoop 1 start +.assert >(Kernel48Pixel__kerneldraw__16) = >(*), error, "Kernel48Pixel__kerneldraw__16 crosses a page boundary!" +.assert (* - Kernel48Pixel__kerneldraw__16) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__16 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__16)) .endscope Title__Start = Title::__Start .scope Title2 @@ -847,50 +854,50 @@ __Start: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__20 FrameLoop__start__20__NextFrame: FRAME_START -;;; start action StaticKernel 3 preframe +;;; start action StaticKernel__preframe__21 -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__22 -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__22 -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__23 -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__23 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__24 lda BGColor_bgcolor_b0 sta COLUBK -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__24 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__25 -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__25 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__26 -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__26 -;;; end action StaticKernel 3 preframe +;;; end action StaticKernel__preframe__21 KERNEL_START -;;; start action StaticKernel 3 kernel +;;; start action StaticKernel__kernel__27 ldx #0 StaticKernel__kernel__27____each: sta WSYNC -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__28 cpx #2+1 jcs Kernel48Pixel__kernelsetup__28____skipxhi @@ -925,9 +932,9 @@ Kernel48Pixel__kernelsetup__28____skipxlo: Kernel48Pixel__kernelsetup__28____skipxhi: -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__28 -;;; start action Kernel48Pixel 2 kernelsetup +;;; start action Kernel48Pixel__kernelsetup__29 cpx #2+1 jcs Kernel48Pixel__kernelsetup__29____skipxhi @@ -943,16 +950,16 @@ Kernel48Pixel__kernelsetup__29____skipxlo: Kernel48Pixel__kernelsetup__29____skipxhi: -;;; end action Kernel48Pixel 2 kernelsetup +;;; end action Kernel48Pixel__kernelsetup__29 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__30 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__30 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__31 cpx #2+1 jcs StaticKernel__kernelsetup__31____skipxhi @@ -967,14 +974,56 @@ StaticKernel__kernelsetup__31____skipxlo: StaticKernel__kernelsetup__31____skipxhi: -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__31 -;;; start action StaticKernel 3 kernelsetup +;;; start action StaticKernel__kernelsetup__32 -;;; end action StaticKernel 3 kernelsetup +;;; end action StaticKernel__kernelsetup__32 -;;; start action Kernel48Pixel 2 kerneldraw + jsr Kernel48Pixel__kerneldraw__33 + +;;; start action StaticKernel__kerneldraw__34 + + ldy KernelSection_lines_b0,x +StaticKernel__kerneldraw__34__loop: + sta WSYNC + + dey + bne StaticKernel__kerneldraw__34__loop + +;;; end action StaticKernel__kerneldraw__34 + + + + inx + cpx #5 + jne StaticKernel__kernel__27____each +StaticKernel__kernel__27____exit: + +;;; end action StaticKernel__kernel__27 + + KERNEL_END + +;;; start action Colors__postframe__35 + + inc PFColor_pfcolor_b0 + bne :+ + inc BGColor_bgcolor_b0+2 + inc BGColor_bgcolor_b0+2 +: + +;;; end action Colors__postframe__35 + + FRAME_END + + jmp FrameLoop__start__20__NextFrame ; loop to next frame + +;;; end action FrameLoop__start__20 +.rodata +__ALIGNORIGIN: +.rodata +Kernel48Pixel__kerneldraw__33: cpx #2+1 jcs Kernel48Pixel__kerneldraw__33____skipxhi @@ -1011,46 +1060,11 @@ Kernel48Pixel__kerneldraw__33____skipxlo: Kernel48Pixel__kerneldraw__33____skipxhi: -;;; end action Kernel48Pixel 2 kerneldraw + rts -;;; start action StaticKernel 3 kerneldraw - - ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__34__loop: - sta WSYNC - - dey - bne StaticKernel__kerneldraw__34__loop - -;;; end action StaticKernel 3 kerneldraw - - - - inx - cpx #5 - jne StaticKernel__kernel__27____each -StaticKernel__kernel__27____exit: - -;;; end action StaticKernel 3 kernel - - KERNEL_END - -;;; start action Colors 4 postframe - - inc PFColor_pfcolor_b0 - bne :+ - inc BGColor_bgcolor_b0+2 - inc BGColor_bgcolor_b0+2 -: - -;;; end action Colors 4 postframe - - FRAME_END - - jmp FrameLoop__start__20__NextFrame ; loop to next frame - -;;; end action FrameLoop 1 start +.assert >(Kernel48Pixel__kerneldraw__33) = >(*), error, "Kernel48Pixel__kerneldraw__33 crosses a page boundary!" +.assert (* - Kernel48Pixel__kerneldraw__33) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__33 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__33)) .endscope Title2__Start = Title2::__Start .endscope diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index 240ae8c4..6208b667 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -45,7 +45,7 @@ Main__INITDATA: .byte 0 __Start: -;;; start action Init 7 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .define PAL 0 @@ -60,49 +60,49 @@ __BRK: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__2 FrameLoop__start__2__NextFrame: FRAME_START -;;; start action StaticKernel 4 preframe +;;; start action StaticKernel__preframe__3 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__4 lda #24 sta COLUBK -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__4 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__5 -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__5 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__6 -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__6 -;;; end action StaticKernel 4 preframe +;;; end action StaticKernel__preframe__3 KERNEL_START -;;; start action StaticKernel 4 kernel +;;; start action StaticKernel__kernel__7 ldx #0 StaticKernel__kernel__7____each: sta WSYNC -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__8 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__8 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__9 cpx #5+2 jcs StaticKernel__kernelsetup__9____skipxhi @@ -117,9 +117,9 @@ StaticKernel__kernelsetup__9____skipxlo: StaticKernel__kernelsetup__9____skipxhi: -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__9 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__10 cpx #4 jcc StaticKernel__kernelsetup__10____skipxlo @@ -133,7 +133,7 @@ StaticKernel__kernelsetup__9____skipxhi: StaticKernel__kernelsetup__10____skipxlo: -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__10 ldy KernelSection_lines_b0,x StaticKernel__kernel__7__loop: @@ -148,46 +148,46 @@ StaticKernel__kernel__7__loop: jne StaticKernel__kernel__7____each StaticKernel__kernel__7____exit: -;;; end action StaticKernel 4 kernel +;;; end action StaticKernel__kernel__7 KERNEL_END -;;; start action JoyButton 5 postframe +;;; start action JoyButton__postframe__11 lda INPT4 ;read button input bmi JoyButton__postframe__11__NotPressed -;;; start action Local 6 joybutton +;;; start action Local__joybutton__12 inc Local__6__tmp+0 inc PFColor_pfcolor_b0 -;;; end action Local 6 joybutton +;;; end action Local__joybutton__12 JoyButton__postframe__11__NotPressed: -;;; end action JoyButton 5 postframe +;;; end action JoyButton__postframe__11 FRAME_END -;;; start action ResetSwitch 2 nextframe +;;; start action ResetSwitch__nextframe__13 lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__13__NoStart -;;; start action ResetConsole 3 resetswitch +;;; start action ResetConsole__resetswitch__14 jmp Main::__Reset ; jump to Reset handler -;;; end action ResetConsole 3 resetswitch +;;; end action ResetConsole__resetswitch__14 ResetSwitch__nextframe__13__NoStart: -;;; end action ResetSwitch 2 nextframe +;;; end action ResetSwitch__nextframe__13 jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop 1 start +;;; end action FrameLoop__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -195,7 +195,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 7 main_init +;;; end action Init__main_init__1 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index a842db8b..301c3e39 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -45,7 +45,7 @@ Main__INITDATA: .byte 0 __Start: -;;; start action Init 7 main_init +;;; start action Init__main_init__1 .include "vcs-ca65.h" .macpack longbranch @@ -61,49 +61,49 @@ __BRK: dey bne :- -;;; start action FrameLoop 1 start +;;; start action FrameLoop__start__2 FrameLoop__start__2__NextFrame: FRAME_START -;;; start action StaticKernel 4 preframe +;;; start action StaticKernel__preframe__3 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__4 lda #24 sta COLUBK -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__4 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__5 -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__5 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__6 -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__6 -;;; end action StaticKernel 4 preframe +;;; end action StaticKernel__preframe__3 KERNEL_START -;;; start action StaticKernel 4 kernel +;;; start action StaticKernel__kernel__7 ldx #0 StaticKernel__kernel__7____each: sta WSYNC -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__8 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__8 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__9 cpx #5+2 jcs StaticKernel__kernelsetup__9____skipxhi @@ -118,9 +118,9 @@ StaticKernel__kernelsetup__9____skipxlo: StaticKernel__kernelsetup__9____skipxhi: -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__9 -;;; start action StaticKernel 4 kernelsetup +;;; start action StaticKernel__kernelsetup__10 cpx #4 jcc StaticKernel__kernelsetup__10____skipxlo @@ -134,10 +134,10 @@ StaticKernel__kernelsetup__9____skipxhi: StaticKernel__kernelsetup__10____skipxlo: -;;; end action StaticKernel 4 kernelsetup +;;; end action StaticKernel__kernelsetup__10 -;;; start action StaticKernel 4 kerneldraw +;;; start action StaticKernel__kerneldraw__11 ldy KernelSection_lines_b0,x StaticKernel__kerneldraw__11__loop: @@ -146,7 +146,7 @@ StaticKernel__kerneldraw__11__loop: dey bne StaticKernel__kerneldraw__11__loop -;;; end action StaticKernel 4 kerneldraw +;;; end action StaticKernel__kerneldraw__11 @@ -155,46 +155,46 @@ StaticKernel__kerneldraw__11__loop: jne StaticKernel__kernel__7____each StaticKernel__kernel__7____exit: -;;; end action StaticKernel 4 kernel +;;; end action StaticKernel__kernel__7 KERNEL_END -;;; start action JoyButton 5 postframe +;;; start action JoyButton__postframe__12 lda INPT4 ;read button input bmi JoyButton__postframe__12__NotPressed -;;; start action Local 6 joybutton +;;; start action Local__joybutton__13 inc Local__6__tmp+0 inc PFColor_pfcolor_b0 -;;; end action Local 6 joybutton +;;; end action Local__joybutton__13 JoyButton__postframe__12__NotPressed: -;;; end action JoyButton 5 postframe +;;; end action JoyButton__postframe__12 FRAME_END -;;; start action ResetSwitch 2 nextframe +;;; start action ResetSwitch__nextframe__14 lsr SWCHB ; test Game Reset switch bcs ResetSwitch__nextframe__14__NoStart -;;; start action ResetConsole 3 resetswitch +;;; start action ResetConsole__resetswitch__15 jmp Main::__Reset ; jump to Reset handler -;;; end action ResetConsole 3 resetswitch +;;; end action ResetConsole__resetswitch__15 ResetSwitch__nextframe__14__NoStart: -;;; end action ResetSwitch 2 nextframe +;;; end action ResetSwitch__nextframe__14 jmp FrameLoop__start__2__NextFrame ; loop to next frame -;;; end action FrameLoop 1 start +;;; end action FrameLoop__start__2 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -202,7 +202,7 @@ VecNMI: VecReset: .word Main::__Reset VecBRK: .word Main::__BRK -;;; end action Init 7 main_init +;;; end action Init__main_init__1 .endscope Main__Start = Main::__Start \ No newline at end of file From c9e75ae77d26d802e408c6a137cf4d5764ae593c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 23 Feb 2022 11:16:47 -0600 Subject: [PATCH 090/104] ecs: EVENT__xxx defines --- src/common/ecs/ecs.ts | 8 ++++++++ test/ecs/narrow1.txt | 1 + test/ecs/score.txt | 17 +++++++++++++++++ test/ecs/sprites.txt | 16 ++++++++++++++++ test/ecs/sprites1.txt | 17 +++++++++++++++++ test/ecs/superman.txt | 20 ++++++++++++++++++++ test/ecs/titles.txt | 11 +++++++++++ test/ecs/vcs1.txt | 10 ++++++++++ test/ecs/vcslib.txt | 11 +++++++++++ 9 files changed, 111 insertions(+) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 038685f3..8c3b0fef 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -353,6 +353,10 @@ export class Dialect_CA65 { equate(symbol: string, value: string): string { return `${symbol} = ${value}`; } + define(symbol: string, value?: string): string { + if (value) return `.define ${symbol} ${value}`; + else return `.define ${symbol}`; + } call(symbol: string) { return ` jsr ${symbol}`; } @@ -1008,6 +1012,7 @@ class ActionEval { //eidofs -= xreg.offset(); //eidofs -= int.entities[0].id - xreg.elo; eidofs = xreg.elo - range.elo; + // TODO? if (xreg.ehi > range.ehi) throw new ECSError(`field "${field.name}" could overflow`, action); } else if (yreg && (int = yreg.eset?.intersection(qr))) { ir = yreg.eset; //eidofs -= yreg.offset(); @@ -1716,6 +1721,9 @@ export class EntityManager { }) } exportToFile(file: SourceFileExport) { + for (let event of Object.keys(this.event2systems)) { + file.line(this.dialect.equate(`EVENT__${event}`, '1')) + } for (let scope of Object.values(this.topScopes)) { if (!scope.isDemo || scope.filePath == this.mainPath) { scope.dump(file); diff --git a/test/ecs/narrow1.txt b/test/ecs/narrow1.txt index f21762c1..b7f3c3c9 100644 --- a/test/ecs/narrow1.txt +++ b/test/ecs/narrow1.txt @@ -1,3 +1,4 @@ +EVENT__start = 1 .scope Main .zeropage Xpos_x_b0: diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 9a90a1a1..e9dea9b1 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -1,3 +1,20 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 +EVENT__FontTable = 1 +EVENT__compute2digit = 1 +EVENT__fetchdigit = 1 +EVENT__FontTablePF = 1 +EVENT__FontTablePFFancy = 1 +EVENT__AddBCD4 = 1 .scope Main .zeropage BCDScore6_digits_b0: diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index 6095ad23..4f96cf7b 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -1,3 +1,19 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 +EVENT__scanline = 1 +EVENT__joyleft = 1 +EVENT__joyright = 1 +EVENT__joyup = 1 +EVENT__joydown = 1 .scope Main .zeropage HasBitmap_bitmap_b0: diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index fb6ce8c8..9ab7c0fe 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -1,3 +1,20 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 +EVENT__scanline1 = 1 +EVENT__scanline2 = 1 +EVENT__joyleft = 1 +EVENT__joyright = 1 +EVENT__joyup = 1 +EVENT__joydown = 1 .scope Main .zeropage HasBitmap_bitmap_b0: diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index 07f8b8e4..0e136420 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -1,3 +1,23 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 +EVENT__scanline = 1 +EVENT__joyleft = 1 +EVENT__joyright = 1 +EVENT__joyup = 1 +EVENT__joydown = 1 +EVENT__gowest = 1 +EVENT__goeast = 1 +EVENT__gonorth = 1 +EVENT__gosouth = 1 .scope Main .zeropage Location_room_b0: diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index 6083a1ad..d5748492 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -1,3 +1,14 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 .scope TitleDemo .zeropage .code diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index 6208b667..d0e5da45 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -1,3 +1,13 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 .scope Main .zeropage PFColor_pfcolor_b0: diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 301c3e39..84657a50 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -1,3 +1,14 @@ +EVENT__main_init = 1 +EVENT__start = 1 +EVENT__nextframe = 1 +EVENT__resetswitch = 1 +EVENT__postframe = 1 +EVENT__SetHorizPos = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 +EVENT__joybutton = 1 .scope Main .zeropage PFColor_pfcolor_b0: From 75fb2a8e5c900ee27ba5201b669cac48c2e91492 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 09:47:34 -0600 Subject: [PATCH 091/104] worker: debuginfo passed thru to link step --- src/common/baseplatform.ts | 5 ++++- src/common/ecs/ecs.ts | 7 ++++++- src/worker/tools/ecs.ts | 16 +++++++++------- src/worker/workermain.ts | 8 ++++++++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/common/baseplatform.ts b/src/common/baseplatform.ts index 7e0aad13..763f37bc 100644 --- a/src/common/baseplatform.ts +++ b/src/common/baseplatform.ts @@ -223,7 +223,10 @@ export abstract class BasePlatform { return inspectSymbol((this as any) as Platform, sym); } getDebugTree() : {} { - return this.saveState(); + var o : any = { }; + o.state = this.saveState(); + if (this.debugSymbols.debuginfo) o.debuginfo = this.debugSymbols.debuginfo; + return o; } readFile(path: string) : FileData { return this.internalFiles[path]; diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 8c3b0fef..f15588ea 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1410,7 +1410,6 @@ export class EntityScope implements SourceLocated { if (action.critical) this.inCritical--; if (!this.inCritical && codeeval.isSubroutineSized(eventcode)) { let normcode = this.normalizeCode(eventcode, action); - // TODO: label rewriting messes this up let estats = this.eventCodeStats[normcode]; if (!estats) { estats = this.eventCodeStats[normcode] = new EventCodeStats( @@ -1730,4 +1729,10 @@ export class EntityManager { } } } + getDebugTree() : {} { + let scopes = this.topScopes; + let components = this.components; + let systems = this.systems; + return { scopes, components, systems }; + } } diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 0ca641f1..443daac7 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -23,6 +23,7 @@ export function assembleECS(step: BuildStep): BuildStepResult { putWorkFile(destpath, outtext); var listings: CodeListingMap = {}; listings[destpath] = {lines:[], text:outtext} // TODO + var debuginfo = compiler.em.getDebugTree(); } catch (e) { if (e instanceof ECSError) { compiler.addError(e.message, e.$loc); @@ -33,12 +34,13 @@ export function assembleECS(step: BuildStep): BuildStepResult { throw e; } } + return { + nexttool: "ca65", + path: destpath, + args: [destpath], + files: [destpath].concat(step.files), + listings, + debuginfo + }; } - return { - nexttool: "ca65", - path: destpath, - args: [destpath], - files: [destpath].concat(step.files), - listings - }; } diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index 04ee1955..ea4d77fe 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -424,6 +424,7 @@ export interface BuildStep extends WorkerBuildStep { code? prefix? maxts? + debuginfo? }; /// @@ -511,6 +512,11 @@ class Builder { } if (step.result) { (step.result as any).params = step.params; // TODO: type check + if (step.debuginfo) { + let r = step.result as any; // TODO + if (!r.debuginfo) r.debuginfo = {}; + Object.assign(r.debuginfo, step.debuginfo); + } // errors? return them if ('errors' in step.result && step.result.errors.length) { applyDefaultErrorPath(step.result.errors, step.path); @@ -522,6 +528,7 @@ class Builder { } // combine files with a link tool? if ('linktool' in step.result) { + // add to existing link step if (linkstep) { linkstep.files = linkstep.files.concat(step.result.files); linkstep.args = linkstep.args.concat(step.result.args); @@ -533,6 +540,7 @@ class Builder { args:step.result.args }; } + linkstep.debuginfo = step.debuginfo; // TODO: multiple debuginfos } // process with another tool? if ('nexttool' in step.result) { From e7e5ea78470dac76cf68e07c7bcd5e7deebca914 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 10:06:20 -0600 Subject: [PATCH 092/104] ecs: skip parsing demo if not in main file --- src/common/ecs/README.md | 5 +++++ src/common/ecs/compiler.ts | 36 ++++++++++++++++++++++++++---------- src/common/ecs/ecs.ts | 24 +++++++++++++++++++++--- src/test/testecs.ts | 4 ++-- src/worker/tools/ecs.ts | 4 ++-- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md index e461202d..7e72edb6 100644 --- a/src/common/ecs/README.md +++ b/src/common/ecs/README.md @@ -184,3 +184,8 @@ end ldy {{= 0 && iend > istart) { @@ -1729,10 +1734,23 @@ export class EntityManager { } } } + *iterateScopes() { + for (let scope of Object.values(this.topScopes)) { + yield scope; + scope.iterateChildScopes(); + } + } getDebugTree() : {} { let scopes = this.topScopes; let components = this.components; + let fields = this.name2cfpairs; let systems = this.systems; - return { scopes, components, systems }; + let events = this.event2systems; + let entities : {[key:string]:Entity} = {}; + for (let scope of Array.from(this.iterateScopes())) { + for (let e of scope.entities) + entities[e.name || '#'+e.id.toString()] = e; + } + return { scopes, components, fields, systems, events, entities }; } } diff --git a/src/test/testecs.ts b/src/test/testecs.ts index 6936fe0c..0d64ea96 100644 --- a/src/test/testecs.ts +++ b/src/test/testecs.ts @@ -8,7 +8,7 @@ import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs function testCompiler() { let em = new EntityManager(new Dialect_CA65()); // TODO - let c = new ECSCompiler(em); + let c = new ECSCompiler(em, true); try { c.parseFile(` // comment @@ -93,7 +93,7 @@ describe('Compiler', function() { let dialect = new Dialect_CA65(); let em = new EntityManager(dialect); em.mainPath = ecspath; - let compiler = new ECSCompiler(em); + let compiler = new ECSCompiler(em, true); compiler.getImportFile = (path: string) => { return readFileSync(testdir + path, 'utf-8'); } diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 443daac7..040d1b94 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -6,7 +6,7 @@ import { BuildStep, BuildStepResult, fixParamsWithDefines, gatherFiles, getWorkF export function assembleECS(step: BuildStep): BuildStepResult { let em = new EntityManager(new Dialect_CA65()); // TODO - let compiler = new ECSCompiler(em); + let compiler = new ECSCompiler(em, true); compiler.getImportFile = (path: string) => { return getWorkFileAsString(path); } @@ -17,7 +17,7 @@ export function assembleECS(step: BuildStep): BuildStepResult { let code = getWorkFileAsString(step.path); fixParamsWithDefines(step.path, step.params); try { - compiler.debuginfo = true; + compiler.includeDebugInfo = true; compiler.parseFile(code, step.path); let outtext = compiler.export().toString(); putWorkFile(destpath, outtext); From 30db34837c0cbe3bc445c9779ac3e9466d54770a Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 12:11:08 -0600 Subject: [PATCH 093/104] nes: updated memory map --- src/common/ecs/README.md | 2 +- src/ide/views/asseteditor.ts | 6 ++++++ src/platform/nes.ts | 5 +++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md index 7e72edb6..b27db6c7 100644 --- a/src/common/ecs/README.md +++ b/src/common/ecs/README.md @@ -187,5 +187,5 @@ end Slice PNGs into sprites Maybe output decoder text - Action priorities - before, after +Generate C symbols diff --git a/src/ide/views/asseteditor.ts b/src/ide/views/asseteditor.ts index 73334e74..1cdf47c9 100644 --- a/src/ide/views/asseteditor.ts +++ b/src/ide/views/asseteditor.ts @@ -175,6 +175,12 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { var meta = {defs:metadefs,width:width*8,height:height*8}; result.push({fileid:id,label:ident,meta:meta}); } + // TODO: look for decode --- ... --- + /* + var re3 = /\bdecode\s+(\w+)\s*---(.+?)---/gims; + while (m = re3.exec(data)) { + } + */ return result; } diff --git a/src/platform/nes.ts b/src/platform/nes.ts index 87dc7441..fe7859bc 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -475,11 +475,12 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { } getMemoryMap = function() { return { main:[ - //{name:'Work RAM',start:0x0,size:0x800,type:'ram'}, + {name:'Zero Page RAM',start:0x0,size:0x100,type:'ram'}, {name:'OAM Buffer',start:0x200,size:0x100,type:'ram'}, + {name:'Work RAM',start:0x300,size:0x1000-0x300,type:'ram'}, {name:'PPU Registers',start:0x2000,last:0x2008,size:0x2000,type:'io'}, {name:'APU Registers',start:0x4000,last:0x4020,size:0x2000,type:'io'}, - {name:'Cartridge RAM',start:0x6000,size:0x2000,type:'ram'}, + {name:'Optional Cartridge RAM',start:0x6000,size:0x2000,type:'ram'}, ] } }; showHelp(tool:string, ident:string) { From d87a05747e97f685141a8dbf4dad172c6775cba5 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 12:59:45 -0600 Subject: [PATCH 094/104] ecs: only register events when used in a system instance --- src/common/ecs/ecs.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 2dfea2bb..8178a3ba 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1117,6 +1117,7 @@ export class EntityScope implements SourceLocated { if (!inst) throw new Error(); inst.id = this.instances.length+1; this.instances.push(inst); + this.em.registerSystemEvents(inst.system); return inst; } newSystemInstanceWithDefaults(system: System) { @@ -1649,13 +1650,15 @@ export class EntityManager { defineSystem(system: System) { let existing = this.systems[system.name]; if (existing) throw new ECSError(`system ${system.name} already defined`, existing); + return this.systems[system.name] = system; + } + registerSystemEvents(system: System) { for (let a of system.actions) { let event = a.event; let list = this.event2systems[event]; if (list == null) list = this.event2systems[event] = []; if (!list.includes(system)) list.push(system); } - return this.systems[system.name] = system; } addArchetype(atype: EntityArchetype): EntityArchetype { let key = atype.components.map(c => c.name).join(','); From 614fdf92f7eae0138aeba55de998bac2f7fc1b10 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 13:30:10 -0600 Subject: [PATCH 095/104] ecs: token.eol; fixed bugs and tests --- src/common/ecs/compiler.ts | 1 + src/common/ecs/ecs.ts | 6 +++--- src/common/ecs/main.ts | 2 +- src/common/tokenizer.ts | 10 ++++++++-- src/test/testutil.ts | 3 +++ test/ecs/errors1.ecs | 2 +- test/ecs/score.ecs | 3 ++- test/ecs/score.txt | 11 ++--------- test/ecs/sprites.ecs | 3 ++- test/ecs/sprites.txt | 10 ++-------- test/ecs/sprites1.ecs | 3 ++- test/ecs/sprites1.txt | 11 ++--------- test/ecs/superman.txt | 14 ++++---------- test/ecs/titles.ecs | 3 ++- test/ecs/titles.txt | 10 +++------- test/ecs/vcs1.ecs | 3 ++- test/ecs/vcs1.txt | 4 +--- test/ecs/vcslib.ecs | 3 ++- test/ecs/vcslib.txt | 4 +--- 19 files changed, 44 insertions(+), 62 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 099ff64f..0ffc880d 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -41,6 +41,7 @@ export class ECSCompiler extends Tokenizer { { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, { type: TokenType.Ignore, regex: /\/\/.*?[\n\r]/ }, { type: TokenType.Ignore, regex: /\/\*.*?\*\// }, + { type: TokenType.EOL, regex: /[\n\r]+/ }, { type: TokenType.Ignore, regex: /\s+/ }, ]); this.errorOnCatchAll = true; diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 8178a3ba..218ba520 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1548,9 +1548,9 @@ export class EntityScope implements SourceLocated { } let subcall = this.dialect.call(stats.labels[0]); for (let label of stats.labels) { - // TODO: use dialect - let startdelim = this.dialect.comment(`start action ${label}`); - let enddelim = this.dialect.comment(`end action ${label}`); + // TODO: need to trim()? + let startdelim = this.dialect.comment(`start action ${label}`).trim(); + let enddelim = this.dialect.comment(`end action ${label}`).trim(); let istart = code.indexOf(startdelim); let iend = code.indexOf(enddelim, istart); if (istart >= 0 && iend > istart) { diff --git a/src/common/ecs/main.ts b/src/common/ecs/main.ts index 20057603..e93382be 100644 --- a/src/common/ecs/main.ts +++ b/src/common/ecs/main.ts @@ -4,7 +4,7 @@ import { Dialect_CA65, EntityManager, SourceFileExport } from "./ecs"; class ECSMain { - compiler = new ECSCompiler(new EntityManager(new Dialect_CA65())); // TODO + compiler = new ECSCompiler(new EntityManager(new Dialect_CA65()), true); // TODO constructor(readonly args: string[]) { } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 567e38d3..2e88ce63 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -32,6 +32,7 @@ export enum TokenType { export class Token implements SourceLocated { str: string; type: string; + eol: boolean; // end of line? $loc: SourceLocation; } @@ -92,7 +93,7 @@ export class Tokenizer { this.lineindex.push(m.index); } this._tokenize(contents); - this.eof = { type: TokenType.EOF, str: "", $loc: { path: this.path, line: this.lineno } }; + this.eof = { type: TokenType.EOF, str: "", eol: true, $loc: { path: this.path, line: this.lineno } }; this.pushToken(this.eof); } _tokenize(text: string): void { @@ -121,7 +122,12 @@ export class Tokenizer { this.compileError(`I didn't expect the character "${m[0]}" here.`, loc); } default: - this.pushToken({ str: s, type: rule.type, $loc: loc }); + this.pushToken({ str: s, type: rule.type, $loc: loc, eol: false }); + break; + case TokenType.EOL: + // set EOL for last token + if (this.tokens.length) + this.tokens[this.tokens.length-1].eol = true; case TokenType.Comment: case TokenType.Ignore: break; diff --git a/src/test/testutil.ts b/src/test/testutil.ts index a1f7a75c..2bba1e7f 100644 --- a/src/test/testutil.ts +++ b/src/test/testutil.ts @@ -143,6 +143,7 @@ describe('Tokenizer', function () { { type: 'delim', regex: /[\(\)\{\}\[\]]/ }, { type: 'qstring', regex: /".*?"/ }, { type: 'integer', regex: /[-]?\d+/ }, + { type: 'eol', regex: /\n+/ }, { type: 'ignore', regex: /\s+/ }, ]); t.tokenizeFile("a\n{\"key\" value\n \"number\" 531\n\n \"f\" (fn [x] (+ x 2))}\n", "test.file"); @@ -150,6 +151,8 @@ describe('Tokenizer', function () { 'ident delim qstring ident qstring integer qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eof'); assert.strictEqual(t.tokens.map(t => t.str).join(' '), 'a { "key" value "number" 531 "f" ( fn [ x ] ( + x 2 ) ) } '); + assert.strictEqual(t.tokens.filter(t => t.eol).map(t => t.str).join(' '), + 'a value 531 } '); assert.strictEqual(t.tokens.map(t => t.$loc.line).join(' '), '1 2 2 2 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 6'); assert.strictEqual(20, t.tokens.length); diff --git a/test/ecs/errors1.ecs b/test/ecs/errors1.ecs index b0ad8cb7..8e433df3 100644 --- a/test/ecs/errors1.ecs +++ b/test/ecs/errors1.ecs @@ -28,4 +28,4 @@ demo Main const lines = 2 const color = $16 end -end +end demo diff --git a/test/ecs/score.ecs b/test/ecs/score.ecs index ec467f77..c4a328fe 100644 --- a/test/ecs/score.ecs +++ b/test/ecs/score.ecs @@ -293,4 +293,5 @@ demo Main {{!AddBCD4 $0210}} --- end -end +end demo + diff --git a/test/ecs/score.txt b/test/ecs/score.txt index e9dea9b1..3845e055 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -1,20 +1,13 @@ -EVENT__main_init = 1 EVENT__start = 1 -EVENT__nextframe = 1 -EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__kerneldraw = 1 -EVENT__kernelsetup = 1 -EVENT__joybutton = 1 EVENT__FontTable = 1 EVENT__compute2digit = 1 EVENT__fetchdigit = 1 EVENT__FontTablePF = 1 -EVENT__FontTablePFFancy = 1 +EVENT__postframe = 1 EVENT__AddBCD4 = 1 +EVENT__joybutton = 1 .scope Main .zeropage BCDScore6_digits_b0: diff --git a/test/ecs/sprites.ecs b/test/ecs/sprites.ecs index aa28b6ab..1bbafcdc 100644 --- a/test/ecs/sprites.ecs +++ b/test/ecs/sprites.ecs @@ -436,5 +436,6 @@ demo Main init sprite = #Sprite3 end -end +end demo + diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index 4f96cf7b..a3c56bc9 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -1,19 +1,13 @@ -EVENT__main_init = 1 EVENT__start = 1 -EVENT__nextframe = 1 -EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__kerneldraw = 1 -EVENT__kernelsetup = 1 -EVENT__joybutton = 1 EVENT__scanline = 1 +EVENT__postframe = 1 EVENT__joyleft = 1 EVENT__joyright = 1 EVENT__joyup = 1 EVENT__joydown = 1 +EVENT__SetHorizPos = 1 .scope Main .zeropage HasBitmap_bitmap_b0: diff --git a/test/ecs/sprites1.ecs b/test/ecs/sprites1.ecs index 382bd3cf..4b93bfb3 100644 --- a/test/ecs/sprites1.ecs +++ b/test/ecs/sprites1.ecs @@ -396,5 +396,6 @@ demo Main init sprite = #Sprite3 end -end +end demo + diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 9ab7c0fe..80fdaf94 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -1,20 +1,13 @@ -EVENT__main_init = 1 EVENT__start = 1 -EVENT__nextframe = 1 -EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__kerneldraw = 1 -EVENT__kernelsetup = 1 -EVENT__joybutton = 1 EVENT__scanline1 = 1 -EVENT__scanline2 = 1 +EVENT__postframe = 1 EVENT__joyleft = 1 EVENT__joyright = 1 EVENT__joyup = 1 EVENT__joydown = 1 +EVENT__SetHorizPos = 1 .scope Main .zeropage HasBitmap_bitmap_b0: diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index 0e136420..0370412e 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -1,23 +1,17 @@ -EVENT__main_init = 1 EVENT__start = 1 -EVENT__nextframe = 1 -EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__kerneldraw = 1 -EVENT__kernelsetup = 1 -EVENT__joybutton = 1 EVENT__scanline = 1 +EVENT__postframe = 1 EVENT__joyleft = 1 EVENT__joyright = 1 -EVENT__joyup = 1 -EVENT__joydown = 1 EVENT__gowest = 1 EVENT__goeast = 1 EVENT__gonorth = 1 EVENT__gosouth = 1 +EVENT__joyup = 1 +EVENT__joydown = 1 +EVENT__SetHorizPos = 1 .scope Main .zeropage Location_room_b0: diff --git a/test/ecs/titles.ecs b/test/ecs/titles.ecs index 70ab5e7a..51b6e05a 100644 --- a/test/ecs/titles.ecs +++ b/test/ecs/titles.ecs @@ -256,4 +256,5 @@ end --- end -end +end demo + diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index d5748492..d4dc580d 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -1,13 +1,9 @@ -EVENT__main_init = 1 EVENT__start = 1 -EVENT__nextframe = 1 -EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 +EVENT__kernelsetup = 1 +EVENT__kerneldraw = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__kerneldraw = 1 -EVENT__kernelsetup = 1 +EVENT__postframe = 1 EVENT__joybutton = 1 .scope TitleDemo .zeropage diff --git a/test/ecs/vcs1.ecs b/test/ecs/vcs1.ecs index e38b6e18..5301a65c 100644 --- a/test/ecs/vcs1.ecs +++ b/test/ecs/vcs1.ecs @@ -231,4 +231,5 @@ demo Main inc {{set Trees.pfcolor}} --- end -end +end demo + diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index d0e5da45..8efbded3 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -1,12 +1,10 @@ -EVENT__main_init = 1 EVENT__start = 1 EVENT__nextframe = 1 EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 EVENT__preframe = 1 EVENT__kernel = 1 EVENT__kernelsetup = 1 +EVENT__postframe = 1 EVENT__joybutton = 1 .scope Main .zeropage diff --git a/test/ecs/vcslib.ecs b/test/ecs/vcslib.ecs index 2ef982b7..e78c16ae 100644 --- a/test/ecs/vcslib.ecs +++ b/test/ecs/vcslib.ecs @@ -236,4 +236,5 @@ demo Main inc {{set Trees.pfcolor}} --- end -end +end demo + diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 84657a50..321c1214 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -1,13 +1,11 @@ -EVENT__main_init = 1 EVENT__start = 1 EVENT__nextframe = 1 EVENT__resetswitch = 1 -EVENT__postframe = 1 -EVENT__SetHorizPos = 1 EVENT__preframe = 1 EVENT__kernel = 1 EVENT__kerneldraw = 1 EVENT__kernelsetup = 1 +EVENT__postframe = 1 EVENT__joybutton = 1 .scope Main .zeropage From a965bc83d122c8745a5068b41b9d25c3a910c5d5 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 14:02:18 -0600 Subject: [PATCH 096/104] ecs: default values work now --- src/common/ecs/ecs.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 218ba520..f08fb9d7 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1181,8 +1181,9 @@ export class EntityScope implements SourceLocated { // constants and array pointers go into rodata let cfname = mksymbol(c, f.name); let ftype = this.fieldtypes[cfname]; - let segment = ftype == 'const' ? this.rodata : this.bss; - if (v === undefined && ftype == 'const') + let isConst = ftype == 'const'; + let segment = isConst ? this.rodata : this.bss; + if (v === undefined && isConst) throw new ECSError(`no value for const field ${cfname}`, e); // determine range of indices for entities let array = segment.fieldranges[cfname]; @@ -1194,11 +1195,12 @@ export class EntityScope implements SourceLocated { throw new ECSError(`too many entities have field ${cfname}, limit is 256`); } // set default values for entity/field - if (ftype == 'init') { + if (!isConst) { if (f.dtype == 'int' && f.defvalue !== undefined) { let ecfname = mkscopesymbol(this, c, f.name); - if (e.inits[ecfname] == null) + if (e.inits[ecfname] == null) { this.setInitValue(e, c, f, f.defvalue); + } } } } From 05965397fd8dc3a9d1840770f0dc6cf5f3cbb29e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 24 Feb 2022 19:46:13 -0600 Subject: [PATCH 097/104] ecs: started on expressions --- src/common/ecs/compiler.ts | 247 +++++++++++++++++++++++++++++++------ src/common/ecs/ecs.ts | 113 +++++++++++++++++ src/common/tokenizer.ts | 10 +- 3 files changed, 331 insertions(+), 39 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 0ffc880d..a0de4bfc 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -2,22 +2,65 @@ import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { newDecoder } from "./decoder"; -import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair } from "./ecs"; +import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair, Expr, ExprBase, ForwardRef, isLiteral, EntitySetField, LExpr } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', - Operator = 'delimiter', + Operator = 'operator', + Relational = 'relational', QuotedString = 'quoted-string', Integer = 'integer', CodeFragment = 'code-fragment', Placeholder = 'placeholder', } -interface ForwardRef { - reftype: RefType | undefined - token: Token +const OPERATORS = { + 'IMP': {f:'bimp',p:4}, + 'EQV': {f:'beqv',p:5}, + 'XOR': {f:'bxor',p:6}, + 'OR': {f:'bor',p:7}, // or "lor" for logical + 'AND': {f:'band',p:8}, // or "land" for logical + '||': {f:'lor',p:17}, // not used + '&&': {f:'land',p:18}, // not used + '=': {f:'eq',p:50}, + '==': {f:'eq',p:50}, + '<>': {f:'ne',p:50}, + '><': {f:'ne',p:50}, + '!=': {f:'ne',p:50}, + '#': {f:'ne',p:50}, + '<': {f:'lt',p:50}, + '>': {f:'gt',p:50}, + '<=': {f:'le',p:50}, + '>=': {f:'ge',p:50}, + 'MIN': {f:'min',p:75}, + 'MAX': {f:'max',p:75}, + '+': {f:'add',p:100}, + '-': {f:'sub',p:100}, +}; + +function getOperator(op: string) { + return (OPERATORS as any)[op]; } +function getPrecedence(tok: Token): number { + switch (tok.type) { + case ECSTokenType.Operator: + case ECSTokenType.Relational: + case TokenType.Ident: + let op = getOperator(tok.str); + if (op) return op.p; + } + return -1; +} + +// is token an end of statement marker? (":" or end of line) +function isEOS(tok: Token) { + return tok.type == TokenType.EOL || tok.type == TokenType.Comment + || tok.str == ':' || tok.str == 'ELSE'; // TODO: only ELSE if ifElse==true +} + +/// + export class ECSCompiler extends Tokenizer { currentScope: EntityScope | null = null; @@ -33,11 +76,12 @@ export class ECSCompiler extends Tokenizer { { type: ECSTokenType.Ellipsis, regex: /\.\./ }, { type: ECSTokenType.QuotedString, regex: /".*?"/ }, { type: ECSTokenType.CodeFragment, regex: /---.*?---/ }, - { 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: /0[xX][A-Fa-f0-9]+/ }, + { type: ECSTokenType.Integer, regex: /\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[%][01]+/ }, - { type: ECSTokenType.Operator, regex: /[.#=,:(){}\[\]\-]/ }, + { type: ECSTokenType.Integer, regex: /\d+/ }, + { type: ECSTokenType.Relational, regex: /[=<>][=<>]?/ }, + { type: ECSTokenType.Operator, regex: /[.#,:(){}\[\]\-\+]/ }, { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, { type: TokenType.Ignore, regex: /\/\/.*?[\n\r]/ }, { type: TokenType.Ignore, regex: /\/\*.*?\*\// }, @@ -153,9 +197,9 @@ export class ECSCompiler extends Tokenizer { parseDataType(): DataType { if (this.peekToken().type == 'integer') { - let lo = this.expectInteger(); + let lo = this.parseIntegerConstant(); this.expectToken('..'); - let hi = this.expectInteger(); + let hi = this.parseIntegerConstant(); this.checkLowerLimit(lo, -0x80000000, "lower int range"); this.checkUpperLimit(hi, 0x7fffffff, "upper int range"); this.checkUpperLimit(hi-lo, 0xffffffff, "int range"); @@ -163,7 +207,7 @@ export class ECSCompiler extends Tokenizer { // TODO: use default value? let defvalue; if (this.ifToken('default')) { - defvalue = this.expectInteger(); + defvalue = this.parseIntegerConstant(); } // TODO: check types return { dtype: 'int', lo, hi, defvalue } as IntType; @@ -180,7 +224,7 @@ export class ECSCompiler extends Tokenizer { let elem = this.parseDataType(); let baseoffset; if (this.ifToken('baseoffset')) { - baseoffset = this.expectInteger(); + baseoffset = this.parseIntegerConstant(); this.checkLowerLimit(baseoffset, -32768, "base offset"); this.checkUpperLimit(baseoffset, 32767, "base offset"); } @@ -201,11 +245,11 @@ export class ECSCompiler extends Tokenizer { // TODO: use default value? let defvalue; if (this.ifToken('default')) { - defvalue = this.expectInteger(); + defvalue = this.parseIntegerConstant(); } return { dtype: 'int', lo, hi, defvalue, enums } as IntType; } - this.compileError(`I expected a data type here.`); throw new Error(); + throw this.compileError(`I expected a data type here.`); } parseEnumIdent() { @@ -221,9 +265,7 @@ export class ECSCompiler extends Tokenizer { parseDataValue(field: DataField): DataValue | ForwardRef { let tok = this.peekToken(); - if (tok.type == ECSTokenType.Integer) { - return this.expectInteger(); - } + // TODO: move to expr if (tok.type == TokenType.Ident && field.dtype == 'int') { return this.parseEnumValue(this.consumeToken(), field); } @@ -251,7 +293,9 @@ export class ECSCompiler extends Tokenizer { let reftype = field.dtype == 'ref' ? field as RefType : undefined; return this.parseEntityForwardRef(reftype); } - this.compileError(`I expected a ${field.dtype} here.`); throw new Error(); + // TODO? + return this.parseIntegerConstant(); + // TODO: throw this.compileError(`I expected a ${field.dtype} here.`); } parseEntityForwardRef(reftype?: RefType): ForwardRef { @@ -261,7 +305,7 @@ export class ECSCompiler extends Tokenizer { parseDataArray() { this.expectToken('['); - let arr = this.parseList(this.expectInteger, ','); + let arr = this.parseList(this.parseIntegerConstant, ','); this.expectToken(']'); return arr; } @@ -289,7 +333,7 @@ export class ECSCompiler extends Tokenizer { let action = this.annotate(() => this.parseAction(system)); actions.push(action); } else if (cmd == 'locals') { - system.tempbytes = this.expectInteger(); + system.tempbytes = this.parseIntegerConstant(); } else { this.compileError(`Unexpected system keyword: ${cmd}`); } @@ -302,7 +346,7 @@ export class ECSCompiler extends Tokenizer { let tempbytes; if (this.peekToken().str == 'locals') { this.consumeToken(); - tempbytes = this.expectInteger(); + tempbytes = this.parseIntegerConstant(); } let system: System = { name, tempbytes, actions: [] }; let context: ActionContext = { scope: null, system }; @@ -333,12 +377,12 @@ export class ECSCompiler extends Tokenizer { } if (this.ifToken('limit')) { if (!query) { this.compileError(`A "${select}" query can't include a limit.`); } - else query.limit = this.expectInteger(); + else query.limit = this.parseIntegerConstant(); } const modifiers = this.parseModifiers(all_modifiers); let fitbytes = undefined; if (this.ifToken('fit')) { - fitbytes = this.expectInteger(); + fitbytes = this.parseIntegerConstant(); } let context: ActionContext = { scope: null, system }; // parse --- code --- @@ -376,8 +420,7 @@ export class ECSCompiler extends Tokenizer { } else if (prefix.str == '#') { const scope = this.currentScope; if (scope == null) { - this.compileError('You can only reference specific entities inside of a scope.'); - throw new Error(); + throw this.compileError('You can only reference specific entities inside of a scope.'); } let eref = this.parseEntityForwardRef(); this.deferred.push(() => { @@ -465,7 +508,7 @@ export class ECSCompiler extends Tokenizer { } parseEntity(): Entity { - if (!this.currentScope) { this.internalError(); throw new Error(); } + if (!this.currentScope) { throw this.internalError(); } const scope = this.currentScope; let entname = ''; if (this.peekToken().type == TokenType.Ident) { @@ -515,7 +558,7 @@ export class ECSCompiler extends Tokenizer { let code = codetok.str; code = code.substring(3, code.length - 3); let decoder = newDecoder(decoderid, code); - if (!decoder) { this.compileError(`I can't find a "${decoderid}" decoder.`); throw new Error() } + if (!decoder) { throw this.compileError(`I can't find a "${decoderid}" decoder.`); } let result; try { result = decoder.parse(); @@ -529,13 +572,13 @@ export class ECSCompiler extends Tokenizer { } getEntityField(e: Entity, name: string): ComponentFieldPair { - if (!this.currentScope) { this.internalError(); throw new Error(); } + if (!this.currentScope) { throw this.internalError(); } 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 component = comps[0]; let field = component.fields.find(f => f.name == name); - if (!field) { this.internalError(); throw new Error(); } + if (!field) { throw this.internalError(); } return { c: component, f: field }; } @@ -557,8 +600,7 @@ export class ECSCompiler extends Tokenizer { let name = 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.`, token.$loc) - throw new Error(); + throw this.compileError(`I couldn't find an entity named "${name}" in this scope.`, token.$loc) } return eref; } @@ -570,7 +612,7 @@ export class ECSCompiler extends Tokenizer { 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); + throw this.compileError(`This entity doesn't seem to fit the reference type.`, ref.token.$loc); id -= entities[0].id; } return id; @@ -579,7 +621,7 @@ export class ECSCompiler extends Tokenizer { parseSystemInstanceRef(): SystemInstance { let name = this.expectIdent().str; let system = this.em.getSystemByName(name); - if (!system) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc); + if (!system) throw this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc); let params = {}; let inst = { system, params, id: 0 }; return inst; @@ -587,7 +629,7 @@ export class ECSCompiler extends Tokenizer { parseSystemInstanceParameters(): SystemInstanceParameters { let scope = this.currentScope; - if (scope == null) throw new Error(); + if (scope == null) throw this.internalError(); if (this.peekToken().str == '[') { return { query: this.parseQuery() }; } @@ -619,6 +661,141 @@ export class ECSCompiler extends Tokenizer { checkLowerLimit(value: number, lower: number, what: string) { if (value < lower) this.compileError(`This ${what} is too low; must be ${lower} or more`); } + + // expression stuff + + parseConstant(): DataValue { + let expr = this.parseExpr(); + expr = this.em.evalExpr(expr, this.currentScope); + if (isLiteral(expr)) return expr.value; + throw this.compileError('This expression is not a constant.'); + } + parseIntegerConstant(): number { + let value = this.parseConstant(); + if (typeof value === 'number') return value; + throw this.compileError('This expression is not an integer.'); + } + parseExpr(): Expr { + var startloc = this.peekToken().$loc; + var expr = this.parseExpr1(this.parsePrimary(), 0); + var endloc = this.lasttoken.$loc; + expr.$loc = mergeLocs(startloc, endloc); + return expr; + } + parseExpr1(left: Expr, minPred: number): Expr { + let look = this.peekToken(); + while (getPrecedence(look) >= minPred) { + let op = this.consumeToken(); + let right: Expr = this.parsePrimary(); + look = this.peekToken(); + while (getPrecedence(look) > getPrecedence(op)) { + right = this.parseExpr1(right, getPrecedence(look)); + look = this.peekToken(); + } + var opfn = getOperator(op.str).f; + // use logical operators instead of bitwise? + if (op.str == 'AND') opfn = 'land'; + if (op.str == 'OR') opfn = 'lor'; + var valtype = this.exprTypeForOp(opfn, left, right, op); + left = { valtype:valtype, op:opfn, left: left, right: right }; + } + return left; + } + parsePrimary(): Expr { + let tok = this.consumeToken(); + switch (tok.type) { + case ECSTokenType.Integer: + this.pushbackToken(tok); + let value = this.expectInteger(); + let valtype : IntType = { dtype: 'int', lo: value, hi: value }; + return { valtype, value }; + case TokenType.Ident: + if (tok.str == 'NOT') { + let expr = this.parsePrimary(); + let valtype : IntType = { dtype: 'int', lo: 0, hi: 1 }; + return { valtype, op: 'lnot', expr: expr }; + } else { + this.pushbackToken(tok); + return this.parseVarSubscriptOrFunc(); + } + case ECSTokenType.Operator: + if (tok.str == '(') { + let expr = this.parseExpr(); + this.expectToken(')', `There should be another expression or a ")" here.`); + return expr; + } else if (tok.str == '-') { + let expr = this.parsePrimary(); // TODO: -2^2=-4 and -2-2=-4 + let valtype = (expr as ExprBase).valtype; + if (valtype?.dtype == 'int') { + let hi = Math.abs(valtype.hi); + let negtype : IntType = { dtype: 'int', lo: -hi, hi: hi }; + return { valtype: negtype, op: 'neg', expr: expr }; + } + } else if (tok.str == '+') { + return this.parsePrimary(); // ignore unary + + } + default: + throw this.compileError(`The expression is incomplete.`); + } + } + parseVarSubscriptOrFunc(): LExpr { + var tok = this.consumeToken(); + switch (tok.type) { + case TokenType.Ident: + // component:field + if (this.ifToken(':')) { + let ftok = this.consumeToken(); + let component = this.em.getComponentByName(tok.str); + if (!component) throw this.compileError(`A component named "${tok.str}" has not been defined.`); + let field = component.fields.find(f => f.name == ftok.str); + if (!field) throw this.compileError(`There is no "${ftok.str}" field in the ${tok.str} component.`); + if (!this.currentScope) throw this.compileError(`This operation only works inside of a scope.`); + let atypes = this.em.archetypesMatching({ include: [component] }) + let entities = this.currentScope.entitiesMatching(atypes); + return { entities, field } as EntitySetField; + } + // entity.field + if (this.ifToken('.')) { + let ftok = this.consumeToken(); + if (!this.currentScope) throw this.compileError(`This operation only works inside of a scope.`); + let entity = this.currentScope.getEntityByName(tok.str); + if (!entity) throw this.compileError(`An entity named "${tok.str}" has not been defined.`); + let component = this.em.singleComponentWithFieldName([entity.etype], ftok.str, ftok); + let field = component.fields.find(f => f.name == ftok.str); + if (!field) throw this.compileError(`There is no "${ftok.str}" field in this entity.`); + let entities = [entity]; + return { entities, field } as EntitySetField; + } + let args : Expr[] = []; + if (this.ifToken('(')) { + args = this.parseExprList(); + this.expectToken(')', `There should be another expression or a ")" here.`); + } + var loc = mergeLocs(tok.$loc, this.lasttoken.$loc); + var valtype = this.exprTypeForSubscript(tok.str, args, loc); + return { valtype: valtype, name: tok.str, args: args, $loc:loc }; + default: + throw this.compileError(`There should be a variable name here.`); + } + } + parseLexpr(): LExpr { + var lexpr = this.parseVarSubscriptOrFunc(); + //this.vardefs[lexpr.name] = lexpr; + //this.validateVarName(lexpr); + return lexpr; + } + exprTypeForOp(fnname: string, left: Expr, right: Expr, optok: Token) : DataType { + return { dtype: 'int', lo:0, hi:255 }; // TODO? + } + exprTypeForSubscript(fnname: string, args: Expr[], loc: SourceLocation) : DataType { + return { dtype: 'int', lo:0, hi:255 }; // TODO? + } + parseLexprList(): LExpr[] { + return this.parseList(this.parseLexpr, ','); + } + parseExprList(): Expr[] { + return this.parseList(this.parseExpr, ','); + } } /// diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index f08fb9d7..8a91864e 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -1,4 +1,5 @@ +import { Token } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { Bin, Packer } from "./binpack"; @@ -196,6 +197,72 @@ export interface ComponentFieldPair { f: DataField; } +// Expressions + +export interface ForwardRef extends SourceLocated { + reftype: RefType | undefined + token: Token +} + +export type LExpr = IndOp | EntitySetField; +export type ExprTypes = BinOp | UnOp | Literal | ForwardRef | LExpr; +export type Expr = ExprTypes; // & SourceLocated; +export type Opcode = string; +export type Value = DataValue; + +export interface ExprBase extends SourceLocated { + valtype: DataType; +} + +export interface Literal extends ExprBase { + value: Value; +} + +export interface LiteralInt extends Literal { + value: number; + valtype: IntType; +} + +export interface BinOp extends ExprBase { + op: Opcode; + left: Expr; + right: Expr; +} + +export interface UnOp extends ExprBase { + op: 'neg' | 'lnot' | 'bnot'; + expr: Expr; +} + +export interface IndOp extends ExprBase { + name: string; + args: Expr[]; +} + +export interface EntitySetField extends ExprBase { + entities: Entity[]; + field: DataField; +} + +export function isLiteral(arg: Expr): arg is Literal { + return (arg as any).value != null; +} +export function isLiteralInt(arg: Expr): arg is LiteralInt { + return isLiteral(arg) && arg.valtype.dtype == 'int'; +} +export function isLookup(arg: Expr): arg is IndOp { + return (arg as any).name != null; +} +export function isBinOp(arg: Expr): arg is BinOp { + return (arg as any).op != null && (arg as any).left != null && (arg as any).right != null; +} +export function isUnOp(arg: Expr): arg is UnOp { + return (arg as any).op != null && (arg as any).expr != null; +} + + +/// DIALECT + export class Dialect_CA65 { ASM_ITERATE_EACH_ASC = ` @@ -1758,4 +1825,50 @@ export class EntityManager { } return { scopes, components, fields, systems, events, entities }; } + + // expression stuff + + evalExpr(expr: Expr, scope: EntityScope | null) : Expr { + if (isLiteral(expr)) return expr; + if (isBinOp(expr) || isUnOp(expr)) { + var fn = (this as any)['evalop__' + expr.op]; + if (!fn) throw new ECSError(`no eval function for "${expr.op}"`); + } + if (isBinOp(expr)) { + expr.left = this.evalExpr(expr.left, scope); + expr.right = this.evalExpr(expr.right, scope); + let e = fn(expr.left, expr.right); + return e || expr; + } + if (isUnOp(expr)) { + expr.expr = this.evalExpr(expr.expr, scope); + let e = fn(expr.expr); + return e || expr; + } + return expr; + } + evalop__neg(arg: Expr) : Expr | undefined { + if (isLiteralInt(arg)) { + let valtype : IntType = { dtype:'int', + lo: -arg.valtype.hi, + hi: arg.valtype.hi }; + return { valtype, value: -arg.value }; + } + } + evalop__add(left: Expr, right: Expr) : Expr | undefined { + if (isLiteralInt(left) && isLiteralInt(right)) { + let valtype : IntType = { dtype:'int', + lo: left.valtype.lo + right.valtype.lo, + hi: left.valtype.hi + right.valtype.hi }; + return { valtype, value: left.value + right.value }; + } + } + evalop__sub(left: Expr, right: Expr) : Expr | undefined { + if (isLiteralInt(left) && isLiteralInt(right)) { + let valtype : IntType = { dtype:'int', + lo: left.valtype.lo - right.valtype.hi, + hi: left.valtype.hi - right.valtype.lo }; + return { valtype, value: left.value - right.value }; + } + } } diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index 2e88ce63..21111204 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -149,15 +149,17 @@ export class Tokenizer { this.errors.push({ path: loc.path, line: loc.line, label: this.curlabel, start: loc.start, end: loc.end, msg: msg }); } internalError() { - this.compileError("Internal error."); + return this.compileError("Internal error."); } notImplementedError() { - this.compileError("Not yet implemented."); + return this.compileError("Not yet implemented."); } - compileError(msg: string, loc?: SourceLocation, loc2?: SourceLocation) { + compileError(msg: string, loc?: SourceLocation, loc2?: SourceLocation) : CompileError { this.addError(msg, loc); //if (loc2 != null) this.addError(`...`, loc2); - throw new CompileError(msg, loc); + let e = new CompileError(msg, loc); + throw e; + return e; } peekToken(lookahead?: number): Token { let tok = this.tokens[lookahead || 0]; From fc79cf9b0f50adc92fe38f33fb272029e50b099c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 25 Feb 2022 11:45:13 -0600 Subject: [PATCH 098/104] ecs: block statement expr --- src/common/ecs/compiler.ts | 45 +++++++++++++++++---------- src/common/ecs/ecs.ts | 64 +++++++++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index a0de4bfc..d3dd6809 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -2,7 +2,7 @@ import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { newDecoder } from "./decoder"; -import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair, Expr, ExprBase, ForwardRef, isLiteral, EntitySetField, LExpr } from "./ecs"; +import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair, Expr, ExprBase, ForwardRef, isLiteral, EntityFieldOp, LExpr, Statement } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -349,10 +349,9 @@ export class ECSCompiler extends Tokenizer { tempbytes = this.parseIntegerConstant(); } let system: System = { name, tempbytes, actions: [] }; - let context: ActionContext = { scope: null, system }; - let text = this.parseCode(context); let select: SelectType = 'once'; - let action: Action = { text, event: name, select }; + let expr = this.parseBlockStatement(); + let action: Action = { expr, event: name, select }; system.actions.push(action); return system; } @@ -384,13 +383,11 @@ export class ECSCompiler extends Tokenizer { if (this.ifToken('fit')) { fitbytes = this.parseIntegerConstant(); } - let context: ActionContext = { scope: null, system }; - // parse --- code --- - let text = this.parseCode(context); let direction = undefined; if (modifiers['asc']) direction = 'asc'; else if (modifiers['desc']) direction = 'desc'; - let action = { text, event, query, join, select, direction, fitbytes }; + let expr = this.parseBlockStatement(); + let action = { expr, event, query, join, select, direction, fitbytes }; if (modifiers['critical']) (action as ActionWithJoin).critical = true; return action as ActionWithJoin; } @@ -441,7 +438,7 @@ export class ECSCompiler extends Tokenizer { return this.parseList(this.parseEventName, ","); } - parseCode(context: ActionContext): string { // TODOActionNode[] { + parseCode(): string { // TODOActionNode[] { // TODO: add $loc let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); let code = tok.str.substring(3, tok.str.length - 3); @@ -450,8 +447,8 @@ export class ECSCompiler extends Tokenizer { if (this.includeDebugInfo) this.addDebugInfo(lines, tok.$loc.line); code = lines.join('\n'); - let acomp = new ECSActionCompiler(context); - let nodes = acomp.parseFile(code, this.path); + //let acomp = new ECSActionCompiler(context); + //let nodes = acomp.parseFile(code, this.path); // TODO: return nodes return code; } @@ -694,8 +691,8 @@ export class ECSCompiler extends Tokenizer { } var opfn = getOperator(op.str).f; // use logical operators instead of bitwise? - if (op.str == 'AND') opfn = 'land'; - if (op.str == 'OR') opfn = 'lor'; + if (op.str == 'and') opfn = 'land'; + if (op.str == 'or') opfn = 'lor'; var valtype = this.exprTypeForOp(opfn, left, right, op); left = { valtype:valtype, op:opfn, left: left, right: right }; } @@ -710,7 +707,7 @@ export class ECSCompiler extends Tokenizer { let valtype : IntType = { dtype: 'int', lo: value, hi: value }; return { valtype, value }; case TokenType.Ident: - if (tok.str == 'NOT') { + if (tok.str == 'not') { let expr = this.parsePrimary(); let valtype : IntType = { dtype: 'int', lo: 0, hi: 1 }; return { valtype, op: 'lnot', expr: expr }; @@ -752,7 +749,7 @@ export class ECSCompiler extends Tokenizer { if (!this.currentScope) throw this.compileError(`This operation only works inside of a scope.`); let atypes = this.em.archetypesMatching({ include: [component] }) let entities = this.currentScope.entitiesMatching(atypes); - return { entities, field } as EntitySetField; + return { entities, field } as EntityFieldOp; } // entity.field if (this.ifToken('.')) { @@ -764,7 +761,7 @@ export class ECSCompiler extends Tokenizer { let field = component.fields.find(f => f.name == ftok.str); if (!field) throw this.compileError(`There is no "${ftok.str}" field in this entity.`); let entities = [entity]; - return { entities, field } as EntitySetField; + return { entities, field } as EntityFieldOp; } let args : Expr[] = []; if (this.ifToken('(')) { @@ -796,6 +793,22 @@ export class ECSCompiler extends Tokenizer { parseExprList(): Expr[] { return this.parseList(this.parseExpr, ','); } + parseBlockStatement(): Statement { + let valtype : IntType = { dtype:'int', lo:0, hi: 0 } + if (this.peekToken().type == ECSTokenType.CodeFragment) { + return { valtype, code: this.parseCode() }; + } + let cmd = this.expectTokens(['end','begin']); + if (cmd.str == 'begin') { + let stmts = []; + while (this.peekToken().str != 'end') { + stmts.push(this.parseBlockStatement()); + } + this.expectToken('end'); + return { valtype, stmts }; + } + throw this.internalError(); + } } /// diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 8a91864e..7f3c2a21 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -78,6 +78,7 @@ export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select', ' export type SelectType = typeof SELECT_TYPE[number]; +// TODO? export interface ActionContext { system: System scope: EntityScope | null @@ -112,20 +113,10 @@ export class CodePlaceholderNode extends ActionNode { } } -class QueryNode extends ActionNode { -} - -class WrapperNode extends ActionNode { -} - -class LoopNode extends ActionNode { -} - - export interface ActionBase extends SourceLocated { select: SelectType; event: string; - text: string; + expr: Statement; critical?: boolean; fitbytes?: number; } @@ -204,18 +195,17 @@ export interface ForwardRef extends SourceLocated { token: Token } -export type LExpr = IndOp | EntitySetField; -export type ExprTypes = BinOp | UnOp | Literal | ForwardRef | LExpr; -export type Expr = ExprTypes; // & SourceLocated; +export type LExpr = IndOp | EntityFieldOp; +export type Statement = InlineCode | BlockExpr; +export type Expr = BinOp | UnOp | Literal | ForwardRef | LExpr | Statement; export type Opcode = string; -export type Value = DataValue; export interface ExprBase extends SourceLocated { valtype: DataType; } export interface Literal extends ExprBase { - value: Value; + value: DataValue; } export interface LiteralInt extends Literal { @@ -239,11 +229,34 @@ export interface IndOp extends ExprBase { args: Expr[]; } -export interface EntitySetField extends ExprBase { +export interface CondOp extends ExprBase { + cond: Expr; + iftrue?: Expr; + iffalse?: Expr; +} + +export interface BlockExpr extends ExprBase { + loop?: boolean; + stmts: Expr[]; +} + +export interface BranchOp extends ExprBase { + branch: BlockExpr; +} + +export interface EntityFieldOp extends ExprBase { entities: Entity[]; field: DataField; } +export interface EntityContextOp extends ExprBase { + entities: Entity[]; +} + +export interface InlineCode extends ExprBase { + code: string; +} + export function isLiteral(arg: Expr): arg is Literal { return (arg as any).value != null; } @@ -259,6 +272,12 @@ export function isBinOp(arg: Expr): arg is BinOp { export function isUnOp(arg: Expr): arg is UnOp { return (arg as any).op != null && (arg as any).expr != null; } +export function isBlockStmt(arg: Expr): arg is BlockExpr { + return (arg as any).stmts != null; +} +export function isInlineCode(arg: Expr): arg is InlineCode { + return (arg as any).code != null; +} /// DIALECT @@ -796,7 +815,7 @@ class ActionEval { return code; } private getCodeAndProps(action: Action) { - let code = action.text; + let code = this.exprToCode(action.expr); let props: { [name: string]: string } = {}; if (action.select != 'once') { // TODO: detect cycles @@ -1125,6 +1144,15 @@ class ActionEval { if (code.split('\n ').length >= 4) return true; // TODO: :^/ return false; } + exprToCode(expr: Expr) : string { + if (isBlockStmt(expr)) { + return expr.stmts.map(node => this.exprToCode(node)).join('\n'); + } + if (isInlineCode(expr)) { + return expr.code; + } + throw new ECSError(`cannot convert expression to code`, expr); + } } class EventCodeStats { From e86eb56570791e5f12f2c01205d293fdb197c7fe Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 25 Feb 2022 15:02:44 -0600 Subject: [PATCH 099/104] ecs: moved query ops to exprs --- src/codemirror/ecs.js | 7 +- src/common/ecs/README.md | 22 ++ src/common/ecs/compiler.ts | 76 +++--- src/common/ecs/ecs.ts | 460 ++++++++++++++++++++----------------- test/ecs/titles.ecs | 2 +- 5 files changed, 319 insertions(+), 248 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 9d8bcad2..b65fdc2f 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -17,11 +17,12 @@ var keywords1, keywords2; var directives_list = [ - 'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', 'resource', + 'begin', 'end', + 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', 'resource', 'const', 'locals', 'var', - 'enum', 'default', 'array', 'baseoffset', 'critical', + 'enum', 'default', 'array', 'baseoffset', 'critical', 'fit', 'asc', 'desc', 'on', 'do', 'emit', 'limit', - 'once', 'foreach', 'with', 'join', 'if', + 'once', 'foreach', 'with', 'join', 'if', 'select', 'unroll', ]; var keywords_list = [ 'processor', diff --git a/src/common/ecs/README.md b/src/common/ecs/README.md index b27db6c7..cdb2b71d 100644 --- a/src/common/ecs/README.md +++ b/src/common/ecs/README.md @@ -189,3 +189,25 @@ Slice PNGs into sprites Maybe output decoder text Action priorities - before, after Generate C symbols + + +QUERIES +- when to intersect / union / start over + - with vs. foreach limit 1 (at top level) + - once vs begin/end + - mapping of regs vs working set +- debug info into source code (and make listing work) + + // subtract one when player wraps around vertically + on ymoved do begin + --- + lda {{ qr.atypes.find(a2 => a2 == a1)); return new EntitySet(this.scope, undefined, atypes, ents); } + union(qr: EntitySet) { + // TODO: remove dups + let ents = this.entities.concat(qr.entities); + let atypes = this.atypes.concat(qr.atypes); + return new EntitySet(this.scope, undefined, atypes, ents); + } isContiguous() { if (this.entities.length == 0) return true; let id = this.entities[0].id; @@ -700,6 +699,8 @@ class IndexRegister { // todo: generalize class ActionCPUState { + working: EntitySet; + props: {[key: string] : string}; xreg: IndexRegister | null = null; yreg: IndexRegister | null = null; } @@ -707,10 +708,6 @@ class ActionCPUState { class ActionEval { em : EntityManager; dialect : Dialect_CA65; - qr: EntitySet; - jr: EntitySet | undefined; - oldState : ActionCPUState; - entities : Entity[]; tmplabel = ''; label : string; seq : number; @@ -724,25 +721,7 @@ class ActionEval { { this.em = scope.em; this.dialect = scope.em.dialect; - this.oldState = scope.state; this.tmplabel = this.dialect.tempLabel(this.instance); - let q = (action as ActionWithQuery).query; - if (q) - this.qr = new EntitySet(scope, q); - else - this.qr = new EntitySet(scope, undefined, [], []); - // TODO? error if none? - if (instance.params.refEntity && instance.params.refField) { - let rf = instance.params.refField; - if (rf.f.dtype == 'ref') { - let rq = rf.f.query; - this.qr = this.qr.intersection(new EntitySet(scope, rq)); - //console.log('with', instance.params, rq, this.qr); - } - } else if (instance.params.query) { - this.qr = this.qr.intersection(new EntitySet(scope, instance.params.query)); - } - this.entities = this.qr.entities; //let query = (this.action as ActionWithQuery).query; //TODO? if (query && this.entities.length == 0) //throw new ECSError(`query doesn't match any entities`, query); // TODO @@ -750,131 +729,13 @@ class ActionEval { this.label = `${this.instance.system.name}__${action.event}__${this.seq}`; } begin() { - let state = this.scope.state = Object.assign(new ActionCPUState(), this.scope.state); - // TODO: generalize to other cpus/langs - switch (this.action.select) { - case 'once': - state.xreg = state.yreg = null; - break; - case 'foreach': - case 'unroll': - if (state.xreg && state.yreg) throw new ECSError('no more index registers', this.action); - if (state.xreg) state.yreg = new IndexRegister(this.scope, this.qr); - else state.xreg = new IndexRegister(this.scope, this.qr); - break; - case 'join': - // TODO: Joins don't work in superman (arrays offset?) - // ignore the join query, use the ref - if (state.xreg || state.yreg) throw new ECSError('no free index registers for join', this.action); - this.jr = new EntitySet(this.scope, (this.action as ActionWithJoin).join); - state.xreg = new IndexRegister(this.scope, this.jr); - state.yreg = new IndexRegister(this.scope, this.qr); - break; - case 'if': - case 'with': - // TODO: what if not in X because 1 element? - if (state.xreg && state.xreg.eset) { - state.xreg = state.xreg.narrow(this.qr, this.action); - if (state.xreg == null || state.xreg.eset?.entities == null) { - if (this.action.select == 'if') { - this.entities = []; - break; // "if" failed - } else { - throw new ECSError(`no entities in statement`, this.action); - } - } else { - this.entities = state.xreg.eset.entities; // TODO? - } - } else if (this.action.select == 'with') { - if (this.instance.params.refEntity && this.instance.params.refField) { - if (state.xreg) - state.xreg.eset = this.qr; - else - state.xreg = new IndexRegister(this.scope, this.qr); - // ??? - } else if (this.qr.entities.length != 1) - throw new ECSError(`${this.instance.system.name} query outside of loop must match exactly one entity`, this.action); //TODO - } - break; - } } end() { - this.scope.state = this.oldState; } codeToString(): string { - const allowEmpty = ['if','foreach','unroll','join']; - if (this.entities.length == 0 && allowEmpty.includes(this.action.select)) - return ''; - - let { code, props } = this.getCodeAndProps(this.action); - // replace @labels - code = this.replaceLabels(code, this.label); - // replace {{...}} tags - // TODO: use nodes instead - code = this.replaceTags(code, this.action, props); + let code = this.exprToCode(this.action.expr); return code; } - private getCodeAndProps(action: Action) { - let code = this.exprToCode(action.expr); - let props: { [name: string]: string } = {}; - if (action.select != 'once') { - // TODO: detect cycles - // TODO: "source"? - // TODO: what if only 1 item? - // TODO: what if join is subset of items? - if (action.select == 'join' && this.jr) { - //let jentities = this.jr.entities; - // TODO? if (jentities.length == 0) return ''; - // TODO? throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO - let joinfield = this.getJoinField(action, this.qr.atypes, this.jr.atypes); - // TODO: what if only 1 item? - // TODO: should be able to access fields via Y reg - code = this.wrapCodeInLoop(code, action, this.qr.entities, joinfield); - props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); //TODO? - } - // select subset of entities - let fullEntityCount = this.qr.entities.length; //entities.length.toString(); - let entities = this.entities; - // TODO: let loopreduce = !loopents || entities.length < loopents.length; - //console.log(action.event, entities.length, loopents.length); - // filter entities from loop? - // TODO: when to ignore if entities.length == 1 and not in for loop? - if (action.select == 'with') { - // TODO? when to load x? - if (this.instance.params.refEntity && this.instance.params.refField) { - let re = this.instance.params.refEntity; - let rf = this.instance.params.refField; - code = this.wrapCodeInRefLookup(code); - // TODO: only fetches 1st entity in list, need offset - let range = this.scope.getFieldRange(rf.c, rf.f.name); - let eidofs = re.id - range.elo; - props['%reffield'] = `${this.dialect.fieldsymbol(rf.c, rf.f, 0)}+${eidofs}`; - } else { - code = this.wrapCodeInFilter(code); - } - } - if (action.select == 'if') { - code = this.wrapCodeInFilter(code); - } - if (action.select == 'foreach' && entities.length > 1) { - code = this.wrapCodeInLoop(code, action, this.qr.entities); - } - if (action.select == 'unroll' && entities.length > 1) { - throw new ECSError('unroll is not yet implemented'); - } - // define properties - if (entities.length) { - props['%elo'] = entities[0].id.toString(); - props['%ehi'] = entities[entities.length - 1].id.toString(); - } - props['%ecount'] = entities.length.toString(); - props['%efullcount'] = fullEntityCount.toString(); - // TODO - props['%xofs'] = (this.scope.state.xreg?.offset() || 0).toString(); - props['%yofs'] = (this.scope.state.yreg?.offset() || 0).toString(); - } - return { code, props }; - } private replaceTags(code: string, action: Action, props: { [name: string]: string; }) { const tag_re = /\{\{(.+?)\}\}/g; code = code.replace(tag_re, (entire, group: string) => { @@ -924,7 +785,7 @@ class ActionEval { parseFieldArgs(args: string[]) { let fieldName = args[0]; let bitofs = parseInt(args[1] || '0'); - let component = this.em.singleComponentWithFieldName(this.qr.atypes, fieldName, this.action); + let component = this.em.singleComponentWithFieldName(this.scope.state.working.atypes, fieldName, this.action); let field = component.fields.find(f => f.name == fieldName); if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, this.action); return { component, field, bitofs }; @@ -935,14 +796,16 @@ class ActionEval { } __data(args: string[]) { let { component, field, bitofs } = this.parseFieldArgs(args); - if (this.qr.entities.length != 1) throw new ECSError(`data operates on exactly one entity`, this.action); // TODO? - let eid = this.qr.entities[0].id; // TODO? + let entities = this.scope.state.working.entities; + if (entities.length != 1) throw new ECSError(`data operates on exactly one entity`, this.action); // TODO? + let eid = entities[0].id; // TODO? return this.dialect.datasymbol(component, field, eid, bitofs); } __const(args: string[]) { let { component, field, bitofs } = this.parseFieldArgs(args); - if (this.qr.entities.length != 1) throw new ECSError(`const operates on exactly one entity`, this.action); // TODO? - let constVal = this.qr.entities[0].consts[mksymbol(component, field.name)]; + let entities = this.scope.state.working.entities; + if (entities.length != 1) throw new ECSError(`const operates on exactly one entity`, this.action); // TODO? + let constVal = entities[0].consts[mksymbol(component, field.name)]; if (constVal === undefined) throw new ECSError(`field is not constant`, this.action); // TODO? if (typeof constVal !== 'number') throw new ECSError(`field is not numeric`, this.action); // TODO? return constVal << bitofs; @@ -951,7 +814,8 @@ class ActionEval { // TODO: check select type and if we actually have an index... let ident = args[0]; let index = parseInt(args[1] || '0'); - if (this.entities.length == 1) { + let entities = this.scope.state.working.entities; + if (entities.length == 1) { return this.dialect.absolute(ident); } else { return this.dialect.indexed_x(ident, index); //TODO? @@ -989,40 +853,9 @@ class ActionEval { let startSymbol = this.dialect.scopeSymbol(args[0]); return this.dialect.jump(startSymbol); } - wrapCodeInLoop(code: string, action: ActionWithQuery, ents: Entity[], joinfield?: ComponentFieldPair): string { - // TODO: check ents - // TODO: check segment bounds - // TODO: what if 0 or 1 entitites? - // TODO: check > 127 or > 255 - let dir = action.direction; - let s = dir == 'desc' ? this.dialect.ASM_ITERATE_EACH_DESC : this.dialect.ASM_ITERATE_EACH_ASC; - if (joinfield) s = dir == 'desc' ? this.dialect.ASM_ITERATE_JOIN_DESC : this.dialect.ASM_ITERATE_JOIN_ASC; - s = s.replace('{{%code}}', code); - return s; - } - wrapCodeInFilter(code: string) { - // TODO: :-p filters too often? - const ents = this.entities; - const ents2 = this.oldState.xreg?.eset?.entities; - if (ents && ents2) { - let lo = ents[0].id; - let hi = ents[ents.length - 1].id; - let lo2 = ents2[0].id; - let hi2 = ents2[ents2.length - 1].id; - if (lo != lo2) - code = this.dialect.ASM_FILTER_RANGE_LO_X.replace('{{%code}}', code); - if (hi != hi2) - code = this.dialect.ASM_FILTER_RANGE_HI_X.replace('{{%code}}', code); - } - return code; - } - wrapCodeInRefLookup(code: string) { - code = this.dialect.ASM_LOOKUP_REF_X.replace('{{%code}}', code); - return code; - } generateCodeForField(fieldName: string, bitofs: number, canWrite: boolean): string { const action = this.action; - const qr = this.jr || this.qr; // TODO: why not both! + const qr = this.scope.state.working; var component: ComponentType; var baseLookup = false; @@ -1041,12 +874,12 @@ class ActionEval { let [cname, fname] = fieldName.split(':'); component = this.em.getComponentByName(cname); if (component == null) throw new ECSError(`no component named "${cname}"`, action) - entities = this.entities; + entities = this.scope.state.working.entities; fieldName = fname; baseLookup = true; } else { component = this.em.singleComponentWithFieldName(qr.atypes, fieldName, action); - entities = this.entities; + entities = this.scope.state.working.entities; } // find archetypes let field = component.fields.find(f => f.name == fieldName); @@ -1145,14 +978,221 @@ class ActionEval { return false; } exprToCode(expr: Expr) : string { + if (isQueryExpr(expr)) { + return this.queryExprToCode(expr); + } if (isBlockStmt(expr)) { - return expr.stmts.map(node => this.exprToCode(node)).join('\n'); + return this.blockStmtToCode(expr); } if (isInlineCode(expr)) { - return expr.code; + return this.evalInlineCode(expr.code); } throw new ECSError(`cannot convert expression to code`, expr); } + evalInlineCode(code: string) { + let props = this.scope.state.props || {}; + // replace @labels + code = this.replaceLabels(code, this.label); + // replace {{...}} tags + // TODO: use nodes instead + code = this.replaceTags(code, this.action, props); + return code; + } + blockStmtToCode(expr: BlockExpr) { + return expr.stmts.map(node => this.exprToCode(node)).join('\n'); + } + queryExprToCode(qexpr: QueryExpr) : string { + //console.log('query', this.action.event, qexpr.select, qexpr.query.include); + let q = this.startQuery(qexpr); + const allowEmpty = ['if','foreach','unroll','join']; + if (q.working.entities.length == 0 && allowEmpty.includes(qexpr.select)) { + //console.log('empty', this.action.event); + this.endQuery(q); + return ''; + } else { + this.scope.state.working = q.working; + this.scope.state.props = q.props; + //console.log('begin', this.action.event, this.scope.state); + q.code = this.evalInlineCode(q.code); + let body = this.blockStmtToCode(qexpr); + this.endQuery(q); + //console.log('end', this.action.event, this.scope.state); + body = q.code.replace('%%CODE%%', body); + return body; + } + } + startQuery(qexpr: QueryExpr) { + let oldState = this.scope.state; + let state = this.scope.state = Object.assign(new ActionCPUState(), oldState); + const action = this.action; + const scope = this.scope; + const instance = this.instance; + let select = qexpr.select; + let q = qexpr.query; + let qr; + let jr; + if (q) + qr = new EntitySet(scope, q); + else + qr = new EntitySet(scope, undefined, [], []); + // TODO? error if none? + if (instance.params.refEntity && instance.params.refField) { + let rf = instance.params.refField; + if (rf.f.dtype == 'ref') { + let rq = rf.f.query; + qr = qr.intersection(new EntitySet(scope, rq)); + //console.log('with', instance.params, rq, this.qr); + } + } else if (instance.params.query) { + qr = qr.intersection(new EntitySet(scope, instance.params.query)); + } + let entities = qr.entities; + // TODO: generalize to other cpus/langs + switch (select) { + case 'once': + // TODO: how is this different from begin/end? + //state.xreg = state.yreg = null; + break; + case 'foreach': + case 'unroll': + if (state.xreg && state.yreg) throw new ECSError('no more index registers', action); + if (state.xreg) state.yreg = new IndexRegister(scope, qr); + else state.xreg = new IndexRegister(scope, qr); + break; + case 'join': + // TODO: Joins don't work in superman (arrays offset?) + // ignore the join query, use the ref + if (state.xreg || state.yreg) throw new ECSError('no free index registers for join', action); + jr = new EntitySet(scope, qexpr.join); + state.xreg = new IndexRegister(scope, jr); + state.yreg = new IndexRegister(scope, qr); + break; + case 'if': + case 'with': + // TODO: what if not in X because 1 element? + if (state.xreg && state.xreg.eset) { + state.xreg = state.xreg.narrow(qr, action); + if (state.xreg == null || state.xreg.eset?.entities == null) { + if (select == 'if') { + entities = []; + break; // "if" failed + } else { + throw new ECSError(`no entities in statement`, action); + } + } else { + entities = state.xreg.eset.entities; // TODO? + } + } else if (select == 'with') { + if (instance.params.refEntity && instance.params.refField) { + if (state.xreg) + state.xreg.eset = qr; + else + state.xreg = new IndexRegister(scope, qr); + // ??? + } else if (qr.entities.length != 1) { + throw new ECSError(`${instance.system.name} query outside of loop must match exactly one entity`, action); //TODO + } + } + break; + } + // + let code = '%%CODE%%'; + let props: { [name: string]: string } = {}; + // TODO: detect cycles + // TODO: "source"? + // TODO: what if only 1 item? + // TODO: what if join is subset of items? + if (select == 'join' && jr) { + //let jentities = this.jr.entities; + // TODO? + // TODO? throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO + //console.log('join', qr, jr); + if (qr.entities.length) { + let joinfield = this.getJoinField(action, qr.atypes, jr.atypes); + // TODO: what if only 1 item? + // TODO: should be able to access fields via Y reg + code = this.wrapCodeInLoop(code, qexpr, qr.entities, joinfield); + props['%joinfield'] = this.dialect.fieldsymbol(joinfield.c, joinfield.f, 0); //TODO? + } + } + // select subset of entities + let fullEntityCount = qr.entities.length; //entities.length.toString(); + // TODO: let loopreduce = !loopents || entities.length < loopents.length; + //console.log(action.event, entities.length, loopents.length); + // filter entities from loop? + // TODO: when to ignore if entities.length == 1 and not in for loop? + if (select == 'with') { + // TODO? when to load x? + if (this.instance.params.refEntity && this.instance.params.refField) { + let re = this.instance.params.refEntity; + let rf = this.instance.params.refField; + code = this.wrapCodeInRefLookup(code); + // TODO: only fetches 1st entity in list, need offset + let range = this.scope.getFieldRange(rf.c, rf.f.name); + let eidofs = re.id - range.elo; + props['%reffield'] = `${this.dialect.fieldsymbol(rf.c, rf.f, 0)}+${eidofs}`; + } else { + code = this.wrapCodeInFilter(code, qr, oldState); + } + } + if (select == 'if') { + code = this.wrapCodeInFilter(code, qr, oldState); + } + if (select == 'foreach' && entities.length > 1) { + code = this.wrapCodeInLoop(code, qexpr, qr.entities); + } + if (select == 'unroll' && entities.length > 1) { + throw new ECSError('unroll is not yet implemented'); + } + // define properties + if (entities.length) { + props['%elo'] = entities[0].id.toString(); + props['%ehi'] = entities[entities.length - 1].id.toString(); + } + props['%ecount'] = entities.length.toString(); + props['%efullcount'] = fullEntityCount.toString(); + // TODO + props['%xofs'] = (this.scope.state.xreg?.offset() || 0).toString(); + props['%yofs'] = (this.scope.state.yreg?.offset() || 0).toString(); + let working = jr ? jr.union(qr) : qr; + working.entities = entities; // TODO? not good... + //console.log('working', action.event, working.entities.length, entities.length); + return { working, oldState, props, code }; + } + endQuery(q : { oldState: ActionCPUState }) { + this.scope.state = q.oldState; + } + wrapCodeInLoop(code: string, qexpr: QueryExpr, ents: Entity[], joinfield?: ComponentFieldPair): string { + // TODO: check ents + // TODO: check segment bounds + // TODO: what if 0 or 1 entitites? + // TODO: check > 127 or > 255 + let dir = qexpr.direction; + let s = dir == 'desc' ? this.dialect.ASM_ITERATE_EACH_DESC : this.dialect.ASM_ITERATE_EACH_ASC; + if (joinfield) s = dir == 'desc' ? this.dialect.ASM_ITERATE_JOIN_DESC : this.dialect.ASM_ITERATE_JOIN_ASC; + s = s.replace('{{%code}}', code); + return s; + } + wrapCodeInFilter(code: string, qr: EntitySet, oldState: ActionCPUState) { + // TODO: :-p filters too often? + const ents = this.scope.state.xreg?.eset?.entities; + const ents2 = oldState.xreg?.eset?.entities; + if (ents && ents.length && ents2) { + let lo = ents[0].id; + let hi = ents[ents.length - 1].id; + let lo2 = ents2[0].id; + let hi2 = ents2[ents2.length - 1].id; + if (lo != lo2) + code = this.dialect.ASM_FILTER_RANGE_LO_X.replace('{{%code}}', code); + if (hi != hi2) + code = this.dialect.ASM_FILTER_RANGE_HI_X.replace('{{%code}}', code); + } + return code; + } + wrapCodeInRefLookup(code: string) { + code = this.dialect.ASM_LOOKUP_REF_X.replace('{{%code}}', code); + return code; + } } class EventCodeStats { @@ -1177,7 +1217,7 @@ export class EntityScope implements SourceLocated { code = new CodeSegment(); componentsInScope = new Set(); resources = new Set(); - state = new ActionCPUState(); + state; isDemo = false; filePath = ''; @@ -1192,6 +1232,8 @@ export class EntityScope implements SourceLocated { public readonly parent: EntityScope | undefined ) { parent?.childScopes.push(this); + this.state = new ActionCPUState(); + this.state.working = new EntitySet(this, undefined, [], []); } newEntity(etype: EntityArchetype, name: string): Entity { // TODO: add parent ID? lock parent scope? diff --git a/test/ecs/titles.ecs b/test/ecs/titles.ecs index 51b6e05a..ca2f35d1 100644 --- a/test/ecs/titles.ecs +++ b/test/ecs/titles.ecs @@ -44,7 +44,7 @@ system Kernel48Pixel sta COLUP0 sta COLUP1 --- - on kerneldraw do if [Bitmap48] critical fit 63 + on kerneldraw do critical fit 63 if [Bitmap48] --- txa pha From 6f3affaaef16c460ffe1e465e6f7c19bca076b9e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 27 Feb 2022 09:31:09 -0600 Subject: [PATCH 100/104] ecs: nested labels --- src/common/ecs/ecs.ts | 18 +-- test/ecs/narrow1.txt | 6 +- test/ecs/score.txt | 118 +++++++++---------- test/ecs/sprites.txt | 204 ++++++++++++++++---------------- test/ecs/sprites1.txt | 178 ++++++++++++++-------------- test/ecs/superman.txt | 266 +++++++++++++++++++++--------------------- test/ecs/titles.txt | 248 +++++++++++++++++++-------------------- test/ecs/vcs1.txt | 86 +++++++------- test/ecs/vcslib.txt | 90 +++++++------- 9 files changed, 607 insertions(+), 607 deletions(-) diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 6e8a7e18..e60e91bf 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -765,8 +765,10 @@ class ActionEval { }); return code; } - private replaceLabels(code: string, label: string) { + private replaceLabels(code: string) { const label_re = /@(\w+)\b/g; + let seq = this.em.seq++; + let label = `${this.instance.system.name}__${this.action.event}__${seq}`; code = code.replace(label_re, (s: string, a: string) => `${label}__${a}`); return code; } @@ -992,7 +994,7 @@ class ActionEval { evalInlineCode(code: string) { let props = this.scope.state.props || {}; // replace @labels - code = this.replaceLabels(code, this.label); + code = this.replaceLabels(code); // replace {{...}} tags // TODO: use nodes instead code = this.replaceTags(code, this.action, props); @@ -1046,7 +1048,6 @@ class ActionEval { } else if (instance.params.query) { qr = qr.intersection(new EntitySet(scope, instance.params.query)); } - let entities = qr.entities; // TODO: generalize to other cpus/langs switch (select) { case 'once': @@ -1074,13 +1075,13 @@ class ActionEval { state.xreg = state.xreg.narrow(qr, action); if (state.xreg == null || state.xreg.eset?.entities == null) { if (select == 'if') { - entities = []; - break; // "if" failed + qr.entities = []; // "if" failed } else { throw new ECSError(`no entities in statement`, action); } } else { - entities = state.xreg.eset.entities; // TODO? + // TODO: must be a better way... + qr.entities = state.xreg.eset.entities; } } else if (select == 'with') { if (instance.params.refEntity && instance.params.refField) { @@ -1096,6 +1097,7 @@ class ActionEval { break; } // + let entities = qr.entities; let code = '%%CODE%%'; let props: { [name: string]: string } = {}; // TODO: detect cycles @@ -1155,7 +1157,6 @@ class ActionEval { props['%xofs'] = (this.scope.state.xreg?.offset() || 0).toString(); props['%yofs'] = (this.scope.state.yreg?.offset() || 0).toString(); let working = jr ? jr.union(qr) : qr; - working.entities = entities; // TODO? not good... //console.log('working', action.event, working.entities.length, entities.length); return { working, oldState, props, code }; } @@ -1581,7 +1582,7 @@ export class EntityScope implements SourceLocated { } normalizeCode(code: string, action: Action) { // TODO: use dialect to help with this - code = code.replace(/(\w+__\w+__)(\d+)(\w+)/g, (z,a,b,c) => a+c); + code = code.replace(/\b(\w+__\w+__)(\d+)__(\w+)\b/g, (z,a,b,c) => a+c); return code; } getSystemStats(inst: SystemInstance) : SystemStats { @@ -1687,7 +1688,6 @@ export class EntityScope implements SourceLocated { } let subcall = this.dialect.call(stats.labels[0]); for (let label of stats.labels) { - // TODO: need to trim()? let startdelim = this.dialect.comment(`start action ${label}`).trim(); let enddelim = this.dialect.comment(`end action ${label}`).trim(); let istart = code.indexOf(startdelim); diff --git a/test/ecs/narrow1.txt b/test/ecs/narrow1.txt index b7f3c3c9..8a231b34 100644 --- a/test/ecs/narrow1.txt +++ b/test/ecs/narrow1.txt @@ -23,14 +23,14 @@ __Start: ;;; start action move__start__1 ldx #0 -move__start__1____each: +move__start__2____each: lda Xpos_x_b0+2,x inx cpx #2 - jne move__start__1____each -move__start__1____exit: + jne move__start__2____each +move__start__2____exit: ;;; end action move__start__1 diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 3845e055..7ab5b81b 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -75,12 +75,12 @@ __BRK: dey bne :- -;;; start action FrameLoop__start__2 +;;; start action FrameLoop__start__3 -FrameLoop__start__2__NextFrame: +FrameLoop__start__4__NextFrame: FRAME_START -;;; start action Kernel6Digit__preframe__3 +;;; start action Kernel6Digit__preframe__5 Digit0 = Kernel6Digit__2__tmp+0 Digit1 = Kernel6Digit__2__tmp+2 @@ -88,20 +88,20 @@ Digit2 = Kernel6Digit__2__tmp+4 Digit3 = Kernel6Digit__2__tmp+6 Digit4 = Kernel6Digit__2__tmp+8 Digit5 = Kernel6Digit__2__tmp+10 -Kernel6Digit__preframe__3__BCD0 = Kernel6Digit__2__tmp+12 -Kernel6Digit__preframe__3__BCD1 = Kernel6Digit__2__tmp+13 -Kernel6Digit__preframe__3__BCD2 = Kernel6Digit__2__tmp+14 +Kernel6Digit__preframe__7__BCD0 = Kernel6Digit__2__tmp+12 +Kernel6Digit__preframe__7__BCD1 = Kernel6Digit__2__tmp+13 +Kernel6Digit__preframe__7__BCD2 = Kernel6Digit__2__tmp+14 lda BCDScore6_digits_b0 - sta Kernel6Digit__preframe__3__BCD0 + sta Kernel6Digit__preframe__7__BCD0 lda BCDScore6_digits_b8 - sta Kernel6Digit__preframe__3__BCD1 + sta Kernel6Digit__preframe__7__BCD1 lda BCDScore6_digits_b16 - sta Kernel6Digit__preframe__3__BCD2 + sta Kernel6Digit__preframe__7__BCD2 ldx #0 ; leftmost bitmap ldy #2 ; start from most-sigificant BCD value -Kernel6Digit__preframe__3__Loop: - lda Kernel6Digit__preframe__3__BCD0,y ; get BCD value +Kernel6Digit__preframe__7__Loop: + lda Kernel6Digit__preframe__7__BCD0,y ; get BCD value and #$f0 ; isolate high nibble (* 16) lsr ; shift right 1 bit (* 8) clc @@ -112,7 +112,7 @@ Kernel6Digit__preframe__3__Loop: sta Digit0+1,x ; store pointer hi byte inx inx ; next bitmap pointer - lda Kernel6Digit__preframe__3__BCD0,y ; get BCD value (again) + lda Kernel6Digit__preframe__7__BCD0,y ; get BCD value (again) and #$f ; isolate low nibble asl asl @@ -126,13 +126,13 @@ Kernel6Digit__preframe__3__Loop: inx inx ; next bitmap pointer dey ; next BCD value - bpl Kernel6Digit__preframe__3__Loop ; repeat until < 0 + bpl Kernel6Digit__preframe__7__Loop ; repeat until < 0 -;;; end action Kernel6Digit__preframe__3 +;;; end action Kernel6Digit__preframe__5 KERNEL_START -;;; start action Kernel6Digit__kernel__4 +;;; start action Kernel6Digit__kernel__8 lda PFColor_pfcolor_b0 sta COLUP0 @@ -156,23 +156,23 @@ Kernel6Digit__preframe__3__Loop: sta VDELP0 sta VDELP1 -;;; end action Kernel6Digit__kernel__4 +;;; end action Kernel6Digit__kernel__8 -;;; start action Kernel6Digit__kernel__5 +;;; start action Kernel6Digit__kernel__11 ; Display the resulting 48x8 bitmap ; using the Digit0-5 pointers. -Kernel6Digit__kernel__5__LoopCount = Kernel6Digit__2__tmp+12 -Kernel6Digit__kernel__5__Temp = Kernel6Digit__2__tmp+13 +Kernel6Digit__kernel__13__LoopCount = Kernel6Digit__2__tmp+12 +Kernel6Digit__kernel__13__Temp = Kernel6Digit__2__tmp+13 lda BGColor_bgcolor_b0 sta WSYNC sta COLUBK lda #7 - sta Kernel6Digit__kernel__5__LoopCount + sta Kernel6Digit__kernel__13__LoopCount SLEEPR 20 ; TODO? -Kernel6Digit__kernel__5__BigLoop: - ldy Kernel6Digit__kernel__5__LoopCount ; counts backwards +Kernel6Digit__kernel__13__BigLoop: + ldy Kernel6Digit__kernel__13__LoopCount ; counts backwards lda (Digit0),y ; load B0 (1st sprite byte) sta GRP0 ; B0 -> [GRP0] lda (Digit1),y ; load B1 -> A @@ -181,17 +181,17 @@ Kernel6Digit__kernel__5__BigLoop: lda (Digit2),y ; load B2 -> A sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 lda (Digit5),y ; load B5 -> A - sta Kernel6Digit__kernel__5__Temp ; B5 -> temp + sta Kernel6Digit__kernel__13__Temp ; B5 -> temp lda (Digit4),y ; load B4 tax ; -> X lda (Digit3),y ; load B3 -> A - ldy Kernel6Digit__kernel__5__Temp ; load B5 -> Y + ldy Kernel6Digit__kernel__13__Temp ; load B5 -> Y sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 - dec Kernel6Digit__kernel__5__LoopCount ; go to next line - bpl Kernel6Digit__kernel__5__BigLoop ; repeat until < 0 + dec Kernel6Digit__kernel__13__LoopCount ; go to next line + bpl Kernel6Digit__kernel__13__BigLoop ; repeat until < 0 lda #0 ; clear the sprite registers sta WSYNC @@ -201,9 +201,9 @@ Kernel6Digit__kernel__5__BigLoop: sta GRP1 sta COLUBK -;;; end action Kernel6Digit__kernel__5 +;;; end action Kernel6Digit__kernel__11 -;;; start action Kernel2Digit__kernel__6 +;;; start action Kernel2Digit__kernel__14 lda #$02 sta CTRLPF @@ -214,17 +214,17 @@ Kernel6Digit__kernel__5__BigLoop: lda PFColor_pfcolor_b0+1 sta COLUP1 -;;; end action Kernel2Digit__kernel__6 +;;; end action Kernel2Digit__kernel__14 -;;; start action Kernel2Digit__kernel__7 +;;; start action Kernel2Digit__kernel__17 lda #7 sta Kernel2Digit__4__tmp+0 -Kernel2Digit__kernel__7__Loop: +Kernel2Digit__kernel__19__Loop: ldx #0 sta WSYNC -;;; start action Kernel2Digit__compute2digit__8 +;;; start action Kernel2Digit__compute2digit__20 lda Kernel2Digit__4__tmp+1 ; load 1st pf sta PF1 ; store 1st pf @@ -236,14 +236,14 @@ Kernel2Digit__kernel__7__Loop: asl asl -;;; start action Kernel2Digit__fetchdigit__9 +;;; start action Kernel2Digit__fetchdigit__22 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit__fetchdigit__9 +;;; end action Kernel2Digit__fetchdigit__22 and #$0f ldy Kernel2Digit__4__tmp+2 ; load 2nd pf @@ -254,24 +254,24 @@ Kernel2Digit__kernel__7__Loop: lsr sty PF1 ; store 2nd pf -;;; start action Kernel2Digit__fetchdigit__10 +;;; start action Kernel2Digit__fetchdigit__24 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit__fetchdigit__10 +;;; end action Kernel2Digit__fetchdigit__24 and #$f0 ora Kernel2Digit__4__tmp+1 + 0 sta Kernel2Digit__4__tmp+1 + 0 -;;; end action Kernel2Digit__compute2digit__8 +;;; end action Kernel2Digit__compute2digit__20 inx -;;; start action Kernel2Digit__compute2digit__11 +;;; start action Kernel2Digit__compute2digit__26 lda Kernel2Digit__4__tmp+1 ; load 1st pf sta PF1 ; store 1st pf @@ -283,14 +283,14 @@ Kernel2Digit__kernel__7__Loop: asl asl -;;; start action Kernel2Digit__fetchdigit__12 +;;; start action Kernel2Digit__fetchdigit__28 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit__fetchdigit__12 +;;; end action Kernel2Digit__fetchdigit__28 and #$0f ldy Kernel2Digit__4__tmp+2 ; load 2nd pf @@ -301,40 +301,40 @@ Kernel2Digit__kernel__7__Loop: lsr sty PF1 ; store 2nd pf -;;; start action Kernel2Digit__fetchdigit__13 +;;; start action Kernel2Digit__fetchdigit__30 adc Kernel2Digit__4__tmp+0 tay ; TODO: select your own? lda FontTablePF,y -;;; end action Kernel2Digit__fetchdigit__13 +;;; end action Kernel2Digit__fetchdigit__30 and #$f0 ora Kernel2Digit__4__tmp+1 + 1 sta Kernel2Digit__4__tmp+1 + 1 -;;; end action Kernel2Digit__compute2digit__11 +;;; end action Kernel2Digit__compute2digit__26 ; playfield dec Kernel2Digit__4__tmp+0 - bpl Kernel2Digit__kernel__7__Loop + bpl Kernel2Digit__kernel__19__Loop ; dex ; stx PF1 -;;; end action Kernel2Digit__kernel__7 +;;; end action Kernel2Digit__kernel__17 KERNEL_END -;;; start action JoyButton__postframe__14 +;;; start action JoyButton__postframe__32 lda INPT4 ;read button input - bmi JoyButton__postframe__14__NotPressed + bmi JoyButton__postframe__34__NotPressed -;;; start action IncScore__joybutton__15 +;;; start action IncScore__joybutton__35 -;;; start action BCDMath__AddBCD4__16 +;;; start action BCDMath__AddBCD4__38 .ifnblank $0210 lda #<$0210 @@ -355,20 +355,20 @@ Kernel2Digit__kernel__7__Loop: sta BCDScore6_digits_b16 cld ; exit BCD mode -;;; end action BCDMath__AddBCD4__16 +;;; end action BCDMath__AddBCD4__38 -;;; end action IncScore__joybutton__15 +;;; end action IncScore__joybutton__35 -JoyButton__postframe__14__NotPressed: +JoyButton__postframe__34__NotPressed: -;;; end action JoyButton__postframe__14 +;;; end action JoyButton__postframe__32 FRAME_END - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__4__NextFrame ; loop to next frame -;;; end action FrameLoop__start__2 +;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -380,7 +380,7 @@ VecBRK: .word Main::__BRK FontTable: -;;; start action FontTable__FontTable__17 +;;; start action FontTable__FontTable__41 ; Font table for digits 0-9 (8x8 pixels) ;;{w:8,h:8,count:10,brev:1,flip:1};; @@ -390,11 +390,11 @@ FontTable: .byte $00,$3c,$66,$66,$7c,$60,$66,$3c,$00,$18,$18,$18,$18,$0c,$66,$7e .byte $00,$3c,$66,$66,$3c,$66,$66,$3c,$00,$3c,$66,$06,$3e,$66,$66,$3c -;;; end action FontTable__FontTable__17 +;;; end action FontTable__FontTable__41 FontTablePF: -;;; start action FontTablePF__FontTablePF__18 +;;; start action FontTablePF__FontTablePF__43 ; Font table for digits 0-9 (4x8 pixels) ;;{w:8,h:8,count:10,brev:1,flip:1};; @@ -410,7 +410,7 @@ FontTablePF: .byte $00,$00,$EE,$22,$EE,$AA,$EE,$00 ;; -;;; end action FontTablePF__FontTablePF__18 +;;; end action FontTablePF__FontTablePF__43 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/sprites.txt b/test/ecs/sprites.txt index a3c56bc9..b02aedb7 100644 --- a/test/ecs/sprites.txt +++ b/test/ecs/sprites.txt @@ -142,12 +142,12 @@ __BRK: dey bne :- -;;; start action FrameLoop__start__2 +;;; start action FrameLoop__start__3 -FrameLoop__start__2__NextFrame: +FrameLoop__start__4__NextFrame: FRAME_START -;;; start action Kernel2Sprite__preframe__3 +;;; start action Kernel2Sprite__preframe__5 .define KLINES #192 .define KPAD 32 @@ -162,17 +162,17 @@ FrameLoop__start__2__NextFrame: sta Kernel2Sprite__2__tmp+6 sta Kernel2Sprite__2__tmp+7 -;;; end action Kernel2Sprite__preframe__3 +;;; end action Kernel2Sprite__preframe__5 -;;; start action Kernel2Sprite__preframe__4 +;;; start action Kernel2Sprite__preframe__8 ldy #0 -Kernel2Sprite__preframe__4____each: +Kernel2Sprite__preframe__9____each: ldx SpriteSlot_sprite_b0,y ; flags set according to sprite slot value ; skip sprite if negative - jmi Kernel2Sprite__preframe__4__nosprite + jmi Kernel2Sprite__preframe__10__nosprite ; set player object flags lda Sprite_plyrflags_b0,x sta NUSIZ0,y @@ -211,16 +211,16 @@ Kernel2Sprite__preframe__4____each: ldx Kernel2Sprite__2__tmp+12 ; restore X lda HasYpos_ypos_b0,x sta Kernel2Sprite__2__tmp+10,y -Kernel2Sprite__preframe__4__nosprite: +Kernel2Sprite__preframe__10__nosprite: iny cpy #2 - jne Kernel2Sprite__preframe__4____each -Kernel2Sprite__preframe__4____exit: + jne Kernel2Sprite__preframe__9____each +Kernel2Sprite__preframe__9____exit: -;;; end action Kernel2Sprite__preframe__4 +;;; end action Kernel2Sprite__preframe__8 -;;; start action Kernel2Sprite__preframe__5 +;;; start action Kernel2Sprite__preframe__11 ; shuffle pointers into (MSB, LSB) byte order ; L0 L1 H0 H1 -> L0 H0 L1 H1 @@ -233,34 +233,34 @@ Kernel2Sprite__preframe__4____exit: sty Kernel2Sprite__2__tmp+5 sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite__preframe__5 +;;; end action Kernel2Sprite__preframe__11 -;;; start action Kernel2Sprite__preframe__6 +;;; start action Kernel2Sprite__preframe__13 lda #162 sta COLUBK -;;; end action Kernel2Sprite__preframe__6 +;;; end action Kernel2Sprite__preframe__13 -;;; start action Kernel2Sprite__preframe__7 +;;; start action Kernel2Sprite__preframe__16 -;;; end action Kernel2Sprite__preframe__7 +;;; end action Kernel2Sprite__preframe__16 -;;; start action SetXPos__preframe__8 +;;; start action SetXPos__preframe__17 sta HMCLR -;;; end action SetXPos__preframe__8 +;;; end action SetXPos__preframe__17 -;;; start action SetXPos__preframe__9 +;;; start action SetXPos__preframe__19 ldy #0 -SetXPos__preframe__9____each: +SetXPos__preframe__20____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x -;;; start action SetHorizPos__SetHorizPos__10 +;;; start action SetHorizPos__SetHorizPos__22 ; SetHorizPos routine ; A = X coordinate @@ -268,9 +268,9 @@ SetXPos__preframe__9____each: sta WSYNC ; start a new line sec ; set carry flag nop -SetHorizPos__SetHorizPos__10__DivideLoop: +SetHorizPos__SetHorizPos__23__DivideLoop: sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__10__DivideLoop ; branch until negative + bcs SetHorizPos__SetHorizPos__23__DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl @@ -279,52 +279,52 @@ SetHorizPos__SetHorizPos__10__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos__SetHorizPos__10 +;;; end action SetHorizPos__SetHorizPos__22 iny cpy #2 - jne SetXPos__preframe__9____each -SetXPos__preframe__9____exit: + jne SetXPos__preframe__20____each +SetXPos__preframe__20____exit: -;;; end action SetXPos__preframe__9 +;;; end action SetXPos__preframe__19 -;;; start action SetXPos__preframe__11 +;;; start action SetXPos__preframe__24 -;;; end action SetXPos__preframe__11 +;;; end action SetXPos__preframe__24 -;;; start action SetXPos__preframe__12 +;;; start action SetXPos__preframe__25 sta WSYNC sta HMOVE -;;; end action SetXPos__preframe__12 +;;; end action SetXPos__preframe__25 KERNEL_START -;;; start action Kernel2Sprite__kernel__13 +;;; start action Kernel2Sprite__kernel__27 ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite__kernel__13 +;;; end action Kernel2Sprite__kernel__27 -;;; start action Kernel2Sprite__kernel__14 +;;; start action Kernel2Sprite__kernel__30 ldy #192 -Kernel2Sprite__kernel__14__LVScan: +Kernel2Sprite__kernel__32__LVScan: -;;; start action Kernel2Sprite__scanline__15 +;;; start action Kernel2Sprite__scanline__33 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__15__DoDraw1 + bcs Kernel2Sprite__scanline__34__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__15__DoDraw1: +Kernel2Sprite__scanline__34__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 0 = 0 sta WSYNC @@ -335,32 +335,32 @@ Kernel2Sprite__scanline__15__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__15__DoDraw2 + bcs Kernel2Sprite__scanline__34__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__15__DoDraw2: +Kernel2Sprite__scanline__34__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite__scanline__15 +;;; end action Kernel2Sprite__scanline__33 -;;; start action Kernel2Sprite__scanline__16 +;;; start action Kernel2Sprite__scanline__35 -;;; end action Kernel2Sprite__scanline__16 +;;; end action Kernel2Sprite__scanline__35 dey ; next scanline -;;; start action Kernel2Sprite__scanline__17 +;;; start action Kernel2Sprite__scanline__36 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__17__DoDraw1 + bcs Kernel2Sprite__scanline__37__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__17__DoDraw1: +Kernel2Sprite__scanline__37__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 1 = 0 sta WSYNC @@ -371,27 +371,27 @@ Kernel2Sprite__scanline__17__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__17__DoDraw2 + bcs Kernel2Sprite__scanline__37__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__17__DoDraw2: +Kernel2Sprite__scanline__37__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite__scanline__17 +;;; end action Kernel2Sprite__scanline__36 -;;; start action Kernel2Sprite__scanline__18 +;;; start action Kernel2Sprite__scanline__38 -;;; end action Kernel2Sprite__scanline__18 +;;; end action Kernel2Sprite__scanline__38 dey ; next scanline - bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines + bne Kernel2Sprite__kernel__32__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite__kernel__14 +;;; end action Kernel2Sprite__kernel__30 -;;; start action Kernel2Sprite__kernel__19 +;;; start action Kernel2Sprite__kernel__39 lda #0 sta GRP0 @@ -399,94 +399,94 @@ Kernel2Sprite__scanline__17__DoDraw2: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite__kernel__19 +;;; end action Kernel2Sprite__kernel__39 KERNEL_END -;;; start action Joystick__postframe__20 +;;; start action Joystick__postframe__41 ; 2 control inputs share a single byte, 4 bits each lda SWCHA sta Joystick__3__tmp+0 -;;; end action Joystick__postframe__20 +;;; end action Joystick__postframe__41 -;;; start action Joystick__postframe__21 +;;; start action Joystick__postframe__43 ldx #0 -Joystick__postframe__21____each: +Joystick__postframe__44____each: asl Joystick__3__tmp+0 - bcs Joystick__postframe__21__SkipMoveRight + bcs Joystick__postframe__45__SkipMoveRight -;;; start action MoveJoyX__joyright__22 +;;; start action MoveJoyX__joyright__46 lda HasXpos_xpos_b0,x clc adc #1 cmp #152 - bcs MoveJoyX__joyright__22__nomove + bcs MoveJoyX__joyright__48__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyright__22__nomove: +MoveJoyX__joyright__48__nomove: -;;; end action MoveJoyX__joyright__22 +;;; end action MoveJoyX__joyright__46 -Joystick__postframe__21__SkipMoveRight: +Joystick__postframe__45__SkipMoveRight: asl Joystick__3__tmp+0 - bcs Joystick__postframe__21__SkipMoveLeft + bcs Joystick__postframe__45__SkipMoveLeft -;;; start action MoveJoyX__joyleft__23 +;;; start action MoveJoyX__joyleft__49 lda HasXpos_xpos_b0,x sec sbc #1 - bcc MoveJoyX__joyleft__23__nomove + bcc MoveJoyX__joyleft__51__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyleft__23__nomove: +MoveJoyX__joyleft__51__nomove: -;;; end action MoveJoyX__joyleft__23 +;;; end action MoveJoyX__joyleft__49 -Joystick__postframe__21__SkipMoveLeft: +Joystick__postframe__45__SkipMoveLeft: asl Joystick__3__tmp+0 - bcs Joystick__postframe__21__SkipMoveDown + bcs Joystick__postframe__45__SkipMoveDown -;;; start action MoveJoyY__joydown__24 +;;; start action MoveJoyY__joydown__52 lda HasYpos_ypos_b0,x clc adc #1 cmp #220 - bcs MoveJoyY__joydown__24__nomove + bcs MoveJoyY__joydown__54__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joydown__24__nomove: +MoveJoyY__joydown__54__nomove: -;;; end action MoveJoyY__joydown__24 +;;; end action MoveJoyY__joydown__52 -Joystick__postframe__21__SkipMoveDown: +Joystick__postframe__45__SkipMoveDown: asl Joystick__3__tmp+0 - bcs Joystick__postframe__21__SkipMoveUp + bcs Joystick__postframe__45__SkipMoveUp -;;; start action MoveJoyY__joyup__25 +;;; start action MoveJoyY__joyup__55 lda HasYpos_ypos_b0,x sec sbc #1 - bcc MoveJoyY__joyup__25__nomove + bcc MoveJoyY__joyup__57__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joyup__25__nomove: +MoveJoyY__joyup__57__nomove: -;;; end action MoveJoyY__joyup__25 +;;; end action MoveJoyY__joyup__55 -Joystick__postframe__21__SkipMoveUp: +Joystick__postframe__45__SkipMoveUp: inx cpx #4 - jne Joystick__postframe__21____each -Joystick__postframe__21____exit: + jne Joystick__postframe__44____each +Joystick__postframe__44____exit: -;;; end action Joystick__postframe__21 +;;; end action Joystick__postframe__43 -;;; start action SpriteShuffler__postframe__26 +;;; start action SpriteShuffler__postframe__58 ; load two sprite slots at left side of array lda SpriteSlot_sprite_b0 @@ -495,36 +495,36 @@ Joystick__postframe__21____exit: sta SpriteShuffler__8__tmp+1 ; move two slots to the left ldx #0 -SpriteShuffler__postframe__26__loop: +SpriteShuffler__postframe__60__loop: lda SpriteSlot_sprite_b0+2,x sta SpriteSlot_sprite_b0,x inx cpx #4-2 - bne SpriteShuffler__postframe__26__loop + bne SpriteShuffler__postframe__60__loop ; store two sprite slots at right side of array lda SpriteShuffler__8__tmp+0 sta SpriteSlot_sprite_b0+4-2 lda SpriteShuffler__8__tmp+1 sta SpriteSlot_sprite_b0+4-1 -;;; end action SpriteShuffler__postframe__26 +;;; end action SpriteShuffler__postframe__58 -;;; start action SpriteHider__postframe__27 +;;; start action SpriteHider__postframe__61 lda #4-1 sta SpriteHider__9__tmp+0 -;;; end action SpriteHider__postframe__27 +;;; end action SpriteHider__postframe__61 -;;; start action SpriteHider__postframe__28 +;;; start action SpriteHider__postframe__64 ldy #0 -SpriteHider__postframe__28____each: +SpriteHider__postframe__65____each: ldx SpriteSlot_sprite_b0,y lda HasYpos_ypos_b0,x cmp #192 - bcc SpriteHider__postframe__28__skip + bcc SpriteHider__postframe__66__skip ; swap this sprite slot with slot at end of array lda SpriteSlot_sprite_b0,y pha @@ -534,20 +534,20 @@ SpriteHider__postframe__28____each: pla sta SpriteSlot_sprite_b0,x dec SpriteHider__9__tmp+0 -SpriteHider__postframe__28__skip: +SpriteHider__postframe__66__skip: iny cpy #2 - jne SpriteHider__postframe__28____each -SpriteHider__postframe__28____exit: + jne SpriteHider__postframe__65____each +SpriteHider__postframe__65____exit: -;;; end action SpriteHider__postframe__28 +;;; end action SpriteHider__postframe__64 FRAME_END - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__4__NextFrame ; loop to next frame -;;; end action FrameLoop__start__2 +;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index 80fdaf94..f32432b5 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -142,22 +142,22 @@ __BRK: dey bne :- -;;; start action FrameLoop__start__2 +;;; start action FrameLoop__start__3 -FrameLoop__start__2__NextFrame: +FrameLoop__start__4__NextFrame: FRAME_START -;;; start action Kernel2Sprite__preframe__3 +;;; start action Kernel2Sprite__preframe__5 .define KLINES #192 .define KPAD 32 -;;; end action Kernel2Sprite__preframe__3 +;;; end action Kernel2Sprite__preframe__5 -;;; start action Kernel2Sprite__preframe__4 +;;; start action Kernel2Sprite__preframe__8 ldy #0 -Kernel2Sprite__preframe__4____each: +Kernel2Sprite__preframe__9____each: ldx SpriteSlot_sprite_b0,y ; set player object flags @@ -201,12 +201,12 @@ Kernel2Sprite__preframe__4____each: iny cpy #2 - jne Kernel2Sprite__preframe__4____each -Kernel2Sprite__preframe__4____exit: + jne Kernel2Sprite__preframe__9____each +Kernel2Sprite__preframe__9____exit: -;;; end action Kernel2Sprite__preframe__4 +;;; end action Kernel2Sprite__preframe__8 -;;; start action Kernel2Sprite__preframe__5 +;;; start action Kernel2Sprite__preframe__11 ; L0 L1 H0 H1 -> L0 H0 L1 H1 lda Kernel2Sprite__2__tmp+1 @@ -218,34 +218,34 @@ Kernel2Sprite__preframe__4____exit: sty Kernel2Sprite__2__tmp+5 sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite__preframe__5 +;;; end action Kernel2Sprite__preframe__11 -;;; start action Kernel2Sprite__preframe__6 +;;; start action Kernel2Sprite__preframe__13 lda #162 sta COLUBK -;;; end action Kernel2Sprite__preframe__6 +;;; end action Kernel2Sprite__preframe__13 -;;; start action Kernel2Sprite__preframe__7 +;;; start action Kernel2Sprite__preframe__16 -;;; end action Kernel2Sprite__preframe__7 +;;; end action Kernel2Sprite__preframe__16 -;;; start action SetXPos__preframe__8 +;;; start action SetXPos__preframe__17 sta HMCLR -;;; end action SetXPos__preframe__8 +;;; end action SetXPos__preframe__17 -;;; start action SetXPos__preframe__9 +;;; start action SetXPos__preframe__19 ldy #0 -SetXPos__preframe__9____each: +SetXPos__preframe__20____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x -;;; start action SetHorizPos__SetHorizPos__10 +;;; start action SetHorizPos__SetHorizPos__22 ; SetHorizPos routine ; A = X coordinate @@ -253,9 +253,9 @@ SetXPos__preframe__9____each: sta WSYNC ; start a new line sec ; set carry flag nop -SetHorizPos__SetHorizPos__10__DivideLoop: +SetHorizPos__SetHorizPos__23__DivideLoop: sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__10__DivideLoop ; branch until negative + bcs SetHorizPos__SetHorizPos__23__DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl @@ -264,42 +264,42 @@ SetHorizPos__SetHorizPos__10__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos__SetHorizPos__10 +;;; end action SetHorizPos__SetHorizPos__22 iny cpy #2 - jne SetXPos__preframe__9____each -SetXPos__preframe__9____exit: + jne SetXPos__preframe__20____each +SetXPos__preframe__20____exit: -;;; end action SetXPos__preframe__9 +;;; end action SetXPos__preframe__19 -;;; start action SetXPos__preframe__11 +;;; start action SetXPos__preframe__24 -;;; end action SetXPos__preframe__11 +;;; end action SetXPos__preframe__24 -;;; start action SetXPos__preframe__12 +;;; start action SetXPos__preframe__25 sta WSYNC sta HMOVE -;;; end action SetXPos__preframe__12 +;;; end action SetXPos__preframe__25 KERNEL_START -;;; start action Kernel2Sprite__kernel__13 +;;; start action Kernel2Sprite__kernel__27 ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite__kernel__13 +;;; end action Kernel2Sprite__kernel__27 -;;; start action Kernel2Sprite__kernel__14 +;;; start action Kernel2Sprite__kernel__30 ; define macro for each line - .macro Kernel2Sprite__kernel__14__DrawLine do_wsync + .macro Kernel2Sprite__kernel__32__DrawLine do_wsync .local DoDraw1 .local DoDraw2 ; draw player 0 @@ -330,22 +330,22 @@ DoDraw2: .endmacro ldy #192 -Kernel2Sprite__kernel__14__LVScan: +Kernel2Sprite__kernel__32__LVScan: -;;; start action Kernel2Sprite__scanline1__15 +;;; start action Kernel2Sprite__scanline1__33 -;;; end action Kernel2Sprite__scanline1__15 +;;; end action Kernel2Sprite__scanline1__33 - Kernel2Sprite__kernel__14__DrawLine 1 ; macro: draw scanline w/ WSYNC + Kernel2Sprite__kernel__32__DrawLine 1 ; macro: draw scanline w/ WSYNC dey ; next scanline - Kernel2Sprite__kernel__14__DrawLine 0 ; macro: draw scanline no WSYNC + Kernel2Sprite__kernel__32__DrawLine 0 ; macro: draw scanline no WSYNC dey ; next scanline - bne Kernel2Sprite__kernel__14__LVScan ; repeat until out of lines + bne Kernel2Sprite__kernel__32__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite__kernel__14 +;;; end action Kernel2Sprite__kernel__30 -;;; start action Kernel2Sprite__kernel__16 +;;; start action Kernel2Sprite__kernel__34 lda #0 sta GRP0 @@ -353,94 +353,94 @@ Kernel2Sprite__kernel__14__LVScan: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite__kernel__16 +;;; end action Kernel2Sprite__kernel__34 KERNEL_END -;;; start action Joystick__postframe__17 +;;; start action Joystick__postframe__37 ; 2 control inputs share a single byte, 4 bits each lda SWCHA sta Joystick__3__tmp+0 -;;; end action Joystick__postframe__17 +;;; end action Joystick__postframe__37 -;;; start action Joystick__postframe__18 +;;; start action Joystick__postframe__39 ldx #0 -Joystick__postframe__18____each: +Joystick__postframe__40____each: asl Joystick__3__tmp+0 - bcs Joystick__postframe__18__SkipMoveRight + bcs Joystick__postframe__41__SkipMoveRight -;;; start action MoveJoyX__joyright__19 +;;; start action MoveJoyX__joyright__42 lda HasXpos_xpos_b0,x clc adc #1 cmp #152 - bcs MoveJoyX__joyright__19__nomove + bcs MoveJoyX__joyright__44__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyright__19__nomove: +MoveJoyX__joyright__44__nomove: -;;; end action MoveJoyX__joyright__19 +;;; end action MoveJoyX__joyright__42 -Joystick__postframe__18__SkipMoveRight: +Joystick__postframe__41__SkipMoveRight: asl Joystick__3__tmp+0 - bcs Joystick__postframe__18__SkipMoveLeft + bcs Joystick__postframe__41__SkipMoveLeft -;;; start action MoveJoyX__joyleft__20 +;;; start action MoveJoyX__joyleft__45 lda HasXpos_xpos_b0,x sec sbc #1 - bcc MoveJoyX__joyleft__20__nomove + bcc MoveJoyX__joyleft__47__nomove sta HasXpos_xpos_b0,x -MoveJoyX__joyleft__20__nomove: +MoveJoyX__joyleft__47__nomove: -;;; end action MoveJoyX__joyleft__20 +;;; end action MoveJoyX__joyleft__45 -Joystick__postframe__18__SkipMoveLeft: +Joystick__postframe__41__SkipMoveLeft: asl Joystick__3__tmp+0 - bcs Joystick__postframe__18__SkipMoveDown + bcs Joystick__postframe__41__SkipMoveDown -;;; start action MoveJoyY__joydown__21 +;;; start action MoveJoyY__joydown__48 lda HasYpos_ypos_b0,x clc adc #1 cmp #220 - bcs MoveJoyY__joydown__21__nomove + bcs MoveJoyY__joydown__50__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joydown__21__nomove: +MoveJoyY__joydown__50__nomove: -;;; end action MoveJoyY__joydown__21 +;;; end action MoveJoyY__joydown__48 -Joystick__postframe__18__SkipMoveDown: +Joystick__postframe__41__SkipMoveDown: asl Joystick__3__tmp+0 - bcs Joystick__postframe__18__SkipMoveUp + bcs Joystick__postframe__41__SkipMoveUp -;;; start action MoveJoyY__joyup__22 +;;; start action MoveJoyY__joyup__51 lda HasYpos_ypos_b0,x sec sbc #1 - bcc MoveJoyY__joyup__22__nomove + bcc MoveJoyY__joyup__53__nomove sta HasYpos_ypos_b0,x -MoveJoyY__joyup__22__nomove: +MoveJoyY__joyup__53__nomove: -;;; end action MoveJoyY__joyup__22 +;;; end action MoveJoyY__joyup__51 -Joystick__postframe__18__SkipMoveUp: +Joystick__postframe__41__SkipMoveUp: inx cpx #4 - jne Joystick__postframe__18____each -Joystick__postframe__18____exit: + jne Joystick__postframe__40____each +Joystick__postframe__40____exit: -;;; end action Joystick__postframe__18 +;;; end action Joystick__postframe__39 -;;; start action SpriteShuffler__postframe__23 +;;; start action SpriteShuffler__postframe__54 ; load two sprite slots at left side of array lda SpriteSlot_sprite_b0 @@ -449,36 +449,36 @@ Joystick__postframe__18____exit: sta SpriteShuffler__8__tmp+1 ; move two slots to the left ldx #0 -SpriteShuffler__postframe__23__loop: +SpriteShuffler__postframe__56__loop: lda SpriteSlot_sprite_b0+2,x sta SpriteSlot_sprite_b0,x inx cpx #4-2 - bne SpriteShuffler__postframe__23__loop + bne SpriteShuffler__postframe__56__loop ; store two sprite slots at right side of array lda SpriteShuffler__8__tmp+0 sta SpriteSlot_sprite_b0+4-2 lda SpriteShuffler__8__tmp+1 sta SpriteSlot_sprite_b0+4-1 -;;; end action SpriteShuffler__postframe__23 +;;; end action SpriteShuffler__postframe__54 -;;; start action SpriteHider__postframe__24 +;;; start action SpriteHider__postframe__57 lda #4-1 sta SpriteHider__9__tmp+0 -;;; end action SpriteHider__postframe__24 +;;; end action SpriteHider__postframe__57 -;;; start action SpriteHider__postframe__25 +;;; start action SpriteHider__postframe__60 ldy #0 -SpriteHider__postframe__25____each: +SpriteHider__postframe__61____each: ldx SpriteSlot_sprite_b0,y lda HasYpos_ypos_b0,x cmp #192 - bcc SpriteHider__postframe__25__skip + bcc SpriteHider__postframe__62__skip ; swap this sprite slot with slot at end of array lda SpriteSlot_sprite_b0,y pha @@ -488,20 +488,20 @@ SpriteHider__postframe__25____each: pla sta SpriteSlot_sprite_b0,x dec SpriteHider__9__tmp+0 -SpriteHider__postframe__25__skip: +SpriteHider__postframe__62__skip: iny cpy #2 - jne SpriteHider__postframe__25____each -SpriteHider__postframe__25____exit: + jne SpriteHider__postframe__61____each +SpriteHider__postframe__61____exit: -;;; end action SpriteHider__postframe__25 +;;; end action SpriteHider__postframe__60 FRAME_END - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__4__NextFrame ; loop to next frame -;;; end action FrameLoop__start__2 +;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 diff --git a/test/ecs/superman.txt b/test/ecs/superman.txt index 0370412e..9357b6f9 100644 --- a/test/ecs/superman.txt +++ b/test/ecs/superman.txt @@ -658,12 +658,12 @@ __BRK: dey bne :- -;;; start action FrameLoop__start__2 +;;; start action FrameLoop__start__3 -FrameLoop__start__2__NextFrame: +FrameLoop__start__4__NextFrame: FRAME_START -;;; start action Kernel2Sprite__preframe__3 +;;; start action Kernel2Sprite__preframe__5 .define KLINES #192 .define KPAD 32 @@ -678,17 +678,17 @@ FrameLoop__start__2__NextFrame: sta Kernel2Sprite__2__tmp+6 sta Kernel2Sprite__2__tmp+7 -;;; end action Kernel2Sprite__preframe__3 +;;; end action Kernel2Sprite__preframe__5 -;;; start action Kernel2Sprite__preframe__4 +;;; start action Kernel2Sprite__preframe__8 ldy #0 -Kernel2Sprite__preframe__4____each: +Kernel2Sprite__preframe__9____each: ldx SpriteSlot_sprite_b0,y ; flags set according to sprite slot value ; skip sprite if negative - jmi Kernel2Sprite__preframe__4__nosprite + jmi Kernel2Sprite__preframe__10__nosprite ; set player object flags lda Sprite_plyrflags_b0,x sta NUSIZ0,y @@ -727,16 +727,16 @@ Kernel2Sprite__preframe__4____each: ldx Kernel2Sprite__2__tmp+12 ; restore X lda HasYpos_ypos_b0,x sta Kernel2Sprite__2__tmp+10,y -Kernel2Sprite__preframe__4__nosprite: +Kernel2Sprite__preframe__10__nosprite: iny cpy #2 - jne Kernel2Sprite__preframe__4____each -Kernel2Sprite__preframe__4____exit: + jne Kernel2Sprite__preframe__9____each +Kernel2Sprite__preframe__9____exit: -;;; end action Kernel2Sprite__preframe__4 +;;; end action Kernel2Sprite__preframe__8 -;;; start action Kernel2Sprite__preframe__5 +;;; start action Kernel2Sprite__preframe__11 ; shuffle pointers into (MSB, LSB) byte order ; L0 L1 H0 H1 -> L0 H0 L1 H1 @@ -749,34 +749,34 @@ Kernel2Sprite__preframe__4____exit: sty Kernel2Sprite__2__tmp+5 sta Kernel2Sprite__2__tmp+6 -;;; end action Kernel2Sprite__preframe__5 +;;; end action Kernel2Sprite__preframe__11 -;;; start action Kernel2Sprite__preframe__6 +;;; start action Kernel2Sprite__preframe__13 lda #162 sta COLUBK -;;; end action Kernel2Sprite__preframe__6 +;;; end action Kernel2Sprite__preframe__13 -;;; start action Kernel2Sprite__preframe__7 +;;; start action Kernel2Sprite__preframe__16 -;;; end action Kernel2Sprite__preframe__7 +;;; end action Kernel2Sprite__preframe__16 -;;; start action SetXPos__preframe__8 +;;; start action SetXPos__preframe__17 sta HMCLR -;;; end action SetXPos__preframe__8 +;;; end action SetXPos__preframe__17 -;;; start action SetXPos__preframe__9 +;;; start action SetXPos__preframe__19 ldy #0 -SetXPos__preframe__9____each: +SetXPos__preframe__20____each: ldx SpriteSlot_sprite_b0,y lda HasXpos_xpos_b0,x -;;; start action SetHorizPos__SetHorizPos__10 +;;; start action SetHorizPos__SetHorizPos__22 ; SetHorizPos routine ; A = X coordinate @@ -784,9 +784,9 @@ SetXPos__preframe__9____each: sta WSYNC ; start a new line sec ; set carry flag nop -SetHorizPos__SetHorizPos__10__DivideLoop: +SetHorizPos__SetHorizPos__23__DivideLoop: sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__10__DivideLoop ; branch until negative + bcs SetHorizPos__SetHorizPos__23__DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl @@ -795,28 +795,28 @@ SetHorizPos__SetHorizPos__10__DivideLoop: sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset -;;; end action SetHorizPos__SetHorizPos__10 +;;; end action SetHorizPos__SetHorizPos__22 iny cpy #2 - jne SetXPos__preframe__9____each -SetXPos__preframe__9____exit: + jne SetXPos__preframe__20____each +SetXPos__preframe__20____exit: -;;; end action SetXPos__preframe__9 +;;; end action SetXPos__preframe__19 -;;; start action SetXPos__preframe__11 +;;; start action SetXPos__preframe__24 -;;; end action SetXPos__preframe__11 +;;; end action SetXPos__preframe__24 -;;; start action SetXPos__preframe__12 +;;; start action SetXPos__preframe__25 sta WSYNC sta HMOVE -;;; end action SetXPos__preframe__12 +;;; end action SetXPos__preframe__25 -;;; start action VersatilePlayfield__preframe__13 +;;; start action VersatilePlayfield__preframe__27 ldx Location_room_b0+0 @@ -826,33 +826,33 @@ SetXPos__preframe__9____exit: sta VersatilePlayfield__10__tmp+1 -;;; end action VersatilePlayfield__preframe__13 +;;; end action VersatilePlayfield__preframe__27 KERNEL_START -;;; start action Kernel2Sprite__kernel__14 +;;; start action Kernel2Sprite__kernel__30 ldy #0 sty VDELP0 iny sta VDELP1 -;;; end action Kernel2Sprite__kernel__14 +;;; end action Kernel2Sprite__kernel__30 -;;; start action Kernel2Sprite__kernel__15 +;;; start action Kernel2Sprite__kernel__33 ldy #192 -Kernel2Sprite__kernel__15__LVScan: +Kernel2Sprite__kernel__35__LVScan: -;;; start action Kernel2Sprite__scanline__16 +;;; start action Kernel2Sprite__scanline__36 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__16__DoDraw1 + bcs Kernel2Sprite__scanline__37__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__16__DoDraw1: +Kernel2Sprite__scanline__37__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 0 = 0 sta WSYNC @@ -863,50 +863,50 @@ Kernel2Sprite__scanline__16__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__16__DoDraw2 + bcs Kernel2Sprite__scanline__37__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__16__DoDraw2: +Kernel2Sprite__scanline__37__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite__scanline__16 +;;; end action Kernel2Sprite__scanline__36 -;;; start action Kernel2Sprite__scanline__17 +;;; start action Kernel2Sprite__scanline__38 -;;; end action Kernel2Sprite__scanline__17 +;;; end action Kernel2Sprite__scanline__38 -;;; start action VersatilePlayfield__scanline__18 +;;; start action VersatilePlayfield__scanline__39 .if 0 = 0 lda (VersatilePlayfield__10__tmp+0),y tax .endif -;;; end action VersatilePlayfield__scanline__18 +;;; end action VersatilePlayfield__scanline__39 -;;; start action VersatilePlayfield__scanline__19 +;;; start action VersatilePlayfield__scanline__41 .if 0 = 1 lda (VersatilePlayfield__10__tmp+0),y sta $00,x .endif -;;; end action VersatilePlayfield__scanline__19 +;;; end action VersatilePlayfield__scanline__41 dey ; next scanline -;;; start action Kernel2Sprite__scanline__20 +;;; start action Kernel2Sprite__scanline__43 ; draw player 0 lda Kernel2Sprite__2__tmp+8 ; height dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__20__DoDraw1 + bcs Kernel2Sprite__scanline__44__DoDraw1 lda #0 .byte $2C -Kernel2Sprite__scanline__20__DoDraw1: +Kernel2Sprite__scanline__44__DoDraw1: lda (Kernel2Sprite__2__tmp+0),y .if 1 = 0 sta WSYNC @@ -917,45 +917,45 @@ Kernel2Sprite__scanline__20__DoDraw1: ; draw player 1 lda Kernel2Sprite__2__tmp+9 ; height dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__20__DoDraw2 + bcs Kernel2Sprite__scanline__44__DoDraw2 lda #0 .byte $2C -Kernel2Sprite__scanline__20__DoDraw2: +Kernel2Sprite__scanline__44__DoDraw2: lda (Kernel2Sprite__2__tmp+2),y sta GRP1 lda (Kernel2Sprite__2__tmp+6),y sta COLUP1 -;;; end action Kernel2Sprite__scanline__20 +;;; end action Kernel2Sprite__scanline__43 -;;; start action Kernel2Sprite__scanline__21 +;;; start action Kernel2Sprite__scanline__45 -;;; end action Kernel2Sprite__scanline__21 +;;; end action Kernel2Sprite__scanline__45 -;;; start action VersatilePlayfield__scanline__22 +;;; start action VersatilePlayfield__scanline__46 .if 1 = 0 lda (VersatilePlayfield__10__tmp+0),y tax .endif -;;; end action VersatilePlayfield__scanline__22 +;;; end action VersatilePlayfield__scanline__46 -;;; start action VersatilePlayfield__scanline__23 +;;; start action VersatilePlayfield__scanline__48 .if 1 = 1 lda (VersatilePlayfield__10__tmp+0),y sta $00,x .endif -;;; end action VersatilePlayfield__scanline__23 +;;; end action VersatilePlayfield__scanline__48 dey ; next scanline - bne Kernel2Sprite__kernel__15__LVScan ; repeat until out of lines + bne Kernel2Sprite__kernel__35__LVScan ; repeat until out of lines -;;; end action Kernel2Sprite__kernel__15 +;;; end action Kernel2Sprite__kernel__33 -;;; start action Kernel2Sprite__kernel__24 +;;; start action Kernel2Sprite__kernel__50 lda #0 sta GRP0 @@ -963,223 +963,223 @@ Kernel2Sprite__scanline__20__DoDraw2: sta GRP0 sta GRP1 -;;; end action Kernel2Sprite__kernel__24 +;;; end action Kernel2Sprite__kernel__50 KERNEL_END -;;; start action Joystick__postframe__25 +;;; start action Joystick__postframe__52 ; 2 control inputs share a single byte, 4 bits each lda SWCHA sta Joystick__3__tmp+0 -;;; end action Joystick__postframe__25 +;;; end action Joystick__postframe__52 -;;; start action Joystick__postframe__26 +;;; start action Joystick__postframe__54 asl Joystick__3__tmp+0 - bcs Joystick__postframe__26__SkipMoveRight + bcs Joystick__postframe__56__SkipMoveRight -;;; start action JoyFaceDirection__joyright__27 +;;; start action JoyFaceDirection__joyright__57 lda Sprite_plyrflags_b0 and #$f7 sta Sprite_plyrflags_b0 -;;; end action JoyFaceDirection__joyright__27 +;;; end action JoyFaceDirection__joyright__57 -;;; start action SuperFly__joyright__28 +;;; start action SuperFly__joyright__60 lda HasXpos_xpos_b0 clc adc #2 cmp #142 - jcc SuperFly__joyright__28__nomove + jcc SuperFly__joyright__62__nomove -;;; start action SuperFly__goeast__29 +;;; start action SuperFly__goeast__63 ldy Location_room_b0 lda Room_east_b0,y sta Location_room_b0 -;;; end action SuperFly__goeast__29 +;;; end action SuperFly__goeast__63 lda #2 -SuperFly__joyright__28__nomove: +SuperFly__joyright__62__nomove: sta HasXpos_xpos_b0 -;;; end action SuperFly__joyright__28 +;;; end action SuperFly__joyright__60 -Joystick__postframe__26__SkipMoveRight: +Joystick__postframe__56__SkipMoveRight: asl Joystick__3__tmp+0 - bcs Joystick__postframe__26__SkipMoveLeft + bcs Joystick__postframe__56__SkipMoveLeft -;;; start action JoyFaceDirection__joyleft__30 +;;; start action JoyFaceDirection__joyleft__66 lda Sprite_plyrflags_b0 ora #$08 sta Sprite_plyrflags_b0 -;;; end action JoyFaceDirection__joyleft__30 +;;; end action JoyFaceDirection__joyleft__66 -;;; start action SuperFly__joyleft__31 +;;; start action SuperFly__joyleft__69 lda HasXpos_xpos_b0 sec sbc #2 - jcs SuperFly__joyleft__31__nomove + jcs SuperFly__joyleft__71__nomove -;;; start action SuperFly__gowest__32 +;;; start action SuperFly__gowest__72 ldy Location_room_b0 lda Room_west_b0,y sta Location_room_b0 -;;; end action SuperFly__gowest__32 +;;; end action SuperFly__gowest__72 lda #142 -SuperFly__joyleft__31__nomove: +SuperFly__joyleft__71__nomove: sta HasXpos_xpos_b0 -;;; end action SuperFly__joyleft__31 +;;; end action SuperFly__joyleft__69 -Joystick__postframe__26__SkipMoveLeft: +Joystick__postframe__56__SkipMoveLeft: asl Joystick__3__tmp+0 - bcs Joystick__postframe__26__SkipMoveDown + bcs Joystick__postframe__56__SkipMoveDown -;;; start action SuperFly__joydown__33 +;;; start action SuperFly__joydown__75 lda HasYpos_ypos_b0 clc adc #2 cmp #220 - jcc SuperFly__joydown__33__nomove + jcc SuperFly__joydown__77__nomove -;;; start action SuperFly__gosouth__34 +;;; start action SuperFly__gosouth__78 ldy Location_room_b0 lda Room_south_b0,y sta Location_room_b0 -;;; end action SuperFly__gosouth__34 +;;; end action SuperFly__gosouth__78 lda #2 -SuperFly__joydown__33__nomove: +SuperFly__joydown__77__nomove: sta HasYpos_ypos_b0 -;;; end action SuperFly__joydown__33 +;;; end action SuperFly__joydown__75 -Joystick__postframe__26__SkipMoveDown: +Joystick__postframe__56__SkipMoveDown: asl Joystick__3__tmp+0 - bcs Joystick__postframe__26__SkipMoveUp + bcs Joystick__postframe__56__SkipMoveUp -;;; start action SuperFly__joyup__35 +;;; start action SuperFly__joyup__81 lda HasYpos_ypos_b0 sec sbc #2 - jcs SuperFly__joyup__35__nomove + jcs SuperFly__joyup__83__nomove -;;; start action SuperFly__gonorth__36 +;;; start action SuperFly__gonorth__84 ldy Location_room_b0 lda Room_north_b0,y sta Location_room_b0 -;;; end action SuperFly__gonorth__36 +;;; end action SuperFly__gonorth__84 lda #200 -SuperFly__joyup__35__nomove: +SuperFly__joyup__83__nomove: sta HasYpos_ypos_b0 -;;; end action SuperFly__joyup__35 +;;; end action SuperFly__joyup__81 -Joystick__postframe__26__SkipMoveUp: +Joystick__postframe__56__SkipMoveUp: -;;; end action Joystick__postframe__26 +;;; end action Joystick__postframe__54 -;;; start action BadMove__postframe__37 +;;; start action BadMove__postframe__87 ldx #0 -BadMove__postframe__37____each: +BadMove__postframe__88____each: -;;; start action JoyFaceDirection__joyright__38 +;;; start action JoyFaceDirection__joyright__90 lda Sprite_plyrflags_b0+1,x and #$f7 sta Sprite_plyrflags_b0+1,x -;;; end action JoyFaceDirection__joyright__38 +;;; end action JoyFaceDirection__joyright__90 -;;; start action SuperFly__joyright__39 +;;; start action SuperFly__joyright__93 lda HasXpos_xpos_b0+1,x clc adc #1 cmp #142 - jcc SuperFly__joyright__39__nomove + jcc SuperFly__joyright__95__nomove -;;; start action SuperFly__goeast__40 +;;; start action SuperFly__goeast__96 ldy Location_room_b0+1,x lda Room_east_b0,y sta Location_room_b0+1,x -;;; end action SuperFly__goeast__40 +;;; end action SuperFly__goeast__96 lda #2 -SuperFly__joyright__39__nomove: +SuperFly__joyright__95__nomove: sta HasXpos_xpos_b0+1,x -;;; end action SuperFly__joyright__39 +;;; end action SuperFly__joyright__93 inx cpx #3 - jne BadMove__postframe__37____each -BadMove__postframe__37____exit: + jne BadMove__postframe__88____each +BadMove__postframe__88____exit: -;;; end action BadMove__postframe__37 +;;; end action BadMove__postframe__87 -;;; start action RoomShuffle__postframe__41 +;;; start action RoomShuffle__postframe__99 ldy 4 ldx SpriteSlot_sprite_b0+1 - bmi RoomShuffle__postframe__41__empty ; empty slot, load 1st entry -RoomShuffle__postframe__41__loop: + bmi RoomShuffle__postframe__101__empty ; empty slot, load 1st entry +RoomShuffle__postframe__101__loop: inx cpx 4 - bcc RoomShuffle__postframe__41__norecycle + bcc RoomShuffle__postframe__101__norecycle ; TODO: need to get index of specific entity -RoomShuffle__postframe__41__empty: +RoomShuffle__postframe__101__empty: ldx #1 ; skip null sprite and super dude? -RoomShuffle__postframe__41__norecycle: +RoomShuffle__postframe__101__norecycle: lda Location_room_b0,x cmp Location_room_b0 - beq RoomShuffle__postframe__41__exit + beq RoomShuffle__postframe__101__exit dey - bne RoomShuffle__postframe__41__loop + bne RoomShuffle__postframe__101__loop ldx #$ff -RoomShuffle__postframe__41__exit: +RoomShuffle__postframe__101__exit: stx SpriteSlot_sprite_b0+1 -;;; end action RoomShuffle__postframe__41 +;;; end action RoomShuffle__postframe__99 -;;; start action VersatilePlayfield__postframe__42 +;;; start action VersatilePlayfield__postframe__102 lda #0 sta PF0 sta PF1 sta PF2 -;;; end action VersatilePlayfield__postframe__42 +;;; end action VersatilePlayfield__postframe__102 FRAME_END - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__4__NextFrame ; loop to next frame -;;; end action FrameLoop__start__2 +;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index d4dc580d..53e68cec 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -20,11 +20,11 @@ __Reset: __BRK: CLEAN_START -;;; start action Demo__start__2 +;;; start action Demo__start__3 jmp Title__Start -;;; end action Demo__start__2 +;;; end action Demo__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 @@ -311,56 +311,56 @@ Bitmap48_height_b0: .byte 36 __Start: -;;; start action FrameLoop__start__3 +;;; start action FrameLoop__start__5 -FrameLoop__start__3__NextFrame: +FrameLoop__start__6__NextFrame: FRAME_START -;;; start action StaticKernel__preframe__4 +;;; start action StaticKernel__preframe__7 -;;; start action Kernel48Pixel__kernelsetup__5 +;;; start action Kernel48Pixel__kernelsetup__10 -;;; end action Kernel48Pixel__kernelsetup__5 +;;; end action Kernel48Pixel__kernelsetup__10 -;;; start action Kernel48Pixel__kernelsetup__6 +;;; start action Kernel48Pixel__kernelsetup__11 -;;; end action Kernel48Pixel__kernelsetup__6 +;;; end action Kernel48Pixel__kernelsetup__11 -;;; start action StaticKernel__kernelsetup__7 +;;; start action StaticKernel__kernelsetup__12 lda #160 sta COLUBK -;;; end action StaticKernel__kernelsetup__7 +;;; end action StaticKernel__kernelsetup__12 -;;; start action StaticKernel__kernelsetup__8 +;;; start action StaticKernel__kernelsetup__15 -;;; end action StaticKernel__kernelsetup__8 +;;; end action StaticKernel__kernelsetup__15 -;;; start action StaticKernel__kernelsetup__9 +;;; start action StaticKernel__kernelsetup__16 -;;; end action StaticKernel__kernelsetup__9 +;;; end action StaticKernel__kernelsetup__16 -;;; end action StaticKernel__preframe__4 +;;; end action StaticKernel__preframe__7 KERNEL_START -;;; start action StaticKernel__kernel__10 +;;; start action StaticKernel__kernel__17 ldx #0 -StaticKernel__kernel__10____each: +StaticKernel__kernel__18____each: sta WSYNC -;;; start action Kernel48Pixel__kernelsetup__11 +;;; start action Kernel48Pixel__kernelsetup__20 cpx #2+1 - jcs Kernel48Pixel__kernelsetup__11____skipxhi + jcs Kernel48Pixel__kernelsetup__21____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__11____skipxlo + jcc Kernel48Pixel__kernelsetup__21____skipxlo lda #36 sta Kernel48Pixel__2__tmp+0 ; scanline counter @@ -385,117 +385,117 @@ StaticKernel__kernel__10____each: SLEEPH 24-8 ; sleep 24 cycles sta HMCLR ; clear HMOVE registers -Kernel48Pixel__kernelsetup__11____skipxlo: +Kernel48Pixel__kernelsetup__21____skipxlo: -Kernel48Pixel__kernelsetup__11____skipxhi: +Kernel48Pixel__kernelsetup__21____skipxhi: -;;; end action Kernel48Pixel__kernelsetup__11 +;;; end action Kernel48Pixel__kernelsetup__20 -;;; start action Kernel48Pixel__kernelsetup__12 +;;; start action Kernel48Pixel__kernelsetup__23 cpx #2+1 - jcs Kernel48Pixel__kernelsetup__12____skipxhi + jcs Kernel48Pixel__kernelsetup__24____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__12____skipxlo + jcc Kernel48Pixel__kernelsetup__24____skipxlo lda #252 sta COLUP0 sta COLUP1 -Kernel48Pixel__kernelsetup__12____skipxlo: +Kernel48Pixel__kernelsetup__24____skipxlo: -Kernel48Pixel__kernelsetup__12____skipxhi: +Kernel48Pixel__kernelsetup__24____skipxhi: -;;; end action Kernel48Pixel__kernelsetup__12 +;;; end action Kernel48Pixel__kernelsetup__23 -;;; start action StaticKernel__kernelsetup__13 +;;; start action StaticKernel__kernelsetup__26 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel__kernelsetup__13 +;;; end action StaticKernel__kernelsetup__26 -;;; start action StaticKernel__kernelsetup__14 +;;; start action StaticKernel__kernelsetup__29 cpx #2+1 - jcs StaticKernel__kernelsetup__14____skipxhi + jcs StaticKernel__kernelsetup__30____skipxhi cpx #2 - jcc StaticKernel__kernelsetup__14____skipxlo + jcc StaticKernel__kernelsetup__30____skipxlo lda #252 sta COLUPF -StaticKernel__kernelsetup__14____skipxlo: +StaticKernel__kernelsetup__30____skipxlo: -StaticKernel__kernelsetup__14____skipxhi: +StaticKernel__kernelsetup__30____skipxhi: -;;; end action StaticKernel__kernelsetup__14 +;;; end action StaticKernel__kernelsetup__29 -;;; start action StaticKernel__kernelsetup__15 +;;; start action StaticKernel__kernelsetup__32 -;;; end action StaticKernel__kernelsetup__15 +;;; end action StaticKernel__kernelsetup__32 - jsr Kernel48Pixel__kerneldraw__16 + jsr Kernel48Pixel__kerneldraw__33 -;;; start action StaticKernel__kerneldraw__17 +;;; start action StaticKernel__kerneldraw__36 ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__17__loop: +StaticKernel__kerneldraw__38__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__17__loop + bne StaticKernel__kerneldraw__38__loop -;;; end action StaticKernel__kerneldraw__17 +;;; end action StaticKernel__kerneldraw__36 inx cpx #5 - jne StaticKernel__kernel__10____each -StaticKernel__kernel__10____exit: + jne StaticKernel__kernel__18____each +StaticKernel__kernel__18____exit: -;;; end action StaticKernel__kernel__10 +;;; end action StaticKernel__kernel__17 KERNEL_END -;;; start action JoyButton__postframe__18 +;;; start action JoyButton__postframe__39 lda INPT4 ;read button input - bmi JoyButton__postframe__18__NotPressed + bmi JoyButton__postframe__41__NotPressed -;;; start action Advance__joybutton__19 +;;; start action Advance__joybutton__42 jmp Title2__Start -;;; end action Advance__joybutton__19 +;;; end action Advance__joybutton__42 -JoyButton__postframe__18__NotPressed: +JoyButton__postframe__41__NotPressed: -;;; end action JoyButton__postframe__18 +;;; end action JoyButton__postframe__39 FRAME_END - jmp FrameLoop__start__3__NextFrame ; loop to next frame + jmp FrameLoop__start__6__NextFrame ; loop to next frame -;;; end action FrameLoop__start__3 +;;; end action FrameLoop__start__5 .rodata __ALIGNORIGIN: .rodata -Kernel48Pixel__kerneldraw__16: +Kernel48Pixel__kerneldraw__33: cpx #2+1 - jcs Kernel48Pixel__kerneldraw__16____skipxhi + jcs Kernel48Pixel__kerneldraw__34____skipxhi cpx #2 - jcc Kernel48Pixel__kerneldraw__16____skipxlo + jcc Kernel48Pixel__kerneldraw__34____skipxlo txa pha -Kernel48Pixel__kerneldraw__16__Loop: +Kernel48Pixel__kerneldraw__35__Loop: ldy Kernel48Pixel__2__tmp+0 ; counts backwards sta WSYNC ; sync to next scanline lda Bitmap48_bitmap0_e3_b0,y ; load B0 (1st sprite byte) @@ -514,19 +514,19 @@ Kernel48Pixel__kerneldraw__16__Loop: sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec Kernel48Pixel__2__tmp+0 ; go to next line - bpl Kernel48Pixel__kerneldraw__16__Loop ; repeat until < 0 + bpl Kernel48Pixel__kerneldraw__35__Loop ; repeat until < 0 pla tax -Kernel48Pixel__kerneldraw__16____skipxlo: +Kernel48Pixel__kerneldraw__34____skipxlo: -Kernel48Pixel__kerneldraw__16____skipxhi: +Kernel48Pixel__kerneldraw__34____skipxhi: rts -.assert >(Kernel48Pixel__kerneldraw__16) = >(*), error, "Kernel48Pixel__kerneldraw__16 crosses a page boundary!" +.assert >(Kernel48Pixel__kerneldraw__33) = >(*), error, "Kernel48Pixel__kerneldraw__33 crosses a page boundary!" -.assert (* - Kernel48Pixel__kerneldraw__16) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__16 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__16)) +.assert (* - Kernel48Pixel__kerneldraw__33) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__33 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__33)) .endscope Title__Start = Title::__Start .scope Title2 @@ -861,56 +861,56 @@ __Start: dey bne :- -;;; start action FrameLoop__start__20 +;;; start action FrameLoop__start__44 -FrameLoop__start__20__NextFrame: +FrameLoop__start__45__NextFrame: FRAME_START -;;; start action StaticKernel__preframe__21 +;;; start action StaticKernel__preframe__46 -;;; start action Kernel48Pixel__kernelsetup__22 +;;; start action Kernel48Pixel__kernelsetup__49 -;;; end action Kernel48Pixel__kernelsetup__22 +;;; end action Kernel48Pixel__kernelsetup__49 -;;; start action Kernel48Pixel__kernelsetup__23 +;;; start action Kernel48Pixel__kernelsetup__50 -;;; end action Kernel48Pixel__kernelsetup__23 +;;; end action Kernel48Pixel__kernelsetup__50 -;;; start action StaticKernel__kernelsetup__24 +;;; start action StaticKernel__kernelsetup__51 lda BGColor_bgcolor_b0 sta COLUBK -;;; end action StaticKernel__kernelsetup__24 +;;; end action StaticKernel__kernelsetup__51 -;;; start action StaticKernel__kernelsetup__25 +;;; start action StaticKernel__kernelsetup__54 -;;; end action StaticKernel__kernelsetup__25 +;;; end action StaticKernel__kernelsetup__54 -;;; start action StaticKernel__kernelsetup__26 +;;; start action StaticKernel__kernelsetup__55 -;;; end action StaticKernel__kernelsetup__26 +;;; end action StaticKernel__kernelsetup__55 -;;; end action StaticKernel__preframe__21 +;;; end action StaticKernel__preframe__46 KERNEL_START -;;; start action StaticKernel__kernel__27 +;;; start action StaticKernel__kernel__56 ldx #0 -StaticKernel__kernel__27____each: +StaticKernel__kernel__57____each: sta WSYNC -;;; start action Kernel48Pixel__kernelsetup__28 +;;; start action Kernel48Pixel__kernelsetup__59 cpx #2+1 - jcs Kernel48Pixel__kernelsetup__28____skipxhi + jcs Kernel48Pixel__kernelsetup__60____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__28____skipxlo + jcc Kernel48Pixel__kernelsetup__60____skipxlo lda #43 sta Kernel48Pixel__2__tmp+0 ; scanline counter @@ -935,84 +935,84 @@ StaticKernel__kernel__27____each: SLEEPH 24-8 ; sleep 24 cycles sta HMCLR ; clear HMOVE registers -Kernel48Pixel__kernelsetup__28____skipxlo: +Kernel48Pixel__kernelsetup__60____skipxlo: -Kernel48Pixel__kernelsetup__28____skipxhi: +Kernel48Pixel__kernelsetup__60____skipxhi: -;;; end action Kernel48Pixel__kernelsetup__28 +;;; end action Kernel48Pixel__kernelsetup__59 -;;; start action Kernel48Pixel__kernelsetup__29 +;;; start action Kernel48Pixel__kernelsetup__62 cpx #2+1 - jcs Kernel48Pixel__kernelsetup__29____skipxhi + jcs Kernel48Pixel__kernelsetup__63____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__29____skipxlo + jcc Kernel48Pixel__kernelsetup__63____skipxlo lda PFColor_pfcolor_b0 sta COLUP0 sta COLUP1 -Kernel48Pixel__kernelsetup__29____skipxlo: +Kernel48Pixel__kernelsetup__63____skipxlo: -Kernel48Pixel__kernelsetup__29____skipxhi: +Kernel48Pixel__kernelsetup__63____skipxhi: -;;; end action Kernel48Pixel__kernelsetup__29 +;;; end action Kernel48Pixel__kernelsetup__62 -;;; start action StaticKernel__kernelsetup__30 +;;; start action StaticKernel__kernelsetup__65 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel__kernelsetup__30 +;;; end action StaticKernel__kernelsetup__65 -;;; start action StaticKernel__kernelsetup__31 +;;; start action StaticKernel__kernelsetup__68 cpx #2+1 - jcs StaticKernel__kernelsetup__31____skipxhi + jcs StaticKernel__kernelsetup__69____skipxhi cpx #2 - jcc StaticKernel__kernelsetup__31____skipxlo + jcc StaticKernel__kernelsetup__69____skipxlo lda PFColor_pfcolor_b0 sta COLUPF -StaticKernel__kernelsetup__31____skipxlo: +StaticKernel__kernelsetup__69____skipxlo: -StaticKernel__kernelsetup__31____skipxhi: +StaticKernel__kernelsetup__69____skipxhi: -;;; end action StaticKernel__kernelsetup__31 +;;; end action StaticKernel__kernelsetup__68 -;;; start action StaticKernel__kernelsetup__32 +;;; start action StaticKernel__kernelsetup__71 -;;; end action StaticKernel__kernelsetup__32 +;;; end action StaticKernel__kernelsetup__71 - jsr Kernel48Pixel__kerneldraw__33 + jsr Kernel48Pixel__kerneldraw__72 -;;; start action StaticKernel__kerneldraw__34 +;;; start action StaticKernel__kerneldraw__75 ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__34__loop: +StaticKernel__kerneldraw__77__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__34__loop + bne StaticKernel__kerneldraw__77__loop -;;; end action StaticKernel__kerneldraw__34 +;;; end action StaticKernel__kerneldraw__75 inx cpx #5 - jne StaticKernel__kernel__27____each -StaticKernel__kernel__27____exit: + jne StaticKernel__kernel__57____each +StaticKernel__kernel__57____exit: -;;; end action StaticKernel__kernel__27 +;;; end action StaticKernel__kernel__56 KERNEL_END -;;; start action Colors__postframe__35 +;;; start action Colors__postframe__78 inc PFColor_pfcolor_b0 bne :+ @@ -1020,27 +1020,27 @@ StaticKernel__kernel__27____exit: inc BGColor_bgcolor_b0+2 : -;;; end action Colors__postframe__35 +;;; end action Colors__postframe__78 FRAME_END - jmp FrameLoop__start__20__NextFrame ; loop to next frame + jmp FrameLoop__start__45__NextFrame ; loop to next frame -;;; end action FrameLoop__start__20 +;;; end action FrameLoop__start__44 .rodata __ALIGNORIGIN: .rodata -Kernel48Pixel__kerneldraw__33: +Kernel48Pixel__kerneldraw__72: cpx #2+1 - jcs Kernel48Pixel__kerneldraw__33____skipxhi + jcs Kernel48Pixel__kerneldraw__73____skipxhi cpx #2 - jcc Kernel48Pixel__kerneldraw__33____skipxlo + jcc Kernel48Pixel__kerneldraw__73____skipxlo txa pha -Kernel48Pixel__kerneldraw__33__Loop: +Kernel48Pixel__kerneldraw__74__Loop: ldy Kernel48Pixel__2__tmp+0 ; counts backwards sta WSYNC ; sync to next scanline lda Bitmap48_bitmap0_e2_b0,y ; load B0 (1st sprite byte) @@ -1059,19 +1059,19 @@ Kernel48Pixel__kerneldraw__33__Loop: sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec Kernel48Pixel__2__tmp+0 ; go to next line - bpl Kernel48Pixel__kerneldraw__33__Loop ; repeat until < 0 + bpl Kernel48Pixel__kerneldraw__74__Loop ; repeat until < 0 pla tax -Kernel48Pixel__kerneldraw__33____skipxlo: +Kernel48Pixel__kerneldraw__73____skipxlo: -Kernel48Pixel__kerneldraw__33____skipxhi: +Kernel48Pixel__kerneldraw__73____skipxhi: rts -.assert >(Kernel48Pixel__kerneldraw__33) = >(*), error, "Kernel48Pixel__kerneldraw__33 crosses a page boundary!" +.assert >(Kernel48Pixel__kerneldraw__72) = >(*), error, "Kernel48Pixel__kerneldraw__72 crosses a page boundary!" -.assert (* - Kernel48Pixel__kerneldraw__33) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__33 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__33)) +.assert (* - Kernel48Pixel__kerneldraw__72) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__72 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__72)) .endscope Title2__Start = Title2::__Start .endscope diff --git a/test/ecs/vcs1.txt b/test/ecs/vcs1.txt index 8efbded3..a5ff83a8 100644 --- a/test/ecs/vcs1.txt +++ b/test/ecs/vcs1.txt @@ -68,69 +68,69 @@ __BRK: dey bne :- -;;; start action FrameLoop__start__2 +;;; start action FrameLoop__start__3 -FrameLoop__start__2__NextFrame: +FrameLoop__start__4__NextFrame: FRAME_START -;;; start action StaticKernel__preframe__3 +;;; start action StaticKernel__preframe__5 -;;; start action StaticKernel__kernelsetup__4 +;;; start action StaticKernel__kernelsetup__8 lda #24 sta COLUBK -;;; end action StaticKernel__kernelsetup__4 +;;; end action StaticKernel__kernelsetup__8 -;;; start action StaticKernel__kernelsetup__5 +;;; start action StaticKernel__kernelsetup__11 -;;; end action StaticKernel__kernelsetup__5 +;;; end action StaticKernel__kernelsetup__11 -;;; start action StaticKernel__kernelsetup__6 +;;; start action StaticKernel__kernelsetup__12 -;;; end action StaticKernel__kernelsetup__6 +;;; end action StaticKernel__kernelsetup__12 -;;; end action StaticKernel__preframe__3 +;;; end action StaticKernel__preframe__5 KERNEL_START -;;; start action StaticKernel__kernel__7 +;;; start action StaticKernel__kernel__13 ldx #0 -StaticKernel__kernel__7____each: +StaticKernel__kernel__14____each: sta WSYNC -;;; start action StaticKernel__kernelsetup__8 +;;; start action StaticKernel__kernelsetup__16 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel__kernelsetup__8 +;;; end action StaticKernel__kernelsetup__16 -;;; start action StaticKernel__kernelsetup__9 +;;; start action StaticKernel__kernelsetup__19 cpx #5+2 - jcs StaticKernel__kernelsetup__9____skipxhi + jcs StaticKernel__kernelsetup__20____skipxhi cpx #5 - jcc StaticKernel__kernelsetup__9____skipxlo + jcc StaticKernel__kernelsetup__20____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF -StaticKernel__kernelsetup__9____skipxlo: +StaticKernel__kernelsetup__20____skipxlo: -StaticKernel__kernelsetup__9____skipxhi: +StaticKernel__kernelsetup__20____skipxhi: -;;; end action StaticKernel__kernelsetup__9 +;;; end action StaticKernel__kernelsetup__19 -;;; start action StaticKernel__kernelsetup__10 +;;; start action StaticKernel__kernelsetup__22 cpx #4 - jcc StaticKernel__kernelsetup__10____skipxlo + jcc StaticKernel__kernelsetup__23____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -139,63 +139,63 @@ StaticKernel__kernelsetup__9____skipxhi: lda Playfield_pf_b16-4,x sta PF2 -StaticKernel__kernelsetup__10____skipxlo: +StaticKernel__kernelsetup__23____skipxlo: -;;; end action StaticKernel__kernelsetup__10 +;;; end action StaticKernel__kernelsetup__22 ldy KernelSection_lines_b0,x -StaticKernel__kernel__7__loop: +StaticKernel__kernel__15__loop: sta WSYNC dey - bne StaticKernel__kernel__7__loop + bne StaticKernel__kernel__15__loop inx cpx #8 - jne StaticKernel__kernel__7____each -StaticKernel__kernel__7____exit: + jne StaticKernel__kernel__14____each +StaticKernel__kernel__14____exit: -;;; end action StaticKernel__kernel__7 +;;; end action StaticKernel__kernel__13 KERNEL_END -;;; start action JoyButton__postframe__11 +;;; start action JoyButton__postframe__25 lda INPT4 ;read button input - bmi JoyButton__postframe__11__NotPressed + bmi JoyButton__postframe__27__NotPressed -;;; start action Local__joybutton__12 +;;; start action Local__joybutton__28 inc Local__6__tmp+0 inc PFColor_pfcolor_b0 -;;; end action Local__joybutton__12 +;;; end action Local__joybutton__28 -JoyButton__postframe__11__NotPressed: +JoyButton__postframe__27__NotPressed: -;;; end action JoyButton__postframe__11 +;;; end action JoyButton__postframe__25 FRAME_END -;;; start action ResetSwitch__nextframe__13 +;;; start action ResetSwitch__nextframe__31 lsr SWCHB ; test Game Reset switch - bcs ResetSwitch__nextframe__13__NoStart + bcs ResetSwitch__nextframe__32__NoStart -;;; start action ResetConsole__resetswitch__14 +;;; start action ResetConsole__resetswitch__33 jmp Main::__Reset ; jump to Reset handler -;;; end action ResetConsole__resetswitch__14 +;;; end action ResetConsole__resetswitch__33 -ResetSwitch__nextframe__13__NoStart: +ResetSwitch__nextframe__32__NoStart: -;;; end action ResetSwitch__nextframe__13 +;;; end action ResetSwitch__nextframe__31 - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__4__NextFrame ; loop to next frame -;;; end action FrameLoop__start__2 +;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 diff --git a/test/ecs/vcslib.txt b/test/ecs/vcslib.txt index 321c1214..c53a33fd 100644 --- a/test/ecs/vcslib.txt +++ b/test/ecs/vcslib.txt @@ -70,69 +70,69 @@ __BRK: dey bne :- -;;; start action FrameLoop__start__2 +;;; start action FrameLoop__start__3 -FrameLoop__start__2__NextFrame: +FrameLoop__start__4__NextFrame: FRAME_START -;;; start action StaticKernel__preframe__3 +;;; start action StaticKernel__preframe__5 -;;; start action StaticKernel__kernelsetup__4 +;;; start action StaticKernel__kernelsetup__8 lda #24 sta COLUBK -;;; end action StaticKernel__kernelsetup__4 +;;; end action StaticKernel__kernelsetup__8 -;;; start action StaticKernel__kernelsetup__5 +;;; start action StaticKernel__kernelsetup__11 -;;; end action StaticKernel__kernelsetup__5 +;;; end action StaticKernel__kernelsetup__11 -;;; start action StaticKernel__kernelsetup__6 +;;; start action StaticKernel__kernelsetup__12 -;;; end action StaticKernel__kernelsetup__6 +;;; end action StaticKernel__kernelsetup__12 -;;; end action StaticKernel__preframe__3 +;;; end action StaticKernel__preframe__5 KERNEL_START -;;; start action StaticKernel__kernel__7 +;;; start action StaticKernel__kernel__13 ldx #0 -StaticKernel__kernel__7____each: +StaticKernel__kernel__14____each: sta WSYNC -;;; start action StaticKernel__kernelsetup__8 +;;; start action StaticKernel__kernelsetup__16 lda BGColor_bgcolor_b0,x sta COLUBK -;;; end action StaticKernel__kernelsetup__8 +;;; end action StaticKernel__kernelsetup__16 -;;; start action StaticKernel__kernelsetup__9 +;;; start action StaticKernel__kernelsetup__19 cpx #5+2 - jcs StaticKernel__kernelsetup__9____skipxhi + jcs StaticKernel__kernelsetup__20____skipxhi cpx #5 - jcc StaticKernel__kernelsetup__9____skipxlo + jcc StaticKernel__kernelsetup__20____skipxlo lda PFColor_pfcolor_b0-5,x sta COLUPF -StaticKernel__kernelsetup__9____skipxlo: +StaticKernel__kernelsetup__20____skipxlo: -StaticKernel__kernelsetup__9____skipxhi: +StaticKernel__kernelsetup__20____skipxhi: -;;; end action StaticKernel__kernelsetup__9 +;;; end action StaticKernel__kernelsetup__19 -;;; start action StaticKernel__kernelsetup__10 +;;; start action StaticKernel__kernelsetup__22 cpx #4 - jcc StaticKernel__kernelsetup__10____skipxlo + jcc StaticKernel__kernelsetup__23____skipxlo lda Playfield_pf_b0-4,x sta PF0 @@ -141,69 +141,69 @@ StaticKernel__kernelsetup__9____skipxhi: lda Playfield_pf_b16-4,x sta PF2 -StaticKernel__kernelsetup__10____skipxlo: +StaticKernel__kernelsetup__23____skipxlo: -;;; end action StaticKernel__kernelsetup__10 +;;; end action StaticKernel__kernelsetup__22 -;;; start action StaticKernel__kerneldraw__11 +;;; start action StaticKernel__kerneldraw__25 ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__11__loop: +StaticKernel__kerneldraw__27__loop: sta WSYNC dey - bne StaticKernel__kerneldraw__11__loop + bne StaticKernel__kerneldraw__27__loop -;;; end action StaticKernel__kerneldraw__11 +;;; end action StaticKernel__kerneldraw__25 inx cpx #8 - jne StaticKernel__kernel__7____each -StaticKernel__kernel__7____exit: + jne StaticKernel__kernel__14____each +StaticKernel__kernel__14____exit: -;;; end action StaticKernel__kernel__7 +;;; end action StaticKernel__kernel__13 KERNEL_END -;;; start action JoyButton__postframe__12 +;;; start action JoyButton__postframe__28 lda INPT4 ;read button input - bmi JoyButton__postframe__12__NotPressed + bmi JoyButton__postframe__30__NotPressed -;;; start action Local__joybutton__13 +;;; start action Local__joybutton__31 inc Local__6__tmp+0 inc PFColor_pfcolor_b0 -;;; end action Local__joybutton__13 +;;; end action Local__joybutton__31 -JoyButton__postframe__12__NotPressed: +JoyButton__postframe__30__NotPressed: -;;; end action JoyButton__postframe__12 +;;; end action JoyButton__postframe__28 FRAME_END -;;; start action ResetSwitch__nextframe__14 +;;; start action ResetSwitch__nextframe__34 lsr SWCHB ; test Game Reset switch - bcs ResetSwitch__nextframe__14__NoStart + bcs ResetSwitch__nextframe__35__NoStart -;;; start action ResetConsole__resetswitch__15 +;;; start action ResetConsole__resetswitch__36 jmp Main::__Reset ; jump to Reset handler -;;; end action ResetConsole__resetswitch__15 +;;; end action ResetConsole__resetswitch__36 -ResetSwitch__nextframe__14__NoStart: +ResetSwitch__nextframe__35__NoStart: -;;; end action ResetSwitch__nextframe__14 +;;; end action ResetSwitch__nextframe__34 - jmp FrameLoop__start__2__NextFrame ; loop to next frame + jmp FrameLoop__start__4__NextFrame ; loop to next frame -;;; end action FrameLoop__start__2 +;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" Return: .word $6060 From c73263d94440fd5ab4fdbac119d904cb7b76517b Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 27 Feb 2022 10:58:47 -0600 Subject: [PATCH 101/104] ecs: added label sources --- src/common/ecs/compiler.ts | 15 +++++++++------ src/common/ecs/ecs.ts | 14 +++++++++++--- src/worker/tools/ecs.ts | 5 +++++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 2f109608..13de3ed8 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -92,9 +92,11 @@ export class ECSCompiler extends Tokenizer { } annotate(fn: () => T) { - let tok = this.peekToken(); + let start = this.peekToken(); let obj = fn(); - if (obj) (obj as SourceLocated).$loc = tok.$loc; + let end = this.lasttoken; + let $loc = end ? mergeLocs(start.$loc, end.$loc) : start.$loc; + if (obj) (obj as SourceLocated).$loc = $loc; return obj; } @@ -349,7 +351,7 @@ export class ECSCompiler extends Tokenizer { tempbytes = this.parseIntegerConstant(); } let system: System = { name, tempbytes, actions: [] }; - let expr = this.parseBlockStatement(); + let expr = this.annotate(() => this.parseBlockStatement()); let action: Action = { expr, event: name }; system.actions.push(action); return system; @@ -363,7 +365,7 @@ export class ECSCompiler extends Tokenizer { let critical = undefined; if (this.ifToken('critical')) critical = true; if (this.ifToken('fit')) fitbytes = this.parseIntegerConstant(); - let expr = this.parseBlockStatement(); + let expr = this.annotate(() => this.parseBlockStatement()); //query, join, select, direction, let action : Action = { expr, event, fitbytes, critical }; return action as Action; @@ -770,6 +772,7 @@ export class ECSCompiler extends Tokenizer { parseExprList(): Expr[] { return this.parseList(this.parseExpr, ','); } + // TODO: annotate with location parseBlockStatement(): Statement { let valtype : IntType = { dtype:'int', lo:0, hi: 0 } // TODO? if (this.peekToken().type == ECSTokenType.CodeFragment) { @@ -778,7 +781,7 @@ export class ECSCompiler extends Tokenizer { if (this.ifToken('begin')) { let stmts = []; while (this.peekToken().str != 'end') { - stmts.push(this.parseBlockStatement()); + stmts.push(this.annotate(() => this.parseBlockStatement())); } this.expectToken('end'); return { valtype, stmts }; @@ -812,7 +815,7 @@ export class ECSCompiler extends Tokenizer { let direction = undefined; if (modifiers['asc']) direction = 'asc'; else if (modifiers['desc']) direction = 'desc'; - let body = this.parseBlockStatement(); + let body = this.annotate(() => this.parseBlockStatement()); return { select, query, join, direction, stmts: [body], loop: select == 'foreach' } as QueryExpr; } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index e60e91bf..c38b46e8 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -3,8 +3,10 @@ import { Token } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { Bin, Packer } from "./binpack"; -export class ECSError extends Error { +export class ECSError extends Error implements SourceLocated { $loc: SourceLocation; + $sources: SourceLocated[] = []; + constructor(msg: string, obj?: SourceLocation | SourceLocated) { super(msg); Object.setPrototypeOf(this, ECSError.prototype); @@ -834,7 +836,12 @@ class ActionEval { __emit(args: string[]) { let event = args[0]; let eventargs = args.slice(1); - return this.scope.generateCodeForEvent(event, eventargs); + try { + return this.scope.generateCodeForEvent(event, eventargs); + } catch (e) { + if (e.$sources) e.$sources.push(this.action); + throw e; + } } __local(args: string[]) { let tempinc = parseInt(args[0]); @@ -1053,6 +1060,7 @@ class ActionEval { case 'once': // TODO: how is this different from begin/end? //state.xreg = state.yreg = null; + //state.working = new EntitySet(scope, undefined, [], []); break; case 'foreach': case 'unroll': @@ -1077,7 +1085,7 @@ class ActionEval { if (select == 'if') { qr.entities = []; // "if" failed } else { - throw new ECSError(`no entities in statement`, action); + throw new ECSError(`no entities match query`, qexpr); } } else { // TODO: must be a better way... diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 040d1b94..6798ca5a 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -27,6 +27,11 @@ export function assembleECS(step: BuildStep): BuildStepResult { } catch (e) { if (e instanceof ECSError) { compiler.addError(e.message, e.$loc); + for (let obj of e.$sources) { + let name = (obj as any).event; + if (name == 'start') break; + compiler.addError(`... ${name}`, obj.$loc); // TODO? + } return { errors: compiler.errors }; } else if (e instanceof CompileError) { return { errors: compiler.errors }; From e09f115084911a1bb0f177091761151847878189 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 27 Feb 2022 15:28:18 -0600 Subject: [PATCH 102/104] ecs: refactored working and index register sets --- src/common/ecs/compiler.ts | 3 +- src/common/ecs/ecs.ts | 197 +++++++++++++++++++++---------------- 2 files changed, 115 insertions(+), 85 deletions(-) diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 13de3ed8..ae93fe04 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -795,6 +795,7 @@ export class ECSCompiler extends Tokenizer { parseQueryStatement() : QueryExpr { // TODO: include modifiers in error msg const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check? + let all = this.ifToken('all') != null; let query = undefined; let join = undefined; if (select == 'once') { @@ -816,7 +817,7 @@ export class ECSCompiler extends Tokenizer { if (modifiers['asc']) direction = 'asc'; else if (modifiers['desc']) direction = 'desc'; let body = this.annotate(() => this.parseBlockStatement()); - return { select, query, join, direction, stmts: [body], loop: select == 'foreach' } as QueryExpr; + return { select, query, join, direction, all, stmts: [body], loop: select == 'foreach' } as QueryExpr; } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index c38b46e8..3c5d2dac 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -246,6 +246,7 @@ export interface QueryExpr extends BlockExpr { query: Query direction?: 'asc' | 'desc' join?: Query + all?: boolean } @@ -595,25 +596,28 @@ class EntitySet { entities: Entity[]; scope; - constructor(scope: EntityScope, query?: Query, a?: EntityArchetype[], e?: Entity[]) { + constructor(scope: EntityScope, query?: Query, e?: Entity[]) { this.scope = scope; if (query) { if (query.entities) { this.entities = query.entities.slice(0); - this.atypes = []; - for (let e of this.entities) - if (!this.atypes.includes(e.etype)) - this.atypes.push(e.etype); } else { this.atypes = scope.em.archetypesMatching(query); this.entities = scope.entitiesMatching(this.atypes); - if (query.limit) { - this.entities = this.entities.slice(0, query.limit); - } } - } else if (a && e) { - this.atypes = a; + // TODO: desc? + if (query.limit) { + this.entities = this.entities.slice(0, query.limit); + } + } else if (e) { this.entities = e; + } else { + throw new ECSError('invalid EntitySet constructor') + } + if (!this.atypes) { + let at = new Set(); + for (let e of this.entities) at.add(e.etype); + this.atypes = Array.from(at.values()); } } contains(c: ComponentType, f: DataField, where: SourceLocated) { @@ -622,14 +626,13 @@ class EntitySet { } intersection(qr: EntitySet) { let ents = this.entities.filter(e => qr.entities.includes(e)); - let atypes = this.atypes.filter(a1 => qr.atypes.find(a2 => a2 == a1)); - return new EntitySet(this.scope, undefined, atypes, ents); + return new EntitySet(this.scope, undefined, ents); } union(qr: EntitySet) { // TODO: remove dups let ents = this.entities.concat(qr.entities); let atypes = this.atypes.concat(qr.atypes); - return new EntitySet(this.scope, undefined, atypes, ents); + return new EntitySet(this.scope, undefined, ents); } isContiguous() { if (this.entities.length == 0) return true; @@ -965,7 +968,7 @@ class ActionEval { let refs = Array.from(this.scope.iterateArchetypeFields(atypes, (c, f) => f.dtype == 'ref')); // TODO: better error message if (refs.length == 0) throw new ECSError(`cannot find join fields`, action); - if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields (${refs.map(r => r.f.name).join(' ')})`, action); // TODO: check to make sure join works return refs[0]; // TODO /* TODO @@ -1013,7 +1016,8 @@ class ActionEval { queryExprToCode(qexpr: QueryExpr) : string { //console.log('query', this.action.event, qexpr.select, qexpr.query.include); let q = this.startQuery(qexpr); - const allowEmpty = ['if','foreach','unroll','join']; + // TODO: move elsewhere? is "foreach" and "join" part of the empty set? + const allowEmpty = ['if','foreach','join']; if (q.working.entities.length == 0 && allowEmpty.includes(qexpr.select)) { //console.log('empty', this.action.event); this.endQuery(q); @@ -1030,20 +1034,22 @@ class ActionEval { return body; } } - startQuery(qexpr: QueryExpr) { - let oldState = this.scope.state; - let state = this.scope.state = Object.assign(new ActionCPUState(), oldState); - const action = this.action; + queryWorkingSet(qexpr: QueryExpr) { const scope = this.scope; const instance = this.instance; let select = qexpr.select; let q = qexpr.query; - let qr; - let jr; - if (q) - qr = new EntitySet(scope, q); - else - qr = new EntitySet(scope, undefined, [], []); + let qr = new EntitySet(scope, q); + // narrow query w/ working set? + if (!(qexpr.all || q.entities)) { + let ir = qr.intersection(scope.state.working); + // if intersection is empty, take the global set + // if doing otherwise would generate an error (execpt for "if") + // TODO: ambiguous? + if (ir.entities.length || select == 'if') { + qr = ir; + } + } // TODO? error if none? if (instance.params.refEntity && instance.params.refField) { let rf = instance.params.refField; @@ -1055,57 +1061,55 @@ class ActionEval { } else if (instance.params.query) { qr = qr.intersection(new EntitySet(scope, instance.params.query)); } + return qr; + } + updateIndexRegisters(qr: EntitySet, jr: EntitySet | null, select: SelectType) { + const action = this.action; + const scope = this.scope; + const instance = this.instance; + const state = this.scope.state; // TODO: generalize to other cpus/langs - switch (select) { - case 'once': - // TODO: how is this different from begin/end? - //state.xreg = state.yreg = null; - //state.working = new EntitySet(scope, undefined, [], []); - break; - case 'foreach': - case 'unroll': - if (state.xreg && state.yreg) throw new ECSError('no more index registers', action); - if (state.xreg) state.yreg = new IndexRegister(scope, qr); - else state.xreg = new IndexRegister(scope, qr); - break; - case 'join': - // TODO: Joins don't work in superman (arrays offset?) - // ignore the join query, use the ref - if (state.xreg || state.yreg) throw new ECSError('no free index registers for join', action); - jr = new EntitySet(scope, qexpr.join); - state.xreg = new IndexRegister(scope, jr); - state.yreg = new IndexRegister(scope, qr); - break; - case 'if': - case 'with': - // TODO: what if not in X because 1 element? - if (state.xreg && state.xreg.eset) { - state.xreg = state.xreg.narrow(qr, action); - if (state.xreg == null || state.xreg.eset?.entities == null) { - if (select == 'if') { - qr.entities = []; // "if" failed - } else { - throw new ECSError(`no entities match query`, qexpr); + if (qr.entities.length > 1) { + switch (select) { + case 'once': + break; + case 'foreach': + case 'unroll': + if (state.xreg && state.yreg) throw new ECSError('no more index registers', action); + if (state.xreg) state.yreg = new IndexRegister(scope, qr); + else state.xreg = new IndexRegister(scope, qr); + break; + case 'join': + // TODO: Joins don't work in superman (arrays offset?) + // ignore the join query, use the ref + if (state.xreg || state.yreg) throw new ECSError('no free index registers for join', action); + if (jr) state.xreg = new IndexRegister(scope, jr); + state.yreg = new IndexRegister(scope, qr); + break; + case 'if': + case 'with': + // TODO: what if not in X because 1 element? + if (state.xreg && state.xreg.eset) { + state.xreg = state.xreg.narrow(qr, action); + } else if (select == 'with') { + if (instance.params.refEntity && instance.params.refField) { + if (state.xreg) + state.xreg.eset = qr; + else + state.xreg = new IndexRegister(scope, qr); + // ??? } - } else { - // TODO: must be a better way... - qr.entities = state.xreg.eset.entities; } - } else if (select == 'with') { - if (instance.params.refEntity && instance.params.refField) { - if (state.xreg) - state.xreg.eset = qr; - else - state.xreg = new IndexRegister(scope, qr); - // ??? - } else if (qr.entities.length != 1) { - throw new ECSError(`${instance.system.name} query outside of loop must match exactly one entity`, action); //TODO - } - } - break; + break; + } } - // - let entities = qr.entities; + } + getCodeAndProps(qexpr: QueryExpr, qr: EntitySet, jr: EntitySet|null, + oldState: ActionCPUState) + { + // get properties and code + const entities = qr.entities; + const select = qexpr.select; let code = '%%CODE%%'; let props: { [name: string]: string } = {}; // TODO: detect cycles @@ -1118,7 +1122,7 @@ class ActionEval { // TODO? throw new ECSError(`join query doesn't match any entities`, (action as ActionWithJoin).join); // TODO //console.log('join', qr, jr); if (qr.entities.length) { - let joinfield = this.getJoinField(action, qr.atypes, jr.atypes); + let joinfield = this.getJoinField(this.action, qr.atypes, jr.atypes); // TODO: what if only 1 item? // TODO: should be able to access fields via Y reg code = this.wrapCodeInLoop(code, qexpr, qr.entities, joinfield); @@ -1142,11 +1146,11 @@ class ActionEval { let eidofs = re.id - range.elo; props['%reffield'] = `${this.dialect.fieldsymbol(rf.c, rf.f, 0)}+${eidofs}`; } else { - code = this.wrapCodeInFilter(code, qr, oldState); + code = this.wrapCodeInFilter(code, qr, oldState, props); } } if (select == 'if') { - code = this.wrapCodeInFilter(code, qr, oldState); + code = this.wrapCodeInFilter(code, qr, oldState, props); } if (select == 'foreach' && entities.length > 1) { code = this.wrapCodeInLoop(code, qexpr, qr.entities); @@ -1161,11 +1165,32 @@ class ActionEval { } props['%ecount'] = entities.length.toString(); props['%efullcount'] = fullEntityCount.toString(); - // TODO - props['%xofs'] = (this.scope.state.xreg?.offset() || 0).toString(); - props['%yofs'] = (this.scope.state.yreg?.offset() || 0).toString(); - let working = jr ? jr.union(qr) : qr; //console.log('working', action.event, working.entities.length, entities.length); + return { code, props }; + } + startQuery(qexpr: QueryExpr) { + const scope = this.scope; + const action = this.action; + const select = qexpr.select; + + // save old state and make clone + const oldState = this.scope.state; + this.scope.state = Object.assign(new ActionCPUState(), oldState); + + // get working set for this query + const qr = this.queryWorkingSet(qexpr); + + // is it a join? query that too + const jr = qexpr.join && qr.entities.length ? new EntitySet(scope, qexpr.join) : null; + + // update x, y state + this.updateIndexRegisters(qr, jr, select); + + const { code, props } = this.getCodeAndProps(qexpr, qr, jr, oldState); + + // if join, working set is union of both parts + let working = jr ? qr.union(jr) : qr; + return { working, oldState, props, code }; } endQuery(q : { oldState: ActionCPUState }) { @@ -1182,19 +1207,22 @@ class ActionEval { s = s.replace('{{%code}}', code); return s; } - wrapCodeInFilter(code: string, qr: EntitySet, oldState: ActionCPUState) { + wrapCodeInFilter(code: string, qr: EntitySet, oldState: ActionCPUState, props: any) { // TODO: :-p filters too often? - const ents = this.scope.state.xreg?.eset?.entities; + const ents = qr.entities; const ents2 = oldState.xreg?.eset?.entities; if (ents && ents.length && ents2) { let lo = ents[0].id; let hi = ents[ents.length - 1].id; let lo2 = ents2[0].id; let hi2 = ents2[ents2.length - 1].id; - if (lo != lo2) + if (lo != lo2) { code = this.dialect.ASM_FILTER_RANGE_LO_X.replace('{{%code}}', code); - if (hi != hi2) + props['%xofs'] = lo - lo2; + } + if (hi != hi2) { code = this.dialect.ASM_FILTER_RANGE_HI_X.replace('{{%code}}', code); + } } return code; } @@ -1242,7 +1270,8 @@ export class EntityScope implements SourceLocated { ) { parent?.childScopes.push(this); this.state = new ActionCPUState(); - this.state.working = new EntitySet(this, undefined, [], []); + // TODO: parent scope entities too? + this.state.working = new EntitySet(this, undefined, this.entities); // working set = all entities } newEntity(etype: EntityArchetype, name: string): Entity { // TODO: add parent ID? lock parent scope? From 0fc860f5249aea6bd100addd74f61e70610909c3 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 27 Feb 2022 19:46:26 -0600 Subject: [PATCH 103/104] ecs: updated unit tests --- test/ecs/bigdemo.ecs | 148 +++++++ test/ecs/bigdemo.txt | 885 +++++++++++++++++++++++++++++++++++++++++ test/ecs/jumper.ecs | 685 +++++++++++++++++++++++++++++++ test/ecs/jumper.txt | 1 + test/ecs/kernel1.ecs | 147 +++++++ test/ecs/kernel2.ecs | 231 +++++++++++ test/ecs/kernel2.txt | 587 +++++++++++++++++++++++++++ test/ecs/music.ecs | 189 +++++++++ test/ecs/music.txt | 333 ++++++++++++++++ test/ecs/random.ecs | 35 ++ test/ecs/score.ecs | 90 ++++- test/ecs/score.txt | 458 ++++++++++++--------- test/ecs/sound.ecs | 105 +++++ test/ecs/sound.txt | 174 ++++++++ test/ecs/sprites.ecs | 322 ++------------- test/ecs/sprites.txt | 561 -------------------------- test/ecs/sprites1.txt | 516 +----------------------- test/ecs/superman.ecs | 53 +-- test/ecs/superman.txt | 459 +++++++++++---------- test/ecs/titles.ecs | 77 +++- test/ecs/titles.txt | 779 ++++++++++++++++++++++++++++-------- test/ecs/vcslib.ecs | 156 ++++++-- test/ecs/vcslib.txt | 137 ++++--- test/ecs/versatile.ecs | 69 ++++ test/ecs/versatile.txt | 1 + 25 files changed, 5113 insertions(+), 2085 deletions(-) create mode 100644 test/ecs/bigdemo.ecs create mode 100644 test/ecs/bigdemo.txt create mode 100644 test/ecs/jumper.ecs create mode 100644 test/ecs/jumper.txt create mode 100644 test/ecs/kernel1.ecs create mode 100644 test/ecs/kernel2.ecs create mode 100644 test/ecs/kernel2.txt create mode 100644 test/ecs/music.ecs create mode 100644 test/ecs/music.txt create mode 100644 test/ecs/random.ecs create mode 100644 test/ecs/sound.ecs create mode 100644 test/ecs/sound.txt create mode 100644 test/ecs/versatile.ecs create mode 100644 test/ecs/versatile.txt diff --git a/test/ecs/bigdemo.ecs b/test/ecs/bigdemo.ecs new file mode 100644 index 00000000..6ab9bbf1 --- /dev/null +++ b/test/ecs/bigdemo.ecs @@ -0,0 +1,148 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" +import "sprites.ecs" +import "kernel2.ecs" +import "score.ecs" + +demo Main + + using FrameLoop, Kernel2Sprite + using Joystick, MoveJoyX, MoveJoyY + using SetXPos, SetHorizPos + using SpriteShuffler, SpriteHider + + using Kernel6Digit + using JoyButton, BCDMath + + entity Score [BCDScore6, PFColor, BGColor] + const pfcolor = $3c + const bgcolor = $02 + end + + entity Kernel [KernelSection, BGColor] + const lines = 168 + const bgcolor = 0xa0 + end + + entity Weird [Bitmap,Colormap] + decode vcs_sprite + --- + .x...... 12 + .xx..... 14 + .xxx.... 16 + .xxxx... 18 + .xxxxx.. 1a + .xxxxxx. 1c + .xxx.xxx 1e + .x.x.x.x 18 + --- + end + + entity Cool [Bitmap,Colormap] + decode vcs_sprite + --- + ...xx... 48 + ..xxxx.. 4a + .xxxxxx. 4c + xxxxxxxx 4e + x..xx..x 4e + x.xxx.xx 4e + xxxxxxxx 4e + xxxxxxxx 4e + xxxxxxxx 4e + xx....xx 4e + xxxxxxxx 4e + .xxxxxx. 4c + ..xxxx.. 4a + --- + end + + entity Cool2 [Bitmap,Colormap] + decode vcs_sprite + --- + ...xx... 48 + ..xxxx.. 4a + .xxxxxx. 4c + xxxxxxxx 8e + x..xx..x ce + x..xx..x ce + xxxxxxxx ce + xxxxxxxx 4e + xxxxxxxx 4e + xx.xx.xx 4e + xxx..xxx 4e + .xxxxxx. 4c + ..xxxx.. 4a + --- + end + + entity Bitmap1 [Bitmap] + const bitmapdata = [1, 1, 3, 7, 15, 31, 63, 255] + 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] + var xpos = 50 + var ypos = 150 + var bitmap = #Cool + var colormap = #Cool + const plyrflags = 0 + end + + entity Sprite1 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player] + var xpos = 100 + var ypos = 60 + var bitmap = #Cool2 + var colormap = #Cool2 + const plyrflags = 3 + end + + entity Sprite2 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos] + var xpos = 80 + var ypos = 90 + var bitmap = #Weird + var colormap = #Weird + const plyrflags = 0 + end + + entity Sprite3 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos] + var xpos = 40 + var ypos = 150 + var bitmap = #Weird + var colormap = #Weird + const plyrflags = 0 + end + + entity Slot0 [SpriteSlot] + var sprite = #Sprite0 + end + entity Slot1 [SpriteSlot] + var sprite = #Sprite1 + end + entity Slot2 [SpriteSlot] + var sprite = #Sprite2 + end + entity Slot3 [SpriteSlot] + var sprite = #Sprite3 + end + + system Enemies + on postframe do foreach [Sprite,HasYpos,-Player] + --- + inc {{set ypos}} +; inc {{set ypos}} + --- + end + +end demo diff --git a/test/ecs/bigdemo.txt b/test/ecs/bigdemo.txt new file mode 100644 index 00000000..c10eff64 --- /dev/null +++ b/test/ecs/bigdemo.txt @@ -0,0 +1,885 @@ +EVENT__start = 1 +EVENT__postframe = 1 +EVENT__preframe = 1 +EVENT__kernel = 1 +EVENT__scanline = 1 +EVENT__joyleft = 1 +EVENT__joyright = 1 +EVENT__joyup = 1 +EVENT__joydown = 1 +EVENT__prekernel = 1 +EVENT__SetHorizPos = 1 +EVENT__AddBCD4 = 1 +EVENT__AddBCD2 = 1 +EVENT__SubBCD2 = 1 +.scope Main +.zeropage +Sprite_bitmap_b0: +.res 1 +.res 1 +.res 1 +.res 1 +HasColormap_colormap_b0: +.res 1 +.res 1 +.res 1 +.res 1 +HasXpos_xpos_b0: +.res 1 +.res 1 +.res 1 +.res 1 +HasYpos_ypos_b0: +.res 1 +.res 1 +.res 1 +.res 1 +SpriteSlot_sprite_b0: +.res 1 +.res 1 +.res 1 +.res 1 +BCDScore6_digits_b0: +.res 1 +BCDScore6_digits_b8: +.res 1 +BCDScore6_digits_b16: +.res 1 +TEMP: +Kernel2Sprite__2__tmp: +Joystick__3__tmp: +SpriteHider__9__tmp: +.res 1 +SpriteShuffler__8__tmp: +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 +Kernel6Digit__10__tmp: +.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 +.res 1 +.res 1 +.code +PFColor_pfcolor_b0: +.byte 60 +BGColor_bgcolor_b0: +.byte 2 +.byte 160 +KernelSection_lines_b0: +.byte 168 +Bitmap_bitmapdata_b0: +.byte <(Bitmap_bitmapdata_e2_b0+31) +.byte <(Bitmap_bitmapdata_e3_b0+31) +.byte <(Bitmap_bitmapdata_e4_b0+31) +.byte <(Bitmap_bitmapdata_e5_b0+31) +.byte <(Bitmap_bitmapdata_e6_b0+31) +Bitmap_bitmapdata_b8: +.byte >(Bitmap_bitmapdata_e2_b0+31) +.byte >(Bitmap_bitmapdata_e3_b0+31) +.byte >(Bitmap_bitmapdata_e4_b0+31) +.byte >(Bitmap_bitmapdata_e5_b0+31) +.byte >(Bitmap_bitmapdata_e6_b0+31) +Bitmap_bitmapdata_e2_b0: +.byte 85 +.byte 119 +.byte 126 +.byte 124 +.byte 120 +.byte 112 +.byte 96 +.byte 64 +Bitmap_height_b0: +.byte 7 +.byte 12 +.byte 12 +.byte 8 +.byte 8 +Colormap_colormapdata_b0: +.byte <(Colormap_colormapdata_e2_b0+31) +.byte <(Colormap_colormapdata_e3_b0+31) +.byte <(Colormap_colormapdata_e4_b0+31) +.res 1 +.res 1 +.byte <(Colormap_colormapdata_e7_b0+31) +Colormap_colormapdata_b8: +.byte >(Colormap_colormapdata_e2_b0+31) +.byte >(Colormap_colormapdata_e3_b0+31) +.byte >(Colormap_colormapdata_e4_b0+31) +.res 1 +.res 1 +.byte >(Colormap_colormapdata_e7_b0+31) +Colormap_colormapdata_e2_b0: +.byte 24 +.byte 30 +.byte 28 +.byte 26 +.byte 24 +.byte 22 +.byte 20 +.byte 18 +Bitmap_bitmapdata_e3_b0: +.byte 60 +.byte 126 +.byte 255 +.byte 195 +.byte 255 +.byte 255 +.byte 255 +.byte 187 +.byte 153 +.byte 255 +.byte 126 +.byte 60 +.byte 24 +Colormap_colormapdata_e3_b0: +.byte 74 +.byte 76 +.byte 78 +.byte 78 +.byte 78 +.byte 78 +.byte 78 +.byte 78 +.byte 78 +.byte 78 +.byte 76 +.byte 74 +.byte 72 +Bitmap_bitmapdata_e4_b0: +.byte 60 +.byte 126 +.byte 231 +.byte 219 +.byte 255 +.byte 255 +.byte 255 +.byte 153 +.byte 153 +.byte 255 +.byte 126 +.byte 60 +.byte 24 +Colormap_colormapdata_e4_b0: +.byte 74 +.byte 76 +.byte 78 +.byte 78 +.byte 78 +.byte 78 +.byte 206 +.byte 206 +.byte 206 +.byte 142 +.byte 76 +.byte 74 +.byte 72 +Bitmap_bitmapdata_e5_b0: +.byte 1 +.byte 1 +.byte 3 +.byte 7 +.byte 15 +.byte 31 +.byte 63 +.byte 255 +Bitmap_bitmapdata_e6_b0: +.byte 24 +.byte 62 +.byte 255 +.byte 255 +.byte 255 +.byte 255 +.byte 62 +.byte 24 +Colormap_colormapdata_e7_b0: +.byte 6 +.byte 3 +.byte 6 +.byte 9 +.byte 12 +.byte 14 +.byte 31 +.byte 63 +Sprite_plyrflags_b0: +.byte 0 +.byte 3 +.byte 0 +.byte 0 +Main__INITDATA: +.byte 1 +.byte 2 +.byte 0 +.byte 0 +.byte 1 +.byte 2 +.byte 0 +.byte 0 +.byte 50 +.byte 100 +.byte 80 +.byte 40 +.byte 150 +.byte 60 +.byte 90 +.byte 150 +.byte 0 +.byte 1 +.byte 2 +.byte 3 +.byte 0 +.byte 0 +.byte 0 +__Start: + +;;; start action Init__main_init__1 + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #23 +: lda Main__INITDATA-1,y + sta Sprite_bitmap_b0-1,y + dey + bne :- + +;;; start action FrameLoop__start__3 + + +FrameLoop__start__4__NextFrame: + FRAME_END + + FRAME_START + +;;; start action Kernel2Sprite__preframe__5 + +; TODOO: can store KLINES in memory? +.define KLINES #168 +.define KPAD 32 +; set height to zero in case no sprites + lda #0 + sta Kernel2Sprite__2__tmp+8 + sta Kernel2Sprite__2__tmp+9 + +;;; end action Kernel2Sprite__preframe__5 + +;;; start action Kernel2Sprite__preframe__8 + + ldy #0 +Kernel2Sprite__preframe__9____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 + clc + adc KPAD + sec + sbc HasYpos_ypos_b0,x + sta Kernel2Sprite__2__tmp+11 +; calculate bitmap pointer + stx Kernel2Sprite__2__tmp+12 ; save X (Sprite index) + lda Sprite_bitmap_b0,x ; deref bitmap + tax + lda Bitmap_bitmapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index + lda Bitmap_bitmapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+2,y +; get bitmap height + lda Bitmap_height_b0,x + sta Kernel2Sprite__2__tmp+8,y +; calculate colormap pointer + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasColormap_colormap_b0,x ; deref colormap + tax + lda Colormap_colormapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+4,y + lda Colormap_colormapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+6,y +; save ypos + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasYpos_ypos_b0,x + sta Kernel2Sprite__2__tmp+10,y + + iny + cpy #2 + jne Kernel2Sprite__preframe__9____each +Kernel2Sprite__preframe__9____exit: + +;;; end action Kernel2Sprite__preframe__8 + +;;; start action Kernel2Sprite__preframe__11 + +; shuffle pointers into (MSB, LSB) byte order +; L0 L1 H0 H1 -> L0 H0 L1 H1 + lda Kernel2Sprite__2__tmp+1 + ldy Kernel2Sprite__2__tmp+2 + sty Kernel2Sprite__2__tmp+1 + sta Kernel2Sprite__2__tmp+2 + lda Kernel2Sprite__2__tmp+5 + ldy Kernel2Sprite__2__tmp+6 + sty Kernel2Sprite__2__tmp+5 + sta Kernel2Sprite__2__tmp+6 + +;;; end action Kernel2Sprite__preframe__11 + +;;; start action Kernel2Sprite__preframe__13 + + lda #160 + sta COLUBK + +;;; end action Kernel2Sprite__preframe__13 + +;;; start action Kernel2Sprite__preframe__16 + +;;; end action Kernel2Sprite__preframe__16 + +;;; start action SetXPos__preframe__17 + + ldy #0 +SetXPos__preframe__18____each: + ldx SpriteSlot_sprite_b0,y + + lda HasXpos_xpos_b0,x + + jsr SetHorizPos__SetHorizPos__20 + + + iny + cpy #2 + jne SetXPos__preframe__18____each +SetXPos__preframe__18____exit: + +;;; end action SetXPos__preframe__17 + +;;; start action SetXPos__preframe__22 + +;;; end action SetXPos__preframe__22 + +;;; start action Kernel6Digit__preframe__23 + +Digit0 = Kernel6Digit__10__tmp+0 +Digit1 = Kernel6Digit__10__tmp+2 +Digit2 = Kernel6Digit__10__tmp+4 +Digit3 = Kernel6Digit__10__tmp+6 +Digit4 = Kernel6Digit__10__tmp+8 +Digit5 = Kernel6Digit__10__tmp+10 +Kernel6Digit__preframe__25__BCD0 = Kernel6Digit__10__tmp+12 +Kernel6Digit__preframe__25__BCD1 = Kernel6Digit__10__tmp+13 +Kernel6Digit__preframe__25__BCD2 = Kernel6Digit__10__tmp+14 + + lda BCDScore6_digits_b0 + sta Kernel6Digit__preframe__25__BCD0 + lda BCDScore6_digits_b8 + sta Kernel6Digit__preframe__25__BCD1 + lda BCDScore6_digits_b16 + sta Kernel6Digit__preframe__25__BCD2 + ldx #0 ; leftmost bitmap + ldy #2 ; start from most-sigificant BCD value +Kernel6Digit__preframe__25__Loop: + lda Kernel6Digit__preframe__25__BCD0,y ; get BCD value + and #$f0 ; isolate high nibble (* 16) + lsr ; shift right 1 bit (* 8) + clc + adc #FontTable + adc #0 + sta Digit0+1,x ; store pointer hi byte + inx + inx ; next bitmap pointer + lda Kernel6Digit__preframe__25__BCD0,y ; get BCD value (again) + and #$f ; isolate low nibble + asl + asl + asl ; * 8 + clc + adc #FontTable + adc #0 + sta Digit0+1,x ; store pointer hi byte + inx + inx ; next bitmap pointer + dey ; next BCD value + bpl Kernel6Digit__preframe__25__Loop ; repeat until < 0 + +;;; end action Kernel6Digit__preframe__23 + + +;;; start action SetXPos__prekernel__26 + + sta WSYNC + sta HMOVE + SLEEPR 24 + sta HMCLR + +;;; end action SetXPos__prekernel__26 + + KERNEL_START + +;;; start action Kernel2Sprite__kernel__28 + + ldy #0 + sty VDELP0 + iny + sty VDELP1 + +;;; end action Kernel2Sprite__kernel__28 + + jsr Kernel2Sprite__kernel__31 + +;;; start action Kernel2Sprite__kernel__40 + + lda #0 + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + +;;; end action Kernel2Sprite__kernel__40 + +;;; start action Kernel6Digit__kernel__42 + + lda #60 + sta COLUP0 + sta COLUP1 + lda #3 + sta NUSIZ0 + sta NUSIZ1 +; set horizontal position of player objects + sta WSYNC + sta HMCLR + SLEEPR 24 + sta RESP0 + sta RESP1 + lda #$10 + sta HMP1 + sta WSYNC + sta HMOVE + SLEEPR 24 ; wait 24 cycles between write to HMOVE and HMxxx + sta HMCLR + lda #1 + sta VDELP0 + sta VDELP1 + +;;; end action Kernel6Digit__kernel__42 + + jsr Kernel6Digit__kernel__45 + + KERNEL_END + + +;;; start action FrameLoop__postframe__48 + + lsr SWCHB ; test Game Reset switch + bcs FrameLoop__postframe__49__NoStart + +FrameLoop__postframe__49__NoStart: + +;;; end action FrameLoop__postframe__48 + +;;; start action Joystick__postframe__50 + +; 2 control inputs share a single byte, 4 bits each + lda SWCHA + sta Joystick__3__tmp+0 + +;;; end action Joystick__postframe__50 + +;;; start action Joystick__postframe__52 + + ldx #0 +Joystick__postframe__53____each: + + asl Joystick__3__tmp+0 +.ifdef EVENT__joyright + bcs Joystick__postframe__54__SkipMoveRight + +;;; start action MoveJoyX__joyright__55 + + lda HasXpos_xpos_b0,x + clc + adc #1 + cmp #150 + bcs MoveJoyX__joyright__57__nomove + sta HasXpos_xpos_b0,x +MoveJoyX__joyright__57__nomove: + +;;; end action MoveJoyX__joyright__55 + +Joystick__postframe__54__SkipMoveRight: +.endif + asl Joystick__3__tmp+0 +.ifdef EVENT__joyleft + bcs Joystick__postframe__54__SkipMoveLeft + +;;; start action MoveJoyX__joyleft__58 + + lda HasXpos_xpos_b0,x + sec + sbc #1 + bcc MoveJoyX__joyleft__60__nomove + sta HasXpos_xpos_b0,x +MoveJoyX__joyleft__60__nomove: + +;;; end action MoveJoyX__joyleft__58 + +Joystick__postframe__54__SkipMoveLeft: +.endif + asl Joystick__3__tmp+0 +.ifdef EVENT__joydown + bcs Joystick__postframe__54__SkipMoveDown + +;;; start action MoveJoyY__joydown__61 + + lda HasYpos_ypos_b0,x + clc + adc #1 + cmp #220 + bcs MoveJoyY__joydown__63__nomove + sta HasYpos_ypos_b0,x +MoveJoyY__joydown__63__nomove: + +;;; end action MoveJoyY__joydown__61 + +Joystick__postframe__54__SkipMoveDown: +.endif + asl Joystick__3__tmp+0 +.ifdef EVENT__joyup + bcs Joystick__postframe__54__SkipMoveUp + +;;; start action MoveJoyY__joyup__64 + + lda HasYpos_ypos_b0,x + sec + sbc #1 + bcc MoveJoyY__joyup__66__nomove + sta HasYpos_ypos_b0,x +MoveJoyY__joyup__66__nomove: + +;;; end action MoveJoyY__joyup__64 + +Joystick__postframe__54__SkipMoveUp: +.endif + + inx + cpx #2 + jne Joystick__postframe__53____each +Joystick__postframe__53____exit: + +;;; end action Joystick__postframe__52 + +;;; start action SpriteShuffler__postframe__67 + +; load two sprite slots at left side of array + lda SpriteSlot_sprite_b0 + sta SpriteShuffler__8__tmp+0 + lda SpriteSlot_sprite_b0+1 + sta SpriteShuffler__8__tmp+1 +; move two slots to the left + ldx #0 +SpriteShuffler__postframe__69__loop: + lda SpriteSlot_sprite_b0+2,x + sta SpriteSlot_sprite_b0,x + inx + cpx #4-2 + bne SpriteShuffler__postframe__69__loop +; store two sprite slots at right side of array + lda SpriteShuffler__8__tmp+0 + sta SpriteSlot_sprite_b0+4-2 + lda SpriteShuffler__8__tmp+1 + sta SpriteSlot_sprite_b0+4-1 + +;;; end action SpriteShuffler__postframe__67 + +;;; start action SpriteHider__postframe__70 + + lda #4-1 + sta SpriteHider__9__tmp+0 + +;;; end action SpriteHider__postframe__70 + +;;; start action SpriteHider__postframe__73 + + ldy #0 +SpriteHider__postframe__74____each: + ldx SpriteSlot_sprite_b0,y + + lda HasYpos_ypos_b0,x + cmp #192 + bcc SpriteHider__postframe__75__skip +; swap this sprite slot with slot at end of array + lda SpriteSlot_sprite_b0,y + pha + ldx SpriteHider__9__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__9__tmp+0 +SpriteHider__postframe__75__skip: + + iny + cpy #2 + jne SpriteHider__postframe__74____each +SpriteHider__postframe__74____exit: + +;;; end action SpriteHider__postframe__73 + +;;; start action JoyButton__postframe__76 + + ldx #0 +JoyButton__postframe__77____each: + + lda INPT4,x ;read button input + bmi JoyButton__postframe__78__NotPressed + +JoyButton__postframe__78__NotPressed: + + inx + cpx #2 + jne JoyButton__postframe__77____each +JoyButton__postframe__77____exit: + +;;; end action JoyButton__postframe__76 + +;;; start action Enemies__postframe__79 + + ldx #0 +Enemies__postframe__80____each: + + inc HasYpos_ypos_b0+2,x +; inc HasYpos_ypos_b0+2,x + + inx + cpx #2 + jne Enemies__postframe__80____each +Enemies__postframe__80____exit: + +;;; end action Enemies__postframe__79 + + jmp FrameLoop__start__4__NextFrame ; loop to next frame + +;;; end action FrameLoop__start__3 + ; start main routine +.segment "VECTORS" +ZeroByte: .byte $00 +Return: .byte $60 +VecNMI: +VecReset: .word __Reset +VecBRK: .word __BRK +.code + +;;; end action Init__main_init__1 +.rodata +__ALIGNORIGIN: +.rodata +SetHorizPos__SetHorizPos__20: + +; SetHorizPos routine +; A = X coordinate +; Y = player number (0 or 1) + sec ; set carry flag + sta WSYNC ; start a new line +: + sbc #15 ; subtract 15 + bcs :- ; branch until negative + eor #7 ; calculate fine offset + asl + asl + asl + asl + sta HMP0,y ; set fine offset + sta RESP0,y ; fix coarse position + sta WSYNC ; won't overrun if X < 150 + + rts + +.assert >(SetHorizPos__SetHorizPos__20) = >(*), error, "SetHorizPos__SetHorizPos__20 crosses a page boundary!" + +.assert (* - SetHorizPos__SetHorizPos__20) <= 22, error, .sprintf("SetHorizPos__SetHorizPos__20 does not fit in 22 bytes, it took %d!", (* - SetHorizPos__SetHorizPos__20)) +.rodata +Kernel2Sprite__kernel__31: + + ldy #168 +Kernel2Sprite__kernel__33__LVScan: + +;;; start action Kernel2Sprite__scanline__34 + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__35__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__35__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y +; .if 0 = 0 ; TODO: configurable? + sta WSYNC +; .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__35__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__35__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite__scanline__34 + +;;; start action Kernel2Sprite__scanline__36 + +;;; end action Kernel2Sprite__scanline__36 + + dey ; next scanline + +;;; start action Kernel2Sprite__scanline__37 + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__38__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__38__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y +; .if 1 = 0 ; TODO: configurable? + sta WSYNC +; .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__38__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__38__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite__scanline__37 + +;;; start action Kernel2Sprite__scanline__39 + +;;; end action Kernel2Sprite__scanline__39 + + dey ; next scanline + bne Kernel2Sprite__kernel__33__LVScan ; repeat until out of lines + + rts + +.assert >(Kernel2Sprite__kernel__31) = >(*), error, "Kernel2Sprite__kernel__31 crosses a page boundary!" + +.if <(* - __ALIGNORIGIN) > 256-72 +.align $100 +.endif +.rodata +Kernel6Digit__kernel__45: + +; Display the resulting 48x8 bitmap +; using the Digit0-5 pointers. +Kernel6Digit__kernel__47__LoopCount = Kernel6Digit__10__tmp+12 +Kernel6Digit__kernel__47__Temp = Kernel6Digit__10__tmp+13 + + lda #2 + sta WSYNC + sta COLUBK + lda #7 + sta Kernel6Digit__kernel__47__LoopCount + SLEEPR 20 ; TODO? +: + ldy Kernel6Digit__kernel__47__LoopCount ; counts backwards + lda (Digit0),y ; load B0 (1st sprite byte) + sta GRP0 ; B0 -> [GRP0] + lda (Digit1),y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + sta WSYNC ; sync to next scanline + lda (Digit2),y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda (Digit5),y ; load B5 -> A + sta Kernel6Digit__kernel__47__Temp ; B5 -> temp + lda (Digit4),y ; load B4 + tax ; -> X + lda (Digit3),y ; load B3 -> A + ldy Kernel6Digit__kernel__47__Temp ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec Kernel6Digit__kernel__47__LoopCount ; go to next line + bpl :- ; repeat until < 0 + + lda #0 ; clear the sprite registers + sta WSYNC + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + sta COLUBK + + rts + +.assert >(Kernel6Digit__kernel__45) = >(*), error, "Kernel6Digit__kernel__45 crosses a page boundary!" + +.assert (* - Kernel6Digit__kernel__45) <= 72, error, .sprintf("Kernel6Digit__kernel__45 does not fit in 72 bytes, it took %d!", (* - Kernel6Digit__kernel__45)) +FontTable: + +;;; start action FontTable__FontTable__82 + +; Font table for digits 0-9 (8x8 pixels) +;;{w:8,h:8,count:10,brev:1,flip:1};; + .byte $00,$3c,$66,$66,$76,$6e,$66,$3c,$00,$7e,$18,$18,$18,$38,$18,$18 + .byte $00,$7e,$60,$30,$0c,$06,$66,$3c,$00,$3c,$66,$06,$1c,$06,$66,$3c + .byte $00,$06,$06,$7f,$66,$1e,$0e,$06,$00,$3c,$66,$06,$06,$7c,$60,$7e + .byte $00,$3c,$66,$66,$7c,$60,$66,$3c,$00,$18,$18,$18,$18,$0c,$66,$7e + .byte $00,$3c,$66,$66,$3c,$66,$66,$3c,$00,$3c,$66,$06,$3e,$66,$66,$3c + +;;; end action FontTable__FontTable__82 + +.endscope +Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/jumper.ecs b/test/ecs/jumper.ecs new file mode 100644 index 00000000..16d8aefa --- /dev/null +++ b/test/ecs/jumper.ecs @@ -0,0 +1,685 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" +import "sprites.ecs" +import "score.ecs" +import "sound.ecs" +import "velocity.ecs" +import "kernel2.ecs" +import "random.ecs" +import "versatile.ecs" +import "music.ecs" + +// TODO: not yet used +component Activity + activity: enum [Standing,Walking,Jumping] +end + +component Enemy +end + +component Gravity +end + +component Jumper +end + +system MoveJoyVel + // dampen velocity + on frame8 do with [HasXpos] +--- + lda {{Bitmap:bitmapdata}},x + sbc #0 + sta {{$1}} +; get bitmap height + lda {{Colormap:colormapdata}},x + sbc #0 + sta {{$3}} +; save ypos + ldx {{$6}} ; restore X + lda {{ 1 player? or missile? + on kernelsetup do with [Sprite,HasXpos] +--- + ldy #0 + lda {{ player 0? + stx {{$0}} ; save object index +@nocollide: + --- +// TODO: somehow avoid select? pass arg to explode? + on postframe do select [Sprite] + --- + ldx {{$0}} ; get object index + bmi @noexplode ; was there collision? + {{!explode}} +@noexplode: + --- +end diff --git a/test/ecs/kernel2.ecs b/test/ecs/kernel2.ecs new file mode 100644 index 00000000..c3f4d0e9 --- /dev/null +++ b/test/ecs/kernel2.ecs @@ -0,0 +1,231 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" +import "sprites.ecs" + +system Kernel2Sprite + locals 13 + on preframe do with [KernelSection] +--- +; TODOO: can store KLINES in memory? +.define KLINES {{Bitmap:bitmapdata}},x + sbc #0 + sta {{$2}},y +; get bitmap height + lda {{Colormap:colormapdata}},x + sbc #0 + sta {{$6}},y +; save ypos + ldx {{$12}} ; restore X + lda {{ L0 H0 L1 H1 + lda {{$1}} + ldy {{$2}} + sty {{$1}} + sta {{$2}} + lda {{$5}} + ldy {{$6}} + sty {{$5}} + sta {{$6}} +--- + on preframe do if [KernelSection,BGColor] +--- + lda {{(Bitmap_bitmapdata_e1_b0+31) +.byte >(Bitmap_bitmapdata_e2_b0+31) +Bitmap_bitmapdata_e1_b0: +.byte 1 +.byte 1 +.byte 3 +.byte 7 +.byte 15 +.byte 31 +.byte 63 +.byte 127 +.byte 0 +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 +.byte 0 +Colormap_colormapdata_b0: +.byte <(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_b8: +.byte >(Colormap_colormapdata_e3_b0+31) +Colormap_colormapdata_e3_b0: +.byte 2 +.byte 4 +.byte 6 +.byte 8 +.byte 10 +.byte 12 +.byte 14 +.byte 14 +.byte 14 +Sprite_plyrflags_b0: +.byte 0 +.byte 3 +.byte 2 +.byte 0 +Main__INITDATA: +.byte 1 +.byte 0 +.byte 1 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 50 +.byte 100 +.byte 80 +.byte 40 +.byte 150 +.byte 60 +.byte 90 +.byte 150 +.byte 0 +.byte 1 +.byte 2 +.byte 3 +__Start: + +;;; start action Init__main_init__1 + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #20 +: lda Main__INITDATA-1,y + sta Sprite_bitmap_b0-1,y + dey + bne :- + +;;; start action FrameLoop__start__3 + + +FrameLoop__start__4__NextFrame: + FRAME_END + + FRAME_START + +;;; start action Kernel2Sprite__preframe__5 + +; TODOO: can store KLINES in memory? +.define KLINES #192 +.define KPAD 32 +; set height to zero in case no sprites + lda #0 + sta Kernel2Sprite__2__tmp+8 + sta Kernel2Sprite__2__tmp+9 + +;;; end action Kernel2Sprite__preframe__5 + +;;; start action Kernel2Sprite__preframe__8 + + ldy #0 +Kernel2Sprite__preframe__9____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 + clc + adc KPAD + sec + sbc HasYpos_ypos_b0,x + sta Kernel2Sprite__2__tmp+11 +; calculate bitmap pointer + stx Kernel2Sprite__2__tmp+12 ; save X (Sprite index) + lda Sprite_bitmap_b0,x ; deref bitmap + tax + lda Bitmap_bitmapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index + lda Bitmap_bitmapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+2,y +; get bitmap height + lda Bitmap_height_b0,x + sta Kernel2Sprite__2__tmp+8,y +; calculate colormap pointer + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasColormap_colormap_b0,x ; deref colormap + tax + lda Colormap_colormapdata_b0,x + sec + sbc Kernel2Sprite__2__tmp+11 + sta Kernel2Sprite__2__tmp+4,y + lda Colormap_colormapdata_b8,x + sbc #0 + sta Kernel2Sprite__2__tmp+6,y +; save ypos + ldx Kernel2Sprite__2__tmp+12 ; restore X + lda HasYpos_ypos_b0,x + sta Kernel2Sprite__2__tmp+10,y + + iny + cpy #2 + jne Kernel2Sprite__preframe__9____each +Kernel2Sprite__preframe__9____exit: + +;;; end action Kernel2Sprite__preframe__8 + +;;; start action Kernel2Sprite__preframe__11 + +; shuffle pointers into (MSB, LSB) byte order +; L0 L1 H0 H1 -> L0 H0 L1 H1 + lda Kernel2Sprite__2__tmp+1 + ldy Kernel2Sprite__2__tmp+2 + sty Kernel2Sprite__2__tmp+1 + sta Kernel2Sprite__2__tmp+2 + lda Kernel2Sprite__2__tmp+5 + ldy Kernel2Sprite__2__tmp+6 + sty Kernel2Sprite__2__tmp+5 + sta Kernel2Sprite__2__tmp+6 + +;;; end action Kernel2Sprite__preframe__11 + +;;; start action Kernel2Sprite__preframe__13 + + lda #162 + sta COLUBK + +;;; end action Kernel2Sprite__preframe__13 + +;;; start action Kernel2Sprite__preframe__16 + +;;; end action Kernel2Sprite__preframe__16 + +;;; start action SetXPos__preframe__17 + + ldy #0 +SetXPos__preframe__18____each: + ldx SpriteSlot_sprite_b0,y + + lda HasXpos_xpos_b0,x + + jsr SetHorizPos__SetHorizPos__20 + + + iny + cpy #2 + jne SetXPos__preframe__18____each +SetXPos__preframe__18____exit: + +;;; end action SetXPos__preframe__17 + +;;; start action SetXPos__preframe__22 + +;;; end action SetXPos__preframe__22 + + +;;; start action SetXPos__prekernel__23 + + sta WSYNC + sta HMOVE + SLEEPR 24 + sta HMCLR + +;;; end action SetXPos__prekernel__23 + + KERNEL_START + +;;; start action Kernel2Sprite__kernel__25 + + ldy #0 + sty VDELP0 + iny + sty VDELP1 + +;;; end action Kernel2Sprite__kernel__25 + + jsr Kernel2Sprite__kernel__28 + +;;; start action Kernel2Sprite__kernel__37 + + lda #0 + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + +;;; end action Kernel2Sprite__kernel__37 + + KERNEL_END + + +;;; start action FrameLoop__postframe__39 + + lsr SWCHB ; test Game Reset switch + bcs FrameLoop__postframe__40__NoStart + +FrameLoop__postframe__40__NoStart: + +;;; end action FrameLoop__postframe__39 + +;;; start action Joystick__postframe__41 + +; 2 control inputs share a single byte, 4 bits each + lda SWCHA + sta Joystick__3__tmp+0 + +;;; end action Joystick__postframe__41 + +;;; start action Joystick__postframe__43 + + ldx #0 +Joystick__postframe__44____each: + + asl Joystick__3__tmp+0 +.ifdef EVENT__joyright + bcs Joystick__postframe__45__SkipMoveRight + +;;; start action MoveJoyX__joyright__46 + + lda HasXpos_xpos_b0,x + clc + adc #1 + cmp #150 + bcs MoveJoyX__joyright__48__nomove + sta HasXpos_xpos_b0,x +MoveJoyX__joyright__48__nomove: + +;;; end action MoveJoyX__joyright__46 + +Joystick__postframe__45__SkipMoveRight: +.endif + asl Joystick__3__tmp+0 +.ifdef EVENT__joyleft + bcs Joystick__postframe__45__SkipMoveLeft + +;;; start action MoveJoyX__joyleft__49 + + lda HasXpos_xpos_b0,x + sec + sbc #1 + bcc MoveJoyX__joyleft__51__nomove + sta HasXpos_xpos_b0,x +MoveJoyX__joyleft__51__nomove: + +;;; end action MoveJoyX__joyleft__49 + +Joystick__postframe__45__SkipMoveLeft: +.endif + asl Joystick__3__tmp+0 +.ifdef EVENT__joydown + bcs Joystick__postframe__45__SkipMoveDown + +;;; start action MoveJoyY__joydown__52 + + lda HasYpos_ypos_b0,x + clc + adc #1 + cmp #220 + bcs MoveJoyY__joydown__54__nomove + sta HasYpos_ypos_b0,x +MoveJoyY__joydown__54__nomove: + +;;; end action MoveJoyY__joydown__52 + +Joystick__postframe__45__SkipMoveDown: +.endif + asl Joystick__3__tmp+0 +.ifdef EVENT__joyup + bcs Joystick__postframe__45__SkipMoveUp + +;;; start action MoveJoyY__joyup__55 + + lda HasYpos_ypos_b0,x + sec + sbc #1 + bcc MoveJoyY__joyup__57__nomove + sta HasYpos_ypos_b0,x +MoveJoyY__joyup__57__nomove: + +;;; end action MoveJoyY__joyup__55 + +Joystick__postframe__45__SkipMoveUp: +.endif + + inx + cpx #2 + jne Joystick__postframe__44____each +Joystick__postframe__44____exit: + +;;; end action Joystick__postframe__43 + +;;; start action SpriteShuffler__postframe__58 + +; load two sprite slots at left side of array + lda SpriteSlot_sprite_b0 + sta SpriteShuffler__8__tmp+0 + lda SpriteSlot_sprite_b0+1 + sta SpriteShuffler__8__tmp+1 +; move two slots to the left + ldx #0 +SpriteShuffler__postframe__60__loop: + lda SpriteSlot_sprite_b0+2,x + sta SpriteSlot_sprite_b0,x + inx + cpx #4-2 + bne SpriteShuffler__postframe__60__loop +; store two sprite slots at right side of array + lda SpriteShuffler__8__tmp+0 + sta SpriteSlot_sprite_b0+4-2 + lda SpriteShuffler__8__tmp+1 + sta SpriteSlot_sprite_b0+4-1 + +;;; end action SpriteShuffler__postframe__58 + +;;; start action SpriteHider__postframe__61 + + lda #4-1 + sta SpriteHider__9__tmp+0 + +;;; end action SpriteHider__postframe__61 + +;;; start action SpriteHider__postframe__64 + + ldy #0 +SpriteHider__postframe__65____each: + ldx SpriteSlot_sprite_b0,y + + lda HasYpos_ypos_b0,x + cmp #192 + bcc SpriteHider__postframe__66__skip +; swap this sprite slot with slot at end of array + lda SpriteSlot_sprite_b0,y + pha + ldx SpriteHider__9__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__9__tmp+0 +SpriteHider__postframe__66__skip: + + iny + cpy #2 + jne SpriteHider__postframe__65____each +SpriteHider__postframe__65____exit: + +;;; end action SpriteHider__postframe__64 + + jmp FrameLoop__start__4__NextFrame ; loop to next frame + +;;; end action FrameLoop__start__3 + ; start main routine +.segment "VECTORS" +ZeroByte: .byte $00 +Return: .byte $60 +VecNMI: +VecReset: .word __Reset +VecBRK: .word __BRK +.code + +;;; end action Init__main_init__1 +.rodata +__ALIGNORIGIN: +.rodata +SetHorizPos__SetHorizPos__20: + +; SetHorizPos routine +; A = X coordinate +; Y = player number (0 or 1) + sec ; set carry flag + sta WSYNC ; start a new line +: + sbc #15 ; subtract 15 + bcs :- ; branch until negative + eor #7 ; calculate fine offset + asl + asl + asl + asl + sta HMP0,y ; set fine offset + sta RESP0,y ; fix coarse position + sta WSYNC ; won't overrun if X < 150 + + rts + +.assert >(SetHorizPos__SetHorizPos__20) = >(*), error, "SetHorizPos__SetHorizPos__20 crosses a page boundary!" + +.assert (* - SetHorizPos__SetHorizPos__20) <= 22, error, .sprintf("SetHorizPos__SetHorizPos__20 does not fit in 22 bytes, it took %d!", (* - SetHorizPos__SetHorizPos__20)) +.rodata +Kernel2Sprite__kernel__28: + + ldy #192 +Kernel2Sprite__kernel__30__LVScan: + +;;; start action Kernel2Sprite__scanline__31 + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__32__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__32__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y +; .if 0 = 0 ; TODO: configurable? + sta WSYNC +; .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__32__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__32__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite__scanline__31 + +;;; start action Kernel2Sprite__scanline__33 + +;;; end action Kernel2Sprite__scanline__33 + + dey ; next scanline + +;;; start action Kernel2Sprite__scanline__34 + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__35__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__35__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y +; .if 1 = 0 ; TODO: configurable? + sta WSYNC +; .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__35__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__35__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite__scanline__34 + +;;; start action Kernel2Sprite__scanline__36 + +;;; end action Kernel2Sprite__scanline__36 + + dey ; next scanline + bne Kernel2Sprite__kernel__30__LVScan ; repeat until out of lines + + rts + +.assert >(Kernel2Sprite__kernel__28) = >(*), error, "Kernel2Sprite__kernel__28 crosses a page boundary!" +.endscope +Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/music.ecs b/test/ecs/music.ecs new file mode 100644 index 00000000..0351cd9a --- /dev/null +++ b/test/ecs/music.ecs @@ -0,0 +1,189 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" + +component Song + songdata: array of 0..255 +end + +// TODO: merge with SoundChannel +component MusicChannel + duration: 0..255 + note: 0..255 + duty: 0..255 +end + +component MusicPlayer + timer: 0..255 default 255 + channel: [MusicChannel] + songptr: 0..65535 + volume: 0..15 default 15 + tempo: 0..255 default 7 +end + + +// Table of AUDF base values for each note +resource FREQZ --- + .byte 30, 30, 30, 30, 30, 28, 26, 25, 23, 22, 21, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 7, 6, 6, 5, 5, 30, 29, 27, 25, 24, 22, 21, 20, 19, 18, 16, 15, 15, 14, 13, 12, 11, 11, 10, 31, 29, 27, 25, 24, 23, 21, 20, 19, 18, 16, 15, 15 +--- +// Table of duty-cycle bits for each note +resource DUTYZ --- + .byte 247, 247, 247, 247, 1, 73, 219, 1, 219, 73, 0, 219, 181, 85, 85, 85, 181, 219, 247, 1, 73, 181, 0, 73, 219, 17, 219, 17, 219, 73, 247, 85, 247, 1, 85, 247, 73, 247, 181, 17, 1, 0, 247, 247, 0, 1, 17, 73, 181, 0, 17, 0, 1, 85, 247, 73, 0, 181, 73, 1, 0, 247, 247, 0 +--- +// Table of AUDC values for each note +resource TONEZ --- + .byte 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +--- + +system MusicPlayer + on preframe do once + --- + {{!musicpulse}} ; update song + {{!musicframe}} ; update registers + --- + on prekernel do once + --- + {{!musicframe}} ; update registers + --- + on postkernel do once + --- + {{!musicframe}} ; update registers + --- + on postframe do once + --- + {{!musicframe}} ; update registers + --- + // TODO: unroll? + on musicframe do foreach [MusicChannel] + --- +; Update channel pitch in AUDF0 +; 8-bit rotation of duty cycle bits + lda {{get duration}} + beq :++ + lda {{set duty}} + asl + bcc :+ + ora #1 +: sta {{set duty}} + lda {{get note}} + beq :+ +; If next bit is set, add 1 to AUDF0 + adc #0 + sta AUDF0,x +: + --- + on musicpulse do foreach [MusicChannel] + --- +; Decrement the volumes for each channel +; Also decrement next-note timer, fetch next note + lda {{get duration}} + beq :+ + lsr + sta AUDV0,x + dec {{set duration}} +: +--- + on musicpulse do with [MusicPlayer] + --- + lda {{get timer}} + bmi @Done + beq @NextData + dec {{set timer}} + jmp @Done +; Timer ran out, so fetch next note +@NextData: + ldx #0 + lda ({{get songptr}},x) + bmi @LoadDuration +; < $80, play next note + ldx {{get channel}} ; next channel + tay + {{!musicnote}} + inx + txa + and #1 + sta {{set channel}} ; inc next channel + jmp @IncDataPtr +; >= $80, load next duration +@LoadDuration: + cmp #$ff ; $ff = end of song + bne @NoResetTrack + sta {{set timer}} + {{!musicdone}} + jmp @Done +@NoResetTrack: + and #$7f +; asl + sta {{set timer}} ; store duration * 2 +@IncDataPtr: +; increment song pointer + inc {{set songptr 0}} + bne @Done + inc {{set songptr 8}} +@Done: + --- + // TODO: should use "with"? + on musicnote do select all [MusicChannel] + --- +; Play a note +; X = channel (0,1) +; Y = note index (0-63) + lda {{^FREQZ}},y + sta {{base note}},x + lda {{^DUTYZ}},y + sta {{base duty}},x + lda {{^TONEZ}},y + sta AUDC0,x +; TODO: consts? + lda {{get MusicPlayer:tempo}} + sta {{base duration}},x + lda {{get MusicPlayer:volume}} + sta AUDV0,x + --- + on playmusic do foreach [MusicPlayer] limit 1 + --- + lda #<{{arg 0}} + sta {{set songptr 0}} + lda #>{{arg 0}} + sta {{set songptr 8}} + lda #0 + sta {{set timer}} + --- + on stopmusic do foreach [MusicPlayer] limit 1 + --- + lda #$ff + sta {{set timer}} + --- +end + +/// + +demo music + using FrameLoop + using MusicPlayer + entity [MusicChannel] end + entity [MusicChannel] end + entity MusicPlayer [MusicPlayer] + const volume = 10 + const tempo = 31 + end + system music + on musicdone do with [MusicPlayer] + --- + ; TODO: nested exprs + ; {{!playmusic ^SampleMusic}} + ;{{^SampleMusic}} + {{!playmusic SampleMusic}} + --- + on preframeloop do once + --- + {{!musicdone}} + --- + end +end demo + +resource SampleMusic --- + .byte $35,$41,$8a,$37,$43,$8a,$33,$3f,$8a,$30,$3c,$94,$3e,$32,$8a,$3a,$2e,$94,$35,$29,$8a,$37,$2b,$8a,$33,$27,$8a,$30,$24,$94,$32,$26,$8a,$2e,$22,$94,$29,$1d,$8a,$2b,$1f,$8a,$27,$1b,$8a,$24,$18,$94,$1a,$26,$8a,$18,$24,$8a,$17,$23,$8a,$16,$22,$a8,$3a,$35,$ff +--- + diff --git a/test/ecs/music.txt b/test/ecs/music.txt new file mode 100644 index 00000000..8eb467f0 --- /dev/null +++ b/test/ecs/music.txt @@ -0,0 +1,333 @@ +EVENT__start = 1 +EVENT__postframe = 1 +EVENT__preframe = 1 +EVENT__prekernel = 1 +EVENT__postkernel = 1 +EVENT__musicframe = 1 +EVENT__musicpulse = 1 +EVENT__musicnote = 1 +EVENT__playmusic = 1 +EVENT__stopmusic = 1 +EVENT__musicdone = 1 +EVENT__preframeloop = 1 +.scope music +.zeropage +MusicPlayer_timer_b0: +.res 1 +MusicChannel_duration_b0: +.res 1 +.res 1 +MusicChannel_note_b0: +.res 1 +.res 1 +MusicChannel_duty_b0: +.res 1 +.res 1 +MusicPlayer_channel_b0: +.res 1 +MusicPlayer_songptr_b0: +.res 1 +MusicPlayer_songptr_b8: +.res 1 +.code +MusicPlayer_volume_b0: +.byte 10 +MusicPlayer_tempo_b0: +.byte 31 +music__INITDATA: +.byte 255 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +__Start: + +;;; start action Init__main_init__1 + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #10 +: lda music__INITDATA-1,y + sta MusicPlayer_timer_b0-1,y + dey + bne :- + +;;; start action FrameLoop__start__3 + + +;;; start action music__preframeloop__5 + + +;;; start action music__musicdone__7 + + ; TODO: nested exprs + ; + jsr MusicPlayer__playmusic__10 + + ;SampleMusic + + jsr MusicPlayer__playmusic__13 + + +;;; end action music__musicdone__7 + + +;;; end action music__preframeloop__5 + +FrameLoop__start__4__NextFrame: + FRAME_END + + FRAME_START + +;;; start action MusicPlayer__preframe__16 + + +;;; start action MusicPlayer__musicpulse__18 + + ldx #0 +MusicPlayer__musicpulse__19____each: + +; Decrement the volumes for each channel +; Also decrement next-note timer, fetch next note + lda MusicChannel_duration_b0,x + beq :+ + lsr + sta AUDV0,x + dec MusicChannel_duration_b0,x +: + + inx + cpx #2 + jne MusicPlayer__musicpulse__19____each +MusicPlayer__musicpulse__19____exit: + +;;; end action MusicPlayer__musicpulse__18 + +;;; start action MusicPlayer__musicpulse__21 + + lda MusicPlayer_timer_b0 + bmi MusicPlayer__musicpulse__23__Done + beq MusicPlayer__musicpulse__23__NextData + dec MusicPlayer_timer_b0 + jmp MusicPlayer__musicpulse__23__Done +; Timer ran out, so fetch next note +MusicPlayer__musicpulse__23__NextData: + ldx #0 + lda (MusicPlayer_songptr_b0,x) + bmi MusicPlayer__musicpulse__23__LoadDuration +; < $80, play next note + ldx MusicPlayer_channel_b0 ; next channel + tay + +;;; start action MusicPlayer__musicnote__24 + +; Play a note +; X = channel (0,1) +; Y = note index (0-63) + lda FREQZ,y + sta MusicChannel_note_b0,x + lda DUTYZ,y + sta MusicChannel_duty_b0,x + lda TONEZ,y + sta AUDC0,x +; TODO: consts? + lda MusicPlayer_tempo_b0 + sta MusicChannel_duration_b0,x + lda MusicPlayer_volume_b0 + sta AUDV0,x + +;;; end action MusicPlayer__musicnote__24 + + inx + txa + and #1 + sta MusicPlayer_channel_b0 ; inc next channel + jmp MusicPlayer__musicpulse__23__IncDataPtr +; >= $80, load next duration +MusicPlayer__musicpulse__23__LoadDuration: + cmp #$ff ; $ff = end of song + bne MusicPlayer__musicpulse__23__NoResetTrack + sta MusicPlayer_timer_b0 + +;;; start action music__musicdone__27 + + ; TODO: nested exprs + ; + jsr MusicPlayer__playmusic__10 + + ;SampleMusic + + jsr MusicPlayer__playmusic__13 + + +;;; end action music__musicdone__27 + + jmp MusicPlayer__musicpulse__23__Done +MusicPlayer__musicpulse__23__NoResetTrack: + and #$7f +; asl + sta MusicPlayer_timer_b0 ; store duration * 2 +MusicPlayer__musicpulse__23__IncDataPtr: +; increment song pointer + inc MusicPlayer_songptr_b0 + bne MusicPlayer__musicpulse__23__Done + inc MusicPlayer_songptr_b8 +MusicPlayer__musicpulse__23__Done: + +;;; end action MusicPlayer__musicpulse__21 + ; update song + + jsr MusicPlayer__musicframe__36 + ; update registers + +;;; end action MusicPlayer__preframe__16 + + +;;; start action MusicPlayer__prekernel__39 + + + jsr MusicPlayer__musicframe__36 + ; update registers + +;;; end action MusicPlayer__prekernel__39 + + KERNEL_START + + KERNEL_END + +;;; start action MusicPlayer__postkernel__44 + + + jsr MusicPlayer__musicframe__36 + ; update registers + +;;; end action MusicPlayer__postkernel__44 + + +;;; start action FrameLoop__postframe__49 + + lsr SWCHB ; test Game Reset switch + bcs FrameLoop__postframe__50__NoStart + +FrameLoop__postframe__50__NoStart: + +;;; end action FrameLoop__postframe__49 + +;;; start action MusicPlayer__postframe__51 + + + jsr MusicPlayer__musicframe__36 + ; update registers + +;;; end action MusicPlayer__postframe__51 + + jmp FrameLoop__start__4__NextFrame ; loop to next frame + +;;; end action FrameLoop__start__3 + ; start main routine +.segment "VECTORS" +ZeroByte: .byte $00 +Return: .byte $60 +VecNMI: +VecReset: .word __Reset +VecBRK: .word __BRK +.code + +;;; end action Init__main_init__1 +.rodata +__ALIGNORIGIN: +.rodata +MusicPlayer__playmusic__10: + + lda #<^SampleMusic + sta MusicPlayer_songptr_b0 + lda #>^SampleMusic + sta MusicPlayer_songptr_b8 + lda #0 + sta MusicPlayer_timer_b0 + + rts +.rodata +MusicPlayer__playmusic__13: + + lda #SampleMusic + sta MusicPlayer_songptr_b8 + lda #0 + sta MusicPlayer_timer_b0 + + rts +.rodata +MusicPlayer__musicframe__36: + + ldx #0 +MusicPlayer__musicframe__37____each: + +; Update channel pitch in AUDF0 +; 8-bit rotation of duty cycle bits + lda MusicChannel_duration_b0,x + beq :++ + lda MusicChannel_duty_b0,x + asl + bcc :+ + ora #1 +: sta MusicChannel_duty_b0,x + lda MusicChannel_note_b0,x + beq :+ +; If next bit is set, add 1 to AUDF0 + adc #0 + sta AUDF0,x +: + + inx + cpx #2 + jne MusicPlayer__musicframe__37____each +MusicPlayer__musicframe__37____exit: + + rts +SampleMusic: + +;;; start action SampleMusic__SampleMusic__56 + + .byte $35,$41,$8a,$37,$43,$8a,$33,$3f,$8a,$30,$3c,$94,$3e,$32,$8a,$3a,$2e,$94,$35,$29,$8a,$37,$2b,$8a,$33,$27,$8a,$30,$24,$94,$32,$26,$8a,$2e,$22,$94,$29,$1d,$8a,$2b,$1f,$8a,$27,$1b,$8a,$24,$18,$94,$1a,$26,$8a,$18,$24,$8a,$17,$23,$8a,$16,$22,$a8,$3a,$35,$ff + +;;; end action SampleMusic__SampleMusic__56 + +FREQZ: + +;;; start action FREQZ__FREQZ__58 + + .byte 30, 30, 30, 30, 30, 28, 26, 25, 23, 22, 21, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 7, 6, 6, 5, 5, 30, 29, 27, 25, 24, 22, 21, 20, 19, 18, 16, 15, 15, 14, 13, 12, 11, 11, 10, 31, 29, 27, 25, 24, 23, 21, 20, 19, 18, 16, 15, 15 + +;;; end action FREQZ__FREQZ__58 + +DUTYZ: + +;;; start action DUTYZ__DUTYZ__60 + + .byte 247, 247, 247, 247, 1, 73, 219, 1, 219, 73, 0, 219, 181, 85, 85, 85, 181, 219, 247, 1, 73, 181, 0, 73, 219, 17, 219, 17, 219, 73, 247, 85, 247, 1, 85, 247, 73, 247, 181, 17, 1, 0, 247, 247, 0, 1, 17, 73, 181, 0, 17, 0, 1, 85, 247, 73, 0, 181, 73, 1, 0, 247, 247, 0 + +;;; end action DUTYZ__DUTYZ__60 + +TONEZ: + +;;; start action TONEZ__TONEZ__62 + + .byte 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 + +;;; end action TONEZ__TONEZ__62 + +.endscope +music__Start = music::__Start \ No newline at end of file diff --git a/test/ecs/random.ecs b/test/ecs/random.ecs new file mode 100644 index 00000000..a4c82f9e --- /dev/null +++ b/test/ecs/random.ecs @@ -0,0 +1,35 @@ + +component Random8 + seed: 0..255 default 1 // TODO: default = 1, or seed? +end + +system Random + on nextrand8 do foreach [Random8] + --- + lda {{ [GRP0] @@ -122,7 +125,7 @@ Digit5 = {{$10}} sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec @LoopCount ; go to next line - bpl @BigLoop ; repeat until < 0 + bpl :- ; repeat until < 0 lda #0 ; clear the sprite registers sta WSYNC @@ -147,18 +150,26 @@ resource FontTable --- system Kernel2Digit locals 3 - on kernel do select [BCDScore2,PFColor] + on preframe do once +--- + lda #0 + sta {{$1}} + sta {{$2}} +--- + on kernel do select [BCDScore2] --- lda #$02 sta CTRLPF ; TODO: should be constants ; and it's wrong, too! - lda {{1 inx {{!compute2digit 1}} +.else + {{!compute2digit 0}} +.endif ; playfield dec {{$0}} - bpl @Loop + jpl @Loop ; dex ; stx PF1 --- @@ -242,12 +257,15 @@ resource FontTablePFFancy --- system BCDMath locals 1 - on AddBCD4 do with [BCDScore6] + on AddBCD4 do once --- .ifnblank {{arg 0}} lda #<{{arg 0}} ldy #>{{arg 0}} .endif +--- + on AddBCD4 do with [BCDScore6] +--- ; Adds value to 6-BCD-digit score. ; A = 1st BCD digit ; Y = 2nd BCD digit @@ -262,14 +280,54 @@ system BCDMath adc #0 sta {{set digits 16}} cld ; exit BCD mode +--- + on AddBCD2 do once +--- +.ifnblank {{arg 0}} + lda #<{{arg 0}} +.endif +--- + on SubBCD2 do once +--- +.ifnblank {{arg 0}} + lda #<{{arg 0}} +.endif +--- + on AddBCD2 do with [BCDScore2] +--- + sed ; enter BCD mode + clc ; clear carry + adc {{get digits}} + sta {{set digits}} + cld ; exit BCD mode + bcc :+ + lda #$99 + sta {{set digits}} +: +--- + on SubBCD2 do with [BCDScore2] +--- +; TODO? + tay + lda {{get digits}} + sty {{set digits}} + sed ; enter BCD mode + sec ; set carry + sbc {{get digits}} + sta {{set digits}} + cld ; exit BCD mode + bcs :+ + lda #0 + sta {{set digits}} +: --- end demo Main using FrameLoop - using Kernel6Digit, FontTable - using Kernel2Digit, FontTablePF + using Kernel6Digit + using Kernel2Digit using JoyButton, BCDMath entity [Player,BCDScore6,PFColor,BGColor] @@ -278,13 +336,13 @@ demo Main init bgcolor = $02 end - entity [BCDScore2,PFColor] + entity [BCDScore2] init digits = 0x24 - init pfcolor = $ce + init scorecolor = $ce end - entity [BCDScore2,PFColor] + entity [BCDScore2] init digits = 0x56 - init pfcolor = $3e + init scorecolor = $3e end system IncScore diff --git a/test/ecs/score.txt b/test/ecs/score.txt index 7ab5b81b..1e50ac22 100644 --- a/test/ecs/score.txt +++ b/test/ecs/score.txt @@ -1,12 +1,12 @@ EVENT__start = 1 +EVENT__postframe = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__FontTable = 1 EVENT__compute2digit = 1 EVENT__fetchdigit = 1 -EVENT__FontTablePF = 1 -EVENT__postframe = 1 EVENT__AddBCD4 = 1 +EVENT__AddBCD2 = 1 +EVENT__SubBCD2 = 1 EVENT__joybutton = 1 .scope Main .zeropage @@ -18,13 +18,14 @@ BCDScore6_digits_b16: .res 1 PFColor_pfcolor_b0: .res 1 -.res 1 -.res 1 BGColor_bgcolor_b0: .res 1 BCDScore2_digits_b0: .res 1 .res 1 +BCDScore2_scorecolor_b0: +.res 1 +.res 1 TEMP: Kernel6Digit__2__tmp: .res 1 @@ -42,7 +43,7 @@ Kernel6Digit__2__tmp: .res 1 .res 1 .res 1 -Kernel2Digit__4__tmp: +Kernel2Digit__3__tmp: .res 1 .res 1 .res 1 @@ -52,11 +53,11 @@ Main__INITDATA: .byte 52 .byte 18 .byte 60 -.byte 206 -.byte 62 .byte 2 .byte 36 .byte 86 +.byte 206 +.byte 62 __Start: ;;; start action Init__main_init__1 @@ -77,7 +78,10 @@ __BRK: ;;; start action FrameLoop__start__3 + FrameLoop__start__4__NextFrame: + FRAME_END + FRAME_START ;;; start action Kernel6Digit__preframe__5 @@ -130,9 +134,18 @@ Kernel6Digit__preframe__7__Loop: ;;; end action Kernel6Digit__preframe__5 +;;; start action Kernel2Digit__preframe__8 + + lda #0 + sta Kernel2Digit__3__tmp+1 + sta Kernel2Digit__3__tmp+2 + +;;; end action Kernel2Digit__preframe__8 + + KERNEL_START -;;; start action Kernel6Digit__kernel__8 +;;; start action Kernel6Digit__kernel__10 lda PFColor_pfcolor_b0 sta COLUP0 @@ -156,190 +169,56 @@ Kernel6Digit__preframe__7__Loop: sta VDELP0 sta VDELP1 -;;; end action Kernel6Digit__kernel__8 +;;; end action Kernel6Digit__kernel__10 -;;; start action Kernel6Digit__kernel__11 + jsr Kernel6Digit__kernel__13 -; Display the resulting 48x8 bitmap -; using the Digit0-5 pointers. -Kernel6Digit__kernel__13__LoopCount = Kernel6Digit__2__tmp+12 -Kernel6Digit__kernel__13__Temp = Kernel6Digit__2__tmp+13 - - lda BGColor_bgcolor_b0 - sta WSYNC - sta COLUBK - lda #7 - sta Kernel6Digit__kernel__13__LoopCount - SLEEPR 20 ; TODO? -Kernel6Digit__kernel__13__BigLoop: - ldy Kernel6Digit__kernel__13__LoopCount ; counts backwards - lda (Digit0),y ; load B0 (1st sprite byte) - sta GRP0 ; B0 -> [GRP0] - lda (Digit1),y ; load B1 -> A - sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 - sta WSYNC ; sync to next scanline - lda (Digit2),y ; load B2 -> A - sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 - lda (Digit5),y ; load B5 -> A - sta Kernel6Digit__kernel__13__Temp ; B5 -> temp - lda (Digit4),y ; load B4 - tax ; -> X - lda (Digit3),y ; load B3 -> A - ldy Kernel6Digit__kernel__13__Temp ; load B5 -> Y - sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 - stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 - sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 - sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 - dec Kernel6Digit__kernel__13__LoopCount ; go to next line - bpl Kernel6Digit__kernel__13__BigLoop ; repeat until < 0 - - lda #0 ; clear the sprite registers - sta WSYNC - sta GRP0 - sta GRP1 - sta GRP0 - sta GRP1 - sta COLUBK - -;;; end action Kernel6Digit__kernel__11 - -;;; start action Kernel2Digit__kernel__14 +;;; start action Kernel2Digit__kernel__16 lda #$02 sta CTRLPF ; TODO: should be constants ; and it's wrong, too! - lda PFColor_pfcolor_b0+0 + lda BCDScore2_scorecolor_b0+0 sta COLUP0 - lda PFColor_pfcolor_b0+1 + lda BCDScore2_scorecolor_b0+1 sta COLUP1 -;;; end action Kernel2Digit__kernel__14 +;;; end action Kernel2Digit__kernel__16 -;;; start action Kernel2Digit__kernel__17 - - lda #7 - sta Kernel2Digit__4__tmp+0 -Kernel2Digit__kernel__19__Loop: - ldx #0 - sta WSYNC - -;;; start action Kernel2Digit__compute2digit__20 - - lda Kernel2Digit__4__tmp+1 ; load 1st pf - sta PF1 ; store 1st pf -; first digit - lda BCDScore2_digits_b0 + 0 - pha - and #$0f - asl - asl - asl - -;;; start action Kernel2Digit__fetchdigit__22 - - adc Kernel2Digit__4__tmp+0 - tay - ; TODO: select your own? - lda FontTablePF,y - -;;; end action Kernel2Digit__fetchdigit__22 - - and #$0f - ldy Kernel2Digit__4__tmp+2 ; load 2nd pf - sta Kernel2Digit__4__tmp+1 + 0 -; second digit - pla - and #$f0 - lsr - sty PF1 ; store 2nd pf - -;;; start action Kernel2Digit__fetchdigit__24 - - adc Kernel2Digit__4__tmp+0 - tay - ; TODO: select your own? - lda FontTablePF,y - -;;; end action Kernel2Digit__fetchdigit__24 - - and #$f0 - ora Kernel2Digit__4__tmp+1 + 0 - sta Kernel2Digit__4__tmp+1 + 0 - -;;; end action Kernel2Digit__compute2digit__20 - - inx - -;;; start action Kernel2Digit__compute2digit__26 - - lda Kernel2Digit__4__tmp+1 ; load 1st pf - sta PF1 ; store 1st pf -; first digit - lda BCDScore2_digits_b0 + 1 - pha - and #$0f - asl - asl - asl - -;;; start action Kernel2Digit__fetchdigit__28 - - adc Kernel2Digit__4__tmp+0 - tay - ; TODO: select your own? - lda FontTablePF,y - -;;; end action Kernel2Digit__fetchdigit__28 - - and #$0f - ldy Kernel2Digit__4__tmp+2 ; load 2nd pf - sta Kernel2Digit__4__tmp+1 + 1 -; second digit - pla - and #$f0 - lsr - sty PF1 ; store 2nd pf - -;;; start action Kernel2Digit__fetchdigit__30 - - adc Kernel2Digit__4__tmp+0 - tay - ; TODO: select your own? - lda FontTablePF,y - -;;; end action Kernel2Digit__fetchdigit__30 - - and #$f0 - ora Kernel2Digit__4__tmp+1 + 1 - sta Kernel2Digit__4__tmp+1 + 1 - -;;; end action Kernel2Digit__compute2digit__26 - -; playfield - dec Kernel2Digit__4__tmp+0 - bpl Kernel2Digit__kernel__19__Loop -; dex -; stx PF1 - -;;; end action Kernel2Digit__kernel__17 + jsr Kernel2Digit__kernel__19 KERNEL_END -;;; start action JoyButton__postframe__32 + +;;; start action FrameLoop__postframe__40 + + lsr SWCHB ; test Game Reset switch + bcs FrameLoop__postframe__41__NoStart + +FrameLoop__postframe__41__NoStart: + +;;; end action FrameLoop__postframe__40 + +;;; start action JoyButton__postframe__42 lda INPT4 ;read button input - bmi JoyButton__postframe__34__NotPressed + bmi JoyButton__postframe__44__NotPressed -;;; start action IncScore__joybutton__35 +;;; start action IncScore__joybutton__45 -;;; start action BCDMath__AddBCD4__38 +;;; start action BCDMath__AddBCD4__48 .ifnblank $0210 lda #<$0210 ldy #>$0210 .endif + +;;; end action BCDMath__AddBCD4__48 + +;;; start action BCDMath__AddBCD4__50 + ; Adds value to 6-BCD-digit score. ; A = 1st BCD digit ; Y = 2nd BCD digit @@ -355,32 +234,247 @@ Kernel2Digit__kernel__19__Loop: sta BCDScore6_digits_b16 cld ; exit BCD mode -;;; end action BCDMath__AddBCD4__38 +;;; end action BCDMath__AddBCD4__50 -;;; end action IncScore__joybutton__35 +;;; end action IncScore__joybutton__45 -JoyButton__postframe__34__NotPressed: +JoyButton__postframe__44__NotPressed: -;;; end action JoyButton__postframe__32 +;;; end action JoyButton__postframe__42 - FRAME_END - jmp FrameLoop__start__4__NextFrame ; loop to next frame ;;; end action FrameLoop__start__3 ; start main routine .segment "VECTORS" -Return: .word $6060 +ZeroByte: .byte $00 +Return: .byte $60 VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK +VecReset: .word __Reset +VecBRK: .word __BRK +.code ;;; end action Init__main_init__1 +.rodata +__ALIGNORIGIN: +.rodata +Kernel6Digit__kernel__13: +; Display the resulting 48x8 bitmap +; using the Digit0-5 pointers. +Kernel6Digit__kernel__15__LoopCount = Kernel6Digit__2__tmp+12 +Kernel6Digit__kernel__15__Temp = Kernel6Digit__2__tmp+13 + + lda BGColor_bgcolor_b0 + sta WSYNC + sta COLUBK + lda #7 + sta Kernel6Digit__kernel__15__LoopCount + SLEEPR 20 ; TODO? +: + ldy Kernel6Digit__kernel__15__LoopCount ; counts backwards + lda (Digit0),y ; load B0 (1st sprite byte) + sta GRP0 ; B0 -> [GRP0] + lda (Digit1),y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + sta WSYNC ; sync to next scanline + lda (Digit2),y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda (Digit5),y ; load B5 -> A + sta Kernel6Digit__kernel__15__Temp ; B5 -> temp + lda (Digit4),y ; load B4 + tax ; -> X + lda (Digit3),y ; load B3 -> A + ldy Kernel6Digit__kernel__15__Temp ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec Kernel6Digit__kernel__15__LoopCount ; go to next line + bpl :- ; repeat until < 0 + + lda #0 ; clear the sprite registers + sta WSYNC + sta GRP0 + sta GRP1 + sta GRP0 + sta GRP1 + sta COLUBK + + rts + +.assert >(Kernel6Digit__kernel__13) = >(*), error, "Kernel6Digit__kernel__13 crosses a page boundary!" + +.assert (* - Kernel6Digit__kernel__13) <= 72, error, .sprintf("Kernel6Digit__kernel__13 does not fit in 72 bytes, it took %d!", (* - Kernel6Digit__kernel__13)) + +.if <(* - __ALIGNORIGIN) > 256-98 +.align $100 +.endif +.rodata +Kernel2Digit__kernel__19: + + lda #7 + sta Kernel2Digit__3__tmp+0 +Kernel2Digit__kernel__21__Loop: + ldx #0 + sta WSYNC + +;;; start action Kernel2Digit__compute2digit__22 + + lda Kernel2Digit__3__tmp+1 ; load 1st pf + sta PF1 ; store 1st pf +; first digit + lda BCDScore2_digits_b0 + 0 + pha + and #$0f + asl + asl + asl + +;;; start action Kernel2Digit__fetchdigit__24 + + adc Kernel2Digit__3__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit__fetchdigit__24 + + and #$0f + ldy Kernel2Digit__3__tmp+2 ; load 2nd pf + sta Kernel2Digit__3__tmp+1 + 0 +; second digit + pla + and #$f0 + lsr + sty PF1 ; store 2nd pf + +;;; start action Kernel2Digit__fetchdigit__26 + + adc Kernel2Digit__3__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit__fetchdigit__26 + + and #$f0 + ora Kernel2Digit__3__tmp+1 + 0 + sta Kernel2Digit__3__tmp+1 + 0 + +;;; end action Kernel2Digit__compute2digit__22 + +.if 2>1 + inx + +;;; start action Kernel2Digit__compute2digit__28 + + lda Kernel2Digit__3__tmp+1 ; load 1st pf + sta PF1 ; store 1st pf +; first digit + lda BCDScore2_digits_b0 + 1 + pha + and #$0f + asl + asl + asl + +;;; start action Kernel2Digit__fetchdigit__30 + + adc Kernel2Digit__3__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit__fetchdigit__30 + + and #$0f + ldy Kernel2Digit__3__tmp+2 ; load 2nd pf + sta Kernel2Digit__3__tmp+1 + 1 +; second digit + pla + and #$f0 + lsr + sty PF1 ; store 2nd pf + +;;; start action Kernel2Digit__fetchdigit__32 + + adc Kernel2Digit__3__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit__fetchdigit__32 + + and #$f0 + ora Kernel2Digit__3__tmp+1 + 1 + sta Kernel2Digit__3__tmp+1 + 1 + +;;; end action Kernel2Digit__compute2digit__28 + +.else + +;;; start action Kernel2Digit__compute2digit__34 + + lda Kernel2Digit__3__tmp+1 ; load 1st pf + sta PF1 ; store 1st pf +; first digit + lda BCDScore2_digits_b0 + 0 + pha + and #$0f + asl + asl + asl + +;;; start action Kernel2Digit__fetchdigit__36 + + adc Kernel2Digit__3__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit__fetchdigit__36 + + and #$0f + ldy Kernel2Digit__3__tmp+2 ; load 2nd pf + sta Kernel2Digit__3__tmp+1 + 0 +; second digit + pla + and #$f0 + lsr + sty PF1 ; store 2nd pf + +;;; start action Kernel2Digit__fetchdigit__38 + + adc Kernel2Digit__3__tmp+0 + tay + ; TODO: select your own? + lda FontTablePF,y + +;;; end action Kernel2Digit__fetchdigit__38 + + and #$f0 + ora Kernel2Digit__3__tmp+1 + 0 + sta Kernel2Digit__3__tmp+1 + 0 + +;;; end action Kernel2Digit__compute2digit__34 + +.endif +; playfield + dec Kernel2Digit__3__tmp+0 + jpl Kernel2Digit__kernel__21__Loop +; dex +; stx PF1 + + rts + +.assert >(Kernel2Digit__kernel__19) = >(*), error, "Kernel2Digit__kernel__19 crosses a page boundary!" + +.assert (* - Kernel2Digit__kernel__19) <= 98, error, .sprintf("Kernel2Digit__kernel__19 does not fit in 98 bytes, it took %d!", (* - Kernel2Digit__kernel__19)) FontTable: -;;; start action FontTable__FontTable__41 +;;; start action FontTable__FontTable__53 ; Font table for digits 0-9 (8x8 pixels) ;;{w:8,h:8,count:10,brev:1,flip:1};; @@ -390,11 +484,11 @@ FontTable: .byte $00,$3c,$66,$66,$7c,$60,$66,$3c,$00,$18,$18,$18,$18,$0c,$66,$7e .byte $00,$3c,$66,$66,$3c,$66,$66,$3c,$00,$3c,$66,$06,$3e,$66,$66,$3c -;;; end action FontTable__FontTable__41 +;;; end action FontTable__FontTable__53 FontTablePF: -;;; start action FontTablePF__FontTablePF__43 +;;; start action FontTablePF__FontTablePF__55 ; Font table for digits 0-9 (4x8 pixels) ;;{w:8,h:8,count:10,brev:1,flip:1};; @@ -410,7 +504,7 @@ FontTablePF: .byte $00,$00,$EE,$22,$EE,$AA,$EE,$00 ;; -;;; end action FontTablePF__FontTablePF__43 +;;; end action FontTablePF__FontTablePF__55 .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/sound.ecs b/test/ecs/sound.ecs new file mode 100644 index 00000000..283751a7 --- /dev/null +++ b/test/ecs/sound.ecs @@ -0,0 +1,105 @@ + +//#resource "vcs-ca65.h" + +import "vcslib.ecs" + +component SoundEffect + duration: 0..255 + sounddata: array of 0..255 +end + +component SoundChannel + sfx: [SoundEffect] + timer: 0..255 +end + +// TODO +component SoundPriority + priority: 0..15 +end + +system SoundEngine + locals 3 + on preframe do + join [SoundChannel] + with [SoundEffect] + --- + lda {{base timer}},y + jeq @nosound + sec + sbc #1 + sta {{base timer}},y + pha + lda {{sounddata}} + sta {{$1}} ; save pointer to sound data + sty {{$2}} ; save Y (sound channel #) + pla + tay + lda ({{$0}}),y ; get sound data + bpl @setfreq ; hi bit clear = just freq + ldy {{$2}} + lsr ; right shift (/ 2) + bcs @setvol ; lo bit set = volume + sta AUDC0,y ; lo bit clear = control + lsr ; also set freq (/ 2) +@setfreq: + ldy {{$2}} + sta AUDF0,y ; set frequency + jmp @done +@nosound: + lda #0 +@setvol: + sta AUDV0,y ; set volume +@done: + --- + // TODO: need to pass sound entity as arg + on playsound do select [SoundChannel] + --- +; arg 0 = sound channel + ldy #{{arg 0}} +; arg 1 = sound effect # + lda #{{arg 1}} + sta {{base sfx}},y + tax + --- + // TODO: shouldn't need to split up like this... + on playsound do select [SoundEffect] + --- + lda {{base duration}},x + --- + on playsound do select [SoundChannel] + --- + sta {{base timer}},y +; arg 2 = base volume + lda #{{arg 2}} + sta AUDV0,y + --- +end + +// TODO: default entities? + + +demo SoundDemo + using FrameLoop, SoundEngine + + entity SFXScore [SoundEffect] + const duration = 11 + const sounddata = [ + $02,$03,$04,$08,$10,$20,$10,$20,$10,$08, + $a8] + end + + // TODO: make sfx have priority? + entity SFX1 [SoundChannel] end + entity SFX2 [SoundChannel] end + + system Test + on preframeloop do once + --- + {{!playsound 0 0 15}} + --- + end +end demo + diff --git a/test/ecs/sound.txt b/test/ecs/sound.txt new file mode 100644 index 00000000..ec688807 --- /dev/null +++ b/test/ecs/sound.txt @@ -0,0 +1,174 @@ +EVENT__start = 1 +EVENT__postframe = 1 +EVENT__preframe = 1 +EVENT__playsound = 1 +EVENT__preframeloop = 1 +.scope SoundDemo +.zeropage +SoundChannel_sfx_b0: +.res 1 +.res 1 +SoundChannel_timer_b0: +.res 1 +.res 1 +TEMP: +SoundEngine__2__tmp: +.res 1 +.res 1 +.res 1 +.code +SoundEffect_duration_b0: +.byte 11 +SoundEffect_sounddata_b0: +.byte SoundEffect_sounddata_e0_b0 +SoundEffect_sounddata_e0_b0: +.byte 2 +.byte 3 +.byte 4 +.byte 8 +.byte 16 +.byte 32 +.byte 16 +.byte 32 +.byte 16 +.byte 8 +.byte 168 +SoundDemo__INITDATA: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +__Start: + +;;; start action Init__main_init__1 + +.include "vcs-ca65.h" +.macpack longbranch +.define PAL 0 +__NMI: +__Reset: +__BRK: + CLEAN_START + + ldy #4 +: lda SoundDemo__INITDATA-1,y + sta SoundChannel_sfx_b0-1,y + dey + bne :- + +;;; start action FrameLoop__start__3 + + +;;; start action Test__preframeloop__5 + + +;;; start action SoundEngine__playsound__7 + +; arg 0 = sound channel + ldy #0 +; arg 1 = sound effect # + lda #0 + sta SoundChannel_sfx_b0,y + tax + +;;; end action SoundEngine__playsound__7 + +;;; start action SoundEngine__playsound__10 + + lda SoundEffect_duration_b0,x + +;;; end action SoundEngine__playsound__10 + +;;; start action SoundEngine__playsound__13 + + sta SoundChannel_timer_b0,y +; arg 2 = base volume + lda #15 + sta AUDV0,y + +;;; end action SoundEngine__playsound__13 + + +;;; end action Test__preframeloop__5 + +FrameLoop__start__4__NextFrame: + FRAME_END + + FRAME_START + +;;; start action SoundEngine__preframe__16 + + ldy #0 +SoundEngine__preframe__17____each: + ldx SoundChannel_sfx_b0,y + + lda SoundChannel_timer_b0,y + jeq SoundEngine__preframe__18__nosound + sec + sbc #1 + sta SoundChannel_timer_b0,y + pha + lda SoundEffect_sounddata_b0,x + sta SoundEngine__2__tmp+0 + lda SoundEffect_sounddata_b8,x + sta SoundEngine__2__tmp+1 ; save pointer to sound data + sty SoundEngine__2__tmp+2 ; save Y (sound channel #) + pla + tay + lda (SoundEngine__2__tmp+0),y ; get sound data + bpl SoundEngine__preframe__18__setfreq ; hi bit clear = just freq + ldy SoundEngine__2__tmp+2 + lsr ; right shift (/ 2) + bcs SoundEngine__preframe__18__setvol ; lo bit set = volume + sta AUDC0,y ; lo bit clear = control + lsr ; also set freq (/ 2) +SoundEngine__preframe__18__setfreq: + ldy SoundEngine__2__tmp+2 + sta AUDF0,y ; set frequency + jmp SoundEngine__preframe__18__done +SoundEngine__preframe__18__nosound: + lda #0 +SoundEngine__preframe__18__setvol: + sta AUDV0,y ; set volume +SoundEngine__preframe__18__done: + + iny + cpy #2 + jne SoundEngine__preframe__17____each +SoundEngine__preframe__17____exit: + +;;; end action SoundEngine__preframe__16 + + + KERNEL_START + + KERNEL_END + + +;;; start action FrameLoop__postframe__19 + + lsr SWCHB ; test Game Reset switch + bcs FrameLoop__postframe__20__NoStart + +FrameLoop__postframe__20__NoStart: + +;;; end action FrameLoop__postframe__19 + + jmp FrameLoop__start__4__NextFrame ; loop to next frame + +;;; end action FrameLoop__start__3 + ; start main routine +.segment "VECTORS" +ZeroByte: .byte $00 +Return: .byte $60 +VecNMI: +VecReset: .word __Reset +VecBRK: .word __BRK +.code + +;;; end action Init__main_init__1 + +.endscope +SoundDemo__Start = SoundDemo::__Start \ No newline at end of file diff --git a/test/ecs/sprites.ecs b/test/ecs/sprites.ecs index 1bbafcdc..40adb7a9 100644 --- a/test/ecs/sprites.ecs +++ b/test/ecs/sprites.ecs @@ -8,8 +8,8 @@ component Bitmap height: 0..255 end +// TODO: remove? component HasBitmap - bitmap: [Bitmap] end component Colormap @@ -21,6 +21,7 @@ component HasColormap end component Sprite + bitmap: [Bitmap] plyrflags: 0..63 end @@ -32,218 +33,12 @@ component HasYpos ypos: 0..255 end -component SpriteSlot - sprite: [Sprite,HasBitmap,HasColormap,HasYpos] -end - component Missile + index: 2..3 end -system Kernel2Sprite - locals 13 - on preframe do with [KernelSection] ---- -.define KLINES {{Bitmap:bitmapdata}},x - sbc #0 - sta {{$2}},y -; get bitmap height - lda {{Colormap:colormapdata}},x - sbc #0 - sta {{$6}},y -; save ypos - ldx {{$12}} ; restore X - lda {{ 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 {{data}} - sta {{$1}} ---- - on scanline do once ---- -.if {{arg 0}} = 0 - lda ({{local 0}}),y - tax -.endif ---- - on scanline do once ---- -.if {{arg 0}} = 1 - lda ({{local 0}}),y - sta $00,x -.endif ---- - on postframe do once ---- - lda #0 - sta PF0 - sta PF1 - sta PF2 ---- -end - -system SetXPos - on preframe do once ---- - sta HMCLR ---- - on preframe do join [SpriteSlot] with [HasXpos] - limit 2 ---- - lda {{(Bitmap_bitmapdata_e1_b0+31) -.byte >(Bitmap_bitmapdata_e2_b0+31) -Bitmap_bitmapdata_e1_b0: -.byte 1 -.byte 1 -.byte 3 -.byte 7 -.byte 15 -.byte 31 -.byte 63 -.byte 127 -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_b0: -.byte <(Colormap_colormapdata_e3_b0+31) -Colormap_colormapdata_b8: -.byte >(Colormap_colormapdata_e3_b0+31) -Colormap_colormapdata_e3_b0: -.byte 6 -.byte 3 -.byte 6 -.byte 9 -.byte 12 -.byte 14 -.byte 31 -.byte 63 -Sprite_plyrflags_b0: -.byte 0 -.byte 3 -.byte 2 -.byte 0 -Main__INITDATA: -.byte 1 -.byte 0 -.byte 1 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -.byte 50 -.byte 100 -.byte 80 -.byte 40 -.byte 150 -.byte 60 -.byte 90 -.byte 150 -.byte 0 -.byte 1 -.byte 2 -.byte 3 -__Start: - -;;; start action Init__main_init__1 - -.include "vcs-ca65.h" -.macpack longbranch -.define PAL 0 -__NMI: -__Reset: -__BRK: - CLEAN_START - - ldy #20 -: lda Main__INITDATA-1,y - sta HasBitmap_bitmap_b0-1,y - dey - bne :- - -;;; start action FrameLoop__start__3 - -FrameLoop__start__4__NextFrame: - FRAME_START - -;;; start action Kernel2Sprite__preframe__5 - -.define KLINES #192 -.define KPAD 32 -; set height to zero in case no sprites - lda #0 - sta Kernel2Sprite__2__tmp+8 - sta Kernel2Sprite__2__tmp+9 -; set temp value so we don't read bitmap from h/w registers - lda #$F0 - sta Kernel2Sprite__2__tmp+2 - sta Kernel2Sprite__2__tmp+3 - sta Kernel2Sprite__2__tmp+6 - sta Kernel2Sprite__2__tmp+7 - -;;; end action Kernel2Sprite__preframe__5 - -;;; start action Kernel2Sprite__preframe__8 - - ldy #0 -Kernel2Sprite__preframe__9____each: - ldx SpriteSlot_sprite_b0,y - -; flags set according to sprite slot value -; skip sprite if negative - jmi Kernel2Sprite__preframe__10__nosprite -; 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__2__tmp+11 -; calculate bitmap pointer - stx Kernel2Sprite__2__tmp+12 ; save X (Sprite index) - lda HasBitmap_bitmap_b0,x ; deref bitmap - tax - lda Bitmap_bitmapdata_b0,x - sec - sbc Kernel2Sprite__2__tmp+11 - sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index - lda Bitmap_bitmapdata_b8,x - sbc #0 - sta Kernel2Sprite__2__tmp+2,y -; get bitmap height - lda Bitmap_height_b0,x - sta Kernel2Sprite__2__tmp+8,y -; calculate colormap pointer - ldx Kernel2Sprite__2__tmp+12 ; restore X - lda HasColormap_colormap_b0,x ; deref colormap - tax - lda Colormap_colormapdata_b0,x - sec - sbc Kernel2Sprite__2__tmp+11 - sta Kernel2Sprite__2__tmp+4,y - lda Colormap_colormapdata_b8,x - sbc #0 - sta Kernel2Sprite__2__tmp+6,y -; save ypos - ldx Kernel2Sprite__2__tmp+12 ; restore X - lda HasYpos_ypos_b0,x - sta Kernel2Sprite__2__tmp+10,y -Kernel2Sprite__preframe__10__nosprite: - - iny - cpy #2 - jne Kernel2Sprite__preframe__9____each -Kernel2Sprite__preframe__9____exit: - -;;; end action Kernel2Sprite__preframe__8 - -;;; start action Kernel2Sprite__preframe__11 - -; shuffle pointers into (MSB, LSB) byte order -; L0 L1 H0 H1 -> L0 H0 L1 H1 - lda Kernel2Sprite__2__tmp+1 - ldy Kernel2Sprite__2__tmp+2 - sty Kernel2Sprite__2__tmp+1 - sta Kernel2Sprite__2__tmp+2 - lda Kernel2Sprite__2__tmp+5 - ldy Kernel2Sprite__2__tmp+6 - sty Kernel2Sprite__2__tmp+5 - sta Kernel2Sprite__2__tmp+6 - -;;; end action Kernel2Sprite__preframe__11 - -;;; start action Kernel2Sprite__preframe__13 - - lda #162 - sta COLUBK - -;;; end action Kernel2Sprite__preframe__13 - -;;; start action Kernel2Sprite__preframe__16 - -;;; end action Kernel2Sprite__preframe__16 - -;;; start action SetXPos__preframe__17 - - sta HMCLR - -;;; end action SetXPos__preframe__17 - -;;; start action SetXPos__preframe__19 - - ldy #0 -SetXPos__preframe__20____each: - ldx SpriteSlot_sprite_b0,y - - lda HasXpos_xpos_b0,x - -;;; start action SetHorizPos__SetHorizPos__22 - -; SetHorizPos routine -; A = X coordinate -; Y = player number (0 or 1) - sta WSYNC ; start a new line - sec ; set carry flag - nop -SetHorizPos__SetHorizPos__23__DivideLoop: - sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__23__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__22 - - - iny - cpy #2 - jne SetXPos__preframe__20____each -SetXPos__preframe__20____exit: - -;;; end action SetXPos__preframe__19 - -;;; start action SetXPos__preframe__24 - -;;; end action SetXPos__preframe__24 - -;;; start action SetXPos__preframe__25 - - sta WSYNC - sta HMOVE - -;;; end action SetXPos__preframe__25 - - KERNEL_START - -;;; start action Kernel2Sprite__kernel__27 - - ldy #0 - sty VDELP0 - iny - sta VDELP1 - -;;; end action Kernel2Sprite__kernel__27 - -;;; start action Kernel2Sprite__kernel__30 - - ldy #192 -Kernel2Sprite__kernel__32__LVScan: - -;;; start action Kernel2Sprite__scanline__33 - -; draw player 0 - lda Kernel2Sprite__2__tmp+8 ; height - dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__34__DoDraw1 - lda #0 - .byte $2C -Kernel2Sprite__scanline__34__DoDraw1: - lda (Kernel2Sprite__2__tmp+0),y - .if 0 = 0 - sta WSYNC - .endif - sta GRP0 - lda (Kernel2Sprite__2__tmp+4),y - sta COLUP0 -; draw player 1 - lda Kernel2Sprite__2__tmp+9 ; height - dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__34__DoDraw2 - lda #0 - .byte $2C -Kernel2Sprite__scanline__34__DoDraw2: - lda (Kernel2Sprite__2__tmp+2),y - sta GRP1 - lda (Kernel2Sprite__2__tmp+6),y - sta COLUP1 - -;;; end action Kernel2Sprite__scanline__33 - -;;; start action Kernel2Sprite__scanline__35 - -;;; end action Kernel2Sprite__scanline__35 - - dey ; next scanline - -;;; start action Kernel2Sprite__scanline__36 - -; draw player 0 - lda Kernel2Sprite__2__tmp+8 ; height - dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs Kernel2Sprite__scanline__37__DoDraw1 - lda #0 - .byte $2C -Kernel2Sprite__scanline__37__DoDraw1: - lda (Kernel2Sprite__2__tmp+0),y - .if 1 = 0 - sta WSYNC - .endif - sta GRP0 - lda (Kernel2Sprite__2__tmp+4),y - sta COLUP0 -; draw player 1 - lda Kernel2Sprite__2__tmp+9 ; height - dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs Kernel2Sprite__scanline__37__DoDraw2 - lda #0 - .byte $2C -Kernel2Sprite__scanline__37__DoDraw2: - lda (Kernel2Sprite__2__tmp+2),y - sta GRP1 - lda (Kernel2Sprite__2__tmp+6),y - sta COLUP1 - -;;; end action Kernel2Sprite__scanline__36 - -;;; start action Kernel2Sprite__scanline__38 - -;;; end action Kernel2Sprite__scanline__38 - - dey ; next scanline - bne Kernel2Sprite__kernel__32__LVScan ; repeat until out of lines - -;;; end action Kernel2Sprite__kernel__30 - -;;; start action Kernel2Sprite__kernel__39 - - lda #0 - sta GRP0 - sta GRP1 - sta GRP0 - sta GRP1 - -;;; end action Kernel2Sprite__kernel__39 - - KERNEL_END - -;;; start action Joystick__postframe__41 - -; 2 control inputs share a single byte, 4 bits each - lda SWCHA - sta Joystick__3__tmp+0 - -;;; end action Joystick__postframe__41 - -;;; start action Joystick__postframe__43 - - ldx #0 -Joystick__postframe__44____each: - - asl Joystick__3__tmp+0 - bcs Joystick__postframe__45__SkipMoveRight - -;;; start action MoveJoyX__joyright__46 - - lda HasXpos_xpos_b0,x - clc - adc #1 - cmp #152 - bcs MoveJoyX__joyright__48__nomove - sta HasXpos_xpos_b0,x -MoveJoyX__joyright__48__nomove: - -;;; end action MoveJoyX__joyright__46 - -Joystick__postframe__45__SkipMoveRight: - asl Joystick__3__tmp+0 - bcs Joystick__postframe__45__SkipMoveLeft - -;;; start action MoveJoyX__joyleft__49 - - lda HasXpos_xpos_b0,x - sec - sbc #1 - bcc MoveJoyX__joyleft__51__nomove - sta HasXpos_xpos_b0,x -MoveJoyX__joyleft__51__nomove: - -;;; end action MoveJoyX__joyleft__49 - -Joystick__postframe__45__SkipMoveLeft: - asl Joystick__3__tmp+0 - bcs Joystick__postframe__45__SkipMoveDown - -;;; start action MoveJoyY__joydown__52 - - lda HasYpos_ypos_b0,x - clc - adc #1 - cmp #220 - bcs MoveJoyY__joydown__54__nomove - sta HasYpos_ypos_b0,x -MoveJoyY__joydown__54__nomove: - -;;; end action MoveJoyY__joydown__52 - -Joystick__postframe__45__SkipMoveDown: - asl Joystick__3__tmp+0 - bcs Joystick__postframe__45__SkipMoveUp - -;;; start action MoveJoyY__joyup__55 - - lda HasYpos_ypos_b0,x - sec - sbc #1 - bcc MoveJoyY__joyup__57__nomove - sta HasYpos_ypos_b0,x -MoveJoyY__joyup__57__nomove: - -;;; end action MoveJoyY__joyup__55 - -Joystick__postframe__45__SkipMoveUp: - - inx - cpx #4 - jne Joystick__postframe__44____each -Joystick__postframe__44____exit: - -;;; end action Joystick__postframe__43 - -;;; start action SpriteShuffler__postframe__58 - -; load two sprite slots at left side of array - lda SpriteSlot_sprite_b0 - sta SpriteShuffler__8__tmp+0 - lda SpriteSlot_sprite_b0+1 - sta SpriteShuffler__8__tmp+1 -; move two slots to the left - ldx #0 -SpriteShuffler__postframe__60__loop: - lda SpriteSlot_sprite_b0+2,x - sta SpriteSlot_sprite_b0,x - inx - cpx #4-2 - bne SpriteShuffler__postframe__60__loop -; store two sprite slots at right side of array - lda SpriteShuffler__8__tmp+0 - sta SpriteSlot_sprite_b0+4-2 - lda SpriteShuffler__8__tmp+1 - sta SpriteSlot_sprite_b0+4-1 - -;;; end action SpriteShuffler__postframe__58 - -;;; start action SpriteHider__postframe__61 - - lda #4-1 - sta SpriteHider__9__tmp+0 - -;;; end action SpriteHider__postframe__61 - -;;; start action SpriteHider__postframe__64 - - ldy #0 -SpriteHider__postframe__65____each: - ldx SpriteSlot_sprite_b0,y - - lda HasYpos_ypos_b0,x - cmp #192 - bcc SpriteHider__postframe__66__skip -; swap this sprite slot with slot at end of array - lda SpriteSlot_sprite_b0,y - pha - ldx SpriteHider__9__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__9__tmp+0 -SpriteHider__postframe__66__skip: - - iny - cpy #2 - jne SpriteHider__postframe__65____each -SpriteHider__postframe__65____exit: - -;;; end action SpriteHider__postframe__64 - - FRAME_END - - jmp FrameLoop__start__4__NextFrame ; loop to next frame - -;;; end action FrameLoop__start__3 - ; start main routine -.segment "VECTORS" -Return: .word $6060 -VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK - -;;; end action Init__main_init__1 - -.endscope -Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/sprites1.txt b/test/ecs/sprites1.txt index f32432b5..42eea265 100644 --- a/test/ecs/sprites1.txt +++ b/test/ecs/sprites1.txt @@ -1,515 +1 @@ -EVENT__start = 1 -EVENT__preframe = 1 -EVENT__kernel = 1 -EVENT__scanline1 = 1 -EVENT__postframe = 1 -EVENT__joyleft = 1 -EVENT__joyright = 1 -EVENT__joyup = 1 -EVENT__joydown = 1 -EVENT__SetHorizPos = 1 -.scope Main -.zeropage -HasBitmap_bitmap_b0: -.res 1 -.res 1 -.res 1 -.res 1 -HasXpos_xpos_b0: -.res 1 -.res 1 -.res 1 -.res 1 -HasYpos_ypos_b0: -.res 1 -.res 1 -.res 1 -.res 1 -SpriteSlot_sprite_b0: -.res 1 -.res 1 -.res 1 -.res 1 -HasColormap_colormap_b0: -.res 1 -.res 1 -.res 1 -.res 1 -TEMP: -Kernel2Sprite__2__tmp: -Joystick__3__tmp: -SpriteHider__9__tmp: -.res 1 -SpriteShuffler__8__tmp: -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.res 1 -.code -KernelSection_lines_b0: -.byte 192 -BGColor_bgcolor_b0: -.byte 162 -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_bitmapdata_e1_b0: -.byte 1 -.byte 1 -.byte 3 -.byte 7 -.byte 15 -.byte 31 -.byte 63 -.byte 127 -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_b0: -.byte <(Colormap_colormapdata_e3_b0+31) -Colormap_colormapdata_b8: -.byte >(Colormap_colormapdata_e3_b0+31) -Colormap_colormapdata_e3_b0: -.byte 6 -.byte 3 -.byte 6 -.byte 9 -.byte 12 -.byte 14 -.byte 31 -.byte 63 -Sprite_plyrflags_b0: -.byte 0 -.byte 3 -.byte 0 -.byte 0 -Main__INITDATA: -.byte 1 -.byte 0 -.byte 1 -.byte 0 -.byte 50 -.byte 100 -.byte 80 -.byte 40 -.byte 150 -.byte 60 -.byte 90 -.byte 150 -.byte 0 -.byte 1 -.byte 2 -.byte 3 -.byte 0 -.byte 0 -.byte 0 -.byte 0 -__Start: - -;;; start action Init__main_init__1 - -.include "vcs-ca65.h" -.macpack longbranch -.define PAL 0 -__NMI: -__Reset: -__BRK: - CLEAN_START - - ldy #20 -: lda Main__INITDATA-1,y - sta HasBitmap_bitmap_b0-1,y - dey - bne :- - -;;; start action FrameLoop__start__3 - -FrameLoop__start__4__NextFrame: - FRAME_START - -;;; start action Kernel2Sprite__preframe__5 - -.define KLINES #192 -.define KPAD 32 - -;;; end action Kernel2Sprite__preframe__5 - -;;; start action Kernel2Sprite__preframe__8 - - ldy #0 -Kernel2Sprite__preframe__9____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__2__tmp+11 -; calculate bitmap pointer - stx Kernel2Sprite__2__tmp+12 ; save X (Sprite index) - lda HasBitmap_bitmap_b0,x ; deref bitmap - tax - lda Bitmap_bitmapdata_b0,x - sec - sbc Kernel2Sprite__2__tmp+11 - sta Kernel2Sprite__2__tmp+0,y ; Y = sprite slot index - lda Bitmap_bitmapdata_b8,x - sbc #0 - sta Kernel2Sprite__2__tmp+2,y -; get bitmap height - lda Bitmap_height_b0,x - sta Kernel2Sprite__2__tmp+8,y -; calculate colormap pointer - ldx Kernel2Sprite__2__tmp+12 ; restore X - lda HasColormap_colormap_b0,x ; deref colormap - tax - lda Colormap_colormapdata_b0,x - sec - sbc Kernel2Sprite__2__tmp+11 - sta Kernel2Sprite__2__tmp+4,y - lda Colormap_colormapdata_b8,x - sbc #0 - sta Kernel2Sprite__2__tmp+6,y -; save ypos - ldx Kernel2Sprite__2__tmp+12 ; restore X - lda HasYpos_ypos_b0,x - sta Kernel2Sprite__2__tmp+10,y - - iny - cpy #2 - jne Kernel2Sprite__preframe__9____each -Kernel2Sprite__preframe__9____exit: - -;;; end action Kernel2Sprite__preframe__8 - -;;; start action Kernel2Sprite__preframe__11 - -; L0 L1 H0 H1 -> L0 H0 L1 H1 - lda Kernel2Sprite__2__tmp+1 - ldy Kernel2Sprite__2__tmp+2 - sty Kernel2Sprite__2__tmp+1 - sta Kernel2Sprite__2__tmp+2 - lda Kernel2Sprite__2__tmp+5 - ldy Kernel2Sprite__2__tmp+6 - sty Kernel2Sprite__2__tmp+5 - sta Kernel2Sprite__2__tmp+6 - -;;; end action Kernel2Sprite__preframe__11 - -;;; start action Kernel2Sprite__preframe__13 - - lda #162 - sta COLUBK - -;;; end action Kernel2Sprite__preframe__13 - -;;; start action Kernel2Sprite__preframe__16 - -;;; end action Kernel2Sprite__preframe__16 - -;;; start action SetXPos__preframe__17 - - sta HMCLR - -;;; end action SetXPos__preframe__17 - -;;; start action SetXPos__preframe__19 - - ldy #0 -SetXPos__preframe__20____each: - ldx SpriteSlot_sprite_b0,y - - lda HasXpos_xpos_b0,x - -;;; start action SetHorizPos__SetHorizPos__22 - -; SetHorizPos routine -; A = X coordinate -; Y = player number (0 or 1) - sta WSYNC ; start a new line - sec ; set carry flag - nop -SetHorizPos__SetHorizPos__23__DivideLoop: - sbc #15 ; subtract 15 - bcs SetHorizPos__SetHorizPos__23__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__22 - - - iny - cpy #2 - jne SetXPos__preframe__20____each -SetXPos__preframe__20____exit: - -;;; end action SetXPos__preframe__19 - -;;; start action SetXPos__preframe__24 - -;;; end action SetXPos__preframe__24 - -;;; start action SetXPos__preframe__25 - - sta WSYNC - sta HMOVE - -;;; end action SetXPos__preframe__25 - - KERNEL_START - -;;; start action Kernel2Sprite__kernel__27 - - ldy #0 - sty VDELP0 - iny - sta VDELP1 - -;;; end action Kernel2Sprite__kernel__27 - -;;; start action Kernel2Sprite__kernel__30 - -; define macro for each line - .macro Kernel2Sprite__kernel__32__DrawLine do_wsync - .local DoDraw1 - .local DoDraw2 -; draw player 0 - lda Kernel2Sprite__2__tmp+8 ; height - dcp Kernel2Sprite__2__tmp+10 ; ypos - bcs DoDraw1 - lda #0 - .byte $2C -DoDraw1: - lda (Kernel2Sprite__2__tmp+0),y - .if do_wsync - sta WSYNC - .endif - sta GRP0 - lda (Kernel2Sprite__2__tmp+4),y - sta COLUP0 -; draw player 1 - lda Kernel2Sprite__2__tmp+9 ; height - dcp Kernel2Sprite__2__tmp+11 ; ypos - bcs DoDraw2 - lda #0 - .byte $2C -DoDraw2: - lda (Kernel2Sprite__2__tmp+2),y - sta GRP1 - lda (Kernel2Sprite__2__tmp+6),y - sta COLUP1 - .endmacro - - ldy #192 -Kernel2Sprite__kernel__32__LVScan: - -;;; start action Kernel2Sprite__scanline1__33 - -;;; end action Kernel2Sprite__scanline1__33 - - Kernel2Sprite__kernel__32__DrawLine 1 ; macro: draw scanline w/ WSYNC - dey ; next scanline - - Kernel2Sprite__kernel__32__DrawLine 0 ; macro: draw scanline no WSYNC - dey ; next scanline - bne Kernel2Sprite__kernel__32__LVScan ; repeat until out of lines - -;;; end action Kernel2Sprite__kernel__30 - -;;; start action Kernel2Sprite__kernel__34 - - lda #0 - sta GRP0 - sta GRP1 - sta GRP0 - sta GRP1 - -;;; end action Kernel2Sprite__kernel__34 - - KERNEL_END - -;;; start action Joystick__postframe__37 - -; 2 control inputs share a single byte, 4 bits each - lda SWCHA - sta Joystick__3__tmp+0 - -;;; end action Joystick__postframe__37 - -;;; start action Joystick__postframe__39 - - ldx #0 -Joystick__postframe__40____each: - - asl Joystick__3__tmp+0 - bcs Joystick__postframe__41__SkipMoveRight - -;;; start action MoveJoyX__joyright__42 - - lda HasXpos_xpos_b0,x - clc - adc #1 - cmp #152 - bcs MoveJoyX__joyright__44__nomove - sta HasXpos_xpos_b0,x -MoveJoyX__joyright__44__nomove: - -;;; end action MoveJoyX__joyright__42 - -Joystick__postframe__41__SkipMoveRight: - asl Joystick__3__tmp+0 - bcs Joystick__postframe__41__SkipMoveLeft - -;;; start action MoveJoyX__joyleft__45 - - lda HasXpos_xpos_b0,x - sec - sbc #1 - bcc MoveJoyX__joyleft__47__nomove - sta HasXpos_xpos_b0,x -MoveJoyX__joyleft__47__nomove: - -;;; end action MoveJoyX__joyleft__45 - -Joystick__postframe__41__SkipMoveLeft: - asl Joystick__3__tmp+0 - bcs Joystick__postframe__41__SkipMoveDown - -;;; start action MoveJoyY__joydown__48 - - lda HasYpos_ypos_b0,x - clc - adc #1 - cmp #220 - bcs MoveJoyY__joydown__50__nomove - sta HasYpos_ypos_b0,x -MoveJoyY__joydown__50__nomove: - -;;; end action MoveJoyY__joydown__48 - -Joystick__postframe__41__SkipMoveDown: - asl Joystick__3__tmp+0 - bcs Joystick__postframe__41__SkipMoveUp - -;;; start action MoveJoyY__joyup__51 - - lda HasYpos_ypos_b0,x - sec - sbc #1 - bcc MoveJoyY__joyup__53__nomove - sta HasYpos_ypos_b0,x -MoveJoyY__joyup__53__nomove: - -;;; end action MoveJoyY__joyup__51 - -Joystick__postframe__41__SkipMoveUp: - - inx - cpx #4 - jne Joystick__postframe__40____each -Joystick__postframe__40____exit: - -;;; end action Joystick__postframe__39 - -;;; start action SpriteShuffler__postframe__54 - -; load two sprite slots at left side of array - lda SpriteSlot_sprite_b0 - sta SpriteShuffler__8__tmp+0 - lda SpriteSlot_sprite_b0+1 - sta SpriteShuffler__8__tmp+1 -; move two slots to the left - ldx #0 -SpriteShuffler__postframe__56__loop: - lda SpriteSlot_sprite_b0+2,x - sta SpriteSlot_sprite_b0,x - inx - cpx #4-2 - bne SpriteShuffler__postframe__56__loop -; store two sprite slots at right side of array - lda SpriteShuffler__8__tmp+0 - sta SpriteSlot_sprite_b0+4-2 - lda SpriteShuffler__8__tmp+1 - sta SpriteSlot_sprite_b0+4-1 - -;;; end action SpriteShuffler__postframe__54 - -;;; start action SpriteHider__postframe__57 - - lda #4-1 - sta SpriteHider__9__tmp+0 - -;;; end action SpriteHider__postframe__57 - -;;; start action SpriteHider__postframe__60 - - ldy #0 -SpriteHider__postframe__61____each: - ldx SpriteSlot_sprite_b0,y - - lda HasYpos_ypos_b0,x - cmp #192 - bcc SpriteHider__postframe__62__skip -; swap this sprite slot with slot at end of array - lda SpriteSlot_sprite_b0,y - pha - ldx SpriteHider__9__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__9__tmp+0 -SpriteHider__postframe__62__skip: - - iny - cpy #2 - jne SpriteHider__postframe__61____each -SpriteHider__postframe__61____exit: - -;;; end action SpriteHider__postframe__60 - - FRAME_END - - jmp FrameLoop__start__4__NextFrame ; loop to next frame - -;;; end action FrameLoop__start__3 - ; start main routine -.segment "VECTORS" -Return: .word $6060 -VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK - -;;; end action Init__main_init__1 - -.endscope -Main__Start = Main::__Start \ No newline at end of file +185:I couldn't find a component named "VersatilePlayfield". \ No newline at end of file diff --git a/test/ecs/superman.ecs b/test/ecs/superman.ecs index db50c3c7..4038316d 100644 --- a/test/ecs/superman.ecs +++ b/test/ecs/superman.ecs @@ -3,6 +3,8 @@ import "vcslib.ecs" import "sprites.ecs" +import "kernel2.ecs" +import "versatile.ecs" // https://csanyk.com/2014/02/topology-metropolis-superman-atari-2600/ @@ -116,23 +118,24 @@ end system RoomShuffle on postframe do select [Location,Sprite] --- - ldy {{%ecount}} + ldy #{{%ecount}}-1 ldx {{(SetHorizPos__SetHorizPos__20) = >(*), error, "SetHorizPos__SetHorizPos__20 crosses a page boundary!" + +.assert (* - SetHorizPos__SetHorizPos__20) <= 22, error, .sprintf("SetHorizPos__SetHorizPos__20 does not fit in 22 bytes, it took %d!", (* - SetHorizPos__SetHorizPos__20)) +.rodata +Kernel2Sprite__kernel__31: + + ldy #190 +Kernel2Sprite__kernel__33__LVScan: + +;;; start action Kernel2Sprite__scanline__34 + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__35__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__35__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y +; .if 0 = 0 ; TODO: configurable? + sta WSYNC +; .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__35__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__35__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite__scanline__34 + +;;; start action Kernel2Sprite__scanline__36 + +;;; end action Kernel2Sprite__scanline__36 + +;;; start action VersatilePlayfield__scanline__37 + +.if 0 = 0 + lda (VersatilePlayfield__10__tmp+0),y + tax +.endif + +;;; end action VersatilePlayfield__scanline__37 + +;;; start action VersatilePlayfield__scanline__39 + +.if 0 = 1 + lda (VersatilePlayfield__10__tmp+0),y + sta $00,x +.endif + +;;; end action VersatilePlayfield__scanline__39 + + dey ; next scanline + +;;; start action Kernel2Sprite__scanline__41 + +; draw player 0 + lda Kernel2Sprite__2__tmp+8 ; height + dcp Kernel2Sprite__2__tmp+10 ; ypos + bcs Kernel2Sprite__scanline__42__DoDraw1 + lda #0 + .byte $2C +Kernel2Sprite__scanline__42__DoDraw1: + lda (Kernel2Sprite__2__tmp+0),y +; .if 1 = 0 ; TODO: configurable? + sta WSYNC +; .endif + sta GRP0 + lda (Kernel2Sprite__2__tmp+4),y + sta COLUP0 +; draw player 1 + lda Kernel2Sprite__2__tmp+9 ; height + dcp Kernel2Sprite__2__tmp+11 ; ypos + bcs Kernel2Sprite__scanline__42__DoDraw2 + lda #0 + .byte $2C +Kernel2Sprite__scanline__42__DoDraw2: + lda (Kernel2Sprite__2__tmp+2),y + sta GRP1 + lda (Kernel2Sprite__2__tmp+6),y + sta COLUP1 + +;;; end action Kernel2Sprite__scanline__41 + +;;; start action Kernel2Sprite__scanline__43 + +;;; end action Kernel2Sprite__scanline__43 + +;;; start action VersatilePlayfield__scanline__44 + +.if 1 = 0 + lda (VersatilePlayfield__10__tmp+0),y + tax +.endif + +;;; end action VersatilePlayfield__scanline__44 + +;;; start action VersatilePlayfield__scanline__46 + +.if 1 = 1 + lda (VersatilePlayfield__10__tmp+0),y + sta $00,x +.endif + +;;; end action VersatilePlayfield__scanline__46 + + dey ; next scanline + bne Kernel2Sprite__kernel__33__LVScan ; repeat until out of lines + + rts + +.assert >(Kernel2Sprite__kernel__31) = >(*), error, "Kernel2Sprite__kernel__31 crosses a page boundary!" .endscope Main__Start = Main::__Start \ No newline at end of file diff --git a/test/ecs/titles.ecs b/test/ecs/titles.ecs index ca2f35d1..bc982e7f 100644 --- a/test/ecs/titles.ecs +++ b/test/ecs/titles.ecs @@ -18,6 +18,9 @@ system Kernel48Pixel on kernelsetup do if [Bitmap48] --- lda {{ [GRP0] lda {{data bitmap1}},y ; load B1 -> A @@ -67,7 +71,7 @@ system Kernel48Pixel sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec {{$0}} ; go to next line - bpl @Loop ; repeat until < 0 + bpl :- ; repeat until < 0 pla tax --- @@ -77,7 +81,9 @@ demo TitleDemo scope Title - using FrameLoop, Kernel48Pixel, StaticKernel + using FrameLoop, StandardKernel + using Kernel48Pixel with [#Name] + using Kernel48Pixel with [#Copyright] using JoyButton entity [Player] @@ -91,7 +97,7 @@ demo TitleDemo end entity [KernelSection, BGColor] - const lines = 60 + const lines = 30 const bgcolor = $a0 end @@ -101,7 +107,7 @@ demo TitleDemo end // convert -size 48x -gravity center label:"6502\nECS" pbm: | pnmtopnm -plain - entity [KernelSection, BGColor, PFColor, Bitmap48] + entity Name [KernelSection, BGColor, PFColor, Bitmap48] const lines = 2 const bgcolor = 0xa4 const pfcolor = 0xfc @@ -147,11 +153,43 @@ demo TitleDemo --- end + entity Copyright [KernelSection, BGColor, PFColor, Bitmap48] + const lines = 2 + const bgcolor = 0xa4 + const pfcolor = 0x8c + decode vcs_bitmap48 --- +000000000000000000000000000000100000000100000100 +000000000000000000000000000000000000000100000100 +000111000111001111010001101110100011010111101110 +001001001001001101111001001100100100110110100100 +001001001000101000101001001000100100110100010100 +011000011000101000101001001000101100010100010100 +011000011000101000101010001000101100010100010100 +011001001000101000100110001000100100110100010100 +001001001001001001000110001000100100110100010110 +000110000111001111000110001000100011110100010000 +000000000000001000000100000000000000010000000000 +000000000000001000000100000000000100100000000000 +000000000000001000001000000000000111100000000000 +000000000000000000000000000000000000000000000000 +000000000000001110001110001110001110000000000000 +000000000000010011011010010011010011000000000000 +000000000000010001010011010011010001000000000000 +000000000000000001110001000001000001000000000000 +000000000000000011110001000011000011000000000000 +000000000000000110110001000110000110000000000000 +000000000000001100110001001100001000000000000000 +000000000000010000010001010000010000000000000000 +000000000000010000010010010000010000000000000000 +000000000000111111001110111111111111000000000000 +000000000000000000000000000000000000000000000000 +--- + end + entity [KernelSection, BGColor] const lines = 10 const bgcolor = $a2 end - entity [KernelSection, BGColor] const lines = 10 const bgcolor = $a0 @@ -161,21 +199,21 @@ demo TitleDemo scope Title2 - using FrameLoop, Kernel48Pixel, StaticKernel + using FrameLoop, Kernel48Pixel, StandardKernel entity [KernelSection, BGColor] - const lines = 60 + var lines = 60 var bgcolor = 0x10 end entity [KernelSection, BGColor] - const lines = 10 + var lines = 30 var bgcolor = 0x30 end // convert -size 48x -gravity center label:"6502\nECS" pbm: | pnmtopnm -plain entity [KernelSection, BGColor, PFColor, Bitmap48] - const lines = 2 + var lines = 2 var pfcolor = 0xec var bgcolor = 0x30 decode vcs_bitmap48 --- @@ -227,12 +265,12 @@ demo TitleDemo end entity [KernelSection, BGColor] - const lines = 10 + var lines = 20 var bgcolor = 0x30 end entity [KernelSection, BGColor] - const lines = 10 + var lines = 20 var bgcolor = 0x50 end @@ -242,7 +280,14 @@ system Colors inc {{set pfcolor}} bne :+ inc {{set bgcolor}} - inc {{set bgcolor}} +: +--- + on postframe do foreach [KernelSection] +--- + dec {{set lines}} + bne :+ + lda #1 + sta {{set lines}} : --- end @@ -252,7 +297,7 @@ end system Demo on start do once --- - jmp Title__Start + {{start Title}} --- end diff --git a/test/ecs/titles.txt b/test/ecs/titles.txt index 53e68cec..252d0cd7 100644 --- a/test/ecs/titles.txt +++ b/test/ecs/titles.txt @@ -1,9 +1,10 @@ EVENT__start = 1 -EVENT__kernelsetup = 1 -EVENT__kerneldraw = 1 +EVENT__postframe = 1 +EVENT__wsync = 1 EVENT__preframe = 1 EVENT__kernel = 1 -EVENT__postframe = 1 +EVENT__kerneldraw = 1 +EVENT__kernelsetup = 1 EVENT__joybutton = 1 .scope TitleDemo .zeropage @@ -22,43 +23,53 @@ __BRK: ;;; start action Demo__start__3 - jmp Title__Start + jmp Title__Start ;;; end action Demo__start__3 ; start main routine .segment "VECTORS" -Return: .word $6060 +ZeroByte: .byte $00 +Return: .byte $60 VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK +VecReset: .word __Reset +VecBRK: .word __BRK +.code ;;; end action Init__main_init__1 .scope Title .zeropage TEMP: -Kernel48Pixel__2__tmp: +Kernel48Pixel__3__tmp: +.res 1 +.res 1 +Kernel48Pixel__4__tmp: .res 1 .res 1 .code KernelSection_lines_b0: -.byte 60 +.byte 30 .byte 10 .byte 2 +.byte 2 .byte 10 .byte 10 BGColor_bgcolor_b0: .byte 160 .byte 162 .byte 164 +.byte 164 .byte 162 .byte 160 PFColor_pfcolor_b0: .byte 252 +.byte 140 Bitmap48_bitmap0_b0: .byte Bitmap48_bitmap0_e3_b0 +.byte >Bitmap48_bitmap0_e4_b0 Bitmap48_bitmap0_e3_b0: .byte 0 .byte 0 @@ -99,8 +110,10 @@ Bitmap48_bitmap0_e3_b0: .byte 3 Bitmap48_bitmap1_b0: .byte Bitmap48_bitmap1_e3_b0 +.byte >Bitmap48_bitmap1_e4_b0 Bitmap48_bitmap1_e3_b0: .byte 0 .byte 0 @@ -141,8 +154,10 @@ Bitmap48_bitmap1_e3_b0: .byte 193 Bitmap48_bitmap2_b0: .byte Bitmap48_bitmap2_e3_b0 +.byte >Bitmap48_bitmap2_e4_b0 Bitmap48_bitmap2_e3_b0: .byte 0 .byte 0 @@ -183,8 +198,10 @@ Bitmap48_bitmap2_e3_b0: .byte 254 Bitmap48_bitmap3_b0: .byte Bitmap48_bitmap3_e3_b0 +.byte >Bitmap48_bitmap3_e4_b0 Bitmap48_bitmap3_e3_b0: .byte 0 .byte 0 @@ -225,8 +242,10 @@ Bitmap48_bitmap3_e3_b0: .byte 15 Bitmap48_bitmap4_b0: .byte Bitmap48_bitmap4_e3_b0 +.byte >Bitmap48_bitmap4_e4_b0 Bitmap48_bitmap4_e3_b0: .byte 0 .byte 0 @@ -267,8 +286,10 @@ Bitmap48_bitmap4_e3_b0: .byte 3 Bitmap48_bitmap5_b0: .byte Bitmap48_bitmap5_e3_b0 +.byte >Bitmap48_bitmap5_e4_b0 Bitmap48_bitmap5_e3_b0: .byte 0 .byte 0 @@ -309,61 +330,294 @@ Bitmap48_bitmap5_e3_b0: .byte 224 Bitmap48_height_b0: .byte 36 +.byte 24 +Bitmap48_bitmap0_e4_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 24 +.byte 36 +.byte 100 +.byte 97 +.byte 97 +.byte 36 +.byte 36 +.byte 28 +.byte 0 +.byte 0 +Bitmap48_bitmap1_e4_b0: +.byte 0 +.byte 15 +.byte 4 +.byte 4 +.byte 3 +.byte 1 +.byte 0 +.byte 0 +.byte 4 +.byte 4 +.byte 3 +.byte 0 +.byte 2 +.byte 2 +.byte 2 +.byte 115 +.byte 146 +.byte 138 +.byte 138 +.byte 138 +.byte 138 +.byte 147 +.byte 115 +.byte 0 +.byte 0 +Bitmap48_bitmap2_e4_b0: +.byte 0 +.byte 206 +.byte 18 +.byte 17 +.byte 49 +.byte 177 +.byte 241 +.byte 113 +.byte 83 +.byte 218 +.byte 142 +.byte 0 +.byte 8 +.byte 4 +.byte 4 +.byte 198 +.byte 70 +.byte 38 +.byte 42 +.byte 41 +.byte 41 +.byte 121 +.byte 209 +.byte 0 +.byte 0 +Bitmap48_bitmap3_e4_b0: +.byte 0 +.byte 255 +.byte 65 +.byte 65 +.byte 48 +.byte 24 +.byte 12 +.byte 4 +.byte 77 +.byte 77 +.byte 56 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 34 +.byte 34 +.byte 34 +.byte 34 +.byte 34 +.byte 34 +.byte 50 +.byte 186 +.byte 0 +.byte 2 +Bitmap48_bitmap4_e4_b0: +.byte 0 +.byte 240 +.byte 0 +.byte 0 +.byte 128 +.byte 96 +.byte 48 +.byte 16 +.byte 16 +.byte 48 +.byte 224 +.byte 0 +.byte 120 +.byte 72 +.byte 4 +.byte 61 +.byte 77 +.byte 77 +.byte 197 +.byte 197 +.byte 77 +.byte 77 +.byte 53 +.byte 1 +.byte 1 +Bitmap48_bitmap5_e4_b0: +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 0 +.byte 16 +.byte 22 +.byte 20 +.byte 20 +.byte 20 +.byte 20 +.byte 164 +.byte 238 +.byte 4 +.byte 4 __Start: ;;; start action FrameLoop__start__5 + FrameLoop__start__6__NextFrame: + FRAME_END + FRAME_START -;;; start action StaticKernel__preframe__7 +;;; start action StandardKernel__preframe__7 -;;; start action Kernel48Pixel__kernelsetup__10 +;;; start action StandardKernel__wsync__10 -;;; end action Kernel48Pixel__kernelsetup__10 + sta WSYNC -;;; start action Kernel48Pixel__kernelsetup__11 +;;; end action StandardKernel__wsync__10 -;;; end action Kernel48Pixel__kernelsetup__11 - -;;; start action StaticKernel__kernelsetup__12 + +;;; start action StandardKernel__kernelsetup__12 lda #160 sta COLUBK -;;; end action StaticKernel__kernelsetup__12 +;;; end action StandardKernel__kernelsetup__12 -;;; start action StaticKernel__kernelsetup__15 +;;; start action StandardKernel__kernelsetup__15 -;;; end action StaticKernel__kernelsetup__15 +;;; end action StandardKernel__kernelsetup__15 -;;; start action StaticKernel__kernelsetup__16 +;;; start action StandardKernel__kernelsetup__16 -;;; end action StaticKernel__kernelsetup__16 +;;; end action StandardKernel__kernelsetup__16 +;;; start action Kernel48Pixel__kernelsetup__17 -;;; end action StaticKernel__preframe__7 +;;; end action Kernel48Pixel__kernelsetup__17 - KERNEL_START - -;;; start action StaticKernel__kernel__17 +;;; start action Kernel48Pixel__kernelsetup__18 + +;;; end action Kernel48Pixel__kernelsetup__18 + +;;; start action Kernel48Pixel__kernelsetup__19 + +;;; end action Kernel48Pixel__kernelsetup__19 - ldx #0 -StaticKernel__kernel__18____each: - - sta WSYNC - ;;; start action Kernel48Pixel__kernelsetup__20 - cpx #2+1 - jcs Kernel48Pixel__kernelsetup__21____skipxhi +;;; end action Kernel48Pixel__kernelsetup__20 + +;;; start action Kernel48Pixel__kernelsetup__21 + +;;; end action Kernel48Pixel__kernelsetup__21 + +;;; start action Kernel48Pixel__kernelsetup__22 + +;;; end action Kernel48Pixel__kernelsetup__22 + + +;;; end action StandardKernel__preframe__7 + + + KERNEL_START + +;;; start action StandardKernel__kernel__23 + + ldx #0 +StandardKernel__kernel__24____each: + + +;;; start action StandardKernel__wsync__26 + + sta WSYNC + +;;; end action StandardKernel__wsync__26 + + +;;; start action StandardKernel__kernelsetup__28 + + lda BGColor_bgcolor_b0,x + sta COLUBK + +;;; end action StandardKernel__kernelsetup__28 + +;;; start action StandardKernel__kernelsetup__31 + + cpx #2+2 + jcs StandardKernel__kernelsetup__32____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__21____skipxlo + jcc StandardKernel__kernelsetup__32____skipxlo + + lda PFColor_pfcolor_b0-2,x + sta COLUPF + +StandardKernel__kernelsetup__32____skipxlo: + +StandardKernel__kernelsetup__32____skipxhi: + +;;; end action StandardKernel__kernelsetup__31 + +;;; start action StandardKernel__kernelsetup__34 + +;;; end action StandardKernel__kernelsetup__34 + +;;; start action Kernel48Pixel__kernelsetup__35 + + cpx #2+1 + jcs Kernel48Pixel__kernelsetup__36____skipxhi + + cpx #2 + jcc Kernel48Pixel__kernelsetup__36____skipxlo lda #36 - sta Kernel48Pixel__2__tmp+0 ; scanline counter + sta Kernel48Pixel__3__tmp+0 ; scanline counter + +Kernel48Pixel__kernelsetup__36____skipxlo: + +Kernel48Pixel__kernelsetup__36____skipxhi: + +;;; end action Kernel48Pixel__kernelsetup__35 + +;;; start action Kernel48Pixel__kernelsetup__38 + + cpx #2+1 + jcs Kernel48Pixel__kernelsetup__39____skipxhi + + cpx #2 + jcc Kernel48Pixel__kernelsetup__39____skipxlo + lda #$22 sta COLUP0 ; show how players alternate lda #$12 @@ -385,119 +639,184 @@ StaticKernel__kernel__18____each: SLEEPH 24-8 ; sleep 24 cycles sta HMCLR ; clear HMOVE registers -Kernel48Pixel__kernelsetup__21____skipxlo: +Kernel48Pixel__kernelsetup__39____skipxlo: -Kernel48Pixel__kernelsetup__21____skipxhi: +Kernel48Pixel__kernelsetup__39____skipxhi: -;;; end action Kernel48Pixel__kernelsetup__20 +;;; end action Kernel48Pixel__kernelsetup__38 -;;; start action Kernel48Pixel__kernelsetup__23 +;;; start action Kernel48Pixel__kernelsetup__41 cpx #2+1 - jcs Kernel48Pixel__kernelsetup__24____skipxhi + jcs Kernel48Pixel__kernelsetup__42____skipxhi cpx #2 - jcc Kernel48Pixel__kernelsetup__24____skipxlo + jcc Kernel48Pixel__kernelsetup__42____skipxlo lda #252 sta COLUP0 sta COLUP1 -Kernel48Pixel__kernelsetup__24____skipxlo: +Kernel48Pixel__kernelsetup__42____skipxlo: -Kernel48Pixel__kernelsetup__24____skipxhi: +Kernel48Pixel__kernelsetup__42____skipxhi: -;;; end action Kernel48Pixel__kernelsetup__23 +;;; end action Kernel48Pixel__kernelsetup__41 -;;; start action StaticKernel__kernelsetup__26 +;;; start action Kernel48Pixel__kernelsetup__44 - lda BGColor_bgcolor_b0,x - sta COLUBK - -;;; end action StaticKernel__kernelsetup__26 - -;;; start action StaticKernel__kernelsetup__29 - - cpx #2+1 - jcs StaticKernel__kernelsetup__30____skipxhi + cpx #3+1 + jcs Kernel48Pixel__kernelsetup__45____skipxhi - cpx #2 - jcc StaticKernel__kernelsetup__30____skipxlo + cpx #3 + jcc Kernel48Pixel__kernelsetup__45____skipxlo - lda #252 - sta COLUPF + lda #24 + sta Kernel48Pixel__4__tmp+0 ; scanline counter + +Kernel48Pixel__kernelsetup__45____skipxlo: -StaticKernel__kernelsetup__30____skipxlo: +Kernel48Pixel__kernelsetup__45____skipxhi: -StaticKernel__kernelsetup__30____skipxhi: +;;; end action Kernel48Pixel__kernelsetup__44 -;;; end action StaticKernel__kernelsetup__29 +;;; start action Kernel48Pixel__kernelsetup__47 -;;; start action StaticKernel__kernelsetup__32 + cpx #3+1 + jcs Kernel48Pixel__kernelsetup__48____skipxhi + + cpx #3 + jcc Kernel48Pixel__kernelsetup__48____skipxlo + + lda #$22 + sta COLUP0 ; show how players alternate + lda #$12 + sta COLUP1 ; by having different colors + lda #3 + sta NUSIZ0 + sta NUSIZ1 ; both players have 3 copies + sta WSYNC + SLEEPH 35 + sta RESP0 ; position 1st player + sta RESP1 ; ...and 2nd player + lda #$10 + sta HMP1 ; 1 pixel to the left + sta WSYNC + sta HMOVE ; apply HMOVE + lda #1 + sta VDELP0 ; we need the VDEL registers + sta VDELP1 ; so we can do our 4-store trick + SLEEPH 24-8 ; sleep 24 cycles + sta HMCLR ; clear HMOVE registers -;;; end action StaticKernel__kernelsetup__32 +Kernel48Pixel__kernelsetup__48____skipxlo: + +Kernel48Pixel__kernelsetup__48____skipxhi: + +;;; end action Kernel48Pixel__kernelsetup__47 + +;;; start action Kernel48Pixel__kernelsetup__50 + + cpx #3+1 + jcs Kernel48Pixel__kernelsetup__51____skipxhi + + cpx #3 + jcc Kernel48Pixel__kernelsetup__51____skipxlo + + lda #140 + sta COLUP0 + sta COLUP1 + +Kernel48Pixel__kernelsetup__51____skipxlo: + +Kernel48Pixel__kernelsetup__51____skipxhi: + +;;; end action Kernel48Pixel__kernelsetup__50 - jsr Kernel48Pixel__kerneldraw__33 - -;;; start action StaticKernel__kerneldraw__36 +;;; start action StandardKernel__kerneldraw__53 ldy KernelSection_lines_b0,x -StaticKernel__kerneldraw__38__loop: +StandardKernel__kerneldraw__55__loop: + + +;;; start action StandardKernel__wsync__56 + sta WSYNC + +;;; end action StandardKernel__wsync__56 + dey - bne StaticKernel__kerneldraw__38__loop + bne StandardKernel__kerneldraw__55__loop -;;; end action StaticKernel__kerneldraw__36 +;;; end action StandardKernel__kerneldraw__53 + + jsr Kernel48Pixel__kerneldraw__58 + + jsr Kernel48Pixel__kerneldraw__63 inx - cpx #5 - jne StaticKernel__kernel__18____each -StaticKernel__kernel__18____exit: + cpx #6 + jne StandardKernel__kernel__24____each +StandardKernel__kernel__24____exit: -;;; end action StaticKernel__kernel__17 +;;; end action StandardKernel__kernel__23 KERNEL_END -;;; start action JoyButton__postframe__39 + +;;; start action FrameLoop__postframe__68 + + lsr SWCHB ; test Game Reset switch + bcs FrameLoop__postframe__69__NoStart + +FrameLoop__postframe__69__NoStart: + +;;; end action FrameLoop__postframe__68 + +;;; start action JoyButton__postframe__70 lda INPT4 ;read button input - bmi JoyButton__postframe__41__NotPressed + bmi JoyButton__postframe__72__NotPressed -;;; start action Advance__joybutton__42 +;;; start action Advance__joybutton__73 jmp Title2__Start -;;; end action Advance__joybutton__42 +;;; end action Advance__joybutton__73 -JoyButton__postframe__41__NotPressed: +JoyButton__postframe__72__NotPressed: -;;; end action JoyButton__postframe__39 +;;; end action JoyButton__postframe__70 - FRAME_END - jmp FrameLoop__start__6__NextFrame ; loop to next frame ;;; end action FrameLoop__start__5 .rodata __ALIGNORIGIN: .rodata -Kernel48Pixel__kerneldraw__33: +Kernel48Pixel__kerneldraw__58: cpx #2+1 - jcs Kernel48Pixel__kerneldraw__34____skipxhi + jcs Kernel48Pixel__kerneldraw__59____skipxhi cpx #2 - jcc Kernel48Pixel__kerneldraw__34____skipxlo + jcc Kernel48Pixel__kerneldraw__59____skipxlo txa pha -Kernel48Pixel__kerneldraw__35__Loop: - ldy Kernel48Pixel__2__tmp+0 ; counts backwards - sta WSYNC ; sync to next scanline +: + ldy Kernel48Pixel__3__tmp+0 ; counts backwards + +;;; start action StandardKernel__wsync__61 + + sta WSYNC + +;;; end action StandardKernel__wsync__61 + ; sync to next scanline lda Bitmap48_bitmap0_e3_b0,y ; load B0 (1st sprite byte) sta GRP0 ; B0 -> [GRP0] lda Bitmap48_bitmap1_e3_b0,y ; load B1 -> A @@ -505,32 +824,91 @@ Kernel48Pixel__kerneldraw__35__Loop: lda Bitmap48_bitmap2_e3_b0,y ; load B2 -> A sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 lda Bitmap48_bitmap5_e3_b0,y ; load B5 -> A - sta Kernel48Pixel__2__tmp+1 ; B5 -> temp + sta Kernel48Pixel__3__tmp+1 ; B5 -> temp ldx Bitmap48_bitmap4_e3_b0,y ; load B4 -> X lda Bitmap48_bitmap3_e3_b0,y ; load B3 -> A - ldy Kernel48Pixel__2__tmp+1 ; load B5 -> Y + ldy Kernel48Pixel__3__tmp+1 ; load B5 -> Y sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 - dec Kernel48Pixel__2__tmp+0 ; go to next line - bpl Kernel48Pixel__kerneldraw__35__Loop ; repeat until < 0 + dec Kernel48Pixel__3__tmp+0 ; go to next line + bpl :- ; repeat until < 0 pla tax -Kernel48Pixel__kerneldraw__34____skipxlo: +Kernel48Pixel__kerneldraw__59____skipxlo: -Kernel48Pixel__kerneldraw__34____skipxhi: +Kernel48Pixel__kerneldraw__59____skipxhi: rts -.assert >(Kernel48Pixel__kerneldraw__33) = >(*), error, "Kernel48Pixel__kerneldraw__33 crosses a page boundary!" +.assert >(Kernel48Pixel__kerneldraw__58) = >(*), error, "Kernel48Pixel__kerneldraw__58 crosses a page boundary!" -.assert (* - Kernel48Pixel__kerneldraw__33) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__33 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__33)) +.assert (* - Kernel48Pixel__kerneldraw__58) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__58 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__58)) + +.if <(* - __ALIGNORIGIN) > 256-63 +.align $100 +.endif +.rodata +Kernel48Pixel__kerneldraw__63: + + cpx #3+1 + jcs Kernel48Pixel__kerneldraw__64____skipxhi + + cpx #3 + jcc Kernel48Pixel__kerneldraw__64____skipxlo + + txa + pha +: + ldy Kernel48Pixel__4__tmp+0 ; counts backwards + +;;; start action StandardKernel__wsync__66 + + sta WSYNC + +;;; end action StandardKernel__wsync__66 + ; sync to next scanline + lda Bitmap48_bitmap0_e4_b0,y ; load B0 (1st sprite byte) + sta GRP0 ; B0 -> [GRP0] + lda Bitmap48_bitmap1_e4_b0,y ; load B1 -> A + sta GRP1 ; B1 -> [GRP1], B0 -> GRP0 + lda Bitmap48_bitmap2_e4_b0,y ; load B2 -> A + sta GRP0 ; B2 -> [GRP0], B1 -> GRP1 + lda Bitmap48_bitmap5_e4_b0,y ; load B5 -> A + sta Kernel48Pixel__4__tmp+1 ; B5 -> temp + ldx Bitmap48_bitmap4_e4_b0,y ; load B4 -> X + lda Bitmap48_bitmap3_e4_b0,y ; load B3 -> A + ldy Kernel48Pixel__4__tmp+1 ; load B5 -> Y + sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 + stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 + sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 + sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 + dec Kernel48Pixel__4__tmp+0 ; go to next line + bpl :- ; repeat until < 0 + pla + tax + +Kernel48Pixel__kerneldraw__64____skipxlo: + +Kernel48Pixel__kerneldraw__64____skipxhi: + + rts + +.assert >(Kernel48Pixel__kerneldraw__63) = >(*), error, "Kernel48Pixel__kerneldraw__63 crosses a page boundary!" + +.assert (* - Kernel48Pixel__kerneldraw__63) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__63 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__63)) .endscope Title__Start = Title::__Start .scope Title2 .zeropage +KernelSection_lines_b0: +.res 1 +.res 1 +.res 1 +.res 1 +.res 1 BGColor_bgcolor_b0: .res 1 .res 1 @@ -544,12 +922,6 @@ Kernel48Pixel__2__tmp: .res 1 .res 1 .code -KernelSection_lines_b0: -.byte 60 -.byte 10 -.byte 2 -.byte 10 -.byte 10 Bitmap48_bitmap0_b0: .byte [GRP0] lda Bitmap48_bitmap1_e2_b0,y ; load B1 -> A @@ -1059,19 +1510,19 @@ Kernel48Pixel__kerneldraw__74__Loop: sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 dec Kernel48Pixel__2__tmp+0 ; go to next line - bpl Kernel48Pixel__kerneldraw__74__Loop ; repeat until < 0 + bpl :- ; repeat until < 0 pla tax -Kernel48Pixel__kerneldraw__73____skipxlo: +Kernel48Pixel__kerneldraw__112____skipxlo: -Kernel48Pixel__kerneldraw__73____skipxhi: +Kernel48Pixel__kerneldraw__112____skipxhi: rts -.assert >(Kernel48Pixel__kerneldraw__72) = >(*), error, "Kernel48Pixel__kerneldraw__72 crosses a page boundary!" +.assert >(Kernel48Pixel__kerneldraw__111) = >(*), error, "Kernel48Pixel__kerneldraw__111 crosses a page boundary!" -.assert (* - Kernel48Pixel__kerneldraw__72) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__72 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__72)) +.assert (* - Kernel48Pixel__kerneldraw__111) <= 63, error, .sprintf("Kernel48Pixel__kerneldraw__111 does not fit in 63 bytes, it took %d!", (* - Kernel48Pixel__kerneldraw__111)) .endscope Title2__Start = Title2::__Start .endscope diff --git a/test/ecs/vcslib.ecs b/test/ecs/vcslib.ecs index e78c16ae..47e2c576 100644 --- a/test/ecs/vcslib.ecs +++ b/test/ecs/vcslib.ecs @@ -13,10 +13,12 @@ __BRK: CLEAN_START {{!start}} ; start main routine .segment "VECTORS" -Return: .word $6060 +ZeroByte: .byte $00 +Return: .byte $60 VecNMI: -VecReset: .word Main::__Reset -VecBRK: .word Main::__BRK +VecReset: .word __Reset +VecBRK: .word __BRK +.code --- end @@ -24,13 +26,17 @@ component Player end component KernelSection - lines: 1..255 + lines: 1..255 default 1 end component BGColor bgcolor: 0..255 end +component FGColor + fgcolor: 0..255 +end + component PFColor pfcolor: 0..255 end @@ -44,32 +50,28 @@ component AsymPlayfield pfright: 0..0xffffff end -component VersatilePlayfield - data: array of 0..255 baseoffset -1 -end - system FrameLoop on start do once --- + {{emit preframeloop}} @NextFrame: + FRAME_END + {{emit prevsync}} FRAME_START {{emit preframe}} + {{emit prekernel}} KERNEL_START {{emit kernel}} KERNEL_END + {{emit postkernel}} {{emit postframe}} - FRAME_END - {{emit nextframe}} jmp @NextFrame ; loop to next frame --- -end - -system ResetSwitch - on nextframe do once + on postframe do once --- lsr SWCHB ; test Game Reset switch bcs @NoStart - {{!resetswitch}} + {{emit resetswitch}} @NoStart: --- end @@ -99,58 +101,71 @@ system Joystick lda SWCHA sta {{$0}} --- - on postframe do foreach [Player] + on postframe do foreach [Player] limit 2 --- asl {{$0}} +.ifdef EVENT__joyright bcs @SkipMoveRight {{!joyright}} @SkipMoveRight: +.endif asl {{$0}} +.ifdef EVENT__joyleft bcs @SkipMoveLeft {{!joyleft}} @SkipMoveLeft: +.endif asl {{$0}} +.ifdef EVENT__joydown bcs @SkipMoveDown {{!joydown}} @SkipMoveDown: +.endif asl {{$0}} +.ifdef EVENT__joyup bcs @SkipMoveUp {{!joyup}} @SkipMoveUp: +.endif --- end system SetHorizPos - on SetHorizPos do once + on SetHorizPos do critical fit 22 once --- ; SetHorizPos routine ; A = X coordinate ; Y = player number (0 or 1) - sta WSYNC ; start a new line - sec ; set carry flag - nop -@DivideLoop: + sec ; set carry flag + sta WSYNC ; start a new line +: sbc #15 ; subtract 15 - bcs @DivideLoop ; branch until negative + bcs :- ; branch until negative eor #7 ; calculate fine offset asl asl asl asl - sta RESP0,y ; fix coarse position sta HMP0,y ; set fine offset + sta RESP0,y ; fix coarse position + sta WSYNC ; won't overrun if X < 150 --- end -system StaticKernel +system StandardKernel + on wsync do once +--- + sta WSYNC +--- on preframe do foreach [KernelSection] limit 1 --- + {{!wsync}} {{!kernelsetup}} --- on kernel do foreach [KernelSection] --- - sta WSYNC + {{!wsync}} {{!kernelsetup}} {{!kerneldraw}} {{!kerneldone}} @@ -159,7 +174,8 @@ system StaticKernel --- ldy {{data}} + sta {{$1}} +--- + on scanline do once +--- +.if {{arg 0}} = 0 + lda ({{local 0}}),y + tax +.endif +--- + on scanline do once +--- +.if {{arg 0}} = 1 + lda ({{local 0}}),y + sta $00,x +.endif +--- + on postframe do once +--- + lda #0 + sta PF0 + sta PF1 + sta PF2 +--- + on checkplatform do with [HasXpos,HasYpos] + --- + lda {{ Date: Wed, 22 Jun 2022 12:22:47 -0500 Subject: [PATCH 104/104] fixed tests, removed listings from parseCA65Listing --- src/common/baseplatform.ts | 2 +- src/platform/williams.ts | 4 ++-- src/worker/tools/cc65.ts | 7 ++++--- test/cli/testplatforms.js | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/common/baseplatform.ts b/src/common/baseplatform.ts index 763f37bc..6f6483b3 100644 --- a/src/common/baseplatform.ts +++ b/src/common/baseplatform.ts @@ -225,7 +225,7 @@ export abstract class BasePlatform { getDebugTree() : {} { var o : any = { }; o.state = this.saveState(); - if (this.debugSymbols.debuginfo) o.debuginfo = this.debugSymbols.debuginfo; + if (this.debugSymbols?.debuginfo) o.debuginfo = this.debugSymbols.debuginfo; return o; } readFile(path: string) : FileData { diff --git a/src/platform/williams.ts b/src/platform/williams.ts index 54de92ab..0f36acdc 100644 --- a/src/platform/williams.ts +++ b/src/platform/williams.ts @@ -15,7 +15,7 @@ var WilliamsPlatform = function(mainElement, proto, options) { var self = this; this.__proto__ = new (proto ? proto : Base6809Platform)(); - var isDefender = options.isDefender; + var isDefender = options?.isDefender; var SCREEN_HEIGHT = 304; var SCREEN_WIDTH = 256; @@ -303,7 +303,7 @@ var WilliamsPlatform = function(mainElement, proto, options) { workerchannel = new WorkerSoundChannel(worker); audio.master.addChannel(workerchannel); - let rotate = options.rotate == null ? -90 : parseFloat(options.rotate); + let rotate = options?.rotate == null ? -90 : parseFloat(options.rotate); video = new RasterVideo(mainElement, SCREEN_WIDTH, SCREEN_HEIGHT, { rotate }); video.create(); $(video.canvas).click(function(e) { diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index fe324a58..a7bebcf6 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -230,11 +230,12 @@ export function linkLD65(step: BuildStep): BuildStepResult { if (fn.endsWith('.lst')) { var lstout = FS.readFile(fn, { encoding: 'utf8' }); lstout = lstout.split('\n\n')[1] || lstout; // remove header - var asmlines = []; // TODO: parseCA65Listing(lstout, symbolmap, params, false); - var srclines = parseCA65Listing(lstout, symbolmap, params, true, listings); + var asmlines = parseCA65Listing(lstout, symbolmap, params, false); + var srclines = parseCA65Listing(lstout, symbolmap, params, true); // TODO: listings param for ecs putWorkFile(fn, lstout); listings[fn] = { - lines: [], + asmlines: srclines.length ? asmlines : null, + lines: srclines.length ? srclines : asmlines, text: lstout }; } diff --git a/test/cli/testplatforms.js b/test/cli/testplatforms.js index 8889f072..64e336f9 100644 --- a/test/cli/testplatforms.js +++ b/test/cli/testplatforms.js @@ -196,9 +196,9 @@ async function testPlatform(platid, romname, maxframes, callback) { assert.equal(clks, proberec.countClocks()); } // debug tree - if (platform.getDebugTree) { + if (platform.getDebugTree != null) { var dbgtree = platform.getDebugTree(); - JSON.stringify(dbgtree); + if (dbgtree != null) JSON.stringify(dbgtree); } // misc assert.ok(platform.getDefaultExtension().startsWith('.'));