mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9504711fc7 | ||
|
|
590feda903 | ||
|
|
6e7e2922bf | ||
|
|
01df1f0083 | ||
|
|
397299bd1d | ||
|
|
c275aacd38 | ||
|
|
6a6e18773e | ||
|
|
99e037489b | ||
|
|
fb5290e17b | ||
|
|
818774ab84 | ||
|
|
d667312acc | ||
|
|
64d0cd87a8 | ||
|
|
2fffbdde89 | ||
|
|
dc8ea31c49 | ||
|
|
f4ead66e91 | ||
|
|
314e7f5691 | ||
|
|
da31465b7f | ||
|
|
397a907088 | ||
|
|
50c6962e1f | ||
|
|
b7d1fb1342 | ||
|
|
afb458a7da | ||
|
|
da3c7f267f | ||
|
|
b7ba7c50b1 | ||
|
|
bdbfe7048b |
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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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: ----
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 %)
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
; **experimental** data compression/decompression routines, API subject to change!!
|
||||
; data compression/decompression routines
|
||||
|
||||
%import shared_compression
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 ----
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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 %)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
})
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!!)
|
||||
|
||||
Reference in new issue
Block a user