This commit is contained in:
Irmen de Jong 2018-10-13 18:15:09 +02:00
parent dff4518608
commit b7d8f026f4
15 changed files with 450 additions and 425 deletions

View File

@ -2,6 +2,7 @@ package prog8
import prog8.ast.*
import prog8.compiler.*
import prog8.compiler.target.c64.AsmGen
import prog8.optimizing.constantFold
import prog8.optimizing.optimizeStatements
import prog8.optimizing.simplifyExpressions
@ -103,15 +104,13 @@ fun main(args: Array<String>) {
stackvmFile.close()
println("StackVM program code written to '$stackVmFilename'")
// val assembly = stackvmProg.compileToAssembly()
//
// assembly.assemble(compilerOptions, "input", "output")
// val monitorfile = assembly.generateBreakpointList()
val assembly = AsmGen(compilerOptions).compileToAssembly(intermediate)
assembly.assemble(compilerOptions)
val endTime = System.currentTimeMillis()
println("\nTotal compilation time: ${(endTime-startTime)/1000.0} sec.")
println("\nTotal compilation+assemble time: ${(endTime-startTime)/1000.0} sec.")
// // start the vice emulator
// // todo start the vice emulator
// val program = "foo"
// val cmdline = listOf("x64", "-moncommands", monitorfile,
// "-autostartprgmode", "1", "-autostart-warp", "-autostart", program)

View File

@ -1,6 +1,9 @@
package prog8.compiler
import prog8.ast.*
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.stackvm.*
import java.util.*
import kotlin.math.abs
@ -1394,7 +1397,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
continueStmtLabelStack.push(continueLabel)
breakStmtLabelStack.push(breakLabel)
val zero = Value(if(numElements<=255) DataType.UBYTE else DataType.UWORD, 0)
val zero = Value(if (numElements <= 255) DataType.UBYTE else DataType.UWORD, 0)
prog.instr(opcodePush(zero.type), zero)
prog.instr(opcodePopvar(zero.type), callLabel = indexVar)
prog.label(loopLabel)
@ -1473,7 +1476,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
}
// TODO: optimize edge cases if last value = 255 or 0 (for bytes) etc. to avoid PUSH_BYTE / SUB opcodes and make use of the wrapping around of the value.
prog.instr(opcodePush(varDt), Value(varDt, range.last+range.step))
prog.instr(opcodePush(varDt), Value(varDt, range.last + range.step))
prog.instr(opcodePushvar(varDt), callLabel = varname)
prog.instr(opcodeSub(varDt))
prog.instr(Opcode.BNZ, callLabel = loopLabel)

View File

@ -0,0 +1,39 @@
package prog8.compiler.intermediate
import prog8.stackvm.Syscall
open class Instruction(val opcode: Opcode,
val arg: Value? = null,
val callLabel: String? = null,
val callLabel2: String? = null)
{
lateinit var next: Instruction
var nextAlt: Instruction? = null
override fun toString(): String {
val argStr = arg?.toString() ?: ""
val result =
when {
opcode== Opcode.LINE -> "_line $callLabel"
opcode== Opcode.SYSCALL -> {
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
"syscall $syscall"
}
opcode in opcodesWithVarArgument -> {
// opcodes that manipulate a variable
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
}
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
else -> "${opcode.toString().toLowerCase()} $callLabel $argStr"
}
.trimEnd()
return " $result"
}
}
class LabelInstr(val name: String) : Instruction(opcode = Opcode.NOP) {
override fun toString(): String {
return "\n$name:"
}
}

View File

@ -1,10 +1,11 @@
package prog8.compiler
package prog8.compiler.intermediate
import prog8.ast.DataType
import prog8.ast.LiteralValue
import prog8.ast.Position
import prog8.ast.VarDecl
import prog8.stackvm.*
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
import java.io.PrintStream

View File

