#include #include #include #include #include #include #include #include #include #include #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 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= 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= 'A' && codepoint <= 'Z') || (codepoint >= '0' && codepoint <= '9')) { return ZX_BASIC_TOKEN_ALNUM; } 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