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   873 use strict;
  7         15  
  7         282  
14 7     7   41 use warnings;
  7         15  
  7         211  
15              
16 7     7   48 use Carp;
  7         16  
  7         399  
17 7     7   512 use Bit::Vector;
  7         981  
  7         264  
18              
19 7     7   3219 use CPU::Z80::Disassembler::Label;
  7         20  
  7         49  
20 7     7   231 use CPU::Z80::Disassembler::Format;
  7         15  
  7         581  
21              
22             our $VERSION = '1.00';
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   49 use base 'Class::Accessor';
  7         20  
  7         4642  
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 586 my($class) = @_;
68 19         242 my $has_label = Bit::Vector->new(0x10000);
69 19         206 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 5321     5321 1 63092 my($self, $addr, $name, $from_addr) = @_;
96              
97 5321         20082 my $temp_name = sprintf("L_%04X", $addr);
98              
99             # check for dupplicate names
100 5321         8780 my $label;
101 5321 100 100     15507 if ( defined($name) &&
      100        
102             defined($label = $self->_by_name->{$name}) &&
103             $label->addr != $addr
104             ) {
105 1         28 croak("Label '$name' with addresses ".format_hex4($label->addr).
106             " and ".format_hex4($addr));
107             }
108            
109             # check for dupplicate address
110 5320 100       34319 if (! defined($label = $self->_by_addr->[$addr])) {
111            
112 2095   66     28501 $label = CPU::Z80::Disassembler::Label
113             ->new($addr, $name || $temp_name);
114            
115             # create index
116             $self->_by_addr->[$addr] =
117 2095         5389 $self->_by_name->{$label->name} =
118             $label;
119 2095         61506 $self->_has_label->Bit_On($addr);
120             }
121             else {
122             # label at that address exists
123 3225 100 100     42211 if ( defined($name) &&
    100 100        
124             $label->name eq $temp_name) {
125            
126             # temp label was given a name
127 1324         16002 $label->name($name);
128            
129 1324         14896 delete $self->_by_name->{$temp_name};
130 1324         16578 $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 5319         43697 my $length = length($label->name);
146 5319 100       54132 $self->max_length($length) if $length > $self->max_length;
147              
148             # add references
149 5319 100       60263 $label->add_refer($from_addr) if defined $from_addr;
150            
151 5319         52584 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 603218 my($self, $addr) = @_;
164            
165 19439         41232 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 1255 my($self, $name) = @_;
178            
179 30         78 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 4347 my($self) = @_;
192            
193 25         60 return sort {$a->name cmp $b->name} values %{$self->_by_name};
  34363         546211  
  25         101  
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 32041 my($self, $addr) = @_;
210 1066   100     2180 $addr ||= 0;
211            
212 1066 100       2494 if (my($min,$max) = $self->_has_label->Interval_Scan_inc($addr)) {
213 1059         12322 return $self->search_addr($min);
214             }
215             else {
216 7         133 return undef;
217             }
218             }
219             #------------------------------------------------------------------------------
220              
221             =head1 BUGS, FEEDBACK, AUTHORS, COPYRIGHT and LICENCE
222              
223             See L.
224              
225             =cut
226              
227             1;