Compare commits

...
24 Commits
Author SHA1 Message Date
Irmen de Jong 9504711fc7 IR/VM: SGN sets status bits 2025-11-19 21:37:04 +01:00
Irmen de Jong 590feda903 IR: fix sgn(float) register type error 2025-11-19 21:11:31 +01:00
Irmen de Jong 6e7e2922bf moved the float <> long cast routines to float.asm where the other cast routines are too 2025-11-19 18:45:32 +01:00
Irmen de Jong 01df1f0083 way more efficient implementation of internal_cast_as_long now using QINT 2025-11-19 18:32:40 +01:00
Irmen de Jong 397299bd1d implement 6502 codegen for casting float to long (super slow...) 2025-11-19 00:05:18 +01:00
Irmen de Jong c275aacd38 implement 6502 codegen for casting long to float 2025-11-18 23:26:12 +01:00
Irmen de Jong 6a6e18773e C64: key repeat is now enabled at program startup, to fall in line with the default key repeat behavior on the C128 and X16. 2025-11-18 21:09:13 +01:00
Irmen de Jong 99e037489b IR: added float<>long casts, cx16: blink_cursor extapi tested in test.p8 2025-11-17 23:13:56 +01:00
Irmen de Jong fb5290e17b cx16: added diskio.f_fatlba() 2025-11-15 20:17:26 +01:00
Irmen de Jong 818774ab84 added conv.hex2long(), added more new X16 extapi calls (rom 49+) 2025-11-15 17:52:34 +01:00
Irmen de Jong d667312acc Merge branch 'refs/heads/12.1-SNAPSHOT'
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-11-15 15:15:06 +01:00
Irmen de Jong 64d0cd87a8 diskio.f_seek() and f_tell() now use longs instead of separate words
also fix bug when returning multiple long values
2025-11-15 15:13:26 +01:00
Irmen de Jong 2fffbdde89 todo 2025-11-14 20:05:46 +01:00
Irmen de Jong dc8ea31c49 fix codegen error for a word array equality test 2025-11-13 21:18:04 +01:00
Irmen de Jong f4ead66e91 fix param name breakage in PET txt.setcc for ignored charcolor parameter 2025-11-12 22:00:30 +01:00
Irmen de Jong 314e7f5691 doc updates 2025-11-10 23:42:29 +01:00
Irmen de Jong da31465b7f added the missing smallstack to buffers module 2025-11-10 00:21:13 +01:00
Irmen de Jong 397a907088 fix handling and errors of using long addresses in memread/memwrite 2025-11-09 18:17:57 +01:00
Irmen de Jong 50c6962e1f fix symbol lookup crash 2025-11-09 15:10:10 +01:00
Irmen de Jong b7d1fb1342 translate address-of into + expression for non-const address ptrs too,
also IR optimization to use immediate arithmetic opcodes if possible
2025-11-09 01:36:34 +01:00
Irmen de Jong afb458a7da fix crash in long address-of 2025-11-08 22:24:37 +01:00
Irmen de Jong da3c7f267f improved identifier const value handling if types differ 2025-11-08 20:34:51 +01:00
Irmen de Jong b7ba7c50b1 handle &ptr[offset] for non-uword ptr 2025-11-08 20:31:28 +01:00
Irmen de Jong bdbfe7048b updated library symboldumps 2025-11-08 00:21:53 +01:00
46 changed files with 925 additions and 241 deletions

No files matched your search

