So to decompile the Dealer Demo, we need to start by peeking at the boot sectors to see how it starts.
000010: SECTOR: 001: FILE:
0 ff 01 80 04 c0 e4 a9 f4-d0 06 00 0d 01 00 80 00 ................
10 85 f0 a9 52 8d 02 03 ad-8c 04 8d 0a 03 ad 8d 04 ...R............
The first six bytes of the boot sector describes the boot sequence. The first byte is a flag byte (0xff) and the second byte is the number of sectors to load (DBSECT), in this case one. The third and fourth by
The Internet Archive is a treasure for old computer enthusiasts. Archivists have been uploading old computer newsletters there for years which provide a vivid view of what it was like to be an Atari enthusiast when Atari was still a going concern. Thanks to the archive, I've been able to read such excellent old newsletters as the MACE Journal, SLCC Journal, Current Notes, and dozens of others.
The archive itself though has some quirky behaviors. When you upload content, it examine
I was reading Dungeon Hacks, by David L. Craddock, the other day and found a short reference to the Atari 8-bit in the chapter on the first Rogue-like, Beneath Apple Manor:
So there's another virtual machine hiding inside this game, so I had to find it and figure out how it works.
The first step of course was to download the xex file from www.atarimania.com and hook up a my disassembler to the image with appropriate offsets. The image has a number of parts:
OFFSET: 0006
As originally conceived, Atari computers were going to be a platform where the technical secrets were held closely, allowing Atari to reap easy profits by providing software that third parties would be hard to match. At least, that was the theory. But the technical secrets were not going to remain secret forever, and as Atari was slow to provide the software to drive sales of the computers, it made more and more sense to abandon that strategy and open the platform to more developers. Atari event
I started this blog by fixing up a relatively obscure Atari title (The ABC of CPR). I could fix it because there were several different archives containing purportedly the same data, and since the most common problem with old disks is a bad sector which translates to an empty/dropped sector in an ATR archive, we could see the blank sectors and conclude fixes were needed. So how do we find some (ideally all) disks with dropped sectors in a large archive. Well, it falls out of trying to build
We continue with decompiling Dealer Demo at $175D, seeing -TRAILING, (.") (PDOTQ), and then a handful of words leading to the word ERROR which decompiles incorrectly.
Looking closely, we see that the .WORD PDOTQ precedes strings in the listing, which are represented as a count, followed by the string contents. The code to decompile such a string manually is easy to implement, since we built most of the infrastructure already to decompile Forth name fields, namely:
sub cstr_buf {
my
Recently I've been reading old ACE Eugene newsletters, as Kay Savetz posted a whole slew of them to archive.org. When I read them, I take the opportunity to clean up the OCR of the issue as well so I can search it in the future. For program listings, the OCR's tend to be terrible, so I prefer to find the listing on a PD disk if I can, and insert that into the OCR listing instead.
So I was reading the November 1983, issue, which has a copy of Stan Ockers "Cannibals and Missionaries"
In past blog entries I went through the steps developing an 8-bit ATR disk parser so that we could examine and repair damaged disks. Today, I will walk through the creation of a new disk parser, this time for Atari ST disks. The motivation in this case requires some explanation:
My first computer was an Atari 800, and when I went to university I brought an Atari 130XE and Indus GT drive with me. The school had more sophisticated machines (PC's, Macs, various Unix workstations), so
Continuing our exploration of Atari 8-bit languages and virtual machines, we come to APX20166 Deep Blue C. Reverse engineering here is fairly simple as the entire source code for the runtime and compiler was published as an additional product, APX20179 Deep Blue Secrets. I'm going to only use that as a reference and proceed to produce a listing of the runtime via disassembly for a few reasons. First, it's always nice to have both the source code and the actual assembly output together. Secon
The last blog entry introduced the tools I'm using to explore the Pascal runtime, and included a preliminary (i.e. rough) disassembly. Now we'll start refining that disassembly and start discussing more of the opcodes.
Firstly, the last listing was erroneous around $B959 to $B991. There are strings there I somehow missed when spot checking the disassembly, so I've fixed up that part of the disassembly. There were also a couple of missing $9B's as well after strings, and the p-code disassemb
In the last couple of posts we explored some of the APX Pascal architecture, showing bits of disassembly of the runtime, but I neglected to include the tools I used to extract those bits. This post aims to remedy that, and produce a first draft listing of the APX Pascal runtime.
The runtime disassembly was done using similar perl code as I developed in the Dealer Demo Forth deconstruction blog posts last year, so refer to those if you want a discussion on how this disassembler works. There'
In the last post, we worked through layers of the APX Pascal runtime to find the main interpreter loop, which in fact resides entirely in page zero. In this post, we're going to dig into some of the opcodes to get a flavor for the runtime implementation.
As we discussed last time, the each opcode is represented by a JMP value in a 512-byte table that is copied into $1D00 when the runtime starts. If you peruse though the table, the most common JMP target is $B9B5, in 81 entries. This is the
Bill Lange has been blogging about Atari Pascal since early February at https://insideataripascal.blogspot.com/, so here's my own small contribution after spending an afternoon poking around in APX Pascal and looking for the core interpreter.
If we look at the PASCAL runtime on the APX Pascal disk, it's a simple enough image. It loads itself from disk to $3300-$59ff and then starts running from $3300. So what does that initial bootstrap code do? Here's the preamble:
3300: A2 00
We've now reached a compact bit of code in the Dealer Demo that provides an assembler to Forth. And the assembler in Forth is a thing of beauty indeed. Written by Bill Ragsdale (as was most of the Forth kernel), it provides an assembler that can produce surprisingly readable code without the use of labels. In essence it implements high-level assembler:
Recall TRAVERSE. In traditional assembler, it was written as:
154A: 4C 15 TRAV .WORD *+2
154C: B4 00 LDY 0,X
154E:
OK, let's finish off the kernel of Dealer Demo.
The next chunk is the COLD start routine, starting at $1C84, which is implemented as a primitive. Following that are a variety of standard Forth words dealing with mathematical operations and buffer and screen management. This leads up to TCIOV, a Dealer Demo primitive to invoke CIO routines. After the implementations of EMIT and KEY (labeled XEMIT and XKEY), we add a primitive BLKIO for invoking the disk handler, some SIO DCB constants (DCDNO
In my blog entry on the ABC of CPR, I found issues with all the (known to me) images of the disks by cross comparing the contents of the disk images. In this entry, I walk through how to generate such dumps using a utility in the Perl language.
Perl has fallen in popularity over the years, which is a bit sad, as it still has a lot to recommend it. It still probably has the best text handling facilities of any language, can handle binary data with ease, and has an simple object model that I'
We pick up decompiling again at $148C, where we find 1+ (ONEP). It is followed by 2+ (TWOP), HERE, ALLOT, , (COMMA), C, (CCOMM), - (SUB), = (EQUAL), > (GREAT), ROT, SPACE and -DUP (DDUP). All of these are identical to the fig-Forth listing, which implements these as colon-definitions, which is expected. Although SUB, EQUAL and ROT are sometimes rewritten as primitives to speed up Forth (cf. val-Forth), optimizing these was not done in this Forth.
TRAVERSE is the next word in the listing
Thus far in disassembling the Dealer Demo, we've run across primitive definitions that have a PFA of .WORD *+2 and then some 6502 assembly code. There were two exceptions I didn't draw attention to at the time, the word 'I' and the word 'DROP'. DROP's PFA was simply ".WORD POP", in other words it took advantage of the fact the POP falls into NEXT, so the semantics of DROP could use that routine directly. I's PFA was similarly ".WORD R+2", in other words it had the same implementation as the word
We resume our disassembly of the Dealer Demo and find the next four Forth words, 0= and 0<, and then unexpectedly U< and <.
-def WORD NFA PFA same as fig-Forth?
10A7 0= L605 ZEQU Yes
10BC 0< L619 ZLESS Yes
10CE U< L1246 ULESS No
10EB < L1254 LESS No
I say unexpectedly, because U< and < in fig-Forth are defined much later, and U< is not a primitive, it is a combination of '-' (subtraction) and 0<. That said, t
In the last post I explained how the NEXT routine uses IP and W to JMP from word to word, but otherwise neglected to explain the Forth environment. Let's remedy that.
Forth contains two stacks, a data stack and a return stack.
The 6502 stack pointer indexes the return stack in page one. Saving and restoring the interpreter pointer is done here when Forth 'subroutines' are entered and exited, in a fashion analogous to JSR and RTS for the 6502. The routine SEMIS which we'll disassemble s
So we spent the last two posts working on tooling to reverse engineer the Dealer Demo, and got the bootloader disassembled for study. Now we can start disassembling the Forth kernel. Let's start with "-dis d00 8":
0D00: EA ORIG NOP
0D01: 4C 8D 1C JMP $1C8D
0D04: EA NOP
0D05: 4C A1 1C JMP $1CA1
Looking at the fig-Forth listing, this looks like the start of the kernel. 1C8D will be COLD+2, the cold start routine, and 1CA1 will be WARM, the warm
The last post walked through how to implement the basics of a disassembler, but we omitted several features to simplify the initial presentation. So let's fill in the omissions.
The main omission is the lack of a symbol table. I included a $names hash to hold the symbols and used it where needed, but there was no code to populate it. So let's fix that.
sub open_lst {
open my $fh, '<', 'dealerdemo.lst' or die;
$fh;
}
sub set_name {
my ($label, $val, $type) = @_;
return if $
The Suburban Chicago Atarians (S.C.A.T.) is represented on the Pooldisk with a couple hundred disks from their public domain library. As with much of the Pooldisk, there is some identifiable bitrot in a small number of the disks. In particular, checking the file integrity found four disks with simple single empty sector in the chain.
039.atr: Sector 217 empty (CREAPCAV.BAS). I found this game also on Thumpnugget's ANTIC disk archive, so we'll use that to fix it.
056.atr: Se
Today's blog entry is going to be a bit dull, since it's just all the easy to fix Bellcom disks. No new Perl code is needed, these can all be fixed with the techniques described in past entries. In fact all of them involve just patching a single sector from another disk. The hardest part was finding the disks with equivalent content, but I've written before on how binary grep for content.
Disk 670: Sector 113 empty, damaging WRITER.OBJ (Antic Writer). Patch sector from ANTIC\87_JUL.ATR, se
In the last blog entry, I presented a little bit of Perl code to hex dump disk contents, and promised to extend that to allow disk patching. So here we go.
sub write_file {
my ($file, $buff) = @_;
open my $fhw, '>', $file or return;
binmode $fhw;
print $fhw $buff;
close $fhw;
}
The first routine we'll need is something to write a file. Here's a completely generic routine that will write a buffer to a file. So our strategy will be read_file, change the buffer, and then inv