1 package App
::Scheme79asm
;
9 use Data
::Dumper qw
/Dumper/;
10 use Data
::SExpression qw
/consp scalarp/;
11 use Scalar
::Util qw
/looks_like_number/;
13 our $VERSION = '0.004';
41 *consp
= *Data
::SExpression
::consp
;
42 *scalarp
= *Data
::SExpression
::scalarp
;
45 my ($self, $sexp, $location) = @_;
46 die 'Toplevel is not a list: ', Dumper
($sexp), "\n" unless ref $sexp eq 'ARRAY';
47 my ($type, @addrs) = @
$sexp;
50 die 'Type of toplevel is not atom: '. Dumper
($type), "\n" unless scalarp
($type);
53 $addr = $self->{freeptr
} + 1;
54 $self->{freeptr
} += @addrs;
55 $self->process($addrs[$_], $addr + $_) for 0 .. $#addrs;
60 $addr = $self->process($addr) if ref $addr eq 'ARRAY';
61 die 'Addr of toplevel is not atom: ', Dumper
($addr), "\n" unless scalarp
($addr);
62 my ($comment_type, $comment_addr) = ($type, $addr);
63 die 'Computed addr is not a number: ', Dumper
($addr), "\n" unless looks_like_number
$addr;
65 if (!looks_like_number
$type) {
66 die "No such type: $type\n" unless exists $TYPES{$type};
67 $type = $TYPES{$type};
70 $addr += (1 << $self->{addr_bits
}) if $addr < 0;
71 die "Type too large: $type\n" if $type >= (1 << $self->{type_bits
});
72 die "Addr too large: $addr\n" if $addr >= (1 << $self->{addr_bits
});
73 my $result = ($type << $self->{addr_bits
}) + $addr;
76 $location = $self->{freeptr
}
78 $self->{memory
}[$location] = $result;
79 $self->{comment
}[$location] = "$comment_type $comment_addr";
84 my ($self, $string) = @_;
85 my $ds = Data
::SExpression
->new({symbol_case
=> 'up', use_symbol_class
=> 1, fold_lists
=> 1});
89 last if $string =~ /^\s*$/;
90 ($sexp, $string) = $ds->read($string);
97 $self->{memory
}[5] = $self->{memory
}[$self->{freeptr
}];
98 $self->{comment
}[5] = $self->{comment
}[$self->{freeptr
}];
99 $self->{memory
}[4] = $self->{freeptr
};
100 delete $self->{memory
}[$self->{freeptr
}]
104 my ($class, %args) = @_;
105 $args{type_bits
} //= 3;
106 $args{addr_bits
} //= 8;
107 $args{freeptr
} //= 6;
108 $args{memory
} //= [0, 0, (1<<$args{addr_bits
}), (1<<$args{addr_bits
}), 0, 0, 0];
109 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 for (0 .. $#default_comments) {
111 $args{comment
}[$_] = $default_comments[$_]
117 my ($self, $fh) = @_;
118 $fh //= \
*STDOUT
; # uncoverable condition right
120 die "addr_bits + type_bits >= 16\n"if $self->{addr_bits
} + $self->{type_bits
} > 16;
122 my $length = @
{$self->{memory
}};
123 print $fh pack 'n', $length or croak
"Failed to print memory size: $!";
124 for (@
{$self->{memory
}}) {
125 print $fh pack 'n', $_ or croak
"Failed to print memory: $!"
130 my ($self, $fh) = @_;
131 $fh //= \
*STDOUT
; # uncoverable condition right
133 my $bits = $self->{type_bits
} + $self->{addr_bits
};
134 my $index_length = length $#{$self->{memory}};
135 my $index_format = '%' . $index_length . 'd';
136 for my $index (0 .. $#{$self->{memory}}) {
137 my $val = $self->{memory
}[$index];
138 my $comment = $self->{comment
}[$index];
140 $val = "${bits}'d$val"
142 $val = $val ?
sprintf "%d'b%0${bits}b", $bits, $val : '0';
144 my $spaces = ' ' x
($bits + 5 - (length $val));
145 $index = sprintf $index_format, $index;
147 my $string = "mem[$index] <= $val;";
148 $string .= "$spaces // $comment" if defined $comment;
149 say $fh $string or croak
"Failed to print verilog: $!";
153 sub parse_and_print_binary16
{
154 my ($self, $string, $fh) = @_;
155 $self->parse($string);
157 $self->print_binary16($fh);
160 sub parse_and_print_verilog
{
161 my ($self, $string, $fh) = @_;
162 $self->parse($string);
164 $self->print_verilog($fh);
174 App::Scheme79asm - assemble sexp to Verilog ROM for SIMPLE processor
178 use App::Scheme79asm;
179 my $asm = App::Scheme79asm->new(type_bits => 3, addr_bits => 5);
180 $asm->parse_and_print_verilog('(number 70)');
184 SIMPLE is a LISP processor defined in the 1979
185 B<Design of LISP-Based Processors> paper by Steele and Sussman.
187 The SIMPLE processor expects input in a particular tagged-pointer
188 format. This module takes a string containing a sequence of
189 S-expressions. Each S-expression is a list of one of three types:
191 C<(tag value)>, for example C<(symbol 2)>, represents a value to be
192 put in memory (for example a number, or a symbol, or a variable
193 reference). The value must be a number.
195 C<(tag list)>, where C<list> is of one of these three types,
196 represents a tagged pointer. In this case, C<list> is (recursively)
197 laid out in memory as per these rules, and a pointer to that location
198 (and tagged C<tag>) is put somewhere in memory.
200 C<(tag list1 list2)>, where C<list1> and C<list2> are of one of these
201 three types (not necessarily the same type). In this case, C<list1>
202 and C<list2> are (recursively) laid out in memory such that C<list1>
203 is at position X and C<list2> is at position X+1, and a pointer of
204 type tag and value X is put somewhere in memory.
206 After this process the very last pointer placed in memory is moved to
207 the special location 5 (which is where SIMPLE expects to find the
208 expression to be evaluated).
210 In normal use a single S-expression will be supplied, representing an
213 The C<tag> is either a number, a type, or a primitive.
214 The available types are:
220 =item SYMBOL (syn. NUMBER)
222 =item VAR (syn. VARIABLE)
226 =item PROC (syn. PROCEDURE)
228 =item IF (syn. COND, CONDITIONAL)
232 =item QUOTE (syn. QUOTED)
236 The available primitives are:
258 The following methods are available:
262 =item App::Scheme79asm->B<new>([key => value, key => value, ...])
264 Create a new assembler object. Takes a list of keys and values, here
265 are the possible keys:
273 A word is made of a type and an address, with the type occupying the
274 most significant C<type_bits> (default 3) bits, and the address
275 occupying the least significant C<address_bits> (default 8) bits.
276 Therefore the word size is C<type_bits + address_bits> (default 11).
280 A pointer to the last used byte in memory (default 6). The program
281 will be laid out starting with location C<freeptr + 1>.
285 The initial contents of the memory. Note that locations 4, 5, 6 will
286 be overwritten, as will every location larger than the value of
291 The initial comments for memory entries. C<< $comment->[$i] >> is the
292 comment for C<< $memory->[$i] >>. Note that the first 7 entries of
293 this array will be overwritten with the default comments. This is
294 useful when using custom initial memory contents and freeptr, because
295 this key can be used to provide comments for the extra reserved
300 =item $asm->B<parse>(I<$string>)
302 Parse a sequence of S-expressions and lay it out in memory.
303 Can be called multiple times to lay out multiple sequences of
304 S-expressions one after another.
306 =item $asm->B<process>(I<$sexp>)
308 Given an already-parsed sexp (meaning a
309 L<Data::SExpression> object), lay it out in memory.
310 Can be called multiple times to lay out multiple sequences of
311 S-expressions one after another.
313 =item $asm->B<finish>
315 Move the last pointer to position 5, and put the free pointer at
316 position 4. After all sequences of S-expressions have been given to
317 B<parse>, this method should be called.
319 =item $asm->B<print_binary16>([I<$fh>])
321 Print the length of the memory (as a big-endian 16-bit value),
322 followed by the memory contents as a sequence of big-endian 16-bit
323 values to the given filehandle (default STDOUT). Dies if
324 C<addr_bits + type_bits> is more than 16.
326 Big-endian 16-bit values can be decoded with C<unpack 'n', $value>.
328 =item $asm->B<print_verilog>([I<$fh>])
330 Print a block of Verilog code assigning the memory contents to an
331 array named C<mem> to the given filehandle (default STDOUT).
333 =item $asm->B<parse_and_print_binary16>(I<$string>[, I<$fh>])
335 Convenience method that calls B<parse>($string), B<finish>, and then
336 B<print_binary16>($fh).
338 =item $asm->B<parse_and_print_verilog>(I<$string>[, I<$fh>])
340 Convenience method that calls B<parse>($string), B<finish>, and then
341 B<print_verilog>($fh).
347 L<http://repository.readscheme.org/ftp/papers/ai-lab-pubs/AIM-514.pdf>
351 Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
353 =head1 COPYRIGHT AND LICENSE
355 Copyright (C) 2018 by Marius Gavrilescu
357 This library is free software; you can redistribute it and/or modify
358 it under the same terms as Perl itself, either Perl version 5.24.3 or,
359 at your option, any later version of Perl 5 you may have available.
This page took 0.041667 seconds and 4 git commands to generate.