summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile27
-rw-r--r--src/basic.c163
-rw-r--r--src/charset.c66
-rw-r--r--src/hexdump.c109
-rw-r--r--src/main.c99
5 files changed, 464 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..ba8a9b0
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,27 @@
+CC = cc
+CFLAGS = -g -Wall -O2
+LDFLAGS =
+OBJS = charset.o basic.o hexdump.o main.o
+NAME = zxdump
+
+PREFIX = /usr/local
+INSTALL = /usr/bin/install
+RM = /bin/rm
+
+INCLUDE_PATH = ../include
+HEADERS_SUBDIR = zx
+HEADERS_BUILD = $(addprefix $(INCLUDE_PATH)/$(HEADERS_SUBDIR)/, $(HEADERS))
+
+all: $(NAME)
+
+$(NAME): $(OBJS)
+ $(CC) $(OBJS) -o $(NAME) $(LDFLAGS)
+
+$(OBJS): %.o: %.c $(HEADERS_BUILD)
+ $(CC) $(CFLAGS) -I$(INCLUDE_PATH) -c $<
+
+install: $(NAME)
+ $(INSTALL) -c -m 0755 $(NAME) $(PREFIX)/bin
+
+clean:
+ $(RM) -f $(NAME) $(OBJS)
diff --git a/src/basic.c b/src/basic.c
new file mode 100644
index 0000000..043c66a
--- /dev/null
+++ b/src/basic.c
@@ -0,0 +1,163 @@
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <zx/charset.h>
+#include <zx/basic.h>
+
+char *zx81_basic_tokens_low[3] = {
+ "RND", "INKEY$", "PI",
+};
+
+char *zx81_basic_tokens[64] = {
+ "\"\"", "AT", "TAB", NULL, "CODE", "VAL", "LEN", "SIN",
+ "COS", "TAN", "ASN", "ACS", "ATN", "LN", "EXP", "INT",
+ "SQR", "SGN", "ABS", "PEEK", "USR", "STR$", "CHR$", "NOT",
+ "**", "OR", "AND", "<=", ">=", "<>", "THEN", "TO",
+ "STEP", "LPRINT", "LLIST", "STOP", "SLOW", "FAST", "NEW", "SCROLL",
+ "CONT", "DIM", "REM", "FOR", "GOTO", "GOSUB", "INPUT", "LOAD",
+ "LIST", "LET", "PAUSE", "NEXT", "POKE", "PRINT", "PLOT", "RUN",
+ "SAVE", "RAND", "IF", "CLS", "UNPLOT", "CLEAR", "RETURN", "COPY",
+};
+
+static inline enum zx_basic_token_type zx_basic_token_type_get(uint8_t b) {
+ if (ZX81_CHAR_LOW(b)) {
+ uint32_t codepoint = zx81_charset[b];
+
+ if ((codepoint >= 'A' && codepoint <= 'Z')
+ || (codepoint >= '0' && codepoint <= '9')) {
+ return ZX_BASIC_TOKEN_ALNUM;
+ } else if (codepoint == '"') {
+ return ZX_BASIC_TOKEN_QUOTE;
+ } else {
+ return ZX_BASIC_TOKEN_SYMBOL;
+ }
+ } else if (ZX81_CHAR_INVERSE(b)) {
+ return zx_basic_token_type_get(b - ZX81_CHAR_INVERSE_START);
+ } else if (ZX81_CHAR_TOKEN_LOW(b)) {
+ return ZX_BASIC_TOKEN_WORD;
+ } else if (ZX81_CHAR_TOKEN_HIGH(b)) {
+ char *token = zx81_basic_tokens[b-ZX81_CHAR_TOKEN_HIGH_START];
+
+ if (token[0] >= 'A' && token[0] <= 'Z') {
+ return ZX_BASIC_TOKEN_WORD;
+ } else {
+ return ZX_BASIC_TOKEN_SYMBOL;
+ }
+ }
+
+ return ZX_BASIC_TOKEN_UNKNOWN;
+}
+
+static inline int basic_print(uint8_t c, int tty, FILE *stream) {
+ if (ZX81_CHAR_LOW(c)) {
+ return zx81_fputc(c, 0, stream);
+ } else if (ZX81_CHAR_TOKEN_LOW(c)) {
+ return fputs(zx81_basic_tokens_low[c - ZX81_CHAR_TOKEN_LOW_START], stream);
+ } else if (ZX81_CHAR_NEWLINE(c)) {
+ return putchar('\n');
+ } else if (ZX81_CHAR_INVERSE(c)) {
+ return zx81_fputc(c - ZX81_CHAR_INVERSE_START, tty, stream);
+ } else if (ZX81_CHAR_TOKEN_HIGH(c)) {
+ return fputs(zx81_basic_tokens[c - ZX81_CHAR_TOKEN_HIGH_START], stream);
+ }
+
+ return 0;
+}
+ssize_t zx81_basic_dump(int fd, FILE *stream) {
+ void *buf;
+ ssize_t total = 0;
+ int tty = isatty(fileno(stream));
+ struct stat st;
+
+ if (fstat(fd, &st) < 0) {
+ goto error_fstat;
+ }
+
+ if ((buf = malloc(st.st_blksize)) == NULL) {
+ goto error_malloc;
+ }
+
+ if (lseek(fd, ZX_BASIC_STATE_SIZE, SEEK_CUR) < 0) {
+ goto error_io;
+ }
+
+ while (1) {
+ ssize_t readlen, len, i;
+ zx_basic_line line;
+ uint8_t last = 0xc0;
+
+ if ((readlen = read(fd, &line, sizeof(line))) < 0) {
+ goto error_io;
+ } else if (readlen == 0) {
+ break;
+ }
+
+ len = le16toh(line.len);
+
+ if (be16toh(line.num) == len && len == ZX_BASIC_LINE_LAST) {
+ break;
+ }
+
+ if (printf("%d ", (int)be16toh(line.num)) < 0) {
+ goto error_io;
+ }
+
+ if (read(fd, buf, len) < 0) {
+ goto error_io;
+ }
+
+ for (i=0; i<len; i++) {
+ uint8_t c = ((uint8_t *)buf)[i];
+
+ enum zx_basic_token_type type = zx_basic_token_type_get(c),
+ type_last = zx_basic_token_type_get(last);
+
+ int space = 0;
+
+ if (type == ZX_BASIC_TOKEN_ALNUM) {
+ if (type_last == ZX_BASIC_TOKEN_WORD) {
+ space = 1;
+ }
+ } else if (type == ZX_BASIC_TOKEN_QUOTE) {
+ if (type_last == ZX_BASIC_TOKEN_WORD) {
+ space = 1;
+ }
+ } else if (type == ZX_BASIC_TOKEN_SYMBOL) {
+ space = 0;
+ } else if (type == ZX_BASIC_TOKEN_WORD) {
+ if (type_last != ZX_BASIC_TOKEN_SYMBOL) {
+ space = 1;
+ }
+ } else if (ZX81_CHAR_TOKEN_INTEGRAL(c) || ZX81_CHAR_TOKEN_FLOAT(c)) {
+ i += 5;
+ }
+
+ if (space && fputc(' ', stream) < 0) {
+ goto error_io;
+ }
+
+ if (basic_print(c, tty, stream) < 0) {
+ goto error_io;
+ }
+
+ last = c;
+ }
+
+ if (!ZX81_CHAR_NEWLINE(((uint8_t *)buf)[i-1])) {
+ if (fputc('\n', stream) < 0) {
+ goto error_io;
+ }
+ }
+ }
+
+ free(buf);
+
+ return total;
+
+error_io:
+ free(buf);
+
+error_malloc:
+error_fstat:
+ return -1;
+}
diff --git a/src/charset.c b/src/charset.c
new file mode 100644
index 0000000..64d5b64
--- /dev/null
+++ b/src/charset.c
@@ -0,0 +1,66 @@
+#include <sys/types.h>
+
+#include <zx/charset.h>
+
+uint32_t zx81_charset[ZX81_CHARSET_LEN] = {
+ 0x0020, 0x2598, 0x259d, 0x2580, 0x2596, 0x258c, 0x259e, 0x259b,
+ 0x2592, '.', '.', '"', 0x00a3, '$', ':', '?',
+ '(', ')', '>', '<', '=', '+', '-', '*',
+ '/', ';', ',', '.', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', 'A', 'B',
+ 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+};
+
+static inline size_t utf8_encode(uint8_t *buf, uint32_t codepoint) {
+ if ((codepoint & 0x007f) == codepoint) {
+ buf[0] = codepoint & 0x007f;
+
+ return 1;
+ } else if ((codepoint & 0x07ff) == codepoint) {
+ buf[0] = 0xc0 | ((codepoint & 0x07c0) >> 6);
+ buf[1] = 0x80 | (codepoint & 0x003f);
+
+ return 2;
+ } else if ((codepoint & 0xffff) == codepoint) {
+ buf[0] = 0xe0 | ((codepoint & 0xf000) >> 12);
+ buf[1] = 0x80 | ((codepoint & 0x0fc0) >> 6);
+ buf[2] = 0x80 | (codepoint & 0x003f);
+
+ return 3;
+ } else {
+ buf[0] = 0xf0 | ((codepoint & 0x1c0000) >> 18);
+ buf[1] = 0x80 | ((codepoint & 0x03f000) >> 12);
+ buf[2] = 0x80 | ((codepoint & 0x000fc0) >> 6);
+ buf[3] = 0x80 | (codepoint & 0x00003f);
+
+ return 4;
+ }
+}
+
+int zx81_fputc(uint8_t c, int inverse, FILE *stream) {
+ uint8_t sequence[4];
+ size_t len = utf8_encode(sequence, zx81_charset[c]);
+
+ if (inverse) {
+ if (fputs("\033[7m", stream) < 0) {
+ goto error_io;
+ }
+ }
+
+ if (fwrite(sequence, len, 1, stream) < 1) {
+ goto error_io;
+ }
+
+ if (inverse) {
+ if (fputs("\033[27m", stream) < 0) {
+ goto error_io;
+ }
+ }
+
+ return 0;
+
+error_io:
+ return -1;
+}
diff --git a/src/hexdump.c b/src/hexdump.c
new file mode 100644
index 0000000..fd7d7ff
--- /dev/null
+++ b/src/hexdump.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <zx/charset.h>
+#include <zx/hexdump.h>
+
+static ssize_t hexdump_line(off_t offset, void *buf, size_t len, int tty, FILE *stream) {
+ size_t i;
+
+ if (printf("%08zx: ", offset) < 0) {
+ goto error_io;
+ }
+
+ for (i=0; i<len; i++) {
+ if (i > 0 && (i % ZX81_HEXDUMP_STRIDE_GROUP) == 0) {
+ if (fputc(' ', stream) < 0) {
+ goto error_io;
+ }
+ }
+
+ if (fprintf(stream, "%02x", ((uint8_t *)buf)[offset+i]) < 0) {
+ goto error_io;
+ }
+ }
+
+ if (fputs(" ", stream) < 0) {
+ goto error_io;
+ }
+
+ for (i=0; i<len; i++) {
+ uint8_t c = ((uint8_t *)buf)[offset+i];
+
+ if (c <= 0x3f) {
+ if (zx81_fputc(c, 0, stream) < 0) {
+ goto error_io;
+ }
+ } else if (c >= 0xa0 && c <= 0xbf) {
+ if (zx81_fputc(c - 0xa0, tty, stream) < 0) {
+ goto error_io;
+ }
+ } else {
+ if (fputc('.', stream) < 0) {
+ goto error_io;
+ }
+ }
+ }
+
+ if (fputc('\n', stream) < 0) {
+ goto error_io;
+ }
+
+ return fflush(stream);
+
+error_io:
+ return -1;
+}
+
+ssize_t zx81_hexdump(int fd, FILE *stream) {
+ void *buf;
+ ssize_t total = 0;
+ int tty = isatty(fileno(stdout));
+ struct stat st;
+
+ if (fstat(fd, &st) < 0) {
+ goto error_fstat;
+ }
+
+ if ((buf = malloc(st.st_blksize)) == NULL) {
+ goto error_malloc;
+ }
+
+ while (1) {
+ ssize_t len, i;
+ off_t offset = 0;
+
+ if ((len = read(fd, buf, st.st_blksize)) < 0) {
+ goto error_read;
+ } else if (len == 0) {
+ break;
+ }
+
+ for (i=0; i<len; i+=ZX81_HEXDUMP_STRIDE_LINE) {
+ size_t left = len - i,
+ linesz = left < ZX81_HEXDUMP_STRIDE_LINE? left: ZX81_HEXDUMP_STRIDE_LINE;
+
+ if (hexdump_line(offset, buf, linesz, tty, stream) < 0) {
+ goto error_hexdump_line;
+ }
+
+ offset += linesz;
+ total += linesz;
+ }
+ }
+
+ free(buf);
+
+ return total;
+
+error_hexdump_line:
+error_read:
+ free(buf);
+
+error_malloc:
+error_fstat:
+ return -1;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..bc200cb
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,99 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <zx/hexdump.h>
+#include <zx/basic.h>
+
+#define ZX_DUMP_FLAGS_NONE 0
+#define ZX_DUMP_FLAGS_BASIC (1 << 0)
+
+static void usage(const char *prog, char *message, ...) {
+ if (message != NULL) {
+ va_list args;
+
+ va_start(args, message);
+ vfprintf(stderr, message, args);
+ fputc('\n', stderr);
+ va_end(args);
+ }
+
+ fprintf(stderr, "usage: %s [-b] [file]\n", prog);
+ exit(1);
+}
+
+int main(int argc, char **argv) {
+ int fd,
+ flags = ZX_DUMP_FLAGS_NONE,
+ argn;
+
+ while (1) {
+ int c;
+
+ if ((c = getopt(argc, argv, "b")) < 0) {
+ break;
+ }
+
+ switch (c) {
+ case 'b':
+ flags |= ZX_DUMP_FLAGS_BASIC;
+ break;
+
+ case '?':
+ default:
+ usage(argv[0], NULL);
+ }
+ }
+
+ argn = argc - optind;
+
+ if (argn == 0) {
+ fd = fileno(stdin);
+ } else if (argn == 1) {
+ if ((fd = open(argv[optind], O_RDONLY)) < 0) {
+ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno));
+
+ goto error_open;
+ }
+ } else {
+ usage(argv[0], "Too many arguments provided");
+ }
+
+ if (flags & ZX_DUMP_FLAGS_BASIC) {
+ if (zx81_basic_dump(fd, stdout) < 0) {
+ fprintf(stderr, "%s: %s: %s\n",
+ argv[0],
+ (argn == 1)? argv[optind]: "(stdin)",
+ strerror(errno));
+
+ goto error_dump;
+ }
+ } else {
+ if (zx81_hexdump(fd, stdout) < 0) {
+ fprintf(stderr, "%s: %s: %s\n",
+ argv[0],
+ (argn == 1)? argv[optind]: "(stdin)",
+ strerror(errno));
+
+ goto error_dump;
+ }
+ }
+
+ if (argn == 1) {
+ close(fd);
+ }
+
+ return 0;
+
+error_dump:
+ if (argn == 1) {
+ close(fd);
+ }
+
+error_open:
+ return 1;
+}