#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
+#define SEP ":"
+
#define error(FORMAT, ...) \
do { \
(void)fprintf(stderr, FORMAT, ##__VA_ARGS__); \
} while (0)
-int
-main(void) {
+struct uctx {
+ int insn_id;
+ int op_id;
+ int arg;
+ int argc;
+ char **argv;
+};
+
+static enum opid_state
+operand_cb(void *vuctx, struct opid_operand *operand) {
+ char *arg;
+ char *iter;
+ long long value;
+ struct uctx *uctx = vuctx;
+
+ if (uctx->arg == uctx->argc) {
+ error("missing operand");
+ return OPID_ERROR_CALLBACK;
+ }
+
+ arg = uctx->argv[uctx->arg];
+ if (strcmp(SEP, arg) == 0) {
+ error("invalid operand \"%s\"", arg);
+ return OPID_ERROR_CALLBACK;
+ }
+
+ errno = 0;
+ value = strtoll(uctx->argv[uctx->arg], &iter, 10);
+ if ((iter == arg) || (*iter != '\0') ||
+ ((value == LONG_MIN || value == LONG_MAX) && errno == ERANGE)) {
+ error("invalid operand \"%s\"", arg);
+ return OPID_ERROR_CALLBACK;
+ }
+
+ operand->value = (uint64_t)(int64_t)value;
+
+ ++uctx->arg;
+ ++uctx->op_id;
+
+ return OPID_SUCCESS;
+}
+
+static int
+assemble(int argc, char **argv) {
+ uint64_t insn;
+ struct uctx uctx = {
+ .insn_id = 0,
+ .op_id = 0,
+ .arg = 0,
+ .argc = argc,
+ .argv = argv,
+ };
+
+ while (uctx.arg != uctx.argc) {
+ ssize_t size;
+ uint32_t insn32;
+ struct opid_ctx ctx;
+ enum opid_state state;
+ char const *insn_name = uctx.argv[uctx.arg++];
+
+ uctx.op_id = 0;
+ state = opid_assemble(&ctx, insn_name, &insn, &uctx, operand_cb);
+ if (state != OPID_SUCCESS) {
+ if (state == OPID_ERROR_LOOKUP)
+ error("bad instruction name");
+ error(" (insn_name=\"%s\", insn_id=%d, op_id=%d)\n",
+ insn_name, uctx.insn_id, uctx.op_id);
+ return EXIT_FAILURE;
+ }
+ if (uctx.arg != uctx.argc) {
+ if (strcmp(SEP, uctx.argv[uctx.arg]) != 0) {
+ error("invalid separator ");
+ error(" (insn_name=\"%s\", insn_id=%d, op_id=%d)\n",
+ insn_name, uctx.insn_id, uctx.op_id);
+ return EXIT_FAILURE;
+ }
+ ++uctx.arg;
+ }
+ ++uctx.insn_id;
+
+ insn32 = (uint32_t)insn;
+ size = write(STDOUT_FILENO, &insn32, sizeof(insn32));
+ if (size != sizeof(insn32)) {
+ error("cannot store instruction: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int
+disassemble(void) {
ssize_t size;
- uint32_t insn;
- struct opid_ctx ctx;
- enum opid_state state;
- struct opid_operand const *operand;
do {
+ uint32_t insn;
+
size = read(STDIN_FILENO, &insn, sizeof(insn));
if (size < 0) {
error("cannot fetch instruction: %s\n", strerror(errno));
return EXIT_FAILURE;
} else if (size == sizeof(uint32_t)) {
+ struct opid_ctx ctx;
+ enum opid_state state;
+ struct opid_operand const *operand;
+
state = opid_disassemble(&ctx, insn);
if (state != OPID_SUCCESS) {
- error("invalid instruction: %08" PRIx32 "\n", insn);
+ error("invalid instruction: %016" PRIx32 "\n", insn);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
+
+int
+main(int argc, char *argv[]) {
+ if (argc > 1) {
+ return assemble(--argc, ++argv);
+ } else {
+ return disassemble();
+ }
+}
DIS_REGEX = re.compile(DIS_PATTERN)
-def opid_check(db, insns):
- with tempfile.TemporaryFile(mode="w+b") as stdin:
- def remap_insn(insn):
- (name, arguments) = insn
- record = db[name]
- insn = WordInstruction.assemble(record=db[name],
- arguments=arguments)
-
- def remap_operand(operand):
- table = {
- SignedOperand: "s",
- GPROperand: "g",
- FPROperand: "f",
- PairOperand: "p",
- CR3Operand: "3",
- NonZeroOperand: "z",
- CR5Operand: "5",
- }
- flags = []
- for (cls, flag) in table.items():
- if isinstance(operand, cls):
- flags.append(flag)
- flags = "".join(flags)
- value = next(operand.disassemble(insn, style=Style.LEGACY))
- return (value, flags)
-
- operands = tuple(map(remap_operand,
- insn.dynamic_operands(record=record)))
-
- return (insn, name, operands)
-
- def assert_eq(index, actual, expected):
- if actual != expected:
- print(f"[{index}]", "actual ", actual)
- print(f"[{index}]", "expected", expected)
- return False
- return True
-
- insns = tuple(map(remap_insn, insns))
-
- for (insn, *_) in insns:
- stdin.write(insn.bytes())
- stdin.seek(0)
-
- sp = subprocess.Popen("./opid-check",
- stdin=stdin,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- encoding="UTF-8")
- (stdout, stderr) = sp.communicate()
- if stderr:
- sys.stderr.write(map(str.strip, stderr.splitlines()))
- raise ValueError(stderr)
-
- for (index, line) in enumerate(map(str.strip, stdout.splitlines())):
- match = DIS_REGEX.match(line)
- if match is None:
- raise ValueError(line)
- (name, operands) = match.groups()
- operands = operands.replace(")(", ":")
- operands = operands.replace("(", "").replace(")", "")
- operands = tuple(tuple(operand.split(" ")) for operand in operands.split(":"))
- print(line)
- assert_eq(index=index, actual=name, expected=insns[index][1])
- assert_eq(index=index, actual=operands, expected=insns[index][2])
+def disassemble(db, insns):
+ stdin = tempfile.TemporaryFile(mode="w+b")
+
+ def remap_insn(insn):
+ (name, arguments) = insn
+ record = db[name]
+ insn = WordInstruction.assemble(record=db[name],
+ arguments=arguments)
+
+ def remap_operand(operand):
+ table = {
+ SignedOperand: "s",
+ GPROperand: "g",
+ FPROperand: "f",
+ PairOperand: "p",
+ CR3Operand: "3",
+ NonZeroOperand: "z",
+ CR5Operand: "5",
+ }
+ flags = []
+ for (cls, flag) in table.items():
+ if isinstance(operand, cls):
+ flags.append(flag)
+ flags = "".join(flags)
+ value = next(operand.disassemble(insn, style=Style.LEGACY))
+ return (value, flags)
+
+ operands = tuple(map(remap_operand,
+ insn.dynamic_operands(record=record)))
+
+ return (insn, name, operands)
+
+ def assert_eq(index, actual, expected):
+ if actual != expected:
+ print(f"[{index}]", "actual ", actual)
+ print(f"[{index}]", "expected", expected)
+ return False
+ return True
+
+ insns = tuple(map(remap_insn, insns))
+
+ for (insn, *_) in insns:
+ stdin.write(insn.bytes())
+ stdin.seek(0)
+
+ stdout = subprocess.check_output(["./opid-check"], stdin=stdin, encoding="UTF-8")
+ for (index, line) in enumerate(map(str.strip, stdout.splitlines())):
+ match = DIS_REGEX.match(line)
+ if match is None:
+ raise ValueError(line)
+ (name, operands) = match.groups()
+ operands = operands.replace(")(", ":")
+ operands = operands.replace("(", "").replace(")", "")
+ operands = tuple(tuple(operand.split(" ")) for operand in operands.split(":"))
+ print(line)
+ assert_eq(index=index, actual=name, expected=insns[index][1])
+ assert_eq(index=index, actual=operands, expected=insns[index][2])
+
+ stdin.seek(0)
+
+ return stdin
+
+
+def assemble(db, insns):
+ command = [
+ "./opid-check",
+ ]
+ for (name, arguments) in insns:
+ command.append(name)
+ command.extend(arguments)
+ command.append(":")
+ command.pop()
+
+ stdout = tempfile.TemporaryFile(mode="w+b")
+ subprocess.check_call(command, stdout=stdout)
+ stdout.seek(0)
+
+ return stdout
+
+
+def check(db, insns):
+ with disassemble(db=db, insns=insns) as origin:
+ with assemble(db=db, insns=insns) as target:
+ origin = origin.read()
+ target = target.read()
+ if origin != target:
+ print("actual ", "".join(f"\\x{byte:02x}" for byte in target))
+ print("expected", "".join(f"\\x{byte:02x}" for byte in origin))
def main():
db = Database(find_wiki_dir())
- opid_check(db=db, insns=(
+ check(db=db, insns=(
("addpcis", ("1", "-1")),
("addpcis", ("1", "0")),
("addpcis", ("1", "+1")),