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
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
- various data types other than just bytes (16-bit words, floats, strings)
- 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
- 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
- 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
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
Prog8 is mainly targeted at the Commodore-64 machine.
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!
*Two supported compiler targets* (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.
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
sty P8ZP_SCRATCH_W2+1
ldy P8ZP_SCRATCH_B1
jsr FREADUY
lda #0
jsr GIVAYF
_fac_to_mem ldx P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
jsr MOVMF
@ -74,7 +75,8 @@ stack_ub2float .proc
lda P8ESTACK_LO,x
stx P8ZP_SCRATCH_REG_X
tay
jsr FREADUY
lda #0
jsr GIVAYF
jmp push_fac1_as_result
.pend
@ -663,8 +665,8 @@ _largest_pos_float .byte 255,127,255,255,255 ; largest positive float
.pend
func_sum_f .proc
lda #<FL_ZERO
ldy #>FL_ZERO
lda #<ZERO
ldy #>ZERO
jsr MOVFM
jsr prog8_lib.pop_array_and_lengthmin1Y
stx P8ZP_SCRATCH_REG_X

View File

@ -6,12 +6,12 @@
%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
; ---- C64 basic and kernal ROM float constants and functions ----
@ -35,13 +35,11 @@ c64flt {
&float FL_TWOPI = $e2e5 ; 2 * PI
&float FL_FR4 = $e2ea ; .25
; 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: 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 $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
@ -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 $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 $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 $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) {
; ---- prints the floating point value (without a newline) using basic rom routines.
; ---- 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
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
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", ""
} ; ------ 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 {
asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- clear the character screen with the given fill character and character color.
asmsub clear_screen() {
%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)
%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).
; (assumes color matrix is at the default address)
%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) {
; ---- scroll the whole screen 1 character to the left
; 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
%asm {{
lda row
@ -524,7 +536,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte color) {
inc _colormod+2
+ lda char
_charmod sta $ffff ; modified
lda color
lda charcolor
_colormod sta $ffff ; modified
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
; the sixteen virtual 16-bit registers
&ubyte r0 = $02
&ubyte r0L = $02
&ubyte r0H = $03
&ubyte r1 = $04
&ubyte r1L = $04
&ubyte r1H = $05
&ubyte r2 = $06
&ubyte r2L = $06
&ubyte r2H = $07
&ubyte r3 = $08
&ubyte r3L = $08
&ubyte r3H = $09
&ubyte r4 = $0a
&ubyte r4L = $0a
&ubyte r4H = $0b
&ubyte r5 = $0c
&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
&uword r0 = $02
&uword r1 = $04
&uword r2 = $06
&uword r3 = $08
&uword r4 = $0a
&uword r5 = $0c
&uword r6 = $0e
&uword r7 = $10
&uword r8 = $12
&uword r9 = $14
&uword r10 = $16
&uword r11 = $18
&uword r12 = $1a
&uword r13 = $1c
&uword r14 = $1e
&uword r15 = $20
; 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
romsub $ff4a = close_all()
romsub $ff59 = lkupla()
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 $ff65 = pfkey()
romsub $ff6e = jsrfar()
@ -129,44 +138,59 @@ romsub $ff7d = primm()
; X16 additions
romsub $ff44 = macptr()
romsub $ff47 = enter_basic()
romsub $ff68 = mouse_config()
romsub $ff6b = mouse_get()
romsub $ff71 = mouse_scan()
romsub $ff53 = joystick_scan()
romsub $ff56 = joystick_get()
romsub $ff4d = clock_set_date_time()
romsub $ff50 = clock_get_date_time()
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc)
romsub $ff68 = mouse_config(ubyte shape @A, ubyte scale @X) clobbers (A, X, Y)
romsub $ff6b = mouse_get(ubyte zpdataptr @X) clobbers(A)
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff4d = clock_set_date_time() clobbers(A, X, Y) ; args: r0, r1, r2, r3L
romsub $ff50 = clock_get_date_time() clobbers(A) ; outout args: r0, r1, r2, r3L
; high level graphics & fonts
romsub $ff20 = GRAPH_init()
romsub $ff20 = GRAPH_init() ; uses vectors=r0
romsub $ff23 = GRAPH_clear()
romsub $ff26 = GRAPH_set_window()
romsub $ff29 = GRAPH_set_colors()
romsub $ff2c = GRAPH_draw_line()
romsub $ff2f = GRAPH_draw_rect()
romsub $ff32 = GRAPH_move_rect()
romsub $ff35 = GRAPH_draw_oval()
romsub $ff38 = GRAPH_draw_image()
romsub $ff3b = GRAPH_set_font()
romsub $ff3e = GRAPH_get_char_size()
romsub $ff41 = GRAPH_put_char()
romsub $ff26 = GRAPH_set_window() ; uses x=r0, y=r1, width=r2, height=r3
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y)
romsub $ff2c = GRAPH_draw_line() ; uses x1=r0, y1=r1, x2=r2, y2=r3
romsub $ff2f = GRAPH_draw_rect(ubyte fill @Pc) ; uses x=r0, y=r1, width=r2, height=r3, cornerradius=r4
romsub $ff32 = GRAPH_move_rect() ; uses sx=r0, sy=r1, tx=r2, ty=r3, width=r4, height=r5
romsub $ff35 = GRAPH_draw_oval(ubyte fill @Pc) ; uses x=r0, y=r1, width=r2, height=r3
romsub $ff38 = GRAPH_draw_image() ; uses x=r0, y=r1, ptr=r2, width=r3, height=r4
romsub $ff3b = GRAPH_set_font() ; uses ptr=r0
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(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()
romsub $fef3 = sprite_set_position()
romsub $fee4 = memory_fill()
romsub $fee7 = memory_copy()
romsub $feea = memory_crc()
romsub $feed = memory_decompress()
romsub $fedb = console_init()
romsub $fede = console_put_char()
romsub $fee1 = console_get_char()
romsub $fed8 = console_put_image()
romsub $fed5 = console_set_paging_message()
romsub $fed2 = kbdbuf_put()
romsub $fecf = entropy_get()
; misc
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 $fef3 = sprite_set_position(ubyte number @A) ; also uses x=r0 and y=r1
romsub $fee4 = memory_fill(ubyte value @A) ; uses address=r0, num_bytes=r1
romsub $fee7 = memory_copy() ; uses source=r0, target=r1, num_bytes=r2
romsub $feea = memory_crc() ; uses address=r0, num_bytes=r1 result->r2
romsub $feed = memory_decompress() ; uses input=r0, output=r1 result->r1
romsub $fedb = console_init() ; uses x=r0, y=r1, width=r2, height=r3
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc)
romsub $fee1 = console_get_char() -> ubyte @A
romsub $fed8 = console_put_image() ; uses ptr=r0, width=r1, height=r2
romsub $fed5 = console_set_paging_message() ; uses messageptr=r0
romsub $fed2 = kbdbuf_put(ubyte key @A)
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor()
@ -179,9 +203,8 @@ asmsub init_system() {
%asm {{
sei
cld
lda #0
sta $00
sta $01
stz $00
stz $01
jsr c64.IOINIT
jsr c64.RESTOR
jsr c64.CINT
@ -191,9 +214,6 @@ asmsub init_system() {
clc
clv
cli
lda #66
clc
jsr console_put_char
rts
}}
}

View File

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

View File

@ -59,7 +59,7 @@ multiply_words .proc
sty P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG_X
mult16 lda #$00
mult16 lda #0
sta result+2 ; clear upper bits of product
sta result+3
ldx #16 ; for all 16 bits...
@ -86,8 +86,40 @@ result .byte 0,0,0,0
.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
; TODO divmod_ub_asm doesn't work correctly. (remainder = ok, quotient = FAULTY)
; -- divide A by Y, result quotient in Y, remainder in A (unsigned)
; division by zero will result in quotient = 255 and remainder = original number
sty P8ZP_SCRATCH_REG
@ -109,6 +141,49 @@ divmod_ub_asm .proc
rts
.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
; -- 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

View File

@ -960,7 +960,7 @@ _result_maxuw .word 0
.pend
func_max_w .proc
lda #$00
lda #0
sta _result_maxw
lda #$80
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>) {
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 dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
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.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
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 {

View File

@ -118,6 +118,11 @@ internal class AstChecker(private val program: Program,
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position)
} else {
fun checkLoopRangeValues() {
}
when (loopvar.datatype) {
DataType.UBYTE -> {
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)
}
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)
if (!argIDt.isKnown)
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 regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun amountOfRtsInAsm(): Int = statements
.asSequence()

View File

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

View File

@ -2,6 +2,7 @@ package prog8.compiler.target.c64
import prog8.ast.Program
import prog8.compiler.*
import prog8.compiler.target.CpuType
import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.IMachineFloat
import prog8.parser.ModuleImporter
@ -12,7 +13,7 @@ import kotlin.math.pow
internal object C64MachineDefinition: IMachineDefinition {
override val cpu = "6502"
override val cpu = CpuType.CPU6502
// 5-byte cbm MFLPT format limitations:
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 floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
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(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768"
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.compiler.*
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.IAssemblyGenerator
import prog8.compiler.target.IAssemblyProgram
import prog8.compiler.target.c64.AssemblyProgram
@ -78,7 +79,11 @@ internal class AsmGen(private val program: Program,
private fun header() {
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("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
@ -546,7 +551,12 @@ internal class AsmGen(private val program: Program,
internal fun saveRegister(register: CpuRegister) {
when(register) {
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")
}
}
@ -554,7 +564,12 @@ internal class AsmGen(private val program: Program,
internal fun restoreRegister(register: CpuRegister) {
when(register) {
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")
}
}

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") &&
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 tenth = pair[9].value.trimStart()
@ -161,7 +162,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val fourteenth = pair[13].value.trimStart()
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)) {
// identical float init

View File

@ -4,6 +4,8 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.toHex
import prog8.functions.BuiltinFunctions
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.X -> {
// 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 -> {
// 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 -> {
// 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 -> {
when(expr.type) {
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")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
@ -83,9 +103,14 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out("""
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x""")
bmi +""")
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_b2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")

View File

@ -46,7 +46,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
DataType.ARRAY_B, DataType.ARRAY_UB -> {
if (stepsize==1 || stepsize==-1) {
// bytes, step 1 or -1
// bytes array, step 1 or -1
val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
@ -258,13 +258,6 @@ $endLabel inx""")
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) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
@ -615,4 +608,11 @@ $loopLabel""")
$endLabel""")
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
// 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 saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult()
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
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)
if(stmt.args.isNotEmpty()) {
@ -57,7 +57,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" jsr $subName")
if(saveX)
asmgen.out(" ldx P8ZP_SCRATCH_REG_X") // restore X again
asmgen.restoreRegister(CpuRegister.X)
}
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {

View File

@ -97,7 +97,8 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
else -> {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.out(" stx P8ZP_SCRATCH_REG_X | tax")
asmgen.saveRegister(CpuRegister.X)
asmgen.out(" tax")
when(elementDt) {
in ByteDatatypes -> {
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")
}
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.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.toHex
@ -706,7 +708,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// optimized case for float zero
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
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("""
lda #0
sta ${target.asmVarname}
sta ${target.asmVarname}+1

View File

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

View File

@ -2,6 +2,7 @@ package prog8.compiler.target.cx16
import prog8.ast.Program
import prog8.compiler.*
import prog8.compiler.target.CpuType
import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.parser.ModuleImporter
@ -9,7 +10,7 @@ import java.io.IOException
internal object CX16MachineDefinition: IMachineDefinition {
override val cpu = "65c02"
override val cpu = CpuType.CPU65c02
// 5-byte cbm MFLPT format limitations:
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 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) {
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
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
// TODO add 65C02 opcodes
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
"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",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
"inc", "inx", "iny", "jmp", "jsr",
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
"sec", "sed", "sei",
"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) {

View File

@ -318,11 +318,11 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
}
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 toCast = rangeTo.cast(targetDt)
if(!fromCast.isValid || !toCast.isValid)
return range
return null
val newStep =
if(stepLiteral!=null) {
@ -351,28 +351,32 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.BYTE -> {
if(rangeFrom.type!= DataType.BYTE) {
// attempt to translate the iterable into byte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.UWORD -> {
if(rangeFrom.type!= DataType.UWORD) {
// attempt to translate the iterable into uword values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.WORD -> {
if(rangeFrom.type!= DataType.WORD) {
// attempt to translate the iterable into word values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
else -> throw FatalAstException("invalid loopvar datatype $loopvar")

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``.
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
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
(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
`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.
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>`_.
@ -95,7 +96,6 @@ when compiled an ran on a C-64 you get this:
:alt: result when run on C-64
// TODO fix code example
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.
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::
**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 system ROM routines
The main target machine is the well-known Commodore-64, which is an example of this.
Another (preliminary) supported target machine is the `CommanderX16 <https://www.commanderx16.com/>`_ .
Currently there are two machines that are supported as compiler target (via the ``-target`` compiler argument):
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

View File

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

View File

@ -47,10 +47,10 @@ sub delay() {
sub print_notes(ubyte n1, ubyte n2) {
c64.CHROUT('\n')
txt.plot(n1/2, 24)
c64.COLOR=7
txt.color(7)
c64.CHROUT('Q')
txt.plot(n2/2, 24)
c64.COLOR=4
txt.color(4)
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 {
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
graphics.clear_screen()
graphics.clear_screen(1, 0)
draw_lines()
anglex-=500
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() {
cx16.r0L = 2020 - 1900
cx16.r0H = 8
cx16.r1L = 27
cx16.r1H = 19
cx16.r2L = 16
cx16.r2H = 0
cx16.r3L = 0
; %asm {{
; lda #$80
; jsr cx16.screen_set_mode
; }}
; cx16.r0=0
; cx16.GRAPH_init()
; %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.screen_set_charset(3, 0)
; c64.CHROUT(14) ; lowercase charset
cx16.screen_set_charset(3, 0) ; lowercase charset
repeat {
c64.CHROUT(19) ; HOME
@ -33,33 +49,33 @@ main {
}
sub print_date() {
txt.print_uw(1900 + cx16.r0L)
txt.print_uw(1900 + lsb(cx16.r0))
c64.CHROUT('-')
if cx16.r0H < 10
if msb(cx16.r0) < 10
c64.CHROUT('0')
txt.print_ub(cx16.r0H)
txt.print_ub(msb(cx16.r0))
c64.CHROUT('-')
if cx16.r1L < 10
if lsb(cx16.r1) < 10
c64.CHROUT('0')
txt.print_ub(cx16.r1L)
txt.print_ub(lsb(cx16.r1))
}
sub print_time() {
if cx16.r1H < 10
if msb(cx16.r1) < 10
c64.CHROUT('0')
txt.print_ub(cx16.r1H)
txt.print_ub(msb(cx16.r1))
c64.CHROUT(':')
if cx16.r2L < 10
if lsb(cx16.r2) < 10
c64.CHROUT('0')
txt.print_ub(cx16.r2L)
txt.print_ub(lsb(cx16.r2))
c64.CHROUT(':')
if cx16.r2H < 10
if msb(cx16.r2) < 10
c64.CHROUT('0')
txt.print_ub(cx16.r2H)
txt.print_ub(msb(cx16.r2))
c64.CHROUT('.')
if cx16.r3L < 10
if lsb(cx16.r3) < 10
c64.CHROUT('0')
txt.print_ub(cx16.r3L)
txt.print_ub(lsb(cx16.r3))
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -4,8 +4,47 @@
main {
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
; the compiler recognises these and will substitute the ROM values automatically
c64flt.print_f(3.141592653589793)
c64.CHROUT('\n')

View File

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

View File

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

View File

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