struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / support / scripts / z80-disasm.pl
blobb6d446bf0ed64ee963a9c848ad9d38b0a5e85b3d
1 #!/usr/bin/perl -w
3 =back
5 Copyright (C) 2013-2016, Molnar Karoly <molnarkaroly@users.sf.net>
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
20 2. Altered source versions must be plainly marked as such, and must not be
21 misrepresented as being the original software.
23 3. This notice may not be removed or altered from any source distribution.
25 ================================================================================
27 This program disassembles the hex files. It assumes that the hex file
28 contains Z80 instructions.
30 Proposal for use: ./z80-disasm.pl program.hex > program.dasm
32 $Id$
33 =cut
35 use strict;
36 use warnings;
37 no if $] >= 5.018, warnings => "experimental::smartmatch"; # perl 5.16
38 use 5.12.0; # when (regex)
40 use constant FALSE => 0;
41 use constant TRUE => 1;
43 use constant TAB_LENGTH => 8;
45 ################################################################################
47 use constant INHX8M => 0;
48 use constant INHX32 => 2;
50 use constant INHX_DATA_REC => 0;
51 use constant INHX_EOF_REC => 1;
52 use constant INHX_EXT_LIN_ADDR_REC => 4;
54 use constant EMPTY => -1;
56 use constant COUNT_SIZE => 2;
57 use constant ADDR_SIZE => 4;
58 use constant TYPE_SIZE => 2;
59 use constant BYTE_SIZE => 2;
60 use constant CRC_SIZE => 2;
61 use constant HEADER_SIZE => (COUNT_SIZE + ADDR_SIZE + TYPE_SIZE);
62 use constant MIN_LINE_LENGTH => (HEADER_SIZE + CRC_SIZE);
64 use constant Z80_ROM_SIZE => 0x10000;
66 ################################################################################
68 my $PROGRAM = 'z80-disasm.pl';
70 my $border0 = ('-' x 99);
71 my $border1 = ('#' x 99);
72 my $border2 = ('.' x 39);
74 my @default_paths =
76 '/usr/share/sdcc/include/z180',
77 '/usr/local/share/sdcc/include/z180'
80 my $default_include_path = '';
81 my $include_path = '';
82 my $hex_file = '';
83 my $map_file = '';
84 my $map_readed = FALSE;
85 my $header_file = '';
86 my $name_list = '';
88 my $verbose = 0;
89 my $gen_assembly_code = FALSE;
90 my $no_explanations = FALSE;
91 my $find_lost_labels = FALSE;
93 my @rom = ();
94 my $rom_size = Z80_ROM_SIZE;
95 my %const_areas_by_address = (); # From the command line parameters.
97 my %const_blocks_by_address = ();
99 my %ram_blocks_by_address = ();
100 my %ram_names_by_address = ();
102 =back
103 The structure of one element of the %io_by_address hash:
106 NAME => '',
107 REF_COUNT => 0
109 =cut
111 my %io_by_address = ();
113 # Sizes of the instructions.
115 use constant IPREFIX_DD => -1;
116 use constant IPREFIX_ED => -2;
117 use constant IPREFIX_FD => -3;
119 my @instruction_sizes_ =
121 # 0 1 2 3 4 5 6 7 8 9 A B C D E F
123 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, # 00
124 2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, # 10
125 2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, # 20
126 2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, # 30
127 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40
128 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 50
129 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60
130 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 70
131 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 80
132 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 90
133 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # A0
134 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # B0
135 1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, 2, 3, 3, 2, 1, # C0
136 1, 1, 3, 2, 3, 1, 2, 1, 1, 1, 3, 2, 3,IPREFIX_DD, 2, 1, # D0 -1: DD
137 1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3,IPREFIX_ED, 2, 1, # E0 -2: ED
138 1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3,IPREFIX_FD, 2, 1 # F0 -3: FD
141 my @instruction_sizes_DDFD =
143 # 0 1 2 3 4 5 6 7 8 9 A B C D E F
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, # 00
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, # 10
147 0, 4, 4, 2, 2, 2, 3, 0, 0, 2, 4, 2, 2, 2, 3, 0, # 20
148 0, 0, 0, 0, 3, 3, 4, 0, 0, 2, 0, 0, 0, 0, 0, 0, # 30
149 0, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 2, 2, 3, 0, # 40
150 0, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 2, 2, 3, 0, # 50
151 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, # 60
152 3, 3, 3, 3, 3, 3, 0, 3, 0, 0, 0, 0, 2, 2, 3, 0, # 70
153 0, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 2, 2, 3, 0, # 80
154 0, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 2, 2, 3, 0, # 90
155 0, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 2, 2, 3, 0, # A0
156 0, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 2, 2, 3, 0, # B0
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, # C0 4: CB
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0
159 0, 2, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, # E0
160 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 # F0
163 my @instruction_sizes_ED =
165 # 0 1 2 3 4 5 6 7 8 9 A B C D E F
167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30
171 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, # 40
172 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, # 50
173 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, # 60
174 2, 2, 2, 4, 2, 2, 2, 0, 2, 2, 2, 4, 2, 2, 2, 0, # 70
175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90
177 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, # A0
178 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, # B0
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0
182 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 # F0
185 my $prev_is_jump;
187 use constant SILENT0 => 0;
188 use constant SILENT1 => 1;
190 my $decoder_silent_level;
192 use constant RAM_ALIGN_SIZE => 3;
193 use constant EXPL_ALIGN_SIZE => 5;
194 use constant STAT_ALIGN_SIZE => 6;
195 use constant TBL_COLUMNS => 8;
197 =back
198 The structure of one element of the %blocks_by_address hash:
201 TYPE => 0,
202 ADDR => 0,
203 SIZE => 0,
204 LABEL => {
205 TYPE => 0,
206 NAME => '',
207 PRINTED => FALSE,
208 CALL_COUNT => 0,
209 JUMP_COUNT => 0
211 REF_COUNT => 0
213 =cut
215 use constant BLOCK_INSTR => 0;
216 use constant BLOCK_RAM => 1;
217 use constant BLOCK_CONST => 2;
218 use constant BLOCK_EMPTY => 3;
220 use constant BL_TYPE_NONE => -1;
221 use constant BL_TYPE_SUB => 0;
222 use constant BL_TYPE_LABEL => 1;
223 use constant BL_TYPE_JTABLE => 2;
224 use constant BL_TYPE_VARIABLE => 3;
225 use constant BL_TYPE_CONST => 4;
227 my %label_names =
229 eval BL_TYPE_SUB => 'Function_',
230 eval BL_TYPE_LABEL => 'Label_',
231 eval BL_TYPE_JTABLE => 'Jumptable_',
232 eval BL_TYPE_VARIABLE => 'Variable_',
233 eval BL_TYPE_CONST => 'Constant_'
236 my %empty_blocks_by_address = ();
237 my %blocks_by_address = ();
238 my %labels_by_address = ();
239 my $max_label_addr = 0;
241 my %interrupts_by_address =
243 0x0000 => 'System_init',
244 0x0008 => 'Interrupt_08',
245 0x0010 => 'Interrupt_10',
246 0x0018 => 'Interrupt_18',
247 0x0020 => 'Interrupt_20',
248 0x0028 => 'Interrupt_28',
249 0x0030 => 'Interrupt_30',
250 0x0038 => 'Interrupt_38'
253 my %control_characters =
255 0x00 => '\0',
256 0x07 => '\a',
257 0x08 => '\b',
258 0x09 => '\t',
259 0x0A => '\n',
260 0x0C => '\f',
261 0x0D => '\r',
262 0x1B => '\e',
263 0x7F => '^?'
266 use constant INST_LD_HL => 0x21;
267 use constant INST_ADD_HL_DE => 0x19;
268 use constant INST_JP => 0xC3;
269 use constant INST_JP_HL => 0xE9;
270 use constant INST_JP_CC => 0xC2; # mask: 0xC7
271 use constant INST_JR => 0x18;
272 use constant INST_JR_CC => 0x20; # mask: 0xE7
273 use constant INST_DJNZ => 0x10;
274 use constant INST_CALL => 0xCD;
275 use constant INST_CALL_CC => 0xC4; # mask: 0xC7
276 use constant INST_RET => 0xC9;
277 use constant INST_RETI => 0x4D; # with 0xED prefix
278 use constant INST_RETN => 0x45; # with 0xED prefix
280 my $dcd_address = 0;
281 my $dcd_instr_size = 0;
282 my $dcd_instr = 0;
283 my $dcd_instr_x = 0;
284 my $dcd_instr_y = 0;
285 my $dcd_instr_z = 0;
286 my $dcd_instr_p = 0;
287 my $dcd_instr_q = 0;
288 my $dcd_parm0 = 0;
289 my $dcd_parm1 = 0;
290 my $dcd_parm2 = 0;
292 my $table_header = '';
293 my $table_border = '';
295 ################################################################################
296 ################################################################################
298 my %pp_defines = (); # Value of definitions.
300 my @pp_conditions = ();
301 my @pp_else_conditions = ();
302 my $pp_level = 0; # Shows the lowest level.
303 my $embed_level;
305 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
306 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
307 #@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@
308 #@@@@@@@@@@@@@@@@@@@@@@@ This a simple preprocessor. @@@@@@@@@@@@@@@@@@@@@@@@@
309 #@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@
310 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
311 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
313 # Examines that the parameter is defined or not defined.
315 sub _defined($)
317 return defined($pp_defines{$_[0]});
320 #-------------------------------------------------------------------------------
322 # Records a definition.
324 sub define($)
326 my ($Name) = ($_[0] =~ /^(\S+)/op);
327 my $Body = ${^POSTMATCH};
329 $Body =~ s/^\s+//o;
331 die "define(): This definition already exists: \"$Name\"\n" if (_defined($Name));
333 # The definition is in fact unnecessary.
334 $pp_defines{$Name} = $Body;
337 #-------------------------------------------------------------------------------
339 # Delete a definition.
341 sub undefine($)
343 delete($pp_defines{$_[0]});
346 #-------------------------------------------------------------------------------
348 # Evaluation of the #if give a boolean value. This procedure preserves it.
350 sub if_condition($)
352 my $Val = $_[0];
354 push(@pp_conditions, $Val);
355 push(@pp_else_conditions, $Val);
356 ++$pp_level;
359 #-------------------------------------------------------------------------------
361 # Evaluation of the #else give a boolean value. This procedure preserves it.
363 sub else_condition($$)
365 my ($File, $Line_number) = @_;
367 die "else_condition(): The ${Line_number}th line of $File there is a #else, but does not belong him #if.\n" if ($pp_level <= 0);
369 my $last = $#pp_conditions;
371 if ($last > 0 && $pp_conditions[$last - 1])
373 $pp_conditions[$last] = ($pp_else_conditions[$#pp_else_conditions]) ? FALSE : TRUE;
375 else
377 $pp_conditions[$last] = FALSE;
381 #-------------------------------------------------------------------------------
383 # Closes a logical unit which starts with a #if.
385 sub endif_condition($$)
387 my ($File, $Line_number) = @_;
389 die "endif_condition(): The ${Line_number}th line of $File there is a #endif, but does not belong him #if.\n" if ($pp_level <= 0);
391 pop(@pp_conditions);
392 pop(@pp_else_conditions);
393 --$pp_level;
396 #-------------------------------------------------------------------------------
398 sub reset_preprocessor()
400 %pp_defines = ();
401 @pp_conditions = ();
402 push(@pp_conditions, TRUE);
403 @pp_else_conditions = ();
404 push(@pp_else_conditions, FALSE);
405 $pp_level = 0;
408 #-------------------------------------------------------------------------------
410 # This the preprocessor.
412 sub run_preprocessor($$$$)
414 my ($Fname, $Function, $Line, $Line_number) = @_;
416 if ($Line =~ /^#\s*ifdef\s+(\S+)$/o)
418 if ($pp_conditions[$#pp_conditions])
420 # The ancestor is valid, therefore it should be determined that
421 # the descendants what kind.
423 if_condition(_defined($1));
425 else
427 # The ancestor is invalid, so the descendants will invalid also.
429 if_condition(FALSE);
432 elsif ($Line =~ /^#\s*ifndef\s+(\S+)$/o)
434 if ($pp_conditions[$#pp_conditions])
436 # The ancestor is valid, therefore it should be determined that
437 # the descendants what kind.
439 if_condition(! _defined($1));
441 else
443 # The ancestor is invalid, so the descendants will invalid also.
445 if_condition(FALSE);
448 elsif ($Line =~ /^#\s*else/o)
450 else_condition($Fname, $Line_number);
452 elsif ($Line =~ /^#\s*endif/o)
454 endif_condition($Fname, $Line_number);
456 elsif ($Line =~ /^#\s*define\s+(.+)$/o)
458 # This level is valid, so it should be recorded in the definition.
460 define($1) if ($pp_conditions[$#pp_conditions]);
462 elsif ($Line =~ /^#\s*undef\s+(.+)$/o)
464 # This level is valid, so it should be deleted in the definition.
466 undefine($1) if ($pp_conditions[$#pp_conditions]);
468 elsif ($pp_conditions[$#pp_conditions])
470 # This is a valid line. (The whole magic is in fact therefore there is.)
472 $Function->($Line);
476 ################################################################################
477 ################################################################################
478 ################################################################################
480 sub basename($)
482 return ($_[0] =~ /([^\/]+)$/) ? $1 : '';
485 #-------------------------------------------------------------------------------
487 sub param_exist($$)
489 die "This option \"$_[0]\" requires a parameter.\n" if ($_[1] > $#ARGV);
492 #-------------------------------------------------------------------------------
494 sub Log
496 return if (pop(@_) > $verbose);
497 foreach (@_) { print STDERR $_; }
498 print STDERR "\n";
501 #-------------------------------------------------------------------------------
503 sub str2int($)
505 my $Str = $_[0];
507 return hex($1) if ($Str =~ /^0x([[:xdigit:]]+)$/io);
508 return int($Str) if ($Str =~ /^-?\d+$/o);
510 die "str2int(): This string not integer: \"$Str\"";
513 #-------------------------------------------------------------------------------
516 # Before print, formats the $Text.
519 sub align($$)
521 my ($Text, $Tab_count) = @_;
522 my ($al, $ct);
524 $ct = $Text;
525 1 while $ct =~ s/\t/' ' x (TAB_LENGTH - ($-[0] % TAB_LENGTH))/e;
526 $al = $Tab_count - (int(length($ct) / TAB_LENGTH));
528 # One space will surely becomes behind it.
529 if ($al < 1)
531 return "$Text ";
533 else
535 return ($Text . ("\t" x $al));
539 #-------------------------------------------------------------------------------
542 # Multiple file test.
545 sub is_file_ok($)
547 my $File = $_[0];
549 if (! -e $File)
551 print STDERR "$PROGRAM: Not exists -> \"$File\"\n";
552 exit(1);
555 if (! -f $File)
557 print STDERR "$PROGRAM: Not file -> \"$File\"\n";
558 exit(1);
561 if (! -r $File)
563 print STDERR "$PROGRAM: Can not read -> \"$File\"\n";
564 exit(1);
567 if (! -s $File)
569 print STDERR "$PROGRAM: Empty file -> \"$File\"\n";
570 exit(1);
574 #-------------------------------------------------------------------------------
577 # Initializes the @rom array.
580 sub init_mem($$)
582 my ($Start, $End) = @_;
584 @rom[$Start .. $End] = ((EMPTY) x ($End - $Start + 1));
587 #-------------------------------------------------------------------------------
590 # Store values of the $Code to $AddrRef address.
593 sub store_code($$)
595 my ($Code, $AddrRef) = @_;
597 if ($$AddrRef >= $rom_size)
599 printf STDERR "Warning, this address (0x%04X) outside the code area (0x%04X)!\n", $$AddrRef, $rom_size - 1;
602 Log(sprintf("rom[0x%08X] = 0x%02X", $$AddrRef, $Code), 9);
603 $rom[$$AddrRef++] = $Code;
606 #-------------------------------------------------------------------------------
609 # Reads contents of the $Hex.
612 sub read_hex($)
614 my $Hex = $_[0];
615 my $addr_H = 0;
616 my $format = INHX32;
617 my $line_num = 0;
619 if (! open(IN, '<', $Hex))
621 print STDERR "$PROGRAM : Could not open. -> \"$Hex\"\n";
622 exit(1);
625 while (<IN>)
627 chomp;
628 s/\r$//o;
629 ++$line_num;
631 my $len = length() - 1;
633 if ($len < MIN_LINE_LENGTH)
635 close(IN);
636 print STDERR "$PROGRAM: ${line_num}th line <- Shorter than %u character.\n", MIN_LINE_LENGTH;
637 exit(1);
640 Log("$..(1) (\"$_\") length() = " . length(), 7);
642 my $bytecount = int(($len - MIN_LINE_LENGTH) / BYTE_SIZE);
644 my $binrec = pack('H*', substr($_, 1));
646 if (unpack('%8C*', $binrec) != 0)
648 close(IN);
649 print STDERR "$PROGRAM: $Hex <- Crc error. (${line_num}th line \"$_\").\n";
650 exit(1);
653 my ($count, $addr, $type, $bytes) = unpack('CnCX4Cx3/a', $binrec);
655 my @codes = unpack('C*', $bytes);
657 Log(sprintf("$..(2) (\"$_\") count = $count, bytecount = $bytecount, addr = 0x%04X, type = $type", $addr), 7);
659 if ($type == INHX_EOF_REC)
661 last;
663 elsif ($type == INHX_EXT_LIN_ADDR_REC)
665 $addr_H = unpack('n', $bytes); # big-endian
667 Log(sprintf("$..(3) (\"$_\") addr_H = 0x%04X\n", $addr_H), 7);
669 $format = INHX32;
670 Log('format = INHX32', 7);
671 next;
673 elsif ($type != INHX_DATA_REC)
675 close(IN);
676 printf STDERR "$PROGRAM: $Hex <- Unknown type of record: 0x%02X (${line_num}th line \"$_\").\n", $type;
677 exit(1);
680 if ($bytecount == $count) # INHX32
682 if ($format == INHX8M)
684 close(IN);
685 print STDERR "$PROGRAM: $Hex <- Mixed format of file (${line_num}th line \"$_\").\n";
686 exit(1);
689 my $addr32 = ($addr_H << 16) | $addr;
691 map { store_code($_, \$addr32) } @codes;
693 elsif ($bytecount == ($count * BYTE_SIZE)) # INHX8M
695 if ($format == INHX32)
697 close(IN);
698 print STDERR "$PROGRAM: $Hex <- Mixed format of file (${line_num}th line \"$_\").\n";
699 exit(1);
702 map { store_code($_, \$addr) } @codes;
704 else
706 close(IN);
707 print STDERR "$PROGRAM: $Hex <- Wrong format of file (${line_num}th line \"$_\").\n";
708 exit(1);
710 } # while (<IN>)
712 close(IN);
715 #-------------------------------------------------------------------------------
718 # Determines that the $Address belongs to a constant.
721 sub is_constant($)
723 my $Address = $_[0];
725 foreach (sort {$a <=> $b} keys(%const_areas_by_address))
727 return TRUE if ($_ <= $Address && $Address <= $const_areas_by_address{$_});
728 last if ($_ > $Address);
731 foreach (sort {$a <=> $b} keys(%const_blocks_by_address))
733 return TRUE if ($_ <= $Address && $Address <= $const_blocks_by_address{$_});
734 last if ($_ > $Address);
737 return FALSE;
740 #-------------------------------------------------------------------------------
743 # Determines that the $Address belongs to a empty area.
746 sub is_empty($)
748 my $Address = $_[0];
750 foreach (sort {$a <=> $b} keys(%empty_blocks_by_address))
752 return TRUE if ($_ <= $Address && $Address <= $empty_blocks_by_address{$_});
753 last if ($_ > $Address);
756 return FALSE;
759 #-------------------------------------------------------------------------------
762 # Creates a const block.
765 sub add_const_area($$)
767 $const_areas_by_address{$_[0]} = $_[1];
770 #-------------------------------------------------------------------------------
773 # Creates a new block, or modifies one.
776 sub add_block($$$$$)
778 my ($Address, $Type, $Size, $LabelType, $LabelName) = @_;
779 my ($block, $label, $end);
781 $end = $Address + $Size - 1;
783 if (! defined($blocks_by_address{$Address}))
785 $label = {
786 TYPE => $LabelType,
787 NAME => $LabelName,
788 PRINTED => FALSE,
789 CALL_COUNT => 0,
790 JUMP_COUNT => 0
793 $blocks_by_address{$Address} = {
794 TYPE => $Type,
795 ADDR => $Address,
796 SIZE => $Size,
797 LABEL => $label,
798 REF_COUNT => 0
801 given ($Type)
803 when (BLOCK_INSTR)
805 if ($LabelType != BL_TYPE_NONE)
807 $labels_by_address{$Address} = $label;
808 $max_label_addr = $Address if ($max_label_addr < $Address);
812 when (BLOCK_RAM)
814 if ($LabelType != BL_TYPE_NONE)
816 $labels_by_address{$Address} = $label;
817 $max_label_addr = $Address if ($max_label_addr < $Address);
820 $ram_blocks_by_address{$Address} = $end if ($Size > 0);
823 when (BLOCK_CONST)
825 if ($LabelType != BL_TYPE_NONE)
827 $labels_by_address{$Address} = $label;
828 $max_label_addr = $Address if ($max_label_addr < $Address);
831 $const_blocks_by_address{$Address} = $end if ($Size > 0);
834 when (BLOCK_EMPTY)
836 # At empty area, can not be label.
838 $label->{TYPE} = BL_TYPE_NONE;
839 $label->{NAME} = '';
840 $empty_blocks_by_address{$Address} = $end if ($Size > 0);
843 default
845 printf STDERR "add_block(0x%04X): Unknown block type!\n", $Address;
846 exit(1);
848 } # given ($Type)
849 } # if (! defined($blocks_by_address{$Address}))
850 else
852 $block = $blocks_by_address{$Address};
853 $label = $block->{LABEL};
854 $block->{TYPE} = $Type;
855 $block->{SIZE} = $Size if ($Size > 0);
856 $label->{NAME} = $LabelName if ($label->{NAME} eq '' && $LabelName ne '');
858 given ($Type)
860 when (BLOCK_INSTR)
862 if ($LabelType != BL_TYPE_NONE)
864 $label->{TYPE} = $LabelType;
865 $labels_by_address{$Address} = $label;
866 $max_label_addr = $Address if ($max_label_addr < $Address);
870 when (BLOCK_RAM)
872 if ($LabelType != BL_TYPE_NONE)
874 $label->{TYPE} = $LabelType;
875 $labels_by_address{$Address} = $label;
876 $max_label_addr = $Address if ($max_label_addr < $Address);
879 $ram_blocks_by_address{$Address} = $end if ($Size > 0);
882 when (BLOCK_CONST)
884 if ($LabelType != BL_TYPE_NONE)
886 $label->{TYPE} = $LabelType;
887 $labels_by_address{$Address} = $label;
888 $max_label_addr = $Address if ($max_label_addr < $Address);
891 $const_blocks_by_address{$Address} = $end if ($Size > 0);
894 when (BLOCK_EMPTY)
896 # At empty area, can not be label.
898 $label->{TYPE} = BL_TYPE_NONE;
899 $label->{NAME} = '';
900 $empty_blocks_by_address{$Address} = $end if ($Size > 0);
902 } # given ($Type)
905 return $label;
908 #-------------------------------------------------------------------------------
911 # Store address entry of a procedure.
914 sub add_func_label($$$)
916 my ($Address, $Name, $Map_mode) = @_;
917 my $label;
919 if ($Address < 0)
921 Log(sprintf("add_func_label(): This address (0x%04X) negative!", $Address), 2);
922 return;
925 if (! $Map_mode)
927 if (! defined($blocks_by_address{$Address}))
929 Log(sprintf("add_func_label(): This address (0x%04X) does not shows an instruction!", $Address), 2);
930 return;
934 if (is_constant($Address) || is_empty($Address))
936 Log(sprintf("add_func_label(): This address (0x%04X) outside the code area!", $Address), 2);
937 return;
940 $label = add_block($Address, BLOCK_INSTR, 0, BL_TYPE_SUB, $Name);
942 if (! $Map_mode)
944 ++$label->{CALL_COUNT};
945 ++$blocks_by_address{$Address}->{REF_COUNT};
949 #-------------------------------------------------------------------------------
952 # Store a label.
955 sub add_jump_label($$$$$)
957 my ($TargetAddr, $Name, $Type, $SourceAddr, $Map_mode) = @_;
958 my ($label, $type);
960 if ($TargetAddr < 0)
962 Log(sprintf("add_jump_label(): This address (0x%04X) negative!", $TargetAddr), 2);
963 return;
966 if (! $Map_mode)
968 if (! defined($blocks_by_address{$TargetAddr}))
970 Log(sprintf("add_jump_label(): This address (0x%04X) does not shows an instruction!", $TargetAddr), 2);
971 return;
975 if (is_constant($TargetAddr) || is_empty($TargetAddr))
977 Log(sprintf("add_jump_label(): This address (0x%04X) outside the code area!", $TargetAddr), 2);
978 return;
981 if (defined($interrupts_by_address{$SourceAddr}))
983 $Type = BL_TYPE_SUB;
984 $Name = $interrupts_by_address{$SourceAddr} if ($Name eq '');
987 $label = add_block($TargetAddr, BLOCK_INSTR, 0, $Type, $Name);
989 if (! $Map_mode)
991 ++$label->{JUMP_COUNT};
992 ++$blocks_by_address{$TargetAddr}->{REF_COUNT};
996 #-------------------------------------------------------------------------------
999 # Store a variable name.
1002 sub add_ram($$$)
1004 my ($Address, $Name, $Map_mode) = @_;
1006 return if ($Address == EMPTY);
1008 if ($Address < 0)
1010 Log(sprintf("add_ram(): This address (0x%04X) negative!", $Address), 2);
1011 return;
1014 add_block($Address, BLOCK_RAM, 1, BL_TYPE_VARIABLE, $Name);
1016 ++$blocks_by_address{$Address}->{REF_COUNT} if (! $Map_mode);
1019 #-------------------------------------------------------------------------------
1022 # Store a I/O port name.
1025 sub add_io($$$)
1027 my ($Address, $Name, $Map_mode) = @_;
1028 my $io;
1030 return if ($Address == EMPTY);
1032 if (! defined($io = $io_by_address{$Address}))
1034 $io_by_address{$Address} = {
1035 NAME => $Name,
1036 REF_COUNT => ($Map_mode) ? 0 : 1
1039 else
1041 ++$io->{REF_COUNT} if (! $Map_mode);
1045 ################################################################################
1046 ################################################################################
1048 use constant MAP_NULL => 0;
1049 use constant MAP_BORDER => 1;
1050 use constant MAP_AREA => 2;
1051 use constant MAP_CODE => 3;
1052 use constant MAP_DATA => 4;
1055 # If exists the map file, then extracts out of it the labels,
1056 # variables and some segments.
1059 sub read_map_file()
1061 my $state;
1063 return if ($map_file eq '');
1065 $state = MAP_NULL;
1067 if (! open(MAP, '<', $map_file))
1069 print STDERR "$PROGRAM : Could not open. -> \"$map_file\"\n";
1070 exit(1);
1073 while (<MAP>)
1075 chomp;
1076 s/\r$//o;
1078 if ($state == MAP_NULL)
1080 $state = MAP_BORDER if (/^Area\s+/io);
1082 elsif ($state == MAP_BORDER)
1084 $state = MAP_AREA if (/^-+\s+/o);
1086 elsif ($state == MAP_AREA)
1088 if (/^_CODE\s+/o)
1090 $state = MAP_CODE;
1092 elsif (/^_(DATA|INITIALIZED)\s+/o)
1094 $state = MAP_DATA;
1096 else
1098 $state = MAP_NULL;
1101 elsif ($state == MAP_CODE)
1103 if (/^.ASxxxx Linker\s+/io)
1105 $state = MAP_NULL;
1107 elsif (/^\s+([[:xdigit:]]+)\s+(\S+)/o)
1109 # 00000180 _main main
1110 # 00000190 _main_end main
1111 # 000001A2 _puts conio
1112 # 000001B5 _puthex conio
1113 # 00000201 _puthex8 conio
1115 add_func_label(hex($1), $2, TRUE);
1117 } # elsif ($state == MAP_CODE)
1118 elsif ($state == MAP_DATA)
1120 if (/^.ASxxxx Linker\s+/io)
1122 $state = MAP_NULL;
1124 elsif (/^\s*([[:xdigit:]]+)\s+(\S+)/o)
1126 # 000006C6 _heap_top
1127 # 000006C8 _last_error
1128 # 000006C9 _old_isr
1130 add_ram(hex($1), $2, TRUE);
1132 } # elsif ($state == MAP_DATA)
1133 } # while (<MAP>)
1135 $map_readed = TRUE;
1136 close(MAP);
1139 #-------------------------------------------------------------------------------
1141 use constant NAMES_NULL => 0;
1142 use constant NAMES_RAM => 1;
1143 use constant NAMES_IO => 2;
1144 use constant NAMES_ROM => 3;
1146 sub read_name_list()
1148 my ($line, $addr, $name, $state);
1150 return if ($name_list eq '');
1152 if (! open(NAMES, '<', $name_list))
1154 print STDERR "$PROGRAM : Could not open. -> \"$name_list\"\n";
1155 exit(1);
1158 $state = NAMES_NULL;
1160 foreach (grep(! /^\s*$/o, <NAMES>))
1162 chomp;
1163 s/\r$//o;
1164 s/^\s*|\s*$//go;
1166 if (/^\[RAM\]$/io)
1168 $state = NAMES_RAM;
1169 next;
1171 elsif (/^\[IO\]$/io)
1173 $state = NAMES_IO;
1174 next;
1176 elsif (/^\[ROM\]$/io)
1178 $state = NAMES_ROM;
1179 next;
1182 $line = $_;
1184 given ($state)
1186 when (NAMES_RAM)
1188 if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
1190 add_ram(hex($1), $2, TRUE);
1194 when (NAMES_IO)
1196 if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
1198 add_io(hex($1), $2, TRUE);
1202 when (NAMES_ROM)
1204 if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
1206 add_jump_label(hex($1), $2, BL_TYPE_LABEL, EMPTY, TRUE);
1209 } # given ($state)
1210 } # foreach (grep(! /^\s*$/o, <NAMES>))
1212 close(NAMES);
1215 #-------------------------------------------------------------------------------
1218 # There are some variables that are multi-byte. However, only
1219 # the LSB byte of having a name. This procedure gives a name
1220 # for the higher-significant bytes.
1223 sub fix_multi_byte_variables()
1225 my ($block, $prev_addr, $prev_name, $name, $i, $var_size);
1227 $prev_addr = EMPTY;
1228 $prev_name = '';
1229 foreach (sort {$a <=> $b} keys(%blocks_by_address))
1231 $block = $blocks_by_address{$_};
1232 $name = $block->{LABEL}->{NAME};
1234 if ($block->{TYPE} != BLOCK_RAM)
1236 $prev_addr = EMPTY;
1237 $prev_name = '';
1238 next;
1241 $ram_names_by_address{$_} = $name;
1243 if ($name eq '')
1245 $prev_addr = EMPTY;
1246 $prev_name = '';
1247 next;
1250 if ($prev_addr != EMPTY)
1252 $var_size = $_ - $prev_addr;
1254 if ($var_size > 1)
1256 # This is a multi-byte variable. Make the aliases.
1258 for ($i = 1; $i < $var_size; ++$i)
1260 $ram_names_by_address{$prev_addr + $i} = "($prev_name + $i)";
1263 $blocks_by_address{$prev_addr}->{SIZE} = $var_size;
1267 $prev_addr = $_;
1268 $prev_name = $name;
1269 } # foreach (sort {$a <=> $b} keys(%blocks_by_address))
1272 #-------------------------------------------------------------------------------
1274 sub fix_io_names()
1276 my $i = 0;
1278 foreach (sort {$a <=> $b} keys(%io_by_address))
1280 next if ($io_by_address{$_}->{NAME} ne '');
1282 $io_by_address{$_}->{NAME} = "io_$i";
1283 ++$i;
1287 #-------------------------------------------------------------------------------
1290 # If there is left in yet so label that has no name, this here get one.
1293 sub add_names_labels()
1295 my ($addr, $label, $fidx, $lidx, $jtidx, $cidx, $type);
1297 $fidx = 0;
1298 $lidx = 0;
1299 $jtidx = 0;
1300 $cidx = 0;
1302 for ($addr = 0; $addr <= $max_label_addr; ++$addr)
1304 $label = $labels_by_address{$addr};
1306 next if (! defined($label));
1308 $type = $label->{TYPE};
1310 next if ($type == BL_TYPE_NONE || (defined($label->{NAME}) && $label->{NAME} ne ''));
1312 if ($type == BL_TYPE_SUB)
1314 $label->{NAME} = sprintf("$label_names{$type}%03u", $fidx++);
1316 elsif ($type == BL_TYPE_LABEL)
1318 $label->{NAME} = sprintf("$label_names{$type}%03u", $lidx++);
1320 elsif ($type == BL_TYPE_JTABLE)
1322 $label->{NAME} = sprintf("$label_names{$type}%03u", $jtidx++);
1324 elsif ($type == BL_TYPE_CONST)
1326 $label->{NAME} = sprintf("$label_names{$type}%03u", $cidx++);
1331 ################################################################################
1332 ################################################################################
1335 # Expand a relative offset value.
1338 sub expand_offset($)
1340 my $Offset = $_[0];
1342 return ($Offset & 0x80) ? -(($Offset ^ 0xFF) + 1) : $Offset;
1345 #-------------------------------------------------------------------------------
1348 # Finds address of branchs and procedures.
1351 sub label_finder($$)
1353 my ($Address, $BlockRef) = @_;
1354 my ($instr_size, $instr, $addr);
1356 $instr_size = $BlockRef->{SIZE};
1357 $instr = $rom[$Address];
1359 if ($instr == INST_JP)
1361 # JP addr16 11000011 aaaaaaaa aaaaaaaa a7-a0 a15-a8 absolute address
1363 $addr = ($rom[$Address + 2] << 8) | $rom[$Address + 1];
1364 add_jump_label($addr, '', BL_TYPE_LABEL, $Address, FALSE);
1366 elsif (($instr & 0xC7) == INST_JP_CC)
1368 # JP cc, addr16 11ccc010 aaaaaaaa aaaaaaaa a7-a0 a15-a8 absolute address
1370 $addr = ($rom[$Address + 2] << 8) | $rom[$Address + 1];
1371 add_jump_label($addr, '', BL_TYPE_LABEL, $Address, FALSE);
1373 elsif ($instr == INST_JR)
1375 # JR rel 00011000 eeeeeee relative address
1377 $addr = $Address + $instr_size + expand_offset($rom[$Address + 1]);
1378 add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
1380 elsif (($instr & 0xE7) == INST_JR_CC)
1382 # JR cc, rel 00100000 eeeeeee relative address
1384 $addr = $Address + $instr_size + expand_offset($rom[$Address + 1]);
1385 add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
1387 elsif ($instr == INST_DJNZ)
1389 # DJNZ rel 00010000 eeeeeee relative address
1391 $addr = $Address + $instr_size + expand_offset($rom[$Address + 1]);
1392 add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
1394 elsif ($instr == INST_CALL)
1396 # CALL addr16 11001101 aaaaaaaa aaaaaaaa a7-a0 a15-a8 absolute address
1398 $addr = ($rom[$Address + 2] << 8) | $rom[$Address + 1];
1399 add_func_label($addr, '', FALSE);
1401 elsif (($instr & 0xC7) == INST_CALL_CC)
1403 # CALL cc, addr16 11ccc100 aaaaaaaa aaaaaaaa a7-a0 a15-a8 absolute address
1405 $addr = ($rom[$Address + 2] << 8) | $rom[$Address + 1];
1406 add_func_label($addr, '', FALSE);
1410 #-------------------------------------------------------------------------------
1413 # If exists a label name wich belong to the $Address, then returns it.
1414 # Otherwise, returns the address.
1417 sub label_name($)
1419 my $Address = $_[0];
1420 my $label = $labels_by_address{$Address};
1422 return ((defined($label) && $label->{NAME} ne '') ? $label->{NAME} : (sprintf '0x%04X', $Address));
1425 #-------------------------------------------------------------------------------
1428 # If exists a I/O port name wich belong to the $Address, then returns it.
1429 # Otherwise, returns the address.
1432 sub io_name($)
1434 my $Address = $_[0];
1435 my $io = $io_by_address{$Address};
1437 return ((defined($io) && $io->{NAME} ne '') ? $io->{NAME} : (sprintf '0x%02X', $Address));
1440 #-------------------------------------------------------------------------------
1443 # If exists a variable name wich belong to the $Address, then returns it.
1444 # Otherwise, returns the address.
1447 sub reg_name($$)
1449 my ($Address, $StrRef) = @_;
1450 my ($ram, $str);
1452 if (defined($ram = $ram_names_by_address{$Address}) && $ram ne '')
1454 $str = sprintf "0x%04X", $Address;
1455 ${$StrRef} = "[$str]";
1456 $str = $ram;
1458 else
1460 $str = sprintf "0x%04X", $Address;
1461 ${$StrRef} = "[$str]";
1464 return $str;
1467 #-------------------------------------------------------------------------------
1470 # Auxiliary procedure of prints.
1473 sub print_3($$$)
1475 my ($Instr, $Param, $Expl) = @_;
1477 return if ($decoder_silent_level > SILENT0);
1479 if ($no_explanations)
1481 print(($Param ne '') ? "$Instr\t$Param\n" : "$Instr\n");
1483 elsif ($Expl ne '')
1485 print "$Instr\t" . align($Param, EXPL_ALIGN_SIZE) . "; $Expl\n";
1487 else
1489 print(($Param ne '') ? "$Instr\t$Param\n" : "$Instr\n");
1493 #-------------------------------------------------------------------------------
1496 # If possible, returns the character.
1499 sub decode_char($)
1501 my $Ch = $_[0];
1503 if ($Ch >= ord(' ') && $Ch < 0x7F)
1505 return sprintf " {'%c'}", $Ch;
1507 elsif (defined($control_characters{$Ch}))
1509 return " {'$control_characters{$Ch}'}";
1512 return '';
1515 #-------------------------------------------------------------------------------
1518 # Determines direction of jump.
1521 sub jump_direction($)
1523 my $TargetAddr = $_[0];
1524 my ($str0, $str1, $str2);
1526 $str0 = sprintf "0x%04X", $TargetAddr;
1528 if ($dcd_address < $TargetAddr)
1530 $str1 = '';
1531 $str2 = ' (forward)';
1533 elsif ($dcd_address == $TargetAddr)
1535 $str1 = ' (endless loop)';
1536 $str2 = '';
1538 else
1540 $str1 = '';
1541 $str2 = ' (backward)';
1544 return "$str2 hither: $str0$str1";
1547 #---------------------------------------------------------------------------------------------------
1548 #---------------------------------------------------------------------------------------------------
1549 #---------------------------------------------------------------------------------------------------
1550 #---------------------------------------------------------------------------------------------------
1552 my @core_registers8 =
1555 NAME => 'B',
1556 EXPL => 'B'
1559 NAME => 'C',
1560 EXPL => 'C'
1563 NAME => 'D',
1564 EXPL => 'D'
1567 NAME => 'E',
1568 EXPL => 'E'
1571 NAME => 'H',
1572 EXPL => 'H'
1575 NAME => 'L',
1576 EXPL => 'L'
1579 NAME => '(HL)',
1580 EXPL => '[HL]'
1583 NAME => 'A',
1584 EXPL => 'A'
1588 my @core_registers16a = ( 'BC', 'DE', 'HL', 'SP' );
1589 my @core_registers16b = ( 'BC', 'DE', 'HL', 'AF' );
1590 my @core_registers16c = ( 'BC', 'DE', 'IX', 'SP' );
1592 my @CB_shift_instr =
1595 INSTR => 'rlc',
1596 EXPL => 'CF <- %s[7..0] <- %s.7'
1599 INSTR => 'rrc',
1600 EXPL => '%s.0 -> %s[7..0] -> CF'
1603 INSTR => 'rl',
1604 EXPL => 'CF <- %s[7..0] <- CF'
1607 INSTR => 'rr',
1608 EXPL => 'CF -> %s[7..0] -> CF'
1611 INSTR => 'sla',
1612 EXPL => 'CF <- %s[7..0] <- 0'
1615 INSTR => 'sra',
1616 EXPL => '%s.7 -> %s[7..0] -> CF'
1619 INSTR => 'sll',
1620 EXPL => 'CF <- %s[7..0] <- 1'
1623 INSTR => 'srl',
1624 EXPL => '0 -> %s[7..0] -> CF'
1628 sub CB_prefix_decoder()
1630 my ($str, $i_reg, $reg);
1632 given ($dcd_instr_x)
1634 when (0)
1636 # RLC r CB xx 11001011 00000rrr
1637 # RRC r CB xx 11001011 00001rrr
1638 # RL r CB xx 11001011 00010rrr
1639 # RR r CB xx 11001011 00011rrr
1640 # SLA r CB xx 11001011 00100rrr
1641 # SRA r CB xx 11001011 00101rrr
1642 # SLL r CB xx 11001011 00110rrr
1643 # SRL r CB xx 11001011 00111rrr
1644 # xxyyyzzz
1645 if ($decoder_silent_level == SILENT0)
1647 my $i_shift = $CB_shift_instr[$dcd_instr_y];
1649 $i_reg = $core_registers8[$dcd_instr_z];
1650 $reg = $i_reg->{EXPL};
1651 if ($dcd_instr_y == 0)
1653 $str = sprintf $i_shift->{EXPL}, $reg, $reg;
1655 else
1657 $str = sprintf $i_shift->{EXPL}, $reg;
1659 print_3($i_shift->{INSTR}, $i_reg->{NAME}, $str);
1663 when (1)
1665 # BIT b, r CB 11001011 01bbbrrr
1666 # xxyyyzzz
1668 if ($decoder_silent_level == SILENT0)
1670 $i_reg = $core_registers8[$dcd_instr_z];
1671 print_3('bit', "$dcd_instr_y, $i_reg->{NAME}", "ZF = !$i_reg->{EXPL}.$dcd_instr_y");
1675 when (2)
1677 # RES b, r CB 11001011 10bbbrrr
1678 # xxyyyzzz
1680 if ($decoder_silent_level == SILENT0)
1682 $i_reg = $core_registers8[$dcd_instr_z];
1683 print_3('res', "$dcd_instr_y, $i_reg->{NAME}", "$i_reg->{EXPL}.$dcd_instr_y = 0");
1687 default
1689 # SET b, r CB 11001011 11bbbrrr
1690 # xxyyyzzz
1692 if ($decoder_silent_level == SILENT0)
1694 $i_reg = $core_registers8[$dcd_instr_z];
1695 print_3('set', "$dcd_instr_y, $i_reg->{NAME}", "$i_reg->{EXPL}.$dcd_instr_y = 1");
1698 } # given ($dcd_instr_x)
1701 #-------------------------------------------------------------------------------
1704 # $IndexReg: IX or IY
1707 sub DDFD_CB_prefix_decoder($)
1709 my $IndexReg = $_[0];
1710 my $offset = expand_offset($dcd_parm1);
1711 my ($offs_str, $offs_expl, $i_reg);
1713 if ($offset < 0)
1715 $offs_str = "$offset($IndexReg)";
1716 $offs_expl = "[$IndexReg$offset]";
1718 else
1720 $offs_str = "$offset($IndexReg)";
1721 $offs_expl = "[${IndexReg}+$offset]";
1724 given ($dcd_instr_x)
1726 =back
1727 r: 000 B
1728 001 C
1729 010 D
1730 011 E
1731 100 H
1732 101 L
1733 110 (HL)
1734 111 A
1735 =cut
1736 when (0)
1738 if ($dcd_instr_z == 6)
1740 # RLC (IX+d) DD CB dd 06 11011101 11001011 dddddddd 00000110 d: two's complement number
1741 # RLC (IY+d) FD CB dd 06 11111101 11001011 dddddddd 00000110 d: two's complement number
1742 # RRC (IX+d) DD CB dd 0E 11011101 11001011 dddddddd 00001110 d: two's complement number
1743 # RRC (IY+d) FD CB dd 0E 11111101 11001011 dddddddd 00001110 d: two's complement number
1744 # RL (IX+d) DD CB dd 16 11011101 11001011 dddddddd 00010110 d: two's complement number
1745 # RL (IY+d) FD CB dd 16 11111101 11001011 dddddddd 00010110 d: two's complement number
1746 # RR (IX+d) DD CB dd 1E 11011101 11001011 dddddddd 00011110 d: two's complement number
1747 # RR (IY+d) FD CB dd 1E 11111101 11001011 dddddddd 00011110 d: two's complement number
1748 # SLA (IX+d) DD CB dd 26 11011101 11001011 dddddddd 00100110 d: two's complement number
1749 # SLA (IY+d) FD CB dd 26 11111101 11001011 dddddddd 00100110 d: two's complement number
1750 # SRA (IX+d) DD CB dd 2E 11011101 11001011 dddddddd 00101110 d: two's complement number
1751 # SRA (IY+d) FD CB dd 2E 11111101 11001011 dddddddd 00101110 d: two's complement number
1752 # SLL (IX+d) DD CB dd 36 11011101 11001011 dddddddd 00110110 d: two's complement number
1753 # SLL (IY+d) FD CB dd 36 11111101 11001011 dddddddd 00110110 d: two's complement number
1754 # SRL (IX+d) DD CB dd 3E 11011101 11001011 dddddddd 00111110 d: two's complement number
1755 # SRL (IY+d) FD CB dd 3E 11111101 11001011 dddddddd 00111110 d: two's complement number
1756 # xxyyyzzz
1758 if ($decoder_silent_level == SILENT0)
1760 my $shift = $CB_shift_instr[$dcd_instr_y];
1761 my $str = sprintf $shift->{EXPL}, $offs_expl, $offs_expl;
1763 print_3($shift->{INSTR}, $offs_str, $str);
1766 else
1768 # LD r, RLC(IX+d) DD CB dd 0x 11011101 11001011 dddddddd 00000rrr d: two's complement number
1769 # LD r, RLC(IY+d) FD CB dd 0x 11111101 11001011 dddddddd 00000rrr d: two's complement number
1770 # LD r, RRC(IX+d) DD CB dd 0x 11011101 11001011 dddddddd 00001rrr d: two's complement number
1771 # LD r, RRC(IY+d) FD CB dd 0x 11111101 11001011 dddddddd 00001rrr d: two's complement number
1772 # LD r, RL(IX+d) DD CB dd 1x 11011101 11001011 dddddddd 00010rrr d: two's complement number
1773 # LD r, RL(IY+d) FD CB dd 1x 11111101 11001011 dddddddd 00010rrr d: two's complement number
1774 # LD r, RR(IX+d) DD CB dd 1x 11011101 11001011 dddddddd 00011rrr d: two's complement number
1775 # LD r, RR(IY+d) FD CB dd 1x 11111101 11001011 dddddddd 00011rrr d: two's complement number
1776 # LD r, SLA(IX+d) DD CB dd 2x 11011101 11001011 dddddddd 00100rrr d: two's complement number
1777 # LD r, SLA(IY+d) FD CB dd 2x 11111101 11001011 dddddddd 00100rrr d: two's complement number
1778 # LD r, SRA(IX+d) DD CB dd 2x 11011101 11001011 dddddddd 00101rrr d: two's complement number
1779 # LD r, SRA(IY+d) FD CB dd 2x 11111101 11001011 dddddddd 00101rrr d: two's complement number
1780 # LD r, SLL(IX+d) DD CB dd 3x 11011101 11001011 dddddddd 00110rrr d: two's complement number
1781 # LD r, SLL(IY+d) FD CB dd 3x 11111101 11001011 dddddddd 00110rrr d: two's complement number
1782 # LD r, SRL(IX+d) DD CB dd 3x 11011101 11001011 dddddddd 00111rrr d: two's complement number
1783 # LD r, SRL(IY+d) FD CB dd 3x 11111101 11001011 dddddddd 00111rrr d: two's complement number
1784 # xxyyyzzz
1786 if ($decoder_silent_level == SILENT0)
1788 my $shift = $CB_shift_instr[$dcd_instr_y];
1789 my $str = sprintf $shift->{EXPL}, $offs_expl, $offs_expl;
1791 $i_reg = $core_registers8[$dcd_instr_z];
1792 print_3('ld', "$i_reg->{NAME}, $shift->{INSTR} $offs_str", "$i_reg->{EXPL} = $str");
1795 } # $dcd_instr_x == 0
1797 when (1)
1799 # BIT b, (IX+d) DD CB dd 4x 11011101 11001011 dddddddd 01bbbxxx d: two's complement number
1800 # BIT b, (IY+d) FD CB dd 4x 11111101 11001011 dddddddd 01bbbxxx d: two's complement number
1801 # xxyyyzzz
1803 print_3('bit', "$dcd_instr_y, $offs_str", "ZF = !${offs_expl}.$dcd_instr_y");
1804 } # $dcd_instr_x == 1
1806 when (2)
1808 if ($dcd_instr_z == 6)
1810 # RES b, (IX+d) DD CB dd xx 11011101 11001011 dddddddd 10bbb110 d: two's complement number
1811 # RES b, (IY+d) FD CB dd xx 11111101 11001011 dddddddd 10bbb110 d: two's complement number
1812 # xxyyyzzz
1814 print_3('res', "$dcd_instr_y, $offs_str", "${offs_expl}.$dcd_instr_y = 0");
1816 else
1818 # LD r, RES b, (IX+d) DD CB dd xx 11011101 11001011 dddddddd 10bbbrrr d: two's complement number
1819 # LD r, RES b, (IY+d) FD CB dd xx 11111101 11001011 dddddddd 10bbbrrr d: two's complement number
1820 # xxyyyzzz
1822 $i_reg = $core_registers8[$dcd_instr_z];
1823 print_3('ld', "$i_reg->{NAME}, res $dcd_instr_y, $offs_str", "${offs_expl}.$dcd_instr_y = 0; $i_reg->{NAME} = $offs_expl");
1825 } # $dcd_instr_x == 2
1827 default
1829 if ($dcd_instr_z == 6)
1831 # SET b, (IX+d) DD CB dd xx 11011101 11001011 dddddddd 11bbb110 d: two's complement number
1832 # SET b, (IY+d) FD CB dd xx 11111101 11001011 dddddddd 11bbb110 d: two's complement number
1833 # xxyyyzzz
1835 print_3('set', "$dcd_instr_y, $offs_str", "${offs_expl}.$dcd_instr_y = 1");
1837 else
1839 # LD r, SET b, (IX+d) DD CB dd xx 11011101 11001011 dddddddd 11bbbrrr d: two's complement number
1840 # LD r, SET b, (IY+d) FD CB dd xx 11111101 11001011 dddddddd 11bbbrrr d: two's complement number
1841 # xxyyyzzz
1843 $i_reg = $core_registers8[$dcd_instr_z];
1844 print_3('ld', "$i_reg->{NAME}, set $dcd_instr_y, $offs_str", "${offs_expl}.$dcd_instr_y = 1; $i_reg->{NAME} = $offs_expl");
1847 } # given ($dcd_instr_x)
1850 #-------------------------------------------------------------------------------
1853 # $IndexReg: IX or IY
1856 my @DDFD_instr =
1859 INSTR => 'add',
1860 EXPL => 'A +='
1863 INSTR => 'adc',
1864 EXPL => 'A += CF +'
1867 INSTR => 'sub',
1868 EXPL => 'A -='
1871 INSTR => 'sbc',
1872 EXPL => 'A -= CF +'
1875 INSTR => 'and',
1876 EXPL => 'A &='
1879 INSTR => 'xor',
1880 EXPL => 'A ^='
1883 INSTR => 'or',
1884 EXPL => 'A |='
1887 INSTR => 'cp',
1888 EXPL => 'A ?='
1892 sub DDFD_prefix_decoder($)
1894 my $IndexReg = $_[0];
1895 my ($addr, $offset, $offs_str, $offs_expl, $str);
1897 if ($dcd_parm0 == 0xCB)
1899 instruction_take_to_pieces($dcd_parm2);
1900 DDFD_CB_prefix_decoder($IndexReg);
1902 else
1904 instruction_take_to_pieces($dcd_parm0);
1906 $offset = expand_offset($dcd_parm1);
1908 if ($offset < 0)
1910 $offs_str = "$offset($IndexReg)";
1911 $offs_expl = "[$IndexReg$offset]";
1913 else
1915 $offs_str = "$offset($IndexReg)";
1916 $offs_expl = "[${IndexReg}+$offset]";
1919 given ($dcd_instr_x)
1921 when (0)
1923 if ($dcd_instr_q == 1 && $dcd_instr_z == 1)
1925 # ADD IX, rp DD 09 11011101 00rr1001
1926 # ADD IY, rp FD 09 11111101 00rr1001
1927 # xxppqzzz
1928 # rp: BC, DE, IX, SP
1930 $str = $core_registers16c[$dcd_instr_p];
1931 print_3('add', "$IndexReg, $str", "$IndexReg += $str");
1933 elsif ($dcd_instr_y == 4)
1935 given ($dcd_instr_z)
1937 when (1)
1939 # LD IX, #nn DD 21 nn nn 11011101 00100001 a7-0 a15-8
1940 # LD IY, #nn FD 21 nn nn 11111101 00100001 a7-0 a15-8
1941 # xxyyyzzz
1943 $str = sprintf '0x%04X', ($dcd_parm2 << 8) | $dcd_parm1;
1944 print_3('ld', "$IndexReg, #$str", "$IndexReg = $str");
1947 when (2)
1949 # LD (nn), IX DD 22 nn nn 11011101 00100010 a7-0 a15-8
1950 # LD (nn), IY FD 22 nn nn 11111101 00100010 a7-0 a15-8
1951 # xxyyyzzz
1953 $addr = ($dcd_parm2 << 8) | $dcd_parm1;
1955 if ($decoder_silent_level == SILENT0)
1957 my $name;
1959 $str = reg_name($addr, \$name);
1960 print_3('ld', "($str), $IndexReg", "$name = $IndexReg");
1962 elsif ($decoder_silent_level == SILENT1)
1964 add_ram($addr, '', FALSE);
1968 when (3)
1970 # INC IX DD 23 11011101 00100011
1971 # INC IY FD 23 11111101 00100011
1972 # xxyyyzzz
1974 print_3('inc', $IndexReg, "++$IndexReg");
1977 when (4)
1979 # INC IXh DD 24 11011101 00100100
1980 # INC IYh FD 24 11111101 00100100
1981 # xxyyyzzz
1983 print_3('inc', "${IndexReg}h", "++${IndexReg}.h");
1986 when (5)
1988 # DEC IXh DD 25 11011101 00100101
1989 # DEC IYh FD 25 11111101 00100101
1990 # xxyyyzzz
1992 print_3('dec', "${IndexReg}h", "--${IndexReg}.h");
1995 when (6)
1997 # LD IXh, #n DD 26 nn 11011101 00100110 nnnnnnnn
1998 # LD IYh, #n FD 26 nn 11111101 00100110 nnnnnnnn
1999 # xxyyyzzz
2001 my $char = decode_char($dcd_parm1);
2003 $str = sprintf '0x%02X', $dcd_parm1;
2004 print_3('ld', "${IndexReg}h, #$str", "${IndexReg}.h = $str$char");
2006 } # given ($dcd_instr_z)
2008 elsif ($dcd_instr_y == 5)
2010 given ($dcd_instr_z)
2012 when (2)
2014 # LD IX, (nn) DD 2A nn nn 11011101 00101010 a7-0 a15-8
2015 # LD IY, (nn) FD 2A nn nn 11111101 00101010 a7-0 a15-8
2016 # xxyyyzzz
2018 $addr = ($dcd_parm2 << 8) | $dcd_parm1;
2020 if ($decoder_silent_level == SILENT0)
2022 my $name;
2024 $str = reg_name($addr, \$name);
2025 print_3('ld', "$IndexReg, ($str)", "$IndexReg = $name");
2027 elsif ($decoder_silent_level == SILENT1)
2029 add_ram($addr, '', FALSE);
2033 when (3)
2035 # DEC IX DD 2B 11011101 00101011
2036 # DEC IY FD 2B 11111101 00101011
2037 # xxyyyzzz
2039 print_3('dec', $IndexReg, "--$IndexReg");
2042 when (4)
2044 # INC IXl DD 2C 11011101 00101100
2045 # INC IYl FD 2C 11111101 00101100
2046 # xxyyyzzz
2048 print_3('inc', "${IndexReg}l", "++${IndexReg}.l");
2051 when (5)
2053 # DEC IXl DD 2D 11011101 00101101
2054 # DEC IYl FD 2D 11111101 00101101
2055 # xxyyyzzz
2057 print_3('dec', "${IndexReg}l", "--${IndexReg}.l");
2060 when (6)
2062 # LD IXl, #n DD 2E nn 11011101 00101110 nnnnnnnn
2063 # LD IYl, #n FD 2E nn 11111101 00101110 nnnnnnnn
2064 # xxyyyzzz
2066 my $char = decode_char($dcd_parm1);
2068 $str = sprintf '0x%02X', $dcd_parm1;
2069 print_3('ld', "${IndexReg}l, #$str", "${IndexReg}.l = $str$char");
2071 } # given ($dcd_instr_z)
2073 elsif ($dcd_instr_y == 6)
2075 given ($dcd_instr_z)
2077 when (4)
2079 # INC (IX+d) DD 34 dd 11011101 00110100 dddddddd d: two's complement number
2080 # INC (IY+d) FD 34 dd 11111101 00110100 dddddddd d: two's complement number
2081 # xxyyyzzz
2083 print_3('inc', $offs_str, "++$offs_expl");
2086 when (5)
2088 # DEC (IX+d) DD 35 dd 11011101 00110101 dddddddd d: two's complement number
2089 # DEC (IY+d) FD 35 dd 11111101 00110101 dddddddd d: two's complement number
2090 # xxyyyzzz
2092 print_3('dec', $offs_str, "--$offs_expl");
2095 when (6)
2097 # LD (IX+d), #n DD 36 dd nn 11011101 00110110 dddddddd nnnnnnnn d: two's complement number
2098 # LD (IY+d), #n FD 36 dd nn 11111101 00110110 dddddddd nnnnnnnn d: two's complement number
2099 # xxyyyzzz
2101 my $char = decode_char($dcd_parm2);
2103 $str = sprintf '0x%02X', $dcd_parm2;
2104 print_3('ld', "$offs_str, #$str", "$offs_expl = $str$char");
2106 } # given ($dcd_instr_z)
2108 } # $dcd_instr_x == 0
2110 when (1)
2112 given ($dcd_instr_y)
2114 when ([0 .. 3])
2116 given ($dcd_instr_z)
2118 when (4)
2120 # LD r, IXh DD 44 11011101 010rr100
2121 # LD r, IYh FD 44 11111101 010rr100
2122 # xxyyyzzz
2123 # r: B, C, D, E
2125 $str = $core_registers8[$dcd_instr_y]->{NAME};
2126 print_3('ld', "$str, ${IndexReg}h", "$str = ${IndexReg}.h");
2129 when (5)
2132 # LD r, IXl DD 45 11011101 010rr101
2133 # LD r, IYl FD 45 11111101 010rr101
2134 # xxyyyzzz
2135 # r: B, C, D, E
2137 $str = $core_registers8[$dcd_instr_y]->{NAME};
2138 print_3('ld', "$str, ${IndexReg}l", "$str = ${IndexReg}.l");
2141 when (6)
2144 # LD r, (IX+d) DD 46 dd 11011101 010rr110 dddddddd d: two's complement number
2145 # LD r, (IY+d) FD 46 dd 11111101 010rr110 dddddddd d: two's complement number
2146 # xxyyyzzz
2147 # r: B, C, D, E
2149 $str = $core_registers8[$dcd_instr_y]->{NAME};
2150 print_3('ld', "$str, $offs_str", "$str = $offs_expl");
2152 } # given ($dcd_instr_z)
2153 } # when ([0 .. 3])
2155 when ([4, 5])
2157 my $r = ($dcd_instr_y == 4) ? 'h' : 'l';
2159 given ($dcd_instr_z)
2161 when ([0 .. 3])
2163 # LD IXh, B DD 60 11011101 011000rr
2164 # LD IYh, B FD 60 11111101 011000rr
2165 # LD IXh, C DD 61 11011101 01100001
2166 # LD IYh, C FD 61 11111101 01100001
2167 # LD IXh, D DD 62 11011101 01100010
2168 # LD IYh, D FD 62 11111101 01100010
2169 # LD IXh, E DD 63 11011101 01100011
2170 # LD IYh, E FD 63 11111101 01100011
2171 # xxyyyzzz
2172 # r: B, C, D, E
2174 $str = $core_registers8[$dcd_instr_z]->{NAME};
2175 print_3('ld', "$IndexReg$r, $str", "$IndexReg$r = $str");
2178 when (4)
2180 # LD IXh, IXh DD 64 11011101 01100100
2181 # LD IYh, IYh FD 64 11111101 01100100
2182 # xxyyyzzz
2184 print_3('ld', "$IndexReg$r, {IndexReg}h", "$IndexReg$r = {IndexReg}.h");
2187 when (5)
2189 # LD IXh, IXl DD 65 11011101 01100101
2190 # LD IYh, IYl FD 65 11111101 01100101
2191 # xxyyyzzz
2193 print_3('ld', "$IndexReg$r, {IndexReg}l", "$IndexReg$r = {IndexReg}.l");
2196 when (6)
2198 # LD H, (IX+d) DD 66 dd 11011101 01100110 dddddddd d: two's complement number
2199 # LD H, (IY+d) FD 66 dd 11111101 01100110 dddddddd d: two's complement number
2200 # xxyyyzzz
2202 $str = uc($r);
2203 print_3('ld', "$str, $offs_str", "$str = $offs_expl");
2206 when (7)
2208 # LD IXh, A DD 67 11011101 01100111
2209 # LD IYh, A FD 67 11111101 01100111
2210 # xxyyyzzz
2212 print_3('ld', "$IndexReg$r, A", "$IndexReg$r = A");
2214 } # given ($dcd_instr_z)
2215 } # when ([4, 5])
2217 when (6)
2219 # LD (IX+d), r DD 70 dd 11011101 01110rrr dddddddd d: two's complement number
2220 # LD (IY+d), r FD 70 dd 11111101 01110rrr dddddddd d: two's complement number
2221 # xxyyyzzz
2222 # r: B, C, D, E, H, L, -, A
2224 $str = $core_registers8[$dcd_instr_z]->{NAME};
2225 print_3('ld', "$offs_str, $str", "$offs_expl = $str");
2228 default
2230 given ($dcd_instr_z)
2232 when (4)
2234 # LD A, IXh DD 7C 11011101 01111100
2235 # LD A, IYh FD 7C 11111101 01111100
2236 # xxyyyzzz
2238 print_3('ld', "A, ${IndexReg}h", "A = ${IndexReg}.h");
2241 when (5)
2243 # LD A, IXl DD 7D 11011101 01111101
2244 # LD A, IYl FD 7D 11111101 01111101
2245 # xxyyyzzz
2247 print_3('ld', "A, ${IndexReg}l", "A = ${IndexReg}.l");
2250 when (6)
2252 # LD A, (IX+d) DD 7E dd 11011101 01111110 dddddddd d: two's complement number
2253 # LD A, (IY+d) FD 7E dd 11111101 01111110 dddddddd d: two's complement number
2254 # xxyyyzzz
2256 print_3('ld', "A, $offs_str", "A = $offs_expl");
2258 } # given ($dcd_instr_z)
2260 } # given ($dcd_instr_y)
2261 } # $dcd_instr_x == 1
2263 when (2)
2265 given ($dcd_instr_z)
2267 when (4)
2269 # ADD A, IXh DD 84 11011101 10000100
2270 # ADD A, IYh FD 84 11111101 10000100
2271 # ADC A, IXh DD 8C 11011101 10001100
2272 # ADC A, IYh FD 8C 11111101 10001100
2273 # SUB A, IXh DD 94 11011101 10010100
2274 # SUB A, IYh FD 94 11111101 10010100
2275 # SBC A, IXh DD 9C 11011101 10011100
2276 # SBC A, IYh FD 9C 11111101 10011100
2277 # AND A, IXh DD A4 11011101 10100100
2278 # AND A, IYh FD A4 11111101 10100100
2279 # XOR A, IXh DD AC 11011101 10101100
2280 # XOR A, IYh FD AC 11111101 10101100
2281 # OR A, IXh DD B4 11011101 10110100
2282 # OR A, IYh FD B4 11111101 10110100
2283 # CP A, IXh DD BC 11011101 10111100
2284 # CP A, IYh FD BC 11111101 10111100
2285 # xxyyyzzz
2287 my $i_arith = $DDFD_instr[$dcd_instr_y];
2289 print_3($i_arith->{INSTR}, "A, ${IndexReg}h", "$i_arith->{EXPL} ${IndexReg}.h");
2292 when (5)
2294 # ADD A, IXl DD 85 11011101 10000101
2295 # ADD A, IYl FD 85 11111101 10000101
2296 # ADC A, IXl DD 8D 11011101 10001101
2297 # ADC A, IYl FD 8D 11111101 10001101
2298 # SUB A, IXl DD 95 11011101 10010101
2299 # SUB A, IYl FD 95 11111101 10010101
2300 # SBC A, IXl DD 9D 11011101 10011101
2301 # SBC A, IYl FD 9D 11111101 10011101
2302 # AND A, IXl DD A5 11011101 10100101
2303 # AND A, IYl FD A5 11111101 10100101
2304 # XOR A, IXl DD AD 11011101 10101101
2305 # XOR A, IYl FD AD 11111101 10101101
2306 # OR A, IXl DD B5 11011101 10110101
2307 # OR A, IYl FD B5 11111101 10110101
2308 # CP A, IXl DD BD 11011101 10111101
2309 # CP A, IYl FD BD 11111101 10111101
2310 # xxyyyzzz
2312 my $i_arith = $DDFD_instr[$dcd_instr_y];
2314 print_3($i_arith->{INSTR}, "A, ${IndexReg}l", "$i_arith->{EXPL} ${IndexReg}.l");
2317 when (6)
2319 # ADD A, (IX+d) DD 86 dd 11011101 10000110 dddddddd d: two's complement number
2320 # ADD A, (IY+d) FD 86 dd 11111101 10000110 dddddddd d: two's complement number
2321 # ADC A, (IX+d) DD 8E dd 11011101 10001110 dddddddd d: two's complement number
2322 # ADC A, (IY+d) FD 8E dd 11111101 10001110 dddddddd d: two's complement number
2323 # SUB A, (IX+d) DD 96 dd 11011101 10010110 dddddddd d: two's complement number
2324 # SUB A, (IY+d) FD 96 dd 11111101 10010110 dddddddd d: two's complement number
2325 # SBC A, (IX+d) DD 9E dd 11011101 10011110 dddddddd d: two's complement number
2326 # SBC A, (IY+d) FD 9E dd 11111101 10011110 dddddddd d: two's complement number
2327 # AND A, (IX+d) DD A6 dd 11011101 10100110 dddddddd d: two's complement number
2328 # AND A, (IY+d) FD A6 dd 11111101 10100110 dddddddd d: two's complement number
2329 # XOR A, (IX+d) DD AE dd 11011101 10101110 dddddddd d: two's complement number
2330 # XOR A, (IY+d) FD AE dd 11111101 10101110 dddddddd d: two's complement number
2331 # OR A, (IX+d) DD B6 dd 11011101 10110110 dddddddd d: two's complement number
2332 # OR A, (IY+d) FD B6 dd 11111101 10110110 dddddddd d: two's complement number
2333 # CP A, (IX+d) DD BE dd 11011101 10111110 dddddddd d: two's complement number
2334 # CP A, (IY+d) FD BE dd 11111101 10111110 dddddddd d: two's complement number
2335 # xxyyyzzz
2337 my $i_arith = $DDFD_instr[$dcd_instr_y];
2339 print_3($i_arith->{INSTR}, "A, $offs_str", "$i_arith->{EXPL} $offs_expl");
2341 } # given ($dcd_instr_z)
2342 } # $dcd_instr_x == 2
2344 default
2346 given ($dcd_parm0)
2348 when (0xE1)
2350 # POP IX DD E1 11011101 11100001
2351 # POP IY FD E1 11111101 11100001
2353 print_3('pop', $IndexReg, "${IndexReg}.l = [SP++]; ${IndexReg}.h = [SP++]");
2356 when (0xE3)
2358 # EX (SP), IX DD E3 11011101 11100011
2359 # EX (SP), IY FD E3 11111101 11100011
2361 print_3('ex', "(SP), $IndexReg", "[SP] <-> ${IndexReg}.l; [SP+1] <-> ${IndexReg}.h");
2364 when (0xE5)
2366 # PUSH IX DD E5 11011101 11100101
2367 # PUSH IY FD E5 11111101 11100101
2369 print_3('push', $IndexReg, "[--SP] = ${IndexReg}.h; [--SP] = ${IndexReg}.l");
2372 when (0xE9)
2374 # JP (IX) DD E9 11011101 11101001
2375 # JP (IY) FD E9 11111101 11101001
2377 print_3('jp', "($IndexReg)", "Jumps hither: [$IndexReg]");
2378 $prev_is_jump = TRUE;
2381 when (0xF9)
2383 # LD SP, IX DD F9 11011101 11111001
2384 # LD SP, IY FD F9 11111101 11111001
2386 print_3('ld', "SP, $IndexReg", "SP = $IndexReg");
2388 } # given ($dcd_parm0)
2389 } # $dcd_instr_x == 3
2390 } # given ($dcd_instr_x)
2394 #-------------------------------------------------------------------------------
2396 my @block_instr =
2400 INSTR => 'ldi',
2401 EXPL => '[DE++] = [HL++]; --BC'
2404 INSTR => 'cpi',
2405 EXPL => 'A ?= [HL++]; --BC'
2408 INSTR => 'ini',
2409 EXPL => '[HL++] = In{C}; --B'
2412 INSTR => 'outi',
2413 EXPL => 'Out{C} = [HL++]; --B'
2418 INSTR => 'ldd',
2419 EXPL => '[DE--] = [HL--]; --BC'
2422 INSTR => 'cpd',
2423 EXPL => 'A ?= [HL--]; --BC'
2426 INSTR => 'ind',
2427 EXPL => '[HL--] = In{C}; --B'
2430 INSTR => 'outd',
2431 EXPL => 'Out{C} = [HL--]; --B'
2436 INSTR => 'ldir',
2437 EXPL => '[DE++] = [HL++]; --BC; Exit this loop, then BC == 0.'
2440 INSTR => 'cpir',
2441 EXPL => 'A ?= [HL++]; --BC; Exit this loop, then BC == 0 or A == [HL].'
2444 INSTR => 'inir',
2445 EXPL => '[HL++] = In{C}; --B; Exit this loop, then B == 0.'
2448 INSTR => 'otir',
2449 EXPL => 'Out{C} = [HL++]; --B; Exit this loop, then B == 0.'
2454 INSTR => 'lddr',
2455 EXPL => '[DE--] = [HL--]; --BC; Exit this loop, then BC == 0.'
2458 INSTR => 'cpdr',
2459 EXPL => 'A ?= [HL--]; --BC; Exit this loop, then BC == 0 or A == [HL].'
2462 INSTR => 'indr',
2463 EXPL => '[HL--] = In{C}; --B; Exit this loop, then B == 0.'
2466 INSTR => 'otdr',
2467 EXPL => 'Out{C} = [HL--]; --B; Exit this loop, then B == 0.'
2472 sub ED_prefix_decoder()
2474 my ($addr, $str, $i_reg, $reg);
2476 instruction_take_to_pieces($dcd_parm0);
2478 if ($dcd_instr_x == 1)
2480 given ($dcd_instr_z)
2482 when (0)
2484 if ($decoder_silent_level == SILENT0)
2486 $i_reg = $core_registers8[$dcd_instr_z];
2488 if ($dcd_instr_y == 6)
2490 # IN (C) ED 70 11101011 01110000
2491 # xxyyyzzz
2493 print_3('in', '(C)', "$i_reg->{EXPL} = In{[C]}");
2495 else
2497 # IN r, (C) ED xx 11101011 01rrr000
2498 # xxyyyzzz
2500 print_3('in', "$i_reg->{NAME}, (C)", "$i_reg->{EXPL} = In{[C]}");
2503 } # $dcd_instr_z == 0
2505 when (1)
2507 if ($decoder_silent_level == SILENT0)
2509 $i_reg = $core_registers8[$dcd_instr_z];
2511 if ($dcd_instr_y == 6)
2513 # OUT (C) ED 71 11101101 01110001
2514 # xxyyyzzz
2516 print_3('out', '(C)', "Out{[C]} = $i_reg->{EXPL}");
2518 else
2520 # OUT (C), r ED xx 11101101 01rrr001
2521 # xxyyyzzz
2523 print_3('out', "(C), $i_reg->{NAME}", "Out{[C]} = $i_reg->{EXPL}");
2526 } # $dcd_instr_z == 1
2528 when (2)
2530 if ($dcd_instr_q == 0)
2532 # SBC HL, pp ED x2 11101101 01pp0010
2533 # xxppqzzz
2535 $str = $core_registers16a[$dcd_instr_p];
2536 print_3('sbc', "HL, $str", "HL -= $str + CF");
2538 else
2540 # ADC HL, pp ED xA 11101101 01pp1010
2541 # xxppqzzz
2543 $str = $core_registers16a[$dcd_instr_p];
2544 print_3('adc', "HL, $str", "HL += $str + CF");
2546 } # $dcd_instr_z == 2
2548 when (3)
2550 $addr = ($dcd_parm2 << 8) | $dcd_parm1;
2552 if ($dcd_instr_q == 0)
2554 # LD (nn), pp ED x3 aa aa 11101101 01pp0011 a7-0 a15-8
2555 # xxppqzzz
2557 if ($decoder_silent_level == SILENT0)
2559 my $name;
2561 $reg = $core_registers16a[$dcd_instr_p];
2562 $str = reg_name($addr, \$name);
2563 print_3('ld', "($str), $reg", "$name = $reg");
2565 elsif ($decoder_silent_level == SILENT1)
2567 add_ram($addr, '', FALSE);
2570 else
2572 # LD pp, (nn) ED xB aa aa 11101101 01pp1011 a7-0 a15-8
2573 # xxppqzzz
2575 if ($decoder_silent_level == SILENT0)
2577 my $name;
2579 $reg = $core_registers16a[$dcd_instr_p];
2580 $str = reg_name($addr, \$name);
2581 print_3('ld', "$reg, ($str)", "$reg = $name");
2583 elsif ($decoder_silent_level == SILENT1)
2585 add_ram($addr, '', FALSE);
2588 } # $dcd_instr_z == 3
2590 when (4)
2592 # NEG ED xx 11101101 01xxx100
2593 # xxyyyzzz
2595 print_3('neg', '', 'A = -A');
2596 } # $dcd_instr_z == 4
2598 when (5)
2600 if ($dcd_instr_y == 1)
2602 # RETI ED 4D 11101101 01001101
2603 # xxyyyzzz
2605 print_3('reti', '', 'PC.l = [SP++]; PC.h = [SP++]; End of maskable interrupt.');
2606 $prev_is_jump = TRUE;
2608 else
2610 # RETN ED xx 11101101 01xxx101
2611 # xxyyyzzz
2613 print_3('retn', '', 'PC.l = [SP++]; PC.h = [SP++]; End of non-maskable interrupt.');
2614 $prev_is_jump = TRUE;
2616 } # $dcd_instr_z == 5
2618 when (6)
2620 # IM n ED xx 11101101 01xxx110
2621 # xxyyyzzz
2622 # y: 0 - im 0
2623 # 1 - im 0
2624 # 2 - im 1
2625 # 3 - im 2
2626 # 4 - im 0
2627 # 5 - im 0
2628 # 6 - im 1
2629 # 7 - im 2
2631 $dcd_instr_y &= 3;
2632 --$dcd_instr_y if ($dcd_instr_y);
2634 print_3('im', $dcd_instr_y, "Interrupt mode ${dcd_instr_y}.");
2635 } # $dcd_instr_z == 6
2637 when (7)
2639 given ($dcd_instr_y)
2641 when (0)
2643 # LD I, A ED 47 11101101 01000111
2644 # xxyyyzzz
2646 print_3('ld', 'I, A', 'I = A');
2649 when (1)
2651 # LD R, A ED 4F 11101101 01001111
2652 # xxyyyzzz
2654 print_3('ld', 'R, A', 'R = A');
2657 when (2)
2659 # LD A, I ED 57 11101101 01010111
2660 # xxyyyzzz
2662 print_3('ld', 'A, I', 'A = I');
2665 when (3)
2667 # LD A, R ED 5F 11101101 01011111
2668 # xxyyyzzz
2670 print_3('ld', 'A, R', 'A = R');
2673 when (4)
2675 # RRD ED 67 11101101 01100111
2676 # xxyyyzzz
2678 print_3('rrd', '', 'A[3..0] -> [HL][7..4] -> [HL][3..0] -> A[3..0]');
2681 when (5)
2683 # RLD ED 6F 11101101 01101111
2684 # xxyyyzzz
2686 print_3('rld', '', 'A[3..0] <- [HL][7..4] <- [HL][3..0] <- A[3..0]');
2689 default
2691 # NOP ED 77 11101101 01110111
2692 # NOP ED 7F 11101101 01111111
2693 # xxyyyzzz
2695 print_3('nop', '', 'No operation.');
2696 } # $dcd_instr_y == 6 || $dcd_instr_y == 7
2697 } # given ($dcd_instr_y)
2698 } # $dcd_instr_z == 7
2699 } # given ($dcd_instr_z)
2700 } # if ($dcd_instr_x == 1)
2701 elsif ($dcd_instr_x == 2 && $dcd_instr_y >= 4 && $dcd_instr_z <= 3)
2703 # LDI ED A0 11101101 10100000
2704 # CPI ED A1 11101101 10100001
2705 # INI ED A2 11101101 10100010
2706 # OUTI ED A3 11101101 10100011
2707 # xxyyyzzz
2709 # LDD ED A8 11101101 10101000
2710 # CPD ED A9 11101101 10101001
2711 # IND ED AA 11101101 10101010
2712 # OUTD ED AB 11101101 10101011
2713 # xxyyyzzz
2715 # LDIR ED B0 11101101 10110000
2716 # CPIR ED B1 11101101 10110001
2717 # INIR ED B2 11101101 10110010
2718 # OTIR ED B3 11101101 10110011
2719 # xxyyyzzz
2721 # LDDR ED B8 11101101 10111000
2722 # CPDR ED B9 11101101 10111001
2723 # INDR ED BA 11101101 10111010
2724 # OTDR ED BB 11101101 10111011
2725 # xxyyyzzz
2727 my $i_block = $block_instr[$dcd_instr_y - 4][$dcd_instr_z];
2729 print_3($i_block->{INSTR}, '', $i_block->{EXPL});
2731 else
2733 print_3('invalid instruction', '', '');
2737 #-------------------------------------------------------------------------------
2739 sub instruction_take_to_pieces($)
2741 my $Instruction = $_[0];
2743 $dcd_instr_x = ($Instruction >> 6) & 3;
2744 $dcd_instr_y = ($Instruction >> 3) & 7;
2745 $dcd_instr_z = $Instruction & 7;
2746 $dcd_instr_p = ($Instruction >> 4) & 3;
2747 $dcd_instr_q = ($Instruction >> 3) & 1;
2750 #-------------------------------------------------------------------------------
2753 # Decodes the $BlockRef.
2756 my @shift_instr =
2759 INSTR => 'rlca',
2760 EXPL => 'CF <- A[7..0] <- A.7'
2763 INSTR => 'rrca',
2764 EXPL => 'A.0 -> A[7..0] -> CF'
2767 INSTR => 'rla',
2768 EXPL => 'CF <- A[7..0] <- CF'
2771 INSTR => 'rra',
2772 EXPL => 'CF -> A[7..0] -> CF'
2775 INSTR => 'daa',
2776 EXPL => 'Conditionally decimal adjusts the Accumulator.'
2779 INSTR => 'cpl',
2780 EXPL => 'A = ~A'
2783 INSTR => 'scf',
2784 EXPL => 'CF = 1'
2787 INSTR => 'ccf',
2788 EXPL => 'CF = 0'
2792 my @conditions =
2795 COND => 'NZ',
2796 EXPL => 'ZF == 0'
2799 COND => 'Z',
2800 EXPL => 'ZF == 1'
2803 COND => 'NC',
2804 EXPL => 'CF == 0'
2807 COND => 'C',
2808 EXPL => 'CF == 1'
2811 COND => 'PO',
2812 EXPL => 'PF == 0'
2815 COND => 'PE',
2816 EXPL => 'PF == 1'
2819 COND => 'P',
2820 EXPL => 'SF == 0'
2823 COND => 'M',
2824 EXPL => 'SF == 1'
2828 sub instruction_decoder($$)
2830 my ($Address, $BlockRef) = @_;
2831 my ($addr, $label, $invalid, $str);
2833 $dcd_address = $Address;
2834 $dcd_instr_size = $BlockRef->{SIZE};
2835 $dcd_instr = $rom[$dcd_address];
2836 $label = $BlockRef->{LABEL};
2838 if ($decoder_silent_level == SILENT0)
2840 printf("0x%04X: %02X", $dcd_address, $dcd_instr) if (! $gen_assembly_code);
2843 $invalid = FALSE;
2845 if ($dcd_instr_size == 1)
2847 if ($decoder_silent_level == SILENT0)
2849 print(($gen_assembly_code) ? "\t" : "\t\t");
2852 elsif ($dcd_instr_size == 2)
2854 $dcd_parm0 = $rom[$dcd_address + 1];
2855 $invalid = TRUE if ($dcd_parm0 == EMPTY);
2857 if ($decoder_silent_level == SILENT0)
2859 if ($gen_assembly_code)
2861 print "\t";
2863 else
2865 printf " %02X\t\t", $dcd_parm0;
2869 elsif ($dcd_instr_size == 3)
2871 $dcd_parm0 = $rom[$dcd_address + 1];
2872 $dcd_parm1 = $rom[$dcd_address + 2];
2873 $invalid = TRUE if ($dcd_parm0 == EMPTY || $dcd_parm1 == EMPTY);
2875 if ($decoder_silent_level == SILENT0)
2877 if ($gen_assembly_code)
2879 print "\t";
2881 else
2883 printf " %02X %02X\t", $dcd_parm0, $dcd_parm1;
2887 elsif ($dcd_instr_size == 4)
2889 $dcd_parm0 = $rom[$dcd_address + 1];
2890 $dcd_parm1 = $rom[$dcd_address + 2];
2891 $dcd_parm2 = $rom[$dcd_address + 3];
2892 $invalid = TRUE if ($dcd_parm0 == EMPTY || $dcd_parm1 == EMPTY || $dcd_parm2 == EMPTY);
2894 if ($decoder_silent_level == SILENT0)
2896 if ($gen_assembly_code)
2898 print "\t";
2900 else
2902 printf " %02X %02X %02X\t", $dcd_parm0, $dcd_parm1, $dcd_parm2;
2906 else
2908 printf STDERR "Internal error: The size of instruction (addr:0x%04X) is zero!", $dcd_address;
2909 exit(1);
2913 # x y z
2914 # +---/ \---+ +------/ \------+ +------/ \------+
2915 # | | | | | |
2916 # | 7 6 | | 5 4 3 | | 2 1 0 |
2917 # +-----------------------------------------------+
2918 # | . | . | . | . | . | . | . | . |
2919 # +-----------------------------------------------+
2920 # | 5 4 | | 3 |
2921 # | | | |
2922 # +---\ /---+ +\ /+
2923 # p q
2926 $prev_is_jump = FALSE;
2928 instruction_take_to_pieces($dcd_instr);
2930 if ($dcd_instr_x == 0)
2932 # x y z
2933 # +---/ \---+ +------/ \------+ +------/ \------+
2934 # | 7 6 | | 5 4 3 | | 2 1 0 |
2935 # +-----------------------------------------------+
2936 # | 0 | 0 | . | . | . | . | . | . |
2937 # +-----------------------------------------------+
2938 # | 5 4 | | 3 |
2939 # +---\ /---+ +\ /+
2940 # p q
2942 given ($dcd_instr_z)
2944 when (0)
2946 # x y z
2947 # +---/ \---+ +------/ \------+ +------/ \------+
2948 # | 7 6 | | 5 4 3 | | 2 1 0 |
2949 # +-----------------------------------------------+
2950 # | 0 | 0 | . | . | . | 0 | 0 | 0 |
2951 # +-----------------------------------------------+
2953 given ($dcd_instr_y)
2955 when (0)
2957 # NOP 00 00000000
2958 # xxyyyzzz
2960 print_3('nop', '', 'No operation.');
2963 when (1)
2965 # EX AF, AF' 08 00001000
2966 # xxyyyzzz
2968 print_3('ex', "AF, AF'", "AF <-> AF'");
2971 when (2)
2973 # DJNZ e 10 00010000 eeeeeeee e: two's complement number
2974 # xxyyyzzz
2976 if ($decoder_silent_level == SILENT0)
2978 my $addr = $dcd_address + 2 + expand_offset($dcd_parm0);
2979 my $target;
2981 $str = label_name($addr);
2982 $target = jump_direction($addr);
2983 print_3('djnz', $str, "If (--B != 0) jumps$target");
2984 $prev_is_jump = TRUE;
2988 when (3)
2990 # JR e 18 00011000 eeeeeeee e: two's complement number
2991 # xxyyyzzz
2993 if ($decoder_silent_level == SILENT0)
2995 my $addr = $dcd_address + 2 + expand_offset($dcd_parm0);
2996 my $target;
2998 $str = label_name($addr);
2999 $target = jump_direction($addr);
3000 print_3('jr', $str, "Jumps$target");
3001 $prev_is_jump = TRUE;
3005 default
3007 # 4-7
3008 # JR cc, e xx 00ccc000 eeeeeeee e: two's complement number
3009 # xxyyyzzz
3011 if ($decoder_silent_level == SILENT0)
3013 my $addr = $dcd_address + 2 + expand_offset($dcd_parm0);
3014 my $cond = $conditions[$dcd_instr_y - 4];
3015 my $target;
3017 $str = label_name($addr);
3018 $target = jump_direction($addr);
3019 print_3('jr', "$cond->{COND}, $str", "Jumps if ($cond->{EXPL})$target");
3020 $prev_is_jump = TRUE;
3023 } # given ($dcd_instr_y)
3024 } # $dcd_instr_z == 0
3026 when (1)
3028 # x z
3029 # +---/ \---+ +------/ \------+
3030 # | 7 6 | | 2 1 0 |
3031 # +-----------------------------------------------+
3032 # | 0 | 0 | . | . | . | 0 | 0 | 1 |
3033 # +-----------------------------------------------+
3034 # | 5 4 | | 3 |
3035 # +---\ /---+ +\ /+
3036 # p q
3038 if ($dcd_instr_q == 0)
3040 # LD rp, #nn x1 00rr0001 nnnnnnnn nnnnnnnn
3041 # xxppqzzz
3042 # rp: BC, DE, HL, SP
3044 my $r16 = $core_registers16a[$dcd_instr_p];
3046 $str = sprintf '0x%04X', ($dcd_parm1 << 8) | $dcd_parm0;
3047 print_3('ld', "$r16, #$str", "$r16 = $str");
3049 else
3051 # ADD HL, rp x9 00rr1001
3052 # xxppqzzz
3053 # rp: BC, DE, HL, SP
3055 $str = $core_registers16a[$dcd_instr_p];
3056 print_3('add', "HL, $str", "HL += $str");
3058 } # $dcd_instr_z == 1
3060 when (2)
3062 # x y z
3063 # +---/ \---+ +------/ \------+ +------/ \------+
3064 # | 7 6 | | 5 4 3 | | 2 1 0 |
3065 # +-----------------------------------------------+
3066 # | 0 | 0 | . | . | . | 0 | 1 | 0 |
3067 # +-----------------------------------------------+
3068 # | 5 4 | | 3 |
3069 # +---\ /---+ +\ /+
3070 # p q
3072 if ($dcd_instr_q == 0)
3074 # x z
3075 # +---/ \---+ +------/ \------+
3076 # | 7 6 | | 2 1 0 |
3077 # +-----------------------------------------------+
3078 # | 0 | 0 | . | . | 0 | 0 | 1 | 0 |
3079 # +-----------------------------------------------+
3080 # | 5 4 | | 3 |
3081 # +---\ /---+ +\ /+
3082 # p q
3084 given ($dcd_instr_p)
3086 when (0)
3088 # LD (BC), A 02 00000010
3089 # xxppqzzz
3091 print_3('ld', '(BC), A', '[BC] = A');
3092 } # $dcd_instr_p == 0
3094 when (1)
3096 # LD (DE), A 12 00010010
3097 # xxppqzzz
3099 print_3('ld', '(DE), A', '[DE] = A');
3100 } # $dcd_instr_p == 1
3102 when (2)
3104 # LD (nn), HL 22 00100010 nnnnnnnn nnnnnnnn
3105 # xxppqzzz
3107 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3109 if ($decoder_silent_level == SILENT0)
3111 my $name;
3113 $str = reg_name($addr, \$name);
3114 print_3('ld', "($str), HL", "$name = HL");
3116 elsif ($decoder_silent_level == SILENT1)
3118 add_ram($addr, '', FALSE);
3120 } # $dcd_instr_p == 2
3122 when (3)
3124 # LD (nn), A 32 00110010 nnnnnnnn nnnnnnnn
3125 # xxppqzzz
3127 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3129 if ($decoder_silent_level == SILENT0)
3131 my $name;
3133 $str = reg_name($addr, \$name);
3134 print_3('ld', "($str), A", "$name = A");
3136 elsif ($decoder_silent_level == SILENT1)
3138 add_ram($addr, '', FALSE);
3140 } # $dcd_instr_p == 3
3141 } # given ($dcd_instr_p)
3142 } # if ($dcd_instr_q == 0)
3143 else
3145 # x z
3146 # +---/ \---+ +------/ \------+
3147 # | 7 6 | | 2 1 0 |
3148 # +-----------------------------------------------+
3149 # | 0 | 0 | . | . | 1 | 0 | 1 | 0 |
3150 # +-----------------------------------------------+
3151 # | 5 4 | | 3 |
3152 # +---\ /---+ +\ /+
3153 # p q
3155 given ($dcd_instr_p)
3157 when (0)
3159 # LD A, (BC) 0A 00001010
3160 # xxppqzzz
3162 print_3('ld', 'A, (BC)', 'A = [BC]');
3163 } # $dcd_instr_p == 0
3165 when (1)
3167 # LD A, (DE) 1A 00011010
3168 # xxppqzzz
3170 print_3('ld', 'A, (DE)', 'A = [DE]');
3171 } # $dcd_instr_p == 1
3173 when (2)
3175 # LD HL, (nn) 2A 00101010 nnnnnnnn nnnnnnnn
3176 # xxppqzzz
3178 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3180 if ($decoder_silent_level == SILENT0)
3182 my $name;
3184 $str = reg_name($addr, \$name);
3185 print_3('ld', "HL, ($str)", "HL = $name");
3187 elsif ($decoder_silent_level == SILENT1)
3189 add_ram($addr, '', FALSE);
3191 } # $dcd_instr_p == 2
3193 when (3)
3195 # LD A, (nn) 3A 00111010 nnnnnnnn nnnnnnnn
3196 # xxppqzzz
3198 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3200 if ($decoder_silent_level == SILENT0)
3202 my $name;
3204 $str = reg_name($addr, \$name);
3205 print_3('ld', "A, ($str)", "A = $name");
3207 elsif ($decoder_silent_level == SILENT1)
3209 add_ram($addr, '', FALSE);
3211 } # $dcd_instr_p == 3
3212 } # given ($dcd_instr_p)
3214 } # $dcd_instr_z == 2
3216 when (3)
3218 # x z
3219 # +---/ \---+ +------/ \------+
3220 # | 7 6 | | 2 1 0 |
3221 # +-----------------------------------------------+
3222 # | 0 | 0 | . | . | . | 0 | 1 | 1 |
3223 # +-----------------------------------------------+
3224 # | 5 4 | | 3 |
3225 # +---\ /---+ +\ /+
3226 # p q
3228 if ($dcd_instr_q == 0)
3230 # INC rp x3 00rr0011
3231 # xxppqzzz
3232 # rp: BC, DE, HL, SP
3234 $str = $core_registers16a[$dcd_instr_p];
3235 print_3('inc', $str, "++$str");
3237 else
3239 # DEC rp x3 00rr1011
3240 # xxppqzzz
3241 # rp: BC, DE, HL, SP
3243 $str = $core_registers16a[$dcd_instr_p];
3244 print_3('dec', $str, "--$str");
3246 } # $dcd_instr_z == 3
3248 when (4)
3250 # x y z
3251 # +---/ \---+ +------/ \------+ +------/ \------+
3252 # | 7 6 | | 5 4 3 | | 2 1 0 |
3253 # +-----------------------------------------------+
3254 # | 0 | 0 | . | . | . | 1 | 0 | 0 |
3255 # +-----------------------------------------------+
3257 # INC r xx 00rrr100
3258 # xxyyyzzz
3259 # r: B, C, D, E, H, L, (HL), A
3261 if ($decoder_silent_level == SILENT0)
3263 my $i_reg = $core_registers8[$dcd_instr_y];
3265 print_3('inc', $i_reg->{NAME}, "++$i_reg->{EXPL}");
3267 } # $dcd_instr_z == 4
3269 when (5)
3271 # x y z
3272 # +---/ \---+ +------/ \------+ +------/ \------+
3273 # | 7 6 | | 5 4 3 | | 2 1 0 |
3274 # +-----------------------------------------------+
3275 # | 0 | 0 | . | . | . | 1 | 0 | 1 |
3276 # +-----------------------------------------------+
3278 # DEC r xx 00rrr101
3279 # xxyyyzzz
3280 # r: B, C, D, E, H, L, (HL), A
3282 if ($decoder_silent_level == SILENT0)
3284 my $i_reg = $core_registers8[$dcd_instr_y];
3286 print_3('dec', $i_reg->{NAME}, "--$i_reg->{EXPL}");
3288 } # $dcd_instr_z == 5
3290 when (6)
3292 # x y z
3293 # +---/ \---+ +------/ \------+ +------/ \------+
3294 # | 7 6 | | 5 4 3 | | 2 1 0 |
3295 # +-----------------------------------------------+
3296 # | 0 | 0 | . | . | . | 1 | 1 | 0 |
3297 # +-----------------------------------------------+
3299 # LD r, #n 00rrr110 nnnnnnnn
3300 # xxyyyzzz
3302 if ($decoder_silent_level == SILENT0)
3304 my $i_reg = $core_registers8[$dcd_instr_y];
3305 my $char = decode_char($dcd_parm0);
3307 $str = sprintf '0x%02X', $dcd_parm0;
3308 print_3('ld', "$i_reg->{NAME}, #$str", "$i_reg->{EXPL} = $str$char");
3310 } # $dcd_instr_z == 6
3312 when (7)
3314 # x y z
3315 # +---/ \---+ +------/ \------+ +------/ \------+
3316 # | 7 6 | | 5 4 3 | | 2 1 0 |
3317 # +-----------------------------------------------+
3318 # | 0 | 0 | . | . | . | 1 | 1 | 1 |
3319 # +-----------------------------------------------+
3321 # RLCA 07 00000111
3322 # RRCA 0F 00001111
3323 # RLA 17 00010111
3324 # RRA 1F 00011111
3325 # DAA 27 00100111
3326 # CPL 2F 00101111
3327 # SCF 37 00110111
3328 # CCF 3F 00111111
3329 # xxyyyzzz
3331 my $s_instr = $shift_instr[$dcd_instr_y];
3333 print_3($s_instr->{INSTR}, '', $s_instr->{EXPL});
3334 } # $dcd_instr_z == 7
3335 } # given ($dcd_instr_z)
3336 } # if ($dcd_instr_x == 0)
3337 elsif ($dcd_instr_x == 1)
3339 # x y z
3340 # +---/ \---+ +------/ \------+ +------/ \------+
3341 # | 7 6 | | 5 4 3 | | 2 1 0 |
3342 # +-----------------------------------------------+
3343 # | 0 | 1 | . | . | . | . | . | . |
3344 # +-----------------------------------------------+
3346 if ($dcd_instr_y == 6)
3348 # x y z
3349 # +---/ \---+ +------/ \------+ +------/ \------+
3350 # | 7 6 | | 5 4 3 | | 2 1 0 |
3351 # +-----------------------------------------------+
3352 # | 0 | 1 | 1 | 1 | 0 | . | . | . |
3353 # +-----------------------------------------------+
3355 # HALT 76 01110110
3356 # xxyyyzzz
3358 print_3('halt', '', 'Suspends CPU.');
3360 else
3362 # x y z
3363 # +---/ \---+ +------/ \------+ +------/ \------+
3364 # | 7 6 | | 5 4 3 | | 2 1 0 |
3365 # +-----------------------------------------------+
3366 # | 0 | 1 | . | . | . | . | . | . |
3367 # +-----------------------------------------------+
3369 # LD r, r' xx 01dddsss
3370 # xxyyyzzz
3371 # r: B, C, D, E, H, L, (HL), A
3373 if ($decoder_silent_level == SILENT0)
3375 my $i_rega = $core_registers8[$dcd_instr_y];
3376 my $i_regb = $core_registers8[$dcd_instr_z];
3378 print_3('ld', "$i_rega->{NAME}, $i_regb->{NAME}", "$i_rega->{EXPL} = $i_regb->{EXPL}");
3381 } # elsif ($dcd_instr_x == 1)
3382 elsif ($dcd_instr_x == 2)
3384 # x y z
3385 # +---/ \---+ +------/ \------+ +------/ \------+
3386 # | 7 6 | | 5 4 3 | | 2 1 0 |
3387 # +-----------------------------------------------+
3388 # | 1 | 0 | . | . | . | . | . | . |
3389 # +-----------------------------------------------+
3391 # ADD A, r 8x 10000rrr
3392 # ADC A, r 8x 10001rrr
3393 # SUB A, r 9x 10010rrr
3394 # SBC A, r 9x 10011rrr
3395 # AND A, r Ax 10100rrr
3396 # XOR A, r Ax 10101rrr
3397 # OR A, r Bx 10110rrr
3398 # CP A, r Bx 10111rrr
3399 # xxyyyzzz
3400 # r: B, C, D, E, H, L, (HL), A
3402 if ($decoder_silent_level == SILENT0)
3404 my $i_arith = $DDFD_instr[$dcd_instr_y];
3405 my $i_reg = $core_registers8[$dcd_instr_z];
3406 my $str0 = ($dcd_instr_z == 7) ? ' (A = 0)' : '';
3408 print_3($i_arith->{INSTR}, "A, $i_reg->{NAME}", "$i_arith->{EXPL} $i_reg->{EXPL}$str0");
3411 else # $dcd_instr_x == 3
3413 # x y z
3414 # +---/ \---+ +------/ \------+ +------/ \------+
3415 # | 7 6 | | 5 4 3 | | 2 1 0 |
3416 # +-----------------------------------------------+
3417 # | 1 | 1 | . | . | . | . | . | . |
3418 # +-----------------------------------------------+
3419 # | 5 4 | | 3 |
3420 # +---\ /---+ +\ /+
3421 # p q
3423 given ($dcd_instr_z)
3425 when (0)
3427 # x y z
3428 # +---/ \---+ +------/ \------+ +------/ \------+
3429 # | 7 6 | | 5 4 3 | | 2 1 0 |
3430 # +-----------------------------------------------+
3431 # | 1 | 1 | . | . | . | 0 | 0 | 0 |
3432 # +-----------------------------------------------+
3434 # RET cc xx 11ccc000
3435 # xxyyyzzz
3436 # cc: NZ, Z, NC, C, PO, PE, P, M
3438 my $cond = $conditions[$dcd_instr_y];
3440 print_3('ret', $cond->{COND}, "If ($cond->{EXPL}) PC.l = [SP++]; PC.h = [SP++]");
3441 $prev_is_jump = TRUE;
3444 when (1)
3446 # x y z
3447 # +---/ \---+ +------/ \------+ +------/ \------+
3448 # | 7 6 | | 5 4 3 | | 2 1 0 |
3449 # +-----------------------------------------------+
3450 # | 1 | 1 | . | . | . | 0 | 0 | 1 |
3451 # +-----------------------------------------------+
3452 # | 5 4 | | 3 |
3453 # +---\ /---+ +\ /+
3454 # p q
3456 if ($dcd_instr_q == 0)
3458 # x z
3459 # +---/ \---+ +------/ \------+
3460 # | 7 6 | | 2 1 0 |
3461 # +-----------------------------------------------+
3462 # | 1 | 1 | . | . | 0 | 0 | 0 | 1 |
3463 # +-----------------------------------------------+
3464 # | 5 4 | | 3 |
3465 # +---\ /---+ +\ /+
3466 # p q
3468 # POP rp xx 11rr0001
3469 # xxppqzzz
3470 # rp: BC, DE, HL, AF
3472 given ($dcd_instr_p)
3474 when (0) { $str = 'C = [SP++]; B = [SP++]'; }
3475 when (1) { $str = 'E = [SP++]; D = [SP++]'; }
3476 when (2) { $str = 'L = [SP++]; H = [SP++]'; }
3477 when (3) { $str = 'F = [SP++]; A = [SP++]'; }
3480 print_3('pop', $core_registers16b[$dcd_instr_p], $str);
3482 else
3484 # x z
3485 # +---/ \---+ +------/ \------+
3486 # | 7 6 | | 2 1 0 |
3487 # +-----------------------------------------------+
3488 # | 1 | 1 | . | . | 1 | 0 | 0 | 1 |
3489 # +-----------------------------------------------+
3490 # | 5 4 | | 3 |
3491 # +---\ /---+ +\ /+
3492 # p q
3494 given ($dcd_instr_p)
3496 when (0)
3498 # RET C9 11001001
3499 # xxppqzzz
3501 print_3('ret', '', 'PC.l = [SP++]; PC.h = [SP++]');
3502 $prev_is_jump = TRUE;
3505 when (1)
3507 # EXX D9 11011001
3508 # xxppqzzz
3510 print_3('exx', '', "BC <-> BC'; DE <-> DE'; HL <-> HL'");
3513 when (2)
3515 # JP (HL) E9 11101001
3516 # xxppqzzz
3518 print_3('jp', '(HL)', 'Jumps to value of HL.');
3519 $prev_is_jump = TRUE;
3522 when (3)
3524 # LD SP, HL F9 11111001
3525 # xxppqzzz
3527 print_3('ld', 'SP, HL', 'SP = HL');
3529 } # given ($dcd_instr_p)
3531 } # $dcd_instr_z == 1
3533 when (2)
3535 # x y z
3536 # +---/ \---+ +------/ \------+ +------/ \------+
3537 # | 7 6 | | 5 4 3 | | 2 1 0 |
3538 # +-----------------------------------------------+
3539 # | 1 | 1 | . | . | . | 0 | 1 | 0 |
3540 # +-----------------------------------------------+
3542 # JP cc, nn xx nn nn 11ccc010 a7-0 a15-8
3543 # xxyyyzzz
3544 # cc: NZ, Z, NC, C, PO, PE, P, M
3546 if ($decoder_silent_level == SILENT0)
3548 my $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3549 my $cond = $conditions[$dcd_instr_y];
3550 my $target;
3552 $str = label_name($addr);
3553 $target = jump_direction($addr);
3554 print_3('jp', "$cond->{COND}, $str", "Jumps if ($cond->{EXPL})$target");
3555 $prev_is_jump = TRUE;
3559 when (3)
3561 # x y z
3562 # +---/ \---+ +------/ \------+ +------/ \------+
3563 # | 7 6 | | 5 4 3 | | 2 1 0 |
3564 # +-----------------------------------------------+
3565 # | 1 | 1 | . | . | . | 0 | 1 | 1 |
3566 # +-----------------------------------------------+
3568 given ($dcd_instr_y)
3570 when (0)
3572 # JP nn 11000011 a7-0 a15-8
3573 # xxyyyzzz
3575 if ($decoder_silent_level == SILENT0)
3577 my $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3578 my $target;
3580 $str = label_name($addr);
3581 $target = jump_direction($addr);
3582 print_3('jp', $str, "Jumps$target");
3583 $prev_is_jump = TRUE;
3587 when (1)
3589 instruction_take_to_pieces($dcd_parm0);
3590 CB_prefix_decoder();
3593 when (2)
3595 # OUT (n), A D3 11010011 nnnnnnnn
3596 # xxyyyzzz
3598 if ($decoder_silent_level == SILENT0)
3600 my $io = sprintf '0x%02X', $dcd_parm0;
3602 if ($gen_assembly_code)
3604 print_3('out', "($io), A", "Out{$io} = A");
3606 else
3608 $str = io_name($dcd_parm0);
3609 print_3('out', "($str), A", "Out{$io} = A");
3612 elsif ($decoder_silent_level == SILENT1)
3614 add_io($dcd_parm0, '', FALSE);
3618 when (3)
3620 # IN A, (n) DB 11011011 nnnnnnnn
3621 # xxyyyzzz
3623 if ($decoder_silent_level == SILENT0)
3625 my $io = sprintf '0x%02X', $dcd_parm0;
3627 if ($gen_assembly_code)
3629 print_3('in', "A, ($io)", "A = In{$io}");
3631 else
3633 $str = io_name($dcd_parm0);
3634 print_3('in', "A, ($str)", "A = In{$io}");
3637 elsif ($decoder_silent_level == SILENT1)
3639 add_io($dcd_parm0, '', FALSE);
3643 when (4)
3645 # EX (SP), HL E3 11100011
3646 # xxyyyzzz
3648 print_3('ex', '(SP), HL', "[SP] <-> L; [SP+1] <-> H");
3651 when (5)
3653 # EX DE, HL EB 11101011
3654 # xxyyyzzz
3656 print_3('ex', 'DE, HL', "E <-> L; D <-> H");
3659 when (6)
3661 # DI F3 11110011
3662 # xxyyyzzz
3664 print_3('di', '', 'Disable interrupts.');
3667 when (7)
3669 # EI FB 11111011
3670 # xxyyyzzz
3672 print_3('ei', '', 'Enable interrupts.');
3674 } # given ($dcd_instr_y)
3675 } # $dcd_instr_z == 3
3677 when (4)
3679 # x y z
3680 # +---/ \---+ +------/ \------+ +------/ \------+
3681 # | 7 6 | | 5 4 3 | | 2 1 0 |
3682 # +-----------------------------------------------+
3683 # | 1 | 1 | . | . | . | 1 | 0 | 0 |
3684 # +-----------------------------------------------+
3686 # CALL cc, nn xx nn nn 11ccc100 a7-0 a15-8
3687 # xxyyyzzz
3688 # cc: NZ, Z, NC, C, PO, PE, P, M
3690 if ($decoder_silent_level == SILENT0)
3692 my $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3693 my $cond = $conditions[$dcd_instr_y];
3694 my $target;
3696 $str = label_name($addr);
3697 $target = jump_direction($addr);
3698 print_3('call', "$cond->{COND}, $str", "Calls ([--SP] = PC.h; [--SP] = PC.l) if ($cond->{EXPL})$target");
3700 } # $dcd_instr_z == 4
3702 when (5)
3704 # x y z
3705 # +---/ \---+ +------/ \------+ +------/ \------+
3706 # | 7 6 | | 5 4 3 | | 2 1 0 |
3707 # +-----------------------------------------------+
3708 # | 1 | 1 | . | . | . | 1 | 0 | 1 |
3709 # +-----------------------------------------------+
3710 # | 5 4 | | 3 |
3711 # +---\ /---+ +\ /+
3712 # p q
3714 if ($dcd_instr_q == 0)
3716 # x z
3717 # +---/ \---+ +------/ \------+
3718 # | 7 6 | | 2 1 0 |
3719 # +-----------------------------------------------+
3720 # | 1 | 1 | . | . | 0 | 1 | 0 | 1 |
3721 # +-----------------------------------------------+
3722 # | 5 4 | | 3 |
3723 # +---\ /---+ +\ /+
3724 # p q
3726 # PUSH rp xx 11rr0101
3727 # xxppqzzz
3728 # rp: BC, DE, HL, AF
3730 given ($dcd_instr_p)
3732 when (0) { $str = '[--SP] = B; [--SP] = C'; }
3733 when (1) { $str = '[--SP] = D; [--SP] = E'; }
3734 when (2) { $str = '[--SP] = H; [--SP] = L'; }
3735 when (3) { $str = '[--SP] = A; [--SP] = F'; }
3738 print_3('push', $core_registers16b[$dcd_instr_p], $str);
3740 else
3742 # x z
3743 # +---/ \---+ +------/ \------+
3744 # | 7 6 | | 2 1 0 |
3745 # +-----------------------------------------------+
3746 # | 1 | 1 | . | . | 1 | 1 | 0 | 1 |
3747 # +-----------------------------------------------+
3748 # | 5 4 | | 3 |
3749 # +---\ /---+ +\ /+
3750 # p q
3752 given ($dcd_instr_p)
3754 when (0)
3756 # x z
3757 # +---/ \---+ +------/ \------+
3758 # | 7 6 | | 2 1 0 |
3759 # +-----------------------------------------------+
3760 # | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
3761 # +-----------------------------------------------+
3762 # | 5 4 | | 3 |
3763 # +---\ /---+ +\ /+
3764 # p q
3766 # CALL nn CD nn nn 11001101 a7-0 a15-8
3767 # xxyyyzzz
3769 if ($decoder_silent_level == SILENT0)
3771 my $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3772 my $target;
3774 $str = label_name($addr);
3775 $target = jump_direction($addr);
3776 print_3('call', $str, "Calls ([--SP] = PC.h; [--SP] = PC.l)$target");
3780 when (1)
3782 # x z
3783 # +---/ \---+ +------/ \------+
3784 # | 7 6 | | 2 1 0 |
3785 # +-----------------------------------------------+
3786 # | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
3787 # +-----------------------------------------------+
3788 # | 5 4 | | 3 |
3789 # +---\ /---+ +\ /+
3790 # p q
3792 DDFD_prefix_decoder('IX');
3795 when (2)
3797 # x z
3798 # +---/ \---+ +------/ \------+
3799 # | 7 6 | | 2 1 0 |
3800 # +-----------------------------------------------+
3801 # | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
3802 # +-----------------------------------------------+
3803 # | 5 4 | | 3 |
3804 # +---\ /---+ +\ /+
3805 # p q
3807 ED_prefix_decoder();
3810 when (3)
3812 # x z
3813 # +---/ \---+ +------/ \------+
3814 # | 7 6 | | 2 1 0 |
3815 # +-----------------------------------------------+
3816 # | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
3817 # +-----------------------------------------------+
3818 # | 5 4 | | 3 |
3819 # +---\ /---+ +\ /+
3820 # p q
3822 DDFD_prefix_decoder('IY');
3824 } # given ($dcd_instr_p)
3826 } # $dcd_instr_z == 5
3828 when (6)
3830 # x y z
3831 # +---/ \---+ +------/ \------+ +------/ \------+
3832 # | 7 6 | | 5 4 3 | | 2 1 0 |
3833 # +-----------------------------------------------+
3834 # | 1 | 1 | . | . | . | 1 | 1 | 0 |
3835 # +-----------------------------------------------+
3837 # ADD A, #n 11000110 nnnnnnnn
3838 # ADC A, #n 11001110 nnnnnnnn
3839 # SUB A, #n 11010110 nnnnnnnn
3840 # SBC A, #n 11011110 nnnnnnnn
3841 # AND A, #n 11100110 nnnnnnnn
3842 # XOR A, #n 11101110 nnnnnnnn
3843 # OR A, #n 11110110 nnnnnnnn
3844 # CP A, #n 11111110 nnnnnnnn
3845 # xxyyyzzz
3847 my $i_arith = $DDFD_instr[$dcd_instr_y];
3848 my $num = sprintf '0x%02X', $dcd_parm0;
3849 my $char = decode_char($dcd_parm0);
3850 my $str0 = '';
3852 $str0 = ' (A = A)' if ($dcd_instr_y == 0 && $dcd_parm0 == 0); # ADD A, 0
3853 $str0 = ' (A = A)' if ($dcd_instr_y == 2 && $dcd_parm0 == 0); # SUB A, 0
3854 $str0 = ' (A = A)' if ($dcd_instr_y == 4 && $dcd_parm0 == 0xFF); # AND A, 0xFF
3855 $str0 = ' (A = ~A)' if ($dcd_instr_y == 5 && $dcd_parm0 == 0xFF); # XOR A, 0xFF
3856 $str0 = ' (A = A)' if ($dcd_instr_y == 6 && $dcd_parm0 == 0); # OR A, 0
3858 print_3($i_arith->{INSTR}, "A, #$num", "$i_arith->{EXPL} $num$char$str0");
3859 } # $dcd_instr_z == 6
3861 when (7)
3863 # x y z
3864 # +---/ \---+ +------/ \------+ +------/ \------+
3865 # | 7 6 | | 5 4 3 | | 2 1 0 |
3866 # +-----------------------------------------------+
3867 # | 1 | 1 | . | . | . | 1 | 1 | 1 |
3868 # +-----------------------------------------------+
3870 # RST t xx 11ttt111
3871 # xxyyyzzz
3872 # t: 0 - 7
3874 $addr = sprintf '0x%04X', $dcd_instr_y * 8;
3875 $str = sprintf '0x%02X', $dcd_instr_y * 8;
3876 print_3('rst', $str, "Calls interrupt: [--SP] = PC.h; [--SP] = PC.l; PC = $addr");
3877 } # $dcd_instr_z == 7
3878 } # given ($dcd_instr_z)
3879 } # $dcd_instr_x == 3
3882 ################################################################################
3883 ################################################################################
3886 # Reads the sfrs and bits from the $Line.
3889 sub process_header_line($)
3891 my $Line = $_[0];
3893 Log((' ' x $embed_level) . $Line, 5);
3895 if ($Line =~ /^#\s*include\s+["<]\s*(\S+)\s*[">]$/o)
3897 $embed_level += 4;
3898 &read_header("$include_path/$1");
3899 $embed_level -= 4;
3901 elsif ($Line =~ /^__sfr\s+__at\s*(?:\(\s*)?0x([[:xdigit:]]+)(?:\s*\))?\s+([\w_]+)/io)
3903 # __sfr __at (0x80) P0 ; /* PORT 0 */
3905 add_ram(hex($1), $2, TRUE);
3907 elsif ($Line =~ /^SFR\s*\(\s*([\w_]+)\s*,\s*0x([[:xdigit:]]+)\s*\)/io)
3909 # SFR(P0, 0x80); // Port 0
3911 add_ram(hex($2), $1, TRUE);
3913 elsif ($Line =~ /^sfr\s+([\w_]+)\s*=\s*0x([[:xdigit:]]+)/io)
3915 # sfr P1 = 0x90;
3917 add_ram(hex($2), $1, TRUE);
3921 #-------------------------------------------------------------------------------
3924 # Reads in a MCU.h file.
3927 sub read_header($)
3929 my $Header = $_[0];
3930 my ($fh, $pre_comment, $comment, $line_number);
3931 my $head;
3933 if (! open($fh, '<', $Header))
3935 print STDERR "$PROGRAM: Could not open. -> \"$Header\"\n";
3936 exit(1);
3939 $head = ' ' x $embed_level;
3941 Log("${head}read_header($Header) >>>>", 5);
3943 $comment = FALSE;
3944 $line_number = 1;
3945 while (<$fh>)
3947 chomp;
3948 s/\r$//o; # '\r'
3950 # Filters off the C comments.
3952 s/\/\*.*\*\///o; # /* ... */
3953 s/\/\/.*$//o; # // ...
3954 s/^\s*|\s*$//go;
3956 if (/\/\*/o) # /*
3958 $pre_comment = TRUE;
3959 s/\s*\/\*.*$//o;
3961 elsif (/\*\//o) # */
3963 $pre_comment = FALSE;
3964 $comment = FALSE;
3965 s/^.*\*\/\s*//o;
3968 if ($comment)
3970 ++$line_number;
3971 next;
3974 $comment = $pre_comment if ($pre_comment);
3976 if (/^\s*$/o)
3978 ++$line_number;
3979 next;
3982 run_preprocessor($Header, \&process_header_line, $_, $line_number);
3983 ++$line_number;
3984 } # while (<$fh>)
3986 Log("${head}<<<< read_header($Header)", 5);
3987 close($fh);
3990 #-------------------------------------------------------------------------------
3993 # Determines size of the $dcd_instr.
3996 sub determine_instr_size()
3998 my $instr;
3999 my $size = $instruction_sizes_[$dcd_instr];
4001 return $size if ($size >= 0);
4003 $instr = $rom[$dcd_address + 1];
4005 if ($size == IPREFIX_DD || $size == IPREFIX_FD)
4007 return $instruction_sizes_DDFD[$instr];
4009 elsif ($size == IPREFIX_ED)
4011 return $instruction_sizes_ED[$instr];
4013 else
4015 return 0;
4019 #-------------------------------------------------------------------------------
4022 # Among the blocks stows description of an instruction.
4025 sub add_instr_block($)
4027 my $Address = $_[0];
4028 my ($instr_size, $invalid);
4030 $dcd_address = $Address;
4031 $dcd_instr = $rom[$dcd_address];
4032 $invalid = FALSE;
4034 $instr_size = determine_instr_size();
4036 if ($instr_size == 0)
4038 $instr_size = 1;
4039 add_block($Address, BLOCK_CONST, $instr_size, BL_TYPE_NONE, '');
4041 else
4043 if ($instr_size == 1)
4045 $invalid = TRUE if ($dcd_instr == EMPTY);
4048 if ($instr_size == 2)
4050 $invalid = TRUE if ($rom[$dcd_address + 1] == EMPTY);
4053 if ($instr_size == 3)
4055 $invalid = TRUE if ($rom[$dcd_address + 2] == EMPTY);
4058 if ($instr_size == 4)
4060 $invalid = TRUE if ($rom[$dcd_address + 3] == EMPTY);
4063 if ($invalid)
4065 add_block($Address, BLOCK_CONST, $instr_size, BL_TYPE_NONE, '');
4067 else
4069 add_block($Address, BLOCK_INSTR, $instr_size, BL_TYPE_NONE, '');
4073 return $instr_size;
4076 #-------------------------------------------------------------------------------
4079 # Splits the program into small blocks.
4082 sub split_code_to_blocks()
4084 my ($i, $instr);
4085 my ($is_empty, $empty_begin);
4086 my ($is_const, $const_begin);
4088 $is_empty = FALSE;
4089 $is_const = FALSE;
4091 for ($i = 0; $i < $rom_size; )
4093 $instr = $rom[$i];
4095 if ($instr == EMPTY)
4097 if (! $is_empty)
4099 # The begin of the empty section.
4101 if ($is_const)
4103 # The end of the constant section.
4105 add_block($const_begin, BLOCK_CONST, $i - $const_begin, BL_TYPE_NONE, '');
4106 $is_const = FALSE;
4109 $empty_begin = $i;
4110 $is_empty = TRUE;
4113 ++$i;
4114 } # if ($instr == EMPTY)
4115 elsif (is_constant($i))
4117 if (! $is_const)
4119 if ($is_empty)
4121 # The end of the empty section.
4123 add_block($empty_begin, BLOCK_EMPTY, $i - $empty_begin, BL_TYPE_NONE, '');
4124 $is_empty = FALSE;
4127 $const_begin = $i;
4128 $is_const = TRUE;
4131 ++$i;
4132 } # elsif (is_constant($i))
4133 else
4135 if ($is_const)
4137 # The end of the constant section.
4139 add_block($const_begin, BLOCK_CONST, $i - $const_begin, BL_TYPE_NONE, '');
4140 $is_const = FALSE;
4143 if ($is_empty)
4145 # The end of the empty section.
4147 add_block($empty_begin, BLOCK_EMPTY, $i - $empty_begin, BL_TYPE_NONE, '');
4148 $is_empty = FALSE;
4151 $i += add_instr_block($i);
4153 } # for ($i = 0; $i < $rom_size; )
4155 if ($is_const)
4157 add_block($const_begin, BLOCK_CONST, $i - $const_begin, BL_TYPE_NONE, '');
4160 if ($is_empty)
4162 add_block($empty_begin, BLOCK_EMPTY, $i - $empty_begin, BL_TYPE_NONE, '');
4166 #-------------------------------------------------------------------------------
4169 # Previously assess the code.
4172 sub preliminary_survey($)
4174 $decoder_silent_level = $_[0];
4175 foreach (sort {$a <=> $b} keys(%blocks_by_address))
4177 my $block = \%{$blocks_by_address{$_}};
4179 next if ($block->{TYPE} != BLOCK_INSTR);
4181 instruction_decoder($_, $block);
4185 #-------------------------------------------------------------------------------
4188 # Finds address of branchs and procedures.
4191 sub find_labels_in_code()
4193 foreach (sort {$a <=> $b} keys(%blocks_by_address))
4195 my $block = \%{$blocks_by_address{$_}};
4197 next if ($block->{TYPE} != BLOCK_INSTR);
4199 label_finder($_, $block);
4203 #-------------------------------------------------------------------------------
4206 # Finds lost address of branchs and procedures.
4209 sub find_lost_labels_in_code()
4211 my ($block, $prev_block, $prev_addr, $label, $instr);
4213 $prev_addr = EMPTY;
4214 $prev_block = undef;
4215 foreach (sort {$a <=> $b} keys(%blocks_by_address))
4217 $block = \%{$blocks_by_address{$_}};
4219 last if ($block->{TYPE} == BLOCK_RAM);
4220 next if ($block->{TYPE} != BLOCK_INSTR);
4222 if ($prev_addr != EMPTY)
4224 $instr = $rom[$prev_addr];
4225 $label = $block->{LABEL};
4227 if (defined($label) && $label->{TYPE} == BL_TYPE_NONE)
4229 # if ($instr == INST_RET || $instr == INST_RETI)
4230 if ($instr == INST_RET)
4232 Log(sprintf("Lost function label at the 0x%04X address.", $_), 5);
4233 add_func_label($_, '', TRUE);
4235 elsif ($instr == INST_JP || $instr == INST_JR || $instr == INST_JP_HL)
4237 Log(sprintf("Lost jump label at the 0x%04X address.", $_), 5);
4238 add_jump_label($_, '', BL_TYPE_LABEL, EMPTY, TRUE);
4243 $prev_addr = $_;
4244 $prev_block = $block;
4248 #-------------------------------------------------------------------------------
4251 # Jump tables looking for in the code.
4254 sub recognize_jump_tables_in_code()
4256 my @blocks = ((undef) x 5);
4257 my @instrs = ((EMPTY) x 5);
4258 my ($addr);
4260 foreach (sort {$a <=> $b} keys(%blocks_by_address))
4262 shift(@instrs);
4263 push(@instrs, $rom[$_]);
4265 shift(@blocks);
4266 push(@blocks, \%{$blocks_by_address{$_}});
4268 next if (! defined($blocks[0]) || ! defined($blocks[4]));
4269 next if ($blocks[0]->{TYPE} != BLOCK_INSTR);
4270 next if ($blocks[1]->{TYPE} != BLOCK_INSTR);
4271 next if ($blocks[2]->{TYPE} != BLOCK_INSTR);
4272 next if ($blocks[3]->{TYPE} != BLOCK_INSTR);
4273 next if ($blocks[4]->{TYPE} != BLOCK_INSTR);
4275 if ($blocks[0]->{SIZE} == 3 && $instrs[0] == INST_LD_HL &&
4276 $blocks[1]->{SIZE} == 1 && $instrs[1] == INST_ADD_HL_DE &&
4277 $blocks[2]->{SIZE} == 1 && $instrs[2] == INST_ADD_HL_DE &&
4278 $blocks[3]->{SIZE} == 1 &&
4279 (($instrs[3] == INST_ADD_HL_DE && $blocks[4]->{SIZE} == 1 && $instrs[4] == INST_JP_HL) ||
4280 $instrs[3] == INST_JP_HL))
4282 =back
4283 0x019D: 21 A4 01 ld HL, #0x01A4 ; HL = 0x01A4
4284 0x01A0: 19 add HL, DE ; HL += DE
4285 0x01A1: 19 add HL, DE ; HL += DE
4286 0x01A2: 19 add HL, DE ; HL += DE
4287 0x01A3: E9 jp (HL) ; Jumps to value of HL.
4289 0x01A4: C3 D4 01 jp Label_021 ; Jumps (forward) hither: 0x01D4
4291 ----------------------------------------------------------------------------------------------------
4293 0x019D: 21 A4 01 ld HL, #0x01A3 ; HL = 0x01A4
4294 0x01A0: 19 add HL, DE ; HL += DE
4295 0x01A1: 19 add HL, DE ; HL += DE
4296 0x01A2: E9 jp (HL) ; Jumps to value of HL.
4298 0x01A3: 18 2F jr Label_021 ; Jumps (forward) hither: 0x01D4
4299 =cut
4301 $addr = ($rom[$blocks[0]->{ADDR} + 2] << 8) | $rom[$blocks[0]->{ADDR} + 1];
4302 add_jump_label($addr, '', BL_TYPE_JTABLE, EMPTY, FALSE);
4307 #-------------------------------------------------------------------------------
4310 # Prints the global symbols.
4313 sub emit_globals($)
4315 my $Assembly_mode = $_[0];
4316 my ($label, $cnt0, $cnt1, $str0, $str1);
4318 return if (! scalar(keys(%labels_by_address)));
4320 print ";$border0\n;\tPublic labels\n;$border0\n\n";
4322 if ($Assembly_mode)
4324 foreach (sort {$a <=> $b} keys(%labels_by_address))
4326 $label = $labels_by_address{$_};
4328 next if ($label->{TYPE} != BL_TYPE_SUB);
4330 print "\t.globl\t$label->{NAME}\n";
4333 else
4335 foreach (sort {$a <=> $b} keys(%labels_by_address))
4337 $label = $labels_by_address{$_};
4339 next if ($label->{TYPE} != BL_TYPE_SUB);
4341 $str0 = sprintf "0x%04X", $_;
4342 $cnt0 = sprintf "%3u", $label->{CALL_COUNT};
4343 $cnt1 = sprintf "%3u", $label->{JUMP_COUNT};
4344 $str1 = ($label->{CALL_COUNT} || $label->{JUMP_COUNT}) ? "calls: $cnt0, jumps: $cnt1" : 'not used';
4345 print "${str0}:\t" . align($label->{NAME}, STAT_ALIGN_SIZE) . "($str1)\n";
4349 print "\n";
4352 #-------------------------------------------------------------------------------
4355 # Prints the registers (variables).
4358 sub emit_ram_data()
4360 my ($block, $first, $name, $next_addr, $size, $cnt, $str0, $str1);
4362 return if (! scalar(keys(%ram_blocks_by_address)));
4364 print ";$border0\n;\tRAM data\n;$border0\n\n";
4366 $next_addr = EMPTY;
4367 foreach (sort {$a <=> $b} keys(%ram_blocks_by_address))
4369 $block = $blocks_by_address{$_};
4371 if ($block->{TYPE} != BLOCK_RAM)
4373 $next_addr = EMPTY;
4374 next;
4377 next if ($next_addr != EMPTY && $_ < $next_addr);
4379 $str0 = sprintf "0x%04X", $_;
4380 $cnt = sprintf "%3u", $block->{REF_COUNT};
4381 $str1 = ($block->{REF_COUNT}) ? "used $cnt times" : 'not used';
4382 $name = $ram_names_by_address{$_};
4384 if (defined($name) && $name ne '')
4386 $cnt = sprintf "%5u", $block->{SIZE};
4387 print "${str0}:\t" . align($name, STAT_ALIGN_SIZE) . "($cnt bytes) ($str1)\n";
4388 $next_addr = $_ + $block->{SIZE};
4390 else
4392 if ($map_readed)
4394 print "${str0}:\t" . align("variable_$str0", STAT_ALIGN_SIZE) . "( 1 bytes) ($str1)\n";
4396 else
4398 print "${str0}:\t" . align("variable_$str0", STAT_ALIGN_SIZE) . "($str1)\n";
4401 $next_addr = $_ + 1;
4403 } # foreach (sort {$a <=> $b} keys(%ram_blocks_by_address))
4405 print "\n";
4408 #-------------------------------------------------------------------------------
4411 # Prints I/O ports.
4414 sub emit_io_ports()
4416 my ($io, $cnt, $str0, $str1);
4418 return if (! scalar(keys(%io_by_address)));
4420 print ";$border0\n;\tI/O ports\n;$border0\n\n";
4422 foreach (sort {$a <=> $b} keys(%io_by_address))
4424 $io = $io_by_address{$_};
4426 $str0 = sprintf "0x%02X", $_;
4427 $cnt = sprintf "%3u", $io->{REF_COUNT};
4428 $str1 = ($io->{REF_COUNT}) ? "used $cnt times" : 'not used';
4430 if ($io->{NAME} ne '')
4432 print "${str0}:\t" . align($io->{NAME}, STAT_ALIGN_SIZE) . "($str1)\n";
4434 else
4436 print "${str0}:\t" . align("port_$str0", STAT_ALIGN_SIZE) . "($str1)\n";
4438 } # foreach (sort {$a <=> $b} keys(%io_by_address))
4440 print "\n";
4443 #-------------------------------------------------------------------------------
4446 # Prints a label belonging to the $Address.
4449 sub print_label($)
4451 my $Address = $_[0];
4452 my ($label, $type);
4454 $label = $labels_by_address{$Address};
4456 return FALSE if (! defined($label) || $label->{TYPE} == BL_TYPE_NONE);
4458 $type = $label->{TYPE};
4460 print "\n;$border0\n" if ($type == BL_TYPE_SUB);
4462 printf "\n$label->{NAME}:\n\n";
4463 $label->{PRINTED} = TRUE;
4464 $prev_is_jump = FALSE;
4465 return TRUE;
4468 #-------------------------------------------------------------------------------
4471 # Prints a variable belonging to the $Address.
4474 sub print_variable($$)
4476 my ($Address, $BlockRef) = @_;
4477 my ($name, $size, $str0, $str1);
4479 $size = $BlockRef->{SIZE};
4481 return if (! $size);
4483 $name = $ram_names_by_address{$Address};
4485 return if (! defined($name) || $name eq '');
4487 $str0 = sprintf "0x%04X", $Address;
4489 given ($size)
4491 when (1) { $str1 = '.db'; }
4492 when (2) { $str1 = '.dw'; }
4493 when (4) { $str1 = '.dd'; }
4494 when (8) { $str1 = '.dq'; }
4495 when (10) { $str1 = '.dt'; }
4496 default { $str1 = '.db'; }
4499 if ($gen_assembly_code)
4501 print "$name:\n";
4502 $str0 = "\t$str1\t?";
4504 else
4506 $str0 = align("$str0:$name", RAM_ALIGN_SIZE) . "$str1\t?";
4509 print align($str0, RAM_ALIGN_SIZE + 1 + EXPL_ALIGN_SIZE) . "; $size bytes\n";
4512 #-------------------------------------------------------------------------------
4515 # Prints a table of constants.
4518 sub print_constants($$)
4520 my ($Address, $BlockRef) = @_;
4521 my ($size, $i, $len, $frag, $byte, $spc, $col, $brd);
4522 my ($left_align, $right_align);
4523 my @constants;
4524 my @line;
4526 $size = $BlockRef->{SIZE};
4528 return if (! $size);
4530 $prev_is_jump = FALSE;
4531 $col = ' ';
4533 if ($gen_assembly_code)
4535 print ";$table_border\n;\t\t $table_header | $table_header |\n;$table_border\n";
4536 $brd = ' ';
4538 else
4540 print "$table_border\n| | $table_header | $table_header |\n$table_border\n";
4541 $brd = '|';
4544 @constants = @rom[$Address .. ($Address + $size - 1)];
4545 $i = 0;
4546 while (TRUE)
4548 $len = $size - $i;
4550 last if (! $len);
4552 $len = TBL_COLUMNS if ($len > TBL_COLUMNS);
4554 if ($gen_assembly_code)
4556 print "\t.db\t";
4558 else
4560 printf "$brd 0x%04X $brd ", $Address;
4563 if (($spc = $Address % TBL_COLUMNS))
4565 $frag = TBL_COLUMNS - $spc;
4566 $len = $frag if ($len > $frag);
4569 $left_align = $col x $spc;
4570 $right_align = $col x (TBL_COLUMNS - $spc - $len);
4571 @line = @constants[$i .. ($i + $len - 1)];
4572 $Address += $len;
4573 $i += $len;
4575 print " $left_align" . join(' ', map { sprintf("%02X ", $_ & 0xFF); } @line);
4577 print "$right_align $brd $left_align " .
4578 join(' ', map {
4579 sprintf((($_ < ord(' ') || $_ >= 0x7F) ? "%02X " : "'%c'"), $_ & 0xFF);
4580 } @line) . "$right_align $brd\n";
4581 } # while (TRUE)
4583 print (($gen_assembly_code) ? ";$table_border\n" : "$table_border\n");
4584 $prev_is_jump = FALSE;
4587 #-------------------------------------------------------------------------------
4590 # Disassembly contents of $blocks_by_address array.
4593 sub disassembler()
4595 my ($sname, $prev_block_type, $ref);
4597 $prev_is_jump = FALSE;
4598 $decoder_silent_level = SILENT0;
4600 $table_header = join(' ', map { sprintf '%02X', $_ } (0 .. (TBL_COLUMNS - 1)));
4602 if ($gen_assembly_code)
4604 $table_border = ('-' x (TBL_COLUMNS * 4 + 16)) . '+' . ('-' x (TBL_COLUMNS * 4 + 2)) . '+';
4606 else
4608 $table_border = '+' . ('-' x 10) . '+' . ('-' x (TBL_COLUMNS * 4 + 2)) . '+' . ('-' x (TBL_COLUMNS * 4 + 2)) . '+';
4611 print "\n";
4613 if ($gen_assembly_code)
4615 emit_globals(TRUE);
4616 print ";$border0\n;\tCode\n;$border0\n\n\t.area\tCODE\t(CODE)\n\n";
4618 else
4620 emit_globals(FALSE);
4621 emit_ram_data();
4622 emit_io_ports();
4623 print ";$border0\n";
4626 $prev_block_type = EMPTY;
4627 foreach (sort {$a <=> $b} keys(%blocks_by_address))
4629 $ref = $blocks_by_address{$_};
4631 if ($ref->{TYPE} == BLOCK_INSTR)
4633 print_label($_);
4634 print "\n" if ($prev_is_jump);
4636 instruction_decoder($_, $ref);
4637 $prev_block_type = BLOCK_INSTR;
4639 elsif ($ref->{TYPE} == BLOCK_RAM)
4641 print "\n;$border0\n\n" if ($prev_block_type != BLOCK_RAM);
4643 print_variable($_, $ref);
4644 $prev_block_type = BLOCK_RAM;
4646 elsif ($ref->{TYPE} == BLOCK_CONST)
4648 print "\n;$border0\n" if ($prev_block_type != BLOCK_CONST);
4650 print_label($_);
4651 print "\n" if ($prev_is_jump);
4653 print_constants($_, $ref);
4654 $prev_block_type = BLOCK_CONST;
4656 elsif ($ref->{TYPE} == BLOCK_EMPTY)
4658 my $next_block = $_ + $ref->{SIZE};
4660 print "\n;$border0\n" if ($prev_block_type != BLOCK_EMPTY);
4662 if (! $gen_assembly_code)
4664 printf("\n0x%04X: -- -- --\n .... -- -- --\n0x%04X: -- -- --\n", $_, $next_block - 1);
4666 elsif ($next_block <= $rom_size)
4668 # Skip the empty code space.
4670 printf "\n\t.ds\t%u\n", $ref->{SIZE};
4673 $prev_block_type = BLOCK_EMPTY;
4675 } # foreach (sort {$a <=> $b} keys(%blocks_by_address))
4678 #-------------------------------------------------------------------------------
4681 # If there are datas in the code, it is possible that some labels will
4682 # be lost. This procedure prints them.
4685 sub print_hidden_labels()
4687 foreach (sort {$a <=> $b} keys(%labels_by_address))
4689 my $label = $labels_by_address{$_};
4691 print STDERR "The label: $label->{NAME} is hidden!\n" if (! $label->{PRINTED});
4695 ################################################################################
4696 ################################################################################
4698 sub usage()
4700 print <<EOT;
4701 Usage: $PROGRAM [options] <hex file>
4703 Options are:
4705 -M|--mcu <header.h>
4707 Header file of the MCU.
4709 -I|--include <path to header>
4711 Path of the header files of Z80 MCUs. (Default: $default_include_path)
4713 --map-file <file.map>
4715 The map file belonging to the input hex file. (optional)
4717 -r|--rom-size <size of program memory>
4721 printf "\t Defines size of the program memory. (Default %u bytes.)\n", Z80_ROM_SIZE;
4722 print <<EOT;
4724 --const-area <start address> <end address>
4726 Designates a constant area (jumptables, texts, etc.), where data is
4727 stored happen. The option may be given more times, that to select
4728 more areas at the same time. (optional)
4730 -as|--assembly-source
4732 Generates the assembly source file. (Eliminates before the instructions
4733 visible address and hex codes.) Emits global symbol table, etc.
4735 -fl|--find-lost-labels
4737 Finds the "lost" labels. These may be found such in program parts,
4738 which are directly not get call.
4740 --name-list <list_file>
4742 The file contains list of names. They may be: Names of variables and
4743 names of labels. For example:
4745 [IO]
4746 0x21:keyboard_io
4750 [RAM]
4751 0x8021:ram_variable
4755 [ROM]
4756 0x05FC:function_or_label
4761 The contents of list override the names from map file.
4763 -ne|--no-explanations
4765 Eliminates after the instructions visible explaining texts.
4767 -v <level> or --verbose <level>
4769 It provides information on from the own operation.
4770 Possible value of the level between 0 and 10. (default: 0)
4772 -h|--help
4774 This text.
4779 ################################################################################
4780 ################################################################################
4781 ################################################################################
4783 foreach (@default_paths)
4785 if (-d $_)
4787 $default_include_path = $_;
4788 last;
4792 if (! @ARGV)
4794 usage();
4795 exit(1);
4798 for (my $i = 0; $i < @ARGV; )
4800 my $opt = $ARGV[$i++];
4802 given ($opt)
4804 when (/^-(r|-rom-size)$/o)
4806 param_exist($opt, $i);
4807 $rom_size = str2int($ARGV[$i++]);
4809 if ($rom_size < 1024)
4811 printf STDERR "$PROGRAM: Code size of the Z80 family greater than 1024 bytes!\n";
4812 exit(1);
4814 elsif ($rom_size > Z80_ROM_SIZE)
4816 printf STDERR "$PROGRAM: Code size of the Z80 family not greater %u bytes!\n", Z80_ROM_SIZE;
4817 exit(1);
4821 when (/^--const-area$/o)
4823 my ($start, $end);
4825 param_exist($opt, $i);
4826 $start = str2int($ARGV[$i++]);
4828 param_exist($opt, $i);
4829 $end = str2int($ARGV[$i++]);
4831 if ($start > $end)
4833 my $t = $start;
4835 $start = $end;
4836 $end = $t;
4838 elsif ($start == $end)
4840 $start = Z80_ROM_SIZE - 1;
4841 $end = Z80_ROM_SIZE - 1;
4844 add_const_area($start, $end) if ($start < $end);
4845 } # when (/^--const-area$/o)
4847 when (/^-(I|-include)$/o)
4849 param_exist($opt, $i);
4850 $include_path = $ARGV[$i++];
4853 when (/^-(M|-mcu)$/o)
4855 param_exist($opt, $i);
4856 $header_file = $ARGV[$i++];
4859 when (/^--map-file$/o)
4861 param_exist($opt, $i);
4862 $map_file = $ARGV[$i++];
4865 when (/^-(as|-assembly-source)$/o)
4867 $gen_assembly_code = TRUE;
4870 when (/^-(fl|-find-lost-labels)$/o)
4872 $find_lost_labels = TRUE;
4875 when (/^--name-list$/o)
4877 param_exist($opt, $i);
4878 $name_list = $ARGV[$i++];
4881 when (/^-(ne|-no-explanations)$/o)
4883 $no_explanations = TRUE;
4886 when (/^-(v|-verbose)$/o)
4888 param_exist($opt, $i);
4889 $verbose = int($ARGV[$i++]);
4890 $verbose = 0 if (! defined($verbose) || $verbose < 0);
4891 $verbose = 10 if ($verbose > 10);
4894 when (/^-(h|-help)$/o)
4896 usage();
4897 exit(0);
4900 default
4902 if ($hex_file eq '')
4904 $hex_file = $opt;
4906 else
4908 print STDERR "$PROGRAM: We already have the source file name: $hex_file.\n";
4909 exit(1);
4912 } # given ($opt)
4913 } # for (my $i = 0; $i < @ARGV; )
4915 $include_path = $default_include_path if ($include_path eq '');
4917 if ($hex_file eq '')
4919 print STDERR "$PROGRAM: What do you have to disassembled?\n";
4920 exit(1);
4923 is_file_ok($hex_file);
4925 init_mem(0, $rom_size - 1);
4926 read_hex($hex_file);
4928 if ($header_file ne '')
4930 is_file_ok("$include_path/$header_file");
4931 reset_preprocessor();
4932 $embed_level = 0;
4933 read_header("$include_path/$header_file");
4936 if ($map_file eq '')
4938 ($map_file) = ($hex_file =~ /^(.+)\.hex$/io);
4939 $map_file .= '.map';
4942 $map_file = '' if (! -e $map_file);
4944 is_file_ok($name_list) if ($name_list ne '');
4946 ###################################
4948 read_map_file();
4949 read_name_list();
4950 split_code_to_blocks();
4951 recognize_jump_tables_in_code();
4952 preliminary_survey(SILENT1);
4953 find_labels_in_code();
4954 find_lost_labels_in_code() if ($find_lost_labels);
4955 add_names_labels();
4956 fix_multi_byte_variables();
4957 fix_io_names();
4958 disassembler();
4959 print_hidden_labels() if ($verbose > 2);