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   734 use strict;
  7         12  
  7         199  
14 7     7   31 use warnings;
  7         13  
  7         241  
15              
16 7     7   33 use Carp;
  7         10  
  7         323  
17 7     7   360 use Bit::Vector;
  7         787  
  7         195  
18              
19 7     7   2512 use CPU::Z80::Disassembler::Label;
  7         15  
  7         30  
20 7     7   195 use CPU::Z80::Disassembler::Format;
  7         12  
  7         440  
21              
22             our $VERSION = '1.02';
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   38 use base 'Class::Accessor';
  7         10  
  7         3726  
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 489 my($class) = @_;
68 19         199 my $has_label = Bit::Vector->new(0x10000);
69 19         172 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 5320     5320 1 58830 my($self, $addr, $name, $from_addr) = @_;
96              
97 5320         18261 my $temp_name = sprintf("L_%04X", $addr);
98              
99             # check for dupplicate names
100 5320         8158 my $label;
101 5320 100 100     14353 if ( defined($name) &&
      100        
102             defined($label = $self->_by_name->{$name}) &&
103             $label->addr != $addr
104             ) {
105 1         21 croak("Label '$name' with addresses ".format_hex4($label->addr).
106             " and ".format_hex4($addr));
107             }
108            
109             # check for dupplicate address
110 5319 100       32573 if (! defined($label = $self->_by_addr->[$addr])) {
111            
112 2095   66     25713 $label = CPU::Z80::Disassembler::Label
113             ->new($addr, $name || $temp_name);
114            
115             # create index
116             $self->_by_addr->[$addr] =
117 2095         4926 $self->_by_name->{$label->name} =
118             $label;
119 2095         53650 $self->_has_label->Bit_On($addr);
120             }
121             else {
122             # label at that address exists
123 3224 100 100     39285 if ( defined($name) &&
    100 100        
124             $label->name eq $temp_name) {
125            
126             # temp label was given a name
127 1324         15004 $label->name($name);
128            
129 1324         14395 delete $self->_by_name->{$temp_name};
130 1324         14592 $self->_by_name->{$name} = $label;
131             }
132             elsif ( defined($name) &&
133             $label->name ne $name) {
134            
135             # label renamed
136 1         18 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 5318         39306 my $length = length($label->name);
146 5318 100       50051 $self->max_length($length) if $length > $self->max_length;
147              
148             # add references
149 5318 100       55261 $label->add_refer($from_addr) if defined $from_addr;
150            
151 5318         44731 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 567066 my($self, $addr) = @_;
164            
165 19439         38041 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 995 my($self, $name) = @_;
178            
179 30         62 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 3710 my($self) = @_;
192            
193 25         45 return sort {$a->name cmp $b->name} values %{$self->_by_name};
  34526         480766  
  25         75  
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 26806 my($self, $addr) = @_;
210 1066   100     2261 $addr ||= 0;
211            
212 1066 100       2000 if (my($min,$max) = $self->_has_label->Interval_Scan_inc($addr)) {
213 1059         10378 return $self->search_addr($min);
214             }
215             else {
216 7         99 return undef;
217             }
218             }
219             #------------------------------------------------------------------------------
220              
221             =head1 BUGS, FEEDBACK, AUTHORS, COPYRIGHT and LICENCE
222              
223             See L.
224              
225             =cut
226              
227             1;