Compare commits

...

18 Commits
v4.0 ... v4.1

Author SHA1 Message Date
809917f13b version 4.1 2020-08-31 21:44:38 +02:00
2b35498370 added CX16 txt.setcc and swirl examples that use it 2020-08-31 21:01:18 +02:00
f45eabdd9e added CX16 VERA registers, made txt.fill_screen work on CX16 2020-08-31 18:23:52 +02:00
438f3ee8d2 make GIVUAYFAY work (unsigned word to float) 2020-08-31 17:16:51 +02:00
4bea31f051 fl_zero fix 2020-08-31 01:04:04 +02:00
5eae7a2b93 tweak mandelbrots and c64 graphics plot() doesnt work with XY parameter 2020-08-31 00:36:40 +02:00
364ef3e55c tweak cx16 mandelbrots 2020-08-31 00:03:05 +02:00
e61818f194 tweak cx16 mandelbrots 2020-08-30 19:31:20 +02:00
0f9ce319d4 readme 2020-08-30 18:36:02 +02:00
5d90871789 got floating points working in commanderx16, added txt.color() to set text color 2020-08-30 00:15:18 +02:00
88a9e09918 got floating points working in commanderx16 2020-08-29 23:55:26 +02:00
c50ecf6055 fix for loop asm creation with word loopvar 2020-08-29 02:05:24 +02:00
a18de75da9 fix compiler loop and missing type checks on for loop range values 2020-08-29 01:48:41 +02:00
e112dfd910 implemented signed byte and word division 2020-08-29 00:00:53 +02:00
9154d8bd37 optimizing X register saving for 65c02 using phx/plx instead of zp location 2020-08-28 22:11:33 +02:00
0b55372b3b cleanup cx16 things and added call signatures. c64graphics moved into built-in libraries. 2020-08-28 21:42:53 +02:00
3ad7fb010f clearer about emulator 2020-08-27 21:09:59 +02:00
3f64d1bb5a oops. 2020-08-27 21:04:08 +02:00
50 changed files with 1388 additions and 582 deletions

View File

@ -12,9 +12,18 @@ Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
as used in many home computers from that era. It is a medium to low level programming language, as used in many home computers from that era. It is a medium to low level programming language,
which aims to provide many conveniences over raw assembly code (even when using a macro assembler): which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
- reduction of source code length Documentation
-------------
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
https://prog8.readthedocs.io/
What use Prog8 provide?
-----------------------
- reduction of source code length over raw assembly
- modularity, symbol scoping, subroutines - modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- automatic variable allocations, automatic string and array variables and string sharing - automatic variable allocations, automatic string and array variables and string sharing
@ -29,7 +38,7 @@ which aims to provide many conveniences over raw assembly code (even when using
- inline assembly allows you to have full control when every cycle or byte matters - inline assembly allows you to have full control when every cycle or byte matters
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse`` - many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
Rapid edit-compile-run-debug cycle: *Rapid edit-compile-run-debug cycle:*
- use a modern PC to do the work on - use a modern PC to do the work on
- very quick compilation times - very quick compilation times
@ -37,17 +46,16 @@ Rapid edit-compile-run-debug cycle:
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them - breakpoints, that let the Vice emulator drop into the monitor if execution hits them
- source code labels automatically loaded in Vice emulator so it can show them in disassembly - source code labels automatically loaded in Vice emulator so it can show them in disassembly
Prog8 is mainly targeted at the Commodore-64 machine. *Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
Preliminary support for the [CommanderX16](https://www.commanderx16.com) is available as a second compilation target.
Contributions to improve these or to add support for other machines are welcome! - "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
Documentation/manual
--------------------
https://prog8.readthedocs.io/
Required tools
-------------- Additional required tools
-------------------------
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path. [64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project. A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.

View File

@ -8,7 +8,8 @@ ub2float .proc
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_W2+1
ldy P8ZP_SCRATCH_B1 ldy P8ZP_SCRATCH_B1
jsr FREADUY lda #0
jsr GIVAYF
_fac_to_mem ldx P8ZP_SCRATCH_W2 _fac_to_mem ldx P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1 ldy P8ZP_SCRATCH_W2+1
jsr MOVMF jsr MOVMF
@ -74,7 +75,8 @@ stack_ub2float .proc
lda P8ESTACK_LO,x lda P8ESTACK_LO,x
stx P8ZP_SCRATCH_REG_X stx P8ZP_SCRATCH_REG_X
tay tay
jsr FREADUY lda #0
jsr GIVAYF
jmp push_fac1_as_result jmp push_fac1_as_result
.pend .pend
@ -663,8 +665,8 @@ _largest_pos_float .byte 255,127,255,255,255 ; largest positive float
.pend .pend
func_sum_f .proc func_sum_f .proc
lda #<FL_ZERO lda #<ZERO
ldy #>FL_ZERO ldy #>ZERO
jsr MOVFM jsr MOVFM
jsr prog8_lib.pop_array_and_lengthmin1Y jsr prog8_lib.pop_array_and_lengthmin1Y
stx P8ZP_SCRATCH_REG_X stx P8ZP_SCRATCH_REG_X

View File

@ -6,12 +6,12 @@
%option enable_floats %option enable_floats
c64flt { c64flt {
; ---- 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
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586
const float ZERO = 0.0
; ---- C64 basic and kernal ROM float constants and functions ---- ; ---- C64 basic and kernal ROM float constants and functions ----
@ -35,13 +35,11 @@ c64flt {
&float FL_TWOPI = $e2e5 ; 2 * PI &float FL_TWOPI = $e2e5 ; 2 * PI
&float FL_FR4 = $e2ea ; .25 &float FL_FR4 = $e2ea ; .25
; oddly enough, 0.0 isn't available in the kernel. ; oddly enough, 0.0 isn't available in the kernel.
float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel
; note: fac1/2 might get clobbered even if not mentioned in the function's name. ; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1. ; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
; checked functions below:
romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1 romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1 romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2 romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
@ -91,6 +89,7 @@ romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2) romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1 romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1) romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
@ -193,33 +192,26 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
} }
sub print_f (float value) { sub print_f (float value) {
; ---- prints the floating point value (without a newline) using basic rom routines. ; ---- prints the floating point value (without a newline).
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X stx P8ZP_SCRATCH_REG_X
lda #<value lda #<value
ldy #>value ldy #>value
jsr MOVFM ; load float into fac1 jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y jsr FOUT ; fac1 to string in A/Y
jsr c64.STROUT ; print string in A/Y sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy #0
- lda (P8ZP_SCRATCH_B1),y
beq +
jsr c64.CHROUT
iny
bne -
ldx P8ZP_SCRATCH_REG_X ldx P8ZP_SCRATCH_REG_X
rts + rts
}} }}
} }
sub print_fln (float value) {
; ---- prints the floating point value (with a newline at the end) using basic rom routines
%asm {{
stx P8ZP_SCRATCH_REG_X
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FPRINTLN ; print fac1 with newline
ldx P8ZP_SCRATCH_REG_X
rts
}}
}
%asminclude "library:c64floats.asm", "" %asminclude "library:c64floats.asm", ""
} ; ------ end of block c64flt }

View File

@ -0,0 +1,243 @@
%import c64textio
; bitmap pixel graphics module for the C64
; only black/white monchrome for now
; assumes bitmap screen memory is $2000-$3fff
graphics {
const uword bitmap_address = $2000
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
c64.SCROLY |= %00100000
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
clear_screen(1, 0)
}
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
memset(bitmap_address, 320*200/8, 0)
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
}
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
; Bresenham algorithm.
; This code special cases various quadrant loops to allow simple ++ and -- operations.
; TODO rewrite this in optimized assembly
if y1>y2 {
; make sure dy is always positive to avoid 8 instead of just 4 special cases
swap(x1, x2)
swap(y1, y2)
}
word @zp d = 0
ubyte positive_ix = true
word @zp dx = x2 - x1 as word
word @zp dy = y2 as word - y1 as word
if dx < 0 {
dx = -dx
positive_ix = false
}
dx *= 2
dy *= 2
internal_plotx = x1
if dx >= dy {
if positive_ix {
repeat {
internal_plot(y1)
if internal_plotx==x2
return
internal_plotx++
d += dy
if d > dx {
y1++
d -= dx
}
}
} else {
repeat {
internal_plot(y1)
if internal_plotx==x2
return
internal_plotx--
d += dy
if d > dx {
y1++
d -= dx
}
}
}
}
else {
if positive_ix {
repeat {
internal_plot(y1)
if y1 == y2
return
y1++
d += dx
if d > dy {
internal_plotx++
d -= dy
}
}
} else {
repeat {
internal_plot(y1)
if y1 == y2
return
y1++
d += dx
if d > dy {
internal_plotx--
d -= dy
}
}
}
}
}
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
; Midpoint algorithm
ubyte @zp ploty
ubyte @zp xx = radius
ubyte @zp yy = 0
byte @zp decisionOver2 = 1-xx as byte
while xx>=yy {
internal_plotx = xcenter + xx
ploty = ycenter + yy
internal_plot(ploty)
internal_plotx = xcenter - xx
internal_plot(ploty)
internal_plotx = xcenter + xx
ploty = ycenter - yy
internal_plot(ploty)
internal_plotx = xcenter - xx
internal_plot(ploty)
internal_plotx = xcenter + yy
ploty = ycenter + xx
internal_plot(ploty)
internal_plotx = xcenter - yy
internal_plot(ploty)
internal_plotx = xcenter + yy
ploty = ycenter - xx
internal_plot(ploty)
internal_plotx = xcenter - yy
internal_plot(ploty)
yy++
if decisionOver2<=0
decisionOver2 += 2*yy+1
else {
xx--
decisionOver2 += 2*(yy-xx)+1
}
}
}
sub disc(uword cx, ubyte cy, ubyte radius) {
; Midpoint algorithm, filled
ubyte xx = radius
ubyte yy = 0
byte decisionOver2 = 1-xx as byte
while xx>=yy {
ubyte cy_plus_yy = cy + yy
ubyte cy_min_yy = cy - yy
ubyte cy_plus_xx = cy + xx
ubyte cy_min_xx = cy - xx
for internal_plotx in cx to cx+xx {
internal_plot(cy_plus_yy)
internal_plot(cy_min_yy)
}
for internal_plotx in cx-xx to cx-1 {
internal_plot(cy_plus_yy)
internal_plot(cy_min_yy)
}
for internal_plotx in cx to cx+yy {
internal_plot(cy_plus_xx)
internal_plot(cy_min_xx)
}
for internal_plotx in cx-yy to cx {
internal_plot(cy_plus_xx)
internal_plot(cy_min_xx)
}
yy++
if decisionOver2<=0
decisionOver2 += 2*yy+1
else {
xx--
decisionOver2 += 2*(yy-xx)+1
}
}
}
; here is the non-asm code for the plot routine below:
; sub plot_nonasm(uword px, ubyte py) {
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
; uword addr = bitmap_address + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
; @(addr) |= ormask[lsb(px) & 7]
; }
; TODO fix the use of X (or XY) as parameter so we can actually use this plot() routine
; calling it with a byte results in a compiler crash, calling it with word results in clobbering X register I think
asmsub plotXXX(uword plotx @XY, ubyte ploty @A) {
%asm {{
stx internal_plotx
sty internal_plotx+1
jmp internal_plot
}}
}
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
asmsub internal_plot(ubyte ploty @A) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
%asm {{
tay
stx P8ZP_SCRATCH_REG_X
lda internal_plotx+1
sta P8ZP_SCRATCH_W2+1
lsr a ; 0
sta P8ZP_SCRATCH_W2
lda internal_plotx
pha
and #7
tax
lda _y_lookup_lo,y
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda _y_lookup_hi,y
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
pla ; internal_plotx
and #%11111000
tay
lda (P8ZP_SCRATCH_W2),y
ora _ormask,x
sta (P8ZP_SCRATCH_W2),y
ldx P8ZP_SCRATCH_REG_X
rts
_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.
; 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)
; 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)
_y_lookup_lo .byte <_plot_y_values
_y_lookup_hi .byte >_plot_y_values
}}
}
}

