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   4079 use strict;
  31         63  
  31         982  
16 31     31   160 use warnings;
  31         79  
  31         1412  
17              
18             our $VERSION = '2.24';
19              
20 31     31   100911 use CPU::Z80::Assembler::Parser;
  31         1052  
  31         11992  
21 31     31   19770 use CPU::Z80::Assembler::Segment;
  31         100  
  31         1151  
22 31     31   220 use CPU::Z80::Assembler::Expr;
  31         92  
  31         748  
23 31     31   181 use CPU::Z80::Assembler::Opcode;
  31         75  
  31         701  
24 31     31   171 use Data::Dump 'dump';
  31         117  
  31         61621  
25              
26              
27             sub new {
28 2792     2792 1 27565 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     48824 ], $class;
      50        
      50        
      50        
36             }
37 24782 100   24782   101376 sub _segment_id { defined($_[1]) ? $_[0][0] = $_[1] : $_[0][0] }
38 5621 50   5621   16240 sub _segment_map { defined($_[1]) ? $_[0][1] = $_[1] : $_[0][1] }
39 93324 50   93324 1 282360 sub child { defined($_[1]) ? $_[0][2] = $_[1] : $_[0][2] }
40 5888 50   5888 1 16436 sub symbols { defined($_[1]) ? $_[0][3] = $_[1] : $_[0][3] }
41 144 50   144 1 750 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 5999 sub parse { my($self, $input) = @_;
109 2738         7764 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 48498 my($self, $name) = @_;
132            
133 24762 100 100     65859 if (defined($name) || @{$self->child} == 0) {
  24717         54205  
134             # set or get but still no segments -> create
135 2808 100       8169 $name = "_" unless defined($name);
136            
137 2808         6220 my $id = $self->_segment_map->{$name};
138              
139 2808 100       7108 if (! defined $id) {
140             # new segment
141 2783         4124 $id = @{$self->child}; # index of new segment
  2783         5068  
142 2783         10759 my $segment = CPU::Z80::Assembler::Segment->new(name => $name);
143 2783         35171 push(@{$self->child}, $segment);
  2783         5856  
144            
145 2783         5969 $self->_segment_map->{$name} = $id;
146             }
147             # segment exists
148 2808         8217 $self->_segment_id( $id );
149 2808         6025 return $self->child->[$id];
150             }
151             else {
152             # get
153 21954         43743 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   25 my($self, $name) = @_;
163              
164 10         23 while (exists $self->_segment_map->{$name}) {
165 10   50     82 $name =~ s/(\d*)$/ ($1 || 0) + 1/e;
  10         68  
166             }
167 10         32 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 4624 my($self) = @_;
189            
190             return $self->segment
191 2630 100       4180 unless @{$self->segment->child}; # if empty, already split
  2630         5571  
192            
193             # segment id
194 10         28 my $old_id = $self->_segment_id;
195 10         23 my $new_id = $old_id + 1;
196            
197             # build a new name
198 10         21 my $old_name = $self->segment->name;
199 10         28 my $new_name = $self->_build_name( $old_name );
200            
201             # make space in the index map for a new item
202 10         24 my $segment_map = $self->_segment_map;
203 10         37 for (keys %$segment_map) {
204 11 100       34 $segment_map->{$_}++ if $segment_map->{$_} >= $new_id;
205             }
206 10         27 $segment_map->{$new_name} = $new_id;
207            
208             # create the segment and insert it in the child list
209 10         33 my $new_segment = CPU::Z80::Assembler::Segment->new(name => $new_name);
210 10         128 splice( @{$self->child}, $new_id, 0, $new_segment );
  10         23  
211            
212 10         30 $self->_segment_id( $new_id );
213 10         20 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 39383 my($self, @opcodes) = @_;
228              
229 19449 50       63042 $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 17027 my($self, $name, $line) = @_;
247            
248 100         461 my $opcode = CPU::Z80::Assembler::Opcode->new(
249             child => [],
250             line => $line);
251 100         462 $self->add_opcodes($opcode);
252 100 100       444 if (exists $self->symbols->{$name}) {
253 1         7 $line->error("duplicate label definition");
254 0         0 die "not reached";
255             }
256 99         288 $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 5217 my($self, $address) = @_;
272            
273 2627         6444 $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   5102 my($self) = @_;
282            
283 2817         4588 my @jump_opcodes;
284 2817         7999 $self->_locate_opcodes(0, \@jump_opcodes); # preliminary addresses, get list of jumps
285 2815         8532 $self->_check_short_jumps(\@jump_opcodes); # change short to long junps, as needed
286 2815         6232 $self->_locate_opcodes(1); # final addresses
287             }
288              
289             sub _locate_opcodes {
290 5632     5632   11088 my($self, $final, $jump_opcodes) = @_;
291            
292 5632 50       8261 return unless @{$self->child}; # if empty, nothing to do
  5632         10091  
293            
294             # define start address; only define segment address on final pass
295 5632         12196 my $first = $self->child->[0];
296 5632 100       14438 my $address = defined($first->address) ?
    100          
297             $first->address :
298             $final ?
299             $first->address( 0 ) :
300             0;
301            
302 5632         9341 for my $segment_id (0 .. $#{$self->child}) {
  5632         10671  
303 5658         10437 my $segment = $self->child->[$segment_id];
304              
305             # define start
306 5658 100       12091 if (defined($segment->address)) {
307             # check for overlapping segments
308 5496 100       11028 if ($segment->address < $address) {
    100          
309 2         16 $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         29 $address = $segment->address;
317             }
318             }
319             else {
320 162 100       483 $segment->address( $address ) if $final;
321             }
322            
323             # locate the segment
324 5656         9593 for my $opcode_id (0 .. $#{$segment->child}) {
  5656         12113  
325 42272         77838 my $opcode = $segment->child->[$opcode_id];
326            
327 42272         100302 $opcode->address($address); # define opcode address
328 42272 100 100     115775 if ($jump_opcodes && $opcode->can('short_jump_dist')) {
329 3994         10320 push(@$jump_opcodes, [$address, $segment_id, $opcode_id]);
330             }
331              
332 42272         78507 $address += $opcode->size;
333             }
334             }
335            
336 5630         12292 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   5587 my($self, $jump_opcodes) = @_;
342              
343 2815         6600 my $jumps = $self->_compute_slack($jump_opcodes);
344 2815         6515 $self->_change_to_long_jump($jumps);
345             }
346              
347             # compute slack and impacted jumps for each jump
348             sub _compute_slack {
349 2815     2815   4981 my($self, $jump_opcodes) = @_;
350              
351 2815         5085 my $jumps = {};
352 2815         5593 my $symbols = $self->symbols;
353            
354 2815         8533 for (my $i = 0; $i < @$jump_opcodes; $i++) {
355 3994         6353 my($address, $segment_id, $opcode_id) = @{$jump_opcodes->[$i]};
  3994         10305  
356 3994         11684 my $opcode = $self->child->[$segment_id]->child->[$opcode_id];
357            
358 3994         10059 my $dist = $opcode->short_jump_dist($address, $symbols);
359            
360 3994         12823 $jumps->{$address}{segment_id} = $segment_id;
361 3994         7373 $jumps->{$address}{opcode_id} = $opcode_id;
362 3994         17721 $jumps->{$address}{depends} = []; # list of address of other jumps that reduce
363             # their slack if we grow
364            
365 3994         7585 my $target = $address + 2 + $dist;
366 3994 100       8486 if ($dist >= 0) {
367 2022         3311 my $min_target = $address + 2 + 127;
368 2022 100       4419 $min_target = $target if $target < $min_target;
369            
370 2022         4080 $jumps->{$address}{slack} = 127 - $dist;
371 2022   100     10127 for ( my $j = $i + 1;
372             $j < @$jump_opcodes &&
373             (my $depend_address = $jump_opcodes->[$j][0]) < $min_target;
374             $j++ ) {
375 85628         114129 push(@{$jumps->{$depend_address}{depends}}, $address);
  85628         316897  
376             }
377             }
378             else {
379 1972         3583 my $max_target = $address + 2 - 128;
380 1972 100       4548 $max_target = $target if $target > $max_target;
381            
382 1972         3664 $jumps->{$address}{slack} = 128 + $dist;
383 1972   100     9473 for ( my $j = $i - 1;
384             $j >= 0 &&
385             (my $depend_address = $jump_opcodes->[$j][0]) >= $max_target;
386             $j-- ) {
387 84684         116039 push(@{$jumps->{$depend_address}{depends}}, $address);
  84684         310529  
388             }
389             }
390             }
391 2815         5457 $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   5490 my($self, $jumps) = @_;
398            
399 2815         4204 my $changed;
400 2815         4416 do {
401 2871         4423 $changed = 0;
402 2871         16004 for my $address (keys %$jumps) {
403 5586         8311 my $jump = $jumps->{$address};
404 5586 100       18333 if ($jump->{slack} < 0) {
405             # need to change this
406 1580         2540 my $segment_id = $jump->{segment_id};
407 1580         2337 my $opcode_id = $jump->{opcode_id};
408            
409 1580         3024 my $opcode = $self->child->[$segment_id]->child->[$opcode_id];
410 1580         3223 my $inc_size = $opcode->long_jump->size - $opcode->short_jump->size;
411            
412             # discard the short jump
413 1580         3149 $self->child->[$segment_id]->child->[$opcode_id] = $opcode->long_jump;
414            
415             # impact all dependents
416 1580         2444 for my $depend_address (@{$jump->{depends}}) {
  1580         3045  
417             exists $jumps->{$depend_address}
418 24192 100       46018 and $jumps->{$depend_address}{slack} -= $inc_size;
419             }
420            
421             # delete this from the list
422 1580         2546 delete $jumps->{$address};
423            
424 1580         16172 $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 6615 my($self, $list_output) = @_;
451              
452 2816 100       4455 return "" unless @{$self->child}; # if empty, nothing to do
  2816         6088  
453              
454 2814         7360 my $symbols = $self->symbols;
455            
456             # locate the code
457 2814         7813 $self->_locate;
458            
459             # get start address
460 2812         5818 my $address = $self->child->[0]->address;
461              
462             # char used to fill gaps between segments
463 2812 50       8357 my $fill_byte = defined($CPU::Z80::Assembler::fill_byte) ?
464             chr($CPU::Z80::Assembler::fill_byte) :
465             chr(0xFF);
466              
467 2812         4916 my $bytes = "";
468 2812         4184 for my $segment (@{$self->child}) {
  2812         5402  
469            
470             # fill in the gap, if any
471 2824         5992 my $segment_address = $segment->address;
472 2824 100 100     8384 if (length($bytes) && $address != $segment_address) {
473 6         31 my $fill = $segment_address - $address;
474 6 50       32 die if $fill < 0; # ASSERT
475              
476 6         21 $bytes .= $fill_byte x $fill;
477 6         11 $address = $segment_address;
478             }
479              
480             # fill segment bytes
481 2824         4140 for my $opcode (@{$segment->child}) {
  2824         6359  
482 21125         56626 $opcode->address($address);
483 21125         40282 my $opcode_bytes = $opcode->bytes($address, $symbols);
484 21125         34056 $bytes .= $opcode_bytes;
485            
486 21125 50       39373 $list_output->add($opcode->line, $address, $opcode_bytes) if ($list_output);
487            
488 21125         47484 $address += $opcode->size;
489             }
490             }
491 2812         9296 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;