lunes, 24 de marzo de 2008

COMLOAD

El proyecto consiste en un cargador de archivos .COM, que demuestra como se implementa la función Cargar/Ejecutar usando llamadas al sistema DOS, incluidas en la tabla de interrupciones de servicios de este sistema operativo.

Para cargar y ejecutar un programa .COM se utilizaron estructuras de registros (SREGS y REGS) y varias subfunciones de las interrupciones 21h, las cuales se llevan a cabo en C++ con las funciones INTDOS() e INTDOSX(). La diferencia es que ésta última considera las estructuras de segmentos SREGS que se utiliza.

La interrupción 21h, con registro AH = 4a , permite reservar memoria para la rutina principal y liberar el resto para que la utilice el subproceso o programa a cargar, asi:
re.x.bx = 2000;
sr.es = _psp;
donde BX indica los párrafos en Hexadecimal (aquí se reservan 8 KB para la rutina principal) y ES apunta al PSP del programa COMLOAD.EXE.

Una vez que se ha considerado la memoria disponible se dispone a cargar y ejecutar el programa con registro AH = 4B, donde AL = 0 para cargar y ejecutar a la vez.
Se apunta DS:DX a la dirección del nombre del programa (una cadena ASCIIZ, esto es, terminada por cero) que puede incluir la ruta de directorios y debe incluir la extensión. Se Luego apuntamos ES:BX a una estructura de datos (bloque de parámetros) que se interpreta de la siguiente forma:

El subprograma cargado hereda los ficheros abiertos del programa padre.
Cuando se ejecuta un programa COM ordinario, toda la memoria del sistema está asignada al mismo (el mayor bloque en realidad, lo que en la práctica significa toda la memoria). Para ello, se calcula cuanta memoria necesita el programa y se llama a la
función del sistema para modificar el tamaño del bloque de memoria del propio programa (función 4Ah del DOS, pasando en ES la dirección del PSP).
Por ello, si el programa va a ocupar menos de 64 Kb, será preciso mover SP más abajo para que no se salga del futuro bloque de memoria del programa.


#include
#include
#include
#include

void ayuda()
{
printf("\nCOMLOAD: Carga en memoria y ejecuta archivos del tipo .COM\n con paso de parametros\n");
printf("\r\n");
printf("SINTAXIS: comload [ -c \ -h ]\n");
printf("-c Indica que se va a ejecutar un archivo .COM\n");
printf("archivo Nombre del archivo sin la extension .COM\n");
printf("lineacom Parametros para pasar al archivo .COM\n");
printf("-h Imprime esta ayuda\n");
}