View File

@ -11,8 +11,16 @@
txt { txt {
asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { asmsub clear_screen() {
; ---- clear the character screen with the given fill character and character color. %asm {{
lda #' '
jmp clear_screenchars
}}
}
asmsub fill_screen (ubyte char @ A, ubyte charcolor @ Y) clobbers(A) {
; ---- 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)
%asm {{ %asm {{
@ -41,7 +49,7 @@ _loop sta c64.Screen,y
}} }}
} }
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) { asmsub clear_screencolors (ubyte scrcolor @ 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 {{
@ -56,6 +64,10 @@ _loop sta c64.Colors,y
}} }}
} }
sub color (ubyte txtcol) {
c64.COLOR = txtcol
}
asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left_full (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
@ -504,7 +516,7 @@ _mod lda $ffff ; modified
}} }}
} }
sub setcc (ubyte column, ubyte row, ubyte char, ubyte color) { sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
; ---- set char+color at the given position on the screen ; ---- set char+color at the given position on the screen
%asm {{ %asm {{
lda row lda row
@ -524,7 +536,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte color) {
inc _colormod+2 inc _colormod+2
+ lda char + lda char
_charmod sta $ffff ; modified _charmod sta $ffff ; modified
lda color lda charcolor
_colormod sta $ffff ; modified _colormod sta $ffff ; modified
rts rts
}} }}

View File

@ -0,0 +1,153 @@
; Prog8 definitions for floating point handling on the CommanderX16
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%option enable_floats
c64flt {
; ---- this block contains C-64 floating point related functions ----
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
const float ZERO = 0.0
; ---- ROM float functions ----
; note: the fac1 and fac2 are working registers and take 6 bytes each,
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
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
; there is also c64flt.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
; (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)
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
romsub $fe06 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
romsub $fe09 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
romsub $fe0c = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
romsub $fe0f = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands
romsub $fe12 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
romsub $fe15 = FADDT() clobbers(A,X,Y) ; fac1 += fac2
romsub $fe1b = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
romsub $fe1e = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
romsub $fe24 = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
romsub $fe27 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
romsub $fe2a = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
romsub $fe33 = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
romsub $fe36 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
romsub $fe3c = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
romsub $fe3f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
romsub $fe42 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
romsub $fe48 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
romsub $fe4b = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
romsub $fe4e = MOVFA() clobbers(A,X) ; copy fac2 to fac1
romsub $fe51 = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
romsub $fe54 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
romsub $fe5a = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
romsub $fe5d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
romsub $fe60 = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
romsub $fe6c = ABS() ; fac1 = ABS(fac1)
romsub $fe6f = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
romsub $fe78 = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
romsub $fea2 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
romsub $fea5 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
romsub $fea8 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
romsub $feab = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
romsub $feae = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
%asm {{
phx
sta P8ZP_SCRATCH_REG
sty P8ZP_SCRATCH_B1
tya
ldy P8ZP_SCRATCH_REG
jsr GIVAYF ; load it as signed... correct afterwards
lda P8ZP_SCRATCH_B1
bpl +
lda #<_flt65536
ldy #>_flt65536
jsr FADD
+ plx
rts
_flt65536 .byte 145,0,0,0,0 ; 65536.0
}}
}
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
%asm {{
sta P8ZP_SCRATCH_REG
tya
ldy P8ZP_SCRATCH_REG
jmp GIVAYF ; this uses the inverse order, Y/A
}}
}
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
; ---- fac1 to signed word in A/Y
%asm {{
jsr FTOSWORDYA ; note the inverse Y/A order
sta P8ZP_SCRATCH_REG
tya
ldy P8ZP_SCRATCH_REG
rts
}}
}
asmsub GETADRAY () clobbers(X) -> uword @ AY {
; ---- fac1 to unsigned word in A/Y
%asm {{
jsr GETADR ; this uses the inverse order, Y/A
sta P8ZP_SCRATCH_B1
tya
ldy P8ZP_SCRATCH_B1
rts
}}
}
sub print_f (float value) {
; ---- prints the floating point value (without a newline).
%asm {{
stx P8ZP_SCRATCH_REG_X
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy #0
- lda (P8ZP_SCRATCH_B1),y
beq +
jsr c64.CHROUT
iny
bne -
ldx P8ZP_SCRATCH_REG_X
+ rts
}}
}
%asminclude "library:c64floats.asm", ""
}

View File

