| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::Scheme79asm; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 54084 | use 5.014000; | 
|  | 1 |  |  |  |  | 3 |  | 
| 4 | 1 |  |  | 1 |  | 13 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 15 |  | 
| 5 | 1 |  |  | 1 |  | 4 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 30 |  | 
| 6 | 1 |  |  | 1 |  | 5 | use re '/s'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 73 |  | 
| 7 | 1 |  |  | 1 |  | 5 | use Carp qw/croak/; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 1 |  |  | 1 |  | 476 | use Data::Dumper qw/Dumper/; | 
|  | 1 |  |  |  |  | 5342 |  | 
|  | 1 |  |  |  |  | 51 |  | 
| 10 | 1 |  |  | 1 |  | 349 | use Data::SExpression qw/consp scalarp/; | 
|  | 1 |  |  |  |  | 13275 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 11 | 1 |  |  | 1 |  | 44 | use Scalar::Util qw/looks_like_number/; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1077 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | our $VERSION = '0.005'; | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | our %TYPES = ( | 
| 16 |  |  |  |  |  |  | LIST => 0, | 
| 17 |  |  |  |  |  |  | SYMBOL => 1, | 
| 18 |  |  |  |  |  |  | NUMBER => 1, | 
| 19 |  |  |  |  |  |  | VAR => 2, | 
| 20 |  |  |  |  |  |  | VARIABLE => 2, | 
| 21 |  |  |  |  |  |  | CLOSURE => 3, | 
| 22 |  |  |  |  |  |  | PROC => 4, | 
| 23 |  |  |  |  |  |  | PROCEDURE => 4, | 
| 24 |  |  |  |  |  |  | IF => 5, | 
| 25 |  |  |  |  |  |  | COND => 5, | 
| 26 |  |  |  |  |  |  | CONDITIONAL => 5, | 
| 27 |  |  |  |  |  |  | CALL => 6, | 
| 28 |  |  |  |  |  |  | QUOTE => 7, | 
| 29 |  |  |  |  |  |  | QUOTED => 7, | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | MORE => 0, | 
| 32 |  |  |  |  |  |  | CAR => 1, | 
| 33 |  |  |  |  |  |  | CDR => 2, | 
| 34 |  |  |  |  |  |  | CONS => 3, | 
| 35 |  |  |  |  |  |  | ATOM => 4, | 
| 36 |  |  |  |  |  |  | PROGN => 5, | 
| 37 |  |  |  |  |  |  | 'REVERSE-LIST' => 6, | 
| 38 |  |  |  |  |  |  | FUNCALL => 7, | 
| 39 |  |  |  |  |  |  | ); | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | *consp = *Data::SExpression::consp; | 
| 42 |  |  |  |  |  |  | *scalarp = *Data::SExpression::scalarp; | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | sub process { | 
| 45 | 36 |  |  | 36 | 1 | 60 | my ($self, $sexp, $location) = @_; | 
| 46 | 36 | 100 |  |  |  | 68 | die 'Toplevel is not a list: ', Dumper($sexp), "\n" unless ref $sexp eq 'ARRAY'; | 
| 47 | 35 |  |  |  |  | 67 | my ($type, @addrs) = @$sexp; | 
| 48 | 35 |  |  |  |  | 33 | my $addr; | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 35 | 100 |  |  |  | 50 | die 'Type of toplevel is not atom: '. Dumper($type), "\n" unless scalarp($type); | 
| 51 |  |  |  |  |  |  |  | 
| 52 | 34 | 100 |  |  |  | 179 | if (@addrs > 1) { | 
| 53 | 8 |  |  |  |  | 12 | $addr = $self->{freeptr} + 1; | 
| 54 | 8 |  |  |  |  | 9 | $self->{freeptr} += @addrs; | 
| 55 | 8 |  |  |  |  | 24 | $self->process($addrs[$_], $addr + $_) for 0 .. $#addrs; | 
| 56 |  |  |  |  |  |  | } else { | 
| 57 | 26 |  |  |  |  | 38 | $addr = $addrs[0]; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 34 | 100 |  |  |  | 63 | $addr = $self->process($addr) if ref $addr eq 'ARRAY'; | 
| 61 | 34 | 100 |  |  |  | 50 | die 'Addr of toplevel is not atom: ', Dumper($addr), "\n" unless scalarp($addr); | 
| 62 | 33 |  |  |  |  | 146 | my ($comment_type, $comment_addr) = ($type, $addr); | 
| 63 | 33 | 100 |  |  |  | 78 | die 'Computed addr is not a number: ', Dumper($addr), "\n" unless looks_like_number $addr; | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 32 | 100 |  |  |  | 110 | if (!looks_like_number $type) { | 
| 66 | 30 | 100 |  |  |  | 1096 | die "No such type: $type\n" unless exists $TYPES{$type}; | 
| 67 | 29 |  |  |  |  | 956 | $type = $TYPES{$type}; | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 31 | 100 |  |  |  | 961 | $addr += (1 << $self->{addr_bits}) if $addr < 0; | 
| 71 | 31 | 100 |  |  |  | 97 | die "Type too large: $type\n" if $type >= (1 << $self->{type_bits}); | 
| 72 | 30 | 100 |  |  |  | 66 | die "Addr too large: $addr\n" if $addr >= (1 << $self->{addr_bits}); | 
| 73 | 29 |  |  |  |  | 37 | my $result = ($type << $self->{addr_bits}) + $addr; | 
| 74 | 29 | 100 |  |  |  | 40 | unless ($location) { | 
| 75 | 13 |  |  |  |  | 16 | $self->{freeptr}++; | 
| 76 |  |  |  |  |  |  | $location = $self->{freeptr} | 
| 77 | 13 |  |  |  |  | 14 | } | 
| 78 | 29 |  |  |  |  | 47 | $self->{memory}[$location] = $result; | 
| 79 | 29 |  |  |  |  | 55 | $self->{comment}[$location] = "$comment_type $comment_addr"; | 
| 80 | 29 |  |  |  |  | 1016 | $location | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | sub parse { | 
| 84 | 15 |  |  | 15 | 1 | 25 | my ($self, $string) = @_; | 
| 85 | 15 |  |  |  |  | 69 | my $ds = Data::SExpression->new({symbol_case => 'up', use_symbol_class => 1, fold_lists => 1}); | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 15 |  |  |  |  | 1674 | my $sexp; | 
| 88 | 15 |  |  |  |  | 18 | while () { | 
| 89 | 24 | 100 |  |  |  | 260 | last if $string =~ /^\s*$/; | 
| 90 | 15 |  |  |  |  | 36 | ($sexp, $string) = $ds->read($string); | 
| 91 | 15 |  |  |  |  | 24081 | $self->process($sexp) | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | sub finish { | 
| 96 | 9 |  |  | 9 | 1 | 15 | my ($self) = @_; | 
| 97 | 9 |  |  |  |  | 14 | $self->{memory}[5] = $self->{memory}[$self->{freeptr}]; | 
| 98 | 9 |  |  |  |  | 14 | $self->{comment}[5] = $self->{comment}[$self->{freeptr}]; | 
| 99 | 9 |  |  |  |  | 10 | $self->{memory}[4] = $self->{freeptr}; | 
| 100 | 9 |  |  |  |  | 15 | delete $self->{memory}[$self->{freeptr}] | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | sub new { | 
| 104 | 16 |  |  | 16 | 1 | 8613 | my ($class, %args) = @_; | 
| 105 | 16 |  | 100 |  |  | 101 | $args{type_bits} //= 3; | 
| 106 | 16 |  | 100 |  |  | 38 | $args{addr_bits} //= 8; | 
| 107 | 16 |  | 100 |  |  | 43 | $args{freeptr} //= 6; | 
| 108 | 16 |  | 100 |  |  | 61 | $args{memory} //= [0, 0, (1<<$args{addr_bits}), (1<<$args{addr_bits}), 0, 0, 0]; | 
| 109 | 16 |  |  |  |  | 35 | my @default_comments = ('(cdr part of NIL)', '(car part of NIL)', '(cdr part of T)', '(car part of T)', '(free storage pointer)', '', '(result of computation)'); | 
| 110 | 16 |  |  |  |  | 36 | for (0 .. $#default_comments) { | 
| 111 | 112 |  |  |  |  | 179 | $args{comment}[$_] = $default_comments[$_] | 
| 112 |  |  |  |  |  |  | } | 
| 113 | 16 |  |  |  |  | 78 | bless \%args, $class | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | sub print_binary16 { | 
| 117 | 5 |  |  | 5 | 1 | 7 | my ($self, $fh) = @_; | 
| 118 | 5 |  | 50 |  |  | 9 | $fh //= \*STDOUT; # uncoverable condition right | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 5 | 100 |  |  |  | 30 | die "addr_bits + type_bits >= 16\n"if $self->{addr_bits} + $self->{type_bits} > 16; | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 4 |  |  |  |  | 4 | my $length = @{$self->{memory}}; | 
|  | 4 |  |  |  |  | 7 |  | 
| 123 | 4 | 50 |  |  |  | 19 | print $fh pack 'n', $length or croak "Failed to print memory size: $!"; | 
| 124 | 4 |  |  |  |  | 6 | for (@{$self->{memory}}) { | 
|  | 4 |  |  |  |  | 7 |  | 
| 125 | 41 | 50 |  |  |  | 71 | print $fh pack 'n', $_ or croak "Failed to print memory: $!" | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | sub print_verilog { | 
| 130 | 4 |  |  | 4 | 1 | 8 | my ($self, $fh) = @_; | 
| 131 | 4 |  | 50 |  |  | 8 | $fh //= \*STDOUT; # uncoverable condition right | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 4 |  |  |  |  | 9 | my $bits = $self->{type_bits} + $self->{addr_bits}; | 
| 134 | 4 |  |  |  |  | 5 | my $index_length = length $#{$self->{memory}}; | 
|  | 4 |  |  |  |  | 8 |  | 
| 135 | 4 |  |  |  |  | 9 | my $index_format = '%' . $index_length . 'd'; | 
| 136 | 4 |  |  |  |  | 7 | for my $index (0 .. $#{$self->{memory}}) { | 
|  | 4 |  |  |  |  | 9 |  | 
| 137 | 41 |  |  |  |  | 50 | my $val = $self->{memory}[$index]; | 
| 138 | 41 |  |  |  |  | 45 | my $comment = $self->{comment}[$index]; | 
| 139 | 41 | 100 |  |  |  | 48 | if ($index == 4) { | 
| 140 | 4 |  |  |  |  | 7 | $val = "${bits}'d$val" | 
| 141 |  |  |  |  |  |  | } else { | 
| 142 | 37 | 100 |  |  |  | 82 | $val = $val ? sprintf "%d'b%0${bits}b", $bits, $val : '0'; | 
| 143 |  |  |  |  |  |  | } | 
| 144 | 41 |  |  |  |  | 56 | my $spaces = ' ' x ($bits + 5 - (length $val)); | 
| 145 | 41 |  |  |  |  | 63 | $index = sprintf $index_format, $index; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 41 |  |  |  |  | 54 | my $string = "mem[$index] <= $val;"; | 
| 148 | 41 | 100 |  |  |  | 69 | $string .= "$spaces // $comment" if defined $comment; | 
| 149 | 41 | 50 |  |  |  | 99 | say $fh $string or croak "Failed to print verilog: $!"; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  | sub parse_and_print_binary16 { | 
| 154 | 11 |  |  | 11 | 1 | 37 | my ($self, $string, $fh) = @_; | 
| 155 | 11 |  |  |  |  | 24 | $self->parse($string); | 
| 156 | 5 |  |  |  |  | 12 | $self->finish; | 
| 157 | 5 |  |  |  |  | 11 | $self->print_binary16($fh); | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | sub parse_and_print_verilog { | 
| 161 | 4 |  |  | 4 | 1 | 15 | my ($self, $string, $fh) = @_; | 
| 162 | 4 |  |  |  |  | 9 | $self->parse($string); | 
| 163 | 4 |  |  |  |  | 11 | $self->finish; | 
| 164 | 4 |  |  |  |  | 19 | $self->print_verilog($fh); | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | 1; | 
| 168 |  |  |  |  |  |  | __END__ |