@ -0,0 +1,241 @@
package prog8.compiler.intermediate
enum class Opcode {
// pushing values on the (evaluation) stack
PUSH_BYTE, // push byte value
PUSH_WORD, // push word value (or 'address' of string / array / matrix)
PUSH_FLOAT, // push float value
PUSH_MEM_B, // push byte value from memory to stack
PUSH_MEM_UB, // push byte value from memory to stack
PUSH_MEM_W, // push word value from memory to stack
PUSH_MEM_UW, // push word value from memory to stack
PUSH_MEM_FLOAT, // push float value from memory to stack
PUSH_VAR_BYTE, // push byte variable (ubyte, byte)
PUSH_VAR_WORD, // push word variable (uword, word)
PUSH_VAR_FLOAT, // push float variable
// popping values off the (evaluation) stack, possibly storing them in another location
DISCARD_BYTE, // discard top byte value
DISCARD_WORD, // discard top word value
DISCARD_FLOAT, // discard top float value
POP_MEM_B, // pop byte value into destination memory address
POP_MEM_UB, // pop byte value into destination memory address
POP_MEM_W, // pop word value into destination memory address
POP_MEM_UW, // pop word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address
POP_VAR_BYTE, // pop byte value into variable (byte, ubyte)
POP_VAR_WORD, // pop word value into variable (word, uword)
POP_VAR_FLOAT, // pop float value into variable
// optimized copying of one var to another (replaces push+pop)
COPY_VAR_BYTE,
COPY_VAR_WORD,
COPY_VAR_FLOAT,
// numeric arithmetic
ADD_UB,
ADD_B,
ADD_UW,
ADD_W,
ADD_F,
SUB_UB,
SUB_B,
SUB_UW,
SUB_W,
SUB_F,
MUL_UB,
MUL_B,
MUL_UW,
MUL_W,
MUL_F,
DIV_UB,
DIV_B,
DIV_UW,
DIV_W,
DIV_F,
FLOORDIV_UB,
FLOORDIV_B,
FLOORDIV_UW,
FLOORDIV_W,
FLOORDIV_F,
REMAINDER_UB,
REMAINDER_B,
REMAINDER_UW,
REMAINDER_W,
REMAINDER_F,
POW_UB,
POW_B,
POW_UW,
POW_W,
POW_F,
NEG_B,
NEG_W,
NEG_F,
// bit shifts and bitwise arithmetic
SHL_BYTE,
SHL_WORD,
SHL_MEM_BYTE,
SHL_MEM_WORD,
SHL_VAR_BYTE,
SHL_VAR_WORD,
SHR_BYTE,
SHR_WORD,
SHR_MEM_BYTE,
SHR_MEM_WORD,
SHR_VAR_BYTE,
SHR_VAR_WORD,
ROL_BYTE,
ROL_WORD,
ROL_MEM_BYTE,
ROL_MEM_WORD,
ROL_VAR_BYTE,
ROL_VAR_WORD,
ROR_BYTE,
ROR_WORD,
ROR_MEM_BYTE,
ROR_MEM_WORD,
ROR_VAR_BYTE,
ROR_VAR_WORD,
ROL2_BYTE,
ROL2_WORD,
ROL2_MEM_BYTE,
ROL2_MEM_WORD,
ROL2_VAR_BYTE,
ROL2_VAR_WORD,
ROR2_BYTE,
ROR2_WORD,
ROR2_MEM_BYTE,
ROR2_MEM_WORD,
ROR2_VAR_BYTE,
ROR2_VAR_WORD,
BITAND_BYTE,
BITAND_WORD,
BITOR_BYTE,
BITOR_WORD,
BITXOR_BYTE,
BITXOR_WORD,
INV_BYTE,
INV_WORD,
// numeric type conversions
LSB,
MSB,
B2UB,
UB2B,
B2WORD, // convert a byte into a word where it is the lower eight bits $ssxx with sign extension
UB2UWORD, // convert a byte into a word where it is the lower eight bits $00xx
MSB2WORD, // convert a byte into a word where it is the upper eight bits $xx00
B2FLOAT, // convert byte into floating point
UB2FLOAT, // convert unsigned byte into floating point
W2FLOAT, // convert word into floating point
UW2FLOAT, // convert unsigned word into floating point
// logical operations
AND_BYTE,
AND_WORD,
OR_BYTE,
OR_WORD,
XOR_BYTE,
XOR_WORD,
NOT_BYTE,
NOT_WORD,
// increment, decrement
INC_B,
INC_UB,
INC_W,
INC_UW,
INC_F,
INC_VAR_B,
INC_VAR_UB,
INC_VAR_W,
INC_VAR_UW,
INC_VAR_F,
DEC_B,
DEC_UB,
DEC_W,
DEC_UW,
DEC_F,
DEC_VAR_B,
DEC_VAR_UB,
DEC_VAR_W,
DEC_VAR_UW,
DEC_VAR_F,
// comparisons
LESS_B,
LESS_UB,
LESS_W,
LESS_UW,
LESS_F,
GREATER_B,
GREATER_UB,
GREATER_W,
GREATER_UW,
GREATER_F,
LESSEQ_B,
LESSEQ_UB,
LESSEQ_W,
LESSEQ_UW,
LESSEQ_F,
GREATEREQ_B,
GREATEREQ_UB,
GREATEREQ_W,
GREATEREQ_UW,
GREATEREQ_F,
EQUAL_BYTE,
EQUAL_WORD,
EQUAL_F,
NOTEQUAL_BYTE,
NOTEQUAL_WORD,
NOTEQUAL_F,
// array access
READ_INDEXED_VAR_BYTE,
READ_INDEXED_VAR_WORD,
READ_INDEXED_VAR_FLOAT,
WRITE_INDEXED_VAR_BYTE,
WRITE_INDEXED_VAR_WORD,
WRITE_INDEXED_VAR_FLOAT,
// branching
JUMP,
BCS,
BCC,
BZ, // branch if value on top of stack is zero
BNZ, // branch if value on top of stack is not zero
BNEG, // branch if value on top of stack < 0
BPOS, // branch if value on top of stack >= 0
// BVS, // status flag V (overflow) not implemented
// BVC, // status flag V (overflow) not implemented
// subroutine calling
CALL,
RETURN,
SYSCALL,
// misc
SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
SEI, // set irq-disable status flag
CLI, // clear irq-disable status flag
NOP, // do nothing
BREAKPOINT, // breakpoint
TERMINATE, // end the program
LINE // track source file line number
}
val opcodesWithVarArgument = setOf(
Opcode.INC_VAR_B, Opcode.INC_VAR_W, Opcode.DEC_VAR_B, Opcode.DEC_VAR_W,
Opcode.INC_VAR_UB, Opcode.INC_VAR_UW, Opcode.DEC_VAR_UB, Opcode.DEC_VAR_UW,
Opcode.SHR_VAR_BYTE, Opcode.SHR_VAR_WORD, Opcode.SHL_VAR_BYTE, Opcode.SHL_VAR_WORD,
Opcode.ROL_VAR_BYTE, Opcode.ROL_VAR_WORD, Opcode.ROR_VAR_BYTE, Opcode.ROR_VAR_WORD,
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD,
Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT,
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT,
Opcode.COPY_VAR_BYTE, Opcode.COPY_VAR_WORD, Opcode.COPY_VAR_FLOAT,
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT,
Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT
)

