Disassemble To Printer Or Disk For Atari
Mark Chasm
If you've been wondering how to take disassemblies of machine language and either store them on a disk or print them out — here's your answer. These programs will make the Atari Assembler/Editor cartridge an even more useful programming tool.
One of the best ways to learn assembly language programming is to look at the ways professional programmers have written complex programs and to study and learn their techniques. Unfortunately, when we buy programs that were originally written in assembly language, they have already been assembled (translated) into machine language. To make sense out of this code, we must be able to disassemble (retranslate) it back into assembly language.
Fortunately, those of us who have the Atari Assembler/Editor cartridge know that Atari has the built-in ability to disassemble machine language back into assembly language, using the L option in the DEBUG mode. This option will convert the information stored in any section of memory into assembly language. This conversion is then displayed on your screen, so that you can look at any part of any machine language program in assembly language.
That's the good news. The bad news is: 1) you can look at only about 20 lines of assembly language code at a time, and 2) you have no way of storing the assembly language version for studying later, except to copy the program from the screen with pencil and paper. This article shows you how to divert the output either to a printer or to your disk and provides programs to implement these options.
Output To A Printer
In your Atari, the Input/Output Control Block (IOCB) #0 is the default IOCB for all output operations, and it is the screen editor. The output from the Assembler/Editor cartridge (and all other cartridges) is routed through this IOCB to direct the output to the screen. In your Atari, all output to any device is handled through the handler table, which is simply a series of pointers to places in the Operating System (OS), where the directions for how the Atari is to deal with each device can be found. Actually, these pointers are directed at address-1 for each set of directions. Therefore, to redirect the output of the Assembler/Editor cartridge to a printer, all we have to do is to change the pointer so that it points at the address-1 of the printer instructions in the OS.
Let's try to disassemble the first part of DOS and get a printout of the assembly language code. I'll assume that you have your system booted up with DOS 2, that the Assembler/Editor cartridge is in place in your computer, and that your printer (and interface module, if you need it) is on. First, go into DEBUG mode by typing BUG, followed by a RETURN. Your screen should say DEBUG. Next, type C346<A6,EE and another RETURN. This changes memory locations $0346 and $0347 to $A6 and $EE, respectively. By the way, the directions for dealing with a printer begin in memory location $EEA7. Remember, we point to address-1.
All output is now directed to your printer. If at this point you type L0700,0756 and hit RETURN, your printer should produce the first part of DOS 2 in assembly language, exactly as it appears in Program 1. The format of this listing is discussed in detail below.
Remember: All output is now directed to your printer. To get back to the screen, you'll have to change the pointer back to where it was. You'll need to type C346<A3,F6 and hit RETURN. Now you can see what you're doing, so you can go ahead with normal output.
To A Disk File
Directing the disassembled listing of some portion of memory to your disk drive is a bit more complicated and requires a brief program to handle housekeeping. This assembly language program is shown in Program 2, with the origin at $0600. Before we can direct the output to disk, we need to open a file on the disk. For the purposes of this discussion, we will open a file using IOCB #3, and we'll call the file D1:DISASSEM.
To do this, we first load the X register with #$30 (for IOCB #3), in line 110 of Program 2. Weil use this as an index into IOCB #3 throughout the program. Next, we store the command byte for the OPEN command, $03, into $0342, X in lines 120-130, and the command byte for the OPEN for WRITE command, $08, into $034A,X. Then we point to the name of the file we want to OPEN by storing the low and high bytes of the address of this string in $0344, X and $0345, X respectively, in lines 160-190. We can then OPEN the file by jumping to the CIO subroutine in line 200. The RTS in the next line returns control to your keyboard, so that you can handle the next steps manually.
The program that actually directs the output to this disk file begins on line 230 of Program 2, at $0620. We set the IOCB to #3 in line 230, and temporarily store the character being sent in the Y register in line 240. By setting the buffer size to zero in lines 250-270, we can pass one character at a time, from the accumulator, directly to the disk file. The command byte for PUT CHARACTER is $0B (lines 280-290). In line 300, we retrieve the character being sent, and we send it to the disk by calling the CIO routine in line 310. Line 320 returns control to the Assembler/Editor cartridge to fetch the next byte of the disassembly. As each character is passed to the disk in turn, the OS takes care of keeping track of how the disk file is to be organized and saves us a lot of work in the process.
It is important, once a file is OPENed for writing, that it be closed, or you are likely to lose the last sections of information you wanted to write to the disk. Since your keyboard is not in control during the disassembly, you need to close the file by hitting BREAK when the drive has stopped, indicating that the file has been written.
To use these programs, type them in exactly as shown in Program 2, and LIST them to your disk for safekeeping. Then type ASM and RETURN to assemble these programs. After this is completed, type BUG to enter DEBUG mode, and then G0600 to run the first program. You should hear the disk drive start as the file is OPENed. Next, type C346 < IF, 06 and RETURN. This directs the output to our routine to send one character at a time to the disk (remember: address-1). Then type L0700,0756 and RETURN. This will disassemble the first part of DOS 2 to your disk. When the drive stops, hit the BREAK key to close the file. SYSTEM RESET will now set everything back the way it was before we started our tampering.
Reformatting The Output File
One last problem remains. If we refer to Program 1, we can see that the first set of numbers on each line represents the hexadecimal address of each instruction. The second set of numbers is the machine language nomenclature for the instruction, and the instruction mnemonic itself is the next set. Following the instruction is the operand. In a typical assembly language listing, two more fields would be present. Between the machine language instruction and the mnemonic would be a line number, and frequently following the operand is a comments field. The problem that remains is that the output from the L option of the Assembler/Editor cartridge is not in a form that can be used as input for the Assembler itself. That is, the disk file D1:DISASSEM that we have created cannot be used as source code – yet.
Program 3 is a BASIC program which will reorganize and reformat D1:DISASSEM into another file, D1:OUTPUT, which can be used as source code for the cartridge. Line 100 sets the first line number for the OUTPUT file to 1000, and lines 110-160 dimension the input, output, and blank strings, set the blank string equal to all blanks, and erase anything in the other two strings. Lines 170 and 180 open DISASSEM for input, and OUTPUT for output.
We are going to set up a loop, from lines 230-330, which will work its way through all of DISASSEM; so, in line 190, we set a trap to close the files when we get to the end. Lines 200 and 210 discard the first two lines of DISASSEM, a blank line and the word DEBUG on the second line (see Program 1), which are put in by the cartridge. Line 220 blanks out the input string, and line 240 reads the first line of DISASSEM into the input string, INTAKE$.
We would like our output to start with a line number, so line 240 handles this for us. Line 250 leaves the next two spaces blank, because that's how the Assembler/Editor expects to get its source code. Line 260 checks to see if the cartridge understood that particular byte. If it can't interpret a byte, the cartridge puts ??? into the mnemonic field. This program stores the contents of that location in memory as a .BYTE mnemonic. Line 270 fills in the remainder of the line, and line 280 puts in a comments field, with the contents as the memory location of that particular instruction, as an aid in understanding the output. Line 290 puts the output to the disk file, lines 300 and 310 rezero OUT$ and INTAKE$, line 320 increments the line number by two, and line 330 loops back to get the next line for reformatting. Line 340 closes the files and ends the program.
Program 4, the OUTPUT file structure for the first part of DOS 2, requires a few comments. The beginning of DOS is used to store certain variables. For that reason, the first part of the output file (lines 1000-1030) looks slightly strange. However, it should be noted that all information is there, and in a form which is understandable to the Assembler. That is, this file can be used as source code. Some thought must be given, however, to the interpretation of this code, as with all disassembled machine language programs.
Two final comments: First, if you want to disassemble all of DOS 2, do it in two steps; although the programs described in this article can handle all of DOS, the Assembler/Editor cartridge cannot accept an input file that large. The source code for DOS 2 using these programs is more than 300 sectors long! Second, all references to addresses in the OUTPUT file are absolute. Therefore, you will not be able to relocate this program with a different origin unless you substitute labels for all of the absolute addresses. However, you will be able to experiment with changes to DOS, or any other machine language program, if you're careful about the specific addresses in your disassembled source code.
If you are specifically interested in modifying or experimenting with DOS 2,1 highly recommend the recent book by Bill Wilkinson, Inside Atari DOS, published by COMPUTE! Books. The documented source code and detailed explanations of the various subroutines within DOS make this an invaluable resource for anyone attempting to change or understand DOS. There are also some very interesting suggestions for modifications to DOS, which should be reasonably simple to implement now that you have a way to obtain the source code.