summaryrefslogtreecommitdiff
path: root/support/scripts/mcs51-disasm.pl
diff options
context:
space:
mode:
authorXavier ASUS <xavi92psx@gmail.com>2019-10-18 00:31:54 +0200
committerXavier ASUS <xavi92psx@gmail.com>2019-10-18 00:31:54 +0200
commit268a53de823a6750d6256ee1fb1e7707b4b45740 (patch)
tree42c1799a9a82b2f7d9790ee9fe181d72a7274751 /support/scripts/mcs51-disasm.pl
downloadsdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'support/scripts/mcs51-disasm.pl')
-rwxr-xr-xsupport/scripts/mcs51-disasm.pl6333
1 files changed, 6333 insertions, 0 deletions
diff --git a/support/scripts/mcs51-disasm.pl b/support/scripts/mcs51-disasm.pl
new file mode 100755
index 0000000..55d20db
--- /dev/null
+++ b/support/scripts/mcs51-disasm.pl
@@ -0,0 +1,6333 @@
+#!/usr/bin/perl -w
+
+=back
+
+ Copyright (C) 2013, Molnar Karoly <molnarkaroly@users.sf.net>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+================================================================================
+
+ This program disassembles the hex files. It assumes that the hex file
+ contains MCS51 instructions.
+
+ Proposal for use: ./mcs51-disasm.pl -M 8052.h -fl -rj program.hex > program.dasm
+
+ or ./mcs51-disasm.pl -M 8052.h -fl -rj -as program.hex > program.asm
+
+ or ./mcs51-disasm.pl -M 8052.h -fl -rj -as -hc program.hex > program.asm
+
+
+ Warning! This program is not able to find all variable. Especially them not, whose
+ can be found in the indirect or external RAM.
+
+ $Id: mcs51-disasm.pl 9068 2014-09-04 12:57:30Z molnarkaroly $
+=cut
+
+use strict;
+use warnings;
+no if $] >= 5.018, warnings => "experimental::smartmatch"; # perl 5.16
+use 5.12.0; # when (regex)
+
+use constant FALSE => 0;
+use constant TRUE => 1;
+
+use constant TAB_LENGTH => 8;
+
+################################################################################
+
+use constant INHX8M => 0;
+use constant INHX32 => 2;
+
+use constant INHX_DATA_REC => 0;
+use constant INHX_EOF_REC => 1;
+use constant INHX_EXT_LIN_ADDR_REC => 4;
+
+use constant EMPTY => -1;
+
+use constant COUNT_SIZE => 2;
+use constant ADDR_SIZE => 4;
+use constant TYPE_SIZE => 2;
+use constant BYTE_SIZE => 2;
+use constant CRC_SIZE => 2;
+use constant HEADER_SIZE => (COUNT_SIZE + ADDR_SIZE + TYPE_SIZE);
+use constant MIN_LINE_LENGTH => (HEADER_SIZE + CRC_SIZE);
+
+use constant MCS51_ROM_SIZE => 0x10000;
+
+################################################################################
+
+my $PROGRAM = 'mcs51-disasm.pl';
+
+my $border0 = ('-' x 79);
+my $border1 = ('#' x 79);
+my $border2 = ('.' x 39);
+
+my @default_paths =
+ (
+ '/usr/share/sdcc/include/mcs51',
+ '/usr/local/share/sdcc/include/mcs51'
+ );
+
+my $default_include_path = '';
+my $include_path = '';
+my $hex_file = '';
+my $map_file = '';
+my $map_readed = FALSE;
+my $header_file = '';
+my $name_list = '';
+
+my $verbose = 0;
+my $hex_constant = FALSE;
+my $gen_assembly_code = FALSE;
+my $no_explanations = FALSE;
+my $recognize_jump_tables = FALSE;
+my $find_lost_labels = FALSE;
+
+my @rom = ();
+my $rom_size = MCS51_ROM_SIZE;
+my %const_areas_by_address = (); # From the command line parameters.
+
+my %const_blocks_by_address = ();
+
+=back
+ The structure of one element of the %sfr_by_address hash:
+
+ {
+ NAME => '',
+ REF_COUNT => 0
+ }
+=cut
+
+my %sfr_by_address = ();
+my %sfr_by_names = ();
+
+=back
+ The structure of one element of the %sfr_bit_by_address hash:
+
+ {
+ NAME => '',
+ REF_COUNT => 0
+ }
+=cut
+
+my %sfr_bit_by_address = ();
+
+my %used_banks = ();
+
+=back
+ The structure of one element of the %bit_by_address hash:
+
+ {
+ NAME => '',
+ REF_COUNT => 0
+ }
+=cut
+
+my %bit_by_address = ();
+
+=back
+ The structure of one element of the %ram_by_address hash:
+
+ {
+ TYPE => 0,
+ NAME => '',
+ SIZE => 0,
+ REF_COUNT => 0
+ }
+=cut
+
+use constant RAM_TYPE_DIR => 0;
+use constant RAM_TYPE_IND => 1;
+
+my %ram_by_address = ();
+my %ram_names_by_address = ();
+
+=back
+ The structure of one element of the %xram_by_address hash:
+
+ {
+ NAME => '',
+ SIZE => 0,
+ REF_COUNT => 0
+ }
+=cut
+
+my %xram_by_address = ();
+my %xram_names_by_address = ();
+
+my $stack_start = -1;
+my $stack_size = 0;
+
+ # Sizes of the instructions.
+
+my @instruction_sizes =
+ (
+ 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 1, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 1, 1, 3, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ );
+
+use constant BANK_LAST_ADDR => 0x1F;
+use constant BIT_LAST_ADDR => 0x7F;
+use constant RAM_MAX_ADDR => 0xFF;
+use constant RAM_LAST_DIR_ADDR => 0x7F;
+
+use constant SP => 0x81;
+use constant DPL => 0x82;
+use constant DPH => 0x83;
+use constant PSW => 0xD0;
+use constant ACC => 0xE0;
+
+use constant INST_AJMP => 0x01;
+use constant INST_LJMP => 0x02;
+use constant INST_ACALL => 0x11;
+use constant INST_LCALL => 0x12;
+use constant INST_SJMP => 0x80;
+use constant INST_RET => 0x22;
+use constant INST_RETI => 0x32;
+use constant INST_ADD_A_DATA => 0x24;
+use constant INST_JMP_A_DPTR => 0x73;
+use constant INST_MOV_DIRECT_DATA => 0x75;
+use constant INST_MOVC_A_APC => 0x83;
+use constant INST_MOV_DIRECT_DIRECT => 0x85;
+use constant INST_MOV_DPTR_DATA => 0x90;
+use constant INST_MOVC_A_DPTR => 0x93;
+use constant INST_PUSH_DIRECT => 0xC0;
+use constant INST_XCH_A_DIRECT => 0xC5;
+use constant INST_POP_DIRECT => 0xD0;
+use constant INST_CLR_A => 0xE4;
+use constant INST_MOV_A_DIRECT => 0xE5;
+use constant INST_MOV_A_Ri => 0xE6;
+use constant INST_MOV_A_Rn => 0xE8;
+use constant INST_MOV_DIRECT_A => 0xF5;
+use constant INST_CJNE_A_DATA => 0xB4;
+use constant INST_CJNE_A_DIRECT => 0xB5;
+use constant INST_CJNE__Ri_DATA => 0xB6;
+use constant INST_CJNE_Rn_DATA => 0xB8;
+use constant INST_DJNZ_Rn => 0xD8;
+use constant INST_DJNZ_DIRECT => 0xD5;
+use constant INST_JBC_BIT => 0x10;
+use constant INST_JB_BIT => 0x20;
+use constant INST_JNB_BIT => 0x30;
+use constant INST_JC => 0x40;
+use constant INST_JNC => 0x50;
+use constant INST_JZ => 0x60;
+use constant INST_JNZ => 0x70;
+
+use constant LJMP_SIZE => 3;
+
+my $DPTR;
+my @R_regs;
+
+my $prev_is_jump;
+
+use constant SILENT0 => 0;
+use constant SILENT1 => 1;
+use constant SILENT2 => 2;
+
+my $decoder_silent_level;
+
+use constant EXPL_ALIGN_SIZE => 5;
+use constant STAT_ALIGN_SIZE => 6;
+use constant TBL_COLUMNS => 8;
+
+=back
+ The structure of one element of the %blocks_by_address hash:
+
+ {
+ TYPE => 0,
+ ADDR => 0,
+ SIZE => 0,
+ LABEL => {
+ TYPE => 0,
+ NAME => '',
+ PRINTED => FALSE,
+ CALL_COUNT => 0,
+ JUMP_COUNT => 0
+ }
+ }
+=cut
+
+use constant BLOCK_INSTR => 0;
+use constant BLOCK_CONST => 1;
+use constant BLOCK_JTABLE => 2;
+use constant BLOCK_EMPTY => 3;
+use constant BLOCK_DISABLED => 4;
+
+use constant BL_TYPE_NONE => -1;
+use constant BL_TYPE_SUB => 0;
+use constant BL_TYPE_LABEL => 1;
+use constant BL_TYPE_JTABLE => 2;
+use constant BL_TYPE_JLABEL => 3;
+use constant BL_TYPE_CONST => 4;
+
+my %label_names =
+ (
+ eval BL_TYPE_SUB => 'Function_',
+ eval BL_TYPE_LABEL => 'Label_',
+ eval BL_TYPE_JTABLE => 'Jumptable_',
+ eval BL_TYPE_JLABEL => 'JTlabel_',
+ eval BL_TYPE_CONST => 'Constant_'
+ );
+
+my %empty_blocks_by_address = ();
+my %blocks_by_address = ();
+my %labels_by_address = ();
+my $max_label_addr = 0;
+
+my %indirect_addr_instr =
+ (
+ 0x06 => TRUE,
+ 0x16 => TRUE,
+ 0x26 => TRUE,
+ 0x36 => TRUE,
+ 0x46 => TRUE,
+ 0x56 => TRUE,
+ 0x66 => TRUE,
+ 0x76 => TRUE,
+ 0x86 => TRUE,
+ 0x96 => TRUE,
+ 0xA6 => TRUE,
+ 0xB6 => TRUE,
+ 0xC6 => TRUE,
+ 0xD6 => TRUE,
+ 0xE6 => TRUE,
+ 0xF6 => TRUE
+ );
+
+my %interrupts_by_address =
+ (
+ 0x0000 => 'System_init',
+ 0x0003 => 'Int0_interrupt',
+ 0x000B => 'Timer0_interrupt',
+ 0x0013 => 'Int1_interrupt',
+ 0x001B => 'Timer1_interrupt',
+ 0x0023 => 'Uart_interrupt',
+ 0x002B => 'Timer2_interrupt',
+ 0x0033 => 'Int2_interrupt',
+ 0x003B => 'Int3_interrupt'
+ );
+
+my %control_characters =
+ (
+ 0x00 => '\0',
+ 0x07 => '\a',
+ 0x08 => '\b',
+ 0x09 => '\t',
+ 0x0A => '\n',
+ 0x0C => '\f',
+ 0x0D => '\r',
+ 0x1B => '\e',
+ 0x7F => '^?'
+ );
+
+my $dcd_address = 0;
+my $dcd_instr_size = 0;
+my $dcd_instr = 0;
+my $dcd_parm0 = 0;
+my $dcd_parm1 = 0;
+my $dcd_Ri_regs = 0;
+my $dcd_Ri_name = '';
+my $dcd_Rn_regs = 0;
+my $dcd_Rn_name = '';
+
+my $table_header = '';
+my $table_border = '';
+
+################################################################################
+################################################################################
+
+my %pp_defines = (); # Value of definitions.
+
+my @pp_conditions = ();
+my @pp_else_conditions = ();
+my $pp_level = 0; # Shows the lowest level.
+my $embed_level;
+
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@
+#@@@@@@@@@@@@@@@@@@@@@@@ This a simple preprocessor. @@@@@@@@@@@@@@@@@@@@@@@@@
+#@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+ # Examines that the parameter is defined or not defined.
+
+sub _defined($)
+ {
+ return defined($pp_defines{$_[0]});
+ }
+
+#-------------------------------------------------------------------------------
+
+ # Records a definition.
+
+sub define($)
+ {
+ my ($Name) = ($_[0] =~ /^(\S+)/op);
+ my $Body = ${^POSTMATCH};
+
+ $Body =~ s/^\s+//o;
+
+ die "define(): This definition already exists: \"$Name\"\n" if (_defined($Name));
+
+ # The definition is in fact unnecessary.
+ $pp_defines{$Name} = $Body;
+ }
+
+#-------------------------------------------------------------------------------
+
+ # Delete a definition.
+
+sub undefine($)
+ {
+ delete($pp_defines{$_[0]});
+ }
+
+#-------------------------------------------------------------------------------
+
+ # Evaluation of the #if give a boolean value. This procedure preserves it.
+
+sub if_condition($)
+ {
+ my $Val = $_[0];
+
+ push(@pp_conditions, $Val);
+ push(@pp_else_conditions, $Val);
+ ++$pp_level;
+ }
+
+#-------------------------------------------------------------------------------
+
+ # Evaluation of the #else give a boolean value. This procedure preserves it.
+
+sub else_condition($$)
+ {
+ my ($File, $Line_number) = @_;
+
+ 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);
+
+ my $last = $#pp_conditions;
+
+ if ($last > 0 && $pp_conditions[$last - 1])
+ {
+ $pp_conditions[$last] = ($pp_else_conditions[$#pp_else_conditions]) ? FALSE : TRUE;
+ }
+ else
+ {
+ $pp_conditions[$last] = FALSE;
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ # Closes a logical unit which starts with a #if.
+
+sub endif_condition($$)
+ {
+ my ($File, $Line_number) = @_;
+
+ 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);
+
+ pop(@pp_conditions);
+ pop(@pp_else_conditions);
+ --$pp_level;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub reset_preprocessor()
+ {
+ %pp_defines = ();
+ @pp_conditions = ();
+ push(@pp_conditions, TRUE);
+ @pp_else_conditions = ();
+ push(@pp_else_conditions, FALSE);
+ $pp_level = 0;
+ }
+
+#-------------------------------------------------------------------------------
+
+ # This the preprocessor.
+
+sub run_preprocessor($$$$)
+ {
+ my ($Fname, $Function, $Line, $Line_number) = @_;
+
+ if ($Line =~ /^#\s*ifdef\s+(\S+)$/o)
+ {
+ if ($pp_conditions[$#pp_conditions])
+ {
+ # The ancestor is valid, therefore it should be determined that
+ # the descendants what kind.
+
+ if_condition(_defined($1));
+ }
+ else
+ {
+ # The ancestor is invalid, so the descendants will invalid also.
+
+ if_condition(FALSE);
+ }
+ }
+ elsif ($Line =~ /^#\s*ifndef\s+(\S+)$/o)
+ {
+ if ($pp_conditions[$#pp_conditions])
+ {
+ # The ancestor is valid, therefore it should be determined that
+ # the descendants what kind.
+
+ if_condition(! _defined($1));
+ }
+ else
+ {
+ # The ancestor is invalid, so the descendants will invalid also.
+
+ if_condition(FALSE);
+ }
+ }
+ elsif ($Line =~ /^#\s*else/o)
+ {
+ else_condition($Fname, $Line_number);
+ }
+ elsif ($Line =~ /^#\s*endif/o)
+ {
+ endif_condition($Fname, $Line_number);
+ }
+ elsif ($Line =~ /^#\s*define\s+(.+)$/o)
+ {
+ # This level is valid, so it should be recorded in the definition.
+
+ define($1) if ($pp_conditions[$#pp_conditions]);
+ }
+ elsif ($Line =~ /^#\s*undef\s+(.+)$/o)
+ {
+ # This level is valid, so it should be deleted in the definition.
+
+ undefine($1) if ($pp_conditions[$#pp_conditions]);
+ }
+ elsif ($pp_conditions[$#pp_conditions])
+ {
+ # This is a valid line. (The whole magic is in fact therefore there is.)
+
+ $Function->($Line);
+ }
+ }
+
+################################################################################
+################################################################################
+################################################################################
+
+sub basename($)
+ {
+ return ($_[0] =~ /([^\/]+)$/) ? $1 : '';
+ }
+
+#-------------------------------------------------------------------------------
+
+sub param_exist($$)
+ {
+ die "This option \"$_[0]\" requires a parameter.\n" if ($_[1] > $#ARGV);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub Log
+ {
+ return if (pop(@_) > $verbose);
+ foreach (@_) { print STDERR $_; }
+ print STDERR "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+sub str2int($)
+ {
+ my $Str = $_[0];
+
+ return hex($1) if ($Str =~ /^0x([[:xdigit:]]+)$/io);
+ return int($Str) if ($Str =~ /^-?\d+$/o);
+
+ die "str2int(): This string not integer: \"$Str\"";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Before print, formats the $Text.
+ #
+
+sub align($$)
+ {
+ my $Text = $_[0];
+ my $al = $_[1] - int(length($Text) / TAB_LENGTH);
+
+ # One space will surely becomes behind it.
+ if ($al < 1)
+ {
+ return "$Text ";
+ }
+ else
+ {
+ return ($Text . ("\t" x $al));
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Multiple file test.
+ #
+
+sub is_file_ok($)
+ {
+ my $File = $_[0];
+
+ if (! -e $File)
+ {
+ print STDERR "$PROGRAM: Not exists -> \"$File\"\n";
+ exit(1);
+ }
+
+ if (! -f $File)
+ {
+ print STDERR "$PROGRAM: Not file -> \"$File\"\n";
+ exit(1);
+ }
+
+ if (! -r $File)
+ {
+ print STDERR "$PROGRAM: Can not read -> \"$File\"\n";
+ exit(1);
+ }
+
+ if (! -s $File)
+ {
+ print STDERR "$PROGRAM: Empty file -> \"$File\"\n";
+ exit(1);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Initializes the @rom array.
+ #
+
+sub init_mem($$)
+ {
+ my ($Start, $End) = @_;
+
+ @rom[$Start .. $End] = ((EMPTY) x ($End - $Start + 1));
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Store values of the $Code to $AddrRef address.
+ #
+
+sub store_code($$)
+ {
+ my ($Code, $AddrRef) = @_;
+
+ if ($$AddrRef >= $rom_size)
+ {
+ printf STDERR "Warning, this address (0x%04X) outside the code area (0x%04X)!\n", $$AddrRef, $rom_size - 1;
+ }
+
+ Log(sprintf("rom[0x%08X] = 0x%02X", $$AddrRef, $Code), 9);
+ $rom[$$AddrRef++] = $Code;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Reads contents of the $Hex.
+ #
+
+sub read_hex($)
+ {
+ my $Hex = $_[0];
+ my $addr_H = 0;
+ my $format = INHX32;
+ my $line_num = 0;
+
+ if (! open(IN, '<', $Hex))
+ {
+ print STDERR "$PROGRAM : Could not open. -> \"$Hex\"\n";
+ exit(1);
+ }
+
+ while (<IN>)
+ {
+ chomp;
+ s/\r$//o;
+ ++$line_num;
+
+ my $len = length() - 1;
+
+ if ($len < MIN_LINE_LENGTH)
+ {
+ close(IN);
+ print STDERR "$PROGRAM: ${line_num}th line <- Shorter than %u character.\n", MIN_LINE_LENGTH;
+ exit(1);
+ }
+
+ Log("$..(1) (\"$_\") length() = " . length(), 7);
+
+ my $bytecount = int(($len - MIN_LINE_LENGTH) / BYTE_SIZE);
+
+ my $binrec = pack('H*', substr($_, 1));
+
+ if (unpack('%8C*', $binrec) != 0)
+ {
+ close(IN);
+ print STDERR "$PROGRAM: $Hex <- Crc error. (${line_num}th line \"$_\").\n";
+ exit(1);
+ }
+
+ my ($count, $addr, $type, $bytes) = unpack('CnCX4Cx3/a', $binrec);
+
+ my @codes = unpack('C*', $bytes);
+
+ Log(sprintf("$..(2) (\"$_\") count = $count, bytecount = $bytecount, addr = 0x%04X, type = $type", $addr), 7);
+
+ if ($type == INHX_EOF_REC)
+ {
+ last;
+ }
+ elsif ($type == INHX_EXT_LIN_ADDR_REC)
+ {
+ $addr_H = unpack('n', $bytes); # big-endian
+
+ Log(sprintf("$..(3) (\"$_\") addr_H = 0x%04X\n", $addr_H), 7);
+
+ $format = INHX32;
+ Log('format = INHX32', 7);
+ next;
+ }
+ elsif ($type != INHX_DATA_REC)
+ {
+ close(IN);
+ printf STDERR "$PROGRAM: $Hex <- Unknown type of record: 0x%02X (${line_num}th line \"$_\").\n", $type;
+ exit(1);
+ }
+
+ if ($bytecount == $count) # INHX32
+ {
+ if ($format == INHX8M)
+ {
+ close(IN);
+ print STDERR "$PROGRAM: $Hex <- Mixed format of file (${line_num}th line \"$_\").\n";
+ exit(1);
+ }
+
+ my $addr32 = ($addr_H << 16) | $addr;
+
+ map { store_code($_, \$addr32) } @codes;
+ }
+ elsif ($bytecount == ($count * BYTE_SIZE)) # INHX8M
+ {
+ if ($format == INHX32)
+ {
+ close(IN);
+ print STDERR "$PROGRAM: $Hex <- Mixed format of file (${line_num}th line \"$_\").\n";
+ exit(1);
+ }
+
+ map { store_code($_, \$addr) } @codes;
+ }
+ else
+ {
+ close(IN);
+ print STDERR "$PROGRAM: $Hex <- Wrong format of file (${line_num}th line \"$_\").\n";
+ exit(1);
+ }
+ } # while (<IN>)
+
+ close(IN);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Determines that the $Address belongs to a constant.
+ #
+
+sub is_constant($)
+ {
+ my $Address = $_[0];
+
+ foreach (sort {$a <=> $b} keys(%const_areas_by_address))
+ {
+ return TRUE if ($_ <= $Address && $Address <= $const_areas_by_address{$_});
+ last if ($_ > $Address);
+ }
+
+ foreach (sort {$a <=> $b} keys(%const_blocks_by_address))
+ {
+ return TRUE if ($_ <= $Address && $Address <= $const_blocks_by_address{$_});
+ last if ($_ > $Address);
+ }
+
+ return FALSE;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Determines that the $Address belongs to a empty area.
+ #
+
+sub is_empty($)
+ {
+ my $Address = $_[0];
+
+ foreach (sort {$a <=> $b} keys(%empty_blocks_by_address))
+ {
+ return TRUE if ($_ <= $Address && $Address <= $empty_blocks_by_address{$_});
+ last if ($_ > $Address);
+ }
+
+ return FALSE;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Creates a const block.
+ #
+
+sub add_const_area($$)
+ {
+ $const_areas_by_address{$_[0]} = $_[1];
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Creates a new block, or modifies one.
+ #
+
+sub add_block($$$$$)
+ {
+ my ($Address, $Type, $Size, $LabelType, $LabelName) = @_;
+ my ($block, $label, $end);
+
+ $end = $Address + $Size - 1;
+
+ if (! defined($blocks_by_address{$Address}))
+ {
+ $label = {
+ TYPE => $LabelType,
+ NAME => $LabelName,
+ PRINTED => FALSE,
+ CALL_COUNT => 0,
+ JUMP_COUNT => 0
+ };
+
+ $blocks_by_address{$Address} = {
+ TYPE => $Type,
+ ADDR => $Address,
+ SIZE => $Size,
+ LABEL => $label
+ };
+
+ if ($Type == BLOCK_INSTR)
+ {
+ if ($LabelType != BL_TYPE_NONE)
+ {
+ $labels_by_address{$Address} = $label;
+ $max_label_addr = $Address if ($max_label_addr < $Address);
+ }
+ }
+ elsif ($Type == BLOCK_CONST || $Type == BLOCK_JTABLE)
+ {
+ if ($LabelType != BL_TYPE_NONE)
+ {
+ $labels_by_address{$Address} = $label;
+ $max_label_addr = $Address if ($max_label_addr < $Address);
+ }
+
+ $const_blocks_by_address{$Address} = $end if ($Size > 0);
+ }
+ elsif ($Type == BLOCK_EMPTY)
+ {
+ # At empty area, can not be label.
+
+ $label->{TYPE} = BL_TYPE_NONE;
+ $label->{NAME} = '';
+ $empty_blocks_by_address{$Address} = $end if ($Size > 0);
+ }
+ else
+ {
+ printf STDERR "add_block(0x%04X): Unknown block type!\n", $Address;
+ exit(1);
+ }
+ } # if (! defined($blocks_by_address{$Address}))
+ else
+ {
+ $block = $blocks_by_address{$Address};
+ $label = $block->{LABEL};
+ $block->{TYPE} = $Type;
+ $block->{SIZE} = $Size if ($Size > 0);
+ $label->{NAME} = $LabelName if ($label->{NAME} eq '' && $LabelName ne '');
+
+ if ($Type == BLOCK_INSTR)
+ {
+ if ($LabelType != BL_TYPE_NONE)
+ {
+ $label->{TYPE} = $LabelType if ($label->{TYPE} != BL_TYPE_JLABEL);
+ $labels_by_address{$Address} = $label;
+ $max_label_addr = $Address if ($max_label_addr < $Address);
+ }
+ }
+ elsif ($Type == BLOCK_CONST || $Type == BLOCK_JTABLE)
+ {
+ if ($LabelType != BL_TYPE_NONE)
+ {
+ $label->{TYPE} = $LabelType;
+ $labels_by_address{$Address} = $label;
+ $max_label_addr = $Address if ($max_label_addr < $Address);
+ }
+
+ $const_blocks_by_address{$Address} = $end if ($Size > 0);
+ }
+ elsif ($Type == BLOCK_EMPTY)
+ {
+ # At empty area, can not be label.
+
+ $label->{TYPE} = BL_TYPE_NONE;
+ $label->{NAME} = '';
+ $empty_blocks_by_address{$Address} = $end if ($Size > 0);
+ }
+ }
+
+ return $label;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Store address entry of a procedure.
+ #
+
+sub add_func_label($$$)
+ {
+ my ($Address, $Name, $Map_mode) = @_;
+ my $label;
+
+ if ($Address < 0)
+ {
+ Log(sprintf("add_func_label(): This address (0x%04X) negative!", $Address), 2);
+ return;
+ }
+
+ if (! $Map_mode)
+ {
+ if (! defined($blocks_by_address{$Address}))
+ {
+ Log(sprintf("add_func_label(): This address (0x%04X) does not shows an instruction!", $Address), 2);
+ return;
+ }
+ }
+
+ if (is_constant($Address) || is_empty($Address))
+ {
+ Log(sprintf("add_func_label(): This address (0x%04X) outside the code area!", $Address), 2);
+ return;
+ }
+
+ $label = add_block($Address, BLOCK_INSTR, 0, BL_TYPE_SUB, $Name);
+ ++$label->{CALL_COUNT} if (! $Map_mode);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Store a label.
+ #
+
+sub add_jump_label($$$$$)
+ {
+ my ($TargetAddr, $Name, $Type, $SourceAddr, $Map_mode) = @_;
+ my ($label, $type);
+
+ if ($TargetAddr < 0)
+ {
+ Log(sprintf("add_jump_label(): This address (0x%04X) negative!", $TargetAddr), 2);
+ return;
+ }
+
+ if (! $Map_mode)
+ {
+ if (! defined($blocks_by_address{$TargetAddr}))
+ {
+ Log(sprintf("add_jump_label(): This address (0x%04X) does not shows an instruction!", $TargetAddr), 2);
+ return;
+ }
+ }
+
+ if (is_constant($TargetAddr) || is_empty($TargetAddr))
+ {
+ Log(sprintf("add_jump_label(): This address (0x%04X) outside the code area!", $TargetAddr), 2);
+ return;
+ }
+
+ if (defined($interrupts_by_address{$SourceAddr}))
+ {
+ $Type = BL_TYPE_SUB;
+ $Name = $interrupts_by_address{$SourceAddr} if ($Name eq '');
+ }
+
+ $label = add_block($TargetAddr, BLOCK_INSTR, 0, $Type, $Name);
+ ++$label->{JUMP_COUNT} if (! $Map_mode);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Store a bit or sbit name from bit area.
+ #
+
+sub add_bit($$$)
+ {
+ my ($Address, $Name, $Map_mode) = @_;
+ my $bit;
+
+ if ($Address > BIT_LAST_ADDR)
+ {
+ if (! defined($bit = $sfr_bit_by_address{$Address}))
+ {
+ $sfr_bit_by_address{$Address} = {
+ NAME => $Name,
+ REF_COUNT => ($Map_mode) ? 0 : 1
+ };
+ }
+ else
+ {
+ if ($Name ne '' && $bit->{NAME} ne $Name)
+ {
+ Log(sprintf("Warning, the address: 0x%02X already busy by the $bit->{NAME} bit.", $Address), 2);
+ }
+ else
+ {
+ ++$bit->{REF_COUNT} if (! $Map_mode);
+ }
+ }
+ }
+ else
+ {
+ if (! defined($bit = $bit_by_address{$Address}))
+ {
+ $bit_by_address{$Address} = {
+ NAME => $Name,
+ REF_COUNT => ($Map_mode) ? 0 : 1
+ };
+ }
+ else
+ {
+ ++$bit->{REF_COUNT} if (! $Map_mode);
+ }
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Store a sfr or variable name.
+ #
+
+sub add_ram($$$$)
+ {
+ my ($Address, $Name, $Type, $Map_mode) = @_;
+ my $ram;
+
+ return if ($Address == EMPTY);
+
+ if ($Address > RAM_LAST_DIR_ADDR && $Type == RAM_TYPE_DIR)
+ {
+ if (! defined($ram = $sfr_by_address{$Address}))
+ {
+ $sfr_by_address{$Address} = {
+ NAME => $Name,
+ REF_COUNT => ($Map_mode) ? 0 : 1
+ };
+ }
+ else
+ {
+ if ($Name ne '' && $ram->{NAME} ne $Name)
+ {
+ Log(sprintf("Warning, the address: 0x%02X already busy by the $ram->{NAME} register.", $Address), 2);
+ }
+ else
+ {
+ ++$ram->{REF_COUNT} if (! $Map_mode);
+ }
+ }
+
+ $sfr_by_names{$Name} = $Address;
+ }
+ else
+ {
+ if (! defined($ram = $ram_by_address{$Address}))
+ {
+ $ram_by_address{$Address} = {
+ TYPE => $Type,
+ NAME => $Name,
+ SIZE => 1,
+ REF_COUNT => ($Map_mode) ? 0 : 1
+ };
+ }
+ else
+ {
+ ++$ram->{REF_COUNT} if (! $Map_mode);
+ }
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Store a variable name from XRAM.
+ #
+
+sub add_xram($$$)
+ {
+ my ($Address, $Name, $Map_mode) = @_;
+ my $xram;
+
+ if (! defined($xram = $xram_by_address{$Address}))
+ {
+ $xram_by_address{$Address} = {
+ NAME => $Name,
+ SIZE => 1,
+ REF_COUNT => ($Map_mode) ? 0 : 1
+ };
+ }
+ else
+ {
+ ++$xram->{REF_COUNT} if (! $Map_mode);
+ }
+ }
+
+################################################################################
+################################################################################
+
+use constant MAP_NULL => 0;
+use constant MAP_BORDER => 1;
+use constant MAP_AREA => 2;
+use constant MAP_ABS => 3;
+use constant MAP_CABS => 4;
+use constant MAP_CODE0 => 5;
+use constant MAP_CODE1 => 6;
+use constant MAP_DATA => 7;
+use constant MAP_ISEG => 8;
+use constant MAP_CONST => 9;
+
+ #
+ # If exists the map file, then extracts out of it the labels,
+ # variables and some segments.
+ #
+
+sub read_map_file()
+ {
+ my ($addr, $name, $state, $label);
+
+ return if ($map_file eq '');
+
+ $state = MAP_NULL;
+
+ if (! open(MAP, '<', $map_file))
+ {
+ print STDERR "$PROGRAM : Could not open. -> \"$map_file\"\n";
+ exit(1);
+ }
+
+ while (<MAP>)
+ {
+ chomp;
+ s/\r$//o;
+
+ if ($state == MAP_NULL)
+ {
+ $state = MAP_BORDER if (/^Area\s+/io);
+ }
+ elsif ($state == MAP_BORDER)
+ {
+ $state = MAP_AREA if (/^-+\s+/o);
+ }
+ elsif ($state == MAP_AREA)
+ {
+ if (/^\.\s+\.ABS\.\s+/o)
+ {
+ $state = MAP_ABS;
+ }
+ elsif (/^CABS\s+/o)
+ {
+ $state = MAP_CABS;
+ }
+ elsif (/^(HOME|CSEG)\s+/o)
+ {
+ $state = MAP_CODE0;
+ }
+ elsif (/^GSINIT\d+\s+/o)
+ {
+ $state = MAP_CODE1;
+ }
+ elsif (/^(D|O)SEG\s+/o)
+ {
+ $state = MAP_DATA;
+ }
+ elsif (/^ISEG\s+/o)
+ {
+ $state = MAP_ISEG;
+ }
+ elsif (/^CONST\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+/o)
+ {
+ my ($start, $size) = (hex($1), hex($2));
+
+ add_const_area($start, $start + $size - 1);
+ $state = MAP_CONST;
+ }
+ elsif (/^SSEG\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)/o)
+ {
+ ($stack_start, $stack_size) = (hex($1), hex($2));
+ }
+ else
+ {
+ $state = MAP_NULL;
+ }
+ }
+ elsif ($state == MAP_ABS)
+ {
+ if (/^.ASxxxx Linker\s+/io)
+ {
+ $state = MAP_NULL;
+ }
+ elsif (/\s*([[:xdigit:]]+)\s+(\S+)/o)
+ {
+ # 000002C0 _Dword xdata
+
+ ($addr, $name) = (hex($1), $2);
+
+ add_xram(hex($1), $2, TRUE) if ($addr > RAM_MAX_ADDR);
+ }
+ }
+ elsif ($state == MAP_CABS)
+ {
+ if (/^.ASxxxx Linker\s+/io)
+ {
+ $state = MAP_NULL;
+ }
+ elsif (/^C:\s+([[:xdigit:]]+)\s+(\S+)/o)
+ {
+ ($addr, $name) = (hex($1), $2);
+
+ if ($name eq 's_CONST' || $name eq 's_XINIT')
+ {
+ # C: 000001E8 s_CONST
+ # C: 00000215 s_XINIT
+
+ add_block($addr, BLOCK_CONST, 0, BL_TYPE_CONST, $name);
+ }
+ }
+ } # elsif ($state == MAP_CABS)
+ elsif ($state == MAP_CODE0 || $state == MAP_CODE1)
+ {
+ if (/^.ASxxxx Linker\s+/io)
+ {
+ $state = MAP_NULL;
+ }
+ elsif (/^C:\s+([[:xdigit:]]+)\s+(\S+)/o)
+ {
+ # C: 00000040 __mcs51_genXINIT
+ # C: 00000061 __mcs51_genRAMCLEAR
+ # C: 00000067 __mcs51_genXRAMCLEAR
+ # C: 00000086 _Uart_int main
+ # C: 000000CE _toHexChar main
+ # C: 000001E4 __sdcc_external_startup _startup
+
+ ($addr, $name) = (hex($1), $2);
+
+ if ($state == MAP_CODE0)
+ {
+ add_func_label($addr, $name, TRUE);
+ }
+ else
+ {
+ add_jump_label($addr, $name, BL_TYPE_LABEL, EMPTY, TRUE);
+ }
+ }
+ } # elsif ($state == MAP_CODE0 || $state == MAP_CODE1)
+ elsif ($state == MAP_DATA)
+ {
+ if (/^.ASxxxx Linker\s+/io)
+ {
+ $state = MAP_NULL;
+ }
+ elsif (/^\s*([[:xdigit:]]+)\s+(\S+)/o)
+ {
+ # 00000039 _counter data
+ # 0000004C _flash_read_PARM_2 flash
+
+ add_ram(hex($1), $2, RAM_TYPE_DIR, TRUE);
+ }
+ } # elsif ($state == MAP_DATA)
+ elsif ($state == MAP_ISEG)
+ {
+ if (/^.ASxxxx Linker\s+/io)
+ {
+ $state = MAP_NULL;
+ }
+ elsif (/^\s*([[:xdigit:]]+)\s+(\S+)/o)
+ {
+ # 00000082 _length data
+
+ add_ram(hex($1), $2, RAM_TYPE_IND, TRUE);
+ }
+ } # elsif ($state == MAP_DATA)
+ elsif ($state == MAP_CONST)
+ {
+ $state = MAP_NULL if (/^.ASxxxx Linker\s+/io);
+ }
+ } # while (<MAP>)
+
+ $map_readed = TRUE;
+ close(MAP);
+ }
+
+#-------------------------------------------------------------------------------
+
+use constant NAMES_NULL => 0;
+use constant NAMES_BIT => 1;
+use constant NAMES_RAM => 2;
+use constant NAMES_IRAM => 3;
+use constant NAMES_XRAM => 4;
+use constant NAMES_ROM => 5;
+
+sub read_name_list()
+ {
+ my ($line, $addr, $name, $state);
+
+ return if ($name_list eq '');
+
+ if (! open(NAMES, '<', $name_list))
+ {
+ print STDERR "$PROGRAM : Could not open. -> \"$name_list\"\n";
+ exit(1);
+ }
+
+ $state = NAMES_NULL;
+
+ foreach (grep(! /^\s*$/o, <NAMES>))
+ {
+ chomp;
+ s/\r$//o;
+ s/^\s*|\s*$//go;
+
+ if (/^\[BIT\]$/io)
+ {
+ $state = NAMES_BIT;
+ next;
+ }
+ elsif (/^\[RAM\]$/io)
+ {
+ $state = NAMES_RAM;
+ next;
+ }
+ elsif (/^\[XRAM\]$/io)
+ {
+ $state = NAMES_XRAM;
+ next;
+ }
+ elsif (/^\[IRAM\]$/io)
+ {
+ $state = NAMES_IRAM;
+ next;
+ }
+ elsif (/^\[ROM\]$/io)
+ {
+ $state = NAMES_ROM;
+ next;
+ }
+
+ $line = $_;
+
+ given ($state)
+ {
+ when (NAMES_BIT)
+ {
+ if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
+ {
+ add_bit(hex($1), $2, TRUE);
+ }
+ }
+
+ when (NAMES_RAM)
+ {
+ if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
+ {
+ add_ram(hex($1), $2, RAM_TYPE_DIR, TRUE);
+ }
+ }
+
+ when (NAMES_IRAM)
+ {
+ if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
+ {
+ add_ram(hex($1), $2, RAM_TYPE_IND, TRUE);
+ }
+ }
+
+ when (NAMES_XRAM)
+ {
+ if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
+ {
+ add_xram(hex($1), $2, TRUE);
+ }
+ }
+
+ when (NAMES_ROM)
+ {
+ if ($line =~ /^0x([[:xdigit:]]+)\s*:\s*(\S+)$/io)
+ {
+ add_jump_label(hex($1), $2, BL_TYPE_LABEL, EMPTY, TRUE);
+ }
+ }
+ } # given ($state)
+ } # foreach (grep(! /^\s*$/o, <NAMES>))
+
+ close(NAMES);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # There are some variables that are multi-byte. However, only
+ # the LSB byte of having a name. This procedure gives a name
+ # for the higher-significant bytes.
+ #
+
+sub fix_multi_byte_variables()
+ {
+ my ($prev_addr, $prev_name, $name, $i, $var_size);
+
+ $prev_addr = EMPTY;
+ $prev_name = '';
+ foreach (sort {$a <=> $b} keys(%ram_by_address))
+ {
+ $name = $ram_by_address{$_}->{NAME};
+ $ram_names_by_address{$_} = $name;
+
+ if ($prev_addr != EMPTY)
+ {
+ $var_size = $_ - $prev_addr;
+
+ if ($var_size > 1)
+ {
+ # This is a multi-byte variable. Make the aliases.
+
+ for ($i = 1; $i < $var_size; ++$i)
+ {
+ $ram_names_by_address{$prev_addr + $i} = "($prev_name + $i)";
+ }
+
+ $ram_by_address{$prev_addr}->{SIZE} = $var_size;
+ }
+ }
+
+ $prev_addr = $_;
+ $prev_name = $name;
+ }
+
+ $prev_addr = EMPTY;
+ $prev_name = '';
+ foreach (sort {$a <=> $b} keys(%xram_by_address))
+ {
+ $name = $xram_by_address{$_}->{NAME};
+ $xram_names_by_address{$_} = $name;
+
+ if ($prev_addr != EMPTY)
+ {
+ $var_size = $_ - $prev_addr;
+
+ if ($var_size > 1)
+ {
+ # This is a multi-byte variable. Make the aliases.
+
+ for ($i = 1; $i < $var_size; ++$i)
+ {
+ $xram_names_by_address{$prev_addr + $i} = "($prev_name + $i)";
+ }
+
+ $xram_by_address{$prev_addr}->{SIZE} = $var_size;
+ }
+ }
+
+ $prev_addr = $_;
+ $prev_name = $name;
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If there is left in yet so label that has no name, this here get one.
+ #
+
+sub add_names_labels()
+ {
+ my ($addr, $label, $fidx, $lidx, $jidx, $jtidx, $cidx, $type);
+
+ $fidx = 0;
+ $lidx = 0;
+ $jidx = 0;
+ $jtidx = 0;
+ $cidx = 0;
+
+ for ($addr = 0; $addr <= $max_label_addr; ++$addr)
+ {
+ $label = $labels_by_address{$addr};
+
+ next if (! defined($label));
+
+ $type = $label->{TYPE};
+
+ next if ($type == BL_TYPE_NONE || (defined($label->{NAME}) && $label->{NAME} ne ''));
+
+ if ($type == BL_TYPE_SUB)
+ {
+ $label->{NAME} = sprintf("$label_names{$type}%03u", $fidx++);
+ }
+ elsif ($type == BL_TYPE_LABEL)
+ {
+ $label->{NAME} = sprintf("$label_names{$type}%03u", $lidx++);
+ }
+ elsif ($type == BL_TYPE_JTABLE)
+ {
+ $label->{NAME} = sprintf("$label_names{$type}%03u", $jidx++);
+ }
+ elsif ($type == BL_TYPE_JLABEL)
+ {
+ $label->{NAME} = sprintf("$label_names{$type}%03u", $jtidx++);
+ }
+ elsif ($type == BL_TYPE_CONST)
+ {
+ $label->{NAME} = sprintf("$label_names{$type}%03u", $cidx++);
+ }
+ }
+ }
+
+################################################################################
+################################################################################
+
+=back
+ Instruction set of the 8051 family:
+
+ NOP 00000000
+ AJMP addr11 aaa00001 aaaaaaaa a10 a9 a8 1 0 0 0 1 a7-a0
+ LJMP addr16 00000010 aaaaaaaa aaaaaaaa a15-a8 a7-a0 absolute address
+ RR A 00000011
+ INC A 00000100
+ INC direct 00000101 aaaaaaaa register address
+ INC @Ri 0000011i R0 .. R1
+ INC Rn 00001rrr R0 .. R7
+ JBC bit, rel 00010000 bbbbbbbb rrrrrrrr bit address relative address
+ ACALL addr11 aaa10001 aaaaaaaa a10 a9 a8 1 0 0 0 1 a7-a0
+ LCALL addr16 00010010 aaaaaaaa aaaaaaaa a15-a8 a7-a0 absolute address
+ RRC A 00010011
+ DEC A 00010100
+ DEC direct 00010101 aaaaaaaa register address
+ DEC @Ri 0001011i R0 .. R1
+ DEC Rn 00011rrr R0 .. R7
+ JB bit, rel 00100000 bbbbbbbb rrrrrrrr bit address relative address
+ RET 00100010
+ RL A 00100011
+ ADD A, #data 00100100 dddddddd adat
+ ADD A, direct 00100101 aaaaaaaa register address
+ ADD A, @Ri 0010011i R0 .. R1
+ ADD A, Rn 00101rrr R0 .. R7
+ JNB bit, rel 00110000 bbbbbbbb rrrrrrrr bit address relative address
+ RETI 00110010
+ RLC A 00110011
+ ADDC A, #data 00110100 dddddddd adat
+ ADDC A, direct 00110101 aaaaaaaa register address
+ ADDC A, @Ri 0011011i R0 .. R1
+ ADDC A, Rn 00111rrr R0 .. R7
+ JC rel 01000000 rrrrrrrr relative address
+ ORL direct, A 01000010 aaaaaaaa register address
+ ORL direct, #data 01000011 aaaaaaaa dddddddd register address adat
+ ORL A, #data 01000100 dddddddd adat
+ ORL A, direct 01000101 aaaaaaaa register address
+ ORL A, @Ri 0100011i R0 .. R1
+ ORL A, Rn 01001rrr R0 .. R7
+ JNC rel 01010000 rrrrrrrr relative address
+ ANL direct, A 01010010 aaaaaaaa register address
+ ANL direct, #data 01010011 aaaaaaaa dddddddd register address adat
+ ANL A, #data 01010100 dddddddd adat
+ ANL A, direct 01010101 aaaaaaaa register address
+ ANL A, @Ri 0101011i R0 .. R1
+ ANL A, Rn 01011rrr R0 .. R7
+ JZ rel 01100000 rrrrrrrr relative address
+ XRL direct, A 01100010 aaaaaaaa register address
+ XRL direct, #data 01100011 aaaaaaaa dddddddd register address adat
+ XRL A, #data 01100100 dddddddd adat
+ XRL A, direct 01100101 aaaaaaaa register address
+ XRL A, @Ri 0110011i R0 .. R1
+ XRL A, Rn 01101rrr R0 .. R7
+ JNZ rel 01110000 rrrrrrrr relative address
+ ORL C, bit 01110010 bbbbbbbb bit address
+ JMP @A+DPTR 01110011
+ MOV A, #data 01110100 dddddddd adat
+ MOV direct, #data 01110101 aaaaaaaa dddddddd register address adat
+ MOV @Ri, #data 0111011i dddddddd adat
+ MOV Rn, #data 01111rrr dddddddd R0 .. R7 adat
+ SJMP rel 10000000 rrrrrrrr relative address
+ ANL C, bit 10000010 bbbbbbbb bit address
+ MOVC A, @A+PC 10000011
+ DIV AB 10000100
+ MOV direct, direct 10000101 aaaaaaaa aaaaaaaa forrás reg. cél reg.
+ MOV direct, @Ri 1000011i aaaaaaaa R0 .. R1 register address
+ MOV direct, Rn 10001rrr aaaaaaaa R0 .. R7 register address
+ MOV DPTR, #data16 10010000 dddddddd dddddddd d15-d8 d7-d0
+ MOV bit, C 10010010 bbbbbbbb bit address
+ MOVC A, @A+DPTR 10010011
+ SUBB A, #data 10010100 dddddddd adat
+ SUBB A, direct 10010101 aaaaaaaa register address
+ SUBB A, @Ri 1001011i R0 .. R1
+ SUBB A, Rn 10011rrr R0 .. R7
+ ORL C, /bit 10100000 bbbbbbbb bit address
+ MOV C, bit 10100010 bbbbbbbb bit address
+ INC DPTR 10100011
+ MUL AB 10100100
+ MOV @Ri, direct 1010011i aaaaaaaa register address
+ MOV Rn, direct 10101rrr aaaaaaaa R0 .. R7 register address
+ ANL C, /bit 10110000 bbbbbbbb bit address
+ CPL bit 10110010 bbbbbbbb bit address
+ CPL C 10110011
+ CJNE A, #data, rel 10110100 dddddddd rrrrrrrr adat relative address
+ CJNE A, direct, rel 10110101 aaaaaaaa rrrrrrrr register address relative address
+ CJNE @Ri, #data, rel 1011011i dddddddd rrrrrrrr R0 .. R1 data relative address
+ CJNE Rn, #data, rel 10111rrr dddddddd rrrrrrrr R0 .. R7 data relative address
+ PUSH direct 11000000 aaaaaaaa register address
+ CLR bit 11000010 bbbbbbbb bit address
+ CLR C 11000011
+ SWAP A 11000100
+ XCH A, direct 11000101 aaaaaaaa register address
+ XCH A, @Ri 1100011i R0 .. R1
+ XCH A, Rn 11001rrr R0 .. R7
+ POP direct 11010000 aaaaaaaa register address
+ SETB bit 11010010 bbbbbbbb bit address
+ SETB C 11010011
+ DA A 11010100
+ DJNZ direct, rel 11010101 aaaaaaaa rrrrrrrr register address relative address
+ XCHD A, @Ri 1101011i R0 .. R1
+ DJNZ Rn, rel 11011rrr rrrrrrrr R0 .. R7 relative address
+ MOVX A, @DPTR 11100000
+ MOVX A, @Ri 1110001i R0 .. R1
+ CLR A 11100100
+ MOV A, direct 11100101 aaaaaaaa register address The "MOV A, ACC" invalid instruction.
+ MOV A, @Ri 1110011i R0 .. R1
+ MOV A, Rn 11101rrr R0 .. R7
+ MOVX @DPTR, A 11110000
+ MOVX @Ri, A 1111001i R0 .. R1
+ CPL A 11110100
+ MOV direct, A 11110101 aaaaaaaa register address
+ MOV @Ri, A 1111011i R0 .. R1
+ MOV Rn, A 11111rrr R0 .. R7
+=cut
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Expand a relative offset value.
+ #
+
+sub expand_offset($)
+ {
+ my $Offset = $_[0];
+
+ return ($Offset & 0x80) ? -(($Offset ^ 0xFF) + 1) : $Offset;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Finds address of branchs and procedures.
+ #
+
+sub label_finder($$)
+ {
+ my ($Address, $BlockRef) = @_;
+ my ($instr_size, $instr);
+ my ($addr, $instr_mask0, $instr_mask1, $instr_mask2);
+
+ $instr_size = $BlockRef->{SIZE};
+ $instr = $rom[$Address];
+
+ $instr_mask0 = $instr & 0x1F;
+ $instr_mask1 = $instr & 0xFE;
+ $instr_mask2 = $instr & 0xF8;
+
+ if ($instr_mask0 == INST_AJMP)
+ {
+ # AJMP addr11 aaa00001 aaaaaaaa a10 a9 a8 0 0 0 0 1 a7-a0
+
+ $addr = (($Address + $instr_size) & 0xF800) | (($instr & 0xE0) << 3) | $rom[$Address + 1];
+ add_jump_label($addr, '', BL_TYPE_LABEL, $Address, FALSE);
+ }
+ elsif ($instr_mask0 == INST_ACALL)
+ {
+ # ACALL addr11 aaa10001 aaaaaaaa a10 a9 a8 1 0 0 0 1 a7-a0
+
+ $addr = (($Address + $instr_size) & 0xF800) | (($instr & 0xE0) << 3) | $rom[$Address + 1];
+ add_func_label($addr, '', FALSE);
+ }
+ elsif ($instr_mask1 == INST_CJNE__Ri_DATA ||
+ $instr_mask2 == INST_CJNE_Rn_DATA)
+ {
+ # CJNE @Ri, #data, rel 1011011i dddddddd rrrrrrrr R0 .. R1 data relative address
+ # CJNE Rn, #data, rel 10111rrr dddddddd rrrrrrrr R0 .. R7 data relative address
+
+ $addr = $Address + $instr_size + expand_offset($rom[$Address + 2]);
+ add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
+ }
+ elsif ($instr_mask2 == INST_DJNZ_Rn)
+ {
+ # DJNZ Rn, rel 11011rrr rrrrrrrr R0 .. R7 relative address
+
+ $addr = $Address + $instr_size + expand_offset($rom[$Address + 1]);
+ add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
+ }
+ elsif ($instr == INST_LJMP)
+ {
+ # LJMP addr16 00000010 aaaaaaaa aaaaaaaa a15-a8 a7-a0 absolute address
+
+ $addr = ($rom[$Address + 1] << 8) | $rom[$Address + 2];
+ add_jump_label($addr, '', BL_TYPE_LABEL, $Address, FALSE);
+ }
+ elsif ($instr == INST_LCALL)
+ {
+ # LCALL addr16 00010010 aaaaaaaa aaaaaaaa a15-a8 a7-a0 absolute address
+
+ $addr = ($rom[$Address + 1] << 8) | $rom[$Address + 2];
+ add_func_label($addr, '', FALSE);
+ }
+ elsif ($instr == INST_JBC_BIT ||
+ $instr == INST_JB_BIT ||
+ $instr == INST_JNB_BIT ||
+ $instr == INST_CJNE_A_DATA ||
+ $instr == INST_CJNE_A_DIRECT ||
+ $instr == INST_DJNZ_DIRECT)
+ {
+ # JBC bit, rel 00010000 bbbbbbbb rrrrrrrr bit address relative address
+ # JB bit, rel 00100000 bbbbbbbb rrrrrrrr bit address relative address
+ # JNB bit, rel 00110000 bbbbbbbb rrrrrrrr bit address relative address
+ # CJNE A, #data, rel 10110100 dddddddd rrrrrrrr data relative address
+ # CJNE A, direct, rel 10110101 aaaaaaaa rrrrrrrr register address relative address
+ # DJNZ direct, rel 11010101 aaaaaaaa rrrrrrrr register address relative address
+
+ $addr = $Address + $instr_size + expand_offset($rom[$Address + 2]);
+ add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
+ }
+ elsif ($instr == INST_JC ||
+ $instr == INST_JNC ||
+ $instr == INST_JZ ||
+ $instr == INST_JNZ ||
+ $instr == INST_SJMP)
+ {
+ # JC rel 01000000 rrrrrrrr relative address
+ # JNC rel 01010000 rrrrrrrr relative address
+ # JZ rel 01100000 rrrrrrrr relative address
+ # JNZ rel 01110000 rrrrrrrr relative address
+ # SJMP rel 10000000 rrrrrrrr relative address
+
+ $addr = $Address + $instr_size + expand_offset($rom[$Address + 1]);
+ add_jump_label($addr, '', BL_TYPE_LABEL, EMPTY, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If exists a variable name wich belong to the $Address, then returns it.
+ # Otherwise, returns the address.
+ #
+
+sub reg_name($$)
+ {
+ my ($Address, $StrRef) = @_;
+ my ($ram, $str);
+
+ if ($Address <= BANK_LAST_ADDR)
+ {
+ # This register belongs to one of the register bank.
+
+ my $bank = ($Address >> 3) & 3;
+ my $reg = $Address & 7;
+
+ $str = ($gen_assembly_code) ? sprintf("0x%02X", $Address) : "R${reg}<#$bank>";
+ ${$StrRef} = $str;
+
+ if (defined($ram_names_by_address{$Address}))
+ {
+ my $var = $ram_names_by_address{$Address};
+
+ printf STDERR ("This address (0x%02X) belongs to two names: \"$str\" and \"$var\"\n", $Address);
+ }
+ }
+ elsif (defined($ram = $sfr_by_address{$Address}) && $ram->{NAME} ne '')
+ {
+ $str = $ram->{NAME};
+ ${$StrRef} = $str;
+ }
+ elsif (defined($ram = $ram_names_by_address{$Address}))
+ {
+ $str = sprintf "0x%02X", $Address;
+ ${$StrRef} = "[$str]";
+ $str = $ram;
+ }
+ else
+ {
+ $str = sprintf "0x%02X", $Address;
+ ${$StrRef} = "[$str]";
+ }
+
+ return $str;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If exists a XRAM name wich belong to the $Address, then returns it.
+ # Otherwise, returns the address.
+ #
+
+sub xram_name($$)
+ {
+ my ($Address, $StrRef) = @_;
+ my ($xram, $str);
+
+ $str = sprintf "0x%04X", $Address;
+ ${$StrRef} = $str;
+
+ $str = $xram if (defined($xram = $xram_names_by_address{$Address}));
+
+ return $str;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If exists a iRAM name wich belong to the $Address, then returns it.
+ # Otherwise, returns the address.
+ #
+
+sub iram_name($$)
+ {
+ my ($Address, $StrRef) = @_;
+ my ($ram, $str);
+
+ $str = sprintf "0x%02X", $Address;
+ ${$StrRef} = $str;
+ $ram = $ram_names_by_address{$Address};
+
+ $str = $ram if (defined($ram));
+
+ return $str;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If exists a bit name wich belong to the $Address, then returns it.
+ # Otherwise, returns the address.
+ #
+
+sub bit_name($$)
+ {
+ my ($Address, $StrRef) = @_;
+ my ($bit, $str);
+
+ if (defined($bit = $sfr_bit_by_address{$Address}) && $bit->{NAME} ne '')
+ {
+ $str = $bit->{NAME};
+ ${$StrRef} = $str;
+ }
+ elsif (defined($bit = $bit_by_address{$Address}) && $bit->{NAME} ne '')
+ {
+ $str = sprintf "0x%02X", $Address;
+ ${$StrRef} = "[$str]";
+ $str = $bit->{NAME};
+ }
+ else
+ {
+ $str = sprintf "0x%02X", $Address;
+ ${$StrRef} = "[$str]";
+ }
+
+ return $str;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If exists a label name wich belong to the $Address, then returns it.
+ # Otherwise, returns the address.
+ #
+
+sub labelname($)
+ {
+ my $Address = $_[0];
+ my $label = $labels_by_address{$Address};
+
+ return ((defined($label)) ? $label->{NAME} : (sprintf "0x%04X", $Address));
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Auxiliary procedure of prints.
+ #
+
+sub print_3($$$)
+ {
+ return if ($decoder_silent_level > SILENT0);
+
+ if ($no_explanations)
+ {
+ print(($_[1] ne '') ? "$_[0]\t$_[1]\n" : "$_[0]\n");
+ }
+ else
+ {
+ print "$_[0]\t" . align($_[1], EXPL_ALIGN_SIZE) . "; $_[2]\n";
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Invalidates the DPTR and the Rx registers.
+ #
+
+sub invalidate_DPTR_Rx()
+ {
+ $DPTR = EMPTY;
+ @R_regs[0 .. 7] = ((EMPTY) x 8);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Invalidates the DPTR or the Rx registers.
+ #
+
+sub invalidate_reg($)
+ {
+ my $Address = $_[0];
+
+ return if ($Address == EMPTY);
+
+ if ($Address == DPL || $Address == DPH)
+ {
+ $DPTR = EMPTY;
+ }
+ elsif ($Address <= BANK_LAST_ADDR)
+ {
+ $R_regs[$Address & 7] = EMPTY;
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Carries out the operations with the R registers.
+ #
+
+use constant Rx_INV => 0;
+use constant Rx_INC => 1;
+use constant Rx_DEC => 2;
+use constant Rx_MOV => 3;
+
+sub operation_R_reg
+ {
+ my $Rx = shift(@_);
+ my $Oper = shift(@_);
+ my $r;
+
+ if ($Oper == Rx_INV)
+ {
+ $R_regs[$Rx] = EMPTY;
+ }
+ elsif ($Oper == Rx_INC)
+ {
+ $r = $R_regs[$Rx];
+
+ if ($r != EMPTY)
+ {
+ ++$r;
+ $R_regs[$Rx] = $r & 0xFF;
+ return TRUE;
+ }
+ }
+ elsif ($Oper == Rx_DEC)
+ {
+ $r = $R_regs[$Rx];
+
+ if ($r != EMPTY)
+ {
+ --$r;
+ $R_regs[$Rx] = $r & 0xFF;
+ return TRUE;
+ }
+ }
+ elsif ($Oper == Rx_MOV)
+ {
+ $R_regs[$Rx] = shift(@_) & 0xFF;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If possible, returns the character.
+ #
+
+sub present_char($)
+ {
+ my $Ch = $_[0];
+
+ if ($Ch >= ord(' ') && $Ch < 0x7F)
+ {
+ return sprintf " {'%c'}", $Ch;
+ }
+ elsif (defined($control_characters{$Ch}))
+ {
+ return " {'$control_characters{$Ch}'}";
+ }
+
+ return '';
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Decodes value of the $Ch.
+ #
+
+sub decode_char($)
+ {
+ my $Ch = $_[0];
+
+ if ($Ch >= ord(' ') && $Ch < 0x7F)
+ {
+ return sprintf "'%c'", $Ch;
+ }
+ elsif (defined($control_characters{$Ch}))
+ {
+ return "'$control_characters{$Ch}'";
+ }
+
+ return sprintf "0x%02X", $Ch;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Determines direction of jump.
+ #
+
+sub jump_direction($)
+ {
+ my $TargetAddr = $_[0];
+
+ if ($dcd_address < $TargetAddr)
+ {
+ return ' (forward)';
+ }
+ elsif ($dcd_address == $TargetAddr)
+ {
+ return '';
+ }
+ else
+ {
+ return ' (backward)';
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Returns with TRUE if the instruction use indirect RAM addressing
+ # with @Ri.
+ #
+
+sub is_Ri_instr($)
+ {
+ my $Address = $_[0];
+ my $block;
+
+ $block = \%{$blocks_by_address{$Address}};
+ return FALSE if (! defined($block) || $block->{TYPE} != BLOCK_INSTR);
+
+ # These instructions use indirect RAM addressing with @Ri?
+ return defined($indirect_addr_instr{$rom[$Address] & 0xFE});
+ }
+
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@
+#@@@@@@@@@@@@@@@@@@@@@ These the instruction decoders. @@@@@@@@@@@@@@@@@@@@@
+#@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+sub ajmp()
+ {
+ my ($addr, $a11, $rb0, $rb1, $str0, $str1, $str2);
+
+ # AJMP addr11 aaa00001 aaaaaaaa a10 a9 a8 0 0 0 0 1 a7-a0
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb1 = (($dcd_instr & 0xE0) << 3) | $dcd_parm0;
+ $addr = (($dcd_address + $dcd_instr_size) & 0xF800) | $rb1;
+ $rb0 = labelname($addr);
+ $a11 = sprintf "0x%04X", $rb1;
+ $str0 = sprintf "0x%04X", $addr;
+
+ if ($dcd_address < $addr)
+ {
+ $str1 = '';
+ $str2 = ' (forward)';
+ }
+ elsif ($dcd_address == $addr)
+ {
+ $str1 = ' (endless loop)';
+ $str2 = '';
+ }
+ else
+ {
+ $str1 = '';
+ $str2 = ' (backward)';
+ }
+
+ print_3('ajmp', $rb0, "Jumps$str2 hither: $str0 (PC += $dcd_instr_size, PC(10-0) = $a11)$str1");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub acall()
+ {
+ my ($addr, $rb0, $rb1, $str0, $str1, $str2);
+
+ # ACALL addr11 aaa10001 aaaaaaaa a10 a9 a8 1 0 0 0 1 a7-a0
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb1 = (($dcd_instr & 0xE0) << 3) | $dcd_parm0;
+ $addr = (($dcd_address + $dcd_instr_size) & 0xF800) | $rb1;
+ $rb0 = labelname($addr);
+ $str0 = sprintf "0x%04X", $rb1;
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ print_3('acall', $rb0, "Calls$str2 this: $str1 (PC += $dcd_instr_size, [++SP] = PCL, [++SP] = PCH, PC(10-0) = $str0)");
+ }
+
+ invalidate_DPTR_Rx();
+ }
+
+#-------------------------------------------------------------------------------
+
+sub inc_ind_Ri()
+ {
+ # INC @Ri 0000011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('inc', "\@$dcd_Ri_name", "++[$dcd_Ri_name]");
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub dec_ind_Ri()
+ {
+ # DEC @Ri 0001011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('dec', "\@$dcd_Ri_name", "--[$dcd_Ri_name]");
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub add_A_ind_Ri()
+ {
+ # ADD A, @Ri 0010011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('add', "A, \@$dcd_Ri_name", "ACC += [$dcd_Ri_name]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub addc_A_ind_Ri()
+ {
+ # ADDC A, @Ri 0011011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('addc', "A, \@$dcd_Ri_name", "ACC += [$dcd_Ri_name] + CY");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_A_ind_Ri()
+ {
+ # ORL A, @Ri 0100011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('orl', "A, \@$dcd_Ri_name", "ACC |= [$dcd_Ri_name]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_A_ind_Ri()
+ {
+ # ANL A, @Ri 0101011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('anl', "A, \@$dcd_Ri_name", "ACC &= [$dcd_Ri_name]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xrl_A_ind_Ri()
+ {
+ # XRL A, @Ri 0110011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('xrl', "A, \@$dcd_Ri_name", "ACC ^= [$dcd_Ri_name]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_ind_Ri_data()
+ {
+ my ($rb, $str);
+
+ # MOV @Ri, #data 0111011i dddddddd data
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('mov', "\@$dcd_Ri_name, #$rb", "[$dcd_Ri_name] = $rb$str");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($i, '', RAM_TYPE_IND, FALSE);
+ }
+
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_direct_ind_Ri()
+ {
+ my ($rb, $name);
+
+ # MOV direct, @Ri 1000011i aaaaaaaa R0 .. R1 register address
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('mov', "$rb, \@$dcd_Ri_name", "$name = [$dcd_Ri_name]");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ add_ram($i, '', RAM_TYPE_IND, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub subb_A_ind_Ri()
+ {
+ # SUBB A, @Ri 1001011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('subb', "A, \@$dcd_Ri_name", "ACC -= [$dcd_Ri_name] + CY");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_ind_Ri_direct()
+ {
+ my ($rb, $name);
+
+ # MOV @Ri, direct 1010011i aaaaaaaa register address
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('mov', "\@$dcd_Ri_name, $rb", "[$dcd_Ri_name] = $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ add_ram($i, '', RAM_TYPE_IND, FALSE);
+ }
+
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cjne_ind_Ri_data()
+ {
+ my ($addr, $rb, $str0, $str1, $str2, $str3);
+
+ # CJNE @Ri, #data, rel 1011011i dddddddd rrrrrrrr R0 .. R1 data relative address
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%02X", $dcd_parm0;
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ $str3 = present_char($dcd_parm0);
+ print_3('cjne', "\@$dcd_Ri_name, #$str0, $rb", "If ([$dcd_Ri_name] != $str0$str3) then jumps$str2 hither: $str1");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($i, '', RAM_TYPE_IND, FALSE);
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xch_A_ind_Ri()
+ {
+ # XCH A, @Ri 1100011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('xch', "A, \@$dcd_Ri_name", "ACC <-> [$dcd_Ri_name]");
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xchd_A_ind_Ri()
+ {
+ # XCHD A, @Ri 1101011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('xchd', "A, \@$dcd_Ri_name", "ACC(3-0) <-> [$dcd_Ri_name](3-0)");
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub movx_A_ind_Ri()
+ {
+ # MOVX A, @Ri 1110001i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_xram($i, '', FALSE) if ($i != EMPTY && $decoder_silent_level == SILENT1);
+
+ print_3('movx', "A, \@$dcd_Ri_name", "ACC = XRAM[$dcd_Ri_name]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_A_ind_Ri()
+ {
+ # MOV A, @Ri 1110011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('mov', "A, \@$dcd_Ri_name", "ACC = [$dcd_Ri_name]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub movx_ind_Ri_A()
+ {
+ # MOVX @Ri, A 1111001i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_xram($i, '', FALSE) if ($i != EMPTY && $decoder_silent_level == SILENT1);
+
+ print_3('movx', "\@$dcd_Ri_name, A", "XRAM[$dcd_Ri_name] = ACC");
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_ind_Ri_A()
+ {
+ # MOV @Ri, A 1111011i R0 .. R1
+
+ my $i = $R_regs[$dcd_Ri_regs];
+
+ add_ram($i, '', RAM_TYPE_IND, FALSE) if ($decoder_silent_level == SILENT1);
+
+ print_3('mov', "\@$dcd_Ri_name, A", "[$dcd_Ri_name] = ACC");
+ invalidate_reg($i);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub inc_Rn()
+ {
+ my $str = '';
+
+ # INC Rn 00001rrr R0 .. R7
+
+ if (operation_R_reg($dcd_Rn_regs, Rx_INC))
+ {
+ $str = sprintf " (0x%02X)", $R_regs[$dcd_Rn_regs];
+ }
+
+ print_3('inc', $dcd_Rn_name, "++$dcd_Rn_name$str");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub dec_Rn()
+ {
+ my $str = '';
+
+ # DEC Rn 00011rrr R0 .. R7
+
+ if (operation_R_reg($dcd_Rn_regs, Rx_DEC))
+ {
+ $str = sprintf " (0x%02X)", $R_regs[$dcd_Rn_regs];
+ }
+
+ print_3('dec', $dcd_Rn_name, "--$dcd_Rn_name$str");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub add_A_Rn()
+ {
+ # ADD A, Rn 00101rrr R0 .. R7
+
+ print_3('add', "A, $dcd_Rn_name", "ACC += $dcd_Rn_name");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub addc_A_Rn()
+ {
+ # ADDC A, Rn 00111rrr R0 .. R7
+
+ print_3('addc', "A, $dcd_Rn_name", "ACC += $dcd_Rn_name + CY");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_A_Rn()
+ {
+ # ORL A, Rn 01001rrr R0 .. R7
+
+ print_3('orl', "A, $dcd_Rn_name", "ACC |= $dcd_Rn_name");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_A_Rn()
+ {
+ # ANL A, Rn 01011rrr R0 .. R7
+
+ print_3('anl', "A, $dcd_Rn_name", "ACC &= $dcd_Rn_name");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xrl_A_Rn()
+ {
+ # XRL A, Rn 01101rrr R0 .. R7
+
+ print_3('xrl', "A, $dcd_Rn_name", "ACC ^= $dcd_Rn_name");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_Rn_data()
+ {
+ my ($rb, $name, $str);
+
+ # MOV Rn, #data 01111rrr dddddddd R0 .. R7 data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ if (($dcd_Rn_regs == 0 || $dcd_Rn_regs == 1) && is_Ri_instr($dcd_address + $dcd_instr_size))
+ {
+ $rb = iram_name($dcd_parm0, \$name);
+ print_3('mov', "$dcd_Rn_name, #$rb", "$dcd_Rn_name = $name");
+ }
+ else
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('mov', "$dcd_Rn_name, #$rb", "$dcd_Rn_name = $rb$str");
+ }
+ }
+
+ operation_R_reg($dcd_Rn_regs, Rx_MOV, $dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_direct_Rn()
+ {
+ my ($rb, $name);
+
+ # MOV direct, Rn 10001rrr aaaaaaaa R0 .. R7 register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('mov', "$rb, $dcd_Rn_name", "$name = $dcd_Rn_name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub subb_A_Rn()
+ {
+ # SUBB A, Rn 10011rrr R0 .. R7
+
+ print_3('subb', "A, $dcd_Rn_name", "ACC -= $dcd_Rn_name + CY");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_Rn_direct()
+ {
+ my ($rb, $name);
+
+ # MOV Rn, direct 10101rrr aaaaaaaa R0 .. R7 register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('mov', "$dcd_Rn_name, $rb", "$dcd_Rn_name = $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ operation_R_reg($dcd_Rn_regs, Rx_INV);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cjne_Rn_data()
+ {
+ my ($addr, $rb, $str0, $str1, $str2, $str3);
+
+ # CJNE Rn, #data, rel 10111rrr dddddddd rrrrrrrr R0 .. R7 data relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%02X", $dcd_parm0;
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ $str3 = present_char($dcd_parm0);
+ print_3('cjne', "$dcd_Rn_name, #$str0, $rb", "If ($dcd_Rn_name != $str0$str3) then jumps$str2 hither: $str1");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xch_A_Rn()
+ {
+ # XCH A, Rn 11001rrr R0 .. R7
+
+ print_3('xch', "A, $dcd_Rn_name", "ACC <-> $dcd_Rn_name");
+ operation_R_reg($dcd_Rn_regs, Rx_INV);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub djnz_Rn()
+ {
+ my ($addr, $rb, $str0, $str1, $str2);
+
+ # DJNZ Rn, rel 11011rrr rrrrrrrr R0 .. R7 relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm0);
+ $rb = labelname($addr);
+ $str0 = ($dcd_address == $addr) ? ' (waiting loop)' : '';
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ print_3('djnz', "$dcd_Rn_name, $rb", "If (--$dcd_Rn_name != 0) then jumps$str2 hither: $str1$str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_A_Rn()
+ {
+ # MOV A, Rn 11101rrr R0 .. R7
+
+ print_3('mov', "A, $dcd_Rn_name", "ACC = $dcd_Rn_name");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_Rn_A()
+ {
+ # MOV Rn, A 11111rrr R0 .. R7
+
+ print_3('mov', "$dcd_Rn_name, A", "$dcd_Rn_name = ACC");
+ operation_R_reg($dcd_Rn_regs, Rx_INV);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub nop()
+ {
+ # NOP 00000000
+ print "nop\n" if ($decoder_silent_level == SILENT0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub ljmp()
+ {
+ my ($addr, $rb, $str0, $str1, $str2);
+
+ # LJMP addr16 00000010 aaaaaaaa aaaaaaaa a15-a8 a7-a0 absolute address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = ($dcd_parm0 << 8) | $dcd_parm1;
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+
+ if ($dcd_address < $addr)
+ {
+ $str1 = '';
+ $str2 = ' (forward)';
+ }
+ elsif ($dcd_address == $addr)
+ {
+ $str1 = ' (endless loop)';
+ $str2 = '';
+ }
+ else
+ {
+ $str1 = '';
+ $str2 = ' (backward)';
+ }
+
+ print_3('ljmp', $rb, "Jumps$str2 hither: $str0$str1");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub rr_A()
+ {
+ # RR A 00000011
+
+ print_3('rr', 'A', 'ACC[76543210] = ACC[07654321]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub inc_A()
+ {
+ # INC A 00000100
+
+ print_3('inc', 'A', '++ACC');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub inc_direct()
+ {
+ my ($rb, $name);
+
+ # INC direct 00000101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('inc', $rb, "++$name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jbc_bit()
+ {
+ my ($addr, $rb0, $rb1, $name0, $str0, $str1);
+
+ # JBC bit, rel 00100000 bbbbbbbb rrrrrrrr bit address relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb0 = bit_name($dcd_parm0, \$name0);
+ $rb1 = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ print_3('jbc', "$rb0, $rb1", "If ($name0 == H) then $name0 = L and jumps$str1 hither: $str0");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub lcall()
+ {
+ my ($addr, $rb, $str0, $str1);
+
+ # LCALL addr16 00010010 aaaaaaaa aaaaaaaa a15-a8 a7-a0 absolute address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = ($dcd_parm0 << 8) | $dcd_parm1;
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ print_3('lcall', $rb, "Calls$str1 this: $str0 (PC += $dcd_instr_size, [++SP] = PCL, [++SP] = PCH, PC = $str0)");
+ }
+
+ invalidate_DPTR_Rx();
+ }
+
+#-------------------------------------------------------------------------------
+
+sub rrc_A()
+ {
+ # RRC A 00010011
+
+ print_3('rrc', 'A', 'ACC[76543210] = ACC[C7654321], CY = ACC[0]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub dec_A()
+ {
+ # DEC A 00010100
+
+ print_3('dec', 'A', '--ACC');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub dec_direct()
+ {
+ my ($rb, $name);
+
+ # DEC direct 00010101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('dec', $rb, "--$name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jb_bit()
+ {
+ my ($addr, $rb0, $rb1, $name0, $str0, $str1, $str2);
+
+ # JB bit, rel 00100000 bbbbbbbb rrrrrrrr bit address relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb0 = bit_name($dcd_parm0, \$name0);
+ $rb1 = labelname($addr);
+ $str0 = ($dcd_address == $addr) ? ' (waiting loop)' : '';
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ print_3('jb', "$rb0, $rb1", "If ($name0 == H) then jumps$str2 hither: $str1$str0");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub ret()
+ {
+ # RET 00100010
+
+ print_3('ret', '', 'PCH = [SP--], PCL = [SP--]');
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub rl_A()
+ {
+ # RL A 00100011
+
+ print_3('rl', 'A', 'ACC[76543210] = ACC[65432107]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub add_A_data()
+ {
+ my ($rb, $str);
+
+ # ADD A, #data 00100100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('add', "A, #$rb", "ACC += $rb$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub add_A_direct()
+ {
+ my ($rb, $name);
+
+ # ADD A, direct 00100101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('add', "A, $rb", "ACC += $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jnb_bit()
+ {
+ my ($addr, $rb0, $rb1, $name0, $str0, $str1, $str2);
+
+ # JNB bit, rel 00110000 bbbbbbbb rrrrrrrr bit address relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb0 = bit_name($dcd_parm0, \$name0);
+ $rb1 = labelname($addr);
+ $str0 = ($dcd_address == $addr) ? ' (waiting loop)' : '';
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ print_3('jnb', "$rb0, $rb1", "If ($name0 == L) then jumps$str2 hither: $str1$str0");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub reti()
+ {
+ # RETI 00110010
+
+ print_3('reti', '', 'PCH = [SP--], PCL = [SP--]');
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub rcl_A()
+ {
+ # RLC A 00110011
+
+ print_3('rlc', 'A', 'ACC[76543210] = ACC[6543210C], CY = ACC[7]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub addc_A_data()
+ {
+ my ($rb, $str);
+
+ # ADDC A, #data 00110100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('addc', "A, #$rb", "ACC += $rb + CY$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub addc_A_direct()
+ {
+ my ($rb, $name);
+
+ # ADDC A, direct 00110101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('addc', "A, $rb", "ACC += $name + CY");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jc()
+ {
+ my ($addr, $rb, $str0, $str1);
+
+ # JC rel 01000000 rrrrrrrr relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm0);
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ print_3('jc', $rb, "If (CY == H) then jumps$str1 hither: $str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_direct_A()
+ {
+ my ($rb, $name);
+
+ # ORL direct, A 01000010 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('orl', "$rb, A", "$name |= ACC");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_direct_data()
+ {
+ my ($rb0, $rb1, $name, $str);
+
+ # ORL direct, #data 01000011 aaaaaaaa dddddddd register address data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb0 = reg_name($dcd_parm0, \$name);
+ $rb1 = sprintf "0x%02X", $dcd_parm1;
+ $str = present_char($dcd_parm1);
+ }
+
+ if ($dcd_parm0 == PSW)
+ {
+ my $bank = ($dcd_parm1 >> 3) & 0x03;
+
+ $used_banks{$bank} = TRUE;
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $str = " (select bank #$bank)" if (($dcd_parm1 & ~0x18) == 0x00);
+ }
+ }
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ print_3('orl', "$rb0, #$rb1", "$name |= $rb1$str");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_A_data()
+ {
+ my ($rb, $str);
+
+ # ORL A, #data 01000100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('orl', "A, #$rb", "ACC |= $rb$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_A_direct()
+ {
+ my ($rb, $name);
+
+ # ORL A, direct 01000101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('orl', "A, $rb", "ACC |= $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jnc()
+ {
+ my ($addr, $rb, $str0, $str1);
+
+ # JNC rel 01010000 rrrrrrrr relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm0);
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ print_3('jnc', $rb, "If (CY == L) then jumps$str1 hither: $str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_direct_A()
+ {
+ my ($rb, $name);
+
+ # ANL direct, A 01010010 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('anl', "$rb, A", "$name &= ACC");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_direct_data()
+ {
+ my ($rb0, $rb1, $name, $str);
+
+ # ANL direct, #data 01010011 aaaaaaaa dddddddd register address data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb0 = reg_name($dcd_parm0, \$name);
+ $rb1 = sprintf "0x%02X", $dcd_parm1;
+ $str = present_char($dcd_parm1);
+ print_3('anl', "$rb0, #$rb1", "$name &= $rb1$str");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_A_data()
+ {
+ my ($rb, $str);
+
+ # ANL A, #data 01010100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('anl', "A, #$rb", "ACC &= $rb$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_A_direct()
+ {
+ my ($rb, $name);
+
+ # ANL A, direct 01010101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('anl', "A, $rb", "ACC &= $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jz()
+ {
+ my ($addr, $rb, $str0, $str1);
+
+ # JZ rel 01100000 rrrrrrrr relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm0);
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ print_3('jz', $rb, "If (ACC == 0) then jumps$str1 hither: $str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xrl_direct_A()
+ {
+ my ($rb, $name);
+
+ # XRL direct, A 01100010 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('xrl', "$rb, A", "$name ^= ACC");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xrl_direct_data()
+ {
+ my ($rb0, $rb1, $name, $str);
+
+ # XRL direct, #data 01100011 aaaaaaaa dddddddd register address data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb0 = reg_name($dcd_parm0, \$name);
+ $rb1 = sprintf "0x%02X", $dcd_parm1;
+ $str = present_char($dcd_parm1);
+ print_3('xrl', "$rb0, #$rb1", "$name |= $rb1$str");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xrl_A_data()
+ {
+ my ($rb, $str);
+
+ # XRL A, #data 01100100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('xrl', "A, #$rb", "ACC ^= $rb$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xrl_A_direct()
+ {
+ my ($rb, $name);
+
+ # XRL A, direct 01100101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('xrl', "A, $rb", "ACC |= $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jnz()
+ {
+ my ($addr, $rb, $str0, $str1);
+
+ # JNZ rel 01110000 rrrrrrrr relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm0);
+ $rb = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ print_3('jnz', $rb, "If (ACC != 0) then jumps$str1 hither: $str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_C_bit()
+ {
+ my ($rb, $name);
+
+ # ORL C, bit 01110010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('orl', "C, $rb", "CY |= $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub jmp_A_DPTR()
+ {
+ my $str;
+
+ # JMP @A+DPTR 01110011
+
+ if ($DPTR != EMPTY)
+ {
+ add_jump_label($DPTR, '', BL_TYPE_JTABLE, EMPTY, FALSE);
+ $str = jump_direction($DPTR);
+ }
+ else
+ {
+ $str = '';
+ }
+
+ print_3('jmp', '@A+DPTR', "Jumps$str hither: [DPTR + ACC]");
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_A_data()
+ {
+ my ($rb, $str);
+
+ # MOV A, #data 01110100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('mov', "A, #$rb", "ACC = $rb$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_direct_data()
+ {
+ my ($rb0, $rb1, $name, $str);
+
+ # MOV direct, #data 01110101 aaaaaaaa dddddddd register address data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb0 = reg_name($dcd_parm0, \$name);
+ $rb1 = sprintf "0x%02X", $dcd_parm1;
+ $str = '';
+ }
+
+ if ($dcd_parm0 == PSW)
+ {
+ my $bank = ($dcd_parm1 >> 3) & 0x03;
+
+ $used_banks{$bank} = TRUE;
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $str = " (select bank #$bank)" if (($dcd_parm1 & ~0x18) == 0x00);
+ }
+ }
+# elsif ($dcd_parm0 == SP)
+# {
+# $stack_start = ($dcd_parm1 + 1) if ($stack_start < 0);
+# }
+ else
+ {
+ $str = present_char($dcd_parm1);
+ }
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ print_3('mov', "$rb0, #$rb1", "$name = $rb1$str");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub sjmp()
+ {
+ my ($addr, $rb0, $rb1, $name0, $name1, $str0, $str1, $str2);
+
+ # SJMP rel 10000000 rrrrrrrr relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm0);
+ $rb0 = labelname($addr);
+ $str0 = ($dcd_address == $addr) ? ' (endless loop)' : '';
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ print_3('sjmp', $rb0, "Jumps$str2 hither: $str1$str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_C_bit()
+ {
+ my ($rb, $name);
+
+ # ANL C, bit 10000010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('anl', "C, $rb", "CY &= $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub movc_A_A_PC()
+ {
+ # MOVC A, @A+PC 10000011
+
+ print_3('movc', 'A, @A+PC', "ACC = ROM[PC + $dcd_instr_size + ACC]");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub div_AB()
+ {
+ # DIV AB 10000100
+
+ print_3('div', 'AB', 'ACC = ACC / B, B = ACC % B');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_direct_direct()
+ {
+ my ($rb0, $rb1, $name0, $name1);
+
+ # MOV direct, direct 10000101 aaaaaaaa aaaaaaaa forrás reg. cél reg.
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb0 = reg_name($dcd_parm0, \$name0);
+ $rb1 = reg_name($dcd_parm1, \$name1);
+ print_3('mov', "$rb1, $rb0", "$name1 = $name0");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ add_ram($dcd_parm1, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_DPTR_data()
+ {
+ my ($addr, $str, $name);
+
+ # MOV DPTR, #data16 10010000 dddddddd dddddddd d15-d8 d7-d0
+
+ $addr = ($dcd_parm0 << 8) | $dcd_parm1;
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $str = xram_name($addr, \$name);
+ print_3('mov', "DPTR, #$str", "DPTR = $name");
+ }
+
+ $DPTR = $addr;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_bit_C()
+ {
+ my ($rb, $name);
+
+ # MOV bit, C 10010010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('mov', "$rb, C", "$name = CY");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub movc_A_A_DPTR()
+ {
+ # MOVC A, @A+DPTR 10010011
+
+ add_jump_label($DPTR, '', BL_TYPE_LABEL, EMPTY, FALSE) if ($DPTR != EMPTY);
+
+ print_3('movc', 'A, @A+DPTR', 'ACC = ROM[DPTR + ACC]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub subb_A_data()
+ {
+ my ($rb, $str);
+
+ # SUBB A, #data 10010100 dddddddd data
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0;
+ $str = present_char($dcd_parm0);
+ print_3('subb', "A, #$rb", "ACC -= $rb + CY$str");
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub subb_A_direct()
+ {
+ my ($rb, $name);
+
+ # SUBB A, direct 10010101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('subb', "A, $rb", "ACC -= $name + CY");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub orl_C__bit()
+ {
+ my ($rb, $name);
+
+ # ORL C, /bit 10100000 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('orl', "C, /$rb", "CY = ~$name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_C_bit()
+ {
+ my ($rb, $name);
+
+ # MOV C, bit 10100010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('mov', "C, $rb", "CY = $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub inc_DPTR()
+ {
+ my $str;
+
+ # INC DPTR 10100011
+
+ if ($DPTR != EMPTY)
+ {
+ ++$DPTR;
+ $str = sprintf " (0x%04X)", $DPTR;
+ }
+ else
+ {
+ $str = '';
+ }
+
+ print_3('inc', 'DPTR', "++DPTR$str");
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mul_AB()
+ {
+ # MUL AB 10100100
+
+ print_3('mul', 'AB', 'B:ACC = ACC * B');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub unknown()
+ {
+ my ($rb, $str0, $str1);
+
+ # The unknown instruction is actually simple embedded data in the code.
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = sprintf "0x%02X", $dcd_instr & 0xFF;
+ $str0 = present_char($dcd_instr);
+ $str1 = sprintf "0x%04X", $dcd_address;
+ print_3('.db', $rb, "$str1: $rb$str0");
+
+ if ($dcd_instr_size == 2)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm0 & 0xFF;
+ $str0 = present_char($dcd_parm0);
+ $str1 = sprintf "0x%04X", $dcd_address;
+ print_3('.db', $rb, "$str1: $rb$str0");
+ }
+ elsif ($dcd_instr_size == 3)
+ {
+ $rb = sprintf "0x%02X", $dcd_parm1 & 0xFF;
+ $str0 = present_char($dcd_parm1);
+ $str1 = sprintf "0x%04X", $dcd_address;
+ print_3('.db', $rb, "$str1: $rb$str0");
+ }
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub anl_C__bit()
+ {
+ my ($rb, $name);
+
+ # ANL C, /bit 10110000 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('anl', "C, /$rb", "CY &= ~$name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cpl_bit()
+ {
+ my ($rb, $name);
+
+ # CPL bit 10110010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('cpl', $rb, "$name = ~$name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cpl_C()
+ {
+ # CPL C 10110011
+
+ print_3('cpl', 'C', 'CY = ~CY');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cjne_A_data()
+ {
+ my ($addr, $rb0, $rb1, $str0, $str1, $str2);
+
+ # CJNE A, #data, rel 10110100 dddddddd rrrrrrrr data relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb0 = labelname($addr);
+ $rb1 = sprintf "0x%02X", $dcd_parm0;
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = jump_direction($addr);
+ $str2 = present_char($dcd_parm0);
+ print_3('cjne', "A, #$rb1, $rb0", "If (ACC != $rb1$str2) then jumps$str1 hither: $str0");
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cjne_A_direct()
+ {
+ my ($addr, $rb0, $rb1, $name, $str0, $str1, $str2);
+
+ # CJNE A, direct, rel 10110101 aaaaaaaa rrrrrrrr register address relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb0 = reg_name($dcd_parm0, \$name);
+ $rb1 = labelname($addr);
+ $str0 = sprintf "0x%04X", $addr;
+ $str1 = ($dcd_address == $addr) ? ' (waiting loop)' : '';
+ $str2 = jump_direction($addr);
+ print_3('cjne', "A, $rb0, $rb1", "If (ACC != $name) then jumps$str2 hither: $str0$str1");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub push_direct()
+ {
+ my ($rb, $name);
+
+ # PUSH direct 11000000 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('push', $rb, "++SP, [SP] = $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub clr_bit()
+ {
+ my ($rb, $name);
+
+ # CLR bit 11000010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('clr', $rb, "$name = L");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub clr_C()
+ {
+ # CLR C 11000011
+
+ print_3('clr', 'C', 'CY = L');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub swap_A()
+ {
+ # SWAP A 11000100
+
+ print_3('swap', 'A', 'ACC[76543210] = ACC[32107654]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub xch_A_direct()
+ {
+ my ($rb, $name);
+
+ # XCH A, direct 11000101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('xch', "A, $rb", "ACC <-> $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub pop_direct()
+ {
+ my ($rb, $name);
+
+ # POP direct 11010000 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('pop', $rb, "$name = [SP], --SP");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+sub setb_bit()
+ {
+ my ($rb, $name);
+
+ # SETB bit 11010010 bbbbbbbb bit address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = bit_name($dcd_parm0, \$name);
+ print_3('setb', $rb, "$name = H");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_bit($dcd_parm0, '', FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub setb_C()
+ {
+ # SETB C 11010011
+
+ print_3('setb', 'C', 'CY = H');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub da_A()
+ {
+ # DA A 11010100
+
+ print_3('da', 'A', 'Decimal adjust the ACC.');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub djnz_direct()
+ {
+ my ($addr, $rb0, $rb1, $name, $str0, $str1, $str2);
+
+ # DJNZ direct, rel 11010101 aaaaaaaa rrrrrrrr register address relative address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $addr = $dcd_address + $dcd_instr_size + expand_offset($dcd_parm1);
+ $rb0 = reg_name($dcd_parm0, \$name);
+ $rb1 = labelname($addr);
+ $str0 = ($dcd_address == $addr) ? ' (waiting loop)' : '';
+ $str1 = sprintf "0x%04X", $addr;
+ $str2 = jump_direction($addr);
+ print_3('djnz', "$rb0, $rb1", "If (--$name != 0) then jumps$str2 hither: $str1$str0");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_DPTR_Rx();
+ $prev_is_jump = TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+sub movx_A_DPTR()
+ {
+ # MOVX A, @DPTR 11100000
+
+ add_xram($DPTR, '', FALSE) if ($DPTR != EMPTY && $decoder_silent_level == SILENT1);
+
+ print_3('movx', 'A, @DPTR', 'ACC = XRAM[DPTR]');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub clr_A()
+ {
+ # CLR A 11100100
+
+ print_3('clr', 'A', 'ACC = 0');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_A_direct()
+ {
+ my ($rb, $name);
+
+ # MOV A, direct 11100101 aaaaaaaa register address The "MOV A, ACC" invalid instruction.
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('mov', "A, $rb", "ACC = $name");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+sub movx_DPTR_A()
+ {
+ # MOVX @DPTR, A 11110000
+
+ add_xram($DPTR, '', FALSE) if ($DPTR != EMPTY && $decoder_silent_level == SILENT1);
+
+ print_3('movx', '@DPTR, A', 'XRAM[DPTR] = ACC');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub cpl_A()
+ {
+ # CPL A 11110100
+
+ print_3('cpl', 'A', 'ACC = ~ACC');
+ }
+
+#-------------------------------------------------------------------------------
+
+sub mov_direct_A()
+ {
+ my ($rb, $name);
+
+ # MOV direct, A 11110101 aaaaaaaa register address
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ $rb = reg_name($dcd_parm0, \$name);
+ print_3('mov', "$rb, A", "$name = ACC");
+ }
+ elsif ($decoder_silent_level == SILENT1)
+ {
+ add_ram($dcd_parm0, '', RAM_TYPE_DIR, FALSE);
+ }
+
+ invalidate_reg($dcd_parm0);
+ }
+
+#-------------------------------------------------------------------------------
+
+my @instr_decoders =
+ (
+ # 0x00 - 0x0F
+ \&nop,
+ \&ajmp,
+ \&ljmp,
+ \&rr_A,
+ \&inc_A,
+ \&inc_direct,
+ \&inc_ind_Ri,
+ \&inc_ind_Ri,
+ \&inc_Rn,
+ \&inc_Rn,
+ \&inc_Rn,
+ \&inc_Rn,
+ \&inc_Rn,
+ \&inc_Rn,
+ \&inc_Rn,
+ \&inc_Rn,
+
+ # 0x10 - 0x1F
+ \&jbc_bit,
+ \&acall,
+ \&lcall,
+ \&rrc_A,
+ \&dec_A,
+ \&dec_direct,
+ \&dec_ind_Ri,
+ \&dec_ind_Ri,
+ \&dec_Rn,
+ \&dec_Rn,
+ \&dec_Rn,
+ \&dec_Rn,
+ \&dec_Rn,
+ \&dec_Rn,
+ \&dec_Rn,
+ \&dec_Rn,
+
+ # 0x20 - 0x2F
+ \&jb_bit,
+ \&ajmp,
+ \&ret,
+ \&rl_A,
+ \&add_A_data,
+ \&add_A_direct,
+ \&add_A_ind_Ri,
+ \&add_A_ind_Ri,
+ \&add_A_Rn,
+ \&add_A_Rn,
+ \&add_A_Rn,
+ \&add_A_Rn,
+ \&add_A_Rn,
+ \&add_A_Rn,
+ \&add_A_Rn,
+ \&add_A_Rn,
+
+ # 0x30 - 0x3F
+ \&jnb_bit,
+ \&acall,
+ \&reti,
+ \&rcl_A,
+ \&addc_A_data,
+ \&addc_A_direct,
+ \&addc_A_ind_Ri,
+ \&addc_A_ind_Ri,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+ \&addc_A_Rn,
+
+ # 0x40 - 0x4F
+ \&jc,
+ \&ajmp,
+ \&orl_direct_A,
+ \&orl_direct_data,
+ \&orl_A_data,
+ \&orl_A_direct,
+ \&orl_A_ind_Ri,
+ \&orl_A_ind_Ri,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+ \&orl_A_Rn,
+
+ # 0x50 - 0x5F
+ \&jnc,
+ \&acall,
+ \&anl_direct_A,
+ \&anl_direct_data,
+ \&anl_A_data,
+ \&anl_A_direct,
+ \&anl_A_ind_Ri,
+ \&anl_A_ind_Ri,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+ \&anl_A_Rn,
+
+ # 0x60 - 0x6F
+ \&jz,
+ \&ajmp,
+ \&xrl_direct_A,
+ \&xrl_direct_data,
+ \&xrl_A_data,
+ \&xrl_A_direct,
+ \&xrl_A_ind_Ri,
+ \&xrl_A_ind_Ri,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+ \&xrl_A_Rn,
+
+ # 0x70 - 0x7F
+ \&jnz,
+ \&acall,
+ \&orl_C_bit,
+ \&jmp_A_DPTR,
+ \&mov_A_data,
+ \&mov_direct_data,
+ \&mov_ind_Ri_data,
+ \&mov_ind_Ri_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+ \&mov_Rn_data,
+
+ # 0x80 - 0x8F
+ \&sjmp,
+ \&ajmp,
+ \&anl_C_bit,
+ \&movc_A_A_PC,
+ \&div_AB,
+ \&mov_direct_direct,
+ \&mov_direct_ind_Ri,
+ \&mov_direct_ind_Ri,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+ \&mov_direct_Rn,
+
+ # 0x90 - 0x9F
+ \&mov_DPTR_data,
+ \&acall,
+ \&mov_bit_C,
+ \&movc_A_A_DPTR,
+ \&subb_A_data,
+ \&subb_A_direct,
+ \&subb_A_ind_Ri,
+ \&subb_A_ind_Ri,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+ \&subb_A_Rn,
+
+ # 0xA0 - 0xAF
+ \&orl_C__bit,
+ \&ajmp,
+ \&mov_C_bit,
+ \&inc_DPTR,
+ \&mul_AB,
+ \&unknown,
+ \&mov_ind_Ri_direct,
+ \&mov_ind_Ri_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+ \&mov_Rn_direct,
+
+ # 0xB0 - 0xBF
+ \&anl_C__bit,
+ \&acall,
+ \&cpl_bit,
+ \&cpl_C,
+ \&cjne_A_data,
+ \&cjne_A_direct,
+ \&cjne_ind_Ri_data,
+ \&cjne_ind_Ri_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+ \&cjne_Rn_data,
+
+ # 0xC0 - 0xCF
+ \&push_direct,
+ \&ajmp,
+ \&clr_bit,
+ \&clr_C,
+ \&swap_A,
+ \&xch_A_direct,
+ \&xch_A_ind_Ri,
+ \&xch_A_ind_Ri,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+ \&xch_A_Rn,
+
+ # 0xD0 - 0xDF
+ \&pop_direct,
+ \&acall,
+ \&setb_bit,
+ \&setb_C,
+ \&da_A,
+ \&djnz_direct,
+ \&xchd_A_ind_Ri,
+ \&xchd_A_ind_Ri,
+ \&djnz_Rn,
+ \&djnz_Rn,
+ \&djnz_Rn,
+ \&djnz_Rn,
+ \&djnz_Rn,
+ \&djnz_Rn,
+ \&djnz_Rn,
+ \&djnz_Rn,
+
+ # 0xE0 - 0xEF
+ \&movx_A_DPTR,
+ \&ajmp,
+ \&movx_A_ind_Ri,
+ \&movx_A_ind_Ri,
+ \&clr_A,
+ \&mov_A_direct,
+ \&mov_A_ind_Ri,
+ \&mov_A_ind_Ri,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+ \&mov_A_Rn,
+
+ # 0xF0 - 0xFF
+ \&movx_DPTR_A,
+ \&acall,
+ \&movx_ind_Ri_A,
+ \&movx_ind_Ri_A,
+ \&cpl_A,
+ \&mov_direct_A,
+ \&mov_ind_Ri_A,
+ \&mov_ind_Ri_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A,
+ \&mov_Rn_A
+ );
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Decodes the $BlockRef.
+ #
+
+sub instruction_decoder($$)
+ {
+ my ($Address, $BlockRef) = @_;
+ my ($label, $invalid);
+
+ $dcd_address = $Address;
+ $dcd_instr_size = $BlockRef->{SIZE};
+ $dcd_instr = $rom[$dcd_address];
+ $label = $BlockRef->{LABEL};
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ printf("0x%04X: %02X", $dcd_address, $dcd_instr) if (! $gen_assembly_code);
+ }
+
+ $invalid = FALSE;
+
+ if ($dcd_instr_size == 1)
+ {
+ if ($decoder_silent_level == SILENT0)
+ {
+ print(($gen_assembly_code) ? "\t" : "\t\t");
+ }
+ }
+ elsif ($dcd_instr_size == 2)
+ {
+ $dcd_parm0 = $rom[$dcd_address + 1];
+ $invalid = TRUE if ($dcd_parm0 == EMPTY);
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ if ($gen_assembly_code)
+ {
+ print "\t";
+ }
+ else
+ {
+ printf " %02X\t\t", $dcd_parm0;
+ }
+ }
+ }
+ elsif ($dcd_instr_size == 3)
+ {
+ $dcd_parm0 = $rom[$dcd_address + 1];
+ $dcd_parm1 = $rom[$dcd_address + 2];
+ $invalid = TRUE if ($dcd_parm0 == EMPTY || $dcd_parm1 == EMPTY);
+
+ if ($decoder_silent_level == SILENT0)
+ {
+ if ($gen_assembly_code)
+ {
+ print "\t";
+ }
+ else
+ {
+ printf " %02X %02X\t", $dcd_parm0, $dcd_parm1;
+ }
+ }
+ }
+ else
+ {
+ printf STDERR "An instruction and a block overlapped with each other at this address: 0x%04X!\n", $dcd_address;
+ }
+
+ $dcd_Ri_regs = $dcd_instr & 0x01;
+ $dcd_Ri_name = "R$dcd_Ri_regs";
+
+ $dcd_Rn_regs = $dcd_instr & 0x07;
+ $dcd_Rn_name = "R$dcd_Rn_regs";
+
+ $prev_is_jump = FALSE;
+
+ invalidate_DPTR_Rx() if ($label->{TYPE} != BL_TYPE_NONE);
+
+ if ($dcd_instr != EMPTY && ! $invalid)
+ {
+ $instr_decoders[$dcd_instr]();
+ }
+ else
+ {
+ unknown();
+ }
+ }
+
+################################################################################
+################################################################################
+
+ #
+ # Reads the sfrs and bits from the $Line.
+ #
+
+sub process_header_line($)
+ {
+ my $Line = $_[0];
+
+ Log((' ' x $embed_level) . $Line, 5);
+
+ if ($Line =~ /^#\s*include\s+["<]\s*(\S+)\s*[">]$/o)
+ {
+ $embed_level += 4;
+ &read_header("$include_path/$1");
+ $embed_level -= 4;
+ }
+ elsif ($Line =~ /^__sfr\s+__at\s*(?:\(\s*)?0x([[:xdigit:]]+)(?:\s*\))?\s+([\w_]+)/io)
+ {
+ # __sfr __at (0x80) P0 ; /* PORT 0 */
+
+ add_ram(hex($1), $2, RAM_TYPE_DIR, TRUE);
+ }
+ elsif ($Line =~ /^SFR\s*\(\s*([\w_]+)\s*,\s*0x([[:xdigit:]]+)\s*\)/io)
+ {
+ # SFR(P0, 0x80); // Port 0
+
+ add_ram(hex($2), $1, RAM_TYPE_DIR, TRUE);
+ }
+ elsif ($Line =~ /^sfr\s+([\w_]+)\s*=\s*0x([[:xdigit:]]+)/io)
+ {
+ # sfr P1 = 0x90;
+
+ add_ram(hex($2), $1, RAM_TYPE_DIR, TRUE);
+ }
+ elsif ($Line =~ /^__sbit\s+__at\s*(?:\(\s*)?0x([[:xdigit:]]+)(?:\s*\))?\s+([\w_]+)/io)
+ {
+ # __sbit __at (0x86) P0_6 ;
+
+ add_bit(hex($1), $2, TRUE);
+ }
+ elsif ($Line =~ /^SBIT\s*\(\s*([\w_]+)\s*,\s*0x([[:xdigit:]]+)\s*,\s*(\d)\s*\)/io)
+ {
+ # SBIT(P0_0, 0x80, 0); // Port 0 bit 0
+
+ add_bit(hex($2) + int($3), $1, TRUE);
+ }
+ elsif ($Line =~ /^sbit\s+([\w_]+)\s*=\s*0x([[:xdigit:]]+)/io)
+ {
+ # sbit P3_1 = 0xB1;
+
+ add_bit(hex($2), $1, TRUE);
+ }
+ elsif ($Line =~ /^sbit\s+([\w_]+)\s*=\s*([\w_]+)\s*\^\s*(\d)/io)
+ {
+ # sbit SM0 = SCON^7;
+
+ my ($name, $reg, $bit) = ($1, $2, $3);
+
+ add_bit($sfr_by_names{$reg} + $bit, $name, TRUE) if (defined($sfr_by_names{$reg}));
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Reads in a MCU.h file.
+ #
+
+sub read_header($)
+ {
+ my $Header = $_[0];
+ my ($fh, $pre_comment, $comment, $line_number);
+ my $head;
+
+ if (! open($fh, '<', $Header))
+ {
+ print STDERR "$PROGRAM: Could not open. -> \"$Header\"\n";
+ exit(1);
+ }
+
+ $head = ' ' x $embed_level;
+
+ Log("${head}read_header($Header) >>>>", 5);
+
+ $comment = FALSE;
+ $line_number = 1;
+ while (<$fh>)
+ {
+ chomp;
+ s/\r$//o; # '\r'
+
+ # Filters off the C comments.
+
+ s/\/\*.*\*\///o; # /* ... */
+ s/\/\/.*$//o; # // ...
+ s/^\s*|\s*$//go;
+
+ if (/\/\*/o) # /*
+ {
+ $pre_comment = TRUE;
+ s/\s*\/\*.*$//o;
+ }
+ elsif (/\*\//o) # */
+ {
+ $pre_comment = FALSE;
+ $comment = FALSE;
+ s/^.*\*\/\s*//o;
+ }
+
+ if ($comment)
+ {
+ ++$line_number;
+ next;
+ }
+
+ $comment = $pre_comment if ($pre_comment);
+
+ if (/^\s*$/o)
+ {
+ ++$line_number;
+ next;
+ }
+
+ run_preprocessor($Header, \&process_header_line, $_, $line_number);
+ ++$line_number;
+ } # while (<$fh>)
+
+ Log("${head}<<<< read_header($Header)", 5);
+ close($fh);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Among the blocks stows description of an instruction.
+ #
+
+sub add_instr_block($$)
+ {
+ my ($Address, $Instruction) = @_;
+ my $instr_size = $instruction_sizes[$Instruction];
+ my $invalid;
+
+ $dcd_address = $Address;
+ $dcd_instr = $rom[$dcd_address];
+ $invalid = FALSE;
+
+ if (! $instr_size)
+ {
+ $instr_size = 1;
+ add_block($Address, BLOCK_CONST, $instr_size, BL_TYPE_NONE, '');
+ }
+ else
+ {
+ if ($instr_size == 1)
+ {
+ $invalid = TRUE if ($dcd_instr == EMPTY);
+ }
+
+ if ($instr_size == 2)
+ {
+ $dcd_parm0 = $rom[$dcd_address + 1];
+ $invalid = TRUE if ($dcd_parm0 == EMPTY);
+ }
+
+ if ($instr_size == 3)
+ {
+ $dcd_parm1 = $rom[$dcd_address + 2];
+ $invalid = TRUE if ($dcd_parm1 == EMPTY);
+ }
+
+ if ($invalid)
+ {
+ add_block($Address, BLOCK_CONST, $instr_size, BL_TYPE_NONE, '') if ($instr_size);
+ }
+ else
+ {
+ add_block($Address, BLOCK_INSTR, $instr_size, BL_TYPE_NONE, '');
+ }
+ }
+
+ return $instr_size;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Splits the program into small blocks.
+ #
+
+sub split_code_to_blocks()
+ {
+ my ($i, $instr);
+ my ($is_empty, $empty_begin);
+ my ($is_const, $const_begin);
+
+ $is_empty = FALSE;
+ $empty_begin = 0;
+ $is_const = FALSE;
+ $const_begin = 0;
+
+ for ($i = 0; $i < $rom_size; )
+ {
+ $instr = $rom[$i];
+
+ if ($instr == EMPTY)
+ {
+ if (! $is_empty)
+ {
+ # The begin of the empty section.
+
+ if ($is_const)
+ {
+ # The end of the constant section.
+
+ add_block($const_begin, BLOCK_CONST, $i - $const_begin, BL_TYPE_NONE, '');
+ $is_const = FALSE;
+ }
+
+ $empty_begin = $i;
+ $is_empty = TRUE;
+ }
+
+ ++$i;
+ } # if ($instr == EMPTY)
+ elsif (is_constant($i))
+ {
+ if (! $is_const)
+ {
+ if ($is_empty)
+ {
+ # The end of the empty section.
+
+ add_block($empty_begin, BLOCK_EMPTY, $i - $empty_begin, BL_TYPE_NONE, '');
+ $is_empty = FALSE;
+ }
+
+ $const_begin = $i;
+ $is_const = TRUE;
+ }
+
+ ++$i;
+ } # elsif (is_constant($i))
+ else
+ {
+ if ($is_const)
+ {
+ # The end of the constant section.
+
+ add_block($const_begin, BLOCK_CONST, $i - $const_begin, BL_TYPE_NONE, '');
+ $is_const = FALSE;
+ }
+
+ if ($is_empty)
+ {
+ # The end of the empty section.
+
+ add_block($empty_begin, BLOCK_EMPTY, $i - $empty_begin, BL_TYPE_NONE, '');
+ $is_empty = FALSE;
+ }
+
+ $i += add_instr_block($i, $instr);
+ }
+ } # for ($i = 0; $i < $rom_size; )
+
+ if ($is_const)
+ {
+ add_block($const_begin, BLOCK_CONST, $i - $const_begin, BL_TYPE_NONE, '');
+ }
+
+ if ($is_empty)
+ {
+ add_block($empty_begin, BLOCK_EMPTY, $i - $empty_begin, BL_TYPE_NONE, '');
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If the $BlockRef a call, then follows.
+ #
+
+sub follow_call($)
+ {
+ my $BlockRef = $_[0];
+ my ($addr, $size, $instr);
+
+ $addr = $BlockRef->{ADDR};
+ $size = $BlockRef->{SIZE};
+ $instr = $rom[$addr];
+
+ if ($instr == INST_LCALL)
+ {
+ return (($rom[$addr + 1] << 8) | $rom[$addr + 2]);
+ }
+ elsif (($instr & 0x1F) == INST_ACALL)
+ {
+ return ((($addr + $size) & 0xF800) | (($instr & 0xE0) << 3) | $rom[$addr + 1]);
+ }
+
+ return EMPTY;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If the $BlockRef a unconditional jump, then follows.
+ #
+
+sub follow_unconditional_jump($)
+ {
+ my $BlockRef = $_[0];
+ my ($addr, $size, $instr);
+
+ $addr = $BlockRef->{ADDR};
+ $size = $BlockRef->{SIZE};
+ $instr = $rom[$addr];
+
+ if ($size == 3 && $instr == INST_LJMP)
+ {
+ return (($rom[$addr + 1] << 8) | $rom[$addr + 2]);
+ }
+ elsif ($size == 2)
+ {
+ if (($instr & 0x1F) == INST_AJMP)
+ {
+ return ((($addr + $size) & 0xF800) | ((($instr & 0xE0) << 3) | $rom[$addr + 1]));
+ }
+ elsif ($instr == INST_SJMP)
+ {
+ return ($addr + $size + expand_offset($rom[$addr + 1]));
+ }
+ }
+
+ return EMPTY;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If the $BlockRef a conditional jump, then follows.
+ #
+
+sub follow_conditional_jump($)
+ {
+ my $BlockRef = $_[0];
+ my ($addr, $size, $instr, $instr_mask1, $instr_mask2);
+
+ return EMPTY if ($BlockRef->{TYPE} != BLOCK_INSTR);
+
+ $addr = $BlockRef->{ADDR};
+ $size = $BlockRef->{SIZE};
+ $instr = $rom[$addr];
+
+ $instr_mask1 = $instr & 0xFE;
+ $instr_mask2 = $instr & 0xF8;
+
+ if ($instr_mask1 == INST_CJNE__Ri_DATA ||
+ $instr_mask2 == INST_CJNE_Rn_DATA)
+ {
+ return ($addr + $size + expand_offset($rom[$addr + 2]));
+ }
+ elsif ($instr_mask2 == INST_DJNZ_Rn)
+ {
+ return ($addr + $size + expand_offset($rom[$addr + 1]));
+ }
+ elsif ($instr == INST_JBC_BIT ||
+ $instr == INST_JB_BIT ||
+ $instr == INST_JNB_BIT ||
+ $instr == INST_CJNE_A_DATA ||
+ $instr == INST_CJNE_A_DIRECT ||
+ $instr == INST_DJNZ_DIRECT)
+ {
+ return ($addr + $size + expand_offset($rom[$addr + 2]));
+ }
+ elsif ($instr == INST_JC ||
+ $instr == INST_JNC ||
+ $instr == INST_JZ ||
+ $instr == INST_JNZ ||
+ $instr == INST_SJMP)
+ {
+ return ($addr + $size + expand_offset($rom[$addr + 1]));
+ }
+
+ return EMPTY;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Determines the start of stack.
+ #
+
+sub determine_stack()
+ {
+ my ($block, $instr, $addr, $count, $size, $t);
+
+ return if (! defined($blocks_by_address{0}));
+
+ $addr = follow_unconditional_jump(\%{$blocks_by_address{0}});
+ return if ($addr == EMPTY);
+
+ return if (! defined($blocks_by_address{$addr}));
+
+ # At most so much block looks through.
+ $count = 30;
+ do
+ {
+ $block = \%{$blocks_by_address{$addr}};
+
+ return if ($block->{TYPE} != BLOCK_INSTR);
+ return if (follow_unconditional_jump($block) != EMPTY);
+ return if (follow_call($block) != EMPTY);
+
+ $addr = $block->{ADDR};
+ $size = $block->{SIZE};
+ $instr = $rom[$addr];
+
+ return if ($size == 2 && ($instr == INST_PUSH_DIRECT || $instr == INST_POP_DIRECT));
+
+ $t = follow_conditional_jump($block);
+
+ return if ($t != EMPTY && $t > $addr);
+
+ if ($size == 3 && $instr == INST_MOV_DIRECT_DATA && $rom[$addr + 1] == SP)
+ {
+ $stack_start = $rom[$addr + 2] + 1;
+ $stack_size = RAM_MAX_ADDR - $stack_start + 1;
+ return;
+ }
+
+ $addr += $size; # Compute the address of next block.
+ }
+ while (--$count);
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Previously assess the code.
+ #
+
+sub preliminary_survey($)
+ {
+ $decoder_silent_level = $_[0];
+ foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ {
+ my $block = \%{$blocks_by_address{$_}};
+
+ next if ($block->{TYPE} != BLOCK_INSTR);
+
+ instruction_decoder($_, $block);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Finds address of branchs and procedures.
+ #
+
+sub find_labels_in_code()
+ {
+ foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ {
+ my $block = \%{$blocks_by_address{$_}};
+
+ next if ($block->{TYPE} != BLOCK_INSTR);
+
+ label_finder($_, $block);
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Finds lost address of branchs and procedures.
+ #
+
+sub find_lost_labels_in_code()
+ {
+ my ($block, $prev_block, $prev_addr, $label, $instr);
+
+ $prev_addr = EMPTY;
+ $prev_block = undef;
+ foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ {
+ $block = \%{$blocks_by_address{$_}};
+
+ next if ($block->{TYPE} != BLOCK_INSTR);
+
+ if ($prev_addr != EMPTY)
+ {
+ $instr = $rom[$prev_addr];
+ $label = $block->{LABEL};
+
+ if (defined($label) && $label->{TYPE} == BL_TYPE_NONE)
+ {
+ if ($instr == INST_RET || $instr == INST_RETI)
+ {
+ Log(sprintf("Lost function label at the 0x%04X address.", $_), 5);
+ add_func_label($_, '', TRUE);
+ }
+ elsif ($instr == INST_LJMP || $instr == INST_SJMP || ($instr & 0x1F) == INST_AJMP)
+ {
+ Log(sprintf("Lost jump label at the 0x%04X address.", $_), 5);
+ add_jump_label($_, '', BL_TYPE_LABEL, EMPTY, TRUE);
+ }
+ }
+ }
+
+ $prev_addr = $_;
+ $prev_block = $block;
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Adds the jump addresses from a jump table.
+ #
+
+sub add_jump_address_in_table($$$)
+ {
+ my ($AddrLow, $AddrHigh, $Size) = @_;
+ my ($end, $addr);
+
+ $end = $AddrLow + $Size;
+ while ($AddrLow < $end)
+ {
+ $addr = $rom[$AddrLow] | ($rom[$AddrHigh] << 8);
+ add_jump_label($addr, '', BL_TYPE_JLABEL, EMPTY, FALSE);
+ ++$AddrLow;
+ ++$AddrHigh;
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Jump tables looking for in the code.
+ #
+
+sub recognize_jump_tables_in_code($)
+ {
+ my $Survey = $_[0];
+ my @blocks = ((undef) x 9);
+ my @instrs = ((EMPTY) x 9);
+ my ($parm, $data1, $data2, $addrL, $addrH, $end, $size);
+
+ foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ {
+ shift(@instrs);
+ push(@instrs, $rom[$_]);
+
+ shift(@blocks);
+ push(@blocks, \%{$blocks_by_address{$_}});
+
+ next if (! defined($blocks[0]) || ! defined($blocks[8]));
+ next if ($blocks[0]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[1]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[2]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[3]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[4]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[5]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[6]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[7]->{TYPE} != BLOCK_INSTR);
+ next if ($blocks[8]->{TYPE} != BLOCK_INSTR);
+
+ if ($blocks[0]->{SIZE} == 2 && $instrs[0] == INST_ADD_A_DATA && # add A, #0xZZ
+
+ $blocks[1]->{SIZE} == 1 && $instrs[1] == INST_MOVC_A_APC && # movc A, @A+PC
+
+ $blocks[2]->{SIZE} == 2 && $rom[$blocks[2]->{ADDR} + 1] == DPL &&
+ ($instrs[2] == INST_XCH_A_DIRECT || # xch A, DPL
+ $instrs[2] == INST_MOV_DIRECT_A) && # mov DPL, A
+
+ (($blocks[3]->{SIZE} == 2 && $instrs[3] == INST_MOV_A_DIRECT) || # mov A, direct
+ ($blocks[3]->{SIZE} == 1 && (($instrs[3] & 0xF8) == INST_MOV_A_Rn || # mov A, Rn
+ ($instrs[3] & 0xFE) == INST_MOV_A_Ri))) && # mov A, @Ri
+
+ $blocks[4]->{SIZE} == 2 && $instrs[4] == INST_ADD_A_DATA && # add A, #0xZZ
+
+ $blocks[5]->{SIZE} == 1 && $instrs[5] == INST_MOVC_A_APC && # movc A, @A+PC
+
+ $blocks[6]->{SIZE} == 2 && $instrs[6] == INST_MOV_DIRECT_A && # mov DPH, A
+ $rom[$blocks[6]->{ADDR} + 1] == DPH &&
+
+ $blocks[7]->{SIZE} == 1 && $instrs[7] == INST_CLR_A && # clr A
+
+ $blocks[8]->{SIZE} == 1 && $instrs[8] == INST_JMP_A_DPTR) # jmp @A+DPTR
+ {
+=back
+ 24 0B add A, #0x0B
+ 83 movc A, @A+PC
+ F5 82 mov DPL, A
+
+ E5 F0 mov A, B
+ or
+ ED mov A, R5
+
+ 24 25 add A, #0x25
+ 83 movc A, @A+PC
+ F5 83 mov DPH, A
+ E4 clr A
+ 73 jmp @A+DPTR
+=cut
+
+ $addrL = $blocks[2]->{ADDR} + $rom[$blocks[0]->{ADDR} + 1];
+ $addrH = $blocks[6]->{ADDR} + $rom[$blocks[4]->{ADDR} + 1];
+ $size = $addrH - $addrL;
+ $end = $addrH + $size - 1;
+
+ if ($Survey)
+ {
+ add_const_area($addrL, $end);
+ }
+ else
+ {
+ add_block($addrL, BLOCK_JTABLE, $size, BL_TYPE_JTABLE, '');
+ add_block($addrH, BLOCK_JTABLE, $size, BL_TYPE_JTABLE, '');
+ add_jump_address_in_table($addrL, $addrH, $size);
+ }
+ }
+ elsif ($blocks[0]->{SIZE} == 3 && $instrs[0] == INST_MOV_DPTR_DATA && # mov DPTR, #tLow
+
+ $blocks[1]->{SIZE} == 1 && $instrs[1] == INST_MOVC_A_DPTR && # movc A, @A+DPTR
+
+ (($blocks[2]->{SIZE} == 2 && $instrs[2] == INST_XCH_A_DIRECT) || # xch A, XX
+ ($blocks[2]->{SIZE} == 2 && $instrs[2] == INST_PUSH_DIRECT && # push ACC
+ $rom[$blocks[2]->{ADDR} + 1] == ACC)) &&
+
+ $blocks[3]->{SIZE} == 3 && $instrs[3] == INST_MOV_DPTR_DATA && # mov DPTR, #tHigh
+
+ $blocks[4]->{SIZE} == 1 && $instrs[4] == INST_MOVC_A_DPTR && # movc A, @A+DPTR
+
+ $blocks[5]->{SIZE} == 2 && $instrs[5] == INST_MOV_DIRECT_A && # mov DPH, A
+ $rom[$blocks[5]->{ADDR} + 1] == DPH &&
+
+ (($blocks[6]->{SIZE} == 3 && $instrs[6] == INST_MOV_DIRECT_DIRECT && # mov DPL, XX
+ $rom[$blocks[6]->{ADDR} + 2] == DPL) ||
+ ($blocks[6]->{SIZE} == 2 && $instrs[6] == INST_POP_DIRECT && # pop DPL
+ $rom[$blocks[6]->{ADDR} + 1] == DPL)) &&
+
+ $blocks[7]->{SIZE} == 1 && $instrs[7] == INST_CLR_A && # clr A
+
+ $blocks[8]->{SIZE} == 1 && $instrs[8] == INST_JMP_A_DPTR) # jmp @A+DPTR
+ {
+=back
+ 90 00 79 mov DPTR, #0x0079
+ 93 movc A, @A+DPTR
+
+ C5 F0 xch A, B
+ or
+ C0 E0 push ACC
+
+ 90 01 79 mov DPTR, #0x0179
+ 93 movc A, @A+DPTR
+ F5 83 mov DPH, A
+
+ 85 F0 82 mov DPL, B
+ or
+ D0 82 pop DPL
+
+ E4 clr A
+ 73 jmp @A+DPTR
+=cut
+
+ $addrL = ($rom[$blocks[0]->{ADDR} + 1] << 8) | $rom[$blocks[0]->{ADDR} + 2];
+ $addrH = ($rom[$blocks[3]->{ADDR} + 1] << 8) | $rom[$blocks[3]->{ADDR} + 2];
+ $size = $addrH - $addrL;
+ $end = $addrH + $size - 1;
+
+ if ($Survey)
+ {
+ add_const_area($addrL, $end);
+ }
+ else
+ {
+ add_block($addrL, BLOCK_JTABLE, $size, BL_TYPE_JTABLE, '');
+ add_block($addrH, BLOCK_JTABLE, $size, BL_TYPE_JTABLE, '');
+ add_jump_address_in_table($addrL, $addrH, $size);
+ }
+ }
+ }
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the global symbols.
+ #
+
+sub emit_globals($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($label, $cnt0, $cnt1, $str0, $str1);
+
+ return if (! scalar(keys(%labels_by_address)));
+
+ print ";$border0\n;\tPublic labels\n;$border0\n\n";
+
+ if ($Assembly_mode)
+ {
+ foreach (sort {$a <=> $b} keys(%labels_by_address))
+ {
+ $label = $labels_by_address{$_};
+
+ next if ($label->{TYPE} != BL_TYPE_SUB);
+
+ print "\t.globl\t$label->{NAME}\n";
+ }
+ }
+ else
+ {
+ foreach (sort {$a <=> $b} keys(%labels_by_address))
+ {
+ $label = $labels_by_address{$_};
+
+ next if ($label->{TYPE} != BL_TYPE_SUB);
+
+ $str0 = sprintf "0x%04X", $_;
+ $cnt0 = sprintf "%3u", $label->{CALL_COUNT};
+ $cnt1 = sprintf "%3u", $label->{JUMP_COUNT};
+ $str1 = ($label->{CALL_COUNT} || $label->{JUMP_COUNT}) ? "calls: $cnt0, jumps: $cnt1" : 'not used';
+ print "${str0}:\t" . align($label->{NAME}, STAT_ALIGN_SIZE) . "($str1)\n";
+ }
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the SFRs.
+ #
+
+sub emit_sfrs($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($sfr, $cnt, $str0, $str1);
+
+ return if (! scalar(keys(%sfr_by_address)));
+
+ if ($Assembly_mode)
+ {
+ print ";$border0\n;\tSpecial function registers\n;$border0\n\n" .
+ "\t.area\tRSEG\t(ABS,DATA)\n\t.org\t0x0000\n\n";
+
+ foreach (sort {$a <=> $b} keys(%sfr_by_address))
+ {
+ printf "$sfr_by_address{$_}->{NAME}\t=\t0x%02X\n", $_;
+ }
+ }
+ else
+ {
+ print ";$border0\n;\tSFR registers\n;$border0\n\n";
+
+ foreach (sort {$a <=> $b} keys(%sfr_by_address))
+ {
+ $sfr = $sfr_by_address{$_};
+ $str0 = sprintf "0x%02X", $_;
+ $cnt = sprintf "%3u", $sfr->{REF_COUNT};
+ $str1 = ($sfr->{REF_COUNT}) ? "used $cnt times" : 'not used';
+ print "${str0}:\t" . align($sfr->{NAME}, STAT_ALIGN_SIZE) . "($str1)\n";
+ }
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the SFR bits.
+ #
+
+sub emit_sfr_bits($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($sbit, $cnt, $str0, $str1);
+
+ return if (! scalar(keys(%sfr_bit_by_address)));
+
+ if ($Assembly_mode)
+ {
+ print ";$border0\n;\tSpecial function bits\n;$border0\n\n" .
+ "\t.area\tRSEG\t(ABS,DATA)\n\t.org\t0x0000\n\n";
+
+ foreach (sort {$a <=> $b} keys(%sfr_bit_by_address))
+ {
+ printf "$sfr_bit_by_address{$_}->{NAME}\t=\t0x%02X\n", $_;
+ }
+ }
+ else
+ {
+ print ";$border0\n;\tSpecial function bits\n;$border0\n\n";
+
+ foreach (sort {$a <=> $b} keys(%sfr_bit_by_address))
+ {
+ $sbit = $sfr_bit_by_address{$_};
+ $str0 = sprintf "0x%02X", $_;
+ $cnt = sprintf "%3u", $sbit->{REF_COUNT};
+ $str1 = ($sbit->{REF_COUNT}) ? "used $cnt times" : 'not used';
+ print "${str0}:\t" . align($sbit->{NAME}, STAT_ALIGN_SIZE) . "($str1)\n";
+ }
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the banks.
+ #
+
+sub emit_banks($)
+ {
+ my $Assembly_mode = $_[0];
+
+ $used_banks{0} = TRUE;
+
+ print ";$border0\n;\tOverlayable register banks\n;$border0\n\n";
+
+ if ($Assembly_mode)
+ {
+ foreach (sort {$a <=> $b} keys(%used_banks))
+ {
+ print "\t.area\tREG_BANK_$_\t(REL,OVR,DATA)\n\t.ds 8\n";
+ }
+ }
+ else
+ {
+ foreach (sort {$a <=> $b} keys(%used_banks))
+ {
+ printf "0x%02X:\tREG_BANK_$_\n", $_ * 8;
+ }
+ }
+
+ print "\n";
+ }
+
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the registers (variables).
+ #
+
+sub emit_ram_data($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($ram, $first, $next_addr, $size);
+ my ($cnt, $str0, $str1);
+
+ return if (! scalar(keys(%ram_by_address)));
+
+ if ($Assembly_mode)
+ {
+ print ";$border0\n;\tAbsolute internal RAM data\n;$border0\n\n" .
+ "\t.area\tIABS\t(ABS,DATA)\n";
+
+ $first = TRUE;
+ $next_addr = EMPTY;
+ foreach (sort {$a <=> $b} keys(%ram_by_address))
+ {
+ last if ($_ > RAM_LAST_DIR_ADDR);
+
+ $ram = $ram_by_address{$_};
+
+ next if ($ram->{TYPE} != RAM_TYPE_DIR);
+
+ if ($first)
+ {
+ printf "\t.org\t0x%02X\n\n", $_;
+ $first = FALSE;
+ }
+
+ if ($next_addr != EMPTY)
+ {
+ next if ($_ < $next_addr);
+
+ printf("\t.ds 0x%02X\n", $_ - $next_addr) if ($_ > $next_addr);
+ }
+
+ if ($ram->{NAME} ne '')
+ {
+ print "$ram->{NAME}::\n\t.ds $ram->{SIZE}\n";
+ $next_addr = $_ + $size;
+ }
+ else
+ {
+ printf "r0x%02X::\n\t.ds 1\n", $_;
+ $next_addr = $_ + 1;
+ }
+ } # foreach (sort {$a <=> $b} keys(%ram_by_address))
+ }
+ else
+ {
+ print ";$border0\n;\tInternal RAM data\n;$border0\n\n";
+
+ $next_addr = EMPTY;
+ foreach (sort {$a <=> $b} keys(%ram_by_address))
+ {
+ last if ($_ > RAM_LAST_DIR_ADDR);
+
+ $ram = $ram_by_address{$_};
+
+ next if ($ram->{TYPE} != RAM_TYPE_DIR);
+
+ next if ($next_addr != EMPTY && $_ < $next_addr);
+
+ $str0 = sprintf "0x%02X", $_;
+ $cnt = sprintf "%3u", $ram->{REF_COUNT};
+ $str1 = ($ram->{REF_COUNT}) ? "used $cnt times" : 'not used';
+
+ if ($ram->{NAME} ne '')
+ {
+ $cnt = sprintf "%3u", $ram->{SIZE};
+ print "${str0}:\t" . align($ram->{NAME}, STAT_ALIGN_SIZE) . "($cnt bytes) ($str1)\n";
+ $next_addr = $_ + $ram->{SIZE};
+ }
+ else
+ {
+ if ($map_readed)
+ {
+ print "${str0}:\t" . align("variable_$str0", STAT_ALIGN_SIZE) . "( 1 bytes) ($str1)\n";
+ }
+ else
+ {
+ print "${str0}:\t" . align("variable_$str0", STAT_ALIGN_SIZE) . "($str1)\n";
+ }
+
+ $next_addr = $_ + 1;
+ }
+ } # foreach (sort {$a <=> $b} keys(%ram_by_address))
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the list of registers in indirect RAM.
+ #
+
+sub emit_indirect_ram($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($ram, $name, $cnt, $str0, $str1, $str2);
+
+ return if (! scalar(keys(%ram_by_address)));
+
+ if ($Assembly_mode)
+ {
+ print ";$border0\n;\tIndirectly addressable internal RAM data\n;$border0\n\n" .
+ "\t.area\tISEG\t(DATA)\n\n";
+
+ foreach (sort {$a <=> $b} keys(%ram_by_address))
+ {
+ $ram = $ram_by_address{$_};
+
+ next if ($ram->{TYPE} != RAM_TYPE_IND);
+
+ $name = $ram->{NAME};
+ $str0 = sprintf "0x%02X", $_;
+ $str1 = ($name ne '') ? $name : "iram_$str0";
+ printf "${str1}::\n\t.ds 1\n", $_;
+ }
+ }
+ else
+ {
+ print ";$border0\n;\tIndirectly addressable internal RAM data\n;$border0\n\n";
+
+ foreach (sort {$a <=> $b} keys(%ram_by_address))
+ {
+ $ram = $ram_by_address{$_};
+
+ next if ($ram->{TYPE} != RAM_TYPE_IND);
+
+ $name = $ram->{NAME};
+ $str0 = sprintf "0x%02X", $_;
+ $str1 = ($name ne '') ? $name : "iram_$str0";
+ $cnt = sprintf "%3u", $ram->{REF_COUNT};
+ $str2 = ($ram->{REF_COUNT}) ? "used $cnt times" : 'not used';
+ print "${str0}:\t" . align($str1, STAT_ALIGN_SIZE) . "($str2)\n";
+ }
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the list of registers in external RAM.
+ #
+
+sub emit_external_ram($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($xram, $name, $next_addr, $cnt, $str0, $str1, $str2);
+
+ return if (! scalar(keys(%xram_by_address)));
+
+ if ($Assembly_mode)
+ {
+ print ";$border0\n;\tExternal RAM data\n;$border0\n\n\t.area\tXSEG\t(XDATA)\n\n";
+
+ foreach (sort {$a <=> $b} keys(%xram_by_address))
+ {
+ $xram = $xram_by_address{$_};
+ $name = $xram->{NAME};
+ $str0 = sprintf "0x%04X", $_;
+ $str1 = ($name ne '') ? $name : "xram_$str0";
+ print "$str1\t=\t$str0\n";
+ }
+ }
+ else
+ {
+ print ";$border0\n;\tExternal RAM data\n;$border0\n\n";
+
+ $next_addr = EMPTY;
+ foreach (sort {$a <=> $b} keys(%xram_by_address))
+ {
+ next if ($next_addr != EMPTY && $_ < $next_addr);
+
+ $xram = $xram_by_address{$_};
+ $str0 = sprintf "0x%04X", $_;
+ $cnt = sprintf "%3u", $xram->{REF_COUNT};
+ $str1 = ($xram->{REF_COUNT}) ? "used $cnt times" : 'not used';
+
+ if ($xram->{NAME} ne '')
+ {
+ $cnt = sprintf "%3u", $xram->{SIZE};
+ print "${str0}:\t" . align($xram->{NAME}, STAT_ALIGN_SIZE) . "($cnt bytes) ($str1)\n";
+ $next_addr = $_ + $xram->{SIZE};
+ }
+ else
+ {
+ $str2 = "xram_$str0";
+ print "${str0}:\t" . align($str2, STAT_ALIGN_SIZE) . "($str1)\n";
+ $next_addr = $_ + 1;
+ }
+ }
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the bits.
+ #
+
+sub emit_bits($)
+ {
+ my $Assembly_mode = $_[0];
+ my ($bit, $name, $cnt, $str0, $str1, $str2);
+
+ return if (! scalar(keys(%bit_by_address)));
+
+ if ($Assembly_mode)
+ {
+ print ";$border0\n;\tbit data\n;$border0\n\n" .
+ "\t.area\tBSEG\t(BIT)\n\n";
+
+ foreach (sort {$a <=> $b} keys(%bit_by_address))
+ {
+ $bit = $bit_by_address{$_};
+ $name = $bit->{NAME};
+ $str0 = ($name ne '') ? $name : (sprintf "bit_0x%02X", $_);
+ print "${str0}::\n\t.ds 1\n";
+ }
+ }
+ else
+ {
+ print ";$border0\n;\tbit data\n;$border0\n\n";
+
+ foreach (sort {$a <=> $b} keys(%bit_by_address))
+ {
+ $bit = $bit_by_address{$_};
+ $name = $bit->{NAME};
+ $str0 = sprintf "0x%02X", $_;
+ $str1 = ($name ne '') ? $name : "bit_$str0";
+ $cnt = sprintf "%3u", $bit->{REF_COUNT};
+ $str2 = ($bit->{REF_COUNT}) ? "used $cnt times" : 'not used';
+ print "${str0}:\t" . align($str1, STAT_ALIGN_SIZE) . "($str2)\n";
+ }
+ }
+
+ print "\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints the map from the RAM.
+ #
+
+sub emit_ram_map()
+ {
+ my @map = ();
+ my ($ram, $i, $v);
+
+ for ($i = 0; $i <= RAM_MAX_ADDR; ++$i)
+ {
+ $ram = $ram_by_address{$i};
+
+ if (defined($ram))
+ {
+ $map[$i] = ($ram->{TYPE} == RAM_TYPE_DIR) ? 'd' : 'I';
+ }
+ else
+ {
+ $map[$i] = ' ';
+ }
+ }
+
+ $used_banks{0} = TRUE;
+ foreach (keys(%used_banks))
+ {
+ $i = $_ * 8;
+ $v = $i + 7;
+ do
+ {
+ $map[$i] = $_;
+ ++$i;
+ }
+ while ($i <= $v);
+ }
+
+ foreach (keys(%bit_by_address))
+ {
+ $map[0x20 + int($_ / 8)] = 'B';
+ }
+
+ if ($stack_start > 0)
+ {
+ for ($i = $stack_start; $i <= RAM_MAX_ADDR; ++$i)
+ {
+ $map[$i] = 'S';
+ }
+ }
+
+ print ";$border0\n;\tInternal RAM layout\n;$border0\n\n";
+
+ for ($i = 0; $i <= RAM_MAX_ADDR; $i += 16)
+ {
+ printf "0x%02X: |", $i & 0xF0;
+ print join('|', @map[$i .. ($i + 15)]) . "|\n";
+ }
+
+ print "\n0-3:Register Banks, B:Bits, d:Data, I:iRAM, S:Stack\n\n";
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Find the last address of HOME segment.
+ #
+
+sub find_HOME_last_address()
+ {
+ my $last_addr = 0;
+
+ foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ {
+ my $block = \%{$blocks_by_address{$_}};
+
+ last if ($block->{TYPE} == BLOCK_CONST);
+
+ if ($block->{TYPE} == BLOCK_INSTR)
+ {
+ last if (! defined($interrupts_by_address{$_}));
+
+ last if ($block->{SIZE} > LJMP_SIZE);
+
+ $last_addr = $_;
+ }
+ }
+
+ return $last_addr;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints a label belonging to the $Address.
+ #
+
+sub print_label($)
+ {
+ my $Address = $_[0];
+ my ($label, $type);
+
+ $label = $labels_by_address{$Address};
+
+ return FALSE if (! defined($label) || $label->{TYPE} == BL_TYPE_NONE);
+
+ $type = $label->{TYPE};
+
+ if ($type == BL_TYPE_SUB)
+ {
+ print "\n;$border0\n";
+ }
+ elsif ($type == BL_TYPE_JLABEL)
+ {
+ print "\n\t;$border2\n";
+ }
+
+ printf "\n$label->{NAME}:\n\n";
+ $label->{PRINTED} = TRUE;
+ $prev_is_jump = FALSE;
+ return TRUE;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Prints a table of constants.
+ #
+
+sub print_constants($$)
+ {
+ my ($Address, $BlockRef) = @_;
+ my ($size, $i, $len, $frag, $byte, $spc, $col);
+ my ($left_align, $right_align, $hc);
+ my @constants;
+ my @line;
+
+ $size = $BlockRef->{SIZE};
+
+ return if (! $size);
+
+ $hc = ($BlockRef->{TYPE} == BLOCK_JTABLE) || $hex_constant;
+
+ $prev_is_jump = FALSE;
+
+ $col = ($gen_assembly_code) ? ' ' : ' ';
+
+ if ($gen_assembly_code)
+ {
+ print ";$table_border\n;\t\t $table_header |\n;$table_border\n";
+ }
+ else
+ {
+ print "$table_border\n| | $table_header | $table_header |\n$table_border\n";
+ }
+
+ @constants = @rom[$Address .. ($Address + $size - 1)];
+ $i = 0;
+ while (TRUE)
+ {
+ $len = $size - $i;
+
+ last if (! $len);
+
+ $len = TBL_COLUMNS if ($len > TBL_COLUMNS);
+
+ if ($gen_assembly_code)
+ {
+ print "\t.db\t";
+ }
+ else
+ {
+ printf "| 0x%04X | ", $Address;
+ }
+
+ if (($spc = $Address % TBL_COLUMNS))
+ {
+ $frag = TBL_COLUMNS - $spc;
+ $len = $frag if ($len > $frag);
+ }
+
+ $left_align = $col x $spc;
+ $right_align = $col x (TBL_COLUMNS - $spc - $len);
+ @line = @constants[$i .. ($i + $len - 1)];
+ $Address += $len;
+ $i += $len;
+
+ if ($gen_assembly_code)
+ {
+ print $left_align .
+ join(', ', map {
+ sprintf((($hc || $_ < ord(' ') || $_ >= 0x7F) ? "0x%02X" : "'%c' "), $_ & 0xFF);
+ } @line) . "$right_align ;\n";
+ }
+ else
+ {
+ print " $left_align" . join(' ', map { sprintf("%02X ", $_ & 0xFF); } @line);
+
+ print "$right_align | $left_align " .
+ join(' ', map {
+ sprintf((($_ < ord(' ') || $_ >= 0x7F) ? "%02X " : "'%c'"), $_ & 0xFF);
+ } @line) . "$right_align |\n";
+ }
+ } # while (TRUE)
+
+ print (($gen_assembly_code) ? ";$table_border\n" : "$table_border\n");
+ $prev_is_jump = FALSE;
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # Disassembly contents of $blocks_by_address array.
+ #
+
+sub disassembler()
+ {
+ my ($ref, $home_last_addr, $sname);
+
+ $home_last_addr = 0;
+ $prev_is_jump = FALSE;
+ $decoder_silent_level = SILENT0;
+
+ if ($gen_assembly_code)
+ {
+ $table_header = join(' ', map { sprintf '%02X', $_ } (0 .. (TBL_COLUMNS - 1)));
+ $table_border = ('-' x (TBL_COLUMNS * 6 + 14)) . '+';
+ }
+ else
+ {
+ $table_header = join(' ', map { sprintf '%02X', $_ } (0 .. (TBL_COLUMNS - 1)));
+ $table_border = '+' . ('-' x 10) . '+' . ('-' x (TBL_COLUMNS * 4 + 2)) . '+' . ('-' x (TBL_COLUMNS * 4 + 2)) . '+';
+ }
+
+ invalidate_DPTR_Rx();
+ print "\n";
+
+ if ($gen_assembly_code)
+ {
+ emit_globals(TRUE);
+ emit_sfrs(TRUE);
+ emit_sfr_bits(TRUE);
+ emit_banks(TRUE);
+ emit_ram_data(TRUE);
+ emit_bits(TRUE);
+
+ if ($stack_start > 0)
+ {
+ print ";$border0\n;\tStack segment\n;$border0\n\n\t.area\tSSEG\t(DATA)\n" .
+ "__start__stack:\n\t.ds 1\n\n";
+ }
+
+ emit_indirect_ram(TRUE);
+ emit_external_ram(TRUE);
+
+ $home_last_addr = find_HOME_last_address();
+ $sname = ($home_last_addr > 0) ? 'HOME' : 'CSEG';
+ print ";$border0\n;\tCode\n;$border0\n\n\t.area\t$sname\t(CODE)\n\n";
+ }
+ else
+ {
+ emit_ram_map();
+ emit_globals(FALSE);
+ emit_sfrs(FALSE);
+ emit_sfr_bits(FALSE);
+ emit_banks(FALSE);
+ emit_ram_data(FALSE);
+ emit_bits(FALSE);
+
+ if ($stack_start > 0)
+ {
+ printf ";$border0\n;\tStack: start=0x%02X, size=%u bytes\n;$border0\n\n", $stack_start, $stack_size;
+ }
+
+ emit_indirect_ram(FALSE);
+ emit_external_ram(FALSE);
+ print ";$border0\n\n";
+ }
+
+ foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ {
+ $ref = \%{$blocks_by_address{$_}};
+
+ if ($ref->{TYPE} == BLOCK_INSTR)
+ {
+ if ($home_last_addr > 0 && $_ > $home_last_addr)
+ {
+ print "\n;$border0\n;\tCode\n;$border0\n\n\t.area\tCSEG\t(CODE)\n";
+ $home_last_addr = 0;
+ }
+
+ print_label($_);
+ print "\n" if ($prev_is_jump);
+
+ instruction_decoder($_, $ref);
+ }
+ elsif ($ref->{TYPE} == BLOCK_CONST || $ref->{TYPE} == BLOCK_JTABLE)
+ {
+ print_label($_);
+ print "\n" if ($prev_is_jump);
+
+ print_constants($_, $ref);
+ }
+ elsif ($ref->{TYPE} == BLOCK_EMPTY)
+ {
+ my $next_block = $_ + $ref->{SIZE};
+
+ if (! $gen_assembly_code)
+ {
+ printf("\n0x%04X: -- -- --\n .... -- -- --\n0x%04X: -- -- --\n", $_, $next_block - 1);
+ }
+ elsif ($next_block < $rom_size)
+ {
+ # Skip the empty code space.
+
+ printf "\n\t.ds\t%u\n", $ref->{SIZE};
+ }
+ }
+ } # foreach (sort {$a <=> $b} keys(%blocks_by_address))
+ }
+
+#-------------------------------------------------------------------------------
+
+ #
+ # If there are datas in the code, it is possible that some labels will
+ # be lost. This procedure prints them.
+ #
+
+sub print_hidden_labels()
+ {
+ foreach (sort {$a <=> $b} keys(%labels_by_address))
+ {
+ my $label = $labels_by_address{$_};
+
+ print STDERR "The label: $label->{NAME} is hidden!\n" if (! $label->{PRINTED});
+ }
+ }
+
+################################################################################
+################################################################################
+
+sub usage()
+ {
+ print <<EOT;
+Usage: $PROGRAM [options] <hex file>
+
+ Options are:
+
+ -M|--mcu <header.h>
+
+ Header file of the MCU.
+
+ -I|--include <path to header>
+
+ Path of the header files of MCS51 MCUs. (Default: $default_include_path)
+
+ --map-file <file.map>
+
+ The map file belonging to the input hex file. (optional)
+
+ -r|--rom-size <size of program memory>
+
+EOT
+;
+ printf "\t Defines size of the program memory. (Default %u bytes.)\n", MCS51_ROM_SIZE;
+ print <<EOT;
+
+ --const-area <start address> <end address>
+
+ Designates a constant area (jumptables, texts, etc.), where data is
+ stored happen. The option may be given more times, that to select
+ more areas at the same time. (optional)
+
+ -hc|--hex-constant
+
+ The constants only shows in hexadecimal form. Otherwise if possible,
+ then shows all in character form.
+
+ -as|--assembly-source
+
+ Generates the assembly source file. (Eliminates before the instructions
+ visible address, hex codes and besides replaces the pseudo Rn<#x>
+ register names.) Emits global symbol table, SFR table, Bits table, etc.
+
+ -rj|--recognize-jump-tables
+
+ Recognizes the jump tables.
+
+ -fl|--find-lost-labels
+
+ Finds the "lost" labels. These may be found such in program parts,
+ which are directly not get call.
+
+ --name-list <list_file>
+
+ The file contains list of names. They may be: Names of variables and
+ names of labels. For example:
+
+ [BIT]
+ 0x07:bit_variable
+ ..
+ ..
+ ..
+ [RAM]
+ 0x31:direct_ram_variable
+ ..
+ ..
+ ..
+ [IRAM]
+ 0x91:indirect_ram_variable
+ ..
+ ..
+ ..
+ [XRAM]
+ 0x208:external_ram_variable
+ ..
+ ..
+ ..
+ [ROM]
+ 0x05FC:function_or_label
+ ..
+ ..
+ ..
+
+ The contents of list override the names from map file.
+
+ -ne|--no-explanations
+
+ Eliminates after the instructions visible explaining texts.
+
+ -v <level> or --verbose <level>
+
+ It provides information on from the own operation.
+ Possible value of the level between 0 and 10. (default: 0)
+
+ -h|--help
+
+ This text.
+EOT
+;
+ }
+
+################################################################################
+################################################################################
+################################################################################
+
+foreach (@default_paths)
+ {
+ if (-d $_)
+ {
+ $default_include_path = $_;
+ last;
+ }
+ }
+
+if (! @ARGV)
+ {
+ usage();
+ exit(1);
+ }
+
+for (my $i = 0; $i < @ARGV; )
+ {
+ my $opt = $ARGV[$i++];
+
+ given ($opt)
+ {
+ when (/^-(r|-rom-size)$/o)
+ {
+ param_exist($opt, $i);
+ $rom_size = str2int($ARGV[$i++]);
+
+ if ($rom_size < 1024)
+ {
+ printf STDERR "$PROGRAM: Code size of the MCS51 family greater than 1024 bytes!\n";
+ exit(1);
+ }
+ elsif ($rom_size > MCS51_ROM_SIZE)
+ {
+ printf STDERR "$PROGRAM: Code size of the MCS51 family not greater %u bytes!\n", MCS51_ROM_SIZE;
+ exit(1);
+ }
+ }
+
+ when (/^--const-area$/o)
+ {
+ my ($start, $end);
+
+ param_exist($opt, $i);
+ $start = str2int($ARGV[$i++]);
+
+ param_exist($opt, $i);
+ $end = str2int($ARGV[$i++]);
+
+ if ($start > $end)
+ {
+ my $t = $start;
+
+ $start = $end;
+ $end = $t;
+ }
+ elsif ($start == $end)
+ {
+ $start = MCS51_ROM_SIZE - 1;
+ $end = MCS51_ROM_SIZE - 1;
+ }
+
+ add_const_area($start, $end) if ($start < $end);
+ } # when (/^--const-area$/o)
+
+ when (/^-(hc|-hex-constant)$/o)
+ {
+ $hex_constant = TRUE;
+ }
+
+ when (/^-(I|-include)$/o)
+ {
+ param_exist($opt, $i);
+ $include_path = $ARGV[$i++];
+ }
+
+ when (/^-(M|-mcu)$/o)
+ {
+ param_exist($opt, $i);
+ $header_file = $ARGV[$i++];
+ }
+
+ when (/^--map-file$/o)
+ {
+ param_exist($opt, $i);
+ $map_file = $ARGV[$i++];
+ }
+
+ when (/^-(as|-assembly-source)$/o)
+ {
+ $gen_assembly_code = TRUE;
+ }
+
+ when (/^-(rj|-recognize-jump-tables)$/o)
+ {
+ $recognize_jump_tables = TRUE;
+ }
+
+ when (/^-(fl|-find-lost-labels)$/o)
+ {
+ $find_lost_labels = TRUE;
+ }
+
+ when (/^--name-list$/o)
+ {
+ param_exist($opt, $i);
+ $name_list = $ARGV[$i++];
+ }
+
+ when (/^-(ne|-no-explanations)$/o)
+ {
+ $no_explanations = TRUE;
+ }
+
+ when (/^-(v|-verbose)$/o)
+ {
+ param_exist($opt, $i);
+ $verbose = int($ARGV[$i++]);
+ $verbose = 0 if (! defined($verbose) || $verbose < 0);
+ $verbose = 10 if ($verbose > 10);
+ }
+
+ when (/^-(h|-help)$/o)
+ {
+ usage();
+ exit(0);
+ }
+
+ default
+ {
+ if ($hex_file eq '')
+ {
+ $hex_file = $opt;
+ }
+ else
+ {
+ print STDERR "$PROGRAM: We already have the source file name: $hex_file.\n";
+ exit(1);
+ }
+ }
+ } # given ($opt)
+ } # for (my $i = 0; $i < @ARGV; )
+
+$include_path = $default_include_path if ($include_path eq '');
+
+if ($hex_file eq '')
+ {
+ print STDERR "$PROGRAM: What do you have to disassembled?\n";
+ exit(1);
+ }
+
+is_file_ok($hex_file);
+
+init_mem(0, $rom_size - 1);
+read_hex($hex_file);
+
+if ($header_file ne '')
+ {
+ is_file_ok("$include_path/$header_file");
+ reset_preprocessor();
+ $embed_level = 0;
+ read_header("$include_path/$header_file");
+ }
+else
+ {
+ $embed_level = 0;
+ foreach (grep(! /^\s*$|^\s*#/o, <DATA>))
+ {
+ process_header_line($_);
+ }
+ }
+
+if ($map_file eq '')
+ {
+ ($map_file) = ($hex_file =~ /^(.+)\.hex$/io);
+ $map_file .= '.map';
+ }
+
+$map_file = '' if (! -e $map_file);
+
+is_file_ok($name_list) if ($name_list ne '');
+
+if ($recognize_jump_tables)
+ {
+ split_code_to_blocks();
+ recognize_jump_tables_in_code(TRUE);
+ %blocks_by_address = ();
+ read_map_file();
+ read_name_list();
+ split_code_to_blocks();
+ recognize_jump_tables_in_code(FALSE);
+ }
+else
+ {
+ read_map_file();
+ read_name_list();
+ split_code_to_blocks();
+ }
+
+fix_multi_byte_variables();
+determine_stack();
+preliminary_survey(SILENT2);
+preliminary_survey(SILENT1);
+find_labels_in_code();
+find_lost_labels_in_code() if ($find_lost_labels);
+add_names_labels();
+disassembler();
+print_hidden_labels() if ($verbose > 2);
+
+__END__
+################################################################################
+#
+# These the SFR-s and SBIT-s of the i8052 MCU.
+#
+
+__sfr __at (0x80) P0;
+__sfr __at (0x81) SP;
+__sfr __at (0x82) DPL;
+__sfr __at (0x83) DPH;
+__sfr __at (0x87) PCON;
+__sfr __at (0x88) TCON;
+__sfr __at (0x89) TMOD;
+__sfr __at (0x8A) TL0;
+__sfr __at (0x8B) TL1;
+__sfr __at (0x8C) TH0;
+__sfr __at (0x8D) TH1;
+__sfr __at (0x90) P1;
+__sfr __at (0x98) SCON;
+__sfr __at (0x99) SBUF;
+__sfr __at (0xA0) P2;
+__sfr __at (0xA8) IE;
+__sfr __at (0xB0) P3;
+__sfr __at (0xB8) IP;
+__sfr __at (0xC8) T2CON;
+__sfr __at (0xCA) RCAP2L;
+__sfr __at (0xCB) RCAP2H;
+__sfr __at (0xCC) TL2;
+__sfr __at (0xCD) TH2;
+__sfr __at (0xD0) PSW;
+__sfr __at (0xE0) ACC;
+__sfr __at (0xF0) B;
+
+__sbit __at (0x80) P0_0;
+__sbit __at (0x81) P0_1;
+__sbit __at (0x82) P0_2;
+__sbit __at (0x83) P0_3;
+__sbit __at (0x84) P0_4;
+__sbit __at (0x85) P0_5;
+__sbit __at (0x86) P0_6;
+__sbit __at (0x87) P0_7;
+
+__sbit __at (0x88) IT0;
+__sbit __at (0x89) IE0;
+__sbit __at (0x8A) IT1;
+__sbit __at (0x8B) IE1;
+__sbit __at (0x8C) TR0;
+__sbit __at (0x8D) TF0;
+__sbit __at (0x8E) TR1;
+__sbit __at (0x8F) TF1;
+
+__sbit __at (0x90) P1_0;
+__sbit __at (0x91) P1_1;
+__sbit __at (0x92) P1_2;
+__sbit __at (0x93) P1_3;
+__sbit __at (0x94) P1_4;
+__sbit __at (0x95) P1_5;
+__sbit __at (0x96) P1_6;
+__sbit __at (0x97) P1_7;
+
+__sbit __at (0x98) RI;
+__sbit __at (0x99) TI;
+__sbit __at (0x9A) RB8;
+__sbit __at (0x9B) TB8;
+__sbit __at (0x9C) REN;
+__sbit __at (0x9D) SM2;
+__sbit __at (0x9E) SM1;
+__sbit __at (0x9F) SM0;
+
+__sbit __at (0xA0) P2_0;
+__sbit __at (0xA1) P2_1;
+__sbit __at (0xA2) P2_2;
+__sbit __at (0xA3) P2_3;
+__sbit __at (0xA4) P2_4;
+__sbit __at (0xA5) P2_5;
+__sbit __at (0xA6) P2_6;
+__sbit __at (0xA7) P2_7;
+
+__sbit __at (0xA8) EX0;
+__sbit __at (0xA9) ET0;
+__sbit __at (0xAA) EX1;
+__sbit __at (0xAB) ET1;
+__sbit __at (0xAC) ES;
+__sbit __at (0xAD) ET2;
+__sbit __at (0xAF) EA;
+
+__sbit __at (0xB0) RXD;
+__sbit __at (0xB1) TXD;
+__sbit __at (0xB2) INT0;
+__sbit __at (0xB3) INT1;
+__sbit __at (0xB4) T0;
+__sbit __at (0xB5) T1;
+__sbit __at (0xB6) WR;
+__sbit __at (0xB7) RD;
+
+__sbit __at (0xB8) PX0;
+__sbit __at (0xB9) PT0;
+__sbit __at (0xBA) PX1;
+__sbit __at (0xBB) PT1;
+__sbit __at (0xBC) PS;
+__sbit __at (0xBD) PT2;
+
+__sbit __at (0xC8) CP_RL2;
+__sbit __at (0xC9) C_T2;
+__sbit __at (0xCA) TR2;
+__sbit __at (0xCB) EXEN2;
+__sbit __at (0xCC) TCLK;
+__sbit __at (0xCD) RCLK;
+__sbit __at (0xCE) EXF2;
+__sbit __at (0xCF) TF2;
+
+__sbit __at (0xD0) P;
+__sbit __at (0xD1) F1;
+__sbit __at (0xD2) OV;
+__sbit __at (0xD3) RS0;
+__sbit __at (0xD4) RS1;
+__sbit __at (0xD5) F0;
+__sbit __at (0xD6) AC;
+__sbit __at (0xD7) CY;