View File

@ -1,8 +1,9 @@
package prog8.stackvm
package prog8.compiler.intermediate
import prog8.ast.DataType
import prog8.ast.IterableDatatypes
import prog8.ast.NumericDatatypes
import prog8.stackvm.VmExecutionException
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.pow
@ -123,9 +124,9 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
// storing a negative number in an unsigned one is done by storing the 2's complement instead
val number = abs(result.toDouble().toInt())
if(leftDt==DataType.UBYTE)
Value(DataType.UBYTE, (number xor 255) +1)
Value(DataType.UBYTE, (number xor 255) + 1)
else
Value(DataType.UBYTE, (number xor 65535) +1)
Value(DataType.UBYTE, (number xor 65535) + 1)
}
DataType.BYTE -> Value(DataType.BYTE, result.toInt())
DataType.WORD -> Value(DataType.WORD, result.toInt())

View File

@ -0,0 +1,15 @@
package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions
import prog8.compiler.intermediate.IntermediateProgram
class AsmGen(val options: CompilationOptions) {
fun compileToAssembly(program: IntermediateProgram): AssemblyProgram {
println("\nGenerating assembly code from intermediate code... ")
// todo generate 6502 assembly
return AssemblyProgram(program.name)
}
}

View File

@ -0,0 +1,57 @@
package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions
import prog8.compiler.OutputType
import java.io.File
import kotlin.system.exitProcess
class AssemblyProgram(val name: String) {
private val assemblyFile = "$name.asm"
private val viceMonListFile = "$name.vice-mon-list"
fun assemble(options: CompilationOptions) {
println("Generating machine code program...")
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool",
"--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
val outFile = when(options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating C-64 prg.")
"$name.prg"
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary.")
"$name.bin"
}
}
command.addAll(listOf("--output", outFile, assemblyFile))
val proc = ProcessBuilder(command).inheritIO().start()
val result = proc.waitFor()
if(result!=0) {
System.err.println("assembler failed with returncode $result")
exitProcess(result)
}
generateBreakpointList()
}
private fun generateBreakpointList() {
// builds list of breakpoints, appends to monitor list file
val breakpoints = mutableListOf<String>()
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // todo what's with the _prog8_breakpoint_? how to find breakpoint?
for(line in File(viceMonListFile).readLines()) {
val match = pattern.matchEntire(line)
if(match!=null)
breakpoints.add("break \$" + match.groupValues[0]) // todo check
}
val num = breakpoints.size
breakpoints.add(0, "; vice monitor breakpoint list now follows")
breakpoints.add(1, "; $num breakpoints have been defined")
breakpoints.add(2, "del")
File(viceMonListFile).appendText(breakpoints.joinToString("\n"))
}
}

View File

@ -1,37 +0,0 @@
package prog8.compiler.target.c64
import java.awt.Color
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
object Charset {
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
transparent.createGraphics().drawImage(img, 0, 0, null)
val black = Color(0,0,0).rgb
val nopixel = Color(0,0,0,0).rgb
for(y in 0 until transparent.height) {
for(x in 0 until transparent.width) {
val col = transparent.getRGB(x, y)
if(col==black)
transparent.setRGB(x, y, nopixel)
}
}
val numColumns = transparent.width / 8
val charImages = (0..255).map {
val charX = it % numColumns
val charY = it/ numColumns
transparent.getSubimage(charX*8, charY*8, 8, 8)
}
return charImages.toTypedArray()
}
val normalChars = scanChars(normalImg)
val shiftedChars = scanChars(shiftedImg)
}

