tweak c64 balloonflight example etc.

This commit is contained in:
Irmen de Jong 2024-09-22 12:20:28 +02:00
parent 90b8a22a71
commit 1d1d6b3d98
6 changed files with 146 additions and 62 deletions

View File

@ -152,10 +152,16 @@ The IRQ handler routine must return a boolean value (0 or 1) in the A register:
**CommanderX16 specific notes**
.. sidebar::
X16 specific routines
For the X16 there are also some specialized IRQ handling routines, see :ref:`x16-specific-irq` below.
Note that for the CommanderX16 the set_rasterirq() will disable VSYNC irqs and never call the system IRQ handler regardless
of the return value of the user handler routine. This also means the default sys.wait() routine won't work anymore,
when using this handler.
These two helper routines are not particularly suited to handle multiple IRQ sources on the Commander X16.
It's possible but it requires correct fiddling with IRQ enable bits, acknowledging the IRQs, and properly calling
or not calling the system IRQ handler routine. See the section below for perhaps a better and easier solution that
@ -192,6 +198,8 @@ will corrupt any Vera operations that were going on in the main program. The rou
and restored at the end of the handler, further increasing its execution time...
.. _x16-specific-irq:
Commander X16 specific IRQ handling
===================================

View File

@ -1,6 +1,14 @@
TODO
====
Can we move the asm init code that is injected into the start() subroutine, to init_system_phase2 instead?
Doc improvements: some short overview for people coming from other programming languages like C:
tell something about prog8 not having function overloading, max 16 bit (u)word integer as native type (and floats sometimes),
static variable allocations, no dynamic memory allocation in the language itself (although possible via user written libraries),
etc ...
Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)

View File

