Contenido>Indice>Intro CursoC51

SOLAPAMIENTO DE VARIABLES DATA REALIZADO POR L51



Principios de solapamiento

Una de las principales características de C51 es la función de solapamiento, o mecanismo mediante el cual diferentes variables del programa utilizan las mismas posiciones de RAM interna. La posibilidad de utilizar solapamiento surge cuando se declaran variables automáticas. Estas variables tienen una vida muy corta, se crean al entrar a la función o bloque que las declara, y desaparecen después de la llave '}' de final de bloque o función. En consecuencia,  el área ocupada por las variables automáticas queda libre y puede ser utilizada por otras funciones. Lo mismo sucede con el área de memoria utilizada por C51 para el paso de parámetros.

Si a lo largo de un programa, cada función conservara el área de  memoria asignada a sus variables y parámetros, la RAM interna del 8051 se agotaría incluso con pequeños programas

En C51, el linker es quien se encarga de aplicar el mecanismo de solapamiento. El linker examina las necesidades de RAM interna de todas las funciones, y las llamadas que estas realizan entre sí, y con esa información determina los segmentos data y bit que pueden solaparse. El uso de los registros para ubicación de variables temporales también tiende a reducir las necesidades en el segmento data

La función de solapamiento se basa en que si la función 1 llama a la función 2, sus áreas data no pueden solaparse, ya que ambas se encuentran activas al mismo tiempo. Una tercera función 3, también llamada por la 1, si puede solaparse con la 2, ya que las dos no pueden correr a la vez.

            main
             |
            funcA - func2 - func3 - func4
             |
            funcB - func5 - func6 - func7
             |
            funcC - func8 - func9 - func10
             |  

Como la función A llama a func2, y func2 llama a func3 etc., A, 2, 3 y 4 no pueden solapar sus áreas data. De igual forma B, 5, 6, y 7 tampoco pueden solaparse. Sin embargo los grupos 2,3,4; 5,6,7 y 8,9,10 si pueden solapar sus áreas data, ya que pertenecen a ramas distintas. Esta es la base de la estrategia de solapamiento.

Sin embargo las funciones de interrupción pueden producirse en cualquier momento, y si no se tomase un cuidado especial con ellas, podrían sobre-escribir las áreas data del programa principal (o de las interrupciones de menor prioridad). Para evitarlo, C51 identifica a las funciones de interrupción y a las funciones llamadas por estas, y las ubica en áreas de memoria individuales.

Impacto del solapamiento en la construcción de programas

La regla general utilizada por L51 es que si dos funciones nunca se ejecutan a la vez, sus áreas data pueden solaparse. En el 99% de los casos el mecanismo de solapamiento trabaja correctamente, pero en algunas ocasiones pueden obtenerse resultados inesperados. Estos pocos casos son:

  1. Las funciones llamadas indirectamente utilizando punteros
  2. Las funciones llamadas desde tablas de llamada a funciones
  3. Las funciones re-entrantes no declaradas de la forma apropiada

Cuando se dan estas condiciones el linker puede emitir los siguientes avisos:

MULTIPLE CALL TO SEGMENT
UNCALLED SEGMENT
RECURSIVE CALL TO SEGMENT

Llamada indirecta a función mediante punteros

En el siguiente ejemplo, func4 y func5 se llaman desde main por medio de una función llamada EJECUTA que recibe como parámetro un puntero a la función a llamar. Cuando L51 analiza el programa, no es capaz de establecer un vínculo entre las funciones 4 y 5 porque el puntero a función que recibe como parámetro rompe la cadena de referencias (El valor del puntero a función es indeterminado durante el linkado). Por ello, L51 solapa los segmentos data de func4, func5 y ejecuta como si todas ellas fueran llamadas desde main.

Como resultado de lo anterior, las variables locales de func4 y func5 pueden destruir las variables locales de ejecuta, lo cual es MUY peligroso, especialmente cuando la sobre-escritura de variables no resulta obvia, y se produce solamente bajo condiciones anormales de operación. Ejemplo:

 
/*****************************************************************
*  Riesgo de solapamiento 1 - Funciones llamadas indirectamente  *
******************************************************************/
#include<reg51.h>
char func1(void) {    // Función llamada directamente
 char x, arr[10] ;
for(x = 0 ; x < 10 ; x++) {
   arr[x] = x ;
   }
return(x) ; 
}

char func2(void) {   // Función llamada directamente
//(.... Código C ...)
return 2;
}

