8051 What does SDCC do part 3 ?

dev.to

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;
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
callee_function((int16_t)accumulator, 3);
int32_t callee_function(int16_t a, int16_t b) {
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
  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
Enter fullscreen mode Exit fullscreen mode

5. Inside the function: Arithmetic operations

 // Basic and mixed arithmetic operations (8, 16, 32 bits)
    local_result += (int32_t)(a * b);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

pretty straightforward

Then we have the division with the line below

 local_result -= (int32_t)(a / (b | 1)); // Avoid division by zero
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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:

  1. 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.

  1. 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.

  2. 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).

  1. 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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Source: dev.to

arrow_back Back to News