From d8e18df3a121af17ddd1eaaca72f5658b32128ba Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 14 Aug 2022 10:46:34 +0200 Subject: [PATCH] added c64 starfield example --- compiler/test/TestCompilerOnExamples.kt | 1 + docs/source/todo.rst | 12 ++ examples/starfield.p8 | 163 ++++++++++++++++++++++++ gradle.properties | 2 +- 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 examples/starfield.p8 diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 39ba9c4d6..741348884 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -70,6 +70,7 @@ class TestCompilerOnExamplesC64: FunSpec({ "cube3d-sprites", "plasma", "sprites", + "starfield", "turtle-gfx", "wizzine", ), diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 15d4f1e73..4eeb9dd8f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,18 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- fix operator precedence: if rasterCount ^ $80 >= 230 EOR should precede >= +- @(ptr) |= 3 -> asm peephole optimize remove the second ldy if the instruction before doesn't modify y + ldy #0 + lda (starfieldPtr2),y + ora #3 + ldy #0 + sta (starfieldPtr2),y +- vm: intermediate code: don't flatten everything. Instead, as a new intermediary step, + convert the new Ast into *structured* intermediary code. + Basically keep the blocks and subroutines structure, including full subroutine signature information, + + ... diff --git a/examples/starfield.p8 b/examples/starfield.p8 new file mode 100644 index 000000000..cc4436148 --- /dev/null +++ b/examples/starfield.p8 @@ -0,0 +1,163 @@ +; Galencia starfield ported to Prog8. Original: https://github.com/JasonAldred/C64-Starfield +; This is for the C64 only. + +%option no_sysinit + +main { + const uword starScreenChar = $0400 ; Screen address + const uword StarScreenCols = $d800 ; Character attribute address + + const uword charBase = $3000 ; Address of our character set + const uword star1Init = charBase+$1d0 ; Init address for each star + const uword star2Init = charBase+$298 + const uword star3Init = charBase+$240 + const uword star4Init = charBase+$2e0 + + const uword star1Limit = charBase+$298 ; Limit for each star + const uword star2Limit = charBase+$360 ; Once limit is reached, they are reset + const uword star3Limit = charBase+$298 + const uword star4Limit = charBase+$360 + const uword star1Reset = charBase+$1d0 ; Reset address for each star + const uword star2Reset = charBase+$298 + const uword star3Reset = charBase+$1d0 + const uword star4Reset = charBase+$298 + const uword staticStar1 = charBase+$250 ; 2 Locations for blinking static stars + const uword staticStar2 = charBase+$1e0 + + const ubyte starColourLimit = 20 ; use values 1 to 20 + ; Galencia uses these values + ; 1 = mono + ; 2 = duo + ; 20 = full colour + + ; 4 x pointers for moving stars + uword @zp starfieldPtr1 + uword @zp starfieldPtr2 + uword @zp starfieldPtr3 + uword @zp starfieldPtr4 + + ubyte rasterCount ; Counter that increments each frame + + sub start () { + sys.set_irqd() + sys.memset(charBase, 8*256, 0) ; clear charset data + c64.EXTCOL = 0 + c64.BGCOL0 = 0 + c64.VMCSB = (charBase/1024) | %00010000 ; Characters at $3000 + initStarfield() + createStarScreen() + + repeat { + sys.waitvsync() + rasterCount ++ + doStarfield() + } + } + + sub doStarfield() { + ; This routine does 3 things: + ; 1) Erases stars + ; 2) Moves stars + ; 3) Draws stars in new position + + @(starfieldPtr1) = 0 + @(starfieldPtr2) = 0 + @(starfieldPtr3) = 0 + @(starfieldPtr4) = 0 + + if rasterCount & 1 { + starfieldPtr1++ + if starfieldPtr1==star1Limit + starfieldPtr1=star1Reset + } + starfieldPtr2++ + if starfieldPtr2==star2Limit + starfieldPtr2=star2Reset + if rasterCount & 3 == 0 { + starfieldPtr3++ + if starfieldPtr3==star3Limit + starfieldPtr3=star3Reset + } + starfieldPtr4 += 2 + if starfieldPtr4==star4Limit + starfieldPtr4=star4Reset + + ; 2 static stars that flicker + if rasterCount >= 230 + @(staticStar1) = 0 + else + @(staticStar1) = 192 + if (rasterCount ^ $80) >= 230 + @(staticStar2) = 0 + else + @(staticStar2) = 192 + + ; Plot new stars + @(starfieldPtr1) |= 3 + @(starfieldPtr2) |= 3 + @(starfieldPtr3) |= 12 + @(starfieldPtr4) |= 48 + } + + sub initStarfield() { + starfieldPtr1 = star1Init + starfieldPtr2 = star2Init + starfieldPtr3 = star3Init + starfieldPtr4 = star4Init + } + + sub createStarScreen() { + ; Creates the starfield charmap and colour charmap + ; This routine paints vertical stripes of colour into the colourmap + ; so the stars are different colours + ; It also plots the correct characters to the screen, wrapping them around + ; at the correct char count to give to the starfield effect. + + uword @zp ptr + ubyte x + for x in 0 to 39 { + ubyte limit + ubyte char = starfieldRow[x] + if char >= 58+25 + limit = 58+50 + else + limit = 58+25 + ubyte start = limit - 25 + ptr = starScreenChar + repeat 25 { + ptr[x] = char + ptr += 40 + char ++ + if char == limit + char = start + } + } + + ; Fill colour map with vertical stripes of colour for starfield + ptr = StarScreenCols + ubyte ci = 0 + repeat 25 { + for x in 0 to 39 { + ptr[x] = starfieldCols[ci] + ci ++ + if ci==starColourLimit + ci=0 + } + ptr += 40 + } + } + + ; Dark starfield so it doesnt distract from bullets and text + ubyte[20] starfieldCols = [ + 14,10,12,15,14,13,12,11,10,14, + 14,10,14,15,14,13,12,11,10,12 + ] + + ; Star positions, 40 X positions, range 58-107 + ubyte[40] starfieldRow = [ + 058,092,073,064,091,062,093,081,066,094, + 086,059,079,087,080,071,076,067,082,095, + 100,078,099,060,075,063,084,065,083,096, + 068,088,074,061,090,098,085,101,097,077 + ] +} diff --git a/gradle.properties b/gradle.properties index b4cb6ba0b..7e04dc5a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.parallel=true org.gradle.daemon=true kotlin.code.style=official javaVersion=11 -kotlinVersion=1.7.0 +kotlinVersion=1.7.10 \ No newline at end of file