summaryrefslogtreecommitdiffstats
path: root/pcm-assembler/pcm-assembler.c
diff options
context:
space:
mode:
Diffstat (limited to 'pcm-assembler/pcm-assembler.c')
-rw-r--r--pcm-assembler/pcm-assembler.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/pcm-assembler/pcm-assembler.c b/pcm-assembler/pcm-assembler.c
new file mode 100644
index 0000000..2e25e65
--- /dev/null
+++ b/pcm-assembler/pcm-assembler.c
@@ -0,0 +1,502 @@
+/*
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pcm/pcm.h>
+#include "pcm-assembler.h"
+
+struct pcm_ops_data pcm_ops_data = {
+ .func_address = NULL,
+ .func_address_current = NULL,
+ .fd = -1,
+};
+
+struct pcm_ops pcm_ops = {
+ .asm_write = NULL,
+ .disasm_write = NULL,
+ .func_address = NULL,
+ .label_address = NULL,
+ .data = &pcm_ops_data,
+};
+
+int pcm_asm_write(void *data, unsigned int value, char *mnemonic)
+{
+ struct pcm_ops_data *ops_data;
+
+ if (data == NULL)
+ return -1;
+
+ ops_data = (struct pcm_ops_data *) data;
+
+ if (ops_data->fd < 0)
+ return -1;
+
+ write(ops_data->fd, &value, sizeof(value));
+
+ return 0;
+}
+
+int pcm_asm_write_dummy(void *data, unsigned int value, char *mnemonic)
+{
+ return 0;
+}
+
+int pcm_func_address(void *data, char *func)
+{
+ struct pcm_func_address *func_address;
+
+ func_address = pcm_func_address_find(func);
+ if (func_address == NULL) {
+ if (func[0] != '0')
+ fprintf(stderr, "Unable to find address for func %s\n", func);
+
+ return -1;
+ }
+
+ return func_address->address;
+}
+
+int pcm_label_address(void *data, char *label)
+{
+ struct pcm_label_address *label_address;
+
+ label_address = pcm_label_address_find(label);
+ if (label_address == NULL) {
+ if (label[0] != '0')
+ fprintf(stderr, "Unable to find address for label %s\n", label);
+
+ return -1;
+ }
+
+ return label_address->address;
+}
+
+int pcm_address_dummy(void *data, char *string)
+{
+ return -1;
+}
+
+int pcm_func_address_register(char *func, unsigned int address)
+{
+ struct pcm_func_address *func_address;
+ struct list_head *list;
+
+ if (func == NULL)
+ return -1;
+
+ func_address = (struct pcm_func_address *) calloc(1, sizeof(struct pcm_func_address));
+ func_address->func = strdup(func);
+ func_address->address = address;
+
+ list = list_head_alloc(pcm_ops_data.func_address, NULL, (void *) func_address);
+ pcm_ops_data.func_address = list;
+
+ return 0;
+}
+
+void pcm_func_address_flush(void)
+{
+ struct pcm_func_address *func_address;
+ struct list_head *list;
+ struct list_head *list_prev;
+
+ list = pcm_ops_data.func_address;
+ while (list != NULL) {
+ if (list->data != NULL) {
+ pcm_ops_data.func_address_current = list;
+ pcm_label_address_flush();
+
+ func_address = list->data;
+
+ if (func_address->func != NULL)
+ free(func_address->func);
+
+ free(list->data);
+ }
+
+ list_prev = list->prev;
+
+ list_head_free(list);
+
+list_continue:
+ list = list_prev;
+ }
+
+ pcm_ops_data.func_address = NULL;
+}
+
+struct pcm_func_address *pcm_func_address_find(char *func)
+{
+ struct pcm_func_address *func_address;
+ struct list_head *list;
+ struct list_head *list_prev;
+
+ if (func == NULL)
+ return NULL;
+
+ list = pcm_ops_data.func_address;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ func_address = (struct pcm_func_address *) list->data;
+
+ if (func_address->func != NULL && strcmp(func, func_address->func) == 0)
+ return func_address;
+
+list_continue:
+ list = list->prev;
+ }
+
+ return NULL;
+}
+
+int pcm_func_address_current_set(char *func)
+{
+ struct pcm_func_address *func_address;
+ struct list_head *list;
+ struct list_head *list_prev;
+
+ if (func == NULL)
+ return -1;
+
+ list = pcm_ops_data.func_address;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ func_address = (struct pcm_func_address *) list->data;
+
+ if (func_address->func != NULL && strcmp(func, func_address->func) == 0) {
+ pcm_ops_data.func_address_current = list;
+ return 0;
+ }
+
+list_continue:
+ list = list->prev;
+ }
+
+ return -1;
+}
+
+int pcm_label_address_register(char *label, unsigned int address)
+{
+ struct pcm_func_address *func_address;
+ struct pcm_label_address *label_address;
+ struct list_head *list;
+
+ if (label == NULL || pcm_ops_data.func_address == NULL || pcm_ops_data.func_address->data == NULL)
+ return -1;
+
+ func_address = pcm_ops_data.func_address->data;
+
+ label_address = (struct pcm_label_address *) calloc(1, sizeof(struct pcm_label_address));
+ label_address->label = strdup(label);
+ label_address->address = address;
+
+ list = list_head_alloc(func_address->label_address, NULL, (void *) label_address);
+ func_address->label_address = list;
+
+ return 0;
+}
+
+void pcm_label_address_flush(void)
+{
+ struct pcm_func_address *func_address;
+ struct pcm_label_address *label_address;
+ struct list_head *list;
+ struct list_head *list_prev;
+
+ if (pcm_ops_data.func_address_current == NULL || pcm_ops_data.func_address_current->data == NULL)
+ return;
+
+ func_address = pcm_ops_data.func_address_current->data;
+
+ list = func_address->label_address;
+ while (list != NULL) {
+ if (list->data != NULL) {
+ label_address = list->data;
+
+ if (label_address->label != NULL)
+ free(label_address->label);
+
+ free(list->data);
+ }
+
+ list_prev = list->prev;
+
+ list_head_free(list);
+
+list_continue:
+ list = list_prev;
+ }
+
+ func_address->label_address = NULL;
+}
+
+struct pcm_label_address *pcm_label_address_find(char *label)
+{
+ struct pcm_func_address *func_address;
+ struct pcm_label_address *label_address;
+ struct list_head *list;
+ struct list_head *list_prev;
+
+ if (label == NULL || pcm_ops_data.func_address_current == NULL || pcm_ops_data.func_address_current->data == NULL)
+ return NULL;
+
+ func_address = pcm_ops_data.func_address_current->data;
+
+ list = func_address->label_address;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ label_address = (struct pcm_label_address *) list->data;
+
+ if (label_address->label != NULL && strcmp(label, label_address->label) == 0)
+ return label_address;
+
+list_continue:
+ list = list->prev;
+ }
+
+ return NULL;
+}
+
+int source_data(char *file, char **data)
+{
+ struct stat stat_data;
+ size_t size;
+ int fd = -1;
+ int rc;
+
+ if (file == NULL || data == NULL)
+ return -1;
+
+ rc = stat(file, &stat_data);
+ if (rc < 0) {
+ fprintf(stderr, "Invalid file: %s\n", file);
+ goto error;
+ }
+
+ size = stat_data.st_size;
+ *data = malloc(size);
+
+ fd = open(file, O_RDONLY, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open file: %s\n", file);
+ goto error;
+ }
+
+ rc = read(fd, *data, size);
+ if (rc <= 0) {
+ fprintf(stderr, "Unable to read file: %s\n", file);
+ goto error;
+ }
+
+ rc = size;
+ goto complete;
+
+error:
+ rc = -1;
+
+ if (*data != NULL)
+ free(*data);
+
+complete:
+ if (fd >= 0)
+ close(fd);
+
+ return rc;
+}
+
+int arguments_parse(int stage, char *arguments[], unsigned int count, unsigned int address)
+{
+ unsigned int location;
+ char *mnemonic;
+ int rc;
+
+ if (arguments == NULL || arguments[0] == NULL || count == 0)
+ return -1;
+
+ mnemonic = arguments[0];
+
+ if (strcmp(mnemonic, ".loc") == 0) {
+ location = strtol(arguments[1], NULL, 16);
+
+ if (address > (location - 1)) {
+ fprintf(stderr, "Unable to set location to 0x%x from address 0x%x\n", location, address);
+ return -1;
+ }
+
+ return pcm_padding_asm(location - 1 - address);
+ } else if (strcmp(mnemonic, ".func") == 0) {
+ if (stage == 0) {
+ rc = pcm_func_address_register(arguments[1], address);
+ if (rc < 0)
+ return -1;
+ }
+
+ rc = pcm_func_address_current_set(arguments[1]);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+ } else if (strcmp(mnemonic, ".label") == 0) {
+ rc = pcm_label_address_register(arguments[1], address);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+ } else {
+ return 1 + pcm_op_asm(arguments, count);
+ }
+}
+
+int assemble(int stage, char *data, size_t size)
+{
+ unsigned int address;
+ unsigned int count;
+ char *arguments[PCM_ASSEMBLER_ARGUMENTS_MAX];
+ char *argument;
+ char *token;
+ char *line = NULL;
+ int rc;
+
+ if (data == NULL || size == 0)
+ return -1;
+
+ if (stage == 0) {
+ pcm_ops.asm_write = pcm_asm_write_dummy;
+ pcm_ops.func_address = pcm_address_dummy;
+ pcm_ops.label_address = pcm_address_dummy;
+ } else {
+ pcm_ops.asm_write = pcm_asm_write;
+ pcm_ops.func_address = pcm_func_address;
+ pcm_ops.label_address = pcm_label_address;
+ }
+
+ address = 0;
+
+ token = strtok(data, "\n");
+
+ while (token != NULL) {
+ line = strdup(token);
+
+ argument = strtok(line, " ");
+ count = 0;
+
+ while (argument != NULL && count < PCM_ASSEMBLER_ARGUMENTS_MAX) {
+ arguments[count++] = argument;
+ argument = strtok(NULL, " ");
+ }
+
+ if (count == PCM_ASSEMBLER_ARGUMENTS_MAX)
+ goto error;
+
+ rc = arguments_parse(stage, arguments, count, address);
+ if (rc < 0)
+ goto error;
+
+ address += rc;
+
+ free(line);
+ line = NULL;
+
+ token = strtok(token + strlen(token) + 1, "\n");
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+ if (line != NULL)
+ free(line);
+
+complete:
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ char *data = NULL;
+ size_t size;
+ int fd = -1;
+ int rc;
+ int i;
+
+ if (argc < 3 || argv[1] == NULL || argv[2] == NULL)
+ return 1;
+
+ rc = source_data(argv[1], &data);
+ if (rc < 0 || data == NULL)
+ goto error;
+
+ size = rc;
+
+ rc = pcm_ops_register(&pcm_ops);
+ if (rc < 0) {
+ fprintf(stderr, "Unable to set PCM OPS\n");
+ goto error;
+ }
+
+ rc = assemble(0, data, size);
+ if (rc < 0) {
+ fprintf(stderr, "Unable to assemble source\n");
+ goto error;
+ }
+
+ fd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open output file\n");
+ }
+
+ pcm_ops_data.fd = fd;
+
+ rc = assemble(1, data, size);
+ if (rc < 0) {
+ fprintf(stderr, "Unable to assemble source\n");
+ goto error;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = 1;
+
+complete:
+ if (fd >= 0)
+ close(fd);
+
+ pcm_func_address_flush();
+
+ if (data != NULL)
+ free(data);
+
+ return rc;
+}