mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 01:23:34 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
473efbe67a | |||
aeabf0f324 | |||
80ab552ad8 | |||
7d4695c5b2 | |||
5189eaca36 | |||
cfb31377fc | |||
a07c52e112 | |||
8e1071aa89 | |||
7cb9a6ba60 | |||
350dc731f1 | |||
f690f58bd4 | |||
4bc65e9ef7 | |||
2d600da8b6 | |||
35af53828a |
@ -29,10 +29,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
free.addAll(listOf(
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||
0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||
0x22, 0x23, 0x24, 0x25,
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
@ -48,8 +47,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
// remove the zeropage locations used for floating point operations from the free list
|
||||
free.removeAll(listOf(
|
||||
0x22, 0x23, 0x24, 0x25,
|
||||
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x03, 0x04, 0x10, 0x11, 0x12,
|
||||
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
|
@ -45,8 +45,9 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
// note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
|
@ -139,20 +139,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||
|
||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
||||
if(bank==null || address==null)
|
||||
throw AssemblyError("callfar (jsrfar) requires constant arguments")
|
||||
|
||||
if(address !in 0xa000..0xbfff)
|
||||
throw AssemblyError("callfar done on address outside of cx16 banked ram")
|
||||
if(bank==0)
|
||||
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
||||
|
||||
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
|
||||
val argAddrArg = fcall.args[2]
|
||||
if(bank==null)
|
||||
throw AssemblyError("callfar (jsrfar) bank has to be a constant")
|
||||
if(fcall.args[1].constValue(program) == null) {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
|
||||
}
|
||||
|
||||
if(argAddrArg.constValue(program)?.number == 0.0) {
|
||||
asmgen.out("""
|
||||
jsr cx16.jsrfar
|
||||
.word ${address.toHex()}
|
||||
+ .word ${address.toHex()}
|
||||
.byte ${bank.toHex()}""")
|
||||
} else {
|
||||
when(argAddrArg) {
|
||||
@ -162,7 +161,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
asmgen.out("""
|
||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||
jsr cx16.jsrfar
|
||||
.word ${address.toHex()}
|
||||
+ .word ${address.toHex()}
|
||||
.byte ${bank.toHex()}
|
||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||
}
|
||||
@ -170,7 +169,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
asmgen.out("""
|
||||
lda ${argAddrArg.number.toHex()}
|
||||
jsr cx16.jsrfar
|
||||
.word ${address.toHex()}
|
||||
+ .word ${address.toHex()}
|
||||
.byte ${bank.toHex()}
|
||||
sta ${argAddrArg.number.toHex()}""")
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition:
|
||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
||||
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
||||
asmgen.out(".endweak")
|
||||
|
@ -738,10 +738,10 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
return
|
||||
}
|
||||
|
||||
if(origTypeCastExpression.type == DataType.UBYTE) {
|
||||
if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) {
|
||||
val parentTc = origTypeCastExpression.parent as? TypecastExpression
|
||||
if(parentTc!=null && parentTc.type==DataType.UWORD) {
|
||||
// typecast something to ubyte and directly back to uword
|
||||
// typecast a word value to ubyte and directly back to uword
|
||||
// generate code for lsb(value) here instead of the ubyte typecast
|
||||
return assignCastViaLsbFunc(value, target)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "io.kotest" version "0.3.9"
|
||||
}
|
||||
|
||||
java {
|
||||
@ -31,6 +32,7 @@ dependencies {
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -42,6 +44,22 @@ sourceSets {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDir "${project.projectDir}/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
test {
|
||||
// Enable JUnit 5 (Gradle 4.6+).
|
||||
useJUnitPlatform()
|
||||
|
||||
// Always run tests, even when nothing changed.
|
||||
dependsOn 'cleanTest'
|
||||
|
||||
// Show test results.
|
||||
testLogging {
|
||||
events "skipped", "failed"
|
||||
}
|
||||
}
|
@ -4,11 +4,14 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="codeAst" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
|
@ -279,6 +279,7 @@ class CodeGen(internal val program: PtProgram,
|
||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||
} else {
|
||||
// TODO WHY THID DISTINCTION?
|
||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||
code += addConstReg(loopvarDt, indexReg, step)
|
||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||
@ -318,6 +319,7 @@ class CodeGen(internal val program: PtProgram,
|
||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||
} else {
|
||||
// TODO WHY THIS DISTICTION ?
|
||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||
code += addConstReg(loopvarDt, indexReg, step)
|
||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||
@ -383,13 +385,13 @@ class CodeGen(internal val program: PtProgram,
|
||||
if(value>0) {
|
||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
|
||||
code += VmCodeInstruction(Opcode.ADDR, dt, reg1 = valueReg, reg2 = operandReg)
|
||||
code += VmCodeInstruction(Opcode.ADDR, dt, reg1 = valueReg, reg2 = operandReg) // TODO USE ADDM?
|
||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
||||
}
|
||||
else {
|
||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
|
||||
code += VmCodeInstruction(Opcode.SUBR, dt, reg1 = valueReg, reg2 = operandReg)
|
||||
code += VmCodeInstruction(Opcode.SUBR, dt, reg1 = valueReg, reg2 = operandReg) // TODO USE ADDM?
|
||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
||||
}
|
||||
}
|
||||
|
19
codeGenVirtual/test/Dummies.kt
Normal file
19
codeGenVirtual/test/Dummies.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package prog8tests.vm.helpers
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = 0
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
|
||||
}
|
||||
|
||||
internal object DummyStringEncoder : IStringEncoding {
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
||||
return ""
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package prog8tests.vm
|
||||
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.SymbolTable
|
||||
@ -7,8 +8,8 @@ import prog8.code.ast.PtProgram
|
||||
import prog8.codegen.virtual.*
|
||||
import prog8.vm.Opcode
|
||||
import prog8.vm.VmDataType
|
||||
import prog8tests.helpers.DummyMemsizer
|
||||
import prog8tests.helpers.DummyStringEncoder
|
||||
import prog8tests.vm.helpers.DummyMemsizer
|
||||
import prog8tests.vm.helpers.DummyStringEncoder
|
||||
|
||||
class TestVmPeepholeOpt: FunSpec({
|
||||
fun makeVmProgram(lines: List<VmCodeLine>): Pair<AssemblyProgram, VariableAllocator> {
|
@ -30,8 +30,9 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
}
|
||||
|
||||
// remove redundant nested typecasts
|
||||
|
@ -34,7 +34,6 @@ dependencies {
|
||||
implementation project(':codeGenCpu6502')
|
||||
implementation project(':codeGenVirtual')
|
||||
implementation project(':codeGenExperimental')
|
||||
implementation project(':virtualmachine')
|
||||
implementation 'org.antlr:antlr4-runtime:4.10.1'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
@ -69,7 +68,6 @@ sourceSets {
|
||||
test {
|
||||
java {
|
||||
srcDir "${project.projectDir}/test"
|
||||
srcDir "${project(':compilerAst').projectDir}/test/helpers"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,5 @@
|
||||
<orderEntry type="module" module-name="codeGenCpu6502" />
|
||||
<orderEntry type="module" module-name="codeGenExperimental" />
|
||||
<orderEntry type="module" module-name="codeGenVirtual" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
</component>
|
||||
</module>
|
@ -416,6 +416,9 @@ _loop
|
||||
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'
|
||||
@ -451,6 +454,11 @@ _add_letter
|
||||
sta P8ZP_SCRATCH_B1
|
||||
pla
|
||||
jmp _calc
|
||||
_try_iso
|
||||
cmp #103
|
||||
bcs _stop
|
||||
and #63
|
||||
bne _add_letter
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -2,15 +2,20 @@
|
||||
%import textio
|
||||
|
||||
; Bitmap pixel graphics module for the CommanderX16
|
||||
; wraps the graphics functions that are in ROM.
|
||||
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
|
||||
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
|
||||
; Wraps the graphics functions that are in ROM.
|
||||
; Only lo-res 320x240 256 color mode for now.
|
||||
; Unlike graphics module on the C64, you can use colors() to set new drawing colors for every draw operation.
|
||||
; For other resolutions or other color modes, use the "gfx2" module instead. (which is Cx16-specific)
|
||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||
|
||||
|
||||
graphics {
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
const ubyte HEIGHT = 240
|
||||
|
||||
|
||||
ubyte stroke_color = 1
|
||||
ubyte background_color = 0
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
@ -27,10 +32,18 @@ graphics {
|
||||
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
stroke_color = pixelcolor
|
||||
background_color = bgcolor
|
||||
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
||||
cx16.GRAPH_clear()
|
||||
}
|
||||
|
||||
sub colors(ubyte stroke, ubyte fill) {
|
||||
; this routine is only available on the cx16, other targets can't change colors on the fly
|
||||
cx16.GRAPH_set_colors(stroke, fill, background_color)
|
||||
stroke_color = stroke
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
||||
}
|
||||
@ -71,31 +84,31 @@ graphics {
|
||||
cx16.r0 = xcenter + xx
|
||||
cx16.r1 = ycenter + yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter + xx
|
||||
cx16.r1 = ycenter - yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter + yy
|
||||
cx16.r1 = ycenter + xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter + yy
|
||||
cx16.r1 = ycenter - xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
yy++
|
||||
if decisionOver2<=0 {
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
|
@ -90,6 +90,10 @@ psg {
|
||||
; cx16.r1L = the voice number
|
||||
; cx16.r2L = attack value
|
||||
|
||||
pushw(cx16.r0)
|
||||
push(cx16.r1L)
|
||||
push(cx16.r2L)
|
||||
pushw(cx16.r9)
|
||||
; calculate new volumes
|
||||
for cx16.r1L in 0 to 15 {
|
||||
when envelope_states[cx16.r1L] {
|
||||
@ -138,6 +142,10 @@ psg {
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||
}
|
||||
cx16.pop_vera_context()
|
||||
popw(cx16.r9)
|
||||
pop(cx16.r2L)
|
||||
pop(cx16.r1L)
|
||||
popw(cx16.r0)
|
||||
}
|
||||
|
||||
ubyte[16] envelope_states
|
||||
|
@ -735,49 +735,53 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_vera_context() clobbers(A) {
|
||||
asmsub push_vera_context() clobbers(A) {
|
||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||
%asm {{
|
||||
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||
lda cx16.VERA_ADDR_L
|
||||
pha
|
||||
sta _vera_storage
|
||||
lda cx16.VERA_ADDR_M
|
||||
pha
|
||||
sta _vera_storage+1
|
||||
lda cx16.VERA_ADDR_H
|
||||
pha
|
||||
sta _vera_storage+2
|
||||
lda cx16.VERA_CTRL
|
||||
pha
|
||||
sta _vera_storage+3
|
||||
eor #1
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.VERA_ADDR_L
|
||||
pha
|
||||
sta _vera_storage+4
|
||||
lda cx16.VERA_ADDR_M
|
||||
pha
|
||||
sta _vera_storage+5
|
||||
lda cx16.VERA_ADDR_H
|
||||
pha
|
||||
sta _vera_storage+6
|
||||
lda cx16.VERA_CTRL
|
||||
pha
|
||||
sta _vera_storage+7
|
||||
rts
|
||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop_vera_context() clobbers(A) {
|
||||
asmsub pop_vera_context() clobbers(A) {
|
||||
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||
%asm {{
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+7
|
||||
sta cx16.VERA_CTRL
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+6
|
||||
sta cx16.VERA_ADDR_H
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+5
|
||||
sta cx16.VERA_ADDR_M
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+4
|
||||
sta cx16.VERA_ADDR_L
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+3
|
||||
sta cx16.VERA_CTRL
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+2
|
||||
sta cx16.VERA_ADDR_H
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
pla
|
||||
lda cx16.push_vera_context._vera_storage+0
|
||||
sta cx16.VERA_ADDR_L
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@ -864,7 +868,9 @@ sys {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
stz $01 ; bank the kernal in
|
||||
stz $01 ; bank the kernal in
|
||||
lda #$80
|
||||
sta cx16.VERA_CTRL ; reset Vera (kernal doesn't do this?)
|
||||
jmp (cx16.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
@ -208,14 +208,16 @@ close_end:
|
||||
c64.SETLFS(11, drivenumber, 0)
|
||||
void c64.OPEN() ; open 11,8,0,"filename"
|
||||
if_cc {
|
||||
iteration_in_progress = true
|
||||
have_first_byte = false
|
||||
void c64.CHKIN(11) ; use #11 as input channel
|
||||
if_cc {
|
||||
first_byte = c64.CHRIN() ; read first byte to test for file not found
|
||||
if not c64.READST() {
|
||||
have_first_byte = true
|
||||
return true
|
||||
if c64.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
have_first_byte = false
|
||||
void c64.CHKIN(11) ; use #11 as input channel
|
||||
if_cc {
|
||||
first_byte = c64.CHRIN() ; read first byte to test for file not found
|
||||
if not c64.READST() {
|
||||
have_first_byte = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -393,11 +395,14 @@ _end rts
|
||||
goto io_error
|
||||
|
||||
while not c64.READST() {
|
||||
@(messageptr) = c64.CHRIN()
|
||||
first_byte = c64.CHRIN()
|
||||
if first_byte=='\r' or first_byte=='\n'
|
||||
break
|
||||
@(messageptr) = first_byte
|
||||
messageptr++
|
||||
}
|
||||
|
||||
@(messageptr) = 0
|
||||
|
||||
done:
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(15)
|
||||
|
@ -665,13 +665,10 @@ greaterequalzero_sb .proc
|
||||
|
||||
greaterequalzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._false
|
||||
ora P8ESTACK_LO+1,x
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
|
||||
memcopy16_up .proc
|
||||
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
||||
; clobbers register A,X,Y
|
||||
|
@ -251,4 +251,13 @@ sub bin2uword(str string) -> uword {
|
||||
}
|
||||
}
|
||||
|
||||
sub any2uword(str string) -> uword {
|
||||
; -- convert any number string (any prefix allowed) to uword.
|
||||
when string[0] {
|
||||
'$' -> return hex2uword(string)
|
||||
'%' -> return bin2uword(string)
|
||||
else -> return str2uword(string)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
8.3
|
||||
8.3.1
|
||||
|
@ -799,8 +799,11 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||
if(e is IdentifierReference) {
|
||||
val decl = e.targetVarDecl(program)!!
|
||||
return decl.datatype in PassByReferenceDatatypes
|
||||
val decl = e.targetVarDecl(program)
|
||||
return if(decl!=null)
|
||||
decl.datatype in PassByReferenceDatatypes
|
||||
else
|
||||
true // is probably a symbol that needs addr-of
|
||||
}
|
||||
return e is StringLiteral
|
||||
}
|
||||
|
@ -57,8 +57,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch]
|
||||
val expected = consideredParamTypes[mismatch]
|
||||
if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number !in setOf(0.0, 1.0))
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
return if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number in setOf(0.0, 1.0))
|
||||
null // specifying a 1 or 0 as a BOOL is okay
|
||||
else
|
||||
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
if(target.isAsmSubroutine) {
|
||||
if(target.asmReturnvaluesRegisters.size>1) {
|
||||
|
@ -94,6 +94,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"amiga",
|
||||
"bdmusic",
|
||||
"bobs",
|
||||
"circles",
|
||||
"cobramk3-gfx",
|
||||
"colorbars",
|
||||
"datetime",
|
||||
|
@ -15,6 +15,25 @@ import prog8tests.helpers.compileText
|
||||
|
||||
class TestSubroutines: FunSpec({
|
||||
|
||||
test("string arg for byte param proper errormessage and subroutineptr in array too") {
|
||||
val text="""
|
||||
main {
|
||||
sub func(ubyte bb) {
|
||||
bb++
|
||||
}
|
||||
|
||||
sub start() {
|
||||
func("abc")
|
||||
uword[] commands = ["abc", func]
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "type mismatch, was: STR expected: UBYTE"
|
||||
errors.errors[1] shouldContain "initialisation value has incompatible type"
|
||||
}
|
||||
|
||||
test("stringParameter") {
|
||||
val text = """
|
||||
main {
|
||||
|
@ -701,11 +701,12 @@ main {
|
||||
fl = bb as float
|
||||
bb = fl as byte
|
||||
uw = fl as uword
|
||||
uw = 8888 + (bb as ubyte)
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||
result.program.entrypoint.statements.size shouldBe 13
|
||||
result.program.entrypoint.statements.size shouldBe 15
|
||||
}
|
||||
|
||||
test("invalid typecasts of numbers") {
|
||||
|
@ -117,9 +117,9 @@ class TestC64Zeropage: FunSpec({
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||
zp1.availableBytes() shouldBe 18
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target, 999u))
|
||||
zp2.availableBytes() shouldBe 85
|
||||
zp2.availableBytes() shouldBe 92
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target, 999u))
|
||||
zp3.availableBytes() shouldBe 125
|
||||
zp3.availableBytes() shouldBe 134
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||
zp4.availableBytes() shouldBe 239
|
||||
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
||||
|
@ -197,6 +197,14 @@ interface INameScope: IStatementContainer, INamedStatement {
|
||||
else
|
||||
statementScope = (statementScope as Node).definingScope
|
||||
}
|
||||
|
||||
// still not found, maybe it is a symbol in another module
|
||||
for(module in (this as Node).definingModule.program.modules) {
|
||||
val stmt = module.searchSymbol(name)
|
||||
if(stmt!=null)
|
||||
return stmt
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -757,9 +757,11 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, select the "biggegst" datatype based on the elements in the array.
|
||||
// otherwise, select the "biggest" datatype based on the elements in the array.
|
||||
require(value.isNotEmpty()) { "can't determine type of empty array" }
|
||||
val datatypesInArray = value.map { it.inferType(program) }
|
||||
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
||||
if(datatypesInArray.any{ it.isUnknown })
|
||||
return InferredTypes.InferredType.unknown()
|
||||
val dts = datatypesInArray.map { it.getOr(DataType.UNDEFINED) }
|
||||
return when {
|
||||
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
||||
|
@ -495,7 +495,9 @@ Breaking out of a loop prematurely is possible with the ``break`` statement.
|
||||
languages if this is *not* the case! A for loop from ubyte 10 to ubyte 2, for example, will iterate through
|
||||
all values 10, 11, 12, 13, .... 254, 255, 0 (wrapped), 1, 2. In other languages the entire loop will
|
||||
be skipped in such cases. But prog8 omits the overhead of an extra loop range check and/or branch for every for loop
|
||||
by assuming the normal ranges.
|
||||
because the most common case is that it is not needed.
|
||||
You should add an explicit range check yourself if the ending value can be less than the start value and
|
||||
a full wrap-around loop is not what you want!
|
||||
|
||||
|
||||
Conditional Execution
|
||||
@ -897,14 +899,17 @@ memory(name, size, alignment)
|
||||
|
||||
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
||||
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine)
|
||||
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte).
|
||||
Notice that bank $00 is used by the Kernal and should not be used by user code.
|
||||
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte), but you can specify
|
||||
any address in system ram (why this can be useful is explained at the end of this paragraph)
|
||||
The third argument can be used to designate the memory address
|
||||
of an argument for the routine; it will be loaded into the A register and will
|
||||
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||
no argument passing will be done.
|
||||
If the routine requires different arguments or return values, ``callfar`` cannot be used
|
||||
and you'll have to set up a call to ``jsrfar`` yourself to process this.
|
||||
Note: the address can be a variable or other expression, which allows you to use ``callfar`` with bank 0 to do an indirect JSR to a subroutine
|
||||
whose address can vary (jump table, etc. ``goto`` can do an indirect JMP to a variable address): ``callfar(0, &routine, &argument)``
|
||||
This is not very efficient though, so maybe you should write a small piece of inline assembly for this instead.
|
||||
|
||||
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
||||
Calls an assembly routine in another rom-bank on the CommanderX16
|
||||
|
@ -753,14 +753,14 @@ You can still ``break`` out of such a loop if you want though.
|
||||
Conditional Execution and Jumps
|
||||
-------------------------------
|
||||
|
||||
Unconditional jump
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Unconditional jump: goto
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To jump to another part of the program, you use a ``goto`` statement with an addres or the name
|
||||
of a label or subroutine::
|
||||
|
||||
goto $c000 ; address
|
||||
goto name ; label or subroutine
|
||||
goto $c000 ; address
|
||||
goto name ; label or subroutine
|
||||
|
||||
uword address = $4000
|
||||
goto address ; jump via address variable
|
||||
@ -770,6 +770,8 @@ to another piece of code that eventually returns).
|
||||
|
||||
If you jump to an address variable (uword), it is doing an 'indirect' jump: the jump will be done
|
||||
to the address that's currently in the variable.
|
||||
Note: to do an indirect *JSR* to a routine with a varying address, you can use the ``callfar`` builtin function
|
||||
(which is not very efficient) or you have to write a small piece of inline assembly.
|
||||
|
||||
|
||||
Conditional execution
|
||||
|
@ -17,33 +17,36 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- on non-cx16 targets: have an option that if zeropage=FULL, moves the cx16 virtual registers to ZP (same location as on x16?)
|
||||
needs the dynamic base address for the symbols in syslib.p8
|
||||
also needs a trick to allocate them in ZP like Cx16Zeropage already does
|
||||
- add item to XZeropage that enables an option that if zeropage=FULL or KERNALSAFE, moves the cx16 virtual registers to ZP, same location as on x16
|
||||
(can be done on C64 only for now) Remove those addresses from the ZP free pool = allocate them in ZP like Cx16Zeropage does
|
||||
Adapt the code in AstPreprocessor that relocates the registers as well.
|
||||
- for uword pointer variables: allow pointer[uword] array indexing >255 , rewrite it to @(pointer+index)
|
||||
DO NOT allow this for regular array indexing because normal arrays can never exceed size 256
|
||||
|
||||
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
||||
this info is needed for more advanced optimizations and later code generation steps.
|
||||
- vm: implement remaining sin/cos functions in math.p8
|
||||
- vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now
|
||||
- vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8
|
||||
- vm: don't store symbol names in instructions to make optimizing the IR easier? but what about jumps to labels. And it's no longer readable by humans.
|
||||
- vm: how to remove all unused subroutines? (in the 6502 assembly codegen, we let 64tass solve this for us)
|
||||
- vm: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us)
|
||||
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too
|
||||
- vm: add ore optimizations in VmPeepholeOptimizer
|
||||
- move the vm unit tests to codeGenVirtual module and remove virtualmachine dependency in the compiler module
|
||||
- see if we can let for loops skip the loop if end<start, like other programming languages. Without adding a lot of code size/duplicating the loop condition.
|
||||
this is documented behavior to now loop around but it's too easy to forget about!
|
||||
Lot of work because of so many special cases in ForLoopsAsmgen.....
|
||||
How is it for the vm target? -> just 2 special cases in CodeGen.
|
||||
- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed.
|
||||
It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed.
|
||||
So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step.
|
||||
- generate WASM from the new ast (or from vm code?) to run prog8 on a browser canvas?
|
||||
- createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
|
||||
but probably better to rewrite the 6502 codegen on top of the new Ast.
|
||||
- simplifyConditionalExpression() should not split expression if it still results in stack-based evaluation, but how does it know?
|
||||
- simplifyConditionalExpression() sometimes introduces needless assignment to r9 tempvar (what scenarios?)
|
||||
- simplifyConditionalExpression() sometimes introduces needless assignment to r9 tempvar, can we detect & prevent this?
|
||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway)
|
||||
then we can get rid of the instruction lists in the machinedefinitions as well?
|
||||
- [problematic due to using 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||
Perhaps replace all uses of .proc/.pend by .block/.bend will fix that?
|
||||
(but we lose the optimizing aspect of the assembler where it strips out unused code.
|
||||
There's not really a dynamic switch possible as all assembly lib code is static and uses one or the other)
|
||||
Perhaps replace all uses of .proc/.pend by .block/.bend will fix that with a compiler flag?
|
||||
But all library code written in asm uses .proc already.....
|
||||
- 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)
|
||||
- add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
||||
- ast: don't rewrite by-reference parameter type to uword, but keep the original type (str, array)
|
||||
@ -59,7 +62,7 @@ Libraries:
|
||||
- fix the problems in atari target, and flesh out its libraries.
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||
- optimize several inner loops in gfx2 even further?
|
||||
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?
|
||||
- add modes 3 and perhaps even 2 to gfx2 (16 color and 4 color)?
|
||||
- add a flood fill routine to gfx2?
|
||||
|
||||
|
||||
@ -80,9 +83,7 @@ Optimizations:
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. When 6502-codegen is no longer done from
|
||||
the old CompilerAst, those checks should probably be removed, or be made permanent
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served
|
||||
- AssignmentAsmGen.assignExpression() -> improve code gen for assigning boolean comparison expressions
|
||||
Check what the vm target does here, maybe just do this as part of the vm -> 6502 codegen.
|
||||
- when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
||||
- when a loopvariable of a forloop isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
||||
but we have no efficient way right now to see if the body references a variable.
|
||||
|
||||
|
||||
@ -92,7 +93,7 @@ STRUCTS again?
|
||||
What if we were to re-introduce Structs in prog8? Some thoughts:
|
||||
|
||||
- can contain only numeric types (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs
|
||||
- is just some syntactic sugar for a scoped set of variables -> ast transform to do exactly this before codegen
|
||||
- is just some syntactic sugar for a scoped set of variables -> ast transform to do exactly this before codegen. Codegen doesn't know about struct.
|
||||
- no arrays of struct -- because too slow on 6502 to access those, rather use struct of arrays instead.
|
||||
can we make this a compiler/codegen only issue? i.e. syntax is just as if it was an array of structs?
|
||||
or make it explicit in the syntax so that it is clear what the memory layout of it is.
|
||||
|
89
examples/cx16/circles.p8
Normal file
89
examples/cx16/circles.p8
Normal file
@ -0,0 +1,89 @@
|
||||
%import graphics
|
||||
|
||||
; note: this program is tuned for the CX16, but with some minor modifications can run on other systems too.
|
||||
|
||||
main {
|
||||
const ubyte MAX_NUM_CIRCLES = 80
|
||||
const ubyte GROWTH_RATE = 2
|
||||
uword[MAX_NUM_CIRCLES] circle_x
|
||||
uword[MAX_NUM_CIRCLES] circle_y
|
||||
ubyte[MAX_NUM_CIRCLES] circle_radius
|
||||
ubyte num_circles = 0
|
||||
ubyte background_color
|
||||
|
||||
sub start() {
|
||||
graphics.enable_bitmap_mode()
|
||||
|
||||
repeat {
|
||||
background_color = rnd()
|
||||
graphics.clear_screen(0, background_color)
|
||||
num_circles = 0
|
||||
draw_circles()
|
||||
}
|
||||
}
|
||||
|
||||
sub draw_circles() {
|
||||
uword @zp x
|
||||
uword @zp y
|
||||
ubyte @zp radius
|
||||
|
||||
while num_circles<MAX_NUM_CIRCLES {
|
||||
x = rndw() % graphics.WIDTH
|
||||
y = rndw() % graphics.HEIGHT
|
||||
radius = GROWTH_RATE * 2 ; use a bit of a buffer between circles.
|
||||
if not_colliding() {
|
||||
radius -= GROWTH_RATE
|
||||
ubyte color = rnd()
|
||||
while color==background_color
|
||||
color = rnd()
|
||||
graphics.colors(color, 0)
|
||||
while not_edge() and not_colliding() {
|
||||
graphics.disc(x, y as ubyte, radius)
|
||||
sys.waitvsync()
|
||||
radius += GROWTH_RATE
|
||||
}
|
||||
circle_x[num_circles] = x
|
||||
circle_y[num_circles] = y
|
||||
circle_radius[num_circles] = radius - GROWTH_RATE
|
||||
num_circles++
|
||||
}
|
||||
}
|
||||
|
||||
sub not_colliding() -> bool {
|
||||
if num_circles==0
|
||||
return true
|
||||
ubyte @zp c
|
||||
for c in 0 to num_circles-1 {
|
||||
if distance(c) < (radius as uword) + circle_radius[c]
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub distance(ubyte cix) -> uword {
|
||||
word dx = x as word - circle_x[cix]
|
||||
word dy = y as word - circle_y[cix]
|
||||
uword sqx = dx*dx as uword
|
||||
uword sqy = dy*dy as uword
|
||||
return sqrt16(sqx + sqy)
|
||||
}
|
||||
|
||||
; sub distance(ubyte cix) -> uword {
|
||||
; float dx = x as float - circle_x[cix]
|
||||
; float dy = y as float - circle_y[cix]
|
||||
; return floats.sqrt(dx*dx + dy*dy) as uword
|
||||
; }
|
||||
|
||||
sub not_edge() -> bool {
|
||||
if x as word - radius < 0
|
||||
return false
|
||||
if x + radius >= graphics.WIDTH
|
||||
return false
|
||||
if y as word - radius < 0
|
||||
return false
|
||||
if y + radius >= graphics.HEIGHT
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
@ -3,20 +3,14 @@
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte value1 = %1110
|
||||
ubyte value2 = %1111
|
||||
ubyte ci
|
||||
ubyte from=10
|
||||
ubyte end=1
|
||||
|
||||
bool[2] @shared barr = [true, false]
|
||||
|
||||
; if value1 and value2 ; TODO value1 isn't converted to bool in 6502 preprocess
|
||||
; txt.print("ok")
|
||||
; else
|
||||
; txt.print("fail!")
|
||||
; txt.nl()
|
||||
|
||||
if value1 and value2!=255 ; TODO value1 isn't converted to bool in 6502 preprocess
|
||||
txt.print("ok")
|
||||
else
|
||||
txt.print("fail!")
|
||||
for ci in from to end {
|
||||
txt.print_ub(ci)
|
||||
txt.spc()
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user