Jump to content
  • entries
    45
  • comments
    10
  • views
    10,352

Dealer Demo, part 3: Dealing with symbols


Atari_Ace

527 views

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 $val < 10;
  $names->{$val} = $label;
  $names->{$val+1} = "$label+1" if $type != 1;
}

sub populate_names {
  my $fh = open_lst();
  while (<$fh>) {
    if (/^([0-9A-F]{4}): .{10}(\w+)\s+\.WORD/) {
      my ($label, $val) = ($2, hex $1);
      set_name($label, $val, 2);
    }
    elsif (/^([0-9A-F]{4}): .{10}(\w+)\s+(.*)/) {
      my ($label, $val) = ($2, hex $1);
      set_name($label, $val, 1);
    }
    elsif (/^ {5}=([0-9A-F]{4}) {6}(\w+)\s*=\s*(\S+)/) {
      my ($label, $val, $rest) = ($2, hex $1, $3);
      set_name($label, $val, 2);
    }
  }
}

This code says we can get our symbols from the file we are slowly building up, 'dealerdemo.lst'. It looks for assignments (e.g., ' =0000 FOO = $00'), and labels and put them into the name table, ignoring small values.

Now we need to hook this up.

sub main {
  populate_names();
  disasm(@_);
}

main(@ARGV);

And add some symbols for it to consume. Since the disassembly showed values in the low $03xx range, we add the SIO symbols to the top of our listing:

     =0301      DUNIT = $0301
     =0302      DCOMND = $0302
     =0303      DSTATS = $0303
     =0304      DBUFLO = $0304
     =0305      DBUFHI = $0305
     =0308      DBYTLO = $0308
     =0309      DBYTHI = $0309
     =030A      DAUX1 = $030A
     =030B      DAUX2 = $030B
                ;
     =E459      SIOV = $E459

And rerun the disassembler to start seeing symbols appear.

...
0490: 85 F0             STA $F0
0492: A9 52             LDA #$52
0494: 8D 02 03          STA DCOMND
0497: AD 8C 04          LDA $048C
049A: 8D 0A 03          STA DAUX1
049D: AD 8D 04          LDA $048D
04A0: 8D 0B 03          STA DAUX2
04A3: A9 01             LDA #1
04A5: 8D 01 03          STA DUNIT
...

Now in addition to disassembling, we also need to just plain dump data out. To do that first we need to direct read_img to specific areas rather than hard code it. Here's our initial replacement.

sub read_img {
  my ($addr, $size) = @_;  $addr = 0 if !defined $addr;
  $size = 0x30 if !defined $size;
  $addr = hex $addr;
  my ($buff, $offset) = ('', 0);
  $offset = 0x10 - 0x480 if $addr >= 0x480 && $addr < 0x500;
  $buff = read_file('../../../atr/dealerdemo.atr', $size, $offset + $addr) if $offset != 0;
  ($buff, $addr, length($buff));
}

So we translate addresses between 0x480 and 0x500 to offsets into the atr file, rather than hard coding the offset, address and size as in v1.

Next, we need to add code to dump data.

sub multi_buf {
  my ($buff, $addr, $size, $def) = @_;
  my $i = 0;
  for (;;) {
    my $count = $size - $i;
    $count = 3 if $count > 3;
    last if $count == 0;
    print sb($addr + $i, unpack "C*", substr($buff, $i, $count)), "$def\n";
    $def = '';
    $i += $count;
  }
}

sub get_label {
  my $label = $names->{$_[0]} || '';
  $label = '' if $label =~ /\+\d+$/;
  sprintf "%-8s", $label;
}

sub bytes {
  my $count = shift;
  my ($buff, $addr, $size) = read_img(@_);
  for (my $i = 0; $i + $count - 1 < $size; $i += $count) {
    my @vals = unpack "C*", substr($buff, $i, $count);
    my $string = sprintf "%s.BYTE ", get_label($addr + $i);
    $string .= join ',', map { sprintf "\$%02X", $_ } @vals;
    multi_buf(substr($buff, $i, $count), $addr + $i, $count, $string);
  }
}

sub words {
  my $count = shift;
  my ($buff, $addr, $size) = read_img(@_);
  $count *= 2;
  for (my $i = 0; $i + $count - 1 < $size; $i += $count) {
    my @vals = unpack "v*", substr($buff, $i, $count);
    my $string = sprintf "%s.WORD ", get_label($addr + $i);
    $string .= join ',', map { sprintf "\$%04X", $_ } @vals;
    multi_buf(substr($buff, $i, $count), $addr + $i, $count, $string);
  }
}

The routines bytes and words chunks up bytes and words into groups of $count, and uses multi_buf to output them in listing format. The routine get_label is an oversight from the last post, the disassembler should be able to put a label instead of 8 spaces before the opcode/directive if it has one handy. Since we didn't populate the symbol table in the last post, the code wasn't yet needed, but now we should replace this line in print_op:

  my $string = sprintf "%s%s%s\n", '        ', $def->{name}, $sval;
with this one:
  my $string = sprintf "%s%s%s\n", get_label($addr + $i), $def->{name}, $sval;
Now let's hook up all the pieces.
sub main {
  populate_names();
  my $opt = shift;
  if ($opt =~ /\-dis/) {
    disasm(@_);  }
  elsif ($opt =~ /^\-b\d+$/) {
    bytes(substr($opt, 2), @_);
  }
  elsif ($opt =~ /^\-w\d+$/) {
    words(substr($opt, 2), @_);
  }
}

main(@ARGV);

Now we're in a position to make sense of the boot loader. Since the first six bytes are 2 bytes and 2 words, we can use the arguments "-b1 480 2" and "-w1 482 4" to dump that data. The word at 0x484 is $E4C0, which is ERRTN, so let's add a declaration for that and change it to .WORD ERRTN in the listing. We can then disassemble the code at $0486, which does a load and then an immediate branch over the next six bytes. Those six bytes appear to be grouped as 3 words, $0D00, 1, and $80. Let's label them XDST, XSECT, and XSIZE, add ORIG = $0D00 for the first value and continue disassembly (the names for those locations will make sense in a minute). If we now disassemble using "-dis 0x490" we should see:

0490: 85 F0             STA $F0
0492: A9 52             LDA #$52
0494: 8D 02 03          STA DCOMND
0497: AD 8C 04          LDA XSECT
049A: 8D 0A 03          STA DAUX1
049D: AD 8D 04          LDA XSECT+1
04A0: 8D 0B 03          STA DAUX2
04A3: A9 01             LDA #1
...

We need to add branch labels at locations 0490 (XBOOT), 4C0 (XBOOT1) 04C8 (XBOOT2), 04D7 (XBOOT3). The last six bytes in the sector look like leftover data, so lets just list those as groups of three bytes using "-b3 4fa 6"

We can now study the code and see that it simply calls SIOV with the READ ($52) command $F4 times, starting the buffer at ORIG, and incrementing it by $80 each time. So it loads sectors 2 up into 0xd00 to 0x8700, so let's add the following to read_img.

  $offset = 0x90 - 0xd00 if $addr >= 0xd00 && $addr < 0x8700;

Having now doubled the size of tool, we now are in a position to start disassembling the Forth kernel, but I'll leave that for the next post. Here's the progress so far.

dd2.zip

0 Comments


Recommended Comments

There are no comments to display.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...