Merge branch 'ecs'

This commit is contained in:
Steven Hugg 2022-06-30 13:20:39 -05:00
commit fa09db6026
81 changed files with 17965 additions and 282 deletions

14
.gitignore vendored
View File

@ -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

View File

@ -25,6 +25,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:
@ -108,3 +110,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

90
ecsroot/kernel.txt Normal file
View File

@ -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 %{<bgcolor}
sta COLUBK
ldy %{<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
--
end
scope Root
entity kernel [SimpleKernel]
const lines = 100
end
entity player1 [Sprite,HasBitmap,HasColormap,HasYpos]
const plyrindex = 0
init height = 8
init xpos = 100
init ypos = 100
end
end scope

View File

@ -542,6 +542,7 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<script src="src/codemirror/basic.js"></script>
<script src="src/codemirror/wiz.js"></script>
<script src="src/codemirror/vasm.js"></script>
<script src="src/codemirror/ecs.js"></script>
<link rel="stylesheet" href="css/codemirror.css">
<script src="codemirror/addon/edit/matchbrackets.js"></script>
<script src="codemirror/addon/search/search.js"></script>

1087
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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",
@ -32,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",
@ -66,8 +69,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",

687
presets/vcs/ecs/jumper.ecs Normal file
View File

@ -0,0 +1,687 @@
//#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 {{<xvel}}
lsr
clc
adc #4
sta {{<xvel}}
---
// move left and right
on joyleft do with [HasXpos]
---
lda {{<xvel}}
sec
sbc #1
cmp #2
bcc @nomove
sta {{<xvel}}
@nomove:
---
on joyright do with [HasXpos]
---
lda {{<xvel}}
clc
adc #1
cmp #14
bcs @nomove
sta {{<xvel}}
@nomove:
---
end
system Gravity
on frame8 do foreach [Gravity]
---
lda {{get bitmap}}
cmp #{{&DudeStanding1}}-{{&Blank}}+1
bcc @skip
lda {{<yvel}}
cmp #14
bcs @nofall
clc
adc #2
bne @done
@nofall:
lda #15
@done:
sta {{<yvel}}
@skip:
---
on xmoved do with [TinyVelY]
---
lda {{<yvel}}
cmp #7
bcc @nodown
{{!checkplatform}}
@nodown:
---
on ymoved do with [TinyVelY]
---
lda {{<yvel}}
cmp #7
bcc @nodown
{{!checkplatform}}
@nodown:
---
on joybutton do with [Player]
---
; TODO
lda {{get bitmap}}
cmp #{{&DudeStanding1}}-{{&Blank}}+1
bcs @nojump
lda #0
sta {{get yvel}}
lda #{{&DudeJumping1}}-{{&Blank}}
sta {{set bitmap}}
{{!playsound 0 1 5}}
@nojump:
---
end
system PlatformJumper
on platformstopped do with [Jumper]
---
lda {{<ypos}}
and #$fe
sta {{<ypos}}
---
on platformstopped do if [Player]
---
lda {{<yvel}}
cmp #8
beq @skip
lda #8
sta {{<yvel}}
lda #{{&DudeStanding1}}-{{&Blank}}
sta {{set bitmap}}
{{!playsound 0 2 5}}
@skip:
---
on platformnotstopped do if [Player]
---
lda #{{&DudeJumping1}}-{{&Blank}}
sta {{set bitmap}}
---
end
resource PFCollideMask ---
.byte $10,$20,$40,$80 ; PF0
.byte $80,$40,$20,$10,$08,$04,$02,$01 ; PF1
.byte $01,$02,$04,$08,$10,$20,$40,$80 ; PF2
.byte $80,$40,$20,$10,$08,$04,$02,$01 ; PF2
.byte $01,$02,$04,$08,$10,$20,$40,$80 ; PF1
.byte $80,$40,$20,$10 ; PF0
---
resource PFCollideReg ---
.byte $d,$d,$d,$d
.byte $e,$e,$e,$e,$e,$e,$e,$e
.byte $f,$f,$f,$f,$f,$f,$f,$f
.byte $f,$f,$f,$f,$f,$f,$f,$f
.byte $e,$e,$e,$e,$e,$e,$e,$e
.byte $d,$d,$d,$d
---
system EnemyMotion
on stopped do if [Enemy]
---
lda #5
sta {{<yvel}}
---
on frame16 do foreach [Enemy]
---
lda {{<xvel}}
adc {{<Les.xpos}}
and #15
sta {{<xvel}}
---
end
system EnemyCollider
on preframe do once
---
sta CXCLR
---
on postframe do once
---
lda CXPPMM
bpl @nocollide
{{!enemyspawn}}
@nocollide:
---
on enemyspawn do once
---
{{!nextrand8}}
and #$e0
clc
adc {{get Les.xpos}}
and #$7f
clc
adc #10
sta {{set Rock.xpos}}
{{!nextrand8}}
and #$20
clc
adc {{get Les.ypos}}
and #$7f
clc
adc #24
sta {{set Rock.ypos}}
; TODO:
;inc {{set Rock.bitmap}}
---
end
system EnemyDifficulty
locals 1
on preframe do foreach [Enemy]
---
{{!nextrand8}}
lsr
lsr
cmp {{get PlayerScore.digits}}
bcs @nomove
; run away from player
lda {{get xpos}}
cmp #2
bcc @nox
cmp #140
bcs @nox
sec
sbc {{get Les.xpos}}
bcc @xdec
inc {{set xpos}}
inc {{set xpos}}
@xdec:
dec {{set xpos}}
@nox:
; move Y a bit
lda {{get ypos}}
cmp #40
bcc @nomove
cmp #170
bcs @nomove
sec
sbc {{get Les.ypos}}
bcc @ydec
inc {{set ypos}}
inc {{set ypos}}
@ydec:
dec {{set ypos}}
@nomove:
---
end
//
scope Main
using FrameLoop, ResetConsole
using Kernel2Digit, BCDMath
using Kernel2Sprite
using SetXPos, SetHorizPos
using TinyMover
using Gravity
using VersatilePlayfield
using PlatformJumper
using FrameCounter
entity FrameCount [FrameCount]
end
// TODO: this has to be after FrameCounter
// for velocity damping to work correctly
// but "after" keyword might be better
using Joystick, JoyButton, MoveJoyVel with [Player]
using EnemyCollider
using EnemyDifficulty
entity Level1 [VersatilePlayfield]
decode vcs_versatile
---
.................... .. .. ..
.................... .. .. 01
.................... e8 .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
............xxx..... .. .. ..
.............x...... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.........xxx........ .. .. ..
..........xx........ .. .. ..
...........x........ .. .. ..
.................... .. .. ..
.................... 18 .. ..
.................... .. .. ..
xxxx................ .. .. ..
xxx................. .. .. ..
xx.................. .. .. ..
x................... .. .. ..
.................... .. .. ..
.................... 28 .. ..
.................... .. .. ..
....xxxxxx.......... .. .. ..
......xxxx.......... .. .. ..
........x........... .. .. ..
.................... .. .. ..
............xxx..... .. .. ..
.............xx..xxx .. .. ..
..............x...xx .. .. ..
..................xx .. .. ..
...................x .. .. ..
...................x .. .. ..
.................... .. .. ..
.................... 38 .. ..
.....xxxx........... .. .. ..
......xxx........... .. .. ..
.......xx........... .. .. ..
........x........... .. .. ..
.................... .. .. ..
.................... 48 .. ..
............xxx..... .. .. ..
.............xx..... .. .. ..
..............x..... .. .. ..
.................... .. .. ..
.................... 58 .. ..
..................xx .. .. ..
.................xxx .. .. ..
................xxxx .. .. ..
..................xx .. .. ..
...................x .. .. ..
.................... .. .. ..
.................... .. 82 ..
.................... .. .. ..
.................... .. 84 ..
.................... .. .. ..
.................... .. 86 ..
.................... .. .. ..
.................... 68 .. ..
............xxxxxx.. .. .. ..
............xxxxxx.. .. .. ..
--- end
entity Blank [Bitmap,Colormap]
decode vcs_sprite
---
........ 00
---
end
entity DudeStanding1 [Bitmap,Colormap,Activity]
const activity = Standing
decode vcs_sprite
---
..xxx... f8
.xxxxx.. f8
x.xxxx.. f8
.xxx.x.. f8
x.xxxxx. f8
x.xxxx.. f8
..xx.... f8
..xxxx.. f8
...xxx.. f8
...xx... f8
...xx... f8
..xxx... 38
..xxx... 48
.x.xxx.. 58
x..xxx.. 68
x..xx.x. 78
x..xx..x 88
...xxx.. 98
..xxxx.. 98
..x..x.. 98
..x..xx. 98
.xx...x. 98
.x....x. 98
.x....x. f8
xx....xx f8
---
end
entity DudeJumping1 [Bitmap,Colormap,Activity]
const activity = Jumping
const colormapdata = DudeStanding1.colormapdata
decode vcs_bitmap
---
x.xxx...
.xxxxx..
x.xxxx..
.xxx.x..
..xxxxx.
..xxxx..
..xx....
..xxx...
...xxx..
...xx...
x..xx...
x.xxx..x
x.xxx.x.
.x.xxx..
...xx...
...xx...
...xx...
...xxx..
..xxxx..
.xx..xx.
.x....x.
.x....x.
.x....x.
.x....x.
xx....xx
---
end
entity Coin1 [Bitmap,Colormap]
decode vcs_sprite
---
00001000 ca
01001001 dc
00100010 ee
00000000 0e
00011110 0e
00100011 ee
01000101 ee
10001001 ee
11111001 ee
11101001 ee
11011001 ee
11111001 dc
11111001 ca
11011010 ca
10111100 ca
01111000 ca
---
end
entity Coin2 [Bitmap,Colormap]
decode vcs_sprite
---
..xxxx.. ca
.xxxxxx. dc
xxxxxxxx ee
x..xx..x 0e
xx.xx.xx 0e
xxxxxxxx ee
xxxxxxxx ee
.xx..xx. ee
.xxxxxx. ee
.xx..xx. ee
..xxxx.. dc
...xx... ca
........ ca
---
end
entity Empty [Sprite,HasBitmap,HasColormap]
const bitmap = #Blank
const colormap = #Blank
end
entity Les [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,TinyVelX,TinyVelY,Player,Jumper,Gravity]
var xpos = 73
var ypos = 90
var bitmap = #DudeJumping1
const colormap = #DudeStanding1
var plyrflags = $00
var xvel = 8
var yvel = 7
end
entity Rock [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Enemy]
var bitmap = #Coin1
const colormap = #Coin1
var xpos = 73
var ypos = 140
end
entity PlayerScore [BCDScore2]
var scorecolor = $ca
end
entity TimerScore [BCDScore2]
var scorecolor = $0e
var digits = $00
end
entity Kernel [KernelSection, BGColor]
const lines = 174
var bgcolor = $80
end
entity Slot0 [SpriteSlot]
var sprite = #Les
end
entity Slot1 [SpriteSlot]
var sprite = #Rock
end
// SOUNDS
using SoundEngine
entity SFXNull [SoundEffect]
const duration = 0
const sounddata = [0]
end
entity SFXJump [SoundEffect]
const duration = 14
const sounddata = [
$81,$83,$85,
$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,
$a8]
end
entity SFXBounce [SoundEffect]
const duration = 10
const sounddata = [
$02,$03,$04,$08,$10,$20,$40,$20,$10,
$a4]
end
entity SFXScore [SoundEffect]
const duration = 11
const sounddata = [
$02,$03,$04,$08,$10,$20,$10,$20,$10,$08,
$a2]
end
entity SFXScore2 [SoundEffect]
const duration = 11
const sounddata = [
$02,$03,$04,$08,$10,$20,$10,$20,$10,$08,
$a8]
end
// TODO: make sfx have priority over music?
entity SFX1 [SoundChannel] end
entity SFX2 [SoundChannel] end
// MUSIC
using MusicPlayer
entity MusicPlayer [MusicPlayer]
const volume = 10
const tempo = 31
end
entity Music1 [MusicChannel] end
entity Music2 [MusicChannel] end
//
using Random
system GameManager
// start the game? set timer to 99
// but only if timer = 0 and score = 0
on joybutton do select [#TimerScore]
---
lda {{<digits}}
bne @skip
lda {{<PlayerScore.digits}}
bne @skip
lda #$99
sta {{<digits}}
@skip:
---
// decrease the timer every 16 frames
on frame16 do foreach [#TimerScore]
---
lda {{<digits}}
beq @done
{{!SubBCD2 1}}
lda {{<digits}}
bne @done
{{!timerexpired}}
@done:
---
// add score for each new spawn
on enemyspawn do begin
foreach [#PlayerScore]
---
{{!AddBCD2 1}}
{{!playsound 1 3 10}}
---
// add timer for each new spawn
foreach [#TimerScore]
---
{{!AddBCD2 10}}
---
end
// subtract one when player wraps around vertically
on ymoved do begin
---
lda {{<yvel}}
cmp #7
bcc @nowrap
lda {{<ypos}}
cmp #2
bcs @nowrap
{{!playerwrapped}}
inc {{set ypos}}
@nowrap:
---
end
// TODO: doesn't work yet
on playerwrapped do with [#PlayerScore]
---
{{!SubBCD2 1}}
---
// each frame, game is either running or stopped
on preframe do once
---
lda {{<TimerScore.digits}}
bne @gameon
{{!gamestopped}}
jmp @done
@gameon:
{{!gamerunning}}
@done:
---
// stop everything when game is stopped
// TODO: if "Sprite" it overwrites array elements
on gamestopped do foreach [TinyVelX,TinyVelY]
// on gamestopped do foreach [Sprite]
---
lda #8
sta {{set xvel}}
sta {{set yvel}}
---
// set colors when game is stopped
on gamestopped do once
---
lda #$4c
sta {{set TimerScore.scorecolor}}
lda #$02
sta {{set Kernel.bgcolor}}
---
// set colors when game is running
on gamerunning do once
---
lda #$0a
sta {{set TimerScore.scorecolor}}
lda #$80
sta {{set Kernel.bgcolor}}
; blink when timer is close to zero
lda {{get TimerScore.digits}}
cmp #$20
bcs @nocolor
lda {{get FrameCount:frame}}
lsr
lsr
and #$07
ora #$40
sta {{set Kernel.bgcolor}}
@nocolor:
---
// play music when timer expires
on timerexpired do once
---
;{{^SampleMusic}}
{{!playmusic SampleMusic}}
---
on xxx_preframeloop do once
---
;{{^IntroMusic}}
{{!playmusic IntroMusic}}
---
end
entity [Random8] end
end
// TODO: use byte array too?
resource IntroMusic ---
.byte $27,$90,$2c,$8f,$2f,$8f,$33,$8f,$38,$8f,$3b,$8f,$20,$23,$88,$3a,$83,$38,$82,$37,$83,$38,$20,$ff
---

231
presets/vcs/ecs/kernel2.ecs Normal file
View File

@ -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 {{<lines}}
.define KPAD 32
; set height to zero in case no sprites
lda #0
sta {{$8}}
sta {{$9}}
---
on preframe do join
[SpriteSlot] with
[Sprite] limit 2
---
; set player object flags
lda {{<plyrflags}}
sta NUSIZ0,y
sta REFP0,y
; calculate screen height - ypos
lda KLINES
clc
adc KPAD
sec
sbc {{<ypos}}
sta {{$11}}
; calculate bitmap pointer
stx {{$12}} ; save X (Sprite index)
lda {{<bitmap}} ; deref bitmap
tax
lda {{<Bitmap:bitmapdata}},x
sec
sbc {{$11}}
sta {{$0}},y ; Y = sprite slot index
lda {{>Bitmap:bitmapdata}},x
sbc #0
sta {{$2}},y
; get bitmap height
lda {{<Bitmap:height}},x
sta {{$8}},y
; calculate colormap pointer
ldx {{$12}} ; restore X
lda {{<colormap}} ; deref colormap
tax
lda {{<Colormap:colormapdata}},x
sec
sbc {{$11}}
sta {{$4}},y
lda {{>Colormap:colormapdata}},x
sbc #0
sta {{$6}},y
; save ypos
ldx {{$12}} ; restore X
lda {{<ypos}}
sta {{$10}},y
---
on preframe do once
---
; shuffle pointers into (MSB, LSB) byte order
; L0 L1 H0 H1 -> L0 H0 L1 H1
lda {{$1}}
ldy {{$2}}
sty {{$1}}
sta {{$2}}
lda {{$5}}
ldy {{$6}}
sty {{$5}}
sta {{$6}}
---
on preframe do if [KernelSection,BGColor]
---
lda {{<bgcolor}}
sta COLUBK
---
on preframe do if [Missile,HasYpos]
---
lda KLINES
sec
sbc {{<ypos}}
sta {{$12}}
---
on kernel do with [KernelSection]
---
ldy #0
sty VDELP0
iny
sty VDELP1
---
on kernel do critical with [KernelSection]
---
ldy {{<lines}}
@LVScan:
{{!scanline 0}}
dey ; next scanline
{{!scanline 1}}
dey ; next scanline
bne @LVScan ; repeat until out of lines
---
on scanline do once
---
; draw player 0
lda {{$8}} ; height
dcp {{$10}} ; ypos
bcs @DoDraw1
lda #0
.byte $2C
@DoDraw1:
lda ({{$0}}),y
; .if {{#0}} = 0 ; TODO: configurable?
sta WSYNC
; .endif
sta GRP0
lda ({{$4}}),y
sta COLUP0
; draw player 1
lda {{$9}} ; height
dcp {{$11}} ; ypos
bcs @DoDraw2
lda #0
.byte $2C
@DoDraw2:
lda ({{$2}}),y
sta GRP1
lda ({{$6}}),y
sta COLUP1
---
on kernel do once
---
lda #0
sta GRP0
sta GRP1
sta GRP0
sta GRP1
---
on scanline do if [Missile,HasYpos]
---
cpy {{$12}}
php
pla
sta ENAM0
---
end
///
demo Main
using FrameLoop, Kernel2Sprite
using Joystick, MoveJoyX, MoveJoyY
using SetXPos, SetHorizPos
using SpriteShuffler, SpriteHider
entity Kernel [KernelSection, BGColor]
const lines = 192
const bgcolor = 0xa2
end
entity Bitmap1 [Bitmap]
const bitmapdata = [1, 1, 3, 7, 15, 31, 63, 127, 0]
const height = 8
end
entity Bitmap2 [Bitmap]
const bitmapdata = [$18,$3e,$ff,$ff,$ff,$ff,$3e,$18, 0]
const height = 8
end
entity Colormap1 [Colormap]
const colormapdata = [2, 4, 6, 8, 10, 12, 14, 14, 14]
end
entity Sprite0 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 50
init ypos = 150
init bitmap = #Bitmap2
init colormap = #Colormap1
const plyrflags = 0
end
entity Sprite1 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 100
init ypos = 60
init bitmap = #Bitmap1
init colormap = #Colormap1
const plyrflags = 3
end
entity Sprite2 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 80
init ypos = 90
init bitmap = #Bitmap2
init colormap = #Colormap1
const plyrflags = 2
end
entity Sprite3 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 40
init ypos = 150
init bitmap = #Bitmap1
init colormap = #Colormap1
const plyrflags = 0
end
/*
entity [Missile,HasXpos,HasYpos]
init xpos = 70
init ypos = 70
end
*/
entity Slot0 [SpriteSlot]
init sprite = #Sprite0
end
entity Slot1 [SpriteSlot]
init sprite = #Sprite1
end
entity Slot2 [SpriteSlot]
init sprite = #Sprite2
end
entity Slot3 [SpriteSlot]
init sprite = #Sprite3
end
end demo

189
presets/vcs/ecs/music.ecs Normal file
View File

@ -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
---

View File

@ -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 {{<seed}}
lsr
bcc :+
eor #$d4
:
sta {{<seed}}
---
on prevrand8 do foreach [Random8]
---
lda {{<seed}}
asl
bcc :+
eor #$a9
:
sta {{<seed}}
---
/* add entropy via joystick inputs */
on preframe do foreach [Random8]
---
lda {{<seed}}
eor SWCHA
beq :+
sta {{<seed}}
:
---
end

355
presets/vcs/ecs/score.ecs Normal file
View File

@ -0,0 +1,355 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
component BCDScore2
digits: 0..$ff
scorecolor: 0..$ff
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 {{<pfcolor}}
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
---
on kernel do
critical fit 72
with [BCDScore6,BGColor]
---
; Display the resulting 48x8 bitmap
; using the Digit0-5 pointers.
@LoopCount = {{$12}}
@Temp = {{$13}}
lda {{<bgcolor}}
sta WSYNC
sta COLUBK
lda #7
sta @LoopCount
SLEEPR 20 ; TODO?
:
ldy @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 @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 :- ; 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)
;;{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 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 {{<BCDScore2:scorecolor}}+0
sta COLUP0
lda {{<BCDScore2:scorecolor}}+1
sta COLUP1
---
on kernel do
critical fit 98
select [BCDScore2]
---
lda #7
sta {{$0}}
@Loop:
ldx #0
sta WSYNC
{{!compute2digit 0}}
.if {{%ecount}}>1
inx
{{!compute2digit 1}}
.else
{{!compute2digit 0}}
.endif
; playfield
dec {{$0}}
jpl @Loop
; dex
; stx PF1
---
on compute2digit do once
---
lda {{$1}} ; load 1st pf
sta PF1 ; store 1st pf
; first digit
lda {{<BCDScore2:digits}} + {{#0}}
pha
and #$0f
asl
asl
asl
{{!fetchdigit}}
and #$0f
ldy {{$2}} ; load 2nd pf
sta {{$1}} + {{#0}}
; second digit
pla
and #$f0
lsr
sty PF1 ; store 2nd pf
{{!fetchdigit}}
and #$f0
ora {{$1}} + {{#0}}
sta {{$1}} + {{#0}}
---
on fetchdigit do once
---
adc {{$0}}
tay
; TODO: select your own?
lda {{^FontTablePF}},y
---
end
resource FontTablePF ---
; Font table for digits 0-9 (4x8 pixels)
;;{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
;;
---
resource FontTablePFFancy ---
; Font table for digits 0-9 (4x8 pixels)
;;{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
.byte $00,$EE,$88,$88,$44,$22,$AA,$44
.byte $00,$CC,$22,$22,$66,$22,$22,$CC
.byte $00,$22,$22,$22,$EE,$AA,$AA,$88
.byte $00,$44,$AA,$22,$44,$88,$88,$EE
.byte $00,$44,$AA,$AA,$CC,$88,$AA,$44
.byte $00,$22,$22,$22,$22,$22,$AA,$EE
.byte $00,$44,$AA,$AA,$44,$AA,$AA,$44
.byte $00,$44,$AA,$22,$66,$AA,$AA,$44
;;
---
system BCDMath
locals 1
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
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
---
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
using Kernel2Digit
using JoyButton, BCDMath
entity [Player,BCDScore6,PFColor,BGColor]
init digits = 0x123456
init pfcolor = $3c
init bgcolor = $02
end
entity [BCDScore2]
init digits = 0x24
init scorecolor = $ce
end
entity [BCDScore2]
init digits = 0x56
init scorecolor = $3e
end
system IncScore
on joybutton do with [Player,BCDScore6]
---
{{!AddBCD4 $0210}}
---
end
end demo

105
presets/vcs/ecs/sound.ecs Normal file
View File

@ -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 {{$0}}
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

177
presets/vcs/ecs/sprites.ecs Normal file
View File

@ -0,0 +1,177 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
component Bitmap
bitmapdata: array of 0..255 baseoffset 31
height: 0..255
end
// TODO: remove?
component HasBitmap
end
component Colormap
colormapdata: array of 0..255 baseoffset 31
end
component HasColormap
colormap: [Colormap]
end
component Sprite
bitmap: [Bitmap]
plyrflags: 0..63
end
component HasXpos
xpos: 0..255
end
component HasYpos
ypos: 0..255
end
component Missile
index: 2..3
end
component SpriteSlot
sprite: [Sprite] // TODO: HasBitmap?
end
system JoyFaceDirection
on joyleft do with [Sprite]
---
lda {{<plyrflags}}
ora #$08
sta {{<plyrflags}}
---
on joyright do with [Sprite]
---
lda {{<plyrflags}}
and #$f7
sta {{<plyrflags}}
---
end
system MoveJoyX
on joyleft do with [HasXpos]
---
lda {{<xpos}}
sec
sbc #1
bcc @nomove
sta {{<xpos}}
@nomove:
---
on joyright do with [HasXpos]
---
lda {{<xpos}}
clc
adc #1
cmp #150
bcs @nomove
sta {{<xpos}}
@nomove:
---
end
system MoveJoyY
on joyup do with [HasYpos]
---
lda {{<ypos}}
sec
sbc #1
bcc @nomove
sta {{<ypos}}
@nomove:
---
on joydown do with [HasYpos]
---
lda {{<ypos}}
clc
adc #1
cmp #220
bcs @nomove
sta {{<ypos}}
@nomove:
---
end
system SetXPos
on preframe do join [SpriteSlot] with [Sprite]
limit 2
---
lda {{<xpos}}
{{!SetHorizPos}}
---
on preframe do foreach [Missile,HasXpos]
---
lda {{<xpos}}
ldy {{<index}}
{{!SetHorizPos}}
---
on prekernel do once
---
sta WSYNC
sta HMOVE
SLEEPR 24
sta HMCLR
---
end
system SpriteShuffler
locals 2
on postframe do select [SpriteSlot]
---
; load two sprite slots at left side of array
lda {{<SpriteSlot:sprite}}
sta {{$0}}
lda {{<SpriteSlot:sprite}}+1
sta {{$1}}
; move two slots to the left
ldx #0
@loop:
lda {{<SpriteSlot:sprite}}+2,x
sta {{<SpriteSlot:sprite}},x
inx
cpx #{{%ecount}}-2
bne @loop
; store two sprite slots at right side of array
lda {{$0}}
sta {{<SpriteSlot:sprite}}+{{%ecount}}-2
lda {{$1}}
sta {{<SpriteSlot:sprite}}+{{%ecount}}-1
---
end
system SpriteHider
locals 1
on postframe do select [SpriteSlot]
---
lda #{{%efullcount}}-1
sta {{$0}}
---
on postframe do
join [SpriteSlot]
with [Sprite,HasYpos]
limit 2
---
lda {{<ypos}}
cmp #192
bcc @skip
; swap this sprite slot with slot at end of array
lda {{<SpriteSlot:sprite}},y
pha
ldx {{$0}} ; clobbers X, but no longer used
lda {{<SpriteSlot:sprite}},x
sta {{<SpriteSlot:sprite}},y
pla
sta {{<SpriteSlot:sprite}},x
dec {{$0}}
@skip:
---
end

286
presets/vcs/ecs/vcs-ca65.h Normal file
View File

@ -0,0 +1,286 @@
.setcpu "6502X"
; TIA write registers
VSYNC := $00 ; ---- --1- This address controls vertical sync time by writing D1 into the VSYNC latch.
VBLANK := $01 ; 76-- --1- 1=Start VBLANK, 6=Enable INPT4, INPT5 latches, 7=Dump INPT1,2,3,6 to ground
WSYNC := $02 ; ---- ---- This address halts microprocessor by clearing RDY latch to zero. RDY is set true again by the leading edge of horizontal blank.
RSYNC := $03 ; ---- ---- This address resets the horizontal sync counter to define the beginning of horizontal blank time, and is used in chip testing.
NUSIZ0 := $04 ; --54 -210 \ 0,1,2: player copys'n'size, 4,5: missile size: 2^x pixels
NUSIZ1 := $05 ; --54 -210 /
COLUP0 := $06 ; 7654 321- color player 0
COLUP1 := $07 ; 7654 321- color player 1
COLUPF := $08 ; 7654 321- color playfield
COLUBK := $09 ; 7654 321- color background
CTRLPF := $0A ; --54 -210 0=reflect playfield, 1=pf uses player colors, 2=playfield over sprites 4,5=ballsize:2^x
REFP0 := $0B ; ---- 3--- reflect player 0
REFP1 := $0C ; ---- 3--- reflect player 1
PF0 := $0D ; DCBA ---- \ Playfield bits: ABCDEFGHIJKLMNOPQRST
PF1 := $0E ; EFGH IJKL > normal: ABCDEFGHIJKLMNOPQRSTABCDEFGHIJKLMNOPQRST
PF2 := $0F ; TSRQ PONM / reflect: ABCDEFGHIJKLMNOPQRSTTSRQPONMLKJIHGFEDCBA
RESP0 := $10 ; ---- ---- \
RESP1 := $11 ; ---- ---- \
RESM0 := $12 ; ---- ---- > reset players, missiles and the ball. The object will begin its serial graphics at the time of a horizontal line at which the reset address occurs.
RESM1 := $13 ; ---- ---- /
RESBL := $14 ; ---- ---- /
AUDC0 := $15 ; ---- 3210 audio control voice 0
AUDC1 := $16 ; ---- 3210 audio control voice 1
AUDF0 := $17 ; ---4 3210 frequency divider voice 0
AUDF1 := $18 ; ---4 3210 frequency divider voice 1
AUDV0 := $19 ; ---- 3210 audio volume voice 0
AUDV1 := $1A ; ---- 3210 audio volume voice 1
GRP0 := $1B ; 7654 3210 graphics player 0
GRP1 := $1C ; 7654 3210 graphics player 1
ENAM0 := $1D ; ---- --1- enable missile 0
ENAM1 := $1E ; ---- --1- enable missile 1
ENABL := $1F ; ---- --1- enable ball
HMP0 := $20 ; 7654 ---- write data (horizontal motion values) into the horizontal motion registers
HMP1 := $21 ; 7654 ---- write data (horizontal motion values) into the horizontal motion registers
HMM0 := $22 ; 7654 ---- write data (horizontal motion values) into the horizontal motion registers
HMM1 := $23 ; 7654 ---- write data (horizontal motion values) into the horizontal motion registers
HMBL := $24 ; 7654 ---- write data (horizontal motion values) into the horizontal motion registers
VDELP0 := $25 ; ---- ---0 delay player 0 by one vertical line
VDELP1 := $26 ; ---- ---0 delay player 1 by one vertical line
VDELBL := $27 ; ---- ---0 delay ball by one vertical line
RESMP0 := $28 ; ---- --1- keep missile 0 aligned with player 0
RESMP1 := $29 ; ---- --1- keep missile 1 aligned with player 1
HMOVE := $2A ; ---- ---- This address causes the horizontal motion register values to be acted upon during the horizontal blank time in which it occurs.
HMCLR := $2B ; ---- ---- This address clears all horizontal motion registers to zero (no motion).
CXCLR := $2C ; ---- ---- clears all collision latches
; TIA read registers
CXM0P := $00 ; xx00 0000 Read Collision M0-P1 M0-P0
CXM1P := $01 ; xx00 0000 M1-P0 M1-P1
CXP0FB := $02 ; xx00 0000 P0-PF P0-BL
CXP1FB := $03 ; xx00 0000 P1-PF P1-BL
CXM0FB := $04 ; xx00 0000 M0-PF M0-BL
CXM1FB := $05 ; xx00 0000 M1-PF M1-BL
CXBLPF := $06 ; x000 0000 BL-PF -----
CXPPMM := $07 ; xx00 0000 P0-P1 M0-M1
INPT0 := $08 ; x000 0000 Read Pot Port 0
INPT1 := $09 ; x000 0000 Read Pot Port 1
INPT2 := $0A ; x000 0000 Read Pot Port 2
INPT3 := $0B ; x000 0000 Read Pot Port 3
INPT4 := $0C ; x000 0000 Read Input (Trigger) 0
INPT5 := $0D ; x000 0000 Read Input (Trigger) 1
; RIOT
SWCHA := $0280
SWACNT := $0281
SWCHB := $0282
SWBCNT := $0283
INTIM := $0284 ; Timer output
TIMINT := $0285
TIM1T := $0294
TIM8T := $0295
TIM64T := $0296
TIM1024T := $0297
;-------------------------------------------------------------------------------
; SLEEP duration
; Original author: Thomas Jentzsch
; Inserts code which takes the specified number of cycles to execute. This is
; useful for code where precise timing is required.
; ILLEGAL-OPCODE VERSION DOES NOT AFFECT FLAGS OR REGISTERS.
; LEGAL OPCODE VERSION MAY AFFECT FLAGS
; Uses illegal opcode (DASM 2.20.01 onwards).
.macro SLEEP cycles
.if cycles < 0 || cycles = 1
.error "MACRO ERROR: 'SLEEP': Duration must be >= 2"
.endif
.if cycles & 1
.ifndef NO_ILLEGAL_OPCODES
nop 0
.else
bit VSYNC
.endif
.repeat (cycles-3)/2
nop
.endrep
.else
.repeat cycles/2
nop
.endrep
.endif
.endmacro
;-------------------------------------------------------------------------------
; VERTICAL_SYNC
; revised version by Edwin Blink -- saves bytes!
; Inserts the code required for a proper 3 scanline vertical sync sequence
; Note: Alters the accumulator
; OUT: A = 0
.macro VERTICAL_SYNC
lda #%1110 ; each '1' bits generate a VSYNC ON line (bits 1..3)
: sta WSYNC ; 1st '0' bit resets Vsync, 2nd '0' bit exit loop
sta VSYNC
.ifdef VERTICAL_SYNC_MACRO
pha
VERTICAL_SYNC_MACRO
pla
.endif
lsr
bne :- ; branch until VYSNC has been reset
.endmacro
;-------------------------------------------------------
; Usage: TIMER_SETUP lines
; where lines is the number of scanlines to skip (> 2).
; The timer will be set so that it expires before this number
; of scanlines. A WSYNC will be done first.
.macro TIMER_SETUP lines
.local cycles
cycles = ((lines * 76) - 13)
; special case for when we have two timer events in a line
; and our 2nd event straddles the WSYNC boundary
.if (cycles .mod 64) < 12
lda #(cycles / 64) - 1
sta WSYNC
.else
lda #(cycles / 64)
sta WSYNC
.endif
sta TIM64T
.endmacro
;-------------------------------------------------------
; Use with TIMER_SETUP to wait for timer to complete.
; Performs a WSYNC afterwards.
.macro TIMER_WAIT
.local waittimer
waittimer:
lda INTIM
bne waittimer
sta WSYNC
.endmacro
;-------------------------------------------------------------------------------
; CLEAN_START
; Original author: Andrew Davie
; Standardised start-up code, clears stack, all TIA registers and RAM to 0
; Sets stack pointer to $FF, and all registers to 0
; Sets decimal mode off, sets interrupt flag (kind of un-necessary)
; Use as very first section of code on boot (ie: at reset)
; Code written to minimise total ROM usage - uses weird 6502 knowledge :)
.macro CLEAN_START
.local CLEAR_STACK
sei
cld
ldx #0
txa
tay
CLEAR_STACK: dex
txs
pha
bne CLEAR_STACK ; SP=$FF, X = A = Y = 0
.endmacro
;-------------------------------------------------------
; SET_POINTER
; Original author: Manuel Rotschkar
;
; Sets a 2 byte RAM pointer to an absolute address.
;
; Usage: SET_POINTER pointer, address
; Example: SET_POINTER SpritePTR, SpriteData
;
; Note: Alters the accumulator, NZ flags
; IN 1: 2 byte RAM location reserved for pointer
; IN 2: absolute address
.macro SET_POINTER ptr, addr
lda #<addr
sta ptr
lda #>addr
sta ptr+1
.endmacro
; assume NTSC unless PAL defined
.ifndef PAL
PAL = 0
.endif
; 192 visible scanlines for NTSC, 228 for PAL
.if PAL
SCANLINES = 228
LINESD12 = 19
.else
SCANLINES = 192
LINESD12 = 16
.endif
; start of frame -- vsync and set back porch timer
.macro FRAME_START
VERTICAL_SYNC
.if PAL
TIMER_SETUP 44
.else
TIMER_SETUP 36
.endif
.endmacro
; end of back porch -- start kernel
.macro KERNEL_START
TIMER_WAIT
lda #0
sta VBLANK
.if !PAL
TIMER_SETUP 194
.endif
.endmacro
; end of kernel -- start front porch timer
.macro KERNEL_END
.if !PAL
TIMER_WAIT
.endif
lda #2
sta VBLANK
.if PAL
TIMER_SETUP 36
.else
TIMER_SETUP 28
.endif
.endmacro
; end of frame -- jump to frame start
.macro FRAME_END
TIMER_WAIT
.endmacro
;-----------------------------------------------------------
; SLEEPR - sleep macro that uses JSR/RTS for 12 cycle delays
; Requires a lone RTS instruction with the label "Return"
; (note: may fool 8bitworkshop's Anaylze CPU Timing feature)
.macro SLEEPR cycles
.if cycles >= 14 || cycles = 12
jsr Return
SLEEPR (cycles-12)
.else
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

336
presets/vcs/ecs/vcslib.ecs Normal file
View File

@ -0,0 +1,336 @@
//#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
{{!start}} ; start main routine
.segment "VECTORS"
ZeroByte: .byte $00
Return: .byte $60
VecNMI:
VecReset: .word __Reset
VecBRK: .word __BRK
.code
---
end
component Player
end
component KernelSection
lines: 1..255 default 1
end
component BGColor
bgcolor: 0..255
end
component FGColor
fgcolor: 0..255
end
component PFColor
pfcolor: 0..255
end
component Playfield
pf: 0..0xffffff
end
component AsymPlayfield
pfleft: 0..0xffffff
pfright: 0..0xffffff
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}}
jmp @NextFrame ; loop to next frame
---
on postframe do once
---
lsr SWCHB ; test Game Reset switch
bcs @NoStart
{{emit 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] 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 critical fit 22 once
---
; 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
---
end
system StandardKernel
on wsync do once
---
sta WSYNC
---
on preframe do foreach [KernelSection] limit 1
---
{{!wsync}}
{{!kernelsetup}}
---
on kernel do foreach [KernelSection]
---
{{!wsync}}
{{!kernelsetup}}
{{!kerneldraw}}
{{!kerneldone}}
---
on kerneldraw do with [KernelSection]
---
ldy {{<lines}}
@loop:
{{!prescanline}}
{{!wsync}}
{{!scanline}}
dey
bne @loop
---
on kernelsetup do if [BGColor]
---
lda {{<bgcolor}}
sta COLUBK
---
on kernelsetup do if [PFColor]
---
lda {{get pfcolor}}
sta COLUPF
---
on kernelsetup do if [Playfield]
---
lda {{get pf 0}}
sta PF0
lda {{get pf 8}}
sta PF1
lda {{get pf 16}}
sta PF2
---
end
component SimpleCollidable
lastx: 0..255
lasty: 0..255
end
system SimpleCollision
on postframe do with [SimpleCollidable]
---
lda CXP0FB
bpl @nocollide
lda {{<lastx}}
sta {{<xpos}}
lda {{<lasty}}
sta {{<ypos}}
@nocollide:
---
on ymoved do with [SimpleCollidable]
---
lda {{<xpos}}
sta {{<lastx}}
lda {{<ypos}}
sta {{<lasty}}
---
end
component FrameCount
frame: 0..255
end
system FrameCounter
on postframe do with [FrameCount]
---
inc {{set frame}}
.ifdef EVENT__frame256
jne @noframe256
{{emit frame256}}
@noframe256:
.endif
---
// TODO: only .if responds to events
on postframe do with [FrameCount]
---
jmp @go
.ifdef EVENT__frame4
@frame4:
{{emit frame4}}
jmp @done
.endif
.ifdef EVENT__frame8
@frame8:
{{emit frame8}}
jmp @done
.endif
.ifdef EVENT__frame16
@frame16:
{{emit frame16}}
jmp @done
.endif
@go:
lda {{get frame}}
.ifdef EVENT__frame16
and #15
jeq @frame16
.endif
.ifdef EVENT__frame8
and #7
cmp #3
jeq @frame8
.endif
.ifdef EVENT__frame4
and #3
cmp #1
jeq @frame4
.endif
@done:
---
end
///
demo Main
using FrameLoop, ResetConsole
using StandardKernel, JoyButton
entity [Player]
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $1a
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $16
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $14
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $12
end
entity [KernelSection,BGColor,Playfield]
const lines = 10
const bgcolor = $14
const pf = 0x125244
end
entity Trees [KernelSection,BGColor,PFColor,Playfield]
const lines = 50
const bgcolor = $14
const pf = 0x112244
end
entity Trees2 [KernelSection,BGColor,PFColor,Playfield]
const lines = 50
const bgcolor = $16
const pf = 0x124
end
entity [KernelSection,BGColor,Playfield]
const lines = 10
const bgcolor = $18
const pf = 0
end
system Local
on joybutton do with [#Trees] ---
inc {{set pfcolor}}
---
end
end demo

View File

@ -0,0 +1,155 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
import "sprites.ecs"
component TinyVelX
// TODO: -8..7? packed?
xvel: 0..15 default 8
end
component TinyVelY
// TODO: -8..7? packed?
yvel: 0..15 default 8
end
system TinyMover
locals 1
on postframe do foreach [HasXpos,TinyVelX]
---
lda {{<FrameCount:frame}}
and #7 ; reduce frame # to 0..7
tay
lda {{^VMaskTable}},y ; lookup frame bit mask
ldy {{<xvel}} ; xvel from 0..15
and {{^VAndTable}},y ; mask with velocity table
jeq @nomove ; no bits set? no move
lda {{^DeltaVTable}},y ; lookup position delta
clc
adc {{<xpos}} ; add to coordinate
sta {{<xpos}} ; store to coordinate
{{!xmoved}} ; maybe modify it?
@nomove:
---
on postframe do foreach [HasXpos,TinyVelY]
---
lda {{<FrameCount:frame}}
and #7 ; reduce frame # to 0..7
tay
lda {{^VMaskTable}},y ; lookup frame bit mask
ldy {{<yvel}} ; xvel from 0..15
and {{^VAndTable}},y ; mask with velocity table
jeq @nomove ; no bits set? no move
lda {{^DeltaVTable}},y ; lookup position delta
clc
adc {{<ypos}} ; add to coordinate
sta {{<ypos}} ; store to coordinate
{{!ymoved}} ; maybe modify it?
@nomove:
---// set player reflection bit
on xmoved do with [Sprite]
---
pha
lda {{<plyrflags}}
and #255-8
ora {{^VReflectTab}},y
sta {{<plyrflags}}
and #7
tay
lda {{^VXLimitTab}},y
sta {{$0}}
pla
---
// TODO: x/y limits should be properties
on xmoved do with [HasXpos]
---
cmp #2
jcc @left
cmp {{$0}}
jcc @skip
@right:
{{!xlimit_right}}
jne @skip
@left:
{{!xlimit_left}}
@skip:
---
on xlimit_right do with [HasXpos]
---
lda {{$0}}
sta {{<xpos}}
---
on xlimit_left do with [HasXpos]
---
lda #2
sta {{<xpos}}
---
end
resource DeltaVTable ---
; this table tells us which direction to move
; for each of the 16 velocity values (-1, 0 or +1)
.byte $fe,$fe,$ff,$ff,$ff,$ff,$ff,$00
.byte $00,$01,$01,$01,$01,$01,$02,$02
---
resource VAndTable ---
; this table contains a bitmask for each of the 16
; velocity values -- it's ANDed with a global shift
; register that tells us which frames to update position
.byte %11111111 ;16/8
.byte %01110111 ;12/8
.byte %11111111 ;8/8
.byte %01110111 ;6/8
.byte %10101010 ;4/8
.byte %01010010 ;3/8
.byte %10001000 ;2/8
.byte %00000000 ;0
.byte %00000000 ;0
.byte %10001000 ;2/8
.byte %01010010 ;3/8
.byte %10101010 ;4/8
.byte %01110111 ;6/8
.byte %11111111 ;8/8
.byte %01110111 ;12/8
.byte %11111111 ;16/8
---
resource VMaskTable ---
.byte %00000001
.byte %00000010
.byte %00000100
.byte %00001000
.byte %00010000
.byte %00100000
.byte %01000000
.byte %10000000
---
resource VReflectTab ---
.byte 8,8,8,8,8,8,8,8
.byte 0,0,0,0,0,0,0,0
---
/*
if 000, X one copy
if 001, X X two copies, close
if 010, X X two copies, medium
if 011, X X X three copies, close
if 100, X X Two copies, far
if 101, XX one copy, double width
if 110, X X X 3 copies, medium
if 111, XXXX one copy, quad width
*/
resource VXLimitTab ---
.byte 152-8*1
.byte 152-8*3
.byte 152-8*5
.byte 152-8*5
.byte 152-8*9
.byte 152-8*2
.byte 152-8*9
.byte 152-8*4
---

View File

@ -0,0 +1,69 @@
component VersatilePlayfield
data: array of 0..255 baseoffset -1
end
system VersatilePlayfield
locals 2
on preframe do with [VersatilePlayfield]
---
lda {{<data}}
sta {{$0}}
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
---
on checkplatform do with [HasXpos,HasYpos]
---
lda {{<xpos}}
clc
adc #6
lsr
lsr
tay
lda {{^PFCollideMask}},y
pha
lda {{^PFCollideReg}},y
pha
lda #176 ; TODO: adjust for kernel size
sec
sbc {{<ypos}}
and #$fe
tay
; TODO: use system local pointer
pla
cmp ({{$0}}),y ; match register?
bne @nostop1
dey
pla
and ({{$0}}),y ; mask bitmap?
beq @nostop
{{!platformstopped}}
jmp @done
@nostop1:
pla
@nostop:
{{!platformnotstopped}}
@done:
---
end

View File

@ -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

117
src/codemirror/ecs.js Normal file
View File

@ -0,0 +1,117 @@
// 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 = [
'begin', 'end',
'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', 'resource',
'const', 'locals', 'var',
'enum', 'default', 'array', 'baseoffset', 'critical', 'fit', 'asc', 'desc',
'on', 'do', 'emit', 'limit',
'once', 'foreach', 'with', 'join', 'if', 'select', 'unroll',
];
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', 'else', 'endif', 'eif', 'repeat', 'repend'
];
var directives = new Map();
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]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i;
var tags = /^\{\{.*\}\}/;
var comment = /^\/\/.*$/;
var mlcomment = /^\/\*.*?\*\//s; // TODO
var codedelim = /^---/;
return {
startState: function () {
return {
context: 0
};
},
token: function (stream, state) {
if (stream.eatSpace())
return null;
if (stream.match(tags)) {
return 'meta';
}
if (stream.match(comment)) {
return 'comment';
}
if (stream.match(mlcomment)) {
return 'comment';
}
if (stream.match(codedelim)) {
state.context = state.context ^ 1;
return null;
}
var w;
if (stream.eatWhile(/\w/)) {
w = stream.current();
var cur = w.toLowerCase();
var style = directives.get(cur);
if (style)
return style;
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();
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");
});

View File

@ -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];
@ -432,6 +435,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
}

View File

@ -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) {

213
src/common/ecs/README.md Normal file
View File

@ -0,0 +1,213 @@
# 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
nested scopes for game modes? (title / demo / play)
access parent data from child scope
critical data fields
if accessed in critical section, make critical
ignore arrays that aren't referenced
use DASM for multipass?
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
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 {{<room}}
lda {{<Room:west}},y
sta {{<room}}
---
on preframe do
with y=[SpriteSlot] limit 2
with x=y.sprite
---
lda {{<xpos}}
{{!SetHorizPos}}
---
on preframe do
foreach x=[Missile,HasXpos]
---
lda {{<xpos}}
ldy {{<index}}
{{!SetHorizPos}}
---
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 {{<ypos}}
bne @nowrap
---
select [#PlayerScore] ---
{{!SubBCD2 1}}}
---
---
@nowrap:
---
end

239
src/common/ecs/binpack.ts Normal file
View File

@ -0,0 +1,239 @@
var debug = false;
export interface BoxConstraints {
left?: number;
top?: number;
width: number;
height: number;
box?: PlacedBox;
label?: string;
}
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;
parents: 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 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, boxes?: Box[]) : Box[] {
let result = [];
if (!boxes) boxes = this.boxes;
for (let box of 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 (!boxesContain(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: BoxConstraints) : Box | null {
let bestscore = 0;
let best = null;
for (let f of this.free) {
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 + f.left * 0.001);
if (score > bestscore) {
best = f;
bestscore = score;
if (score == 1) break;
}
}
}
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;
if (score == 1) break;
}
}
}
return best;
}
add(b: PlacedBox) {
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
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(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?
}
}
export class Packer {
bins : Bin[] = [];
boxes : BoxConstraints[] = [];
defaultPlacement : BoxPlacement = BoxPlacement.TopLeft; //TODO
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 {
for (let bin of this.bins) {
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,
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 (place == BoxPlacement.BottomLeft || place == BoxPlacement.BottomRight) {
let h = box.bottom - box.top;
box.top = parent.bottom - h;
box.bottom = parent.bottom;
}
if (place == BoxPlacement.TopRight || place == BoxPlacement.BottomRight) {
let w = box.right - box.left;
box.left = parent.right - w;
box.right = parent.right;
}
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);
return { parents, place, bin, ...box };
}
}
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 += `<svg viewBox="0 0 ${r.width} ${r.height}" xmlns="http://www.w3.org/2000/svg"><style><![CDATA[text {font: 1px sans-serif;}]]></style>`;
for (let bin of this.bins) {
let be = bin.extents;
s += '<g>'
s += `<rect width="${be.right-be.left}" height="${be.bottom-be.top}" stroke="black" stroke-width="0.5" fill="none"/>`;
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 += `<rect width="${b.right-b.left}" height="${b.bottom-b.top}" x="${b.left}" y="${b.top}" stroke="black" stroke-width="0.25" fill="#ccc"/>`;
if (b.top == texty) textx += 10; else textx = be.right+1;
texty = b.top;
if (box.label) s += `<text x="${textx}" y="${texty}" height="1">${box.label}</text>`;
}
}
/*
for (let b of bin.free) {
s += `<rect width="${b.right-b.left}" height="${b.bottom-b.top}" x="${b.left}" y="${b.top}" stroke="red" stroke-width="0.1" fill="none"/>`;
}
*/
s += '</g>'
}
s += `</svg>`;
return s;
}
toSVGUrl() {
return `data:image/svg+xml;base64,${btoa(this.toSVG())}`;
}
}

851
src/common/ecs/compiler.ts Normal file
View File

@ -0,0 +1,851 @@
import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer";
import { SourceLocated, SourceLocation } from "../workertypes";
import { newDecoder } from "./decoder";
import { Action, ActionContext, 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, QueryExpr } from "./ecs";
export enum ECSTokenType {
Ellipsis = 'ellipsis',
Operator = 'operator',
Relational = 'relational',
QuotedString = 'quoted-string',
Integer = 'integer',
CodeFragment = 'code-fragment',
Placeholder = 'placeholder',
}
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;
currentContext: ActionContext | null = null;
includeDebugInfo = false;
constructor(
public readonly em: EntityManager,
public readonly isMainFile: boolean) {
super();
//this.includeEOL = true;
this.setTokenRules([
{ 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: /[%][01]+/ },
{ 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: /\/\*.*?\*\// },
{ type: TokenType.EOL, regex: /[\n\r]+/ },
{ type: TokenType.Ignore, regex: /\s+/ },
]);
this.errorOnCatchAll = true;
}
annotate<T extends SourceLocated>(fn: () => T) {
let start = this.peekToken();
let obj = fn();
let end = this.lasttoken;
let $loc = end ? mergeLocs(start.$loc, end.$loc) : start.$loc;
if (obj) (obj as SourceLocated).$loc = $loc;
return obj;
}
parseFile(text: string, path: string) {
this.tokenizeFile(text, path);
while (!this.isEOF()) {
let top = this.parseTopLevel();
if (top) {
let t = top;
this.annotate(() => t); // TODO? typescript bug?
}
}
this.runDeferred();
}
getImportFile: (path: string) => string;
importFile(path: string) {
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}".`);
this.em.imported[path] = true;
let comp = new ECSCompiler(this.em, false);
comp.includeDebugInfo = this.includeDebugInfo; // TODO: clone compiler
try {
comp.parseFile(text, path);
} catch (e) {
for (var err of comp.errors) this.errors.push(err);
throw e;
}
}
}
parseTopLevel() {
//this.skipBlankLines();
let tok = this.expectTokens(['component', 'system', 'scope', 'resource', 'import', 'demo', '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 == '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') {
if (this.isMainFile) {
let scope = this.parseScope();
scope.isDemo = true;
this.expectToken('demo');
return scope;
} else {
this.skipDemo(); // don't even parse it, just skip it
return;
}
}
if (tok.str == 'comment') {
this.expectTokenTypes([ECSTokenType.CodeFragment]);
return;
}
this.compileError(`Unexpected top-level keyword: ${tok.str}`);
}
skipDemo() {
var tok;
while ((tok = this.consumeToken()) && !this.isEOF()) {
if (tok.str == 'end' && this.peekToken().str == 'demo') {
this.consumeToken();
return;
}
}
throw new ECSError(`Expected "end demo" after a "demo" declaration.`);
}
parseComponentDefinition(): ComponentType {
let name = this.expectIdent().str;
let fields = [];
this.em.deferComponent(name);
while (this.peekToken().str != 'end') {
fields.push(this.parseComponentField());
}
this.expectToken('end');
return { name, fields };
}
parseComponentField(): DataField {
let name = this.expectIdent();
this.expectToken(':', 'I expected either a ":" or "end" here.'); // TODO
let type = this.parseDataType();
return { name: name.str, $loc: name.$loc, ...type };
}
parseDataType(): DataType {
if (this.peekToken().type == 'integer') {
let lo = this.parseIntegerConstant();
this.expectToken('..');
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");
this.checkLowerLimit(hi, lo, "int range");
// TODO: use default value?
let defvalue;
if (this.ifToken('default')) {
defvalue = this.parseIntegerConstant();
}
// TODO: check types
return { dtype: 'int', lo, hi, defvalue } as IntType;
}
if (this.peekToken().str == '[') {
return { dtype: 'ref', query: this.parseQuery() } as RefType;
}
if (this.ifToken('array')) {
let index: IntType | undefined = undefined;
if (this.peekToken().type == ECSTokenType.Integer) {
index = this.parseDataType() as IntType;
}
this.expectToken('of');
let elem = this.parseDataType();
let baseoffset;
if (this.ifToken('baseoffset')) {
baseoffset = this.parseIntegerConstant();
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.parseIntegerConstant();
}
return { dtype: 'int', lo, hi, defvalue, enums } as IntType;
}
throw this.compileError(`I expected a data type here.`);
}
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();
// TODO: move to expr
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());
}
if (tok.str == '#') {
this.consumeToken();
let reftype = field.dtype == 'ref' ? field as RefType : undefined;
return this.parseEntityForwardRef(reftype);
}
// TODO?
return this.parseIntegerConstant();
// TODO: throw this.compileError(`I expected a ${field.dtype} here.`);
}
parseEntityForwardRef(reftype?: RefType): ForwardRef {
let token = this.expectIdent();
return { reftype, token };
}
parseDataArray() {
this.expectToken('[');
let arr = this.parseList(this.parseIntegerConstant, ',');
this.expectToken(']');
return arr;
}
expectInteger(): number {
let s = this.consumeToken().str;
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;
}
parseSystem(): System {
let name = this.expectIdent().str;
let actions: Action[] = [];
let system: System = { name, actions };
let cmd;
while ((cmd = this.expectTokens(['on', 'locals', 'end']).str) != 'end') {
if (cmd == 'on') {
let action = this.annotate(() => this.parseAction(system));
actions.push(action);
} else if (cmd == 'locals') {
system.tempbytes = this.parseIntegerConstant();
} else {
this.compileError(`Unexpected system keyword: ${cmd}`);
}
}
return system;
}
parseResource(): System {
let name = this.expectIdent().str;
let tempbytes;
if (this.peekToken().str == 'locals') {
this.consumeToken();
tempbytes = this.parseIntegerConstant();
}
let system: System = { name, tempbytes, actions: [] };
let expr = this.annotate(() => this.parseBlockStatement());
let action: Action = { expr, event: name };
system.actions.push(action);
return system;
}
parseAction(system: System): Action {
// TODO: unused events?
const event = this.expectIdent().str;
this.expectToken('do');
let fitbytes = undefined;
let critical = undefined;
if (this.ifToken('critical')) critical = true;
if (this.ifToken('fit')) fitbytes = this.parseIntegerConstant();
let expr = this.annotate(() => this.parseBlockStatement());
//query, join, select, direction,
let action : Action = { expr, event, fitbytes, critical };
return action as Action;
}
parseQuery() {
let q: Query = { include: [] };
let start = this.expectToken('[');
this.parseList(() => this.parseQueryItem(q), ',');
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();
}
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) {
throw this.compileError('You can only reference specific entities inside of a scope.');
}
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 '-'.`);
}
}
parseEventName() {
return this.expectIdent().str;
}
parseEventList() {
return this.parseList(this.parseEventName, ",");
}
parseCode(): string { // TODOActionNode[] {
// TODO: add $loc
let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
let code = tok.str.substring(3, tok.str.length - 3);
// TODO: add after parsing maybe?
let lines = code.split('\n');
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);
// TODO: return nodes
return code;
}
addDebugInfo(lines: string[], startline: number) {
const re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines
for (let i = 0; i < lines.length; i++) {
if (!lines[i].match(re))
lines[i] = this.em.dialect.debug_line(this.path, startline + i) + '\n' + lines[i];
}
}
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(['end', 'using', 'entity', 'scope', 'comment', 'system']).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]);
}
// 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);
this.currentScope.newSystemInstanceWithDefaults(sys);
}
}
this.currentScope = scope.parent || null;
return scope;
}
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);
}
}
parseEntity(): Entity {
if (!this.currentScope) { throw this.internalError(); }
const scope = this.currentScope;
let entname = '';
if (this.peekToken().type == TokenType.Ident) {
entname = this.expectIdent().str;
}
let etype = this.parseEntityArchetype();
let entity = this.currentScope.newEntity(etype, entname);
let cmd2: string;
// 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'; // TODO: remove?
if (cmd == 'init' || cmd == 'const') {
this.parseInitConst(cmd, scope, entity);
} else if (cmd == 'decode') {
this.parseDecode(scope, entity);
}
}
return entity;
}
parseInitConst(cmd: string, scope: EntityScope, entity: Entity) {
// TODO: check data types
let name = this.expectIdent().str;
let { c, f } = this.getEntityField(entity, name);
let symtype = scope.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(f);
if ((valueOrRef as ForwardRef).token != null) {
this.deferred.push(() => {
this.lasttoken = (valueOrRef as ForwardRef).token; // for errors
let refvalue = this.resolveEntityRef(scope, valueOrRef as ForwardRef);
if (cmd == 'const') scope.setConstValue(entity, c, f, refvalue);
if (cmd == 'init') scope.setInitValue(entity, c, f, refvalue);
});
} else {
if (cmd == 'const') scope.setConstValue(entity, c, f, valueOrRef as DataValue);
if (cmd == 'init') scope.setInitValue(entity, c, f, valueOrRef as DataValue);
}
}
parseDecode(scope: EntityScope, entity: Entity) {
let decoderid = this.expectIdent().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) { throw this.compileError(`I can't find a "${decoderid}" decoder.`); }
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, entry[1] as DataValue);
}
}
getEntityField(e: Entity, name: string): ComponentFieldPair {
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) { throw this.internalError(); }
return { c: component, f: field };
}
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;
}
findEntityByName(scope: EntityScope, token: Token) {
let name = token.str;
let eref = scope.entities.find(e => e.name == name);
if (!eref) {
throw this.compileError(`I couldn't find an entity named "${name}" in this scope.`, token.$loc)
}
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);
let entities = scope.entitiesMatching(atypes);
if (entities.length == 0)
throw this.compileError(`This entity doesn't seem to fit the reference type.`, ref.token.$loc);
id -= entities[0].id;
}
return id;
}
parseSystemInstanceRef(): SystemInstance {
let name = this.expectIdent().str;
let system = this.em.getSystemByName(name);
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;
}
parseSystemInstanceParameters(): SystemInstanceParameters {
let scope = this.currentScope;
if (scope == null) throw this.internalError();
if (this.peekToken().str == '[') {
return { query: this.parseQuery() };
}
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) {
this.em.exportToFile(src);
}
export() {
let src = new SourceFileExport();
src.line(this.em.dialect.debug_file(this.path));
for (let path of Object.keys(this.em.imported))
src.line(this.em.dialect.debug_file(path));
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`);
}
// 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 EntityFieldOp;
}
// 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 EntityFieldOp;
}
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, ',');
}
// TODO: annotate with location
parseBlockStatement(): Statement {
let valtype : IntType = { dtype:'int', lo:0, hi: 0 } // TODO?
if (this.peekToken().type == ECSTokenType.CodeFragment) {
return { valtype, code: this.parseCode() };
}
if (this.ifToken('begin')) {
let stmts = [];
while (this.peekToken().str != 'end') {
stmts.push(this.annotate(() => this.parseBlockStatement()));
}
this.expectToken('end');
return { valtype, stmts };
}
let cmd = this.peekToken();
if (SELECT_TYPE.includes(cmd.str as any)) {
return this.parseQueryStatement();
}
throw this.compileError(`There should be a statement or "end" here.`, cmd.$loc);
}
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') {
if (this.peekToken().str == '[') this.compileError(`A "${select}" action can't include a query.`)
} else {
query = this.parseQuery();
}
if (select == 'join') {
this.expectToken('with');
join = this.parseQuery();
}
if (this.ifToken('limit')) {
if (!query) { this.compileError(`A "${select}" query can't include a limit.`); }
else query.limit = this.parseIntegerConstant();
}
const all_modifiers = ['asc', 'desc']; // TODO
const modifiers = this.parseModifiers(all_modifiers);
let direction = undefined;
if (modifiers['asc']) direction = 'asc';
else if (modifiers['desc']) direction = 'desc';
let body = this.annotate(() => this.parseBlockStatement());
return { select, query, join, direction, all, stmts: [body], loop: select == 'foreach' } as QueryExpr;
}
}
///
export class ECSActionCompiler extends Tokenizer {
constructor(
public readonly context: ActionContext) {
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;
}
}

203
src/common/ecs/decoder.ts Normal file
View File

@ -0,0 +1,203 @@
import { SourceLocation } from "../workertypes";
import { DataValue, ECSError } from "./ecs";
export interface DecoderResult {
properties: {[name: string] : DataValue}
}
abstract class LineDecoder {
curline: number = 0; // for debugging, zero-indexed
lines : string[][]; // array of token arrays
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<n; i++) {
let bit;
let ch = s.charAt(i);
if (ch == 'x' || ch == 'X' || ch == '1') bit = 1;
else if (ch == '.' || ch == '0') bit = 0;
else throw new ECSError('need x or . (or 0 or 1)');
if (bit) {
if (msbfirst) b |= 1 << (n-1-i);
else b |= 1 << i;
}
}
return b;
}
assertTokens(toks: string[], count: number) {
if (toks.length != count) throw new ECSError(`Expected ${count} tokens on line.`);
}
hex(s: string) {
let v = parseInt(s, 16);
if (isNaN(v)) throw new ECSError(`Invalid hex value: ${s}`)
return v;
}
getErrorLocation($loc: SourceLocation): SourceLocation {
// TODO: blank lines mess this up
$loc.line += this.curline + 1;
return $loc;
}
abstract parse() : DecoderResult;
}
export class VCSSpriteDecoder extends LineDecoder {
parse() {
let height = this.lines.length;
let bitmapdata = new Uint8Array(height);
let colormapdata = new Uint8Array(height);
for (let i=0; i<height; i++) {
this.curline = height - 1 - i;
let toks = this.lines[this.curline];
this.assertTokens(toks, 2);
bitmapdata[i] = this.decodeBits(toks[0], 8, true);
colormapdata[i] = this.hex(toks[1]);
}
return {
properties: {
bitmapdata, colormapdata, height: height-1
}
}
}
}
export class VCSBitmapDecoder extends LineDecoder {
parse() {
let height = this.lines.length;
let bitmapdata = new Uint8Array(height);
for (let i=0; i<height; i++) {
this.curline = height - 1 - i;
let toks = this.lines[this.curline];
this.assertTokens(toks, 1);
bitmapdata[i] = this.decodeBits(toks[0], 8, true);
}
return {
properties: {
bitmapdata, height: height-1
}
}
}
}
export class VCSPlayfieldDecoder extends LineDecoder {
parse() {
let height = this.lines.length;
let pf = new Uint32Array(height);
for (let i=0; i<height; i++) {
this.curline = height - 1 - i;
let toks = this.lines[this.curline];
this.assertTokens(toks, 1);
let pf0 = this.decodeBits(toks[0].substring(0,4), 4, false) << 4;
let pf1 = this.decodeBits(toks[0].substring(4,12), 8, true);
let pf2 = this.decodeBits(toks[0].substring(12,20), 8, false);
pf[i] = (pf0 << 0) | (pf1 << 8) | (pf2 << 16);
}
return {
properties: {
pf
}
}
}
}
export class VCSVersatilePlayfieldDecoder extends LineDecoder {
parse() {
let height = this.lines.length;
let data = new Uint8Array(height * 2);
data.fill(0x3f);
// pf0 pf1 pf2 colupf colubk ctrlpf trash
const regs = [0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x3f];
let prev = [0,0,0,0,0,0,0];
let cur = [0,0,0,0,0,0,0];
for (let i=0; i<height; i++) {
let dataofs = height*2 - i*2;
this.curline = i;
let toks = this.lines[this.curline];
if (toks.length == 2) {
data[dataofs - 1] = this.hex(toks[0]);
data[dataofs - 2] = this.hex(toks[1]);
continue;
}
this.assertTokens(toks, 4);
cur[0] = this.decodeBits(toks[0].substring(0,4), 4, false) << 4;
cur[1] = this.decodeBits(toks[0].substring(4,12), 8, true);
cur[2] = this.decodeBits(toks[0].substring(12,20), 8, false);
if (toks[1] != '..') cur[3] = this.hex(toks[1]);
if (toks[2] != '..') cur[4] = this.hex(toks[2]);
if (toks[3] != '..') cur[5] = this.hex(toks[3]);
let changed = [];
for (let j=0; j<cur.length; j++) {
if (cur[j] != prev[j])
changed.push(j);
}
if (changed.length > 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[dataofs - 1] = regs[chgidx];
data[dataofs - 2] = cur[chgidx];
prev[chgidx] = cur[chgidx];
}
return {
properties: {
data
}
}
}
}
export class VCSBitmap48Decoder extends LineDecoder {
parse() {
let height = this.lines.length;
let bitmap0 = new Uint8Array(height);
let bitmap1 = new Uint8Array(height);
let bitmap2 = new Uint8Array(height);
let bitmap3 = new Uint8Array(height);
let bitmap4 = new Uint8Array(height);
let bitmap5 = new Uint8Array(height);
for (let i=0; i<height; i++) {
this.curline = height - 1 - i;
let toks = this.lines[this.curline];
this.assertTokens(toks, 1);
bitmap0[i] = this.decodeBits(toks[0].slice(0,8), 8, true);
bitmap1[i] = this.decodeBits(toks[0].slice(8,16), 8, true);
bitmap2[i] = this.decodeBits(toks[0].slice(16,24), 8, true);
bitmap3[i] = this.decodeBits(toks[0].slice(24,32), 8, true);
bitmap4[i] = this.decodeBits(toks[0].slice(32,40), 8, true);
bitmap5[i] = this.decodeBits(toks[0].slice(40,48), 8, true);
}
return {
properties: {
bitmap0, bitmap1, bitmap2, bitmap3, bitmap4, bitmap5,
height: height-1
}
}
}
}
export function newDecoder(name: string, text: string) : LineDecoder | undefined {
let cons = (DECODERS as any)[name];
if (cons) return new cons(text);
}
const DECODERS = {
'vcs_sprite': VCSSpriteDecoder,
'vcs_bitmap': VCSBitmapDecoder,
'vcs_playfield': VCSPlayfieldDecoder,
'vcs_versatile': VCSVersatilePlayfieldDecoder,
'vcs_bitmap48': VCSBitmap48Decoder,
}

1981
src/common/ecs/ecs.ts Normal file

File diff suppressed because it is too large Load Diff

32
src/common/ecs/main.ts Normal file
View File

@ -0,0 +1,32 @@
import { readFileSync } from "fs";
import { ECSCompiler } from "./compiler";
import { Dialect_CA65, EntityManager, SourceFileExport } from "./ecs";
class ECSMain {
compiler = new ECSCompiler(new EntityManager(new Dialect_CA65()), true); // TODO
constructor(readonly args: string[]) {
}
run() {
for (let path of this.args) {
let text = readFileSync(path, 'utf-8');
try {
this.compiler.parseFile(text, path);
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}`);
}
}
}
}
new ECSMain(process.argv.slice(2)).run();

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"noImplicitThis": true,
"noImplicitAny": true,
"preserveConstEnums": true,
"alwaysStrict": true,
"strictNullChecks": true,
}
}

247
src/common/tokenizer.ts Normal file
View File

@ -0,0 +1,247 @@
import type { SourceLocation, SourceLine, WorkerError, SourceLocated } from "./workertypes";
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',
CatchAll = 'catch-all',
}
export class Token implements SourceLocated {
str: string;
type: string;
eol: boolean; // end of line?
$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 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;
eof: Token;
errorOnCatchAll = false;
deferred: (() => void)[] = [];
constructor() {
this.errors = [];
this.lineno = 0;
this.lineindex = [];
this.tokens = [];
}
setTokenRuleSet(ruleset: TokenizerRuleSet) {
this.ruleset = ruleset;
}
setTokenRules(rules: TokenRule[]) {
this.setTokenRuleSet(new TokenizerRuleSet(rules));
}
tokenizeFile(contents: string, path: string) {
this.path = path;
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: "", eol: true, $loc: { path: this.path, line: this.lineno } };
this.pushToken(this.eof);
}
_tokenize(text: string): void {
// iterate over each token via re_toks regex
let m: RegExpMatchArray;
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
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 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) {
case TokenType.CatchAll:
if (this.errorOnCatchAll) {
this.compileError(`I didn't expect the character "${m[0]}" here.`, loc);
}
default:
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;
}
break;
}
}
if (!found) {
this.compileError(`Could not parse token: <<${m[0]}>>`)
}
}
}
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() {
return this.compileError("Internal error.");
}
notImplementedError() {
return this.compileError("Not yet implemented.");
}
compileError(msg: string, loc?: SourceLocation, loc2?: SourceLocation) : CompileError {
this.addError(msg, loc);
//if (loc2 != null) this.addError(`...`, loc2);
let e = new CompileError(msg, loc);
throw e;
return e;
}
peekToken(lookahead?: number): Token {
let tok = this.tokens[lookahead || 0];
return tok ? tok : this.eof;
}
consumeToken(): Token {
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;
if (str != tokstr) {
this.compileError(msg || `There should be a "${str}" here.`);
}
return tok;
}
expectTokens(strlist: readonly string[], msg?: string): Token {
let tok = this.consumeToken();
let tokstr = tok.str;
if (!strlist.includes(tokstr)) {
this.compileError(msg || `These keywords are valid here: ${strlist.join(', ')}`);
}
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<T>(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;
}
runDeferred() {
while (this.deferred.length) {
this.deferred.shift()();
}
}
}