char func3(void) {   // Función llamada directamente
//(.... Código C ...)
return(3); 
}

char func4(void) {   // Función llamada indirectamente
char x4, arr4[10] ;  // variables locales
for(x4 = 0 ; x4 < 10 ; x4++) {
   arr4[x4] = x4 ;
   }
return(x4) ; 
}

char func5(void) {   // Función llamada indirectamente
char x5, arr5[10] ;  // variables locales 
for(x5 = 0 ; x5 < 10 ; x5++) {
   arr5[x5] = x5 ;
   }
return(x5) ; 
}

/*** Función que llama a otras funciones ***/

char ejecuta(char (*fptr)())  // 
   { // char (*fptr)(void) fptr es un puntero a función que retorna un char
   char tex ;       // variables locales de ejecuta
   char arrex[10] ;       
   for(tex = 0 ; tex < 10 ; tex++) {
      arrex[tex] = (*fptr)() ;
      }
   return(tex) ;
   }

/*** Declaración de array de punteros a función ***/

char (code *fp[3])(void) ; 

/*** Llamadas a función desde main ***/

void main(void) {
   char am ;

   fp[0] = func1 ;     // Elementos del array de punteros a funciones
   fp[1] = func2 ;
   fp[2] = func3 ;

   am = (* fp[0])() ;        // Ejecución de las funciones
   am = (* fp[1])() ;
   am = (* fp[2])() ;

   if(P1) {            // Control para llamadas indirectas a función
      am = ejecuta(func4) ;
      }
   else {
      am = ejecuta(func5) ;
      }
   }

Resultados de condiciones peligrosas en el fichero '.M51' de salida del linker.

MS-DOS L51 LINKER/LOCATOR V3.70c, INVOKED BY:
E:\KEIL\BIN\L51.EXE MAIN.OBJ


MEMORY MODEL: SMALL


INPUT MODULES INCLUDED:
  MAIN.OBJ (MAIN)
  E:\KEIL\LIB\C51S.LIB (?C_STARTUP)
  E:\KEIL\LIB\C51S.LIB (?C?ICALL)


LINK MAP OF MODULE:  MAIN (MAIN)


            TYPE    BASE      LENGTH    RELOCATION   SEGMENT NAME
            -----------------------------------------------------

            * * * * * * *   D A T A   M E M O R Y   * * * * * * *
            REG     0000H     0008H     ABSOLUTE     "REG BANK 0"
            DATA    0008H     000FH     UNIT         _DATA_GROUP_
            DATA    0017H     0006H     UNIT         ?DT?MAIN
            IDATA   001DH     0001H     UNIT         ?STACK

            * * * * * * *   C O D E   M E M O R Y   * * * * * * *
            CODE    0000H     0003H     ABSOLUTE     
            CODE    0003H     0049H     UNIT         ?PR?MAIN?MAIN
            ......................................................    


OVERLAY MAP OF MODULE:   MAIN (MAIN)


SEGMENT                          DATA_GROUP 
  +--> CALLED SEGMENT          START    LENGTH
----------------------------------------------
?C_C51STARTUP                  -----    -----
  +--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN                  0008H    0001H
  +--> ?PR?FUNC1?MAIN
  +--> ?PR?FUNC2?MAIN
  +--> ?PR?FUNC3?MAIN
  +--> ?PR?FUNC4?MAIN
  +--> ?PR?_EJECUTA?MAIN
  +--> ?PR?FUNC5?MAIN

?PR?FUNC1?MAIN                 0009H    000AH

?PR?FUNC4?MAIN                 0009H    000AH // Peligro de solapamiento
  
?PR?_EJECUTA?MAIN              0009H    000EH // con FUNC4, EJECUTA y FUNC5

?PR?FUNC5?MAIN                 0009H    000AH 


SYMBOL TABLE OF MODULE:  MAIN (MAIN)

  VALUE           TYPE          NAME
  ----------------------------------

  -------         MODULE        MAIN
  D:0090H         PUBLIC        P1
  D:0017H         PUBLIC        FP

  -------         PROC          FUNC1
  D:0007H         SYMBOL        X
  D:0009H         SYMBOL        ARR
  -------         ENDPROC       FUNC1

  -------         PROC          FUNC4
  D:0007H         SYMBOL        X4
  D:0009H         SYMBOL        ARR4       // peligro
  -------         ENDPROC       FUNC4

  -------         PROC          FUNC5
  D:0007H         SYMBOL        X5
  D:0009H         SYMBOL        ARR5       // peligro
  -------         ENDPROC       FUNC5

  -------         PROC          _EJECUTA
  D:0009H         SYMBOL        FPTR       // peligro
  D:000CH         SYMBOL        TEX
  D:000DH         SYMBOL        ARREX
  -------         ENDPROC       _EJECUTA

  -------         PROC          MAIN
  D:0008H         SYMBOL        AM
  -------         ENDPROC       MAIN
