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   204 use strict;
  31         60  
  31         908  
16 31     31   166 use warnings;
  31         64  
  31         1241  
17              
18             our $VERSION = '2.24';
19              
20 31     31   172 use CPU::Z80::Assembler;
  31         58  
  31         776  
21 31     31   166 use CPU::Z80::Assembler::Parser;
  31         58  
  31         913  
22 31     31   187 use Iterator::Simple::Lookahead;
  31         110  
  31         208  
23 31     31   1109 use Asm::Preproc::Line;
  31         93  
  31         196  
24 31     31   895 use Asm::Preproc::Token;
  31         67  
  31         444  
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 3295370 my($class, %args) = @_;
37             bless [
38             $args{type},
39             $args{line} || Asm::Preproc::Line->new(),
40 20619   66     126833 $args{child} || [],
      100        
41             ], $class;
42             }
43 13105 100   13105 1 47671 sub type { defined($_[1]) ? $_[0][0] = $_[1] : $_[0][0] }
44 7048 100   7048 1 45154 sub line { defined($_[1]) ? $_[0][1] = $_[1] : $_[0][1] }
45 36286 100   36286 1 143152 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 18216 my($self, $input) = @_;
135 33         109 $self->child([]);
136            
137 33         142 my $value = CPU::Z80::Assembler::Parser::parse($input, undef, "expr");
138 27         91 $self->child($value);
139 27         109 $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 134498 sub evaluate { my($self, $address, $symbol_table, $seen) = @_;
165 25779   100     107068 $seen ||= {}; # to detect circular references
166 25779         43469 my @code;
167 25779         37285 for my $token (@{$self->child}) {
  25779         50290  
168 54485         128641 my($type, $value) = ($token->type, $token->value);
169 54485 100       1032454 if ($type eq "NUMBER") {
    100          
    100          
    100          
170 20972         45408 push(@code, $value);
171             }
172             elsif ($type eq "NAME") {
173 15114 100       26622 if ($value eq '$') {
174 6449         13305 push(@code, $address);
175             }
176             else {
177 8665         15524 my $expr = $symbol_table->{$value};
178 8665         12001 my $expr_value;
179            
180 8665 100       16652 defined($expr) or
181             $self->line->error("Symbol '$value' not defined");
182 8664 100       17191 if (ref($expr)) { # compute sub-expression first
183 8643 100       17624 $seen->{$value} and
184             $self->line->error("Circular reference computing '$value'");
185 8642         26446 my %local_seen = (%$seen, $value => 1);
186 8642         23465 $expr_value = $expr->evaluate($address, $symbol_table,
187             \%local_seen);
188             }
189             else {
190 21         37 $expr_value = $expr;
191             }
192 8662         25370 push(@code, $expr_value);
193             }
194             }
195             elsif ($type eq "STRING") {
196 16 100       58 if (length($value) > 2) {
197 7         17 $self->line->warning("Expression $value: extra bytes ignored");
198 7         408 $value = substr($value, 0, 2);
199             }
200 16         32 $value .= "\0\0";
201 16         52 my @bytes = map {ord($_)} split(//, $value);
  53         103  
202 16         46 my $value = $bytes[0] + ($bytes[1] << 8);
203 16         42 push(@code, $value);
204             }
205             elsif ($type eq "EXPR") {
206 2653         5709 my $expr_value = $value->evaluate($address, $symbol_table, $seen);
207 2653         7240 push(@code, $expr_value);
208             }
209             else {
210 15730 50       40519 $type =~ /^[a-z_]/ and # reserved word
211             $self->line->error("Expression '$type': syntax error");
212 15730         33450 push(@code, $type);
213             }
214             }
215 25776 100       56976 return 0 if !@code;
216 25766         69532 my $code = join(" ", @code);
217 25766         1207136 my $value = eval $code;
218 25766 100       96095 if ($@) {
219 1         5 $@ =~ s/ at .*//;
220 1         4 $self->line->error("Expression '$code': $@");
221             }
222              
223 25765         77548 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 7880 sub build { my($self, $expr_text, @init_args) = @_;
250 1801         4022 my $line = $self->line;
251 1801         4611 my $new_expr = ref($self)->new(line => $line, type => $self->type, @init_args);
252 1801         5419 my $token_stream = CPU::Z80::Assembler::z80lexer($expr_text);
253 1801         4856 while (defined(my $token = $token_stream->next)) {
254 10440 100       1210167 if ($token->type eq '{') {
255 1801 100 66     20117 (defined($token_stream->peek) && $token_stream->next->type eq '}')
256             or die "unmatched {}";
257            
258             # refer to this expression
259 1799         275134 push(@{$new_expr->child},
  1799         4498  
260             Asm::Preproc::Token->new(EXPR => $self, $line));
261             }
262             else {
263 8639         97025 $token->line($line);
264 8639         209409 push(@{$new_expr->child}, $token);
  8639         17315  
265             }
266             }
267 1799         432417 $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 23335 sub bytes { my($self, $address, $symbol_table) = @_;
284 11297   100     23687 my $type = $self->type || "";
285 11297         24272 my $value = $self->evaluate($address, $symbol_table);
286            
287 11297         18036 my $ret;
288 11297 100       33336 if ($type eq "w") {
    100          
    100          
289 2732 100       7610 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         11 $self->line->error(sprintf("value -0x%04X out of range", (-$value) & 0xFFFF));
295 0         0 die; # not reached
296             }
297 2730         8041 $ret = pack("v", $value & 0xFFFF); # 16 bit little endian unsigned
298             }
299             elsif ($type eq "ub") {
300 343 100       1126 if ($value > 0xFF) {
    100          
301             # accept values > 0xFF, but issue warning
302 7         25 $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         4 $self->line->error(sprintf("value -0x%02X out of range", (-$value) & 0xFF));
308 0         0 die; # not reached
309             }
310 342         1573 $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       20665 if ($value > 0x7F) {
    100          
316 2         7 $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         23075 $ret = pack("C", $value & 0xFF); # 8 bit unsigned
324             }
325             else {
326 1         10 die "Expr::bytes(): unrecognized type '$type'"; # exception
327             }
328 11289         40817 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;