mirror of
https://github.com/ApfelTeeSaft/WebMetal.git
synced 2026-08-26 19:43:24 +00:00
557 lines
17 KiB
JSON
557 lines
17 KiB
JSON
{
|
|
"description": "WebMetal micro-op conformance vectors. Every engine implementation (TypeScript reference, C++/WASM) must pass all vectors. 'expect' blocks are partial: only listed values are compared. Semantics: docs/execution-model.md.",
|
|
"defaultModel": {
|
|
"modelVersion": 1,
|
|
"banks": [{ "name": "R", "width": 8, "count": 4 }],
|
|
"memories": [{ "name": "MAIN", "size": 256, "width": 8 }],
|
|
"flags": ["Z", "N", "C"],
|
|
"pc": { "width": 16 },
|
|
"programMemory": "MAIN"
|
|
},
|
|
"vectors": [
|
|
{
|
|
"name": "move const to register, masked to bank width",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "const", "value": 511 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [255, 0, 0, 0] } }
|
|
},
|
|
{
|
|
"name": "move register to register via operand-selected index",
|
|
"setup": { "banks": { "R": [0, 42] } },
|
|
"run": [
|
|
{
|
|
"operands": { "rd": 3, "rs": 1 },
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "operand", "field": "rd" } },
|
|
"src": { "kind": "reg", "bank": "R", "index": { "kind": "operand", "field": "rs" } }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [0, 42, 0, 42] } }
|
|
},
|
|
{
|
|
"name": "operand value through temp into register",
|
|
"run": [
|
|
{
|
|
"operands": { "imm": 77 },
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "temp", "index": 2 },
|
|
"src": { "kind": "operand", "field": "imm" }
|
|
},
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"src": { "kind": "temp", "index": 2 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [0, 77, 0, 0] } }
|
|
},
|
|
{
|
|
"name": "add without setFlags leaves flags untouched",
|
|
"setup": { "banks": { "R": [5, 7] }, "flags": { "Z": true, "C": true } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "add",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": false
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [12, 7] }, "flags": { "Z": true, "C": true } }
|
|
},
|
|
{
|
|
"name": "add overflow sets C and Z (0xFF + 1 = 0x00)",
|
|
"setup": { "banks": { "R": [255, 1] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "add",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [0, 1] }, "flags": { "Z": true, "N": false, "C": true } }
|
|
},
|
|
{
|
|
"name": "add sets N from the result msb (0x40 + 0x40 = 0x80)",
|
|
"setup": { "banks": { "R": [64, 64] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "add",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [128, 64] }, "flags": { "Z": false, "N": true, "C": false } }
|
|
},
|
|
{
|
|
"name": "sub with no borrow sets C (5 - 3, ARM/6502 convention)",
|
|
"setup": { "banks": { "R": [5, 3] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "sub",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [2, 3] }, "flags": { "Z": false, "N": false, "C": true } }
|
|
},
|
|
{
|
|
"name": "sub with borrow clears C and wraps (3 - 5 = 0xFE)",
|
|
"setup": { "banks": { "R": [3, 5] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "sub",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [254, 5] }, "flags": { "Z": false, "N": true, "C": false } }
|
|
},
|
|
{
|
|
"name": "compare pattern: sub into a temp only affects flags",
|
|
"setup": { "banks": { "R": [9, 9] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "sub",
|
|
"width": 8,
|
|
"dst": { "kind": "temp", "index": 0 },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [9, 9] }, "flags": { "Z": true, "N": false, "C": true } }
|
|
},
|
|
{
|
|
"name": "and clears C and can set Z",
|
|
"setup": { "banks": { "R": [240, 15] }, "flags": { "C": true } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "and",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [0, 15] }, "flags": { "Z": true, "N": false, "C": false } }
|
|
},
|
|
{
|
|
"name": "or and xor produce expected bit patterns",
|
|
"setup": { "banks": { "R": [12, 10, 12, 10] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "or",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"setFlags": false
|
|
},
|
|
{
|
|
"op": "alu",
|
|
"fn": "xor",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 2 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 2 } },
|
|
"b": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 3 } },
|
|
"setFlags": false
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [14, 10, 6, 10] } }
|
|
},
|
|
{
|
|
"name": "not is unary and masks to width",
|
|
"setup": { "banks": { "R": [15] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "not",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [240] }, "flags": { "Z": false, "N": true, "C": false } }
|
|
},
|
|
{
|
|
"name": "shl shifts one bit; C receives the old msb",
|
|
"setup": { "banks": { "R": [129] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "shl",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [2] }, "flags": { "Z": false, "N": false, "C": true } }
|
|
},
|
|
{
|
|
"name": "shr shifts one bit; C receives the old bit 0",
|
|
"setup": { "banks": { "R": [5] } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "shr",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [2] }, "flags": { "Z": false, "N": false, "C": true } }
|
|
},
|
|
{
|
|
"name": "alu inputs are truncated to the op width before computing",
|
|
"run": [
|
|
{
|
|
"operands": { "big": 260 },
|
|
"microOps": [
|
|
{
|
|
"op": "alu",
|
|
"fn": "add",
|
|
"width": 8,
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"a": { "kind": "operand", "field": "big" },
|
|
"b": { "kind": "const", "value": 0 },
|
|
"setFlags": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [4] }, "flags": { "Z": false, "C": false } }
|
|
},
|
|
{
|
|
"name": "load reads a memory word",
|
|
"setup": { "memories": { "MAIN": { "16": 99 } } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "load",
|
|
"memory": "MAIN",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"addr": { "kind": "const", "value": 16 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [99] } }
|
|
},
|
|
{
|
|
"name": "store writes a memory word, masked to memory width",
|
|
"run": [
|
|
{
|
|
"operands": { "addr": 32 },
|
|
"microOps": [
|
|
{
|
|
"op": "store",
|
|
"memory": "MAIN",
|
|
"addr": { "kind": "operand", "field": "addr" },
|
|
"src": { "kind": "const", "value": 300 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "memories": { "MAIN": { "32": 44 } } }
|
|
},
|
|
{
|
|
"name": "setFlag stores (value != 0)",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{ "op": "setFlag", "name": "C", "src": { "kind": "const", "value": 2 } },
|
|
{ "op": "setFlag", "name": "Z", "src": { "kind": "const", "value": 0 } }
|
|
]
|
|
}
|
|
],
|
|
"expect": { "flags": { "C": true, "Z": false } }
|
|
},
|
|
{
|
|
"name": "flag reads back as 0 or 1 through a move",
|
|
"setup": { "flags": { "N": true } },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "flag", "name": "N" }
|
|
},
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"src": { "kind": "flag", "name": "Z" }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [1, 0] } }
|
|
},
|
|
{
|
|
"name": "jump sets pc, masked to pc width",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{ "op": "jump", "target": { "kind": "const", "value": 65540 } }
|
|
]
|
|
}
|
|
],
|
|
"expect": { "pc": 4 }
|
|
},
|
|
{
|
|
"name": "branch taken when flag matches ifSet",
|
|
"setup": { "flags": { "Z": true }, "pc": 10 },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "branch",
|
|
"flag": "Z",
|
|
"ifSet": true,
|
|
"target": { "kind": "const", "value": 200 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "pc": 200 }
|
|
},
|
|
{
|
|
"name": "branch not taken leaves pc alone",
|
|
"setup": { "flags": { "Z": false }, "pc": 10 },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "branch",
|
|
"flag": "Z",
|
|
"ifSet": true,
|
|
"target": { "kind": "const", "value": 200 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "pc": 10 }
|
|
},
|
|
{
|
|
"name": "halt stops the sequence and freezes the machine",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "const", "value": 1 }
|
|
},
|
|
{ "op": "halt" },
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "const", "value": 9 }
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 1 } },
|
|
"src": { "kind": "const", "value": 9 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [1, 0] }, "halted": true }
|
|
},
|
|
{
|
|
"name": "temps are cleared between sequences",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "temp", "index": 0 },
|
|
"src": { "kind": "const", "value": 123 }
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "temp", "index": 0 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [0] } }
|
|
},
|
|
{
|
|
"name": "pc can be read as a value source",
|
|
"setup": { "pc": 513 },
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "pc" }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expect": { "banks": { "R": [1] } }
|
|
},
|
|
{
|
|
"name": "error: unknown register bank",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "GHOST", "index": { "kind": "literal", "value": 0 } },
|
|
"src": { "kind": "const", "value": 1 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expectError": "unknown register bank"
|
|
},
|
|
{
|
|
"name": "error: register index out of range",
|
|
"run": [
|
|
{
|
|
"operands": { "rd": 12 },
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "reg", "bank": "R", "index": { "kind": "operand", "field": "rd" } },
|
|
"src": { "kind": "const", "value": 1 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expectError": "out of range"
|
|
},
|
|
{
|
|
"name": "error: memory address out of range",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "load",
|
|
"memory": "MAIN",
|
|
"dst": { "kind": "temp", "index": 0 },
|
|
"addr": { "kind": "const", "value": 256 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expectError": "out of range"
|
|
},
|
|
{
|
|
"name": "error: unknown operand field",
|
|
"run": [
|
|
{
|
|
"microOps": [
|
|
{
|
|
"op": "move",
|
|
"dst": { "kind": "temp", "index": 0 },
|
|
"src": { "kind": "operand", "field": "missing" }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"expectError": "unknown operand field"
|
|
}
|
|
]
|
|
}
|