@@ -2003,9 +2003,9 @@ $repeatLabel""")
lda #<$leftName
ldy #>$leftName""")
if(lessOrEquals)
out("jsr floats.vars_lesseq_f")
out(" jsr floats.vars_lesseq_f")
else
out("jsr floats.vars_less_f")
out(" jsr floats.vars_less_f")
}
fun lessf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
@@ -1938,7 +1938,7 @@ _jump jmp (${target.asmLabel})
if(notEquals)
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
else
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
translateAYEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
}
}
}
@@ -3077,7 +3077,14 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
BaseDataType.LONG -> TODO("cast float to long")
BaseDataType.LONG -> {
asmgen.out("""
sta cx16.r0L
sty cx16.r0H
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
jsr floats.cast_as_long""")
}
else -> throw AssemblyError("weird type")
}
}
@@ -3087,6 +3094,15 @@ $endLabel""")
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
} else if(targetDt.isWord || targetDt.isPointer) {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
} else if(targetDt.isFloat) {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta cx16.r0L
sty cx16.r0H
lda #<$sourceAsmVarName
ldy #>$sourceAsmVarName
jsr floats.cast_from_long""")
} else
throw AssemblyError("weird type")
}
@@ -5326,7 +5342,7 @@ $endLabel""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), ignoreDatatype, scope)
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), scope)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5387,7 +5403,7 @@ $endLabel""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), ignoreDatatype, scope)
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), scope)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5412,7 +5428,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register negate ${target.position}")
TargetStorageKind.POINTER -> TODO("long negate via pointer ${target.position}")
TargetStorageKind.POINTER -> pointergen.inplaceLongNegate(PtrTarget(target), scope)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -117,14 +117,18 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
TODO("inplace word invert pointer deref ${target.position}")
}
internal fun inplaceByteNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
internal fun inplaceByteNegate(target: PtrTarget, scope: IPtSubroutine?) {
TODO("inplace byte negate to pointer deref ${target.position}")
}
internal fun inplaceWordNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
internal fun inplaceWordNegate(target: PtrTarget, scope: IPtSubroutine?) {
TODO("inplace word negate pointer deref ${target.position}")
}
internal fun inplaceLongNegate(target: PtrTarget, scope: IPtSubroutine?) {
TODO("inplace long negate pointer deref ${target.position}")
}
internal fun inplaceFloatNegate(target: PtrTarget, scope: IPtSubroutine?) {
TODO("inplace float negate pointer deref ${target.position}")
}
@@ -1325,8 +1329,8 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
fun divide(signed: Boolean) {
// on entry here: number placed in P8ZP_SCRATCH_PTR, divisor placed in AY
if(signed) asmgen.out("jsr prog8_math.divmod_w_asm")
else asmgen.out("jsr prog8_math.divmod_uw_asm")
if(signed) asmgen.out(" jsr prog8_math.divmod_w_asm")
else asmgen.out(" jsr prog8_math.divmod_uw_asm")
asmgen.out("""
tax
tya
@@ -819,6 +819,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.FTOSL, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
}
}
@@ -837,6 +841,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
BaseDataType.LONG -> {
addInstr(result, IRInstruction(Opcode.FFROMSL, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
@@ -440,6 +440,54 @@ jump p8_label_gen_2
}
else -> {}
}
fun optimizeImmediateLoadAssociative(replacementOpcode: Opcode) {
fun getImmediateLoad(reg: Int): Pair<Int, Int>? {
// look if the given register gets an immediate value 1 or 2 istructions back
// returns (index of load instruction, immediate value) or null.
if(idx>=1) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-1 to previous.immediate!!
}
if(idx>=2) {
val previous = indexedInstructions[idx-2].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-2 to previous.immediate!!
}
return null
}
if(ins.reg1!=null) {
val immediate1 = getImmediateLoad(ins.reg1!!)
if(immediate1!=null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg2, immediate = immediate1.second)
chunk.instructions.removeAt(immediate1.first)
changed=true
} else {
val immediate2 = getImmediateLoad(ins.reg2!!)
if (immediate2 != null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg1, immediate = immediate2.second)
chunk.instructions.removeAt(immediate2.first)
changed=true
}
}
}
}
// try to use immediate arithmetic instruction if possible
when(ins.opcode) {
Opcode.ADDR -> optimizeImmediateLoadAssociative(Opcode.ADD)
Opcode.MULR -> optimizeImmediateLoadAssociative(Opcode.MUL)
Opcode.MULSR -> optimizeImmediateLoadAssociative(Opcode.MULS)
// Opcode.SUBR -> TODO("ir peephole Subr")
// Opcode.DIVR -> TODO("ir peephole Divr")
// Opcode.DIVSR -> TODO("ir peephole Divsr")
// Opcode.MODR -> TODO("ir peephole Modr")
// Opcode.DIVMODR -> TODO("ir peephole DivModr")
else -> {}
}
}
return changed
}
+59 -1
View File
@@ -70,7 +70,65 @@ smallringbuffer {
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
smallstack {
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
ubyte[256] buffer
ubyte sp = 255
sub init() {
sp = 255
}
sub size() -> ubyte {
return 255-sp
}
sub free() -> ubyte {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==255
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer[sp] = lsb(value)
sp--
buffer[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer[sp]
sp++
cx16.r0L = buffer[sp]
return cx16.r0
}
}
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
+1 -1
View File
@@ -358,7 +358,7 @@ extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without r
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
extsub $FF7D = PRIMM() ; print immediate string
extsub $FF7D = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
; ---- C128 specific system utility routines: ----
+68
View File
@@ -3,6 +3,8 @@
FL_ONE_const .byte 129 ; 1.0
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
.section BSS
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
@@ -126,6 +128,72 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
.pend
cast_from_long .proc
; convert long pointed to by AY into a float pointed to by R0
; a bit slow algorithm implemented below: (msw(l) as word) as float * 65536.0 + (lsw(l) as float)
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #3
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_REG
dey
lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_REG
jsr GIVAYFAY
lda #<FL_65536_const
ldy #>FL_65536_const
jsr CONUPK
jsr FMULTT
jsr pushFAC1
ldy #1
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_REG
dey
lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_REG
jsr GIVUAYFAY
jsr MOVEF
clc
jsr popFAC
jsr FADDT
ldx cx16.r0L
ldy cx16.r0H
jmp MOVMF
.pend
cast_as_long .proc
; convert float pointed to by R0 into a long pointed to by AY
FACHO = FAC_ADDR + 1
; save the target variable pointer on the stack
pha
tya
pha
lda cx16.r0L
ldy cx16.r0H
jsr MOVFM
jsr QINT
; restore the target variable pointer from the stack, and put the result in it
pla
sta P8ZP_SCRATCH_PTR+1
pla
sta P8ZP_SCRATCH_PTR
ldx #3
ldy #0
- lda FACHO,x
sta (P8ZP_SCRATCH_PTR),y
iny
dex
bpl -
rts
.pend
copy_float .proc
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
+1
View File
@@ -11,6 +11,7 @@ floats {
; note: the fac1 and fac2 are working registers and take 6 bytes each,
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
const uword FAC_ADDR = $61
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
+4
View File
@@ -1313,6 +1313,8 @@ asmsub init_system() {
lda #0
sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch
lda #$80
sta 650 ; enable key repeat for normal keys too
lda #PROG8_C64_BANK_CONFIG ; apply bank config
sta $01
and #1
@@ -1349,6 +1351,8 @@ asmsub cleanup_at_exit() {
jsr cbm.MEMTOP ; adjust MEMTOP down again
jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch
lda #0
sta 650 ; disable keyrepeats
lda _exitcarry
lsr a
lda _exitcode
+79
View File
@@ -572,6 +572,85 @@ _try_iso
}}
}
asmsub hex2long(str string @AY) -> long @R0R1_32 {
; -- hexadecimal string (with or without '$') to long.
; string may be in petscii or c64-screencode encoding.
; stops parsing at the first character that's not a hex digit (except leading $)
; result in R0:R1, number of characters processed also remains in cx16.r15L if you want to use it!! (0 = error)
%asm {{
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
sty cx16.r0
sty cx16.r0+1
sty cx16.r0+2
sty cx16.r0+3
sty cx16.r15H
lda (P8ZP_SCRATCH_W2),y
beq _stop
cmp #'$'
bne _loop
iny
_loop
lda #0
sta P8ZP_SCRATCH_B1
lda (P8ZP_SCRATCH_W2),y
beq _stop
cmp #7 ; screencode letters A-F are 1-6
bcc _add_letter
and #127
cmp #97
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
cmp #'g'
bcs _stop
cmp #'a'
bcs _add_letter
cmp #'0'
bcc _stop
cmp #'9'+1
bcs _stop
_calc
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
and #$0f
clc
adc P8ZP_SCRATCH_B1
ora cx16.r0
sta cx16.r0
iny
bne _loop
_stop
sty cx16.r15L
rts
_add_letter
pha
lda #9
sta P8ZP_SCRATCH_B1
pla
jmp _calc
_try_iso
cmp #103
bcs _stop
and #63
bne _add_letter
; !notreached!
}}
}
asmsub bin2uword(str string @AY) -> uword @AY {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
+61 -3
View File
@@ -68,7 +68,65 @@ smallringbuffer {
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
smallstack {
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
ubyte[256] buffer
ubyte sp = 255
sub init() {
sp = 255
}
sub size() -> ubyte {
return 255-sp
}
sub free() -> ubyte {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==255
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer[sp] = lsb(value)
sp--
buffer[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer[sp]
sp++
cx16.r0L = buffer[sp]
return cx16.r0
}
}
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
@@ -78,7 +136,7 @@ stack {
ubyte bank
sub init(ubyte rambank) {
; -- initialize the stack, must be called before use. Supply the HiRAM bank to use as buffer space.
; -- initialize the stack, must be called before use. Supply the HIRAM bank to use as buffer space.
sp = $bfff
bank = rambank
}
@@ -157,7 +215,7 @@ ringbuffer {
ubyte bank = 255 ; set via init()
sub init(ubyte rambank) {
; -- initialize the ringbuffer, must be called before use. Supply the HiRAM bank to use as buffer space.
; -- initialize the ringbuffer, must be called before use. Supply the HIRAM bank to use as buffer space.
head = $a000
tail = $bfff
fill = 0
+1 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
%import shared_compression
+58 -55
View File
@@ -999,15 +999,12 @@ io_error:
return false
}
sub f_seek(uword pos_hiword, uword pos_loword) {
sub f_seek(long position) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
; Note: this will not work if you have already read the last byte of the file! Then you must close and reopen the file first.
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
pokel(&command+2, position)
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
@@ -1016,13 +1013,10 @@ io_error:
}
sub f_seek_w(uword pos_hiword, uword pos_loword) {
sub f_seek_w(long position) {
; -- seek in the output file opened with f_open_w_seek, to the given 32-bits position
diskio.f_seek.command[1] = WRITE_IO_CHANNEL ; f_open_w uses this secondary address
diskio.f_seek.command[2] = lsb(pos_loword)
diskio.f_seek.command[3] = msb(pos_loword)
diskio.f_seek.command[4] = lsb(pos_hiword)
diskio.f_seek.command[5] = msb(pos_hiword)
pokel(&diskio.f_seek.command+2, position)
cbm.SETNAM(sizeof(diskio.f_seek.command), &diskio.f_seek.command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
@@ -1030,54 +1024,63 @@ io_error:
reset_write_channel() ; back to the write io channel
}
asmsub f_tell() -> uword @R0, uword @R1, uword @R2, uword @R3 {
; -- Returns the current read position of the opened read file,
; in R0 and R1 (low + high words) and the file size in R2 and R3 (low + high words).
; Returns 0 as size if the command is not supported by the DOS implementation/version.
%asm {{
jmp internal_f_tell
}}
}
sub internal_f_tell() {
; gets the (32 bits) position + file size of the opened read file channel
ubyte[2] command = ['t',0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
void cbm.CHKIN(15) ; use #15 as input channel
sub f_tell() -> long, long {
; -- Returns the current read position of the opened read file, and the file size.
; Returns 0,0 if the command is not supported by the DOS implementation/version.
ubyte[3] command = ['?','?',0]
bool success=false
; valid response starts with "07," followed by hex notations of the position and filesize
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
cx16.r1 = read4hex()
cx16.r0 = read4hex() ; position in R1:R0
void cbm.CHRIN() ; separator space
cx16.r3 = read4hex()
cx16.r2 = read4hex() ; filesize in R3:R2
success = true
long filesize, filepos
ubyte commandoffset
command[1] = 't'
commandoffset = 1
f_tell_internal()
return filepos, filesize
sub f_tell_internal() {
; this code is used for both the T (tell) and FL (fatlba) commands
command[2] = READ_IO_CHANNEL ; f_open uses this secondary address
cbm.SETNAM(sizeof(command)-commandoffset, &command+commandoffset)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
void cbm.CHKIN(15) ; use #15 as input channel
; valid response starts with "07," followed by hex notations of the position and filesize
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
filepos = read8hex()
void cbm.CHRIN() ; separator space
filesize = read8hex()
success = true
} else {
filesize = filepos = 0
}
while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
}
cbm.CLOSE(15)
reset_read_channel() ; back to the read io channel
}
while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
}
cbm.CLOSE(15)
reset_read_channel() ; back to the read io channel
if success
return
cx16.r0 = cx16.r1 = cx16.r2 = cx16.r3 = 0
}
sub read4hex() -> uword {
str hex = "0000"
hex[0] = cbm.CHRIN()
hex[1] = cbm.CHRIN()
hex[2] = cbm.CHRIN()
hex[3] = cbm.CHRIN()
return conv.hex2uword(hex)
sub f_fatlba() -> long, long {
; -- Return the current LBA, cluster number (sector index and shift value are ignored for now)
diskio.f_tell.command[0] = 'f'
diskio.f_tell.command[1] = 'l'
diskio.f_tell.commandoffset = 0
diskio.f_tell.f_tell_internal()
return diskio.f_tell.filepos, diskio.f_tell.filesize
}
sub read8hex() -> long {
str hex = "00000000"
for cx16.r0L in 0 to 7 {
hex[cx16.r0L] = cbm.CHRIN()
}
return conv.hex2long(hex)
}
}
+2
View File
@@ -14,6 +14,8 @@ floats {
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
const uword FAC_ADDR = $c3
extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. DON'T USE THIS, USE WRAPPER 'AYINT2' INSTEAD. (might throw ILLEGAL QUANTITY)
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
+58 -19
View File
@@ -463,7 +463,7 @@ extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobb
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
extsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) ; note: The the zero page address containing the base address is passed in stavec ($03B2)
extsub $ff7d = PRIMM()
extsub $ff7d = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
; high level graphics & fonts
extsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y)
@@ -620,6 +620,10 @@ const ubyte EXTAPI_scnsiz = $0D
const ubyte EXTAPI_kbd_leds = $0E
const ubyte EXTAPI_memory_decompress_from_func = $0F
const ubyte EXTAPI_default_palette = $10
const ubyte EXTAPI_has_machine_property = $11
const ubyte EXTAPI_kbdbuf_get = $12
const ubyte EXTAPI_kbdbuf_clear = $13
const ubyte EXTAPI_blink_enable = $14
; extapi16 call numbers
const ubyte EXTAPI16_test = $00
@@ -676,6 +680,30 @@ sub mouse_present() -> bool {
return cx16.r0L != $fc ; $fc = BAT_FAIL
}
asmsub get_charset() -> ubyte @A {
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
%asm {{
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
lda KERNAL_MODE
beq _end
bit #$40 ;ISO mode flag
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
lda #1
bra _end
+ bit #1 ;PETSCII upper case/graphics
bne + ;usually KERNAL_MODE 2 or 4
lda #2
bra _end
+ lda #3 ;PETSCII upper/lower case
_end:
rts
}}
}
; shims for the kernal routines called via the extapi call:
asmsub mouse_set_pos(uword xpos @R0, uword ypos @R1) clobbers(X) {
@@ -758,31 +786,42 @@ asmsub set_default_palette() {
}}
}
asmsub get_charset() -> ubyte @A {
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
asmsub has_machine_property(ubyte property @X) clobbers(A,X) -> bool @Pc {
; requires rom v49+
%asm {{
lda #EXTAPI_has_machine_property
jmp cx16.extapi
}}
}
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
lda KERNAL_MODE
beq _end
asmsub kbdbuf_get() clobbers(X,Y) -> ubyte @A {
; requires rom v49+
%asm {{
lda #EXTAPI_kbdbuf_get
jmp cx16.extapi
}}
}
bit #$40 ;ISO mode flag
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
lda #1
bra _end
asmsub kbdbuf_clear() clobbers(A) {
; requires rom v49+
%asm {{
lda #EXTAPI_kbdbuf_clear
jmp cx16.extapi
}}
}
+ bit #1 ;PETSCII upper case/graphics
bne + ;usually KERNAL_MODE 2 or 4
lda #2
bra _end
+ lda #3 ;PETSCII upper/lower case
_end:
rts
asmsub blink_enable(bool enable @X) clobbers(A,X) {
; requires rom v49+
%asm {{
txa
and #1
eor #1
tax
lda #EXTAPI_blink_enable
jmp cx16.extapi
}}
}
; TODO : implement shims for the remaining extapi calls.
; ---- end of kernal routines ----
+2 -2
View File
@@ -182,8 +182,8 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
}
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
; ---- set char at the given position on the screen. charcolor is ignored on PET
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
; ---- set char at the given position on the screen. NOTE: charcolor is ignored on PET
%asm {{
lda row
asl a
+1 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
; This file contains the shared routines that work on all targets.
compression {
+20
View File
@@ -279,6 +279,26 @@ sub hex2uword(str string) -> uword {
}
}
sub hex2long(str string) -> long {
; -- hexadecimal string (with or without '$') to long.
; stops parsing at the first character that's not a hex digit (except leading $)
long result
ubyte char
if @(string)=='$'
string++
repeat {
char = @(string)
if char==0
return result
result <<= 4
if char>='0' and char<='9'
result |= char-'0'
else
result |= char-'a'+10
string++
}
}
sub bin2uword(str string) -> uword {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
+3 -3
View File
@@ -60,13 +60,13 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
@@ -145,7 +145,7 @@ private fun compileMain(args: Array<String>): Boolean {
}
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
System.err.println("On the Commander X16, HIRAM bank 0 is used by the kernal and can't be used.")
return false
}
@@ -497,8 +497,8 @@ internal class AstChecker(private val program: Program,
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
}
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && param.first.type!=DataType.pointer(BaseDataType.UBYTE)) {
err("parameter '${param.first.name}' should be (u)word, str or ^^ubyte")
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && !param.first.type.isPointer) {
err("parameter '${param.first.name}' should be (u)word, str or a pointer")
}
}
else if(param.second.statusflag!=null) {
@@ -2093,7 +2093,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memread: DirectMemoryRead) {
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
if(memread.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memread.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
val pointervar = memread.addressExpression as? IdentifierReference
@@ -2126,7 +2128,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
if(memwrite.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memwrite.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
val pointervar = memwrite.addressExpression as? IdentifierReference
@@ -339,8 +339,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memread.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -354,8 +359,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memwrite.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -480,18 +480,31 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
if(addressOf.arrayIndex!=null) {
val tgt = addressOf.identifier?.constValue(program)
if (tgt != null && tgt.type.isWord) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(
IAstModification.ReplaceNode(addressOf, add, parent)
)
val tgt = addressOf.identifier
if(tgt!=null) {
val constAddress = tgt.constValue(program)
if (constAddress != null && constAddress.type.isInteger) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) issimpletype constAddress.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(constAddress.type), true, indexExpr.position)
val add = BinaryExpression(constAddress, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
} else {
val decl = tgt.targetVarDecl()
if(decl!=null && decl.datatype.isInteger) {
// &addressvar[idx] --> addressvar + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) istype decl.datatype)
indexExpr
else
TypecastExpression(indexExpr, decl.datatype, true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
}
}
}
}
return noModifications
+21 -4
View File
@@ -16,10 +16,7 @@ import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.code.target.C128Target
import prog8.code.target.C64Target
import prog8.code.target.PETTarget
import prog8.code.target.VMTarget
import prog8.code.target.*
import prog8tests.helpers.*
@@ -438,4 +435,24 @@ main {
errors.errors[0] shouldContain "8:9: cannot assign to an array or string that is located in ROM"
errors.errors[1] shouldContain "9:9: cannot assign to an array or string that is located in ROM"
}
test("long addresses not yet supported in memread and memwrite") {
val src = """
main {
sub start() {
const long bufferll = 888888
long @shared addrlv = 777777
cx16.r0L = bufferll[2]
cx16.r1L = @(999999)
cx16.r2L = @(addrlv)
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), true, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "long address not yet supported"
errors.errors[1] shouldContain "long address not yet supported"
errors.errors[2] shouldContain "long address not yet supported"
}
})
+19 -11
View File
@@ -306,26 +306,34 @@ main {
(waDeclV as NumericLiteral).number shouldBe 40960.0
}
test("address of a const uword pointer array expression") {
test("address of a const integer pointer array expression") {
val src = """
main {
sub start() {
const uword buffer = 2000
const long bufferl = 999999
uword @shared addr = &buffer[2]
const ubyte width = 100
ubyte @shared i
ubyte @shared j
uword @shared addr2 = &buffer[i * width + j]
long @shared addr2 = &bufferl[2]
const uword width = 100
uword @shared addr3 = &buffer[width]
long @shared addr4 = &bufferl[width]
}
}"""
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = true)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 11
val assignAddr = (st[2] as Assignment).value
(assignAddr as NumericLiteral).number shouldBe 2002.0
val assignAddr2 = (st[9] as Assignment).value as BinaryExpression
assignAddr2.operator shouldBe "+"
st.size shouldBe 12
val assignAddr = (st[3] as Assignment).value as NumericLiteral
assignAddr.number shouldBe 2002.0
assignAddr.type shouldBe BaseDataType.UWORD
val assignAddr2 = (st[5] as Assignment).value as NumericLiteral
assignAddr2.number shouldBe 1000001.0
assignAddr2.type shouldBe BaseDataType.LONG
val assignAddr3 = (st[8] as Assignment).value as NumericLiteral
assignAddr3.number shouldBe 2100
assignAddr3.type shouldBe BaseDataType.UWORD
val assignAddr4 = (st[10] as Assignment).value as NumericLiteral
assignAddr4.number shouldBe 1000099.0
assignAddr4.type shouldBe BaseDataType.LONG
}
test("out of range const byte and word give correct error") {
@@ -572,14 +572,15 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array
val index = arrayIndex?.constIndex()
if (index != null) {
address += when {
target.datatype.isUnsignedWord -> index
target.datatype.isInteger -> index
target.datatype.isArray -> program.memsizer.memorySize(targetVar.datatype, index)
else -> throw FatalAstException("need array or uword ptr")
else -> throw FatalAstException("need array or ptr")
}
} else
return null
}
return NumericLiteral(BaseDataType.UWORD, address, position)
val addressType = if(targetVar.datatype.isLong) BaseDataType.LONG else BaseDataType.UWORD
return NumericLiteral(addressType, address, position)
}
}
}
@@ -1416,14 +1417,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
// the value of a variable can (temporarily) be a different type as the vardecl itself.
// don't return the value if the types don't match yet!
val value = vardecl.value?.constValue(program)
if(value==null || value.type==vardecl.datatype.base)
return value
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
if(optimal.type==vardecl.datatype.base)
return optimal
return null
val casted = value.cast(vardecl.datatype.base, true)
return if(casted.isValid)
casted.valueOrZero()
else
null
}
override fun toString(): String {
@@ -45,6 +45,20 @@ smallringbuffer {
size () -> ubyte
}
smallstack {
ubyte[] buffer
ubyte sp
free () -> ubyte
init ()
isempty () -> bool
isfull () -> bool
pop () -> ubyte
popw () -> uword
push (ubyte value)
pushw (uword value)
size () -> ubyte
}
stack {
uword buffer_ptr
uword sp
@@ -99,6 +113,7 @@ conv {
ubyte[] @shared string_out
any2uword (str string @AY) -> uword @AY, ubyte @X
bin2uword (str string @AY) -> uword @AY
hex2long (str string @AY) -> long @R0R1_32
hex2uword (str string @AY) -> uword @AY
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
@@ -293,7 +308,9 @@ strings {
lstripped (str s) -> str
ltrim (str s)
ltrimmed (str s) -> str
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
@@ -45,6 +45,20 @@ smallringbuffer {
size () -> ubyte
}
smallstack {
ubyte[] buffer
ubyte sp
free () -> ubyte
init ()
isempty () -> bool
isfull () -> bool
pop () -> ubyte
popw () -> uword
push (ubyte value)
pushw (uword value)
size () -> ubyte
}
stack {
uword buffer_ptr
uword sp
@@ -99,6 +113,7 @@ conv {
ubyte[] @shared string_out
any2uword (str string @AY) -> uword @AY, ubyte @X
bin2uword (str string @AY) -> uword @AY
hex2long (str string @AY) -> long @R0R1_32
hex2uword (str string @AY) -> uword @AY
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
@@ -208,6 +223,7 @@ LIBRARY MODULE NAME: floats
floats {
const float E
const float EPSILON
const uword FAC_ADDR
const float PI
const float TWOPI
const float π
@@ -420,7 +436,9 @@ strings {
lstripped (str s) -> str
ltrim (str s)
ltrimmed (str s) -> str
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
@@ -84,6 +84,20 @@ smallringbuffer {
size () -> ubyte
}
smallstack {
ubyte[] buffer
ubyte sp
free () -> ubyte
init ()
isempty () -> bool
isfull () -> bool
pop () -> ubyte
popw () -> uword
push (ubyte value)
pushw (uword value)
size () -> ubyte
}
stack {
ubyte bank
uword sp
@@ -137,6 +151,7 @@ conv {
ubyte[] @shared string_out
any2uword (str string @AY) -> uword @AY, ubyte @X
bin2uword (str string @AY) -> uword @AY
hex2long (str string @AY) -> long @R0R1_32
hex2uword (str string @AY) -> uword @AY
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
@@ -216,20 +231,20 @@ diskio {
exists (str filename) -> bool
f_close ()
f_close_w ()
f_fatlba () -> long, long
f_open (str filenameptr) -> bool
f_open_w (str filename) -> bool
f_open_w_seek (str filename) -> bool
f_read (uword bufferpointer, uword num_bytes) -> uword
f_read_all (uword bufferpointer) -> uword
f_readline (^^ubyte bufptr @AY) clobbers (X) -> ubyte @Y, ubyte @A
f_seek (uword pos_hiword, uword pos_loword)
f_seek_w (uword pos_hiword, uword pos_loword)
f_tell () -> uword @R0, uword @R1, uword @R2, uword @R3
f_seek (long position)
f_seek_w (long position)
f_tell () -> long, long
f_write (uword bufferpointer, uword num_bytes) -> bool
fastmode (ubyte mode) -> bool
get_loadaddress (str filename) -> uword
internal_f_open_w (str filename, bool open_for_seeks) -> bool
internal_f_tell ()
internal_load_routine (str filenameptr, uword address_override, bool headerless) -> uword
internal_save_routine (str filenameptr, uword startaddress, uword savesize, bool headerless) -> bool
lf_end_list ()
@@ -243,7 +258,7 @@ diskio {
load_size (ubyte startbank, uword startaddress, uword endaddress) -> long
loadlib (str libnameptr, uword libaddress) -> uword
mkdir (str name)
read4hex () -> uword
read8hex () -> long
relabel (str name)
rename (str oldfileptr, str newfileptr)
reset_read_channel ()
@@ -300,6 +315,7 @@ LIBRARY MODULE NAME: floats
floats {
const float E
const float EPSILON
const uword FAC_ADDR
const float PI
const float TWOPI
const float π
@@ -732,7 +748,9 @@ strings {
lstripped (str s) -> str
ltrim (str s)
ltrimmed (str s) -> str
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
@@ -835,13 +853,17 @@ cx16 {
const ubyte EXTAPI16_stack_pop
const ubyte EXTAPI16_stack_push
const ubyte EXTAPI16_test
const ubyte EXTAPI_blink_enable
const ubyte EXTAPI_clear_status
const ubyte EXTAPI_cursor_blink
const ubyte EXTAPI_default_palette
const ubyte EXTAPI_getlfs
const ubyte EXTAPI_has_machine_property
const ubyte EXTAPI_iso_cursor_char
const ubyte EXTAPI_joystick_ps2_keycodes
const ubyte EXTAPI_kbd_leds
const ubyte EXTAPI_kbdbuf_clear
const ubyte EXTAPI_kbdbuf_get
const ubyte EXTAPI_led_update
const ubyte EXTAPI_memory_decompress_from_func
const ubyte EXTAPI_mouse_set_position
@@ -1141,6 +1163,7 @@ cx16 {
bas_psgnote (ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers (A,X,Y) -> bool @Pc @bank 10 = $c012
bas_psgplaystring (ubyte length @A, str string @XY) clobbers (A,X,Y) @bank 10 = $c018
bas_psgwav (ubyte voice @A, ubyte waveform @X) clobbers (A,X,Y) -> bool @Pc @bank 10 = $c015
blink_enable (bool enable @X) clobbers (A,X)
clock_get_date_time () clobbers (A,X,Y) -> uword @R0, uword @R1, uword @R2, uword @R3 = $ff50
clock_set_date_time (uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, uword jiffiesweekday @R3) clobbers (A,X,Y) = $ff4d
console_get_char () clobbers (X,Y) -> ubyte @A = $fee1
@@ -1163,6 +1186,7 @@ cx16 {
getlfs () -> ubyte @X, ubyte @A, ubyte @Y
getrambank () -> ubyte @A
getrombank () -> ubyte @A
has_machine_property (ubyte property @X) clobbers (A,X) -> bool @Pc
i2c_batch_read (ubyte device @X, uword buffer @R0, uword length @R1, bool advance @Pc) clobbers (A,Y) -> bool @Pc = $feb4
i2c_batch_write (ubyte device @X, uword buffer @R0, uword length @R1, bool advance @Pc) clobbers (A,Y) -> bool @Pc = $feb7
i2c_read_byte (ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc = $fec6
@@ -1172,6 +1196,8 @@ cx16 {
joystick_scan () clobbers (A,X,Y) = $ff53
joysticks_detect () -> ubyte
joysticks_getall (bool also_keyboard_js) -> uword
kbdbuf_clear () clobbers (A)
kbdbuf_get () clobbers (X,Y) -> ubyte @A
kbdbuf_get_modifiers () -> ubyte @A = $fec0
kbdbuf_peek () -> ubyte @A, ubyte @X = $febd
kbdbuf_put (ubyte key @A) clobbers (X) = $fec3
@@ -45,6 +45,20 @@ smallringbuffer {
size () -> ubyte
}
smallstack {
ubyte[] buffer
ubyte sp
free () -> ubyte
init ()
isempty () -> bool
isfull () -> bool
pop () -> ubyte
popw () -> uword
push (ubyte value)
pushw (uword value)
size () -> ubyte
}
stack {
uword buffer_ptr
uword sp
@@ -99,6 +113,7 @@ conv {
ubyte[] @shared string_out
any2uword (str string @AY) -> uword @AY, ubyte @X
bin2uword (str string @AY) -> uword @AY
hex2long (str string @AY) -> long @R0R1_32
hex2uword (str string @AY) -> uword @AY
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
@@ -244,7 +259,9 @@ strings {
lstripped (str s) -> str
ltrim (str s)
ltrimmed (str s) -> str
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
@@ -537,7 +554,7 @@ txt {
scroll_left () clobbers (A,X,Y)
scroll_right () clobbers (A,X)
scroll_up () clobbers (A,X)
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored)
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col, ubyte row, ubyte color)
size () clobbers (A) -> ubyte @X, ubyte @Y
@@ -25,6 +25,20 @@ smallringbuffer {
size () -> ubyte
}
smallstack {
ubyte[] buffer
ubyte sp
free () -> ubyte
init ()
isempty () -> bool
isfull () -> bool
pop () -> ubyte
popw () -> uword
push (ubyte value)
pushw (uword value)
size () -> ubyte
}
stack {
uword buffer_ptr
uword sp
@@ -75,6 +89,7 @@ conv {
str string_out
any2uword (str string) -> uword, ubyte
bin2uword (str string) -> uword
hex2long (str string) -> long
hex2uword (str string) -> uword
internal_str_ub (ubyte value, str out_ptr)
internal_str_uw (uword value, str out_ptr)
@@ -311,7 +326,9 @@ strings {
lstripped (str s) -> str
ltrim (str s)
ltrimmed (str s) -> str
nappend (str target, str suffix, ubyte maxlength) -> ubyte
ncompare (str st1, str st2, ubyte length) -> byte
ncopy (str source, str target, ubyte maxlength) -> ubyte
rfind (str stringptr, ubyte character) -> ubyte, bool
right (str source, ubyte slen, str target)
rstrip (str s)
+1 -1
View File
@@ -18,7 +18,7 @@ The language
- Identifiers and string literals can contain non-ASCII characters so for example ``knäckebröd`` and ``見せしめ`` are valid identifiers.
- There's usually a single statement per line. There is no statement separator.
- Semicolon ``;`` is used to start a line comment. Multi-line comments are also possible by enclosing it all in ``/*`` and ``*/``.
- Ternary operator ``x ? value1 : value2`` is available in the form of an *if-expression*: ``if x value1 else value2``
- Ternary operator ``x ? value1 : value2`` is available in the form of an *if-expression*: ``if x [then] value1 else value2``
- There's a Swift/Zig/Go style ``defer`` statement for delayed cleanup is available in the subroutine scope.
- Qualified names are searched from within the top level namespace (so you have to provide the full qualified name). Unqualified names are locally scoped.
- A trailing comma is allowed optionally in array literals: [1,2,3,] is a valid array of values 1, 2 and 3.
+2 -2
View File
@@ -228,7 +228,7 @@ One or more .p8 module files
``-slabshigh``
put memory() slabs in high memory area instead of at the end of the program.
On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.
On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.
``-nosourcelines``
Do not include the original prog8 source code lines as comments in the generated assembly code file,
@@ -263,7 +263,7 @@ One or more .p8 module files
c64: $C000 - $CFFF ; 4 kB, and the specified rambank number is ignored
cx16: $A000 - $BFFF ; 8 kB in the specified HiRAM bank (note: no auto bank switching is done, you must make sure yourself that this HiRAM bank is active when accessing these variables!)
cx16: $A000 - $BFFF ; 8 kB in the specified HIRAM bank (note: no auto bank switching is done, you must make sure yourself that this HIRAM bank is active when accessing these variables!)
If you use this option, you can no longer use the part of the above memory area that is
alotted to the variables, for your own purposes. The output of the 64tass assembler step at the
+4
View File
@@ -200,3 +200,7 @@ If multiple options are listed above, you can select which one you want to launc
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
Look in the :source:`syntax-files <syntax-files>` directory in the github repository to find them.
**CTags:** for editors that support them, such as Vim, there is an effort by a member of the cummunity to provide "ctags" files for Prog8.
The basic purpose of ctags is to index the definitions from your source code and let you find them easily.
It is not really to find each use of print for example, but to find where print is defined.
Visit `the Github repository <https://github.com/gillham/prog8ctags>`_ for the files and detailed usage information.
+53 -45
View File
@@ -312,7 +312,7 @@ Routines to load and save "BMX" files, the CommanderX16 bitmap file format:
`BMX file format specification <https://cx16forum.com/forum/viewtopic.php?t=6945>`_
Only the *uncompressed* bitmaps variant is supported in this library for now.
The routines are designed to be fast and bulk load/save the data directly into or from vram,
The routines are designed to be fast and they bulk load/save the data directly into or from vram,
without the need to buffer something in main memory.
For details about what routines are available, have a look at
@@ -320,25 +320,44 @@ the :source:`bmx source code <compiler/res/prog8lib/cx16/bmx.p8>` .
There's also the "showbmx" example to look at.
buffers (experimental)
^^^^^^^^^^^^^^^^^^^^^^
buffers
^^^^^^^
A small library providing a 8 KB stack, an 8 KB ringbuffer, and a fast 256 bytes ringbuffer.
API is experimental and may change or disappear in a future version.
Stack is a LIFO container, ringbuffers are FIFO containers.
On the Commander X16 the stack and ringbuffer will use a HiRAM bank instead of system ram,
you have to initialize that via the init(bank) routine.
Provides few data buffer routines. These are available:
Read the :source:`buffers source code <compiler/res/prog8lib/diskio.p8>`
to see what's in there. Note that the init() routines have that extra bank parameter on the X16.
- ``smallstack`` a fast 256 byte stack (LIFO) that is independent of the CPU stack.
- ``smallringbuffer`` a fast 256 byte ringbuffer (FIFO).
- ``stack`` a stack (LIFO) with up to 8Kb of data.
- ``ringbuffer`` a ringbuffer (FIFO) with up to 8Kb of data.
On the Commander X16 the 8Kb stack and ringbuffer implementations use a HIRAM bank instead of regular system memory.
You tell it which bank to use by calling ``init(bank)`` with the bank number as argument.
Read the :source:`buffers source code <compiler/res/prog8lib/buffers.p8>`
to see what's in there. Note that on the X16, the init() routines have that extra bank parameter.
compression (slightly experimental)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cbm
^^^
Commodore (CBM) common variables, vectors and kernal routines. This 'library' is part of syslib and as such is always available.
There's too much in this library to list in the docs here, but you can find things like:
Routines for data compression and decompression. Currently only the 'ByteRun1' aka 'PackBits' RLE encoding
is available. This is the compression that was also used in Amiga IFF images and in old MacPaint images.
API is slightly experimental and may change in a future version.
- memory mapped variables such as ``TIME_HI``, ``TIME_MID`` and ``TIME_LO`` (the 3 bytes making up the jiffy clock)
- cbm kernal routine extsubs such as ``CHROUT`` and ``PLOT``
- helper routines such as ``GETIN2`` and ``RDTIML`` (convenience wrappers around existing kernal routines)
It has way too much to include here, you have to study the
:source:`syslib source code <compiler/res/prog8lib/c64/syslib.p8>` to see what is there.
The version linked here is the C64 version itself - on other targets in the CBM family (PET, C128 etc)
it will contain different things based on what is available there. But the stuff common to the CBM family of compiler targets is there.
compression
^^^^^^^^^^^
Routines for data compression and decompression.
For compression the 'ByteRun1' aka 'PackBits' RLE encoding is available, this is the compression that was used in old MacPaint and Amiga IFF images.
Decompressors are available for RLE, TSCrunch and ZX0 (Salvador).
``encode_rle (uword data, uword size, uword target, bool is_last_block) -> uword``
Compress the given data block using ByteRun1 aka PackBits RLE encoding.
@@ -383,10 +402,10 @@ API is slightly experimental and may change in a future version.
Decompress a block of data compressed in the TSCrunch format *inplace*.
This can save an extra memory buffer if you are reading crunched data from a file into a buffer.
It has extremely fast decompression (approaching RLE speeds),
better compression as RLE, but slightly worse compression ration than LZSA.
better compression than RLE, but slightly worse compression ratio than LZSA.
See https://github.com/tonysavon/TSCrunch for the compression format and compressor tool.
**NOTE:** for speed reasons this decompressor is *not* bank-aware and *not* I/O register aware;
it only outputs to a memory buffer somewhere in the active 64 Kb address range.
it only outputs to a memory buffer somewhere in the 64 Kb main memory address range.
.. note::
The TSCrunch in-place format is a bit different than regular memory decompression.
@@ -410,7 +429,7 @@ API is slightly experimental and may change in a future version.
See https://github.com/emmanuel-marty/salvador for the compressor tool.
**NOTE:** You have to use it with the "-classic" option to produce a data format that this decoder can handle!
**NOTE:** for speed reasons this decompressor is *not* bank-aware and *not* I/O register aware;
it only outputs to a memory buffer somewhere in the active 64 Kb address range.
it only outputs to a memory buffer somewhere in the 64 Kb main memory address range.
conv
@@ -422,7 +441,6 @@ Routines to convert strings to numbers or vice versa.
- strings in decimal, hex and binary format into numbers (bytes, words)
Read the :source:`conv source code <compiler/res/prog8lib/conv.p8>`
to see what's in there.
@@ -430,13 +448,15 @@ coroutines
^^^^^^^^^^
Provides a system to make cooperative multitasking programs via coroutines.
A 'coroutine' is a subroutine whose execution you can pause and resume.
This library handles the voodoo for you to switch between such coroutines transparently,
so it can seem that your program is executing many subroutines at the same time.
A 'coroutine' is a subroutine whose execution can be paused and resumed. This is done
in a cooperative way where the coroutine calls ``coroutines.yield`` to pause and yield control back to the
system (which then selects the next coroutine to run).
This way it can seem that your program is executing many subroutines at the same time (without the use of an interrupt routine).
This library handles the voodoo required to switch between such coroutines.
Read the :source:`coroutines source code <compiler/res/prog8lib/coroutines.p8>`
to see what's in there. And look at the ``multitasking`` example to see how it can be used.
Here is a minimal example (if the library gets more stable, better docs will be written here)::
Better docs will be written here in the manual, at some point, but until then: here is a minimal example::
%import coroutine
@@ -460,12 +480,8 @@ Here is a minimal example (if the library gets more stable, better docs will be
cx16
^^^^
This is available on *all targets*, it is always imported as part of syslib.
On the Commander X16 this module contains a *whole bunch* of things specific to that machine.
It's way too much to include here, you have to study the
:source:`syslib source code <compiler/res/prog8lib/cx16/syslib.p8>`
to see what is there.
Despite its name, this 'module' is available on *all targets*; it also is always available because it is a part of syslib.
On the Commander X16 this module contains a *whole bunch* of things specific to that machine
On the other targets, it only contains the definition of the 16 memory-mapped virtual registers
(cx16.r0 - cx16.r15) and the following utility routines:
@@ -475,15 +491,9 @@ On the other targets, it only contains the definition of the 16 memory-mapped vi
``restore_virtual_registers()``
restore the values of all 16 virtual registers r0 - r15 from the buffer. Might be useful in an IRQ handler to avoid clobbering them.
``reset_system ()``
Soft-reset the system back to initial power-on BASIC prompt. (same as the routine in sys)
``poweroff_system ()``
Powers down the computer.
``set_led_brightness (ubyte brightness)``
Sets the brightness of the activity led on the computer.
For the Commander X16 version it has way too much to include here, you have to study the
:source:`syslib source code <compiler/res/prog8lib/cx16/syslib.p8>` to see what is there.
cx16logo
^^^^^^^^
@@ -565,8 +575,7 @@ You have to explicitly output a character 10 on the console to see a newline the
emudbg.console_write(iso:"hello\x0a")
Read the :source:`emudbg source code <compiler/res/prog8lib/cx16/emudbg.p8>`
to see what's in there.
Read the :source:`emudbg source code <compiler/res/prog8lib/cx16/emudbg.p8>` to see what's in there.
Information about the exposed debug registers is in the `emulator's documentation <https://github.com/X16Community/x16-emulator#debug-io-registers>`_.
@@ -715,8 +724,7 @@ math
Low-level integer math routines (which you usually don't have to bother with directly, but they are used by the compiler internally).
Pseudo-Random number generators (byte and word).
Various 8-bit integer trig functions that use lookup tables to quickly calculate sine and cosines.
Usually a custom lookup table is the way to go if your application needs these,
but perhaps the provided ones can be of service too.
checksumming
''''''''''''
@@ -1318,16 +1326,16 @@ textio (txt.*)
This will probably be the most used library module. It contains a whole lot of routines
dealing with text-based input and output (to the screen). Such as
- printing strings, numbers and booleans
- reading text input from the user via the keyboard
- printing strings, numbers and booleans: ``print``, ``chrout``, ``nl``, ``print_ub`` etc.
- reading text input from the user via the keyboard: ``input_chars``
- filling or clearing the screen and colors
- scrolling the text on the screen
- placing individual characters on the screen
- convert petscii to screencode characters
All routines work with Screencode character encoding, except `print`, `chrout` and `input_chars`,
these work with PETSCII encoding instead.
All routines work with Screencode character encoding, except ``print``, ``chrout`` and ``input_chars``, these work with PETSCII encoding instead.
There are *a lot of routines* in this module, many more than mentioned above.
Read the :source:`textio source code <compiler/res/prog8lib/cx16/textio.p8>`
to see what's in there. (Note: slight variations for different compiler targets)
+2 -2
View File
@@ -735,8 +735,8 @@ if expression
^^^^^^^^^^^^^
Similar to the if statement, but this time selects one of two possible values as the outcome of the expression,
depending on the condition. You write it as ``if <condition> <value1> else <value2>`` and it can be
used anywhere an expression is used to assign or pass a value.
depending on the condition. You write it as ``if <condition> [then] <value1> else <value2>`` (the then keyword is optional)
and it can be used anywhere an expression is used to assign or pass a value.
The first value will be used if the condition is true, otherwise the second value is used.
Sometimes it may be more legible if you surround the condition expression with parentheses so it is better
separated visually from the first value following it.
+9 -1
View File
@@ -357,4 +357,12 @@ There are a few things to note:
.. note::
The ROMable code generation is still quite experimental. Problems may still surface, and perhaps
stuff will change a bit in a future compiler version.
stuff will change a bit in a future compiler version.
Formal ANTLR4 syntax and grammar definition
-------------------------------------------
.. literalinclude :: ../../parser/src/main/antlr/Prog8ANTLR.g4
:language: antlr
+16 -7
View File
@@ -1,9 +1,13 @@
TODO
====
- before final release: test all examples and programs again with final version of the compiler!
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
- add cx16.r0r1sL, r2r3sL, ... etc to map signed longs on the virtual registers?
- implement rest of long comparisons in IfElseAsmGen compareLongValues(): expressions operands that might clobber the R14-R15 registers...
- struct/ptr: implement the remaining TODOs in PointerAssignmentsGen.
- struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
@@ -16,26 +20,25 @@ Future Things and Ideas
- struct/ptr: support for typed function pointers? (&routine could be typed by default as well then)
- struct/ptr: really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- struct/ptr: (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
- array-as-param bug: printf([1111,2,3,-4444]) gives argument type mismatch (param is of type uword), while printf([1111,2,3,4444]) just works fine (passes address of @nosplit array)
- make memory mapped variables support more constant expressions such as: &uword MyHigh = &mylong1+2
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?) i.e. make it a constant (see github issue #192)
- handle Alias in a general way in LiteralsToAutoVarsAndRecombineIdentifiers instead of replacing it scattered over multiple functions
- After long variable type is completed: make all constants long by default (remove type name altogether), reduce to target type implictly if the actual value fits.
- After long variable type is completed: make all constants long by default? or not? (remove type name altogether), reduce to target type implictly if the actual value fits.
This will break some existing programs that depend on value wrap arounds, but gives more intuitive constant number handling.
Can give descriptive error message for old syntax that still includes the type name?
- improve ANTLR grammar with better error handling (as suggested by Qwen AI)
- add documentation for more library modules instead of just linking to the source code
- add an Index to the documentation
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?) i.e. make it a constant (see github issue #192)
- sizeof(pointer) is now always 2 (an uword), make this a variable in the ICompilationTarget so that it could be 4 at the time we might ad a 32-bits 68000 target for example.
- Two- or even multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays?
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
- enums?
- fix the c64 multiplexer example
- romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
- Kotlin: can we use inline value classes in certain spots? (domain types instead of primitives)
- add float support to the configurable compiler targets
- add float support to the configurable compiler targets. Restrictions: just have "cbm-style floats" as an option (to that it can slot into the current float codegen), where all you have to specify is the addresses of AYINT and GIVAYF and FADDT and all their friends.
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
@@ -50,12 +53,15 @@ Future Things and Ideas
(this is already done hardcoded for several of the builtin functions)
- more support for (64tass) SEGMENTS in the prog8 syntax itself? maybe %segment blah in blocks?
- ability to use a sub instead of only a var for @bank ? what for though? dynamic bank/overlay loading?
- enums?
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
IR/VM
-----
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- optimize bool b = sgn(value)<0: still does a compare with 0 even though SGN sets all status bits. What is the code when a BIT instruction is used?
- optimize float<0 float==0 float>0 to use SGN instruction? Check what code is generated for other data types.
- getting it in shape for code generation: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- fix call() return value handling (... what's wrong with it again?)
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
- is it possible to use LOADFIELD/STOREFIELD instructions even more?
@@ -88,7 +94,8 @@ IR/VM
- implement fast code paths for TODO("inplace split....
- implement more TODOs in AssignmentGen
- do something with the 'split' tag on split word arrays
- add more optimizations in IRPeepholeOptimizer
- add more optimizations in IRPeepholeOptimizer, implement the TODOs in there at least
- extend the index range from 0-255 to 0-32767 in the LOADX, STOREX, LOADFIELD, STOREFIELD etc instructions (not compatible with 8 bit 6502, but the 68000 can use that)
- idea: replace all scalar variables that are not @shared by an allocated register. Keep a table of the variable to register mapping (including the datatype)
global initialization values are simply a list of LOAD instructions.
Variables replaced include all subroutine parameters? Or not? So the only variables that remain as variables are arrays and strings.
@@ -108,6 +115,7 @@ Libraries
Optimizations
-------------
- change float<0, float==0, float>0 to use sgn(float) instead? (also see IR)
- optimize inplaceLongShiftRight() for byte aligned cases
- more optimized operator handling of different types, for example uword a ^ byte b now does a type cast of b to word first
- optimize longEqualsValue() for const and variable operands to not assign needlessly to R0-R3.
@@ -121,6 +129,7 @@ Optimizations
- in Identifier: use typedarray of strings instead of listOf? Other places?
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
- optimize floats.cast_from_long and floats.cast_as_long by directly accessing FAC bits?
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
This will probably need the register categorization from the IR explained there, for the old 6502 codegen there is not enough information to act on
+2 -2
View File
@@ -33,7 +33,7 @@ main {
txt.print("\nseeking to 1292 and writing a few bytes...\n")
if diskio.f_open_w_seek("seektestfile.bin") {
diskio.f_seek_w(0, 1292)
diskio.f_seek_w(1292)
void diskio.f_write("123", 3)
diskio.f_close_w()
} else {
@@ -66,7 +66,7 @@ main {
; now the actual seek and read of the last few bytes
txt.print("\nseeking to 1290 and reading...\n")
if diskio.f_open("seektestfile.bin") {
diskio.f_seek(0, 1290)
diskio.f_seek(1290)
uword ptr = megabuffer
do {
size = diskio.f_read(ptr, 255)
+1 -1
View File
@@ -13,7 +13,7 @@ The ZSMKIT-A000.BIN included here is release version 2.6 of zsmkit.
DEMO PROGRAM
------------
The demo program loads the zsmkit player program in HiRAM bank 1.
The demo program loads the zsmkit player program in HIRAM bank 1.
The ZSM music file is read into memory too starting from bank 2.
The zmskit library routines are defined in zsmkit.p8 as extsubs,
using prog8's automatic ram banking mechanism.
+78 -24
View File
@@ -1,33 +1,87 @@
%import floats
%import textio
%zeropage basicsafe
main {
sub start() {
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
str name = "hello"
long @shared lv = -1
word @shared wv = -1
byte @shared bv = -1
float @shared fv = -1.1
bool b1, b2, b3, b4 = false
name[5] = '!' ; don't do this in real code...
name[5] = 0
name[6] = 99 ; out of bounds
name[-1] = 99 ; ok
name[-5] = 99 ; ok
b1 = bv<0
b2 = wv<0
b3 = lv<0
b4 = fv<0
txt.print_bool(b1)
txt.print_bool(b2)
txt.print_bool(b3)
txt.print_bool(b4)
txt.nl()
cx16.r0L = name[5]
cx16.r1L = name[6] ; out of bounds
cx16.r1L = name[-1] ; ok
cx16.r1L = name[-5] ; ok
b1=b2=b3=b4=false
b1 = sgn(bv)<0
b2 = sgn(wv)<0
b3 = sgn(lv)<0
b4 = sgn(fv)<0
txt.print_bool(b1)
txt.print_bool(b2)
txt.print_bool(b3)
txt.print_bool(b4)
txt.nl()
array[5] = '!'
array[5] = 0
array[6] = 99 ; out of bounds
array[-1] = 99 ; ok
array[-5] = 99 ; ok
array[-6] = 99 ; ok
cx16.r0L = array[5]
cx16.r1L = array[6] ; out of bounds
cx16.r1L = array[-1] ; ok
cx16.r1L = array[-5] ; ok
cx16.r1L = array[-6] ; ok
bv = 1
wv = 1
lv = 1
fv = 1.1
b1 = sgn(bv)<0
b2 = sgn(wv)<0
b3 = sgn(lv)<0
b4 = sgn(fv)<0
txt.print_bool(b1)
txt.print_bool(b2)
txt.print_bool(b3)
txt.print_bool(b4)
txt.nl()
}
/*
sub start2() {
uword uw
word sw
long lv
float fl
uw = 44555
fl = uw as float
txt.print_f(fl)
txt.nl()
fl /= 2
uw = fl as uword
txt.print_uw(uw)
txt.nl()
sw = -8888
fl = sw as float
txt.print_f(fl)
txt.nl()
fl /= 2
sw = fl as word
txt.print_w(sw)
txt.nl()
lv = -99886666
fl = lv as float
txt.print_f(fl)
txt.nl()
fl /= 2
lv = fl as long
txt.print_l(lv)
txt.nl()
}
*/
}
+1 -1
View File
@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.configuration-cache=false
kotlin.code.style=official
version=12.0-BETA6
version=12.0-BETA7
@@ -220,10 +220,12 @@ ffromub fpreg1, reg1 - fpreg1 = reg1 from usigned byte
ffromsb fpreg1, reg1 - fpreg1 = reg1 from signed byte
ffromuw fpreg1, reg1 - fpreg1 = reg1 from unsigned word
ffromsw fpreg1, reg1 - fpreg1 = reg1 from signed word
ffromsl fpreg1, reg1 - fpreg1 = reg1 from signed long
ftoub reg1, fpreg1 - reg1 = fpreg1 as unsigned byte
ftosb reg1, fpreg1 - reg1 = fpreg1 as signed byte
ftouw reg1, fpreg1 - reg1 = fpreg1 as unsigned word
ftosw reg1, fpreg1 - reg1 = fpreg1 as signed word
ftosl reg1, fpreg1 - reg1 = fpreg1 as signed long
fpow fpreg1, fpreg2 - fpreg1 = fpreg1 to the power of fpreg2
fabs fpreg1, fpreg2 - fpreg1 = abs(fpreg2)
fcomp reg1, fpreg1, fpreg2 - reg1 = result of comparison of fpreg1 and fpreg2: 0.b=equal, 1.b=fpreg1 is greater, -1.b=fpreg1 is smaller
@@ -398,10 +400,12 @@ enum class Opcode {
FFROMSB,
FFROMUW,
FFROMSW,
FFROMSL,
FTOUB,
FTOSB,
FTOUW,
FTOSW,
FTOSL,
FPOW,
FABS,
FSIN,
@@ -494,7 +498,8 @@ val OpcodesThatEndSSAblock = OpcodesThatBranchUnconditionally + arrayOf(
val OpcodesThatSetStatusbitsIncludingCarry = arrayOf(
Opcode.BIT,
Opcode.CMP,
Opcode.CMPI
Opcode.CMPI,
Opcode.SGN
)
val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
Opcode.LOAD,
@@ -787,10 +792,12 @@ val instructionFormats = mutableMapOf(
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMUW to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMSW to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMSL to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FTOUB to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOSB to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOUW to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOSW to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOSL to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FPOW to InstructionFormat.from("F,<>fr1,<fr2"),
Opcode.FABS to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FCOMP to InstructionFormat.from("F,>r1,<fr1,<fr2"),
@@ -1086,10 +1093,21 @@ data class IRInstruction(
private fun determineReg1Type(): IRDataType? {
if(type==IRDataType.FLOAT) {
// some float instructions have an integer (byte or word) register as well in reg1
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP, Opcode.LOADIX, Opcode.LOADX, Opcode.STOREX, Opcode.STOREIX, Opcode.STOREZX))
IRDataType.BYTE
else
IRDataType.WORD
return when (opcode) {
Opcode.FFROMUB,
Opcode.FFROMSB,
Opcode.FTOUB,
Opcode.FTOSB,
Opcode.FCOMP,
Opcode.LOADIX,
Opcode.LOADX,
Opcode.STOREX,
Opcode.STOREIX,
Opcode.STOREZX,
Opcode.SGN -> IRDataType.BYTE
Opcode.FFROMSL, Opcode.FTOSL -> IRDataType.LONG
else -> IRDataType.WORD
}
}
if(type==IRDataType.WORD) {
// some word instructions have byte reg1
@@ -41,6 +41,8 @@ sealed interface IPtSubroutine {
val availableFloatRegisters = mutableListOf(RegisterOrPair.FAC1) // just one value is possible
val availableLongRegisters = combinedLongRegisters.toMutableList()
availableLongRegisters.remove(firstRegister.first.registerOrPair)
fun getLongRegister(): RegisterOrPair {
val reg = availableLongRegisters.removeLastOrNull()
if(reg==null)
@@ -86,7 +88,7 @@ sealed interface IPtSubroutine {
}
}
fun getIntergerRegister(): RegisterOrPair {
fun getIntegerRegister(): RegisterOrPair {
val reg = availableIntegerRegisters.removeLastOrNull()
if(reg==null)
throw AssemblyError("out of registers for byte/word return type ${this.position}")
@@ -111,7 +113,7 @@ sealed interface IPtSubroutine {
when {
type.isFloat -> RegisterOrStatusflag(availableFloatRegisters.removeLastOrNull()!!, null) to type
type.isLong -> RegisterOrStatusflag(getLongRegister(), null) to type
type.isWordOrByteOrBool -> RegisterOrStatusflag(getIntergerRegister(), null) to type
type.isWordOrByteOrBool -> RegisterOrStatusflag(getIntegerRegister(), null) to type
else -> throw AssemblyError("unsupported return type $type")
}
}
+30 -4
View File
@@ -330,10 +330,12 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.FFROMSB -> InsFFROMSB(ins)
Opcode.FFROMUW -> InsFFROMUW(ins)
Opcode.FFROMSW -> InsFFROMSW(ins)
Opcode.FFROMSL -> InsFFROMSL(ins)
Opcode.FTOUB -> InsFTOUB(ins)
Opcode.FTOSB -> InsFTOSB(ins)
Opcode.FTOUW -> InsFTOUW(ins)
Opcode.FTOSW -> InsFTOSW(ins)
Opcode.FTOSL -> InsFTOSL(ins)
Opcode.FPOW -> InsFPOW(ins)
Opcode.FABS -> InsFABS(ins)
Opcode.FSIN -> InsFSIN(ins)
@@ -1431,12 +1433,26 @@ class VirtualMachine(irProgram: IRProgram) {
}
private fun InsSGN(i: IRInstruction) {
var sign: Int
when(i.type!!) {
IRDataType.BYTE -> registers.setSB(i.reg1!!, registers.getSB(i.reg2!!).toInt().sign.toByte())
IRDataType.WORD -> registers.setSB(i.reg1!!, registers.getSW(i.reg2!!).toInt().sign.toByte())
IRDataType.LONG -> registers.setSB(i.reg1!!, registers.getSL(i.reg2!!).sign.toByte())
IRDataType.FLOAT -> registers.setSB(i.reg1!!, registers.getFloat(i.fpReg1!!).sign.toInt().toByte())
IRDataType.BYTE -> {
sign = registers.getSB(i.reg2!!).toInt().sign
registers.setSB(i.reg1!!, sign.toByte())
}
IRDataType.WORD -> {
sign = registers.getSW(i.reg2!!).toInt().sign
registers.setSB(i.reg1!!, sign.toByte())
}
IRDataType.LONG -> {
sign = registers.getSL(i.reg2!!).sign
registers.setSB(i.reg1!!, sign.toByte())
}
IRDataType.FLOAT -> {
sign = registers.getFloat(i.fpReg1!!).sign.toInt()
registers.setSB(i.reg1!!, sign.toByte())
}
}
statusbitsComparison(sign, i.type!!)
nextPc()
}
@@ -2686,6 +2702,11 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc()
}
private fun InsFFROMSL(i: IRInstruction) {
registers.setFloat(i.fpReg1!!, registers.getSL(i.reg1!!).toDouble())
nextPc()
}
private fun InsFTOUB(i: IRInstruction) {
registers.setUB(i.reg1!!, registers.getFloat(i.fpReg1!!).toInt().toUByte())
nextPc()
@@ -2706,6 +2727,11 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc()
}
private fun InsFTOSL(i: IRInstruction) {
registers.setSL(i.reg1!!, registers.getFloat(i.fpReg1!!).toInt())
nextPc()
}
private fun InsFPOW(i: IRInstruction) {
val value = registers.getFloat(i.fpReg1!!)
val exponent = registers.getFloat(i.fpReg2!!)