LINK/LOCATE RUN COMPLETE.  0 WARNING(S),  0 ERROR(S)

Solución al caso de llamada indirecta a funciones

El problema señalado en el apartado anterior se soluciona mediante el uso del comando OVERLAY durante el proceso de linkado:

L51 main.obj   OVERLAY(main ~ (func4,func5), _ejecuta ! (func4,func5))

Nota: El signo tilde '~' significa: "Ignorar las referencias a func4/5 desde main" El signo '!' significa: "Añadir a la lista las referencias entre la función 'ejecuta' y func4/5 para evitar el solapamiento de las variables locales de estas funciones."

La nueva salida del linker es:

MS-DOS L51 LINKER/LOCATOR V3.70c, INVOKED BY:
E:\KEIL\BIN\L51.EXE MAIN.OBJ OVERLAY (MAIN ~(FUNC4, FUNC5),_EJECUTA !(FUNC4, FUNC5))


MEMORY MODEL: SMALL


INPUT MODULES INCLUDED:
  MAIN.OBJ (MAIN)
  E:\KEIL\LIB\C51S.LIB (?C_STARTUP)
  E:\KEIL\LIB\C51S.LIB (?C?ICALL)


LINK MAP OF MODULE:  MAIN (MAIN)


            TYPE    BASE      LENGTH    RELOCATION   SEGMENT NAME
            -----------------------------------------------------

            * * * * * * *   D A T A   M E M O R Y   * * * * * * *
            REG     0000H     0008H     ABSOLUTE     "REG BANK 0"
            DATA    0008H     0019H     UNIT         _DATA_GROUP_
            DATA    0021H     0006H     UNIT         ?DT?MAIN
            IDATA   0027H     0001H     UNIT         ?STACK

            * * * * * * *   C O D E   M E M O R Y   * * * * * * *
            CODE    0000H     0003H     ABSOLUTE     


OVERLAY MAP OF MODULE:   MAIN (MAIN)


SEGMENT                          DATA_GROUP 
  +--> CALLED SEGMENT          START    LENGTH
----------------------------------------------
?C_C51STARTUP                  -----    -----
  +--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN                  0008H    0001H
  +--> ?PR?FUNC1?MAIN
  +--> ?PR?FUNC2?MAIN
  +--> ?PR?FUNC3?MAIN
  +--> ?PR?_EJECUTA?MAIN

?PR?FUNC1?MAIN                 0009H    000AH

?PR?_EJECUTA?MAIN              0009H    000EH
  +--> ?PR?FUNC4?MAIN
  +--> ?PR?FUNC5?MAIN

?PR?FUNC4?MAIN                 0017H    000AH

?PR?FUNC5?MAIN                 0017H    000AH



SYMBOL TABLE OF MODULE:  MAIN (MAIN)

  VALUE           TYPE          NAME
  ----------------------------------

  -------         MODULE        MAIN
  D:0090H         PUBLIC        P1
  D:0021H         PUBLIC        FP

  -------         PROC          FUNC1
  D:0007H         SYMBOL        X
  D:0009H         SYMBOL        ARR
  -------         ENDPROC       FUNC1


  -------         PROC          FUNC4
  D:0007H         SYMBOL        X4
  D:0017H         SYMBOL        ARR4
  -------         ENDPROC       FUNC4

  -------         PROC          FUNC5
  D:0007H         SYMBOL        X5
  D:0017H         SYMBOL        ARR5
  -------         ENDPROC       FUNC5

  -------         PROC          _EJECUTA
  D:0009H         SYMBOL        FPTR
  D:000CH         SYMBOL        TEX
  D:000DH         SYMBOL        ARREX
  -------         ENDPROC       _EJECUTA


  -------         PROC          MAIN
  D:0008H         SYMBOL        AM
  -------         ENDPROC       MAIN
  -------         ENDMOD        MAIN

LINK/LOCATE RUN COMPLETE.  0 WARNING(S),  0 ERROR(S)

Avisos en las llamadas a función mediante tablas

