diff options
author | H. Peter Anvin <hpa@zytor.com> | 2014-06-10 20:07:27 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2014-06-10 20:08:20 -0700 |
commit | a86afc2b242239466520211a35b70df770159850 (patch) | |
tree | 60194bf3d7dcaba09b5d502a2c8b0056804ce4f8 | |
parent | 8dce4ce7da12dfc035c1b6e76f536f128709e4ef (diff) | |
download | abc80-a86afc2b242239466520211a35b70df770159850.tar.gz abc80-a86afc2b242239466520211a35b70df770159850.tar.xz abc80-a86afc2b242239466520211a35b70df770159850.zip |
bin2bac: Add program to convert an assembly program to a .BAC file
A fairly compact way to store an assembly program, comparable in
overhead to an .ABS file.
-rw-r--r-- | data/Makefile | 5 | ||||
-rw-r--r-- | data/bacldr.asm | 45 | ||||
-rwxr-xr-x | data/bin2bac.pl | 110 |
3 files changed, 159 insertions, 1 deletions
diff --git a/data/Makefile b/data/Makefile index 5f1fba0..2dba4e2 100644 --- a/data/Makefile +++ b/data/Makefile @@ -9,7 +9,7 @@ Z80ASM = ../tools/z80asm/z80asm all : keyboard.mif abc80rom.bin basic80.mif \ mmu.mif chargen.mif videoram.mif fgcol.mif sddrom.mif \ - abcsefi.bas abcdkno.bas abcintl.bas cpm.bas cpm.abs + abcsefi.bas abcdkno.bas abcintl.bas cpm.bas cpm.abs cpm.bac abc80rom.bin : buildrom.pl abcbasic.rom ufddos.bin printer.bin $(PERL) buildrom.pl $@ 32768 \ @@ -67,6 +67,9 @@ cpm.bas: cpm.bin cpm.abs: cpm.bin $(PERL) bin2abs.pl $< 32768 > $@ || ( rm -f $@ ; exit 1 ) +cpm.bac: cpm.bin + $(PERL) bin2bac.pl $< 32768 32768 > $@ || ( rm -f $@ ; exit 1 ) + .bin.bas: $(PERL) bin2poke.pl $< 16384 100 RETURN | cat charpoke.bah - > $@ \ || ( rm -f $@ ; exit 1 ) diff --git a/data/bacldr.asm b/data/bacldr.asm new file mode 100644 index 0000000..2d0e43b --- /dev/null +++ b/data/bacldr.asm @@ -0,0 +1,45 @@ + org 0 ; This code must be position-independent + +BOFA: equ 65052 +offset: equ 0xeeee ; Length of the BASIC prefix + + ld hl,(BOFA) + ld de,offset + add hl,de + + ld b,0 + + ; Get data block length +loop: + ld a,-10 + add (hl) + jr nc,run ; End of file marked by an empty REM + + ; Skip length byte + line number (2 bytes) + string opcode (3 bytes) + ld c,6 + add hl,bc + + ; Load address + ld e,(hl) + inc hl + ld d,(hl) + inc hl + + ; Copy remaining data + ld c,a + ldir + + ; Skip return + CR + inc hl + inc hl + jr loop + +run: +; jp 0x1234 ; Replace with RET if no entry point + ret + nop + nop + +endbyte: + defb 13 +data: diff --git a/data/bin2bac.pl b/data/bin2bac.pl new file mode 100755 index 0000000..4ef7eef --- /dev/null +++ b/data/bin2bac.pl @@ -0,0 +1,110 @@ +#!/usr/bin/perl + +use bytes; + +sub genpad($$) { + my($left, $final) = @_; + + printf STDERR "Padding %d bytes\n", $$left; + + my $o = ($final ? "\x01" : "\x00") . ("\x00" x $$left); + $$left = 252; # Bytes left in block minus end marker + + return $o; +} + +sub bacstmt($$$) { + my($line, $left, $data) = @_; + my $l = length($data) + 4; + my $d = pack("Cv", $l, $line) . $data . "\x0d"; + + printf STDERR "bacstmt: l = %d, left = %d\n", $l, $$left; + + if ($l > $$left) { + $d = genpad($left,0).$d; + } + + $$left -= $l; + return $pad.$d; +} + +sub makebac($$) { + my($segdata, $entrypt) = @_; + + # <bacldr.asm code> + my $bld1 = "\x2a\x1c\xfe\x11"; + # 16-bit offset from BOFA to first data line between $bld1 and $bld2 + my $bld2 = "\x19\x06\x00\x3e\xf6\x86\x30\x0e\x0e\x06"; + $bld2 .= "\x09\x5e\x23\x56\x23\x4f\xed\xb0\x23\x23\x18\xed"; + # C3 = JP, 00C9 is the address for END in all ABC80 BASIC interpreters + $bld2 .= pack("Cv", 0xc3, defined($entrypt) ? $entrypt : 0x00c9); + + # +2 for the offset, +2 for the final 0xbb + CR + my $bldlen = length($bld1) + length($bld2) + 2 + 2; + print STDERR "bldlen = ", $bldlen, "\n"; + + my $q = "\x82"; # Output (program start marker) + my $left = 251; # Bytes left in block + my $r = 0; # Last emitted line number + + # 1 E%=PEEK(65054%)+SWAP%(PEEK(65055%)) [EOFA] + $q .= bacstmt(++$r, \$left, + "\x83\xc1\xf1\x45\x37\x80\xc7\x1e\xfe\xce\x36\xc7". + "\x1f\xfe\xce\x36\xce\x34\xf5\xb7"); + # 2 Z%=CALL(E%-<loader offset>) + $q .= bacstmt(++$r, \$left, + "\x83\xc1\xf1\x5a\x37\x80\xc1\xf1\x45\xba\x0d\xd5\xc7". + pack("v",$bldlen)."\xf8\xce\x3a\xb7"); + + my $pfxlen = length($q) - 1; # The initial 0x82 isn't stored in RAM + my $bld = $bld1 . pack("v", $pfxlen) . $bld2; + print STDERR "pfxlen = ", $pfxlen, "\n"; + + foreach my $sega ( sort(keys(%{$segdata})) ) { + my $addr = $sega; + my $data = ${$segdata}{$sega}; + + my $i = 0; + my $dl = length($data); + while ($i < $dl) { + my $l = $dl - $i; + + printf STDERR "left = %d, l = %d\n", $left, $dl; + + # 9 = 3 byte BAC header + 6 byte overhead + CR + $q .= genpad(\$left,0) if ($left <= 10); + $l = $left-10 if ($l > $left-10); + + printf STDERR "Line %d address %04X, %d/%X bytes\n", + $r+1, $addr, $l, $l; + + # String expression + address + data + return + $q .= bacstmt(++$r, \$left, "\xcb\"".pack("C", $l+2). + pack("v",$addr).substr($data,$i,$l)."\xbb"); + + $i += $l; + $addr += $l; + } + } + + # Terminal END statement + $q .= bacstmt(++$r, \$left, "\x86\x8a"); # END + + # Loader code (string expression) + $q .= bacstmt(++$r, \$left, "\xcb\"".pack("C",length($bld)).$bld."\xbb"); + $q .= genpad(\$left,1); + + return $q; +} + +($file, $org, $entry) = @ARGV; + +$org = oct $org if ( $org =~ /^0/ ); + +open(FILE, '<:raw', $file) or die "$0: $file: $!\n"; +$l = read(FILE, $dd, 65536); +close(FILE); + +$segs{$org} = $dd; + +print makebac(\%segs, $entry); |