File Coverage

blib/lib/CPU/Z80/Disassembler/Labels.pm
Criterion Covered Total %
statement 55 55 100.0
branch 14 14 100.0
condition 16 17 94.1
subroutine 13 13 100.0
pod 6 6 100.0
total 104 105 99.0


line stmt bran cond sub pod time code
1             package CPU::Z80::Disassembler::Labels;
2              
3             #------------------------------------------------------------------------------
4              
5             =head1 NAME
6              
7             CPU::Z80::Disassembler::Labels - All labels used in the disassembled program
8              
9             =cut
10              
11             #------------------------------------------------------------------------------
12              
13 7     7   833 use strict;
  7         20  
  7         256  
14 7     7   43 use warnings;
  7         16  
  7         237  
15              
16 7     7   37 use Carp;
  7         14  
  7         422  
17 7     7   489 use Bit::Vector;
  7         1044  
  7         248  
18              
19 7     7   3176 use CPU::Z80::Disassembler::Label;
  7         19  
  7         39  
20 7     7   233 use CPU::Z80::Disassembler::Format;
  7         17  
  7         530  
21              
22             our $VERSION = '1.01';
23              
24             #------------------------------------------------------------------------------
25              
26             =head1 SYNOPSYS
27              
28             $labels = CPU::Z80::Disassembler::Labels->new();
29             $labels->add($addr, $name, $from_addr);
30             $found = $labels->search_addr($addr);
31             $found = $labels->search_name($name);
32             @labels = $labels->search_all;
33             print $labels->max_length;
34             $label = $labels->next_label($addr);
35              
36             =head1 DESCRIPTION
37              
38             Contains an indexed list of all L labels
39             in the disassembled program.
40              
41             Each label is created by the add()
42             method, that simultaneously prepares the indexes for a fast search. There are also
43             methods to search for labels at a given address of with a given name.
44              
45             This module assumes that the address of a label does not change after being defined,
46             i.e. there is never the need to reindex all labels.
47              
48             =head1 FUNCTIONS
49              
50             =head2 new
51              
52             Creates a new empty object.
53              
54             =cut
55              
56             #------------------------------------------------------------------------------
57 7     7   45 use base 'Class::Accessor';
  7         14  
  7         4643  
58             __PACKAGE__->mk_accessors(
59             '_by_addr', # array of labels by address
60             '_by_name', # hash of labels by name
61             'max_length', # max length of all defined labels
62             '_has_label', # Bit::Vector, one bit per address, 1 if label
63             # exists at that address
64             );
65              
66             sub new {
67 19     19 1 629 my($class) = @_;
68 19         244 my $has_label = Bit::Vector->new(0x10000);
69 19         200 return bless { _by_addr => [],
70             _by_name => {},
71             max_length => 0,
72             _has_label => $has_label,
73             }, $class;
74             }
75             #------------------------------------------------------------------------------
76              
77             =head2 add
78              
79             Creates and adds a new label to the indexes. If the same name and address as an
80             existing label is given then the $from_addr is updated.
81              
82             If the name is not given, creates a temporary label of the form L_HHHH.
83              
84             It is an error to add a label already added with a different address.
85              
86             =head2 max_length
87              
88             Length of the longest label name of all defined labels. This is updated when
89             a label is added to the index, and can be used for formating label lists in columns.
90              
91             =cut
92              
93             #------------------------------------------------------------------------------
94             sub add {
95 5319     5319 1 64345 my($self, $addr, $name, $from_addr) = @_;
96              
97 5319         19890 my $temp_name = sprintf("L_%04X", $addr);
98              
99             # check for dupplicate names
100 5319         9455 my $label;
101 5319 100 100     16056 if ( defined($name) &&
      100        
102             defined($label = $self->_by_name->{$name}) &&
103             $label->addr != $addr
104             ) {
105 1         31 croak("Label '$name' with addresses ".format_hex4($label->addr).
106             " and ".format_hex4($addr));
107             }
108            
109             # check for dupplicate address
110 5318 100       33999 if (! defined($label = $self->_by_addr->[$addr])) {
111            
112 2095   66     28890 $label = CPU::Z80::Disassembler::Label
113             ->new($addr, $name || $temp_name);
114            
115             # create index
116             $self->_by_addr->[$addr] =
117 2095         5616 $self->_by_name->{$label->name} =
118             $label;
119 2095         60221 $self->_has_label->Bit_On($addr);
120             }
121             else {
122             # label at that address exists
123 3223 100 100     41947 if ( defined($name) &&
    100 100        
124             $label->name eq $temp_name) {
125            
126             # temp label was given a name
127 1324         15731 $label->name($name);
128            
129 1324         14715 delete $self->_by_name->{$temp_name};
130 1324         16561 $self->_by_name->{$name} = $label;
131             }
132             elsif ( defined($name) &&
133             $label->name ne $name) {
134            
135             # label renamed
136 1         23 croak("Labels '".$label->name."' and '$name' at the same address ".
137             format_hex4($addr));
138             }
139             else {
140             # OK, same address and name
141             }
142             }
143            
144             # define max length
145 5317         42959 my $length = length($label->name);
146 5317 100       55222 $self->max_length($length) if $length > $self->max_length;
147              
148             # add references
149 5317 100       60627 $label->add_refer($from_addr) if defined $from_addr;
150            
151 5317         51334 return $label;
152             }
153             #------------------------------------------------------------------------------
154              
155             =head2 search_addr
156              
157             Return the label object defined at the given address, undef if none.
158              
159             =cut
160              
161             #------------------------------------------------------------------------------
162             sub search_addr {
163 19439     19439 1 594358 my($self, $addr) = @_;
164            
165 19439         40514 return $self->_by_addr->[$addr];
166             }
167             #------------------------------------------------------------------------------
168              
169             =head2 search_name
170              
171             Return the label object with the given name, undef if none.
172              
173             =cut
174              
175             #------------------------------------------------------------------------------
176             sub search_name {
177 30     30 1 1311 my($self, $name) = @_;
178            
179 30         73 return $self->_by_name->{$name};
180             }
181             #------------------------------------------------------------------------------
182              
183             =head2 search_all
184              
185             Return all the defined label objects.
186              
187             =cut
188              
189             #------------------------------------------------------------------------------
190             sub search_all {
191 25     25 1 4376 my($self) = @_;
192            
193 25         54 return sort {$a->name cmp $b->name} values %{$self->_by_name};
  34563         550525  
  25         95  
194             }
195             #------------------------------------------------------------------------------
196              
197             =head2 next_label
198              
199             Return the first label defined on the given address or after. If no address
200             is given, returns the first defined label.
201             Returns undef if there is no label on the address or after.
202              
203             This can be used to find the next label after the current instruction.
204              
205             =cut
206              
207             #------------------------------------------------------------------------------
208             sub next_label {
209 1066     1066 1 31509 my($self, $addr) = @_;
210 1066   100     2343 $addr ||= 0;
211            
212 1066 100       2282 if (my($min,$max) = $self->_has_label->Interval_Scan_inc($addr)) {
213 1059         12392 return $self->search_addr($min);
214             }
215             else {
216 7         137 return undef;
217             }
218             }
219             #------------------------------------------------------------------------------
220              
221             =head1 BUGS, FEEDBACK, AUTHORS, COPYRIGHT and LICENCE
222              
223             See L.
224              
225             =cut
226              
227             1;