En el siguiente ejemplo se hacen llamadas a dos funciones por medio de un array de punteros a funciones. La tabla "tabla_saltos" existe en un segmento llamado "?CO?MAIN", es decir en el área de constantes asignada al módulo main. El problema surge debido a que los argumentos cadena de printf, también residen aquí. lo cual conduce una definición recursiva de la dirección de comienzo de las funciones en la tabla de saltos. Aunque ello no es peligroso, evita que se establezcan las referencias reales de las funciones y se inhibe el proceso de solapamiento.

#include <stdio.h>
#include <reg517.h>

void func1(void) {
   unsigned char i1 ;
   for(i1 = 0 ; i1 < 10 ; i1++) {
      printf("Esta es la función 1\n") ; // Cadena almacenada en
                                        // el segmento ?CO?MAIN
      }
   }

void func2(void) {   
   unsigned char i2 ;
   for(i2 = 0 ; i2 < 10 ; i2++) {
      printf("Esta es la función 2\n") ; // Cadena almacenada en
                                        // el segmento ?CO?MAIN
      }
   }

code void(*tabla_saltos[])()={func1,func2}; // Tabla de saltos a 
                                           // funciones, almacenada
                                          // en el segmento ?CO?MAIN
/*** Llamada a las funciones ***/

void main(void) {
   (*tabla_saltos[P1 & 0x01])() ;   // Llamada a función por medio
                                   // de la tabla en ?CO?MAIN
   }/* FIN DE main */

 

La salida '.MAP' del linker es:

MS-DOS L51 LINKER/LOCATOR V3.70c, INVOKED BY:
E:\KEIL\BIN\L51.EXE MAIN.OBJ


OVERLAY MAP OF MODULE:   MAIN (MAIN)


SEGMENT                           BIT_GROUP          DATA_GROUP 
  +--> CALLED SEGMENT          START    LENGTH     START    LENGTH
------------------------------------------------------------------
?C_C51STARTUP                  -----    -----      -----    -----
  +--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN                  -----    -----      -----    -----
  +--> ?CO?MAIN

?CO?MAIN                       -----    -----      -----    -----
  +--> ?PR?FUNC1?MAIN
  +--> ?PR?FUNC2?MAIN

?PR?FUNC1?MAIN                 -----    -----      0008H    0001H
  +--> ?PR?PRINTF?PRINTF


?PR?PRINTF?PRINTF              0020H.0  0001H.1    0009H    0014H
  +--> ?PR?PUTCHAR?PUTCHAR

?PR?FUNC2?MAIN                 -----    -----      0008H    0001H
  +--> ?PR?PRINTF?PRINTF


*** WARNING 13: RECURSIVE CALL TO SEGMENT
    SEGMENT: ?CO?MAIN
    CALLER:  ?PR?FUNC1?MAIN

*** WARNING 13: RECURSIVE CALL TO SEGMENT
    SEGMENT: ?CO?MAIN
    CALLER:  ?PR?FUNC2?MAIN

LINK/LOCATE RUN COMPLETE.  2 WARNING(S),  0 ERROR(S)

Solución al caso de la tabla de saltos a funciones

La solución es usar el comando OVERLAY durante el linkado de la siguiente forma:

L51 main.obj OVERLAY(?CO?MAIN ~ (func1,func2), main ! (func1,func2))

La línea anterior borra las referencias  desde ?CO?MAIN hacia func1 y func2, e inserta las verdaderas referencias desde main hacia func1 y func2. La salida que produce ahora el linker es:

MS-DOS L51 LINKER/LOCATOR V3.70c, INVOKED BY:
E:\KEIL\BIN\L51.EXE MAIN.OBJ OVERLAY(?CO?MAIN ~(FUNC1, FUNC2), MAIN !(FUNC1, FUNC2))


OVERLAY MAP OF MODULE:   MAIN (MAIN)


SEGMENT                           BIT_GROUP          DATA_GROUP 
  +--> CALLED SEGMENT          START    LENGTH     START    LENGTH
------------------------------------------------------------------
?C_C51STARTUP                  -----    -----      -----    -----
  +--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN                  -----    -----      -----    -----
  +--> ?CO?MAIN
  +--> ?PR?FUNC1?MAIN
  +--> ?PR?FUNC2?MAIN

?PR?FUNC1?MAIN                 -----    -----      0008H    0001H
  +--> ?CO?MAIN
  +--> ?PR?PRINTF?PRINTF

?PR?PRINTF?PRINTF              0020H.0  0001H.1    0009H    0014H


  +--> ?PR?PUTCHAR?PUTCHAR