@ -62,63 +62,72 @@ cx16 {
; spelling of the names is taken from the Commander X-16 rom sources ; spelling of the names is taken from the Commander X-16 rom sources
; the sixteen virtual 16-bit registers ; the sixteen virtual 16-bit registers
&ubyte r0 = $02 &uword r0 = $02
&ubyte r0L = $02 &uword r1 = $04
&ubyte r0H = $03 &uword r2 = $06
&ubyte r1 = $04 &uword r3 = $08
&ubyte r1L = $04 &uword r4 = $0a
&ubyte r1H = $05 &uword r5 = $0c
&ubyte r2 = $06 &uword r6 = $0e
&ubyte r2L = $06 &uword r7 = $10
&ubyte r2H = $07 &uword r8 = $12
&ubyte r3 = $08 &uword r9 = $14
&ubyte r3L = $08 &uword r10 = $16
&ubyte r3H = $09 &uword r11 = $18
&ubyte r4 = $0a &uword r12 = $1a
&ubyte r4L = $0a &uword r13 = $1c
&ubyte r4H = $0b &uword r14 = $1e
&ubyte r5 = $0c &uword r15 = $20
&ubyte r5L = $0c
&ubyte r5H = $0d
&ubyte r6 = $0e
&ubyte r6L = $0e
&ubyte r6H = $0f
&ubyte r7 = $10
&ubyte r7L = $10
&ubyte r7H = $11
&ubyte r8 = $12
&ubyte r8L = $12
&ubyte r8H = $13
&ubyte r9 = $14
&ubyte r9L = $14
&ubyte r9H = $15
&ubyte r10 = $16
&ubyte r10L = $16
&ubyte r10H = $17
&ubyte r11 = $18
&ubyte r11L = $18
&ubyte r11H = $19
&ubyte r12 = $1a
&ubyte r12L = $1a
&ubyte r12H = $1b
&ubyte r13 = $1c
&ubyte r13L = $1c
&ubyte r13H = $1d
&ubyte r14 = $1e
&ubyte r14L = $1e
&ubyte r14H = $1f
&ubyte r15 = $20
&ubyte r15L = $20
&ubyte r15H = $21
; VERA registers
const uword VERA_BASE = $9F20
&uword VERA_ADDR_L = VERA_BASE + $00
&uword VERA_ADDR_M = VERA_BASE + $01
&uword VERA_ADDR_H = VERA_BASE + $02
&uword VERA_DATA0 = VERA_BASE + $03
&uword VERA_DATA1 = VERA_BASE + $04
&uword VERA_CTRL = VERA_BASE + $05
&uword VERA_IEN = VERA_BASE + $06
&uword VERA_ISR = VERA_BASE + $07
&uword VERA_IRQ_LINE_L = VERA_BASE + $08
&uword VERA_DC_VIDEO = VERA_BASE + $09
&uword VERA_DC_HSCALE = VERA_BASE + $0A
&uword VERA_DC_VSCALE = VERA_BASE + $0B
&uword VERA_DC_BORDER = VERA_BASE + $0C
&uword VERA_DC_HSTART = VERA_BASE + $09
&uword VERA_DC_HSTOP = VERA_BASE + $0A
&uword VERA_DC_VSTART = VERA_BASE + $0B
&uword VERA_DC_VSTOP = VERA_BASE + $0C
&uword VERA_L0_CONFIG = VERA_BASE + $0D
&uword VERA_L0_MAPBASE = VERA_BASE + $0E
&uword VERA_L0_TILEBASE = VERA_BASE + $0F
&uword VERA_L0_HSCROLL_L = VERA_BASE + $10
&uword VERA_L0_HSCROLL_H = VERA_BASE + $11
&uword VERA_L0_VSCROLL_L = VERA_BASE + $12
&uword VERA_L0_VSCROLL_H = VERA_BASE + $13
&uword VERA_L1_CONFIG = VERA_BASE + $14
&uword VERA_L1_MAPBASE = VERA_BASE + $15
&uword VERA_L1_TILEBASE = VERA_BASE + $16
&uword VERA_L1_HSCROLL_L = VERA_BASE + $17
&uword VERA_L1_HSCROLL_H = VERA_BASE + $18
&uword VERA_L1_VSCROLL_L = VERA_BASE + $19
&uword VERA_L1_VSCROLL_H = VERA_BASE + $1A
&uword VERA_AUDIO_CTRL = VERA_BASE + $1B
&uword VERA_AUDIO_RATE = VERA_BASE + $1C
&uword VERA_AUDIO_DATA = VERA_BASE + $1D
&uword VERA_SPI_DATA = VERA_BASE + $1E
&uword VERA_SPI_CTRL = VERA_BASE + $1F
; VERA_PSG_BASE = $1F9C0
; VERA_PALETTE_BASE = $1FA00
; VERA_SPRITES_BASE = $1FC00
; TODO subroutine args + soubroutine returnvalues + clobber registers
; supported C128 additions ; supported C128 additions
romsub $ff4a = close_all() romsub $ff4a = close_all()
romsub $ff59 = lkupla() romsub $ff59 = lkupla()
romsub $ff5c = lkupsa() romsub $ff5c = lkupsa()
romsub $ff5f = screen_set_mode() romsub $ff5f = screen_set_mode(ubyte mode @A) clobbers(A, X, Y) -> ubyte @Pc
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr() romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
romsub $ff65 = pfkey() romsub $ff65 = pfkey()
romsub $ff6e = jsrfar() romsub $ff6e = jsrfar()
@ -129,44 +138,59 @@ romsub $ff7d = primm()
; X16 additions ; X16 additions
romsub $ff44 = macptr() romsub $ff44 = macptr()
romsub $ff47 = enter_basic() romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc)
romsub $ff68 = mouse_config() romsub $ff68 = mouse_config(ubyte shape @A, ubyte scale @X) clobbers (A, X, Y)
romsub $ff6b = mouse_get() romsub $ff6b = mouse_get(ubyte zpdataptr @X) clobbers(A)
romsub $ff71 = mouse_scan() romsub $ff71 = mouse_scan() clobbers(A, X, Y)
romsub $ff53 = joystick_scan() romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get() romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff4d = clock_set_date_time() romsub $ff4d = clock_set_date_time() clobbers(A, X, Y) ; args: r0, r1, r2, r3L
romsub $ff50 = clock_get_date_time() romsub $ff50 = clock_get_date_time() clobbers(A) ; outout args: r0, r1, r2, r3L
; high level graphics & fonts ; high level graphics & fonts
romsub $ff20 = GRAPH_init() romsub $ff20 = GRAPH_init() ; uses vectors=r0
romsub $ff23 = GRAPH_clear() romsub $ff23 = GRAPH_clear()
romsub $ff26 = GRAPH_set_window() romsub $ff26 = GRAPH_set_window() ; uses x=r0, y=r1, width=r2, height=r3
romsub $ff29 = GRAPH_set_colors() romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y)
romsub $ff2c = GRAPH_draw_line() romsub $ff2c = GRAPH_draw_line() ; uses x1=r0, y1=r1, x2=r2, y2=r3
romsub $ff2f = GRAPH_draw_rect() romsub $ff2f = GRAPH_draw_rect(ubyte fill @Pc) ; uses x=r0, y=r1, width=r2, height=r3, cornerradius=r4
romsub $ff32 = GRAPH_move_rect() romsub $ff32 = GRAPH_move_rect() ; uses sx=r0, sy=r1, tx=r2, ty=r3, width=r4, height=r5
romsub $ff35 = GRAPH_draw_oval() romsub $ff35 = GRAPH_draw_oval(ubyte fill @Pc) ; uses x=r0, y=r1, width=r2, height=r3
romsub $ff38 = GRAPH_draw_image() romsub $ff38 = GRAPH_draw_image() ; uses x=r0, y=r1, ptr=r2, width=r3, height=r4
romsub $ff3b = GRAPH_set_font() romsub $ff3b = GRAPH_set_font() ; uses ptr=r0
romsub $ff3e = GRAPH_get_char_size() romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc)
romsub $ff41 = GRAPH_put_char() romsub $ff41 = GRAPH_put_char(ubyte char @A) ; uses x=r0, y=r1
; TODO framebuffer API not yet included, include it ; framebuffer
romsub $fef6 = FB_init()
romsub $fef9 = FB_get_info() -> byte @A ; also outputs width=r0, height=r1
romsub $fefc = FB_set_palette(ubyte index @A, ubyte bytecount @X) ; also uses pointer=r0
romsub $feff = FB_cursor_position() ; uses x=r0, y=r1
romsub $ff02 = FB_cursor_next_line() ; uses x=r0
romsub $ff05 = FB_get_pixel() -> ubyte @A
romsub $ff08 = FB_get_pixels() ; uses ptr=r0, count=r1
romsub $ff0b = FB_set_pixel(ubyte color @A)
romsub $ff0e = FB_set_pixels() ; uses ptr=r0, count=r1
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X)
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) ; also uses mask=r0L
romsub $ff17 = FB_fill_pixels(ubyte color @A) ; also uses count=r0, step=r1
romsub $ff1a = FB_filter_pixels() ; uses ptr=r0, count=r1
romsub $ff1d = FB_move_pixels() ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
romsub $fef0 = sprite_set_image() ; misc
romsub $fef3 = sprite_set_position() romsub $fef0 = sprite_set_image(ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) -> ubyte @Pc ; also uses pixels=r0, mask=r1, bpp=r2L
romsub $fee4 = memory_fill() romsub $fef3 = sprite_set_position(ubyte number @A) ; also uses x=r0 and y=r1
romsub $fee7 = memory_copy() romsub $fee4 = memory_fill(ubyte value @A) ; uses address=r0, num_bytes=r1
romsub $feea = memory_crc() romsub $fee7 = memory_copy() ; uses source=r0, target=r1, num_bytes=r2
romsub $feed = memory_decompress() romsub $feea = memory_crc() ; uses address=r0, num_bytes=r1 result->r2
romsub $fedb = console_init() romsub $feed = memory_decompress() ; uses input=r0, output=r1 result->r1
romsub $fede = console_put_char() romsub $fedb = console_init() ; uses x=r0, y=r1, width=r2, height=r3
romsub $fee1 = console_get_char() romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc)
romsub $fed8 = console_put_image() romsub $fee1 = console_get_char() -> ubyte @A
romsub $fed5 = console_set_paging_message() romsub $fed8 = console_put_image() ; uses ptr=r0, width=r1, height=r2
romsub $fed2 = kbdbuf_put() romsub $fed5 = console_set_paging_message() ; uses messageptr=r0
romsub $fecf = entropy_get() romsub $fed2 = kbdbuf_put(ubyte key @A)
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() romsub $fecc = monitor()
@ -179,9 +203,8 @@ asmsub init_system() {
%asm {{ %asm {{
sei sei
cld cld
lda #0 stz $00
sta $00 stz $01
sta $01
jsr c64.IOINIT jsr c64.IOINIT
jsr c64.RESTOR jsr c64.RESTOR
jsr c64.CINT jsr c64.CINT
@ -191,9 +214,6 @@ asmsub init_system() {
clc clc
clv clv
cli cli
lda #66
clc
jsr console_put_char
rts rts
}} }}
} }

View File

@ -11,16 +11,61 @@
txt { txt {
asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { sub clear_screen() {
; ---- clear the character screen with the given fill character and character color. c64.CHROUT(147) ; clear screen (spaces)
; (assumes screen and color matrix are at their default addresses) }
asmsub fill_screen (ubyte char @ A, ubyte txtcolor @ Y) clobbers(A) {
; ---- fill the character screen with the given fill character and character color.
%asm {{ %asm {{
brk ; TODO 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
}} }}
} }
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) { 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
@ -42,7 +87,7 @@ asmsub print (str text @ AY) clobbers(A,Y) {
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) { asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total) ; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
@ -51,7 +96,7 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr c64.CHROUT jsr c64.CHROUT
txa txa
jsr c64.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG_X plx
rts rts
}} }}
} }
@ -59,7 +104,7 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub (ubyte value @ A) clobbers(A,Y) { asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s ; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
_print_byte_digits _print_byte_digits
pha pha
@ -76,7 +121,7 @@ _print_byte_digits
jsr c64.CHROUT jsr c64.CHROUT
_ones txa _ones txa
jsr c64.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG_X plx
rts rts
}} }}
} }
@ -84,7 +129,7 @@ _ones txa
asmsub print_b (byte value @ A) clobbers(A,Y) { asmsub print_b (byte value @ A) clobbers(A,Y) {
; ---- print the byte in A in decimal form, without left padding 0s ; ---- print the byte in A in decimal form, without left padding 0s
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
pha pha
cmp #0 cmp #0
bpl + bpl +
@ -92,16 +137,14 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
jsr c64.CHROUT jsr c64.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
jsr print_ub._print_byte_digits jmp print_ub._print_byte_digits
ldx P8ZP_SCRATCH_REG_X
rts
}} }}
} }
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { 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) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
bcc + bcc +
pha pha
lda #'$' lda #'$'
@ -111,7 +154,7 @@ asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
jsr c64.CHROUT jsr c64.CHROUT
tya tya
jsr c64.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG_X plx
rts rts
}} }}
} }
@ -119,7 +162,7 @@ asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { 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) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
@ -132,7 +175,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
+ jsr c64.CHROUT + jsr c64.CHROUT
dey dey
bne - bne -
ldx P8ZP_SCRATCH_REG_X plx
rts rts
}} }}
} }
@ -165,7 +208,7 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) { 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) ; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
jsr conv.uword2decimal jsr conv.uword2decimal
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
@ -173,7 +216,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
jsr c64.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ ldx P8ZP_SCRATCH_REG_X + plx
rts rts
}} }}
} }
@ -181,9 +224,9 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
asmsub print_uw (uword value @ AY) clobbers(A,Y) { asmsub print_uw (uword value @ AY) clobbers(A,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s ; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
jsr conv.uword2decimal jsr conv.uword2decimal
ldx P8ZP_SCRATCH_REG_X plx
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq _allzero beq _allzero
@ -225,14 +268,45 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
}} }}
} }
; 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) { asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernel routine, to save the X register. ; ---- safe wrapper around PLOT kernel routine, to save the X register.
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG_X phx
tax tax
clc clc
jsr c64.PLOT jsr c64.PLOT
ldx P8ZP_SCRATCH_REG_X plx
rts rts
}} }}
} }

View File

@ -59,7 +59,7 @@ multiply_words .proc
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG_X stx P8ZP_SCRATCH_REG_X
mult16 lda #$00 mult16 lda #0
sta result+2 ; clear upper bits of product sta result+2 ; clear upper bits of product
sta result+3 sta result+3
ldx #16 ; for all 16 bits... ldx #16 ; for all 16 bits...
@ -86,8 +86,40 @@ result .byte 0,0,0,0
.pend .pend
divmod_b_asm .proc
; signed byte division: make everything positive and fix sign afterwards
sta P8ZP_SCRATCH_B1
tya
eor P8ZP_SCRATCH_B1
php ; save sign
lda P8ZP_SCRATCH_B1
bpl +
eor #$ff
sec
adc #0 ; make it positive
+ pha
tya
bpl +
eor #$ff
sec
adc #0 ; make it positive
tay
+ pla
jsr divmod_ub_asm
sta _remainder
plp
bpl +
tya
eor #$ff
sec
adc #0 ; negate result
tay
+ rts
_remainder .byte 0
.pend
divmod_ub_asm .proc divmod_ub_asm .proc
; TODO divmod_ub_asm doesn't work correctly. (remainder = ok, quotient = FAULTY)
; -- divide A by Y, result quotient in Y, remainder in A (unsigned) ; -- divide A by Y, result quotient in Y, remainder in A (unsigned)
; division by zero will result in quotient = 255 and remainder = original number ; division by zero will result in quotient = 255 and remainder = original number
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
@ -109,6 +141,49 @@ divmod_ub_asm .proc
rts rts
.pend .pend
divmod_w_asm .proc
; signed word division: make everything positive and fix sign afterwards
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_W1+1
eor P8ZP_SCRATCH_W2+1
php ; save sign
lda P8ZP_SCRATCH_W1+1
bpl +
lda #0
sec
sbc P8ZP_SCRATCH_W1
sta P8ZP_SCRATCH_W1
lda #0
sbc P8ZP_SCRATCH_W1+1
sta P8ZP_SCRATCH_W1+1
+ lda P8ZP_SCRATCH_W2+1
bpl +
lda #0
sec
sbc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda #0
sbc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
+ tay
lda P8ZP_SCRATCH_W2
jsr divmod_uw_asm
plp ; restore sign
bpl +
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #0
sec
sbc P8ZP_SCRATCH_W2
pha
lda #0
sbc P8ZP_SCRATCH_W2+1
tay
pla
+ rts
.pend
divmod_uw_asm .proc divmod_uw_asm .proc
; -- divide two unsigned words (16 bit each) into 16 bit results ; -- divide two unsigned words (16 bit each) into 16 bit results
; input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor ; input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor

