stackvm can now intercept system asm calls (to a rom address)

This commit is contained in:
Irmen de Jong 2019-03-11 02:05:30 +01:00
parent 747ee32e81
commit ac7faa8d25
8 changed files with 113 additions and 28 deletions

View File

@ -1 +1 @@
1.4 (beta)
1.5 (beta)

View File

@ -377,7 +377,16 @@ internal class Compiler(private val rootModule: Module,
}
private fun translate(stmt: InlineAssembly) {
prog.instr(Opcode.INLINE_ASSEMBLY, callLabel = stmt.assembly)
// If the inline assembly is the only statement inside a subroutine (except vardecls),
// we can use the name of that subroutine to identify it.
// The compiler could then convert it to a special system call
val sub = stmt.parent as? Subroutine
val scopename =
if(sub!=null && sub.statements.filter{it !is VarDecl}.size==1)
sub.scopedname
else
null
prog.instr(Opcode.INLINE_ASSEMBLY, callLabel=scopename, callLabel2 = stmt.assembly)
}
private fun translate(stmt: Continue) {
@ -2233,7 +2242,7 @@ internal class Compiler(private val rootModule: Module,
File(filename).readText()
}
prog.instr(Opcode.INLINE_ASSEMBLY, callLabel=scopeprefix+sourcecode+scopeprefixEnd)
prog.instr(Opcode.INLINE_ASSEMBLY, callLabel=null, callLabel2=scopeprefix+sourcecode+scopeprefixEnd)
}
private fun translateAsmBinary(args: List<DirectiveArg>) {

View File

@ -10,13 +10,21 @@ open class Instruction(val opcode: Opcode,
{
lateinit var next: Instruction
var nextAlt: Instruction? = null
var branchAddress: Int? = null
override fun toString(): String {
val argStr = arg?.toString() ?: ""
val result =
when {
opcode==Opcode.LINE -> "_line $callLabel"
opcode==Opcode.INLINE_ASSEMBLY -> "inline_assembly"
opcode==Opcode.INLINE_ASSEMBLY -> {
// inline assembly is not written out (it can't be processed as intermediate language)
// instead, it is converted into a system call that can be intercepted by the vm
if(callLabel!=null)
"syscall SYSASM.$callLabel\n return"
else
"inline_assembly"
}
opcode==Opcode.SYSCALL -> {
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
"syscall $syscall"

View File

@ -476,7 +476,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.DISCARD_BYTE -> " inx"
Opcode.DISCARD_WORD -> " inx"
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel ?: "") // All of the inline assembly is stored in the calllabel property. the '@inline@' is a special marker to process it.
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to process it.
Opcode.SYSCALL -> {
if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr })
throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}")

View File

@ -142,8 +142,19 @@ class Program (val name: String,
Instruction(opcode, callLabel = withoutQuotes)
}
Opcode.SYSCALL -> {
val call = Syscall.valueOf(args!!)
Instruction(opcode, Value(DataType.UBYTE, call.callNr))
if(args!! in syscallNames) {
val call = Syscall.valueOf(args)
Instruction(opcode, Value(DataType.UBYTE, call.callNr))
} else {
val args2 = args.replace('.', '_')
if(args2 in syscallNames) {
val call = Syscall.valueOf(args2)
Instruction(opcode, Value(DataType.UBYTE, call.callNr))
} else {
// the syscall is not yet implemented. emit a stub.
Instruction(Opcode.SYSCALL, Value(DataType.UBYTE, Syscall.SYSCALLSTUB.callNr), callLabel = args2)
}
}
}
else -> {
Instruction(opcode, getArgValue(args, heap))
@ -275,31 +286,24 @@ class Program (val name: String,
Opcode.TERMINATE -> instr.next = instr // won't ever execute a next instruction
Opcode.RETURN -> instr.next = instr // kinda a special one, in actuality the return instruction is dynamic
Opcode.JUMP -> {
if(instr.callLabel==null) {
if(instr.callLabel==null)
throw VmExecutionException("stackVm doesn't support JUMP to memory address")
} else {
// jump to label
val target = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
instr.next = target
}
else
linkJumpTarget(instr)
}
Opcode.BCC, Opcode.BCS, Opcode.BZ, Opcode.BNZ, Opcode.BNEG, Opcode.BPOS, Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW -> {
if(instr.callLabel==null) {
throw VmExecutionException("stackVm doesn't support branch to memory address")
} else {
// branch to label
val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
instr.next = jumpInstr
instr.nextAlt = nextInstr
linkJumpTarget(instr) // branch label
instr.nextAlt = nextInstr // alternate branch
}
}
Opcode.CALL -> {
if(instr.callLabel==null) {
throw VmExecutionException("stackVm doesn't support CALL to memory address")
} else {
// call label
val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
instr.next = jumpInstr
linkJumpTarget(instr) // call label
instr.nextAlt = nextInstr // instruction to return to
}
}
@ -307,4 +311,17 @@ class Program (val name: String,
}
}
}
private fun linkJumpTarget(instr: Instruction) {
val target = labels[instr.callLabel]
if(target==null) {
val memtarget = memoryPointers[instr.callLabel]?.first
if(memtarget==null)
throw VmExecutionException("undefined label: ${instr.callLabel}")
else {
instr.branchAddress=memtarget
}
} else
instr.next = target
}
}

View File

@ -15,6 +15,8 @@ class BitmapScreenPanel : KeyListener, JPanel() {
private val image = BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_ARGB)
private val g2d = image.graphics as Graphics2D
private var cursorX: Int=0
private var cursorY: Int=0
init {
val size = Dimension(image.width * SCALING, image.height * SCALING)
@ -56,13 +58,13 @@ class BitmapScreenPanel : KeyListener, JPanel() {
g2d.color = palette[color and 15]
g2d.drawLine(x1, y1, x2, y2)
}
fun writeText(x: Int, y: Int, text: String, color: Int) {
fun writeText(x: Int, y: Int, text: String, color: Int, lowercase: Boolean) {
if(color!=1) {
TODO("text can only be white for now")
}
var xx=x
var yy=y
for(sc in Petscii.encodeScreencode(text, true)) {
for(sc in Petscii.encodeScreencode(text, lowercase)) {
setChar(xx, yy, sc)
xx++
if(xx>=(SCREENWIDTH/8)) {
@ -75,6 +77,15 @@ class BitmapScreenPanel : KeyListener, JPanel() {
g2d.drawImage(Charset.shiftedChars[screenCode.toInt()], 8*x, 8*y , null)
}
fun setCursorPos(x: Int, y: Int) {
cursorX = x
cursorY = y
}
fun getCursorPos(): Pair<Int, Int> {
return Pair(cursorX, cursorY)
}
companion object {
const val SCREENWIDTH = 320

View File

@ -79,12 +79,21 @@ enum class Syscall(val callNr: Short) {
FUNC_MEMCOPY(138),
FUNC_MEMSET(139),
FUNC_MEMSETW(140),
FUNC_READ_FLAGS(141)
FUNC_READ_FLAGS(141),
// note: not all builtin functions of the Prog8 language are present as functions:
// some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL_BYTE, ROR, ROL2, ROR2, and FLT)!
// vm intercepts of system routines:
SYSCALLSTUB(200),
SYSASM_c64scr_PLOT(201),
SYSASM_c64scr_print(202),
SYSASM_c64scr_setcc(203),
}
val syscallNames = enumValues<Syscall>().map { it.name }.toSet()
val syscallsForStackVm = setOf(
Syscall.VM_WRITE_MEMCHR,
Syscall.VM_WRITE_MEMSTR,
@ -1502,7 +1511,7 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.RSAVEX -> evalstack.push(variables["X"])
Opcode.RRESTOREX -> variables["X"] = evalstack.pop()
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code")
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins")
Opcode.PUSH_ADDR_HEAPVAR -> {
val heapId = variables[ins.callLabel]!!.heapId
if(heapId<0)
@ -1538,7 +1547,16 @@ class StackVm(private var traceOutputFile: String?) {
evalstack.printTop(4, traceOutput!!)
}
return ins.next
if(ins.branchAddress!=null) {
// this is an instruction that jumps to a system routine (memory address)
if(ins.nextAlt==null)
throw VmExecutionException("call to system routine requires nextAlt return instruction set: $ins")
else {
println("CALLING SYSTEM ROUTINE $ins")
return ins.nextAlt!!
}
} else
return ins.next
}
private fun typecast(from: DataType, to: DataType) {
@ -1599,7 +1617,7 @@ class StackVm(private var traceOutputFile: String?) {
val color = evalstack.pop()
val (cy, cx) = evalstack.pop2()
val text = heap.get(textPtr)
canvas?.writeText(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue())
canvas?.writeText(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue(), true)
}
Syscall.FUNC_RND -> evalstack.push(Value(DataType.UBYTE, rnd.nextInt() and 255))
Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.UWORD, rnd.nextInt() and 65535))
@ -1803,7 +1821,29 @@ class StackVm(private var traceOutputFile: String?) {
mem.setSWord(addr, wordvalue)
else -> throw VmExecutionException("(u)word value expected")
}
} }
}
Syscall.SYSCALLSTUB -> throw VmExecutionException("unimplemented sysasm called: ${ins.callLabel} Create a Syscall enum for this and implement the vm intercept for it.")
Syscall.SYSASM_c64scr_PLOT -> {
val x = variables["Y"]!!.integerValue()
val y = variables["A"]!!.integerValue()
canvas?.setCursorPos(x, y)
}
Syscall.SYSASM_c64scr_print -> {
val (x, y) = canvas!!.getCursorPos()
val straddr = variables["A"]!!.integerValue() + 256*variables["Y"]!!.integerValue()
val str = heap.get(straddr).str!!
canvas?.writeText(x, y, str, 1, true)
}
Syscall.SYSASM_c64scr_setcc -> {
val x = variables["c64scr.setcc.column"]!!.integerValue()
val y = variables["c64scr.setcc.row"]!!.integerValue()
val char = variables["c64scr.setcc.char"]!!.integerValue()
// val color = variables["c64scr.setcc.color"]!!.integerValue() // text color other than 1 (white) can't be used right now
canvas?.setChar(x, y, char.toShort())
println("setcc $x $y") // TODO the vm is in an endless loop here when running tehtriz with this!
}
else -> throw VmExecutionException("unimplemented syscall $syscall")
}
}
fun irq(timestamp: Long) {

View File

@ -323,7 +323,7 @@ waitkey:
ubyte[5] colors = [6,8,7,5,4]
for i in len(colors)-1 to 0 step -1 {
for ubyte x in 5 to 0 step -1 {
c64scr.setcc(6+x-i, 11+2*i, 102, colors[i])
c64scr.setcc(6+x-i, 11+2*i, 102, colors[i]) ; @todo when run in the stackVM, this loop never ends and corrupts the screen
}
}
drawScore()