File Coverage

blib/lib/CPU/Z80/Assembler/Expr.pm
Criterion Covered Total %
statement 100 104 96.1
branch 51 52 98.0
condition 10 12 83.3
subroutine 15 15 100.0
pod 8 8 100.0
total 184 191 96.3


line stmt bran cond sub pod time code
1             # $Id$
2              
3             package CPU::Z80::Assembler::Expr;
4              
5             #------------------------------------------------------------------------------
6              
7             =head1 NAME
8              
9             CPU::Z80::Assembler::Expr - Represents one assembly expression to be computed at link time
10              
11             =cut
12              
13             #------------------------------------------------------------------------------
14              
15 31     31   172 use strict;
  31         54  
  31         808  
16 31     31   138 use warnings;
  31         93  
  31         1141  
17              
18             our $VERSION = '2.25';
19              
20 31     31   150 use CPU::Z80::Assembler;
  31         59  
  31         668  
21 31     31   141 use CPU::Z80::Assembler::Parser;
  31         52  
  31         798  
22 31     31   188 use Iterator::Simple::Lookahead;
  31         73  
  31         204  
23 31     31   885 use Asm::Preproc::Line;
  31         54  
  31         152  
24 31     31   760 use Asm::Preproc::Token;
  31         70  
  31         339  
