En una respuesta a RALF
LINKLo que sigue a continuacion no tiene NADA que ver con PIC18, ni con ASM, sino con el proceso de compilacion.
Hable sobre que como el compilador genere un .o y luego el linker los une, pero esto es un invento hasta que no se demuestre obviamente.
Para demostrar masomenos que eso realmente ocurre observamos la salida de la consola del MPLAB:
Compilacion con el mpasmx del archivo configuracion.asm , genera 3 archivos, object ( .o ), listing ( .lst ) y error ( .err )
"/opt/microchip/mplabx/v3.10/mpasmx/mpasmx" -q -p18f2550 -u -l\"build/default/production/configuration.lst\" -e\"build/default/production/configuration.err\" -y -o\"build/default/production/configuration.o\" \"configuration.asm\"
Compilacion con el mpasmx del archivo main.asm , genera 3 archivos, object ( .o ), listing ( .lst ) y error ( .err ) , el listing y error son para el usuario
"/opt/microchip/mplabx/v3.10/mpasmx/mpasmx" -q -p18f2550 -u -l\"build/default/production/main.lst\" -e\"build/default/production/main.err\" -y -o\"build/default/production/main.o\" \"main.asm\"
Finalmente Linkeamos con el mplink,
Entradas: el archivo linker ( .lkr ) opciones como _EXTENDEDMODE (indica que tengo activo el set extendido) y que use el main.o y configuration.o creado antes
Salidas: el .map que indica cuanto ocupan cada espacio y como se distribuyo la memoria, y el .cof que ya es el archivo compilado
"/opt/microchip/mplabx/v3.10/mpasmx/mplink" "/opt/microchip/mplabx/v3.10/mpasmx/LKR/18f2550_g.lkr" -p18f2550 -w -m"dist/default/production/PIC18asm.X.production.map" -u_EXTENDEDMODE -z__MPLAB_BUILD=1 -odist/default/production/PIC18asm.X.production.cof build/default/production/main.o build/default/production/configuration.o
Finalmente, llama a otro ejecutable que transforma el .cof en un .hex , lo traduce de un formato a otro. este es el mp2hex
MP2HEX 5.08, COFF to HEX File Converter
Copyright (c) 1998-2011 Microchip Technology Inc.
Errors : 0
Y aqui queda demostrado que eso ocurre! realmente OCURRE!,
CompiladorPara seguir investigando vamos a crear un simple proyecto con 2 archivos, vamos a llamar de un archivo a otro, vamos a usar variables definidas en un lado y en otro. Y vamos a ver los resultados que obtenemos, finalmente ver como queda posicionado en memoria TODO esto.
Para esto creamos 2 archivos, que llame: main.s y auxiliar.s , estos archivos estan simplificados al maximo. Es decir no tiene codigo para configurar los modulos, no inclui el .inc para hacer mas visible esto, ni nada por el estilo. Tienen muy pocas intrucciones tambien.
Aqui dejo los 2 archivos:
main.s ( si, no posee include, lo saque a proposito y no deben agregarlo para que funcione )
;************************************************
; Definicion de variables
;************************************************
; Externo lo de afuera, y global para que los demas lo vean
global Var1
extern _Suma, Var3
Bank0: udata_acs
Var1: res 1
Var2: res 1
;************************************************
; Vectores de reset e interrupcion
;************************************************
Rst: code 0x00 ;Vector de Reset
GOTO Main
org 0x08 ;Vector de Interrupcion Alta Prioridad
RETURN
org 0x18 ;Vector de Interrupcion Baja Prioridad
RETURN
;************************************************
; Programa Principal
;************************************************
Main:
CALL _Suma
NOP
MOVLW 0x78
IORWF Var3
NOP
GOTO $ ;Loop infinito
;----------- Fin de programa ---------------------
END
auxiliar.asm
;************************************************
; Mas variables, 1 mas y definiciones
;************************************************
; Externo lo de afuera, y global para que los demas lo vean
extern Var1
global _Suma, Var3
udata_acs
Var3: res 1
;************************************************
; CODIGOOOOOOOOOOOOOOOOOO
;************************************************
code
_Suma:
MOVLW 0x10
ADDWF Var1
RETURN
; Fin del archivo
END
OK compilamos y..... TODO PERFECTO!
Probamos nuestro codigo. Este comienza en el vector de reset, va al Main, entra en el CALL y se ejecuta _Suma del otro archivo, modifica la variable que esta definida en el primer archivo, vuelve y ahi desde el main.s modifico la variable definida en el auxiliar.
Y lo que es peor funciona! .. Lo pueden simular
Antes dijimos que el linker va a acomodar el programa donde MAS le guste, va a posicionar las variables como le guste si es que no especificamos una direccion concreta.
Vamos a la memoria de programa y tenemos esto:
Line Address Opcode Label DisAssy
1 0000 EF0D GOTO 0x1A
2 0002 F000 NOP
3 0004 0000 NOP
4 0006 FFFF NOP
5 0008 0012 RETURN 0
6 000A 0E10 _Suma MOVLW 0x10
7 000C 2600 ADDWF Var1, F, ACCESS
8 000E 0012 RETURN 0
9 0010 FFFF NOP
10 0012 FFFF NOP
11 0014 FFFF NOP
12 0016 FFFF NOP
13 0018 0012 RETURN 0
14 001A EC05 Main CALL 0xA, 0
15 001C F000 NOP
16 001E 0000 NOP
17 0020 0E78 MOVLW 0x78
18 0022 1202 IORWF Var3, F, ACCESS
19 0024 0000 NOP
20 0026 EF13 _.org_1_0026 GOTO 0x26
Veamos como estan ubicados. Los vectores de reset e interrupcion estan en sus correspondientes lugares como se pidio. (0x0 , 0x8 y 0x18), El Main sigue luego del vector de interrupcion, esto es obvio por que eso pedimos. Dijimos con el "org" esto va a partir de ACA (0x18). Y _Suma ???? Como era tan pequeño y entraba entre los vectores de interrupcion el linker decidio ponerlo ahi!. Es decir el linker acomodo solo la seccion de codigo, hasta aprovechando el espacio en este caso. ¿Esto va a ocurrir siempre?, no esto no ocurre siempre asi. Pero aca se puede ver como es el linker quien toma los 2 archivos, y los une en uno solo sin necesidad de includes. Ahora veamos la memoria de datos:
Address Symbol Hex Decimal Binary Char
000 Var1 0x00 0 00000000 '.'
001 Var2 0x00 0 00000000 '.'
002 Var3 0x00 0 00000000 '.'
El linker me acomodo las secciones de datos de cada archivo ( access ) como a el le gusto. Y todo esto:
- Sin indicar direcciones ( a excepcion de los vectores de reset/interrupcion )
- Sin includes .. y no me importan donde esta la función (si al final del archivo o no), yo puedo llamarla, solo tengo que decirle: "Esto esta en otro archivo arreglatelas vos linker".
Al comienzo hablamos que el compilador genera varios archivo mas aparte del .o , vamos a build/default/production/ desde la carpeta de nuestro proyecto y... encontramos todos los archivos esos, como todo compilo bien no hay errores, por lo cual main.err y auxiliar.err estan vacios. ahora vamos a ver los listing, este archivo nuevamente repito y remarco lo crea el compilador, y no el linker.
Veamos algunos, y de paso les muestro lo que quiero que vean. Al abrir el archivo al final nos encontramos con esto:
mains.lst
SYMBOL TABLE
LABEL VALUE
Main 0000001A
Var1 00000000
Var2 00000001
Var3 00000000
_.org_1_0026 00000026
_Suma 00000000
__18F2550 00000001
auxiliar.lst
SYMBOL TABLE
LABEL VALUE
Var1 00000000
Var3 00000000
_Suma 00000000
__18F2550 00000001
Se ven la tabla de los simbolos, cada archivo posee la suya. Ahi vemos todas las "etiquetas" que creamos y tambien las que difinimos como "externas" es decir que existen (en algun lugar), esto al compilador no le interesa, el solo compila.
Ademas hay un __18F2550 agregado por el compilador, esto no se si les recuerda cuando tiene un archivo para muchos micros y ponen:
ifdef __18F2550
Hacer algo
endif
ifdef __16F628A
Hacer otra cosa
endif
Para eso esta
.
Otra cosa interesante de esto se ve en la parte del codigo dentro de estos lst:
main.lst
LOC OBJECT CODE LINE SOURCE TEXT
VALUE
00001 list
00002 ;************************************************
00003 ; Definicion de variables
00004 ;************************************************
00005
0000 00006 global Var1
0000 00007 extern _Suma, Var3
00008
00009 Bank0: udata_acs
000000 00010 Var1: res 1
000001 00011 Var2: res 1
00012
00013 ;************************************************
00014 ; Vectores de reset e interrupcion
00015 ;************************************************
00016
00017 Rst: code 0x00 ;Vector de Reset
000000 EF?? F??? 00018 GOTO Main
00019
00020 org 0x08 ;Vector de Interrupcion Alta Prioridad
000008 0012 00021 RETURN
00022
00023 org 0x18 ;Vector de Interrupcion Baja Prioridad
000018 0012 00024 RETURN
00025
00026 ;************************************************
00027 ; Programa Principal
00028 ;************************************************
00001A 00029 Main:
00001A EC?? F??? 00030 CALL _Suma
00001E 0000 00031 NOP
000020 0E78 00032 MOVLW 0x78
000022 12?? 00033 IORWF Var3
000024 0000 00034 NOP
000026 EF?? F??? 00035 GOTO $ ;Loop infinito
00036
00037 ;----------- Fin de programa ---------------------
00038 END
auxiliar.lst
LOC OBJECT CODE LINE SOURCE TEXT
VALUE
00001
00002 ;************************************************
00003 ; Mas variables Wiii
00004 ;************************************************
00005
00006 ; Externo lo de afuera, y global para que los demas lo vean
0000 00007 extern Var1
0000 00008 global _Suma, Var3
00009
00010 udata_acs
000000 00011 Var3: res 1
00012
00013
00014 ;************************************************
00015 ; CODIGOOOOOOOOOOOOOOOOOO
00016 ;************************************************
00017 code
00018
000000 00019 _Suma:
000000 0E10 00020 MOVLW 0x10
000002 26?? 00021 ADDWF Var1
000004 0012 00022 RETURN
00023
00024
00025 ; Fin del archivo
00026
00027 END
Observen la posicion de cada una de las instrucciones/datos ( es la columna de la izquierda ) , es justamente LOC (Ubicacion) , lo que ven a la derecha de ese numero es el opcode de la instruccion, las directivas tiene LOC 0.
Viendo el main.lst notamos como cuando creamos datos los numera, Var1 ocuparia la posicion de memoria 000000 de datos, Var2 la 000001 de datos, luego cuando comienza el codigo ( code ) el indice cambia y comienza nuevamente de 0, responde a los "org" y va dando la posicion de la flash como si solo de ese archivo se tratase. Esto significa que tiene un "puntero" para datos y un puntero para "programas" o para ser mas correcto es un puntero por seccion.
Luego vamos a nuestro auxiliar.lst y encontramos algo curioso: el dato comienza en 0x00 y no 0x02 ( si recordamos del main) y el codigo tambien. Estarian superponiendose "supuestamente", esto no es asi. Luego el linker va a acomodar todo eso. Ya que como dijimos y vuelvo a repetir, esto al compilador NO le importa, NO es su trabajo.
Nombremos ademas otra cosa, recuerda cual era el opcode de el ADDWF? era asi 0010 01fa xxxx xxxx (0x26xx con f=1 y a=0)
En este caso f es 1, a es 0 y pero ¿x?. Veran que tiene un 0x26?? , ese opcode no es valido, Ese opcode luego el linker lo va a acomodar cuando proceda a unir todo y reemplaze los simbolos por las posiciones de memoria que les asigno el linker a esos datos.
LinkerSe vio claramente el tema de los simbolos, como el compilador piensa en un solo archivo a la ves, y luego es el linker quien reune todos estos pedazos. Solo nos queda un solo archivo para ver. Y es lo que devuelve el linker, ademas del .cof que ya conocemos, el linker devuelve un .map, podriamos decirlo como un informe del linker, asi como el .lst es un informe del compilador.
Abrimos el MAP y encontramos:
Primero un informe de las secciones ( lo recorte un poco )
Section Info
Section Type Address Location Size(Bytes)
--------- --------- --------- --------- ---------
Rst code 0x000000 program 0x000004
.cinit romdata 0x000004 program 0x000002
.org_0 code 0x000008 program 0x000002
.code code 0x00000a program 0x000006
.org_1 code 0x000018 program 0x000012
.config_300000_BUILD/DEFAULT/PRODUCTION/CONFIGU romdata 0x300000 program 0x000001
...........................................................
.config_30000D_BUILD/DEFAULT/PRODUCTION/CONFIGU romdata 0x30000d program 0x000001
Bank0 udata 0x000000 data 0x000002
.udata_acs udata 0x000002 data 0x000001
Veamos que secciones tiene mi linker, pero antes no se si se preguntaron el por que al codigo de _Suma no lo puso luego del vector de reset, si este entraba ahi. Aca hay algunas respuestas. Cada seccion tiene un nombre, a la izquierda vemos cuales son. Rst ( Miren la etiqueta en el programa ) ubicado en el 0x00, en la memoria de programa y de largo 4 bytes. y luego de eso... La seccion .cinit, seccion utilizada por C pero que nuestro linker parece tener ya pre-establecido (Esto evito que _Suma ubique la posicion de comienzo). Luego tenemos los .org que pusimos y el .code de nuestro auxiliar!. Luego de eso esta la lista de fuses. y al ultimo tenemos 2 secciones, si 2, la primera llamada Bank0 y la otra .udata_acs , ¿ pero no lo definimos a los con udata_acs ?, Si pero sin querer agregue la etiqueta Bank0 en el main.s
. Mientras que auxiliar no tiene nada. , observen ademas que ahi dice donde comienza, el largo y donde se almanena, es decir en la memoria de datos. Si yo no hubiera agregado la etiqueta los hubiera puesto todo dentro de una misma seccion. Lo mismo si antes de la etiqueta Main hubiera puesto "code", esto hubiera hecho que se junte en una sola seccion "code" el codigo de auxiliar ( y ya no entraria mas entre medio de los vectores - Al final pruebo esto )
Siguiendo un poco mas abajo el archivo se puede encontrar las porciones de memoria contiguas llenas de Flash, como que no hay mucho para explicar aca:
Program Memory Usage
Start End
--------- ---------
0x000000 0x000005
0x000008 0x00000f
0x000018 0x000029
0x300000 0x300003
0x300005 0x300006
0x300008 0x30000d
44 out of 33048 program addresses used, program memory utilization is 0%
Y finalizando, mas SIMBOLOS!
Symbols - Sorted by Name
Name Address Location Storage File
--------- --------- --------- --------- ---------
Main 0x00001a program static /home/x/workspace/MPLAB/PIC18asm.X/main.asm
_.org_1_0026 0x000026 program static /home/x/workspace/MPLAB/PIC18asm.X/main.asm
_Suma 0x00000a program extern /home/x/workspace/MPLAB/PIC18asm.X/auxiliar.asm
Var1 0x000000 data extern /home/x/workspace/MPLAB/PIC18asm.X/main.asm
Var2 0x000001 data static /home/x/workspace/MPLAB/PIC18asm.X/main.asm
Var3 0x000002 data extern /home/x/workspace/MPLAB/PIC18asm.X/auxiliar.asm
Donde dice la direccion asignada, el nombre, donde se ubican, y si son locales o globales, esto es 1 por cada archivo. Se observa que esas variables que declaramos globales estan como extern y las que son solamente locales, es decir solo del archivo y no se pueden acceder de otro lado son static.
( ¿ Ahora te suena los modificadores static y extern de C ? )
-----------------------------------
Probando mas cosasProbando unir las secciones. Comente que iba a probar esto y ver como quedaba, lo que hice fue: Quitarle el Bank0: al udata_acs del main.s y antes del Main: le agregue un "code" asi como esta el archivo auxiliar.s, Resumen:
main.s ( Cambios, asi quedaron ahora)
Simulo:
- Memoria de datos igual.
- Memoria de programa: CAMBIO!, ahora esta todo luego de los vectores, por que ahora es todo un pedazo grande de instrucciones.
- Los archivos .lst perdieron ese simbolo ( Bank0 )
- El .map ahora esta formado por otra cosa:
Section Type Address Location Size(Bytes)
--------- --------- --------- --------- ---------
Rst code 0x000000 program 0x000004
.cinit romdata 0x000004 program 0x000002
.org_0 code 0x000008 program 0x000002
.org_1 code 0x000018 program 0x000002
.code code 0x00001a program 0x000016
.config_300000_BUILD/DEFAULT/PRODUCTION/CONFIGU romdata 0x300000 program 0x000001
.udata_acs udata 0x000000 data 0x000003
Ahora .udata_acs esta todo en una seccion propia, y .code en su propia seccion, conteniendo a ambos codigo de los 2 archivos. Como ahora la seccion ".code" era muy grande no entro entre medio de los vectores. Y el linker jamas puede poner un GOTO por el mismo para aprovechar eso. Tampoco lo podria dividir ya que sino alteraria el programa.
Error de linker - Simbolo faltanteBueno ya probe esto, ahora quiero ver si le meto un error, Voy a comentar el global a un archivo y ver que sucede
Comente la linea "global Var1" en el main.s, asi de esa forma no se crea el simbolo y auxiliar.s no tiene donde unirse y obtengo un error:
make[2]: *** [dist/default/production/PIC18asm.X.production.hex] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2
MPLINK 5.08, LINKER
Error - could not find definition of symbol 'Var1' in file 'build/default/production/auxiliar.o'.
Errors : 1
¿De quien es ese error?, parece del linker, la cultura popular dira que es del compilador, ya que este hace todo. Si voy a los archivos main.err y auxiliar.err veo y ..... NADA, vacios!, claramente al compilador le importa poco si existen o no por fuera del archivo xD, solo el linker quien es el encargado de unir las cosas.
Error de linker - Memoria Fuera de rango(Esto no es la idea de ponerle una direccion, realmente si vamos a hacer un codigo que el linker lo acomode solo no deberiamos poner direcciones)
A mi main lo vuelvo al original y le hago un cambio mas:
Bank0: udata_acs 0x5F
Var1: res 1
Var2: res 1
(Aclarar que el Bank0 esta ahi asi creo otra seccion, como se vio en el map)
Con CBLOCK Var1 se definiria como 0x5F y Var2 como 0x60 sin problemas.
Si yo compilo asi, termino con esto:
Error - section 'Bank0' can not fit the absolute section. Section 'Bank0' start=0x0000005f, length=0x00000002
Errors : 1
Exacto, no podes alojar eso ahi. ya que esta en el borde y solo hay 1 lugar, nuevamente los .err generados por el compilador estan vacios.
Error del compilador - Mala instruccionY ya que estamos por curiosodad, veamos que sale de un archivo .err
Volvemos al original y le cambie el IORWF Var3 por IOXWF Var3
Miramos la consola y vemos los errores:
Compilador:
Warning[207] /home/fabian/workspace/MPLAB/PIC18asm.X/main.asm 34 : Found label after column 1. (IOXWF)
Error[122] /home/fabian/workspace/MPLAB/PIC18asm.X/main.asm 34 : Illegal opcode (Var3)
Linker:
Error - Error reading object file 'build/default/production/main.o'
Errors : 1
El compilador que no entiende nada y el linker que no encontro los archivos .o por que se suspendio todo y no se generaron, ahora voy al .err y tengo lo mismo que me dio por la consola:
Warning[207] /home/fabian/workspace/MPLAB/PIC18asm.X/main.asm 34 : Found label after column 1. (IOXWF)
Error[122] /home/fabian/workspace/MPLAB/PIC18asm.X/main.asm 34 : Illegal opcode (Var3)
Bueno esto es todo, un lindo paseo por las herramientos y la compilacion, si alguna ves leyeron de listing, map entre otras opciones en C, es realmente esto.
El compilador C ( mejor dicho su ejecutable ) puede llegar a hacer todo en un solo paso y no en 2 como hice al comienzo, este seria un caso bastante especial, pero por dentro ( internamente) estan divididos asi como esta explicado, el compilador C funciona como el de ASM, va crear varias secciones de la misma forma, nomas que estas ya tienen un nombre particular como .text , .data y .bss .
Por eso mucho no me queria meter aca con esto xD, ya que esto es programacion general y no solo de PIC18, en fin, espero no haber dicho cualquier barbaridad y haber demostrado algo. Estoy seguro que hay gente que me supera en conocimientos por mucho, y seguro que algun defecto le va a encontrar, pero esto es hasta lo que yo se.