@ -1,38 +1,32 @@
%import syslib
%import textio
%import math
%import test_stack
%zeropage basicsafe
; note: The flickering in the scrolling is caused by the CPU requiring
; too long to scroll the characters + the colors in course scroll.
; This takes nearly a full frame to accomplish, and causes tearing.
; It's very difficult to remove this flicker: it requires double buffering
; and splitting the coarse character scrolling on multiple phases...
main {
bool perform_scroll = false
bool do_char_scroll = false
sub start() {
uword moon_x = 310
c64.set_sprite_ptr(0, $0f00) ; alternatively, set directly: c64.SPRPTR[0] = $0f00 / 64
c64.SPENA = 1
c64.set_sprite_ptr(1, $0f00+64) ; alternatively, set directly: c64.SPRPTR[0] = $0f00 / 64
c64.SPENA = %00000011
c64.SP0COL = 14
c64.SP1COL = 7
c64.SPXY[0] = 80
c64.SPXY[1] = 100
set_moon_pos(moon_x)
c64.SCROLX &= %11110111 ; 38 column mode
sys.set_rasterirq(&irq.irqhandler, 200) ; enable animation via raster interrupt
sys.set_rasterirq(&irq.irqhandler, 250) ; enable animation via raster interrupt
ubyte target_height = 10
ubyte active_height = 24
ubyte active_height = 25
bool upwards = true
repeat {
;txt.plot(0,0)
;test_stack.test()
ubyte mountain = 223 ; slope upwards
if active_height < target_height {
active_height++
@ -42,66 +36,124 @@ main {
active_height--
upwards = false
} else {
target_height = 8 + math.rnd() % 16
; determine new height for next mountain
target_height = 9 + math.rnd() % 15
if upwards
mountain = 233
else
mountain = 223
}
while not perform_scroll {
while not do_char_scroll {
; let the raster irq do its timing job
}
perform_scroll = false
txt.scroll_left(true)
do_char_scroll = false
scroll_characters_left()
; float the balloon
if math.rnd() & %10000 !=0
; float the balloon and the moon sprites
if math.rnd() & 1 !=0
c64.SPXY[1] ++
else
c64.SPXY[1] --
moon_x--
if msb(moon_x)==255
moon_x = 340
set_moon_pos(moon_x)
; draw new mountain etc.
const ubyte RIGHT_COLUMN = 39
ubyte yy
for yy in 0 to active_height-1 {
txt.setcc(39, yy, 32, 2) ; clear top of screen
txt.setcc(RIGHT_COLUMN, yy, 32, 2) ; clear top of screen
}
txt.setcc(39, active_height, mountain, 8) ; mountain edge
txt.setcc(RIGHT_COLUMN, active_height, mountain, 8) ; mountain edge
for yy in active_height+1 to 24 {
txt.setcc(39, yy, 160, 8) ; draw mountain
txt.setcc(RIGHT_COLUMN, yy, 160, 8) ; draw filled mountain
}
yy = math.rnd()
if yy > 100 {
ubyte clutter = math.rnd()
if clutter > 100 {
; draw a star
txt.setcc(39, yy % (active_height-1), '.', math.rnd())
txt.setcc(RIGHT_COLUMN, clutter % (active_height-1), sc:'.', math.rnd())
}
if yy > 200 {
if clutter > 200 {
; draw a tree
ubyte tree = 30
ubyte tree = sc:'↑'
ubyte treecolor = 5
if yy & %01000000 != 0
tree = 88
else if yy & %00100000 != 0
tree = 65
if clutter & %00010000 != 0
tree = sc:'♣'
else if clutter & %00100000 != 0
tree = sc:'♠'
if math.rnd() > 130
treecolor = 13
txt.setcc(39, active_height, tree, treecolor)
txt.setcc(RIGHT_COLUMN, active_height, tree, treecolor)
}
if yy > 235 {
if clutter > 235 {
; draw a camel
txt.setcc(39, active_height, 94, 9)
txt.setcc(RIGHT_COLUMN, active_height, sc:'π', 9)
}
}
}
sub set_moon_pos(uword x) {
c64.SPXY[2] = lsb(x)
c64.SPXY[3] = 55
if msb(x)!=0
c64.MSIGX |= %00000010
else
c64.MSIGX &= %11111101
}
sub scroll_characters_left () {
; Scroll the bottom half (approx.) of the character screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Without clever split-screen tricks, the C64 is not fast enough to scroll the whole
; screen smootly without tearing. So for simplicity it's constrained to less rows
; such that what is scrolled, *does* scrolls smoothly.
; For maximum performance the scrolling is done in unrolled assembly code.
%asm {{
ldx #0
ldy #38
-
.for row=10, row<=24, row+=1
lda cbm.Screen + 40*row + 1,x
sta cbm.Screen + 40*row + 0,x
lda cbm.Colors + 40*row + 1,x
sta cbm.Colors + 40*row + 0,x
.next
inx
dey
bpl -
rts
}}
}
}
irq {
; does the smooth scrolling immediately after the visible screen area,
; so there is no screen tearing. The main loop does the "big" character
; scrolling when the soft-scroll runs out after 8 pixels
ubyte smoothx=0
sub irqhandler() -> bool {
smoothx = (smoothx-1) & 7
main.do_char_scroll = smoothx==7
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
return false
}
}
spritedata $0f00 {
; this memory block contains the sprite data
; it must start on an address aligned to 64 bytes.
; this memory block contains the sprite data. it must start on an address aligned to 64 bytes.
; for simplicity, it's currently statically located at $0f00 (not far in memory after the program code),
; but there are ways to do this more dynamically.
%option force_output ; make sure the data in this block appears in the resulting program
ubyte[] balloonsprite = [ %00000000,%01111111,%00000000,
@ -124,17 +176,29 @@ spritedata $0f00 {
%00000000,%00111110,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00011100,%00000000 ]
%00000000,%00011100,%00000000,
0]
ubyte[] moonsprite = [ %00000000,%00000110,%00000000,
%00000000,%00011100,%00000000,
%00000000,%01111000,%00000000,
%00000000,%11111000,%00000000,
%00000001,%11110000,%00000000,
%00000011,%11110000,%00000000,
%00000011,%11110000,%00000000,
%00000111,%11100000,%00000000,
%00000111,%11100000,%00000000,
%00000111,%11100000,%00000000,
%00000111,%11100000,%00000000,
%00000111,%11100000,%00000000,
%00000111,%11100000,%00000000,
%00000111,%11100000,%00000000,
%00000011,%11110000,%00000000,
%00000011,%11110000,%00000000,
%00000001,%11110000,%00000000,
%00000000,%11111000,%00000000,
%00000000,%01111000,%00000000,
%00000000,%00011100,%00000000,
%00000000,%00000110,%00000000,
0]
}
irq {
ubyte smoothx=0
sub irqhandler() -> bool {
smoothx = (smoothx-1) & 7
main.perform_scroll = smoothx==7
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
return false
}
}

View File

@ -92,15 +92,18 @@ main {
for y in 0 to 24 {
ubyte @zp @shared yvalue = ybuf[y]
for x in 0 to 39 {
; @(screen+x) = xbuf[x] + yvalue
; max optimized asm is this: (achieving ~21 fps on the C64):
%asm {{
lda p8v_yvalue
ldy p8v_x
clc
adc p8v_xbuf,y
sta (p8v_screen),y
}}
@(screen+x) = xbuf[x] + yvalue
; optimized asm for the line above is this:
; (achieving ~23 fps on the C64, about 1 or 2 fps more than with the pure prog8 code):
; %asm {{
; lda p8v_yvalue
; ldy p8v_x
; clc
; adc p8v_xbuf,y
; sta (p8v_screen),y
; }}
}
screen += 40
}

View File

@ -57,8 +57,9 @@ irq {
uword @zp x = math.sin8u(angle1-spri*16) as uword + 50
ubyte @zp y = math.sin8u(angle2-spri*16) / 2 + 70
c64.SPXYW[spri] = mkword(y, lsb(x))
c64.MSIGX <<= 1
if msb(x)!=0 c64.MSIGX++
if msb(x)!=0
sys.set_carry()
rol(c64.MSIGX)
}
c64.EXTCOL-=8
return true

View File

@ -5,4 +5,4 @@ org.gradle.daemon=true
kotlin.code.style=official
javaVersion=11
kotlinVersion=2.0.20
version=10.4.1
version=10.4.2-SNAPSHOT