View File

@ -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];
}

View File

@ -19,6 +19,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;

View File

@ -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);
}
@ -150,6 +152,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 +202,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;
}
@ -370,7 +378,7 @@ export class CodeProject {
this.sendBuild();
}
processBuildResult(data: WorkerOutputResult<any>) {
processBuildListings(data: WorkerOutputResult<any> | WorkerErrorResult) {
// TODO: link listings with source files
if (data.listings) {
this.listings = data.listings;
@ -382,6 +390,14 @@ export class CodeProject {
lst.assemblyfile = new SourceFile(lst.asmlines, lst.text);
}
}
}
processBuildResult(data: WorkerOutputResult<any>) {
this.processBuildListings(data);
this.processBuildSegments(data);
}
processBuildSegments(data: WorkerOutputResult<any>) {
// save and sort segment list
var segs = (this.platform.getMemoryMap && this.platform.getMemoryMap()["main"]) || [];
if (data.segments) { segs = segs.concat(data.segments || []); }
@ -401,6 +417,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) {

View File

@ -115,7 +115,8 @@ const TOOL_TO_SOURCE_STYLE = {
'silice': 'verilog',
'wiz': 'text/x-wiz',
'vasmarm': 'vasm',
'armips': 'vasm'
'armips': 'vasm',
'ecs': 'ecs',
}
const TOOL_TO_HELPURL = {
@ -323,7 +324,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);
});
@ -1829,8 +1830,10 @@ function _addIncludeFile() {
addFileToProject("Include", ".inc", (s) => { return '\t.include "'+s+'"' });
else if (tool == 'verilator')
addFileToProject("Verilog", ".v", (s) => { return '`include "'+s+'"' });
else if (tool == 'wiz')
else if (tool == 'wiz')
addFileToProject("Include", ".wiz", (s) => { return 'import "'+s+'";' });
else if (tool == 'ecs')
addFileToProject("Include", ".ecs", (s) => { return 'import "'+s+'"' });
else
alertError("Can't add include file to this project type (" + tool + ")");
}

