6502 start

This commit is contained in:
Irmen de Jong 2018-10-14 01:55:39 +02:00
parent b7d8f026f4
commit b52120139c
8 changed files with 390 additions and 11 deletions

View File

@ -16,6 +16,16 @@ sub start() {
word wv2
uword uwv2
X=X
X=X
Y=Y
X=A
A=Y
A=ubvar
AX=XY
XY=XY
AY=uwvar
XY=uwvar
bv2 = ub2b(ubvar)
ubv2 = b2ub(bvar)

View File

@ -42,6 +42,8 @@ fun main(args: Array<String>) {
as? Directive)?.args?.single()?.name?.toUpperCase()
val launcherType = (moduleAst.statements.singleOrNull { it is Directive && it.directive=="%launcher"}
as? Directive)?.args?.single()?.name?.toUpperCase()
moduleAst.loadAddress = (moduleAst.statements.singleOrNull { it is Directive && it.directive=="%address"}
as? Directive)?.args?.single()?.int ?: 0
val zpoption: String? = (moduleAst.statements.singleOrNull { it is Directive && it.directive=="%zeropage"}
as? Directive)?.args?.single()?.name?.toUpperCase()
val zpType: ZeropageType =

View File

@ -421,6 +421,8 @@ class Module(override val name: String,
this.parent=parent
}
var loadAddress: Int = 0 // can be set with the %address directive
fun linkParents() {
parent = ParentSentinel
statements.forEach {it.linkParents(this)}

View File

@ -159,7 +159,7 @@ class Compiler(private val options: CompilationOptions) {
println("\nCreating stackVM code...")
val namespace = module.definingScope()
val program = IntermediateProgram(module.name, heap)
val program = IntermediateProgram(module.name, module.loadAddress, heap)
val translator = StatementTranslator(program, namespace, heap)
translator.process(module)
@ -178,7 +178,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
val continueStmtLabelStack : Stack<String> = Stack()
override fun process(block: Block): IStatement {
prog.newBlock(block.scopedname, block.address)
prog.newBlock(block.scopedname, block.name, block.address)
processVariables(block)
prog.label(block.scopedname)
prog.line(block.position)

View File

@ -9,9 +9,9 @@ import prog8.compiler.HeapValues
import java.io.PrintStream
class IntermediateProgram(val name: String, val heap: HeapValues) {
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues) {
private class ProgramBlock(val scopedname: String, val address: Int?) {
class ProgramBlock(val scopedname: String, val shortname: String, var address: Int?) {
val instructions = mutableListOf<Instruction>()
val variables = mutableMapOf<String, Value>()
val labels = mutableMapOf<String, Instruction>()
@ -20,10 +20,16 @@ class IntermediateProgram(val name: String, val heap: HeapValues) {
get() { return variables.size }
val numInstructions: Int
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
fun getIns(idx: Int): Instruction {
if(idx>=0 && idx <instructions.size)
return instructions[idx]
return Instruction(Opcode.NOP)
}
}
private val blocks = mutableListOf<ProgramBlock>()
private val memory = mutableMapOf<Int, List<Value>>()
val blocks = mutableListOf<ProgramBlock>()
val memory = mutableMapOf<Int, List<Value>>()
private lateinit var currentBlock: ProgramBlock
val numVariables: Int
@ -273,8 +279,8 @@ class IntermediateProgram(val name: String, val heap: HeapValues) {
currentBlock.instructions.add(Instruction(Opcode.LINE, callLabel = "${position.line} ${position.file}"))
}
fun newBlock(scopedname: String, address: Int?) {
currentBlock = ProgramBlock(scopedname, address)
fun newBlock(scopedname: String, shortname: String, address: Int?) {
currentBlock = ProgramBlock(scopedname, shortname, address)
blocks.add(currentBlock)
}

View File

@ -1,15 +1,371 @@
package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions
import prog8.compiler.LauncherType
import prog8.compiler.OutputType
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.LabelInstr
import prog8.compiler.intermediate.Opcode
import prog8.compiler.toHex
import java.io.File
import java.util.*
class AssemblyError(msg: String) : RuntimeException(msg)
class AsmGen(val options: CompilationOptions) {
fun compileToAssembly(program: IntermediateProgram): AssemblyProgram {
println("\nGenerating assembly code from intermediate code... ")
// todo generate 6502 assembly
val out = File("${program.name}.asm").printWriter()
out.use {
header(out::println, program)
for(block in program.blocks)
block2asm(out::println, block)
}
return AssemblyProgram(program.name)
}
private fun header(out: (String)->Unit, program: IntermediateProgram) {
val ourName = this.javaClass.name
out("; 6502 assembly code for '${program.name}'")
out("; generated by $ourName on ${Date()}")
out("; assembler syntax is for the 64tasm cross-assembler")
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
out("\n.cpu '6502'\n.enc 'none'\n")
if(program.loadAddress==0) // fix load address
program.loadAddress = if(options.launcher==LauncherType.BASIC) BASIC_LOAD_ADDRESS else RAW_LOAD_ADDRESS
when {
options.launcher == LauncherType.BASIC -> {
if (program.loadAddress != 0x0801)
throw AssemblyError("BASIC output must have load address $0801")
out("; ---- basic program with sys call ----")
out("* = ${program.loadAddress.toHex()}")
val year = Calendar.getInstance().get(Calendar.YEAR)
out("\t.word (+), $year")
out("\t.null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
out("+\t.word 0")
out("_prog8_entrypoint\t; assembly code starts here\n")
}
options.output == OutputType.PRG -> {
out("; ---- program without sys call ----")
out("* = ${program.loadAddress.toHex()}\n")
}
options.output == OutputType.RAW -> {
out("; ---- raw assembler program ----")
out("* = ${program.loadAddress.toHex()}\n")
}
}
// call the init methods of each block and then jump to the main.start entrypoint
out("\t; initialize all blocks(reset vars)")
// todo zeropage if it's there
for(block in program.blocks)
out("\tjsr ${block.scopedname}._prog8_init")
out("\tjmp main.start\t; jump to program entrypoint")
}
private fun block2asm(out: (String)->Unit, block: IntermediateProgram.ProgramBlock) {
out("\n; ---- block: '${block.shortname}' ----")
if(block.address!=null) {
out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'")
out("* = ${block.address?.toHex()}")
}
out("${block.shortname}\t.proc\n")
// init the variables
out("_prog8_init\t; (re)set vars to initial values")
// todo init vars
var skip = 0
for(ins in block.instructions.withIndex()) {
if(skip==0)
skip = ins2asm(out, ins.index, ins.value, block)
else
skip--
}
out("\n\t.pend\n")
}
private val registerStrings = setOf("A", "X", "Y", "AX", "AY", "XY")
private fun ins2asm(out: (String) -> Unit, insIdx: Int, ins: Instruction, block: IntermediateProgram.ProgramBlock): Int {
if(ins is LabelInstr) {
if(ins.name==block.shortname)
return 0
if(ins.name.startsWith("${block.shortname}."))
out(ins.name.substring(block.shortname.length+1))
else
out(ins.name)
out("\trts") // todo weg
return 0
}
when(ins.opcode) {
Opcode.LINE -> out("\t; src line: ${ins.callLabel}")
Opcode.NOP -> out("\tnop") // shouldn't be present anymore though
Opcode.SEC -> out("\tsec")
Opcode.CLC -> out("\tclc")
Opcode.SEI -> out("\tsei")
Opcode.CLI -> out("\tcli")
Opcode.TERMINATE -> out("\tbrk\t; terminate!")
Opcode.B2UB -> {} // is a no-op, just carry on with the byte as-is
Opcode.UB2B -> {} // is a no-op, just carry on with the byte as-is
Opcode.PUSH_BYTE -> {
// check if we load a register (X, Y) with constant value
val nextIns = block.getIns(insIdx+1)
if(nextIns.opcode==Opcode.POP_VAR_BYTE && nextIns.callLabel in registerStrings) {
out("\tld${nextIns.callLabel!!.toLowerCase()} #${ins.arg!!.integerValue().toHex()}")
return 1 // skip 1
}
// todo push_byte
}
Opcode.PUSH_WORD -> {
// check if we load a register (AX, AY, XY) with constant value
val nextIns = block.getIns(insIdx+1)
if(nextIns.opcode==Opcode.POP_VAR_WORD && nextIns.callLabel in registerStrings) {
val regs = nextIns.callLabel!!.toLowerCase()
val value = ins.arg!!.integerValue().toHex()
out("\tld${regs[0]} #<$value")
out("\tld${regs[1]} #>$value")
return 1 // skip 1
}
// todo push_word
}
Opcode.COPY_VAR_BYTE -> {
if(ins.callLabel2 in registerStrings) {
if(ins.callLabel in registerStrings) {
// copying register -> register
when {
ins.callLabel == "A" -> out("\tta${ins.callLabel2!!.toLowerCase()}")
ins.callLabel == "X" -> if (ins.callLabel2 == "Y") {
// 6502 doesn't have txy
out("\ttxa\n\ttay")
} else out("\ttxa")
ins.callLabel == "Y" -> if (ins.callLabel2 == "X") {
// 6502 doesn't have tyx
out("\ttya\n\ttax")
} else out("\ttya")
}
return 0
}
}
// todo copy_var_byte
}
Opcode.COPY_VAR_WORD -> {
if(ins.callLabel2 in registerStrings) {
if(ins.callLabel in registerStrings) {
// copying registerpair -> registerpair
when {
ins.callLabel == "AX" -> when (ins.callLabel2) {
"AY" -> out("\ttxy")
"XY" -> out("\tpha\n\ttxa\n\ttay\n\tpla\n\ttax")
}
ins.callLabel == "AY" -> when (ins.callLabel2) {
"AX" -> out("\tpha\n\ttya\n\ttax\n\tpla")
"XY" -> out("\ttax")
}
ins.callLabel == "XY" -> when (ins.callLabel2) {
"AX" -> out("\ttxa\n\tpha\n\ttya\n\ttax\n\tpla")
"AY" -> out("\ttxa")
}
}
return 0
}
}
// todo copy_var_byte
}
else-> {}
// Opcode.PUSH_FLOAT -> TODO()
// Opcode.PUSH_MEM_B -> TODO()
// Opcode.PUSH_MEM_UB -> TODO()
// Opcode.PUSH_MEM_W -> TODO()
// Opcode.PUSH_MEM_UW -> TODO()
// Opcode.PUSH_MEM_FLOAT -> TODO()
// Opcode.PUSH_VAR_BYTE -> TODO()
// Opcode.PUSH_VAR_WORD -> TODO()
// Opcode.PUSH_VAR_FLOAT -> TODO()
// Opcode.DISCARD_BYTE -> TODO()
// Opcode.DISCARD_WORD -> TODO()
// Opcode.DISCARD_FLOAT -> TODO()
// Opcode.POP_MEM_B -> TODO()
// Opcode.POP_MEM_UB -> TODO()
// Opcode.POP_MEM_W -> TODO()
// Opcode.POP_MEM_UW -> TODO()
// Opcode.POP_MEM_FLOAT -> TODO()
// Opcode.POP_VAR_BYTE -> TODO()
// Opcode.POP_VAR_WORD -> TODO()
// Opcode.POP_VAR_FLOAT -> TODO()
// Opcode.COPY_VAR_BYTE -> TODO()
// Opcode.COPY_VAR_WORD -> TODO()
// Opcode.COPY_VAR_FLOAT -> TODO()
// Opcode.ADD_UB -> TODO()
// Opcode.ADD_B -> TODO()
// Opcode.ADD_UW -> TODO()
// Opcode.ADD_W -> TODO()
// Opcode.ADD_F -> TODO()
// Opcode.SUB_UB -> TODO()
// Opcode.SUB_B -> TODO()
// Opcode.SUB_UW -> TODO()
// Opcode.SUB_W -> TODO()
// Opcode.SUB_F -> TODO()
// Opcode.MUL_UB -> TODO()
// Opcode.MUL_B -> TODO()
// Opcode.MUL_UW -> TODO()
// Opcode.MUL_W -> TODO()
// Opcode.MUL_F -> TODO()
// Opcode.DIV_UB -> TODO()
// Opcode.DIV_B -> TODO()
// Opcode.DIV_UW -> TODO()
// Opcode.DIV_W -> TODO()
// Opcode.DIV_F -> TODO()
// Opcode.FLOORDIV_UB -> TODO()
// Opcode.FLOORDIV_B -> TODO()
// Opcode.FLOORDIV_UW -> TODO()
// Opcode.FLOORDIV_W -> TODO()
// Opcode.FLOORDIV_F -> TODO()
// Opcode.REMAINDER_UB -> TODO()
// Opcode.REMAINDER_B -> TODO()
// Opcode.REMAINDER_UW -> TODO()
// Opcode.REMAINDER_W -> TODO()
// Opcode.REMAINDER_F -> TODO()
// Opcode.POW_UB -> TODO()
// Opcode.POW_B -> TODO()
// Opcode.POW_UW -> TODO()
// Opcode.POW_W -> TODO()
// Opcode.POW_F -> TODO()
// Opcode.NEG_B -> TODO()
// Opcode.NEG_W -> TODO()
// Opcode.NEG_F -> TODO()
// Opcode.SHL_BYTE -> TODO()
// Opcode.SHL_WORD -> TODO()
// Opcode.SHL_MEM_BYTE -> TODO()
// Opcode.SHL_MEM_WORD -> TODO()
// Opcode.SHL_VAR_BYTE -> TODO()
// Opcode.SHL_VAR_WORD -> TODO()
// Opcode.SHR_BYTE -> TODO()
// Opcode.SHR_WORD -> TODO()
// Opcode.SHR_MEM_BYTE -> TODO()
// Opcode.SHR_MEM_WORD -> TODO()
// Opcode.SHR_VAR_BYTE -> TODO()
// Opcode.SHR_VAR_WORD -> TODO()
// Opcode.ROL_BYTE -> TODO()
// Opcode.ROL_WORD -> TODO()
// Opcode.ROL_MEM_BYTE -> TODO()
// Opcode.ROL_MEM_WORD -> TODO()
// Opcode.ROL_VAR_BYTE -> TODO()
// Opcode.ROL_VAR_WORD -> TODO()
// Opcode.ROR_BYTE -> TODO()
// Opcode.ROR_WORD -> TODO()
// Opcode.ROR_MEM_BYTE -> TODO()
// Opcode.ROR_MEM_WORD -> TODO()
// Opcode.ROR_VAR_BYTE -> TODO()
// Opcode.ROR_VAR_WORD -> TODO()
// Opcode.ROL2_BYTE -> TODO()
// Opcode.ROL2_WORD -> TODO()
// Opcode.ROL2_MEM_BYTE -> TODO()
// Opcode.ROL2_MEM_WORD -> TODO()
// Opcode.ROL2_VAR_BYTE -> TODO()
// Opcode.ROL2_VAR_WORD -> TODO()
// Opcode.ROR2_BYTE -> TODO()
// Opcode.ROR2_WORD -> TODO()
// Opcode.ROR2_MEM_BYTE -> TODO()
// Opcode.ROR2_MEM_WORD -> TODO()
// Opcode.ROR2_VAR_BYTE -> TODO()
// Opcode.ROR2_VAR_WORD -> TODO()
// Opcode.BITAND_BYTE -> TODO()
// Opcode.BITAND_WORD -> TODO()
// Opcode.BITOR_BYTE -> TODO()
// Opcode.BITOR_WORD -> TODO()
// Opcode.BITXOR_BYTE -> TODO()
// Opcode.BITXOR_WORD -> TODO()
// Opcode.INV_BYTE -> TODO()
// Opcode.INV_WORD -> TODO()
// Opcode.LSB -> TODO()
// Opcode.MSB -> TODO()
// Opcode.B2WORD -> TODO()
// Opcode.UB2UWORD -> TODO()
// Opcode.MSB2WORD -> TODO()
// Opcode.B2FLOAT -> TODO()
// Opcode.UB2FLOAT -> TODO()
// Opcode.W2FLOAT -> TODO()
// Opcode.UW2FLOAT -> TODO()
// Opcode.AND_BYTE -> TODO()
// Opcode.AND_WORD -> TODO()
// Opcode.OR_BYTE -> TODO()
// Opcode.OR_WORD -> TODO()
// Opcode.XOR_BYTE -> TODO()
// Opcode.XOR_WORD -> TODO()
// Opcode.NOT_BYTE -> TODO()
// Opcode.NOT_WORD -> TODO()
// Opcode.INC_B -> TODO()
// Opcode.INC_UB -> TODO()
// Opcode.INC_W -> TODO()
// Opcode.INC_UW -> TODO()
// Opcode.INC_F -> TODO()
// Opcode.INC_VAR_B -> TODO()
// Opcode.INC_VAR_UB -> TODO()
// Opcode.INC_VAR_W -> TODO()
// Opcode.INC_VAR_UW -> TODO()
// Opcode.INC_VAR_F -> TODO()
// Opcode.DEC_B -> TODO()
// Opcode.DEC_UB -> TODO()
// Opcode.DEC_W -> TODO()
// Opcode.DEC_UW -> TODO()
// Opcode.DEC_F -> TODO()
// Opcode.DEC_VAR_B -> TODO()
// Opcode.DEC_VAR_UB -> TODO()
// Opcode.DEC_VAR_W -> TODO()
// Opcode.DEC_VAR_UW -> TODO()
// Opcode.DEC_VAR_F -> TODO()
// Opcode.LESS_B -> TODO()
// Opcode.LESS_UB -> TODO()
// Opcode.LESS_W -> TODO()
// Opcode.LESS_UW -> TODO()
// Opcode.LESS_F -> TODO()
// Opcode.GREATER_B -> TODO()
// Opcode.GREATER_UB -> TODO()
// Opcode.GREATER_W -> TODO()
// Opcode.GREATER_UW -> TODO()
// Opcode.GREATER_F -> TODO()
// Opcode.LESSEQ_B -> TODO()
// Opcode.LESSEQ_UB -> TODO()
// Opcode.LESSEQ_W -> TODO()
// Opcode.LESSEQ_UW -> TODO()
// Opcode.LESSEQ_F -> TODO()
// Opcode.GREATEREQ_B -> TODO()
// Opcode.GREATEREQ_UB -> TODO()
// Opcode.GREATEREQ_W -> TODO()
// Opcode.GREATEREQ_UW -> TODO()
// Opcode.GREATEREQ_F -> TODO()
// Opcode.EQUAL_BYTE -> TODO()
// Opcode.EQUAL_WORD -> TODO()
// Opcode.EQUAL_F -> TODO()
// Opcode.NOTEQUAL_BYTE -> TODO()
// Opcode.NOTEQUAL_WORD -> TODO()
// Opcode.NOTEQUAL_F -> TODO()
// Opcode.READ_INDEXED_VAR_BYTE -> TODO()
// Opcode.READ_INDEXED_VAR_WORD -> TODO()
// Opcode.READ_INDEXED_VAR_FLOAT -> TODO()
// Opcode.WRITE_INDEXED_VAR_BYTE -> TODO()
// Opcode.WRITE_INDEXED_VAR_WORD -> TODO()
// Opcode.WRITE_INDEXED_VAR_FLOAT -> TODO()
// Opcode.JUMP -> TODO()
// Opcode.BCS -> TODO()
// Opcode.BCC -> TODO()
// Opcode.BZ -> TODO()
// Opcode.BNZ -> TODO()
// Opcode.BNEG -> TODO()
// Opcode.BPOS -> TODO()
// Opcode.CALL -> TODO()
// Opcode.RETURN -> TODO()
// Opcode.SYSCALL -> TODO()
// Opcode.BREAKPOINT -> TODO()
}
return 0
}
}

View File

@ -12,6 +12,9 @@ import kotlin.math.pow
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38
const val BASIC_LOAD_ADDRESS = 0x0801
const val RAW_LOAD_ADDRESS = 0xc000
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {

View File

@ -88,7 +88,7 @@ Directives
- default for ``raw`` output is ``$c000``
- default for ``prg`` output is ``$0801``
- cannot be changed if you select ``prg`` with a ``basic`` launcher;
then it is always ``$081d`` (immediately after the BASIC program), and the BASIC program itself is always at ``$0801``.
then it is always ``$081e`` (immediately after the BASIC program), and the BASIC program itself is always at ``$0801``.
This is because the C64 expects BASIC programs to start at this address.