diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 27 | ||||
-rw-r--r-- | src/basic.c | 163 | ||||
-rw-r--r-- | src/charset.c | 66 | ||||
-rw-r--r-- | src/hexdump.c | 109 | ||||
-rw-r--r-- | src/main.c | 99 |
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; +} |