File Coverage

blib/lib/CPU/Z80/Assembler/Program.pm
Criterion Covered Total %
statement 172 174 98.8
branch 51 60 85.0
condition 20 25 80.0
subroutine 26 26 100.0
pod 11 11 100.0
total 280 296 94.5


line stmt bran cond sub pod time code
1             # $Id$
2              
3             package CPU::Z80::Assembler::Program;
4              
5             #------------------------------------------------------------------------------
6              
7             =head1 NAME
8              
9             CPU::Z80::Assembler::Program - Represents one assembly program
10              
11             =cut
12              
13             #------------------------------------------------------------------------------
14              
15 31     31   3888 use strict;
  31         70  
  31         992  
16 31     31   171 use warnings;
  31         64  
  31         1436  
17              
18             our $VERSION = '2.23';
19              
20 31     31   96920 use CPU::Z80::Assembler::Parser;
  31         1089  
  31         12908  
21 31     31   20796 use CPU::Z80::Assembler::Segment;
  31         94  
  31         1095  
22 31     31   218 use CPU::Z80::Assembler::Expr;
  31         75  
  31         765  
23 31     31   159 use CPU::Z80::Assembler::Opcode;
  31         84  
  31         680  
24 31     31   164 use Data::Dump 'dump';
  31         67  
  31         63058  