View File

@ -960,7 +960,7 @@ _result_maxuw .word 0
.pend .pend
func_max_w .proc func_max_w .proc
lda #$00 lda #0
sta _result_maxw sta _result_maxw
lda #$80 lda #$80
sta _result_maxw+1 sta _result_maxw+1

View File

@ -1 +1 @@
4.0 4.1

View File

@ -36,7 +36,7 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
private fun compileMain(args: Array<String>) { private fun compileMain(args: Array<String>) {
val cli = CommandLineInterface("prog8compiler") val cli = CommandLineInterface("prog8compiler")
val startEmulator by cli.flagArgument("-emu", "auto-start the Vice C-64 emulator after successful compilation") val startEmulator by cli.flagArgument("-emu", "auto-start emulator after successful compilation")
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".") val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code") val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations") val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")

View File

@ -642,7 +642,14 @@ class RangeExpr(var from: Expression,
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR) fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W) fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B) fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
else -> InferredTypes.knownFor(DataType.ARRAY_UB) else -> {
val fdt = fromDt.typeOrElse(DataType.STRUCT)
val tdt = toDt.typeOrElse(DataType.STRUCT)
if(fdt largerThan tdt)
InferredTypes.knownFor(ElementArrayTypes.getValue(fdt))
else
InferredTypes.knownFor(ElementArrayTypes.getValue(tdt))
}
} }
} }
override fun toString(): String { override fun toString(): String {

View File

@ -118,6 +118,11 @@ internal class AstChecker(private val program: Program,
if(loopvar==null || loopvar.type== VarDeclType.CONST) { if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position) errors.err("for loop requires a variable to loop with", forLoop.position)
} else { } else {
fun checkLoopRangeValues() {
}
when (loopvar.datatype) { when (loopvar.datatype) {
DataType.UBYTE -> { DataType.UBYTE -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR) if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
@ -142,6 +147,22 @@ internal class AstChecker(private val program: Program,
} }
else -> errors.err("loop variable must be numeric type", forLoop.position) else -> errors.err("loop variable must be numeric type", forLoop.position)
} }
if(errors.isEmpty()) {
// check loop range values
val range = forLoop.iterable as? RangeExpr
if(range!=null) {
val from = range.from as? NumericLiteralValue
val to = range.to as? NumericLiteralValue
if(from != null)
checkValueTypeAndRange(loopvar.datatype, from)
else if(!range.from.inferType(program).istype(loopvar.datatype))
errors.err("range start value is incompatible with loop variable type", range.position)
if(to != null)
checkValueTypeAndRange(loopvar.datatype, to)
else if(!range.to.inferType(program).istype(loopvar.datatype))
errors.err("range end value is incompatible with loop variable type", range.position)
}
}
} }
} }
@ -915,9 +936,6 @@ internal class AstChecker(private val program: Program,
val argIDt = arg.first.value.inferType(program) val argIDt = arg.first.value.inferType(program)
if (!argIDt.isKnown) if (!argIDt.isKnown)
return return
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)
&& arg.first.value !is NumericLiteralValue && arg.first.value !is IdentifierReference)
errors.warn("calling a subroutine that expects X as a parameter is problematic. If you see a compiler error/crash about this later, try to change this call", position)
} }
} }
} }

View File

@ -657,6 +657,7 @@ class Subroutine(override val name: String,
} }
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun amountOfRtsInAsm(): Int = statements fun amountOfRtsInAsm(): Int = statements
.asSequence() .asSequence()

View File

@ -11,6 +11,11 @@ internal interface IMachineFloat {
fun makeFloatFillAsm(): String fun makeFloatFillAsm(): String
} }
internal enum class CpuType {
CPU6502,
CPU65c02
}
internal interface IMachineDefinition { internal interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double val FLOAT_MAX_POSITIVE: Double
@ -24,7 +29,7 @@ internal interface IMachineDefinition {
val opcodeNames: Set<String> val opcodeNames: Set<String>
var zeropage: Zeropage var zeropage: Zeropage
val initSystemProcname: String val initSystemProcname: String
val cpu: String val cpu: CpuType
fun initializeZeropage(compilerOptions: CompilationOptions) fun initializeZeropage(compilerOptions: CompilationOptions)
fun getFloat(num: Number): IMachineFloat fun getFloat(num: Number): IMachineFloat

View File

@ -2,6 +2,7 @@ package prog8.compiler.target.c64
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CpuType
import prog8.compiler.target.IMachineDefinition import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.IMachineFloat import prog8.compiler.target.IMachineFloat
import prog8.parser.ModuleImporter import prog8.parser.ModuleImporter
@ -12,7 +13,7 @@ import kotlin.math.pow
internal object C64MachineDefinition: IMachineDefinition { internal object C64MachineDefinition: IMachineDefinition {
override val cpu = "6502" override val cpu = CpuType.CPU6502
// 5-byte cbm MFLPT format limitations: // 5-byte cbm MFLPT format limitations:
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255 override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
@ -37,7 +38,7 @@ 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.FL_ZERO" floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.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 "c64flt.FL_PIVAL"
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768" floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768"
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FONE" floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FONE"

View File

@ -9,6 +9,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.IAssemblyGenerator import prog8.compiler.target.IAssemblyGenerator
import prog8.compiler.target.IAssemblyProgram import prog8.compiler.target.IAssemblyProgram
import prog8.compiler.target.c64.AssemblyProgram import prog8.compiler.target.c64.AssemblyProgram
@ -78,7 +79,11 @@ internal class AsmGen(private val program: Program,
private fun header() { private fun header() {
val ourName = this.javaClass.name val ourName = this.javaClass.name
val cpu = CompilationTarget.machine.cpu val cpu = when(CompilationTarget.machine.cpu) {
CpuType.CPU6502 -> "6502"
CpuType.CPU65c02 -> "65c02"
else -> "unsupported"
}
out("; $cpu assembly code for '${program.name}'") out("; $cpu assembly code for '${program.name}'")
out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}") out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
@ -546,7 +551,12 @@ internal class AsmGen(private val program: Program,
internal fun saveRegister(register: CpuRegister) { internal fun saveRegister(register: CpuRegister) {
when(register) { when(register) {
CpuRegister.A -> out(" pha") CpuRegister.A -> out(" pha")
CpuRegister.X -> out(" txa | pha") CpuRegister.X -> {
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
out(" phx")
else
out(" stx P8ZP_SCRATCH_REG_X")
}
CpuRegister.Y -> out(" tya | pha") CpuRegister.Y -> out(" tya | pha")
} }
} }
@ -554,7 +564,12 @@ internal class AsmGen(private val program: Program,
internal fun restoreRegister(register: CpuRegister) { internal fun restoreRegister(register: CpuRegister) {
when(register) { when(register) {
CpuRegister.A -> out(" pla") CpuRegister.A -> out(" pla")
CpuRegister.X -> out(" pla | tax") CpuRegister.X -> {
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
out(" plx")
else
out(" ldx P8ZP_SCRATCH_REG_X")
}
CpuRegister.Y -> out(" pla | tay") CpuRegister.Y -> out(" pla | tay")
} }
} }

View File

@ -151,7 +151,8 @@ 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") && seventh.startsWith("jsr c64flt.copy_float")) { fifth.startsWith("lda") && sixth.startsWith("ldy") &&
(seventh.startsWith("jsr c64flt.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()
@ -161,7 +162,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val fourteenth = pair[13].value.trimStart() val fourteenth = pair[13].value.trimStart()
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") && fourteenth.startsWith("jsr c64flt.copy_float")) { twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
(fourteenth.startsWith("jsr c64flt.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

View File

@ -4,6 +4,8 @@ import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.toHex import prog8.compiler.toHex
import prog8.functions.BuiltinFunctions import prog8.functions.BuiltinFunctions
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -45,15 +47,28 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
RegisterOrPair.X -> { RegisterOrPair.X -> {
// return value in X register has been discarded, just push a zero // return value in X register has been discarded, just push a zero
asmgen.out(" lda #0 | sta P8ESTACK_LO,x | dex") if(CompilationTarget.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_LO,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
asmgen.out(" dex")
} }
RegisterOrPair.AX -> { RegisterOrPair.AX -> {
// return value in X register has been discarded, just push a zero in this place // return value in X register has been discarded, just push a zero in this place
asmgen.out(" sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex") asmgen.out(" sta P8ESTACK_LO,x")
if(CompilationTarget.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x")
asmgen.out(" dex")
} }
RegisterOrPair.XY -> { RegisterOrPair.XY -> {
// return value in X register has been discarded, just push a zero in this place // return value in X register has been discarded, just push a zero in this place
asmgen.out(" lda #0 | sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") if(CompilationTarget.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_LO,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
asmgen.out(" tya | sta P8ESTACK_HI,x | dex")
} }
} }
} }
@ -69,7 +84,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.UBYTE -> { DataType.UBYTE -> {
when(expr.type) { when(expr.type) {
DataType.UBYTE, DataType.BYTE -> {} DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x") DataType.UWORD, DataType.WORD -> {
if(CompilationTarget.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float") DataType.FLOAT -> asmgen.out(" jsr c64flt.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")
@ -83,7 +103,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(""" asmgen.out("""
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
ora #$7f ora #$7f
bmi + bmi +""")
if(CompilationTarget.machine.cpu==CpuType.CPU65c02)
asmgen.out("""
+ stz P8ESTACK_HI+1,x""")
else
asmgen.out("""
lda #0 lda #0
+ sta P8ESTACK_HI+1,x""") + sta P8ESTACK_HI+1,x""")
} }

View File

@ -46,7 +46,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
if (stepsize==1 || stepsize==-1) { if (stepsize==1 || stepsize==-1) {
// bytes, step 1 or -1 // bytes array, step 1 or -1
val incdec = if(stepsize==1) "inc" else "dec" val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar // loop over byte range via loopvar
@ -258,13 +258,6 @@ $endLabel inx""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), variable=stmt.loopVar)
val src = AsmAssignSource.fromAstSource(range.from, program).adjustDataTypeToTarget(target)
val assign = AsmAssignment(src, target, false, range.position)
asmgen.translateNormalAssignment(assign)
}
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) { private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
@ -615,4 +608,11 @@ $loopLabel""")
$endLabel""") $endLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), variable=stmt.loopVar)
val src = AsmAssignSource.fromAstSource(range.from, program).adjustDataTypeToTarget(target)
val assign = AsmAssignment(src, target, false, range.position)
asmgen.translateNormalAssignment(assign)
}
} }

View File

@ -16,9 +16,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// output the code to setup the parameters and perform the actual call // output the code to setup the parameters and perform the actual call
// does NOT output the code to deal with the result values! // does NOT output the code to deal with the result values!
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
if(saveX) if(saveX)
asmgen.out(" stx P8ZP_SCRATCH_REG_X") // we only save X for now (required! is the eval stack pointer), screw A and Y... asmgen.saveRegister(CpuRegister.X) // we only save X for now (required! is the eval stack pointer), screw A and Y...
val subName = asmgen.asmSymbolName(stmt.target) val subName = asmgen.asmSymbolName(stmt.target)
if(stmt.args.isNotEmpty()) { if(stmt.args.isNotEmpty()) {
@ -57,7 +57,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" jsr $subName") asmgen.out(" jsr $subName")
if(saveX) if(saveX)
asmgen.out(" ldx P8ZP_SCRATCH_REG_X") // restore X again asmgen.restoreRegister(CpuRegister.X)
} }
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) { private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {

View File

@ -97,7 +97,8 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
} }
else -> { else -> {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.out(" stx P8ZP_SCRATCH_REG_X | tax") asmgen.saveRegister(CpuRegister.X)
asmgen.out(" tax")
when(elementDt) { when(elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x") asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
@ -124,7 +125,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
} }
else -> throw AssemblyError("weird array elt dt") else -> throw AssemblyError("weird array elt dt")
} }
asmgen.out(" ldx P8ZP_SCRATCH_REG_X") asmgen.restoreRegister(CpuRegister.X)
} }
} }
} }

