Playing further with radare I noticed that there are mostly disassembler backends but only a few assemblers.
I kind of like the idea of having something table driven that allows for a certain symmetry of assembler and disassembler.
Also looks more like a datasheet. Again not complete but recognizes the instructions given, if you want more instructions its literally copy and paste from the website somewhere above.
Don't know if I'll be able to finish this soon though. Just to give an idea that this could be generalized I did the same for pic12 and at least it looks good

/* Padauk (dis)assembler plugin for radare 3.1 git - DocBen 2018
now looking more like a datasheet
*/
#include <r_asm.h>
#include <r_lib.h>
#include <string.h>
static struct {
char *op;
char *name;
ut8 args;
char *comment;
} ops[] = {
// memory mapped io i (6-bit)
// nth bit n (3-bit)
// memory address m (6 bit)
// memory address M (7 bit)
// immediate c (8 bit)
// address k (11 bit)
//"binary representation","mnemonic"(,"num parameters (could be inferred)"),"comment"
"0000.0000.0000.0000", "nop", 0, "does nothing (tm)",
"0000.0000.0110.0000", "addc a", 1, "A ← A + CF",
"0000.0000.11ii.iiii", "xor io,a", 2, "IO ← IO ^ A",
"0000.0001.10ii.iiii", "mov io,a", 2, "IO ← A",
"0000.0001.11ii.iiii", "mov a,io", 2, "A ← IO",
"0000.0010.cccc.cccc", "ret c", 1, "",
"0000.0011.1MMM.MMM0", "idxm M, a", 2, "[M] ← A (last bit of M set to 0, M must be word aligned, 2 cycles)",
"0000.0011.1MMM.MMM1", "idxm a, M", 2, "a ← [M] (last bit of M set to 1, M must be word aligned, 2 cycles)",
"0000.010n.nnii.iiii", "swapc io.n", 2, "",
"0000.0110.0MMM.MMMM", "comp a, M", 2, "",
"0010.000n.nnmm.mmmm", "t0sn m.n", 2, "",
"0011.0kkk.kkkk.kkkk", "goto k", 1, "goto address",
"0011.1kkk.kkkk.kkkk", "call k", 1, "call address",
"2222.2222.2222.2222", NULL, 0, "",
};
void bitstring(uint16_t val, char buffer[]) {
int size = 20;
buffer[--size] = 0;
while (size > 0) {
buffer[--size] = (val % 2 ? '1' : '0');
if ( size % 5 == 0 && size > 0) buffer[--size] = '.';
val = val >> 1;
}
}
int _PADAUKDisass (RAsm *a, RAsmOp *op, const ut8 *buf, int len) {
int i;
op->size = 2;
const char *buf_asm = "unknown";
char buf_bin[40];
bitstring(0x100 * buf[0] + buf[1], buf_bin);
for (i=0; ops[i].name != NULL ; i++) {
for (int j = 0; j < 20; j++) {
if (ops[i].op[j] != buf_bin[j]) {
if (ops[i].op[j] != '0' && ops[i].op[j] != '1') { // treat all letters as dont care
continue;
} else {
break;
}
}
if (j == 19) {
buf_asm = sdb_fmt ("%s = %s ; %s", buf_bin, ops[i].name, ops[i].comment);
r_strbuf_set (&op->buf_asm, buf_asm);
return op->size;
}
}
}
r_strbuf_set (&op->buf_asm, buf_asm);
return op->size;
}
int _PADAUKAss (RAsm *a, RAsmOp *op, const char *buf) {
int i;
op->size = 2;
ut16 opbuf = 0x4000;
const char *buf_hex = "unknown";
for (i = 0; ops[i].name != NULL; i++) {
if (!strncmp(ops[i].name, buf, strlen(ops[i].name))) {
//opbuf = ops[i].op;
buf_hex = sdb_fmt ("0x%.4X\n", opbuf);
break;
}
}
r_strbuf_set (&op->buf_hex, buf_hex);
return op->size;
}
RAsmPlugin r_asm_plugin_padauk = {
.name = "padauk2",
.arch = "padauk2",
.license = "LGPL3",
.bits = 16,
.desc = "Padauk (dis)assembler",
.disassemble = &_PADAUKDisass,
.assemble = &_PADAUKAss,
};
#ifndef CORELIB
struct r_lib_struct_t radare_plugin = {
.type = R_LIB_TYPE_ASM,
.data = &r_asm_plugin_padauk
};
#endif
PIC12
static struct {
char *op;
char *name;
ut8 args;
char *comment;
} ops[] = {
// direction d (1 bit)
// tri-state register t (2 bit)
// nth bit b (3-bit)
// register bank (3 bit)
// register f (5 bit)
// immediate c (8 bit)
// address k (8 bit)
// address K (9 bit)
//"binary representation","mnemonic"(,"num parameters"), "comment"
"0000.0000.0000.0000", "nop", 0, "No operation (MOVW 0,W)",
"0000.0000.0000.0010", "option", 0, "Copy W to OPTION register",
"0000.0000.0000.0011", "sleep", 0, "Go into standby mode",
"0000.0000.0000.0100", "clrwdt", 0, "Restart watchdog timer",
"0000.0000.0000.01tt", "tris t", 0, "Copy W to tri-state register (f = 1, 2 or 3)",
"0000.0000.0001.0BBB", "movlb k", 1, "Set bank select register to k",
"0000.0000.0001.1110", "return", 0, "Return from subroutine, W unmodified",
"0000.0000.0001.1111", "retfie", 0, "Return from interrupt; return & enable interrupts",
"0000.0000.001f.ffff", "movwf f", 1, "dest ← W",
"0000.0000.01df.ffff", "clr f,d", 2, "dest ← 0, usually written CLRW or CLRF f",
"0000.0000.10df.ffff", "subwf f,d", 2, "dest ← f−W (dest ← f+~W+1)",
"0000.0000.11df.ffff", "decf f,d", 2, "dest ← f−1",
"0000.0001.00df.ffff", "iorwf f,d", 2, "dest ← f | W, logical inclusive or",
"0000.0001.01df.ffff", "andwf f,d", 2, "dest ← f & W, logical and",
"0000.0001.10df.ffff", "xorwf f,d", 2, "dest ← f ^ W, logical exclusive or",
"0000.0001.11df.ffff", "addwf f,d", 2, "dest ← f+W",
"0000.0010.00df.ffff", "movwf f,d", 2, "dest ← f",
"0000.0010.01df.ffff", "comf f,d", 2, "dest ← ~f, bitwise complement",
"0000.0010.10df.ffff", "incf f,d", 2, "dest ← f+1",
"0000.0010.11df.ffff", "decfsz f,d", 2, "dest ← f−1, then skip if zero",
"0000.0011.00df.ffff", "rrf f,d", 2, "dest ← CARRY<<7 | f>>1, rotate right through carry",
"0000.0011.01df.ffff", "rlf f,d", 2, "dest ← F<<1 | CARRY, rotate left through carry",
"0000.0011.10df.ffff", "swapf f,d", 2, "dest ← f<<4 | f>>4, swap nibbles",
"0000.0011.11df.ffff", "incfsz f,d", 2, "dest ← f+1, then skip if zero",
"0000.0100.bbbf.ffff", "bcf f,b", 2, "Clear bit b of f",
"0000.0101.bbbf.ffff", "bsf f,b", 2, "Set bit b of f",
"0000.0110.bbbf.ffff", "btfsc f,b", 2, "Skip if bit b of f is clear",
"0000.0111.bbbf.ffff", "btfss f,b", 2, "Skip if bit b of f is set",
"0000.1000.cccc.cccc", "retlw c", 2, "Set W ← k, then return from subroutine",
"0000.1001.kkkk.kkkk", "call k", 2, "Call subroutine, 8-bit address k",
"0000.101K.KKKK.KKKK", "goto k", 2, "Jump to 9-bit address k",
"0000.1100.cccc.cccc", "movlw c", 2, "W ← c",
"0000.1101.cccc.cccc", "iorlw c", 2, "W ← c | W, bitwise logical or",
"0000.1110.cccc.cccc", "andlw c", 2, "W ← c & W, bitwise and",
"0000.1111.cccc.cccc", "xorlw c", 2, "W ← c ^ W, bitwise exclusive or",
"2222.2222.2222.2222", NULL, 0, "",
};