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   3466 use strict;
  31         63  
  31         830  
16 31     31   167 use warnings;
  31         57  
  31         1259  
17              
18             our $VERSION = '2.25';
19              
20 31     31   79290 use CPU::Z80::Assembler::Parser;
  31         915  
  31         10078  
21 31     31   16353 use CPU::Z80::Assembler::Segment;
  31         78  
  31         898  
22 31     31   174 use CPU::Z80::Assembler::Expr;
  31         62  
  31         555  
23 31     31   137 use CPU::Z80::Assembler::Opcode;
  31         60  
  31         575  
24 31     31   137 use Data::Dump 'dump';
  31         58  
  31         51280  
25              
26              
27             sub new {
28 2792     2792 1 22120 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     41961 ], $class;
      50        
      50        
      50        
36             }
37 24782 100   24782   82497 sub _segment_id { defined($_[1]) ? $_[0][0] = $_[1] : $_[0][0] }
38 5621 50   5621   13480 sub _segment_map { defined($_[1]) ? $_[0][1] = $_[1] : $_[0][1] }
39 93324 50   93324 1 227989 sub child { defined($_[1]) ? $_[0][2] = $_[1] : $_[0][2] }
40 5888 50   5888 1 13618 sub symbols { defined($_[1]) ? $_[0][3] = $_[1] : $_[0][3] }
41 144 50   144 1 585 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 4607 sub parse { my($self, $input) = @_;
109 2738         6526 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 38319 my($self, $name) = @_;
132            
133 24762 100 100     50723 if (defined($name) || @{$self->child} == 0) {
  24717         42506  
134             # set or get but still no segments -> create
135 2808 100       5691 $name = "_" unless defined($name);
136            
137 2808         5291 my $id = $self->_segment_map->{$name};
138              
139 2808 100       5330 if (! defined $id) {
140             # new segment
141 2783         3228 $id = @{$self->child}; # index of new segment
  2783         4126  
142 2783         7849 my $segment = CPU::Z80::Assembler::Segment->new(name => $name);
143 2783         28371 push(@{$self->child}, $segment);
  2783         4250  
144            
145 2783         4974 $self->_segment_map->{$name} = $id;
146             }
147             # segment exists
148 2808         6583 $self->_segment_id( $id );
149 2808         4382 return $self->child->[$id];
150             }
151             else {
152             # get
153 21954         36540 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   17 my($self, $name) = @_;
163              
164 10         24 while (exists $self->_segment_map->{$name}) {
165 10   50     63 $name =~ s/(\d*)$/ ($1 || 0) + 1/e;
  10         75  
166             }
167 10         27 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 3955 my($self) = @_;
189            
190             return $self->segment
191 2630 100       3201 unless @{$self->segment->child}; # if empty, already split
  2630         5459  
192            
193             # segment id
194 10         18 my $old_id = $self->_segment_id;
195 10         19 my $new_id = $old_id + 1;
196            
197             # build a new name
198 10         20 my $old_name = $self->segment->name;
199 10         24 my $new_name = $self->_build_name( $old_name );
200            
201             # make space in the index map for a new item
202 10         17 my $segment_map = $self->_segment_map;
203 10         29 for (keys %$segment_map) {
204 11 100       31 $segment_map->{$_}++ if $segment_map->{$_} >= $new_id;
205             }
206 10         22 $segment_map->{$new_name} = $new_id;
207            
208             # create the segment and insert it in the child list
209 10         29 my $new_segment = CPU::Z80::Assembler::Segment->new(name => $new_name);
210 10         106 splice( @{$self->child}, $new_id, 0, $new_segment );
  10         19  
211            
212 10         25 $self->_segment_id( $new_id );
213 10         29 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 34238 my($self, @opcodes) = @_;
228              
229 19449 50       52364 $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 16899 my($self, $name, $line) = @_;
247            
248 100         393 my $opcode = CPU::Z80::Assembler::Opcode->new(
249             child => [],
250             line => $line);
251 100         321 $self->add_opcodes($opcode);
252 100 100       322 if (exists $self->symbols->{$name}) {
253 1         6 $line->error("duplicate label definition");
254 0         0 die "not reached";
255             }
256 99         194 $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 4349 my($self, $address) = @_;
272            
273 2627         5460 $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   3976 my($self) = @_;
282            
283 2817         3491 my @jump_opcodes;
284 2817         6540 $self->_locate_opcodes(0, \@jump_opcodes); # preliminary addresses, get list of jumps
285 2815         7240 $self->_check_short_jumps(\@jump_opcodes); # change short to long junps, as needed
286 2815         5228 $self->_locate_opcodes(1); # final addresses
287             }
288              
289             sub _locate_opcodes {
290 5632     5632   9173 my($self, $final, $jump_opcodes) = @_;
291            
292 5632 50       6404 return unless @{$self->child}; # if empty, nothing to do
  5632         7981  
293            
294             # define start address; only define segment address on final pass
295 5632         8882 my $first = $self->child->[0];
296 5632 100       12775 my $address = defined($first->address) ?
    100          
297             $first->address :
298             $final ?
299             $first->address( 0 ) :
300             0;
301            
302 5632         7078 for my $segment_id (0 .. $#{$self->child}) {
  5632         7764  
303 5658         8316 my $segment = $self->child->[$segment_id];
304              
305             # define start
306 5658 100       9891 if (defined($segment->address)) {
307             # check for overlapping segments
308 5496 100       8642 if ($segment->address < $address) {
    100          
309 2         9 $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         17 $address = $segment->address;
317             }
318             }
319             else {
320 162 100       407 $segment->address( $address ) if $final;
321             }
322            
323             # locate the segment
324 5656         8555 for my $opcode_id (0 .. $#{$segment->child}) {
  5656         10552  
325 42272         63171 my $opcode = $segment->child->[$opcode_id];
326            
327 42272         82610 $opcode->address($address); # define opcode address
328 42272 100 100     95594 if ($jump_opcodes && $opcode->can('short_jump_dist')) {
329 3994         7769 push(@$jump_opcodes, [$address, $segment_id, $opcode_id]);
330             }
331              
332 42272         65292 $address += $opcode->size;
333             }
334             }
335            
336 5630         9803 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   4807 my($self, $jump_opcodes) = @_;
342              
343 2815         5405 my $jumps = $self->_compute_slack($jump_opcodes);
344 2815         6000 $self->_change_to_long_jump($jumps);
345             }
346              
347             # compute slack and impacted jumps for each jump
348             sub _compute_slack {
349 2815     2815   4055 my($self, $jump_opcodes) = @_;
350              
351 2815         4060 my $jumps = {};
352 2815         4371 my $symbols = $self->symbols;
353            
354 2815         6889 for (my $i = 0; $i < @$jump_opcodes; $i++) {
355 3994         5632 my($address, $segment_id, $opcode_id) = @{$jump_opcodes->[$i]};
  3994         7750  
356 3994         9272 my $opcode = $self->child->[$segment_id]->child->[$opcode_id];
357            
358 3994         8132 my $dist = $opcode->short_jump_dist($address, $symbols);
359            
360 3994         9572 $jumps->{$address}{segment_id} = $segment_id;
361 3994         6304 $jumps->{$address}{opcode_id} = $opcode_id;
362 3994         15153 $jumps->{$address}{depends} = []; # list of address of other jumps that reduce
363             # their slack if we grow
364            
365 3994         6018 my $target = $address + 2 + $dist;
366 3994 100       7034 if ($dist >= 0) {
367 2022         2639 my $min_target = $address + 2 + 127;
368 2022 100       3459 $min_target = $target if $target < $min_target;
369            
370 2022         3445 $jumps->{$address}{slack} = 127 - $dist;
371 2022   100     8068 for ( my $j = $i + 1;
372             $j < @$jump_opcodes &&
373             (my $depend_address = $jump_opcodes->[$j][0]) < $min_target;
374             $j++ ) {
375 85628         94060 push(@{$jumps->{$depend_address}{depends}}, $address);
  85628         259853  
376             }
377             }
378             else {
379 1972         2737 my $max_target = $address + 2 - 128;
380 1972 100       3555 $max_target = $target if $target > $max_target;
381            
382 1972         3057 $jumps->{$address}{slack} = 128 + $dist;
383 1972   100     7837 for ( my $j = $i - 1;
384             $j >= 0 &&
385             (my $depend_address = $jump_opcodes->[$j][0]) >= $max_target;
386             $j-- ) {
387 84684         91798 push(@{$jumps->{$depend_address}{depends}}, $address);
  84684         261807  
388             }
389             }
390             }
391 2815         4363 $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   4540 my($self, $jumps) = @_;
398            
399 2815         3538 my $changed;
400 2815         4066 do {
401 2871         3870 $changed = 0;
402 2871         12945 for my $address (keys %$jumps) {
403 5586         6702 my $jump = $jumps->{$address};
404 5586 100       14905 if ($jump->{slack} < 0) {
405             # need to change this
406 1580         1912 my $segment_id = $jump->{segment_id};
407 1580         1922 my $opcode_id = $jump->{opcode_id};
408            
409 1580         2498 my $opcode = $self->child->[$segment_id]->child->[$opcode_id];
410 1580         2799 my $inc_size = $opcode->long_jump->size - $opcode->short_jump->size;
411            
412             # discard the short jump
413 1580         2869 $self->child->[$segment_id]->child->[$opcode_id] = $opcode->long_jump;
414            
415             # impact all dependents
416 1580         1922 for my $depend_address (@{$jump->{depends}}) {
  1580         2513  
417             exists $jumps->{$depend_address}
418 24192 100       38385 and $jumps->{$depend_address}{slack} -= $inc_size;
419             }
420            
421             # delete this from the list
422 1580         2186 delete $jumps->{$address};
423            
424 1580         11397 $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 5301 my($self, $list_output) = @_;
451              
452 2816 100       3820 return "" unless @{$self->child}; # if empty, nothing to do
  2816         4750  
453              
454 2814         5418 my $symbols = $self->symbols;
455            
456             # locate the code
457 2814         6820 $self->_locate;
458            
459             # get start address
460 2812         4754 my $address = $self->child->[0]->address;
461              
462             # char used to fill gaps between segments
463 2812 50       6479 my $fill_byte = defined($CPU::Z80::Assembler::fill_byte) ?
464             chr($CPU::Z80::Assembler::fill_byte) :
465             chr(0xFF);
466              
467 2812         4064 my $bytes = "";
468 2812         3620 for my $segment (@{$self->child}) {
  2812         4240  
469            
470             # fill in the gap, if any
471 2824         4865 my $segment_address = $segment->address;
472 2824 100 100     6962 if (length($bytes) && $address != $segment_address) {
473 6         10 my $fill = $segment_address - $address;
474 6 50       14 die if $fill < 0; # ASSERT
475              
476 6         12 $bytes .= $fill_byte x $fill;
477 6         10 $address = $segment_address;
478             }
479              
480             # fill segment bytes
481 2824         3522 for my $opcode (@{$segment->child}) {
  2824         4690  
482 21125         45229 $opcode->address($address);
483 21125         34223 my $opcode_bytes = $opcode->bytes($address, $symbols);
484 21125         28722 $bytes .= $opcode_bytes;
485            
486 21125 50       32830 $list_output->add($opcode->line, $address, $opcode_bytes) if ($list_output);
487            
488 21125         39714 $address += $opcode->size;
489             }
490             }
491 2812         7646 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;