DXA(1)

πŸ“¦ version 0.1.5, in nixos 26.05, nixpkgs unstable

welcome to DXA(1) from the General Commands Manual β€” yes bestie, it's the disassembler page, and we're gonna get through every single bit of it together 🫢


✨ NAME ✨

dxa β€” a 6502/R65C02 disassembler. that's the whole vibe. πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

dxa [OPTION]... FILE

πŸ’œ DESCRIPTION πŸ’œ

okay so dxa is the semi-official disassembler option for the xa(1) package β€” it's a weakly patched version of Marko MΓ€kelΓ€'s d65 disassembler that generates output similar to the de facto coding conventions used for xa(1). the package is designed to intelligently(?) scan arbitrary code and (with hints) can identify the difference between data and valid machine code, generating a sane looking, "perfect" disassembly with data and code portions. honestly? iconic. πŸ‘‘

and "perfect" here means... you can take what dxa spits out and feed it right back into xa(1) and get the exact same object file you started with β€” even if sometimes dxa can't identify everything correctly, no cap. with a few extra options you can tease and twist the output to generate something not quite so parseable, or even more like true assembler source. slay πŸ’…


🌸 OPTIONS 🌸

heads up bestie: for historical and compatibility reasons, the long options (--) only exist if dxa were compiled with LONG_OPTIONS enabled in options.h. iykyk 🫢
  • --datablock xxxx-yyyy, -b xxxx-yyyy β€” defines the memory range xxxx to yyyy (hexadecimal, inclusive) to be a data block. and you can get even more specific with it:
    • if the range is preceded by ! (an exclamation point), like !c000-cfff, then it is further defined to be a data block with no vectors in it either.
    • if the range is preceded by ? (a question mark), then it is further defined to be a data block that is completely unused and therefore no valid routine may contain instructions whose parameter lie in this range. useful for providing enhanced protection against misinterpreting data to be program code β€” but be careful, or some code may be listed as data. for instance, the Commodore 64 firmware uses the base address $CFFF when initializing the video chip, and the BASIC interpreter uses the addresses $9FEA and $9FEB while loading the instruction pointers. on top of that, there are a number of BIT instructions used for skipping the next instruction. thus, you must allow addresses like $1A9, $2A9 and so on. 😭
  • --datablocks filename, -B filename β€” reads data blocks from file filename as if they had been specified on the command line, one per line (such as xxxx-yyyy, ?xxxx-yyyy, etc.). πŸ“„
  • --labels filename, -l filename β€” causes label names to be read from file filename. this file format is the same as the labelfile/symbol table file generated by xa(1) with the -l option. and yes, -l was chosen on purpose for consistency with xa(1). πŸ’…
  • --routine xxxx, -r xxxx β€” specifies an address (in hexadecimal) that is declared to be a valid routine. it is strongly recommended that you specify the initial execution address as a routine. for example, for a Commodore 64 binary with a BASIC header that performs SYS 2064, specify -r0810 so that disassembly starts at that location (or use the -U option, which can automatically do this for you). note that specifying this manually may have interactions with datablock detection (-d). 🫢
  • --routines filename, -R filename β€” causes a list of routines to be read from file filename, one per line, as if they had been specified on the command line. πŸ“‹
  • --addresses option, -a option β€” determines if and what kind of address information should be dumped with the disassembly, if any. heads up: this may make your output no longer intelligible to xa(1). the valid options are:
    • disabled β€” dump source only with no address information. this is the default.
    • enabled β€” write the current address at the beginning of each line.
    • dump β€” write the current address at the beginning of each line, along with a hexdump of the bytes for which the statement was generated.
  • --colon-newline, -N, --no-colon-newline, -n β€” a purely cosmetic option to determine how labels are emitted. many people, including myself, prefer a listing where the label is given, then a tab, then the code (-n). since this is my preference, it's the default. on the other hand, there are also many who prefer to have the label demarcated by a colon and a newline, and the code beginning indented on the next line. this is the way d65 used to do it, and is still supported with -N. πŸŽ€
  • --processor option, -p option β€” specify the instruction set. note that specifying an instruction set that permits and disassembles illegal and/or undocumented NMOS opcodes may make your output unintelligible to xa(1). only one may be specified. the valid options are:
    • standard-nmos6502 β€” only official opcodes will be recognized. this is the default.
    • r65c02 β€” opcodes specific to the Rockwell 65C02 (R65C02) will also be allowed.
    • all-nmos6502 β€” allows all 256 NMOS opcodes to be disassembled, whether documented or undocumented. ngl, instructions generated by this mode are not guaranteed to work on all NMOS 6502s.
    • rational-nmos6502 β€” only allows "rational" undocumented instructions. this excludes ANE, SHA, SHS, SHY, SHX, LXA and LAXS. this is a judgment call.
    • useful-nmos6502 β€” only allows "useful" undocumented instructions. this excludes ANE, SHA, SHS, SHY, SHX, LXA, LAXS, NOOP and STP. this is a judgment call.
    • traditional-nmos6502 β€” only allows the most widely accepted undocumented instructions based on combinations of ALU and RMW operations. this excludes ANE, SHA, SHS, SHY, SHX, LXA, LAXS, NOOP, STP, ARR, ASR, ANC, SBX and USBC. this is a judgment call.
  • --get-sa, -G, --no-get-sa xxxx, -g xxxx β€” enables or disables automatic starting address detection. if enabled (the default), dxa looks at the first two bytes as a 16-bit word in 6502 little-endian format and considers that to be the starting address for the object, discarding them without further interpretation. this is very useful for Commodore computers in particular. if your binary does not have a starting address, you must specify one using -g or --no-get-sa followed by a hexadecimal address. the starting address will then be encoded into the output using * =. ⭐
  • --no-word-sa, -q, --word-sa, -Q β€” only relevant if automatic starting address detection is enabled. if so, the default is to also emit the starting address as a .word pseudo-op before the starting address indicated with * = so that it will be regenerated on re-assembly (-Q). otherwise, if this option is disabled, the starting address word will not be re-emitted and will need to be tacked back on if the target requires it. if you specify an address with -g, then that address will be used here too. 🧚
  • --no-detect-basic, -u, --detect-basic, -U β€” if the starting address is recognized as a typical BASIC entry point (currently supported for Commodore computers), then dxa will attempt to see if a BASIC header is present, and if so, determine its length and mark the section as a completely dead datablock not eligible for further disassembly or referencing. if the first line is a construct such as 10 SYS 2061, then dxa will additionally parse the provided address and mark it as a valid routine if the address is within the boundaries of the disassembled file. note that although its heuristics are designed to be permissive, it may nevertheless misinterpret certain files with intentionally pathologic line link addresses, and unusual applications where the linked machine code is designed to actually modify the BASIC text may not disassemble correctly with this option. these are highly atypical situations, so this option will likely become the default in a future release. πŸ”₯
  • --verbose, -v β€” enables verbose output, which may or may not be useful in the same way that Schroedinger's Cat may or may not be dead. πŸ’€
  • --help, -?, -V β€” a quick summary of options. ✨

