Compare commits

...

36 Commits
v5.3 ... v5.4

Author SHA1 Message Date
52e8a44517 version 5.4 2020-12-15 22:59:02 +01:00
59f33658ad removed the rom path argument for launching the x16emu which made it fail on a non-Linux system 2020-12-15 22:51:10 +01:00
e0315bffdc decided not to change mkword() again, added note to docs about argument order 2020-12-15 22:25:06 +01:00
cd28d0c0e0 tweak 2020-12-14 21:57:16 +01:00
0baa2c8b23 fix oversight in binexpr operand swap that could result in suboptimal code 2020-12-14 21:37:40 +01:00
4977d1fbd5 bit shift expressions are "expanded" to the target value's datatype, now also for subroutine arguments.
implemented word bit shifts by variable number of bits.
2020-12-14 20:44:48 +01:00
3b7a92f1b4 adding strcopy() 2020-12-14 17:26:17 +01:00
f6920172dd image viewer tweaks 2020-12-14 15:36:15 +01:00
93bfc8f5f4 rename 2020-12-14 14:30:55 +01:00
39b7655264 imageviewer is now a single program 2020-12-14 14:30:18 +01:00
8b75ceb412 diskio.list_files now has a bigger buffer to store more filenames (around 30-40 max) 2020-12-14 14:29:42 +01:00
c39fc4010d textio.clear_screen() now uses kernal routine to clear the text screen, this also resets the cursor to top left. 2020-12-14 14:28:53 +01:00
8df778a515 fixed crash when importing modules from the same directory as the main program 2020-12-14 13:14:12 +01:00
5134ea76bf added bmp viewer 2020-12-14 02:12:26 +01:00
3ba37df29d added iff viewer 2020-12-13 19:42:30 +01:00
e221d674d9 pcxviewer done 2020-12-13 01:32:03 +01:00
251f947293 fixed parameter signature for FB_set_8_pixels_opaque() (docs are wrong) 2020-12-12 03:32:01 +01:00
41e1e1cbb0 adding pcxviewer 2020-12-12 02:40:54 +01:00
da1bc351d2 koalaviewer auto disk detect 2020-12-11 23:32:47 +01:00
43c0afdea0 fixed strlen() to work on arguments other than just a variable 2020-12-11 23:32:29 +01:00
add5bfa2ec koalaviewer scans directory for *.koa 2020-12-11 23:00:58 +01:00
34babfb5de added diskio.list_files(). ci-viewer now loads all *.ci files it finds. 2020-12-11 22:36:14 +01:00
4f6c45c86c incremental file loading 2020-12-11 21:05:03 +01:00
e6220a464c using progend() to maximize amount of mem available to load image 2020-12-10 23:52:30 +01:00
8dcd49934a added progend() builtin function 2020-12-10 23:33:45 +01:00
bedc3bdb56 allow bit shifting to be as large as the target variable's datatype 2020-12-10 22:49:27 +01:00
83ceb0fde9 optimize various simple cases for '**' (pow) like 2**x => bitshift 2020-12-10 22:37:12 +01:00
1d299c56e0 fix float '**' (pow) on cx16 2020-12-10 22:19:07 +01:00
0d735c2ccc workaround for FB_set_pixels bug 2020-12-10 21:51:32 +01:00
4094f89d4a not a bug 2020-12-10 03:22:43 +01:00
cf1e8b194a fix compiler crash for expressions of the form x = x and y (the logical booleans, not the bitwise) 2020-12-10 03:12:32 +01:00
74e5644f55 working on CI viewer 2020-12-10 03:00:37 +01:00
b5dc5fc615 added iterative file loading to diskio 2020-12-10 00:58:59 +01:00
7a7270d769 adding CI (CommanderX16 Image) file viewer 2020-12-10 00:03:47 +01:00
7549ddcd2b added TODOs for missing assignments 2020-12-10 00:03:20 +01:00
08f0303178 diskio status() now returns the status string instead of printing it 2020-12-10 00:02:21 +01:00
45 changed files with 1438 additions and 262 deletions

View File

