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
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);
76 '/usr/share/sdcc/include/z180',
77 '/usr/local/share/sdcc/include/z180'
80 my $default_include_path = '';
81 my $include_path = '';
84 my $map_readed = FALSE
;
89 my $gen_assembly_code = FALSE
;
90 my $no_explanations = FALSE
;
91 my $find_lost_labels = FALSE
;
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 = ();
103 The structure of one element of the %io_by_address hash:
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
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;
198 The structure of one element of the %blocks_by_address hash:
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;
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 =
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
281 my $dcd_instr_size = 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.
305 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
306 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
307 #@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@
308 #@@@@@@@@@@@@@@@@@@@@@@@ This a simple preprocessor. @@@@@@@@@@@@@@@@@@@@@@@@@
309 #@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@
310 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
311 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
313 # Examines that the parameter is defined or not defined.
317 return defined($pp_defines{$_[0]});
320 #-------------------------------------------------------------------------------
322 # Records a definition.
326 my ($Name) = ($_[0] =~ /^(\S+)/op);
327 my $Body = ${^POSTMATCH
};
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.
343 delete($pp_defines{$_[0]});
346 #-------------------------------------------------------------------------------
348 # Evaluation of the #if give a boolean value. This procedure preserves it.
354 push(@pp_conditions, $Val);
355 push(@pp_else_conditions, $Val);
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
;
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);
392 pop(@pp_else_conditions);
396 #-------------------------------------------------------------------------------
398 sub reset_preprocessor
()
402 push(@pp_conditions, TRUE
);
403 @pp_else_conditions = ();
404 push(@pp_else_conditions, FALSE
);
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));
427 # The ancestor is invalid, so the descendants will invalid also.
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));
443 # The ancestor is invalid, so the descendants will invalid also.
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.)
476 ################################################################################
477 ################################################################################
478 ################################################################################
482 return ($_[0] =~ /([^\/]+)$/) ?
$1 : '';
485 #-------------------------------------------------------------------------------
489 die "This option \"$_[0]\" requires a parameter.\n" if ($_[1] > $#ARGV);
492 #-------------------------------------------------------------------------------
496 return if (pop(@_) > $verbose);
497 foreach (@_) { print STDERR
$_; }
501 #-------------------------------------------------------------------------------
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.
521 my ($Text, $Tab_count) = @_;
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.
535 return ($Text . ("\t" x
$al));
539 #-------------------------------------------------------------------------------
542 # Multiple file test.
551 print STDERR
"$PROGRAM: Not exists -> \"$File\"\n";
557 print STDERR
"$PROGRAM: Not file -> \"$File\"\n";
563 print STDERR
"$PROGRAM: Can not read -> \"$File\"\n";
569 print STDERR
"$PROGRAM: Empty file -> \"$File\"\n";
574 #-------------------------------------------------------------------------------
577 # Initializes the @rom array.
582 my ($Start, $End) = @_;
584 @rom[$Start .. $End] = ((EMPTY
) x
($End - $Start + 1));
587 #-------------------------------------------------------------------------------
590 # Store values of the $Code to $AddrRef address.
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.
619 if (! open(IN
, '<', $Hex))
621 print STDERR
"$PROGRAM : Could not open. -> \"$Hex\"\n";
631 my $len = length() - 1;
633 if ($len < MIN_LINE_LENGTH
)
636 print STDERR
"$PROGRAM: ${line_num}th line <- Shorter than %u character.\n", MIN_LINE_LENGTH
;
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)
649 print STDERR
"$PROGRAM: $Hex <- Crc error. (${line_num}th line \"$_\").\n";
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
)
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);
670 Log
('format = INHX32', 7);
673 elsif ($type != INHX_DATA_REC
)
676 printf STDERR
"$PROGRAM: $Hex <- Unknown type of record: 0x%02X (${line_num}th line \"$_\").\n", $type;
680 if ($bytecount == $count) # INHX32
682 if ($format == INHX8M
)
685 print STDERR
"$PROGRAM: $Hex <- Mixed format of file (${line_num}th line \"$_\").\n";
689 my $addr32 = ($addr_H << 16) | $addr;
691 map { store_code
($_, \
$addr32) } @codes;
693 elsif ($bytecount == ($count * BYTE_SIZE
)) # INHX8M
695 if ($format == INHX32
)
698 print STDERR
"$PROGRAM: $Hex <- Mixed format of file (${line_num}th line \"$_\").\n";
702 map { store_code
($_, \
$addr) } @codes;
707 print STDERR
"$PROGRAM: $Hex <- Wrong format of file (${line_num}th line \"$_\").\n";
715 #-------------------------------------------------------------------------------
718 # Determines that the $Address belongs to a constant.
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);
740 #-------------------------------------------------------------------------------
743 # Determines that the $Address belongs to a empty area.
750 foreach (sort {$a <=> $b} keys(%empty_blocks_by_address))
752 return TRUE
if ($_ <= $Address && $Address <= $empty_blocks_by_address{$_});
753 last if ($_ > $Address);
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.
778 my ($Address, $Type, $Size, $LabelType, $LabelName) = @_;
779 my ($block, $label, $end);
781 $end = $Address + $Size - 1;
783 if (! defined($blocks_by_address{$Address}))
793 $blocks_by_address{$Address} = {
805 if ($LabelType != BL_TYPE_NONE
)
807 $labels_by_address{$Address} = $label;
808 $max_label_addr = $Address if ($max_label_addr < $Address);
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);
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);
836 # At empty area, can not be label.
838 $label->{TYPE
} = BL_TYPE_NONE
;
840 $empty_blocks_by_address{$Address} = $end if ($Size > 0);
845 printf STDERR
"add_block(0x%04X): Unknown block type!\n", $Address;
849 } # if (! defined($blocks_by_address{$Address}))
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 '');
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);
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);
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);
896 # At empty area, can not be label.
898 $label->{TYPE
} = BL_TYPE_NONE
;
900 $empty_blocks_by_address{$Address} = $end if ($Size > 0);
908 #-------------------------------------------------------------------------------
911 # Store address entry of a procedure.
914 sub add_func_label
($$$)
916 my ($Address, $Name, $Map_mode) = @_;
921 Log
(sprintf("add_func_label(): This address (0x%04X) negative!", $Address), 2);
927 if (! defined($blocks_by_address{$Address}))
929 Log
(sprintf("add_func_label(): This address (0x%04X) does not shows an instruction!", $Address), 2);
934 if (is_constant
($Address) || is_empty
($Address))
936 Log
(sprintf("add_func_label(): This address (0x%04X) outside the code area!", $Address), 2);
940 $label = add_block
($Address, BLOCK_INSTR
, 0, BL_TYPE_SUB
, $Name);
944 ++$label->{CALL_COUNT
};
945 ++$blocks_by_address{$Address}->{REF_COUNT
};
949 #-------------------------------------------------------------------------------
955 sub add_jump_label
($$$$$)
957 my ($TargetAddr, $Name, $Type, $SourceAddr, $Map_mode) = @_;
962 Log
(sprintf("add_jump_label(): This address (0x%04X) negative!", $TargetAddr), 2);
968 if (! defined($blocks_by_address{$TargetAddr}))
970 Log
(sprintf("add_jump_label(): This address (0x%04X) does not shows an instruction!", $TargetAddr), 2);
975 if (is_constant
($TargetAddr) || is_empty
($TargetAddr))
977 Log
(sprintf("add_jump_label(): This address (0x%04X) outside the code area!", $TargetAddr), 2);
981 if (defined($interrupts_by_address{$SourceAddr}))
984 $Name = $interrupts_by_address{$SourceAddr} if ($Name eq '');
987 $label = add_block
($TargetAddr, BLOCK_INSTR
, 0, $Type, $Name);
991 ++$label->{JUMP_COUNT
};
992 ++$blocks_by_address{$TargetAddr}->{REF_COUNT
};
996 #-------------------------------------------------------------------------------
999 # Store a variable name.
1004 my ($Address, $Name, $Map_mode) = @_;
1006 return if ($Address == EMPTY
);
1010 Log
(sprintf("add_ram(): This address (0x%04X) negative!", $Address), 2);
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.
1027 my ($Address, $Name, $Map_mode) = @_;
1030 return if ($Address == EMPTY
);
1032 if (! defined($io = $io_by_address{$Address}))
1034 $io_by_address{$Address} = {
1036 REF_COUNT
=> ($Map_mode) ?
0 : 1
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.
1063 return if ($map_file eq '');
1067 if (! open(MAP
, '<', $map_file))
1069 print STDERR
"$PROGRAM : Could not open. -> \"$map_file\"\n";
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
)
1092 elsif (/^_(DATA|INITIALIZED)\s+/o)
1101 elsif ($state == MAP_CODE
)
1103 if (/^.ASxxxx Linker\s+/io)
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)
1124 elsif (/^\s*([[:xdigit:]]+)\s+(\S+)/o)
1126 # 000006C6 _heap_top
1127 # 000006C8 _last_error
1130 add_ram
(hex($1), $2, TRUE
);
1132 } # elsif ($state == MAP_DATA)
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";
1158 $state = NAMES_NULL
;
1160 foreach (grep(! /^\s*$/o, <NAMES
>))
1171 elsif (/^\[IO\]$/io)
1176 elsif (/^\[ROM\]$/io)
1188 if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
1190 add_ram
(hex($1), $2, TRUE
);
1196 if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
1198 add_io
(hex($1), $2, TRUE
);
1204 if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
1206 add_jump_label
(hex($1), $2, BL_TYPE_LABEL
, EMPTY
, TRUE
);
1210 } # foreach (grep(! /^\s*$/o, <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);
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
)
1241 $ram_names_by_address{$_} = $name;
1250 if ($prev_addr != EMPTY
)
1252 $var_size = $_ - $prev_addr;
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;
1269 } # foreach (sort {$a <=> $b} keys(%blocks_by_address))
1272 #-------------------------------------------------------------------------------
1278 foreach (sort {$a <=> $b} keys(%io_by_address))
1280 next if ($io_by_address{$_}->{NAME
} ne '');
1282 $io_by_address{$_}->{NAME
} = "io_$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);
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
($)
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.
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.
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.
1449 my ($Address, $StrRef) = @_;
1452 if (defined($ram = $ram_names_by_address{$Address}) && $ram ne '')
1454 $str = sprintf "0x%04X", $Address;
1455 ${$StrRef} = "[$str]";
1460 $str = sprintf "0x%04X", $Address;
1461 ${$StrRef} = "[$str]";
1467 #-------------------------------------------------------------------------------
1470 # Auxiliary procedure of prints.
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");
1485 print "$Instr\t" . align
($Param, EXPL_ALIGN_SIZE
) . "; $Expl\n";
1489 print(($Param ne '') ?
"$Instr\t$Param\n" : "$Instr\n");
1493 #-------------------------------------------------------------------------------
1496 # If possible, returns the character.
1503 if ($Ch >= ord(' ') && $Ch < 0x7F)
1505 return sprintf " {'%c'}", $Ch;
1507 elsif (defined($control_characters{$Ch}))
1509 return " {'$control_characters{$Ch}'}";
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)
1531 $str2 = ' (forward)';
1533 elsif ($dcd_address == $TargetAddr)
1535 $str1 = ' (endless loop)';
1541 $str2 = ' (backward)';
1544 return "$str2 hither: $str0$str1";
1547 #---------------------------------------------------------------------------------------------------
1548 #---------------------------------------------------------------------------------------------------
1549 #---------------------------------------------------------------------------------------------------
1550 #---------------------------------------------------------------------------------------------------
1552 my @core_registers8 =
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 =
1596 EXPL
=> 'CF <- %s[7..0] <- %s.7'
1600 EXPL
=> '%s.0 -> %s[7..0] -> CF'
1604 EXPL
=> 'CF <- %s[7..0] <- CF'
1608 EXPL
=> 'CF -> %s[7..0] -> CF'
1612 EXPL
=> 'CF <- %s[7..0] <- 0'
1616 EXPL
=> '%s.7 -> %s[7..0] -> CF'
1620 EXPL
=> 'CF <- %s[7..0] <- 1'
1624 EXPL
=> '0 -> %s[7..0] -> CF'
1628 sub CB_prefix_decoder
()
1630 my ($str, $i_reg, $reg);
1632 given ($dcd_instr_x)
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
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;
1657 $str = sprintf $i_shift->{EXPL
}, $reg;
1659 print_3
($i_shift->{INSTR
}, $i_reg->{NAME
}, $str);
1665 # BIT b, r CB 11001011 01bbbrrr
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");
1677 # RES b, r CB 11001011 10bbbrrr
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");
1689 # SET b, r CB 11001011 11bbbrrr
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);
1715 $offs_str = "$offset($IndexReg)";
1716 $offs_expl = "[$IndexReg$offset]";
1720 $offs_str = "$offset($IndexReg)";
1721 $offs_expl = "[${IndexReg}+$offset]";
1724 given ($dcd_instr_x)
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
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);
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
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
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
1803 print_3
('bit', "$dcd_instr_y, $offs_str", "ZF = !${offs_expl}.$dcd_instr_y");
1804 } # $dcd_instr_x == 1
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
1814 print_3
('res', "$dcd_instr_y, $offs_str", "${offs_expl}.$dcd_instr_y = 0");
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
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
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
1835 print_3
('set', "$dcd_instr_y, $offs_str", "${offs_expl}.$dcd_instr_y = 1");
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
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
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);
1904 instruction_take_to_pieces
($dcd_parm0);
1906 $offset = expand_offset
($dcd_parm1);
1910 $offs_str = "$offset($IndexReg)";
1911 $offs_expl = "[$IndexReg$offset]";
1915 $offs_str = "$offset($IndexReg)";
1916 $offs_expl = "[${IndexReg}+$offset]";
1919 given ($dcd_instr_x)
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
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)
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
1943 $str = sprintf '0x%04X', ($dcd_parm2 << 8) | $dcd_parm1;
1944 print_3
('ld', "$IndexReg, #$str", "$IndexReg = $str");
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
1953 $addr = ($dcd_parm2 << 8) | $dcd_parm1;
1955 if ($decoder_silent_level == SILENT0
)
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
);
1970 # INC IX DD 23 11011101 00100011
1971 # INC IY FD 23 11111101 00100011
1974 print_3
('inc', $IndexReg, "++$IndexReg");
1979 # INC IXh DD 24 11011101 00100100
1980 # INC IYh FD 24 11111101 00100100
1983 print_3
('inc', "${IndexReg}h", "++${IndexReg}.h");
1988 # DEC IXh DD 25 11011101 00100101
1989 # DEC IYh FD 25 11111101 00100101
1992 print_3
('dec', "${IndexReg}h", "--${IndexReg}.h");
1997 # LD IXh, #n DD 26 nn 11011101 00100110 nnnnnnnn
1998 # LD IYh, #n FD 26 nn 11111101 00100110 nnnnnnnn
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)
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
2018 $addr = ($dcd_parm2 << 8) | $dcd_parm1;
2020 if ($decoder_silent_level == SILENT0
)
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
);
2035 # DEC IX DD 2B 11011101 00101011
2036 # DEC IY FD 2B 11111101 00101011
2039 print_3
('dec', $IndexReg, "--$IndexReg");
2044 # INC IXl DD 2C 11011101 00101100
2045 # INC IYl FD 2C 11111101 00101100
2048 print_3
('inc', "${IndexReg}l", "++${IndexReg}.l");
2053 # DEC IXl DD 2D 11011101 00101101
2054 # DEC IYl FD 2D 11111101 00101101
2057 print_3
('dec', "${IndexReg}l", "--${IndexReg}.l");
2062 # LD IXl, #n DD 2E nn 11011101 00101110 nnnnnnnn
2063 # LD IYl, #n FD 2E nn 11111101 00101110 nnnnnnnn
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)
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
2083 print_3
('inc', $offs_str, "++$offs_expl");
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
2092 print_3
('dec', $offs_str, "--$offs_expl");
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
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
2112 given ($dcd_instr_y)
2116 given ($dcd_instr_z)
2120 # LD r, IXh DD 44 11011101 010rr100
2121 # LD r, IYh FD 44 11111101 010rr100
2125 $str = $core_registers8[$dcd_instr_y]->{NAME
};
2126 print_3
('ld', "$str, ${IndexReg}h", "$str = ${IndexReg}.h");
2132 # LD r, IXl DD 45 11011101 010rr101
2133 # LD r, IYl FD 45 11111101 010rr101
2137 $str = $core_registers8[$dcd_instr_y]->{NAME
};
2138 print_3
('ld', "$str, ${IndexReg}l", "$str = ${IndexReg}.l");
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
2149 $str = $core_registers8[$dcd_instr_y]->{NAME
};
2150 print_3
('ld', "$str, $offs_str", "$str = $offs_expl");
2152 } # given ($dcd_instr_z)
2157 my $r = ($dcd_instr_y == 4) ?
'h' : 'l';
2159 given ($dcd_instr_z)
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
2174 $str = $core_registers8[$dcd_instr_z]->{NAME
};
2175 print_3
('ld', "$IndexReg$r, $str", "$IndexReg$r = $str");
2180 # LD IXh, IXh DD 64 11011101 01100100
2181 # LD IYh, IYh FD 64 11111101 01100100
2184 print_3
('ld', "$IndexReg$r, {IndexReg}h", "$IndexReg$r = {IndexReg}.h");
2189 # LD IXh, IXl DD 65 11011101 01100101
2190 # LD IYh, IYl FD 65 11111101 01100101
2193 print_3
('ld', "$IndexReg$r, {IndexReg}l", "$IndexReg$r = {IndexReg}.l");
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
2203 print_3
('ld', "$str, $offs_str", "$str = $offs_expl");
2208 # LD IXh, A DD 67 11011101 01100111
2209 # LD IYh, A FD 67 11111101 01100111
2212 print_3
('ld', "$IndexReg$r, A", "$IndexReg$r = A");
2214 } # given ($dcd_instr_z)
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
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");
2230 given ($dcd_instr_z)
2234 # LD A, IXh DD 7C 11011101 01111100
2235 # LD A, IYh FD 7C 11111101 01111100
2238 print_3
('ld', "A, ${IndexReg}h", "A = ${IndexReg}.h");
2243 # LD A, IXl DD 7D 11011101 01111101
2244 # LD A, IYl FD 7D 11111101 01111101
2247 print_3
('ld', "A, ${IndexReg}l", "A = ${IndexReg}.l");
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
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
2265 given ($dcd_instr_z)
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
2287 my $i_arith = $DDFD_instr[$dcd_instr_y];
2289 print_3
($i_arith->{INSTR
}, "A, ${IndexReg}h", "$i_arith->{EXPL} ${IndexReg}.h");
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
2312 my $i_arith = $DDFD_instr[$dcd_instr_y];
2314 print_3
($i_arith->{INSTR
}, "A, ${IndexReg}l", "$i_arith->{EXPL} ${IndexReg}.l");
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
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
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++]");
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");
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");
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
;
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 #-------------------------------------------------------------------------------
2401 EXPL
=> '[DE++] = [HL++]; --BC'
2405 EXPL
=> 'A ?= [HL++]; --BC'
2409 EXPL
=> '[HL++] = In{C}; --B'
2413 EXPL
=> 'Out{C} = [HL++]; --B'
2419 EXPL
=> '[DE--] = [HL--]; --BC'
2423 EXPL
=> 'A ?= [HL--]; --BC'
2427 EXPL
=> '[HL--] = In{C}; --B'
2431 EXPL
=> 'Out{C} = [HL--]; --B'
2437 EXPL
=> '[DE++] = [HL++]; --BC; Exit this loop, then BC == 0.'
2441 EXPL
=> 'A ?= [HL++]; --BC; Exit this loop, then BC == 0 or A == [HL].'
2445 EXPL
=> '[HL++] = In{C}; --B; Exit this loop, then B == 0.'
2449 EXPL
=> 'Out{C} = [HL++]; --B; Exit this loop, then B == 0.'
2455 EXPL
=> '[DE--] = [HL--]; --BC; Exit this loop, then BC == 0.'
2459 EXPL
=> 'A ?= [HL--]; --BC; Exit this loop, then BC == 0 or A == [HL].'
2463 EXPL
=> '[HL--] = In{C}; --B; Exit this loop, then B == 0.'
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)
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
2493 print_3
('in', '(C)', "$i_reg->{EXPL} = In{[C]}");
2497 # IN r, (C) ED xx 11101011 01rrr000
2500 print_3
('in', "$i_reg->{NAME}, (C)", "$i_reg->{EXPL} = In{[C]}");
2503 } # $dcd_instr_z == 0
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
2516 print_3
('out', '(C)', "Out{[C]} = $i_reg->{EXPL}");
2520 # OUT (C), r ED xx 11101101 01rrr001
2523 print_3
('out', "(C), $i_reg->{NAME}", "Out{[C]} = $i_reg->{EXPL}");
2526 } # $dcd_instr_z == 1
2530 if ($dcd_instr_q == 0)
2532 # SBC HL, pp ED x2 11101101 01pp0010
2535 $str = $core_registers16a[$dcd_instr_p];
2536 print_3
('sbc', "HL, $str", "HL -= $str + CF");
2540 # ADC HL, pp ED xA 11101101 01pp1010
2543 $str = $core_registers16a[$dcd_instr_p];
2544 print_3
('adc', "HL, $str", "HL += $str + CF");
2546 } # $dcd_instr_z == 2
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
2557 if ($decoder_silent_level == SILENT0
)
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
);
2572 # LD pp, (nn) ED xB aa aa 11101101 01pp1011 a7-0 a15-8
2575 if ($decoder_silent_level == SILENT0
)
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
2592 # NEG ED xx 11101101 01xxx100
2595 print_3
('neg', '', 'A = -A');
2596 } # $dcd_instr_z == 4
2600 if ($dcd_instr_y == 1)
2602 # RETI ED 4D 11101101 01001101
2605 print_3
('reti', '', 'PC.l = [SP++]; PC.h = [SP++]; End of maskable interrupt.');
2606 $prev_is_jump = TRUE
;
2610 # RETN ED xx 11101101 01xxx101
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
2620 # IM n ED xx 11101101 01xxx110
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
2639 given ($dcd_instr_y)
2643 # LD I, A ED 47 11101101 01000111
2646 print_3
('ld', 'I, A', 'I = A');
2651 # LD R, A ED 4F 11101101 01001111
2654 print_3
('ld', 'R, A', 'R = A');
2659 # LD A, I ED 57 11101101 01010111
2662 print_3
('ld', 'A, I', 'A = I');
2667 # LD A, R ED 5F 11101101 01011111
2670 print_3
('ld', 'A, R', 'A = R');
2675 # RRD ED 67 11101101 01100111
2678 print_3
('rrd', '', 'A[3..0] -> [HL][7..4] -> [HL][3..0] -> A[3..0]');
2683 # RLD ED 6F 11101101 01101111
2686 print_3
('rld', '', 'A[3..0] <- [HL][7..4] <- [HL][3..0] <- A[3..0]');
2691 # NOP ED 77 11101101 01110111
2692 # NOP ED 7F 11101101 01111111
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
2709 # LDD ED A8 11101101 10101000
2710 # CPD ED A9 11101101 10101001
2711 # IND ED AA 11101101 10101010
2712 # OUTD ED AB 11101101 10101011
2715 # LDIR ED B0 11101101 10110000
2716 # CPIR ED B1 11101101 10110001
2717 # INIR ED B2 11101101 10110010
2718 # OTIR ED B3 11101101 10110011
2721 # LDDR ED B8 11101101 10111000
2722 # CPDR ED B9 11101101 10111001
2723 # INDR ED BA 11101101 10111010
2724 # OTDR ED BB 11101101 10111011
2727 my $i_block = $block_instr[$dcd_instr_y - 4][$dcd_instr_z];
2729 print_3
($i_block->{INSTR
}, '', $i_block->{EXPL
});
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.
2760 EXPL
=> 'CF <- A[7..0] <- A.7'
2764 EXPL
=> 'A.0 -> A[7..0] -> CF'
2768 EXPL
=> 'CF <- A[7..0] <- CF'
2772 EXPL
=> 'CF -> A[7..0] -> CF'
2776 EXPL
=> 'Conditionally decimal adjusts the Accumulator.'
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);
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)
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)
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)
2902 printf " %02X %02X %02X\t", $dcd_parm0, $dcd_parm1, $dcd_parm2;
2908 printf STDERR
"Internal error: The size of instruction (addr:0x%04X) is zero!", $dcd_address;
2914 # +---/ \---+ +------/ \------+ +------/ \------+
2916 # | 7 6 | | 5 4 3 | | 2 1 0 |
2917 # +-----------------------------------------------+
2918 # | . | . | . | . | . | . | . | . |
2919 # +-----------------------------------------------+
2926 $prev_is_jump = FALSE
;
2928 instruction_take_to_pieces
($dcd_instr);
2930 if ($dcd_instr_x == 0)
2933 # +---/ \---+ +------/ \------+ +------/ \------+
2934 # | 7 6 | | 5 4 3 | | 2 1 0 |
2935 # +-----------------------------------------------+
2936 # | 0 | 0 | . | . | . | . | . | . |
2937 # +-----------------------------------------------+
2942 given ($dcd_instr_z)
2947 # +---/ \---+ +------/ \------+ +------/ \------+
2948 # | 7 6 | | 5 4 3 | | 2 1 0 |
2949 # +-----------------------------------------------+
2950 # | 0 | 0 | . | . | . | 0 | 0 | 0 |
2951 # +-----------------------------------------------+
2953 given ($dcd_instr_y)
2960 print_3
('nop', '', 'No operation.');
2965 # EX AF, AF' 08 00001000
2968 print_3
('ex', "AF, AF'", "AF <-> AF'");
2973 # DJNZ e 10 00010000 eeeeeeee e: two's complement number
2976 if ($decoder_silent_level == SILENT0
)
2978 my $addr = $dcd_address + 2 + expand_offset
($dcd_parm0);
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
;
2990 # JR e 18 00011000 eeeeeeee e: two's complement number
2993 if ($decoder_silent_level == SILENT0
)
2995 my $addr = $dcd_address + 2 + expand_offset
($dcd_parm0);
2998 $str = label_name
($addr);
2999 $target = jump_direction
($addr);
3000 print_3
('jr', $str, "Jumps$target");
3001 $prev_is_jump = TRUE
;
3008 # JR cc, e xx 00ccc000 eeeeeeee e: two's complement number
3011 if ($decoder_silent_level == SILENT0
)
3013 my $addr = $dcd_address + 2 + expand_offset
($dcd_parm0);
3014 my $cond = $conditions[$dcd_instr_y - 4];
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
3029 # +---/ \---+ +------/ \------+
3031 # +-----------------------------------------------+
3032 # | 0 | 0 | . | . | . | 0 | 0 | 1 |
3033 # +-----------------------------------------------+
3038 if ($dcd_instr_q == 0)
3040 # LD rp, #nn x1 00rr0001 nnnnnnnn nnnnnnnn
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");
3051 # ADD HL, rp x9 00rr1001
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
3063 # +---/ \---+ +------/ \------+ +------/ \------+
3064 # | 7 6 | | 5 4 3 | | 2 1 0 |
3065 # +-----------------------------------------------+
3066 # | 0 | 0 | . | . | . | 0 | 1 | 0 |
3067 # +-----------------------------------------------+
3072 if ($dcd_instr_q == 0)
3075 # +---/ \---+ +------/ \------+
3077 # +-----------------------------------------------+
3078 # | 0 | 0 | . | . | 0 | 0 | 1 | 0 |
3079 # +-----------------------------------------------+
3084 given ($dcd_instr_p)
3088 # LD (BC), A 02 00000010
3091 print_3
('ld', '(BC), A', '[BC] = A');
3092 } # $dcd_instr_p == 0
3096 # LD (DE), A 12 00010010
3099 print_3
('ld', '(DE), A', '[DE] = A');
3100 } # $dcd_instr_p == 1
3104 # LD (nn), HL 22 00100010 nnnnnnnn nnnnnnnn
3107 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3109 if ($decoder_silent_level == SILENT0
)
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
3124 # LD (nn), A 32 00110010 nnnnnnnn nnnnnnnn
3127 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3129 if ($decoder_silent_level == SILENT0
)
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)
3146 # +---/ \---+ +------/ \------+
3148 # +-----------------------------------------------+
3149 # | 0 | 0 | . | . | 1 | 0 | 1 | 0 |
3150 # +-----------------------------------------------+
3155 given ($dcd_instr_p)
3159 # LD A, (BC) 0A 00001010
3162 print_3
('ld', 'A, (BC)', 'A = [BC]');
3163 } # $dcd_instr_p == 0
3167 # LD A, (DE) 1A 00011010
3170 print_3
('ld', 'A, (DE)', 'A = [DE]');
3171 } # $dcd_instr_p == 1
3175 # LD HL, (nn) 2A 00101010 nnnnnnnn nnnnnnnn
3178 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3180 if ($decoder_silent_level == SILENT0
)
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
3195 # LD A, (nn) 3A 00111010 nnnnnnnn nnnnnnnn
3198 $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3200 if ($decoder_silent_level == SILENT0
)
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
3219 # +---/ \---+ +------/ \------+
3221 # +-----------------------------------------------+
3222 # | 0 | 0 | . | . | . | 0 | 1 | 1 |
3223 # +-----------------------------------------------+
3228 if ($dcd_instr_q == 0)
3230 # INC rp x3 00rr0011
3232 # rp: BC, DE, HL, SP
3234 $str = $core_registers16a[$dcd_instr_p];
3235 print_3
('inc', $str, "++$str");
3239 # DEC rp x3 00rr1011
3241 # rp: BC, DE, HL, SP
3243 $str = $core_registers16a[$dcd_instr_p];
3244 print_3
('dec', $str, "--$str");
3246 } # $dcd_instr_z == 3
3251 # +---/ \---+ +------/ \------+ +------/ \------+
3252 # | 7 6 | | 5 4 3 | | 2 1 0 |
3253 # +-----------------------------------------------+
3254 # | 0 | 0 | . | . | . | 1 | 0 | 0 |
3255 # +-----------------------------------------------+
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
3272 # +---/ \---+ +------/ \------+ +------/ \------+
3273 # | 7 6 | | 5 4 3 | | 2 1 0 |
3274 # +-----------------------------------------------+
3275 # | 0 | 0 | . | . | . | 1 | 0 | 1 |
3276 # +-----------------------------------------------+
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
3293 # +---/ \---+ +------/ \------+ +------/ \------+
3294 # | 7 6 | | 5 4 3 | | 2 1 0 |
3295 # +-----------------------------------------------+
3296 # | 0 | 0 | . | . | . | 1 | 1 | 0 |
3297 # +-----------------------------------------------+
3299 # LD r, #n 00rrr110 nnnnnnnn
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
3315 # +---/ \---+ +------/ \------+ +------/ \------+
3316 # | 7 6 | | 5 4 3 | | 2 1 0 |
3317 # +-----------------------------------------------+
3318 # | 0 | 0 | . | . | . | 1 | 1 | 1 |
3319 # +-----------------------------------------------+
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)
3340 # +---/ \---+ +------/ \------+ +------/ \------+
3341 # | 7 6 | | 5 4 3 | | 2 1 0 |
3342 # +-----------------------------------------------+
3343 # | 0 | 1 | . | . | . | . | . | . |
3344 # +-----------------------------------------------+
3346 if ($dcd_instr_y == 6)
3349 # +---/ \---+ +------/ \------+ +------/ \------+
3350 # | 7 6 | | 5 4 3 | | 2 1 0 |
3351 # +-----------------------------------------------+
3352 # | 0 | 1 | 1 | 1 | 0 | . | . | . |
3353 # +-----------------------------------------------+
3358 print_3
('halt', '', 'Suspends CPU.');
3363 # +---/ \---+ +------/ \------+ +------/ \------+
3364 # | 7 6 | | 5 4 3 | | 2 1 0 |
3365 # +-----------------------------------------------+
3366 # | 0 | 1 | . | . | . | . | . | . |
3367 # +-----------------------------------------------+
3369 # LD r, r' xx 01dddsss
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)
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
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
3414 # +---/ \---+ +------/ \------+ +------/ \------+
3415 # | 7 6 | | 5 4 3 | | 2 1 0 |
3416 # +-----------------------------------------------+
3417 # | 1 | 1 | . | . | . | . | . | . |
3418 # +-----------------------------------------------+
3423 given ($dcd_instr_z)
3428 # +---/ \---+ +------/ \------+ +------/ \------+
3429 # | 7 6 | | 5 4 3 | | 2 1 0 |
3430 # +-----------------------------------------------+
3431 # | 1 | 1 | . | . | . | 0 | 0 | 0 |
3432 # +-----------------------------------------------+
3434 # RET cc xx 11ccc000
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
;
3447 # +---/ \---+ +------/ \------+ +------/ \------+
3448 # | 7 6 | | 5 4 3 | | 2 1 0 |
3449 # +-----------------------------------------------+
3450 # | 1 | 1 | . | . | . | 0 | 0 | 1 |
3451 # +-----------------------------------------------+
3456 if ($dcd_instr_q == 0)
3459 # +---/ \---+ +------/ \------+
3461 # +-----------------------------------------------+
3462 # | 1 | 1 | . | . | 0 | 0 | 0 | 1 |
3463 # +-----------------------------------------------+
3468 # POP rp xx 11rr0001
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);
3485 # +---/ \---+ +------/ \------+
3487 # +-----------------------------------------------+
3488 # | 1 | 1 | . | . | 1 | 0 | 0 | 1 |
3489 # +-----------------------------------------------+
3494 given ($dcd_instr_p)
3501 print_3
('ret', '', 'PC.l = [SP++]; PC.h = [SP++]');
3502 $prev_is_jump = TRUE
;
3510 print_3
('exx', '', "BC <-> BC'; DE <-> DE'; HL <-> HL'");
3515 # JP (HL) E9 11101001
3518 print_3
('jp', '(HL)', 'Jumps to value of HL.');
3519 $prev_is_jump = TRUE
;
3524 # LD SP, HL F9 11111001
3527 print_3
('ld', 'SP, HL', 'SP = HL');
3529 } # given ($dcd_instr_p)
3531 } # $dcd_instr_z == 1
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
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];
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
;
3562 # +---/ \---+ +------/ \------+ +------/ \------+
3563 # | 7 6 | | 5 4 3 | | 2 1 0 |
3564 # +-----------------------------------------------+
3565 # | 1 | 1 | . | . | . | 0 | 1 | 1 |
3566 # +-----------------------------------------------+
3568 given ($dcd_instr_y)
3572 # JP nn 11000011 a7-0 a15-8
3575 if ($decoder_silent_level == SILENT0
)
3577 my $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3580 $str = label_name
($addr);
3581 $target = jump_direction
($addr);
3582 print_3
('jp', $str, "Jumps$target");
3583 $prev_is_jump = TRUE
;
3589 instruction_take_to_pieces
($dcd_parm0);
3590 CB_prefix_decoder
();
3595 # OUT (n), A D3 11010011 nnnnnnnn
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");
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
);
3620 # IN A, (n) DB 11011011 nnnnnnnn
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}");
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
);
3645 # EX (SP), HL E3 11100011
3648 print_3
('ex', '(SP), HL', "[SP] <-> L; [SP+1] <-> H");
3653 # EX DE, HL EB 11101011
3656 print_3
('ex', 'DE, HL', "E <-> L; D <-> H");
3664 print_3
('di', '', 'Disable interrupts.');
3672 print_3
('ei', '', 'Enable interrupts.');
3674 } # given ($dcd_instr_y)
3675 } # $dcd_instr_z == 3
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
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];
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
3705 # +---/ \---+ +------/ \------+ +------/ \------+
3706 # | 7 6 | | 5 4 3 | | 2 1 0 |
3707 # +-----------------------------------------------+
3708 # | 1 | 1 | . | . | . | 1 | 0 | 1 |
3709 # +-----------------------------------------------+
3714 if ($dcd_instr_q == 0)
3717 # +---/ \---+ +------/ \------+
3719 # +-----------------------------------------------+
3720 # | 1 | 1 | . | . | 0 | 1 | 0 | 1 |
3721 # +-----------------------------------------------+
3726 # PUSH rp xx 11rr0101
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);
3743 # +---/ \---+ +------/ \------+
3745 # +-----------------------------------------------+
3746 # | 1 | 1 | . | . | 1 | 1 | 0 | 1 |
3747 # +-----------------------------------------------+
3752 given ($dcd_instr_p)
3757 # +---/ \---+ +------/ \------+
3759 # +-----------------------------------------------+
3760 # | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
3761 # +-----------------------------------------------+
3766 # CALL nn CD nn nn 11001101 a7-0 a15-8
3769 if ($decoder_silent_level == SILENT0
)
3771 my $addr = ($dcd_parm1 << 8) | $dcd_parm0;
3774 $str = label_name
($addr);
3775 $target = jump_direction
($addr);
3776 print_3
('call', $str, "Calls ([--SP] = PC.h; [--SP] = PC.l)$target");
3783 # +---/ \---+ +------/ \------+
3785 # +-----------------------------------------------+
3786 # | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
3787 # +-----------------------------------------------+
3792 DDFD_prefix_decoder
('IX');
3798 # +---/ \---+ +------/ \------+
3800 # +-----------------------------------------------+
3801 # | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
3802 # +-----------------------------------------------+
3807 ED_prefix_decoder
();
3813 # +---/ \---+ +------/ \------+
3815 # +-----------------------------------------------+
3816 # | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
3817 # +-----------------------------------------------+
3822 DDFD_prefix_decoder
('IY');
3824 } # given ($dcd_instr_p)
3826 } # $dcd_instr_z == 5
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
3847 my $i_arith = $DDFD_instr[$dcd_instr_y];
3848 my $num = sprintf '0x%02X', $dcd_parm0;
3849 my $char = decode_char
($dcd_parm0);
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
3864 # +---/ \---+ +------/ \------+ +------/ \------+
3865 # | 7 6 | | 5 4 3 | | 2 1 0 |
3866 # +-----------------------------------------------+
3867 # | 1 | 1 | . | . | . | 1 | 1 | 1 |
3868 # +-----------------------------------------------+
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
($)
3893 Log
((' ' x
$embed_level) . $Line, 5);
3895 if ($Line =~ /^#\s*include\s+["<]\s*(\S+)\s*[">]$/o)
3898 &read_header
("$include_path/$1");
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)
3917 add_ram
(hex($2), $1, TRUE
);
3921 #-------------------------------------------------------------------------------
3924 # Reads in a MCU.h file.
3930 my ($fh, $pre_comment, $comment, $line_number);
3933 if (! open($fh, '<', $Header))
3935 print STDERR
"$PROGRAM: Could not open. -> \"$Header\"\n";
3939 $head = ' ' x
$embed_level;
3941 Log
("${head}read_header($Header) >>>>", 5);
3950 # Filters off the C comments.
3952 s/\/\*.*\*\///o; # /* ... */
3953 s/\/\/.*$//o; # // ...
3958 $pre_comment = TRUE
;
3961 elsif (/\*\//o) # */
3963 $pre_comment = FALSE
;
3974 $comment = $pre_comment if ($pre_comment);
3982 run_preprocessor
($Header, \
&process_header_line
, $_, $line_number);
3986 Log
("${head}<<<< read_header($Header)", 5);
3990 #-------------------------------------------------------------------------------
3993 # Determines size of the $dcd_instr.
3996 sub determine_instr_size
()
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];
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];
4034 $instr_size = determine_instr_size
();
4036 if ($instr_size == 0)
4039 add_block
($Address, BLOCK_CONST
, $instr_size, BL_TYPE_NONE
, '');
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
);
4065 add_block
($Address, BLOCK_CONST
, $instr_size, BL_TYPE_NONE
, '');
4069 add_block
($Address, BLOCK_INSTR
, $instr_size, BL_TYPE_NONE
, '');
4076 #-------------------------------------------------------------------------------
4079 # Splits the program into small blocks.
4082 sub split_code_to_blocks
()
4085 my ($is_empty, $empty_begin);
4086 my ($is_const, $const_begin);
4091 for ($i = 0; $i < $rom_size; )
4095 if ($instr == EMPTY
)
4099 # The begin of the empty section.
4103 # The end of the constant section.
4105 add_block
($const_begin, BLOCK_CONST
, $i - $const_begin, BL_TYPE_NONE
, '');
4114 } # if ($instr == EMPTY)
4115 elsif (is_constant
($i))
4121 # The end of the empty section.
4123 add_block
($empty_begin, BLOCK_EMPTY
, $i - $empty_begin, BL_TYPE_NONE
, '');
4132 } # elsif (is_constant($i))
4137 # The end of the constant section.
4139 add_block
($const_begin, BLOCK_CONST
, $i - $const_begin, BL_TYPE_NONE
, '');
4145 # The end of the empty section.
4147 add_block
($empty_begin, BLOCK_EMPTY
, $i - $empty_begin, BL_TYPE_NONE
, '');
4151 $i += add_instr_block
($i);
4153 } # for ($i = 0; $i < $rom_size; )
4157 add_block
($const_begin, BLOCK_CONST
, $i - $const_begin, BL_TYPE_NONE
, '');
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);
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
);
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);
4260 foreach (sort {$a <=> $b} keys(%blocks_by_address))
4263 push(@instrs, $rom[$_]);
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
))
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
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.
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";
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";
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";
4352 #-------------------------------------------------------------------------------
4355 # Prints the registers (variables).
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";
4367 foreach (sort {$a <=> $b} keys(%ram_blocks_by_address))
4369 $block = $blocks_by_address{$_};
4371 if ($block->{TYPE
} != BLOCK_RAM
)
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
};
4394 print "${str0}:\t" . align
("variable_$str0", STAT_ALIGN_SIZE
) . "( 1 bytes) ($str1)\n";
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))
4408 #-------------------------------------------------------------------------------
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";
4436 print "${str0}:\t" . align
("port_$str0", STAT_ALIGN_SIZE
) . "($str1)\n";
4438 } # foreach (sort {$a <=> $b} keys(%io_by_address))
4443 #-------------------------------------------------------------------------------
4446 # Prints a label belonging to the $Address.
4451 my $Address = $_[0];
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
;
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;
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)
4502 $str0 = "\t$str1\t?";
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);
4526 $size = $BlockRef->{SIZE
};
4528 return if (! $size);
4530 $prev_is_jump = FALSE
;
4533 if ($gen_assembly_code)
4535 print ";$table_border\n;\t\t $table_header | $table_header |\n;$table_border\n";
4540 print "$table_border\n| | $table_header | $table_header |\n$table_border\n";
4544 @constants = @rom[$Address .. ($Address + $size - 1)];
4552 $len = TBL_COLUMNS
if ($len > TBL_COLUMNS
);
4554 if ($gen_assembly_code)
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)];
4575 print " $left_align" . join(' ', map { sprintf("%02X ", $_ & 0xFF); } @line);
4577 print "$right_align $brd $left_align " .
4579 sprintf((($_ < ord(' ') || $_ >= 0x7F) ?
"%02X " : "'%c'"), $_ & 0xFF);
4580 } @line) . "$right_align $brd\n";
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.
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)) . '+';
4608 $table_border = '+' . ('-' x
10) . '+' . ('-' x
(TBL_COLUMNS
* 4 + 2)) . '+' . ('-' x
(TBL_COLUMNS
* 4 + 2)) . '+';
4613 if ($gen_assembly_code)
4616 print ";$border0\n;\tCode\n;$border0\n\n\t.area\tCODE\t(CODE)\n\n";
4620 emit_globals
(FALSE
);
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
)
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
);
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 ################################################################################
4701 Usage: $PROGRAM [options] <hex file>
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
;
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:
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)
4779 ################################################################################
4780 ################################################################################
4781 ################################################################################
4783 foreach (@default_paths)
4787 $default_include_path = $_;
4798 for (my $i = 0; $i < @ARGV; )
4800 my $opt = $ARGV[$i++];
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";
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
;
4821 when (/^--const-area$/o)
4825 param_exist
($opt, $i);
4826 $start = str2int
($ARGV[$i++]);
4828 param_exist
($opt, $i);
4829 $end = str2int
($ARGV[$i++]);
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)
4902 if ($hex_file eq '')
4908 print STDERR
"$PROGRAM: We already have the source file name: $hex_file.\n";
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";
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
();
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 ###################################
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);
4956 fix_multi_byte_variables
();
4959 print_hidden_labels
() if ($verbose > 2);