Skip to content

Instantly share code, notes, and snippets.

@Alwinfy
Created June 22, 2020 16:17
Show Gist options
  • Save Alwinfy/50fc0a3facbf835138dcb63381fdf5b2 to your computer and use it in GitHub Desktop.
Save Alwinfy/50fc0a3facbf835138dcb63381fdf5b2 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl -w
#perl2exe_include strict
#perl2exe_include warnings
use strict;
use warnings;
#use feature "switch";
use Env qw($MAX_RECURSION_DEPTH $AUTOCOMPILE);
use Data::Dumper;
my %words = (
# Files
include => ".include",
# Macros
macro => ".macro",
endm => ".endm",
definition => ".define",
undefine => ".undef",
# Counters
counter => ".set",
increment => ".incr",
decrement => ".decr",
);
my $max_recursion = $MAX_RECURSION_DEPTH || 100;
my $autocompile = $AUTOCOMPILE || 0;
my $cmacro;
my $out;
my $recursion;
my %defines;
my %macros;
my %counters;
my %seen;
sub trimmed {
my $word = shift;
$word =~ s/^\s+|\s+$//g;
return $word;
}
sub expline {
my $line = shift;
my @tokens = split /\s+/, trimmed $line;
if($#tokens < 0) {
print $out $line;
return;
}
my $word = shift @tokens;
my %immsubs = (
$words{include} => sub {
process(shift @tokens) or die "$words{include} missing a name";
},
$words{macro} => sub {
die "Can't define a macro within a macro" if $cmacro;
$cmacro = '@' . shift @tokens or die "$words{macro} missing a name";
$macros{$cmacro} = [];
},
$words{endm} => sub {
die "Can't end a macro outside a macro" unless $cmacro;
$cmacro = 0;
},
$words{undefine} => sub {
my $token = shift @tokens or die "$words{undefine} missing a name";
if($token =~ /^@/) {
die "Attempting to undefine nonexistent macro $token" unless exists $macros{$token};
delete $macros{$token};
}
elsif($token =~ /^&&/) {
$token =~ s/^&&//;
die "Attempting to undefine nonexistent counter $token" unless exists $counters{$token};
delete $counters{$token};
}
else {
die "Attempting to undefine nonexistent define'd $token" unless exists $defines{$token};
delete $defines{$token};
}
}
);
my %defsubs = (
$words{definition} => sub {
my $name = shift @tokens or die "$words{definition} missing a name";
$defines{$name} = join ' ', @tokens;
},
$words{counter} => sub {
my $name = shift @tokens or die "$words{counter} missing a name";
#warn "Counter $name defined already" if exists $counters{$name};
$counters{$name} = (scalar @tokens) && (shift @tokens);
},
$words{increment} => sub {
my $name = shift @tokens or die "$words{decrement} missing a name";
die "Counter $name doesn't exist" unless exists $counters{$name};
if(scalar @tokens) {
$counters{$name} += shift @tokens;
}
else {
$counters{$name}++;
}
},
$words{decrement} => sub {
my $name = shift @tokens or die "$words{decrement} missing a name";
die "Counter $name doesn't exist" unless exists $counters{$name};
if(scalar @tokens) {
$counters{$name} -= shift @tokens;
}
else {
$counters{$name}--;
}
}
);
if(exists $immsubs{$word}) {
$immsubs{$word}();
}
elsif(not $cmacro and exists $defsubs{$word}) {
$defsubs{$word}();
}
else {
unshift @tokens, $word;
if($cmacro) {
push @{$macros{$cmacro}}, $line;
}
else {
my $prefix = '';
my $or = $recursion;
for(;;) {
my $rc = 0;
$rc += ($line =~ s/&&(\w+)\b/$counters{$1}/eg);
$rc += ($line =~ s/\b$_\b/$defines{$_}/g) for keys %defines;
last if not $rc;
$recursion += $rc;
die "Recursion depth $max_recursion exceeded" if $recursion > $max_recursion;
}
$recursion = $or;
$recursion++;
for my $pos (0..$#tokens) {
$word = $tokens[$pos];
if($cmacro ne $word and
exists $macros{$word}) {
for my $ln (0..$#{$macros{$word}}) {
my $exp = $macros{$word}[$ln];
for my $i (1..$#tokens-$pos) {
$tokens[$i+$pos] =~ s/,$//;
$exp =~ s/&$i/$tokens[$i+$pos]/g;
}
expline(($ln ? '' : $prefix) . $exp);
}
return;
}
$prefix .= "$word ";
}
$recursion--;
print $out $line;
}
}
}
sub process {
my $fin = shift;
die "Include loop detected ", join(", ", keys %seen) if (exists $seen{$fin});
$seen{$fin} = undef;
my $in;
if($fin eq "-") {
$in = *STDIN;
}
else {
open($in, "<", $fin) or die "Can't open $fin for reading $!";
}
while(my $line = <$in>) {
expline($line);
}
close $in;
delete $seen{$fin};
}
unshift(@ARGV, "-") unless @ARGV;
for my $fname (@ARGV) {
my $toread = $fname;
$fname =~ s/\.[^.]+$//;
if($fname eq "-") {
$out = *STDOUT;
}
else {
open($out, ">", "$fname.s") or die "Can't open $fname for writing $!";
}
$cmacro = 0;
%seen = ();
%defines = ();
%macros = ();
%counters = ();
process($toread);
#print Data::Dumper->new([\%defines, \%macros, \%counters], ['*defines', '*macros', '*counters'])->Dump;
die "Dangling macro $cmacro at EOF" if $cmacro;
close $out;
if($autocompile) {
print "Compiling '$fname.s'\n";
system("lc3as", "$fname.s") == 0 or die "lc3as failed $!";
}
}
.define rsp r6
.macro push
.incr fsp
str &1, rsp, #&&fsp
.endm
.macro pop
ldr &1, rsp, #&&fsp
.decr fsp
.endm
.macro neg
not &1, &1
add &1, &1, x1
.endm
.macro seti
and &1, &1, x0
add &1, &1, &2
.endm
.orig x3000
ld r0, big
jsr putd
ld r0, nl
out
ld r0, smol
jsr putd
ld r0, nl
out
lea r0, str0
puts
jsr getd
st r0, tmp
lea r0, str1
puts
jsr getd
add r1, r0, x0
ld r0, tmp
jsr putd
; mult
lea r0, str2
puts
add r0, r1, x0
jsr putd
lea r0, str4
puts
ld r0, tmp
jsr mult
jsr putd
lea r0, str6
puts
; div
ld r0, tmp
jsr putd
lea r0, str3
puts
add r0, r1, x0
jsr putd
lea r0, str4
puts
ld r0, tmp
jsr div
st r0, tmp
add r0, r1, x0
jsr putd
lea r0, str5
puts
ld r0, tmp
jsr putd
lea r0, str6
puts
halt
str0: .stringz "First number: "
str1: .stringz "Second number: "
str2: .stringz " times "
str3: .stringz " divided by "
str4: .stringz " is "
str5: .stringz ", remainder "
str6: .stringz ".\n"
tmp: .fill x0
big: .fill x7fff
smol: .fill x8000
nl: .fill x0a
; Computes r0' = r0 % r1, r1' = r0 / r1.
div:
; Save state
st rsp, DD0
@push r2
@push r3
@push r4
@push r7
; Ensure r0 negative
and r2, r2, x0 ; r2 = quotient signbit
and r3, r3, x0 ; r3 = remainder signbit
; If negative r0, cope
add r0, r0, x0
brn D0
@neg r0
not r3, r3
not r2, r2
; Ensure r1 positive
D0: add r1, r1, x0
brz DE
brp D1
@neg r1
not r2, r2
; Prepare right-shifts
D1: add r0, r0, x-1
st r2, DD1
st r3, DD2
lea r2, DD4 ; r2 = rightshift stack pointer
@neg r2
st r2, DD3
lea r2, DD4
and r3, r3, x0 ; r3 = mask
add r3, r3, x1
D2: ; Push onto stack
str r1, r2, x0
str r3, r2, x1
add r2, r2, x2
;
add r3, r3, r3 ; left shift
add r1, r1, r1 ; "
brnz D3 ; move on on overflow
add r7, r1, r0 ; r7 = trash
brn D2 ; if r1 < -r0 keep moving up
D3:
and r1, r1, x0
; Check if done
D4: ld r7, DD3
add r7, r2, r7
brnz D5
; Pop rightshift
add r2, r2, x-2
ldr r4, r2, x0 ; r4 = tested
ldr r3, r2, x1 ; r3 = mask
add r7, r0, r4
; If nonnegative
brzp D4
add r0, r7, x0 ; Commit subtraction
add r1, r1, r3 ; Apply mask
br D4
; Put signs back
D5: add r0, r0, x0
ld r7, DD1
brnp D6
@neg r1
D6: add r0, r0, x1
ld r7, DD2
brz D7
@neg r0
; Restore state
D7: @pop r7
@pop r4
@pop r3
@pop r2
ld rsp, DD0
ret
; Divide-by-0 handling
DE: lea r0, DD5
puts
halt
DD0: .blkw x5 ; save state
DD1: .fill x0 ; div sign
DD2: .fill x0 ; mod sign
DD3: .fill x0 ; -DD4
DD4: .blkw x20 ; rightshift stack
DD5: .stringz "Divide by zero, halting."
; Outputs r0 as a base-10 signed int to stdout.
putd:
st rsp, PD0
@push r7
@push r2
@push r1
@push r0
lea r2, PD4 ; string pointer
add r0, r0, x0
brzp P0
@neg r0
brn P3
add r1, r0, x0
ld r0, PD3
out
add r0, r1, x0
P0: ld r1, PD1
jsr div
ld r7, PD2
add r7, r7, r0
add r2, r2, x-1
str r7, r2, x0
add r0, r1, x0
brnp P0
P1:
;and r7, r7, x0
;str r7, r2, x0
add r0, r2, x0
P2: puts
@pop r0
@pop r1
@pop r2
@pop r7
ld rsp, PD0
ret
P3: lea r0, PD5
br P2
PD0: .blkw x5 ; save state
PD1: .fill x0a ; base 10
PD2: .fill x30 ; zero offset
PD3: .fill x2d ; negative sign
.blkw x7
PD4: .fill x0 ; digit list
PD5: .stringz "-32768"
; Puts r0 * r1 into r0.
mult:
; Save state
st rsp, MD0
@push r1
@push r2
@push r3
@push r4
; Save signage
and r3, r3, x0 ; r3 = signage
add r0, r0, x0
brzp M0
not r3, r3
@neg r0
M0: add r1, r1, x0
brzp M1
not r3, r3
@neg r1
; Initialize counter
M1: st r3, MD1
and r2, r2, x0 ; r2 = output
and r3, r3, x0
add r3, r3, x1 ; r3 = mask
; Mainloop
M2: and r4, r3, r0 ; r4 = discard
brz M3
add r2, r2, r1
M3: add r1, r1, r1
add r3, r3, r3
brnp M2
; Restore state
add r0, r2, x0
ld r3, MD1
brz M4
@neg r0
M4: @pop r4
@pop r3
@pop r2
@pop r1
ld rsp, MD0
ret
MD0: .fill x4 ; save state
MD1: .fill x0 ; signed?
; Puts whether r0 is a ws char in r0. Also sets POSITIVE/ZERO for convenience.
is_ws: st rsp, ID0
@push r7
@push r1
lea r1, ID1
I0: ldr r7, r1, x0
brz I1
add r1, r1, x1
@neg r7
add r7, r0, r7
brnp I0
and r0, r0, x0
add r0, r0, x1
br I2
I1: add r0, r7, x0
I2: @pop r1
@pop r7
ld rsp, ID0
add r0, r0, x0
ret
ID0: .blkw x3 ; save state
ID1: .stringz " \t\n"
; Parses stdin as a base-10 signed int to r0, stopping at the first invalid char.
getd: st rsp, GD0
@push r7
@push r1
G0: getc
out
add r1, r0, x0
jsr is_ws
brp G0
add r0, r1, x0
and r1, r1, x0
.macro cmpj
ld r7, &2
add r7, &1, r7
brz &3
.endm
@cmpj r0, GD2, G1
@cmpj r0, GD3, G2
.undef @cmpj
br G3
G1: not r1, r1
G2: add r1, r1, x0
getc
out
G3: st r1, GD6
and r2, r2, x0
add r2, r2, x-6
and r1, r1, x0
; end-of-parse check
G4: st r0, GD7
jsr is_ws
brp G5
Ld r0, GD7
; bounds check
ld r7, GD4
add r0, r0, r7
brzp GE0
ld r7, GD5
add r0, r0, r7
brn GE0
; ...and add
x: st r0, GD7
ld r0, GD1
jsr mult
ld r7, GD7
add r1, r0, r7
getc
out
br G4
G5: ld r0, GD6
brz G6
@neg r1
G6: add r0, r1, x0
@pop r1
@pop r7
ld rsp, GD0
ret
GE0: lea r0, GD8
puts
halt
GD0: .blkw x3 ; save state
GD1: .fill x0a ; base-10
GD2: .fill x-2d ; minus sign
GD3: .fill x-2b ; plus sign
GD4: .fill x-3a ; upper cap
GD5: .fill x0a ; lower cap adjust
GD6: .fill x0 ; signage
GD7: .fill x0 ; temp store
GD8: .stringz "\nRead illegal digit, halting."
.end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment