mirror of
https://github.com/irmen/prog8.git
synced 2025-06-15 02:23:36 +00:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
52e8a44517 | |||
59f33658ad | |||
e0315bffdc | |||
cd28d0c0e0 | |||
0baa2c8b23 | |||
4977d1fbd5 | |||
3b7a92f1b4 | |||
f6920172dd | |||
93bfc8f5f4 | |||
39b7655264 | |||
8b75ceb412 | |||
c39fc4010d | |||
8df778a515 | |||
5134ea76bf | |||
3ba37df29d | |||
e221d674d9 | |||
251f947293 | |||
41e1e1cbb0 | |||
da1bc351d2 | |||
43c0afdea0 | |||
add5bfa2ec | |||
34babfb5de | |||
4f6c45c86c | |||
e6220a464c | |||
8dcd49934a | |||
bedc3bdb56 | |||
83ceb0fde9 | |||
1d299c56e0 | |||
0d735c2ccc | |||
4094f89d4a | |||
cf1e8b194a | |||
74e5644f55 | |||
b5dc5fc615 | |||
7a7270d769 | |||
7549ddcd2b | |||
08f0303178 |
@ -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() {
|
||||
%asm {{
|
||||
lda #$80
|
||||
|
@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
clear_screenchars(' ')
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
|
@ -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 $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(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 $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
|
||||
|
@ -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 $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 $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 $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
|
||||
@ -245,7 +245,7 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
; ---- end of kernal routines ----
|
||||
|
||||
; ---- 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
|
||||
; note: inefficient when reading multiple sequential bytes!
|
||||
%asm {{
|
||||
@ -257,9 +257,9 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
lda cx16.VERA_DATA0
|
||||
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
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
@ -275,7 +275,59 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
sta cx16.VERA_DATA0
|
||||
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 -----
|
||||
|
@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 60
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
clear_screenchars(' ')
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
|
@ -7,14 +7,14 @@ diskio {
|
||||
|
||||
|
||||
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.SETLFS(1, drivenumber, 0)
|
||||
void c64.OPEN() ; open 1,8,0,"$"
|
||||
c64.SETLFS(13, drivenumber, 0)
|
||||
void c64.OPEN() ; open 13,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(1) ; use #1 as input channel
|
||||
void c64.CHKIN(13) ; use #13 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@ -39,14 +39,14 @@ diskio {
|
||||
void c64.CHRIN()
|
||||
status = c64.READST()
|
||||
void c64.STOP()
|
||||
if_nz
|
||||
if_z
|
||||
break
|
||||
}
|
||||
|
||||
io_error:
|
||||
status = c64.READST()
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(1)
|
||||
c64.CLOSE(13)
|
||||
|
||||
if status and status != 64 { ; 64=end of file
|
||||
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_in_progress = false
|
||||
ubyte list_pattern_size
|
||||
ubyte list_skip_disk_name
|
||||
uword list_pattern
|
||||
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 {
|
||||
; -- 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()
|
||||
list_pattern = pattern
|
||||
list_suffixmatch = suffixmatch
|
||||
list_skip_disk_name = true
|
||||
list_in_progress = true
|
||||
iteration_in_progress = true
|
||||
if pattern==0
|
||||
list_pattern_size = 0
|
||||
else
|
||||
list_pattern_size = strlen(pattern)
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(1, drivenumber, 0)
|
||||
void c64.OPEN() ; open 1,8,0,"$"
|
||||
c64.SETLFS(12, drivenumber, 0)
|
||||
void c64.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(1) ; use #1 as input channel
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@ -106,13 +136,19 @@ io_error:
|
||||
; 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 not list_in_progress
|
||||
if not iteration_in_progress
|
||||
return false
|
||||
|
||||
while not c64.READST() {
|
||||
repeat {
|
||||
void c64.CHKIN(12) ; use #12 as input channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = c64.CHRIN()
|
||||
ubyte blocks_msb = c64.CHRIN()
|
||||
|
||||
if c64.READST()
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
@ -163,16 +199,67 @@ close_end:
|
||||
|
||||
sub lf_end_list() {
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if list_in_progress {
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
list_in_progress = false
|
||||
c64.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub status(ubyte drivenumber) {
|
||||
; -- display the disk drive's current status message
|
||||
; ----- iterative file loader functions -----
|
||||
|
||||
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.SETLFS(15, drivenumber, 15)
|
||||
void c64.OPEN() ; open 15,8,15
|
||||
@ -182,12 +269,16 @@ close_end:
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not c64.READST()
|
||||
txt.chrout(c64.CHRIN())
|
||||
while not c64.READST() {
|
||||
@(messageptr) = c64.CHRIN()
|
||||
messageptr++
|
||||
}
|
||||
|
||||
io_error:
|
||||
@(messageptr) = 0
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(15)
|
||||
return filename
|
||||
}
|
||||
|
||||
|
||||
|
@ -1352,6 +1352,33 @@ shift_left_w_3 .proc
|
||||
jmp shift_left_w_7._shift3
|
||||
.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
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@ -1482,6 +1509,21 @@ shift_right_w_3 .proc
|
||||
.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:
|
||||
|
||||
|
@ -1346,3 +1346,24 @@ func_strcmp_stack .proc
|
||||
dex
|
||||
rts
|
||||
.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
|
||||
|
@ -993,6 +993,7 @@ _arg_index .byte 0
|
||||
strcpy .proc
|
||||
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
|
||||
; it is assumed the target string is large enough.
|
||||
; returns the length of the string that was copied in Y.
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #$ff
|
||||
|
@ -1 +1 @@
|
||||
5.3
|
||||
5.4
|
||||
|
@ -4,6 +4,7 @@ import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
|
||||
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> {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val subroutine = expr.definingSubroutine()!!
|
||||
|
@ -160,6 +160,7 @@ internal class AsmGen(private val program: Program,
|
||||
val floatvalue = flt.key
|
||||
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
out("prog8_program_end\t; end of program label for progend()")
|
||||
}
|
||||
|
||||
private fun block2asm(block: Block) {
|
||||
|
@ -5,10 +5,7 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.ArrayIndex
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.Cx16Target
|
||||
@ -84,6 +81,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
"set_irqd" -> asmgen.out(" sei")
|
||||
"strlen" -> funcStrlen(fcall, resultToStack)
|
||||
"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)
|
||||
"substr", "leftstr", "rightstr" -> {
|
||||
translateArguments(fcall.args, func, scope)
|
||||
@ -93,6 +97,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
translateArguments(fcall.args, func, scope)
|
||||
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}")
|
||||
}
|
||||
}
|
||||
@ -532,14 +547,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
|
||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||
val type = fcall.args[0].inferType(program)
|
||||
when {
|
||||
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 (fcall.args[0] is IdentifierReference) {
|
||||
// use the address of the variable
|
||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||
val type = fcall.args[0].inferType(program)
|
||||
when {
|
||||
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")
|
||||
else
|
||||
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
|
||||
|
@ -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) {
|
||||
"**" -> throw AssemblyError("** operator requires floats")
|
||||
"*" -> 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")
|
||||
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||
}
|
||||
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||
"<<" -> throw AssemblyError("<< should not operate via stack")
|
||||
">>" -> throw AssemblyError(">> should not operate via stack")
|
||||
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||
">>" -> {
|
||||
if(dt==DataType.UWORD)
|
||||
asmgen.out(" jsr math.shift_right_uw")
|
||||
else
|
||||
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.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||
|
@ -20,7 +20,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
|
||||
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
|
||||
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")
|
||||
|
||||
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)
|
||||
// must take care of popping this value again at the end!
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
if (preserveStatusRegisterAfterCall)
|
||||
asmgen.out(" plp\t; restore status flags from call")
|
||||
asmgen.out(" plp") // restore status flags from call
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val signature = BuiltinFunctions.getValue(sub.name)
|
||||
|
@ -7,6 +7,7 @@ import prog8.ast.statements.Subroutine
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
||||
import prog8.compiler.toHex
|
||||
@ -317,9 +318,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"|", "or" -> 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")
|
||||
}
|
||||
if(ptrOnZp)
|
||||
@ -357,9 +359,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> asmgen.out(" and $otherName")
|
||||
"^" -> asmgen.out(" eor $otherName")
|
||||
"|" -> asmgen.out(" ora $otherName")
|
||||
"&", "and" -> asmgen.out(" and $otherName")
|
||||
"|", "or" -> 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")
|
||||
}
|
||||
if(ptrOnZp)
|
||||
@ -438,7 +441,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" and #$value")
|
||||
if(ptrOnZp)
|
||||
@ -446,15 +449,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
else
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
"^" -> {
|
||||
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")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" ora #$value")
|
||||
if(ptrOnZp)
|
||||
@ -462,6 +457,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
else
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -525,18 +529,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -591,27 +596,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||
"and" -> asmgen.out("""
|
||||
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""")
|
||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||
in comparisonOperators -> TODO("in-place modification for $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")
|
||||
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
"|" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||
"|", "or" -> 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")
|
||||
}
|
||||
}
|
||||
@ -949,7 +938,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
when {
|
||||
value == 0 -> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
"^" -> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
when {
|
||||
value == 0 -> {}
|
||||
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")
|
||||
}
|
||||
}
|
||||
"^", "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")
|
||||
}
|
||||
}
|
||||
@ -1100,7 +1090,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
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 $otherName | eor $name | sta $name")
|
||||
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||
"|", "or" -> 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")
|
||||
}
|
||||
}
|
||||
@ -1181,9 +1172,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
""")
|
||||
}
|
||||
"<<", ">>" -> 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")
|
||||
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
||||
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
||||
"|", "or" -> 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")
|
||||
}
|
||||
}
|
||||
@ -1356,7 +1348,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
@ -1366,14 +1358,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
}
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -1402,18 +1395,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
remainderVarByWordInAY()
|
||||
}
|
||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -1461,6 +1455,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||
}
|
||||
asmgen.out("""
|
||||
@ -1480,14 +1475,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$otherName
|
||||
ldy #>$otherName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
if(CompilationTarget.instance is Cx16Target) {
|
||||
// cx16 doesn't have FPWR() only FPWRT()
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
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("""
|
||||
@ -1529,6 +1536,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||
}
|
||||
// store Fac1 back into memory
|
||||
@ -1545,14 +1553,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$constValueName
|
||||
ldy #>$constValueName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
if(CompilationTarget.instance is Cx16Target) {
|
||||
// cx16 doesn't have FPWR() only FPWRT()
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
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)
|
||||
@ -1601,6 +1621,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||
}
|
||||
// store Fac1 back into memory
|
||||
|
@ -38,8 +38,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
||||
override fun launchEmulator(programName: String) {
|
||||
for(emulator in listOf("x16emu")) {
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
|
||||
"-run", "-prg", programName + ".prg")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
|
@ -141,6 +141,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
FSignature("set_irqd" , false, emptyList(), null),
|
||||
FSignature("clear_irqd" , false, emptyList(), null),
|
||||
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("memcopy" , false, listOf(
|
||||
FParam("from", IterableDatatypes + DataType.UWORD),
|
||||
@ -155,6 +156,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
FParam("numwords", setOf(DataType.UWORD)),
|
||||
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||
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(
|
||||
FParam("source", 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]
|
||||
if(argument is StringLiteralValue)
|
||||
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.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
|
||||
throw SyntaxError("strlen must have string argument", position)
|
||||
|
@ -7,6 +7,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.*
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
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> {
|
||||
val leftconst = expr.left.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 {
|
||||
leftconst!=null -> expr.right as? BinaryExpression
|
||||
@ -111,7 +149,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
val change = groupTwoConstsTogether(expr, subExpr,
|
||||
leftconst != null, rightconst != 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) {
|
||||
val evaluator = ConstExprEvaluator()
|
||||
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> {
|
||||
|
@ -6,6 +6,7 @@ import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.Assignment
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
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)
|
||||
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
|
||||
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
@ -338,7 +345,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
||||
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||
if (rightVal2 != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val rightConst: NumericLiteralValue = rightVal2
|
||||
@ -562,7 +569,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
||||
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||
if (rightVal2 != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val leftValue: Expression = expr2.left
|
||||
@ -682,17 +689,17 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
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) {
|
||||
// swap left and right so that right is always the constant
|
||||
val tmp = expr.left
|
||||
expr.left = expr.right
|
||||
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?)
|
||||
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val op1 = binExpr.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)
|
||||
return listOf(IAstModification.SwapOperands(rExpr))
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ internal class ModuleImporter {
|
||||
|
||||
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
||||
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")
|
||||
if(propPath!=null)
|
||||
|
@ -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.
|
||||
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
|
||||
^^^^^^^^^^^^^
|
||||
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.
|
||||
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()
|
||||
returns a pseudo-random byte from 0..255
|
||||
|
||||
@ -894,6 +906,10 @@ set_irqd() / clear_irqd()
|
||||
swap(x, y)
|
||||
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
|
||||
----------------
|
||||
|
@ -324,6 +324,10 @@ main {
|
||||
txt.print(result)
|
||||
txt.chrout('\n')
|
||||
|
||||
void strcopy(s2, s1)
|
||||
txt.print_ub(99+strcopy(s2,s1))
|
||||
txt.chrout('\n')
|
||||
|
||||
test_stack.test()
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,12 @@
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
; note: The flickering in the scrolling is caused by the CPU requiring
|
||||
; too long to scroll the characters + the colors in course scroll.
|
||||
; This takes nearly a full frame to accomplish, and causes tearing.
|
||||
; It's very difficult to remove this flicker: it requires double buffering
|
||||
; and splitting the coarse character scrolling on multiple phases...
|
||||
|
||||
main {
|
||||
|
||||
ubyte perform_scroll = false
|
||||
@ -17,7 +23,7 @@ main {
|
||||
|
||||
c64.SCROLX &= %11110111 ; 38 column mode
|
||||
|
||||
c64.set_rasterirq(40) ; enable animation
|
||||
c64.set_rasterirq(200) ; enable animation
|
||||
|
||||
ubyte target_height = 10
|
||||
ubyte active_height = 24
|
||||
@ -123,10 +129,10 @@ spritedata $0f00 {
|
||||
|
||||
|
||||
irq {
|
||||
ubyte smoothx=7
|
||||
ubyte smoothx=0
|
||||
sub irq() {
|
||||
smoothx = (smoothx-1) & 7
|
||||
main.perform_scroll = smoothx==0
|
||||
main.perform_scroll = smoothx==7
|
||||
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
125
examples/cx16/imageviewer/bmp_module.p8
Normal file
125
examples/cx16/imageviewer/bmp_module.p8
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
140
examples/cx16/imageviewer/ci_module.p8
Normal file
140
examples/cx16/imageviewer/ci_module.p8
Normal 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
|
||||
}
|
||||
}
|
214
examples/cx16/imageviewer/iff_module.p8
Normal file
214
examples/cx16/imageviewer/iff_module.p8
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
197
examples/cx16/imageviewer/imageviewer.p8
Normal file
197
examples/cx16/imageviewer/imageviewer.p8
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +1,28 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import textio
|
||||
%import diskio
|
||||
%import c64colors
|
||||
|
||||
main {
|
||||
const uword load_location = $4000
|
||||
koala_module {
|
||||
const uword load_location = $6000
|
||||
|
||||
sub start() {
|
||||
|
||||
str[] pictures = [
|
||||
"i01-blubb-sphinx.bin",
|
||||
"i02-bugjam-jsl.bin",
|
||||
"i03-dinothawr-ar.bin",
|
||||
"i04-fox-leon.bin",
|
||||
"i05-hunter-agod.bin",
|
||||
"i06-jazzman-jds.bin",
|
||||
"i07-katakis-jegg.bin"
|
||||
]
|
||||
|
||||
; 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()
|
||||
sub show_image(uword filenameptr) -> ubyte {
|
||||
ubyte load_ok=false
|
||||
if diskio.f_open(8, filenameptr) {
|
||||
uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
|
||||
if size==2 {
|
||||
size = diskio.f_read(load_location, 10001)
|
||||
if size == 10001 {
|
||||
; set a better C64 color palette, the Cx16's default is too saturated
|
||||
c64colors.set_palette_pepto()
|
||||
convert_koalapic()
|
||||
load_ok = true
|
||||
}
|
||||
}
|
||||
diskio.f_close()
|
||||
}
|
||||
}
|
||||
|
||||
sub wait() {
|
||||
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()
|
||||
return load_ok
|
||||
}
|
||||
|
||||
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
|
||||
ubyte[8] pixels
|
||||
|
||||
graphics.clear_screen(1, 0)
|
||||
|
||||
for cy in 0 to 24*8 step 8 {
|
||||
for cx in 0 to 39 {
|
||||
for d in 0 to 7 {
|
180
examples/cx16/imageviewer/pcx_module.p8
Normal file
180
examples/cx16/imageviewer/pcx_module.p8
Normal 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
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
main {
|
||||
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) {
|
||||
txt.print("\nfiles starting with 'cub':\n")
|
||||
|
@ -1,23 +1,13 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%import floats
|
||||
%import graphics
|
||||
%zeropage basicsafe
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
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')
|
||||
sub start () {
|
||||
|
||||
test_stack.test()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
<keywords keywords="&;->;@;\$;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" />
|
||||
<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>
|
||||
<extensionMap>
|
||||
<mapping ext="p8" />
|
||||
|
@ -6,3 +6,6 @@ The exact path may vary with the version of the IDE,
|
||||
but for me it is currently this on Linux:
|
||||
|
||||
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
|
||||
|
||||
|
||||
(note the version number in the path, adjust accordingly)
|
||||
|
Reference in New Issue
Block a user