View File

@ -1,10 +1,11 @@
package prog8.compiler.target.c64
import prog8.compiler.*
import prog8.stackvm.Program
import java.awt.Color
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import kotlin.math.absoluteValue
import kotlin.math.pow
import kotlin.system.exitProcess
// 5-byte cbm MFLPT format limitations:
@ -12,7 +13,6 @@ const val FLOAT_MAX_POSITIVE = 1.7014118345e+38
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
companion object {
@ -113,60 +113,34 @@ data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, va
}
}
object Charset {
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
fun compileToAssembly(program: Program): AssemblyResult {
println("\nGenerating assembly code from stackvmProg code... ")
// todo generate 6502 assembly
return AssemblyResult(program.name)
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
transparent.createGraphics().drawImage(img, 0, 0, null)
val black = Color(0,0,0).rgb
val nopixel = Color(0,0,0,0).rgb
for(y in 0 until transparent.height) {
for(x in 0 until transparent.width) {
val col = transparent.getRGB(x, y)
if(col==black)
transparent.setRGB(x, y, nopixel)
}
}
val numColumns = transparent.width / 8
val charImages = (0..255).map {
val charX = it % numColumns
val charY = it/ numColumns
transparent.getSubimage(charX*8, charY*8, 8, 8)
}
return charImages.toTypedArray()
}
val normalChars = scanChars(normalImg)
val shiftedChars = scanChars(shiftedImg)
}
class AssemblyResult(val name: String) {
fun assemble(options: CompilationOptions, inputfilename: String, outputfilename: String) {
println("\nGenerating machine code program...")
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool",
"--dump-labels", "--vice-labels", "-l", "$outputfilename.vice-mon-list",
"--no-monitor", "--output", outputfilename, inputfilename)
when(options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating C-64 prg.")
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary.")
}
}
val proc = ProcessBuilder(command).inheritIO().start()
val result = proc.waitFor()
if(result!=0) {
System.err.println("assembler failed with returncode $result")
exitProcess(result)
}
}
fun generateBreakpointList(): String {
// todo build breakpoint list
/*
def generate_breakpoint_list(self, program_filename: str) -> str:
breakpoints = []
vice_mon_file = program_filename + ".vice-mon-list"
with open(vice_mon_file, "rU") as f:
for line in f:
match = re.fullmatch(r"al (?P<address>\w+) \S+_prog8_breakpoint_\d+.?", line, re.DOTALL)
if match:
breakpoints.append("$" + match.group("address"))
with open(vice_mon_file, "at") as f:
print("; vice monitor breakpoint list now follows", file=f)
print("; {:d} breakpoints have been defined here".format(len(breakpoints)), file=f)
print("del", file=f)
for b in breakpoints:
print("break", b, file=f)
return vice_mon_file
*/
return "monitorfile.txt"
}
}

View File

@ -2,6 +2,7 @@ package prog8.stackvm
import prog8.ast.DataType
import prog8.compiler.HeapValues
import prog8.compiler.intermediate.*
import prog8.compiler.unescape
import java.io.File
import java.util.*

View File

