aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2018-09-10 02:41:01 -0700
committerH. Peter Anvin <hpa@zytor.com>2018-09-10 02:41:01 -0700
commitb02f938db5d8bf0b0306f664c6b448da1d3527c5 (patch)
tree19903f4b801aa5cdf8236eede147cd9342d7322c
parentaceee818e04e859b6f0a6647cc329295dc68f278 (diff)
downloadabc80-b02f938db5d8bf0b0306f664c6b448da1d3527c5.tar.gz
abc80-b02f938db5d8bf0b0306f664c6b448da1d3527c5.tar.xz
abc80-b02f938db5d8bf0b0306f664c6b448da1d3527c5.zip
bin2bac: add support for ABC800
Add support for generating relocatable .bac files for ABC800/BASIC II. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--data/bac800.asm123
-rwxr-xr-xdata/bin2bac.pl101
2 files changed, 209 insertions, 15 deletions
diff --git a/data/bac800.asm b/data/bac800.asm
new file mode 100644
index 0000000..f2aba8a
--- /dev/null
+++ b/data/bac800.asm
@@ -0,0 +1,123 @@
+;
+; .bac relocator for ABC800
+;
+
+ defc BOFA=65286
+ defc EOFA=65288
+ defc HEAP=65290
+ defc BOTM=65292
+ defc TOPM=65294
+ defc TOPP=65296
+
+ defc HLBOFA=003Bh ; LD HL,BOFA
+ defc IXBOFA=003Fh ; LD IX,BOFA
+ defc HLPROG=0044h ; LD HL,BOFA+20 (after header)
+
+ defc ENTRY=0 ; Placeholder: entrypoint (vs _start)
+
+ ;; First, the ABC800 BAC file header
+_header:
+ defb 8Fh ; BASIC II
+ defb 0
+ defb 21h ; Program flags (meaning unclear)
+ defb 0
+LEN: defw _end + 7 ; Adjust to total file length (7 = trailer)
+ defb 0, 0
+ defb 0, 0, 0, 0
+ defb 0, 0, 0, 0
+ defb 0, 0, 0, 0
+
+ ;; BASIC bytecode to invoke _start
+_basic:
+ defb 221 + 11 ; PUSH int 11
+ defb 209 ; Function call, 1 parameter
+ defb 25 ; SYS() SYS(11) = BOFA
+ defb 221 + _start ; PUSH int _start
+ defb 156 ; + int
+ defb 209 ; Function call, 1 parameter
+ defb 23 ; CALL()
+ defb 184 ; POP int
+ defb 146 ; END
+
+ ;; Relocator code. In order to support saving and re-loading
+ ;; programs, this must handle being restarted at either the
+ ;; same or a different address and do the right thing with
+ ;; the relocations having already been run before.
+
+_start: ; HL points here
+ defc REL1=_start+1
+ ;; Subtract the length of the (added) relocations from this value
+ ;; so that 0 offset = start of program
+ ld bc,_start - _end ; Current base address
+ ld a,l
+ sub c
+ ld c,a
+ ld a,h
+ sbc b
+ ld b,a
+ or c
+ jr z,done ; If the relocation delta is zero, done
+
+ push hl
+ ld de,reloc_data - _start
+ add hl,de
+ pop de
+
+ ;; BC = relocation delta
+ ;; DE -> data (initially _start)
+ ;; HL -> relocations
+
+reloc:
+ ld a,(hl)
+ cp 80h ; JR doesn't support testing the sign bit
+ jr nc,reloc_large
+
+ ; Apply relocation: HL += 1, DE += A, (DE) += BC, DE += 2
+reloc_apply:
+ inc hl
+
+ add e
+ ld e,a
+ jr nc,nocarry
+ inc d
+nocarry:
+
+ ld a,(de)
+ add c
+ ld (de),a
+ inc de
+ ld a,(de)
+ adc b
+ ld (de),a
+ inc de
+ jr reloc
+
+ ; 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,done
+ inc hl
+ add d
+ ld d,a
+ ld a,(hl)
+ jr reloc_apply
+
+ ; All done, jump to entry point. A RET will return to BASIC
+ ; which will execute an END statement.
+done:
+ defc RELENTRY=done+1 ; Relocation here
+ jp ENTRY ; Entry relative to start of program
+
+ ; Relocation data follows immediately afterwards
+reloc_data:
+ ; Our internal relocations first
+ defb REL1 - _start
+ defb RELENTRY - REL1 - 2
+_end:
+ ;; The relocation pointer is at _end - 2, so the first
+ ;; relocation must be set to 2 + size of relocation data +
+ ;; offset into program
+
+ ;; Follows: compacted relocations, program,
+ ;; trailer: 00 00 00 00 FF FB FF = no long variables
diff --git a/data/bin2bac.pl b/data/bin2bac.pl
index 17b68a9..bd5e655 100755
--- a/data/bin2bac.pl
+++ b/data/bin2bac.pl
@@ -3,7 +3,7 @@
use bytes;
# Pad out to the next block. $final = 1 if and only if this is the last block.
-sub genpad($$) {
+sub genpad80($$) {
my($left, $final) = @_;
my $o = ($final ? "\x01" : "\x00") . ("\x00" x $$left);
@@ -13,13 +13,13 @@ sub genpad($$) {
}
# Generate a line in a .bac file; pad the previous block if necessary
-sub bacstmt($$$) {
+sub bacstmt80($$$) {
my($line, $left, $data) = @_;
my $l = length($data) + 4;
my $d = pack("Cv", $l, $line) . $data . "\x0d";
if ($l > $$left) {
- $d = genpad($left,0).$d;
+ $d = genpad80($left,0).$d;
}
$$left -= $l;
@@ -27,8 +27,8 @@ sub bacstmt($$$) {
}
# 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
+# to packed form. Returns a list of three elements: the adjusted
+# data block, the packed relocations, and the final
# position of the address pointer.
sub pack_relocs($$) {
my($data, $bin_relocs) = @_;
@@ -66,13 +66,12 @@ sub pack_relocs($$) {
$prels .= pack('C', 0x80); # Relocation end marker
- return ($data.$prels, $ptr);
+ return ($data, $prels, $ptr);
}
-sub makebac($$$$) {
+sub makebac80($$$$) {
my($data, $addr, $entrypt, $relocs) = @_;
- my $packed_rels;
my $bld;
if (defined($addr)) {
@@ -96,7 +95,8 @@ sub makebac($$$$) {
my $data_len = length($data);
my $relptr;
- ($data, $relptr) = pack_relocs($data, $relocs);
+ ($data, $relocs, $relptr) = pack_relocs($data, $relocs);
+ $data .= $reldata;
$bld = "\x01\xc9\x00\xc5\x2a\x1c\xfe\xe5\x5d\x54\x4e\x09";
$bld .= "\x7e\xd6\x08\x38\x0a\x0e\x06\x09\x4f\xed\xb0";
@@ -121,10 +121,10 @@ sub makebac($$$$) {
# way we don't create any variables on the heap. As a side benefit,
# it is actually shorter. Since the CALL() never returns, it will
# never actually print anything anyway.
- $q .= bacstmt(++$r, \$left,
- "\x84\xc7\x1e\xfe\xce\x36\xc7\x1f\xfe\xce\x34\xf5\xc7".
- pack("v", length($bld)+2). # +2 for statement trailer
- "\xf8\xce\x3a\xba");
+ $q .= bacstmt80(++$r, \$left,
+ "\x84\xc7\x1e\xfe\xce\x36\xc7\x1f\xfe\xce\x34\xf5\xc7".
+ pack("v", length($bld)+2). # +2 for statement trailer
+ "\xf8\xce\x3a\xba");
my $i = 0;
my $dl = length($data);
@@ -161,10 +161,75 @@ sub makebac($$$$) {
return $q;
}
+sub makebac800($$$) {
+ my($data, $entry, $relocs) = @_;
+
+ my $adata, $relptr;
+ ($adata, $relocs, $relptr) = pacK_relocs($data, $relocs);
+ # For ABC800 we don't use the adjusted data
+
+ # The first relocation needs to be adjusted to account for the
+ # size of the relocations themselves
+ my $r1 = unpack("C", $relocs);
+ if ($r1 != 0x80) { # No relocations whatsoever?
+ my $rlen = 1;
+ if ($r1 > 0x7f) {
+ $r1 = unpack("n", $relocs) - 0x8100;
+ $rlen = 2;
+ }
+
+ $r1 += 2 + length($relocs);
+ if ($r1 > 0x7f) {
+ if ($rlen == 1) {
+ $r1++;
+ }
+ substr($relocs, 0, $rlen) = pack("n", $r1 + 0x8100);
+ } else {
+ substr($relocs, 0, $rlen) = pack("C", $r1);
+ }
+ }
+
+ my $bhdr;
+ # See bac800.asm
+ $bhdr = "\x8f\x00\x21\x00";
+ $bhdr .= pack("v", 0x5a + length($relocs) + length($data));
+ $bhdr .= "\0\0\0\0\0\0\0\0\0\0";
+ $bhdr .= "\xe8\xd1\x19\xfa\x9c\xd1\x17\xb8\x92";
+ $bhdr .= "\x01" . pack("v", 0xffca - length($relocs));
+ $bhdr .= "\x7d\x91\x4f\x7c\x98\x47\xb1\x28\x25\xe5\x11\x34\x00";
+ $bhdr .= "\x19\xd1\x7e\xfe\x80\x30\x10\x23\x83\x5f\x30\x01\x14";
+ $bhdr .= "\x1a\x81\x12\x13\x1a\x88\x12\x13\x18\xeb\xd6\x81\x38";
+ $bhdr .= "\x06\x23\x82\x57\x7e\x18\xe6";
+ $bhdr .= "\xc3" . pack("v", $entry);
+ $bhdr .= "\x01\x2f";
+
+ $data = $bhdr . $relocs . $data;
+
+ # Trailer = "no long variable names"
+ $data .= "\x00\x00\x00\x00\xff\xfb\xff";
+
+ # Pad to the end of a binary sector
+ $data .= ("\0" x (253 - (((length($data)+1) % 253) + 1)));
+
+ return $data;
+}
+
+my $abc800 = 0;
+
+while ($ARGV[0] =~ /^-/) {
+ my $opt = shift;
+ if ($opt eq '-80') {
+ $abc000 = 0;
+ } elsif ($opt eq '-800') {
+ $abc800 = 1;
+ }
+}
+
($file, $org, $entry, $entryname) = @ARGV;
if (!defined($file)) {
- die "Usage: $0 inputfile {load_addr|relocfile} [entrypoint|deffile [entryname]]\n";
+ die "Usage: $0 [-80|-800] inputfile {load_addr|relocfile}\n".
+ " [entrypoint|deffile [entryname]]\n";
}
if ($org !~ /^[0-9]/) {
@@ -209,6 +274,12 @@ if (defined($relocfile)) {
open(REL, '<:raw', $relocfile) or die "$0: $file: $!\n";
read(REL, $relocs, 2*65536);
close(REL);
+} elsif (defined($org) && $abc800) {
+ die "$0: abc800 only supported as relocatable\n";
}
-print makebac($dd, $org, $entry, $relocs);
+if ($abc800) {
+ print makebac800($dd, $entry, $relocs);
+} else {
+ print makebac80($dd, $org, $entry, $relocs);
+}