diff options
Diffstat (limited to 'pcm/pcm.c')
-rw-r--r-- | pcm/pcm.c | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/pcm/pcm.c b/pcm/pcm.c new file mode 100644 index 0000000..35a6faa --- /dev/null +++ b/pcm/pcm.c @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <pcm/pcm.h> + +/* + * Utils + */ + +int binary_print(unsigned int value) +{ + unsigned int count = sizeof(value) * 8; + unsigned int index; + + for (index = 0; index < count; index++) { + printf("%c", value & (1 << (count - 1 - index)) ? '1' : '0'); + + if ((count - 1 - index) % 4 == 0) + printf(" "); + } + + printf("(0x%x)\n", value); +} + +/* + * PCM OPS + */ + +static struct pcm_ops *pcm_ops = NULL; + +int pcm_ops_register(struct pcm_ops *ops) +{ + if (pcm_ops != NULL) + return -1; + + pcm_ops = ops; + + return 0; +} + +int pcm_ops_asm_write(unsigned int value, char *mnemonic) +{ + if (pcm_ops == NULL || pcm_ops->asm_write == NULL) + return -1; + + return pcm_ops->asm_write(pcm_ops->data, value, mnemonic); +} + +int pcm_ops_disasm_write(unsigned int value, char *mnemonic) +{ + if (pcm_ops == NULL || pcm_ops->disasm_write == NULL) + return -1; + + return pcm_ops->disasm_write(pcm_ops->data, value, mnemonic); +} + +static int pcm_ops_asm_format(unsigned int value, const char *format, ...) +{ + char *mnemonic = NULL; + va_list ap; + int rc; + + va_start(ap, format); + + rc = vasprintf(&mnemonic, format, ap); + if (rc < 0 || mnemonic == NULL) + goto error; + + rc = pcm_ops_asm_write(value, mnemonic); + if (rc < 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + va_end(ap); + + return rc; +} + +static int pcm_ops_disasm_format(unsigned int value, const char *format, ...) +{ + char *mnemonic = NULL; + va_list ap; + int rc; + + va_start(ap, format); + + rc = vasprintf(&mnemonic, format, ap); + if (rc < 0 || mnemonic == NULL) + goto error; + + rc = pcm_ops_disasm_write(value, mnemonic); + if (rc < 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + va_end(ap); + + return rc; +} + +/* + * PCM OP codes + */ + +static int pcm_op_padding_asm(char *arguments[], unsigned int count, unsigned int padding) +{ + unsigned int value; + unsigned int i; + int rc; + + if (arguments == NULL) + return -1; + + i = 0; + + /* Grab padding from following nops first. */ + while (arguments[i] != NULL && count > 0 && padding > 0) { + if (strncmp(arguments[i], "nop", 3) != 0) + break; + + rc = pcm_ops_asm_write(PCM_OP_NOP_VALUE, "nop"); + if (rc < 0) + return -1; + + padding--; + i++; + } + + /* Generate leftover padding. */ + while (padding-- > 0) { + rc = pcm_ops_asm_write(PCM_OP_NOP_VALUE, "nop"); + if (rc < 0) + return -1; + } + + return i; +} + +static int pcm_op_immediate_asm(char *arguments[], unsigned int count) +{ + unsigned int value; + unsigned int i; + int rc; + + if (arguments == NULL || count < 1) + return -1; + + for (i = 0; i < count && arguments[i] != NULL ; i++) { + value = strtol(arguments[i], NULL, 16); + + rc = pcm_ops_asm_write(value, "immediate"); + if (rc < 0) + return -1; + } +} + +static int pcm_op_immediate_disasm(unsigned int *arguments, unsigned int count) +{ + unsigned int i; + int rc; + + if (arguments == NULL || count < 1) + return -1; + + i = 0; + + while (count-- > 0) { + rc = pcm_ops_disasm_write(arguments[i], "immediate"); + if (rc < 0) + return -1; + + i++; + } +} + +static int pcm_op_dummy_asm(struct pcm_op_desc *desc, char *arguments[], unsigned int count) +{ + int rc; + + if (desc == NULL || arguments == NULL || count < 1) + return -1; + + rc = pcm_ops_asm_write(desc->value, arguments[0]); + if (rc < 0) + return -1; + + return 0; +} + +static int pcm_op_dummy_disasm(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count) +{ + int rc; + + if (desc == NULL || arguments == NULL || count < 1) + return -1; + + rc = pcm_ops_disasm_format(arguments[0], "%s", desc->mnemonic); + if (rc < 0) + return -1; + + return 0; +} + +static int pcm_op_loadi_asm(struct pcm_op_desc *desc, char *arguments[], unsigned int count) +{ + unsigned int value; + unsigned int reg; + char *string; + int rc; + + if (desc == NULL || arguments == NULL || arguments[1] == NULL || count < 3) + return -1; + + value = desc->value; + + string = arguments[1]; + if (string[0] == 'r') + string = &string[1]; + + reg = atoi(string); + value |= (reg & PCM_OP_LOADI_REG_MASK) << PCM_OP_LOADI_REG_SHIFT; + + rc = pcm_ops_asm_format(value, "%s r%d", arguments[0], reg); + if (rc < 0) + return -1; + + rc = pcm_op_immediate_asm(&arguments[2], 1); + if (rc < 0) + return -1; + + return 2; +} + +static int pcm_op_loadi_disasm(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count) +{ + unsigned int value; + unsigned int reg; + int rc; + + if (desc == NULL || arguments == NULL || count < 1) + return -1; + + value = arguments[0]; + reg = (value >> PCM_OP_LOADI_REG_SHIFT) & PCM_OP_LOADI_REG_MASK; + + rc = pcm_ops_disasm_format(value, "%s r%d", desc->mnemonic, reg); + if (rc < 0) + return -1; + + rc = pcm_op_immediate_disasm(&arguments[1], 1); + if (rc < 0) + return -1; + + return 1; +} + +static int pcm_op_jump_call_asm(struct pcm_op_desc *desc, char *arguments[], unsigned int count) +{ + unsigned int value; + unsigned int address; + int rc; + + if (desc == NULL || arguments == NULL || arguments[1] == NULL || count < 2) + return -1; + + value = desc->value; + address = strtol(arguments[1], NULL, 16); + + value |= (address & PCM_OP_JUMP_ADDRESS_MASK) << PCM_OP_JUMP_ADDRESS_SHIFT; + + rc = pcm_ops_asm_format(value, "%s 0x%04x", arguments[0], address); + if (rc < 0) + return -1; + + return 1; +} + +static int pcm_op_jump_call_disasm(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count) +{ + unsigned int value; + unsigned int address; + int rc; + + if (desc == NULL || arguments == NULL || count < 1) + return -1; + + value = arguments[0]; + address = (value >> PCM_OP_JUMP_ADDRESS_SHIFT) & PCM_OP_JUMP_ADDRESS_MASK; + + rc = pcm_ops_disasm_format(arguments[0], "%s 0x%04x", desc->mnemonic, address); + if (rc < 0) + return -1; + + return 0; +} + +static struct pcm_op_desc pcm_op_descs[] = { + { + .mnemonic = "nop", + .mask = PCM_OP_NOP_MASK, + .value = PCM_OP_NOP_VALUE, + .padding = 0, + .asm_callback = pcm_op_dummy_asm, + .disasm_callback = pcm_op_dummy_disasm, + }, + { + .mnemonic = "loadi", + .mask = PCM_OP_LOADI_MASK, + .value = PCM_OP_LOADI_VALUE, + .padding = 0, + .asm_callback = pcm_op_loadi_asm, + .disasm_callback = pcm_op_loadi_disasm, + }, + { + .mnemonic = "jump", + .mask = PCM_OP_JUMP_MASK, + .value = PCM_OP_JUMP_VALUE, + .padding = 1, + .asm_callback = pcm_op_jump_call_asm, + .disasm_callback = pcm_op_jump_call_disasm, + }, + { + .mnemonic = "call", + .mask = PCM_OP_CALL_MASK, + .value = PCM_OP_CALL_VALUE, + .padding = 1, + .asm_callback = pcm_op_jump_call_asm, + .disasm_callback = pcm_op_jump_call_disasm, + }, + { + .mnemonic = "ret", + .mask = PCM_OP_RET_MASK, + .value = PCM_OP_RET_VALUE, + .padding = 1, + .asm_callback = pcm_op_dummy_asm, + .disasm_callback = pcm_op_dummy_disasm, + }, +}; + +static size_t pcm_op_descs_count = sizeof(pcm_op_descs) / sizeof(struct pcm_op_desc); + +static struct pcm_op_desc *pcm_op_desc_find_mnemonic(char *mnemonic) +{ + unsigned int i; + size_t length; + + for (i = 0; i < pcm_op_descs_count; i++) { + length = strlen(pcm_op_descs[i].mnemonic); + + /* Compare mnemonic as a prefix, not as a full string. */ + if (strncmp(pcm_op_descs[i].mnemonic, mnemonic, length) == 0) + return &pcm_op_descs[i]; + } + + return NULL; +} + +static struct pcm_op_desc *pcm_op_desc_find_value(unsigned int value) +{ + unsigned int i; + size_t length; + + for (i = 0; i < pcm_op_descs_count; i++) { + if ((value & pcm_op_descs[i].mask) == pcm_op_descs[i].value) + return &pcm_op_descs[i]; + } + + return NULL; +} + +int pcm_op_asm(char *arguments[], unsigned int count) +{ + struct pcm_op_desc *desc; + char *mnemonic; + int increment; + int rc; + + if (arguments == NULL || count < 1) + return -1; + + mnemonic = arguments[0]; + if (mnemonic == NULL) + return -1; + + desc = pcm_op_desc_find_mnemonic(mnemonic); + if (desc == NULL) + return -1; + + if (desc->asm_callback == NULL) + return -1; + + rc = desc->asm_callback(desc, arguments, count); + if (rc < 0) + return -1; + + increment = rc; + + if (desc->padding > 0) { + arguments = &arguments[increment + 1]; + + if ((increment + 1) < count) + count -= increment + 1; + else + count = 0; + + rc = pcm_op_padding_asm(arguments, count, desc->padding); + if (rc < 0) + return -1; + + increment += rc; + } + + return increment; +} + +int pcm_op_disasm(unsigned int *arguments, unsigned int count) +{ + struct pcm_op_desc *desc; + unsigned int value; + int increment; + int rc; + + if (arguments == NULL || count < 1) + return -1; + + value = arguments[0]; + + desc = pcm_op_desc_find_value(value); + if (desc == NULL) + return -1; + + if (desc->disasm_callback == NULL) + return -1; + + rc = desc->disasm_callback(desc, arguments, count); + if (rc < 0) + return -1; + + increment = rc; + + return increment; +} |