mirror of
https://github.com/irmen/prog8.git
synced 2025-06-15 18:23:35 +00:00
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
c50cbbb526 | |||
b93d9ecd7e | |||
96243db88b | |||
4daf75a8cc | |||
8c63d7cf5b | |||
6f78a32e64 | |||
af6731c9c8 | |||
25cf0d2b94 | |||
9389791d91 | |||
aa8191d0a1 | |||
0d5c78e875 | |||
e8679ae03b | |||
d1d224b7fc | |||
df995f7bc9 | |||
af39502450 | |||
ffa38955d6 | |||
8d82fb6d8f | |||
306770331a | |||
d3f433c8cf | |||
cf49cbd1f8 | |||
8a99e75299 | |||
2dbf849c82 | |||
ba3dce0b4c | |||
ca9588380a | |||
ae2619602d | |||
de06353194 | |||
3ff3f5e1cc | |||
4b747859b3 | |||
2201765366 | |||
dfa1d5e398 | |||
ce9a90f626 | |||
2deb18beb2 | |||
0f7454059c | |||
f9ba09ac4d | |||
4e74873eae | |||
f0cd03d14f | |||
f2b069c562 | |||
bc89306dc1 | |||
bf4da1655b | |||
d819aa270f | |||
e6d945f835 | |||
4fe408f1fd | |||
c376e42092 | |||
63a653cdf0 | |||
5d900800f2 | |||
def06dbc0b | |||
9b66a597bb |
34
README.md
34
README.md
@ -50,7 +50,7 @@ What use Prog8 provide?
|
|||||||
|
|
||||||
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
|
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
|
||||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
|
||||||
|
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -75,16 +75,18 @@ Example code
|
|||||||
|
|
||||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
ubyte[256] sieve
|
ubyte[256] sieve
|
||||||
ubyte candidate_prime = 2
|
ubyte candidate_prime = 2 ; is increased in the loop
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
memset(sieve, 256, false) ; clear the sieve
|
; clear the sieve, to reset starting situation on subsequent runs
|
||||||
|
memset(sieve, 256, false)
|
||||||
|
; calculate primes
|
||||||
txt.print("prime numbers up to 255:\n\n")
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
repeat {
|
repeat {
|
||||||
@ -95,22 +97,23 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
txt.print(", ")
|
txt.print(", ")
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
txt.print("number of primes (expected 54): ")
|
txt.print("number of primes (expected 54): ")
|
||||||
txt.print_ub(amount)
|
txt.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub find_next_prime() -> ubyte {
|
||||||
while sieve[candidate_prime] {
|
while sieve[candidate_prime] {
|
||||||
candidate_prime++
|
candidate_prime++
|
||||||
if candidate_prime==0
|
if candidate_prime==0
|
||||||
return 0 ; we wrapped; no more primes available
|
return 0 ; we wrapped; no more primes available in the sieve
|
||||||
}
|
}
|
||||||
|
|
||||||
; found next one, mark the multiples and return it.
|
; found next one, mark the multiples and return it.
|
||||||
sieve[candidate_prime] = true
|
sieve[candidate_prime] = true
|
||||||
uword multiple = candidate_prime
|
uword multiple = candidate_prime
|
||||||
|
|
||||||
while multiple < len(sieve) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
sieve[lsb(multiple)] = true
|
||||||
multiple += candidate_prime
|
multiple += candidate_prime
|
||||||
@ -120,11 +123,11 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when compiled an ran on a C-64 you'll get:
|
when compiled an ran on a C-64 you'll get:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
One of the included examples (wizzine.p8) animates a bunch of sprite balloons and looks like this:
|
One of the included examples (wizzine.p8) animates a bunch of sprite balloons and looks like this:
|
||||||
|
|
||||||

|

|
||||||
@ -136,3 +139,8 @@ Another example (cube3d-sprites.p8) draws the vertices of a rotating 3d cube:
|
|||||||
If you want to play a video game, a fully working Tetris clone is included in the examples:
|
If you want to play a video game, a fully working Tetris clone is included in the examples:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
The CommanderX16 compiler target is quite capable already too, here's a well known space ship
|
||||||
|
animated in 3D with hidden line removal, in the CommanderX16 emulator:
|
||||||
|
|
||||||
|

|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
%target c64
|
%target c64
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
c64flt {
|
floats {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
@ -50,22 +50,22 @@ romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
|||||||
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
|
|
||||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||||
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use floats.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
||||||
|
|
||||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||||
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use floats.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||||
|
|
||||||
romsub $bc9b = QINT() clobbers(A,X,Y) ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
romsub $bc9b = QINT() clobbers(A,X,Y) ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||||
romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||||
|
|
||||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||||
; (tip: use c64flt.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
; (tip: use floats.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||||
; there is also c64flt.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||||
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
; there is also floats.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||||
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
; there is also floats.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||||
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
; there is also floats.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||||
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||||
|
|
||||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||||
@ -212,6 +212,6 @@ sub print_f (float value) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
%asminclude "library:c64floats.asm", ""
|
%asminclude "library:c64/floats.asm", ""
|
||||||
|
|
||||||
}
|
}
|
@ -1,12 +1,14 @@
|
|||||||
%target c64
|
%target c64
|
||||||
%import c64textio
|
%import textio
|
||||||
|
|
||||||
; bitmap pixel graphics module for the C64
|
; bitmap pixel graphics module for the C64
|
||||||
; only black/white monchrome for now
|
; only black/white monchrome 320x200 for now
|
||||||
; assumes bitmap screen memory is $2000-$3fff
|
; assumes bitmap screen memory is $2000-$3fff
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
const uword bitmap_address = $2000
|
const uword BITMAP_ADDRESS = $2000
|
||||||
|
const uword WIDTH = 320
|
||||||
|
const ubyte HEIGHT = 200
|
||||||
|
|
||||||
sub enable_bitmap_mode() {
|
sub enable_bitmap_mode() {
|
||||||
; enable bitmap screen, erase it and set colors to black/white.
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
@ -16,7 +18,7 @@ graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||||
memset(bitmap_address, 320*200/8, 0)
|
memset(BITMAP_ADDRESS, 320*200/8, 0)
|
||||||
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,33 +137,33 @@ graphics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub disc(uword cx, ubyte cy, ubyte radius) {
|
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
; Midpoint algorithm, filled
|
; Midpoint algorithm, filled
|
||||||
ubyte xx = radius
|
ubyte xx = radius
|
||||||
ubyte yy = 0
|
ubyte yy = 0
|
||||||
byte decisionOver2 = 1-xx as byte
|
byte decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
while xx>=yy {
|
while xx>=yy {
|
||||||
ubyte cy_plus_yy = cy + yy
|
ubyte ycenter_plus_yy = ycenter + yy
|
||||||
ubyte cy_min_yy = cy - yy
|
ubyte ycenter_min_yy = ycenter - yy
|
||||||
ubyte cy_plus_xx = cy + xx
|
ubyte ycenter_plus_xx = ycenter + xx
|
||||||
ubyte cy_min_xx = cy - xx
|
ubyte ycenter_min_xx = ycenter - xx
|
||||||
|
|
||||||
for internal_plotx in cx to cx+xx {
|
for internal_plotx in xcenter to xcenter+xx {
|
||||||
internal_plot(cy_plus_yy)
|
internal_plot(ycenter_plus_yy)
|
||||||
internal_plot(cy_min_yy)
|
internal_plot(ycenter_min_yy)
|
||||||
}
|
}
|
||||||
for internal_plotx in cx-xx to cx-1 {
|
for internal_plotx in xcenter-xx to xcenter-1 {
|
||||||
internal_plot(cy_plus_yy)
|
internal_plot(ycenter_plus_yy)
|
||||||
internal_plot(cy_min_yy)
|
internal_plot(ycenter_min_yy)
|
||||||
}
|
}
|
||||||
for internal_plotx in cx to cx+yy {
|
for internal_plotx in xcenter to xcenter+yy {
|
||||||
internal_plot(cy_plus_xx)
|
internal_plot(ycenter_plus_xx)
|
||||||
internal_plot(cy_min_xx)
|
internal_plot(ycenter_min_xx)
|
||||||
}
|
}
|
||||||
for internal_plotx in cx-yy to cx {
|
for internal_plotx in xcenter-yy to xcenter {
|
||||||
internal_plot(cy_plus_xx)
|
internal_plot(ycenter_plus_xx)
|
||||||
internal_plot(cy_min_xx)
|
internal_plot(ycenter_min_xx)
|
||||||
}
|
}
|
||||||
yy++
|
yy++
|
||||||
if decisionOver2<=0
|
if decisionOver2<=0
|
||||||
@ -177,7 +179,7 @@ graphics {
|
|||||||
; here is the non-asm code for the plot routine below:
|
; here is the non-asm code for the plot routine below:
|
||||||
; sub plot_nonasm(uword px, ubyte py) {
|
; sub plot_nonasm(uword px, ubyte py) {
|
||||||
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||||
; uword addr = bitmap_address + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
; uword addr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||||
; @(addr) |= ormask[lsb(px) & 7]
|
; @(addr) |= ormask[lsb(px) & 7]
|
||||||
; }
|
; }
|
||||||
|
|
||||||
@ -226,7 +228,7 @@ _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
|||||||
|
|
||||||
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
||||||
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||||
; the y lookup tables encodes this formula: bitmap_address + 320*(py>>3) + (py & 7) (y from 0..199)
|
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||||
|
|
||||||
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
@ -11,6 +11,7 @@ c64 {
|
|||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
|
&ubyte STATUS = $90 ; kernel status variable for I/O
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
|
||||||
@ -259,6 +260,16 @@ asmsub init_system() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub reset_system() {
|
||||||
|
; Soft-reset the system back to Basic prompt.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #14
|
||||||
|
sta $01 ; bank the kernal in
|
||||||
|
jmp (c64.RESET_VEC)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub set_irqvec_excl() clobbers(A) {
|
asmsub set_irqvec_excl() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
@ -4,23 +4,22 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
|
||||||
%target c64
|
%target c64
|
||||||
%import c64lib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
|
||||||
|
|
||||||
txt {
|
txt {
|
||||||
|
|
||||||
asmsub clear_screen() {
|
const ubyte DEFAULT_WIDTH = 40
|
||||||
%asm {{
|
const ubyte DEFAULT_HEIGHT = 25
|
||||||
lda #' '
|
|
||||||
jmp clear_screenchars
|
|
||||||
}}
|
sub clear_screen() {
|
||||||
|
clear_screenchars(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
asmsub fill_screen (ubyte char @ A, ubyte charcolor @ Y) clobbers(A) {
|
|
||||||
; ---- fill the character screen with the given fill character and character color.
|
; ---- fill the character screen with the given fill character and character color.
|
||||||
; (assumes screen and color matrix are at their default addresses)
|
; (assumes screen and color matrix are at their default addresses)
|
||||||
|
|
||||||
@ -50,7 +49,7 @@ _loop sta c64.Screen,y
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub clear_screencolors (ubyte scrcolor @ A) clobbers(Y) {
|
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||||
; ---- clear the character screen colors with the given color (leaves characters).
|
; ---- clear the character screen colors with the given color (leaves characters).
|
||||||
; (assumes color matrix is at the default address)
|
; (assumes color matrix is at the default address)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -69,7 +68,15 @@ sub color (ubyte txtcol) {
|
|||||||
c64.COLOR = txtcol
|
c64.COLOR = txtcol
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
sub lowercase() {
|
||||||
|
c64.VMCSB |= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
sub uppercase() {
|
||||||
|
c64.VMCSB &= ~2
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||||
; ---- scroll the whole screen 1 character to the left
|
; ---- scroll the whole screen 1 character to the left
|
||||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -108,7 +115,7 @@ _scroll_screen ; scroll the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_right_full (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character to the right
|
; ---- scroll the whole screen 1 character to the right
|
||||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -142,7 +149,7 @@ _scroll_screen ; scroll the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_up_full (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character up
|
; ---- scroll the whole screen 1 character up
|
||||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -176,7 +183,7 @@ _scroll_screen ; scroll the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_down_full (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character down
|
; ---- scroll the whole screen 1 character down
|
||||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -210,6 +217,8 @@ _scroll_screen ; scroll the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||||
|
|
||||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
; ---- print null terminated string from A/Y
|
; ---- print null terminated string from A/Y
|
||||||
; note: the compiler contains an optimization that will replace
|
; note: the compiler contains an optimization that will replace
|
||||||
@ -433,21 +442,22 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub setchr (ubyte col @Y, ubyte row @A) clobbers(A) {
|
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
|
||||||
; ---- set the character in SCRATCH_ZPB1 on the screen matrix at the given position
|
; ---- sets the character in the screen matrix at the given position
|
||||||
%asm {{
|
%asm {{
|
||||||
sty P8ZP_SCRATCH_REG
|
pha
|
||||||
|
tya
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _screenrows+1,y
|
lda _screenrows+1,y
|
||||||
sta _mod+2
|
sta _mod+2
|
||||||
lda _screenrows,y
|
txa
|
||||||
clc
|
clc
|
||||||
adc P8ZP_SCRATCH_REG
|
adc _screenrows,y
|
||||||
sta _mod+1
|
sta _mod+1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc _mod+2
|
||||||
+ lda P8ZP_SCRATCH_B1
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
_mod sta $ffff ; modified
|
||||||
rts
|
rts
|
||||||
|
|
||||||
@ -455,17 +465,18 @@ _screenrows .word $0400 + range(0, 1000, 40)
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub getchr (ubyte col @Y, ubyte row @A) clobbers(Y) -> ubyte @ A {
|
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||||
; ---- get the character in the screen matrix at the given location
|
; ---- get the character in the screen matrix at the given location
|
||||||
%asm {{
|
%asm {{
|
||||||
sty P8ZP_SCRATCH_B1
|
pha
|
||||||
|
tya
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _mod+2
|
sta _mod+2
|
||||||
lda setchr._screenrows,y
|
pla
|
||||||
clc
|
clc
|
||||||
adc P8ZP_SCRATCH_B1
|
adc setchr._screenrows,y
|
||||||
sta _mod+1
|
sta _mod+1
|
||||||
bcc _mod
|
bcc _mod
|
||||||
inc _mod+2
|
inc _mod+2
|
||||||
@ -474,21 +485,22 @@ _mod lda $ffff ; modified
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub setclr (ubyte col @Y, ubyte row @A) clobbers(A) {
|
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||||
; ---- set the color in SCRATCH_ZPB1 on the screen matrix at the given position
|
; ---- set the color in A on the screen matrix at the given position
|
||||||
%asm {{
|
%asm {{
|
||||||
sty P8ZP_SCRATCH_REG
|
pha
|
||||||
|
tya
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _colorrows+1,y
|
lda _colorrows+1,y
|
||||||
sta _mod+2
|
sta _mod+2
|
||||||
lda _colorrows,y
|
txa
|
||||||
clc
|
clc
|
||||||
adc P8ZP_SCRATCH_REG
|
adc _colorrows,y
|
||||||
sta _mod+1
|
sta _mod+1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc _mod+2
|
||||||
+ lda P8ZP_SCRATCH_B1
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
_mod sta $ffff ; modified
|
||||||
rts
|
rts
|
||||||
|
|
||||||
@ -496,17 +508,18 @@ _colorrows .word $d800 + range(0, 1000, 40)
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub getclr (ubyte col @Y, ubyte row @A) clobbers(Y) -> ubyte @ A {
|
asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||||
; ---- get the color in the screen color matrix at the given location
|
; ---- get the color in the screen color matrix at the given location
|
||||||
%asm {{
|
%asm {{
|
||||||
sty P8ZP_SCRATCH_B1
|
pha
|
||||||
|
tya
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setclr._colorrows+1,y
|
lda setclr._colorrows+1,y
|
||||||
sta _mod+2
|
sta _mod+2
|
||||||
lda setclr._colorrows,y
|
pla
|
||||||
clc
|
clc
|
||||||
adc P8ZP_SCRATCH_B1
|
adc setclr._colorrows,y
|
||||||
sta _mod+1
|
sta _mod+1
|
||||||
bcc _mod
|
bcc _mod
|
||||||
inc _mod+2
|
inc _mod+2
|
||||||
@ -553,4 +566,22 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen width (number of columns)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
txa
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen height (number of rows)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
tya
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,7 +7,7 @@
|
|||||||
%target cx16
|
%target cx16
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
c64flt {
|
floats {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
@ -25,7 +25,7 @@ c64flt {
|
|||||||
romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||||
|
|
||||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||||
; there is also c64flt.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||||
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||||
|
|
||||||
@ -143,11 +143,11 @@ sub print_f (float value) {
|
|||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
plx
|
+ plx
|
||||||
+ rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
%asminclude "library:c64floats.asm", ""
|
%asminclude "library:c64/floats.asm", ""
|
||||||
|
|
||||||
}
|
}
|
156
compiler/res/prog8lib/cx16/graphics.p8
Normal file
156
compiler/res/prog8lib/cx16/graphics.p8
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
%target cx16
|
||||||
|
%import syslib
|
||||||
|
|
||||||
|
; bitmap pixel graphics module for the CommanderX16
|
||||||
|
; wraps the graphics functions that are in ROM.
|
||||||
|
; only black/white monchrome 320x200 for now.
|
||||||
|
|
||||||
|
graphics {
|
||||||
|
const uword WIDTH = 320
|
||||||
|
const ubyte HEIGHT = 200
|
||||||
|
|
||||||
|
sub enable_bitmap_mode() {
|
||||||
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
|
void cx16.screen_set_mode($80)
|
||||||
|
cx16.r0 = 0
|
||||||
|
cx16.GRAPH_init()
|
||||||
|
clear_screen(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||||
|
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
||||||
|
cx16.GRAPH_clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||||
|
cx16.r0 = x1
|
||||||
|
cx16.r1 = y1
|
||||||
|
cx16.r2 = x2
|
||||||
|
cx16.r3 = y2
|
||||||
|
cx16.GRAPH_draw_line()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
;cx16.r0 = xcenter - radius/2
|
||||||
|
;cx16.r1 = ycenter - radius/2
|
||||||
|
;cx16.r2 = radius*2
|
||||||
|
;cx16.r3 = radius*2
|
||||||
|
;cx16.GRAPH_draw_oval(false) ; TODO currently is not implemented on cx16, does a BRK
|
||||||
|
|
||||||
|
; Midpoint algorithm
|
||||||
|
ubyte @zp xx = radius
|
||||||
|
ubyte @zp yy = 0
|
||||||
|
byte @zp decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
cx16.r0 = xcenter + xx
|
||||||
|
cx16.r1 = ycenter + yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter + xx
|
||||||
|
cx16.r1 = ycenter - yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter + yy
|
||||||
|
cx16.r1 = ycenter + xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter + yy
|
||||||
|
cx16.r1 = ycenter - xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0 {
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
} else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
; cx16.r0 = xcenter - radius/2
|
||||||
|
; cx16.r1 = ycenter - radius/2
|
||||||
|
; cx16.r2 = radius*2
|
||||||
|
; cx16.r3 = radius*2
|
||||||
|
; cx16.GRAPH_draw_oval(true) ; TODO currently is not implemented on cx16, does a BRK
|
||||||
|
|
||||||
|
ubyte xx = radius
|
||||||
|
ubyte yy = 0
|
||||||
|
byte decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
ubyte ycenter_plus_yy = ycenter + yy
|
||||||
|
ubyte ycenter_min_yy = ycenter - yy
|
||||||
|
ubyte ycenter_plus_xx = ycenter + xx
|
||||||
|
ubyte ycenter_min_xx = ycenter - xx
|
||||||
|
uword @zp plotx
|
||||||
|
|
||||||
|
for plotx in xcenter to xcenter+xx {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
for plotx in xcenter-xx to xcenter-1 {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
for plotx in xcenter to xcenter+yy {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
for plotx in xcenter-yy to xcenter {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub plot(uword plotx, ubyte ploty) {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ploty
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; r
|
|||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> ubyte @A, uword @ XY ; read/set top of memory pointer, returns number of banks in A
|
||||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
@ -60,69 +60,69 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
|||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
|
||||||
; 65c02 hardware vectors:
|
; 65c02 hardware vectors:
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers
|
; the sixteen virtual 16-bit registers
|
||||||
&uword r0 = $02
|
&uword r0 = $0002
|
||||||
&uword r1 = $04
|
&uword r1 = $0004
|
||||||
&uword r2 = $06
|
&uword r2 = $0006
|
||||||
&uword r3 = $08
|
&uword r3 = $0008
|
||||||
&uword r4 = $0a
|
&uword r4 = $000a
|
||||||
&uword r5 = $0c
|
&uword r5 = $000c
|
||||||
&uword r6 = $0e
|
&uword r6 = $000e
|
||||||
&uword r7 = $10
|
&uword r7 = $0010
|
||||||
&uword r8 = $12
|
&uword r8 = $0012
|
||||||
&uword r9 = $14
|
&uword r9 = $0014
|
||||||
&uword r10 = $16
|
&uword r10 = $0016
|
||||||
&uword r11 = $18
|
&uword r11 = $0018
|
||||||
&uword r12 = $1a
|
&uword r12 = $001a
|
||||||
&uword r13 = $1c
|
&uword r13 = $001c
|
||||||
&uword r14 = $1e
|
&uword r14 = $001e
|
||||||
&uword r15 = $20
|
&uword r15 = $0020
|
||||||
|
|
||||||
; VERA registers
|
; VERA registers
|
||||||
|
|
||||||
const uword VERA_BASE = $9F20
|
const uword VERA_BASE = $9F20
|
||||||
&ubyte VERA_ADDR_L = VERA_BASE + $00
|
&ubyte VERA_ADDR_L = VERA_BASE + $0000
|
||||||
&ubyte VERA_ADDR_M = VERA_BASE + $01
|
&ubyte VERA_ADDR_M = VERA_BASE + $0001
|
||||||
&ubyte VERA_ADDR_H = VERA_BASE + $02
|
&ubyte VERA_ADDR_H = VERA_BASE + $0002
|
||||||
&ubyte VERA_DATA0 = VERA_BASE + $03
|
&ubyte VERA_DATA0 = VERA_BASE + $0003
|
||||||
&ubyte VERA_DATA1 = VERA_BASE + $04
|
&ubyte VERA_DATA1 = VERA_BASE + $0004
|
||||||
&ubyte VERA_CTRL = VERA_BASE + $05
|
&ubyte VERA_CTRL = VERA_BASE + $0005
|
||||||
&ubyte VERA_IEN = VERA_BASE + $06
|
&ubyte VERA_IEN = VERA_BASE + $0006
|
||||||
&ubyte VERA_ISR = VERA_BASE + $07
|
&ubyte VERA_ISR = VERA_BASE + $0007
|
||||||
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $08
|
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
||||||
&ubyte VERA_DC_VIDEO = VERA_BASE + $09
|
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
||||||
&ubyte VERA_DC_HSCALE = VERA_BASE + $0A
|
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
||||||
&ubyte VERA_DC_VSCALE = VERA_BASE + $0B
|
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
||||||
&ubyte VERA_DC_BORDER = VERA_BASE + $0C
|
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
||||||
&ubyte VERA_DC_HSTART = VERA_BASE + $09
|
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
||||||
&ubyte VERA_DC_HSTOP = VERA_BASE + $0A
|
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
||||||
&ubyte VERA_DC_VSTART = VERA_BASE + $0B
|
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
||||||
&ubyte VERA_DC_VSTOP = VERA_BASE + $0C
|
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
||||||
&ubyte VERA_L0_CONFIG = VERA_BASE + $0D
|
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
||||||
&ubyte VERA_L0_MAPBASE = VERA_BASE + $0E
|
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
||||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $0F
|
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||||
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $10
|
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
|
||||||
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $11
|
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
|
||||||
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $12
|
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
|
||||||
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $13
|
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
|
||||||
&ubyte VERA_L1_CONFIG = VERA_BASE + $14
|
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
|
||||||
&ubyte VERA_L1_MAPBASE = VERA_BASE + $15
|
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
|
||||||
&ubyte VERA_L1_TILEBASE = VERA_BASE + $16
|
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
|
||||||
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $17
|
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
|
||||||
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $18
|
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
|
||||||
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $19
|
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
|
||||||
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $1A
|
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
|
||||||
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $1B
|
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
|
||||||
&ubyte VERA_AUDIO_RATE = VERA_BASE + $1C
|
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
|
||||||
&ubyte VERA_AUDIO_DATA = VERA_BASE + $1D
|
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
||||||
&ubyte VERA_SPI_DATA = VERA_BASE + $1E
|
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
|
||||||
&ubyte VERA_SPI_CTRL = VERA_BASE + $1F
|
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
|
||||||
; VERA_PSG_BASE = $1F9C0
|
; VERA_PSG_BASE = $1F9C0
|
||||||
; VERA_PALETTE_BASE = $1FA00
|
; VERA_PALETTE_BASE = $1FA00
|
||||||
; VERA_SPRITES_BASE = $1FC00
|
; VERA_SPRITES_BASE = $1FC00
|
||||||
@ -259,6 +259,14 @@ asmsub init_system() {
|
|||||||
jsr c64.IOINIT
|
jsr c64.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr c64.RESTOR
|
||||||
jsr c64.CINT
|
jsr c64.CINT
|
||||||
|
lda #$90 ; black
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #1 ; swap fg/bg
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #$9e ; yellow
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #147 ; clear screen
|
||||||
|
jsr c64.CHROUT
|
||||||
lda #0
|
lda #0
|
||||||
tax
|
tax
|
||||||
tay
|
tay
|
||||||
@ -269,4 +277,15 @@ asmsub init_system() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub reset_system() {
|
||||||
|
; Soft-reset the system back to Basic prompt.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #14
|
||||||
|
sta $01
|
||||||
|
stz cx16.d1prb ; bank the kernal in
|
||||||
|
jmp (cx16.RESET_VEC)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
696
compiler/res/prog8lib/cx16/textio.p8
Normal file
696
compiler/res/prog8lib/cx16/textio.p8
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target cx16
|
||||||
|
%import syslib
|
||||||
|
%import conv
|
||||||
|
|
||||||
|
|
||||||
|
txt {
|
||||||
|
|
||||||
|
const ubyte DEFAULT_WIDTH = 80
|
||||||
|
const ubyte DEFAULT_HEIGHT = 60
|
||||||
|
|
||||||
|
|
||||||
|
sub clear_screen() {
|
||||||
|
clear_screenchars(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
|
; ---- fill the character screen with the given fill character and character color.
|
||||||
|
%asm {{
|
||||||
|
sty _ly+1
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
jsr c64.SCREEN ; get dimensions in X/Y
|
||||||
|
txa
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
sta _lx+1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
stz cx16.VERA_ADDR_L ; start at (0,0)
|
||||||
|
stz cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
phy
|
||||||
|
_ly ldy #1 ; modified
|
||||||
|
- sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
ply
|
||||||
|
dey
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
|
bra _lx
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||||
|
; ---- clear the character screen with the given fill character (leaves colors)
|
||||||
|
; (assumes screen matrix is at the default address)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
jsr c64.SCREEN ; get dimensions in X/Y
|
||||||
|
txa
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
sta _lx+1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
lda #%00100000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 2, bank 0.
|
||||||
|
stz cx16.VERA_ADDR_L ; start at (0,0)
|
||||||
|
stz cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
- sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dey
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
|
bra _lx
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||||
|
; ---- clear the character screen colors with the given color (leaves characters).
|
||||||
|
; (assumes color matrix is at the default address)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
sta _la+1
|
||||||
|
jsr c64.SCREEN ; get dimensions in X/Y
|
||||||
|
txa
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
sta _lx+1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
lda #%00100000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 2, bank 0.
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_ADDR_L ; start at (1,0)
|
||||||
|
stz cx16.VERA_ADDR_M
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
_la lda #0 ; modified
|
||||||
|
- sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dey
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
|
bra _lx
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
||||||
|
|
||||||
|
sub color (ubyte txtcol) {
|
||||||
|
c64.CHROUT(color_to_charcode[txtcol & 15])
|
||||||
|
}
|
||||||
|
|
||||||
|
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||||
|
c64.CHROUT(color_to_charcode[bgcol & 15])
|
||||||
|
c64.CHROUT(1) ; switch fg and bg colors
|
||||||
|
c64.CHROUT(color_to_charcode[txtcol & 15])
|
||||||
|
}
|
||||||
|
|
||||||
|
sub lowercase() {
|
||||||
|
cx16.screen_set_charset(3, 0) ; lowercase charset
|
||||||
|
}
|
||||||
|
|
||||||
|
sub uppercase() {
|
||||||
|
cx16.screen_set_charset(2, 0) ; uppercase charset
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_left (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character to the left
|
||||||
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
dex
|
||||||
|
stx _lx+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
stz cx16.VERA_CTRL ; data port 0: source column
|
||||||
|
lda #%00010000 ; auto increment 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda #2
|
||||||
|
sta cx16.VERA_ADDR_L ; begin in column 1
|
||||||
|
ldy P8ZP_SCRATCH_B1
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||||
|
lda #%00010000 ; auto increment 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
bpl _nextline
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_right (ubyte dummy @ Pc) clobbers(A) {
|
||||||
|
; ---- scroll the whole screen 1 character to the right
|
||||||
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
dex
|
||||||
|
stx _lx+1
|
||||||
|
txa
|
||||||
|
asl a
|
||||||
|
dea
|
||||||
|
sta _rcol+1
|
||||||
|
ina
|
||||||
|
ina
|
||||||
|
sta _rcol2+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
stz cx16.VERA_CTRL ; data port 0: source column
|
||||||
|
lda #%00011000 ; auto decrement 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
_rcol lda #79*2-1 ; modified
|
||||||
|
sta cx16.VERA_ADDR_L ; begin in rightmost column minus one
|
||||||
|
ldy P8ZP_SCRATCH_B1
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||||
|
lda #%00011000 ; auto decrement 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
_rcol2 lda #79*2+1 ; modified
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
bpl _nextline
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_up (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character up
|
||||||
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
stx _nextline+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
stz cx16.VERA_CTRL ; data port 0 is source
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_ADDR_M ; start at second line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1 is destination
|
||||||
|
stz cx16.VERA_ADDR_M ; start at top line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
ldx #80 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_CTRL ; data port 0
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M
|
||||||
|
bra _nextline
|
||||||
|
|
||||||
|
+ lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_down (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character down
|
||||||
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
stx _nextline+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
stz cx16.VERA_CTRL ; data port 0 is source
|
||||||
|
dey
|
||||||
|
sty cx16.VERA_ADDR_M ; start at line before bottom line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1 is destination
|
||||||
|
iny
|
||||||
|
sty cx16.VERA_ADDR_M ; start at bottom line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
ldx #80 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_CTRL ; data port 0
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
dec cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
dec cx16.VERA_ADDR_M
|
||||||
|
bra _nextline
|
||||||
|
|
||||||
|
+ lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||||
|
|
||||||
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print null terminated string from A/Y
|
||||||
|
; note: the compiler contains an optimization that will replace
|
||||||
|
; a call to this subroutine with a string argument of just one char,
|
||||||
|
; by just one call to c64.CHROUT of that single char.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
_print_byte_digits
|
||||||
|
pha
|
||||||
|
cpy #'0'
|
||||||
|
beq +
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
jmp _ones
|
||||||
|
+ pla
|
||||||
|
cmp #'0'
|
||||||
|
beq _ones
|
||||||
|
jsr c64.CHROUT
|
||||||
|
_ones txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the byte in A in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
cmp #0
|
||||||
|
bpl +
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ pla
|
||||||
|
jsr conv.byte2decimal
|
||||||
|
jmp print_ub._print_byte_digits
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
bcc +
|
||||||
|
pha
|
||||||
|
lda #'$'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
+ jsr conv.ubyte2hex
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'%'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ ldy #8
|
||||||
|
- lda #'0'
|
||||||
|
asl P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'1'
|
||||||
|
+ jsr c64.CHROUT
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr print_ubbin
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
jmp print_ubbin
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr print_ubhex
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
jmp print_ubhex
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
plx
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq _allzero
|
||||||
|
cmp #'0'
|
||||||
|
bne _gotdigit
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
|
||||||
|
_gotdigit
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
|
bne _gotdigit
|
||||||
|
rts
|
||||||
|
_allzero
|
||||||
|
lda #'0'
|
||||||
|
jmp c64.CHROUT
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||||
|
%asm {{
|
||||||
|
cpy #0
|
||||||
|
bpl +
|
||||||
|
pha
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
eor #255
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
eor #255
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jmp print_uw
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||||
|
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||||
|
; It assumes the keyboard is selected as I/O channel!
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0 ; char counter = 0
|
||||||
|
- jsr c64.CHRIN
|
||||||
|
cmp #$0d ; return (ascii 13) pressed?
|
||||||
|
beq + ; yes, end.
|
||||||
|
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ lda #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||||
|
rts
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A) {
|
||||||
|
; ---- sets the character in the screen matrix at the given position
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
asl a
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||||
|
; ---- get the character in the screen matrix at the given location
|
||||||
|
%asm {{
|
||||||
|
asl a
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
||||||
|
; ---- set the color in A on the screen matrix at the given position
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
asl a
|
||||||
|
ina
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||||
|
; ---- get the color in the screen color matrix at the given location
|
||||||
|
%asm {{
|
||||||
|
asl a
|
||||||
|
ina
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||||
|
; ---- set char+color at the given position on the screen
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
lda column
|
||||||
|
asl a
|
||||||
|
tax
|
||||||
|
ldy row
|
||||||
|
lda charcolor
|
||||||
|
and #$0f
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
stx cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda char
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
inx
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
stx cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
and #$f0
|
||||||
|
ora P8ZP_SCRATCH_B1
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
|
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
tax
|
||||||
|
clc
|
||||||
|
jsr c64.PLOT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen width (number of columns)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
txa
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen height (number of rows)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
tya
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,344 +0,0 @@
|
|||||||
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
|
||||||
;
|
|
||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
;
|
|
||||||
; indent format: TABS, size=8
|
|
||||||
|
|
||||||
%target cx16
|
|
||||||
%import cx16lib
|
|
||||||
%import conv
|
|
||||||
|
|
||||||
|
|
||||||
txt {
|
|
||||||
|
|
||||||
sub clear_screen() {
|
|
||||||
c64.CHROUT(147) ; clear screen (spaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
asmsub fill_screen (ubyte char @ A, ubyte txtcolor @ Y) clobbers(A) {
|
|
||||||
; ---- fill the character screen with the given fill character and character color.
|
|
||||||
%asm {{
|
|
||||||
sta P8ZP_SCRATCH_W1 ; fillchar
|
|
||||||
sty P8ZP_SCRATCH_W1+1 ; textcolor
|
|
||||||
phx
|
|
||||||
jsr c64.SCREEN ; get dimensions in X/Y
|
|
||||||
dex
|
|
||||||
dey
|
|
||||||
txa
|
|
||||||
asl a
|
|
||||||
adc #1
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
- ldx P8ZP_SCRATCH_B1
|
|
||||||
- stz cx16.VERA_ADDR_H
|
|
||||||
stx cx16.VERA_ADDR_L
|
|
||||||
sty cx16.VERA_ADDR_M
|
|
||||||
lda cx16.VERA_DATA0
|
|
||||||
and #$f0
|
|
||||||
ora P8ZP_SCRATCH_W1+1
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
dex
|
|
||||||
stz cx16.VERA_ADDR_H
|
|
||||||
stx cx16.VERA_ADDR_L
|
|
||||||
sty cx16.VERA_ADDR_M
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
dex
|
|
||||||
cpx #255
|
|
||||||
bne -
|
|
||||||
dey
|
|
||||||
bpl --
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
|
||||||
; ---- clear the character screen with the given fill character (leaves colors)
|
|
||||||
; (assumes screen matrix is at the default address)
|
|
||||||
%asm {{
|
|
||||||
pha
|
|
||||||
phx
|
|
||||||
jsr c64.SCREEN ; get dimensions in X/Y
|
|
||||||
dex
|
|
||||||
dey
|
|
||||||
txa
|
|
||||||
asl a
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
pla
|
|
||||||
- ldx P8ZP_SCRATCH_B1
|
|
||||||
- stz cx16.VERA_ADDR_H
|
|
||||||
stx cx16.VERA_ADDR_L
|
|
||||||
sty cx16.VERA_ADDR_M
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cpx #254
|
|
||||||
bne -
|
|
||||||
dey
|
|
||||||
bpl --
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
|
||||||
|
|
||||||
sub color (ubyte txtcol) {
|
|
||||||
c64.CHROUT(color_to_charcode[txtcol & 15])
|
|
||||||
}
|
|
||||||
|
|
||||||
sub color2 (ubyte txtcol, ubyte bgcol) {
|
|
||||||
c64.CHROUT(color_to_charcode[bgcol & 15])
|
|
||||||
c64.CHROUT(1) ; switch fg and bg colors
|
|
||||||
c64.CHROUT(color_to_charcode[txtcol & 15])
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
|
||||||
; ---- print null terminated string from A/Y
|
|
||||||
; note: the compiler contains an optimization that will replace
|
|
||||||
; a call to this subroutine with a string argument of just one char,
|
|
||||||
; by just one call to c64.CHROUT of that single char.
|
|
||||||
%asm {{
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
sty P8ZP_SCRATCH_REG
|
|
||||||
ldy #0
|
|
||||||
- lda (P8ZP_SCRATCH_B1),y
|
|
||||||
beq +
|
|
||||||
jsr c64.CHROUT
|
|
||||||
iny
|
|
||||||
bne -
|
|
||||||
+ rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
|
||||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
jsr conv.ubyte2decimal
|
|
||||||
pha
|
|
||||||
tya
|
|
||||||
jsr c64.CHROUT
|
|
||||||
pla
|
|
||||||
jsr c64.CHROUT
|
|
||||||
txa
|
|
||||||
jsr c64.CHROUT
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
|
||||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
jsr conv.ubyte2decimal
|
|
||||||
_print_byte_digits
|
|
||||||
pha
|
|
||||||
cpy #'0'
|
|
||||||
beq +
|
|
||||||
tya
|
|
||||||
jsr c64.CHROUT
|
|
||||||
pla
|
|
||||||
jsr c64.CHROUT
|
|
||||||
jmp _ones
|
|
||||||
+ pla
|
|
||||||
cmp #'0'
|
|
||||||
beq _ones
|
|
||||||
jsr c64.CHROUT
|
|
||||||
_ones txa
|
|
||||||
jsr c64.CHROUT
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|
||||||
; ---- print the byte in A in decimal form, without left padding 0s
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
pha
|
|
||||||
cmp #0
|
|
||||||
bpl +
|
|
||||||
lda #'-'
|
|
||||||
jsr c64.CHROUT
|
|
||||||
+ pla
|
|
||||||
jsr conv.byte2decimal
|
|
||||||
jmp print_ub._print_byte_digits
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|
||||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
bcc +
|
|
||||||
pha
|
|
||||||
lda #'$'
|
|
||||||
jsr c64.CHROUT
|
|
||||||
pla
|
|
||||||
+ jsr conv.ubyte2hex
|
|
||||||
jsr c64.CHROUT
|
|
||||||
tya
|
|
||||||
jsr c64.CHROUT
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|
||||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
bcc +
|
|
||||||
lda #'%'
|
|
||||||
jsr c64.CHROUT
|
|
||||||
+ ldy #8
|
|
||||||
- lda #'0'
|
|
||||||
asl P8ZP_SCRATCH_B1
|
|
||||||
bcc +
|
|
||||||
lda #'1'
|
|
||||||
+ jsr c64.CHROUT
|
|
||||||
dey
|
|
||||||
bne -
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|
||||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
|
||||||
%asm {{
|
|
||||||
pha
|
|
||||||
tya
|
|
||||||
jsr print_ubbin
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
jmp print_ubbin
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|
||||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
|
||||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
|
||||||
%asm {{
|
|
||||||
pha
|
|
||||||
tya
|
|
||||||
jsr print_ubhex
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
jmp print_ubhex
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
|
||||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
jsr conv.uword2decimal
|
|
||||||
ldy #0
|
|
||||||
- lda conv.uword2decimal.decTenThousands,y
|
|
||||||
beq +
|
|
||||||
jsr c64.CHROUT
|
|
||||||
iny
|
|
||||||
bne -
|
|
||||||
+ plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
|
||||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
jsr conv.uword2decimal
|
|
||||||
plx
|
|
||||||
ldy #0
|
|
||||||
- lda conv.uword2decimal.decTenThousands,y
|
|
||||||
beq _allzero
|
|
||||||
cmp #'0'
|
|
||||||
bne _gotdigit
|
|
||||||
iny
|
|
||||||
bne -
|
|
||||||
|
|
||||||
_gotdigit
|
|
||||||
jsr c64.CHROUT
|
|
||||||
iny
|
|
||||||
lda conv.uword2decimal.decTenThousands,y
|
|
||||||
bne _gotdigit
|
|
||||||
rts
|
|
||||||
_allzero
|
|
||||||
lda #'0'
|
|
||||||
jmp c64.CHROUT
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
|
||||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
|
||||||
%asm {{
|
|
||||||
cpy #0
|
|
||||||
bpl +
|
|
||||||
pha
|
|
||||||
lda #'-'
|
|
||||||
jsr c64.CHROUT
|
|
||||||
tya
|
|
||||||
eor #255
|
|
||||||
tay
|
|
||||||
pla
|
|
||||||
eor #255
|
|
||||||
clc
|
|
||||||
adc #1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jmp print_uw
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
; TODO implement the "missing" txtio subroutines
|
|
||||||
|
|
||||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
|
||||||
; ---- set char+color at the given position on the screen
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
lda column
|
|
||||||
asl a
|
|
||||||
tax
|
|
||||||
ldy row
|
|
||||||
lda charcolor
|
|
||||||
and #$0f
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
stz cx16.VERA_ADDR_H
|
|
||||||
stx cx16.VERA_ADDR_L
|
|
||||||
sty cx16.VERA_ADDR_M
|
|
||||||
lda char
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
inx
|
|
||||||
stz cx16.VERA_ADDR_H
|
|
||||||
stx cx16.VERA_ADDR_L
|
|
||||||
sty cx16.VERA_ADDR_M
|
|
||||||
lda cx16.VERA_DATA0
|
|
||||||
and #$f0
|
|
||||||
ora P8ZP_SCRATCH_B1
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|
||||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
tax
|
|
||||||
clc
|
|
||||||
jsr c64.PLOT
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -31,7 +31,7 @@ _enterloop lsr P8ZP_SCRATCH_REG
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
multiply_bytes_16 .proc
|
multiply_bytes_into_word .proc
|
||||||
; -- multiply 2 bytes A and Y, result as word in A/Y (unsigned)
|
; -- multiply 2 bytes A and Y, result as word in A/Y (unsigned)
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
sty P8ZP_SCRATCH_REG
|
sty P8ZP_SCRATCH_REG
|
||||||
|
@ -1 +1 @@
|
|||||||
4.2
|
4.3
|
||||||
|
@ -30,7 +30,7 @@ private fun ParserRuleContext.toPosition() : Position {
|
|||||||
val customTokensource = this.start.tokenSource as? CustomLexer
|
val customTokensource = this.start.tokenSource as? CustomLexer
|
||||||
val filename =
|
val filename =
|
||||||
when {
|
when {
|
||||||
customTokensource!=null -> customTokensource.modulePath.fileName.toString()
|
customTokensource!=null -> customTokensource.modulePath.toString()
|
||||||
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
|
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
|
||||||
else -> File(start.inputStream.sourceName).name
|
else -> File(start.inputStream.sourceName).name
|
||||||
}
|
}
|
||||||
@ -397,7 +397,7 @@ private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
fun makeLiteral(text: String, radix: Int): NumericLiteral {
|
||||||
val integer: Int
|
val integer: Int
|
||||||
var datatype = DataType.UBYTE
|
var datatype = DataType.UBYTE
|
||||||
when (radix) {
|
when (radix) {
|
||||||
@ -435,14 +435,14 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
|||||||
}
|
}
|
||||||
else -> throw FatalAstException("invalid radix")
|
else -> throw FatalAstException("invalid radix")
|
||||||
}
|
}
|
||||||
return NumericLiteral(integer, if (forceWord) DataType.UWORD else datatype)
|
return NumericLiteral(integer, datatype)
|
||||||
}
|
}
|
||||||
val terminal: TerminalNode = children[0] as TerminalNode
|
val terminal: TerminalNode = children[0] as TerminalNode
|
||||||
val integerPart = this.intpart.text
|
val integerPart = this.intpart.text
|
||||||
return when (terminal.symbol.type) {
|
return when (terminal.symbol.type) {
|
||||||
prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10, wordsuffix()!=null)
|
prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10)
|
||||||
prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16, wordsuffix()!=null)
|
prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16)
|
||||||
prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2, wordsuffix()!=null)
|
prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
|
||||||
else -> throw FatalAstException(terminal.text)
|
else -> throw FatalAstException(terminal.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,7 @@ object ParentSentinel : Node {
|
|||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
|
fun toClickableStr(): String = "$file:$line:$startCol:"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val DUMMY = Position("<dummy>", 0, 0, 0)
|
val DUMMY = Position("<dummy>", 0, 0, 0)
|
||||||
|
@ -24,7 +24,7 @@ class ErrorReporter {
|
|||||||
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
||||||
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
||||||
}
|
}
|
||||||
val msg = "${it.position} ${it.severity} ${it.message}".trim()
|
val msg = "${it.position.toClickableStr()} ${it.severity} ${it.message}".trim()
|
||||||
if(msg !in alreadyReportedMessages) {
|
if(msg !in alreadyReportedMessages) {
|
||||||
System.err.println(msg)
|
System.err.println(msg)
|
||||||
alreadyReportedMessages.add(msg)
|
alreadyReportedMessages.add(msg)
|
||||||
|
@ -7,11 +7,11 @@ open class FatalAstException (override var message: String) : Exception(message)
|
|||||||
open class AstException (override var message: String) : Exception(message)
|
open class AstException (override var message: String) : Exception(message)
|
||||||
|
|
||||||
open class SyntaxError(override var message: String, val position: Position) : AstException(message) {
|
open class SyntaxError(override var message: String, val position: Position) : AstException(message) {
|
||||||
override fun toString() = "$position Syntax error: $message"
|
override fun toString() = "${position.toClickableStr()} Syntax error: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
||||||
override fun toString() = "$position Error: $message"
|
override fun toString() = "${position.toClickableStr()} Error: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
class UndefinedSymbolError(symbol: IdentifierReference)
|
class UndefinedSymbolError(symbol: IdentifierReference)
|
||||||
|
@ -744,7 +744,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
override fun referencesIdentifiers(vararg name: String): Boolean =
|
||||||
|
nameInSource.size==name.size && nameInSource.toTypedArray().contentEquals(name)
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
return when (val targetStmt = targetStatement(program.namespace)) {
|
return when (val targetStmt = targetStatement(program.namespace)) {
|
||||||
|
@ -584,13 +584,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.value !is NumericLiteralValue) {
|
if(decl.value is NumericLiteralValue) {
|
||||||
err("value of memory var decl is not a numeric literal (it is a ${decl.value!!.javaClass.simpleName}).", decl.value?.position)
|
|
||||||
} else {
|
|
||||||
val value = decl.value as NumericLiteralValue
|
val value = decl.value as NumericLiteralValue
|
||||||
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
|
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
|
||||||
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
err("value of memory mapped variable can only be a number, perhaps you meant to use an address pointer type instead?", decl.value?.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -742,6 +742,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!array.value.all { it is NumericLiteralValue || it is AddressOf || it is StringLiteralValue }) {
|
||||||
|
// TODO for now, array literals have to consist of all compile time constant values...
|
||||||
|
errors.err("array literal doesn't consist of only compile time constant values", array.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(array)
|
super.visit(array)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1098,7 +1103,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(value.type.isUnknown)
|
if(value.type.isUnknown)
|
||||||
return err("attempt to check values of array with as yet unknown datatype")
|
return false
|
||||||
|
|
||||||
when (targetDt) {
|
when (targetDt) {
|
||||||
DataType.STR -> return err("string value expected")
|
DataType.STR -> return err("string value expected")
|
||||||
|
@ -34,6 +34,16 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
super.visit(block)
|
super.visit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(directive: Directive) {
|
||||||
|
if(directive.directive=="%target") {
|
||||||
|
val compatibleTarget = directive.args.single().name
|
||||||
|
if (compatibleTarget != CompilationTarget.instance.name)
|
||||||
|
errors.err("module's compilation target ($compatibleTarget) differs from active target (${CompilationTarget.instance.name})", directive.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.visit(directive)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
||||||
|
|
||||||
@ -88,14 +98,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
if (existing != null && existing !== subroutine)
|
if (existing != null && existing !== subroutine)
|
||||||
nameError(subroutine.name, subroutine.position, existing)
|
nameError(subroutine.name, subroutine.position, existing)
|
||||||
|
|
||||||
// does the parameter redefine a variable declared elsewhere?
|
|
||||||
for(param in subroutine.parameters) {
|
|
||||||
val existingVar = subroutine.lookup(listOf(param.name), subroutine)
|
|
||||||
if (existingVar != null && existingVar.parent !== subroutine) {
|
|
||||||
nameError(param.name, param.position, existingVar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
|
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
|
||||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
|
@ -10,44 +10,6 @@ import prog8.ast.statements.*
|
|||||||
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(functionCallStatement.target.nameInSource == listOf("swap")) {
|
|
||||||
// TODO don't replace swap(), let the code generator figure this all out
|
|
||||||
// if x and y are both just identifiers, do not rewrite (there should be asm generation for that)
|
|
||||||
// otherwise:
|
|
||||||
// rewrite swap(x,y) as follows:
|
|
||||||
// - declare a temp variable of the same datatype
|
|
||||||
// - temp = x, x = y, y= temp
|
|
||||||
val first = functionCallStatement.args[0]
|
|
||||||
val second = functionCallStatement.args[1]
|
|
||||||
if(first !is IdentifierReference && second !is IdentifierReference) {
|
|
||||||
val dt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
val tempname = "prog8_swaptmp_${functionCallStatement.hashCode()}"
|
|
||||||
val tempvardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, tempname, null, null, isArray = false, autogeneratedDontRemove = true, position = first.position)
|
|
||||||
val tempvar = IdentifierReference(listOf(tempname), first.position)
|
|
||||||
val assignTemp = Assignment(
|
|
||||||
AssignTarget(tempvar, null, null, first.position),
|
|
||||||
first,
|
|
||||||
first.position
|
|
||||||
)
|
|
||||||
val assignFirst = Assignment(
|
|
||||||
AssignTarget.fromExpr(first),
|
|
||||||
second,
|
|
||||||
first.position
|
|
||||||
)
|
|
||||||
val assignSecond = Assignment(
|
|
||||||
AssignTarget.fromExpr(second),
|
|
||||||
tempvar,
|
|
||||||
first.position
|
|
||||||
)
|
|
||||||
val scope = AnonymousScope(mutableListOf(tempvardecl, assignTemp, assignFirst, assignSecond), first.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, scope, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
// and include the original decl as well.
|
// and include the original decl as well.
|
||||||
|
@ -15,6 +15,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
private val noModifications = emptyList<IAstModification>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
subroutineVariables.add(Pair(decl.name, decl))
|
||||||
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
// a numeric vardecl without an initial value is initialized with zero.
|
// a numeric vardecl without an initial value is initialized with zero.
|
||||||
decl.value = decl.zeroElementValue()
|
decl.value = decl.zeroElementValue()
|
||||||
@ -41,37 +42,47 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val subroutineVariables = mutableListOf<Pair<String, VarDecl>>()
|
||||||
|
|
||||||
|
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
subroutineVariables.clear()
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
val decls = scope.statements.filterIsInstance<VarDecl>()
|
val decls = scope.statements.filterIsInstance<VarDecl>()
|
||||||
|
subroutineVariables.addAll(decls.map { Pair(it.name, it) })
|
||||||
|
|
||||||
val sub = scope.definingSubroutine()
|
val sub = scope.definingSubroutine()
|
||||||
if (sub != null) {
|
if (sub != null) {
|
||||||
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associateBy { it.name }
|
// move vardecls of the scope into the upper scope. Make sure the position remains the same!
|
||||||
var conflicts = false
|
val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes }
|
||||||
decls.forEach {
|
val replaceVardecls =numericVarsWithValue.map {
|
||||||
val existing = existingVariables[it.name]
|
val initValue = it.value!! // assume here that value has always been set by now
|
||||||
if (existing != null) {
|
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
||||||
errors.err("variable ${it.name} already defined in subroutine ${sub.name} at ${existing.position}", it.position)
|
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
||||||
conflicts = true
|
val assign = Assignment(target, initValue, it.position)
|
||||||
}
|
initValue.parent = assign
|
||||||
}
|
IAstModification.ReplaceNode(it, assign, scope)
|
||||||
if (!conflicts) {
|
|
||||||
// move vardecls of the scope into the upper scope. Make sure the order remains the same!
|
|
||||||
val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes }.reversed()
|
|
||||||
return numericVarsWithValue.map {
|
|
||||||
val initValue = it.value!! // assume here that value has always been set by now
|
|
||||||
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
|
||||||
val assign = Assignment(target, initValue, it.position)
|
|
||||||
initValue.parent = assign
|
|
||||||
IAstModification.InsertFirst(assign, scope)
|
|
||||||
} + decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } +
|
|
||||||
decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine
|
|
||||||
}
|
}
|
||||||
|
val moveVardeclsUp = decls.map { IAstModification.InsertFirst(it, sub) }
|
||||||
|
return replaceVardecls + moveVardeclsUp
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
val firstDeclarations = mutableMapOf<String, VarDecl>()
|
||||||
|
for(decl in subroutineVariables) {
|
||||||
|
val existing = firstDeclarations[decl.first]
|
||||||
|
if(existing!=null && existing !== decl.second) {
|
||||||
|
errors.err("variable ${decl.first} already defined in subroutine ${subroutine.name} at ${existing.position}", decl.second.position)
|
||||||
|
} else {
|
||||||
|
firstDeclarations[decl.first] = decl.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
||||||
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
|
@ -27,8 +27,7 @@ data class CompilationOptions(val output: OutputType,
|
|||||||
val launcher: LauncherType,
|
val launcher: LauncherType,
|
||||||
val zeropage: ZeropageType,
|
val zeropage: ZeropageType,
|
||||||
val zpReserved: List<IntRange>,
|
val zpReserved: List<IntRange>,
|
||||||
val floats: Boolean,
|
val floats: Boolean)
|
||||||
val compilationTarget: String?)
|
|
||||||
|
|
||||||
|
|
||||||
class CompilerException(message: String?) : Exception(message)
|
class CompilerException(message: String?) : Exception(message)
|
||||||
|
@ -91,7 +91,7 @@ fun compileProgram(filepath: Path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
||||||
println("Parsing...")
|
println("Compiler target: ${CompilationTarget.instance.name}. Parsing...")
|
||||||
val importer = ModuleImporter()
|
val importer = ModuleImporter()
|
||||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||||
importer.importModule(programAst, filepath)
|
importer.importModule(programAst, filepath)
|
||||||
@ -125,7 +125,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||||
val zpType: ZeropageType =
|
var zpType: ZeropageType =
|
||||||
if (zpoption == null)
|
if (zpoption == null)
|
||||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||||
else
|
else
|
||||||
@ -135,6 +135,12 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
ZeropageType.KERNALSAFE
|
ZeropageType.KERNALSAFE
|
||||||
// error will be printed by the astchecker
|
// error will be printed by the astchecker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (zpType==ZeropageType.FLOATSAFE && CompilationTarget.instance.name == Cx16Target.name) {
|
||||||
|
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
|
||||||
|
zpType = ZeropageType.BASICSAFE
|
||||||
|
}
|
||||||
|
|
||||||
val zpReserved = mainModule.statements
|
val zpReserved = mainModule.statements
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||||
@ -142,20 +148,19 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
.map { it[0].int!!..it[1].int!! }
|
.map { it[0].int!!..it[1].int!! }
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
var target = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%target" }
|
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
|
||||||
as? Directive)?.args?.single()?.name
|
System.err.println("invalid output type $outputType")
|
||||||
|
exitProcess(1)
|
||||||
when(target) {
|
}
|
||||||
C64Target.name -> CompilationTarget.instance = C64Target
|
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
|
||||||
Cx16Target.name -> CompilationTarget.instance = Cx16Target
|
System.err.println("invalid launcher type $launcherType")
|
||||||
null -> target = CompilationTarget.instance.name
|
exitProcess(1)
|
||||||
else -> throw FatalAstException("invalid target")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompilationOptions(
|
return CompilationOptions(
|
||||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||||
zpType, zpReserved, floatsEnabled, target
|
zpType, zpReserved, floatsEnabled
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +188,7 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
|||||||
// keep optimizing expressions and statements until no more steps remain
|
// keep optimizing expressions and statements until no more steps remain
|
||||||
val optsDone1 = programAst.simplifyExpressions()
|
val optsDone1 = programAst.simplifyExpressions()
|
||||||
val optsDone2 = programAst.optimizeStatements(errors)
|
val optsDone2 = programAst.optimizeStatements(errors)
|
||||||
programAst.constantFold(errors) // because simplified statements and expressions could give rise to more constants that can be folded away:
|
programAst.constantFold(errors) // because simplified statements and expressions could give rise to more constants that can be folded away
|
||||||
errors.handle()
|
errors.handle()
|
||||||
if (optsDone1 + optsDone2 == 0)
|
if (optsDone1 + optsDone2 == 0)
|
||||||
break
|
break
|
||||||
@ -214,9 +219,6 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir:
|
|||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
if(compilerOptions.compilationTarget!=null && compilerOptions.compilationTarget != CompilationTarget.instance.name)
|
|
||||||
throw AssemblyError("program's compilation target differs from configured target")
|
|
||||||
|
|
||||||
CompilationTarget.instance.machine.initializeZeropage(compilerOptions)
|
CompilationTarget.instance.machine.initializeZeropage(compilerOptions)
|
||||||
val assembly = CompilationTarget.instance.asmGenerator(
|
val assembly = CompilationTarget.instance.asmGenerator(
|
||||||
programAst,
|
programAst,
|
||||||
|
@ -17,8 +17,8 @@ internal interface CompilationTarget {
|
|||||||
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
||||||
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
||||||
fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator
|
fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator
|
||||||
val asmForSystemReset: String
|
|
||||||
val initProcName: String?
|
val initProcName: String?
|
||||||
|
val resetProcName: String?
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: CompilationTarget
|
lateinit var instance: CompilationTarget
|
||||||
@ -35,8 +35,8 @@ internal object C64Target: CompilationTarget {
|
|||||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
||||||
AsmGen(program, errors, zp, options, path)
|
AsmGen(program, errors, zp, options, path)
|
||||||
override val asmForSystemReset = " sei | lda #14 | sta $1 | jmp (c64.RESET_VEC)"
|
|
||||||
override val initProcName = "c64.init_system"
|
override val initProcName = "c64.init_system"
|
||||||
|
override val resetProcName = "c64.reset_system"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object Cx16Target: CompilationTarget {
|
internal object Cx16Target: CompilationTarget {
|
||||||
@ -48,6 +48,6 @@ internal object Cx16Target: CompilationTarget {
|
|||||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
||||||
AsmGen(program, errors, zp, options, path)
|
AsmGen(program, errors, zp, options, path)
|
||||||
override val asmForSystemReset = " sei | stz cx16.d1prb | jmp (cx16.RESET_VEC)"
|
|
||||||
override val initProcName = "cx16.init_system"
|
override val initProcName = "cx16.init_system"
|
||||||
|
override val resetProcName = "cx16.reset_system"
|
||||||
}
|
}
|
||||||
|
@ -37,28 +37,28 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
val mflpt5 = Mflpt5.fromNumber(number)
|
val mflpt5 = Mflpt5.fromNumber(number)
|
||||||
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
||||||
when {
|
when {
|
||||||
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.ZERO" // not a ROM const
|
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "floats.ZERO" // not a ROM const
|
||||||
floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "c64flt.FL_PIVAL"
|
floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "floats.FL_PIVAL"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768"
|
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "floats.FL_N32768"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FONE"
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FONE"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRHLF"
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x35, 0x04, 0xf3, 0x34)) -> return "floats.FL_SQRHLF"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRTWO"
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x35, 0x04, 0xf3, 0x34)) -> return "floats.FL_SQRTWO"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_NEGHLF"
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)) -> return "floats.FL_NEGHLF"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xf8)) -> return "c64flt.FL_LOG2"
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xf8)) -> return "floats.FL_LOG2"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)) -> return "c64flt.FL_TENC"
|
floatbytes.contentEquals(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)) -> return "floats.FL_TENC"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x9e, 0x6e, 0x6b, 0x28, 0x00)) -> return "c64flt.FL_NZMIL"
|
floatbytes.contentEquals(shortArrayOf(0x9e, 0x6e, 0x6b, 0x28, 0x00)) -> return "floats.FL_NZMIL"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FHALF"
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FHALF"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x38, 0xaa, 0x3b, 0x29)) -> return "c64flt.FL_LOGEB2"
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x38, 0xaa, 0x3b, 0x29)) -> return "floats.FL_LOGEB2"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_PIHALF"
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x49, 0x0f, 0xda, 0xa2)) -> return "floats.FL_PIHALF"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x83, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_TWOPI"
|
floatbytes.contentEquals(shortArrayOf(0x83, 0x49, 0x0f, 0xda, 0xa2)) -> return "floats.FL_TWOPI"
|
||||||
floatbytes.contentEquals(shortArrayOf(0x7f, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FR4"
|
floatbytes.contentEquals(shortArrayOf(0x7f, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FR4"
|
||||||
else -> {
|
else -> {
|
||||||
// attempt to correct for a few rounding issues
|
// attempt to correct for a few rounding issues
|
||||||
when (number.toBigDecimal().setScale(10, RoundingMode.HALF_DOWN).toDouble()) {
|
when (number.toBigDecimal().setScale(10, RoundingMode.HALF_DOWN).toDouble()) {
|
||||||
3.1415926536 -> return "c64flt.FL_PIVAL"
|
3.1415926536 -> return "floats.FL_PIVAL"
|
||||||
1.4142135624 -> return "c64flt.FL_SQRTWO"
|
1.4142135624 -> return "floats.FL_SQRTWO"
|
||||||
0.7071067812 -> return "c64flt.FL_SQRHLF"
|
0.7071067812 -> return "floats.FL_SQRHLF"
|
||||||
0.6931471806 -> return "c64flt.FL_LOG2"
|
0.6931471806 -> return "floats.FL_LOG2"
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
importer.importLibraryModule(program, "c64lib")
|
importer.importLibraryModule(program, "syslib")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(programName: String) {
|
override fun launchEmulator(programName: String) {
|
||||||
@ -147,7 +147,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zero page locations used for floating point operations from the free list
|
// remove the zero page locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(listOf(
|
||||||
0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
@ -163,7 +163,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
Zeropage.ExitProgramStrategy.SYSTEM_RESET -> {
|
Zeropage.ExitProgramStrategy.SYSTEM_RESET -> {
|
||||||
out(" jsr main.start\t; call program entrypoint")
|
out(" jsr main.start\t; call program entrypoint")
|
||||||
out(CompilationTarget.instance.asmForSystemReset)
|
out(" jmp ${CompilationTarget.instance.resetProcName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,7 +355,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
out("\n; memdefs and kernel subroutines")
|
out("\n; memdefs and kernel subroutines")
|
||||||
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
||||||
for(m in memvars) {
|
for(m in memvars) {
|
||||||
out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}")
|
if(m.value is NumericLiteralValue)
|
||||||
|
out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}")
|
||||||
|
else
|
||||||
|
out(" ${m.name} = ${asmVariableName((m.value as AddressOf).identifier)}")
|
||||||
}
|
}
|
||||||
val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine }
|
val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine }
|
||||||
for(sub in asmSubs) {
|
for(sub in asmSubs) {
|
||||||
|
@ -152,7 +152,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
|
|
||||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||||
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||||
(seventh.startsWith("jsr c64flt.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
val nineth = pair[8].value.trimStart()
|
val nineth = pair[8].value.trimStart()
|
||||||
val tenth = pair[9].value.trimStart()
|
val tenth = pair[9].value.trimStart()
|
||||||
@ -163,7 +163,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
|
|
||||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||||
(fourteenth.startsWith("jsr c64flt.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||||
// identical float init
|
// identical float init
|
||||||
|
@ -4,8 +4,14 @@ import prog8.ast.IFunctionCall
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.functions.FSignature
|
import prog8.functions.FSignature
|
||||||
|
|
||||||
@ -342,7 +348,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
||||||
translateFunctionArguments(fcall.args, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" jsr c64flt.func_$functionName")
|
asmgen.out(" jsr floats.func_$functionName")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSgn(fcall: IFunctionCall, func: FSignature) {
|
private fun funcSgn(fcall: IFunctionCall, func: FSignature) {
|
||||||
@ -353,7 +359,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
||||||
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
||||||
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.sign_f")
|
DataType.FLOAT -> asmgen.out(" jsr floats.sign_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,7 +370,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${functionName}_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -377,7 +383,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
||||||
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${functionName}_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -395,12 +401,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcSwap(fcall: IFunctionCall) {
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
val first = fcall.args[0]
|
val first = fcall.args[0]
|
||||||
val second = fcall.args[1]
|
val second = fcall.args[1]
|
||||||
|
|
||||||
|
// optimized simple case: swap two variables
|
||||||
if(first is IdentifierReference && second is IdentifierReference) {
|
if(first is IdentifierReference && second is IdentifierReference) {
|
||||||
val firstName = asmgen.asmVariableName(first)
|
val firstName = asmgen.asmVariableName(first)
|
||||||
val secondName = asmgen.asmVariableName(second)
|
val secondName = asmgen.asmVariableName(second)
|
||||||
val dt = first.inferType(program)
|
val dt = first.inferType(program)
|
||||||
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
||||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | tya | sta $secondName")
|
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(dt.istype(DataType.WORD) || dt.istype(DataType.UWORD)) {
|
if(dt.istype(DataType.WORD) || dt.istype(DataType.UWORD)) {
|
||||||
@ -426,14 +434,196 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda #>$secondName
|
lda #>$secondName
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
jsr c64flt.swap_floats
|
jsr floats.swap_floats
|
||||||
""")
|
""")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// other types of swap() calls should have been replaced by a different statement sequence involving a temp variable
|
// optimized simple case: swap two memory locations
|
||||||
throw AssemblyError("no asm generation for swap funccall $fcall")
|
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
||||||
|
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||||
|
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||||
|
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
|
||||||
|
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
|
||||||
|
|
||||||
|
when {
|
||||||
|
addr1!=null && addr2!=null -> {
|
||||||
|
asmgen.out(" ldy $addr1 | lda $addr2 | sta $addr1 | sty $addr2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addr1!=null && name2!=null -> {
|
||||||
|
asmgen.out(" ldy $addr1 | lda $name2 | sta $addr1 | sty $name2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name1!=null && addr2 != null -> {
|
||||||
|
asmgen.out(" ldy $name1 | lda $addr2 | sta $name1 | sty $addr2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name1!=null && name2!=null -> {
|
||||||
|
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
||||||
|
val indexValue1 = first.arrayspec.index as? NumericLiteralValue
|
||||||
|
val indexName1 = first.arrayspec.index as? IdentifierReference
|
||||||
|
val indexValue2 = second.arrayspec.index as? NumericLiteralValue
|
||||||
|
val indexName2 = second.arrayspec.index as? IdentifierReference
|
||||||
|
val arrayVarName1 = asmgen.asmVariableName(first.identifier)
|
||||||
|
val arrayVarName2 = asmgen.asmVariableName(second.identifier)
|
||||||
|
val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
|
if(indexValue1!=null && indexValue2!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, indexValue1, arrayVarName2, indexValue2)
|
||||||
|
return
|
||||||
|
} else if(indexName1!=null && indexName2!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, indexName1, arrayVarName2, indexName2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all other types of swap() calls are done via the evaluation stack
|
||||||
|
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
||||||
|
return when (expr) {
|
||||||
|
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, variable=expr)
|
||||||
|
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, array = expr)
|
||||||
|
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
||||||
|
else -> throw AssemblyError("invalid expression object $expr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.translateExpression(first)
|
||||||
|
asmgen.translateExpression(second)
|
||||||
|
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val assignFirst = AsmAssignment(
|
||||||
|
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
|
||||||
|
targetFromExpr(first, datatype),
|
||||||
|
false, first.position
|
||||||
|
)
|
||||||
|
val assignSecond = AsmAssignment(
|
||||||
|
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
|
||||||
|
targetFromExpr(second, datatype),
|
||||||
|
false, second.position
|
||||||
|
)
|
||||||
|
asmgen.translateNormalAssignment(assignFirst)
|
||||||
|
asmgen.translateNormalAssignment(assignSecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||||
|
val index1 = indexValue1.number.toInt() * elementDt.memorySize()
|
||||||
|
val index2 = indexValue2.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName1+$index1
|
||||||
|
ldy $arrayVarName2+$index2
|
||||||
|
sta $arrayVarName2+$index2
|
||||||
|
sty $arrayVarName1+$index1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName1+$index1
|
||||||
|
ldy $arrayVarName2+$index2
|
||||||
|
sta $arrayVarName2+$index2
|
||||||
|
sty $arrayVarName1+$index1
|
||||||
|
lda $arrayVarName1+$index1+1
|
||||||
|
ldy $arrayVarName2+$index2+1
|
||||||
|
sta $arrayVarName2+$index2+1
|
||||||
|
sty $arrayVarName1+$index1+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<(${arrayVarName1}+$index1)
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>(${arrayVarName1}+$index1)
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<(${arrayVarName2}+$index2)
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>(${arrayVarName2}+$index2)
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid aray elt type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||||
|
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||||
|
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||||
|
when(elementDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
ldx $idxAsmName1
|
||||||
|
ldy $idxAsmName2
|
||||||
|
lda $arrayVarName1,x
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2,y
|
||||||
|
sta $arrayVarName1,x
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2,y
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
lda $idxAsmName1
|
||||||
|
asl a
|
||||||
|
tax
|
||||||
|
lda $idxAsmName2
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda $arrayVarName1,x
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2,y
|
||||||
|
sta $arrayVarName1,x
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2,y
|
||||||
|
lda $arrayVarName1+1,x
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2+1,y
|
||||||
|
sta $arrayVarName1+1,x
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2+1,y
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #>$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda $idxAsmName1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $idxAsmName1
|
||||||
|
adc #<$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
bcc +
|
||||||
|
inc P8ZP_SCRATCH_W1+1
|
||||||
|
+ lda #>$arrayVarName2
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda $idxAsmName2
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $idxAsmName2
|
||||||
|
adc #<$arrayVarName2
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
bcc +
|
||||||
|
inc P8ZP_SCRATCH_W2+1
|
||||||
|
+ jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid aray elt type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
@ -442,7 +632,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.abs_f")
|
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
lda #0
|
lda #0
|
||||||
+ sta P8ESTACK_HI+1,x""")
|
+ sta P8ESTACK_HI+1,x""")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {}
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
DataType.WORD, DataType.UWORD -> {}
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_uw2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -125,17 +125,17 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {}
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
DataType.WORD, DataType.UWORD -> {}
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_w2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr c64flt.stack_float2uw")
|
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
||||||
DataType.BYTE -> asmgen.out(" jsr c64flt.stack_float2w")
|
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
||||||
DataType.UWORD -> asmgen.out(" jsr c64flt.stack_float2uw")
|
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
||||||
DataType.WORD -> asmgen.out(" jsr c64flt.stack_float2w")
|
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
|
||||||
DataType.FLOAT -> {}
|
DataType.FLOAT -> {}
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -182,7 +182,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
""")
|
""")
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(expr.number.toDouble())
|
val floatConst = asmgen.getFloatAsmConst(expr.number.toDouble())
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||||
}
|
}
|
||||||
in IterableDatatypes -> {
|
in IterableDatatypes -> {
|
||||||
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||||
@ -368,7 +368,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(type) {
|
when(type) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.neg_f")
|
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,7 +409,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird element type")
|
else -> throw AssemblyError("weird element type")
|
||||||
}
|
}
|
||||||
@ -431,7 +431,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
adc #<$arrayVarName
|
adc #<$arrayVarName
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr c64flt.push_float""")
|
+ jsr floats.push_float""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -513,17 +513,17 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
|
|
||||||
private fun translateBinaryOperatorFloats(operator: String) {
|
private fun translateBinaryOperatorFloats(operator: String) {
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"**" -> asmgen.out(" jsr c64flt.pow_f")
|
"**" -> asmgen.out(" jsr floats.pow_f")
|
||||||
"*" -> asmgen.out(" jsr c64flt.mul_f")
|
"*" -> asmgen.out(" jsr floats.mul_f")
|
||||||
"/" -> asmgen.out(" jsr c64flt.div_f")
|
"/" -> asmgen.out(" jsr floats.div_f")
|
||||||
"+" -> asmgen.out(" jsr c64flt.add_f")
|
"+" -> asmgen.out(" jsr floats.add_f")
|
||||||
"-" -> asmgen.out(" jsr c64flt.sub_f")
|
"-" -> asmgen.out(" jsr floats.sub_f")
|
||||||
"<" -> asmgen.out(" jsr c64flt.less_f")
|
"<" -> asmgen.out(" jsr floats.less_f")
|
||||||
">" -> asmgen.out(" jsr c64flt.greater_f")
|
">" -> asmgen.out(" jsr floats.greater_f")
|
||||||
"<=" -> asmgen.out(" jsr c64flt.lesseq_f")
|
"<=" -> asmgen.out(" jsr floats.lesseq_f")
|
||||||
">=" -> asmgen.out(" jsr c64flt.greatereq_f")
|
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
||||||
"==" -> asmgen.out(" jsr c64flt.equal_f")
|
"==" -> asmgen.out(" jsr floats.equal_f")
|
||||||
"!=" -> asmgen.out(" jsr c64flt.notequal_f")
|
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
||||||
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
||||||
else -> throw AssemblyError("invalid operator $operator")
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$what | ldy #>$what")
|
asmgen.out(" lda #<$what | ldy #>$what")
|
||||||
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
||||||
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
@ -121,7 +121,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
adc #<$asmArrayvarname
|
adc #<$asmArrayvarname
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr c64flt.inc_var_f""")
|
+ jsr floats.inc_var_f""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird array elt dt")
|
else -> throw AssemblyError("weird array elt dt")
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
in WordDatatypes ->
|
in WordDatatypes ->
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||||
DataType.FLOAT ->
|
DataType.FLOAT ->
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr floats.push_float")
|
||||||
else ->
|
else ->
|
||||||
throw AssemblyError("weird array type")
|
throw AssemblyError("weird array type")
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
adc #<$arrayVarName
|
adc #<$arrayVarName
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr c64flt.push_float""")
|
+ jsr floats.push_float""")
|
||||||
}
|
}
|
||||||
else ->
|
else ->
|
||||||
throw AssemblyError("weird array elt type")
|
throw AssemblyError("weird array elt type")
|
||||||
@ -217,7 +217,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${target.asmVarname}
|
lda #<${target.asmVarname}
|
||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
jsr c64flt.pop_float
|
jsr floats.pop_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
@ -249,7 +249,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${target.asmVarname}+$scaledIdx
|
lda #<${target.asmVarname}+$scaledIdx
|
||||||
ldy #>${target.asmVarname}+$scaledIdx
|
ldy #>${target.asmVarname}+$scaledIdx
|
||||||
jsr c64flt.pop_float
|
jsr floats.pop_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
@ -279,7 +279,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
adc #<${target.asmVarname}
|
adc #<${target.asmVarname}
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr c64flt.pop_float""")
|
+ jsr floats.pop_float""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -406,7 +406,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #<${target.asmVarname}+$scaledIdx
|
lda #<${target.asmVarname}+$scaledIdx
|
||||||
ldy #>${target.asmVarname}+$scaledIdx
|
ldy #>${target.asmVarname}+$scaledIdx
|
||||||
jsr c64flt.copy_float
|
jsr floats.copy_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
@ -439,7 +439,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
adc #<${target.asmVarname}
|
adc #<${target.asmVarname}
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr c64flt.copy_float""")
|
+ jsr floats.copy_float""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -496,13 +496,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
// TODO("array[var] ${target.constArrayIndexValue}")
|
// TODO("array[var] ${target.constArrayIndexValue}")
|
||||||
// }
|
// }
|
||||||
val index = target.array!!.arrayspec.index
|
val index = target.array!!.arrayspec.index
|
||||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.push_float")
|
||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname} | jsr c64flt.pop_float_to_indexed_var")
|
asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname} | jsr floats.pop_float_to_indexed_var")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
TargetStorageKind.STACK -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
TargetStorageKind.STACK -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,7 +850,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda #>${target.asmVarname}
|
lda #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1+1
|
sta P8ZP_SCRATCH_W1+1
|
||||||
jsr c64flt.set_0_array_float
|
jsr floats.set_0_array_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -858,7 +858,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = asmgen.getFloatAsmConst(float)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -913,7 +913,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda #>${arrayVarName}
|
lda #>${arrayVarName}
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
jsr c64flt.set_array_float
|
jsr floats.set_array_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -921,7 +921,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = asmgen.getFloatAsmConst(float)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1117,7 +1117,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
dex
|
dex
|
||||||
lda #<$asmArrayvarname
|
lda #<$asmArrayvarname
|
||||||
ldy #>$asmArrayvarname
|
ldy #>$asmArrayvarname
|
||||||
jsr c64flt.pop_float_to_indexed_var
|
jsr floats.pop_float_to_indexed_var
|
||||||
""")
|
""")
|
||||||
else ->
|
else ->
|
||||||
throw AssemblyError("weird array type")
|
throw AssemblyError("weird array type")
|
||||||
|
@ -191,7 +191,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> {
|
else -> {
|
||||||
println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize...
|
println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize...
|
||||||
asmgen.translateExpression(memory.addressExpression)
|
asmgen.translateExpression(memory.addressExpression)
|
||||||
// TODO buggy?:
|
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
||||||
val zp = CompilationTarget.instance.machine.zeropage
|
val zp = CompilationTarget.instance.machine.zeropage
|
||||||
when {
|
when {
|
||||||
@ -451,6 +450,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name")
|
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name")
|
||||||
"*" -> {
|
"*" -> {
|
||||||
TODO("var mul byte expr")
|
TODO("var mul byte expr")
|
||||||
|
// check optimizedByteMultiplications
|
||||||
// asmgen.out(" jsr prog8_lib.mul_byte")
|
// asmgen.out(" jsr prog8_lib.mul_byte")
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
@ -693,26 +693,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
if(value in asmgen.optimizedWordMultiplications) {
|
if(value in asmgen.optimizedWordMultiplications) {
|
||||||
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
||||||
} else {
|
} else {
|
||||||
TODO("var uword mul litval $value")
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<$value
|
||||||
|
ldy #>$value
|
||||||
|
jsr math.multiply_words
|
||||||
|
lda math.multiply_words.result
|
||||||
|
sta $name
|
||||||
|
lda math.multiply_words.result+1
|
||||||
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(value.absoluteValue in asmgen.optimizedWordMultiplications) {
|
if(value.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||||
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
||||||
} else {
|
} else {
|
||||||
// TODO don't use stack here
|
// TODO does this work for signed words? if so the uword/word distinction can be removed altogether
|
||||||
// TODO does this work for signed words?
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda $name+1
|
lda $name+1
|
||||||
sta P8ZP_SCRATCH_W1+1
|
sta P8ZP_SCRATCH_W1+1
|
||||||
lda #<$value
|
lda #<$value
|
||||||
ldy #>$value
|
ldy #>$value
|
||||||
jsr math.multiply_words
|
jsr math.multiply_words
|
||||||
lda math.multiply_words.result
|
lda math.multiply_words.result
|
||||||
sta $name
|
sta $name
|
||||||
lda math.multiply_words.result+1
|
lda math.multiply_words.result+1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -827,7 +837,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype
|
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype
|
||||||
when (valueDt) {
|
when (valueDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// the other variable is a BYTE type so optimize for that TODO does this even occur?
|
// the other variable is a BYTE type so optimize for that
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> asmgen.out("""
|
"+" -> asmgen.out("""
|
||||||
@ -846,9 +856,22 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bcs +
|
bcs +
|
||||||
dec $name+1
|
dec $name+1
|
||||||
+ """)
|
+ """)
|
||||||
"*" -> TODO("mul word*byte")
|
"*" -> {
|
||||||
"/" -> TODO("div word/byte")
|
asmgen.out("""
|
||||||
"%" -> TODO("word remainder byte")
|
lda $otherName
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #0
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
jsr math.multiply_words
|
||||||
|
lda math.multiply_words.result
|
||||||
|
sta $name
|
||||||
|
lda math.multiply_words.result+1
|
||||||
|
sta $name+1""")
|
||||||
|
}
|
||||||
|
"/" -> TODO("div wordvar/bytevar")
|
||||||
|
"%" -> TODO("word remainder bytevar")
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
@ -876,9 +899,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -""")
|
bne -""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> TODO("bitand word byte")
|
"&" -> TODO("bitand wordvar bytevar")
|
||||||
"^" -> TODO("bitxor word byte")
|
"^" -> TODO("bitxor wordvar bytevar")
|
||||||
"|" -> TODO("bitor word byte")
|
"|" -> TODO("bitor wordvar bytevar")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1121,43 +1144,43 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
asmgen.out(" jsr c64flt.pop_float_fac1")
|
asmgen.out(" jsr floats.pop_float_fac1")
|
||||||
asmgen.saveRegister(CpuRegister.X)
|
asmgen.saveRegister(CpuRegister.X)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.CONUPK
|
jsr floats.CONUPK
|
||||||
jsr c64flt.FPWRT
|
jsr floats.FPWRT
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FADD
|
jsr floats.FADD
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FSUB
|
jsr floats.FSUB
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FMULT
|
jsr floats.FMULT
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
@ -1165,7 +1188,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldx #<$name
|
ldx #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X)
|
||||||
}
|
}
|
||||||
@ -1182,50 +1205,50 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.CONUPK
|
jsr floats.CONUPK
|
||||||
lda #<$otherName
|
lda #<$otherName
|
||||||
ldy #>$otherName
|
ldy #>$otherName
|
||||||
jsr c64flt.FPWR
|
jsr floats.FPWR
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$otherName
|
lda #<$otherName
|
||||||
ldy #>$otherName
|
ldy #>$otherName
|
||||||
jsr c64flt.FADD
|
jsr floats.FADD
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$otherName
|
lda #<$otherName
|
||||||
ldy #>$otherName
|
ldy #>$otherName
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FSUB
|
jsr floats.FSUB
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$otherName
|
lda #<$otherName
|
||||||
ldy #>$otherName
|
ldy #>$otherName
|
||||||
jsr c64flt.FMULT
|
jsr floats.FMULT
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$otherName
|
lda #<$otherName
|
||||||
ldy #>$otherName
|
ldy #>$otherName
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
@ -1234,7 +1257,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldx #<$name
|
ldx #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X)
|
||||||
}
|
}
|
||||||
@ -1247,10 +1270,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.CONUPK
|
jsr floats.CONUPK
|
||||||
lda #<$constValueName
|
lda #<$constValueName
|
||||||
ldy #>$constValueName
|
ldy #>$constValueName
|
||||||
jsr c64flt.FPWR
|
jsr floats.FPWR
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"+" -> {
|
"+" -> {
|
||||||
@ -1259,10 +1282,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$constValueName
|
lda #<$constValueName
|
||||||
ldy #>$constValueName
|
ldy #>$constValueName
|
||||||
jsr c64flt.FADD
|
jsr floats.FADD
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
@ -1271,10 +1294,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$constValueName
|
lda #<$constValueName
|
||||||
ldy #>$constValueName
|
ldy #>$constValueName
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FSUB
|
jsr floats.FSUB
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
@ -1282,10 +1305,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$constValueName
|
lda #<$constValueName
|
||||||
ldy #>$constValueName
|
ldy #>$constValueName
|
||||||
jsr c64flt.FMULT
|
jsr floats.FMULT
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
@ -1294,10 +1317,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$constValueName
|
lda #<$constValueName
|
||||||
ldy #>$constValueName
|
ldy #>$constValueName
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
lda #<$name
|
lda #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
@ -1306,7 +1329,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldx #<$name
|
ldx #<$name
|
||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr c64flt.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X)
|
||||||
}
|
}
|
||||||
@ -1548,11 +1571,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${target.asmVarname}
|
lda #<${target.asmVarname}
|
||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
jsr c64flt.MOVFM
|
jsr floats.MOVFM
|
||||||
jsr c64flt.NEGOP
|
jsr floats.NEGOP
|
||||||
ldx #<${target.asmVarname}
|
ldx #<${target.asmVarname}
|
||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
jsr c64flt.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X)
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
override fun getFloatRomConst(number: Double): String? = null // Cx16 has no pulblic ROM float locations
|
override fun getFloatRomConst(number: Double): String? = null // Cx16 has no pulblic ROM float locations
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
importer.importLibraryModule(program, "cx16lib")
|
importer.importLibraryModule(program, "syslib")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(programName: String) {
|
override fun launchEmulator(programName: String) {
|
||||||
|
@ -7,178 +7,6 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
|
||||||
|
|
||||||
|
|
||||||
// First thing to do is replace all constant identifiers with their actual value,
|
|
||||||
// and the array var initializer values and sizes.
|
|
||||||
// This is needed because further constant optimizations depend on those.
|
|
||||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
|
||||||
// replace identifiers that refer to const value, with the value itself
|
|
||||||
// if it's a simple type and if it's not a left hand side variable
|
|
||||||
if(identifier.parent is AssignTarget)
|
|
||||||
return noModifications
|
|
||||||
var forloop = identifier.parent as? ForLoop
|
|
||||||
if(forloop==null)
|
|
||||||
forloop = identifier.parent.parent as? ForLoop
|
|
||||||
if(forloop!=null && identifier===forloop.loopVar)
|
|
||||||
return noModifications
|
|
||||||
|
|
||||||
val cval = identifier.constValue(program) ?: return noModifications
|
|
||||||
return when (cval.type) {
|
|
||||||
in NumericDatatypes -> listOf(IAstModification.ReplaceNode(identifier, NumericLiteralValue(cval.type, cval.number, identifier.position), identifier.parent))
|
|
||||||
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
|
||||||
else -> noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
|
||||||
// TODO: use call graph for this?
|
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
|
||||||
errors.err("recursive var declaration", decl.position)
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
|
||||||
if(decl.isArray){
|
|
||||||
if(decl.arraysize==null) {
|
|
||||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
|
||||||
val arrayval = decl.value as? ArrayLiteralValue
|
|
||||||
if(arrayval!=null) {
|
|
||||||
return listOf(IAstModification.SetExpression(
|
|
||||||
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
|
||||||
NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position),
|
|
||||||
decl
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(decl.arraysize?.constIndex()==null) {
|
|
||||||
val size = decl.arraysize!!.index.constValue(program)
|
|
||||||
if(size!=null) {
|
|
||||||
return listOf(IAstModification.SetExpression(
|
|
||||||
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
|
||||||
size, decl
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when(decl.datatype) {
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
|
||||||
val litval = decl.value as? NumericLiteralValue
|
|
||||||
if (litval!=null && litval.type in IntegerDatatypes) {
|
|
||||||
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
val numericLv = decl.value as? NumericLiteralValue
|
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
|
||||||
if(rangeExpr!=null) {
|
|
||||||
// convert the initializer range expression to an actual array
|
|
||||||
val declArraySize = decl.arraysize?.constIndex()
|
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
|
||||||
if(constRange!=null) {
|
|
||||||
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
|
||||||
val newValue = if(eltType in ByteDatatypes) {
|
|
||||||
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
|
||||||
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
|
||||||
position = decl.value!!.position)
|
|
||||||
} else {
|
|
||||||
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
|
||||||
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
|
||||||
position = decl.value!!.position)
|
|
||||||
}
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(numericLv!=null && numericLv.type==DataType.FLOAT)
|
|
||||||
errors.err("arraysize requires only integers here", numericLv.position)
|
|
||||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
|
||||||
if (rangeExpr==null && numericLv!=null) {
|
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
|
||||||
val fillvalue = numericLv.number.toInt()
|
|
||||||
when(decl.datatype){
|
|
||||||
DataType.ARRAY_UB -> {
|
|
||||||
if(fillvalue !in 0..255)
|
|
||||||
errors.err("ubyte value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_B -> {
|
|
||||||
if(fillvalue !in -128..127)
|
|
||||||
errors.err("byte value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW -> {
|
|
||||||
if(fillvalue !in 0..65535)
|
|
||||||
errors.err("uword value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_W -> {
|
|
||||||
if(fillvalue !in -32768..32767)
|
|
||||||
errors.err("word value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
// create the array itself, filled with the fillvalue.
|
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
|
||||||
val litval = decl.value as? NumericLiteralValue
|
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
|
||||||
if(rangeExpr!=null) {
|
|
||||||
// convert the initializer range expression to an actual array of floats
|
|
||||||
val declArraySize = decl.arraysize?.constIndex()
|
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
|
||||||
if(constRange!=null) {
|
|
||||||
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
|
||||||
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
|
||||||
position = decl.value!!.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rangeExpr==null && litval!=null) {
|
|
||||||
// arraysize initializer is a single int, and we know the size.
|
|
||||||
val fillvalue = litval.number.toDouble()
|
|
||||||
if (fillvalue < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
|
||||||
errors.err("float value overflow", litval.position)
|
|
||||||
else {
|
|
||||||
// create the array itself, filled with the fillvalue.
|
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) }.toTypedArray<Expression>()
|
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// nothing to do for this type
|
|
||||||
// this includes strings and structs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val declValue = decl.value
|
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR
|
|
||||||
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
|
||||||
// cast the numeric literal to the appropriate datatype of the variable
|
|
||||||
val cast = declValue.cast(decl.datatype)
|
|
||||||
if(cast.isValid)
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||||
@ -216,9 +44,24 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||||
}
|
}
|
||||||
"~" -> when (subexpr.type) {
|
"~" -> when (subexpr.type) {
|
||||||
in IntegerDatatypes -> {
|
DataType.BYTE -> {
|
||||||
listOf(IAstModification.ReplaceNode(expr,
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.optimalInteger(subexpr.number.toInt().inv(), subexpr.position),
|
NumericLiteralValue(DataType.BYTE, subexpr.number.toInt().inv(), subexpr.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
|
NumericLiteralValue(DataType.UBYTE, subexpr.number.toInt().inv() and 255, subexpr.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
|
NumericLiteralValue(DataType.WORD, subexpr.number.toInt().inv(), subexpr.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
|
NumericLiteralValue(DataType.UWORD, subexpr.number.toInt().inv() and 65535, subexpr.position),
|
||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||||
|
192
compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt
Normal file
192
compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.ArrayIndex
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.ForLoop
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
|
||||||
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
|
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
val declConstValue = decl.value?.constValue(program)
|
||||||
|
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||||
|
&& !declConstValue.inferType(program).istype(decl.datatype)) {
|
||||||
|
// cast the numeric literal to the appropriate datatype of the variable
|
||||||
|
val cast = declConstValue.cast(decl.datatype)
|
||||||
|
if(cast.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Replace all constant identifiers with their actual value,
|
||||||
|
// and the array var initializer values and sizes.
|
||||||
|
// This is needed because further constant optimizations depend on those.
|
||||||
|
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
|
// replace identifiers that refer to const value, with the value itself
|
||||||
|
// if it's a simple type and if it's not a left hand side variable
|
||||||
|
if(identifier.parent is AssignTarget)
|
||||||
|
return noModifications
|
||||||
|
var forloop = identifier.parent as? ForLoop
|
||||||
|
if(forloop==null)
|
||||||
|
forloop = identifier.parent.parent as? ForLoop
|
||||||
|
if(forloop!=null && identifier===forloop.loopVar)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
val cval = identifier.constValue(program) ?: return noModifications
|
||||||
|
return when (cval.type) {
|
||||||
|
in NumericDatatypes -> listOf(IAstModification.ReplaceNode(identifier, NumericLiteralValue(cval.type, cval.number, identifier.position), identifier.parent))
|
||||||
|
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
||||||
|
else -> noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
|
// TODO: use call graph for this?
|
||||||
|
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||||
|
errors.err("recursive var declaration", decl.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
if(decl.type== VarDeclType.CONST || decl.type== VarDeclType.VAR) {
|
||||||
|
if(decl.isArray){
|
||||||
|
if(decl.arraysize==null) {
|
||||||
|
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
||||||
|
val arrayval = decl.value as? ArrayLiteralValue
|
||||||
|
if(arrayval!=null) {
|
||||||
|
return listOf(IAstModification.SetExpression(
|
||||||
|
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||||
|
NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position),
|
||||||
|
decl
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(decl.arraysize?.constIndex()==null) {
|
||||||
|
val size = decl.arraysize!!.index.constValue(program)
|
||||||
|
if(size!=null) {
|
||||||
|
return listOf(IAstModification.SetExpression(
|
||||||
|
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||||
|
size, decl
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(decl.datatype) {
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
||||||
|
val litval = decl.value as? NumericLiteralValue
|
||||||
|
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||||
|
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
val numericLv = decl.value as? NumericLiteralValue
|
||||||
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
|
if(rangeExpr!=null) {
|
||||||
|
// convert the initializer range expression to an actual array
|
||||||
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
|
if(constRange!=null) {
|
||||||
|
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
||||||
|
val newValue = if(eltType in ByteDatatypes) {
|
||||||
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
|
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
} else {
|
||||||
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
|
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||||
|
errors.err("arraysize requires only integers here", numericLv.position)
|
||||||
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
|
if (rangeExpr==null && numericLv!=null) {
|
||||||
|
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||||
|
val fillvalue = numericLv.number.toInt()
|
||||||
|
when(decl.datatype){
|
||||||
|
DataType.ARRAY_UB -> {
|
||||||
|
if(fillvalue !in 0..255)
|
||||||
|
errors.err("ubyte value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B -> {
|
||||||
|
if(fillvalue !in -128..127)
|
||||||
|
errors.err("byte value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> {
|
||||||
|
if(fillvalue !in 0..65535)
|
||||||
|
errors.err("uword value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W -> {
|
||||||
|
if(fillvalue !in -32768..32767)
|
||||||
|
errors.err("word value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// create the array itself, filled with the fillvalue.
|
||||||
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||||
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
|
val litval = decl.value as? NumericLiteralValue
|
||||||
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
|
if(rangeExpr!=null) {
|
||||||
|
// convert the initializer range expression to an actual array of floats
|
||||||
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
|
if(constRange!=null) {
|
||||||
|
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||||
|
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rangeExpr==null && litval!=null) {
|
||||||
|
// arraysize initializer is a single int, and we know the size.
|
||||||
|
val fillvalue = litval.number.toDouble()
|
||||||
|
if (fillvalue < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
||||||
|
errors.err("float value overflow", litval.position)
|
||||||
|
else {
|
||||||
|
// create the array itself, filled with the fillvalue.
|
||||||
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) }.toTypedArray<Expression>()
|
||||||
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// nothing to do for this type
|
||||||
|
// this includes strings and structs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
@ -5,20 +5,31 @@ import prog8.ast.base.ErrorReporter
|
|||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
internal fun Program.constantFold(errors: ErrorReporter) {
|
||||||
val replacer = ConstantIdentifierReplacer(this, errors)
|
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||||
replacer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.isEmpty()) {
|
if(errors.isEmpty()) {
|
||||||
replacer.applyModifications()
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
val optimizer = ConstantFoldingOptimizer(this)
|
val replacer = ConstantIdentifierReplacer(this, errors)
|
||||||
optimizer.visit(this)
|
replacer.visit(this)
|
||||||
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
if (errors.isEmpty()) {
|
||||||
optimizer.visit(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(errors.isEmpty()) {
|
|
||||||
replacer.visit(this)
|
|
||||||
replacer.applyModifications()
|
replacer.applyModifications()
|
||||||
|
|
||||||
|
valuetypefixer.visit(this)
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
|
val optimizer = ConstantFoldingOptimizer(this)
|
||||||
|
optimizer.visit(this)
|
||||||
|
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
||||||
|
optimizer.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.isEmpty()) {
|
||||||
|
replacer.visit(this)
|
||||||
|
replacer.applyModifications()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import prog8.ast.base.SyntaxError
|
|||||||
import prog8.ast.base.checkImportedValid
|
import prog8.ast.base.checkImportedValid
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.ast.statements.DirectiveArg
|
import prog8.ast.statements.DirectiveArg
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.pathFrom
|
import prog8.pathFrom
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -19,14 +20,6 @@ import java.nio.file.Paths
|
|||||||
internal class ParsingFailedError(override var message: String) : Exception(message)
|
internal class ParsingFailedError(override var message: String) : Exception(message)
|
||||||
|
|
||||||
|
|
||||||
private class LexerErrorListener: BaseErrorListener() {
|
|
||||||
var numberOfErrors: Int = 0
|
|
||||||
override fun syntaxError(p0: Recognizer<*, *>?, p1: Any?, p2: Int, p3: Int, p4: String?, p5: RecognitionException?) {
|
|
||||||
numberOfErrors++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
|
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
|
||||||
|
|
||||||
|
|
||||||
@ -60,13 +53,28 @@ internal class ModuleImporter {
|
|||||||
return executeImportDirective(program, import, Paths.get(""))
|
return executeImportDirective(program, import, Paths.get(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MyErrorListener: ConsoleErrorListener() {
|
||||||
|
var numberOfErrors: Int = 0
|
||||||
|
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
|
||||||
|
numberOfErrors++
|
||||||
|
when (recognizer) {
|
||||||
|
is CustomLexer -> System.err.println("${recognizer.modulePath}:$line:$charPositionInLine: $msg")
|
||||||
|
is prog8Parser -> System.err.println("${recognizer.inputStream.sourceName}:$line:$charPositionInLine: $msg")
|
||||||
|
else -> System.err.println("$line:$charPositionInLine $msg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||||
val moduleName = moduleName(modulePath.fileName)
|
val moduleName = moduleName(modulePath.fileName)
|
||||||
val lexer = CustomLexer(modulePath, stream)
|
val lexer = CustomLexer(modulePath, stream)
|
||||||
val lexerErrors = LexerErrorListener()
|
lexer.removeErrorListeners()
|
||||||
|
val lexerErrors = MyErrorListener()
|
||||||
lexer.addErrorListener(lexerErrors)
|
lexer.addErrorListener(lexerErrors)
|
||||||
val tokens = CommentHandlingTokenStream(lexer)
|
val tokens = CommentHandlingTokenStream(lexer)
|
||||||
val parser = prog8Parser(tokens)
|
val parser = prog8Parser(tokens)
|
||||||
|
parser.removeErrorListeners()
|
||||||
|
parser.addErrorListener(MyErrorListener())
|
||||||
val parseTree = parser.module()
|
val parseTree = parser.module()
|
||||||
val numberOfErrors = parser.numberOfSyntaxErrors + lexerErrors.numberOfErrors
|
val numberOfErrors = parser.numberOfSyntaxErrors + lexerErrors.numberOfErrors
|
||||||
if(numberOfErrors > 0)
|
if(numberOfErrors > 0)
|
||||||
@ -141,6 +149,10 @@ internal class ModuleImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun tryGetEmbeddedResource(name: String): InputStream? {
|
private fun tryGetEmbeddedResource(name: String): InputStream? {
|
||||||
|
val target = CompilationTarget.instance.name
|
||||||
|
val targetSpecific = object{}.javaClass.getResourceAsStream("/prog8lib/$target/$name")
|
||||||
|
if(targetSpecific!=null)
|
||||||
|
return targetSpecific
|
||||||
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNames() {
|
fun testNames() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, "c64"))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||||
|
|
||||||
zp.allocate("", DataType.UBYTE, null, errors)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
zp.allocate("", DataType.UBYTE, null, errors)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
@ -142,37 +142,37 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpFloatEnable() {
|
fun testZpFloatEnable() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate("", DataType.FLOAT, null, errors)
|
zp.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, "c64"))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp2.allocate("", DataType.FLOAT, null, errors)
|
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, "c64"))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||||
zp3.allocate("", DataType.FLOAT, null, errors)
|
zp3.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpModesWithFloats() {
|
fun testZpModesWithFloats() {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||||
}
|
}
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, "c64"))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpDontuse() {
|
fun testZpDontuse() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, "c64"))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false))
|
||||||
println(zp.free)
|
println(zp.free)
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
@ -182,19 +182,19 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFreeSpaces() {
|
fun testFreeSpaces() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||||
assertEquals(16, zp1.available())
|
assertEquals(16, zp1.available())
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, "c64"))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
||||||
assertEquals(91, zp2.available())
|
assertEquals(89, zp2.available())
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, "c64"))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
||||||
assertEquals(125, zp3.available())
|
assertEquals(125, zp3.available())
|
||||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
assertEquals(238, zp4.available())
|
assertEquals(238, zp4.available())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReservedSpace() {
|
fun testReservedSpace() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
assertEquals(238, zp1.available())
|
assertEquals(238, zp1.available())
|
||||||
assertTrue(50 in zp1.free)
|
assertTrue(50 in zp1.free)
|
||||||
assertTrue(100 in zp1.free)
|
assertTrue(100 in zp1.free)
|
||||||
@ -203,7 +203,7 @@ class TestC64Zeropage {
|
|||||||
assertTrue(200 in zp1.free)
|
assertTrue(200 in zp1.free)
|
||||||
assertTrue(255 in zp1.free)
|
assertTrue(255 in zp1.free)
|
||||||
assertTrue(199 in zp1.free)
|
assertTrue(199 in zp1.free)
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, "c64"))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false))
|
||||||
assertEquals(139, zp2.available())
|
assertEquals(139, zp2.available())
|
||||||
assertFalse(50 in zp2.free)
|
assertFalse(50 in zp2.free)
|
||||||
assertFalse(100 in zp2.free)
|
assertFalse(100 in zp2.free)
|
||||||
@ -216,7 +216,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBasicsafeAllocation() {
|
fun testBasicsafeAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(16, zp.available())
|
||||||
|
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
@ -239,7 +239,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFullAllocation() {
|
fun testFullAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
assertEquals(238, zp.available())
|
assertEquals(238, zp.available())
|
||||||
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||||
assertTrue(loc > 3)
|
assertTrue(loc > 3)
|
||||||
@ -269,7 +269,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEfficientAllocation() {
|
fun testEfficientAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(16, zp.available())
|
||||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
|
BIN
docs/source/_static/cobra3d.png
Normal file
BIN
docs/source/_static/cobra3d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -149,10 +149,10 @@ If your running program hits one of the breakpoints, Vice will halt execution an
|
|||||||
Troubleshooting
|
Troubleshooting
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Getting an assembler error about undefined symbols such as ``not defined 'c64flt'``?
|
Getting an assembler error about undefined symbols such as ``not defined 'floats'``?
|
||||||
This happens when your program uses floating point values, and you forgot to import ``c64flt`` library.
|
This happens when your program uses floating point values, and you forgot to import ``floats`` library.
|
||||||
If you use floating points, the compiler needs routines from that library.
|
If you use floating points, the compiler needs routines from that library.
|
||||||
Fix it by adding an ``%import c64flt``.
|
Fix it by adding an ``%import floats``.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -38,21 +38,22 @@ This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/license
|
|||||||
:alt: Fully playable tetris clone
|
:alt: Fully playable tetris clone
|
||||||
|
|
||||||
|
|
||||||
Code examples
|
Code example
|
||||||
-------------
|
------------
|
||||||
|
|
||||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
ubyte[256] sieve
|
ubyte[256] sieve
|
||||||
ubyte candidate_prime = 2
|
ubyte candidate_prime = 2 ; is increased in the loop
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
memset(sieve, 256, false) ; clear the sieve
|
; clear the sieve, to reset starting situation on subsequent runs
|
||||||
|
memset(sieve, 256, false)
|
||||||
|
; calculate primes
|
||||||
txt.print("prime numbers up to 255:\n\n")
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
repeat {
|
repeat {
|
||||||
@ -63,18 +64,19 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
txt.print(", ")
|
txt.print(", ")
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
txt.print("number of primes (expected 54): ")
|
txt.print("number of primes (expected 54): ")
|
||||||
txt.print_ub(amount)
|
txt.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub find_next_prime() -> ubyte {
|
||||||
while sieve[candidate_prime] {
|
while sieve[candidate_prime] {
|
||||||
candidate_prime++
|
candidate_prime++
|
||||||
if candidate_prime==0
|
if candidate_prime==0
|
||||||
return 0 ; we wrapped; no more primes available
|
return 0 ; we wrapped; no more primes available in the sieve
|
||||||
}
|
}
|
||||||
|
|
||||||
; found next one, mark the multiples and return it.
|
; found next one, mark the multiples and return it.
|
||||||
sieve[candidate_prime] = true
|
sieve[candidate_prime] = true
|
||||||
uword multiple = candidate_prime
|
uword multiple = candidate_prime
|
||||||
@ -88,7 +90,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when compiled an ran on a C-64 you get this:
|
when compiled an ran on a C-64 you get this:
|
||||||
|
|
||||||
.. image:: _static/primes_example.png
|
.. image:: _static/primes_example.png
|
||||||
@ -96,43 +97,6 @@ when compiled an ran on a C-64 you get this:
|
|||||||
:alt: result when run on C-64
|
:alt: result when run on C-64
|
||||||
|
|
||||||
|
|
||||||
The following programs shows a use of the high level ``struct`` type::
|
|
||||||
|
|
||||||
|
|
||||||
%import c64textio
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
main {
|
|
||||||
|
|
||||||
struct Color {
|
|
||||||
ubyte red
|
|
||||||
ubyte green
|
|
||||||
ubyte blue
|
|
||||||
}
|
|
||||||
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
Color purple = [255, 0, 255]
|
|
||||||
|
|
||||||
Color other
|
|
||||||
|
|
||||||
other = purple
|
|
||||||
other.red /= 2
|
|
||||||
other.green = 10 + other.green / 2
|
|
||||||
other.blue = 99
|
|
||||||
|
|
||||||
txt.print_ub(other.red)
|
|
||||||
c64.CHROUT(',')
|
|
||||||
txt.print_ub(other.green)
|
|
||||||
c64.CHROUT(',')
|
|
||||||
txt.print_ub(other.blue)
|
|
||||||
c64.CHROUT('\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when compiled and ran, it prints ``127,10,99`` on the screen.
|
|
||||||
|
|
||||||
|
|
||||||
Design principles and features
|
Design principles and features
|
||||||
------------------------------
|
------------------------------
|
||||||
@ -162,6 +126,7 @@ Design principles and features
|
|||||||
the ability to easily write embedded assembly code directly in the program source code.
|
the ability to easily write embedded assembly code directly in the program source code.
|
||||||
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
|
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
|
||||||
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
||||||
|
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
.. _requirements:
|
.. _requirements:
|
||||||
|
@ -226,7 +226,7 @@ This is because routines in the C-64 BASIC and KERNAL ROMs are used for that.
|
|||||||
So floating point operations will only work if the C-64 BASIC ROM (and KERNAL ROM)
|
So floating point operations will only work if the C-64 BASIC ROM (and KERNAL ROM)
|
||||||
are banked in.
|
are banked in.
|
||||||
|
|
||||||
Also your code needs to import the ``c64flt`` library to enable floating point support
|
Also your code needs to import the ``floats`` library to enable floating point support
|
||||||
in the compiler, and to gain access to the floating point routines.
|
in the compiler, and to gain access to the floating point routines.
|
||||||
(this library contains the directive to enable floating points, you don't have
|
(this library contains the directive to enable floating points, you don't have
|
||||||
to worry about this yourself)
|
to worry about this yourself)
|
||||||
@ -578,25 +578,24 @@ within parentheses will be evaluated first. So ``(4 + 8) * 2`` is 24 and not 20,
|
|||||||
and ``(true or false) and false`` is false instead of true.
|
and ``(true or false) and false`` is false instead of true.
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
**calculations keep their datatype:**
|
**calculations keep their datatype even if the target variable is larger:**
|
||||||
When you do calculations on a BYTE type, the result will remain a BYTE.
|
When you do calculations on a BYTE type, the result will remain a BYTE.
|
||||||
When you do calculations on a WORD type, the result will remain a WORD.
|
When you do calculations on a WORD type, the result will remain a WORD.
|
||||||
For instance::
|
For instance::
|
||||||
|
|
||||||
byte b = 44
|
byte b = 44
|
||||||
word w = b*55 ; the result will be 116! (even though the target variable is a word)
|
word w = b*55 ; the result will be 116! (even though the target variable is a word)
|
||||||
w *= 999 ; the result will be -15188 (the multiplication stays within a word)
|
w *= 999 ; the result will be -15188 (the multiplication stays within a word, but overflows)
|
||||||
|
|
||||||
The compiler will NOT give a warning about this! It's doing this for
|
*The compiler does NOT warn about this!* It's doing this for
|
||||||
performance reasons - so you won't get sudden 16 bit (or even float)
|
performance reasons - so you won't get sudden 16 bit (or even float)
|
||||||
calculations where you needed only simple fast byte arithmetic.
|
calculations where you needed only simple fast byte arithmetic.
|
||||||
If you do need the extended resulting value, cast at least one of the
|
If you do need the extended resulting value, cast at least one of the
|
||||||
operands of an operator to the larger datatype. For example::
|
operands explicitly to the larger datatype. For example::
|
||||||
|
|
||||||
byte b = 44
|
byte b = 44
|
||||||
word w = b*55.w ; the result will be 2420
|
w = (b as word)*55
|
||||||
w = (b as word)*55 ; same result
|
w = b*(55 as word)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -803,7 +802,7 @@ memset(address, numbytes, bytevalue)
|
|||||||
Efficiently set a part of memory to the given (u)byte value.
|
Efficiently set a part of memory to the given (u)byte value.
|
||||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||||
Note that for clearing the character screen, very fast specialized subroutines are
|
Note that for clearing the character screen, very fast specialized subroutines are
|
||||||
available in the ``screen`` block (part of the ``c64textio`` or ``cx16textio`` modules)
|
available in the ``txt`` block (part of the ``textio`` module)
|
||||||
|
|
||||||
memsetw(address, numwords, wordvalue)
|
memsetw(address, numwords, wordvalue)
|
||||||
Efficiently set a part of memory to the given (u)word value.
|
Efficiently set a part of memory to the given (u)word value.
|
||||||
|
@ -36,9 +36,8 @@ Directives
|
|||||||
.. data:: %target <target>
|
.. data:: %target <target>
|
||||||
|
|
||||||
Level: module.
|
Level: module.
|
||||||
Global setting, selects a compilation target from within the source file.
|
Global setting, specifies that this module can only work for the given compiler target.
|
||||||
The default compilation target is "c64" which targets the Commodore-64 machine.
|
If compiled with a different target, compilation is aborted with an error message.
|
||||||
You can also omit this and use the ``-target`` command line option.
|
|
||||||
|
|
||||||
|
|
||||||
.. data:: %output <type>
|
.. data:: %output <type>
|
||||||
@ -297,7 +296,8 @@ of something with an operand starting with 1 or 0, you'll have to add a space in
|
|||||||
- When an integer value ranges from 256..65535 the compiler sees it as a ``uword``. For -32768..32767 it's a ``word``.
|
- When an integer value ranges from 256..65535 the compiler sees it as a ``uword``. For -32768..32767 it's a ``word``.
|
||||||
- When a hex number has 3 or 4 digits, for example ``$0004``, it is seen as a ``word`` otherwise as a ``byte``.
|
- When a hex number has 3 or 4 digits, for example ``$0004``, it is seen as a ``word`` otherwise as a ``byte``.
|
||||||
- When a binary number has 9 to 16 digits, for example ``%1100110011``, it is seen as a ``word`` otherwise as a ``byte``.
|
- When a binary number has 9 to 16 digits, for example ``%1100110011``, it is seen as a ``word`` otherwise as a ``byte``.
|
||||||
- You can force a byte value into a word value by adding the ``.w`` datatype suffix to the number: ``$2a.w`` is equivalent to ``$002a``.
|
- If the number fits in a byte but you really require it as a word value, you'll have to explicitly cast it: ``60 as uword``
|
||||||
|
or you can use the full word hexadecimal notation ``$003c``.
|
||||||
|
|
||||||
|
|
||||||
Data type conversion
|
Data type conversion
|
||||||
|
@ -9,13 +9,16 @@ Prog8 targets the following hardware:
|
|||||||
- optional use of memory mapped I/O registers
|
- optional use of memory mapped I/O registers
|
||||||
- optional use of system ROM routines
|
- optional use of system ROM routines
|
||||||
|
|
||||||
Currently there are two machines that are supported as compiler target (via the ``-target`` compiler argument):
|
Currently there are two machines that are supported as compiler target (selectable via the ``-target`` compiler argument):
|
||||||
|
|
||||||
- 'c64': the well-known Commodore-64, premium support
|
- 'c64': the well-known Commodore-64, premium support
|
||||||
- 'cx16': the `CommanderX16 <https://www.commanderx16.com/>`_ a project from the 8-Bit Guy. Support for this is still experimental.
|
- 'cx16': the `CommanderX16 <https://www.commanderx16.com/>`_ a project from the 8-Bit Guy. Support for this is still experimental.
|
||||||
|
|
||||||
This chapter explains the relevant system details of these machines.
|
This chapter explains the relevant system details of these machines.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
Memory Model
|
Memory Model
|
||||||
============
|
============
|
||||||
|
@ -3,16 +3,19 @@ TODO
|
|||||||
====
|
====
|
||||||
|
|
||||||
- get rid of all other TODO's in the code ;-)
|
- get rid of all other TODO's in the code ;-)
|
||||||
|
- move the ldx #$ff | clc | cld from the startup logic into the start() function as first instructions
|
||||||
|
- add an %option that omits the 'system-init' code at the start. Useful to create separate standalone routines that shouldn't re-init the whole machine every time they're called
|
||||||
- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed?
|
- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed?
|
||||||
- compiler errors and warnings in standard format so the IDE shows them as clickable links; ./test.asm:2578:3: blablabla
|
- until condition should be able to refer to variables defined IN the do-until block itself.
|
||||||
|
- add support? example? for processing arguments to a sys call : sys 999, 1, 2, "aaa"
|
||||||
|
- make it possible for array literals to not only contain compile time constants
|
||||||
- further optimize assignment codegeneration
|
- further optimize assignment codegeneration
|
||||||
- auto select correct library to import based on target, instead of having c64- and cx16- prefix variants
|
|
||||||
- implement @stack for asmsub parameters
|
- implement @stack for asmsub parameters
|
||||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
||||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print' ?
|
|
||||||
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
||||||
|
- use VIC banking to move up the graphics bitmap memory location. Don't move it under the ROM though as that would require IRQ disabling and memory bank swapping for every bitmap manipulation
|
||||||
|
- add some primitives/support/examples for using custom char sets, copying the default charset.
|
||||||
|
|
||||||
More optimizations
|
More optimizations
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
@ -21,14 +24,13 @@ Add more compiler optimizations to the existing ones.
|
|||||||
|
|
||||||
- more targeted optimizations for assigment asm code, such as the following:
|
- more targeted optimizations for assigment asm code, such as the following:
|
||||||
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
||||||
- remove unreachable code after an exit(), return or goto
|
- can such parameter passing to subroutines be optimized to avoid copying?
|
||||||
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
||||||
the program will then rely solely on the values as they are in memory at the time of program startup.
|
the program will then rely solely on the values as they are in memory at the time of program startup.
|
||||||
- Also some library routines and code patterns could perhaps be optimized further
|
- Also some library routines and code patterns could perhaps be optimized further
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
- note: abandoned subroutine inlining because of problems referencing non-local stuff. Can't move everything around.
|
- note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around.
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
Eval stack redesign? (lot of work)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -93,11 +93,11 @@ main {
|
|||||||
txt.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
txt.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
txt.print(" / ")
|
txt.print(" / ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
txt.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -101,11 +101,11 @@ main {
|
|||||||
txt.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
txt.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
txt.print(" - ")
|
txt.print(" - ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
txt.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -95,11 +95,11 @@ main {
|
|||||||
txt.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
txt.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
txt.print(" * ")
|
txt.print(" * ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
txt.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -99,11 +99,11 @@ main {
|
|||||||
txt.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
txt.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
txt.print(" + ")
|
txt.print(" + ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
txt.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -132,9 +132,9 @@ main {
|
|||||||
else
|
else
|
||||||
txt.print("err! ")
|
txt.print("err! ")
|
||||||
txt.print(" float ")
|
txt.print(" float ")
|
||||||
c64flt.print_f(value)
|
floats.print_f(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64flt.print_f(expected)
|
floats.print_f(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
%import c64lib
|
%target c64
|
||||||
%import c64textio
|
%import syslib
|
||||||
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -43,7 +44,7 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
perform_scroll = false
|
perform_scroll = false
|
||||||
txt.scroll_left_full(true)
|
txt.scroll_left(true)
|
||||||
if c64.RASTER & 1
|
if c64.RASTER & 1
|
||||||
c64.SPXY[1] ++
|
c64.SPXY[1] ++
|
||||||
else
|
else
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
%import c64lib
|
%target c64
|
||||||
%import c64textio
|
%import syslib
|
||||||
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
%import c64textio
|
%target c64
|
||||||
|
%import textio
|
||||||
|
%import syslib
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%import c64flt
|
%import floats
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%import c64flt
|
%import floats
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%import c64textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
examples/compiled/diskdir-sys50000.prg
Normal file
BIN
examples/compiled/diskdir-sys50000.prg
Normal file
Binary file not shown.
BIN
examples/compiled/diskdir.prg
Normal file
BIN
examples/compiled/diskdir.prg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
examples/compiled/screencodes.prg
Normal file
BIN
examples/compiled/screencodes.prg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,12 +1,11 @@
|
|||||||
%import c64lib
|
%import floats
|
||||||
%import c64textio
|
%import textio
|
||||||
%import c64flt
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
const uword width = 40
|
|
||||||
const uword height = 25
|
|
||||||
|
|
||||||
; vertices
|
; vertices
|
||||||
float[] xcoor = [ -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0 ]
|
float[] xcoor = [ -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0 ]
|
||||||
float[] ycoor = [ -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0 ]
|
float[] ycoor = [ -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0 ]
|
||||||
@ -19,18 +18,29 @@ main {
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
float time=0.0
|
float time=0.0
|
||||||
|
ubyte timer_jiffies
|
||||||
|
|
||||||
repeat {
|
repeat {
|
||||||
rotate_vertices(time)
|
rotate_vertices(time)
|
||||||
txt.clear_screenchars(32)
|
txt.clear_screenchars(' ')
|
||||||
draw_edges()
|
draw_edges()
|
||||||
time+=0.2
|
time+=0.1
|
||||||
|
|
||||||
txt.plot(0,0)
|
txt.plot(0,0)
|
||||||
txt.print("3d cube! (float) ")
|
txt.print("3d cube! floats. ")
|
||||||
txt.print_ub(c64.TIME_LO)
|
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr c64.RDTIM ; A/X/Y
|
||||||
|
sta timer_jiffies
|
||||||
|
lda #0
|
||||||
|
jsr c64.SETTIM
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
}}
|
||||||
|
txt.print_ub(timer_jiffies)
|
||||||
txt.print(" jiffies/fr = ")
|
txt.print(" jiffies/fr = ")
|
||||||
txt.print_ub(60/c64.TIME_LO)
|
txt.print_ub(60/timer_jiffies)
|
||||||
txt.print(" fps")
|
txt.print(" fps")
|
||||||
c64.TIME_LO=0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,20 +88,20 @@ main {
|
|||||||
for i in 0 to len(xcoor)-1 {
|
for i in 0 to len(xcoor)-1 {
|
||||||
rz = rotatedz[i]
|
rz = rotatedz[i]
|
||||||
if rz >= 0.1 {
|
if rz >= 0.1 {
|
||||||
persp = (5.0+rz)/(height as float)
|
persp = (5.0+rz)/(txt.DEFAULT_HEIGHT as float)
|
||||||
sx = rotatedx[i] / persp + width/2.0 as ubyte
|
sx = rotatedx[i] / persp + txt.DEFAULT_WIDTH/2.0 as ubyte
|
||||||
sy = rotatedy[i] / persp + height/2.0 as ubyte
|
sy = rotatedy[i] / persp + txt.DEFAULT_HEIGHT/2.0 as ubyte
|
||||||
txt.setcc(sx, sy, 46, i+2)
|
txt.setcc(sx, sy, 46, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 to len(xcoor)-1 {
|
for i in 0 to len(xcoor)-1 {
|
||||||
rz = rotatedz[i]
|
rz = rotatedz[i]
|
||||||
if rz < 0.1 {
|
if rz < 0.1 {
|
||||||
persp = (5.0+rz)/(height as float)
|
persp = (5.0+rz)/(txt.DEFAULT_HEIGHT as float)
|
||||||
sx = rotatedx[i] / persp + width/2.0 as ubyte
|
sx = rotatedx[i] / persp + txt.DEFAULT_WIDTH/2.0 as ubyte
|
||||||
sy = rotatedy[i] / persp + height/2.0 as ubyte
|
sy = rotatedy[i] / persp + txt.DEFAULT_HEIGHT/2.0 as ubyte
|
||||||
txt.setcc(sx, sy, 81, i+2)
|
txt.setcc(sx, sy, 81, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
%import c64lib
|
%import syslib
|
||||||
%import c64graphics
|
%import graphics
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
@ -34,13 +36,23 @@ main {
|
|||||||
angley+=217
|
angley+=217
|
||||||
anglez+=452
|
anglez+=452
|
||||||
|
|
||||||
while c64.RASTER!=255 {
|
wait_a_little_bit()
|
||||||
}
|
|
||||||
while c64.RASTER!=254 {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub wait_a_little_bit() {
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
lda #0
|
||||||
|
jsr c64.SETTIM
|
||||||
|
- jsr c64.RDTIM
|
||||||
|
cmp #1
|
||||||
|
bne -
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
|
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
|
||||||
; rotate around origin (0,0,0)
|
; rotate around origin (0,0,0)
|
||||||
|
|
||||||
@ -74,21 +86,17 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uword screen_width = 320
|
|
||||||
const ubyte screen_height = 200
|
|
||||||
|
|
||||||
|
|
||||||
sub draw_lines() {
|
sub draw_lines() {
|
||||||
ubyte @zp i
|
ubyte @zp i
|
||||||
for i in len(edgesFrom) -1 downto 0 {
|
for i in len(edgesFrom) -1 downto 0 {
|
||||||
ubyte @zp vFrom = edgesFrom[i]
|
ubyte @zp vFrom = edgesFrom[i]
|
||||||
ubyte @zp vTo = edgesTo[i] ; TODO need compiler error for double declaration if also declared outside the for loop!
|
ubyte @zp vTo = edgesTo[i]
|
||||||
word @zp persp1 = 256 + rotatedz[vFrom]/256
|
word @zp persp1 = 256 + rotatedz[vFrom]/256
|
||||||
word @zp persp2 = 256 + rotatedz[vTo]/256
|
word @zp persp2 = 256 + rotatedz[vTo]/256
|
||||||
graphics.line(rotatedx[vFrom] / persp1 + screen_width/2 as uword,
|
graphics.line(rotatedx[vFrom] / persp1 + graphics.WIDTH/2 as uword,
|
||||||
rotatedy[vFrom] / persp1 + screen_height/2 as ubyte,
|
rotatedy[vFrom] / persp1 + graphics.HEIGHT/2 as ubyte,
|
||||||
rotatedx[vTo] / persp2 + screen_width/2 as uword,
|
rotatedx[vTo] / persp2 + graphics.WIDTH/2 as uword,
|
||||||
rotatedy[vTo] / persp2 + screen_height/2 as ubyte)
|
rotatedy[vTo] / persp2 + graphics.HEIGHT/2 as ubyte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
%import c64lib
|
%target c64
|
||||||
%import c64textio
|
%import syslib
|
||||||
|
%import textio
|
||||||
|
|
||||||
|
|
||||||
spritedata $2000 {
|
spritedata $2000 {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
%import c64lib
|
%target c64
|
||||||
%import c64textio
|
%import syslib
|
||||||
|
%import textio
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
const uword width = 40
|
|
||||||
const uword height = 25
|
|
||||||
|
|
||||||
; vertices
|
; vertices
|
||||||
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
|
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
|
||||||
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
|
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
|
||||||
@ -86,8 +84,8 @@ main {
|
|||||||
rz = rotatedz[i]
|
rz = rotatedz[i]
|
||||||
if rz >= 10 {
|
if rz >= 10 {
|
||||||
persp = 900 + rz/32
|
persp = 900 + rz/32
|
||||||
sx = rotatedx[i] / persp as byte + width/2
|
sx = rotatedx[i] / persp as byte + txt.DEFAULT_WIDTH/2
|
||||||
sy = rotatedy[i] / persp as byte + height/2
|
sy = rotatedy[i] / persp as byte + txt.DEFAULT_HEIGHT/2
|
||||||
txt.setcc(sx as ubyte, sy as ubyte, 46, 7)
|
txt.setcc(sx as ubyte, sy as ubyte, 46, 7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,8 +94,8 @@ main {
|
|||||||
rz = rotatedz[i]
|
rz = rotatedz[i]
|
||||||
if rz < 10 {
|
if rz < 10 {
|
||||||
persp = 900 + rz/32
|
persp = 900 + rz/32
|
||||||
sx = rotatedx[i] / persp as byte + width/2
|
sx = rotatedx[i] / persp as byte + txt.DEFAULT_WIDTH/2
|
||||||
sy = rotatedy[i] / persp as byte + height/2
|
sy = rotatedy[i] / persp as byte + txt.DEFAULT_HEIGHT/2
|
||||||
txt.setcc(sx as ubyte, sy as ubyte, 81, 7)
|
txt.setcc(sx as ubyte, sy as ubyte, 81, 7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user