aboutsummaryrefslogtreecommitdiffstats
path: root/data/bin2bac.pl
blob: b3a7208fb1580fdbacf1bfa6fc28c6e6855371ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/perl

use bytes;

sub genpad($$) {
    my($left, $final) = @_;

    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";

    if ($l > $$left) {
	$d = genpad($left,0).$d;
    }

    $$left -= $l;
    return $pad.$d;
}

sub makebac($$$) {
    my($data, $addr, $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\x11";
    # 16-bit load address between $bld2 and $bld3
    my $bld3 = "\x06\x00\x3e\xf8\x86\xd2";
    # 16-bit entr point between $bld3 and $bld4
    my $bld4 = "\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));

    # +2 for the final 0xbb + CR
    my $bldlen = length($bld1) + 2 + length($bld2) + 2 + length($bld3) + 2 +
	length($bld4) + 2;

    my $q  = "\x82";		# Output (program start marker)
    my $left = 251;		# Bytes left in block
    my $r = 0;			# Last emitted line number

    # Address 65054 is EOFA
    # 1 Z%=CALL(PEEK(65054%)+SWAP%(PEEK(65055%))-<loader offset>)
    $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
    my $bld = $bld1 . pack("v", $pfxlen) . $bld2 . pack("v", $addr) .
	$bld3 . pack("v", $entrypt) . $bld4;

    my $i = 0;
    my $dl = length($data);
    while ($i < $dl) {
	my $l = $dl - $i;

	# 8 = 3 byte BAC header + 4 byte overhead + CR
	$q .= genpad(\$left,0) if ($left <= 8);
	$l = $left-8 if ($l > $left-8);

	# String expression + address + data + return
	$q .= bacstmt(++$r, \$left,
		      "\xcb\"" . pack("C", $l) . 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/ );
$entry = oct $entry if ( $entry =~ /^0/ );

open(FILE, '<:raw', $file) or die "$0: $file: $!\n";
$l = read(FILE, $dd, 65536);
close(FILE);

$segs{$org} = $dd;

print makebac($dd, $org, $entry);