implemented all bit rotate and shift operations

This commit is contained in:
Irmen de Jong 2018-10-21 23:03:15 +02:00
parent da3f79c092
commit b8f3f942d4
19 changed files with 461 additions and 1685 deletions

View File

@ -1,3 +1,4 @@
%import c64utils
%option enable_floats
~ irq {

View File

@ -1,3 +1,4 @@
%import c64utils
%option enable_floats
~ main {

View File

@ -1,16 +1,17 @@
%output prg
%import c64lib
%import c64utils
%import mathlib
~ main {
sub start() -> () {
str name = "?" * 80
str guess = "?" * 80
byte secretnumber = 0
byte attempts_left = 10
memory word freadstr_arg = $22 ; argument for FREADSTR
sub start() {
str name = "????????????????????????????????????????"
str guess = "??????????"
ubyte secretnumber = 0
ubyte attempts_left = 10
memory uword freadstr_arg = $22 ; argument for FREADSTR
c64.init_system()
c64utils.init_system()
c64.VMCSB |= 2 ; activate lowercase charset
; greeting

View File

@ -1,3 +1,5 @@
%import c64utils
~ main {
sub start() {
str name = " "

View File

@ -1,3 +1,4 @@
%import c64utils
%option enable_floats
~ main {

View File

@ -1,88 +1,15 @@
;%import c64utils
%option enable_floats
%import c64utils
%output prg
%launcher basic
~ main {
sub start() {
byte bvar
ubyte ubvar
memory byte mbvar = $c400
memory ubyte mubvar = $c400
byte[10] barray
ubyte[10] ubarray
memory word mword = $c000
memory word mword2 = $c200
memory uword muword = $c100
memory uword muword2 = $c300
float fvar
uword uwvar
word wvar
uword[10] uwarray
lsl(Y)
lsl(ubvar)
lsl(mubvar)
lsl(ubarray[2])
lsl(AY)
lsl(uwvar)
lsl(muword)
lsl(uwarray[3])
lsr(Y)
lsr(ubvar)
lsr(mubvar)
lsr(ubarray[2])
lsr(AY)
lsr(uwvar)
lsr(muword)
lsr(uwarray[3])
rol(Y)
rol(ubvar)
rol(mubvar)
rol(ubarray[2])
rol(AY)
rol(uwvar)
rol(muword)
rol(uwarray[3])
ror(Y)
ror(ubvar)
ror(mubvar)
ror(ubarray[2])
ror(AY)
ror(uwvar)
ror(muword)
ror(uwarray[3])
rol2(Y)
rol2(ubvar)
rol2(mubvar)
rol2(ubarray[2])
rol2(AY)
rol2(uwvar)
rol2(muword)
rol2(uwarray[3])
ror2(Y)
ror2(ubvar)
ror2(mubvar)
ror2(ubarray[2])
ror2(AY)
ror2(uwvar)
ror2(muword)
ror2(uwarray[3])
c64.VMCSB |= 2 ; activate lowercase charset
; greeting
c64scr.print_string("Enter your name: ")
return

View File

@ -69,6 +69,12 @@ fun main(args: Array<String>) {
zpType, zpReserved,
options.any{ it.name=="enable_floats"})
if(compilerOptions.launcher==LauncherType.BASIC && compilerOptions.output!=OutputType.PRG)
throw ParsingFailedError("${moduleAst.position} BASIC launcher requires output type PRG.")
if(compilerOptions.output==OutputType.PRG || compilerOptions.launcher==LauncherType.BASIC) {
if(namespace.lookup(listOf("c64utils"), moduleAst.statements.first())==null)
throw ParsingFailedError("${moduleAst.position} When using output type PRG and/or laucher BASIC, the 'c64utils' module must be imported.")
}
// perform initial syntax checks and constant folding
val heap = HeapValues()

View File

@ -409,7 +409,7 @@ class AstChecker(private val namespace: INameScope,
litVal.parent = decl
decl.value = litVal
}
else -> err("var/const declaration needs a compile-time constant initializer value for this type") // const fold should have provided it!
else -> err("var/const declaration needs a compile-time constant initializer value for type ${decl.datatype}") // const fold should have provided it!
}
return super.process(decl)
}

View File

@ -4,7 +4,8 @@ import prog8.ast.*
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.stackvm.*
import prog8.stackvm.Syscall
import prog8.stackvm.VmExecutionException
import java.util.*
import kotlin.math.abs

View File

@ -14,8 +14,9 @@ open class Instruction(val opcode: Opcode,
val argStr = arg?.toString() ?: ""
val result =
when {
opcode== Opcode.LINE -> "_line $callLabel"
opcode== Opcode.SYSCALL -> {
opcode==Opcode.LINE -> "_line $callLabel"
opcode==Opcode.INLINE_ASSEMBLY -> "inline_assembly"
opcode==Opcode.SYSCALL -> {
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
"syscall $syscall"
}

View File

@ -1,5 +1,9 @@
package prog8.compiler.target.c64
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
import prog8.ast.DataType
import prog8.ast.Register
import prog8.compiler.*
@ -312,9 +316,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
else throw AssemblyError("invalid array type")
}
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
private fun instr2asm(ins: List<Instruction>): Int {
// find best patterns (matching the most of the lines, then with the smallest weight)
val fragments = findPatterns(ins).sortedWith(compareBy({it.segmentSize}, {it.prio}))
@ -373,6 +374,13 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.DISCARD_WORD -> " inx"
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
Opcode.INLINE_ASSEMBLY -> ins.callLabel ?: "" // All of the inline assembly is stored in the calllabel property.
Opcode.PUSH_BYTE -> {
" lda #${ins.arg!!.integerValue().toHex()} | sta $ESTACK_LO,x | dex"
}
Opcode.PUSH_WORD -> {
val value = ins.arg!!.integerValue().toHex()
" lda #<$value | sta $ESTACK_LO,x | lda #>$value | sta $ESTACK_HI,x | dex"
}
Opcode.COPY_VAR_BYTE -> {
when {
ins.callLabel2 in registerStrings -> {
@ -574,34 +582,94 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
(opcodes[0]==Opcode.PUSH_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_WORD &&
opcodes[3]==Opcode.PUSH_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_WORD)) {
if(segment[0].arg==segment[3].arg && segment[1].callLabel==segment[4].callLabel) {
val fragment = sameIndexedVarOperation(segment[1].callLabel!!, segment[0].arg!!.integerValue(), segment[2])
val fragment = sameConstantIndexedVarOperation(segment[1].callLabel!!, segment[0].arg!!.integerValue(), segment[2])
if(fragment!=null){
fragment.segmentSize = 5
result.add(fragment)
}
}
}
for(pattern in patterns.filter { opcodes.subList(0, it.sequence.size) == it.sequence }) {
val asm = pattern.asm(segment)
if(asm!=null)
result.add(AsmFragment(asm, pattern.prio, pattern.sequence.size))
else if((opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_BYTE &&
opcodes[3]==Opcode.PUSH_VAR_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_BYTE) ||
(opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_WORD &&
opcodes[3]==Opcode.PUSH_VAR_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_WORD)) {
if(segment[0].callLabel==segment[3].callLabel && segment[1].callLabel==segment[4].callLabel) {
val fragment = sameIndexedVarOperation(segment[1].callLabel!!, segment[0].callLabel!!, segment[2])
if(fragment!=null){
fragment.segmentSize = 5
result.add(fragment)
}
}
}
for(pattern in patterns.filter { it.sequence.size <= segment.size}) {
if(pattern.sequence == opcodes.subList(0, pattern.sequence.size)) {
val asm = pattern.asm(segment)
if(asm!=null)
result.add(AsmFragment(asm, pattern.prio, pattern.sequence.size))
}
}
return result
}
private fun sameIndexedVarOperation(variable: String, index: Int, ins: Instruction): AsmFragment? {
val idx = index.toHex()
private fun sameConstantIndexedVarOperation(variable: String, index: Int, ins: Instruction): AsmFragment? {
return when(ins.opcode) {
Opcode.SHL_BYTE -> AsmFragment(" txa | ldx #$idx | asl $variable,x | tax", 10)
Opcode.SHR_BYTE -> AsmFragment(" txa | ldx #$idx | lsr $variable,x | tax", 10)
Opcode.SHL_WORD -> AsmFragment(" txa | ldx #$idx | asl $variable,x | rol $variable+1,x | tax", 10)
Opcode.SHR_WORD -> AsmFragment(" txa | ldx #$idx | lsr $variable+1,x | ror $variable,x | tax", 10)
Opcode.ROL_BYTE -> AsmFragment(" txa | ldx #$idx | rol $variable,x | tax", 10)
Opcode.ROR_BYTE -> AsmFragment(" txa | ldx #$idx | ror $variable,x | tax", 10)
Opcode.ROL_WORD -> AsmFragment(" txa | ldx #$idx | rol $variable,x | rol $variable+1,x | tax", 10)
Opcode.ROR_WORD -> AsmFragment(" txa | ldx #$idx | ror $variable+1,x | ror $variable,x | tax", 10)
Opcode.ROL2_BYTE -> AsmFragment(" stx ${C64Zeropage.SCRATCH_B1} | ldx #$idx | lda $variable,x | cmp #\$80 | rol $variable,x | ldx ${C64Zeropage.SCRATCH_B1}", 10)
Opcode.ROR2_BYTE -> AsmFragment(" stx ${C64Zeropage.SCRATCH_B1} | ldx #$idx | lda $variable,x | lsr a | bcc + | ora #\$80 |+ | sta $variable,x | ldx ${C64Zeropage.SCRATCH_B1}", 10)
Opcode.SHL_BYTE -> AsmFragment(" asl $variable+$index", 8)
Opcode.SHR_BYTE -> AsmFragment(" lsr $variable+$index", 8)
Opcode.SHL_WORD -> AsmFragment(" asl $variable+$index | rol $variable+$index+1", 8)
Opcode.SHR_WORD -> AsmFragment(" lsr $variable+$index+1,x | ror $variable+$index", 8)
Opcode.ROL_BYTE -> AsmFragment(" rol $variable+$index", 8)
Opcode.ROR_BYTE -> AsmFragment(" ror $variable+$index", 8)
Opcode.ROL_WORD -> AsmFragment(" rol $variable+$index | rol $variable+$index+1", 8)
Opcode.ROR_WORD -> AsmFragment(" ror $variable+$index+1 | ror $variable+$index", 8)
Opcode.ROL2_BYTE -> AsmFragment(" lda $variable+$index | cmp #\$80 | rol $variable+$index", 8)
Opcode.ROR2_BYTE -> AsmFragment(" lda $variable+$index | lsr a | bcc + | ora #\$80 |+ | sta $variable+$index", 10)
Opcode.ROL2_WORD -> AsmFragment(" asl $variable+$index | rol $variable+$index+1 | bcc + | inc $variable+$index |+",20)
Opcode.ROR2_WORD -> AsmFragment(" lsr $variable+$index+1 | ror $variable+$index | bcc + | lda $variable+$index+1 | ora #\$80 | sta $variable+$index+1 |+", 30)
else -> null
}
}
private fun sameIndexedVarOperation(variable: String, indexVar: String, ins: Instruction): AsmFragment? {
val saveX = " stx ${C64Zeropage.SCRATCH_B1} |" // todo optimize to TXA when possible
val restoreX = " | ldx ${C64Zeropage.SCRATCH_B1}"
var loadX = ""
var loadXWord = ""
when(indexVar) {
"X" -> {
loadXWord = " txa | asl a | tax |"
}
"Y" -> {
loadX = " tya | tax |"
loadXWord = " tya | asl a | tax |"
}
"A" -> {
loadX = " tax |"
loadXWord = " asl a | tax |"
}
"AX", "AY", "XY" -> throw AssemblyError("cannot index with word/registerpair")
else -> {
// the indexvar is a real variable, not a register
loadX = " ldx $indexVar |"
loadXWord = " lda $indexVar | asl a | tax |"
}
}
return when (ins.opcode) {
Opcode.SHL_BYTE -> AsmFragment("$saveX $loadX asl $variable,x $restoreX", 10)
Opcode.SHR_BYTE -> AsmFragment("$saveX $loadX lsr $variable,x $restoreX", 10)
Opcode.SHL_WORD -> AsmFragment("$saveX $loadXWord asl $variable,x | rol $variable+1,x $restoreX", 10)
Opcode.SHR_WORD -> AsmFragment("$saveX $loadXWord lsr $variable+1,x | ror $variable,x $restoreX", 10)
Opcode.ROL_BYTE -> AsmFragment("$saveX $loadX rol $variable,x $restoreX", 10)
Opcode.ROR_BYTE -> AsmFragment("$saveX $loadX ror $variable,x $restoreX", 10)
Opcode.ROL_WORD -> AsmFragment("$saveX $loadXWord rol $variable,x | rol $variable+1,x $restoreX", 10)
Opcode.ROR_WORD -> AsmFragment("$saveX $loadXWord ror $variable+1,x | ror $variable,x $restoreX", 10)
Opcode.ROL2_BYTE -> AsmFragment("$saveX $loadX lda $variable,x | cmp #\$80 | rol $variable,x $restoreX", 10)
Opcode.ROR2_BYTE -> AsmFragment("$saveX $loadX lda $variable,x | lsr a | bcc + | ora #\$80 |+ | sta $variable,x $restoreX", 10)
Opcode.ROL2_WORD -> AsmFragment("$saveX $loadXWord asl $variable,x | rol $variable+1,x | bcc + | inc $variable,x |+ $restoreX", 30)
Opcode.ROR2_WORD -> AsmFragment("$saveX $loadXWord lsr $variable+1,x | ror $variable,x | bcc + | lda $variable+1,x | ora #\$80 | sta $variable+1,x |+ $restoreX", 30)
else -> null
}
}
@ -620,6 +688,8 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.ROR_WORD -> AsmFragment(" ror $addrHi | ror $addr", 10)
Opcode.ROL2_BYTE -> AsmFragment(" lda $addr | cmp #\$80 | rol $addr", 10)
Opcode.ROR2_BYTE -> AsmFragment(" lda $addr | lsr a | bcc + | ora #\$80 |+ | sta $addr", 10)
Opcode.ROL2_WORD -> AsmFragment(" lda $addr | cmp #\$80 | rol $addr | rol $addrHi", 10)
Opcode.ROR2_WORD -> AsmFragment(" lsr $addrHi | ror $addr | bcc + | lda $addrHi | ora #$80 | sta $addrHi |+", 20)
else -> null
}
}
@ -692,10 +762,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
Opcode.ROL2_BYTE -> { // 8-bit rol
when (variable) {
"A" -> AsmFragment(" cmp #$80 | rol a", 10)
"X" -> AsmFragment(" txa | cmp #$80 | rol a | tax", 10)
"Y" -> AsmFragment(" tya | cmp #$80 | rol a | tay", 10)
else -> AsmFragment(" lda $variable | cmp #$80 | rol $variable", 10)
"A" -> AsmFragment(" cmp #\$80 | rol a", 10)
"X" -> AsmFragment(" txa | cmp #\$80 | rol a | tax", 10)
"Y" -> AsmFragment(" tya | cmp #\$80 | rol a | tay", 10)
else -> AsmFragment(" lda $variable | cmp #\$80 | rol $variable", 10)
}
}
Opcode.ROR2_BYTE -> { // 8-bit ror
@ -706,6 +776,23 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
else -> AsmFragment(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable", 10)
}
}
Opcode.ROL2_WORD -> {
when(variable) {
"AX" -> AsmFragment(" cmp #\$80 | rol a | tay | txa | rol a | tax | tya", 10)
"AY" -> AsmFragment(" sty ${C64Zeropage.SCRATCH_B1} | cmp #\$80 | rol a | rol ${C64Zeropage.SCRATCH_B1} | ldy ${C64Zeropage.SCRATCH_B1} ", 10)
"XY" -> AsmFragment(" sty ${C64Zeropage.SCRATCH_B1} | txa | cmp #\$80 | rol a | rol ${C64Zeropage.SCRATCH_B1} | ldy ${C64Zeropage.SCRATCH_B1} | tax", 10)
else -> AsmFragment(" lda $variable | cmp #\$80 | rol $variable | rol $variable+1", 10)
}
}
Opcode.ROR2_WORD -> {
// todo: ror2_word is very slow; it requires a library routine
when(variable) {
"AX" -> AsmFragment(" sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | jsr prog8_lib.ror2_word | lda ${C64Zeropage.SCRATCH_W1} | ldx ${C64Zeropage.SCRATCH_W1+1}", 20)
"AY" -> AsmFragment(" sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | jsr prog8_lib.ror2_word | lda ${C64Zeropage.SCRATCH_W1} | ldy ${C64Zeropage.SCRATCH_W1+1}", 20)
"XY" -> AsmFragment(" stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | jsr prog8_lib.ror2_word | ldx ${C64Zeropage.SCRATCH_W1} | ldy ${C64Zeropage.SCRATCH_W1+1}", 20)
else -> AsmFragment(" lda $variable | sta ${C64Zeropage.SCRATCH_W1} | lda $variable+1 | sta ${C64Zeropage.SCRATCH_W1+1} | jsr prog8_lib.ror2_word | lda ${C64Zeropage.SCRATCH_W1} | sta $variable | lda ${C64Zeropage.SCRATCH_W1+1} | sta $variable+1", 30)
}
}
else -> null
}
}
@ -714,216 +801,28 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
class AsmPattern(val sequence: List<Opcode>, val prio: Int, val asm: (List<Instruction>)->String?)
private val patterns = listOf<AsmPattern>(
private val patterns = listOf(
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.POP_VAR_BYTE), 10) { segment ->
when (segment[1].callLabel) {
"A", "X", "Y" -> " ld${segment[1].callLabel!!.toLowerCase()} #${segment[0].arg!!.integerValue().toHex()}"
else -> null
}
},
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_VAR_WORD), 10) { segment ->
val number = segment[0].arg!!.integerValue().toHex()
when (segment[1].callLabel) {
"AX" -> " lda #<$number | ldx #>$number"
"AY" -> " lda #<$number | ldy #>$number"
"XY" -> " ldx #<$number | ldy #>$number"
else -> null
}
},
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.POP_VAR_BYTE), 10) { segment ->
val index = segment[0].arg!!.integerValue()
when (segment[2].callLabel) {
"A", "X", "Y" -> " ld${segment[2].callLabel!!.toLowerCase()} ${segment[1].callLabel}+$index"
else -> null
}
}
)
/*
private fun oldinstr2asm(insIdx: Int, ins: Instruction, block: IntermediateProgram.ProgramBlock): Int {
when(ins.opcode) {
Opcode.PUSH_BYTE -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_BYTE)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_BYTE) {
if(nextIns.callLabel in registerStrings) {
// load a register with constant value
out("\tld${nextIns.callLabel!!.toLowerCase()} #${ins.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// load a variable with a constant value
out("\tlda #${ins.arg!!.integerValue().toHex()}")
out("\tsta ${nextIns.callLabel}")
return 1 // skip 1
}
if(nextIns.opcode==Opcode.POP_MEM_B || nextIns.opcode==Opcode.POP_MEM_UB) {
// memory location = constant value
out("\tlda #${ins.arg!!.integerValue().toHex()}")
out("\tsta ${nextIns.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// byte onto stack
pushByte(ins.arg!!.integerValue())
}
Opcode.PUSH_MEM_UB, Opcode.PUSH_MEM_B -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_BYTE)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_BYTE) {
if(nextIns.callLabel in registerStrings) {
// load a register with memory location
out("\tld${nextIns.callLabel!!.toLowerCase()} ${ins.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// load var with mem b
out("\tlda ${ins.arg!!.integerValue().toHex()}\n\tsta ${nextIns.callLabel}")
return 1 // skip 1
}
if(nextIns.opcode==Opcode.POP_MEM_B || nextIns.opcode==Opcode.POP_MEM_UB) {
// copy byte from mem -> mem
out("\tlda ${ins.arg!!.integerValue().toHex()}\n\tsta ${nextIns.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// byte from memory onto stack
pushMemByte(ins.arg!!.integerValue())
}
Opcode.PUSH_VAR_BYTE -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_BYTE)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_BYTE)
throw CompilerException("push var+pop var should have been replaced by copy var")
if(nextIns.opcode==Opcode.POP_MEM_B || nextIns.opcode==Opcode.POP_MEM_UB) {
// copy byte from var -> mem
out("\tlda ${ins.callLabel}\n\tsta ${nextIns.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// byte from variable onto stack
when(ins.callLabel) {
"A" -> pushByteA()
"X" -> throw CompilerException("makes no sense to push X it's used as a stack pointer itself")
"Y" -> pushByteY()
else -> pushVarByte(ins.callLabel!!)
}
}
Opcode.PUSH_WORD -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_WORD)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_WORD) {
val value = ins.arg!!.integerValue()
if(nextIns.callLabel in registerStrings) {
// we load a register (AX, AY, XY) with constant value
val regs = nextIns.callLabel!!.toLowerCase()
out("\tld${regs[0]} #<${value.toHex()}")
out("\tld${regs[1]} #>${value.toHex()}")
return 1 // skip 1
}
// load a word variable with a constant value
out("\tlda #<${value.toHex()}")
out("\tsta ${nextIns.callLabel}")
out("\tlda #>${value.toHex()}")
out("\tsta ${nextIns.callLabel}+1")
return 1 // skip 1
}
if(nextIns.opcode==Opcode.POP_MEM_W || nextIns.opcode==Opcode.POP_MEM_UW) {
// we're loading a word into memory
out("\tlda #<${ins.arg!!.integerValue().toHex()}")
out("\tsta ${nextIns.arg!!.integerValue().toHex()}")
out("\tlda #>${ins.arg.integerValue().toHex()}")
out("\tsta ${(nextIns.arg.integerValue()+1).toHex()}")
return 1 // skip 1
}
if(ins.arg!!.type in StringDatatypes) {
TODO("strings from heap")
}
pushWord(ins.arg.integerValue())
}
Opcode.PUSH_MEM_UW, Opcode.PUSH_MEM_W -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_WORD)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_WORD) {
if(nextIns.callLabel in registerStrings) {
// load a register (AX, AY, XY) with word from memory
val regs = nextIns.callLabel!!.toLowerCase()
val value = ins.arg!!.integerValue()
out("\tld${regs[0]} ${value.toHex()}")
out("\tld${regs[1]} ${(value + 1).toHex()}")
return 1 // skip 1
}
// load var with mem word
out("\tlda ${ins.arg!!.integerValue().toHex()}")
out("\tsta ${nextIns.callLabel}")
out("\tlda ${(ins.arg.integerValue()+1).toHex()}")
out("\tsta ${nextIns.callLabel}+1")
return 1 // skip 1
}
if(nextIns.opcode==Opcode.POP_MEM_W || nextIns.opcode==Opcode.POP_MEM_UW) {
// copy word mem->mem
out("\tlda ${ins.arg!!.integerValue().toHex()}")
out("\tsta ${nextIns.arg!!.integerValue().toHex()}")
out("\tlda ${(ins.arg.integerValue()+1).toHex()}")
out("\tsta ${(nextIns.arg.integerValue()+1).toHex()}")
return 1 // skip 1
}
// word from memory onto stack
pushMemWord(ins.arg!!.integerValue())
}
Opcode.PUSH_VAR_WORD -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_FLOAT)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_WORD)
throw CompilerException("push var+pop var should have been replaced by copy var")
if(nextIns.opcode==Opcode.POP_MEM_W || nextIns.opcode==Opcode.POP_MEM_UW) {
// copy word from var -> mem
out("\tlda ${ins.callLabel}\n\tsta ${nextIns.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// word from memory onto stack
when(ins.callLabel) {
"AX" -> TODO()
"AY" -> TODO()
"XY" -> TODO()
else -> pushVarWord(ins.callLabel!!)
}
}
Opcode.PUSH_FLOAT -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_FLOAT)
throw CompilerException("discard after push should have been removed")
if(!options.floats)
throw CompilerException("floats not enabled")
val float = ins.arg!!.numericValue().toDouble()
val label = globalFloatConsts[float]!!
if(nextIns.opcode==Opcode.POP_MEM_FLOAT) {
// set a float in memory to a constant float value
copyFloat(label, nextIns.arg!!.integerValue().toHex())
return 1
} else if(nextIns.opcode==Opcode.POP_VAR_FLOAT) {
// set a variable to a constant float value
copyFloat(label, nextIns.callLabel!!)
return 1
}
pushFloat(label)
}
Opcode.PUSH_VAR_FLOAT -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_FLOAT)
throw CompilerException("discard after push should have been removed")
if(nextIns.opcode==Opcode.POP_VAR_FLOAT)
throw CompilerException("push var+pop var should have been replaced by copy var")
if(!options.floats)
throw CompilerException("floats not enabled")
if(nextIns.opcode==Opcode.POP_MEM_FLOAT) {
copyFloat(ins.callLabel!!, nextIns.arg!!.integerValue().toHex()) // copy var float to memory
return 1
}
pushFloat(ins.callLabel!!)
}
Opcode.PUSH_MEM_FLOAT -> {
val nextIns = block.getIns(insIdx+1)
if(nextIns==Opcode.DISCARD_FLOAT)
throw CompilerException("discard after push should have been removed")
if(!options.floats)
throw CompilerException("floats not enabled")
if(nextIns.opcode==Opcode.POP_VAR_FLOAT) {
copyFloat(ins.arg!!.integerValue().toHex(), nextIns.callLabel!!) // copy memory float to var
return 1
}
if(nextIns.opcode==Opcode.POP_MEM_FLOAT) {
copyFloat(ins.arg!!.integerValue().toHex(), nextIns.arg!!.integerValue().toHex()) // copy memory float to memory float
return 1
}
pushFloat(ins.arg!!.integerValue().toHex())
}
else-> TODO("asm for $ins")
}
return 0
}
*/
}

View File

@ -1,6 +1,9 @@
package prog8.compiler.target.c64
import prog8.compiler.*
import prog8.compiler.CompilationOptions
import prog8.compiler.CompilerException
import prog8.compiler.Zeropage
import prog8.compiler.ZeropageType
import java.awt.Color
import java.awt.image.BufferedImage
import javax.imageio.ImageIO

View File

@ -37,6 +37,9 @@ fun importModule(filePath: Path) : Module {
// process imports
val lines = moduleAst.statements.toMutableList()
// always import the prog8 compiler library
if(!moduleAst.position.file.startsWith("prog8lib."))
lines.add(0, Directive("%import", listOf(DirectiveArg(null, "prog8lib", null, moduleAst.position)), moduleAst.position))
val imports = lines
.asSequence()
.mapIndexed { i, it -> Pair(i, it) }

View File

@ -153,10 +153,4 @@ class ScreenDialog : JFrame() {
val repaintTimer = Timer(1000 / 60) { _ -> repaint() }
repaintTimer.start()
}
private fun onOK() {
// add your code here
dispose()
System.exit(0)
}
}

View File

@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.*
import prog8.compiler.*
import prog8.compiler.target.c64.*
import prog8.compiler.intermediate.Value
import prog8.compiler.target.c64.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -7,270 +7,24 @@
~ prog8_lib {
; note: the following ZP scratch registers must be the same as in c64lib
memory byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
memory byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
memory word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
memory word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
memory ubyte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
memory ubyte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
memory uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
memory uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
%asm {{
; ---- jmp (indirect) routines for register pairs containing the indirect address
; @todo still needed??
jsr_indirect_nozpuse_AX
sta jsr_indirect_tmp
stx jsr_indirect_tmp+1
jmp (jsr_indirect_tmp)
jsr_indirect_nozpuse_AY
sta jsr_indirect_tmp
sty jsr_indirect_tmp+1
jmp (jsr_indirect_tmp)
jsr_indirect_nozpuse_XY
stx jsr_indirect_tmp
sty jsr_indirect_tmp+1
jmp (jsr_indirect_tmp)
jsr_indirect_tmp
.byte 0, 0
jsr_indirect_AX
sta SCRATCH_ZP1
stx SCRATCH_ZP2
jmp (SCRATCH_ZP1)
jsr_indirect_AY
sta SCRATCH_ZP1
sty SCRATCH_ZP2
jmp (SCRATCH_ZP1)
jsr_indirect_XY
stx SCRATCH_ZP1
sty SCRATCH_ZP2
jmp (SCRATCH_ZP1)
; copy memory UP from (SCRATCH_ZPWORD1) to (SCRATCH_ZPWORD2) of length X/Y (16-bit, X=lo, Y=hi)
; clobbers register A,X,Y
memcopy16_up
source = SCRATCH_ZPWORD1
dest = SCRATCH_ZPWORD2
length = SCRATCH_ZP1 ; (and SCRATCH_ZP2)
stx length
sty length+1
ldx length ; move low byte of length into X
bne + ; jump to start if X > 0
dec length ; subtract 1 from length
+ ldy #0 ; set Y to 0
- lda (source),y ; set A to whatever (source) points to offset by Y
sta (dest),y ; move A to location pointed to by (dest) offset by Y
iny ; increment Y
bne + ; if Y<>0 then (rolled over) then still moving bytes
inc source+1 ; increment hi byte of source
inc dest+1 ; increment hi byte of dest
+ dex ; decrement X (lo byte counter)
bne - ; if X<>0 then move another byte
dec length ; weve moved 255 bytes, dec length
bpl - ; if length is still positive go back and move more
rts ; done
; copy memory UP from (SCRATCH_ZPWORD1) to (AY) with length X (1 to 256, 0 meaning 256)
; destination must not overlap, or be before start, then overlap is possible.
; clobbers A, X, Y
memcopy
sta SCRATCH_ZPWORD2
sty SCRATCH_ZPWORD2+1
ldy #0
- lda (SCRATCH_ZPWORD1), y
sta (SCRATCH_ZPWORD2), y
iny
dex
bne -
rts
; fill memory from (SCRATCH_ZPWORD1), length XY, with value in A.
; clobbers X, Y
memset stx SCRATCH_ZP1
sty SCRATCH_ZP2
ldy #0
ldx SCRATCH_ZP2
beq _lastpage
_fullpage sta (SCRATCH_ZPWORD1),y
iny
bne _fullpage
inc SCRATCH_ZPWORD1+1 ; next page
dex
bne _fullpage
_lastpage ldy SCRATCH_ZP1
beq +
- dey
sta (SCRATCH_ZPWORD1),y
bne -
+ rts
; fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY.
; clobbers A, X, Y
memsetw
sta _mod1+1 ; self-modify
sty _mod1b+1 ; self-modify
sta _mod2+1 ; self-modify
sty _mod2b+1 ; self-modify
ldx SCRATCH_ZPWORD1
stx SCRATCH_ZP1
ldx SCRATCH_ZPWORD1+1
inx
stx SCRATCH_ZP2 ; second page
ldy #0
ldx SCRATCH_ZPWORD2+1
beq _lastpage
_fullpage
_mod1 lda #0 ; self-modified
sta (SCRATCH_ZPWORD1),y ; first page
sta (SCRATCH_ZP1),y ; second page
iny
_mod1b lda #0 ; self-modified
sta (SCRATCH_ZPWORD1),y ; first page
sta (SCRATCH_ZP1),y ; second page
iny
bne _fullpage
inc SCRATCH_ZPWORD1+1 ; next page pair
inc SCRATCH_ZPWORD1+1 ; next page pair
inc SCRATCH_ZP1+1 ; next page pair
inc SCRATCH_ZP1+1 ; next page pair
dex
bne _fullpage
_lastpage ldx SCRATCH_ZPWORD2
beq _done
ldy #0
-
_mod2 lda #0 ; self-modified
sta (SCRATCH_ZPWORD1), y
inc SCRATCH_ZPWORD1
bne _mod2b
inc SCRATCH_ZPWORD1+1
_mod2b lda #0 ; self-modified
sta (SCRATCH_ZPWORD1), y
inc SCRATCH_ZPWORD1
bne +
inc SCRATCH_ZPWORD1+1
+ dex
bne -
_done rts
; increments/decrements a byte referenced by indirect register pair by 1
; clobbers A/Y, Carry flag determines incr or decr
incrdecr_deref_byte_reg_AX
sta SCRATCH_ZPWORD1
stx SCRATCH_ZPWORD1+1
bcc incr_deref_byte
bcs decr_deref_byte
incrdecr_deref_byte_reg_AY
sta SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcc incr_deref_byte
bcs decr_deref_byte
incrdecr_deref_byte_reg_XY
stx SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcs decr_deref_byte
incr_deref_byte
ldy #0
lda (SCRATCH_ZPWORD1), y
adc #1 ; carry's cleared already
sta (SCRATCH_ZPWORD1), y
rts
decr_deref_byte
ldy #0
lda (SCRATCH_ZPWORD1), y
sbc #1 ; carry's set already
sta (SCRATCH_ZPWORD1), y
rts
; increments/decrements a word referenced by indirect register pair by 1
; clobbers A/Y, Carry flag determines incr or decr
incrdecr_deref_word_reg_AX
sta SCRATCH_ZPWORD1
stx SCRATCH_ZPWORD1+1
bcc incr_deref_word
bcs decr_deref_word
incrdecr_deref_word_reg_AY
sta SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcc incr_deref_word
bcs decr_deref_word
incrdecr_deref_word_reg_XY
stx SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcs decr_deref_word
incr_deref_word
ldy #0
lda (SCRATCH_ZPWORD1), y
adc #1 ; carry's cleared already
sta (SCRATCH_ZPWORD1), y
; 16-bit rotate right (as opposed to the 6502's usual 17-bit rotate with carry)
; the word is placed in SCRATCH_ZPWORD1
ror2_word
lsr SCRATCH_ZPWORD1+1
ror SCRATCH_ZPWORD1
bcc +
iny
lda (SCRATCH_ZPWORD1), y
adc #0 ; carry is set
sta (SCRATCH_ZPWORD1), y
+ rts
decr_deref_word
ldy #0
lda (SCRATCH_ZPWORD1), y
bne +
pha
iny
lda (SCRATCH_ZPWORD1), y
sbc #1 ; carry's set already
sta (SCRATCH_ZPWORD1), y
dey
pla
+ sec
sbc #1
sta (SCRATCH_ZPWORD1), y
rts
; shift bits in A right by X positions
lsr_A_by_X
cpx #8
bcc _shift
lda #0 ; x >=8, result always 0
rts
_shift cpx #0
beq +
- lsr a
dex
bne -
lda SCRATCH_ZPWORD1+1
ora #$80
sta SCRATCH_ZPWORD1+1
+ rts
; shift bits in A left by X positions
asl_A_by_X
cpx #8
bcc _shift
lda #0 ; x >=8, result always 0
rts
_shift cpx #0
beq +
- asl a
dex
bne -
+ rts
}}
}

276
prog8lib/prog8lib_OLD.p8 Normal file
View File

@ -0,0 +1,276 @@
; Prog8 internal library routines - always included by the compiler
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
~ prog8_lib {
; note: the following ZP scratch registers must be the same as in c64lib
memory byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
memory byte SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
memory word SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
memory word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
%asm {{
; ---- jmp (indirect) routines for register pairs containing the indirect address
; @todo still needed??
jsr_indirect_nozpuse_AX
sta jsr_indirect_tmp
stx jsr_indirect_tmp+1
jmp (jsr_indirect_tmp)
jsr_indirect_nozpuse_AY
sta jsr_indirect_tmp
sty jsr_indirect_tmp+1
jmp (jsr_indirect_tmp)
jsr_indirect_nozpuse_XY
stx jsr_indirect_tmp
sty jsr_indirect_tmp+1
jmp (jsr_indirect_tmp)
jsr_indirect_tmp
.byte 0, 0
jsr_indirect_AX
sta SCRATCH_ZP1
stx SCRATCH_ZP2
jmp (SCRATCH_ZP1)
jsr_indirect_AY
sta SCRATCH_ZP1
sty SCRATCH_ZP2
jmp (SCRATCH_ZP1)
jsr_indirect_XY
stx SCRATCH_ZP1
sty SCRATCH_ZP2
jmp (SCRATCH_ZP1)
; copy memory UP from (SCRATCH_ZPWORD1) to (SCRATCH_ZPWORD2) of length X/Y (16-bit, X=lo, Y=hi)
; clobbers register A,X,Y
memcopy16_up
source = SCRATCH_ZPWORD1
dest = SCRATCH_ZPWORD2
length = SCRATCH_ZP1 ; (and SCRATCH_ZP2)
stx length
sty length+1
ldx length ; move low byte of length into X
bne + ; jump to start if X > 0
dec length ; subtract 1 from length
+ ldy #0 ; set Y to 0
- lda (source),y ; set A to whatever (source) points to offset by Y
sta (dest),y ; move A to location pointed to by (dest) offset by Y
iny ; increment Y
bne + ; if Y<>0 then (rolled over) then still moving bytes
inc source+1 ; increment hi byte of source
inc dest+1 ; increment hi byte of dest
+ dex ; decrement X (lo byte counter)
bne - ; if X<>0 then move another byte
dec length ; weve moved 255 bytes, dec length
bpl - ; if length is still positive go back and move more
rts ; done
; copy memory UP from (SCRATCH_ZPWORD1) to (AY) with length X (1 to 256, 0 meaning 256)
; destination must not overlap, or be before start, then overlap is possible.
; clobbers A, X, Y
memcopy
sta SCRATCH_ZPWORD2
sty SCRATCH_ZPWORD2+1
ldy #0
- lda (SCRATCH_ZPWORD1), y
sta (SCRATCH_ZPWORD2), y
iny
dex
bne -
rts
; fill memory from (SCRATCH_ZPWORD1), length XY, with value in A.
; clobbers X, Y
memset stx SCRATCH_ZP1
sty SCRATCH_ZP2
ldy #0
ldx SCRATCH_ZP2
beq _lastpage
_fullpage sta (SCRATCH_ZPWORD1),y
iny
bne _fullpage
inc SCRATCH_ZPWORD1+1 ; next page
dex
bne _fullpage
_lastpage ldy SCRATCH_ZP1
beq +
- dey
sta (SCRATCH_ZPWORD1),y
bne -
+ rts
; fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY.
; clobbers A, X, Y
memsetw
sta _mod1+1 ; self-modify
sty _mod1b+1 ; self-modify
sta _mod2+1 ; self-modify
sty _mod2b+1 ; self-modify
ldx SCRATCH_ZPWORD1
stx SCRATCH_ZP1
ldx SCRATCH_ZPWORD1+1
inx
stx SCRATCH_ZP2 ; second page
ldy #0
ldx SCRATCH_ZPWORD2+1
beq _lastpage
_fullpage
_mod1 lda #0 ; self-modified
sta (SCRATCH_ZPWORD1),y ; first page
sta (SCRATCH_ZP1),y ; second page
iny
_mod1b lda #0 ; self-modified
sta (SCRATCH_ZPWORD1),y ; first page
sta (SCRATCH_ZP1),y ; second page
iny
bne _fullpage
inc SCRATCH_ZPWORD1+1 ; next page pair
inc SCRATCH_ZPWORD1+1 ; next page pair
inc SCRATCH_ZP1+1 ; next page pair
inc SCRATCH_ZP1+1 ; next page pair
dex
bne _fullpage
_lastpage ldx SCRATCH_ZPWORD2
beq _done
ldy #0
-
_mod2 lda #0 ; self-modified
sta (SCRATCH_ZPWORD1), y
inc SCRATCH_ZPWORD1
bne _mod2b
inc SCRATCH_ZPWORD1+1
_mod2b lda #0 ; self-modified
sta (SCRATCH_ZPWORD1), y
inc SCRATCH_ZPWORD1
bne +
inc SCRATCH_ZPWORD1+1
+ dex
bne -
_done rts
; increments/decrements a byte referenced by indirect register pair by 1
; clobbers A/Y, Carry flag determines incr or decr
incrdecr_deref_byte_reg_AX
sta SCRATCH_ZPWORD1
stx SCRATCH_ZPWORD1+1
bcc incr_deref_byte
bcs decr_deref_byte
incrdecr_deref_byte_reg_AY
sta SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcc incr_deref_byte
bcs decr_deref_byte
incrdecr_deref_byte_reg_XY
stx SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcs decr_deref_byte
incr_deref_byte
ldy #0
lda (SCRATCH_ZPWORD1), y
adc #1 ; carry's cleared already
sta (SCRATCH_ZPWORD1), y
rts
decr_deref_byte
ldy #0
lda (SCRATCH_ZPWORD1), y
sbc #1 ; carry's set already
sta (SCRATCH_ZPWORD1), y
rts
; increments/decrements a word referenced by indirect register pair by 1
; clobbers A/Y, Carry flag determines incr or decr
incrdecr_deref_word_reg_AX
sta SCRATCH_ZPWORD1
stx SCRATCH_ZPWORD1+1
bcc incr_deref_word
bcs decr_deref_word
incrdecr_deref_word_reg_AY
sta SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcc incr_deref_word
bcs decr_deref_word
incrdecr_deref_word_reg_XY
stx SCRATCH_ZPWORD1
sty SCRATCH_ZPWORD1+1
bcs decr_deref_word
incr_deref_word
ldy #0
lda (SCRATCH_ZPWORD1), y
adc #1 ; carry's cleared already
sta (SCRATCH_ZPWORD1), y
bcc +
iny
lda (SCRATCH_ZPWORD1), y
adc #0 ; carry is set
sta (SCRATCH_ZPWORD1), y
+ rts
decr_deref_word
ldy #0
lda (SCRATCH_ZPWORD1), y
bne +
pha
iny
lda (SCRATCH_ZPWORD1), y
sbc #1 ; carry's set already
sta (SCRATCH_ZPWORD1), y
dey
pla
+ sec
sbc #1
sta (SCRATCH_ZPWORD1), y
rts
; shift bits in A right by X positions
lsr_A_by_X
cpx #8
bcc _shift
lda #0 ; x >=8, result always 0
rts
_shift cpx #0
beq +
- lsr a
dex
bne -
+ rts
; shift bits in A left by X positions
asl_A_by_X
cpx #8
bcc _shift
lda #0 ; x >=8, result always 0
rts
_shift cpx #0
beq +
- asl a
dex
bne -
+ rts
}}
}