1. Introduction and Problem Statement
A good way to learn what a compiler really does when transforming a C source code into a binary is to disassemble the binary and compare it with the C source code. It is especially true for 8 bits microcontrollers like the 8051.
In order to test SDCC we are going to use the following C source code.
/* ========================================================================== *
* Universal Test Corpus - Heterogeneous Architecture Analysis *
* ========================================================================== */
#include <stdint.h>
// 1. Global variables (testing absolute/relative addressing modes)
volatile uint32_t global_var_32 = 0xDEADBEEF;
volatile uint8_t global_var_8 = 0x42;
const char string_const[] = "TARGET_STRING";
// 2. Function with parameter passing and local variables (stack / Frame Pointer test)
int32_t callee_function(int16_t a, int16_t b) {
volatile int32_t local_result = 0;
// Basic and mixed arithmetic operations (8, 16, 32 bits)
local_result += (int32_t)(a * b);
local_result -= (int32_t)(a / (b | 1)); // Avoid division by zero
// Shift tests and logical operations (highly variable depending on ISAs)
local_result = (local_result << 2) ^ 0x55AA55AA;
local_result = (local_result >> 1) | (int32_t)global_var_8;
return local_result;
}
// 3. Main function grouping complex control flows
int main(void) {
volatile int32_t accumulator = 0;
int16_t i;
// Loop test (Conditional jumps, decrement, comparison tests)
for (i = 0; i < 10; i++) {
if (i == 5) {
accumulator += 100;
} else {
accumulator += i;
}
}
// Multiple branching test (Switch / Jump Table or cascaded if-else)
switch (global_var_8) {
case 0x10:
accumulator += 10;
break;
case 0x20:
accumulator += 20;
break;
default:
accumulator -= 5;
break;
}
// Function call (Stack management, save registers Link Register/PC)
accumulator += callee_function((int16_t)accumulator, 3);
// Pointer and indirect memory access test
volatile uint32_t *ptr = (volatile uint32_t *)&global_var_32;
*ptr = (uint32_t)accumulator;
// Terminal infinite loop (classic for raw binaries / microcontrollers)
while (1) {
accumulator ^= *ptr;
}
return 0;
}
To compile it, we will use SDCC which produces an Intel HEX file. This file will be transformed in a ROM file using either objcopy or makebin (see below).
sdcc "$SRC/test1.c" -o "8/test1_8051"
objcopy -I ihex -O binary "8/test1_8051" "8/bin/test1_8051_bin"
makebin -p "8/test1_8051" "8/bin/test1_8051.rom"
Moreover, to simulate the 8051, I use the MCU 8051 IDE (command mcu8051ide).
In this final part we will study the calling mechanism and what does the callee_function do
This C file is particularly interesting, because in a small source code we have a lot of different cases (for example, some of the variables have a 32 bits size).
2. Function call
// Function call (Stack management, save registers Link Register/PC)
accumulator += callee_function((int16_t)accumulator, 3);
The above code is translated in the following code
01F8 851382 MOV DPL, 13h
01FB 851483 MOV DPH, 14h
01FE 750D03 MOV 0Dh, #3h
0201 750E00 MOV 0Eh, #0h
0204 120071 LCALL L0013
0207 AC82 MOV R4, DPL
0209 AD83 MOV R5, DPH
020B AEF0 MOV R6, B
020D FF MOV R7, A
020E EC MOV A, R4
020F 2513 ADD A, 13h
0211 F513 MOV 13h, A
0213 ED MOV A, R5
0214 3514 ADDC A, 14h
0216 F514 MOV 14h, A
0218 EE MOV A, R6
0219 3515 ADDC A, 15h
021B F515 MOV 15h, A
021D EF MOV A, R7
021E 3516 ADDC A, 16h
0220 F516 MOV 16h, A
See how the cast of accumulator variable is done. The accumulator is stored on 4 bytes at addresses 13h,14h,15h,16h.
Casting the 32 bits integer to a 16 bits integer is done by simply storing to DPL and DPH the value contained at address 13h and 14h by the isntructions
01F8 851382 MOV DPL, 13h
01FB 851483 MOV DPH, 14h
callee_function((int16_t)accumulator, 3);
int32_t callee_function(int16_t a, int16_t b) {
See how 3 is casted to a 16 bits integer because of the defintion of the callee_function
01FE 750D03 MOV 0Dh, #3h
0201 750E00 MOV 0Eh, #0h
In the absence of a sophisticated native stack for function arguments in the default memory model, the compiler often uses fixed locations in internal RAM (DATA) to pass parameters to subroutines.
because callee_function returns a 32 bits integer, we have the following lines
0204 120071 LCALL L0013
0207 AC82 MOV R4, DPL
0209 AD83 MOV R5, DPH
020B AEF0 MOV R6, B
020D FF MOV R7, A
and because we have an addition of the accumulator variable with the result of callee_function
// Function call (Stack management, save registers Link Register/PC)
accumulator += callee_function((int16_t)accumulator, 3);
we have the following lines which does a simple addition of two 32 bits integers
020E EC MOV A, R4
020F 2513 ADD A, 13h
0211 F513 MOV 13h, A
0213 ED MOV A, R5
0214 3514 ADDC A, 14h
0216 F514 MOV 14h, A
0218 EE MOV A, R6
0219 3515 ADDC A, 15h
021B F515 MOV 15h, A
021D EF MOV A, R7
021E 3516 ADDC A, 16h
0220 F516 MOV 16h, A
3. Inside the function
// 2. Function with parameter passing and local variables (stack / Frame Pointer test)
int32_t callee_function(int16_t a, int16_t b) {
volatile int32_t local_result = 0;
// Basic and mixed arithmetic operations (8, 16, 32 bits)
local_result += (int32_t)(a * b);
local_result -= (int32_t)(a / (b | 1)); // Avoid division by zero
// Shift tests and logical operations (highly variable depending on ISAs)
local_result = (local_result << 2) ^ 0x55AA55AA;
local_result = (local_result >> 1) | (int32_t)global_var_8;
return local_result;
}
The above function is translated into a rather complicated code requiring us to divide it in several paragraphs
4. Inside the function: Initialization
L0013:
0071 AE82 MOV R6, DPL ; Save lower byte of first parameter
0073 AF83 MOV R7, DPH ; Save higher byte of first parameter
The first thing done by the function when called is to store the first parameter then the C line below is translated into the assembly lines below
volatile int32_t local_result = 0;
0075 E4 CLR A ;
0076 F50F MOV 0Fh, A ; Initialize local_result to 0.
0078 F510 MOV 10h, A ; Initialize local_result to 0
007A F511 MOV 11h, A ; Initialize local_result to 0
007C F512 MOV 12h, A ; Initialize local_result to 0
; local_result being a 32 bits integer, Bytes at adresses 0F,10h,11h,12h are used to store local_result
5. Inside the function: Arithmetic operations
// Basic and mixed arithmetic operations (8, 16, 32 bits)
local_result += (int32_t)(a * b);
the line above is translated in the block below
; start of local_result += (int32_t)(a * b);
007E 850D17 MOV 17h, 0Dh ; Copy second parameter low byte to 17h
0081 850E18 MOV 18h, 0Eh ; Copy second parameter high byte to 18h
0084 8E82 MOV DPL, R6 ; Restore 'a' low byte to DPL
0086 8F83 MOV DPH, R7 ; Restore 'a' high byte to DPH
0088 C007 PUSH 7h ; Save register R7 on stack
008A C006 PUSH 6h ; Save register R6 on stack
008C 120248 LCALL L0015 ; Call multiplication helper routine (a * b) see below
008F AC82 MOV R4, DPL ; Retrieve multiplication result low byte
0091 AD83 MOV R5, DPH ; Retrieve multiplication result high byte
0093 D006 POP 6h ; Restore register R6 from stack
0095 D007 POP 7h ; Restore register R7 from stack
0097 ED MOV A, R5 ; Move high byte of result to accumulator
0098 33 RLC A ; Rotate left through carry for sign extension
0099 95E0 SUBB A, ACC ; Propagate sign bit to form 32-bit value
009B FB MOV R3, A ; Store extension byte in R3
009C FA MOV R2, A ; Store extension byte in R2
; addition of two 32 bits integer
009D EC MOV A, R4 ; Get low byte of multiplication result
009E 250F ADD A, 0Fh ; Add to accumulated lower result bytes
00A0 F50F MOV 0Fh, A ; Update local result byte 0Fh
00A2 ED MOV A, R5 ; Get high byte of multiplication result
00A3 3510 ADDC A, 10h ; Add with carry to local result byte 10h
00A5 F510 MOV 10h, A ; Update local result byte 10h
00A7 EB MOV A, R3 ; Get sign extension byte
00A8 3511 ADDC A, 11h ; Add with carry to local result byte 11h
00AA F511 MOV 11h, A ; Update local result byte 11h
00AC EA MOV A, R2 ; Get sign extension byte
00AD 3512 ADDC A, 12h ; Add with carry to local result byte 12h
00AF F512 MOV 12h, A ; Update local result byte 12h
; end of local_result += (int32_t)(a * b);
the code for the multiplication subroutine is below
L0015: ; code making the multiplication (int32_t)(a * b);
0248 E582 MOV A, DPL
024A 8517F0 MOV B, 17h
024D A4 MUL AB
024E C582 XCH A, DPL
0250 C0F0 PUSH B
0252 8518F0 MOV B, 18h
0255 A4 MUL AB
0256 D0F0 POP B
0258 25F0 ADD A, B
025A C583 XCH A, DPH
025C 8517F0 MOV B, 17h
025F A4 MUL AB
0260 2583 ADD A, DPH
0262 F583 MOV DPH, A
0264 22 RET
pretty straightforward
Then we have the division with the line below
local_result -= (int32_t)(a / (b | 1)); // Avoid division by zero
the above line is translated into the following assembly code
; start of local_result -= (int32_t)(a / (b | 1)); // Avoid division by zero
00B1 AC0D MOV R4, 0Dh ; Retrieve original parameter 'b' low byte
00B3 AD0E MOV R5, 0Eh ; Retrieve original parameter 'b' high byte
00B5 7401 MOV A, #1h ; Load immediate 1 for division safety check (b | 1)
00B7 4C ORL A, R4 ; Bitwise OR with 'b' low byte to prevent division by zero
00B8 F517 MOV 17h, A ; Store safe denominator low byte
00BA 8D18 MOV 18h, R5 ; Store safe denominator high byte
00BC 8E82 MOV DPL, R6 ; Restore 'a' low byte into DPL for division
00BE 8F83 MOV DPH, R7 ; Restore 'a' high byte into DPH for division
00C0 12028E LCALL L0016 ; Call signed division helper routine (a / (b | 1)) see below
The lines below do the following operation local_result = local_result - result of the division
00C3 AE82 MOV R6, DPL ; Get division quotient low byte
00C5 E583 MOV A, DPH ; Get division quotient high byte
00C7 FF MOV R7, A ; Store quotient high byte in R7
00C8 33 RLC A ; Sign extend quotient high byte
00C9 95E0 SUBB A, ACC ; Generate sign extension bits
00CB FD MOV R5, A ; Store extension byte in R5
00CC FC MOV R4, A ; Store extension byte in R4
00CD E50F MOV A, 0Fh ; Load current accumulated result low byte
00CF C3 CLR C ; Clear carry flag for subtraction
00D0 9E SUBB A, R6 ; Subtract division result low byte
00D1 F50F MOV 0Fh, A ; Update result byte 0Fh
00D3 E510 MOV A, 10h ; Load accumulated result byte 10h
00D5 9F SUBB A, R7 ; Subtract with borrow quotient high byte
00D6 F510 MOV 10h, A ; Update result byte 10h
00D8 E511 MOV A, 11h ; Load accumulated result byte 11h
00DA 9D SUBB A, R5 ; Subtract with borrow extension byte
00DB F511 MOV 11h, A ; Update result byte 11h
00DD E512 MOV A, 12h ; Load accumulated result byte 12h
00DF 9C SUBB A, R4 ; Subtract with borrow extension byte
00E0 F512 MOV 12h, A ; Update result byte 12h
; end of local_result -= (int32_t)(a / (b | 1)); // Avoid division by zero
As you can see below the division subroutine is rather complicated but don't worry—a full explanation follows.
; all code for a / b
L0019:
0265 7A10 MOV R2, #10h
0267 E4 CLR A
0268 FB MOV R3, A
0269 FC MOV R4, A
L0022:
026A E582 MOV A, DPL
026C 25E0 ADD A, ACC
026E F582 MOV DPL, A
0270 E583 MOV A, DPH
0272 33 RLC A
0273 F583 MOV DPH, A
0275 EB MOV A, R3
0276 33 RLC A
0277 FB MOV R3, A
0278 EC MOV A, R4
0279 33 RLC A
027A FC MOV R4, A
027B EB MOV A, R3
027C 9517 SUBB A, 17h
027E F5F0 MOV B, A
0280 EC MOV A, R4
0281 9518 SUBB A, 18h
0283 4006 JC L0021
0285 FC MOV R4, A
0286 ABF0 MOV R3, B
0288 438201 ORL DPL, #1h
L0021:
028B DADD DJNZ R2, L0022
028D 22 RET
L0016: ; entry point of a / b
028E C2D5 CLR F0
0290 E583 MOV A, DPH
0292 30E70D JNB ACC.7, L0017
0295 D2D5 SETB F0
0297 E4 CLR A
0298 C3 CLR C
0299 9582 SUBB A, DPL
029B F582 MOV DPL, A
029D E4 CLR A
029E 9583 SUBB A, DPH
02A0 F583 MOV DPH, A
L0017:
02A2 E518 MOV A, 18h
02A4 30E70D JNB ACC.7, L0018
02A7 B2D5 CPL F0
02A9 E4 CLR A
02AA C3 CLR C
02AB 9517 SUBB A, 17h
02AD F517 MOV 17h, A
02AF E4 CLR A
02B0 9518 SUBB A, 18h
02B2 F518 MOV 18h, A
L0018:
02B4 120265 LCALL L0019
02B7 30D50B JNB F0, L0020
02BA E4 CLR A
02BB C3 CLR C
02BC 9582 SUBB A, DPL
02BE F582 MOV DPL, A
02C0 E4 CLR A
02C1 9583 SUBB A, DPH
02C3 F583 MOV DPH, A
L0020:
02C5 22 RET
This routine implements a bit-by-bit 16-bit by 16-bit signed integer division (using a shift-and-subtract binary division algorithm).Here is how it works step-by-step:
- Sign Handling and Preparation (L0016):
The code first handles signed numbers by determining and saving the final result's sign in a flag (F0). It then converts both the dividend (stored in DPTR) and the divisor (stored in memory addresses 17h-18h) into their absolute values.
Loop Initialization (L0019): MOV R2, #10h sets a loop counter to 16 (since it is a 16-bit division). The partial remainder (spread across registers R3 and R4) is initialized to zero.
The Core Algorithm (Shift & Subtract inside L0022):
Left Shift: On each iteration, the dividend (DPTR) and the remainder (R3:R4) are shifted left together by one bit using additions and rotations with carry (ADD A, ACC, RLC A), feeding the next highest bit of the dividend into the operation.
Divisor Subtraction: The code subtracts the divisor (17h-18h) from the partial remainder using SUBB.
Test and Adjustment:If the result is greater than or equal to zero (no borrow/no jump JC L0021), the subtraction is kept: the new remainder is saved in R3:R4, and a 1 is shifted into the lowest bit of the quotient (ORL DPL, #1h). If the result is negative (JC L0021), the subtraction is discarded (leaving a 0 bit in the quotient).
- Loop Control and Finalization: The DJNZ R2, L0022 instruction repeats this process 16 times (once for each bit). Once finished, DPTR holds the resulting quotient, and L0016 restores the correct mathematical sign before returning.
Now that the arithmetic operations have been studied, it's time to study the shift operations
6. Inside the function: Shift operations
; start of local_result = (local_result << 2) ^ 0x55AA55AA;
00E2 E50F MOV A, 0Fh ; Load result byte 0Fh for shift left operation (<< 2)
00E4 25E0 ADD A, ACC ; Shift left by 1 (multiply by 2)
00E6 FC MOV R4, A ; Save temporary shifted byte in R4
00E7 E510 MOV A, 10h ; Load result byte 10h
00E9 33 RLC A ; Rotate left through carry
00EA FD MOV R5, A ; Save temporary shifted byte in R5
00EB E511 MOV A, 11h ; Load result byte 11h
00ED 33 RLC A ; Rotate left through carry
00EE FE MOV R6, A ; Save temporary shifted byte in R6
00EF E512 MOV A, 12h ; Load result byte 12h
00F1 33 RLC A ; Rotate left through carry
00F2 FF MOV R7, A ; Save temporary shifted byte in R7
00F3 EC MOV A, R4 ; Retrieve temporary byte for second shift left (total << 2)
00F4 2C ADD A, R4 ; Shift left by another 1 (total shift of 2)
00F5 FC MOV R4, A ; Update R4 with final << 2 low byte
00F6 ED MOV A, R5 ; Get next byte
00F7 33 RLC A ; Rotate left through carry
00F8 FD MOV R5, A ; Update R5
00F9 EE MOV A, R6 ; Get next byte
00FA 33 RLC A ; Rotate left through carry
00FB FE MOV R6, A ; Update R6
00FC EF MOV A, R7 ; Get highest byte
00FD 33 RLC A ; Rotate left through carry
00FE FF MOV R7, A ; Update R7 with final << 2 high byte
00FF 74AA MOV A, #0AAh ; Load lower byte mask for XOR operation (^ 0x55AA55AA)
0101 6C XRL A, R4 ; Apply XOR mask to low byte
0102 F50F MOV 0Fh, A ; Save back to working RAM
0104 7455 MOV A, #55h ; Load next byte of XOR mask (0x55)
0106 6D XRL A, R5 ; Apply XOR mask
0107 F510 MOV 10h, A ; Save back to working RAM
0109 74AA MOV A, #0AAh ; Load next byte of XOR mask (0xAA)
010B 6E XRL A, R6 ; Apply XOR mask
010C F511 MOV 11h, A ; Save back to working RAM
; end of local_result = (local_result << 2) ^ 0x55AA55AA;
Operation Summary:
To execute the 2-bit left shift (<< 2), the compiler emulates a 32-bit multiplication by 4 by performing two successive shifts using additions and carry rotations (ADD and RLC) byte by byte across the registers.
Once this global shift is complete, it applies the bitwise XOR operation (XRL) with the immediate mask 0x55AA55AA by combining constants (0xAA and 0x55) injected directly into the accumulator, before writing the final result back to working RAM.
; start of local_result = (local_result >> 1) | (int32_t)global_var_8;
010E 7455 MOV A, #55h ; Load upper byte mask of XOR constant
0110 6F XRL A, R7 ; Apply XOR mask to high byte
0111 F512 MOV 12h, A ; Save back to working RAM
0113 E512 MOV A, 12h ; Load high byte for right shift operation (>> 1)
0115 A2E7 MOV C, ACC.7 ; Save sign bit into Carry flag
0117 13 RRC A ; Shift right through carry
0118 FF MOV R7, A ; Update shifted byte
0119 E511 MOV A, 11h ; Load next byte down
011B 13 RRC A ; Shift right through carry
011C FE MOV R6, A ; Update shifted byte
011D E510 MOV A, 10h ; Load next byte down
011F 13 RRC A ; Shift right through carry
0120 FD MOV R5, A ; Update shifted byte
0121 E50F MOV A, 0Fh ; Load low byte
0123 13 RRC A ; Shift right through carry
0124 FC MOV R4, A ; Update low shifted byte
0125 A80C MOV R0, 0Ch ; Load global variable global_var_8 address into R0
0127 E4 CLR A ; Clear accumulator
0128 F9 MOV R1, A ; Clear upper bytes for zero-extension of global_var_8
0129 FA MOV R2, A ; Clear upper bytes
012A FB MOV R3, A ; Clear upper bytes
012B E8 MOV A, R0 ; Retrieve global_var_8 value into accumulator
012C 4C ORL A, R4 ; Bitwise OR low byte with global_var_8 (| global_var_8)
012D F50F MOV 0Fh, A ; Update final low result byte
012F E9 MOV A, R1 ; Get extension byte
0130 4D ORL A, R5 ; Bitwise OR with second result byte
0131 F510 MOV 10h, A ; Update result byte
0133 EA MOV A, R2 ; Get extension byte
0134 4E ORL A, R6 ; Bitwise OR with third result byte
0135 F511 MOV 11h, A ; Update result byte
0137 EB MOV A, R3 ; Get extension byte
0138 4F ORL A, R7 ; Bitwise OR with high result byte
0139 F512 MOV 12h, A ; Update final high result byte
; end of local_result = (local_result >> 1) | (int32_t)global_var_8;
Operation Summary:
To complete the sequence, the compiler finishes the previous XOR operation on the highest byte, and then performs a 32-bit arithmetic right shift (>> 1) by preserving and propagating the sign bit via the carry flag (MOV C, ACC.7 followed by successive RRC instructions across all bytes).
Next, it zero-extends the 8-bit global_var_8 into a 32-bit value across temporary registers, applies a bitwise OR (ORL) operation combining it with the shifted result, and saves the final 32-bit word back to working RAM.
7 . Inside the function: return of the result
013B 850F82 MOV DPL, 0Fh ; Prepare return value low byte into DPL
013E 851083 MOV DPH, 10h ; Prepare return value high byte into DPH
0141 8511F0 MOV B, 11h ; Prepare return value upper-middle byte into register B
0144 E512 MOV A, 12h ; Move return value highest byte into Accumulator
0146 22 RET ; Return from function with 32-bit result split across registers
The result of the function is sent to the following registers DPL,DPH, B, A before a RET is called.
8. Conclusion
This article finishes the series on the translation in assembly by SDCC of a small C source code. Even if the C source code was small, it had a lot of features which necessitated to be explained.
This example is a textbook case demonstrating that writing standard C without regard for the target model on an 8-bit microcontroller leads to heavy and slow object code. The constant data shuttling between internal memory and registers (MOV 13h, A, etc.) illustrates why, back then, low-level developers invariably ended up bypassing the compiler to rewrite critical portions directly by hand.
I hope that you have enjoyed your journey into the intricacies of the SDCC translation. See you next time for another series.
9. The full disassembly code
CSEG AT 0000h
0000 020006 LJMP L0001
L0004:
0003 020147 LJMP L0005
L0001:
0006 758118 MOV SP, #18h
0009 1202C6 LCALL L0002
000C E582 MOV A, DPL
000E 6003 JZ L0003
0010 020003 LJMP L0004
L0003:
0013 7900 MOV R1, #0h
0015 E9 MOV A, R1
0016 4400 ORL A, #0h
0018 601B JZ L0025
001A 7A00 MOV R2, #0h
001C 9002D8 MOV DPTR, #02D8h
001F 7801 MOV R0, #1h
0021 75A000 MOV P2, #0h
L0027:
0024 E4 CLR A
0025 93 MOVC A, @A+DPTR
0026 F2 MOVX @R0, A
0027 A3 INC DPTR
0028 08 INC R0
0029 B80002 CJNE R0, #0h, L0026
002C 05A0 INC P2
L0026:
002E D9F4 DJNZ R1, L0027
0030 DAF2 DJNZ R2, L0027
0032 75A0FF MOV P2, #0FFh
L0025:
0035 E4 CLR A
0036 78FF MOV R0, #0FFh
L0028:
0038 F6 MOV @R0, A
0039 D8FD DJNZ R0, L0028
003B 7800 MOV R0, #0h
003D E8 MOV A, R0
003E 4400 ORL A, #0h
0040 600A JZ L0029
0042 7901 MOV R1, #1h
0044 75A000 MOV P2, #0h
0047 E4 CLR A
L0030:
0048 F3 MOVX @R1, A
0049 09 INC R1
004A D8FC DJNZ R0, L0030
L0029:
004C 7800 MOV R0, #0h
004E E8 MOV A, R0
004F 4400 ORL A, #0h
0051 600C JZ L0031
0053 7900 MOV R1, #0h
0055 900001 MOV DPTR, #0001h
0058 E4 CLR A
L0032:
0059 F0 MOVX @DPTR, A
005A A3 INC DPTR
005B D8FC DJNZ R0, L0032
005D D9FA DJNZ R1, L0032
L0031:
005F 7508EF MOV 8h, #0EFh
0062 7509BE MOV 9h, #0BEh
0065 750AAD MOV 0Ah, #0ADh
0068 750BDE MOV 0Bh, #0DEh
006B 750C42 MOV 0Ch, #42h
006E 020003 LJMP L0004
L0013:
0071 AE82 MOV R6, DPL
0073 AF83 MOV R7, DPH
0075 E4 CLR A
0076 F50F MOV 0Fh, A
0078 F510 MOV 10h, A
007A F511 MOV 11h, A
007C F512 MOV 12h, A
007E 850D17 MOV 17h, 0Dh
0081 850E18 MOV 18h, 0Eh
0084 8E82 MOV DPL, R6
0086 8F83 MOV DPH, R7
0088 C007 PUSH 7h
008A C006 PUSH 6h
008C 120248 LCALL L0015
008F AC82 MOV R4, DPL
0091 AD83 MOV R5, DPH
0093 D006 POP 6h
0095 D007 POP 7h
0097 ED MOV A, R5
0098 33 RLC A
0099 95E0 SUBB A, ACC
009B FB MOV R3, A
009C FA MOV R2, A
009D EC MOV A, R4
009E 250F ADD A, 0Fh
00A0 F50F MOV 0Fh, A
00A2 ED MOV A, R5
00A3 3510 ADDC A, 10h
00A5 F510 MOV 10h, A
00A7 EB MOV A, R3
00A8 3511 ADDC A, 11h
00AA F511 MOV 11h, A
00AC EA MOV A, R2
00AD 3512 ADDC A, 12h
00AF F512 MOV 12h, A
00B1 AC0D MOV R4, 0Dh
00B3 AD0E MOV R5, 0Eh
00B5 7401 MOV A, #1h
00B7 4C ORL A, R4
00B8 F517 MOV 17h, A
00BA 8D18 MOV 18h, R5
00BC 8E82 MOV DPL, R6
00BE 8F83 MOV DPH, R7
00C0 12028E LCALL L0016
00C3 AE82 MOV R6, DPL
00C5 E583 MOV A, DPH
00C7 FF MOV R7, A
00C8 33 RLC A
00C9 95E0 SUBB A, ACC
00CB FD MOV R5, A
00CC FC MOV R4, A
00CD E50F MOV A, 0Fh
00CF C3 CLR C
00D0 9E SUBB A, R6
00D1 F50F MOV 0Fh, A
00D3 E510 MOV A, 10h
00D5 9F SUBB A, R7
00D6 F510 MOV 10h, A
00D8 E511 MOV A, 11h
00DA 9D SUBB A, R5
00DB F511 MOV 11h, A
00DD E512 MOV A, 12h
00DF 9C SUBB A, R4
00E0 F512 MOV 12h, A
00E2 E50F MOV A, 0Fh
00E4 25E0 ADD A, ACC
00E6 FC MOV R4, A
00E7 E510 MOV A, 10h
00E9 33 RLC A
00EA FD MOV R5, A
00EB E511 MOV A, 11h
00ED 33 RLC A
00EE FE MOV R6, A
00EF E512 MOV A, 12h
00F1 33 RLC A
00F2 FF MOV R7, A
00F3 EC MOV A, R4
00F4 2C ADD A, R4
00F5 FC MOV R4, A
00F6 ED MOV A, R5
00F7 33 RLC A
00F8 FD MOV R5, A
00F9 EE MOV A, R6
00FA 33 RLC A
00FB FE MOV R6, A
00FC EF MOV A, R7
00FD 33 RLC A
00FE FF MOV R7, A
00FF 74AA MOV A, #0AAh
0101 6C XRL A, R4
0102 F50F MOV 0Fh, A
0104 7455 MOV A, #55h
0106 6D XRL A, R5
0107 F510 MOV 10h, A
0109 74AA MOV A, #0AAh
010B 6E XRL A, R6
010C F511 MOV 11h, A
010E 7455 MOV A, #55h
0110 6F XRL A, R7
0111 F512 MOV 12h, A
0113 E512 MOV A, 12h
0115 A2E7 MOV C, ACC.7
0117 13 RRC A
0118 FF MOV R7, A
0119 E511 MOV A, 11h
011B 13 RRC A
011C FE MOV R6, A
011D E510 MOV A, 10h
011F 13 RRC A
0120 FD MOV R5, A
0121 E50F MOV A, 0Fh
0123 13 RRC A
0124 FC MOV R4, A
0125 A80C MOV R0, 0Ch
0127 E4 CLR A
0128 F9 MOV R1, A
0129 FA MOV R2, A
012A FB MOV R3, A
012B E8 MOV A, R0
012C 4C ORL A, R4
012D F50F MOV 0Fh, A
012F E9 MOV A, R1
0130 4D ORL A, R5
0131 F510 MOV 10h, A
0133 EA MOV A, R2
0134 4E ORL A, R6
0135 F511 MOV 11h, A
0137 EB MOV A, R3
0138 4F ORL A, R7
0139 F512 MOV 12h, A
013B 850F82 MOV DPL, 0Fh
013E 851083 MOV DPH, 10h
0141 8511F0 MOV B, 11h
0144 E512 MOV A, 12h
0146 22 RET
L0005:
0147 E4 CLR A
0148 F513 MOV 13h, A
014A F514 MOV 14h, A
014C F515 MOV 15h, A
014E F516 MOV 16h, A
0150 7E00 MOV R6, #0h
0152 7F00 MOV R7, #0h
L0009:
0154 8E04 MOV 4h, R6
0156 8F05 MOV 5h, R7
0158 BC051A CJNE R4, #5h, L0006
015B BD0017 CJNE R5, #0h, L0006
015E 7464 MOV A, #64h
0160 2513 ADD A, 13h
0162 F513 MOV 13h, A
0164 E4 CLR A
0165 3514 ADDC A, 14h
0167 F514 MOV 14h, A
0169 E4 CLR A
016A 3515 ADDC A, 15h
016C F515 MOV 15h, A
016E E4 CLR A
016F 3516 ADDC A, 16h
0171 F516 MOV 16h, A
0173 801D SJMP L0007
L0006:
0175 8E02 MOV 2h, R6
0177 EF MOV A, R7
0178 FB MOV R3, A
0179 33 RLC A
017A 95E0 SUBB A, ACC
017C FC MOV R4, A
017D FD MOV R5, A
017E EA MOV A, R2
017F 2513 ADD A, 13h
0181 F513 MOV 13h, A
0183 EB MOV A, R3
0184 3514 ADDC A, 14h
0186 F514 MOV 14h, A
0188 EC MOV A, R4
0189 3515 ADDC A, 15h
018B F515 MOV 15h, A
018D ED MOV A, R5
018E 3516 ADDC A, 16h
0190 F516 MOV 16h, A
L0007:
0192 0E INC R6
0193 BE0001 CJNE R6, #0h, L0008
0196 0F INC R7
L0008:
0197 8E04 MOV 4h, R6
0199 8F05 MOV 5h, R7
019B C3 CLR C
019C EC MOV A, R4
019D 940A SUBB A, #0Ah
019F ED MOV A, R5
01A0 6480 XRL A, #80h
01A2 9480 SUBB A, #80h
01A4 40AE JC L0009
01A6 AF0C MOV R7, 0Ch
01A8 BF1002 CJNE R7, #10h, L0010
01AB 8005 SJMP L0011
L0010:
01AD BF2030 CJNE R7, #20h, L0023
01B0 8017 SJMP L0024
L0011:
01B2 740A MOV A, #0Ah
01B4 2513 ADD A, 13h
01B6 F513 MOV 13h, A
01B8 E4 CLR A
01B9 3514 ADDC A, 14h
01BB F514 MOV 14h, A
01BD E4 CLR A
01BE 3515 ADDC A, 15h
01C0 F515 MOV 15h, A
01C2 E4 CLR A
01C3 3516 ADDC A, 16h
01C5 F516 MOV 16h, A
01C7 802F SJMP L0012
L0024:
01C9 7414 MOV A, #14h
01CB 2513 ADD A, 13h
01CD F513 MOV 13h, A
01CF E4 CLR A
01D0 3514 ADDC A, 14h
01D2 F514 MOV 14h, A
01D4 E4 CLR A
01D5 3515 ADDC A, 15h
01D7 F515 MOV 15h, A
01D9 E4 CLR A
01DA 3516 ADDC A, 16h
01DC F516 MOV 16h, A
01DE 8018 SJMP L0012
L0023:
01E0 E513 MOV A, 13h
01E2 24FB ADD A, #0FBh
01E4 F513 MOV 13h, A
01E6 E514 MOV A, 14h
01E8 34FF ADDC A, #0FFh
01EA F514 MOV 14h, A
01EC E515 MOV A, 15h
01EE 34FF ADDC A, #0FFh
01F0 F515 MOV 15h, A
01F2 E516 MOV A, 16h
01F4 34FF ADDC A, #0FFh
01F6 F516 MOV 16h, A
L0012:
01F8 851382 MOV DPL, 13h
01FB 851483 MOV DPH, 14h
01FE 750D03 MOV 0Dh, #3h
0201 750E00 MOV 0Eh, #0h
0204 120071 LCALL L0013
0207 AC82 MOV R4, DPL
0209 AD83 MOV R5, DPH
020B AEF0 MOV R6, B
020D FF MOV R7, A
020E EC MOV A, R4
020F 2513 ADD A, 13h
0211 F513 MOV 13h, A
0213 ED MOV A, R5
0214 3514 ADDC A, 14h
0216 F514 MOV 14h, A
0218 EE MOV A, R6
0219 3515 ADDC A, 15h
021B F515 MOV 15h, A
021D EF MOV A, R7
021E 3516 ADDC A, 16h
0220 F516 MOV 16h, A
0222 AC13 MOV R4, 13h
0224 AD14 MOV R5, 14h
0226 AE15 MOV R6, 15h
0228 AF16 MOV R7, 16h
022A 8C08 MOV 8h, R4
022C 8D09 MOV 9h, R5
022E 8E0A MOV 0Ah, R6
0230 8F0B MOV 0Bh, R7
L0014:
0232 AC08 MOV R4, 8h
0234 AD09 MOV R5, 9h
0236 AE0A MOV R6, 0Ah
0238 AF0B MOV R7, 0Bh
023A EC MOV A, R4
023B 6213 XRL 13h, A
023D ED MOV A, R5
023E 6214 XRL 14h, A
0240 EE MOV A, R6
0241 6215 XRL 15h, A
0243 EF MOV A, R7
0244 6216 XRL 16h, A
0246 80EA SJMP L0014
L0015:
0248 E582 MOV A, DPL
024A 8517F0 MOV B, 17h
024D A4 MUL AB
024E C582 XCH A, DPL
0250 C0F0 PUSH B
0252 8518F0 MOV B, 18h
0255 A4 MUL AB
0256 D0F0 POP B
0258 25F0 ADD A, B
025A C583 XCH A, DPH
025C 8517F0 MOV B, 17h
025F A4 MUL AB
0260 2583 ADD A, DPH
0262 F583 MOV DPH, A
0264 22 RET
L0019:
0265 7A10 MOV R2, #10h
0267 E4 CLR A
0268 FB MOV R3, A
0269 FC MOV R4, A
L0022:
026A E582 MOV A, DPL
026C 25E0 ADD A, ACC
026E F582 MOV DPL, A
0270 E583 MOV A, DPH
0272 33 RLC A
0273 F583 MOV DPH, A
0275 EB MOV A, R3
0276 33 RLC A
0277 FB MOV R3, A
0278 EC MOV A, R4
0279 33 RLC A
027A FC MOV R4, A
027B EB MOV A, R3
027C 9517 SUBB A, 17h
027E F5F0 MOV B, A
0280 EC MOV A, R4
0281 9518 SUBB A, 18h
0283 4006 JC L0021
0285 FC MOV R4, A
0286 ABF0 MOV R3, B
0288 438201 ORL DPL, #1h
L0021:
028B DADD DJNZ R2, L0022
028D 22 RET
L0016:
028E C2D5 CLR F0
0290 E583 MOV A, DPH
0292 30E70D JNB ACC.7, L0017
0295 D2D5 SETB F0
0297 E4 CLR A
0298 C3 CLR C
0299 9582 SUBB A, DPL
029B F582 MOV DPL, A
029D E4 CLR A
029E 9583 SUBB A, DPH
02A0 F583 MOV DPH, A
L0017:
02A2 E518 MOV A, 18h
02A4 30E70D JNB ACC.7, L0018
02A7 B2D5 CPL F0
02A9 E4 CLR A
02AA C3 CLR C
02AB 9517 SUBB A, 17h
02AD F517 MOV 17h, A
02AF E4 CLR A
02B0 9518 SUBB A, 18h
02B2 F518 MOV 18h, A
L0018:
02B4 120265 LCALL L0019
02B7 30D50B JNB F0, L0020
02BA E4 CLR A
02BB C3 CLR C
02BC 9582 SUBB A, DPL
02BE F582 MOV DPL, A
02C0 E4 CLR A
02C1 9583 SUBB A, DPH
02C3 F583 MOV DPH, A
L0020:
02C5 22 RET
L0002:
02C6 758200 MOV DPL, #0h
02C9 22 RET
02CA 54 DB 054h ; 'T'
02CB 41 DB 041h ; 'A'
02CC 52 DB 052h ; 'R'
02CD 47 DB 047h ; 'G'
02CE 45 DB 045h ; 'E'
02CF 54 DB 054h ; 'T'
02D0 5F DB 05Fh ; '_'
02D1 53 DB 053h ; 'S'
02D2 54 DB 054h ; 'T'
02D3 52 DB 052h ; 'R'
02D4 49 DB 049h ; 'I'
02D5 4E DB 04Eh ; 'N'
02D6 47 DB 047h ; 'G'
02D7 00 DB 000h
END