@ -269,6 +269,22 @@ asmsub reset_system() {
}} }}
} }
sub wait(uword jiffies) {
uword current_time = 0
c64.SETTIM(0,0,0)
while current_time < jiffies {
; read clock
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta current_time
stx current_time+1
ldx P8ZP_SCRATCH_REG
}}
}
}
asmsub disable_runstop_and_charsetswitch() { asmsub disable_runstop_and_charsetswitch() {
%asm {{ %asm {{
lda #$80 lda #$80

View File

@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 25
sub clear_screen() { sub clear_screen() {
clear_screenchars(' ') txt.chrout(147)
} }
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {

View File

@ -64,6 +64,7 @@ romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += sign
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1) romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1 romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
; note: there is no FPWR() on the Cx16
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1 romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** 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 $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator

View File

@ -220,7 +220,7 @@ romsub $ff08 = FB_get_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y) romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1 romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y) romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses mask=r0L romsub $ff14 = FB_set_8_pixels_opaque(ubyte mask @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses r0L=pattern
romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1 romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1
romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1 romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4 romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
@ -245,7 +245,7 @@ romsub $fecc = monitor() clobbers(A,X,Y)
; ---- end of kernal routines ---- ; ---- end of kernal routines ----
; ---- utilities ----- ; ---- utilities -----
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A { asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
; -- get a byte from VERA's video memory ; -- get a byte from VERA's video memory
; note: inefficient when reading multiple sequential bytes! ; note: inefficient when reading multiple sequential bytes!
%asm {{ %asm {{
@ -257,9 +257,9 @@ romsub $fecc = monitor() clobbers(A,X,Y)
lda cx16.VERA_DATA0 lda cx16.VERA_DATA0
rts rts
}} }}
} }
sub vpoke(ubyte bank, uword address, ubyte value) { sub vpoke(ubyte bank, uword address, ubyte value) {
; -- write a single byte to VERA's video memory ; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
%asm {{ %asm {{
@ -275,7 +275,59 @@ romsub $fecc = monitor() clobbers(A,X,Y)
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
rts rts
}} }}
}
sub FB_set_pixels_from_buf(uword buffer, uword count) {
%asm {{
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
; See https://github.com/commanderx16/x16-rom/issues/179
phx
lda buffer
ldy buffer+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
jsr _pixels
plx
rts
_pixels lda count+1
beq +
ldx #0
- jsr _loop
inc P8ZP_SCRATCH_W1+1
dec count+1
bne -
+ ldx count
_loop ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta cx16.VERA_DATA0
iny
dex
bne -
rts
}}
}
sub wait(uword jiffies) {
uword current_time = 0
c64.SETTIM(0,0,0)
while current_time < jiffies {
; read clock
%asm {{
phx
jsr c64.RDTIM
sta current_time
stx current_time+1
plx
}}
} }
}
; ---- system stuff ----- ; ---- system stuff -----

View File

@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 60
sub clear_screen() { sub clear_screen() {
clear_screenchars(' ') txt.chrout(147)
} }
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {

View File

@ -7,14 +7,14 @@ diskio {
sub directory(ubyte drivenumber) -> ubyte { sub directory(ubyte drivenumber) -> ubyte {
; -- Shows the directory contents of disk drive 8-11 (provide as argument). Returns success flag. ; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
c64.SETNAM(1, "$") c64.SETNAM(1, "$")
c64.SETLFS(1, drivenumber, 0) c64.SETLFS(13, drivenumber, 0)
void c64.OPEN() ; open 1,8,0,"$" void c64.OPEN() ; open 13,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void c64.CHKIN(1) ; use #1 as input channel void c64.CHKIN(13) ; use #13 as input channel
if_cs if_cs
goto io_error goto io_error
@ -39,14 +39,14 @@ diskio {
void c64.CHRIN() void c64.CHRIN()
status = c64.READST() status = c64.READST()
void c64.STOP() void c64.STOP()
if_nz if_z
break break
} }
io_error: io_error:
status = c64.READST() status = c64.READST()
c64.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(1) c64.CLOSE(13)
if status and status != 64 { ; 64=end of file if status and status != 64 { ; 64=end of file
txt.print("\ni/o error, status: ") txt.print("\ni/o error, status: ")
@ -59,33 +59,63 @@ io_error:
} }
; internal variables for the iterative file lister ; internal variables for the iterative file lister / loader
ubyte list_suffixmatch ubyte list_suffixmatch
ubyte list_in_progress = false
ubyte list_pattern_size ubyte list_pattern_size
ubyte list_skip_disk_name ubyte list_skip_disk_name
uword list_pattern uword list_pattern
uword list_blocks uword list_blocks
str list_filename = "????????????????" ubyte iteration_in_progress = false
str list_filename = "?" * 32
; ----- get a list of files (uses iteration functions internally -----
sub list_files(ubyte drivenumber, uword pattern, ubyte suffixmatch, uword name_ptrs, ubyte max_names) -> ubyte {
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
ubyte[256] names_buffer
ubyte[256] names_buffer1 ; to store a bit more names
uword buf_ptr = &names_buffer
ubyte files_found = 0
if lf_start_list(drivenumber, pattern, suffixmatch) {
while lf_next_entry() {
@(name_ptrs) = lsb(buf_ptr)
name_ptrs++
@(name_ptrs) = msb(buf_ptr)
name_ptrs++
buf_ptr += strcopy(diskio.list_filename, buf_ptr) + 1
files_found++
if buf_ptr - &names_buffer > (len(names_buffer) + len(names_buffer1) - 18)
break
if files_found == max_names
break
}
lf_end_list()
}
return files_found
}
; ----- iterative file lister functions -----
sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte { sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte {
; -- start an iterative file listing with optional prefix or suffix name matching. ; -- start an iterative file listing with optional prefix or suffix name matching.
; note: only a single iteration loop can be active at a time!
lf_end_list() lf_end_list()
list_pattern = pattern list_pattern = pattern
list_suffixmatch = suffixmatch list_suffixmatch = suffixmatch
list_skip_disk_name = true list_skip_disk_name = true
list_in_progress = true iteration_in_progress = true
if pattern==0 if pattern==0
list_pattern_size = 0 list_pattern_size = 0
else else
list_pattern_size = strlen(pattern) list_pattern_size = strlen(pattern)
c64.SETNAM(1, "$") c64.SETNAM(1, "$")
c64.SETLFS(1, drivenumber, 0) c64.SETLFS(12, drivenumber, 0)
void c64.OPEN() ; open 1,8,0,"$" void c64.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void c64.CHKIN(1) ; use #1 as input channel void c64.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
@ -106,13 +136,19 @@ io_error:
; results will be found in list_blocks and list_filename. ; results will be found in list_blocks and list_filename.
; if it returns false though, there are no more entries (or an error occurred). ; if it returns false though, there are no more entries (or an error occurred).
if not list_in_progress if not iteration_in_progress
return false return false
while not c64.READST() { repeat {
void c64.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename uword nameptr = &list_filename
ubyte blocks_lsb = c64.CHRIN() ubyte blocks_lsb = c64.CHRIN()
ubyte blocks_msb = c64.CHRIN() ubyte blocks_msb = c64.CHRIN()
if c64.READST()
goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb) list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first " ; read until the filename starts after the first "
@ -163,16 +199,67 @@ close_end:
sub lf_end_list() { sub lf_end_list() {
; -- end an iterative file listing session (close channels). ; -- end an iterative file listing session (close channels).
if list_in_progress { if iteration_in_progress {
c64.CLRCHN() c64.CLRCHN()
c64.CLOSE(1) c64.CLOSE(12)
list_in_progress = false iteration_in_progress = false
} }
} }
sub status(ubyte drivenumber) { ; ----- iterative file loader functions -----
; -- display the disk drive's current status message
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time!
f_close()
c64.SETNAM(strlen(filenameptr), filenameptr)
c64.SETLFS(11, drivenumber, 0)
void c64.OPEN() ; open 11,8,0,"filename"
if_cc {
iteration_in_progress = true
void c64.CHKIN(11) ; use #11 as input channel
if_cc
return true
}
f_close()
return false
}
sub f_read(uword bufferpointer, uword buffersize) -> uword {
if not iteration_in_progress
return 0
uword actual = 0
void c64.CHKIN(11) ; use #11 as input channel again
repeat buffersize {
ubyte data = c64.CHRIN()
@(bufferpointer) = data
bufferpointer++
actual++
ubyte status = c64.READST()
if status==64
f_close() ; end of file, close it
if status
return actual
}
return actual
}
sub f_close() {
; -- end an iterative file loading session (close channels).
if iteration_in_progress {
c64.CLRCHN()
c64.CLOSE(11)
iteration_in_progress = false
}
}
sub status(ubyte drivenumber) -> uword {
; -- retrieve the disk drive's current status message
uword messageptr = &filename
c64.SETNAM(0, filename) c64.SETNAM(0, filename)
c64.SETLFS(15, drivenumber, 15) c64.SETLFS(15, drivenumber, 15)
void c64.OPEN() ; open 15,8,15 void c64.OPEN() ; open 15,8,15
@ -182,12 +269,16 @@ close_end:
if_cs if_cs
goto io_error goto io_error
while not c64.READST() while not c64.READST() {
txt.chrout(c64.CHRIN()) @(messageptr) = c64.CHRIN()
messageptr++
}
io_error: io_error:
@(messageptr) = 0
c64.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(15) c64.CLOSE(15)
return filename
} }

View File

@ -1352,6 +1352,33 @@ shift_left_w_3 .proc
jmp shift_left_w_7._shift3 jmp shift_left_w_7._shift3
.pend .pend
shift_left_w .proc
; -- variable number of shifts left
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
dey
bne _shift
rts
.pend
shift_right_uw .proc
; -- uword variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lsr P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
shift_right_uw_7 .proc shift_right_uw_7 .proc
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
@ -1482,6 +1509,21 @@ shift_right_w_3 .proc
.pend .pend
shift_right_w .proc
; -- signed word variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lda P8ESTACK_HI+1,x
asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
; support for bit shifting that is too large to be unrolled: ; support for bit shifting that is too large to be unrolled:

View File

@ -1346,3 +1346,24 @@ func_strcmp_stack .proc
dex dex
rts rts
.pend .pend
func_strcopy .proc
lda _arg_to
ldy _arg_to+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_from
ldy _arg_from+1
jsr strcpy
tya
rts
_arg_from .word 0
_arg_to .word 0
.pend
func_strcopy_to_stack .proc
jsr func_strcopy
sta P8ESTACK_LO,x
dex
rts
.pend

View File

@ -993,6 +993,7 @@ _arg_index .byte 0
strcpy .proc strcpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1) ; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
; it is assumed the target string is large enough. ; it is assumed the target string is large enough.
; returns the length of the string that was copied in Y.
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_W2+1
ldy #$ff ldy #$ff

View File

@ -1 +1 @@
5.3 5.4

View File

@ -4,6 +4,7 @@ import prog8.ast.*
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() { internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
@ -91,6 +92,55 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
} }
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// when using a simple bit shift and assigning it to a variable of a different type,
// try to make the bit shifting 'wide enough' to fall into the variable's type.
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
if(expr.operator=="<<" || expr.operator==">>") {
val leftDt = expr.left.inferType(program)
when (parent) {
is Assignment -> {
val targetDt = parent.target.inferType(program)
if(leftDt != targetDt) {
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is VarDecl -> {
if(!leftDt.istype(parent.datatype)) {
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is IFunctionCall -> {
val argnum = parent.args.indexOf(expr)
when (val callee = parent.target.targetStatement(program.namespace)) {
is Subroutine -> {
val paramType = callee.parameters[argnum].type
if(leftDt isAssignableTo paramType) {
val cast = TypecastExpression(expr.left, paramType, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is BuiltinFunctionStatementPlaceholder -> {
val func = BuiltinFunctions.getValue(callee.name)
val paramTypes = func.parameters[argnum].possibleDatatypes
for(type in paramTypes) {
if(leftDt isAssignableTo type) {
val cast = TypecastExpression(expr.left, type, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
}
else -> throw FatalAstException("weird callee")
}
}
else -> return noModifications
}
}
return noModifications
}
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> { private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
val modifications = mutableListOf<IAstModification>() val modifications = mutableListOf<IAstModification>()
val subroutine = expr.definingSubroutine()!! val subroutine = expr.definingSubroutine()!!

View File

@ -160,6 +160,7 @@ internal class AsmGen(private val program: Program,
val floatvalue = flt.key val floatvalue = flt.key
out("${flt.value}\t.byte $floatFill ; float $floatvalue") out("${flt.value}\t.byte $floatFill ; float $floatvalue")
} }
out("prog8_program_end\t; end of program label for progend()")
} }
private fun block2asm(block: Block) { private fun block2asm(block: Block) {

View File

@ -5,10 +5,7 @@ import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.ArrayIndex import prog8.ast.statements.*
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Subroutine
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target import prog8.compiler.target.Cx16Target
@ -84,6 +81,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"set_irqd" -> asmgen.out(" sei") "set_irqd" -> asmgen.out(" sei")
"strlen" -> funcStrlen(fcall, resultToStack) "strlen" -> funcStrlen(fcall, resultToStack)
"strcmp" -> funcStrcmp(fcall, func, resultToStack, scope) "strcmp" -> funcStrcmp(fcall, func, resultToStack, scope)
"strcopy" -> {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_strcopy_to_stack")
else
asmgen.out(" jsr prog8_lib.func_strcopy")
}
"memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope) "memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope)
"substr", "leftstr", "rightstr" -> { "substr", "leftstr", "rightstr" -> {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
@ -93,6 +97,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
asmgen.out(" jmp prog8_lib.func_exit") asmgen.out(" jmp prog8_lib.func_exit")
} }
"progend" -> {
if(resultToStack)
asmgen.out("""
lda #<prog8_program_end
sta P8ESTACK_LO,x
lda #>prog8_program_end
sta P8ESTACK_HI,x
dex""")
else
asmgen.out(" lda #<prog8_program_end | ldy #>prog8_program_end")
}
else -> TODO("missing asmgen for builtin func ${func.name}") else -> TODO("missing asmgen for builtin func ${func.name}")
} }
} }
@ -532,14 +547,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) { private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference) if (fcall.args[0] is IdentifierReference) {
val type = fcall.args[0].inferType(program) // use the address of the variable
when { val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name") val type = fcall.args[0].inferType(program)
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1") when {
else -> throw AssemblyError("strlen requires str or uword arg") type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
else -> throw AssemblyError("strlen requires str or uword arg")
}
} }
if(resultToStack) else {
// use the expression value as address of the string
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
}
if (resultToStack)
asmgen.out(" jsr prog8_lib.func_strlen_stack") asmgen.out(" jsr prog8_lib.func_strlen_stack")
else else
asmgen.out(" jsr prog8_lib.func_strlen_into_A") asmgen.out(" jsr prog8_lib.func_strlen_into_A")

View File

@ -1947,24 +1947,29 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
} }
} }
private fun translateBinaryOperatorWords(operator: String, types: DataType) { private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
when(operator) { when(operator) {
"**" -> throw AssemblyError("** operator requires floats") "**" -> throw AssemblyError("** operator requires floats")
"*" -> asmgen.out(" jsr prog8_lib.mul_word") "*" -> asmgen.out(" jsr prog8_lib.mul_word")
"/" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w") "/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"%" -> { "%" -> {
if(types==DataType.WORD) if(dt==DataType.WORD)
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_uw") asmgen.out(" jsr prog8_lib.remainder_uw")
} }
"+" -> asmgen.out(" jsr prog8_lib.add_w") "+" -> asmgen.out(" jsr prog8_lib.add_w")
"-" -> asmgen.out(" jsr prog8_lib.sub_w") "-" -> asmgen.out(" jsr prog8_lib.sub_w")
"<<" -> throw AssemblyError("<< should not operate via stack") "<<" -> asmgen.out(" jsr math.shift_left_w")
">>" -> throw AssemblyError(">> should not operate via stack") ">>" -> {
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w") if(dt==DataType.UWORD)
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w") asmgen.out(" jsr math.shift_right_uw")
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w") else
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w") asmgen.out(" jsr math.shift_right_w")
}
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w") "==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w") "!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w") "^" -> asmgen.out(" jsr prog8_lib.bitxor_w")

View File

@ -20,7 +20,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
translateFunctionCall(stmt, preserveStatusRegisterAfterCall) translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
// functioncalls no longer return results on the stack, so simply ignore the results in the registers // functioncalls no longer return results on the stack, so simply ignore the results in the registers
if(preserveStatusRegisterAfterCall) if(preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call") asmgen.out(" plp") // restore status flags from call
} }
@ -69,7 +69,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" jsr $subName") asmgen.out(" jsr $subName")
if(preserveStatusRegisterAfterCall) { if(preserveStatusRegisterAfterCall) {
asmgen.out(" php\t; save status flags from call") asmgen.out(" php") // save status flags from call
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression) // note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
// must take care of popping this value again at the end! // must take care of popping this value again at the end!
} }

View File

@ -183,7 +183,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
} }
if (preserveStatusRegisterAfterCall) if (preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call") asmgen.out(" plp") // restore status flags from call
} }
is BuiltinFunctionStatementPlaceholder -> { is BuiltinFunctionStatementPlaceholder -> {
val signature = BuiltinFunctions.getValue(sub.name) val signature = BuiltinFunctions.getValue(sub.name)

View File

@ -7,6 +7,7 @@ import prog8.ast.statements.Subroutine
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.CpuType
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex import prog8.compiler.toHex
@ -317,9 +318,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne - bne -
+""") +""")
} }
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1") "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1") "|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1") "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
if(ptrOnZp) if(ptrOnZp)
@ -357,9 +359,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne - bne -
+""") +""")
} }
"&" -> asmgen.out(" and $otherName") "&", "and" -> asmgen.out(" and $otherName")
"^" -> asmgen.out(" eor $otherName") "|", "or" -> asmgen.out(" ora $otherName")
"|" -> asmgen.out(" ora $otherName") "^", "xor" -> asmgen.out(" eor $otherName")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
if(ptrOnZp) if(ptrOnZp)
@ -438,7 +441,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
} }
"&" -> { "&", "and" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value") asmgen.out(" and #$value")
if(ptrOnZp) if(ptrOnZp)
@ -446,15 +449,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
"^" -> { "|", "or" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"|" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value") asmgen.out(" ora #$value")
if(ptrOnZp) if(ptrOnZp)
@ -462,6 +457,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
"^", "xor" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -525,18 +529,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&" -> { "&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
} }
"^" -> { "|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name") asmgen.out(" ora $name | sta $name")
} }
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -591,27 +596,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&" -> asmgen.out(" lda $name | and $otherName | sta $name") "&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name") "|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name") "^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
"and" -> asmgen.out(""" in comparisonOperators -> TODO("in-place modification for $operator")
lda $name
and $otherName
beq +
lda #1
+ sta $name""")
"or" -> asmgen.out("""
lda $name
ora $otherName
beq +
lda #1
+ sta $name""")
"xor" -> asmgen.out("""
lda $name
eor $otherName
beq +
lda #1
+ sta $name""")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -680,9 +668,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
"&" -> asmgen.out(" lda $name | and #$value | sta $name") "&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
"^" -> asmgen.out(" lda $name | eor #$value | sta $name") "|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
"|" -> asmgen.out(" lda $name | ora #$value | sta $name") "^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -949,7 +938,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
"&" -> { "&", "and" -> {
when { when {
value == 0 -> { value == 0 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
@ -974,15 +963,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
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")
} }
} }
"^" -> { "|", "or" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
}
}
"|" -> {
when { when {
value == 0 -> {} value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1") value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
@ -990,6 +971,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1") else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
} }
} }
"^", "xor" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
}
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1100,7 +1090,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&" -> { "&", "and" -> {
asmgen.out(" lda $otherName | and $name | sta $name") asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
@ -1109,8 +1099,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
} }
} }
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name") "|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name") "^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1181,9 +1172,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""") """)
} }
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") "&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1") "|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") "^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1356,7 +1348,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne - bne -
+""") +""")
} }
"&" -> { "&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
@ -1366,14 +1358,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
} }
} }
"^" -> { "|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name") asmgen.out(" ora $name | sta $name")
} }
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1402,18 +1395,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
remainderVarByWordInAY() remainderVarByWordInAY()
} }
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> { "&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1") asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
} }
"^" -> { "|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1") asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
} }
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1461,6 +1455,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV jsr floats.FDIV
""") """)
} }
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator") else -> throw AssemblyError("invalid operator for in-place float modification $operator")
} }
asmgen.out(""" asmgen.out("""
@ -1480,14 +1475,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegister(CpuRegister.X, false, scope) asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) { when (operator) {
"**" -> { "**" -> {
asmgen.out(""" if(CompilationTarget.instance is Cx16Target) {
lda #<$name // cx16 doesn't have FPWR() only FPWRT()
ldy #>$name asmgen.out("""
jsr floats.CONUPK lda #<$name
lda #<$otherName ldy #>$name
ldy #>$otherName jsr floats.CONUPK
jsr floats.FPWR lda #<$otherName
""") ldy #>$otherName
jsr floats.MOVFM
jsr floats.FPWRT
""")
} else
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.FPWR
""")
} }
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
@ -1529,6 +1536,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV jsr floats.FDIV
""") """)
} }
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator") else -> throw AssemblyError("invalid operator for in-place float modification $operator")
} }
// store Fac1 back into memory // store Fac1 back into memory
@ -1545,14 +1553,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegister(CpuRegister.X, false, scope) asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) { when (operator) {
"**" -> { "**" -> {
asmgen.out(""" if(CompilationTarget.instance is Cx16Target) {
lda #<$name // cx16 doesn't have FPWR() only FPWRT()
ldy #>$name asmgen.out("""
jsr floats.CONUPK lda #<$name
lda #<$constValueName ldy #>$name
ldy #>$constValueName jsr floats.CONUPK
jsr floats.FPWR lda #<$constValueName
""") ldy #>$constValueName
jsr floats.MOVFM
jsr floats.FPWRT
""")
} else
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.FPWR
""")
} }
"+" -> { "+" -> {
if (value == 0.0) if (value == 0.0)
@ -1601,6 +1621,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV jsr floats.FDIV
""") """)
} }
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator") else -> throw AssemblyError("invalid operator for in-place float modification $operator")
} }
// store Fac1 back into memory // store Fac1 back into memory

View File

@ -38,8 +38,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
override fun launchEmulator(programName: String) { override fun launchEmulator(programName: String) {
for(emulator in listOf("x16emu")) { for(emulator in listOf("x16emu")) {
println("\nStarting Commander X16 emulator $emulator...") println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2", val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
"-run", "-prg", programName + ".prg")
val processb = ProcessBuilder(cmdline).inheritIO() val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process val process: Process
try { try {

View File

@ -141,6 +141,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("set_irqd" , false, emptyList(), null), FSignature("set_irqd" , false, emptyList(), null),
FSignature("clear_irqd" , false, emptyList(), null), FSignature("clear_irqd" , false, emptyList(), null),
FSignature("read_flags" , false, emptyList(), DataType.UBYTE), FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
FSignature("progend" , true, emptyList(), DataType.UWORD),
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null), FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
FSignature("memcopy" , false, listOf( FSignature("memcopy" , false, listOf(
FParam("from", IterableDatatypes + DataType.UWORD), FParam("from", IterableDatatypes + DataType.UWORD),
@ -155,6 +156,7 @@ private val functionSignatures: List<FSignature> = listOf(
FParam("numwords", setOf(DataType.UWORD)), FParam("numwords", setOf(DataType.UWORD)),
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null), FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen), FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
FSignature("strcopy" , false, listOf(FParam("from", IterableDatatypes + DataType.UWORD), FParam("to", IterableDatatypes + DataType.UWORD)), DataType.UBYTE),
FSignature("substr" , false, listOf( FSignature("substr" , false, listOf(
FParam("source", IterableDatatypes + DataType.UWORD), FParam("source", IterableDatatypes + DataType.UWORD),
FParam("target", IterableDatatypes + DataType.UWORD), FParam("target", IterableDatatypes + DataType.UWORD),
@ -353,7 +355,7 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
val argument=args[0] val argument=args[0]
if(argument is StringLiteralValue) if(argument is StringLiteralValue)
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position) return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace) val vardecl = (argument as? IdentifierReference)?.targetVarDecl(program.namespace)
if(vardecl!=null) { if(vardecl!=null) {
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD) if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
throw SyntaxError("strlen must have string argument", position) throw SyntaxError("strlen must have string argument", position)

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification import prog8.ast.processing.IAstModification
import prog8.ast.statements.* import prog8.ast.statements.*
import kotlin.math.pow
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@ -97,6 +98,43 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> { override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val leftconst = expr.left.constValue(program) val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program) val rightconst = expr.right.constValue(program)
val modifications = mutableListOf<IAstModification>()
if(expr.operator == "**" && leftconst!=null) {
// optimize various simple cases of ** :
// optimize away 1 ** x into just 1 and 0 ** x into just 0
// optimize 2 ** x into (1<<x) if both operands are integer.
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
when (leftconst.number.toDouble()) {
0.0 -> {
val value = NumericLiteralValue(leftDt, 0, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
1.0 -> {
val value = NumericLiteralValue(leftDt, 1, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
2.0 -> {
if(rightconst!=null) {
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
} else {
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val targetDt =
when (parent) {
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
is VarDecl -> parent.datatype
else -> leftDt
}
val one = NumericLiteralValue(targetDt, 1, expr.position)
val shift = BinaryExpression(one, "<<", expr.right, expr.position)
modifications += IAstModification.ReplaceNode(expr, shift, parent)
}
}
}
}
}
val subExpr: BinaryExpression? = when { val subExpr: BinaryExpression? = when {
leftconst!=null -> expr.right as? BinaryExpression leftconst!=null -> expr.right as? BinaryExpression
@ -111,7 +149,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
val change = groupTwoConstsTogether(expr, subExpr, val change = groupTwoConstsTogether(expr, subExpr,
leftconst != null, rightconst != null, leftconst != null, rightconst != null,
subleftconst != null, subrightconst != null) subleftconst != null, subrightconst != null)
return change?.let { listOf(it) } ?: noModifications if(change!=null)
modifications += change
} }
} }
@ -119,10 +158,10 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(leftconst != null && rightconst != null) { if(leftconst != null && rightconst != null) {
val evaluator = ConstExprEvaluator() val evaluator = ConstExprEvaluator()
val result = evaluator.evaluate(leftconst, expr.operator, rightconst) val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
return listOf(IAstModification.ReplaceNode(expr, result, parent)) modifications += IAstModification.ReplaceNode(expr, result, parent)
} }
return noModifications return modifications
} }
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> { override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {

View File

@ -6,6 +6,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification import prog8.ast.processing.IAstModification
import prog8.ast.statements.Assignment
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow import kotlin.math.pow
@ -98,6 +99,12 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal != null && expr.operator in associativeOperators && rightVal == null) if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
return listOf(IAstModification.SwapOperands(expr)) return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in associativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target))
return listOf(IAstModification.SwapOperands(expr))
}
// X + (-A) --> X - A // X + (-A) --> X - A
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") { if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
return listOf(IAstModification.ReplaceNode( return listOf(IAstModification.ReplaceNode(
@ -338,7 +345,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal == null && rightVal == null) if (leftVal == null && rightVal == null)
return null return null
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal) val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) { if (rightVal2 != null) {
// right value is a constant, see if we can optimize // right value is a constant, see if we can optimize
val rightConst: NumericLiteralValue = rightVal2 val rightConst: NumericLiteralValue = rightVal2
@ -562,7 +569,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal == null && rightVal == null) if (leftVal == null && rightVal == null)
return null return null
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal) val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) { if (rightVal2 != null) {
// right value is a constant, see if we can optimize // right value is a constant, see if we can optimize
val leftValue: Expression = expr2.left val leftValue: Expression = expr2.left
@ -682,17 +689,17 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
return null return null
} }
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr { private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
if (expr.operator in associativeOperators && leftVal != null) { if (expr.operator in associativeOperators && leftVal != null) {
// swap left and right so that right is always the constant // swap left and right so that right is always the constant
val tmp = expr.left val tmp = expr.left
expr.left = expr.right expr.left = expr.right
expr.right = tmp expr.right = tmp
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal) return BinExprWithConstants(expr, expr.right.constValue(program), leftVal)
} }
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program)) return BinExprWithConstants(expr, leftVal, expr.right.constValue(program))
} }
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?) private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
} }

View File

@ -323,7 +323,7 @@ internal class StatementOptimizer(private val program: Program,
val op1 = binExpr.operator val op1 = binExpr.operator
val op2 = rExpr.operator val op2 = rExpr.operator
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) { if(rExpr.left is NumericLiteralValue && op2 in associativeOperators) {
// associative operator, make sure the constant numeric value is second (right) // associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr)) return listOf(IAstModification.SwapOperands(rExpr))
} }

View File

@ -102,7 +102,7 @@ internal class ModuleImporter {
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path { private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
val fileName = "$name.p8" val fileName = "$name.p8"
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent) val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent ?: Path.of("."))
val propPath = System.getProperty("prog8.libdir") val propPath = System.getProperty("prog8.libdir")
if(propPath!=null) if(propPath!=null)

View File

@ -814,6 +814,12 @@ substr(source, target, start, length)
Also, you have to make sure yourself that start and length are within bounds of the strings. Also, you have to make sure yourself that start and length are within bounds of the strings.
Modifies in-place, doesn't return a value (so can't be used in an expression). Modifies in-place, doesn't return a value (so can't be used in an expression).
strcopy(from, to)
Copy a string to another, overwriting that one. Returns the length of the string that was copied.
Often you don't have to call this explicitly and can just write ``string1 = string2``
but this function is useful if you're dealing with addresses for instance.
Miscellaneous Miscellaneous
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
exit(returncode) exit(returncode)
@ -830,6 +836,12 @@ mkword(msb, lsb)
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting. Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
So mkword($80, $22) results in $8022. So mkword($80, $22) results in $8022.
.. note::
The arguments to the mkword() function are in 'natural' order that is first the msb then the lsb.
Don't get confused by how the system actually stores this 16-bit word value in memory (which is
in little-endian format, so lsb first then msb)
rnd() rnd()
returns a pseudo-random byte from 0..255 returns a pseudo-random byte from 0..255
@ -894,6 +906,10 @@ set_irqd() / clear_irqd()
swap(x, y) swap(x, y)
Swap the values of numerical variables (or memory locations) x and y in a fast way. Swap the values of numerical variables (or memory locations) x and y in a fast way.
progend()
Returns the last address of the program in memory + 1.
Can be used to load dynamic data after the program, instead of hardcoding something.
Library routines Library routines
---------------- ----------------

View File

@ -324,6 +324,10 @@ main {
txt.print(result) txt.print(result)
txt.chrout('\n') txt.chrout('\n')
void strcopy(s2, s1)
txt.print_ub(99+strcopy(s2,s1))
txt.chrout('\n')
test_stack.test() test_stack.test()
} }

View File

@ -4,6 +4,12 @@
%import test_stack %import test_stack
%zeropage basicsafe %zeropage basicsafe
; note: The flickering in the scrolling is caused by the CPU requiring
; too long to scroll the characters + the colors in course scroll.
; This takes nearly a full frame to accomplish, and causes tearing.
; It's very difficult to remove this flicker: it requires double buffering
; and splitting the coarse character scrolling on multiple phases...
main { main {
ubyte perform_scroll = false ubyte perform_scroll = false
@ -17,7 +23,7 @@ main {
c64.SCROLX &= %11110111 ; 38 column mode c64.SCROLX &= %11110111 ; 38 column mode
c64.set_rasterirq(40) ; enable animation c64.set_rasterirq(200) ; enable animation
ubyte target_height = 10 ubyte target_height = 10
ubyte active_height = 24 ubyte active_height = 24
@ -123,10 +129,10 @@ spritedata $0f00 {
irq { irq {
ubyte smoothx=7 ubyte smoothx=0
sub irq() { sub irq() {
smoothx = (smoothx-1) & 7 smoothx = (smoothx-1) & 7
main.perform_scroll = smoothx==0 main.perform_scroll = smoothx==7
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
} }
} }

Binary file not shown.

View File

@ -0,0 +1,125 @@
%target cx16
%import graphics
%import diskio
bmp_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
ubyte[$36] header
uword size
uword width
uword height
ubyte bpp
uword offsetx
uword offsety
ubyte[256] palette0
ubyte[256] palette1
ubyte[256] palette2
ubyte[256] palette3
uword total_read = 0
palette0[0] = 0
palette1[0] = 0
palette2[0] = 0
palette3[0] = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(header, $36)
if size==$36 {
total_read = $36
if header[0]=='b' and header[1]=='m' {
uword bm_data_offset = mkword(header[11], header[10])
uword header_size = mkword(header[$f], header[$e])
width = mkword(header[$13], header[$12])
height = mkword(header[$17], header[$16])
bpp = header[$1c]
uword num_colors = header[$2e]
if num_colors == 0
num_colors = 2**bpp
uword skip_hdr = header_size - 40
repeat skip_hdr
void c64.CHRIN()
total_read += skip_hdr
size = diskio.f_read(&palette0, num_colors*4)
if size==num_colors*4 {
total_read += size
repeat bm_data_offset - total_read
void c64.CHRIN()
graphics.clear_screen(1, 0)
palette.set_bgra(&palette0, num_colors)
decode_bitmap()
load_ok = true
}
}
}
diskio.f_close()
}
return load_ok
sub start_plot() {
offsetx = 0
offsety = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
if width > graphics.WIDTH
width = graphics.WIDTH
if height > graphics.HEIGHT-1
height = graphics.HEIGHT-1
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub decode_bitmap() {
start_plot()
uword bits_width = width * bpp
ubyte pad_bytes = (((bits_width + 31) >> 5) << 2) - ((bits_width + 7) >> 3) as ubyte
uword x
uword y
ubyte b
for y in height-1 downto 0 {
set_cursor(0, y)
when bpp {
8 -> {
for x in 0 to width-1
cx16.FB_set_pixel(c64.CHRIN())
}
4 -> {
for x in 0 to width-1 step 2 {
b = c64.CHRIN()
cx16.FB_set_pixel(b>>4)
cx16.FB_set_pixel(b&15)
}
}
2 -> {
for x in 0 to width-1 step 4 {
b = c64.CHRIN()
cx16.FB_set_pixel(b>>6)
cx16.FB_set_pixel(b>>4 & 3)
cx16.FB_set_pixel(b>>2 & 3)
cx16.FB_set_pixel(b & 3)
}
}
1 -> {
for x in 0 to width-1 step 8 {
cx16.r0 = c64.CHRIN()
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
}
}
repeat pad_bytes
void c64.CHRIN()
}
}
}
}

View File

@ -0,0 +1,140 @@
%target cx16
%import graphics
%import textio
%import diskio
%option no_sysinit
; CommanderX16 Image file format. (EXPERIMENTAL/ UNFINISHED)
; Numbers are encoded in little endian format (lsb first).
;
; offset value
; -----------------
; HEADER (12 bytes):
; 0-1 'CI' in petscii , from "CommanderX16 Image".
; 2 Size of the header data following this byte (always 9, could become more if format changes)
; 3-4 Width in pixels (must be multiple of 8)
; 5-6 Height in pixels
; 7 Bits-per-pixel (1, 2, 4 or 8) (= 2, 4, 16 or 256 colors)
; this also determines the number of palette entries following later.
; 8 Settings bits.
; bit 0 and 1 = compression. 00 = uncompressed
; 01 = RLE [TODO not yet implemented]
; 10 = LZSA [TODO not yet implemented]
; 11 = Exomizer [TODO not yet implemented]
; bit 2 = palette format. 0 = 4 bits/channel (2 bytes per color, $0R $GB)
; 1 = 8 bits/channel (3 bytes per color, $RR $GG $BB)
; 4 bits per channel is what the Vera in the Cx16 supports.
; bit 3 = bitmap format. 0 = raw bitmap pixels
; 1 = tile-based image [TODO not yet implemented]
; bit 4 = hscale (horizontal display resulution) 0 = 320 pixels, 1 = 640 pixels
; bit 5 = vscale (vertical display resulution) 0 = 240 pixels, 1 = 480 pixels
; bit 6,7: reserved, set to 0
; 9-11 Size of the bitmap data following the palette data.
; This is a 24-bits number, can be 0 ("unknown", in which case just read until the end).
;
; PALETTE (always present but size varies):
; 12-... Color palette. Number of entries = 2 ^ bits-per-pixel. Number of bytes per
; entry is 2 or 3, depending on the chosen palette format in the setting bits.
;
; BITMAPDATA (size varies):
; After this, the actual image data follows.
; If the bitmap format is 'raw bitmap pixels', the bimap is simply written as a sequence
; of bytes making up the image's scan lines. #bytes per scan line = width * bits-per-pixel / 8
; If it is 'tiles', .... [TODO]
; If a compression scheme is used, the bitmap data here has to be decompressed first.
; TODO: with compressed files, store the data in compressed chunks of max 8kb uncompressed?
; (it is a problem to load let alone decompress a full bitmap at once because there will likely not be enough ram to do that)
; (doing it in chunks of 8 kb allows for sticking each chunk in one of the banked 8kb ram blocks, or even copy it directly to the screen)
ci_module {
%option force_output
ubyte[256] buffer
ubyte[256] buffer2 ; add two more buffers to make enough space
ubyte[256] buffer3 ; to store a 256 color palette
ubyte[256] buffer4 ; .. and some more to be able to store 1280=
ubyte[256] buffer5 ; two 640 bytes worth of bitmap scanline data
sub show_image(uword filename) -> ubyte {
ubyte read_success = false
uword bitmap_load_address = progend()
; uword max_bitmap_size = $9eff - bitmap_load_address
if(diskio.f_open(8, filename)) {
uword size = diskio.f_read(buffer, 12) ; read the header
if size==12 {
if buffer[0]=='c' and buffer[1]=='i' and buffer[2] == 9 {
uword width = mkword(buffer[4], buffer[3])
uword height = mkword(buffer[6], buffer[5])
ubyte bpp = buffer[7]
uword num_colors = 2 ** bpp
ubyte flags = buffer[8]
ubyte compression = flags & %00000011
ubyte palette_format = (flags & %00000100) >> 2
ubyte bitmap_format = (flags & %00001000) >> 3
; ubyte hscale = (flags & %00010000) >> 4
; ubyte vscale = (flags & %00100000) >> 5
uword bitmap_size = mkword(buffer[10], buffer[9])
uword palette_size = num_colors*2
if palette_format
palette_size += num_colors ; 3
if width > graphics.WIDTH {
txt.print("image is too wide for the display!\n")
} else if compression!=0 {
txt.print("compressed image not yet supported!\n") ; TODO implement the various decompressions
} else if bitmap_format==1 {
txt.print("tiled bitmap not yet supported!\n") ; TODO implement tiled image
} else {
size = diskio.f_read(buffer, palette_size)
if size==palette_size {
if compression {
txt.print("todo: compressed image support\n")
} else {
; uncompressed bitmap data. read it a scanline at a time and display as we go.
; restrict height to the maximun that can be displayed
if height > graphics.HEIGHT
height = graphics.HEIGHT
graphics.enable_bitmap_mode()
if palette_format
palette.set_rgb8(buffer, num_colors)
else
palette.set_rgb4(buffer, num_colors)
graphics.clear_screen(1,0)
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
uword scanline_size = width * bpp / 8
ubyte y
for y in 0 to lsb(height)-1 {
void diskio.f_read(buffer, scanline_size)
when bpp {
8 -> cx16.FB_set_pixels_from_buf(buffer, scanline_size) ; FB_set_pixels in rom v38 crashes with a size > 255 so we use our own replacement for now
4 -> display_scanline_16c(buffer, scanline_size)
2 -> display_scanline_4c(buffer, scanline_size)
1 -> display_scanline_2c(buffer, scanline_size)
}
}
read_success = true
}
}
}
}
}
diskio.f_close()
}
return read_success
}
sub display_scanline_16c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_4c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_2c(uword dataptr, uword numbytes) {
; TODO
}
}

View File

@ -0,0 +1,214 @@
%target cx16
%import graphics
%import textio
%import diskio
iff_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
uword size
ubyte[32] buffer
ubyte[256] cmap
ubyte[256] cmap1
ubyte[256] cmap2
uword camg = 0
str chunk_id = "????"
uword chunk_size_hi
uword chunk_size_lo
uword scanline_data_ptr = progend()
uword width
uword height
ubyte num_planes
uword num_colors
ubyte compression
ubyte have_cmap = false
cmap[0] = 0
cmap1[0] = 0
cmap2[0] = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(buffer, 12)
if size==12 {
if buffer[0]=='f' and buffer[1]=='o' and buffer[2]=='r' and buffer[3]=='m'
and buffer[8]=='i' and buffer[9]=='l' and buffer[10]=='b' and buffer[11]=='m'{
while read_chunk_header() {
if chunk_id == "bmhd" {
void diskio.f_read(buffer, chunk_size_lo)
width = mkword(buffer[0], buffer[1])
height = mkword(buffer[2], buffer[3])
num_planes = buffer[8]
num_colors = 2 ** num_planes
compression = buffer[10]
}
else if chunk_id == "camg" {
void diskio.f_read(buffer, chunk_size_lo)
camg = mkword(buffer[2], buffer[3])
if camg & $0800 {
txt.print("ham mode not supported!\n")
break
}
}
else if chunk_id == "cmap" {
have_cmap = true
void diskio.f_read(&cmap, chunk_size_lo)
}
else if chunk_id == "body" {
graphics.clear_screen(1, 0)
if camg & $0004
height /= 2 ; interlaced: just skip every odd scanline later
if camg & $0080 and have_cmap
make_ehb_palette()
palette.set_rgb8(&cmap, num_colors)
if compression
decode_rle()
else
decode_raw()
load_ok = true
break ; done after body
}
else {
skip_chunk()
}
}
} else
txt.print("not an iff ilbm file!\n")
}
diskio.f_close()
}
return load_ok
sub read_chunk_header() -> ubyte {
size = diskio.f_read(buffer, 8)
if size==8 {
chunk_id[0] = buffer[0]
chunk_id[1] = buffer[1]
chunk_id[2] = buffer[2]
chunk_id[3] = buffer[3]
chunk_size_hi = mkword(buffer[4], buffer[5])
chunk_size_lo = mkword(buffer[6], buffer[7])
return true
}
return false
}
sub skip_chunk() {
repeat lsb(chunk_size_hi)*8 + (chunk_size_lo >> 13)
void diskio.f_read(scanline_data_ptr, $2000)
void diskio.f_read(scanline_data_ptr, chunk_size_lo & $1fff)
}
sub make_ehb_palette() {
; generate 32 additional Extra-Halfbrite colors in the cmap
uword palletteptr = &cmap
uword ehbptr = palletteptr + 32*3
repeat 32 {
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
}
}
uword bitplane_stride
uword interleave_stride
uword offsetx
uword offsety
sub start_plot() {
bitplane_stride = width>>3
interleave_stride = bitplane_stride * num_planes
offsetx = 0
offsety = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
if width > graphics.WIDTH
width = graphics.WIDTH
if height > graphics.HEIGHT-1
height = graphics.HEIGHT-1
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub decode_raw() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
void diskio.f_read(scanline_data_ptr, interleave_stride)
if interlaced
void diskio.f_read(scanline_data_ptr, interleave_stride)
set_cursor(0, y)
planar_to_chunky_scanline()
}
}
sub decode_rle() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
decode_rle_scanline()
if interlaced
decode_rle_scanline()
set_cursor(0, y)
planar_to_chunky_scanline()
}
}
sub decode_rle_scanline() {
uword x = interleave_stride
uword plane_ptr = scanline_data_ptr
while x {
ubyte b = c64.CHRIN()
if b > 128 {
ubyte b2 = c64.CHRIN()
repeat 2+(b^255) {
@(plane_ptr) = b2
plane_ptr++
x--
}
} else if b < 128 {
repeat b+1 {
@(plane_ptr) = c64.CHRIN()
plane_ptr++
x--
}
} else
break
}
}
sub planar_to_chunky_scanline() {
uword x
for x in 0 to width-1 {
ubyte bitnr = ((lsb(x) ^ 255) & 7) + 1
uword pixptr = x/8 + scanline_data_ptr
ubyte bits=0
repeat num_planes {
ubyte bb = @(pixptr) >> bitnr ; extract bit
ror(bits) ; shift it into the byte
pixptr += bitplane_stride
}
bits >>= 8-num_planes
cx16.FB_set_pixel(bits)
}
}
}
}

View File

@ -0,0 +1,197 @@
%target cx16
%import graphics
%import textio
%import diskio
%import koala_module
%import iff_module
%import pcx_module
%import bmp_module
;; %import ci_module
main {
sub start() {
; trick to check if we're running on sdcard or host system shared folder
txt.print("\nimage viewer for commander x16\nformats supported: .iff, .pcx, .bmp, .koa (c64 koala)\n\n")
if strlen(diskio.status(8)) {
txt.print("enter image file name or just enter for all on disk: ")
ubyte i = txt.input_chars(diskio.filename)
graphics.enable_bitmap_mode()
if i
attempt_load(diskio.filename)
else
show_pics_sdcard()
txt.print("\nnothing more to do.\n")
}
else
txt.print("files are read with sequential file loading.\nin the emulator this currently only works with files on an sd-card image.\nsorry :(\n")
repeat {
;
}
}
sub show_pics_sdcard() {
; load and show the images on the disk with the given extensions.
; this only works in the emulator V38 with an sd-card image with the files on it.
str[40] filename_ptrs
ubyte num_files = diskio.list_files(8, 0, false, &filename_ptrs, len(filename_ptrs))
if num_files {
while num_files {
num_files--
attempt_load(filename_ptrs[num_files])
}
} else
txt.print("no files in directory!\n")
}
sub attempt_load(uword filenameptr) {
txt.print(">> ")
txt.print(filenameptr)
txt.chrout('\n')
uword extension = filenameptr + rfind(filenameptr, '.')
if strcmp(extension, ".iff")==0 {
txt.print("loading ")
txt.print("iff\n")
if iff_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".pcx")==0 {
txt.print("loading ")
txt.print("pcx\n")
if pcx_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".koa")==0 {
txt.print("loading ")
txt.print("koala\n")
if koala_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".bmp")==0 {
txt.print("loading ")
txt.print("bmp\n")
if bmp_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
; else if strcmp(extension, ".ci")==0 {
; txt.print("loading ")
; txt.print("ci\n")
; if ci_module.show_image(filenameptr)
; txt.clear_screen()
; else
; txt.print("load error!\n")
; cx16.wait(120)
; }
}
sub extension_equals(uword stringptr, uword extensionptr) -> ubyte {
ubyte ix = rfind(stringptr, '.')
return ix<255 and strcmp(stringptr+ix, extensionptr)==0
}
sub rfind(uword stringptr, ubyte char) -> ubyte {
ubyte i
for i in strlen(stringptr)-1 downto 0 {
if @(stringptr+i)==char
return i
}
return 0
}
}
palette {
sub set_rgb4(uword palletteptr, uword num_colors) {
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
uword vera_palette_ptr = $fa00
repeat num_colors {
cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr))
palletteptr++
cx16.vpoke(1, vera_palette_ptr, @(palletteptr))
palletteptr++
vera_palette_ptr+=2
}
}
sub set_rgb8(uword palletteptr, uword num_colors) {
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
repeat num_colors {
red = @(palletteptr) >> 4
palletteptr++
greenblue = @(palletteptr) & %11110000
palletteptr++
greenblue |= @(palletteptr) >> 4 ; add Blue
palletteptr++
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_bgra(uword palletteptr, uword num_colors) {
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
; 4 bytes per color entry (BGRA), adjust color depth from 8 to 4 bits per channel.
repeat num_colors {
red = @(palletteptr+2) >> 4
greenblue = @(palletteptr+1) & %11110000
greenblue |= @(palletteptr+0) >> 4 ; add Blue
palletteptr+=4
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_monochrome() {
uword vera_palette_ptr = $fa00
cx16.vpoke(1, vera_palette_ptr, 0)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 0)
vera_palette_ptr++
repeat 255 {
cx16.vpoke(1, vera_palette_ptr, 255)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 255)
vera_palette_ptr++
}
}
sub set_grayscale() {
ubyte c = 0
uword vera_palette_ptr = $fa00
repeat 16 {
repeat 16 {
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
}
c += $11
}
}
}

View File

@ -1,62 +1,28 @@
%target cx16 %target cx16
%import graphics %import graphics
%import textio
%import diskio %import diskio
%import c64colors %import c64colors
main { koala_module {
const uword load_location = $4000 const uword load_location = $6000
sub start() { sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok=false
str[] pictures = [ if diskio.f_open(8, filenameptr) {
"i01-blubb-sphinx.bin", uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
"i02-bugjam-jsl.bin", if size==2 {
"i03-dinothawr-ar.bin", size = diskio.f_read(load_location, 10001)
"i04-fox-leon.bin", if size == 10001 {
"i05-hunter-agod.bin", ; set a better C64 color palette, the Cx16's default is too saturated
"i06-jazzman-jds.bin", c64colors.set_palette_pepto()
"i07-katakis-jegg.bin" convert_koalapic()
] load_ok = true
}
; set a better C64 color palette, the Cx16's default is too saturated
c64colors.set_palette_pepto()
graphics.enable_bitmap_mode()
repeat {
ubyte file_idx
for file_idx in 0 to len(pictures)-1 {
load_image(pictures[file_idx])
wait()
} }
diskio.f_close()
} }
}
sub wait() { return load_ok
uword jiffies = 0
c64.SETTIM(0,0,0)
while jiffies < 180 {
; read clock
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta jiffies
stx jiffies+1
ldx P8ZP_SCRATCH_REG
}}
}
}
sub load_image(uword filenameptr) {
uword length = diskio.load(8, filenameptr, load_location)
if length != 10001 {
txt.print_uw(length)
txt.print("\nload error\n")
diskio.status(8)
exit(1)
}
convert_koalapic()
} }
sub convert_koalapic() { sub convert_koalapic() {
@ -69,6 +35,8 @@ main {
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance ; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
ubyte[8] pixels ubyte[8] pixels
graphics.clear_screen(1, 0)
for cy in 0 to 24*8 step 8 { for cy in 0 to 24*8 step 8 {
for cx in 0 to 39 { for cx in 0 to 39 {
for d in 0 to 7 { for d in 0 to 7 {

View File

@ -0,0 +1,180 @@
%target cx16
%import graphics
%import textio
%import diskio
pcx_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
if diskio.f_open(8, filenameptr) {
ubyte[128] header
uword size = diskio.f_read(header, 128)
if size==128 {
if header[0] == $0a and header[2] == 1 {
ubyte bits_per_pixel = header[3]
if bits_per_pixel==1 or bits_per_pixel==4 or bits_per_pixel==8 {
uword width = mkword(header[$09], header[$08]) - mkword(header[$05], header[$04]) + 1
uword height = mkword(header[$0b], header[$0a]) - mkword(header[$07], header[$06]) + 1
ubyte number_of_planes = header[$41]
uword palette_format = mkword(header[$45], header[$44])
uword num_colors = 2**bits_per_pixel
if number_of_planes == 1 {
if (width & 7) == 0 {
graphics.clear_screen(1, 0)
if palette_format==2
palette.set_grayscale()
else if num_colors == 16
palette.set_rgb8(&header + $10, 16)
else if num_colors == 2
palette.set_monochrome()
when bits_per_pixel {
8 -> load_ok = bitmap.do8bpp(width, height)
4 -> load_ok = bitmap.do4bpp(width, height)
1 -> load_ok = bitmap.do1bpp(width, height)
}
if load_ok {
load_ok = c64.CHRIN()
if load_ok == 12 {
; there is 256 colors of palette data at the end
uword palette_mem = progend()
load_ok = false
size = diskio.f_read(palette_mem, 3*256)
if size==3*256 {
load_ok = true
palette.set_rgb8(palette_mem, num_colors)
}
}
}
} else
txt.print("width not multiple of 8!\n")
} else
txt.print("has >256 colors!\n")
}
}
}
diskio.f_close()
}
return load_ok
}
}
bitmap {
uword offsetx
uword offsety
uword py
uword px
ubyte y_ok
ubyte status
sub start_plot(uword width, uword height) {
offsetx = 0
offsety = 0
y_ok = true
py = 0
px = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
status = (not c64.READST()) or c64.READST()==64
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub next_scanline() {
px = 0
py++
y_ok = py < graphics.HEIGHT-1
set_cursor(0, py)
status = (not c64.READST()) or c64.READST()==64
}
sub do1bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
repeat b {
if y_ok {
cx16.r0 = dat
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
px += 8
}
} else {
if y_ok {
cx16.r0 = b
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
px += 8
}
if px==width
next_scanline()
}
return status
}
sub do4bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b {
cx16.FB_set_pixel(dat>>4)
cx16.FB_set_pixel(dat & 15)
}
px += b*2
} else {
if y_ok {
cx16.FB_set_pixel(b>>4)
cx16.FB_set_pixel(b & 15)
}
px += 2
}
if px==width
next_scanline()
}
return status
}
sub do8bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b
cx16.FB_set_pixel(dat)
px += b
} else {
if y_ok
cx16.FB_set_pixel(b)
px++
}
if px==width
next_scanline()
}
return status
}
}

View File

@ -1,40 +0,0 @@
; CommanderX16 simple graphics example!
%target cx16
%zeropage basicsafe
main {
sub start() {
void cx16.screen_set_mode($80)
cx16.r0=0
void cx16.screen_set_mode(0)
cx16.FB_init()
cx16.GRAPH_init()
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
ubyte color
repeat 320*199 {
cx16.FB_set_pixel(color)
color++
}
uword xx
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

@ -6,6 +6,9 @@
main { main {
sub start() { sub start() {
txt.print("showing the full directory of drive 8:\n")
void diskio.directory(8)
txt.chrout('\n')
if diskio.lf_start_list(8, "cub", false) { if diskio.lf_start_list(8, "cub", false) {
txt.print("\nfiles starting with 'cub':\n") txt.print("\nfiles starting with 'cub':\n")

View File

@ -1,23 +1,13 @@
%import textio %import textio
%import diskio %import diskio
%import floats %import floats
%import graphics
%zeropage basicsafe %zeropage basicsafe
%import test_stack %import test_stack
%option no_sysinit %option no_sysinit
main { main {
sub start() { sub start () {
ubyte[] barr = [0,0,0]
uword[] warr = [0,0,0]
ubyte xx = 0
barr[1] = xx+9
warr[1] = &warr
txt.print_ub(barr[1])
txt.chrout('\n')
txt.print_uwhex(warr[1],1 )
txt.chrout('\n')
test_stack.test() test_stack.test()
} }

View File

@ -14,7 +14,7 @@
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" /> <keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" /> <keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" /> <keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" /> <keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;progend;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" />
</highlighting> </highlighting>
<extensionMap> <extensionMap>
<mapping ext="p8" /> <mapping ext="p8" />

View File

@ -6,3 +6,6 @@ The exact path may vary with the version of the IDE,
but for me it is currently this on Linux: but for me it is currently this on Linux:
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/ $HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
(note the version number in the path, adjust accordingly)