View File

@ -5,6 +5,8 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.toHex import prog8.compiler.toHex
@ -706,6 +708,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// optimized case for float zero // optimized case for float zero
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out("""
stz ${target.asmVarname}
stz ${target.asmVarname}+1
stz ${target.asmVarname}+2
stz ${target.asmVarname}+3
stz ${target.asmVarname}+4
""")
else
asmgen.out(""" asmgen.out("""
lda #0 lda #0
sta ${target.asmVarname} sta ${target.asmVarname}

View File

@ -5,6 +5,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.toHex import prog8.compiler.toHex
@ -125,7 +126,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident) ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
// TODO more specialized code for types such as memory read etc. // TODO more specialized code for types such as memory read etc.
// value is DirectMemoryRead -> { // value is DirectMemoryRead -> {
// println("warning: slow stack evaluation used (8): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO // println("warning: slow stack evaluation used (8): $name $operator= ${value::class.simpleName} at ${value.position}")
// // assignmentAsmGen.translateOtherAssignment(origAssign) // // assignmentAsmGen.translateOtherAssignment(origAssign)
// asmgen.translateExpression(value.addressExpression) // asmgen.translateExpression(value.addressExpression)
// asmgen.out(""" // asmgen.out("""
@ -134,7 +135,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
// inx // inx
// """) // """)
// inplaceModification_word_value_to_variable(name, operator, ) // inplaceModification_word_value_to_variable(name, operator, )
// // TODO
// } // }
value is TypecastExpression -> { value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return if (tryRemoveRedundantCast(value, target, operator)) return
@ -250,16 +250,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
"*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier "*" -> TODO("mul mem byte")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
"/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "/" -> TODO("div mem byte")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
"%" -> { "%" -> {
TODO("byte remainder") TODO("mem byte remainder")
// if(types==DataType.BYTE) // if(types==DataType.BYTE)
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
// asmgen.out(" jsr prog8_lib.remainder_ub") // asmgen.out(" jsr prog8_lib.remainder_ub")
} }
"<<" -> TODO("ubyte asl") "<<" -> TODO("mem ubyte asl")
">>" -> TODO("ubyte lsr") ">>" -> TODO("mem ubyte lsr")
"&" -> { "&" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and P8ESTACK_LO+1,x") asmgen.out(" and P8ESTACK_LO+1,x")
@ -309,16 +309,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
"*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier "*" -> TODO("mem mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
"/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "/" -> TODO("mem div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
"%" -> { "%" -> {
TODO("byte remainder") TODO("mem byte remainder")
// if(types==DataType.BYTE) // if(types==DataType.BYTE)
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
// asmgen.out(" jsr prog8_lib.remainder_ub") // asmgen.out(" jsr prog8_lib.remainder_ub")
} }
"<<" -> TODO("ubyte asl") "<<" -> TODO("mem ubyte asl")
">>" -> TODO("ubyte lsr") ">>" -> TODO("mem ubyte lsr")
"&" -> { "&" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and $otherName") asmgen.out(" and $otherName")
@ -367,19 +367,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
"*" -> { "*" -> {
TODO("mul byte litval") TODO("mem mul byte litval")
// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
} }
"/" -> { "/" -> {
if(value==0) if(value==0)
throw AssemblyError("division by zero") throw AssemblyError("division by zero")
TODO("div byte litval") TODO("mem div byte litval")
// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
} }
"%" -> { "%" -> {
if(value==0) if(value==0)
throw AssemblyError("division by zero") throw AssemblyError("division by zero")
TODO("byte remainder litval") TODO("mem byte remainder litval")
// if(types==DataType.BYTE) // if(types==DataType.BYTE)
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
// asmgen.out(" jsr prog8_lib.remainder_ub") // asmgen.out(" jsr prog8_lib.remainder_ub")
@ -442,14 +442,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name") "+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name")
"-" -> 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("mul byte expr") TODO("var mul byte expr")
// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
} }
"/" -> { "/" -> {
TODO("div byte expr")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") TODO("var div byte expr")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
} }
"%" -> { "%" -> {
TODO("byte remainder expr") TODO("var byte remainder expr")
// if(types==DataType.BYTE) // if(types==DataType.BYTE)
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
// asmgen.out(" jsr prog8_lib.remainder_ub") // asmgen.out(" jsr prog8_lib.remainder_ub")
@ -506,7 +506,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"*" -> asmgen.out(" lda $name | ldy $otherName | jsr math.multiply_bytes | sta $name") "*" -> asmgen.out(" lda $name | ldy $otherName | jsr math.multiply_bytes | sta $name")
"/" -> { "/" -> {
if(dt==DataType.BYTE) { if(dt==DataType.BYTE) {
TODO("signed byte divide see prog8lib.idiv_b") asmgen.out(" lda $name | ldy $otherName | jsr math.divmod_b_asm | sty $name")
} }
else { else {
asmgen.out(" lda $name | ldy $otherName | jsr math.divmod_ub_asm | sty $name") asmgen.out(" lda $name | ldy $otherName | jsr math.divmod_ub_asm | sty $name")
@ -573,7 +573,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name") "-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
"*" -> { "*" -> {
// TODO what about the optimized mul_5 etc routines? // TODO what about the optimized mul_5 etc routines?
TODO("byte mul litval") TODO("var byte mul litval")
// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
} }
"/" -> { "/" -> {
@ -585,7 +585,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sty $name sty $name
""") """)
} else { } else {
TODO("BYTE div litval") TODO("var BYTE div litval")
} }
} }
"%" -> { "%" -> {
@ -687,7 +687,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if(value==0) if(value==0)
throw AssemblyError("division by zero") throw AssemblyError("division by zero")
if(dt==DataType.WORD) { if(dt==DataType.WORD) {
TODO("signed word divide see prog8lib.idiv_w") asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$value
ldy #>$value
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
} }
else { else {
asmgen.out(""" asmgen.out("""
@ -736,9 +746,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"&" -> { "&" -> {
when { when {
value == 0 -> asmgen.out(" lda #0 | sta $name | sta $name+1") value == 0 -> {
value and 255 == 0 -> asmgen.out(" lda #0 | sta $name | lda $name+1 | and #>$value | sta $name+1") if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
value < 0x0100 -> asmgen.out(" lda $name | and #$value | sta $name | lda #0 | sta $name+1") asmgen.out(" stz $name | stz $name+1")
else
asmgen.out(" lda #0 | sta $name | sta $name+1")
}
value and 255 == 0 -> {
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
}
value < 0x0100 -> {
asmgen.out(" lda $name | and #$value | sta $name")
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1") else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
} }
} }
@ -845,7 +872,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"/" -> { "/" -> {
if(dt==DataType.WORD) { if(dt==DataType.WORD) {
TODO("signed word divide see prog8lib.idiv_w") asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $otherName
ldy $otherName+1
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
} }
else { else {
asmgen.out(""" asmgen.out("""
@ -989,7 +1026,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"/" -> { "/" -> {
if (dt == DataType.WORD) { if (dt == DataType.WORD) {
TODO("signed word divide see prog8lib.idiv_w") asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
} else { } else {
asmgen.out(""" asmgen.out("""
lda $name lda $name
@ -1041,11 +1088,11 @@ 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.saveRegister(CpuRegister.X)
when (operator) { when (operator) {
"**" -> { "**" -> {
asmgen.out(""" asmgen.out("""
jsr c64flt.pop_float_fac1 jsr c64flt.pop_float_fac1
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.CONUPK jsr c64flt.CONUPK
@ -1055,7 +1102,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
jsr c64flt.pop_float_fac1 jsr c64flt.pop_float_fac1
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.FADD jsr c64flt.FADD
@ -1064,7 +1110,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"-" -> { "-" -> {
asmgen.out(""" asmgen.out("""
jsr c64flt.pop_float_fac1 jsr c64flt.pop_float_fac1
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.FSUB jsr c64flt.FSUB
@ -1073,7 +1118,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"*" -> { "*" -> {
asmgen.out(""" asmgen.out("""
jsr c64flt.pop_float_fac1 jsr c64flt.pop_float_fac1
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.FMULT jsr c64flt.FMULT
@ -1082,7 +1126,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"/" -> { "/" -> {
asmgen.out(""" asmgen.out("""
jsr c64flt.pop_float_fac1 jsr c64flt.pop_float_fac1
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.FDIV jsr c64flt.FDIV
@ -1095,8 +1138,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldx #<$name ldx #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVMF jsr c64flt.MOVMF
ldx P8ZP_SCRATCH_REG_X
""") """)
asmgen.restoreRegister(CpuRegister.X)
} }
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference) { private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference) {
@ -1105,10 +1148,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
throw AssemblyError("float variable expected") throw AssemblyError("float variable expected")
val otherName = asmgen.asmVariableName(ident) val otherName = asmgen.asmVariableName(ident)
asmgen.saveRegister(CpuRegister.X)
when (operator) { when (operator) {
"**" -> { "**" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.CONUPK jsr c64flt.CONUPK
@ -1119,7 +1162,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1130,7 +1172,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"-" -> { "-" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$otherName lda #<$otherName
ldy #>$otherName ldy #>$otherName
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1141,7 +1182,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"*" -> { "*" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1152,7 +1192,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"/" -> { "/" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$otherName lda #<$otherName
ldy #>$otherName ldy #>$otherName
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1168,16 +1207,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldx #<$name ldx #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVMF jsr c64flt.MOVMF
ldx P8ZP_SCRATCH_REG_X
""") """)
asmgen.restoreRegister(CpuRegister.X)
} }
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) { private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) {
val constValueName = asmgen.getFloatAsmConst(value) val constValueName = asmgen.getFloatAsmConst(value)
asmgen.saveRegister(CpuRegister.X)
when (operator) { when (operator) {
"**" -> { "**" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.CONUPK jsr c64flt.CONUPK
@ -1190,7 +1229,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if (value == 0.0) if (value == 0.0)
return return
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1203,7 +1241,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if (value == 0.0) if (value == 0.0)
return return
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$constValueName lda #<$constValueName
ldy #>$constValueName ldy #>$constValueName
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1214,7 +1251,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"*" -> { "*" -> {
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$name lda #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1227,7 +1263,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if (value == 0.0) if (value == 0.0)
throw AssemblyError("division by zero") throw AssemblyError("division by zero")
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<$constValueName lda #<$constValueName
ldy #>$constValueName ldy #>$constValueName
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1243,8 +1278,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldx #<$name ldx #<$name
ldy #>$name ldy #>$name
jsr c64flt.MOVMF jsr c64flt.MOVMF
ldx P8ZP_SCRATCH_REG_X
""") """)
asmgen.restoreRegister(CpuRegister.X)
} }
private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) { private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) {
@ -1259,12 +1294,25 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (outerCastDt) { when (outerCastDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> asmgen.out(" lda #0 | sta ${target.asmVarname}+1") TargetStorageKind.VARIABLE -> {
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${target.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
}
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y, true) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y, true)
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${target.asmVarname},y")
else
asmgen.out(" lda #0 | sta ${target.asmVarname},y") asmgen.out(" lda #0 | sta ${target.asmVarname},y")
} }
TargetStorageKind.STACK -> asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x") TargetStorageKind.STACK -> {
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
}
else -> throw AssemblyError("weird target") else -> throw AssemblyError("weird target")
} }
} }
@ -1468,8 +1516,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
DataType.FLOAT -> { DataType.FLOAT -> {
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.saveRegister(CpuRegister.X)
asmgen.out(""" asmgen.out("""
stx P8ZP_SCRATCH_REG_X
lda #<${target.asmVarname} lda #<${target.asmVarname}
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
jsr c64flt.MOVFM jsr c64flt.MOVFM
@ -1477,8 +1525,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldx #<${target.asmVarname} ldx #<${target.asmVarname}
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
jsr c64flt.MOVMF jsr c64flt.MOVMF
ldx P8ZP_SCRATCH_REG_X
""") """)
asmgen.restoreRegister(CpuRegister.X)
} }
TargetStorageKind.ARRAY -> TODO("in-place negate float array") TargetStorageKind.ARRAY -> TODO("in-place negate float array")
TargetStorageKind.STACK -> TODO("stack float negate") TargetStorageKind.STACK -> TODO("stack float negate")

View File

@ -2,6 +2,7 @@ package prog8.compiler.target.cx16
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CpuType
import prog8.compiler.target.IMachineDefinition import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.C64MachineDefinition
import prog8.parser.ModuleImporter import prog8.parser.ModuleImporter
@ -9,7 +10,7 @@ import java.io.IOException
internal object CX16MachineDefinition: IMachineDefinition { internal object CX16MachineDefinition: IMachineDefinition {
override val cpu = "65c02" override val cpu = CpuType.CPU65c02
// 5-byte cbm MFLPT format limitations: // 5-byte cbm MFLPT format limitations:
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255 override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
@ -29,7 +30,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num) override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
override fun getFloatRomConst(number: Double): String? = null // TODO Does Cx16 have 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, "cx16lib")
@ -57,16 +58,17 @@ internal object CX16MachineDefinition: IMachineDefinition {
} }
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names // 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
// TODO add 65C02 opcodes override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc", "beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey", "cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs", "eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las", "inc", "inx", "iny", "jmp", "jsr",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php", "lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx", "pla", "plp", "rol", "ror", "rti", "rts", "sbc",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre", "sec", "sed", "sei",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa") "sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
"rmb", "smb", "stp", "wai")
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) { internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {

View File

@ -318,11 +318,11 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
} }
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> { override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr { fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr? {
val fromCast = rangeFrom.cast(targetDt) val fromCast = rangeFrom.cast(targetDt)
val toCast = rangeTo.cast(targetDt) val toCast = rangeTo.cast(targetDt)
if(!fromCast.isValid || !toCast.isValid) if(!fromCast.isValid || !toCast.isValid)
return range return null
val newStep = val newStep =
if(stepLiteral!=null) { if(stepLiteral!=null) {
@ -351,6 +351,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(rangeFrom.type!= DataType.UBYTE) { if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values // attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
} }
} }
@ -358,6 +359,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(rangeFrom.type!= DataType.BYTE) { if(rangeFrom.type!= DataType.BYTE) {
// attempt to translate the iterable into byte values // attempt to translate the iterable into byte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
} }
} }
@ -365,6 +367,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(rangeFrom.type!= DataType.UWORD) { if(rangeFrom.type!= DataType.UWORD) {
// attempt to translate the iterable into uword values // attempt to translate the iterable into uword values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
} }
} }
@ -372,6 +375,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(rangeFrom.type!= DataType.WORD) { if(rangeFrom.type!= DataType.WORD) {
// attempt to translate the iterable into word values // attempt to translate the iterable into word values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
} }
} }

View File

@ -83,7 +83,7 @@ For normal use the compiler is invoked with the command:
By default, assembly code is generated and written to ``sourcefile.asm``. By default, assembly code is generated and written to ``sourcefile.asm``.
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
that assembles it into the final program. that assembles it into the final program.
If you use the option to let the compiler auto-start a C-64 emulator, it will do so after If you use the option to let the compiler auto-start an emulator, it will do so after
a successful compilation. This will load your program and the symbol and breakpoint lists a successful compilation. This will load your program and the symbol and breakpoint lists
(for the machine code monitor) into the emulator. (for the machine code monitor) into the emulator.

View File

@ -12,6 +12,7 @@ What is Prog8?
This is an experimental compiled programming language targeting the 8-bit This is an experimental compiled programming language targeting the 8-bit
`6502 <https://en.wikipedia.org/wiki/MOS_Technology_6502>`_ / `6502 <https://en.wikipedia.org/wiki/MOS_Technology_6502>`_ /
`65c02 <https://en.wikipedia.org/wiki/MOS_Technology_65C02>`_ /
`6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ microprocessor. `6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ microprocessor.
This CPU is from the late 1970's and early 1980's and was used in many home computers from that era, This CPU is from the late 1970's and early 1980's and was used in many home computers from that era,
such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_. such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_.
@ -95,7 +96,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
// TODO fix code example
The following programs shows a use of the high level ``struct`` type:: The following programs shows a use of the high level ``struct`` type::
@ -179,6 +179,7 @@ Fnd for Windows it's possible to get that as well. Check out `AdoptOpenJDK <http
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on. Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_. The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
If you're targeting the CommanderX16, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
.. important:: .. important::
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*) **Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)

View File

@ -9,10 +9,12 @@ 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
The main target machine is the well-known Commodore-64, which is an example of this. Currently there are two machines that are supported as compiler target (via the ``-target`` compiler argument):
Another (preliminary) supported target machine is the `CommanderX16 <https://www.commanderx16.com/>`_ .
This chapter explains the relevant system details of such machines. - '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.
This chapter explains the relevant system details of these machines.
Memory Model Memory Model

View File

@ -2,8 +2,6 @@
%import c64textio %import c64textio
%zeropage basicsafe %zeropage basicsafe
; TODO implement signed byte/word DIV asm generation, fix unsigned DIV asm generation (for in-place)
main { main {
sub start() { sub start() {
@ -11,17 +9,17 @@ main {
div_ubyte(100, 6, 16) div_ubyte(100, 6, 16)
div_ubyte(255, 2, 127) div_ubyte(255, 2, 127)
;div_byte(0, 1, 0) ; TODO implement div_byte(0, 1, 0)
;div_byte(100, -6, -16) ; TODO implement div_byte(100, -6, -16)
;div_byte(127, -2, -63) ; TODO implement div_byte(127, -2, -63)
div_uword(0,1,0) div_uword(0,1,0)
div_uword(40000,500,80) div_uword(40000,500,80)
div_uword(43211,2,21605) div_uword(43211,2,21605)
;div_word(0,1,0) ; TODO implement div_word(0,1,0)
;div_word(-20000,500,-40) ; TODO implement div_word(-20000,500,-40)
;div_word(-2222,2,-1111) ; TODO implement div_word(-2222,2,-1111)
div_float(0,1,0) div_float(0,1,0)
div_float(999.9,111.0,9.008108108108107) div_float(999.9,111.0,9.008108108108107)

View File

@ -47,10 +47,10 @@ sub delay() {
sub print_notes(ubyte n1, ubyte n2) { sub print_notes(ubyte n1, ubyte n2) {
c64.CHROUT('\n') c64.CHROUT('\n')
txt.plot(n1/2, 24) txt.plot(n1/2, 24)
c64.COLOR=7 txt.color(7)
c64.CHROUT('Q') c64.CHROUT('Q')
txt.plot(n2/2, 24) txt.plot(n2/2, 24)
c64.COLOR=4 txt.color(4)
c64.CHROUT('Q') c64.CHROUT('Q')
} }

View File

@ -1,256 +0,0 @@
%import c64textio
; bitmap pixel graphics module for the C64
; only black/white monchrome for now
; you could put this code at $4000 which is after the bitmap screen in memory ($2000-$3fff),
; this leaves more space for user program code.
graphics {
const uword bitmap_address = $2000
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
c64.SCROLY |= %00100000
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
clear_screen()
}
sub clear_screen() {
memset(bitmap_address, 320*200/8, 0)
txt.clear_screen($10, 0) ; pixel color $1 (white) backround $0 (black)
}
sub line(uword x1, ubyte y1, uword x2, ubyte y2) {
; Bresenham algorithm.
; This code special cases various quadrant loops to allow simple ++ and -- operations.
if y1>y2 {
; make sure dy is always positive to avoid 8 instead of just 4 special cases
swap(x1, x2)
swap(y1, y2)
}
word d = 0
ubyte positive_ix = true
word dx = x2 - x1 as word
word dy = y2 as word - y1 as word
if dx < 0 {
dx = -dx
positive_ix = false
}
dx *= 2
dy *= 2
plotx = x1
if dx >= dy {
if positive_ix {
repeat {
plot(y1)
if plotx==x2
return
plotx++
d += dy
if d > dx {
y1++
d -= dx
}
}
} else {
repeat {
plot(y1)
if plotx==x2
return
plotx--
d += dy
if d > dx {
y1++
d -= dx
}
}
}
}
else {
if positive_ix {
repeat {
plot(y1)
if y1 == y2
return
y1++
d += dx
if d > dy {
plotx++
d -= dy
}
}
} else {
repeat {
plot(y1)
if y1 == y2
return
y1++
d += dx
if d > dy {
plotx--
d -= dy
}
}
}
}
}
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
; Midpoint algorithm
ubyte ploty
ubyte xx = radius
ubyte yy = 0
byte decisionOver2 = 1-xx as byte
while xx>=yy {
plotx = xcenter + xx
ploty = ycenter + yy
plot(ploty)
plotx = xcenter - xx
plot(ploty)
plotx = xcenter + xx
ploty = ycenter - yy
plot(ploty)
plotx = xcenter - xx
plot(ploty)
plotx = xcenter + yy
ploty = ycenter + xx
plot(ploty)
plotx = xcenter - yy
plot(ploty)
plotx = xcenter + yy
ploty = ycenter - xx
plot(ploty)
plotx = xcenter - yy
plot(ploty)
yy++
if decisionOver2<=0
decisionOver2 += 2*yy+1
else {
xx--
decisionOver2 += 2*(yy-xx)+1
}
}
}
sub disc(uword cx, ubyte cy, ubyte radius) {
; Midpoint algorithm, filled
ubyte xx = radius
ubyte yy = 0
byte decisionOver2 = 1-xx as byte
while xx>=yy {
ubyte cy_plus_yy = cy + yy
ubyte cy_min_yy = cy - yy
ubyte cy_plus_xx = cy + xx
ubyte cy_min_xx = cy - xx
for plotx in cx to cx+xx {
plot(cy_plus_yy)
plot(cy_min_yy)
}
for plotx in cx-xx to cx-1 {
plot(cy_plus_yy)
plot(cy_min_yy)
}
for plotx in cx to cx+yy {
plot(cy_plus_xx)
plot(cy_min_xx)
}
for plotx in cx-yy to cx {
plot(cy_plus_xx)
plot(cy_min_xx)
}
yy++
if decisionOver2<=0
decisionOver2 += 2*yy+1
else {
xx--
decisionOver2 += 2*(yy-xx)+1
}
}
}
; here is the non-asm code for the plot routine below:
; sub plot_nonasm(uword px, ubyte py) {
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
; uword addr = bitmap_address + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
; @(addr) |= ormask[lsb(px) & 7]
; }
uword plotx ; 0..319 ; separate 'parameter' for plot()
asmsub plot(ubyte ploty @A) { ; plotx is 16 bits 0 to 319... doesn't fit in a register
%asm {{
tay
stx P8ZP_SCRATCH_REG_X
lda plotx+1
sta P8ZP_SCRATCH_W2+1
lsr a ; 0
sta P8ZP_SCRATCH_W2
lda plotx
pha
and #7
tax
lda _y_lookup_lo,y
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda _y_lookup_hi,y
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
pla ; plotx
and #%11111000
tay
lda (P8ZP_SCRATCH_W2),y
ora _ormask,x
sta (P8ZP_SCRATCH_W2),y
ldx P8ZP_SCRATCH_REG_X
rts
_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.
; 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)
_y_lookup_hi
.byte $20, $20, $20, $20, $20, $20, $20, $20, $21, $21, $21, $21, $21, $21, $21, $21
.byte $22, $22, $22, $22, $22, $22, $22, $22, $23, $23, $23, $23, $23, $23, $23, $23
.byte $25, $25, $25, $25, $25, $25, $25, $25, $26, $26, $26, $26, $26, $26, $26, $26
.byte $27, $27, $27, $27, $27, $27, $27, $27, $28, $28, $28, $28, $28, $28, $28, $28
.byte $2a, $2a, $2a, $2a, $2a, $2a, $2a, $2a, $2b, $2b, $2b, $2b, $2b, $2b, $2b, $2b
.byte $2c, $2c, $2c, $2c, $2c, $2c, $2c, $2c, $2d, $2d, $2d, $2d, $2d, $2d, $2d, $2d
.byte $2f, $2f, $2f, $2f, $2f, $2f, $2f, $2f, $30, $30, $30, $30, $30, $30, $30, $30
.byte $31, $31, $31, $31, $31, $31, $31, $31, $32, $32, $32, $32, $32, $32, $32, $32
.byte $34, $34, $34, $34, $34, $34, $34, $34, $35, $35, $35, $35, $35, $35, $35, $35
.byte $36, $36, $36, $36, $36, $36, $36, $36, $37, $37, $37, $37, $37, $37, $37, $37
.byte $39, $39, $39, $39, $39, $39, $39, $39, $3a, $3a, $3a, $3a, $3a, $3a, $3a, $3a
.byte $3b, $3b, $3b, $3b, $3b, $3b, $3b, $3b, $3c, $3c, $3c, $3c, $3c, $3c, $3c, $3c
.byte $3e, $3e, $3e, $3e, $3e, $3e, $3e, $3e
_y_lookup_lo
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
.byte $00, $01, $02, $03, $04, $05, $06, $07
}}
}
}

View File

@ -32,7 +32,7 @@ main {
repeat { repeat {
rotate_vertices(msb(anglex), msb(angley), msb(anglez)) rotate_vertices(msb(anglex), msb(angley), msb(anglez))
graphics.clear_screen() graphics.clear_screen(1, 0)
draw_lines() draw_lines()
anglex-=500 anglex-=500
angley+=217 angley+=217

23
examples/cx16/floats.p8 Normal file
View File

@ -0,0 +1,23 @@
; CommanderX16 floating point example!
; make sure to compile with the cx16 compiler target.
%import cx16textio
%import cx16flt
%zeropage basicsafe
main {
sub start() {
float f1 = 5.55
float f2 = 33.3
float f3 = f1 * f2
c64flt.print_f(f1)
c64.CHROUT('*')
c64flt.print_f(f2)
c64.CHROUT('=')
c64flt.print_f(f3)
c64.CHROUT('\n')
}
}

39
examples/cx16/graphics.p8 Normal file
View File

@ -0,0 +1,39 @@
; CommanderX16 text clock example!
; make sure to compile with the cx16 compiler target.
%zeropage basicsafe
main {
sub start() {
void cx16.screen_set_mode($80)
cx16.r0=0
cx16.FB_init()
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
uword xx
ubyte yy
for yy in 199 downto 0 {
for xx in 319 downto 0 {
cx16.FB_set_pixel( yy+lsb(xx))
}
}
; cx16.GRAPH_init()
for xx in 0 to 319 step 32 {
ubyte q
for q in 0 to 31 {
cx16.GRAPH_set_colors(q, 2, 0)
cx16.r0 = xx+q
cx16.r1=0
cx16.r2=rnd()
cx16.r3=199
cx16.GRAPH_draw_line()
}
}
}
}

View File

@ -0,0 +1,31 @@
100 REM MANDELBROT GFX - BASIC VERSION FOR COMMANDERX16
110 REM MANDELBROT COORDINATES
120 XL=-2.000:XU=0.500
130 YL=-1.100:YU=1.100
140 MI%=16 :REM MAXIMUM ITERATIONS
150 WI%=256:HI%=200 :REM SCREEN DIMENSIONS
160 DX=(XU-XL)/WI%
170 DY=(YU-YL)/HI%
200 SCREEN $80:TI$="000000"
500 REM MAIN LOOP
520 X0=0:Y0=0:GOSUB2000
530 FOR YY=0 TO HI%-1
540 Y0=YL+DY*YY
610 FOR XX=0 TO WI%-1
620 X0=XL+DX*XX
700 REM CALCULATE MANDELBROT
710 X=0:Y=0:IT%=0
720 X2=0:Y2=0
730 XY=X*Y
740 X=X2-Y2+X0
750 Y=2*XY+Y0
760 IT%=IT%+1
770 X2=X*X:Y2=Y*Y
780 IF (X2+Y2 <= 4) AND (IT% < MI%) THEN GOTO 730
790 PSET XX,YY,MI%-IT%
800 NEXT XX
810 GOSUB2000
820 NEXT YY
1000 GOTO 1000
2000 REM OUTPUT TIME SPENT
2010 PRINT CHR$(19)SPC(33)TI$:RETURN

View File

@ -0,0 +1,94 @@
%import cx16textio
%import cx16flt
%zeropage basicsafe
main {
const uword width = 256
const uword height = 200
const ubyte max_iter = 16 ; 32 looks pretty nice
sub start() {
initialize()
mandel()
repeat {
; do nothing
}
}
sub mandel() {
const float XL=-2.000
const float XU=0.500
const float YL=-1.100
const float YU=1.100
float dx = (XU-XL)/width
float dy = (YU-YL)/height
ubyte pixelx
ubyte pixely
for pixely in 0 to height-1 {
float yy = YL+dy*(pixely as float)
cx16.r0 = 0
cx16.r1 = pixely
cx16.FB_cursor_position()
for pixelx in 0 to width-1 {
float xx = XL+dx*(pixelx as float)
float xsquared = 0.0
float ysquared = 0.0
float x = 0.0
float y = 0.0
ubyte iter = 0
while iter<max_iter and xsquared+ysquared<4.0 {
y = x*y*2.0 + yy
x = xsquared - ysquared + xx
xsquared = x*x
ysquared = y*y
iter++
}
cx16.FB_set_pixel(max_iter-iter)
}
print_time()
}
}
sub initialize() {
void cx16.screen_set_mode($80)
txt.plot(32, 5)
txt.print("256*200")
txt.plot(32, 6)
txt.print("mandel-")
txt.plot(33, 7)
txt.print("brot")
txt.plot(32, 9)
txt.print("floats")
txt.plot(32, 10)
txt.print_b(max_iter)
txt.print(" iter")
cx16.r0 = 0
cx16.r1 = 0
cx16.r2 = 0
cx16.r3 = 0
cx16.clock_set_date_time()
cx16.r0=0
cx16.FB_init()
}
sub print_time() {
cx16.clock_get_date_time()
txt.plot(33, 12)
if lsb(cx16.r2) < 10
c64.CHROUT('0')
txt.print_ub(lsb(cx16.r2))
c64.CHROUT(':')
if msb(cx16.r2) < 10
c64.CHROUT('0')
txt.print_ub(msb(cx16.r2))
}
}

View File

@ -0,0 +1,42 @@
%import cx16textio
%import cx16flt
%zeropage basicsafe
main {
const uword width = 60
const uword height = 50
const ubyte max_iter = 16
sub start() {
txt.print("calculating mandelbrot fractal...\n\n")
ubyte pixelx
ubyte pixely
for pixely in 0 to height-1 {
float yy = (pixely as float)/0.40/height - 1.3
for pixelx in 0 to width-1 {
float xx = (pixelx as float)/0.32/width - 2.2
float xsquared = 0.0
float ysquared = 0.0
float x = 0.0
float y = 0.0
ubyte iter = 0
while iter<max_iter and xsquared+ysquared<4.0 {
y = x*y*2.0 + yy
x = xsquared - ysquared + xx
xsquared = x*x
ysquared = y*y
iter++
}
; txt.setchr(pixelx, pixely, '*')
txt.color2(1, max_iter-iter)
c64.CHROUT(' ')
}
c64.CHROUT('\n')
}
}
}

View File

@ -0,0 +1,27 @@
%import cx16textio
%import cx16flt
%zeropage basicsafe
main {
const uword width = 80
const uword height = 60
struct Ball {
float t
ubyte color
}
sub start() {
Ball ball
repeat {
ubyte xx=(sin(ball.t) * width/2.1) + width/2.0 as ubyte
ubyte yy=(cos(ball.t*1.1356) * height/2.1) + height/2.0 as ubyte
txt.setcc(xx, yy, 81, ball.color)
ball.t += 0.05
ball.color++
}
}
}

30
examples/cx16/swirl.p8 Normal file
View File

@ -0,0 +1,30 @@
%import cx16lib
%import cx16textio
%zeropage basicsafe
main {
const uword width = 79
const uword height = 59
struct Ball {
uword anglex
uword angley
ubyte color
}
sub start() {
Ball ball
repeat {
ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width)
ubyte y = msb(cos8u(msb(ball.angley)) as uword * height)
txt.setcc(x, y, 81, ball.color)
ball.anglex+=266
ball.angley+=215
ball.color++
}
}
}

View File

@ -8,18 +8,34 @@ main {
sub start() { sub start() {
cx16.r0L = 2020 - 1900 ; %asm {{
cx16.r0H = 8 ; lda #$80
cx16.r1L = 27 ; jsr cx16.screen_set_mode
cx16.r1H = 19 ; }}
cx16.r2L = 16 ; cx16.r0=0
cx16.r2H = 0 ; cx16.GRAPH_init()
cx16.r3L = 0 ; %asm {{
; lda #4
; ldy #0
; ldx #1
; jsr cx16.GRAPH_set_colors
; }}
; cx16.GRAPH_clear()
; cx16.r0=10
; cx16.r1=10
; cx16.r2=100
; cx16.r3=150
; cx16.GRAPH_draw_line()
;
; repeat {
; }
cx16.r0 = mkword(8, 2020 - 1900)
cx16.r1 = mkword(19, 27)
cx16.r2 = mkword(0, 16)
cx16.r3 = 0
cx16.clock_set_date_time() cx16.clock_set_date_time()
cx16.screen_set_charset(3, 0) ; lowercase charset
cx16.screen_set_charset(3, 0)
; c64.CHROUT(14) ; lowercase charset
repeat { repeat {
c64.CHROUT(19) ; HOME c64.CHROUT(19) ; HOME
@ -33,33 +49,33 @@ main {
} }
sub print_date() { sub print_date() {
txt.print_uw(1900 + cx16.r0L) txt.print_uw(1900 + lsb(cx16.r0))
c64.CHROUT('-') c64.CHROUT('-')
if cx16.r0H < 10 if msb(cx16.r0) < 10
c64.CHROUT('0') c64.CHROUT('0')
txt.print_ub(cx16.r0H) txt.print_ub(msb(cx16.r0))
c64.CHROUT('-') c64.CHROUT('-')
if cx16.r1L < 10 if lsb(cx16.r1) < 10
c64.CHROUT('0') c64.CHROUT('0')
txt.print_ub(cx16.r1L) txt.print_ub(lsb(cx16.r1))
} }
sub print_time() { sub print_time() {
if cx16.r1H < 10 if msb(cx16.r1) < 10
c64.CHROUT('0') c64.CHROUT('0')
txt.print_ub(cx16.r1H) txt.print_ub(msb(cx16.r1))
c64.CHROUT(':') c64.CHROUT(':')
if cx16.r2L < 10 if lsb(cx16.r2) < 10
c64.CHROUT('0') c64.CHROUT('0')
txt.print_ub(cx16.r2L) txt.print_ub(lsb(cx16.r2))
c64.CHROUT(':') c64.CHROUT(':')
if cx16.r2H < 10 if msb(cx16.r2) < 10
c64.CHROUT('0') c64.CHROUT('0')
txt.print_ub(cx16.r2H) txt.print_ub(msb(cx16.r2))
c64.CHROUT('.') c64.CHROUT('.')
if cx16.r3L < 10 if lsb(cx16.r3) < 10
c64.CHROUT('0') c64.CHROUT('0')
txt.print_ub(cx16.r3L) txt.print_ub(lsb(cx16.r3))
} }
} }

View File

@ -9,7 +9,7 @@ main {
sub start() { sub start() {
; set text color and activate lowercase charset ; set text color and activate lowercase charset
c64.COLOR = 13 txt.color(13)
c64.VMCSB |= 2 c64.VMCSB |= 2
; use optimized routine to write text ; use optimized routine to write text

View File

@ -16,13 +16,13 @@ main {
txt.print("enter for disc:") txt.print("enter for disc:")
void c64.CHRIN() void c64.CHRIN()
c64.CHROUT('\n') c64.CHROUT('\n')
txt.clear_screen(' ', 1) txt.clear_screen()
disc(20, 12, 12) disc(20, 12, 12)
txt.print("enter for lines:") txt.print("enter for lines:")
void c64.CHRIN() void c64.CHRIN()
c64.CHROUT('\n') c64.CHROUT('\n')
txt.clear_screen(' ', 1) txt.clear_screen()
line(1, 10, 38, 24) line(1, 10, 38, 24)
line(1, 20, 38, 2) line(1, 20, 38, 2)
@ -32,7 +32,7 @@ main {
txt.print("enter for rectangles:") txt.print("enter for rectangles:")
void c64.CHRIN() void c64.CHRIN()
c64.CHROUT('\n') c64.CHROUT('\n')
txt.clear_screen(' ', 1) txt.clear_screen()
rect(4, 8, 37, 23, false) rect(4, 8, 37, 23, false)
rect(20, 12, 30, 20, true) rect(20, 12, 30, 20, true)

View File

@ -37,10 +37,10 @@ main {
iter++ iter++
} }
if iter & 1 { if iter & 1
graphics.plotx = pixelx ; TODO fix plot() so we don't have to use separate internal variable
graphics.plot(pixely) graphics.internal_plotx = pixelx
} graphics.internal_plot(pixely)
} }
} }

View File

@ -21,7 +21,7 @@ main {
const ubyte PAGE2 = ((SCREEN2 >> 6) & $F0) | ((CHARSET >> 10) & $0E) const ubyte PAGE2 = ((SCREEN2 >> 6) & $F0) | ((CHARSET >> 10) & $0E)
sub start() { sub start() {
c64.COLOR = 1 txt.color(1)
txt.print("creating charset...\n") txt.print("creating charset...\n")
makechar() makechar()

View File

@ -4,8 +4,47 @@
main { main {
sub start() { sub start() {
print_rom_floats_values()
c64.CHROUT('\n')
print_rom_floats()
c64.CHROUT('\n')
}
sub print_rom_floats() {
c64flt.FL_PIVAL=9.9999
c64flt.print_f(c64flt.FL_PIVAL)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_N32768)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_FONE)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_SQRHLF)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_SQRTWO)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_NEGHLF)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_LOG2)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_TENC)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_NZMIL)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_FHALF)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_LOGEB2)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_PIHALF)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_TWOPI)
c64.CHROUT('\n')
c64flt.print_f(c64flt.FL_FR4)
c64.CHROUT('\n')
}
sub print_rom_floats_values() {
; these are all floating point constants defined in the ROM so no allocation required ; these are all floating point constants defined in the ROM so no allocation required
; the compiler recognises these and will substitute the ROM values automatically
c64flt.print_f(3.141592653589793) c64flt.print_f(3.141592653589793)
c64.CHROUT('\n') c64.CHROUT('\n')

View File

@ -6,17 +6,21 @@ main {
const uword width = 40 const uword width = 40
const uword height = 25 const uword height = 25
sub start() { struct Ball {
float t float t
ubyte color ubyte color
}
sub start() {
Ball ball
repeat { repeat {
ubyte xx=(sin(t) * width/2.2) + width/2.0 as ubyte ubyte xx=(sin(ball.t) * width/2.1) + width/2.0 as ubyte
ubyte yy=(cos(t*1.1356) * height/2.2) + height/2.0 as ubyte ubyte yy=(cos(ball.t*1.1356) * height/2.1) + height/2.0 as ubyte
txt.setcc(xx, yy, 81, color) txt.setcc(xx, yy, 81, ball.color)
t += 0.08 ball.t += 0.08
color++ ball.color++
} }
} }
} }

View File

@ -280,12 +280,12 @@ waitkey:
sub drawBoard() { sub drawBoard() {
c64.CLEARSCR() c64.CLEARSCR()
c64.COLOR = 7 txt.color(7)
txt.plot(1,1) txt.plot(1,1)
txt.print("irmen's") txt.print("irmen's")
txt.plot(2,2) txt.plot(2,2)
txt.print("teh▁triz") txt.print("teh▁triz")
c64.COLOR = 5 txt.color(5)
txt.plot(6,4) txt.plot(6,4)
txt.print("hold:") txt.print("hold:")
txt.plot(2,22) txt.plot(2,22)
@ -296,10 +296,10 @@ waitkey:
txt.print("lines:") txt.print("lines:")
txt.plot(28,14) txt.plot(28,14)
txt.print("score:") txt.print("score:")
c64.COLOR = 12 txt.color(12)
txt.plot(27,18) txt.plot(27,18)
txt.print("controls:") txt.print("controls:")
c64.COLOR = 11 txt.color(11)
txt.plot(28,19) txt.plot(28,19)
txt.print(",/ move") txt.print(",/ move")
txt.plot(28,20) txt.plot(28,20)
@ -342,7 +342,7 @@ waitkey:
} }
sub drawScore() { sub drawScore() {
c64.COLOR=1 txt.color(1)
txt.plot(30,11) txt.plot(30,11)
txt.print_uw(lines) txt.print_uw(lines)
txt.plot(30,15) txt.plot(30,15)

View File

@ -3,16 +3,13 @@
main { main {
sub start() { sub start() {
ubyte b1 = 2
ubyte b2 = 13
ubyte b3 = 100
uword w1 = 2222 ;asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { ...}
uword w2 = 11 ; TODO dont cause name conflict if we define sub or sub with param 'color' or even a var 'color' later.
uword w3 = 33
; sub color(...) {}
; sub other(ubyte color) {} ; TODO don't cause name conflict
w1 %= (w2+w3)
txt.print_uw(w1)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
} }