Simplified opcode definitions and improved ASM editor type signatures.

This commit is contained in:
Daan Vanden Bosch 2021-04-23 18:03:45 +02:00
parent 3122bb4666
commit 5210792a3e
12 changed files with 1037 additions and 1009 deletions

View File

@ -142,7 +142,7 @@ fun opcodeToCode(writer: PrintWriter, opcode: Map<String, Any>) {
val lastParam = params.lastOrNull()
val varargs = lastParam != null && when (lastParam["type"]) {
null -> error("No type for last parameter of $mnemonic opcode.")
"instruction_label_var", "reg_ref_var" -> true
"ilabel_var", "reg_var" -> true
else -> false
}
@ -185,31 +185,27 @@ fun paramsToCode(params: List<Map<String, Any>>, indent: Int): String {
"int" -> "IntType"
"float" -> "FloatType"
"label" -> "LabelType.Instance"
"instruction_label" -> "ILabelType"
"data_label" -> "DLabelType"
"string_label" -> "SLabelType"
"ilabel" -> "ILabelType"
"dlabel" -> "DLabelType"
"slabel" -> "SLabelType"
"string" -> "StringType"
"instruction_label_var" -> "ILabelVarType"
"reg_ref" -> """RegRefType(${
"ilabel_var" -> "ILabelVarType"
"reg" -> """RegType(${
(param["registers"] as List<Map<String, Any>>?)?.let {
paramsToCode(it, indent + 4)
} ?: "null"
})"""
"reg_ref_var" -> "RegRefVarType"
"reg_var" -> "RegVarType"
"pointer" -> "PointerType"
else -> error("Type ${param["type"]} not implemented.")
}
val name = (param["name"] as String?)?.let { "\"$it\"" } ?: "null"
val doc = (param["doc"] as String?)?.let { "\"$it\"" } ?: "null"
val read = param["read"] as Boolean? == true
val write = param["write"] as Boolean? == true
val access = when (param["access"]) {
"read" -> "ParamAccess.Read"
"write" -> "ParamAccess.Write"
"read_write" -> "ParamAccess.ReadWrite"
else -> "null"
}
"$i Param(${type}, ${doc}, ${access})"
"$i Param(${type}, ${name}, ${doc}, ${read}, ${write})"
}
}

View File

@ -487,8 +487,8 @@ private class Assembler(private val asm: List<String>, private val inlineStackAr
Token.Register -> {
typeMatch = stack ||
param.type === RegRefVarType ||
param.type is RegRefType
param.type === RegVarType ||
param.type is RegType
parseRegister()
}
@ -536,8 +536,8 @@ private class Assembler(private val asm: List<String>, private val inlineStackAr
StringType -> "a string"
RegRefVarType,
is RegRefType,
RegVarType,
is RegType,
-> "a register reference"
else -> null
@ -550,7 +550,7 @@ private class Assembler(private val asm: List<String>, private val inlineStackAr
// Inject stack push instructions if necessary.
// If the token is a register, push it as a register, otherwise coerce type.
if (tokenizer.type === Token.Register) {
if (param.type is RegRefType) {
if (param.type is RegType) {
addInstruction(
OP_ARG_PUSHB,
listOf(arg),
@ -570,7 +570,7 @@ private class Assembler(private val asm: List<String>, private val inlineStackAr
} else {
when (param.type) {
ByteType,
is RegRefType,
is RegType,
-> {
addInstruction(
OP_ARG_PUSHB,

View File

@ -216,11 +216,11 @@ class Instruction(
}
}
RegRefVarType -> 1 + args.size
RegVarType -> 1 + args.size
// Check RegRefType and LabelType last, because "is" checks are very slow in JS.
is RegRefType -> 1
is RegType -> 1
is LabelType -> 2

View File

@ -155,7 +155,7 @@ private fun canInlinePushedArg(segment: InstructionSegment, index: Int): Boolean
for (param in opcode.params) {
when (param.type) {
is ILabelVarType -> varArgs = true
is RegRefVarType -> varArgs = true
is RegVarType -> varArgs = true
else -> paramCount++
}
}
@ -204,7 +204,7 @@ private fun StringBuilder.appendArgs(params: List<Param>, args: List<ArgWithType
if (i < args.size) {
val (arg, argType) = args[i]
if (argType is RegRefType) {
if (argType is RegType) {
append("r")
append(arg.value)
} else {
@ -226,7 +226,7 @@ private fun StringBuilder.appendArgs(params: List<Param>, args: List<ArgWithType
}
}
RegRefVarType -> {
RegVarType -> {
while (i < args.size) {
append("r")
append(args[i].arg.value)
@ -235,7 +235,7 @@ private fun StringBuilder.appendArgs(params: List<Param>, args: List<ArgWithType
}
}
is RegRefType -> {
is RegType -> {
append("r")
append(arg.value)
}

View File

@ -17,11 +17,7 @@ private val UNKNOWN_OPCODE_MNEMONIC_REGEX = Regex("""^unknown_((f8|f9)?[0-9a-f]{
* Abstract super type of all types.
*/
sealed class AnyType {
abstract val uiName: String
object Instance : AnyType() {
override val uiName = "Any"
}
object Instance : AnyType()
}
/**
@ -32,148 +28,99 @@ sealed class ValueType : AnyType()
/**
* 8-Bit integer.
*/
object ByteType : ValueType() {
override val uiName = "Byte"
}
object ByteType : ValueType()
/**
* 16-Bit integer.
*/
object ShortType : ValueType() {
override val uiName = "Short"
}
object ShortType : ValueType()
/**
* 32-Bit integer.
*/
object IntType : ValueType() {
override val uiName = "Int"
}
object IntType : ValueType()
/**
* 32-Bit floating point number.
*/
object FloatType : ValueType() {
override val uiName = "Float"
}
object FloatType : ValueType()
/**
* Abstract super type of all label types.
*/
sealed class LabelType : ValueType() {
object Instance : LabelType() {
override val uiName = "Label"
}
object Instance : LabelType()
}
/**
* Named reference to an instruction.
*/
object ILabelType : LabelType() {
override val uiName = "ILabel"
}
object ILabelType : LabelType()
/**
* Named reference to a data segment.
*/
object DLabelType : LabelType() {
override val uiName = "DLabel"
}
object DLabelType : LabelType()
/**
* Named reference to a string segment.
*/
object SLabelType : LabelType() {
override val uiName = "SLabel"
}
object SLabelType : LabelType()
/**
* Arbitrary amount of instruction labels (variadic arguments).
*/
object ILabelVarType : LabelType() {
override val uiName = "...ILabel"
}
object ILabelVarType : LabelType()
/**
* String of arbitrary size.
*/
object StringType : ValueType() {
override val uiName = "String"
}
object StringType : ValueType()
/**
* Purely abstract super type of all reference types.
* Purely abstract super type of all register reference types.
*/
sealed class RefType : AnyType()
sealed class RegRefType : AnyType()
/**
* Register reference. If [registers] is null, references one or more consecutive registers of any
* type (only stack_pushm and stack_popm use this). If [registers] is not null, references a fixed
* amount of consecutive registers of specific types. [Param.type] can't be a variadic type.
*/
class RegRefType(val registers: List<Param>?) : RefType() {
override val uiName = buildString {
append("Register")
if (registers != null) {
if (registers.size > 1) append("s")
append("<")
registers.joinTo(this) { it.type.uiName }
append(">")
}
}
}
class RegType(val registers: List<Param>?) : RegRefType()
/**
* Arbitrary amount of register references (variadic arguments).
*/
object RegRefVarType : RefType() {
override val uiName = "...Register"
}
object RegVarType : RegRefType()
/**
* Raw memory pointer.
*/
object PointerType : AnyType() {
override val uiName = "Pointer"
}
enum class ParamAccess {
Read,
Write,
ReadWrite
}
object PointerType : AnyType()
class Param(
val type: AnyType,
val name: String?,
/**
* Documentation string.
*/
val doc: String?,
/**
* The way referenced registers are accessed by the instruction. Only set when type is a
* register reference.
* Whether or not the instruction reads this parameter. Only set when type is a register
* reference.
*/
val access: ParamAccess?,
val read: Boolean,
/**
* Whether or not the instruction writes this parameter. Only set when type is a register
* reference.
*/
val write: Boolean,
) {
/**
* Whether or not this parameter takes a variable number of arguments.
*/
val varargs: Boolean = type === ILabelVarType || type === RegRefVarType
/**
* Whether or not the instruction reads this parameter.
*/
val reads: Boolean
get() =
access === ParamAccess.Read || access === ParamAccess.ReadWrite
/**
* Whether or not the instruction writes this parameter.
*/
val writes: Boolean
get() =
access === ParamAccess.Write || access === ParamAccess.ReadWrite
val varargs: Boolean = type === ILabelVarType || type === RegVarType
}
enum class StackInteraction {

View File

@ -191,11 +191,11 @@ private class RegisterValueFinder {
for (j in 0 until argLen) {
val param = params[j]
if (param.type is RegRefType && param.type.registers != null) {
if (param.type is RegType && param.type.registers != null) {
val regRef = args[j].value as Int
for ((k, regParam) in param.type.registers.withIndex()) {
if (regParam.writes && regRef + k == register) {
if (regParam.write && regRef + k == register) {
return ValueSet.all()
}
}

View File

@ -276,7 +276,7 @@ private fun findAndParseSegments(
}
}
is RegRefType -> if (param.type.registers != null) {
is RegType -> if (param.type.registers != null) {
for (j in param.type.registers.indices) {
val registerParam = param.type.registers[j]
@ -609,11 +609,11 @@ private fun parseInstructionArguments(
)
}
is RegRefType -> {
is RegType -> {
args.add(Arg(cursor.uByte().toInt()))
}
is RegRefVarType -> {
is RegVarType -> {
varargCount++
val argSize = cursor.uByte()
args.addAll(cursor.uByteArray(argSize.toInt()).map { Arg(it.toInt()) })
@ -825,10 +825,10 @@ fun writeBytecode(bytecodeIr: BytecodeIr, dcGcFormat: Boolean): BytecodeAndLabel
if (dcGcFormat) cursor.writeStringAscii(str, str.length + 1)
else cursor.writeStringUtf16(str, 2 * str.length + 2)
}
is RegRefType -> {
is RegType -> {
cursor.writeByte((arg.value as Int).toByte())
}
RegRefVarType -> {
RegVarType -> {
cursor.writeByte(args.size.toByte())
for (a in args) {

View File

@ -29,12 +29,21 @@ class OpcodeTests : LibTestSuite {
assertEquals(hasVarargs, opcode.varargs)
// Register references.
for (param in opcode.params) {
val type = param.type
if (type is RegRefType) {
assertTrue(type.registers == null || type.registers!!.isNotEmpty())
if (type is RegType) {
val registers = type.registers
if (registers == null) {
assertTrue(param.read || param.write)
} else {
assertTrue(registers.isNotEmpty())
for (register in registers) {
assertTrue(register.read || register.write)
}
}
}
}
}

View File

@ -62,29 +62,39 @@
"type": "string",
"description": "Parameter-specific documentation."
},
"access": {
"$ref": "#/definitions/access"
"read": {
"type": "boolean",
"description": "Does this opcode read the given register. Should only be specified if type is \"reg\" or \"reg_var\"."
},
"write": {
"type": "boolean",
"description": "Does this opcode write to the given register. Should only be specified if type is \"reg\" or \"reg_var\"."
},
"registers": {
"type": "array",
"minItems": 1,
"description": "Specifies the way the referenced registers will be interpreted. Should only be specified if the parameter type is \"reg_tup_ref\".",
"description": "Specifies the way the referenced registers will be interpreted. Should only be specified if the parameter type is \"reg\".",
"items": {
"type": "object",
"required": ["type", "access"],
"required": [
"type"
],
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/param_type"
},
"name": {
"type": "string"
"type": "string",
"description": "Register name."
},
"doc": {
"type": "string"
"read": {
"type": "boolean",
"description": "Does this opcode read the given register."
},
"access": {
"$ref": "#/definitions/access"
"write": {
"type": "boolean",
"description": "Does this opcode write to the given register."
}
}
}
@ -100,20 +110,15 @@
"int",
"float",
"label",
"instruction_label",
"data_label",
"string_label",
"ilabel",
"dlabel",
"slabel",
"string",
"instruction_label_var",
"reg_ref",
"reg_ref_var",
"ilabel_var",
"reg",
"reg_var",
"pointer"
]
},
"access": {
"type": "string",
"enum": ["read", "write", "read_write"],
"description": "Specifies the way the instruction accesses the referenced register(s)."
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -317,7 +317,7 @@ class AssemblyWorker(private val sendMessage: (ServerMessage) -> Unit) {
}
private fun getSignature(opcode: Opcode): Signature {
var signature = opcode.mnemonic + " "
val signature = StringBuilder(opcode.mnemonic).append(" ")
val params = mutableListOf<Parameter>()
var first = true
@ -325,27 +325,79 @@ class AssemblyWorker(private val sendMessage: (ServerMessage) -> Unit) {
if (first) {
first = false
} else {
signature += ", "
signature.append(", ")
}
val labelStart = signature.length
signature.appendParam(param)
params.add(
Parameter(
labelStart = signature.length,
labelEnd = signature.length + param.type.uiName.length,
labelStart,
labelEnd = signature.length,
documentation = param.doc,
)
)
signature += param.type.uiName
}
return Signature(
label = signature,
label = signature.toString(),
documentation = opcode.doc,
parameters = params,
)
}
private fun StringBuilder.appendParam(param: Param) {
if (param.read || param.write) {
if (param.read) append("in")
if (param.write) append("out")
append(" ")
}
when (val type = param.type) {
AnyType.Instance -> append("Any")
ByteType -> append("Byte")
ShortType -> append("Short")
IntType -> append("Int")
FloatType -> append("Float")
LabelType.Instance -> append("Label")
ILabelType -> append("ILabel")
DLabelType -> append("DLabel")
SLabelType -> append("SLabel")
ILabelVarType -> append("...ILabel")
StringType -> append("String")
is RegType -> {
append("Reg")
type.registers?.let { registers ->
append("<")
var first = true
for (register in registers) {
if (first) {
first = false
} else {
append(", ")
}
appendParam(register)
}
append(">")
}
}
RegVarType -> append("...Reg")
PointerType -> append("Pointer")
}
param.name?.let {
append(" ")
append(param.name)
}
}
private fun getHover(requestId: Int, lineNo: Int, col: Int) {
val hover = signatureHelp(lineNo, col)?.let { help ->
val sig = help.signature

View File

@ -27,6 +27,7 @@ class AsmEditorWidget(private val ctrl: AsmController) : Widget() {
folding = false
wordBasedSuggestions = false
occurrencesHighlight = false
fixedOverflowWidgets = true
})
addDisposable(disposable { editor.dispose() })
@ -108,10 +109,14 @@ class AsmEditorWidget(private val ctrl: AsmController) : Widget() {
@Suppress("CssUnusedSymbol")
// language=css
style("""
style(
"""
.pw-quest-editor-asm-editor {
flex-grow: 1;
}
.pw-quest-editor-asm-editor .editor-widget {
z-index: 30;
}
""".trimIndent())
}
}