Compare commits

...

7 Commits

Author SHA1 Message Date
Irmen de Jong
8b86f97aaa Implement struct field long and signed word bitshifts. Fixes #194 2025-11-25 22:28:28 +01:00
Irmen de Jong
93135774e6 fix IR signed bitshift right 2025-11-25 22:16:28 +01:00
Irmen de Jong
a64f27c6b0 add cx16.r0r1sl, cx16.r2r3sl, ... that memory-map signed longs on the virtual registers 2025-11-25 21:13:03 +01:00
Irmen de Jong
69ef63c96d tweaks 2025-11-24 22:23:40 +01:00
Irmen de Jong
df1a2a1611 also optimize BRA+RTS into just BRA
release 12.0
2025-11-23 15:18:05 +01:00
Irmen de Jong
d19a3af9ed change some single use float global constants to their asm proc 2025-11-21 21:35:51 +01:00
Irmen de Jong
352c11ad9f optimize float<>0 into sgn(float)<>0 2025-11-21 00:57:43 +01:00
30 changed files with 424 additions and 119 deletions

View File

@@ -544,7 +544,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
val third = lines[2].value val third = lines[2].value
if(!haslabel(second)) { if(!haslabel(second)) {
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) { if ((" jmp" in first || "\tjmp" in first || " bra" in first || "\tbra" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[1].index, true, null) mods += Modification(lines[1].index, true, null)
} }
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) { else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {

View File

@@ -305,13 +305,13 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
"<<" -> { "<<" -> {
if(target.dt.isByte) inplaceByteShiftLeft(target, value) if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(target, value) else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
else if(target.dt.isLong) TODO("inplace long << ${target.position}") else if(target.dt.isLong) inplaceLongShiftLeft(target, value)
else throw AssemblyError("weird dt ${target.position}") else throw AssemblyError("weird dt ${target.position}")
} }
">>" -> { ">>" -> {
if(target.dt.isByte) inplaceByteShiftRight(target, value) if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(target, value) else if(target.dt.isWord) inplaceWordShiftRight(target, value)
else if(target.dt.isLong) TODO("inplace long >> ${target.position}") else if(target.dt.isLong) inplaceLongShiftRight(target, value)
else throw AssemblyError("weird dt ${target.position}") else throw AssemblyError("weird dt ${target.position}")
} }
"&", "and" -> { "&", "and" -> {
@@ -756,11 +756,129 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
asmgen.assignRegister(RegisterOrPair.AY, target) asmgen.assignRegister(RegisterOrPair.AY, target)
} }
private fun inplaceLongShiftRight(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
fun shift1signed() {
asmgen.out("""
ldy #${offset}+3
lda ($zpPtrVar),y
asl a ; save sign bit
lda ($zpPtrVar),y
ror a
sta ($zpPtrVar),y
dey
lda ($zpPtrVar),y
ror a
sta ($zpPtrVar),y
dey
lda ($zpPtrVar),y
ror a
sta ($zpPtrVar),y
dey
lda ($zpPtrVar),y
ror a
sta ($zpPtrVar),y""")
}
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
shift1signed()
} else if(number>1) {
asmgen.out(" ldx #$number")
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out(" ldx $varname")
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceWordShiftRight(target: PtrTarget, value: AsmAssignSource) { private fun inplaceWordShiftRight(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer) val (zpPtrVar, offset) = deref(target.pointer)
if(target.dt.isSigned) if(target.dt.isSigned) {
TODO("signed word shift right ${target.position} $value") // signed word shift right
fun shift1signed() {
asmgen.out("""
ldy #${offset+1u}
lda ($zpPtrVar),y
asl a ; save sign bit
lda ($zpPtrVar),y
ror a
sta ($zpPtrVar),y
dey
lda ($zpPtrVar),y
ror a
sta ($zpPtrVar),y""")
}
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
shift1signed()
} else if(number>1) {
asmgen.out(" ldx #$number")
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out(" ldx $varname")
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
shift1signed()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
} else {
// unsigned word shift right
fun shift1unsigned() { fun shift1unsigned() {
asmgen.out(""" asmgen.out("""
@@ -812,6 +930,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
else -> throw AssemblyError("weird source value $value") else -> throw AssemblyError("weird source value $value")
} }
} }
}
private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) { private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer) val (zpPtrVar, offset) = deref(target.pointer)
@@ -878,6 +997,68 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} }
} }
private fun inplaceLongShiftLeft(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
fun shift1() {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
asl a
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
rol a
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
rol a
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
rol a
sta ($zpPtrVar),y""")
}
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
shift1()
} else if(number>1) {
asmgen.out(" ldx #$number")
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out(" ldx $varname")
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceWordShiftLeft(target: PtrTarget, value: AsmAssignSource) { private fun inplaceWordShiftLeft(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer) val (zpPtrVar, offset) = deref(target.pointer)

View File

@@ -191,7 +191,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null) "&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null) "^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null) "<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null) ">>=" -> {
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
addInstr(inplaceInstrs, IRInstruction(opc, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
}
"or=" -> { "or=" -> {
val shortcutLabel = codeGen.createLabelName() val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null) addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)

View File

@@ -1140,6 +1140,16 @@ cx16 {
&word r14s = $1bfc &word r14s = $1bfc
&word r15s = $1bfe &word r15s = $1bfe
; signed long versions
&long r0r1sl = $1be0
&long r2r3sl = $1be4
&long r4r5sl = $1be8
&long r6r7sl = $1bec
&long r8r0sl = $1bf0
&long r10r11sl = $1bf4
&long r12r13sl = $1bf8
&long r14r15sl = $1bfc
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $1be0 &ubyte r0L = $1be0
&ubyte r1L = $1be2 &ubyte r1L = $1be2

View File

@@ -2,8 +2,7 @@
FL_ONE_const .byte 129 ; 1.0 FL_ONE_const .byte 129 ; 1.0
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0 FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2) ; note: don't add too many constants here because they all end up in the resulting program
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
.section BSS .section BSS
@@ -159,6 +158,9 @@ cast_from_long .proc
ldx cx16.r0L ldx cx16.r0L
ldy cx16.r0H ldy cx16.r0H
jmp MOVMF jmp MOVMF
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
; !notreached!
.pend .pend
cast_as_long .proc cast_as_long .proc

View File

@@ -2,8 +2,11 @@
func_sign_f_into_A .proc func_sign_f_into_A .proc
; sign in A, also sets status flags
jsr MOVFM jsr MOVFM
jmp SIGN jsr SIGN
cmp #0
rts
.pend .pend

View File

@@ -1150,6 +1150,16 @@ cx16 {
&word r14s = $cffc &word r14s = $cffc
&word r15s = $cffe &word r15s = $cffe
; signed long versions
&long r0r1sl = $cfe0
&long r2r3sl = $cfe4
&long r4r5sl = $cfe8
&long r6r7sl = $cfec
&long r8r0sl = $cff0
&long r10r11sl = $cff4
&long r12r13sl = $cff8
&long r14r15sl = $cffc
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0 &ubyte r0L = $cfe0
&ubyte r1L = $cfe2 &ubyte r1L = $cfe2

View File

@@ -217,6 +217,16 @@ cx16 {
&word r14s = $001e &word r14s = $001e
&word r15s = $0020 &word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r0sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $0002 &ubyte r0L = $0002
&ubyte r1L = $0004 &ubyte r1L = $0004

View File

@@ -605,6 +605,16 @@ cx16 {
&word r14s = $7ffc &word r14s = $7ffc
&word r15s = $7ffe &word r15s = $7ffe
; signed long versions
&long r0r1sl = $7fe0
&long r2r3sl = $7fe4
&long r4r5sl = $7fe8
&long r6r7sl = $7fec
&long r8r0sl = $7fe0
&long r10r11sl = $7fe4
&long r12r13sl = $7fe8
&long r14r15sl = $7fec
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0 &ubyte r0L = $7fe0
&ubyte r1L = $7fe2 &ubyte r1L = $7fe2

View File

@@ -160,6 +160,9 @@ sub log2(float value) -> float {
ldy #>FL_LOG2_const ldy #>FL_LOG2_const
jsr MOVFM jsr MOVFM
jmp FDIVT jmp FDIVT
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
; !notreached!
}} }}
} }

View File

@@ -307,6 +307,16 @@ cx16 {
&word r14s = $ff1e &word r14s = $ff1e
&word r15s = $ff20 &word r15s = $ff20
; signed long versions
&long r0r1sl = $ff02
&long r2r3sl = $ff06
&long r4r5sl = $ff0a
&long r6r7sl = $ff0e
&long r8r0sl = $ff12
&long r10r11sl = $ff16
&long r12r13sl = $ff1a
&long r14r15sl = $ff1e
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $ff02 &ubyte r0L = $ff02
&ubyte r1L = $ff04 &ubyte r1L = $ff04

View File

@@ -38,9 +38,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharLitAsExtsubArg") { test("testCharLitAsExtsubArg") {
val platform = Cx16Target() val platform = Cx16Target()
val result = compileText(platform, false, """ val result = compileText(platform, false, $$"""
main { main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A) extsub $FFD2 = chrout(ubyte ch @ A)
sub start() { sub start() {
chrout('\n') chrout('\n')
} }
@@ -61,9 +61,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharVarAsExtsubArg") { test("testCharVarAsExtsubArg") {
val platform = Cx16Target() val platform = Cx16Target()
val result = compileText(platform, false, """ val result = compileText(platform, false, $$"""
main { main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A) extsub $FFD2 = chrout(ubyte ch @ A)
sub start() { sub start() {
ubyte ch = '\n' ubyte ch = '\n'
chrout(ch) chrout(ch)
@@ -96,9 +96,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharConstAsExtsubArg") { test("testCharConstAsExtsubArg") {
val platform = Cx16Target() val platform = Cx16Target()
val result = compileText(platform, false, """ val result = compileText(platform, false, $$"""
main { main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A) extsub $FFD2 = chrout(ubyte ch @ A)
sub start() { sub start() {
const ubyte ch = '\n' const ubyte ch = '\n'
chrout(ch) chrout(ch)

View File

@@ -1030,9 +1030,9 @@ class Subroutine(override val name: String,
replacement.parent = this replacement.parent = this
} }
is NumericLiteral -> { is NumericLiteral -> {
if(node===asmAddress?.address) { if(node===asmAddress?.address || node==asmAddress?.address) {
asmAddress.address = replacement asmAddress.address = replacement
} else if(node===asmAddress?.varbank) { } else if(node==asmAddress?.varbank) {
asmAddress.constbank = replacement.number.toInt().toUByte() asmAddress.constbank = replacement.number.toInt().toUByte()
asmAddress.varbank = null asmAddress.varbank = null
} else throw FatalAstException("can't replace") } else throw FatalAstException("can't replace")

View File

@@ -1155,6 +1155,7 @@ so pay attention to any jumps and rts instructions in the inlined code!
- ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used) - ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used)
- ``cx16.r0s`` - ``cx16.r15s`` (memory-mapped **word** values, used when you need a signed word) - ``cx16.r0s`` - ``cx16.r15s`` (memory-mapped **word** values, used when you need a signed word)
- ``cx16.r0r1sl`` - ``cx16.r14r15sl`` (memory-mapped **long** values in 2 consecutive virtual registers, used when you need a signed long)
- ``cx16.r0H``, ``cx16.r0L`` (for each r0..r15; memory-mapped **ubyte** values, both bytes of the register) - ``cx16.r0H``, ``cx16.r0L`` (for each r0..r15; memory-mapped **ubyte** values, both bytes of the register)
- ``cx16.r0sH``, ``cx16.r0sL`` (for each r0..r15; memory-mapped **byte** values, both bytes of the register) - ``cx16.r0sH``, ``cx16.r0sL`` (for each r0..r15; memory-mapped **byte** values, both bytes of the register)
- ``cx16.r0bH``, ``cx16.r0bL`` (for each r0..r15; memory-mapped **bool** values, both bytes of the register) - ``cx16.r0bH``, ``cx16.r0bL`` (for each r0..r15; memory-mapped **bool** values, both bytes of the register)

View File

@@ -1,14 +1,17 @@
TODO TODO
==== ====
- before final release: test all examples and programs again with final version of the compiler!
Weird Heisenbug
^^^^^^^^^^^^^^^
- examples/cube3d-float crashes with div by zero error on C64 (works on cx16. ALready broken in v11, v10 still worked)
caused by the RTS after JMP removal in optimizeJsrRtsAndOtherCombinations (replacing it with a NOP makes the problem disappear !??!?)
Future Things and Ideas Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000 - 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... (github issue 196?)
- 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: implement the remaining TODOs in PointerAssignmentsGen.
- struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0 - struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- struct/ptr: optimize the float copying in assignIndexedPointer() (also word and long?) - struct/ptr: optimize the float copying in assignIndexedPointer() (also word and long?)
@@ -59,7 +62,6 @@ Future Things and Ideas
IR/VM IR/VM
----- -----
- 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. - 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!) - 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?) - fix call() return value handling (... what's wrong with it again?)
@@ -115,20 +117,21 @@ Libraries
Optimizations Optimizations
------------- -------------
- change float<0, float==0, float>0 to use sgn(float) instead? (also see IR) - (6502) optimize if sgn(value)<0: still does a compare with 0 even though SGN sets all status bits.
- longvar = lptr^^ , lptr2^^=lptr^^ now go via temporary registers, optimize this to avoid using temps. (seems like it is dereferencing the pointer first and then assigning the intermediate value)
- optimize inplaceLongShiftRight() for byte aligned cases - 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 - 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. - optimize longEqualsValue() for long const and variable operands to not assign needlessly to R0-R3.
- optimize optimizedBitwiseExpr() for const and variable operands to not assign needlessly to R0-R3. - optimize optimizedBitwiseExpr() for long const and variable operands to not assign needlessly to R0-R3.
- optimize inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account! - optimize inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account!
- optimize simple cases in funcPeekL and funcPokeL - optimize simple cases in funcPeekL and funcPokeL
- bind types in the Ast much sooner than the simplifiedAst creation, so that we maybe could get rid of InferredType ? - bind types in the Ast much sooner than the simplifiedAst creation, so that we maybe could get rid of InferredType ?
- longvar = lptr^^ now goes via temporary registers, optimize this to avoid using temps. Also check lptr^^ = lvar.
- Port more benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up. (see benchmark-c/ directory) - Port more benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up. (see benchmark-c/ directory)
- Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappeared. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again? - Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappeared. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again?
- in Identifier: use typedarray of strings instead of listOf? Other places? - in Identifier: use typedarray of strings instead of listOf? Other places?
- Compilation speed regression: test/comparisons/test_word_lte.p8 compilation takes twice as long as with prog8 10.5
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time - 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 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" ... but maybe postpone until codegen from IR, where it seems solved?
- optimize floats.cast_from_long and floats.cast_as_long by directly accessing FAC bits? - 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? - 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 for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest

View File

@@ -482,6 +482,16 @@ cx16 {
&word r14s = $1b1c &word r14s = $1b1c
&word r15s = $1b1e &word r15s = $1b1e
; signed long versions
&long r0r1sl = $1b00
&long r2r3sl = $1b04
&long r4r5sl = $1b08
&long r6r7sl = $1b0c
&long r8r0sl = $1b00
&long r10r11sl = $1b04
&long r12r13sl = $1b08
&long r14r15sl = $1b0c
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $1b00 &ubyte r0L = $1b00
&ubyte r1L = $1b02 &ubyte r1L = $1b02

View File

@@ -741,6 +741,16 @@ cx16 {
&word r14s = $002c &word r14s = $002c
&word r15s = $002e &word r15s = $002e
; signed long versions
&long r0r1sl = $0010
&long r2r3sl = $0014
&long r4r5sl = $0018
&long r6r7sl = $001c
&long r8r0sl = $0020
&long r10r11sl = $0024
&long r12r13sl = $0028
&long r14r15sl = $002c
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $0010 &ubyte r0L = $0010
&ubyte r1L = $0012 &ubyte r1L = $0012

View File

@@ -414,6 +414,16 @@ cx16 {
&word r14s = $001e &word r14s = $001e
&word r15s = $0020 &word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r0sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $0002 &ubyte r0L = $0002
&ubyte r1L = $0004 &ubyte r1L = $0004

View File

@@ -203,6 +203,16 @@ cx16 {
&word r14s = $cffc &word r14s = $cffc
&word r15s = $cffe &word r15s = $cffe
; signed long versions
&long r0r1sl = $cfe0
&long r2r3sl = $cfe4
&long r4r5sl = $cfe8
&long r6r7sl = $cfec
&long r8r0sl = $cff0
&long r10r11sl = $cff4
&long r12r13sl = $cff8
&long r14r15sl = $cffc
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0 &ubyte r0L = $cfe0
&ubyte r1L = $cfe2 &ubyte r1L = $cfe2

View File

@@ -203,6 +203,16 @@ cx16 {
&word r14s = $001e &word r14s = $001e
&word r15s = $0020 &word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r0sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $0002 &ubyte r0L = $0002
&ubyte r1L = $0004 &ubyte r1L = $0004

View File

@@ -199,6 +199,16 @@ cx16 {
&word r14s = $7ffc &word r14s = $7ffc
&word r15s = $7ffe &word r15s = $7ffe
; signed long versions
&long r0r1sl = $7fe0
&long r2r3sl = $7fe4
&long r4r5sl = $7fe8
&long r6r7sl = $7fec
&long r8r0sl = $7fe0
&long r10r11sl = $7fe4
&long r12r13sl = $7fe8
&long r14r15sl = $7fec
; ubyte versions (low and high bytes) ; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0 &ubyte r0L = $7fe0
&ubyte r1L = $7fe2 &ubyte r1L = $7fe2

View File

@@ -8,4 +8,4 @@ Look in the Makefile to see how to build or run the various programs.
and example program, you can find those efforts here on GitHub: https://github.com/adiee5/prog8-nes-target and example program, you can find those efforts here on GitHub: https://github.com/adiee5/prog8-nes-target
*gillham* has been working on a few other compilation targets, such as VIC-20 (various editions), Foenix, and CX16OS. *gillham* has been working on a few other compilation targets, such as VIC-20 (various editions), Foenix, and CX16OS.
you can find them here on GitHub: https://github.com/gillham/prog8targets These will be much more complete than the examples here. You can find them on GitHub: https://github.com/gillham/prog8targets

View File

@@ -13,6 +13,8 @@ adpcm {
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav ; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8) ; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8)
; ;
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
;
; NOTE: for speed reasons this implementation doesn't guard against clipping errors. ; NOTE: for speed reasons this implementation doesn't guard against clipping errors.
; if the output sounds distorted, lower the volume of the source waveform to 80% and try again etc. ; if the output sounds distorted, lower the volume of the source waveform to 80% and try again etc.

View File

@@ -14,6 +14,8 @@
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav ; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
; Or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set correct block size) ; Or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set correct block size)
; ;
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
;
main { main {

View File

@@ -29,7 +29,7 @@ main {
ubyte vera_rate ubyte vera_rate
sub start() { sub start() {
;; diskio.fastmode(1) diskio.fastmode(1)
txt.print("name of .wav file to play on drive 8: ") txt.print("name of .wav file to play on drive 8: ")
while 0==txt.input_chars(MUSIC_FILENAME) { while 0==txt.input_chars(MUSIC_FILENAME) {
; until user types a name... ; until user types a name...

View File

@@ -2,17 +2,17 @@
%import sprites %import sprites
%option no_sysinit %option no_sysinit
; play a raw pcm stereo 16 bit audio file at 16021 hz, ; play a raw pcm stereo 16 bit audio file at 14877 hz,
; with real-time VU meters and sample waveform displays. ; with real-time VU meters and sample waveform displays.
; you can make an appropriate pcm file with one of the following commands: ; you can make an appropriate pcm file with one of the following commands:
; ffmpeg -i input_audio_file -ac 2 -ar 16021 -f s16le -acodec pcm_s16le music.pcm ; ffmpeg -i input_audio_file -ac 2 -ar 14877 -f s16le -acodec pcm_s16le music.pcm
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 16021 -t raw music.pcm ; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 14877 -t raw music.pcm
main { main {
const ubyte vera_rate = 42 ; 16021 hz const ubyte vera_rate = 39 ; 14877 hz
str pcmfile = "music.pcm" ; see format specs mentioned above str pcmfile = "music.pcm" ; see format specs mentioned above
sub start() { sub start() {
@@ -34,7 +34,7 @@ main {
cx16.GRAPH_put_next_char(cx16.r9L) cx16.GRAPH_put_next_char(cx16.r9L)
cx16.r0 = 250 cx16.r0 = 250
cx16.r1 = 20 cx16.r1 = 20
for cx16.r9L in iso:"16kHz stereo" for cx16.r9L in iso:"15kHz stereo"
cx16.GRAPH_put_next_char(cx16.r9L) cx16.GRAPH_put_next_char(cx16.r9L)
cx16.rombank(0) ; activate kernal bank for faster calls cx16.rombank(0) ; activate kernal bank for faster calls

View File

@@ -8,6 +8,7 @@
; PRG size by a lot because they embed a large multiplication lookup table. ; PRG size by a lot because they embed a large multiplication lookup table.
%import textio
%import math %import math
%import syslib %import syslib
@@ -38,6 +39,8 @@ main {
repeat { repeat {
clear_particles() clear_particles()
update_particles() update_particles()
txt.home()
txt.print_uw(active_particles)
sys.waitvsync() sys.waitvsync()
sys.waitvsync() sys.waitvsync()
} }

View File

@@ -1,87 +1,54 @@
%import floats %option no_sysinit ; leave the CX16 defaults in place
%zeropage basicsafe ; don't step on BASIC zero page locations
%import textio %import textio
%zeropage basicsafe
main { main {
struct element {
ubyte type
long x
word w
}
sub start() { sub start() {
long @shared lv = -1 word w = -1111
word @shared wv = -1 long l = -11111111
byte @shared bv = -1 ^^element myElement = [1, -11111111, -1111]
float @shared fv = -1.1
bool b1, b2, b3, b4 = false
b1 = bv<0 txt.print_w(w)
b2 = wv<0 txt.spc()
b3 = lv<0 w >>= 4
b4 = fv<0 txt.print_w(w)
txt.print_bool(b1) txt.spc()
txt.print_bool(b2) w <<= 4
txt.print_bool(b3) txt.print_w(w)
txt.print_bool(b4)
txt.nl() txt.nl()
b1=b2=b3=b4=false txt.print_w(myElement.w)
b1 = sgn(bv)<0 txt.spc()
b2 = sgn(wv)<0 myElement.w >>= 4
b3 = sgn(lv)<0 txt.print_w(myElement.w)
b4 = sgn(fv)<0 txt.spc()
txt.print_bool(b1) myElement.w <<= 4
txt.print_bool(b2) txt.print_w(myElement.w)
txt.print_bool(b3)
txt.print_bool(b4)
txt.nl() txt.nl()
bv = 1 txt.nl()
wv = 1 txt.print_l(l)
lv = 1 txt.spc()
fv = 1.1 l >>= 4
b1 = sgn(bv)<0 txt.print_l(l)
b2 = sgn(wv)<0 txt.spc()
b3 = sgn(lv)<0 l <<= 4
b4 = sgn(fv)<0 txt.print_l(l)
txt.print_bool(b1) txt.nl()
txt.print_bool(b2) txt.print_l(myElement.x)
txt.print_bool(b3) txt.spc()
txt.print_bool(b4) myElement.x >>= 4
txt.print_l(myElement.x)
txt.spc()
myElement.x <<= 4
txt.print_l(myElement.x)
txt.nl() 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()
}
*/
} }

View File

@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.configuration-cache=false org.gradle.configuration-cache=false
kotlin.code.style=official kotlin.code.style=official
version=12.0-BETA7 version=12.0.1-SNAPSHOT

View File

@@ -13,6 +13,7 @@ fun optimizeSimplifiedAst(program: PtProgram, options: CompilationOptions, st: S
return return
while (errors.noErrors() && while (errors.noErrors() &&
optimizeAssignTargets(program, st) optimizeAssignTargets(program, st)
+ optimizeFloatComparesToZero(program)
+ optimizeBinaryExpressions(program, options) > 0) { + optimizeBinaryExpressions(program, options) > 0) {
// keep rolling // keep rolling
} }
@@ -152,3 +153,27 @@ private fun optimizeBinaryExpressions(program: PtProgram, options: CompilationOp
} }
return changes return changes
} }
private fun optimizeFloatComparesToZero(program: PtProgram): Int {
var changes = 0
walkAst(program) { node: PtNode, depth: Int ->
if (node is PtBinaryExpression) {
val constvalue = node.right.asConstValue()
if(node.type.isBool && constvalue==0.0 && node.left.type.isFloat && node.operator in ComparisonOperators) {
// float == 0 --> sgn(float) == 0
val sign = PtBuiltinFunctionCall("sgn", false, true, DataType.BYTE, node.position)
sign.add(node.left)
val replacement = PtBinaryExpression(node.operator, DataType.BOOL, node.position)
replacement.add(sign)
replacement.add(PtNumber(BaseDataType.BYTE, 0.0, node.position))
replacement.parent = node.parent
val index = node.parent.children.indexOf(node)
node.parent.children[index] = replacement
changes++
}
}
true
}
return changes
}