25              
26              
27             sub new {
28 2792     2792 1 26729 my($class, %args) = @_;
29             bless [
30             $args{_segment_id}, # index of the current segment
31             $args{_segment_map} || {}, # map segment name => index in child
32             $args{child} || [], # list of segments
33             $args{symbols} || {}, # map name => Node with evaluate() method
34             $args{macros} || {}, # list of defined macros
35 2792   50     47109 ], $class;
      50        
      50        
      50        
36             }
37 24782 100   24782   107140 sub _segment_id { defined($_[1]) ? $_[0][0] = $_[1] : $_[0][0] }
38 5621 50   5621   16328 sub _segment_map { defined($_[1]) ? $_[0][1] = $_[1] : $_[0][1] }
39 93324 50   93324 1 284384 sub child { defined($_[1]) ? $_[0][2] = $_[1] : $_[0][2] }
40 5888 50   5888 1 16356 sub symbols { defined($_[1]) ? $_[0][3] = $_[1] : $_[0][3] }
41 144 50   144 1 711 sub macros { defined($_[1]) ? $_[0][4] = $_[1] : $_[0][4] }
42              
43             #------------------------------------------------------------------------------
44              
45             =head1 SYNOPSIS
46              
47             use CPU::Z80::Assembler::Program;
48             my $program = CPU::Z80::Assembler::Program->new(
49             symbols => {},
50             macros => {});
51             $program->parse($input);
52             $segment = $program->segment;
53             $segment = $program->segment("CODE");
54             $segment = $program->split_segment;
55             $program->add_opcodes(@opcodes);
56             $program->add_label($name, $line);
57             $program->org($address);
58             $bytes = $program->bytes;
59             $list_output = CPU::Z80::Assembler::List->new(input => \@input, output => \*STDOUT);
60             $bytes = $program->bytes($list_output);
61              
62             =head1 DESCRIPTION
63              
64             This module defines the class that represents one assembly program composed of
65             L.
66              
67             =head1 EXPORTS
68              
69             Nothing.
70              
71             =head1 FUNCTIONS
72              
73             =head2 new
74              
75             Creates a new object, see L.
76              
77             =head2 child
78              
79             Each child is one L object, in the order found in the
80             program.
81              
82             =head2 symbols
83              
84             Hash of all symbols defined in the program. The key is the symbol name, and
85             the value is either a scalar for a constant, a L for
86             an expression, or a L for a label.
87              
88             =head2 macros
89              
90             Hash of macro names to L objects for all defined macros.
91              
92             =cut
93              
94             #------------------------------------------------------------------------------
95              
96             =head2 parse
97              
98             $program->parse($input);
99              
100             Parse the assembly program and collect the opcodes into the object. $input is
101             a stream of tokens as retrieved by L
102             C.
103              
104             =cut
105              
106             #------------------------------------------------------------------------------
107              
108 2738     2738 1 5582 sub parse { my($self, $input) = @_;
109 2738         8495 z80parser($input, $self);
110             }
111              
112             #------------------------------------------------------------------------------
113              
114             =head2 segment
115              
116             Get/Set the current segment. The current segment is the one where new opcodes
117             are added.
118              
119             When called without arguments returns a L object
120             of the current segment.
121              
122             When called with a $name, it sets the segment with the given name as current.
123             If no such segment exists, a new segment with that name is appended to the list
124             and set current.
125              
126             =cut
127              
128             #------------------------------------------------------------------------------
129              
130             sub segment {
131 24762     24762 1 49688 my($self, $name) = @_;
132            
133 24762 100 100     63982 if (defined($name) || @{$self->child} == 0) {
  24717         53837  
134             # set or get but still no segments -> create
135 2808 100       7855 $name = "_" unless defined($name);
136            
137 2808         7268 my $id = $self->_segment_map->{$name};
138              
139 2808 100       6580 if (! defined $id) {
140             # new segment
141 2783         4876 $id = @{$self->child}; # index of new segment
  2783         5098  
142 2783         9437 my $segment = CPU::Z80::Assembler::Segment->new(name => $name);
143 2783         35327 push(@{$self->child}, $segment);
  2783         6214  
144            
145 2783         6665 $self->_segment_map->{$name} = $id;
146             }
147             # segment exists
148 2808         8597 $self->_segment_id( $id );
149 2808         6388 return $self->child->[$id];
150             }
151             else {
152             # get
153 21954         43831 return $self->child->[ $self->_segment_id ];
154             }
155             }
156              
157              
158             #------------------------------------------------------------------------------
159             # creates a new name based on the given name, with a suffix number to make it
160             # unique
161             sub _build_name {
162 10     10   28 my($self, $name) = @_;
163              
164 10         25 while (exists $self->_segment_map->{$name}) {
165 10   50     85 $name =~ s/(\d*)$/ ($1 || 0) + 1/e;
  10         77  
166             }
167 10         33 return $name;
168             }
169              
170             #------------------------------------------------------------------------------
171              
172             =head2 split_segment
173              
174             Splits the current segment at the current position, creating a new segment,
175             inserting it just after the current one and setting it as current.
176              
177             Returns the new current segment.
178              
179             As a special case, if the current is empty, then nothing is done.
180              
181             This is used to split one segment in two after a second ORG statement.
182              
183             =cut
184              
185             #------------------------------------------------------------------------------
186              
187             sub split_segment {
188 2630     2630 1 5146 my($self) = @_;
189            
190             return $self->segment
191 2630 100       4318 unless @{$self->segment->child}; # if empty, already split
  2630         6165  
192            
193             # segment id
194 10         33 my $old_id = $self->_segment_id;
195 10         23 my $new_id = $old_id + 1;
196            
197             # build a new name
198 10         30 my $old_name = $self->segment->name;
199 10         31 my $new_name = $self->_build_name( $old_name );
200            
201             # make space in the index map for a new item
202 10         26 my $segment_map = $self->_segment_map;
203 10         40 for (keys %$segment_map) {
204 11 100       39 $segment_map->{$_}++ if $segment_map->{$_} >= $new_id;
205             }
206 10         28 $segment_map->{$new_name} = $new_id;
207            
208             # create the segment and insert it in the child list
209 10         36 my $new_segment = CPU::Z80::Assembler::Segment->new(name => $new_name);
210 10         143 splice( @{$self->child}, $new_id, 0, $new_segment );
  10         22  
211            
212 10         31 $self->_segment_id( $new_id );
213 10         22 return $self->child->[ $new_id ];
214             }
215            
216             #------------------------------------------------------------------------------
217              
218             =head2 add_opcodes
219              
220             Adds the opcodes to the current segment.
221              
222             =cut
223              
224             #------------------------------------------------------------------------------
225              
226             sub add_opcodes {
227 19449     19449 1 41313 my($self, @opcodes) = @_;
228              
229 19449 50       62660 $self->segment->add(@opcodes) if @opcodes;
230             }
231              
232             #------------------------------------------------------------------------------
233              
234             =head2 add_label
235              
236             Add a new label at the current position with given name and line. The line
237             is used for error messages and assembly listing.
238              
239             It is an error to add a label twice with the same name.
240              
241             =cut
242              
243             #------------------------------------------------------------------------------
244              
245             sub add_label {
246 100     100 1 17377 my($self, $name, $line) = @_;
247            
248 100         505 my $opcode = CPU::Z80::Assembler::Opcode->new(
249             child => [],
250             line => $line);
251 100         426 $self->add_opcodes($opcode);
252 100 100       513 if (exists $self->symbols->{$name}) {
253 1         10 $line->error("duplicate label definition");
254 0         0 die "not reached";
255             }
256 99         255 $self->symbols->{$name} = $opcode;
257             }
258              
259             #------------------------------------------------------------------------------
260              
261             =head2 org
262              
263             Splits the current segment with split_segment() and sets the start address
264             of the new current segment.
265              
266             =cut
267              
268             #------------------------------------------------------------------------------
269              
270             sub org {
271 2627     2627 1 5816 my($self, $address) = @_;
272            
273 2627         6642 $self->split_segment->address($address);
274             }
275              
276             #------------------------------------------------------------------------------
277             # Allocate addresses for all child segments, starting at
278             # the first segment's C
(defined by a "org" instruction), or at 0.
279             # Returns the first free address after the end of the last segment.
280             sub _locate {
281 2817     2817   5492 my($self) = @_;
282            
283 2817         4965 my @jump_opcodes;
284 2817         8432 $self->_locate_opcodes(0, \@jump_opcodes); # preliminary addresses, get list of jumps
285 2815         8656 $self->_check_short_jumps(\@jump_opcodes); # change short to long junps, as needed
286 2815         6247 $self->_locate_opcodes(1); # final addresses
287             }
288              
289             sub _locate_opcodes {
290 5632     5632   11851 my($self, $final, $jump_opcodes) = @_;
291            
292 5632 50       8181 return unless @{$self->child}; # if empty, nothing to do
  5632         10196  
293            
294             # define start address; only define segment address on final pass
295 5632         11192 my $first = $self->child->[0];
296 5632 100       14407 my $address = defined($first->address) ?
    100          
297             $first->address :
298             $final ?
299             $first->address( 0 ) :
300             0;
301            
302 5632         9081 for my $segment_id (0 .. $#{$self->child}) {
  5632         10080  
303 5658         11061 my $segment = $self->child->[$segment_id];
304              
305             # define start
306 5658 100       11789 if (defined($segment->address)) {
307             # check for overlapping segments
308 5496 100       10356 if ($segment->address < $address) {
    100          
309 2         7 $segment->line->error(sprintf("segments overlap, previous ends at ".
310             "0x%04X, next starts at 0x%04X",
311             $address, $segment->address));
312 0         0 die; # NOT REACHED
313             }
314             # check for new address
315             elsif ($segment->address > $address) {
316 12         26 $address = $segment->address;
317             }
318             }
319             else {
320 162 100       435 $segment->address( $address ) if $final;
321             }
322            
323             # locate the segment
324 5656         9085 for my $opcode_id (0 .. $#{$segment->child}) {
  5656         12555  
325 42272         77890 my $opcode = $segment->child->[$opcode_id];
326            
327 42272         101484 $opcode->address($address); # define opcode address
328 42272 100 100     115891 if ($jump_opcodes && $opcode->can('short_jump_dist')) {
329 3994         10403 push(@$jump_opcodes, [$address, $segment_id, $opcode_id]);
330             }
331              
332 42272         80378 $address += $opcode->size;
333             }
334             }
335            
336 5630         12659 return $address;
337             }
338              
339             # Jump opcodes -> list of [opcode_address, opcode], computed on the first call to _locate()
340             sub _check_short_jumps {
341 2815     2815   5406 my($self, $jump_opcodes) = @_;
342              
343 2815         6855 my $jumps = $self->_compute_slack($jump_opcodes);
344 2815         6914 $self->_change_to_long_jump($jumps);
345             }
346              
347             # compute slack and impacted jumps for each jump
348             sub _compute_slack {
349 2815     2815   5190 my($self, $jump_opcodes) = @_;
350              
351 2815         5398 my $jumps = {};
352 2815         5740 my $symbols = $self->symbols;
353            
354 2815         8510 for (my $i = 0; $i < @$jump_opcodes; $i++) {
355 3994         6280 my($address, $segment_id, $opcode_id) = @{$jump_opcodes->[$i]};
  3994         9649  
356 3994         10896 my $opcode = $self->child->[$segment_id]->child->[$opcode_id];
357            
358 3994         10163 my $dist = $opcode->short_jump_dist($address, $symbols);
359            
360 3994         12651 $jumps->{$address}{segment_id} = $segment_id;
361 3994         7672 $jumps->{$address}{opcode_id} = $opcode_id;
362 3994         18758 $jumps->{$address}{depends} = []; # list of address of other jumps that reduce
363             # their slack if we grow
364            
365 3994         7567 my $target = $address + 2 + $dist;
366 3994 100       8882 if ($dist >= 0) {
367 2022         3668 my $min_target = $address + 2 + 127;
368 2022 100       4235 $min_target = $target if $target < $min_target;
369            
370 2022         4349 $jumps->{$address}{slack} = 127 - $dist;
371 2022   100     10203 for ( my $j = $i + 1;
372             $j < @$jump_opcodes &&
373             (my $depend_address = $jump_opcodes->[$j][0]) < $min_target;
374             $j++ ) {
375 85628         116131 push(@{$jumps->{$depend_address}{depends}}, $address);
  85628         327184  
376             }
377             }
378             else {
379 1972         3641 my $max_target = $address + 2 - 128;
380 1972 100       4970 $max_target = $target if $target > $max_target;
381            
382 1972         3737 $jumps->{$address}{slack} = 128 + $dist;
383 1972   100     9732 for ( my $j = $i - 1;
384             $j >= 0 &&
385             (my $depend_address = $jump_opcodes->[$j][0]) >= $max_target;
386             $j-- ) {
387 84684         115746 push(@{$jumps->{$depend_address}{depends}}, $address);
  84684         326325  
388             }
389             }
390             }
391 2815         5533 $jumps;
392             }
393              
394             # go through the list of jumps and change all with negative slack to long jumps
395             # on each change reduce the slack of the dependent jumps accordingly
396             sub _change_to_long_jump {
397 2815     2815   5725 my($self, $jumps) = @_;
398            
399 2815         5247 my $changed;
400 2815         4468 do {
401 2871         4498 $changed = 0;
402 2871         15922 for my $address (keys %$jumps) {
403 5586         8430 my $jump = $jumps->{$address};
404 5586 100       19631 if ($jump->{slack} < 0) {
405             # need to change this
406 1580         2376 my $segment_id = $jump->{segment_id};
407 1580         2266 my $opcode_id = $jump->{opcode_id};
408            
409 1580         2903 my $opcode = $self->child->[$segment_id]->child->[$opcode_id];
410 1580         3407 my $inc_size = $opcode->long_jump->size - $opcode->short_jump->size;
411            
412             # discard the short jump
413 1580         3193 $self->child->[$segment_id]->child->[$opcode_id] = $opcode->long_jump;
414            
415             # impact all dependents
416 1580         2392 for my $depend_address (@{$jump->{depends}}) {
  1580         3042  
417             exists $jumps->{$depend_address}
418 24192 100       47653 and $jumps->{$depend_address}{slack} -= $inc_size;
419             }
420            
421             # delete this from the list
422 1580         2558 delete $jumps->{$address};
423            
424 1580         17518 $changed++;
425             }
426             }
427             } while ($changed);
428             }
429            
430             #------------------------------------------------------------------------------
431              
432             =head2 bytes
433              
434             Allocate addresses for all child segments, starting at
435             the first segment's C
(defined by a "org" instruction), or at 0.
436              
437             Computes the bytes of each segment, and concatenates them together. Returns the
438             complete object code.
439              
440             Gaps between segments are filled with $CPU::Z80::Assembler::fill_byte.
441              
442             $list_output is an optional L object to dump the assembly
443             listing to.
444              
445             =cut
446              
447             #------------------------------------------------------------------------------
448              
449             sub bytes {
450 2816     2816 1 6826 my($self, $list_output) = @_;
451              
452 2816 100       4683 return "" unless @{$self->child}; # if empty, nothing to do
  2816         6267  
453              
454 2814         6817 my $symbols = $self->symbols;
455            
456             # locate the code
457 2814         8167 $self->_locate;
458            
459             # get start address
460 2812         5874 my $address = $self->child->[0]->address;
461              
462             # char used to fill gaps between segments
463 2812 50       8284 my $fill_byte = defined($CPU::Z80::Assembler::fill_byte) ?
464             chr($CPU::Z80::Assembler::fill_byte) :
465             chr(0xFF);
466              
467 2812         5027 my $bytes = "";
468 2812         4179 for my $segment (@{$self->child}) {
  2812         5302  
469            
470             # fill in the gap, if any
471 2824         6281 my $segment_address = $segment->address;
472 2824 100 100     7760 if (length($bytes) && $address != $segment_address) {
473 6         15 my $fill = $segment_address - $address;
474 6 50       19 die if $fill < 0; # ASSERT
475              
476 6         244 $bytes .= $fill_byte x $fill;
477 6         16 $address = $segment_address;
478             }
479              
480             # fill segment bytes
481 2824         4422 for my $opcode (@{$segment->child}) {
  2824         6429  
482 21125         60740 $opcode->address($address);
483 21125         42045 my $opcode_bytes = $opcode->bytes($address, $symbols);
484 21125         33879 $bytes .= $opcode_bytes;
485            
486 21125 50       39088 $list_output->add($opcode->line, $address, $opcode_bytes) if ($list_output);
487            
488 21125         47845 $address += $opcode->size;
489             }
490             }
491 2812         9341 return $bytes;
492             }
493              
494             #------------------------------------------------------------------------------
495              
496             =head1 BUGS and FEEDBACK
497              
498             See L.
499              
500             =head1 SEE ALSO
501              
502             L
503             L
504             L
505             L
506              
507             =head1 AUTHORS, COPYRIGHT and LICENCE
508              
509             See L.
510              
511             =cut
512              
513             #------------------------------------------------------------------------------
514              
515             1;