summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2016-10-07 08:35:40 (GMT)
committerH. Peter Anvin <hpa@zytor.com>2016-10-07 08:35:40 (GMT)
commitc07de56eb72f7bfe313d3dddd1ccfce87a0de209 (patch)
tree52542ddb9a5ed9a5674cf789be92a2b698e70d9c
parent2b65a31fbf1658d2bc831aa83542c3f861b9b0bc (diff)
downloadabc80-c07de56eb72f7bfe313d3dddd1ccfce87a0de209.zip
abc80-c07de56eb72f7bfe313d3dddd1ccfce87a0de209.tar.gz
abc80-c07de56eb72f7bfe313d3dddd1ccfce87a0de209.tar.bz2
abc80-c07de56eb72f7bfe313d3dddd1ccfce87a0de209.tar.xz
bin2bac: update to handle relocations from z88dk-z80asm
Change the bin2bac code to process a relocation file generated by z80asm from z88dk version 1.99 or later. At the same time, simplify the loader code substantially by removing the recursive dependency between header length and loader length - it doesn't actually buy us anything, because it is just as easy to fetch the line length from the byte at BOFA (true as long as the BASIC prefix is only one line, but it can be more than one statement if necessary.)
-rw-r--r--data/bacldr.asm16
-rw-r--r--data/bacldr2.asm65
-rw-r--r--data/bacldrr.asm84
-rwxr-xr-xdata/bin2bac.pl142
4 files changed, 188 insertions, 119 deletions
diff --git a/data/bacldr.asm b/data/bacldr.asm
index d75f7d2..80e99ec 100644
--- a/data/bacldr.asm
+++ b/data/bacldr.asm
@@ -1,21 +1,21 @@
org 0 ; This code must be position-independent
-BOFA: equ 65052
-offset: equ 0xeeee ; Length of the BASIC prefix
-LOAD: equ 0xdddd ; Load address
-ENTRY: equ 0xbbbb
+ defc BOFA=65052
+ defc LOAD=0DDDDh ; Load address placeholder
+ defc ENTRY=0BBBBh ; Entrypoint address placeholder
ld hl,(BOFA)
- ld bc,offset ; offset <= 255
+ ld b,0
+ ld c,(hl) ; BC <- length of BASIC prefix
add hl,bc
ld de,LOAD
; Get data block length
loop:
- ld a,-8
- add (hl)
- jp nc,ENTRY ; END is a shorter line than any data line
+ ld a,(hl)
+ sub 8
+ jp c,ENTRY ; END is a shorter line than any data line
; Skip length byte + line number (2 bytes) + string opcode (3 bytes)
ld c,6
diff --git a/data/bacldr2.asm b/data/bacldr2.asm
deleted file mode 100644
index ae9c381..0000000
--- a/data/bacldr2.asm
+++ /dev/null
@@ -1,65 +0,0 @@
- ;; Load an assembly program at BOFA and handle relocations
- ;; Relocations are a list of 16-bit addresses at the end of
- ;; the image, processed in reverse order, terminated with
- ;; an FFFFh sentinel.
-
- org 0 ; This code must be position-independent
-
-BOFA: equ 65052
-offset: equ 0xeeee ; Length of the BASIC prefix
-ENTRY: equ 0xbbbb
-
- ld hl,(BOFA)
- push hl
- ld e,l
- ld d,h
- ld bc,offset ; Offset < 256 so B = 0
- add hl,bc
-
- ; Get data block length
-loop:
- ld a,-8
- add (hl)
- jr nc,reloc ; END is a shorter line than any data line
-
- ; Skip length byte + line number (2 bytes) + string opcode (3 bytes)
- ld c,6
- add hl,bc
-
- ; Copy remaining data
- ld c,a
- ldir
-
- ; Skip return + CR
- inc hl
- inc hl
- jr loop
-
- ;; Process relocations. At this point DE points beyond
- ;; the end of the contiguous image.
-reloc:
- pop bc ; BC <- BOFA
-reloc_loop:
- ex de,hl
- dec hl
- ld d,(hl)
- dec hl
- ld e,(hl)
- ld a,e
- and d
- inc a ; A = FFh only if BC = FFFFh
- jr z,reloc_done
- ex de,hl
- add hl,bc
- ld a,(hl)
- add c
- ld (hl),a
- inc hl
- ld a,(hl)
- adc a,b
- ld (hl),a
- jr reloc_loop
-reloc_done:
- ld hl,ENTRY
- add hl,bc
- jp (hl)
diff --git a/data/bacldrr.asm b/data/bacldrr.asm
new file mode 100644
index 0000000..0944c0e
--- /dev/null
+++ b/data/bacldrr.asm
@@ -0,0 +1,84 @@
+ org 0 ; This code must be position-independent
+
+ defc END=00C9h ; END routine in BASIC ROM
+ defc BOFA=65052
+ defc DATALEN=0DDDDh ; Placeholder: length of data, sans relocations
+ defc DENTRY=0BBBBh ; Placeholder: entrypoint vs final addr ptr
+
+
+ ld bc,END ; END entry point in BASIC (B = 0!)
+ push bc ; Save as return address
+ ld hl,(BOFA) ; BOFA = start of data
+ push hl ; Save for reloc routine
+ ld e,l
+ ld d,h
+ ld c,(hl) ; First BASIC line = BASIC prefix
+ add hl,bc ; Skip prefix
+
+ ;; Copy the data into place
+copy_loop:
+ ld a,(hl) ; BASIC line length
+ sub 8 ; 6 byte header, 2 byte trailer
+ jr c,reloc ; END is shorter than any data line
+
+ ld c,6 ; BC <- 6 (header length)
+ add hl,bc ; Skip header
+
+ ; Copy payload data
+ ld c,a ; BC <- len since B = 0
+ ldir
+
+ ; Skip trailer (2 bytes)
+ inc hl
+ inc hl
+
+ jr copy_loop
+
+ ;; Process relocation
+ ;; HL -> data
+ ;; DE -> relocations
+
+reloc:
+ ld hl,DATALEN ; Length of data sans relocations
+ pop de ; BOFA
+ add hl,de
+ ex de,hl
+reloc_loop:
+ ld a,(de)
+ inc de
+ cp 7fh ; JR doesn't support testing the sign bit
+ jr c,reloc_large
+ ld c,a
+ ld b,0
+
+ ; Apply relocation: HL += BC, (HL) += HL, HL += 2
+reloc_apply:
+ add hl,bc
+ ld a,l
+ add (hl)
+ ld (hl),a
+ ld a,h
+ inc hl
+ adc (hl)
+ ld (hl),a
+ inc hl
+ jr reloc_loop
+
+ ; Large jump (> 127 bytes) or end
+ ; 80h = END, 81-FFh are large jump biased by 8100h in *bigendian* order
+reloc_large:
+ sub 81h
+ jr c,reloc_done
+ ld b,a
+ ld a,(de)
+ inc de
+ ld c,a
+ jr reloc_apply
+
+ ; All done, jump to entry point
+ ; Note: top of stack is 00C9h -> END, so a RET from the
+ ; target routine if the same as END.
+reloc_done:
+ ld bc,DENTRY ; Offset from final address ptr to entry point
+ add hl,bc
+ jp (hl)
diff --git a/data/bin2bac.pl b/data/bin2bac.pl
index 8ea4fec..0d00c7c 100755
--- a/data/bin2bac.pl
+++ b/data/bin2bac.pl
@@ -24,43 +24,89 @@ sub bacstmt($$$) {
return $pad.$d;
}
-sub makebac($$$) {
- my($data, $addr, $entrypt) = @_;
+# Take a list of relocations as 16-bit numbers and convert them
+# to packed form. Returns a list of two elements: the adjusted
+# data block (including the packed relocations) and the final
+# position of the address pointer.
+sub pack_relocs($$) {
+ my($data, $bin_relocs) = @_;
+ my @relocs = sort { $a <=> $b } unpack('v*', $bin_relocs);
+ my $l = length($data);
+
+ my $ptr = 0; # Address pointer
+ my $prels = ''; # Packed relocations
+ foreach my $rel ( @relocs ) {
+ if ($rel > $l-1) {
+ die sprintf("$0: relocation past end of data: %d (0x%x)\n",
+ $rel, $rel);
+ }
+
+ my $delta = $rel - $ptr;
+
+ if ($delta < 0) {
+ die "$0: negative relocation jump: $delta\n";
+ } elsif ($delta <= 0x7f) {
+ $prels .= pack('C', $delta);
+ } elsif ($delta <= 0x7eff) {
+ # This is *bigendian*, with an offset of 0x8100
+ $prels .= pack('n', $delta + 0x8100);
+ } else {
+ die sprintf("$0: impossibly large relocation jump: %d (0x%x)\n",
+ $delta, $delta);
+ }
+
+ # Adjust data bytes to make relocations self-relative
+ my $a = unpack("v", substr($data, $rel, 2));
+ substr($data, $rel, 2) = pack("v", $a - $rel);
+
+ $ptr = $rel + 2;
+ }
+
+ $prels .= pack('C', 0x80); # Relocation end marker
+
+ return ($data.$prels, $ptr);
+}
+
+sub makebac($$$$) {
+ my($data, $addr, $entrypt, $relocs) = @_;
- my @bld;
+ my $packed_rels;
+ my $bld;
if (defined($addr)) {
# <bacldr.asm code> - fixed load address
- $bld[0] = "\x2a\x1c\xfe\x01";
- # 16-bit offset from BOFA to first data line, must be <= 255
- $bld[1] = "\x09\x11";
- # 16-bit load address
- $bld[2] = "\x06\x00\x3e\xf8\x86\xd2";
- # 16-bit entry point
- $bld[3] = "\x0e\x06\x09\x4f\xed\xb0\x23\x23\x18\xf0";
# 0x00C9 is the address for END in all ABC80 BASIC interpreters
$entrypt = 0x00c9 unless(defined($entrypt));
+
+ $bld = "\x2a\x1c\xfe\x06\x00\x4e\x09\x11";
+ $bld .= pack('v', $addr); # 16-bit load address
+ $bld .= "\x06\x00\x7e\xd6\x08\xda";
+ $bld .= pack('v', $entrypt); # 16-bit entry point
+ $bld .= "\x0e\x06\x09\x4f\xed\xb0\x23\x23\x18\xf0";
} else {
- # <bacldr2.asm> - relocatable data loaded at BOFA
- $bld[0] = "\x2a\x1c\xfe\xe5\x5d\x54\x01";
- # 16-bit offset from BOFA to first data line, must be <= 255
- $bld[1] = "\x09\x3e\xf8\x86\x30\x0a\x0e\0x6\x09\x4f\xed\xb0";
- $bld[1] .= "\x23\x23\x18\xf1";
- $bld[1] .= "\xc1\xeb\x2b\x56\x2b\x5e\x7b\xa2\x3c\x28\x0b";
- $bld[1] .= "\xeb\x09\x7e\x81\x77\x23\x7e\x88\x77\x18\xeb\x21";
- # 16-bit offset for entry point
- $bld[2] = "\x09\xe9";
+ # <bacldrr.asm> - relocatable data loaded at BOFA
# Default to entry = BOFA
$entrypt = 0 unless(defined($entrypt));
- }
- my $bld;
- my $bldlen = 0;
- foreach $bld (@bld) {
- # +2 for a 16-bit value between each chunk, +2 for final BB 0D
- $bldlen += length($bld) + 2;
+ # Raw data length, must be recorded before pack_relocs()
+ my $data_len = = length($data);
+
+ my $relptr;
+ ($data, $relptr) = pack_relocs($data, $relocs);
+
+ $bld = "\x01\xc9\x00\xc5\x2a\x1c\xfe\xe5\x5d\x54\x4e\x09";
+ $bld .= "\x7e\xd6\x08\x38\x0a\x0e\x06\x09\x4f\xed\x80";
+ $bld .= "\x23\x23\x18\xf1\x21";
+ # 16-bit length of data not including relocations
+ $bld .= pack('v', $data_len);
+ $bld .= "\xd1\x19\xeb\x1a\x13\xfe\x7f\x38\x0e\x4f\x06\x00";
+ $bld .= "\x09\x7d\x86\x77\x7c\x23\x8e\x77\x23\x18\xec";
+ $bld .= "\xd6\x81\x38\x06\x47\x1a\x13\x4f\x18\xeb\x01";
+ # 16-bit offset from final relocation to entry point
+ $bld .= pack('v', $entrypt - $relptr);
+ $bld[2] = "\x09\xe9";
}
my $q = "\x82"; # Output (program start marker)
@@ -72,33 +118,27 @@ sub makebac($$$) {
$q .= bacstmt(++$r, \$left,
"\x83\xc1\xf1\x5a\x00\xbb\xc7\x1e\xfe\xce\x36\xc7".
"\x1f\xfe\xce\x36\xce\x34\xf5\xc7".
- pack("v",$bldlen)."\xf8\xce\x3a\xb7");
-
- my $pfxlen = length($q) - 1; # The initial 0x82 isn't stored in RAM
-
- die if ($pfxlen > 255); # Should not happen
-
- if (defined($addr)) {
- $bld = $bld[0] . pack("v", $pfxlen) .
- $bld[1] . pack("v", $addr) .
- $bld[2] . pack("v", $entrypt) .
- $bld[3];
- } else {
- $bld = $bld[0] . pack("v", $pfxlen);
- $bld[1] . pack("v", $entrypt) .
- $bld[2];
- }
+ pack("v", length($bld)).
+ "\xf8\xce\x3a\xb7");
my $i = 0;
my $dl = length($data);
while ($i < $dl) {
my $l = $dl - $i;
- # 8 = 3 byte BAC header + 4 byte overhead + CR
+ # 8 byte overhead:
+ # - 1 byte instruction length
+ # - 2 bytes line number
+ # - 2 bytes CB 22 [string expression in " quotes]
+ # - 1 byte data length
+ # <data bytes>
+ # - 1 byte BB (drop expression)
+ # - 1 byte <CR> (end of statement)
+
$q .= genpad(\$left,0) if ($left <= 8);
$l = $left-8 if ($l > $left-8);
- # String expression + address + data + return
+ # String expression + data + return
$q .= bacstmt(++$r, \$left,
"\xcb\"" . pack("C", $l) . substr($data,$i,$l)."\xbb");
@@ -116,15 +156,25 @@ sub makebac($$$) {
return $q;
}
-($file, $org, $entry) = @ARGV;
+($file, $org, $entry, $relocfile) = @ARGV;
$org = oct $org if ( $org =~ /^0/ );
$entry = oct $entry if ( $entry =~ /^0/ );
open(FILE, '<:raw', $file) or die "$0: $file: $!\n";
-$l = read(FILE, $dd, 65536);
+read(FILE, $dd, 65536);
close(FILE);
undef $org if ($org =~ /^(bofa|reloc|\-)$/i);
-print makebac($dd, $org, $entry);
+if (defined($relocfile)) {
+ if (defined($org)) {
+ die "$0: cannot specify load address and relocation file both\n";
+ }
+
+ open(REL, '<:raw', $relocfile) or die "$0: $file: $!\n";
+ read(REL, $relocs, 2*65536);
+ close(REL);
+}
+
+print makebac($dd, $org, $entry, $relocs);