?PR?FUNC2?MAIN                 -----    -----      0008H    0001H
  +--> ?CO?MAIN
  +--> ?PR?PRINTF?PRINTF


LINK/LOCATE RUN COMPLETE.  0 WARNING(S),  0 ERROR(S)

Avisos de llamadas múltiples a segmentos

Este aviso sucede generalmente cuando se llama a una función desde el programa principal y desde una interrupción. Significa que potencialmente la interrupción puede llamar a una función que se encuentra corriendo por haber sido llamada desde el programa principal. La solución más sencilla es declarar a la función como REENTRANT para que el compilador genere una pila local para sus parámetros y variables. Así en cada llamada a la función se crea un nuevo conjunto parámetros y de variables locales sin destruir el conjunto existente.

Esta solución aumenta de forma significativa el tiempo de ejecución y el código generado. Otra posibilidad es la creación de una segunda versión de la función, una para el programa principal y otra para la interrupción, lo cual puede plantear un problema de mantenimiento, ya que se tienen dos versiones del mismo código.

En algunos casos la situación no es problemática, si el usuario ha procurado que el uso re-entrante de la función no suceda nunca, y se puede ignorar el aviso del linker. Esto puede lograrse permitiendo la interrupción solo cuando la llamada  a la función sea segura. Sin embargo esta situación es peligrosa sobre todo si intervienen otros programadores. Ejemplo:

void func1(void) {
      unsigned char i1,a1[15] ;
      for(i1 = 0 ; i1 < 10 ; i1++) {
      a1[i1] = i1 ; 
      }
   }

void func2(void) {
   // ( Código C de func2)
   }

void timer0_int(void) interrupt 1 {
   func1() ;
   }

main() {
   func1() ;
   func2() ;
   }/* FIN DE main */

 



El linker produce la siguiente salida:


OVERLAY MAP OF MODULE:   MAIN (MAIN)


SEGMENT                          DATA_GROUP 
  +--> CALLED SEGMENT          START    LENGTH
----------------------------------------------
?PR?TIMER0_INT?MAIN            -----    -----
  +--> ?PR?FUNC1?MAIN

?PR?FUNC1?MAIN                 0017H    000FH

*** NEW ROOT ***************************************************

?C_C51STARTUP                  -----    -----
  +--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN                  -----    -----
  +--> ?PR?FUNC1?MAIN
  +--> ?PR?FUNC2?MAIN

*** WARNING 15: MULTIPLE CALL TO SEGMENT
    SEGMENT: ?PR?FUNC1?MAIN
    CALLER1: ?PR?TIMER0_INT?MAIN
    CALLER2: ?C_C51STARTUP

LINK/LOCATE RUN COMPLETE.  1 WARNING(S),  0 ERROR(S)

Solución al caso de las llamadas múltiples a segmentos

Las posibles soluciones son:

(i) Declarar la función func1 como re-entrante:

void func1(void) reentrant {  }

(ii) Usar la opción OVERLAY del linker:

L51 main.obj OVERLAY(main ~ func1,timer0_int ~ func1)

para romper las conexiones entre func1 y las funciones main y timer0_int. En este segundo caso se obtiene la siguiente salida del linker:

OVERLAY MAP OF MODULE:   MAIN (MAIN)


SEGMENT
  +--> CALLED SEGMENT
---------------------
?C_C51STARTUP
  +--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN
  +--> ?PR?FUNC2?MAIN

*** WARNING 16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS
    SEGMENT: ?PR?FUNC1?MAIN

LINK/LOCATE RUN COMPLETE.  1 WARNING(S),  0 ERROR(S)

Lo que significa que no se producirá solapamiento entre func1 y otras funciones del programa principal. El aviso puede evitarse eliminando solamente el vínculo entre func1 y la interrupción:

L51 main.obj OVERLAY(timer0_int ~ func1)

Lo que produce la siguiente salida del linker:

OVERLAY MAP OF MODULE: MAIN (MAIN)


SEGMENT DATA_GROUP 
+--> CALLED SEGMENT START LENGTH
----------------------------------------------
?C_C51STARTUP ----- -----
+--> ?PR?MAIN?MAIN

?PR?MAIN?MAIN ----- -----
+--> ?PR?FUNC1?MAIN
+--> ?PR?FUNC2?MAIN

?PR?FUNC1?MAIN 0008H 000FH

LINK/LOCATE RUN COMPLETE. 0 WARNING(S), 0 ERROR(S)