@ -4,251 +4,15 @@ import prog8.ast.DataType
import prog8.ast.IterableDatatypes
import prog8.ast.NumericDatatypes
import prog8.compiler.HeapValues
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.compiler.target.c64.Petscii
import java.io.File
import java.io.PrintStream
import java.util.*
import kotlin.math.*
enum class Opcode {
// pushing values on the (evaluation) stack
PUSH_BYTE, // push byte value
PUSH_WORD, // push word value (or 'address' of string / array / matrix)
PUSH_FLOAT, // push float value
PUSH_MEM_B, // push byte value from memory to stack
PUSH_MEM_UB, // push byte value from memory to stack
PUSH_MEM_W, // push word value from memory to stack
PUSH_MEM_UW, // push word value from memory to stack
PUSH_MEM_FLOAT, // push float value from memory to stack
PUSH_VAR_BYTE, // push byte variable (ubyte, byte)
PUSH_VAR_WORD, // push word variable (uword, word)
PUSH_VAR_FLOAT, // push float variable
// popping values off the (evaluation) stack, possibly storing them in another location
DISCARD_BYTE, // discard top byte value
DISCARD_WORD, // discard top word value
DISCARD_FLOAT, // discard top float value
POP_MEM_B, // pop byte value into destination memory address
POP_MEM_UB, // pop byte value into destination memory address
POP_MEM_W, // pop word value into destination memory address
POP_MEM_UW, // pop word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address
POP_VAR_BYTE, // pop byte value into variable (byte, ubyte)
POP_VAR_WORD, // pop word value into variable (word, uword)
POP_VAR_FLOAT, // pop float value into variable
// optimized copying of one var to another (replaces push+pop)
COPY_VAR_BYTE,
COPY_VAR_WORD,
COPY_VAR_FLOAT,
// numeric arithmetic
ADD_UB,
ADD_B,
ADD_UW,
ADD_W,
ADD_F,
SUB_UB,
SUB_B,
SUB_UW,
SUB_W,
SUB_F,
MUL_UB,
MUL_B,
MUL_UW,
MUL_W,
MUL_F,
DIV_UB,
DIV_B,
DIV_UW,
DIV_W,
DIV_F,
FLOORDIV_UB,
FLOORDIV_B,
FLOORDIV_UW,
FLOORDIV_W,
FLOORDIV_F,
REMAINDER_UB,
REMAINDER_B,
REMAINDER_UW,
REMAINDER_W,
REMAINDER_F,
POW_UB,
POW_B,
POW_UW,
POW_W,
POW_F,
NEG_B,
NEG_W,
NEG_F,
// bit shifts and bitwise arithmetic
SHL_BYTE,
SHL_WORD,
SHL_MEM_BYTE,
SHL_MEM_WORD,
SHL_VAR_BYTE,
SHL_VAR_WORD,
SHR_BYTE,
SHR_WORD,
SHR_MEM_BYTE,
SHR_MEM_WORD,
SHR_VAR_BYTE,
SHR_VAR_WORD,
ROL_BYTE,
ROL_WORD,
ROL_MEM_BYTE,
ROL_MEM_WORD,
ROL_VAR_BYTE,
ROL_VAR_WORD,
ROR_BYTE,
ROR_WORD,
ROR_MEM_BYTE,
ROR_MEM_WORD,
ROR_VAR_BYTE,
ROR_VAR_WORD,
ROL2_BYTE,
ROL2_WORD,
ROL2_MEM_BYTE,
ROL2_MEM_WORD,
ROL2_VAR_BYTE,
ROL2_VAR_WORD,
ROR2_BYTE,
ROR2_WORD,
ROR2_MEM_BYTE,
ROR2_MEM_WORD,
ROR2_VAR_BYTE,
ROR2_VAR_WORD,
BITAND_BYTE,
BITAND_WORD,
BITOR_BYTE,
BITOR_WORD,
BITXOR_BYTE,
BITXOR_WORD,
INV_BYTE,
INV_WORD,
// numeric type conversions
LSB,
MSB,
B2UB,
UB2B,
B2WORD, // convert a byte into a word where it is the lower eight bits $ssxx with sign extension
UB2UWORD, // convert a byte into a word where it is the lower eight bits $00xx
MSB2WORD, // convert a byte into a word where it is the upper eight bits $xx00
B2FLOAT, // convert byte into floating point
UB2FLOAT, // convert unsigned byte into floating point
W2FLOAT, // convert word into floating point
UW2FLOAT, // convert unsigned word into floating point
// logical operations
AND_BYTE,
AND_WORD,
OR_BYTE,
OR_WORD,
XOR_BYTE,
XOR_WORD,
NOT_BYTE,
NOT_WORD,
// increment, decrement
INC_B,
INC_UB,
INC_W,
INC_UW,
INC_F,
INC_VAR_B,
INC_VAR_UB,
INC_VAR_W,
INC_VAR_UW,
INC_VAR_F,
DEC_B,
DEC_UB,
DEC_W,
DEC_UW,
DEC_F,
DEC_VAR_B,
DEC_VAR_UB,
DEC_VAR_W,
DEC_VAR_UW,
DEC_VAR_F,
// comparisons
LESS_B,
LESS_UB,
LESS_W,
LESS_UW,
LESS_F,
GREATER_B,
GREATER_UB,
GREATER_W,
GREATER_UW,
GREATER_F,
LESSEQ_B,
LESSEQ_UB,
LESSEQ_W,
LESSEQ_UW,
LESSEQ_F,
GREATEREQ_B,
GREATEREQ_UB,
GREATEREQ_W,
GREATEREQ_UW,
GREATEREQ_F,
EQUAL_BYTE,
EQUAL_WORD,
EQUAL_F,
NOTEQUAL_BYTE,
NOTEQUAL_WORD,
NOTEQUAL_F,
// array access
READ_INDEXED_VAR_BYTE,
READ_INDEXED_VAR_WORD,
READ_INDEXED_VAR_FLOAT,
WRITE_INDEXED_VAR_BYTE,
WRITE_INDEXED_VAR_WORD,
WRITE_INDEXED_VAR_FLOAT,
// branching
JUMP,
BCS,
BCC,
BZ, // branch if value on top of stack is zero
BNZ, // branch if value on top of stack is not zero
BNEG, // branch if value on top of stack < 0
BPOS, // branch if value on top of stack >= 0
// BVS, // status flag V (overflow) not implemented
// BVC, // status flag V (overflow) not implemented
// subroutine calling
CALL,
RETURN,
SYSCALL,
// misc
SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
SEI, // set irq-disable status flag
CLI, // clear irq-disable status flag
NOP, // do nothing
BREAKPOINT, // breakpoint
TERMINATE, // end the program
LINE // track source file line number
}
val opcodesWithVarArgument = setOf(
Opcode.INC_VAR_B, Opcode.INC_VAR_W, Opcode.DEC_VAR_B, Opcode.DEC_VAR_W,
Opcode.INC_VAR_UB, Opcode.INC_VAR_UW, Opcode.DEC_VAR_UB, Opcode.DEC_VAR_UW,
Opcode.SHR_VAR_BYTE, Opcode.SHR_VAR_WORD, Opcode.SHL_VAR_BYTE, Opcode.SHL_VAR_WORD,
Opcode.ROL_VAR_BYTE, Opcode.ROL_VAR_WORD, Opcode.ROR_VAR_BYTE, Opcode.ROR_VAR_WORD,
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD,
Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT,
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT,
Opcode.COPY_VAR_BYTE, Opcode.COPY_VAR_WORD, Opcode.COPY_VAR_FLOAT,
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT,
Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT
)
enum class Syscall(val callNr: Short) {
WRITE_MEMCHR(10), // print a single char from the memory address popped from stack
@ -301,42 +65,6 @@ enum class Syscall(val callNr: Short) {
}
open class Instruction(val opcode: Opcode,
val arg: Value? = null,
val callLabel: String? = null,
val callLabel2: String? = null)
{
lateinit var next: Instruction
var nextAlt: Instruction? = null
override fun toString(): String {
val argStr = arg?.toString() ?: ""
val result =
when {
opcode==Opcode.LINE -> "_line $callLabel"
opcode==Opcode.SYSCALL -> {
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
"syscall $syscall"
}
opcode in opcodesWithVarArgument -> {
// opcodes that manipulate a variable
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
}
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
else -> "${opcode.toString().toLowerCase()} $callLabel $argStr"
}
.trimEnd()
return " $result"
}
}
class LabelInstr(val name: String) : Instruction(opcode = Opcode.NOP) {
override fun toString(): String {
return "\n$name:"
}
}
class VmExecutionException(msg: String?) : Exception(msg)
class VmTerminationException(msg: String?) : Exception(msg)
@ -1307,157 +1035,157 @@ class StackVm(private var traceOutputFile: String?) {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)
checkDt(second, DataType.UBYTE)
evalstack.push(Value(DataType.UBYTE, if(second < top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second < top) 1 else 0))
}
Opcode.LESS_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(Value(DataType.UBYTE, if(second < top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second < top) 1 else 0))
}
Opcode.LESS_UW -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UWORD)
checkDt(second, DataType.UWORD)
evalstack.push(Value(DataType.UBYTE, if(second < top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second < top) 1 else 0))
}
Opcode.LESS_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(Value(DataType.UBYTE, if(second < top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second < top) 1 else 0))
}
Opcode.LESS_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
checkDt(second, DataType.FLOAT)
evalstack.push(Value(DataType.UBYTE, if(second < top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second < top) 1 else 0))
}
Opcode.GREATER_UB -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)
checkDt(second, DataType.UBYTE)
evalstack.push(Value(DataType.UBYTE, if(second > top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second > top) 1 else 0))
}
Opcode.GREATER_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(Value(DataType.UBYTE, if(second > top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second > top) 1 else 0))
}
Opcode.GREATER_UW -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UWORD)
checkDt(second, DataType.UWORD)
evalstack.push(Value(DataType.UBYTE, if(second > top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second > top) 1 else 0))
}
Opcode.GREATER_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(Value(DataType.UBYTE, if(second > top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second > top) 1 else 0))
}
Opcode.GREATER_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
checkDt(second, DataType.FLOAT)
evalstack.push(Value(DataType.UBYTE, if(second > top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second > top) 1 else 0))
}
Opcode.LESSEQ_UB -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)
checkDt(second, DataType.UBYTE)
evalstack.push(Value(DataType.UBYTE, if(second <= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second <= top) 1 else 0))
}
Opcode.LESSEQ_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(Value(DataType.UBYTE, if(second <= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second <= top) 1 else 0))
}
Opcode.LESSEQ_UW -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UWORD)
checkDt(second, DataType.UWORD)
evalstack.push(Value(DataType.UBYTE, if(second <= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second <= top) 1 else 0))
}
Opcode.LESSEQ_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(Value(DataType.UBYTE, if(second <= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second <= top) 1 else 0))
}
Opcode.LESSEQ_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
checkDt(second, DataType.FLOAT)
evalstack.push(Value(DataType.UBYTE, if(second <= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second <= top) 1 else 0))
}
Opcode.GREATEREQ_UB -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)
checkDt(second, DataType.UBYTE)
evalstack.push(Value(DataType.UBYTE, if(second >= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second >= top) 1 else 0))
}
Opcode.GREATEREQ_B -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.BYTE)
checkDt(second, DataType.BYTE)
evalstack.push(Value(DataType.UBYTE, if(second >= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second >= top) 1 else 0))
}
Opcode.GREATEREQ_UW -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UWORD)
checkDt(second, DataType.UWORD)
evalstack.push(Value(DataType.UBYTE, if(second >= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second >= top) 1 else 0))
}
Opcode.GREATEREQ_W -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.WORD)
checkDt(second, DataType.WORD)
evalstack.push(Value(DataType.UBYTE, if(second >= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second >= top) 1 else 0))
}
Opcode.GREATEREQ_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
checkDt(second, DataType.FLOAT)
evalstack.push(Value(DataType.UBYTE, if(second >= top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second >= top) 1 else 0))
}
Opcode.EQUAL_BYTE -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)
checkDt(second, DataType.UBYTE)
evalstack.push(Value(DataType.UBYTE, if(second == top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second == top) 1 else 0))
}
Opcode.EQUAL_WORD -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UWORD)
checkDt(second, DataType.UWORD)
evalstack.push(Value(DataType.UBYTE, if(second == top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second == top) 1 else 0))
}
Opcode.EQUAL_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
checkDt(second, DataType.FLOAT)
evalstack.push(Value(DataType.UBYTE, if(second == top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second == top) 1 else 0))
}
Opcode.NOTEQUAL_BYTE -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UBYTE)
checkDt(second, DataType.UBYTE)
evalstack.push(Value(DataType.UBYTE, if(second != top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second != top) 1 else 0))
}
Opcode.NOTEQUAL_WORD -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.UWORD)
checkDt(second, DataType.UWORD)
evalstack.push(Value(DataType.UBYTE, if(second != top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second != top) 1 else 0))
}
Opcode.NOTEQUAL_F -> {
val (top, second) = evalstack.pop2()
checkDt(top, DataType.FLOAT)
checkDt(second, DataType.FLOAT)
evalstack.push(Value(DataType.UBYTE, if(second != top) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (second != top) 1 else 0))
}
Opcode.B2UB -> {
val byte = evalstack.pop()
@ -1793,7 +1521,7 @@ class StackVm(private var traceOutputFile: String?) {
if (value.str != null)
evalstack.push(Value(DataType.UBYTE, if (Petscii.encodePetscii(value.str, true).any { c -> c != 0.toShort() }) 1 else 0))
else
evalstack.push(Value(DataType.UBYTE, if (value.array!!.any{ v->v!=0}) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (value.array!!.any { v -> v != 0 }) 1 else 0))
}
Syscall.FUNC_ALL -> {
val iterable = evalstack.pop()
@ -1801,7 +1529,7 @@ class StackVm(private var traceOutputFile: String?) {
if (value.str != null)
evalstack.push(Value(DataType.UBYTE, if (Petscii.encodePetscii(value.str, true).all { c -> c != 0.toShort() }) 1 else 0))
else
evalstack.push(Value(DataType.UBYTE, if (value.array!!.all{ v->v!=0}) 1 else 0))
evalstack.push(Value(DataType.UBYTE, if (value.array!!.all { v -> v != 0 }) 1 else 0))
}
Syscall.FUNC_STR2BYTE -> {
val strvar = evalstack.pop()
@ -1845,7 +1573,7 @@ class StackVm(private var traceOutputFile: String?) {
if(value.integerValue() <= 32767)
Value(DataType.WORD, value.integerValue())
else
Value(DataType.WORD, -((value.integerValue() xor 65535)+1))
Value(DataType.WORD, -((value.integerValue() xor 65535) + 1))
evalstack.push(v2)
}
DataType.WORD -> evalstack.push(value)
@ -1863,7 +1591,7 @@ class StackVm(private var traceOutputFile: String?) {
if(value.integerValue()>=0)
Value(DataType.UWORD, value.integerValue())
else
Value(DataType.UWORD, (abs(value.integerValue()) xor 65535)+1)
Value(DataType.UWORD, (abs(value.integerValue()) xor 65535) + 1)
evalstack.push(v2)
}
else -> {}

View File

@ -6,6 +6,9 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.DataType
import prog8.compiler.HeapValues
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.stackvm.*
import kotlin.test.*
@ -317,7 +320,7 @@ class TestStackVmOpcodes {
@Test
fun testSub() {
testBinaryOperator(Value(DataType.UBYTE, 250), Opcode.SUB_UB, Value(DataType.UBYTE, 70), Value(DataType.UBYTE, 180))
testBinaryOperator(Value(DataType.UWORD, 4000), Opcode.SUB_UW, Value(DataType.UWORD, 123), Value(DataType.UWORD, 4000-123))
testBinaryOperator(Value(DataType.UWORD, 4000), Opcode.SUB_UW, Value(DataType.UWORD, 123), Value(DataType.UWORD, 4000 - 123))
testBinaryOperator(Value(DataType.FLOAT, 123.44), Opcode.SUB_F, Value(DataType.FLOAT, 23.44), Value(DataType.FLOAT, 100.0))
assertFailsWith<VmExecutionException> {
testBinaryOperator(Value(DataType.UWORD, 4000 - 40), Opcode.SUB_UW, Value(DataType.FLOAT, 42.25), Value(DataType.FLOAT, 42.25 - (4000 - 40)))
@ -327,7 +330,7 @@ class TestStackVmOpcodes {
@Test
fun testMul() {
testBinaryOperator(Value(DataType.UBYTE, 41), Opcode.MUL_UB, Value(DataType.UBYTE, 4), Value(DataType.UBYTE, 164))
testBinaryOperator(Value(DataType.UWORD, 401), Opcode.MUL_UW, Value(DataType.UWORD, 4), Value(DataType.UWORD, 401*4))
testBinaryOperator(Value(DataType.UWORD, 401), Opcode.MUL_UW, Value(DataType.UWORD, 4), Value(DataType.UWORD, 401 * 4))
testBinaryOperator(Value(DataType.FLOAT, 40.1), Opcode.MUL_F, Value(DataType.FLOAT, 2.4), Value(DataType.FLOAT, 96.24))
assertFailsWith<VmExecutionException> {
testBinaryOperator(Value(DataType.UWORD, 401 * 4), Opcode.MUL_UW, Value(DataType.FLOAT, 42.2533), Value(DataType.FLOAT, 42.2533 * (401 * 4)))
@ -338,7 +341,7 @@ class TestStackVmOpcodes {
fun testDiv() {
testBinaryOperator(Value(DataType.UBYTE, 250), Opcode.DIV_UB, Value(DataType.UBYTE, 12), Value(DataType.UBYTE, 20))
testBinaryOperator(Value(DataType.UWORD, 3999), Opcode.DIV_UW, Value(DataType.UWORD, 40), Value(DataType.UWORD, 99))
testBinaryOperator(Value(DataType.FLOAT, 42.25), Opcode.DIV_F, Value(DataType.FLOAT, 99.0), Value(DataType.FLOAT, 42.25/99.0))
testBinaryOperator(Value(DataType.FLOAT, 42.25), Opcode.DIV_F, Value(DataType.FLOAT, 99.0), Value(DataType.FLOAT, 42.25 / 99.0))
assertFailsWith<VmExecutionException> {
testBinaryOperator(Value(DataType.UWORD, 3333), Opcode.DIV_UW, Value(DataType.FLOAT, 2.22), Value(DataType.FLOAT, 3333 / 2.22))
}
@ -643,12 +646,12 @@ class TestStackVmOpcodes {
@Test
fun testIncVar() {
val ins = mutableListOf(
Instruction(Opcode.INC_VAR_UW, callLabel ="var1"),
Instruction(Opcode.INC_VAR_UB, callLabel ="var2"),
Instruction(Opcode.INC_VAR_F, callLabel ="var3"),
Instruction(Opcode.INC_VAR_UW, callLabel ="var1"),
Instruction(Opcode.INC_VAR_UB, callLabel ="var2"),
Instruction(Opcode.INC_VAR_F, callLabel ="var3")
Instruction(Opcode.INC_VAR_UW, callLabel = "var1"),
Instruction(Opcode.INC_VAR_UB, callLabel = "var2"),
Instruction(Opcode.INC_VAR_F, callLabel = "var3"),
Instruction(Opcode.INC_VAR_UW, callLabel = "var1"),
Instruction(Opcode.INC_VAR_UB, callLabel = "var2"),
Instruction(Opcode.INC_VAR_F, callLabel = "var3")
)
val vars = mapOf("var1" to Value(DataType.UWORD, 65534),
"var2" to Value(DataType.UBYTE, 254),
@ -675,7 +678,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.DEC_VAR_UB, callLabel = "var2"),
Instruction(Opcode.DEC_VAR_F, callLabel = "var3")
)
val vars = mapOf("var1" to Value(DataType.UWORD,1),
val vars = mapOf("var1" to Value(DataType.UWORD, 1),
"var2" to Value(DataType.UBYTE, 1),
"var3" to Value(DataType.FLOAT, 1.5)
)

View File

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

View File

@ -6,7 +6,7 @@ import prog8.ast.DataType
import prog8.ast.ExpressionError
import prog8.ast.LiteralValue
import prog8.ast.Position
import prog8.stackvm.Value
import prog8.compiler.intermediate.Value
import prog8.stackvm.VmExecutionException
import kotlin.test.*