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; |