Better doc and comment handling
[app-scheme79asm.git] / lib / App / Scheme79asm.pm
CommitLineData
509643aa
MG
1package App::Scheme79asm;
2
3use 5.014000;
4use strict;
5use warnings;
6
4a9c3fa9 7use Data::Dumper qw/Dumper/;
509643aa
MG
8use Data::SExpression qw/consp scalarp/;
9use Scalar::Util qw/looks_like_number/;
10
3fff77b7 11our $VERSION = '0.004';
509643aa
MG
12
13our %TYPES = (
14 LIST => 0,
15 SYMBOL => 1,
4a9c3fa9 16 NUMBER => 1,
509643aa
MG
17 VAR => 2,
18 VARIABLE => 2,
19 CLOSURE => 3,
20 PROC => 4,
21 PROCEDURE => 4,
4a9c3fa9 22 IF => 5,
509643aa
MG
23 COND => 5,
24 CONDITIONAL => 5,
25 CALL => 6,
509643aa
MG
26 QUOTE => 7,
27 QUOTED => 7,
4a9c3fa9
MG
28
29 MORE => 0,
30 CAR => 1,
31 CDR => 2,
32 CONS => 3,
33 ATOM => 4,
34 PROGN => 5,
f0289d3d 35 'REVERSE-LIST' => 6,
4a9c3fa9 36 FUNCALL => 7,
509643aa
MG
37);
38
39*consp = *Data::SExpression::consp;
40*scalarp = *Data::SExpression::scalarp;
41
42sub process {
4a9c3fa9
MG
43 my ($self, $sexp, $location) = @_;
44 die 'Toplevel is not a list: ', Dumper($sexp), "\n" unless ref $sexp eq 'ARRAY';
45 my ($type, @addrs) = @$sexp;
46 my $addr;
47
48 die 'Type of toplevel is not atom: '. Dumper($type), "\n" unless scalarp($type);
49
50 if (@addrs > 1) {
51 $addr = $self->{freeptr} + 1;
52 $self->{freeptr} += @addrs;
53 $self->process($addrs[$_], $addr + $_) for 0 .. $#addrs;
54 } else {
55 $addr = $addrs[0];
56 }
57
58 $addr = $self->process($addr) if ref $addr eq 'ARRAY';
59 die 'Addr of toplevel is not atom: ', Dumper($addr), "\n" unless scalarp($addr);
34073a4a 60 my ($comment_type, $comment_addr) = ($type, $addr);
4a9c3fa9 61 die 'Computed addr is not a number: ', Dumper($addr), "\n" unless looks_like_number $addr;
509643aa 62
ab8f838f 63 if (!looks_like_number $type) {
509643aa
MG
64 die "No such type: $type\n" unless exists $TYPES{$type};
65 $type = $TYPES{$type};
509643aa
MG
66 }
67
f0289d3d 68 $addr += (1 << $self->{addr_bits}) if $addr < 0;
509643aa
MG
69 die "Type too large: $type\n" unless $type < (1 << $self->{type_bits});
70 die "Addr too large: $addr\n" unless $addr < (1 << $self->{addr_bits});
71 my $result = ($type << $self->{addr_bits}) + $addr;
4a9c3fa9
MG
72 unless ($location) {
73 $self->{freeptr}++;
74 $location = $self->{freeptr}
75 }
76 $self->{memory}[$location] = $result;
34073a4a 77 $self->{comment}[$location] = "$comment_type $comment_addr";
4a9c3fa9 78 $location
509643aa
MG
79}
80
81sub parse {
82 my ($self, $string) = @_;
4a9c3fa9 83 my $ds = Data::SExpression->new({symbol_case => 'up', use_symbol_class => 1, fold_lists => 1});
509643aa
MG
84
85 my $sexp;
86 while () {
87 last if $string =~ /^\s*$/;
88 ($sexp, $string) = $ds->read($string);
89 $self->process($sexp)
90 }
91}
92
93sub finish {
94 my ($self) = @_;
95 $self->{memory}[5] = $self->{memory}[$self->{freeptr}];
34073a4a 96 $self->{comment}[5] = $self->{comment}[$self->{freeptr}];
509643aa
MG
97 $self->{memory}[4] = $self->{freeptr};
98 delete $self->{memory}[$self->{freeptr}]
99}
100
101sub new {
102 my ($class, %args) = @_;
103 $args{type_bits} //= 3;
104 $args{addr_bits} //= 8;
105 $args{freeptr} //= 6;
106 $args{memory} //= [0, 0, (1<<$args{addr_bits}), (1<<$args{addr_bits}), 0, 0, 0];
5ff3fe85
MG
107 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)');
108 for (0 .. $#default_comments) {
109 $args{comment}[$_] = $default_comments[$_]
110 }
509643aa
MG
111 bless \%args, $class
112}
113
f0289d3d
MG
114sub print_binary16 {
115 my ($self, $fh) = @_;
744f5c2a 116 $fh //= \*STDOUT; # uncoverable condition right
f0289d3d 117
26bb1344
MG
118 die "addr_bits + type_bits >= 16\n"if $self->{addr_bits} + $self->{type_bits} > 16;
119
f0289d3d
MG
120 my $length = @{$self->{memory}};
121 print $fh pack('n', $length);
122 for (@{$self->{memory}}) {
123 print $fh pack('n', $_)
124 }
125}
126
127sub print_verilog {
509643aa 128 my ($self, $fh) = @_;
744f5c2a 129 $fh //= \*STDOUT; # uncoverable condition right
509643aa
MG
130
131 my $bits = $self->{type_bits} + $self->{addr_bits};
34073a4a
MG
132 my $index_length = length $#{$self->{memory}};
133 my $index_format = '%' . $index_length . 'd';
509643aa
MG
134 for my $index (0 .. $#{$self->{memory}}) {
135 my $val = $self->{memory}[$index];
34073a4a 136 my $comment = $self->{comment}[$index];
509643aa
MG
137 if ($index == 4) {
138 $val = "${bits}'d$val"
139 } else {
140 $val = $val ? sprintf "%d'b%0${bits}b", $bits, $val : '0';
141 }
34073a4a
MG
142 my $spaces = ' ' x ($bits + 5 - (length $val));
143 $index = sprintf $index_format, $index;
744f5c2a
MG
144
145 print $fh "mem[$index] <= $val;";
146 print $fh "$spaces // $comment" if defined $comment;
147 print $fh "\n";
509643aa 148 }
f0289d3d
MG
149
150}
151sub parse_and_print_binary16 {
152 my ($self, $string, $fh) = @_;
153 $self->parse($string);
154 $self->finish;
155 $self->print_binary16($fh);
509643aa
MG
156}
157
f0289d3d 158sub parse_and_print_verilog {
509643aa
MG
159 my ($self, $string, $fh) = @_;
160 $self->parse($string);
161 $self->finish;
f0289d3d 162 $self->print_verilog($fh);
509643aa
MG
163}
164
1651;
166__END__
167
168=encoding utf-8
169
170=head1 NAME
171
172App::Scheme79asm - assemble sexp to Verilog ROM for SIMPLE processor
173
174=head1 SYNOPSIS
175
176 use App::Scheme79asm;
177 my $asm = App::Scheme79asm->new(type_bits => 3, addr_bits => 5);
f0289d3d 178 $asm->parse_and_print_verilog('(number 70)');
509643aa
MG
179
180=head1 DESCRIPTION
181
509643aa
MG
182SIMPLE is a LISP processor defined in the 1979
183B<Design of LISP-Based Processors> paper by Steele and Sussman.
184
185The SIMPLE processor expects input in a particular tagged-pointer
186format. This module takes a string containing a sequence of
9924aa7b
MG
187S-expressions. Each S-expression is a list of one of three types:
188
744f5c2a 189C<(tag value)>, for example C<(symbol 2)>, represents a value to be
9924aa7b 190put in memory (for example a number, or a symbol, or a variable
5ff3fe85 191reference). The value must be a number.
9924aa7b
MG
192
193C<(tag list)>, where C<list> is of one of these three types,
194represents a tagged pointer. In this case, C<list> is (recursively)
195laid out in memory as per these rules, and a pointer to that location
196(and tagged C<tag>) is put somewhere in memory.
197
198C<(tag list1 list2)>, where C<list1> and C<list2> are of one of these
199three types (not necessarily the same type). In this case, C<list1>
200and C<list2> are (recursively) laid out in memory such that C<list1>
201is at position X and C<list2> is at position X+1, and a pointer of
202type tag and value X is put somewhere in memory.
203
204After this process the very last pointer placed in memory is moved to
205the special location 5 (which is where SIMPLE expects to find the
206expression to be evaluated).
207
208In normal use a single S-expression will be supplied, representing an
209entire program.
210
68367057
MG
211The C<tag> is either a number, a type, or a primitive.
212The available types are:
509643aa 213
68367057
MG
214=over
215
216=item LIST
217
218=item SYMBOL (syn. NUMBER)
219
220=item VAR (syn. VARIABLE)
221
222=item CLOSURE
223
224=item PROC (syn. PROCEDURE)
225
226=item IF (syn. COND, CONDITIONAL)
227
228=item CALL
229
230=item QUOTE (syn. QUOTED)
231
232=back
233
234The available primitives are:
235
236=over
237
238=item MORE
239
240=item CAR
241
242=item CDR
243
244=item CONS
245
246=item ATOM
247
248=item PROGN
249
f0289d3d 250=item REVERSE-LIST
68367057
MG
251
252=item FUNCALL
253
254=back
255
256The following methods are available:
257
258=over
259
260=item App::Scheme79asm->B<new>([key => value, key => value, ...])
261
262Create a new assembler object. Takes a list of keys and values, here
263are the possible keys:
264
265=over
266
267=item type_bits
268
269=item address_bits
270
271A word is made of a type and an address, with the type occupying the
272most significant C<type_bits> (default 3) bits, and the address
273occupying the least significant C<address_bits> (default 8) bits.
5ff3fe85 274Therefore the word size is C<type_bits + address_bits> (default 11).
68367057
MG
275
276=item freeptr
277
278A pointer to the last used byte in memory (default 6). The program
279will be laid out starting with location C<freeptr + 1>.
280
281=item memory
282
283The initial contents of the memory. Note that locations 4, 5, 6 will
284be overwritten, as will every location larger than the value of
285C<freeptr>.
286
287=item comment
288
289The initial comments for memory entries. C<< $comment->[$i] >> is the
5ff3fe85
MG
290comment for C<< $memory->[$i] >>. Note that the first 7 entries of
291this array will be overwritten with the default comments. This is
292useful when using custom initial memory contents and freeptr, because
293this key can be used to provide comments for the extra reserved
294locations in memory.
68367057 295
68367057
MG
296=back
297
298=item $asm->B<parse>(I<$string>)
299
300Parse a sequence of S-expressions and lay it out in memory.
301Can be called multiple times to lay out multiple sequences of
302S-expressions one after another.
303
8ff4c670
MG
304=item $asm->B<process>(I<$sexp>)
305
306Given an already-parsed sexp (meaning a
307L<Data::SExpression> object), lay it out in memory.
308Can be called multiple times to lay out multiple sequences of
309S-expressions one after another.
310
68367057
MG
311=item $asm->B<finish>
312
313Move the last pointer to position 5, and put the free pointer at
314position 4. After all sequences of S-expressions have been given to
315B<parse>, this method should be called.
316
f0289d3d
MG
317=item $asm->B<print_binary16>([I<$fh>])
318
319Print the length of the memory (as a big-endian 16-bit value),
320followed by the memory contents as a sequence of big-endian 16-bit
321values to the given filehandle (default STDOUT). Dies if
322C<addr_bits + type_bits> is more than 16.
323
324Big-endian 16-bit values can be decoded with C<unpack 'n', $value>.
325
326=item $asm->B<print_verilog>([I<$fh>])
68367057
MG
327
328Print a block of Verilog code assigning the memory contents to an
329array named C<mem> to the given filehandle (default STDOUT).
330
f0289d3d
MG
331=item $asm->B<parse_and_print_binary16>(I<$string>[, I<$fh>])
332
333Convenience method that calls B<parse>($string), B<finish>, and then
334B<print_binary16>($fh).
335
336=item $asm->B<parse_and_print_verilog>(I<$string>[, I<$fh>])
68367057
MG
337
338Convenience method that calls B<parse>($string), B<finish>, and then
f0289d3d 339B<print_verilog>($fh).
68367057
MG
340
341=back
509643aa
MG
342
343=head1 SEE ALSO
344
345L<http://repository.readscheme.org/ftp/papers/ai-lab-pubs/AIM-514.pdf>
346
347=head1 AUTHOR
348
349Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
350
351=head1 COPYRIGHT AND LICENSE
352
353Copyright (C) 2018 by Marius Gavrilescu
354
355This library is free software; you can redistribute it and/or modify
356it under the same terms as Perl itself, either Perl version 5.24.3 or,
357at your option, any later version of Perl 5 you may have available.
358
359
360=cut
This page took 0.031101 seconds and 4 git commands to generate.