25              
26             #use Class::Struct (
27             # child => '@', # list of children of this node
28             # line => 'Asm::Preproc::Line',
29             # # line where tokens found
30             # type => '$', # one of:
31             # # "sb" - signed byte
32             # # "ub" - unsigned byte
33             # # "w" - 2 byte word
34             #);
35             sub new {
36 20619     20619 1 2960081 my($class, %args) = @_;
37             bless [
38             $args{type},
39             $args{line} || Asm::Preproc::Line->new(),
40 20619   66     103551 $args{child} || [],
      100        
41             ], $class;
42             }
43 13105 100   13105 1 37684 sub type { defined($_[1]) ? $_[0][0] = $_[1] : $_[0][0] }
44 7048 100   7048 1 38616 sub line { defined($_[1]) ? $_[0][1] = $_[1] : $_[0][1] }
45 36286 100   36286 1 115568 sub child { defined($_[1]) ? $_[0][2] = $_[1] : $_[0][2] }
46              
47             #------------------------------------------------------------------------------
48              
49             =head1 SYNOPSIS
50              
51             use CPU::Z80::Assembler::Expr;
52             my $node = CPU::Z80::Assembler::Expr->new( type => "sb" );
53             $expr->parse($input);
54             $new_expr = $expr->build($expr_text);
55             $value = $expr->evaluate($address, \%symbol_table);
56             $bytes = $expr->bytes($address, \%symbol_table);
57              
58             =head1 DESCRIPTION
59              
60             This module defines the class that represents one assembly expression to be
61             computed at link time.
62              
63             =head1 EXPORTS
64              
65             Nothing.
66              
67             =head1 FUNCTIONS
68              
69             =head2 new
70              
71             Creates a new object, see L.
72              
73             =head2 type
74              
75             The type string has to be defined before the C method is called, and defines
76             how to code the value returned by C into a byte string.
77              
78             Type is one of:
79              
80             =over 4
81              
82             =item "sb"
83              
84             for signed byte - a 8 bit signed value. A larger value is truncated and a warning
85             is issued.
86              
87             =item "ub"
88              
89             for unsigned byte - a 8 bit unsigned value. A larger value is truncated and a warning
90             is issued.
91              
92             =item "w"
93              
94             for word - a 16 bit unsigned value in little endian format. A larger value is truncated,
95             but in this case no warning is issued. The address part above 0xFFFF is considered
96             a bank selector for memory banked systems.
97              
98             A STRING value is computed in little endian format and only the first two characters are used.
99             "ab" is encoded as ord("a")+(ord("b")<<8).
100              
101             =back
102              
103             The text bytes used in defm / deft are a string of bytes in big endian format, not truncated. For example, 0x112233 is stored as the 3-byte sequence 0x11, 0x22 and 0x33.
104              
105             A STRING value is encoded with the list of characters in the string. If the string is
106             used in an expression, then the expression applies to the last character of the string. This allows expressions like "CALL"+0x80 to invert bit 7 of the last character of the string.
107              
108             C-like escape sequences are expanded both in single- and double-quoted strings.
109              
110             =head2 child
111              
112             List of tokens composing the expression.
113              
114             =head2 line
115              
116             Get/set the line - text, file name and line number where the token was read.
117              
118             =cut
119              
120             #------------------------------------------------------------------------------
121              
122             =head2 parse
123              
124             $expr->parse($input);
125              
126             Parses an expression at the given $input stream
127             (L),
128             leaves the stream pointer after the expression and updates the expression object.
129             Dies if the expression cannot be parsed.
130              
131             =cut
132              
133             sub parse {
134 33     33 1 14122 my($self, $input) = @_;
135 33         108 $self->child([]);
136            
137 33         120 my $value = CPU::Z80::Assembler::Parser::parse($input, undef, "expr");
138 27         85 $self->child($value);
139 27         83 $self->line($value->[0]->line);
140             }
141              
142             #------------------------------------------------------------------------------
143              
144             =head2 evaluate
145              
146             $value = $expr->evaluate($address, $symbol_table)
147              
148             Computes the value of the expression, as found at the given address and looking
149             up any referenced symbols from the given symbol table.
150              
151             The address is used to evaluate the value of '$'.
152              
153             The symbol table is a hash of symbol names to values. The value is either a
154             scalar value that is used directly in the expression, or a reference to a
155             sub-expression that is computed recursively by calling its C method.
156              
157             Exits with a fatal error if the expression cannot be evaluated (circular reference,
158             undefined symbol or mathematical error).
159              
160             =cut
161              
162             #------------------------------------------------------------------------------
163              
164 25779     25779 1 107879 sub evaluate { my($self, $address, $symbol_table, $seen) = @_;
165 25779   100     85921 $seen ||= {}; # to detect circular references
166 25779         31895 my @code;
167 25779         30348 for my $token (@{$self->child}) {
  25779         40235  
168 54485         103486 my($type, $value) = ($token->type, $token->value);
169 54485 100       829862 if ($type eq "NUMBER") {
    100          
    100          
    100          
170 20972         37382 push(@code, $value);
171             }
172             elsif ($type eq "NAME") {
173 15114 100       21591 if ($value eq '$') {
174 6449         11159 push(@code, $address);
175             }
176             else {
177 8665         12943 my $expr = $symbol_table->{$value};
178 8665         9462 my $expr_value;
179            
180 8665 100       13988 defined($expr) or
181             $self->line->error("Symbol '$value' not defined");
182 8664 100       14711 if (ref($expr)) { # compute sub-expression first
183 8643 100       15146 $seen->{$value} and
184             $self->line->error("Circular reference computing '$value'");
185 8642         21238 my %local_seen = (%$seen, $value => 1);
186 8642         18605 $expr_value = $expr->evaluate($address, $symbol_table,
187             \%local_seen);
188             }
189             else {
190 21         35 $expr_value = $expr;
191             }
192 8662         20342 push(@code, $expr_value);
193             }
194             }
195             elsif ($type eq "STRING") {
196 16 100       53 if (length($value) > 2) {
197 7         18 $self->line->warning("Expression $value: extra bytes ignored");
198 7         452 $value = substr($value, 0, 2);
199             }
200 16         32 $value .= "\0\0";
201 16         50 my @bytes = map {ord($_)} split(//, $value);
  53         105  
202 16         39 my $value = $bytes[0] + ($bytes[1] << 8);
203 16         43 push(@code, $value);
204             }
205             elsif ($type eq "EXPR") {
206 2653         4805 my $expr_value = $value->evaluate($address, $symbol_table, $seen);
207 2653         5781 push(@code, $expr_value);
208             }
209             else {
210 15730 50       34313 $type =~ /^[a-z_]/ and # reserved word
211             $self->line->error("Expression '$type': syntax error");
212 15730         30163 push(@code, $type);
213             }
214             }
215 25776 100       45947 return 0 if !@code;
216 25766         58285 my $code = join(" ", @code);
217 25766         954473 my $value = eval $code;
218 25766 100       77727 if ($@) {
219 1         7 $@ =~ s/ at .*//;
220 1         4 $self->line->error("Expression '$code': $@");
221             }
222              
223 25765         64560 return $value;
224             }
225              
226             #------------------------------------------------------------------------------
227              
228             =head2 build
229              
230             $new_expr = $expr->build($expr_text)
231             $new_expr = $expr->build($expr_text, @init_args)
232              
233             Build and return a new expresion object with an expression based on the current
234             object. The expression is passed as a string and is lexed by L C.
235             The special token '{}' is used to refer to this expression.
236              
237             For example, to return a new expression object that, when evaluated, gives the double
238             of the current expression object:
239              
240             my $new_expr = $expr->build("2*{}");
241              
242             C<@init_args> can be used to pass parameters to the constructor of the new expression
243             object.
244              
245             =cut
246              
247             #------------------------------------------------------------------------------
248              
249 1801     1801 1 5981 sub build { my($self, $expr_text, @init_args) = @_;
250 1801         3269 my $line = $self->line;
251 1801         3623 my $new_expr = ref($self)->new(line => $line, type => $self->type, @init_args);
252 1801         4640 my $token_stream = CPU::Z80::Assembler::z80lexer($expr_text);
253 1801         4400 while (defined(my $token = $token_stream->next)) {
254 10440 100       987928 if ($token->type eq '{') {
255 1801 100 66     16327 (defined($token_stream->peek) && $token_stream->next->type eq '}')
256             or die "unmatched {}";
257            
258             # refer to this expression
259 1799         226300 push(@{$new_expr->child},
  1799         3673  
260             Asm::Preproc::Token->new(EXPR => $self, $line));
261             }
262             else {
263 8639         77784 $token->line($line);
264 8639         171477 push(@{$new_expr->child}, $token);
  8639         14348  
265             }
266             }
267 1799         354456 $new_expr;
268             }
269              
270             #------------------------------------------------------------------------------
271              
272             =head2 bytes
273              
274             $bytes = $expr->bytes($address, \%symbol_table);
275              
276             Calls C to compute the value of the expression, and converts the
277             value to a one or two byte string, according to the C.
278              
279             =cut
280              
281             #------------------------------------------------------------------------------
282              
283 11297     11297 1 19208 sub bytes { my($self, $address, $symbol_table) = @_;
284 11297   100     18863 my $type = $self->type || "";
285 11297         21141 my $value = $self->evaluate($address, $symbol_table);
286            
287 11297         15047 my $ret;
288 11297 100       27558 if ($type eq "w") {
    100          
    100          
289 2732 100       6251 if ($value > 0xFFFF) {
    100          
290             # silently accept values > 0xFFFF to ignore segment selectors
291             }
292             elsif ($value < -0x8000) {
293             # error if negative value out of range
294 2         5 $self->line->error(sprintf("value -0x%04X out of range", (-$value) & 0xFFFF));
295 0         0 die; # not reached
296             }
297 2730         6527 $ret = pack("v", $value & 0xFFFF); # 16 bit little endian unsigned
298             }
299             elsif ($type eq "ub") {
300 343 100       926 if ($value > 0xFF) {
    100          
301             # accept values > 0xFF, but issue warning
302 7         26 $self->line->warning(sprintf("value 0x%02X truncated to 0x%02X",
303             $value, $value & 0xFF));
304             }
305             elsif ($value < -0x80) {
306             # error if negative value out of range
307 1         3 $self->line->error(sprintf("value -0x%02X out of range", (-$value) & 0xFF));
308 0         0 die; # not reached
309             }
310 342         1215 $ret = pack("C", $value & 0xFF); # 8 bit unsigned
311             }
312             elsif ($type eq "sb") {
313             # error if value outside of signed byte range
314             # used by (ix+d) and jr NN; error if out of range
315 8221 100       17395 if ($value > 0x7F) {
    100          
316 2         10 $self->line->error(sprintf("value 0x%02X out of range", $value));
317 0         0 die; # not reached
318             }
319             elsif ($value < -0x80) {
320 2         5 $self->line->error(sprintf("value -0x%02X out of range", (-$value) & 0xFF));
321 0         0 die; # not reached
322             }
323 8217         19398 $ret = pack("C", $value & 0xFF); # 8 bit unsigned
324             }
325             else {
326 1         8 die "Expr::bytes(): unrecognized type '$type'"; # exception
327             }
328 11289         32939 return $ret;
329             }
330              
331             #------------------------------------------------------------------------------
332              
333             =head1 BUGS and FEEDBACK
334              
335             See L.
336              
337             =head1 SEE ALSO
338              
339             L
340             L
341             L
342              
343             =head1 AUTHORS, COPYRIGHT and LICENCE
344              
345             See L.
346              
347             =cut
348              
349             1;