line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Language::Zcode::Parser::Generic;
|
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
12
|
use strict;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
61
|
|
4
|
2
|
|
|
2
|
|
10
|
use warnings;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
50
|
|
5
|
2
|
|
|
2
|
|
2042
|
use IO::File;
|
|
2
|
|
|
|
|
28366
|
|
|
2
|
|
|
|
|
463
|
|
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
868
|
use Language::Zcode::Util;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
1280
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 Language::Zcode::Parser::Generic
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Base class for Z-code parsers.
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
A Parser reads and parses a Z-code file into a big Perl hash.
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
For finding where the subroutines start and end, you can either depend on
|
16
|
|
|
|
|
|
|
an external call to txd, a 1992 C program, or a beta pure Perl version.
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Everything else is done in pure Perl.
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head2 new (class, args...)
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
Base class does nothing with args
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=cut
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub new {
|
29
|
2
|
|
|
2
|
1
|
6
|
my ($class, @arg) = @_;
|
30
|
2
|
|
|
|
|
15
|
bless {}, $class;
|
31
|
|
|
|
|
|
|
}
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 find_zfile (filename)
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
If the input filename is not found AND the user did not enter, e.g., '.z5' at
|
36
|
|
|
|
|
|
|
the end of the filename, the system will try to find a file ending with .z[1-9]
|
37
|
|
|
|
|
|
|
or .dat.
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Multiple or no matches -> return false
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=cut
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub find_zfile {
|
44
|
0
|
|
|
0
|
1
|
0
|
my ($self, $infile) = @_;
|
45
|
0
|
0
|
|
|
|
0
|
return $infile if -e $infile;
|
46
|
|
|
|
|
|
|
|
47
|
0
|
|
|
|
|
0
|
my $fn = ""; # filename to return
|
48
|
0
|
0
|
|
|
|
0
|
if ($infile !~ /\.(z[1-9]|dat)$/i) {
|
49
|
0
|
|
|
|
|
0
|
my @files = glob("$infile.z[1-9]");
|
50
|
0
|
0
|
|
|
|
0
|
push @files, "$infile.dat" if -e "$infile.dat";
|
51
|
0
|
0
|
|
|
|
0
|
if (@files == 0) {
|
|
|
0
|
|
|
|
|
|
52
|
0
|
|
|
|
|
0
|
warn "No file $infile.z[1-9] or $infile.dat\n";
|
53
|
|
|
|
|
|
|
} elsif (@files > 1) {
|
54
|
0
|
|
|
|
|
0
|
warn "Too many files match $infile.z[1-9] or $infile.dat: @files\n";
|
55
|
|
|
|
|
|
|
} else {
|
56
|
0
|
|
|
|
|
0
|
$fn = $files[0];
|
57
|
|
|
|
|
|
|
}
|
58
|
|
|
|
|
|
|
} else {
|
59
|
0
|
|
|
|
|
0
|
warn "File '$infile' not found\n";
|
60
|
|
|
|
|
|
|
}
|
61
|
|
|
|
|
|
|
|
62
|
0
|
|
|
|
|
0
|
return $fn;
|
63
|
|
|
|
|
|
|
}
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head2 read_memory (infile)
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Reads the given Z-code file into memory
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=cut
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub read_memory {
|
72
|
2
|
|
|
2
|
1
|
582
|
my ($self, $infile) = @_;
|
73
|
|
|
|
|
|
|
# Read in actual Z file
|
74
|
2
|
50
|
|
|
|
19
|
my $ZFILE = new IO::File "<$infile" or die "Zfile: $!";
|
75
|
2
|
|
|
|
|
271
|
binmode $ZFILE;
|
76
|
2
|
|
|
|
|
66
|
my $size = -s $infile;
|
77
|
2
|
|
|
|
|
7
|
my $q = "";
|
78
|
|
|
|
|
|
|
# Read it all into one big string, split it into an array
|
79
|
2
|
|
|
|
|
64
|
my $err = read($ZFILE, $q, $size);
|
80
|
2
|
50
|
|
|
|
9
|
die "Problem reading Z file from Perl: $!" unless defined $err;
|
81
|
2
|
|
|
|
|
3432
|
@Language::Zcode::Util::Memory = unpack('C*', $q);
|
82
|
2
|
|
|
|
|
415
|
close($ZFILE);
|
83
|
|
|
|
|
|
|
}
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head2 parse_header
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Parse Z-code header.
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Creates %Constants, which stores a bunch of constants
|
90
|
|
|
|
|
|
|
like the Z version number, where in memory things are stored, etc.
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=cut
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub parse_header {
|
95
|
2
|
|
|
2
|
1
|
15
|
my $self = shift;
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# see spec section 11:
|
98
|
2
|
|
|
2
|
|
14
|
use constant HEADER_SIZE => 64;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
145
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# These are all addresses in the header of various Z constants
|
101
|
2
|
|
|
2
|
|
22
|
use constant VERSION_NUMBER => 0x00;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
87
|
|
102
|
2
|
|
|
2
|
|
10
|
use constant RELEASE_NUMBER => 0x02;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
143
|
|
103
|
2
|
|
|
2
|
|
11
|
use constant PAGED_MEMORY_ADDRESS => 0x04;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
89
|
|
104
|
2
|
|
|
2
|
|
12
|
use constant FIRST_INSTRUCTION_ADDRESS => 0x06;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
92
|
|
105
|
2
|
|
|
2
|
|
10
|
use constant DICTIONARY_ADDRESS => 0x08;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
100
|
|
106
|
2
|
|
|
2
|
|
138
|
use constant OBJECT_TABLE_ADDRESS => 0x0a;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
94
|
|
107
|
2
|
|
|
2
|
|
10
|
use constant GLOBAL_VARIABLE_ADDRESS => 0x0c;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
89
|
|
108
|
2
|
|
|
2
|
|
9
|
use constant STATIC_MEMORY_ADDRESS => 0x0e;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
79
|
|
109
|
2
|
|
|
2
|
|
11
|
use constant SERIAL_CODE => 0x12;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
92
|
|
110
|
2
|
|
|
2
|
|
10
|
use constant ABBREV_TABLE_ADDRESS => 0x18;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
93
|
|
111
|
2
|
|
|
2
|
|
10
|
use constant FILE_LENGTH => 0x1a;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
76
|
|
112
|
2
|
|
|
2
|
|
10
|
use constant CHECKSUM => 0x1c;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
95
|
|
113
|
2
|
|
|
2
|
|
12
|
use constant INTERPRETER_NUMBER => 0x1e;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
90
|
|
114
|
2
|
|
|
2
|
|
10
|
use constant INTERPRETER_VERSION => 0x1f;
|
|
2
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
83
|
|
115
|
2
|
|
|
2
|
|
9
|
use constant ROUTINES_OFFSET => 0x28;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
91
|
|
116
|
2
|
|
|
2
|
|
10
|
use constant STRINGS_OFFSET => 0x2a;
|
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
107
|
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# interpreter version name; "P" for Plotz
|
119
|
2
|
|
|
2
|
|
59
|
use constant INTERPRETER_CODE => ord "P";
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1570
|
|
120
|
|
|
|
|
|
|
|
121
|
2
|
|
|
|
|
5
|
my %info;
|
122
|
2
|
|
|
|
|
4
|
my $version = $Language::Zcode::Util::Memory[VERSION_NUMBER];
|
123
|
2
|
50
|
33
|
|
|
85
|
if ($version < 1 or $version > 8) {
|
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
124
|
0
|
|
|
|
|
0
|
die "This does not appear to be a valid game file.\n";
|
125
|
|
|
|
|
|
|
} elsif (($version < 3 or $version > 5) and $version != 8) {
|
126
|
0
|
|
|
|
|
0
|
die "Sorry, only z-code versions 3,4,5 and 8 are supported at present...\nAnd even those need work! :)\n"
|
127
|
|
|
|
|
|
|
}
|
128
|
|
|
|
|
|
|
|
129
|
2
|
|
|
|
|
7
|
$info{version} = $version;
|
130
|
2
|
|
|
|
|
10
|
$info{release_number} = Language::Zcode::Util::get_word_at(RELEASE_NUMBER);
|
131
|
2
|
|
|
|
|
7
|
$info{paged_memory_address} = Language::Zcode::Util::get_word_at(PAGED_MEMORY_ADDRESS);
|
132
|
2
|
|
|
|
|
96
|
$info{first_instruction_address} = Language::Zcode::Util::get_word_at(FIRST_INSTRUCTION_ADDRESS);
|
133
|
2
|
|
|
|
|
7
|
$info{dictionary_address} = Language::Zcode::Util::get_word_at(DICTIONARY_ADDRESS);
|
134
|
2
|
|
|
|
|
10
|
$info{object_table_address} = Language::Zcode::Util::get_word_at(OBJECT_TABLE_ADDRESS);
|
135
|
2
|
|
|
|
|
7
|
$info{global_variable_address} = Language::Zcode::Util::get_word_at(GLOBAL_VARIABLE_ADDRESS);
|
136
|
2
|
|
|
|
|
6
|
$info{static_memory_address} = Language::Zcode::Util::get_word_at(STATIC_MEMORY_ADDRESS);
|
137
|
|
|
|
|
|
|
# see zmach06e.txt
|
138
|
2
|
|
|
|
|
9
|
$info{abbrev_table_address} = Language::Zcode::Util::get_word_at(ABBREV_TABLE_ADDRESS);
|
139
|
2
|
|
|
|
|
5
|
my $c = "";
|
140
|
2
|
|
|
|
|
8
|
for (SERIAL_CODE .. SERIAL_CODE + 5) {
|
141
|
12
|
|
|
|
|
31
|
$c .= chr Language::Zcode::Util::get_byte_at($_);
|
142
|
|
|
|
|
|
|
}
|
143
|
2
|
|
|
|
|
8
|
$info{serial_code} = qq{"$c"};
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# set object/dictionary "constants" for this version...
|
146
|
2
|
50
|
|
|
|
8
|
if ($version <= 3) {
|
147
|
|
|
|
|
|
|
# 13.3, 13.4
|
148
|
0
|
|
|
|
|
0
|
$info{encoded_word_length} = 6;
|
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# 12.3.1
|
151
|
0
|
|
|
|
|
0
|
$info{object_bytes} = 9;
|
152
|
0
|
|
|
|
|
0
|
$info{attribute_bytes} = 4;
|
153
|
0
|
|
|
|
|
0
|
$info{pointer_size} = 1;
|
154
|
0
|
|
|
|
|
0
|
$info{max_properties} = 31; # 12.2
|
155
|
0
|
|
|
|
|
0
|
$info{max_objects} = 255; # 12.3.1
|
156
|
|
|
|
|
|
|
} else {
|
157
|
2
|
|
|
|
|
6
|
$info{encoded_word_length} = 9;
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# 12.3.2
|
160
|
2
|
|
|
|
|
6
|
$info{object_bytes} = 14;
|
161
|
2
|
|
|
|
|
5
|
$info{attribute_bytes} = 6;
|
162
|
2
|
|
|
|
|
5
|
$info{pointer_size} = 2;
|
163
|
2
|
|
|
|
|
4
|
$info{max_properties} = 63; # 12.2
|
164
|
2
|
|
|
|
|
8
|
$info{max_objects} = 65535; # 12.3.2
|
165
|
|
|
|
|
|
|
}
|
166
|
2
|
50
|
|
|
|
12
|
die("check your math!")
|
167
|
|
|
|
|
|
|
if (($info{attribute_bytes} + ($info{pointer_size} * 3) + 2)
|
168
|
|
|
|
|
|
|
!= $info{object_bytes});
|
169
|
|
|
|
|
|
|
|
170
|
2
|
|
|
|
|
9
|
my $flen = Language::Zcode::Util::get_word_at(FILE_LENGTH);
|
171
|
2
|
50
|
33
|
|
|
21
|
if ($version <= 3) {
|
|
|
50
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# see 11.1.6
|
173
|
0
|
|
|
|
|
0
|
$flen *= 2;
|
174
|
|
|
|
|
|
|
} elsif ($version == 4 || $version == 5) {
|
175
|
2
|
|
|
|
|
4
|
$flen *= 4;
|
176
|
|
|
|
|
|
|
} else {
|
177
|
0
|
|
|
|
|
0
|
$flen *= 8;
|
178
|
|
|
|
|
|
|
}
|
179
|
2
|
|
|
|
|
5
|
$info{file_length} = $flen;
|
180
|
|
|
|
|
|
|
|
181
|
2
|
|
|
|
|
8
|
$info{file_checksum} = Language::Zcode::Util::get_word_at(CHECKSUM);
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Packed string/routine calculation
|
184
|
2
|
|
|
|
|
23
|
my %packed_mult = (1=>2, 2=>2, 3=>2, 4=>4, 5=>4, 6=>4, 7=>4, 8=>8);
|
185
|
2
|
|
|
|
|
7
|
$info{packed_multiplier} = $packed_mult{$version};
|
186
|
2
|
50
|
|
|
|
7
|
if ($version >= 6) {
|
187
|
0
|
|
|
|
|
0
|
$info{routines_offset} = &Language::Zcode::Util::get_word_at(ROUTINES_OFFSET);
|
188
|
0
|
|
|
|
|
0
|
$info{strings_offset} = &Language::Zcode::Util::get_word_at(STRINGS_OFFSET);
|
189
|
|
|
|
|
|
|
} else {
|
190
|
2
|
|
|
|
|
5
|
$info{routines_offset} = 0;
|
191
|
2
|
|
|
|
|
5
|
$info{strings_offset} = 0;
|
192
|
|
|
|
|
|
|
}
|
193
|
|
|
|
|
|
|
|
194
|
2
|
|
|
|
|
23
|
%Language::Zcode::Util::Constants = %info;
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Now set any data that we know will be true in the output program
|
197
|
|
|
|
|
|
|
# interpreter number
|
198
|
|
|
|
|
|
|
# &set_byte_at(INTERPRETER_NUMBER, $interpreter_id);
|
199
|
2
|
|
|
|
|
12
|
&Language::Zcode::Util::set_byte_at(INTERPRETER_VERSION, INTERPRETER_CODE);
|
200
|
|
|
|
|
|
|
|
201
|
2
|
|
|
|
|
13
|
return;
|
202
|
|
|
|
|
|
|
}
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
1;
|