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   202 use strict;
  31         64  
  31         913  
16 31     31   158 use warnings;
  31         66  
  31         1240  
17              
18             our $VERSION = '2.23';
19              
20 31     31   172 use CPU::Z80::Assembler;
  31         66  
  31         755  
21 31     31   173 use CPU::Z80::Assembler::Parser;
  31         68  
  31         929  
22 31     31   194 use Iterator::Simple::Lookahead;
  31         96  
  31         229  
23 31     31   1148 use Asm::Preproc::Line;
  31         78  
  31         173  
24 31     31   927 use Asm::Preproc::Token;
  31         71  
  31         423  
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 3453219 my($class, %args) = @_;
37             bless [
38             $args{type},
39             $args{line} || Asm::Preproc::Line->new(),
40 20619   66     129936 $args{child} || [],
      100        
41             ], $class;
42             }
43 13105 100   13105 1 48734 sub type { defined($_[1]) ? $_[0][0] = $_[1] : $_[0][0] }
44 7048 100   7048 1 44598 sub line { defined($_[1]) ? $_[0][1] = $_[1] : $_[0][1] }
45 36286 100   36286 1 150967 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 14779 my($self, $input) = @_;
135 33         104 $self->child([]);
136            
137 33         145 my $value = CPU::Z80::Assembler::Parser::parse($input, undef, "expr");
138 27         93 $self->child($value);
139 27         104 $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 120041 sub evaluate { my($self, $address, $symbol_table, $seen) = @_;
165 25779   100     107975 $seen ||= {}; # to detect circular references
166 25779         41037 my @code;
167 25779         36423 for my $token (@{$self->child}) {
  25779         48890  
168 54485         133330 my($type, $value) = ($token->type, $token->value);
169 54485 100       1055761 if ($type eq "NUMBER") {
    100          
    100          
    100          
170 20972         46249 push(@code, $value);
171             }
172             elsif ($type eq "NAME") {
173 15114 100       25653 if ($value eq '$') {
174 6449         13212 push(@code, $address);
175             }
176             else {
177 8665         16765 my $expr = $symbol_table->{$value};
178 8665         12017 my $expr_value;
179            
180 8665 100       18287 defined($expr) or
181             $self->line->error("Symbol '$value' not defined");
182 8664 100       16990 if (ref($expr)) { # compute sub-expression first
183 8643 100       17605 $seen->{$value} and
184             $self->line->error("Circular reference computing '$value'");
185 8642         27122 my %local_seen = (%$seen, $value => 1);
186 8642         22426 $expr_value = $expr->evaluate($address, $symbol_table,
187             \%local_seen);
188             }
189             else {
190 21         40 $expr_value = $expr;
191             }
192 8662         24351 push(@code, $expr_value);
193             }
194             }
195             elsif ($type eq "STRING") {
196 16 100       63 if (length($value) > 2) {
197 7         23 $self->line->warning("Expression $value: extra bytes ignored");
198 7         434 $value = substr($value, 0, 2);
199             }
200 16         29 $value .= "\0\0";
201 16         56 my @bytes = map {ord($_)} split(//, $value);
  53         104  
202 16         44 my $value = $bytes[0] + ($bytes[1] << 8);
203 16         42 push(@code, $value);
204             }
205             elsif ($type eq "EXPR") {
206 2653         5804 my $expr_value = $value->evaluate($address, $symbol_table, $seen);
207 2653         7917 push(@code, $expr_value);
208             }
209             else {
210 15730 50       41678 $type =~ /^[a-z_]/ and # reserved word
211             $self->line->error("Expression '$type': syntax error");
212 15730         33751 push(@code, $type);
213             }
214             }
215 25776 100       56566 return 0 if !@code;
216 25766         69736 my $code = join(" ", @code);
217 25766         1232129 my $value = eval $code;
218 25766 100       98587 if ($@) {
219 1         7 $@ =~ s/ at .*//;
220 1         4 $self->line->error("Expression '$code': $@");
221             }
222              
223 25765         75976 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 7919 sub build { my($self, $expr_text, @init_args) = @_;
250 1801         4597 my $line = $self->line;
251 1801         4512 my $new_expr = ref($self)->new(line => $line, type => $self->type, @init_args);
252 1801         5931 my $token_stream = CPU::Z80::Assembler::z80lexer($expr_text);
253 1801         5233 while (defined(my $token = $token_stream->next)) {
254 10440 100       1199672 if ($token->type eq '{') {
255 1801 100 66     20477 (defined($token_stream->peek) && $token_stream->next->type eq '}')
256             or die "unmatched {}";
257            
258             # refer to this expression
259 1799         274395 push(@{$new_expr->child},
  1799         4807  
260             Asm::Preproc::Token->new(EXPR => $self, $line));
261             }
262             else {
263 8639         96044 $token->line($line);
264 8639         208447 push(@{$new_expr->child}, $token);
  8639         18440  
265             }
266             }
267 1799         432636 $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 23210 sub bytes { my($self, $address, $symbol_table) = @_;
284 11297   100     22883 my $type = $self->type || "";
285 11297         25155 my $value = $self->evaluate($address, $symbol_table);
286            
287 11297         18795 my $ret;
288 11297 100       32560 if ($type eq "w") {
    100          
    100          
289 2732 100       7841 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         7 $self->line->error(sprintf("value -0x%04X out of range", (-$value) & 0xFFFF));
295 0         0 die; # not reached
296             }
297 2730         8229 $ret = pack("v", $value & 0xFFFF); # 16 bit little endian unsigned
298             }
299             elsif ($type eq "ub") {
300 343 100       1160 if ($value > 0xFF) {
    100          
301             # accept values > 0xFF, but issue warning
302 7         20 $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         1536 $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       20858 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         6 $self->line->error(sprintf("value -0x%02X out of range", (-$value) & 0xFF));
321 0         0 die; # not reached
322             }
323 8217         24183 $ret = pack("C", $value & 0xFF); # 8 bit unsigned
324             }
325             else {
326 1         10 die "Expr::bytes(): unrecognized type '$type'"; # exception
327             }
328 11289         40674 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;