aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--a8xd.1182
-rw-r--r--a8xd.c172
-rw-r--r--a8xd.rst108
4 files changed, 466 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 4afaa47..47cf854 100644
--- a/Makefile
+++ b/Makefile
@@ -16,9 +16,9 @@ CC=gcc
CFLAGS=-Wall $(COPT) -ansi -D_GNU_SOURCE -DVERSION=\"$(VERSION)\"
# BINS and SCRIPTS go in $BINDIR, DOCS go in $DOCDIR
-BINS=a8eol atr2xfd atrsize axe blob2c blob2xex cart2xex cxrefbas dumpbas fenders protbas renumbas rom2cart unmac65 unprotbas vxrefbas xex1to2 xexamine xexcat xexsplit xfd2atr listbas a8cat
+BINS=a8eol atr2xfd atrsize axe blob2c blob2xex cart2xex cxrefbas dumpbas fenders protbas renumbas rom2cart unmac65 unprotbas vxrefbas xex1to2 xexamine xexcat xexsplit xfd2atr listbas a8cat a8xd
SCRIPTS=dasm2atasm a8utf8
-MANS=a8eol.1 xfd2atr.1 atr2xfd.1 blob2c.1 cart2xex.1 fenders.1 xexsplit.1 xexcat.1 atrsize.1 rom2cart.1 unmac65.1 axe.1 dasm2atasm.1 a8utf8.1 blob2xex.1 xexamine.1 xex1to2.1 unprotbas.1 protbas.1 renumbas.1 dumpbas.1 vxrefbas.1 cxrefbas.1 listbas.1 a8cat.1
+MANS=a8eol.1 xfd2atr.1 atr2xfd.1 blob2c.1 cart2xex.1 fenders.1 xexsplit.1 xexcat.1 atrsize.1 rom2cart.1 unmac65.1 axe.1 dasm2atasm.1 a8utf8.1 blob2xex.1 xexamine.1 xex1to2.1 unprotbas.1 protbas.1 renumbas.1 dumpbas.1 vxrefbas.1 cxrefbas.1 listbas.1 a8cat.1 a8xd.1
MAN5S=xex.5
MAN7S=atascii.7
DOCS=README.txt equates.inc *.dasm LICENSE ksiders/atr.txt
@@ -72,6 +72,8 @@ atables.o: atables.c atables.h
a8cat: a8cat.c atables.o wtable.o
+a8xd: a8xd.c atables.o
+
subdirs:
for dir in $(SUBDIRS); do make -C $$dir COPT="$(COPT)"; done
diff --git a/a8xd.1 b/a8xd.1
new file mode 100644
index 0000000..0e73cd6
--- /dev/null
+++ b/a8xd.1
@@ -0,0 +1,182 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "A8XD" 1 "2024-06-29" "0.2.1" "Urchlay's Atari 8-bit Tools"
+.SH NAME
+a8xd \- Atari ASCII aware hex dump
+.SH SYNOPSIS
+.sp
+\fIa8xd\fP [\fB\-i\fP] [\fB\-m\fP] [\fB\-v\fP] [\fIinfile\fP]
+.SH DESCRIPTION
+.sp
+\fBa8xd\fP is a hex dump utility, similar to \fBxxd\fP(1), but it
+understands and prints ATASCII characters rather than ASCII.
+.sp
+The ATASCII codes are converted to UTF\-8, in the same way as
+\fBa8cat\fP(1). Codes with the high bit set are displayed in inverse
+video, using ANSI/VT\-100 escape sequences.
+.sp
+By default, the output is colorized:
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.IP \(bu 2
+Normal characters are green. This includes alphanumerics, spaces, and
+punctuation.
+.IP \(bu 2
+Codes \fB$00\fP (null, or ATASCII heart) and \fB$9B\fP (EOL) are red.
+.IP \(bu 2
+Control characters are yellow. This includes codes \fB$01\fP to \fB$1F\fP,
+(control + letters, escape, punctuation, and arrow keys), \fB$60\fP, \fB$7B\fP, \fB$7D\fP
+(clear screen), \fB$7E\fP (backspace), and \fB$7F\fP (tab).
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+The colors and inverse rendering apply to both the ATASCII and hex bytes.
+.sp
+Without \fIinfile\fP, or if \fIinfile\fP is \fB\-\fP, \fBa8xd\fP reads from standard input.
+.SH OPTIONS
+.sp
+Options marked with \fB*\fP are not yet implemented.
+.INDENT 0.0
+.TP
+.B \-i
+Print XL/XE International Character Set conversions instead of ATASCII.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-l \fIlen\fP
+Stop after dumping \fIlen\fP bytes. \fIlen\fP may be given in decimal or hex (with
+leading \fI0x\fP or \fI$\fP).
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-m
+Monochrome mode. Disables color, but ATASCII characters with the high bit
+set are still displayed in inverse.
+.TP
+.B \-n
+\fB*\fP Narrow dump mode. Displays 8 bytes per line. Maximum line length is
+less than 40 columns. Use this if your terminal is less than 75 columns
+wide.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-o \fIoffset\fP
+\fB*\fP Add \fIoffset\fP to displayed file position. \fIoffset\fP can be given in decimal
+or hex (with leading \fI0x\fP or \fI$\fP).
+.TP
+.B \-s \fI[\-]seek\fP
+\fB*\fP Start at \fIseek\fP bytes. Without \fI\-\fP, this is an absolute offset. With \fI\-\fP,
+it\(aqs relative to the end of the file. The \fI\-\fP option won\(aqt work when
+reading from standard input. \fIseek\fP may be given in decimal or hex
+(with leading \fI0x\fP or \fI$\fP).
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-u
+\fB*\fP Use uppercase letters; the default is lowercase.
+.TP
+.B \-v
+Verbose. Currently, this option is accepted, but it doesn\(aqt do anything yet.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \fB\-\-\fP
+End of options; the nex argument is the filename. Use this if you\(aqre
+trying to work with files whose names begin with \fI\-\fP\&.
+.TP
+.B \fB\-h\fP, \fB\-\-help\fP
+Show built\-in help and exit.
+.TP
+.B \fB\-\-version\fP
+Show version number and exit.
+.UNINDENT
+.SH NOTES
+.sp
+\fBa8xd\fP supports a useful subset of \fBxxd\fP(1) options. The main things
+missing are:
+.INDENT 0.0
+.IP \(bu 2
+\fB\-r\fP (revert).
+.IP \(bu 2
+\fB\-include\fP (output as C include) and all options related to it.
+.IP \(bu 2
+\fB\-g\fP (grouping; \fBa8xd\fP always uses a group size of 1 byte).
+.IP \(bu 2
+\fB\-E\fP (EBCDIC mode).
+.IP \(bu 2
+\fB\-p\fP (PostScript/continuous dump).
+.IP \(bu 2
+\fB\-cols\fP (\fBa8xd\fP only supports 8 or 16 column dumps).
+.IP \(bu 2
+\fB\-b\fP (bits mode).
+.UNINDENT
+.SH COPYRIGHT
+.sp
+WTFPL. See \fI\%http://www.wtfpl.net/txt/copying/\fP for details.
+.SH AUTHOR
+.INDENT 0.0
+.IP B. 3
+Watson <\fI\%urchlay@slackware.uk\fP>; Urchlay on irc.libera.chat \fI##atari\fP\&.
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fBa8eol\fP(1),
+\fBa8utf8\fP(1),
+\fBatr2xfd\fP(1),
+\fBatrsize\fP(1),
+\fBaxe\fP(1),
+\fBblob2c\fP(1),
+\fBblob2xex\fP(1),
+\fBcart2xex\fP(1),
+\fBcxrefbas\fP(1),
+\fBdasm2atasm\fP(1),
+\fBdumpbas\fP(1),
+\fBf2toxex\fP(1),
+\fBfenders\fP(1),
+\fBlistbas\fP(1),
+\fBprotbas\fP(1),
+\fBrenumbas\fP(1),
+\fBrom2cart\fP(1),
+\fBunmac65\fP(1),
+\fBunprotbas\fP(1),
+\fBvxrefbas\fP(1),
+\fBxexamine\fP(1),
+\fBxexcat\fP(1),
+\fBxexsplit\fP(1),
+\fBxfd2atr\fP(1),
+\fBxex\fP(5),
+\fBatascii\fP(7).
+.sp
+Any good Atari 8\-bit book: \fIDe Re Atari\fP, \fIThe Atari BASIC Reference
+Manual\fP, the \fIOS Users\(aq Guide\fP, \fIMapping the Atari\fP, etc.
+.\" Generated by docutils manpage writer.
+.
diff --git a/a8xd.c b/a8xd.c
new file mode 100644
index 0000000..52a8092
--- /dev/null
+++ b/a8xd.c
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "atables.h"
+
+const char **table = ata2utf;
+const char *inverse_on = "\x1b[7m";
+const char *inverse_off = "\x1b[0m";
+
+int verbose = 0, color = 1;
+
+const char *self;
+
+void set_self(const char *argv0) {
+ char *p;
+
+ self = argv0;
+ p = strrchr(self, '/');
+ if(p) self = p + 1;
+}
+
+void die(const char *msg) {
+ fprintf(stderr, "%s: %s\n", self, msg);
+ exit(1);
+}
+
+void print_help(void) {
+ printf("Usage: %s [-v] [file ...]\n", self);
+}
+
+FILE *open_input(const char *file) {
+ FILE *input;
+
+ if(file[0] == '-' && file[1] == 0) {
+ if(verbose)
+ fprintf(stderr, "%s: reading from standard input.\n", self);
+ if(freopen(NULL, "rb", stdin)) {
+ input = stdin;
+ } else {
+ perror("(standard input)");
+ return NULL;
+ }
+ } else if(!(input = fopen(file, "rb"))) {
+ perror(file);
+ return NULL;
+ }
+
+ if(verbose)
+ fprintf(stderr, "%s: reading from file '%s'.\n", self, file);
+
+ return input;
+}
+
+const char *color_off = "\x1b[0m";
+
+char *get_color(unsigned char c) {
+ int color;
+ unsigned char c7 = c & 0x7f;
+ static char outbuf[32];
+
+ if(c == 0 || c == 0x9b) {
+ color = 1; /* red */
+ } else if(c7 < 32 || c7 == 0x60 || c7 == 0x7b || c7 == 0x7d || c7 == 0x7e || c7 == 0x7f) {
+ color = 3; /* yellow/orange */
+ } else {
+ color = 2;
+ }
+
+ if(color) {
+ sprintf(outbuf, "\x1b[0;3%dm", color);
+ if(c & 0x80) strcat(outbuf, inverse_on);
+ } else {
+ outbuf[0] = '\0';
+ }
+
+ return outbuf;
+}
+
+void dump_line(const unsigned char *buf, int len) {
+ char hex[1024], asc[1024];
+ static int filepos = 0;
+ int hpos = 0, apos = 0, count = len;
+
+ memset(hex, 0, sizeof(hex));
+ memset(asc, 0, sizeof(asc));
+
+ printf("%04x: ", filepos);
+ while(len) {
+ if(color) hpos += sprintf(hex + hpos, "%s", get_color(*buf));
+ hpos += sprintf(hex + hpos, "%02x", *buf);
+ if(color) hpos += sprintf(hex + hpos, "%s", color_off);
+ hex[hpos++] = ' ';
+ if(count - len == 7) hex[hpos++] = ' ';
+
+ if(*buf & 0x80) apos += sprintf(asc + apos, "%s", inverse_on);
+ if(color) apos += sprintf(asc + apos, "%s", get_color(*buf));
+ apos += sprintf(asc + apos, "%s", table[*buf & 0x7f]);
+ if(color || *buf & 0x80) apos += sprintf(asc + apos, "%s", color_off);
+
+ filepos++;
+ buf++;
+ len--;
+ }
+ printf("%s", hex);
+
+ /* what shall we use to fill the empty spaces? */
+ if(count < 8) putchar(' ');
+ for(; count < 16; count++) fputs(" ", stdout);
+
+ printf(" %s\n", asc);
+}
+
+int a8xd(const char *file) {
+ FILE *input;
+ int c, len = 0;
+ unsigned char buf[16];
+
+ if( !(input = open_input(file)) )
+ return 1;
+
+ while( (c = fgetc(input)) != EOF ) {
+ if(len && (len % 16 == 0)) {
+ dump_line(buf, len);
+ len = 0;
+ }
+ buf[len++] = c;
+ }
+
+ if(len) dump_line(buf, len);
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int opt, result = 0;
+
+ set_self(argv[0]);
+
+ if(argc > 1 && strcmp(argv[1], "--help") == 0) {
+ print_help();
+ exit(0);
+ }
+
+ if(argc > 1 && strcmp(argv[1], "--version") == 0) {
+ printf("%s %s\n", self, VERSION);
+ exit(0);
+ }
+
+ while( (opt = getopt(argc, argv, "vim")) != -1) {
+ switch(opt) {
+ case 'v': verbose = 1; break;
+ case 'i': table = ics2utf; break;
+ case 'm': color = 0; break;
+ default: print_help(); exit(1); break;
+ }
+ }
+
+ if(optind >= argc) {
+ result = a8xd("-");
+ } else {
+ while(optind < argc) {
+ result += a8xd(argv[optind]);
+ optind++;
+ }
+ }
+ exit(result);
+}
diff --git a/a8xd.rst b/a8xd.rst
new file mode 100644
index 0000000..9d6d00d
--- /dev/null
+++ b/a8xd.rst
@@ -0,0 +1,108 @@
+====
+a8xd
+====
+
+--------------------------
+Atari ASCII aware hex dump
+--------------------------
+
+.. include:: manhdr.rst
+
+SYNOPSIS
+========
+
+*a8xd* [**-i**] [**-m**] [**-v**] [*infile*]
+
+DESCRIPTION
+===========
+
+**a8xd** is a hex dump utility, similar to **xxd**\(1), but it
+understands and prints ATASCII characters rather than ASCII.
+
+The ATASCII codes are converted to UTF-8, in the same way as
+**a8cat**\(1). Codes with the high bit set are displayed in inverse
+video, using ANSI/VT-100 escape sequences.
+
+By default, the output is colorized:
+
+ - Normal characters are green. This includes alphanumerics, spaces, and
+ punctuation.
+
+ - Codes **$00** (null, or ATASCII heart) and **$9B** (EOL) are red.
+
+ - Control characters are yellow. This includes codes **$01** to **$1F**,
+ (control + letters, escape, punctuation, and arrow keys), **$60**, **$7B**, **$7D**
+ (clear screen), **$7E** (backspace), and **$7F** (tab).
+
+The colors and inverse rendering apply to both the ATASCII and hex bytes.
+
+Without *infile*, or if *infile* is **-**, **a8xd** reads from standard input.
+
+OPTIONS
+=======
+
+Options marked with **\*** are not yet implemented.
+
+-i
+ Print XL/XE International Character Set conversions instead of ATASCII.
+
+-l *len*
+ Stop after dumping *len* bytes. *len* may be given in decimal or hex (with
+ leading *0x* or *$*).
+
+-m
+ Monochrome mode. Disables color, but ATASCII characters with the high bit
+ set are still displayed in inverse.
+
+-n
+ **\*** Narrow dump mode. Displays 8 bytes per line. Maximum line length is
+ less than 40 columns. Use this if your terminal is less than 75 columns
+ wide.
+
+-o *offset*
+ **\*** Add *offset* to displayed file position. *offset* can be given in decimal
+ or hex (with leading *0x* or *$*).
+
+-s *[-]seek*
+ **\*** Start at *seek* bytes. Without *-*, this is an absolute offset. With *-*,
+ it's relative to the end of the file. The *-* option won't work when
+ reading from standard input. *seek* may be given in decimal or hex
+ (with leading *0x* or *$*).
+
+-u
+ **\*** Use uppercase letters; the default is lowercase.
+
+-v
+ Verbose. Currently, this option is accepted, but it doesn't do anything yet.
+
+**--**
+ End of options; the nex argument is the filename. Use this if you're
+ trying to work with files whose names begin with *-*.
+
+**-h**, **--help**
+ Show built-in help and exit.
+
+**--version**
+ Show version number and exit.
+
+NOTES
+=====
+
+**a8xd** supports a useful subset of **xxd**\(1) options. The main things
+missing are:
+
+- **-r** (revert).
+
+- **-include** (output as C include) and all options related to it.
+
+- **-g** (grouping; **a8xd** always uses a group size of 1 byte).
+
+- **-E** (EBCDIC mode).
+
+- **-p** (PostScript/continuous dump).
+
+- **-cols** (**a8xd** only supports 8 or 16 column dumps).
+
+- **-b** (bits mode).
+
+.. include:: manftr.rst