También se puede deshabilitar totalmente el solapamiento, aunque se trata de una mala solución que puede agotar rápidamente la RAM interna: 

main2.obj  to main2.abs  NOOVERLAY

Resumiendo, con el aviso "MULTIPLE CALL TO SEGMENT WARNING" la única solución realmente segura es declarar func1 como REENTRANT, quedando la opción de duplicar la función como la segunda mejor opción. El uso del comando OVERLAY corre el peligro de que un nuevo programador con menos experiencia no se de cuenta de que la interrupción se encuentra restringida a los instantes en que pueda llamar con seguridad a la función.

Solapamiento de variables públicas

Los ejemplos anteriores se refieren exclusivamente al solapamiento de los parámetros y variables locales de las funciones. Otra situación diferente se planteó recientemente con un programa dividido en dos mitades. El 8051 debía de ejecutar una u otra mitad en función de la entrada que le proporcionara un usuario durante la fase de inicialización.

Cada mitad del programa tenía un gran número de variables públicas, algunas de las cuales eran comunes a las dos mitades del programa.

Este tipo de estructura de programa necesita una clase de almacenamiento nueva a la que llamaremos "PUBLICA" o conocida por ciertos módulos y que difiere de la clase de almacenamiento "GLOBAL" que resulta conocida por todos los módulos. El nuevo C166 soporta esta nueva clase de almacenamiento, pero no sucede así con el C51, por lo que se requiere algún tipo de ajuste.

El comando OVERLAY del linker no ayuda, ya que solo controla el solapamiento de los parámetros y variables locales de funciones. Una posible solución pasa por utilizar módulos especiales para declarar las variables públicas. Así el módulo 1 declara la variables públicas del programa 1; el módulo 2 declara la variables públicas del programa 2; y finalmente el módulo 3 declara la variables comunes a ambos programas.

El truco consiste en utilizar el linker para ubicar los segmentos de datos de los módulos 1 y 2 en las mismas direcciones, mientras se permite que las variables del  módulo 3 sean colocadas automáticamente por el linker. Esta solución usa tres módulos especiales para declarar las variables:

/* Ejemplo de creación de dos conjuntos de datos */
/* públicos en el mismo espacio de memoria */

extern void main1(void) ; 
extern void main0(void) ;

/* Módulo principal que rompe el programa en dos partes */

void main(void) {
   bit flag ;
   if(flag) {
      main0() ;   // RAMA 0
      }
   else {
      main1() ;   // RAMA1 1
      }
   } ^^^^^^^^^^^^^^^^^^^^^^^ FINAL DEL MODULO PRINCIPAL

/* Módulo que declara las variables públicas para la RAMA 0 */
/* Públicos en la rama 0 */

unsigned char x2,y2 ; 
unsigned int z2 ; 
char a2[0x30] ;

/* Una variable accesible desde las dos ramas */
extern int comun ;  

void main0(void) {
   unsigned char c0 ;
   x2 = 0x80 ;
   y2 = x2 ;
   c0 = y2 ;
   z2 = x2*y2 ;
   a2[2] = x2 ;
   comun = z2 ;
   }
^^^^^^^^^^^^^^^^^^^^^^^ FIN DEL MODULO


/* Módulo que declara las variables públicas para la RAMA 1 */
/* Públicos en la rama 1 */

unsigned char x1,y1 ; 
unsigned int z1 ; 
char a1[0x30] ;

/* Una variable accesible desde las dos ramas */
extern int comun ;

void main1(void) {
   char c1 ;
   x1 = 0x80 ;
   y1 = x1 ;
   c1 = y1 ;
   z1 = x1*y1 ;
   a1[2] = x1 ;
   comun = z1 ;
    }
^^^^^^^^^^^^^^^^^^^^^^^ FIN DEL MODULO


/* Módulo que declara las variables comunes a las dos RAMAS */

int comun ; /* Una variable accesible desde las dos ramas */

^^^^^^^^^^^^^^^^^^^^^^^ FIN DEL MODULO

/* Llamada al linker */
l51 t1.obj,t2.obj,com.obj to t.abs DATA(?DT?T1(20H),?DT?T2(20H))

 

La elección de la posición "20H" coloca los segmentos combinados justo encima de los bancos de registros.

El problema de esta solución es que el linker emite un aviso de solapamiento de segmentos data, que siempre resulta molesto, aunque en esta ocasión no resulte peligroso.  


   Contenido>Indice>Intro CursoC51