mirror of
https://github.com/irmen/prog8.git
synced 2024-09-28 17:54:58 +00:00
stackvm can now intercept system asm calls (to a rom address)
This commit is contained in:
parent
747ee32e81
commit
ac7faa8d25
@ -1 +1 @@
|
||||
1.4 (beta)
|
||||
1.5 (beta)
|
||||
|
@ -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>) {
|
||||
|
@ -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"
|
||||
|
@ -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()}")
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user