the following options control how program code is scanned and determined to be a valid (or invalid) portion of a putative routine. πŸ•΅οΈβ€β™€οΈ
  • --datablock-detection option, -d option β€” this controls how the program automatically detects data blocks for addresses where no previous hints are specified. only one method may be specified. the valid options are:
    • poor β€” as much as the object as possible will be listed as program code, even if there are illegal instructions present. this is the default.
    • strict β€” assumes that all declared routines call and execute only valid instructions. if any portion of code declared as a routine leads to an address block containing illegal opcodes, a consistency error will occur and disassembly will stop.
    • skip-scanning β€” program addresses that are not referenced by any routine will not be scanned for valid routines (thus data a priori).
  • --no-external-labels, -e, --external-labels, -E β€” controls whether labels should be generated for addresses outside of the program itself. the default is not to (i.e., leave the addresses absolute).
  • --address-tables option, -t option β€” controls detection of address tables/dispatch tables. the following options are available:
    • ignore β€” don't attempt to detect address tables.
    • detect-all β€” address tables referencing any label will be detected.
    • detect-internal β€” address tables with labels whose addresses lie within the program's address range will be detected. this is the default.
  • --no-suspect-jsr, -j, --suspect-jsr

…and that's exactly where this part of the page cuts off mid-option, bestie β€” the rest is waiting for you in part 2 🫢

  • -J β€” tells the disassembler that JSRs are always expected to return to the instruction right after them… or that they aren't. Either way, it changes how routines get parsed, so listen up. Example time: the Commodore 128 KERNAL has a routine called PRIMM that prints a null-terminated string sitting directly after the JSR instruction, and it comes back after the null byte. So if you're disassembling something like that, you pass -J to warn dxa that this is a possibility. Default vibe is "normal" JSRs only, i.e. -j. ✨
  • --no-one-byte-routines, -o, --one-byte-routines, -O β€” these let you permit or inhibit a single RTS, RTI or BRK instruction (or STP, if the instruction set has it enabled), or a conditional branch, from being auto-identified as a routine. Default is to inhibit it, ngl. Want a specific case to go the other way? That's what the -r option is for. πŸŽ€
  • --no-stupid-jumps, -m, --stupid-jumps, -M β€” these decide whether jumps or branches that point at the current address (think JMP *, BCC *) count as valid code or invalid code, depending on which one you pick. Heads up though: BVC * is always accepted, because the V flag can sometimes get toggled by an external hardware signal. Default is to treat them as invalid. πŸ¦‹
  • --no-allow-brk, -w, --allow-brk, -W β€” these control whether BRK (or STP, if the instruction set has it enabled) gets treated as a legit exit from a routine, just like RTS or RTI. Default is nope, it doesn't. πŸ’…
  • --no-suspect-branches, -c, --suspect-branches, -C β€” okay these are rarely needed, but they cover the case where a program is out here intentionally obfuscating its own code with branches that have weird destination addresses, like LDA #96:BEQ *-1. By default that'd be considered invalid and not treated as a routine (-c); pass -C and it gets accepted as valid instead. πŸ™ˆ