View File

@ -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 <ident> --- ... ---
/*
var re3 = /\bdecode\s+(\w+)\s*---(.+?)---/gims;
while (m = re3.exec(data)) {
}
*/
return result;
}

View File

@ -36,6 +36,7 @@ const MODEDEFS = {
markdown: { lineWrap: true },
fastbasic: { noGutters: true },
basic: { noLineNumbers: true, noGutters: true }, // TODO: not used?
ecs: { theme: 'mbo', isAsm: true },
}
export var textMapFunctions = {
@ -213,7 +214,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};
@ -278,6 +279,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));
}
@ -579,7 +581,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);
}
@ -589,6 +591,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;

View File

@ -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) {

View File

@ -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";
}

View File

@ -16,7 +16,7 @@ var WilliamsPlatform = function(mainElement, proto, options) {
this.__proto__ = new (proto ? proto : Base6809Platform)();
options = options || {};
var isDefender = options.isDefender;
var isDefender = options?.isDefender;
var SCREEN_HEIGHT = 304;
var SCREEN_WIDTH = 256;
@ -304,7 +304,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) {

190
src/test/testecs.ts Normal file
View File

@ -0,0 +1,190 @@
import { spawnSync } from "child_process";
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";
import { Dialect_CA65, EntityManager, SourceFileExport } from "../common/ecs/ecs";
function testCompiler() {
let em = new EntityManager(new Dialect_CA65()); // TODO
let c = new ECSCompiler(em, true);
try {
c.parseFile(`
// 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
system SimpleKernel
locals 8
on preframe do with [Kernel] --- JUNK_AT_END
lda #5
sta #6
Label:
---
end
comment ---
---
scope Root
entity kernel [Kernel]
const lines = 0xc0
//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 = #bmp
end
end
`, 'foo.txt');
//console.log('json', c.em.toJSON());
let src = new SourceFileExport();
c.exportToFile(src);
// TODO: test?
console.log(src.toString());
return em;
} catch (e) {
console.log(e);
for (let err of c.errors) {
console.log(err);
}
console.log(c.tokens);
throw e;
}
}
// TODO: files in markdown?
// TODO: jsr OperModeExecutionTree?
describe('Tokenizer', function() {
it('Should use Compiler', function() {
testCompiler();
});
});
describe('Compiler', function() {
let testdir = './test/ecs/';
let files = readdirSync(testdir).filter(f => f.endsWith('.ecs'));
files.forEach((ecsfn) => {
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, true);
compiler.getImportFile = (path: string) => {
return readFileSync(testdir + path, 'utf-8');
}
let code = readFileSync(ecspath, 'utf-8');
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;
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}`);
}
});
});
});
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() {
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 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 unaligned 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 },
]
);
});
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 },
]
);
});
});

177
src/test/testutil.ts Normal file
View File

@ -0,0 +1,177 @@
import assert from "assert";
import { describe } from "mocha";
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,
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: '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");
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.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);
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');
});
});

View File

@ -1,7 +1,7 @@
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 { 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"
@ -18,21 +18,30 @@ 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
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];
// 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) {
@ -45,9 +54,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 +74,7 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) {
linenum--;
} else if (!dbg) {
lines.push({
path: curpath,
line: linenum,
offset: offset + segofs,
insns: insns,
@ -90,7 +100,7 @@ function parseCA65Listing(code: string, symbols, params, dbg: boolean) {
}
}
}
return lines;
return origlines;
}
export function assembleCA65(step: BuildStep): BuildStepResult {
@ -118,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);
@ -216,15 +230,25 @@ 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 srclines = parseCA65Listing(lstout, symbolmap, params, true);
putWorkFile(fn, lstout);
// 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,
text: lstout
};
console.log(step);
let isECS = step.debuginfo?.entities != null; // TODO
if (isECS) {
var asmlines = [];
var srclines = parseCA65Listing(lstout, symbolmap, params, true, listings);
listings[fn] = {
lines: [],
text: lstout
}
} else {
var asmlines = parseCA65Listing(lstout, symbolmap, params, false);
var srclines = parseCA65Listing(lstout, symbolmap, params, true); // TODO: listings param for ecs
listings[fn] = {
asmlines: srclines.length ? asmlines : null,
lines: srclines.length ? srclines : asmlines,
text: lstout
}
}
}
}
return {

51
src/worker/tools/ecs.ts Normal file
View File

@ -0,0 +1,51 @@
import { ECSCompiler } from "../../common/ecs/compiler";
import { Dialect_CA65, ECSError, EntityManager } from "../../common/ecs/ecs";
import { CompileError } from "../../common/tokenizer";
import { CodeListingMap } from "../../common/workertypes";
import { BuildStep, BuildStepResult, fixParamsWithDefines, gatherFiles, getWorkFileAsString, putWorkFile, staleFiles } from "../workermain";
export function assembleECS(step: BuildStep): BuildStepResult {
let em = new EntityManager(new Dialect_CA65()); // TODO
let compiler = new ECSCompiler(em, true);
compiler.getImportFile = (path: string) => {
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);
fixParamsWithDefines(step.path, step.params);
try {
compiler.includeDebugInfo = true;
compiler.parseFile(code, step.path);
let outtext = compiler.export().toString();
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);
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 };
} else {
throw e;
}
}
return {
nexttool: "ca65",
path: destpath,
args: [destpath],
files: [destpath].concat(step.files),
listings,
debuginfo
};
}
}

View File

@ -431,6 +431,7 @@ export interface BuildStep extends WorkerBuildStep {
code?
prefix?
maxts?
debuginfo?
};
///
@ -518,6 +519,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);
@ -529,6 +535,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);
@ -540,6 +547,7 @@ class Builder {
args:step.result.args
};
}
linkstep.debuginfo = step.debuginfo; // TODO: multiple debuginfos
}
// process with another tool?
if ('nexttool' in step.result) {
@ -1102,6 +1110,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,
@ -1137,7 +1146,8 @@ var TOOLS = {
'wiz': misc.compileWiz,
'armips': arm.assembleARMIPS,
'vasmarm': arm.assembleVASMARM,
'js': script.runJavascript,
//'js': script.runJavascript,
'ecs': ecs.assembleECS,
}
var TOOL_PRELOADFS = {
@ -1166,6 +1176,8 @@ var TOOL_PRELOADFS = {
'fastbasic': '65-atari8',
'silice': 'Silice',
'wiz': 'wiz',
'ecs-vcs': '65-none', // TODO: support multiple platforms
'ecs-nes': '65-nes', // TODO: support multiple platforms
}
//const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay)); // for testing

View File

@ -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('.'));

View File

@ -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'));
});
});

16
test/ecs/basic1.ecs Normal file
View File

@ -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

0
test/ecs/basic1.txt Normal file
View File

148
test/ecs/bigdemo.ecs Normal file
View File

@ -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

885
test/ecs/bigdemo.txt Normal file
View File

@ -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
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 Kernel6Digit__preframe__25__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 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

31
test/ecs/errors1.ecs Normal file
View File

@ -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 demo

1
test/ecs/errors1.txt Normal file
View File

@ -0,0 +1 @@
21:I found more than one field named "color" for this entity.

685
test/ecs/jumper.ecs Normal file
View File

@ -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 {{<xvel}}
lsr
clc
adc #4
sta {{<xvel}}
---
// move left and right
on joyleft do with [HasXpos]
---
lda {{<xvel}}
sec
sbc #1
cmp #2
bcc @nomove
sta {{<xvel}}
@nomove:
---
on joyright do with [HasXpos]
---
lda {{<xvel}}
clc
adc #1
cmp #14
bcs @nomove
sta {{<xvel}}
@nomove:
---
end
system Gravity
on frame8 do foreach [Gravity]
---
lda {{get bitmap}}
cmp #{{&DudeStanding1}}-{{&Blank}}+1
bcc @skip
lda {{<yvel}}
cmp #14
bcs @nofall
clc
adc #2
bne @done
@nofall:
lda #15
@done:
sta {{<yvel}}
@skip:
---
on xmoved do with [TinyVelY]
---
lda {{<yvel}}
cmp #7
bcc @nodown
{{!checkplatform}}
@nodown:
---
on ymoved do with [TinyVelY]
---
lda {{<yvel}}
cmp #7
bcc @nodown
{{!checkplatform}}
@nodown:
---
on joybutton do with [Player]
---
; TODO
lda {{get bitmap}}
cmp #{{&DudeStanding1}}-{{&Blank}}+1
bcs @nojump
lda #0
sta {{get yvel}}
lda #{{&DudeJumping1}}-{{&Blank}}
sta {{set bitmap}}
{{!playsound 0 1 5}}
@nojump:
---
end
system PlatformJumper
on platformstopped do with [Jumper]
---
lda {{<ypos}}
and #$fe
sta {{<ypos}}
---
on platformstopped do if [Player]
---
lda {{<yvel}}
cmp #8
beq @skip
lda #8
sta {{<yvel}}
lda #{{&DudeStanding1}}-{{&Blank}}
sta {{set bitmap}}
{{!playsound 0 2 5}}
@skip:
---
on platformnotstopped do if [Player]
---
lda #{{&DudeJumping1}}-{{&Blank}}
sta {{set bitmap}}
---
end
resource PFCollideMask ---
.byte $10,$20,$40,$80 ; PF0
.byte $80,$40,$20,$10,$08,$04,$02,$01 ; PF1
.byte $01,$02,$04,$08,$10,$20,$40,$80 ; PF2
.byte $80,$40,$20,$10,$08,$04,$02,$01 ; PF2
.byte $01,$02,$04,$08,$10,$20,$40,$80 ; PF1
.byte $80,$40,$20,$10 ; PF0
---
resource PFCollideReg ---
.byte $d,$d,$d,$d
.byte $e,$e,$e,$e,$e,$e,$e,$e
.byte $f,$f,$f,$f,$f,$f,$f,$f
.byte $f,$f,$f,$f,$f,$f,$f,$f
.byte $e,$e,$e,$e,$e,$e,$e,$e
.byte $d,$d,$d,$d
---
system EnemyMotion
on stopped do if [Enemy]
---
lda #5
sta {{<yvel}}
---
on frame16 do foreach [Enemy]
---
lda {{<xvel}}
adc {{<Les.xpos}}
and #15
sta {{<xvel}}
---
end
system EnemyCollider
on preframe do once
---
sta CXCLR
---
on postframe do once
---
lda CXPPMM
bpl @nocollide
{{!enemyspawn}}
@nocollide:
---
on enemyspawn do once
---
{{!nextrand8}}
and #$e0
clc
adc {{get Les.xpos}}
and #$7f
clc
adc #10
sta {{set Rock.xpos}}
{{!nextrand8}}
and #$20
clc
adc {{get Les.ypos}}
and #$7f
clc
adc #24
sta {{set Rock.ypos}}
; TODO:
;inc {{set Rock.bitmap}}
---
end
system EnemyDifficulty
locals 1
on preframe do foreach [Enemy]
---
{{!nextrand8}}
lsr
lsr
cmp {{get PlayerScore.digits}}
bcs @nomove
; run away from player
lda {{get xpos}}
cmp #2
bcc @nox
cmp #140
bcs @nox
sec
sbc {{get Les.xpos}}
bcc @xdec
inc {{set xpos}}
inc {{set xpos}}
@xdec:
dec {{set xpos}}
@nox:
; move Y a bit
lda {{get ypos}}
cmp #40
bcc @nomove
cmp #170
bcs @nomove
sec
sbc {{get Les.ypos}}
bcc @ydec
inc {{set ypos}}
inc {{set ypos}}
@ydec:
dec {{set ypos}}
@nomove:
---
end
//
scope Main
using FrameLoop, ResetConsole
using Kernel2Digit, BCDMath
using Kernel2Sprite
using SetXPos, SetHorizPos
using TinyMover
using Gravity
using VersatilePlayfield
using PlatformJumper
using FrameCounter
entity FrameCount [FrameCount]
end
// TODO: this has to be after FrameCounter
// for velocity damping to work correctly
// but "after" keyword might be better
using Joystick, JoyButton, MoveJoyVel with [Player]
using EnemyCollider
using EnemyDifficulty
entity Level1 [VersatilePlayfield]
decode vcs_versatile
---
.................... .. .. ..
.................... .. .. 01
.................... e8 .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
............xxx..... .. .. ..
.............x...... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.........xxx........ .. .. ..
..........xx........ .. .. ..
...........x........ .. .. ..
.................... .. .. ..
.................... 18 .. ..
.................... .. .. ..
xxxx................ .. .. ..
xxx................. .. .. ..
xx.................. .. .. ..
x................... .. .. ..
.................... .. .. ..
.................... 28 .. ..
.................... .. .. ..
....xxxxxx.......... .. .. ..
......xxxx.......... .. .. ..
........x........... .. .. ..
.................... .. .. ..
............xxx..... .. .. ..
.............xx..xxx .. .. ..
..............x...xx .. .. ..
..................xx .. .. ..
...................x .. .. ..
...................x .. .. ..
.................... .. .. ..
.................... 38 .. ..
.....xxxx........... .. .. ..
......xxx........... .. .. ..
.......xx........... .. .. ..
........x........... .. .. ..
.................... .. .. ..
.................... 48 .. ..
............xxx..... .. .. ..
.............xx..... .. .. ..
..............x..... .. .. ..
.................... .. .. ..
.................... 58 .. ..
..................xx .. .. ..
.................xxx .. .. ..
................xxxx .. .. ..
..................xx .. .. ..
...................x .. .. ..
.................... .. .. ..
.................... .. 82 ..
.................... .. .. ..
.................... .. 84 ..
.................... .. .. ..
.................... .. 86 ..
.................... .. .. ..
.................... 68 .. ..
............xxxxxx.. .. .. ..
............xxxxxx.. .. .. ..
--- end
entity Blank [Bitmap,Colormap]
decode vcs_sprite
---
........ 00
---
end
entity DudeStanding1 [Bitmap,Colormap,Activity]
const activity = Standing
decode vcs_sprite
---
..xxx... f8
.xxxxx.. f8
x.xxxx.. f8
.xxx.x.. f8
x.xxxxx. f8
x.xxxx.. f8
..xx.... f8
..xxxx.. f8
...xxx.. f8
...xx... f8
...xx... f8
..xxx... 38
..xxx... 48
.x.xxx.. 58
x..xxx.. 68
x..xx.x. 78
x..xx..x 88
...xxx.. 98
..xxxx.. 98
..x..x.. 98
..x..xx. 98
.xx...x. 98
.x....x. 98
.x....x. f8
xx....xx f8
---
end
entity DudeJumping1 [Bitmap,Colormap,Activity]
const activity = Jumping
const colormapdata = DudeStanding1.colormapdata
decode vcs_bitmap
---
x.xxx...
.xxxxx..
x.xxxx..
.xxx.x..
..xxxxx.
..xxxx..
..xx....
..xxx...
...xxx..
...xx...
x..xx...
x.xxx..x
x.xxx.x.
.x.xxx..
...xx...
...xx...
...xx...
...xxx..
..xxxx..
.xx..xx.
.x....x.
.x....x.
.x....x.
.x....x.
xx....xx
---
end
entity Coin1 [Bitmap,Colormap]
decode vcs_sprite
---
00001000 ca
01001001 dc
00100010 ee
00000000 0e
00011110 0e
00100011 ee
01000101 ee
10001001 ee
11111001 ee
11101001 ee
11011001 ee
11111001 dc
11111001 ca
11011010 ca
10111100 ca
01111000 ca
---
end
entity Coin2 [Bitmap,Colormap]
decode vcs_sprite
---
..xxxx.. ca
.xxxxxx. dc
xxxxxxxx ee
x..xx..x 0e
xx.xx.xx 0e
xxxxxxxx ee
xxxxxxxx ee
.xx..xx. ee
.xxxxxx. ee
.xx..xx. ee
..xxxx.. dc
...xx... ca
........ ca
---
end
entity Empty [Sprite,HasBitmap,HasColormap]
const bitmap = #Blank
const colormap = #Blank
end
entity Les [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,TinyVelX,TinyVelY,Player,Jumper,Gravity]
var xpos = 73
var ypos = 90
var bitmap = #DudeJumping1
const colormap = #DudeStanding1
var plyrflags = $00
var xvel = 8
var yvel = 7
end
entity Rock [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Enemy]
var bitmap = #Coin1
const colormap = #Coin1
var xpos = 73
var ypos = 140
end
entity PlayerScore [BCDScore2]
var scorecolor = $ca
end
entity TimerScore [BCDScore2]
var scorecolor = $0e
var digits = $00
end
entity Kernel [KernelSection, BGColor]
const lines = 174
var bgcolor = $80
end
entity Slot0 [SpriteSlot]
var sprite = #Les
end
entity Slot1 [SpriteSlot]
var sprite = #Rock
end
// SOUNDS
using SoundEngine
entity SFXNull [SoundEffect]
const duration = 0
const sounddata = [0]
end
entity SFXJump [SoundEffect]
const duration = 14
const sounddata = [
$81,$83,$85,
$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,
$a8]
end
entity SFXBounce [SoundEffect]
const duration = 10
const sounddata = [
$02,$03,$04,$08,$10,$20,$40,$20,$10,
$a4]
end
entity SFXScore [SoundEffect]
const duration = 11
const sounddata = [
$02,$03,$04,$08,$10,$20,$10,$20,$10,$08,
$a2]
end
entity SFXScore2 [SoundEffect]
const duration = 11
const sounddata = [
$02,$03,$04,$08,$10,$20,$10,$20,$10,$08,
$a8]
end
// TODO: make sfx have priority over music?
entity SFX1 [SoundChannel] end
entity SFX2 [SoundChannel] end
// MUSIC
using MusicPlayer
entity MusicPlayer [MusicPlayer]
const volume = 10
const tempo = 31
end
entity Music1 [MusicChannel] end
entity Music2 [MusicChannel] end
//
using Random
system GameManager
// start the game? set timer to 99
on joybutton do select [#TimerScore]
---
lda {{<digits}}
bne @skip
lda #$99
sta {{<digits}}
@skip:
---
// decrease the timer every 16 frames
on frame16 do foreach [#TimerScore]
---
lda {{<digits}}
beq @done
{{!SubBCD2 1}}
lda {{<digits}}
bne @done
{{!timerexpired}}
@done:
---
// add score for each new spawn
on enemyspawn do begin
foreach [#PlayerScore]
---
{{!AddBCD2 1}}
{{!playsound 1 3 10}}
---
// add timer for each new spawn
foreach [#TimerScore]
---
{{!AddBCD2 10}}
---
end
// subtract one when player wraps around vertically
on ymoved do begin
---
lda {{<yvel}}
cmp #7
bcc @nowrap
lda {{<ypos}}
cmp #2
bcs @nowrap
{{!playerwrapped}}
inc {{set ypos}}
@nowrap:
---
end
// TODO: doesn't work yet
on playerwrapped do with [#PlayerScore]
---
{{!SubBCD2 1}}
---
// each frame, game is either running or stopped
on preframe do once
---
lda {{<TimerScore.digits}}
bne @gameon
{{!gamestopped}}
jmp @done
@gameon:
{{!gamerunning}}
@done:
---
// stop everything when game is stopped
// TODO: if "Sprite" it overwrites array elements
on gamestopped do foreach [TinyVelX,TinyVelY]
// on gamestopped do foreach [Sprite]
---
lda #8
sta {{set xvel}}
sta {{set yvel}}
---
// set colors when game is stopped
on gamestopped do once
---
lda #$4c
sta {{set TimerScore.scorecolor}}
lda #$02
sta {{set Kernel.bgcolor}}
---
// set colors when game is running
on gamerunning do once
---
lda #$0a
sta {{set TimerScore.scorecolor}}
lda #$80
sta {{set Kernel.bgcolor}}
; blink when timer is close to zero
lda {{get TimerScore.digits}}
cmp #$20
bcs @nocolor
lda {{get FrameCount:frame}}
lsr
lsr
and #$07
ora #$40
sta {{set Kernel.bgcolor}}
@nocolor:
---
// play music when timer expires
on timerexpired do once
---
;{{^SampleMusic}}
{{!playmusic SampleMusic}}
---
on xxx_preframeloop do once
---
;{{^IntroMusic}}
{{!playmusic IntroMusic}}
---
end
entity [Random8]
end
end
// TODO: use byte array too?
resource IntroMusic ---
.byte $27,$90,$2c,$8f,$2f,$8f,$33,$8f,$38,$8f,$3b,$8f,$20,$23,$88,$3a,$83,$38,$82,$37,$83,$38,$20,$ff
---

1
test/ecs/jumper.txt Normal file
View File

@ -0,0 +1 @@
Error: ENOENT: no such file or directory, open './test/ecs/velocity.ecs'

147
test/ecs/kernel1.ecs Normal file
View File

@ -0,0 +1,147 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
import "sprites.ecs"
system Kernel1Sprite
locals 7
on kernelsetup do with [Sprite]
---
; set player object flags
lda {{<plyrflags}}
sta NUSIZ0
sta REFP0
---
on kernelsetup do with
[Sprite,HasBitmap,HasColormap,HasYpos]
---
; calculate screen height - ypos
lda {{<lines}}
clc
adc #32
sec
sbc {{<ypos}}
sta {{$5}}
; calculate bitmap pointer
stx {{$6}} ; save X (Sprite index)
lda {{<bitmap}} ; deref bitmap
tax
lda {{<Bitmap:bitmapdata}},x
sec
sbc {{$5}}
sta {{$0}} ; Y = sprite slot index
lda {{>Bitmap:bitmapdata}},x
sbc #0
sta {{$1}}
; get bitmap height
lda {{<Bitmap:height}},x
sta {{$4}}
; calculate colormap pointer
ldx {{$6}} ; restore X
lda {{<colormap}} ; deref colormap
tax
lda {{<Colormap:colormapdata}},x
sec
sbc {{$5}}
sta {{$2}}
lda {{>Colormap:colormapdata}},x
sbc #0
sta {{$3}}
; save ypos
ldx {{$6}} ; restore X
lda {{<ypos}}
sta {{$5}}
@nosprite:
---
// TODO: what if > 1 player? or missile?
on kernelsetup do with [Sprite,HasXpos]
---
ldy #0
lda {{<xpos}}
{{!SetHorizPos}}
sta HMOVE
---
on prescanline do once
---
; draw player 0
lda {{$4}} ; height
dcp {{$5}} ; ypos
bcs @DoDraw1
lda #0
.byte $2C
@DoDraw1:
lda ({{$0}}),y
---
on scanline do once
---
sta GRP0
lda ({{$2}}),y
sta COLUP0
---
on kerneldone do once
---
lda #0
sta GRP0
sta GRP1
sta GRP0
sta GRP1
---
end
system KernelMissile
locals 1
// TODO: unroll loops
on preframe do foreach [Missile,HasYpos] limit 1
---
lda {{<ypos}}
sta {{$0}}
---
on scanline do foreach [Missile,HasYpos] limit 1
---
dec {{$0}}
php
pla
sta ENAM0+{{const index}}-2
---
on kerneldone do foreach [Missile,HasYpos] limit 1
---
; skip 3 lines each kernel section
lda #0
sta ENAM0+{{const index}}-2
dec {{$0}}
dec {{$0}}
dec {{$0}}
---
end
system KernelCollide
locals 1
on preframe do once
---
lda #$ff
sta {{$0}} ; object index if collision
---
on kernelsetup do once
---
sta CXCLR ; clear collision flags
---
on kerneldone do with [Sprite]
---
lda CXM1P
bpl @nocollide ; missile 1 <-> 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

231
test/ecs/kernel2.ecs Normal file
View File

@ -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 {{<lines}}
.define KPAD 32
; set height to zero in case no sprites
lda #0
sta {{$8}}
sta {{$9}}
---
on preframe do join
[SpriteSlot] with
[Sprite] limit 2
---
; set player object flags
lda {{<plyrflags}}
sta NUSIZ0,y
sta REFP0,y
; calculate screen height - ypos
lda KLINES
clc
adc KPAD
sec
sbc {{<ypos}}
sta {{$11}}
; calculate bitmap pointer
stx {{$12}} ; save X (Sprite index)
lda {{<bitmap}} ; deref bitmap
tax
lda {{<Bitmap:bitmapdata}},x
sec
sbc {{$11}}
sta {{$0}},y ; Y = sprite slot index
lda {{>Bitmap:bitmapdata}},x
sbc #0
sta {{$2}},y
; get bitmap height
lda {{<Bitmap:height}},x
sta {{$8}},y
; calculate colormap pointer
ldx {{$12}} ; restore X
lda {{<colormap}} ; deref colormap
tax
lda {{<Colormap:colormapdata}},x
sec
sbc {{$11}}
sta {{$4}},y
lda {{>Colormap:colormapdata}},x
sbc #0
sta {{$6}},y
; save ypos
ldx {{$12}} ; restore X
lda {{<ypos}}
sta {{$10}},y
---
on preframe do once
---
; shuffle pointers into (MSB, LSB) byte order
; L0 L1 H0 H1 -> L0 H0 L1 H1
lda {{$1}}
ldy {{$2}}
sty {{$1}}
sta {{$2}}
lda {{$5}}
ldy {{$6}}
sty {{$5}}
sta {{$6}}
---
on preframe do if [KernelSection,BGColor]
---
lda {{<bgcolor}}
sta COLUBK
---
on preframe do if [Missile,HasYpos]
---
lda KLINES
sec
sbc {{<ypos}}
sta {{$12}}
---
on kernel do with [KernelSection]
---
ldy #0
sty VDELP0
iny
sty VDELP1
---
on kernel do critical with [KernelSection]
---
ldy {{<lines}}
@LVScan:
{{!scanline 0}}
dey ; next scanline
{{!scanline 1}}
dey ; next scanline
bne @LVScan ; repeat until out of lines
---
on scanline do once
---
; draw player 0
lda {{$8}} ; height
dcp {{$10}} ; ypos
bcs @DoDraw1
lda #0
.byte $2C
@DoDraw1:
lda ({{$0}}),y
; .if {{#0}} = 0 ; TODO: configurable?
sta WSYNC
; .endif
sta GRP0
lda ({{$4}}),y
sta COLUP0
; draw player 1
lda {{$9}} ; height
dcp {{$11}} ; ypos
bcs @DoDraw2
lda #0
.byte $2C
@DoDraw2:
lda ({{$2}}),y
sta GRP1
lda ({{$6}}),y
sta COLUP1
---
on kernel do once
---
lda #0
sta GRP0
sta GRP1
sta GRP0
sta GRP1
---
on scanline do if [Missile,HasYpos]
---
cpy {{$12}}
php
pla
sta ENAM0
---
end
///
demo Main
using FrameLoop, Kernel2Sprite
using Joystick, MoveJoyX, MoveJoyY
using SetXPos, SetHorizPos
using SpriteShuffler, SpriteHider
entity Kernel [KernelSection, BGColor]
const lines = 192
const bgcolor = 0xa2
end
entity Bitmap1 [Bitmap]
const bitmapdata = [1, 1, 3, 7, 15, 31, 63, 127, 0]
const height = 8
end
entity Bitmap2 [Bitmap]
const bitmapdata = [$18,$3e,$ff,$ff,$ff,$ff,$3e,$18, 0]
const height = 8
end
entity Colormap1 [Colormap]
const colormapdata = [2, 4, 6, 8, 10, 12, 14, 14, 14]
end
entity Sprite0 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 50
init ypos = 150
init bitmap = #Bitmap2
init colormap = #Colormap1
const plyrflags = 0
end
entity Sprite1 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 100
init ypos = 60
init bitmap = #Bitmap1
init colormap = #Colormap1
const plyrflags = 3
end
entity Sprite2 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 80
init ypos = 90
init bitmap = #Bitmap2
init colormap = #Colormap1
const plyrflags = 2
end
entity Sprite3 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 40
init ypos = 150
init bitmap = #Bitmap1
init colormap = #Colormap1
const plyrflags = 0
end
/*
entity [Missile,HasXpos,HasYpos]
init xpos = 70
init ypos = 70
end
*/
entity Slot0 [SpriteSlot]
init sprite = #Sprite0
end
entity Slot1 [SpriteSlot]
init sprite = #Sprite1
end
entity Slot2 [SpriteSlot]
init sprite = #Sprite2
end
entity Slot3 [SpriteSlot]
init sprite = #Sprite3
end
end demo

587
test/ecs/kernel2.txt Normal file
View File

@ -0,0 +1,587 @@
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
.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
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
.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

189
test/ecs/music.ecs Normal file
View File

@ -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
---

333
test/ecs/music.txt Normal file
View File

@ -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_b0
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

28
test/ecs/narrow1.ecs Normal file
View File

@ -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 {{<x}}
---
end
end

38
test/ecs/narrow1.txt Normal file
View File

@ -0,0 +1,38 @@
EVENT__start = 1
.scope Main
.zeropage
Xpos_x_b0:
.res 1
.res 1
.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__start__1
ldx #0
move__start__2____each:
lda Xpos_x_b0+2,x
inx
cpx #2
jne move__start__2____each
move__start__2____exit:
;;; end action move__start__1
.endscope
Main__Start = Main::__Start

View File

@ -0,0 +1,42 @@
component RoomGraphics
graphics: array 0..8 of 0..255
end
component Room
fgcolor: 0..255
bgcolor: 0..255
north: [Room]
east: [Room]
south: [Room]
west: [Room]
end
component Location
room: [Room]
end
scope Main
/*
entity NullRoom [Room]
end
*/
entity InsideDailyPlanet [Room]
const fgcolor = $0c
const bgcolor = $12
const north = #InsideDailyPlanet
const south = #InsideDailyPlanet
const east = #OutsideDailyPlanet
const west = #InsideDailyPlanet
end
entity OutsideDailyPlanet [Room]
const fgcolor = $0c
const bgcolor = $12
const north = #InsideDailyPlanet
const south = #InsideDailyPlanet
const east = #InsideDailyPlanet
const west = #OutsideDailyPlanet
end
end

View File

@ -0,0 +1,25 @@
.scope Main
.zeropage
.code
Room_fgcolor_b0:
.byte 12
.byte 12
Room_bgcolor_b0:
.byte 18
.byte 18
Room_north_b0:
.byte 0
.byte 0
Room_east_b0:
.byte 1
.byte 0
Room_south_b0:
.byte 0
.byte 0
Room_west_b0:
.byte 0
.byte 1
__Start:
.endscope
Main__Start = Main::__Start

35
test/ecs/random.ecs Normal file
View File

@ -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 {{<seed}}
lsr
bcc :+
eor #$d4
:
sta {{<seed}}
---
on prevrand8 do foreach [Random8]
---
lda {{<seed}}
asl
bcc :+
eor #$a9
:
sta {{<seed}}
---
/* add entropy via joystick inputs */
on preframe do foreach [Random8]
---
lda {{<seed}}
eor SWCHA
beq :+
sta {{<seed}}
:
---
end

355
test/ecs/score.ecs Normal file
View File

@ -0,0 +1,355 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
component BCDScore2
digits: 0..$ff
scorecolor: 0..$ff
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 {{<pfcolor}}
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
---
on kernel do
critical fit 72
with [BCDScore6,BGColor]
---
; Display the resulting 48x8 bitmap
; using the Digit0-5 pointers.
@LoopCount = {{$12}}
@Temp = {{$13}}
lda {{<bgcolor}}
sta WSYNC
sta COLUBK
lda #7
sta @LoopCount
SLEEPR 20 ; TODO?
:
ldy @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 @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 :- ; 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)
;;{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 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 {{<BCDScore2:scorecolor}}+0
sta COLUP0
lda {{<BCDScore2:scorecolor}}+1
sta COLUP1
---
on kernel do
critical fit 98
select [BCDScore2]
---
lda #7
sta {{$0}}
@Loop:
ldx #0
sta WSYNC
{{!compute2digit 0}}
.if {{%ecount}}>1
inx
{{!compute2digit 1}}
.else
{{!compute2digit 0}}
.endif
; playfield
dec {{$0}}
jpl @Loop
; dex
; stx PF1
---
on compute2digit do once
---
lda {{$1}} ; load 1st pf
sta PF1 ; store 1st pf
; first digit
lda {{<BCDScore2:digits}} + {{#0}}
pha
and #$0f
asl
asl
asl
{{!fetchdigit}}
and #$0f
ldy {{$2}} ; load 2nd pf
sta {{$1}} + {{#0}}
; second digit
pla
and #$f0
lsr
sty PF1 ; store 2nd pf
{{!fetchdigit}}
and #$f0
ora {{$1}} + {{#0}}
sta {{$1}} + {{#0}}
---
on fetchdigit do once
---
adc {{$0}}
tay
; TODO: select your own?
lda {{^FontTablePF}},y
---
end
resource FontTablePF ---
; Font table for digits 0-9 (4x8 pixels)
;;{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
;;
---
resource FontTablePFFancy ---
; Font table for digits 0-9 (4x8 pixels)
;;{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
.byte $00,$EE,$88,$88,$44,$22,$AA,$44
.byte $00,$CC,$22,$22,$66,$22,$22,$CC
.byte $00,$22,$22,$22,$EE,$AA,$AA,$88
.byte $00,$44,$AA,$22,$44,$88,$88,$EE
.byte $00,$44,$AA,$AA,$CC,$88,$AA,$44
.byte $00,$22,$22,$22,$22,$22,$AA,$EE
.byte $00,$44,$AA,$AA,$44,$AA,$AA,$44
.byte $00,$44,$AA,$22,$66,$AA,$AA,$44
;;
---
system BCDMath
locals 1
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
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
---
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
using Kernel2Digit
using JoyButton, BCDMath
entity [Player,BCDScore6,PFColor,BGColor]
init digits = 0x123456
init pfcolor = $3c
init bgcolor = $02
end
entity [BCDScore2]
init digits = 0x24
init scorecolor = $ce
end
entity [BCDScore2]
init digits = 0x56
init scorecolor = $3e
end
system IncScore
on joybutton do with [Player,BCDScore6]
---
{{!AddBCD4 $0210}}
---
end
end demo

510
test/ecs/score.txt Normal file
View File

@ -0,0 +1,510 @@
EVENT__start = 1
EVENT__postframe = 1
EVENT__preframe = 1
EVENT__kernel = 1
EVENT__compute2digit = 1
EVENT__fetchdigit = 1
EVENT__AddBCD4 = 1
EVENT__AddBCD2 = 1
EVENT__SubBCD2 = 1
EVENT__joybutton = 1
.scope Main
.zeropage
BCDScore6_digits_b0:
.res 1
BCDScore6_digits_b8:
.res 1
BCDScore6_digits_b16:
.res 1
PFColor_pfcolor_b0:
.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
.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
Kernel2Digit__3__tmp:
.res 1
.res 1
.res 1
.code
Main__INITDATA:
.byte 86
.byte 52
.byte 18
.byte 60
.byte 2
.byte 36
.byte 86
.byte 206
.byte 62
__Start:
;;; start action Init__main_init__1
.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 :-
;;; start action FrameLoop__start__3
FrameLoop__start__4__NextFrame:
FRAME_END
FRAME_START
;;; start action Kernel6Digit__preframe__5
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__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__7__BCD0
lda BCDScore6_digits_b8
sta Kernel6Digit__preframe__7__BCD1
lda BCDScore6_digits_b16
sta Kernel6Digit__preframe__7__BCD2
ldx #0 ; leftmost bitmap
ldy #2 ; start from most-sigificant 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
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 Kernel6Digit__preframe__7__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 Kernel6Digit__preframe__7__Loop ; repeat until < 0
;;; 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__10
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__kernel__10
jsr Kernel6Digit__kernel__13
;;; start action Kernel2Digit__kernel__16
lda #$02
sta CTRLPF
; TODO: should be constants
; and it's wrong, too!
lda BCDScore2_scorecolor_b0+0
sta COLUP0
lda BCDScore2_scorecolor_b0+1
sta COLUP1
;;; end action Kernel2Digit__kernel__16
jsr Kernel2Digit__kernel__19
KERNEL_END
;;; 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__44__NotPressed
;;; start action IncScore__joybutton__45
;;; 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
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__AddBCD4__50
;;; end action IncScore__joybutton__45
JoyButton__postframe__44__NotPressed:
;;; end action JoyButton__postframe__42
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
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__53
; 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__53
FontTablePF:
;;; start action FontTablePF__FontTablePF__55
; Font table for digits 0-9 (4x8 pixels)
;;{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__FontTablePF__55
.endscope
Main__Start = Main::__Start

105
test/ecs/sound.ecs Normal file
View File

@ -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 {{$0}}
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

174
test/ecs/sound.txt Normal file
View File

@ -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_b8:
.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

177
test/ecs/sprites.ecs Normal file
View File

@ -0,0 +1,177 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
component Bitmap
bitmapdata: array of 0..255 baseoffset 31
height: 0..255
end
// TODO: remove?
component HasBitmap
end
component Colormap
colormapdata: array of 0..255 baseoffset 31
end
component HasColormap
colormap: [Colormap]
end
component Sprite
bitmap: [Bitmap]
plyrflags: 0..63
end
component HasXpos
xpos: 0..255
end
component HasYpos
ypos: 0..255
end
component Missile
index: 2..3
end
component SpriteSlot
sprite: [Sprite] // TODO: HasBitmap?
end
system JoyFaceDirection
on joyleft do with [Sprite]
---
lda {{<plyrflags}}
ora #$08
sta {{<plyrflags}}
---
on joyright do with [Sprite]
---
lda {{<plyrflags}}
and #$f7
sta {{<plyrflags}}
---
end
system MoveJoyX
on joyleft do with [HasXpos]
---
lda {{<xpos}}
sec
sbc #1
bcc @nomove
sta {{<xpos}}
@nomove:
---
on joyright do with [HasXpos]
---
lda {{<xpos}}
clc
adc #1
cmp #150
bcs @nomove
sta {{<xpos}}
@nomove:
---
end
system MoveJoyY
on joyup do with [HasYpos]
---
lda {{<ypos}}
sec
sbc #1
bcc @nomove
sta {{<ypos}}
@nomove:
---
on joydown do with [HasYpos]
---
lda {{<ypos}}
clc
adc #1
cmp #220
bcs @nomove
sta {{<ypos}}
@nomove:
---
end
system SetXPos
on preframe do join [SpriteSlot] with [Sprite]
limit 2
---
lda {{<xpos}}
{{!SetHorizPos}}
---
on preframe do foreach [Missile,HasXpos]
---
lda {{<xpos}}
ldy {{<index}}
{{!SetHorizPos}}
---
on prekernel do once
---
sta WSYNC
sta HMOVE
SLEEPR 24
sta HMCLR
---
end
system SpriteShuffler
locals 2
on postframe do select [SpriteSlot]
---
; load two sprite slots at left side of array
lda {{<SpriteSlot:sprite}}
sta {{$0}}
lda {{<SpriteSlot:sprite}}+1
sta {{$1}}
; move two slots to the left
ldx #0
@loop:
lda {{<SpriteSlot:sprite}}+2,x
sta {{<SpriteSlot:sprite}},x
inx
cpx #{{%ecount}}-2
bne @loop
; store two sprite slots at right side of array
lda {{$0}}
sta {{<SpriteSlot:sprite}}+{{%ecount}}-2
lda {{$1}}
sta {{<SpriteSlot:sprite}}+{{%ecount}}-1
---
end
system SpriteHider
locals 1
on postframe do select [SpriteSlot]
---
lda #{{%efullcount}}-1
sta {{$0}}
---
on postframe do
join [SpriteSlot]
with [Sprite,HasYpos]
limit 2
---
lda {{<ypos}}
cmp #192
bcc @skip
; swap this sprite slot with slot at end of array
lda {{<SpriteSlot:sprite}},y
pha
ldx {{$0}} ; clobbers X, but no longer used
lda {{<SpriteSlot:sprite}},x
sta {{<SpriteSlot:sprite}},y
pla
sta {{<SpriteSlot:sprite}},x
dec {{$0}}
@skip:
---
end

0
test/ecs/sprites.txt Normal file
View File

401
test/ecs/sprites1.ecs Normal file
View File

@ -0,0 +1,401 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
component Bitmap
bitmapdata: array of 0..255 baseoffset 31
height: 0..255
end
component HasBitmap
bitmap: [Bitmap]
end
component Colormap
colormapdata: array of 0..255 baseoffset 31
end
component HasColormap
colormap: [Colormap]
end
component Sprite
plyrflags: 0..63
end
component HasXpos
xpos: 0..255
end
component HasYpos
ypos: 0..255
end
component SpriteSlot
sprite: [Sprite,HasBitmap,HasColormap,HasYpos]
end
component Missile
end
system Kernel2Sprite
locals 13
on preframe do with [KernelSection]
---
.define KLINES {{<lines}}
.define KPAD 32
---
on preframe do join
[SpriteSlot] with
[Sprite,HasBitmap,HasColormap,HasYpos] limit 2
---
; set player object flags
lda {{<plyrflags}}
sta NUSIZ0,y
sta REFP0,y
; calculate screen height - ypos
lda KLINES+KPAD
sec
sbc {{<ypos}}
sta {{$11}}
; calculate bitmap pointer
stx {{$12}} ; save X (Sprite index)
lda {{<bitmap}} ; deref bitmap
tax
lda {{<Bitmap:bitmapdata}},x
sec
sbc {{$11}}
sta {{$0}},y ; Y = sprite slot index
lda {{>Bitmap:bitmapdata}},x
sbc #0
sta {{$2}},y
; get bitmap height
lda {{<Bitmap:height}},x
sta {{$8}},y
; calculate colormap pointer
ldx {{$12}} ; restore X
lda {{<colormap}} ; deref colormap
tax
lda {{<Colormap:colormapdata}},x
sec
sbc {{$11}}
sta {{$4}},y
lda {{>Colormap:colormapdata}},x
sbc #0
sta {{$6}},y
; save ypos
ldx {{$12}} ; restore X
lda {{<ypos}}
sta {{$10}},y
---
on preframe do once
---
; L0 L1 H0 H1 -> L0 H0 L1 H1
lda {{$1}}
ldy {{$2}}
sty {{$1}}
sta {{$2}}
lda {{$5}}
ldy {{$6}}
sty {{$5}}
sta {{$6}}
---
on preframe do if [BGColor]
---
lda {{<bgcolor}}
sta COLUBK
---
on preframe do if [Missile,HasYpos]
---
lda KLINES
sec
sbc {{<ypos}}
sta {{$12}}
---
on kernel do with [KernelSection]
---
ldy #0
sty VDELP0
iny
sta VDELP1
---
on kernel do with [KernelSection]
---
; define macro for each line
.macro @DrawLine do_wsync
.local DoDraw1
.local DoDraw2
; draw player 0
lda {{$8}} ; height
dcp {{$10}} ; ypos
bcs DoDraw1
lda #0
.byte $2C
DoDraw1:
lda ({{$0}}),y
.if do_wsync
sta WSYNC
.endif
sta GRP0
lda ({{$4}}),y
sta COLUP0
; draw player 1
lda {{$9}} ; height
dcp {{$11}} ; ypos
bcs DoDraw2
lda #0
.byte $2C
DoDraw2:
lda ({{$2}}),y
sta GRP1
lda ({{$6}}),y
sta COLUP1
.endmacro
ldy {{<lines}}
@LVScan:
{{!scanline1}}
@DrawLine 1 ; macro: draw scanline w/ WSYNC
dey ; next scanline
{{!scanline2}}
@DrawLine 0 ; macro: draw scanline no WSYNC
dey ; next scanline
bne @LVScan ; repeat until out of lines
---
on kernel do with [KernelSection]
---
lda #0
sta GRP0
sta GRP1
sta GRP0
sta GRP1
---
on scanline1 do if [Missile,HasYpos]
---
cpy {{$12}}
php
pla
sta ENAM0
---
end
system VersatilePlayfield
locals 2
on preframe do with [VersatilePlayfield]
---
lda {{<data}}
sta {{$0}}
lda {{>data}}
sta {{$1}}
---
on scanline1 do with [VersatilePlayfield]
---
lda ({{local 0}}),y
tax
---
on scanline2 do with [VersatilePlayfield]
---
lda ({{local 0}}),y
sta $00,x
---
end
system SetXPos
on preframe do once
---
sta HMCLR
---
on preframe do join [SpriteSlot] with [HasXpos]
limit 2
---
lda {{<xpos}}
{{!SetHorizPos}}
---
on preframe do if [Missile,HasXpos]
---
lda {{<xpos}}
ldx #2
{{!SetHorizPos}}
---
on preframe do once
---
sta WSYNC
sta HMOVE
---
end
system MoveJoyX
on joyleft do with [HasXpos]
---
lda {{<xpos}}
sec
sbc #1
bcc @nomove
sta {{<xpos}}
@nomove:
---
on joyright do with [HasXpos]
---
lda {{<xpos}}
clc
adc #1
cmp #152
bcs @nomove
sta {{<xpos}}
@nomove:
---
end
system MoveJoyY
on joyup do with [HasYpos]
---
lda {{<ypos}}
sec
sbc #1
bcc @nomove
sta {{<ypos}}
@nomove:
---
on joydown do with [HasYpos]
---
lda {{<ypos}}
clc
adc #1
cmp #220
bcs @nomove
sta {{<ypos}}
@nomove:
---
end
system SpriteShuffler
locals 2
on postframe do select [SpriteSlot]
---
; load two sprite slots at left side of array
lda {{<SpriteSlot:sprite}}
sta {{$0}}
lda {{<SpriteSlot:sprite}}+1
sta {{$1}}
; move two slots to the left
ldx #0
@loop:
lda {{<SpriteSlot:sprite}}+2,x
sta {{<SpriteSlot:sprite}},x
inx
cpx #{{%ecount}}-2
bne @loop
; store two sprite slots at right side of array
lda {{$0}}
sta {{<SpriteSlot:sprite}}+{{%ecount}}-2
lda {{$1}}
sta {{<SpriteSlot:sprite}}+{{%ecount}}-1
---
end
system SpriteHider
locals 1
on postframe do select [SpriteSlot]
---
lda #{{%efullcount}}-1
sta {{$0}}
---
on postframe do
join [SpriteSlot]
with [Sprite,HasYpos]
limit 2
---
lda {{<ypos}}
cmp #192
bcc @skip
; swap this sprite slot with slot at end of array
lda {{<SpriteSlot:sprite}},y
pha
ldx {{$0}} ; clobbers X, but no longer used
lda {{<SpriteSlot:sprite}},x
sta {{<SpriteSlot:sprite}},y
pla
sta {{<SpriteSlot:sprite}},x
dec {{$0}}
@skip:
---
end
///
demo Main
using FrameLoop, Kernel2Sprite
using Joystick, MoveJoyX, MoveJoyY
using SetXPos, SetHorizPos
using SpriteShuffler, SpriteHider
entity Kernel [KernelSection, BGColor]
const lines = 192
const bgcolor = 0xa2
end
entity Bitmap1 [Bitmap]
const bitmapdata = [1, 1, 3, 7, 15, 31, 63, 127]
const height = 8
end
entity Bitmap2 [Bitmap]
const bitmapdata = [$18,$3e,$ff,$ff,$ff,$ff,$3e,$18]
const height = 8
end
entity Colormap1 [Colormap]
const colormapdata = [6, 3, 6, 9, 12, 14, 31, 63]
end
entity Sprite0 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 50
init ypos = 150
init bitmap = #Bitmap2
const plyrflags = 0
end
entity Sprite1 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 100
init ypos = 60
init bitmap = #Bitmap1
const plyrflags = 3
end
entity Sprite2 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 80
init ypos = 90
init bitmap = #Bitmap2
const plyrflags = 0
end
entity Sprite3 [Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player]
init xpos = 40
init ypos = 150
init bitmap = #Bitmap1
const plyrflags = 0
end
/*
entity [Missile,HasXpos,HasYpos]
init xpos = 70
init ypos = 70
end
*/
entity Slot0 [SpriteSlot]
init sprite = #Sprite0
end
entity Slot1 [SpriteSlot]
init sprite = #Sprite1
end
entity Slot2 [SpriteSlot]
init sprite = #Sprite2
end
entity Slot3 [SpriteSlot]
init sprite = #Sprite3
end
end demo

1
test/ecs/sprites1.txt Normal file
View File

@ -0,0 +1 @@
185:I couldn't find a component named "VersatilePlayfield".

514
test/ecs/superman.ecs Normal file
View File

@ -0,0 +1,514 @@
//#resource "vcs-ca65.h"
import "vcslib.ecs"
import "sprites.ecs"
import "kernel2.ecs"
import "versatile.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 {{<room}}
lda {{<Room:west}},y
sta {{<room}}
---
on goeast do with [Location]
---
ldy {{<room}}
lda {{<Room:east}},y
sta {{<room}}
---
on gonorth do with [Location]
---
ldy {{<room}}
lda {{<Room:north}},y
sta {{<room}}
---
on gosouth do with [Location]
---
ldy {{<room}}
lda {{<Room:south}},y
sta {{<room}}
---
on joyleft do with [Location,HasXpos,Moving]
---
lda {{<xpos}}
sec
sbc {{<speed}}
jcs @nomove
{{!gowest}}
lda #142
@nomove:
sta {{<xpos}}
---
on joyright do with [Location,HasXpos,Moving]
---
lda {{<xpos}}
clc
adc {{<speed}}
cmp #142
jcc @nomove
{{!goeast}}
lda #2
@nomove:
sta {{<xpos}}
---
on joyup do with [Location,HasYpos,Moving]
---
lda {{<ypos}}
sec
sbc {{<speed}}
jcs @nomove
{{!gonorth}}
lda #200
@nomove:
sta {{<ypos}}
---
on joydown do with [Location,HasYpos,Moving]
---
lda {{<ypos}}
clc
adc {{<speed}}
cmp #220
jcc @nomove
{{!gosouth}}
lda #2
@nomove:
sta {{<ypos}}
---
end
system BadMove
on postframe do foreach [Enemy]
---
{{!joyright}}
---
end
system RoomShuffle
on postframe do select [Location,Sprite]
---
ldy #{{%ecount}}-1
ldx {{<Slot1.sprite}}
beq @empty ; empty slot, load 1st entry
@loop:
inx
cpx #{{%ecount}}+1
bcc @norecycle
; TODO: need to get index of specific entity
@empty:
ldx #2 ; skip null sprite and player
@norecycle:
; TODO: should automatically index
lda {{<Location:room}}-1,x
cmp {{<Superdude.room}}
beq @exit
dey
bne @loop
ldx #0 ; null sprite
@exit:
stx {{<Slot1.sprite}}
---
end
scope Main
using FrameLoop, Kernel2Sprite
using Joystick, JoyFaceDirection
using SuperFly
using BadMove
using RoomShuffle
//using MoveJoyX, MoveJoyY
using SetXPos, SetHorizPos
//using SpriteShuffler, SpriteHider
entity Kernel [KernelSection, BGColor]
const lines = 190
const bgcolor = 0xa2
end
entity Blank [Bitmap,Colormap]
decode vcs_sprite
---
........ 00
---
end
entity Superdude1 [Bitmap,Colormap]
decode vcs_sprite
---
......xx 52
.....xx. 54
x....xx. 48
.x...xx. 48
..x..xxx 48
...x.xx. 48
....xx.. 46
...xxx.. 88
...xxx.. 86
..xxxx.x 86
..xxxxx. 86
.xxx.x.. 86
.xxx.... 88
xxx..... 86
xxx..... 86
xx...... 86
xx...... 46
x....... 46
---
end
entity BexButhor1 [Bitmap,Colormap]
decode vcs_sprite
---
x....... 04
.xx..... 04
..xx.... 04
..x.x... 04
..x..... 04
..x..xx. 46
..x.xxx. 48
..x.xxx. 48
..x.xxxx 48
..x.xxx. 48
..x.xxx. 48
..x.xx.. 46
..xxxxx. 2a
..xxxxx. 2a
..xxxxxx 28
..xxxxxx 28
..xxxxxx 28
...xxxxx 28
...xxxx. 28
....xxx. 26
....xx.. c8
....xx.. c8
....xx.. c8
....xx.. c6
....xx.. c6
....xx.. c6
....xxx. 04
....xxx. 04
---
end
entity NullRoom [Room,VersatilePlayfield]
const fgcolor = $0
const bgcolor = $0
const north = #NullRoom
const south = #NullRoom
const east = #NullRoom
const west = #NullRoom
const data = [0]
end
entity InsideDailyPlanet [Room,VersatilePlayfield]
const fgcolor = $0c
const bgcolor = $12
const north = #InsideDailyPlanet
const south = #InsideDailyPlanet
const east = #OutsideDailyPlanet
const west = #OutsideDailyPlanet
decode vcs_versatile
---
.................... .. c0 ..
.................... .. .. 01
.................... 08 .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
..................xx .. .. ..
.....xxxx.........xx .. .. ..
.....xxxx........xxx .. .. ..
.....xxxx.......xxxx .. .. ..
.....xxxx.......xxxx .. .. ..
.....xxxx.......xxxx .. .. ..
.....xxxx.......xxx. .. .. ..
.....xxxx.......xx.. .. .. ..
.....xxxx.......x... .. .. ..
.....xxxx.......x..x .. .. ..
.....xxxx.......x.xx .. .. ..
.....xxxx.......x.xx .. .. ..
.....xxxx.......x.xx .. 60 ..
.....xxxxxxx....x.xx .. .. ..
.xx..xxxxxxx....x.xx .. .. ..
.xx..xxxxxxx....x..x .. .. ..
.xx..xxxxxxx....x... .. .. ..
.xx..xxxxxxx....xx.. .. .. ..
.xx..xxxxxxx....xxx. .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xx.x .. 50 ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. 44 ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. 46 ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. 38 ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
xxxx.xxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. 00
xxxxxxxxxxxxxxxxxxxx .. c8 ..
xxxxxxxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxx...xxx .. .. ..
xxxxxxxxxxxxx.....xx .. .. ..
xxxxxxxxxxxxx.....xx .. .. ..
xxxxxxxxxxxx.......x .. .. ..
xxxxxxxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx 08 .. ..
xxxxxxxxxxxxxxxxxxxx 06 .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. 18 ..
xxxx.x.x.x.xxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
---
end
// xxxxxxx....xxxxxx...
// xxx...x.x...xxxx..xx
// xxx.x....x....xx....
entity OutsideDailyPlanet [Room,VersatilePlayfield]
const fgcolor = $0c
const bgcolor = $12
const north = #InsideDailyPlanet
const south = #InsideDailyPlanet
const east = #InsideDailyPlanet
const west = #InsideDailyPlanet
//9f9f 9f 0c fc 3e 38 46 44 506070a0000000
decode vcs_versatile
---
.................... .. 70 ..
.................... .. .. 01
.................... a8 .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
.................... .. .. ..
..................xx .. .. ..
.....xxxx.........xx .. .. ..
.....xxxx........xxx .. .. ..
.....xxxx.......xxxx .. .. ..
.....xxxx.......xxxx .. .. ..
.....xxxx.......xxxx .. .. ..
.....xxxx.......xxx. .. .. ..
.....xxxx.......xx.. .. .. ..
.....xxxx.......x... .. .. ..
.....xxxx.......x..x .. .. ..
.....xxxx.......x.xx .. .. ..
.....xxxx.......x.xx .. .. ..
.....xxxx.......x.xx .. 60 ..
.....xxxxxxx....x.xx .. .. ..
.xx..xxxxxxx....x.xx .. .. ..
.xx..xxxxxxx....x..x .. .. ..
.xx..xxxxxxx....x... .. .. ..
.xx..xxxxxxx....xx.. .. .. ..
.xx..xxxxxxx....xxx. .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xx.x .. 50 ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. 44 ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xx.x.xx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xx.x .. .. ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx....xxxx .. 46 ..
.xx..xxxxxxx....xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxx.xx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. 38 ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
.xx..xxxxxxxxxx.xxxx .. .. ..
xxxx.xxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxxx.xxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. 00
xxxxxxxxxxxxxxxxxxxx .. c8 ..
xxxxxxxx.xxxxxxxxxxx .. .. ..
xxxxxxxx.xxxxxxxxxxx .. .. ..
xxxxxxx...xxxxxxxxxx .. .. ..
xxxxxx.....xxxxxxxxx .. .. ..
xxxxxx.....xxxxxxxxx .. .. ..
xxxxx.......xxxxxxxx .. .. ..
xxxxxxxx.xxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx .. .. ..
xxxxxxxxxxxxxxxxxxxx 08 .. ..
xxxxxxxxxxxxxxxxxxxx 06 .. ..
xxxxxxxxxxxxxxxxxxxx .. 06 ..
x.x.xxxxxxxxxxxxxxxx .. .. ..
x.x.x.x.x.x.xxxxxxxx .. .. ..
x.x.x.x.x.x.x.x.x.x. .. .. ..
x.x.x.x.x.x.x.x.x.x. .. .. ..
x.x.x.x.x.x.x.x.x.x. .. 18 ..
x.x.x.x.x.x.x.x.x.x. .. 06 ..
3f 3f
3f 3f
3f 3f
3f 3f
3f 3f
3f 3f
3f 3f
3f 3f
---
end
entity NullSprite [Sprite]
const bitmap = #Blank
//var plyrflags = $00
end
entity Superdude [Location,Sprite,HasBitmap,HasColormap,HasXpos,HasYpos,Player,Moving]
var room = #OutsideDailyPlanet
var xpos = 40
var ypos = 30
const bitmap = #Superdude1
const colormap = #Superdude1
var plyrflags = $05
const speed = 2
end
entity BexButhor [Location,Sprite,HasXpos,HasYpos,HasBitmap,HasColormap,Enemy,Moving]
var room = #OutsideDailyPlanet
var xpos = 70
var ypos = 70
const bitmap = #BexButhor1
const colormap = #BexButhor1
var plyrflags = $00
const speed = 1
end
entity JBexButhor [Location,Sprite,HasXpos,HasYpos,HasBitmap,HasColormap,Enemy,Moving]
var room = #OutsideDailyPlanet
var xpos = 100
var ypos = 60
const bitmap = #BexButhor1
const colormap = #BexButhor1
var plyrflags = $00
const speed = 1
end
/*
entity KBexButhor [Location,Sprite,HasXpos,HasYpos,HasBitmap,HasColormap,Enemy,Moving]
var room = #InsideDailyPlanet
var xpos = 50
var ypos = 90
const bitmap = #BexButhor1
const colormap = #BexButhor1
var plyrflags = $00
const speed = 1
end*/
entity Slot0 [SpriteSlot]
var sprite = #Superdude
end
entity Slot1 [SpriteSlot]
var sprite = #NullSprite
end
using VersatilePlayfield with #Superdude.room
end

1210
test/ecs/superman.txt Normal file

File diff suppressed because it is too large Load Diff

305
test/ecs/titles.ecs Normal file
View File

@ -0,0 +1,305 @@
//#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 {{<height}}
sta {{$0}} ; scanline counter
---
// TODO: should factor out this code, but doesnt
on kernelsetup do if [Bitmap48] ---
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
---
on kernelsetup do if [PFColor] ---
lda {{<pfcolor}}
sta COLUP0
sta COLUP1
---
on kerneldraw do critical fit 63
if [Bitmap48]
---
txa
pha
:
ldy {{$0}} ; counts backwards
{{!wsync}} ; sync to next scanline
lda {{data bitmap0}},y ; load B0 (1st sprite byte)
sta GRP0 ; B0 -> [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 :- ; repeat until < 0
pla
tax
---
end
demo TitleDemo
scope Title
using FrameLoop, StandardKernel
using Kernel48Pixel with [#Name]
using Kernel48Pixel with [#Copyright]
using JoyButton
entity [Player]
end
system Advance
on joybutton do once
---
jmp Title2__Start
---
end
entity [KernelSection, BGColor]
const lines = 30
const bgcolor = $a0
end
entity [KernelSection, BGColor]
const lines = 10
const bgcolor = $a2
end
// convert -size 48x -gravity center label:"6502\nECS" pbm: | pnmtopnm -plain
entity Name [KernelSection, BGColor, PFColor, Bitmap48]
const lines = 2
const bgcolor = 0xa4
const pfcolor = 0xfc
decode vcs_bitmap48 ---
000000111100000111111110000011110000001111100000
000011111110000111111110001111111000011111110000
000011000011000110000000001100011000111000011000
000110000011000110000000011000001100110000011000
000110000000000110000000011000001100000000011000
000110111100000111111000011000001100000000011000
000111111110000111111100011000001100000000110000
000111000111000000001110011000001100000001110000
000110000011000000000110011000001100000011100000
000110000011000000000110011000001100000111000000
000110000011000000000110011000001100001110000000
000110000011001100000110011000001100011100000000
000011000011001100001110001100011000111000000000
000011111110000111111100001111111000111111111000
000000111100000011111000000111110000111111111000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000111111111100000011111000000001111100000000
000000111111111100001111111110000111111111000000
000000110000000000011100000111000110000011100000
000000110000000000011000000011001100000001100000
000000110000000000111000000001001100000000000000
000000110000000000110000000000000111000000000000
000000111111111000110000000000000011111000000000
000000111111111000110000000000000001111111000000
000000110000000000110000000000000000000111100000
000000110000000000110000000000000000000001100000
000000110000000000111000000011101100000001100000
000000110000000000011000000011001100000001100000
000000110000000000011100000111000110000011100000
000000111111111100001111111110000111111111000000
000000111111111100000011111000000001111110000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
---
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
end
end
scope Title2
using FrameLoop, Kernel48Pixel, StandardKernel
entity [KernelSection, BGColor]
var lines = 60
var bgcolor = 0x10
end
entity [KernelSection, BGColor]
var lines = 30
var bgcolor = 0x30
end
// convert -size 48x -gravity center label:"6502\nECS" pbm: | pnmtopnm -plain
entity [KernelSection, BGColor, PFColor, Bitmap48]
var lines = 2
var pfcolor = 0xec
var bgcolor = 0x30
decode vcs_bitmap48 ---
000000000000000000000000000000000000000000011000
000000000000000000000000000000000000000000011000
000000000000000000000000000000000000000000011000
000000000000000000000000000000000000000000011000
000001111011000011111000000111110000001111011000
000011111111000111111100001111111000011111111000
000110000111001100001110011000001100110000111000
000110000011001100000110011000001100110000011000
000110000011001100000110011000001100110000011000
000110000011001100000110011000001100110000011000
000110000011001100000110011000001100110000011000
000110000011001100000110011000001100110000011000
000110000111001100000110011000001100110000111000
000011111111000111111100001111111000011111111000
000001111011000011111000000111110000001111011000
000000000011000000000000000000000000000000000000
000110000110000000000000000000000000000000000000
000111111110000000000000000000000000000000000000
000001111100000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000110000000000000110000000000110000000000
000000000110000000000000110000000000110000000000
000000000000000000000000110000000000110000000000
000000000000000000000000110000000000110000000000
000000000110000111110000110111100000110000000000
000000000110001111111000111111110000110000000000
000000000110011100001100111000011000110000000000
000000000110011000001100110000011000110000000000
000000000110011000001100110000011000110000000000
000000000110011000001100110000011000110000000000
000000000110011000001100110000011000110000000000
000000000110011000001100110000011000000000000000
000000000110011000001100111000011000000000000000
000000000110001111111000111100110000110000000000
000000000110000111110000110111100000110000000000
000000000110000000000000000000000000000000000000
000000000110000000000000000000000000000000000000
000000011110000000000000000000000000000000000000
000000011100000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
---
end
entity [KernelSection, BGColor]
var lines = 20
var bgcolor = 0x30
end
entity [KernelSection, BGColor]
var lines = 20
var bgcolor = 0x50
end
system Colors
on postframe do foreach [PFColor]
---
inc {{set pfcolor}}
bne :+
inc {{set bgcolor}}
:
---
on postframe do foreach [KernelSection]
---
dec {{set lines}}
bne :+
lda #1
sta {{set lines}}
:
---
end
end
system Demo
on start do once
---
{{start Title}}
---
end
end demo

1529
test/ecs/titles.txt Normal file

File diff suppressed because it is too large Load Diff

235
test/ecs/vcs1.ecs Normal file
View File

@ -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
{{!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 {{<lines}}
@loop:
sta WSYNC
{{!scanline}}
dey
bne @loop
{{!kerneldone}}
---
on kernelsetup do if [BGColor]
---
lda {{<bgcolor}}
sta COLUBK
---
on kernelsetup do if [PFColor]
---
lda {{get pfcolor}}
sta COLUPF
---
on kernelsetup do if [Playfield]
---
lda {{get pf 0}}
sta PF0
lda {{get pf 8}}
sta PF1
lda {{get pf 16}}
sta PF2
---
end
///
demo Main
using FrameLoop, ResetSwitch, ResetConsole
using StaticKernel, JoyButton
entity [Player]
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $18
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $16
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $14
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $12
end
entity [KernelSection,BGColor,Playfield]
const lines = 10
const bgcolor = $14
const pf = 0x125244
end
entity Trees [KernelSection,BGColor,PFColor,Playfield]
const lines = 50
const bgcolor = $14
const pf = 0x112244
end
entity [KernelSection,BGColor,PFColor,Playfield]
const lines = 50
const bgcolor = $16
const pf = 0x124
end
entity [KernelSection,BGColor,Playfield]
const lines = 10
const bgcolor = $18
const pf = 0
end
system Local
locals 1
on joybutton do foreach [PFColor] limit 1 ---
inc {{$0}}
inc {{set Trees.pfcolor}}
---
end
end demo

209
test/ecs/vcs1.txt Normal file
View File

@ -0,0 +1,209 @@
EVENT__start = 1
EVENT__nextframe = 1
EVENT__resetswitch = 1
EVENT__preframe = 1
EVENT__kernel = 1
EVENT__kernelsetup = 1
EVENT__postframe = 1
EVENT__joybutton = 1
.scope Main
.zeropage
PFColor_pfcolor_b0:
.res 1
.res 1
TEMP:
Local__6__tmp:
.res 1
.code
KernelSection_lines_b0:
.byte 2
.byte 2
.byte 2
.byte 2
.byte 10
.byte 50
.byte 50
.byte 10
BGColor_bgcolor_b0:
.byte 24
.byte 22
.byte 20
.byte 18
.byte 20
.byte 20
.byte 22
.byte 24
Playfield_pf_b0:
.byte 68
.byte 68
.byte 36
.byte 0
Playfield_pf_b8:
.byte 82
.byte 34
.byte 1
.byte 0
Playfield_pf_b16:
.byte 18
.byte 17
.byte 0
.byte 0
Main__INITDATA:
.byte 0
.byte 0
__Start:
;;; start action Init__main_init__1
.include "vcs-ca65.h"
.define PAL 0
__NMI:
__Reset:
__BRK:
CLEAN_START
ldy #2
: lda Main__INITDATA-1,y
sta PFColor_pfcolor_b0-1,y
dey
bne :-
;;; start action FrameLoop__start__3
FrameLoop__start__4__NextFrame:
FRAME_START
;;; start action StaticKernel__preframe__5
;;; start action StaticKernel__kernelsetup__8
lda #24
sta COLUBK
;;; end action StaticKernel__kernelsetup__8
;;; start action StaticKernel__kernelsetup__11
;;; end action StaticKernel__kernelsetup__11
;;; start action StaticKernel__kernelsetup__12
;;; end action StaticKernel__kernelsetup__12
;;; end action StaticKernel__preframe__5
KERNEL_START
;;; start action StaticKernel__kernel__13
ldx #0
StaticKernel__kernel__14____each:
sta WSYNC
;;; start action StaticKernel__kernelsetup__16
lda BGColor_bgcolor_b0,x
sta COLUBK
;;; end action StaticKernel__kernelsetup__16
;;; start action StaticKernel__kernelsetup__19
cpx #5+2
jcs StaticKernel__kernelsetup__20____skipxhi
cpx #5
jcc StaticKernel__kernelsetup__20____skipxlo
lda PFColor_pfcolor_b0-5,x
sta COLUPF
StaticKernel__kernelsetup__20____skipxlo:
StaticKernel__kernelsetup__20____skipxhi:
;;; end action StaticKernel__kernelsetup__19
;;; start action StaticKernel__kernelsetup__22
cpx #4
jcc StaticKernel__kernelsetup__23____skipxlo
lda Playfield_pf_b0-4,x
sta PF0
lda Playfield_pf_b8-4,x
sta PF1
lda Playfield_pf_b16-4,x
sta PF2
StaticKernel__kernelsetup__23____skipxlo:
;;; end action StaticKernel__kernelsetup__22
ldy KernelSection_lines_b0,x
StaticKernel__kernel__15__loop:
sta WSYNC
dey
bne StaticKernel__kernel__15__loop
inx
cpx #8
jne StaticKernel__kernel__14____each
StaticKernel__kernel__14____exit:
;;; end action StaticKernel__kernel__13
KERNEL_END
;;; start action JoyButton__postframe__25
lda INPT4 ;read button input
bmi JoyButton__postframe__27__NotPressed
;;; start action Local__joybutton__28
inc Local__6__tmp+0
inc PFColor_pfcolor_b0
;;; end action Local__joybutton__28
JoyButton__postframe__27__NotPressed:
;;; end action JoyButton__postframe__25
FRAME_END
;;; start action ResetSwitch__nextframe__31
lsr SWCHB ; test Game Reset switch
bcs ResetSwitch__nextframe__32__NoStart
;;; start action ResetConsole__resetswitch__33
jmp Main::__Reset ; jump to Reset handler
;;; end action ResetConsole__resetswitch__33
ResetSwitch__nextframe__32__NoStart:
;;; end action ResetSwitch__nextframe__31
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

338
test/ecs/vcslib.ecs Normal file
View File

@ -0,0 +1,338 @@
//#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
{{!start}} ; start main routine
.segment "VECTORS"
ZeroByte: .byte $00
Return: .byte $60
VecNMI:
VecReset: .word __Reset
VecBRK: .word __BRK
.code
---
end
component Player
end
component KernelSection
lines: 1..255 default 1
end
component BGColor
bgcolor: 0..255
end
component FGColor
fgcolor: 0..255
end
component PFColor
pfcolor: 0..255
end
component Playfield
pf: 0..0xffffff
end
component AsymPlayfield
pfleft: 0..0xffffff
pfright: 0..0xffffff
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}}
jmp @NextFrame ; loop to next frame
---
on postframe do once
---
lsr SWCHB ; test Game Reset switch
bcs @NoStart
{{emit 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] 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 critical fit 22 once
---
; 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
---
end
system StandardKernel
on wsync do once
---
sta WSYNC
---
on preframe do foreach [KernelSection] limit 1
---
{{!wsync}}
{{!kernelsetup}}
---
on kernel do foreach [KernelSection]
---
{{!wsync}}
{{!kernelsetup}}
{{!kerneldraw}}
{{!kerneldone}}
---
on kerneldraw do with [KernelSection]
---
ldy {{<lines}}
@loop:
{{!prescanline}}
{{!wsync}}
{{!scanline}}
dey
bne @loop
---
on kernelsetup do if [BGColor]
---
lda {{<bgcolor}}
sta COLUBK
---
on kernelsetup do if [PFColor]
---
lda {{get pfcolor}}
sta COLUPF
---
on kernelsetup do if [Playfield]
---
lda {{get pf 0}}
sta PF0
lda {{get pf 8}}
sta PF1
lda {{get pf 16}}
sta PF2
---
end
component SimpleCollidable
lastx: 0..255
lasty: 0..255
end
system SimpleCollision
on postframe do with [SimpleCollidable]
---
lda CXP0FB
bpl @nocollide
lda {{<lastx}}
sta {{<xpos}}
lda {{<lasty}}
sta {{<ypos}}
@nocollide:
---
on ymoved do with [SimpleCollidable]
---
lda {{<xpos}}
sta {{<lastx}}
lda {{<ypos}}
sta {{<lasty}}
---
end
component FrameCount
frame: 0..255
end
system FrameCounter
on postframe do with [FrameCount]
---
inc {{set frame}}
.ifdef EVENT__frame256
jne @noframe256
{{emit frame256}}
@noframe256:
.endif
---
// TODO: only .if responds to events
on postframe do with [FrameCount]
---
jmp @go
.ifdef EVENT__frame4
@frame4:
{{emit frame4}}
jmp @done
.endif
.ifdef EVENT__frame8
@frame8:
{{emit frame8}}
jmp @done
.endif
.ifdef EVENT__frame16
@frame16:
{{emit frame16}}
jmp @done
.endif
@go:
lda {{get frame}}
.ifdef EVENT__frame16
and #15
jeq @frame16
.endif
.ifdef EVENT__frame8
and #7
cmp #3
jeq @frame8
.endif
.ifdef EVENT__frame4
and #3
cmp #1
jeq @frame4
.endif
@done:
---
end
///
demo Main
using FrameLoop, ResetConsole
using StandardKernel, JoyButton
entity [Player]
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $18
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $16
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $14
end
entity [KernelSection,BGColor]
const lines = 2
const bgcolor = $12
end
entity [KernelSection,BGColor,Playfield]
const lines = 10
const bgcolor = $14
const pf = 0x125244
end
entity Trees [KernelSection,BGColor,PFColor,Playfield]
const lines = 50
const bgcolor = $14
const pf = 0x112244
end
entity Trees2 [KernelSection,BGColor,PFColor,Playfield]
const lines = 50
const bgcolor = $16
const pf = 0x124
end
entity [KernelSection,BGColor,Playfield]
const lines = 10
const bgcolor = $18
const pf = 0
end
system Local
locals 1
on joybutton do foreach [PFColor] limit 1 ---
inc {{$0}}
inc {{set Trees.pfcolor}}
---
end
end demo

242
test/ecs/vcslib.txt Normal file
View File

@ -0,0 +1,242 @@
EVENT__start = 1
EVENT__postframe = 1
EVENT__resetswitch = 1
EVENT__wsync = 1
EVENT__preframe = 1
EVENT__kernel = 1
EVENT__kerneldraw = 1
EVENT__kernelsetup = 1
EVENT__joybutton = 1
.scope Main
.zeropage
PFColor_pfcolor_b0:
.res 1
.res 1
TEMP:
Local__5__tmp:
.res 1
.code
KernelSection_lines_b0:
.byte 2
.byte 2
.byte 2
.byte 2
.byte 10
.byte 50
.byte 50
.byte 10
BGColor_bgcolor_b0:
.byte 24
.byte 22
.byte 20
.byte 18
.byte 20
.byte 20
.byte 22
.byte 24
Playfield_pf_b0:
.byte 68
.byte 68
.byte 36
.byte 0
Playfield_pf_b8:
.byte 82
.byte 34
.byte 1
.byte 0
Playfield_pf_b16:
.byte 18
.byte 17
.byte 0
.byte 0
Main__INITDATA:
.byte 0
.byte 0
__Start:
;;; start action Init__main_init__1
.include "vcs-ca65.h"
.macpack longbranch
.define PAL 0
__NMI:
__Reset:
__BRK:
CLEAN_START
ldy #2
: lda Main__INITDATA-1,y
sta PFColor_pfcolor_b0-1,y
dey
bne :-
;;; start action FrameLoop__start__3
FrameLoop__start__4__NextFrame:
FRAME_END
FRAME_START
;;; start action StandardKernel__preframe__5
;;; start action StandardKernel__wsync__8
sta WSYNC
;;; end action StandardKernel__wsync__8
;;; start action StandardKernel__kernelsetup__10
lda #24
sta COLUBK
;;; end action StandardKernel__kernelsetup__10
;;; start action StandardKernel__kernelsetup__13
;;; end action StandardKernel__kernelsetup__13
;;; start action StandardKernel__kernelsetup__14
;;; end action StandardKernel__kernelsetup__14
;;; end action StandardKernel__preframe__5
KERNEL_START
;;; start action StandardKernel__kernel__15
ldx #0
StandardKernel__kernel__16____each:
;;; start action StandardKernel__wsync__18
sta WSYNC
;;; end action StandardKernel__wsync__18
;;; start action StandardKernel__kernelsetup__20
lda BGColor_bgcolor_b0,x
sta COLUBK
;;; end action StandardKernel__kernelsetup__20
;;; start action StandardKernel__kernelsetup__23
cpx #5+2
jcs StandardKernel__kernelsetup__24____skipxhi
cpx #5
jcc StandardKernel__kernelsetup__24____skipxlo
lda PFColor_pfcolor_b0-5,x
sta COLUPF
StandardKernel__kernelsetup__24____skipxlo:
StandardKernel__kernelsetup__24____skipxhi:
;;; end action StandardKernel__kernelsetup__23
;;; start action StandardKernel__kernelsetup__26
cpx #4
jcc StandardKernel__kernelsetup__27____skipxlo
lda Playfield_pf_b0-4,x
sta PF0
lda Playfield_pf_b8-4,x
sta PF1
lda Playfield_pf_b16-4,x
sta PF2
StandardKernel__kernelsetup__27____skipxlo:
;;; end action StandardKernel__kernelsetup__26
;;; start action StandardKernel__kerneldraw__29
ldy KernelSection_lines_b0,x
StandardKernel__kerneldraw__31__loop:
;;; start action StandardKernel__wsync__32
sta WSYNC
;;; end action StandardKernel__wsync__32
dey
bne StandardKernel__kerneldraw__31__loop
;;; end action StandardKernel__kerneldraw__29
inx
cpx #8
jne StandardKernel__kernel__16____each
StandardKernel__kernel__16____exit:
;;; end action StandardKernel__kernel__15
KERNEL_END
;;; start action FrameLoop__postframe__34
lsr SWCHB ; test Game Reset switch
bcs FrameLoop__postframe__35__NoStart
;;; start action ResetConsole__resetswitch__36
jmp Main::__Reset ; jump to Reset handler
;;; end action ResetConsole__resetswitch__36
FrameLoop__postframe__35__NoStart:
;;; end action FrameLoop__postframe__34
;;; start action JoyButton__postframe__38
lda INPT4 ;read button input
bmi JoyButton__postframe__40__NotPressed
;;; start action Local__joybutton__41
inc Local__5__tmp+0
inc PFColor_pfcolor_b0
;;; end action Local__joybutton__41
JoyButton__postframe__40__NotPressed:
;;; end action JoyButton__postframe__38
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
Main__Start = Main::__Start

69
test/ecs/versatile.ecs Normal file
View File

@ -0,0 +1,69 @@
component VersatilePlayfield
data: array of 0..255 baseoffset -1
end
system VersatilePlayfield
locals 2
on preframe do with [VersatilePlayfield]
---
lda {{<data}}
sta {{$0}}
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
---
on checkplatform do with [HasXpos,HasYpos]
---
lda {{<xpos}}
clc
adc #6
lsr
lsr
tay
lda {{^PFCollideMask}},y
pha
lda {{^PFCollideReg}},y
pha
lda #176 ; TODO: adjust for kernel size
sec
sbc {{<ypos}}
and #$fe
tay
; TODO: use system local pointer
pla
cmp ({{$0}}),y ; match register?
bne @nostop1
dey
pla
and ({{$0}}),y ; mask bitmap?
beq @nostop
{{!platformstopped}}
jmp @done
@nostop1:
pla
@nostop:
{{!platformnotstopped}}
@done:
---
end

1
test/ecs/versatile.txt Normal file
View File

@ -0,0 +1 @@
36:I couldn't find a component named "HasXpos".