int main( int argc, char near *argv[ ] )
{
// VARIABLES LOCALES
int i, j; // contadores
struct SREGS sregs, sr; // estructuras de registros para manejar segmentos
union REGS registros, re; // estructura de registros
int ret_codigo; // codigo retorno
char near* linea_comando; // linea de comandos como parametros
char near* parametros; //long ES_anterior, SS_anterior;
char *bloque_mem; // para reservar y liberar memoria
int pilaSS, pilaSP; // para guardar los segmentos de la rutina principal antes de ejecutar el .com

struct bloque_param { // estructura para pasar al PSP de poceso hijo (archivo .COM)
int ambiente_seg;
int linea_com_offset; // puntero (segmento:desplazamiento) para la linea de comandos
int linea_com_seg;
int fcb1_offset; // puntero al primer FCB
int fcb1_seg;
int fcb2_offset; // puntero al segundo FBC
int fcb2_seg;
} b_param;


// CODIGO

strcpy(&linea_comando[0],0); // limpiamos las cadenas de texto
strcpy(&parametros[0],0);
if( strcmp(argv[1], "-c")==0)
{
if (argc > 3)
// Hay linea de comando para comload
{
i = 3;
strcpy(&parametros[0],"");
do
{
strcat(&parametros[0], argv[i]);
i++;
if (i < argc)
{ strcat(&parametros[0]," "); }
Else
{ strcat(&parametros[0],"\r"); }
} while (i < argc);

linea_comando[0] = (char) (strlen(parametros)-1);
strcpy(&linea_comando[1],parametros);
}
else
{
linea_comando[0] = (char) 0;
strcat(&linea_comando[0], "\r");
}
// leemos los registros de segmentos y los guardamos antes de ejecutar el subproceso
segread(&sregs);
pilaSS = _SS;
pilaSP = _SP;
printf("\n...lectura registros OK");

// Reservamos 2000h bytes para la rutina principal y liberamos el resto para el subproceso
re.h.ah = 0x4a;
re.x.bx = 2000;
sr.es = _psp;
intdosx(&re,&re,&sr);
printf("\n... reservacion memoria OK\n\n");
if (re.x.cflag) { printf("\nNO SE PUDO LIBERAR LA MEMORIA. ERROR:0x%x\n",re.x.ax); }
else
{
b_param.linea_com_offset = (int) linea_comando; // apunta a la direccion donde
// esta la linea de comandos
b_param.linea_com_seg = sregs.ds;
b_param.fcb1_offset = 0x5c;
b_param.fcb1_seg = _psp; // apunta al segmento del PSP de comload.exe
b_param.fcb2_offset = 0x6c;
b_param.fcb2_seg = _psp;
b_param.ambiente_seg = *((int far *)(((long)_psp<<16) 0x2c));
// apunta al segmento de las variables de ambiente
// contatenamos la extension
strcat(&argv[2][0],".com");

// obtenemos un bloque de memoria
bloque_mem = malloc(60000);

/* re.h.ah = 0x48;
re.x.bx = 1000; // 1000h parrafos = 4096 * 16 = 64Kb
intdos(&re, &re);
if (re.x.cflag) { printf("\nNO SE PUDO RESERVAR LA MEMORIA");}
else { ES_anterior = re.x.ax; }
*/

// Cargamos en memoria y ejecutamos el subproceso
registros.h.ah = 0x4b;
registros.h.al = 0x00;

// buscamos el archivo en disco
registros.x.dx = (int) argv[2];

// segmento extra apunta al Segmento de pila
sregs.es = sregs.ss;

// inea de comandos asignamos la dir. del bloque de parametros al registro BX
registros.x.bx = (int) &b_param;

// Ejecutamos la interrupcion
ret_codigo = intdosx(&registros,&registros,&sregs);
printf("\n...interrupcion DOS OK\n");
// restauramos los registros de segmento de la rutina principal
_SS = pilaSS;
_SP = pilaSP;

if (!(registros.x.cflag ? ret_codigo : 0))
{
printf("Comando terminado: %s\n", argv[2]);
}
else
{
// Mostrar errores posibles, retornados en AX
switch (registros.x.ax)
{
case 0x0001: { printf("\nFuncion no Valida!\n"); break; }
case 0x0002: { printf("\nArchivo no encontrado!\n"); break; }
case 0x0005: { printf("\nAcceso Denegado!\n"); break; }
case 0x0008: { printf("\nMemoria Insuficiente!\n"); break; }
case 0x000A: { printf("\nArea de ambiente No Valido!\n"); break;}
case 0x000B: { printf("\nFormato No Valido!\n"); break; }
default: { printf("\nHa ocurrido un error, codigo: 0x%x!\n", registros.x.ax ); break; }
} // end switch
}

// obtenemos el codigo retornado por el subproceso
re.h.ah = 0x4d;
intdos(&re,&re);
switch (re.h.ah)
{
case 0x00: { printf("\nTerminacion Normal...\n"); break; }
case 0x01: { printf("\nAbortado por Ctrl-Break...\n"); break; }
case 0x02: { printf("\nTerminacion por error critico...\n"); break; }
case 0x03: { printf("\nTerminacion residente...\n"); break; }
}
// liberamos el bloque de memoria
free(bloque_mem);
printf("\n... liberacion de memoria OK\n");
}
}
else
{
if ( strcmp(argv[1], "-h") == 0 )
{
ayuda();
}
else
{
printf("\nSintaxis no valida!\nUtilice comload -h para ver la ayuda\n");
}
}
/*
re.h.ah = 0x49;
sr.es = ES_anterior;
intdosx(&re,&re,&sr);
if (re.x.cflag) { printf("NO SE PUDO LIBERAR LA MEMORIA\n"); }
*/
// libera el bloque de la rutina principal
freemem(_psp);
return 0;
}