🌸 NOTES 🌸

So bestie, here's the tea: files longer than 64K β€” or files that, once you factor in the given starting address, extend past address $FFFF β€” get truncated and you'll see a warning pop up. Why? Because code at an address like that is, by definition, unpossible. At least until 65816 mode is supported. 😭

If you're trying to disassemble a file that gets streamed into memory in parts and is bigger than 64K, you're gonna have to block it into segments first and hand those segments to dxa one at a time. No shortcuts, sorry. 🫢


πŸ’€ BUGS/TO-DO πŸ’€

  • There are probably quite a few bugs yet to be found. (Iconic of her, honestly.)
  • 65816 opcodes are not (yet) supported.
  • There are a few options Marko created that aren't hooked up to anything β€” and they're not documented here, on purpose. Maybe they'll get finished later. πŸ‘€

⭐ SEE ALSO ⭐

xa(1), file65(1), ldo65(1), printcbm(1), reloc65(1), uncpk(1)


πŸ‘‘ AUTHOR πŸ‘‘

This manual page was written by Cameron Kaiser <ckaiser@floodgap.com>.

dxa is based on d65 0.2.1 by Marko MΓ€kelΓ€. Original package (C)1993, 1994, 2000 Marko MΓ€kelΓ€. Additional changes (C)2006-2022 Cameron Kaiser.

dxa is maintained independently. πŸ’œ


🌈 WEBSITE 🌈

http://www.floodgap.com/retrotech/xa/


That's the whole page, bestie β€” go disassemble something iconic. This one's dated 31 January 2019 and lives in the DXA(1) section, fr fr. πŸ“βœ¨

πŸ“¦ where this page comes from