summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile25
-rw-r--r--include/zx/basic.h35
-rw-r--r--include/zx/charset.h40
-rw-r--r--include/zx/hexdump.h12
-rw-r--r--main.c475
-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
10 files changed, 556 insertions, 495 deletions
diff --git a/Makefile b/Makefile
index c0d8cd0..e476f4e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,23 +1,8 @@
-CC = cc
-CFLAGS = -g -Wall -O2
-LDFLAGS =
-OBJS = main.o
-NAME = zxdump
+all:
+ $(MAKE) -C src all
-PREFIX = /usr/local
-INSTALL = /usr/bin/install
-RM = /bin/rm
-
-all: $(NAME)
-
-$(NAME): $(OBJS)
- $(CC) $(OBJS) -o $(NAME) $(LDFLAGS)
-
-$(OBJS): %.o: %.c
- $(CC) $(CFLAGS) -c $<
-
-install: $(NAME)
- $(INSTALL) -c -m 0755 $(NAME) $(PREFIX)/bin
+install:
+ $(MAKE) -C src install
clean:
- $(RM) -f $(NAME) $(OBJS)
+ $(MAKE) -C src clean
diff --git a/include/zx/basic.h b/include/zx/basic.h
new file mode 100644
index 0000000..1989954
--- /dev/null
+++ b/include/zx/basic.h
@@ -0,0 +1,35 @@
+#ifndef _ZX_BASIC_H
+#define _ZX_BASIC_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#define ZX81_CHAR_TOKEN_LOW_START 0x40
+#define ZX81_CHAR_TOKEN_LOW_END 0x42
+
+#define ZX81_CHAR_TOKEN_HIGH_START 0xc0
+#define ZX81_CHAR_TOKEN_HIGH_END 0xff
+
+#define ZX_BASIC_STATE_SIZE 116
+#define ZX_BASIC_LINE_LAST 0x7676
+
+extern char *zx81_basic_tokens_low[3];
+
+extern char *zx81_basic_tokens[64];
+
+typedef struct _zx_basic_line {
+ uint16_t num, len;
+} zx_basic_line;
+
+enum zx_basic_token_type {
+ ZX_BASIC_TOKEN_UNKNOWN,
+ ZX_BASIC_TOKEN_ALNUM,
+ ZX_BASIC_TOKEN_QUOTE,
+ ZX_BASIC_TOKEN_SYMBOL,
+ ZX_BASIC_TOKEN_WORD,
+};
+
+ssize_t zx81_basic_dump(int fd, FILE *stream);
+
+#endif /* _ZX_BASIC_H */
diff --git a/include/zx/charset.h b/include/zx/charset.h
new file mode 100644
index 0000000..34f6eb8
--- /dev/null
+++ b/include/zx/charset.h
@@ -0,0 +1,40 @@
+#ifndef _ZX_CHARSET_H
+#define _ZX_CHARSET_H
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#define ZX81_CHARSET_LEN 64
+
+#define ZX81_CHAR_LOW(c) \
+ (c <= ZX81_CHARSET_LEN)
+
+#define ZX81_CHAR_INVERSE_START 0x80
+#define ZX81_CHAR_INVERSE_END 0xbf
+
+#define ZX81_CHAR_INVERSE(c) \
+ (c >= ZX81_CHAR_INVERSE_START && c <= ZX81_CHAR_INVERSE_END)
+
+#define ZX81_CHAR_TOKEN_LOW(c) \
+ (c >= ZX81_CHAR_TOKEN_LOW_START && c <= ZX81_CHAR_TOKEN_LOW_END)
+
+#define ZX81_CHAR_NEWLINE(c) \
+ (c == 0x76)
+
+#define ZX81_CHAR_TOKEN_HIGH(c) \
+ (c >= 0xc0)
+
+#define ZX81_CHAR_TOKEN(c) \
+ (ZX81_CHAR_TOKEN_LOW(c) || ZX81_CHAR_TOKEN_HIGH(c))
+
+#define ZX81_CHAR_TOKEN_INTEGRAL(c) \
+ (c == 0x0e)
+
+#define ZX81_CHAR_TOKEN_FLOAT(c) \
+ (c == 0x7e)
+
+extern uint32_t zx81_charset[ZX81_CHARSET_LEN];
+
+int zx81_fputc(uint8_t c, int inverse, FILE *stream);
+
+#endif /* _ZX_CHARSET_H */
diff --git a/include/zx/hexdump.h b/include/zx/hexdump.h
new file mode 100644
index 0000000..5b38796
--- /dev/null
+++ b/include/zx/hexdump.h
@@ -0,0 +1,12 @@
+#ifndef _ZX81_HEXDUMP_H
+#define _ZX81_HEXDUMP_H
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#define ZX81_HEXDUMP_STRIDE_LINE 16
+#define ZX81_HEXDUMP_STRIDE_GROUP 2
+
+ssize_t zx81_hexdump(int source_fd, FILE *stream);
+
+#endif /* _ZX81_HEXDUMP_H */
diff --git a/main.c b/main.c
deleted file mode 100644
index 488d230..0000000
--- a/main.c
+++ /dev/null
@@ -1,475 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <inttypes.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <endian.h>
-#include <errno.h>
-
-#define ZX_CHARSET_LEN 64
-
-#define ZX_CHAR_LOW(c) \
- (c <= ZX_CHARSET_LEN)
-
-#define ZX_CHAR_INVERSE_START 0x80
-#define ZX_CHAR_INVERSE_END 0xbf
-
-#define ZX_CHAR_INVERSE(c) \
- (c >= ZX_CHAR_INVERSE_START && c <= ZX_CHAR_INVERSE_END)
-
-#define ZX_CHAR_TOKEN_LOW_START 0x40
-#define ZX_CHAR_TOKEN_LOW_END 0x42
-
-#define ZX_CHAR_TOKEN_LOW(c) \
- (c >= ZX_CHAR_TOKEN_LOW_START && c <= ZX_CHAR_TOKEN_LOW_END)
-
-#define ZX_CHAR_NEWLINE(c) \
- (c == 0x76)
-
-#define ZX_CHAR_TOKEN_HIGH_START 0xc0
-#define ZX_CHAR_TOKEN_HIGH_END 0xff
-
-#define ZX_CHAR_TOKEN_HIGH(c) \
- (c >= 0xc0)
-
-#define ZX_CHAR_TOKEN(c) \
- (ZX_CHAR_TOKEN_LOW(c) || ZX_CHAR_TOKEN_HIGH(c))
-
-#define ZX_CHAR_TOKEN_INTEGRAL(c) \
- (c == 0x0e)
-
-#define ZX_CHAR_TOKEN_FLOAT(c) \
- (c == 0x7e)
-
-typedef struct _zx_basic_line {
- uint16_t num, len;
-} zx_basic_line;
-
-enum zx_basic_token_type {
- ZX_BASIC_TOKEN_UNKNOWN,
- ZX_BASIC_TOKEN_ALNUM,
- ZX_BASIC_TOKEN_QUOTE,
- ZX_BASIC_TOKEN_SYMBOL,
- ZX_BASIC_TOKEN_WORD,
-};
-
-#define ZX_BASIC_STATE_SIZE 116
-#define ZX_BASIC_LINE_LAST 0x7676
-
-static uint32_t zx_charset[ZX_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 char *zx_tokens_low[3] = {
- "RND", "INKEY$", "PI",
-};
-
-static char *zx_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 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;
- }
-}
-
-static inline int zx_putchar(uint8_t c, int inverse) {
- uint8_t sequence[4];
- size_t len = utf8_encode(sequence, zx_charset[c]);
-
- if (inverse) {
- if (fputs("\033[7m", stdout) < 0) {
- goto error_io;
- }
- }
-
- if (fwrite(sequence, len, 1, stdout) < 1) {
- goto error_io;
- }
-
- if (inverse) {
- if (fputs("\033[27m", stdout) < 0) {
- goto error_io;
- }
- }
-
- return 0;
-
-error_io:
- return -1;
-}
-
-#define ZX_HEXDUMP_STRIDE_LINE 16
-#define ZX_HEXDUMP_STRIDE_GROUP 2
-
-static ssize_t hexdump_line(off_t offset, void *buf, size_t len, int tty) {
- size_t i;
-
- if (printf("%08zx: ", offset) < 0) {
- goto error_io;
- }
-
- for (i=0; i<len; i++) {
- if (i > 0 && (i % ZX_HEXDUMP_STRIDE_GROUP) == 0) {
- if (putchar(' ') < 0) {
- goto error_io;
- }
- }
-
- if (printf("%02x", ((uint8_t *)buf)[offset+i]) < 0) {
- goto error_io;
- }
- }
-
- if (fputs(" ", stdout) < 0) {
- goto error_io;
- }
-
- for (i=0; i<len; i++) {
- uint8_t c = ((uint8_t *)buf)[offset+i];
-
- if (c <= 0x3f) {
- if (zx_putchar(c, 0) < 0) {
- goto error_io;
- }
- } else if (c >= 0xa0 && c <= 0xbf) {
- if (zx_putchar(c - 0xa0, tty) < 0) {
- goto error_io;
- }
- } else {
- if (putchar('.') < 0) {
- goto error_io;
- }
- }
- }
-
- if (putchar('\n') < 0) {
- goto error_io;
- }
-
- return fflush(stdout);
-
-error_io:
- return -1;
-}
-
-static ssize_t zx_dump_hex(int fd) {
- 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+=ZX_HEXDUMP_STRIDE_LINE) {
- size_t left = len - i,
- linesz = left < ZX_HEXDUMP_STRIDE_LINE? left: ZX_HEXDUMP_STRIDE_LINE;
-
- if (hexdump_line(offset, buf, linesz, tty) < 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;
-}
-
-static inline int zx_print(uint8_t c, int tty) {
- if (ZX_CHAR_LOW(c)) {
- return zx_putchar(c, 0);
- } else if (ZX_CHAR_TOKEN_LOW(c)) {
- return fputs(zx_tokens_low[c - ZX_CHAR_TOKEN_LOW_START], stdout);
- } else if (ZX_CHAR_NEWLINE(c)) {
- return putchar('\n');
- } else if (ZX_CHAR_INVERSE(c)) {
- return zx_putchar(c - ZX_CHAR_INVERSE_START, tty);
- } else if (ZX_CHAR_TOKEN_HIGH(c)) {
- return fputs(zx_tokens[c - ZX_CHAR_TOKEN_HIGH_START], stdout);
- }
-
- return 0;
-}
-
-static inline enum zx_basic_token_type zx_basic_token_type_get(uint8_t b) {
- if (ZX_CHAR_LOW(b)) {
- uint32_t codepoint = zx_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 (ZX_CHAR_INVERSE(b)) {
- return zx_basic_token_type_get(b - ZX_CHAR_INVERSE_START);
- } else if (ZX_CHAR_TOKEN_LOW(b)) {
- return ZX_BASIC_TOKEN_WORD;
- } else if (ZX_CHAR_TOKEN_HIGH(b)) {
- char *token = zx_tokens[b-ZX_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 ssize_t zx_dump_basic(int fd) {
- 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;
- }
-
- 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 (ZX_CHAR_TOKEN_INTEGRAL(c) || ZX_CHAR_TOKEN_FLOAT(c)) {
- i += 5;
- }
-
- if (space && putchar(' ') < 0) {
- goto error_io;
- }
-
- if (zx_print(c, tty) < 0) {
- goto error_io;
- }
-
- last = c;
- }
-
- if (!ZX_CHAR_NEWLINE(((uint8_t *)buf)[i-1])) {
- if (putchar('\n') < 0) {
- goto error_io;
- }
- }
- }
-
- free(buf);
-
- return total;
-
-error_io:
- free(buf);
-
-error_malloc:
-error_fstat:
- return -1;
-}
-
-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);
-}
-
-#define ZX_DUMP_FLAGS_NONE 0
-#define ZX_DUMP_FLAGS_BASIC (1 << 0)
-
-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 (zx_dump_basic(fd) < 0) {
- fprintf(stderr, "%s: %s: %s\n",
- argv[0],
- (argn == 1)? argv[optind]: "(stdin)",
- strerror(errno));
-
- goto error_dump;
- }
- } else {
- if (zx_dump_hex(fd) < 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;
-}
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;
+}