
/*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\
³                                                                    ³
³     765DEBUG 5.0 - M¢dulo avanzado de an lisis de disquetes.       ³
³                                                                    ³
³               (C) 1992-1995  Ciriaco Garc¡a de Celis               ³
³                                                                    ³
\ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ*/


#include <alloc.h>
#include <conio.h>
#include <dos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <bios.h>
#include "2m-info.h"


#define SMAX    32768L    /* mayor sector soportado por el programa */

#define CX       12       /* color para pantalla de "memoria insuficiente" */
#define CFX       0       /* color de fondo para "memoria insuficiente" */
#define C765      1       /* color de '765DEBUG' */
#define CM       15       /* color para el men£ principal */
#define CFM       3       /* color de fondo para el men£ principal */
#define C3D      10       /* color para el marco 3D */
#define CB        2       /* color para la banda de fondo del marco 3D */
#define CTIT     14       /* color del t¡tulo superior */
#define CVER      4       /* color de la versi¢n */
#define CB1       5       /* color 1 para botones del men£ principal */
#define CB2       7       /* color 2 para botones del men£ principal */
#define CTB1     15       /* color 1 para tinta de botones del men£ principal */
#define CTB2      0       /* color 2 para tinta de botones del men£ principal */
#define CI        1       /* color para informe en men£ principal */
#define CMI      11       /* color para el marco del informe en men£ principal */
#define CS       15       /* color para confirmar salida */
#define CFS       6       /* color de fondo para confirmar salida */
#define CD       15       /* color para advertencia del tama¤o buffer DMA */
#define CFD       6       /* color de fondo advertencia tama¤o buffer DMA */
#define CR       15       /* color para "ayuda" */
#define CDR      14       /* color para direcci¢n en "ayuda" */
#define CFR       5       /* color de fondo para "ayuda" */
#define CL       15       /* color para LeerSector */
#define CFL       4       /* color de fondo para LeerSector */
#define CE       15       /* color para EscribirSector */
#define CFE       4       /* color de fondo para EscribirSector */
#define CF       15       /* color para FormatearPista */
#define CFF       5       /* color de fondo para FormatearPista */
#define CIW      15       /* color para 'Leyendo ID's...' */
#define CFIW      4       /* color de fondo para 'Leyendo ID's...' */
#define CFI       0       /* color de fondo para lectura de ID's */
#define CIC      14       /* color cabecera de lectura ID's */
#define CFIC      4       /* color de fondo cabecera de lectura ID's */
#define CIMS     15       /* color para milisegundos en lectura ID's */
#define CIS      12       /* color para sector en lectura ID's */
#define CIT      10       /* color para tama¤os en lectura ID's */
#define CICC     14       /* color para cilindro/cabezal en lectura ID's */
#define CIST     13       /* color para registros STx en lectura ID's */
#define CIB       9       /* color para mensaje barra en lectura ID's */
#define CRS      15       /* color para resultados R/W/F */
#define CFRS      5       /* color de fondo para resultados R/W/F */
#define CFV       1       /* color de fondo para ver buffer */
#define CFVM      3       /* color de fondo del menu de ver buffer */
#define CVMM      0       /* color del marco del menu de ver buffer */
#define CVMI      1       /* color del texto del menu de ver buffer */
#define CVMS     10       /* color de la ayuda para salir del menu de ver buffer */
#define CVMD     15       /* color de la informaci¢n del menu de ver buffer */
#define CVD      15       /* color para direcciones en ver buffer */
#define CVH      11       /* color para datos hexadecimales en ver buffer */
#define CVS      15       /* color para gui¢n separador en ver buffer */
#define CVA      15       /* color para datos ASCII en ver buffer */
#define CVNS     15       /* color para pedir nueva secci¢n en ver buffer */
#define CFVNS     4       /* color de fondo para pedir nueva secci¢n en ver buffer */
#define CVNB     15       /* color para pedir nuevo tama¤o en ver buffer */
#define CFVNB     5       /* color de fondo para pedir nuevo tama¤o en ver buffer */
#define CVA      15       /* color para ayuda en ver buffer */
#define CFVA      6       /* color de fondo para ayuda en ver buffer */
#define C2M      15       /* color para informaci¢n de disco 2M */
#define CF2M      6       /* color de fondo para informaci¢n de disco 2M */


extern   void EsperaDma0 (int);
extern   void MoveMem (char huge *, char huge *, unsigned);
extern   int  es386 (void);
extern   unsigned rnd (unsigned);


int      HablaSp (void),
         infdc (void),
         EsperarInt (void),
         EsOS2 (void),
         PeligrosoOS2 (void),
         Hay2M3 (void),
         CodePage437 (void),
         SalirConfirmado (void),
         EditarTablaFmt (unsigned char *, int),
         input (char *, int, int, int, int),
         Tecla (void),
         TipoDrive (int),
         hextoi (char *);
void     Vram (int),
         TestDMA (int),
         CursorOff (void),
         BrilloOn (void),
         DibujaOpciones (void),
         DibujaValores (int, int, int, int, int),
         Marco3df (int, int, int, int, int, int, int),
         Marco3dg (int, int, int, int, int, int, int, int),
         v3dg (int, int, int, int, int, int, int, int, int),
         v3df (int, int, int, int, int, int, int, int),
         v3d (int, int, int, int, int),
         ventana (int, int, int, int, int, int, int),
         boton (int, int, int, int, int, int, char *),
         Informe2M (int, int *, int *, int),
         SeleccionarUnidad (int, int),
         ResetUnidad (int),
         DensidadSpecify (int),
         recalibrar (int, int, int, int),
         posicionar (int, int, int, int),
         LeerSector (int, int, int, int, char huge *, unsigned *, int),
         LeerPista (int, int, int, int, char huge *, unsigned *, int),
         EscribirSector (int, int, int, int, int, unsigned *, char huge *),
         FormatearPista (int, int, int, int, int),
         LeerIds (int, int, int, int),
         Resultados (int, int, int),
         MostrarBuffer (char huge *, unsigned *),
         MotorOn (int),
         MotorOff (void),
         outfdc (unsigned char),
         PreparaDma (unsigned, unsigned, char huge *);
char     *hex2str (int),
         *dec2str (unsigned),
         *dec3str (unsigned),
         *dec5str (unsigned),
         *dec2strq (unsigned),
         *dec3strq (unsigned),
         *dec5strq (unsigned),
         *ReservarMemoria (char huge **);
unsigned long VDMA (unsigned *);

int      sp;                               /* 1-espa¤ol 0-ingl‚s */
char     *KeybShifts = MK_FP (0x40, 0x17);
extern   void interrupt NuevaInt0E (void);


void main (int argc, char **argv)
{
  char     huge *buffer,    /* buffer para sector */
           huge *bloque,
           cad[32];
  int      unidad=0, vunidad[2],
           mf_mfm[2], cabezal[2],
           cilindro[2], opc, t, tp, opcion, dmakb, refrescar,
           avisos=1, discos=1, SalirEnOS2=0;
  static   PrimeraVez=1;
  unsigned bytes=0;
  void     interrupt (*ViejaInt0E) (void);

  sp = HablaSp();

  if (argc>1)
    if ((!strcmp(argv[1], "/I") || !strcmp(argv[1], "/i")))
       sp ^=1;
      else {
        printf ("\n765DEBUG 5.0   (C) 1992-1995 Ciriaco Garc¡a de Celis.\n");
        if (sp)
            printf("               M¢dulo avanzado de an lisis de disquetes.\n"
                   "               Indique /I para conmutar el idioma.\n");
          else
            printf("               Advanced low level disk analysis utility.\n"
                   "               Use the /I switch to change the language.\n");
        exit (0);
        }

  Vram (PRESERVAR);
  Vram (MODO80);  /* asegurar modo texto de 80 columnas */

  ViejaInt0E=getvect(0x0E);
  setvect (0x0E, NuevaInt0E);

  if (*KeybShifts & 3) PrimeraVez = 0;  /* con SHIFT, evitar avisos */

  mf_mfm[0]=mf_mfm[1]=1; cabezal[0]=cabezal[1]=cilindro[0]=cilindro[1]=0;

  dmakb=7;
  for (t=0; t<=1; t++) {
    tp = TipoDrive (t);
    if ((tp >= 5) && (dmakb < 26)) dmakb=26;  /* 2.88M */
    if ((tp == 4) && (dmakb < 13)) dmakb=13;  /* 1.44M */
    if ((tp == 2) && (dmakb < 11)) dmakb=11;  /* 1.2M  */
    switch (tp) {
      case 1:  vunidad[t]=1; break;  /* 360K */
      case 3:  vunidad[t]=2; break;  /* 720K */
      default: vunidad[t]=0; break;  /* resto de unidades */
      }
    }

  t=TipoDrive(1);                       /* -1 si falla */
  if (t==-1) t=peekb(0x40, 0x10) >> 6;  /* n§ unidades-1 */
  if (t) discos++;                      /* n§ unidades disquete */

  if ((bloque=ReservarMemoria (&buffer))!=NULL) {
    opcion=0; refrescar=1;
    do {
      BrilloOn();
      if (refrescar) {
        DibujaOpciones();
        refrescar=0;
        }
      if (avisos) {
        avisos=0;
        if (PrimeraVez) SalirEnOS2=PeligrosoOS2();
        if (SalirEnOS2) {
          opcion=SALIR;
          break;
          }
        if (PrimeraVez) TestDMA (dmakb);
        recalibrar (0, 0, 0, 0);      /* recalibrar unidades al */
        if (discos>1)                 /* entrar en el programa */
          recalibrar (1, 0, 0, 0);
        SeleccionarUnidad (unidad, vunidad[unidad]); /* por defecto */
        }
      DibujaValores (unidad, vunidad[unidad], mf_mfm[unidad],
                     cilindro[unidad], cabezal[unidad]);

      CursorOff(); opc=getch();
      if (!opc) opc=getch() << 8; /* usar Tecla() borrar¡a el buffer */
      switch (opc) {
        case '0':    recalibrar (0, cabezal[0], vunidad[0], 1);
                     if (discos>1)
                       recalibrar (1, cabezal[1], vunidad[1], 1);
                     cilindro[0]=cilindro[1]=0;
                     SeleccionarUnidad (unidad, vunidad[unidad]);
                     break;
        case '1':    unidad ^= 1;
                     SeleccionarUnidad (unidad, vunidad[unidad]);
                     break;
        case '2':    vunidad[unidad]++;
                     if (vunidad[unidad]>3) vunidad[unidad]=0;
                     DensidadSpecify (vunidad[unidad]);
                     break;
        case '3':    gotoxy (64, 18);
                     strcpy (cad, dec2strq (cilindro[unidad]));
                     if (input (cad, 1, 2, CI, CFM)) {
                       cilindro[unidad] = atoi (cad);
                       posicionar (unidad, cabezal[unidad], cilindro[unidad],
                                   vunidad[unidad]);
                       }
                     break;
        case '4':    cabezal[unidad]^=1;
                     break;
        case '5':    LeerIds (unidad, mf_mfm[unidad], cabezal[unidad],
                              vunidad[unidad]);
                     refrescar=1;
                     break;
        case '6':    LeerSector (unidad, mf_mfm[unidad], cilindro[unidad],
                                 cabezal[unidad], buffer, &bytes,
                                 vunidad[unidad]);
                     break;
        case '7':    EscribirSector (unidad, mf_mfm[unidad], vunidad[unidad],
                                     cilindro[unidad], cabezal[unidad],
                                     &bytes, buffer);
                     break;
        case '8':    MostrarBuffer (buffer, &bytes);
                     refrescar=1;
                     break;
        case '9':    FormatearPista (unidad, mf_mfm[unidad], vunidad[unidad],
                       cilindro[unidad], cabezal[unidad]);
                     break;
        case 'a':
        case 'A':    mf_mfm[unidad]^=1;          break;
        case 'i':
        case 'I':    Informe2M (unidad, &cilindro[unidad], &cabezal[unidad],
                                vunidad[unidad]);
                     break;
        case 0x1B:   if (SalirConfirmado()) opcion=SALIR; break; /* ESC */
        case 0x2D00: opcion=SALIR;     break;                  /* ALT-X */
        case 0x4400: LeerPista (unidad, mf_mfm[unidad],          /* F10 */
                       cilindro[unidad], cabezal[unidad],
                       buffer, &bytes, vunidad[unidad]);
                     break;
        case 0x3B00: ventana (ABRIR, 3, 3, 77, 22, CR, CFR);      /* F1 */
                     if (sp) {
                         gotoxy ( 2, 1); cputs("  El acceso directo  a la controladora puede dejar ‚sta en situaci¢n no");
                         gotoxy ( 2, 2); cputs("estable en caso de error  (por  ejemplo,  abortando una lectura de ID's");
                         gotoxy ( 2, 3); cputs("que falla).  En ese caso las dem s ¢rdenes podr¡an fallar inicialmente.");
                         gotoxy ( 2, 4); cputs("Si es preciso, ejecute un reset y recalibre las unidades (opci¢n 0).");
                         gotoxy ( 2, 6); cputs("  No  se  limitan,  intencionadamente,  algunas  acciones aparentemente");
                         gotoxy ( 2, 7); cputs("il¢gicas por parte del usuario, con objeto de lograr una herramienta de");
                         gotoxy ( 2, 8); cputs("mayor potencia. En general, pulsando INTRO cuando no se sabe qu‚ hacer,");
                         gotoxy ( 2, 9); cputs("en la mayor¡a de las ocasiones las decisiones se toman autom ticamente.");
                         gotoxy ( 2,11); cputs("  Si durante las operaciones con un disco de prueba se baja la l¡nea de");
                         gotoxy ( 2,12); cputs("cambio, la BIOS podr¡a no detectar luego el soporte. Si tiene problemas");
                         gotoxy ( 2,13); cputs("con dicho disquete al salir al DOS, s quelo y vuelva a introducirlo.");
                         textcolor (CDR);
                         gotoxy (21,15); cputs ("¨Usuario *REGISTRADO* con problemas?");
                         gotoxy (20,16); cputs ("Mis buzones de correo electr¢nico son:");
                         }
                       else {
                         gotoxy ( 2, 1); cputs("  The  direct  access  to  diskette drive controller can leave it in an");
                         gotoxy ( 2, 2); cputs("undefined state if an error occurs (for example after aborting a failed");
                         gotoxy ( 2, 3); cputs("Read IDs command). In such cases the subsequent functions may initially");
                         gotoxy ( 2, 4); cputs("fail. If needed, you can reset and recalibrate the drives (option 0).");
                         gotoxy ( 2, 6); cputs("  Some   apparently   incorrect  actions  performed  by  the  user  are");
                         gotoxy ( 2, 7); cputs("intentionally  not  skipped,  in  order  to  increase the power of this");
                         gotoxy ( 2, 8); cputs("program. Generally, press the ENTER key when you don't know what to do;");
                         gotoxy ( 2, 9); cputs("in most cases, your decisions are automatically adopted by the program.");
                         gotoxy ( 2,11); cputs("  If during some disk operations the disk change line is reset,  system");
                         gotoxy ( 2,12); cputs("BIOS may not always be able to detect the disk media in further access.");
                         gotoxy ( 2,13); cputs("If you have problems after exiting to DOS, remove & insert the disk.");
                         textcolor (CDR);
                         gotoxy (21,15); cputs ("A *REGISTERED* user with problems?");
                         gotoxy (24,16); cputs ("My electronic mailboxes are:");
                         }
                     gotoxy (2,18); cputs ("Internet ciri@gui.uva.es - Compuserve 100657,1253 - Fidonet: 2:341/21.8");
                     CursorOff();
                     Tecla();
                     ventana (CERRAR, 3, 3, 77, 22, 0, 0);
                     break;
        }
    } while (opcion!=SALIR);
  }

  if ((!SalirEnOS2) && (bloque!=NULL)) {
    pokeb (0x40, 0x94, cilindro[0]);
    pokeb (0x40, 0x95, cilindro[1]);
    outportb (FD_DCR, peekb(0x40, 0x8B) >> 6);  /* velocidad normal */
    }
  if (bloque!=NULL) farfree ((char far *) bloque);

  setvect (0x0E, ViejaInt0E);

  Vram (RESTAURAR);

  PrimeraVez=0;  /* evitar avisos molestos en pr¢ximas ejecuciones */
}


void Vram (int operacion)
{
  static scr_ok, modo, pag, cx, cy, colorbits;
  static long tam;
  static char *scrbuf;
  union REGS regs;

  if (operacion==PRESERVAR) {
    scr_ok=0;
    modo=peekb(0x40, 0x49) & 0x7F;
    if (peekb(0x40, 0x84) < 24) pokeb(0x40, 0x84, 24);
    pag=peekb(0x40,0x62);
    cx=peekb(0x40,0x50+pag*2); cy=peekb(0x40,0x51+pag*2);
    colorbits=peek(0x40, 0x10) & 0x30;
    tam = 2L * (peekb(0x40, 0x84) + 1) * peek (0x40, 0x4A);
    if (tam > 32768L) tam=32768L;
    if ((modo<=3)||(modo==7)) {
      if ((scrbuf=farmalloc(tam))!=NULL) {
        scr_ok=1;
        movedata ((modo==7) ? 0xb000: 0xb800,
                  (modo==7) ? 0     : peek(0x40, 0x4e),
                  FP_SEG(scrbuf), FP_OFF(scrbuf), tam);
        }
      }
    if ((modo!=3) && (modo!=7)) textmode ((modo!=7) ? C80:MONO);
    }
    else if ((operacion==RESTAURAR) || (operacion==MOSTRAR)) {
      poke (0x40, 0x10, peek(0x40, 0x10) & 0xFFCF | colorbits);
      if (modo != (peekb(0x40, 0x49) & 0x7F)) {
          regs.x.ax=modo; int86 (0x10, &regs, &regs);
          }
        else {
          regs.x.ax=0x1003; regs.x.bx=1;
          int86 (0x10, &regs, &regs);
          }
      regs.x.ax=0x500+pag; int86 (0x10, &regs, &regs);
      regs.x.ax=0x200; regs.x.bx=pag<<8;
      regs.h.dh=cy; regs.h.dl=cx; int86 (0x10, &regs, &regs);
      if (scr_ok) {
          movedata (FP_SEG(scrbuf), FP_OFF(scrbuf),
                    (modo==7) ? 0xb000 : 0xb800,
                    (modo==7) ? 0      : peek(0x40, 0x4e), tam);
          if (operacion==RESTAURAR) farfree(scrbuf);
          }
      }
    else if ((operacion==MODO80)) {
      poke (0x40, 0x10, peek(0x40, 0x10) & 0xFFCF | colorbits);
      if (((peekb(0x40, 0x49) & 0x7F) == 0x13) || ((modo!=3) && (modo!=7)))
        textmode ((modo!=7) ? C80:MONO);
      if ((scr_ok) && ((modo==2) || (modo==3) || (modo==7)))
        movedata (FP_SEG(scrbuf), FP_OFF(scrbuf),
                  (modo==7) ? 0xb000 : 0xb800,
                  (modo==7) ? 0      : peek(0x40, 0x4e), tam);
      }
}


char *ReservarMemoria (char huge **buffer)
{
  unsigned long dir;
  char     *bloque, *bf;

  *buffer=bloque=farmalloc(SMAX << 1);
  bf=farmalloc(4096L);

  if ((*buffer==NULL) || (bf==NULL)) {
    if (*buffer!=NULL) farfree ((char far *) *buffer);
    textbackground (CFX); textcolor (CX); clrscr();
    gotoxy (24,12);
    if (sp)
        cputs ("Memoria insuficiente para 765DEBUG");
      else
        cputs ("Insufficient memory for 765DEBUG");
    CursorOff();
    delay (2000);
    return (NULL);
    }

  farfree (bf);  /* liberar bloque "de seguridad" */

  dir = ((unsigned long) FP_SEG(*buffer) <<4) + FP_OFF(*buffer);
  if ( (dir>>16) != ( (dir+SMAX) >> 16) )
     *buffer+=SMAX;    /* evitar buffer entre dos p ginas de DMA */

  return (bloque);
}


void DibujaOpciones()
{
  textbackground (CFM); textcolor(CM); clrscr();
  v3dg (3, 2, 76, 12, 1, 2, C3D, CFM, CB);
  v3d  (3, 2, 76, 12, 2);
  textcolor (C765); gotoxy (2,1); cputs("ÛßßßÛ ÛßßÛÛ Ûßßßß     Û       Û                   "); textcolor(CVER); cputs(" ÜÜÜÜÜ   ÜÜÜÜÜ");
  textcolor (C765); gotoxy (2,2); cputs("    Û Û     Û         Û       Û           ÜÜÜÜÜÄÅÄ"); textcolor(CVER); cputs(" Û       Û   Û");
  textcolor (C765); gotoxy (2,3); cputs("   ÛÛ ÛßßÛÛ ßßßÛÛ ÛßßßÛ ÛßßßÛ ÛßßßÛ Û   Û Û   Û   "); textcolor(CVER); cputs(" ßßßÛÛ   Û  ÛÛ");
  textcolor (C765); gotoxy (2,4); cputs("   ÛÛ Û  ÛÛ    ÛÛ Û   Û Ûßßßß Û   Û Û   Û Û   Û   "); textcolor(CVER); cputs(" Ü  ÛÛ   Û  ÛÛ");
  textcolor (C765); gotoxy (2,5); cputs("   ßß ßßßßß ßßßßß ßßßßß ßßßßß ßßßßß ßßßßß ßßßßÛ   "); textcolor(CVER); cputs(" ßßßßß ß ßßßßß");
  textcolor (C765); gotoxy (2,6); cputs("                                            ÛÜÛ   ");
  gotoxy (2,6); textcolor (CTIT);
  if (sp)
      cputs("ANALISIS AVANZADO A BAJO NIVEL DE DISQUETES");
    else
      cputs(" ADVANCED LOW LEVEL DISK ANALYSIS UTILITY");
  window (1,1,80,25);
  if (sp) {
      boton ( 3,15,CB1,CTB1,CM,CFM," 0 "); cputs (" Recalibrar unidades + reset.");
      boton ( 3,16,CB2,CTB2,CM,CFM," 1 "); cputs (" Seleccionar unidad.");
      boton ( 3,17,CB1,CTB1,CM,CFM," 2 "); cputs (" Seleccionar densidad + enviar specify.");
      boton ( 3,18,CB2,CTB2,CM,CFM," 3 "); cputs (" Posicionar cabezal.");
      boton ( 3,19,CB1,CTB1,CM,CFM," 4 "); cputs (" Cambiar de cabezal.");
      boton ( 3,20,CB2,CTB2,CM,CFM," 5 "); cputs (" Leer ID's.");
      boton ( 3,21,CB1,CTB1,CM,CFM," 6 "); cputs (" Leer sector al buffer interno.");
      boton ( 3,22,CB2,CTB2,CM,CFM," 7 "); cputs (" Escribir sector del buffer interno.");
      boton ( 3,23,CB1,CTB1,CM,CFM," 8 "); cputs (" Ver o editar sector del buffer interno.");
      boton ( 3,24,CB2,CTB2,CM,CFM," 9 "); cputs (" Formatear pista.");
      boton (51,21,CB2,CTB2,CM,CFM,"F10"); cputs (" Leer pista cruda.");
      boton (51,22,CB1,CTB1,CM,CFM," A "); cputs (" Conmutar MF/MFM.");
      boton (51,23,CB2,CTB2,CM,CFM," I "); cputs (" Informaci¢n disco 2M.");
      boton (51,24,CB1,CTB1,CM,CFM,"ESC"); cputs (" Salir (ALT-X r pido).");
      }
    else {
      boton ( 3,15,CB1,CTB1,CM,CFM," 0 "); cputs (" Recalibrate drives & reset.");
      boton ( 3,16,CB2,CTB2,CM,CFM," 1 "); cputs (" Select drive.");
      boton ( 3,17,CB1,CTB1,CM,CFM," 2 "); cputs (" Select density & send specify.");
      boton ( 3,18,CB2,CTB2,CM,CFM," 3 "); cputs (" Seek to new track.");
      boton ( 3,19,CB1,CTB1,CM,CFM," 4 "); cputs (" Select other head.");
      boton ( 3,20,CB2,CTB2,CM,CFM," 5 "); cputs (" Read ID's.");
      boton ( 3,21,CB1,CTB1,CM,CFM," 6 "); cputs (" Read sector into internal buffer.");
      boton ( 3,22,CB2,CTB2,CM,CFM," 7 "); cputs (" Write internal buffer's sector.");
      boton ( 3,23,CB1,CTB1,CM,CFM," 8 "); cputs (" View or edit internal buffer's sector.");
      boton ( 3,24,CB2,CTB2,CM,CFM," 9 "); cputs (" Format track.");
      boton (51,21,CB2,CTB2,CM,CFM,"F10"); cputs (" Read a raw track.");
      boton (51,22,CB1,CTB1,CM,CFM," A "); cputs (" Change MF/MFM.");
      boton (51,23,CB2,CTB2,CM,CFM," I "); cputs (" 2M disk information.");
      boton (51,24,CB1,CTB1,CM,CFM,"ESC"); cputs (" Exit (ALT-X quit!).");
      }

  v3df (51, 14, 27, 7, 1, 1, CMI, CFM);
}


void DibujaValores (unidad, vunidad, mf_mfm, cilindro, cabezal)
{
  textcolor (CI); textbackground (CFM);
  gotoxy (57, 16); if (sp) cputs("Unidad "); else cputs("Drive ");
  putch(unidad+'A'); putch(':');
  if (mf_mfm) cputs("   MFM"); else cputs("    MF");
  gotoxy (55, 17);
  switch (vunidad) {
    case 2:   cputs ("250 Kbps"); break;
    case 1:   cputs ("300 Kbps"); break;
    case 0:   cputs ("500 Kbps"); break;
    default:  cputs ("  1 Mbps"); break;
    }
  switch (vunidad) {
    case 1:  cputs(" ..... 360K"); break;
    case 2:  cputs(" ..... 720K"); break;
    case 3:  cputs(" .... 2.88M"); break;
    default: cputs("  1.2/1.44M"); break;
    }
  gotoxy (55, 18);
  if (sp) cputs("Cilindro "); else cputs("Cylinder ");
  cputs (dec2strq(cilindro)); cputs("  ");
  gotoxy (68, 18);
  if (sp) cputs("Cara "); else cputs("Side ");
  cputs (dec2strq(cabezal));
}


int PeligrosoOS2()
{
  int largarse=0, t;

  if (EsOS2()) {
    ventana (ABRIR, 15, 8, 64, 19, CS, CFS);
    if (sp) {
        gotoxy (5, 2); cputs("Esta sesi¢n DOS est  corriendo bajo OS/2");
        gotoxy (5, 4); cputs("765DEBUG puede funcionar bajo  OS/2,  pero");
        gotoxy (3, 5); cputs("sus continuos accesos al hardware de disco y");
        gotoxy (3, 6); cputs("el control que realiza  OS/2  de los puertos");
        gotoxy (3, 7); cputs("convierten la tarea en algo conflictiva.");
        gotoxy (8, 9); cputs("INTRO - CONTINUAR     ESC - SALIR");
        }
      else {
        gotoxy (6, 2); cputs("This DOS session is running under OS/2");
        gotoxy (5, 4); cputs("765DEBUG can work with OS/2 loaded, but it");
        gotoxy (3, 5); cputs("continually accesses disk hardware directly,");
        gotoxy (3, 6); cputs("and the I/O ports control performed by  OS/2");
        gotoxy (3, 7); cputs("cause its functions to conflict somewhat.");
        gotoxy (8, 9); cputs("ENTER - CONTINUE      ESC - EXITS");
        }
    CursorOff(); t = Tecla();
    if (t!=13) largarse=1;
    ventana (CERRAR, 15, 8, 64, 19, 0, 0);
    }

  return (largarse);
}


void TestDMA (int dmakb)
{
  unsigned emm;
  unsigned long bytes;

  if ((bytes=VDMA(&emm)) < 1024L*dmakb) {
    ventana (ABRIR, 21, 6, 61, 20, CD, CFD);
    if (sp) {
        gotoxy (17,1); cputs("AVISO");
        gotoxy (2, 3); cputs("El buffer para transferencias con DMA");
        gotoxy (2, 4); cputs("de su  controlador de memoria  admite");
        gotoxy (2, 5); cputs("s¢lo "); cputs (dec5strq((unsigned) bytes));
                       cputs (" bytes (");
                       cputs(dec2strq((unsigned) bytes/1024)); cputs(" Kb).");
        gotoxy (2, 7); cputs("El sistema podr¡a estrellarse o puede");
        gotoxy (2, 8); cputs("salir un mensaje de su controlador de");
        gotoxy (2, 9); cputs("memoria si trabaja con sectores de un");
        gotoxy (2,10); cputs("tama¤o superior (y s¢lo en ese caso).");
        if (emm == 20805) {
          gotoxy (2,12); cputs("Con su controlador (QEMM) bastar¡a en");
          gotoxy (2,13); cputs("ese caso a¤adir la opci¢n DMA=Kbytes.");
          }
        }
      else {
        gotoxy (16,1); cputs("WARNING");
        gotoxy (2, 3); cputs("The current maximum  DMA  buffer size");
        gotoxy (2, 4); cputs("supported by your memory manager  has");
        gotoxy (2, 5); cputs("only "); cputs (dec5strq((unsigned) bytes));
                       cputs (" bytes (");
                       cputs(dec2strq((unsigned) bytes/1024)); cputs(" Kb).");
        gotoxy (2, 7); cputs("The system may hang;  or  your memory");
        gotoxy (2, 8); cputs("manager may print an error message if");
        gotoxy (2, 9); cputs("you work with sectors bigger than the");
        gotoxy (2,10); cputs("buffer size (and only in this case).");
        if (emm == 20805) {
          gotoxy (2,12); cputs("With your EMM (QEMM) you can add,  if");
          gotoxy (2,13); cputs("needed, a DMA=Kbytes option.");
          }
        }
    CursorOff(); getch();
    ventana (CERRAR, 21, 6, 61, 20, 0, 0);
    }
}


unsigned long VDMA (unsigned *emm)
{
  union    REGS r;
  struct   SREGS s;
  unsigned long bytes;

  bytes = 65536L;    /* supuesto que no hay problemas con el DMA */
  *emm  = 0;         /* supuesto que no hay controlador de memoria */

  if (es386()) {
    r.x.ax = 0x354B;
    int86x (0x21, &r, &r, &s);
    if ((s.es | r.x.bx) && (s.es != 0xFFFF) && (r.x.bx != 0xFFFF)) {
      r.x.ax = 0x8102;
      r.x.dx = r.x.si = r.x.di = 0;
      int86 (0x4B, &r, &r);
      if ((!(r.x.flags & 1)) && (r.x.si | r.x.di)) {
        bytes = ((unsigned long) r.x.si << 16) | r.x.di;
        *emm  = r.x.bx;
        }
      }
    }
  return (bytes);
}


int SalirConfirmado()
{
  int salir, t;

  t = peekb(0x40, 0x49) & 0x7F;

  ventana (ABRIR, 27, 9, 60, 15, CS, CFS);
  textcolor (CS);
  if (sp) {
      gotoxy (5,2); cputs("¨Seguro que desea salir?");
      gotoxy (4,4); cputs("Pulse ");
      }
    else {
      gotoxy (2,2); cputs("Are you sure you want to exit?");
      gotoxy (4,4); cputs("Press ");
      }
  if (t!=7) {
      textcolor (CFS+BLINK); textbackground (CS);
      }
    else {
      textcolor (0); textbackground (7);
      }
  if (sp) {
      cputs ("INTRO");
      textcolor (CS); textbackground (CFS); cputs(" para confirmar");
      }
    else {
      cputs ("ENTER");
      textcolor (CS); textbackground (CFS); cputs(" key to confirm");
      }
  CursorOff();
  t=Tecla();
  if ((t==13) || (t==0x2D00)) salir=1; else salir=0;
  ventana (CERRAR, 27, 9, 60, 15, 0, 0);
  return (salir);
}


void Informe2M (int unidad, int *cilindro, int *cabezal, int vunidad)
{
  Boot   sector0, virtual;
  int    i, error, anio, mes, dia, hora, min, seg, dif;

  *cilindro = *cabezal = 0;   /* terminaremos en pista 0 */

  outportb (FD_DCR, peekb(0x40, 0x8B) >> 6);  /* velocidad normal */

  ventana (ABRIR, 22, 7, 57, 16, C2M, CF2M);

  if (sp) {
      gotoxy (9, 1);
      cputs("INFORME UNIDAD "); putch(unidad+'A'); putch(':');
      }
    else {
      gotoxy (10,1);
      cputs("DRIVE "); putch(unidad+'A'); putch(':'); cputs(" REPORT");
      }

  if (!Hay2M3()) {
      if (sp) {
          gotoxy (5,4); cputs("2M ¢ 2MX 3.X no instalado");
          gotoxy (8,6); cputs("Imposible informar");
          }
        else {
          gotoxy (6,4); cputs("2M ¢ 2MX 3.X not loaded");
          gotoxy (5,6); cputs("Information not available");
          }
      }
    else {
      error=biosdisk(2, unidad, 0x80, 0, 1, 1, &sector0);
      if (error) {
        biosdisk (0, unidad, 0, 0, 1, 1, &sector0);
        error=biosdisk(2, unidad, 0x80, 0, 1, 1, &sector0);
        }
      if (error) error=biosdisk(2, unidad, 0x80, 0, 1, 1, &sector0);
      if (error==4) {
          if (sp) {
              gotoxy (4,5); cputs("El disquete no es de tipo 2M");
              }
            else {
              gotoxy (4,5); cputs("Diskette is not 2M formatted");
              }
          }
      else if (error) {
        if (sp) {
            gotoxy (8,4); cputs("Unidad no preparada");
            gotoxy (8,5); cputs("o disquete extra¤o.");
            }
          else {
            gotoxy (10,4); cputs("Drive not ready");
            gotoxy (7,5); cputs("or strange disk format.");
            }
        }
      else {
        gotoxy (3,3); if (sp) cputs("Tipo de disco:    ");
          else cputs("Disk type:       ");
        for (i=0; i<8; i++) putch(sector0.IdSis[i]);

        gotoxy (3,4); if (sp) cputs("Capacidad:        ");
          else cputs("Disk size:       ");
        cputs (dec5strq(sector0.NumSect/(1024/sector0.BytesSect)));
        cputs(" Kb");

        gotoxy (3,5); if (sp) cputs("Versi¢n formato:  ");
          else cputs("Format release:  ");
        cputs (dec2strq(sector0.VersionFmt));

        gotoxy (3,6); if (sp) cputs("Fecha formateo:   ");
          else cputs("Format date:     ");

        if (sector0.Flags & 1) {
            anio=1980+(sector0.FechaF>>9);
            mes=(sector0.FechaF>>5) & 15;
            dia=sector0.FechaF & 31;
            if (sp) {
                cputs (dec2strq(dia)); putch('/');
                cputs (dec2str(mes)); putch('/');
                cputs (dec5strq(anio));
                }
              else {
                cputs (dec2strq(mes)); putch('/');
                cputs (dec2str(dia)); putch('/');
                cputs (dec5strq(anio));
                }
            }
          else {
            if (sp) cputs("No disponible"); else cputs("Not available");
            }

        gotoxy (3,7); if (sp) cputs("Hora formateo:    ");
          else cputs("Format time:     ");

        if (sector0.Flags & 1) {
            hora=sector0.HoraF>>11;
            min=(sector0.HoraF>>5) & 63;
            seg=(sector0.HoraF & 31) << 1;
                cputs (dec2str(hora)); putch(':');
                cputs (dec2str(min)); putch(':');
                cputs (dec2str(seg));
            }
          else {
            if (sp) cputs("No disponible"); else cputs("Not available");
            }
      }
    }

  CursorOff();
  i = Tecla();
  ventana (CERRAR, 22, 7, 57, 16, 0, 0);

  SeleccionarUnidad (unidad, vunidad); /* restaurar condiciones iniciales */
}


void SeleccionarUnidad (unidad, vunidad)
{
  pokeb(0x40,0x40,0xFF);
  outportb (FD_DOR, 1<<(unidad+4) | 4+8 | unidad);
  pokeb (0x40, 0x3F, peekb(0x40, 0x3F) | (1 << unidad));

  DensidadSpecify (vunidad);

  MotorOff();
}


void ResetUnidad (unidad)
{
  int i, vez;

  for (vez=0; vez<2; vez++) {

    pokeb (0x40, 0x3E, peekb (0x40, 0x3E) & 0x7F); /* asegurar detecci¢n */

    outportb (0x20, 0x66);  /* muy largo de explicar :-) */

    outportb (FD_DOR, 1<<(unidad+4) | unidad | 8);     /* reset */
    delay (10);
    outportb (FD_DOR, 1<<(unidad+4) | unidad | 8+4);   /* fin reset */

    if (EsperarInt()) {    /* esperar interrupci¢n (si viene) */
      outfdc (8);          /* comando 'leer estado de interrupciones' */
      (void) infdc();      /* leer y desechar resultado */
      (void) infdc();
      break;               /* reset con ‚xito */
      }
  }

  MotorOff();
}


void DensidadSpecify (int vunidad)
{
  outportb (FD_DCR, vunidad);  /* seleccionar densidad */

  outfdc (3);           /* comando Specify */
  if (vunidad==3)
      outfdc (0xAF);    /* tiempo de acceso pista-pista y head unload */
    else if (!vunidad)
      outfdc (0xBF);
    else
      outfdc (0xDF);
  outfdc (2);           /* head load time = 1; modo DMA */
}


void recalibrar (unidad, cabezal, vunidad, pausa)
{
  int  recal, res, pis;

  ventana (ABRIR, 30, 10, 50, 17, 15, 1);

  gotoxy (3, 3);
  if (sp) cputs("Recalibrando "); else cputs("Recalibrate ");
  putch(unidad+'A'); putch(':');
  CursorOff();

  MotorOn (unidad);  /* asegurar que el motor est  en marcha */
  SeleccionarUnidad (unidad, vunidad);
  MotorOn (unidad);

  ResetUnidad (unidad);                 /* aprovechar para resetear */
  DensidadSpecify (vunidad);

      /**** Recalibrar hasta dos veces si es preciso ****/

  for (recal=0; recal<2; recal++) {

    outfdc (7);                        /* comando de recalibrado */
    outfdc (cabezal << 2 | unidad);    /* byte 1 de dicho comando */

    EsperarInt();        /* esperar interrupci¢n */

    outfdc (8);          /* comando 'leer estado de interrupciones' */

    res=infdc();         /* leer resultado */
    pis=infdc();

    if (!recal) clrscr();

    gotoxy (5, 1+recal*2);

    if ((res>=0) && (pis>=0)) {
        cputs ("ST0 = 0x"); cputs (hex2str(res));
        gotoxy (5, 2+recal*2);
        if (sp) cputs ("Pista = "); else cputs ("Track = ");
        cputs (dec2strq(pis));
        }
      else
        cputs ("ST0 = ??");

    if (!((res ^ 32) & (0xF0))) break;  /* resultado correcto */
  }

  MotorOff();
  if (recal<2) {     /* sin fallo */
      CursorOff();
      if (pausa) delay(750);
      }
    else {
      gotoxy (3, 6);
      if (sp) cputs ("Pulsa una tecla"); else cputs (" Press any key ");
      CursorOff(); Tecla();
      }
  ventana (CERRAR, 30, 10, 50, 17, 0, 0);
}


void posicionar (unidad, cabezal, cilindro, vunidad)
{
  int  res, pis;

  ventana (ABRIR, 30, 9, 50, 14, 15, 1);

  gotoxy (3, 2);
  if (sp) cputs("Posicionando "); else cputs("    Seek ");
  putch(unidad+'A'); putch(':');
  CursorOff();

  MotorOn (unidad);  /* asegurar que el motor est  en marcha */
  SeleccionarUnidad (unidad, vunidad);
  MotorOn (unidad);

      /**** Desplazar cabezal hasta la pista ****/

  outfdc (0xF);                       /* comando 'Seek' */
  outfdc (cabezal << 2 | unidad);     /* byte 1 de dicho comando */
  outfdc (cilindro);

  EsperarInt();          /* esperar interrupci¢n */

  outfdc (8);            /* comando 'leer estado de interrupciones' */
  res=infdc();
  pis=infdc();

  clrscr(); gotoxy (5, 1);

  if ((res>=0) && (pis>=0)) {
      cputs ("ST0 = 0x"); cputs (hex2str(res)); gotoxy (5, 2);
      if (sp) cputs ("Pista = "); else cputs ("Track = ");
      cputs (dec2strq(pis)); gotoxy (3, 4);
      }
    else
      cputs ("ST0 = ??");

  MotorOff();

  if (!(res & 0xC0)) {
      CursorOff();
      delay(750);
      }
    else {
      if (sp) cputs ("Pulsa una tecla"); else cputs (" Press any key ");
      CursorOff(); Tecla();
      }

  ventana (CERRAR, 30, 9, 50, 14, 0, 0);
}


void LeerSector (unidad, mf_mfm, cilindro, cabezal, buffer, bytes, vunidad)
char      huge *buffer;
unsigned  *bytes;
{
  static   sector=1, nsects=1, tsector=2, t128=128;
  unsigned t, sbytes;
  char     cad[32];

  ventana (ABRIR, 23, 7, 65, 21, CL, CFL);

  if (sp) {
      gotoxy (12,1);
      cputs("LECTURA DE SECTOR");
      }
    else {
      gotoxy (16,1);
      cputs("SECTOR READ");
      }

  gotoxy (2,3); cputs("Sector: ");
  strcpy (cad, dec3strq (sector));
  if (!input (cad, 1, 3, CL, CFL)) goto AbortaLectura;
  sector = atoi (cad); if (sector>255) sector=255;

  gotoxy (2, 5); if (sp) cputs("Tama¤o de sector:"); else cputs("Sector size:");
  gotoxy (2, 6); cputs("  0 -> 1-128 bytes    5 ->  4096 bytes");
  gotoxy (2, 7); cputs("  1 ->   256 bytes    6 ->  8192 bytes");
  gotoxy (2, 8); cputs("  2 ->   512 bytes    7 -> 16384 bytes");
  gotoxy (2, 9); cputs("  3 ->  1024 bytes    8 -> 32768 bytes");
  gotoxy (2,10); cputs("  4 ->  2048 bytes    Val");
  if (sp) cputs("or: "); else cputs("ue: ");
  strcpy (cad, dec2strq (tsector));
  if (!input (cad, 1, 2, CL, CFL)) goto AbortaLectura;
  tsector = atoi (cad);

  if (tsector==0) {
    gotoxy (24, 10); if (sp) cputs("Indica"); else cputs("Select");
    cputs(" 1-128: ");
    strcpy (cad, dec3strq (t128));
    if (!input (cad, 1, 3, CL, CFL)) goto AbortaLectura;
    t128 = atoi (cad); if (t128>255) t128=255; else if (!t128) t128=1;
    }

  sbytes = tsector? (unsigned) 128 << tsector: t128;

  gotoxy (sp?19:16, 3); cputs(sp?"N§ de sectores: ":"Number of sectors: ");
  strcpy (cad, dec3strq (nsects));
  if (!input (cad, 1, 3, CL, CFL)) goto AbortaLectura;
  nsects = atoi (cad);
  if (!nsects || (nsects > SMAX/sbytes)) {
    if (!nsects) nsects=1; else nsects=SMAX/sbytes;
    gotoxy (sp?19:16, 3);
    cputs(sp?"N§ de sectores: ":"Number of sectors: ");
    cputs (dec3strq(nsects)); cputs("  ");
    }

  gotoxy (7,12); if (sp) cputs("Cilindro: "); else cputs("Cylinder: ");
  strcpy (cad, dec3strq (cilindro));
  if (!input (cad, 1, 3, CL, CFL)) goto AbortaLectura;
  cilindro = atoi (cad); if (cilindro>255) cilindro=255;

  gotoxy (25,12); if (sp) cputs("Cara: "); else cputs("Side: ");
  strcpy (cad, dec3strq (cabezal));
  if (!input (cad, 1, 3, CL, CFL)) goto AbortaLectura;
  cabezal = atoi (cad); if (cabezal>255) cabezal=255;

  clrscr();
  gotoxy (17, 6); if (sp) cputs("Leyendo..."); else cputs("Reading...");

  for (t=0; t<SMAX; t+=2) {
    buffer[t]=0x5A; buffer[t+1]=0xA5;  /* "borrar" el buffer */
    }

  MotorOn (unidad);
  SeleccionarUnidad (unidad, vunidad);
  MotorOn (unidad);

  *bytes = sbytes * nsects;

  PreparaDma (0x46, *bytes, buffer);

  outfdc (0x06 | mf_mfm << 6);     /* comando para leer */
  outfdc (cabezal << 2 | unidad);  /* byte 1 de dicho comando */
  outfdc (cilindro);
  outfdc (cabezal);
  outfdc (sector);
  outfdc (tsector);
  outfdc (sector+nsects-1);
  outfdc (1);                     /* GAP para leer: poco importante */
  outfdc (t128);                  /* tama¤o si tsector=0 */

  EsperarInt();                   /* esperar interrupci¢n */

  ventana (CERRAR, 23, 7, 65, 21, 0, 0);
  Resultados (LEER, unidad, cabezal);

  goto FinLectura;

  AbortaLectura: ventana (CERRAR, 23, 7, 65, 21, 0, 0);
  FinLectura: ;
}


void LeerPista (unidad, mf_mfm, cilindro, cabezal, buffer, bytes, vunidad)
char      huge *buffer;
unsigned  *bytes;
{
  int      tsector;
  unsigned t;

  ventana (ABRIR, 23, 11, 58, 15, CL, CFL);
  clrscr();
  gotoxy (7, 2);
  if (sp) cputs("Leyendo pista cruda...");
    else cputs("Reading a raw track...");

  switch (TipoDrive(unidad)) {
    case 2:  if (vunidad==0) tsector = 7; else tsector = 6; break;
    case 5:  if (vunidad==3) tsector = 8;  /* sin break :-) */
    case 4:  if (vunidad==0) tsector = 7;
             if ((vunidad==1) || (vunidad==2)) tsector = 6; break;
    case 1:
    case 3:
    default: tsector = 6; break;
    }

  for (t=0; t<SMAX; t+=2) {
    buffer[t]=0x5A; buffer[t+1]=0xA5;  /* "borrar" el buffer */
    }

  MotorOn (unidad);
  SeleccionarUnidad (unidad, vunidad);
  MotorOn (unidad);

  *bytes = 128 << tsector;  /* "sector" de 8K, 16K ¢ 32K */

  PreparaDma (0x46, *bytes, buffer);

  outfdc (0x02 | mf_mfm << 6);     /* comando para leer pista */
  outfdc (cabezal << 2 | unidad);  /* byte 1 de dicho comando */
  outfdc (cilindro);
  outfdc (cabezal);
  outfdc (1);                      /* sector: cualquier valor valdr¡a */
  outfdc (tsector);
  outfdc (1);                      /* 1 sector */
  outfdc (1);                      /* GAP para leer: poco importante */
  outfdc (128);                    /* tama¤o si tsector=0 */

  EsperarInt();                   /* esperar interrupci¢n */

  ventana (CERRAR, 23, 11, 58, 15, 0, 0);

  Resultados (LEER, unidad, cabezal);
}


void EscribirSector (unidad, mf_mfm, vunidad, cilindro, cabezal, bytes, buffer)
char huge *buffer;
unsigned  *bytes;
{
  static   sector=1, tsector=2, t128=128, wreset=0, dmacnt=-1;
  unsigned nsects, sbytes, huge *p, chk, i, dmares;
  char     cad[32];

  ventana (ABRIR, 23, 5, 65, 23, CE, CFE);

  if (sp) {
      gotoxy (11,1);
      cputs("ESCRITURA DE SECTOR");
      }
    else {
      gotoxy (16,1);
      cputs("SECTOR WRITE");
      }

  gotoxy (2,3); cputs("Sector: ");
  strcpy (cad, dec3strq (sector));
  if (!input (cad, 1, 3, CE, CFE)) goto AbortaEscritura;
  sector = atoi (cad); if (sector>255) sector=255;

  gotoxy (2, 5); if (sp) cputs("Tama¤o de sector:"); else cputs("Sector size:");
  gotoxy (2, 6); cputs("  0 -> 1-128 bytes    5 ->  4096 bytes");
  gotoxy (2, 7); cputs("  1 ->   256 bytes    6 ->  8192 bytes");
  gotoxy (2, 8); cputs("  2 ->   512 bytes    7 -> 16384 bytes");
  gotoxy (2, 9); cputs("  3 ->  1024 bytes    8 -> 32768 bytes");
  gotoxy (2,10); cputs("  4 ->  2048 bytes    Val");
  if (sp) cputs("or: "); else cputs("ue: ");
  strcpy (cad, dec2strq (tsector));
  if (!input (cad, 1, 2, CE, CFE)) goto AbortaEscritura;
  tsector = atoi (cad);

  if (tsector==0) {
    gotoxy (24, 10); if (sp) cputs("Indica"); else cputs("Select");
    cputs(" 1-128: ");
    strcpy (cad, dec3strq (t128));
    if (!input (cad, 1, 3, CE, CFE)) goto AbortaEscritura;
    t128 = atoi (cad); if (t128>255) t128=255; else if (!t128) t128=1;
    }

  sbytes = tsector? (unsigned) 128 << tsector: t128;

  nsects = *bytes / sbytes; if (!nsects) nsects=1;
  gotoxy (sp?19:16, 3); cputs(sp?"N§ de sectores: ":"Number of sectors: ");
  strcpy (cad, dec3strq (nsects));
  if (!input (cad, 1, 3, CL, CFL)) goto AbortaEscritura;
  nsects = atoi (cad);
  if (!nsects || (nsects > SMAX/sbytes)) {
    if (!nsects) nsects=1; else nsects=SMAX/sbytes;
    gotoxy (sp?19:16, 3);
    cputs(sp?"N§ de sectores: ":"Number of sectors: ");
    cputs (dec3strq(nsects)); cputs("  ");
    }

  gotoxy (7,12); if (sp) cputs("Cilindro: "); else cputs("Cylinder: ");
  strcpy (cad, dec3strq (cilindro));
  if (!input (cad, 1, 3, CE, CFE)) goto AbortaEscritura;
  cilindro = atoi (cad); if (cilindro>255) cilindro=255;

  gotoxy (25,12); if (sp) cputs("Cara: "); else cputs("Side: ");
  strcpy (cad, dec3strq (cabezal));
  if (!input (cad, 1, 3, CE, CFE)) goto AbortaEscritura;
  cabezal = atoi (cad); if (cabezal>255) cabezal=255;

  gotoxy (2, 14);
  if (sp)
      cputs("¨Escritura normal sin reset? (S/N): ");
    else
      cputs("Normal write without reset? (Y/N): ");
  if (wreset) strcpy (cad, "N"); else strcpy (cad, sp?"S":"Y");
  if (!input (cad, 0, 1, CE, CFE)) goto AbortaEscritura;
  if ((cad[0] | 32) == 'n') wreset=1; else wreset=0;

  if (wreset) {
    if (dmacnt == -1) /* primera vez */
      switch (TipoDrive(unidad)) {
        case 2:  if (!vunidad) dmacnt = 10240; else dmacnt = 6098; break;
        case 4:  switch (vunidad) {
                   case 1:  dmacnt =  7344; break; /* valores 2MGUI t¡picos */
                   case 2:  dmacnt =  6098; break;
                   case 3:  dmacnt = 24626; break;
                   default: dmacnt = 12314; break;
                   }
                 break;
        default: dmacnt =  6098; break;
        }
    gotoxy (2, 14); clreol();
    if (sp)
        cputs("Valor del contador del DMA: ");
      else
        cputs("Value for DMA count: ");
    strcpy (cad, dec5strq (dmacnt));
    if (!input (cad, 1, 5, CE, CFE)) goto AbortaEscritura;
    dmacnt = atoi (cad);
    if (dmacnt > SMAX - 32) dmacnt = SMAX-32;

    gotoxy (2, 16);
    if (sp)
        cputs("¨M s checksum y GAP de 2MGUI? (S/N): ");
      else
        cputs("May I add 2MGUI checksum/GAP (Y/N)?: ");
    strcpy (cad, sp?"S":"Y");
    if (!input (cad, 0, 1, CE, CFE)) goto AbortaEscritura;
    if ((cad[0] | 32) == 'n')
        dmares = dmacnt;
      else {
        p = (unsigned *) buffer; chk=0;
        for (i=dmacnt; i < dmacnt+22; i++) buffer[i]=0;    /* GAP Anti-FIFO */
        for (i=0; i < ((dmacnt+1) >> 1); i++) chk += *p++; /* checksum */
        chk = ~chk;
        buffer [dmacnt] = chk & 0xFF;  buffer [dmacnt+1] = chk >> 8;
        dmares = dmacnt+22;
        }
    }

  clrscr();
  gotoxy (15, 8);
  if (sp) cputs("Escribiendo..."); else cputs("  Writing...");

  MotorOn (unidad);
  SeleccionarUnidad (unidad, vunidad);
  MotorOn (unidad);

  *bytes = sbytes * nsects;

  PreparaDma (0x4A, wreset? dmares: *bytes, buffer);

  outfdc (0x05 | mf_mfm << 6);     /* comando para escribir */
  outfdc (cabezal << 2 | unidad);  /* byte 1 de dicho comando */
  outfdc (cilindro);
  outfdc (cabezal);
  outfdc (sector);
  outfdc (tsector);
  outfdc (sector+nsects-1);
  outfdc (1);                      /* GAP m¡nimo */
  outfdc (t128);                   /* tama¤o si tsector=0 */

  if (wreset) {
      EsperaDma0 (unidad);         /* con reset precario */
      ResetUnidad (unidad);        /* reset en condiciones */
      SeleccionarUnidad (unidad, vunidad);
      }
    else EsperarInt();

  ventana (CERRAR, 23, 5, 65, 23, 0, 0);

  if (!wreset) Resultados (ESCRIBIR, unidad, cabezal);

  goto FinEscritura;

  AbortaEscritura: ventana (CERRAR, 23, 5, 65, 23, 0, 0);
  FinEscritura: ;
}


void FormatearPista (unidad, mf_mfm, vunidad, cilindro, cabezal)
{
  unsigned char buffer[768];  /* ­hasta 192 sectores! */
  static        sectores=-1,
                pokete=0xE6, tsector=2, eformat=0, freset=0,
                autogap=1, tunidad=4, gap=108, dmacnt=-1;
  int           i, b765;
  long          bytes;
  char          cad[32];

  ventana (ABRIR, 23, 3, 64, 23, CF, CFF);

  if (sp) {
      gotoxy (12,1);
      cputs("FORMATEO DE PISTA");
      }
    else {
      gotoxy (15,1);
      cputs("FORMAT TRACK");
      }

  gotoxy (2, 3); if (sp) cputs("Tama¤o de sector:"); else cputs("Sector size:");
  gotoxy (2, 4); cputs("  0 -> 1-128 bytes    5 ->  4096 bytes");
  gotoxy (2, 5); cputs("  1 ->   256 bytes    6 ->  8192 bytes");
  gotoxy (2, 6); cputs("  2 ->   512 bytes    7 -> 16384 bytes");
  gotoxy (2, 7); cputs("  3 ->  1024 bytes    8 -> 32768 bytes");
  gotoxy (2, 8); cputs("  4 ->  2048 bytes    Val");
  if (sp) cputs("or: "); else cputs("ue: ");
  strcpy (cad, dec2strq (tsector));
  if (!input (cad, 1, 2, CF, CFF)) goto AbortaFormateo;
  tsector = atoi (cad);

  if (sectores==-1) { /* primera vez */
    switch (TipoDrive(unidad)) {
      case 2:  if (vunidad==0) sectores = 15; else sectores=9; break;
      case 5:  if (vunidad==3) sectores = 36;  /* sin break :-) */
      case 4:  if (vunidad==0) sectores = 18;
               if ((vunidad==1) || (vunidad==2)) sectores=9; break;
      case 1:
      case 3:
      default: sectores = 9;  tunidad = 1; break;
      }
    if (!mf_mfm) sectores >>= 1;
    }

  gotoxy (2,10); if (sp) cputs("Sectores: "); else cputs("Sectors: ");
  strcpy (cad, dec3strq (sectores));
  if (!input (cad, 1, 3, CF, CFF)) goto AbortaFormateo;
  sectores = atoi (cad); if (sectores>192) sectores=192;

  gotoxy (2,12);
  if (sp) cputs("¨Calculo yo el GAP3? (S/N): ");
    else cputs("Do I adjust the GAP3? (Y/N): ");
  if (autogap) strcpy (cad, sp?"S":"Y"); else strcpy (cad, "N");
  if (!input (cad, 0, 1, CF, CFF)) goto AbortaFormateo;
  if ((cad[0] | 32) == 'n') autogap=0; else autogap=1;

  if (autogap) {
      gotoxy ( 2, 12); clreol();
      if (TipoDrive(unidad)!=-1)
          tunidad=TipoDrive(unidad);
        else {
          gotoxy (17, 11); cputs("1 -  360K");
          gotoxy (17, 12); cputs("2 -  1.2M");
          gotoxy (17, 13); cputs("3 -  720K");
          gotoxy (17, 14); cputs("4 - 1.44M");
          gotoxy (17, 15); cputs("5 - 2.88M");
          gotoxy ( 2, 17); if (sp) cputs("Indica el tipo de UNIDAD: ");
                             else cputs("Select DRIVE type: ");

          strcpy (cad, dec2strq (tunidad));
          if (!input (cad, 1, 2, CF, CFF)) goto AbortaFormateo;
          tunidad = atoi (cad);
          if (tunidad>5) { tunidad=4; goto AbortaFormateo; }
          for (i=11; i<=17; i++) { gotoxy (1, i); clreol(); }
          }
      if (tunidad==6) tunidad=5;  /* convenci¢n AMI */
      switch (tunidad) {
        case 1:
        case 3: switch (vunidad) {
                  case 1:  bytes =  7500; break;
                  default: bytes =  6250; break;
                  }
                break;
        case 2: switch (vunidad) {
                  case 0:  bytes = 10416; break;
                  case 2:  bytes =  5208; break;
                  default: bytes =  6250; break;
                  }
                break;
        case 4:
        case 5: switch (vunidad) {
                  case 1:  bytes =  7500; break;
                  case 2:  bytes =  6250; break;
                  case 3:  bytes = 25000; break;
                  default: bytes = 12500; break;
                  }
                break;
        }
      b765=62; if (!mf_mfm) { bytes >>= 1; b765 >>= 1; }
      gap = (bytes-((b765 + (128L << tsector)) * sectores)-146-32) / sectores;
      if (gap>255) gap=255;
      if (gap<1) {
        gap=1;
        gotoxy (10,12); if (sp) cputs("-> ­No caben tantos sectores!");
                          else cputs("-> Too many sectors for track!");
        }
      gotoxy (2, 12); cputs("GAP3: "); cputs(dec5strq(gap));
      }
    else {
      gotoxy (2,12); clreol(); cputs("GAP3: ");
      strcpy (cad, dec3strq (gap));
      if (!input (cad, 1, 3, CF, CFF)) goto AbortaFormateo;
      gap = atoi (cad); if (gap>255) gap=255;
      }

  gotoxy (20,10); if (sp) cputs("Byte de relleno: "); else cputs("Fill byte: ");
  strcpy (cad, dec3strq (pokete));
  if (!input (cad, 1, 3, CF, CFF)) goto AbortaFormateo;
  pokete = atoi (cad); if (pokete>255) pokete=255;

  gotoxy (2, 14);
  if (sp)
      cputs("¨Usar valores normales? (S/N): ");
    else
      cputs("Format with standard values? (Y/N): ");
  if (eformat) strcpy (cad, "N"); else strcpy (cad, sp?"S":"Y");
  if (!input (cad, 0, 1, CF, CFF)) goto AbortaFormateo;
  if ((cad[0] | 32) == 'n') eformat=1; else eformat=0;

  gotoxy (2, 16);
  if (sp)
      cputs("¨Formateo normal sin reset? (S/N): ");
    else
      cputs("Normal format without reset? (Y/N): ");
  if (freset) strcpy (cad, "N"); else strcpy (cad, sp?"S":"Y");
  if (!input (cad, 0, 1, CF, CFF)) goto AbortaFormateo;
  if ((cad[0] | 32) == 'n') freset=1; else freset=0;

  if (freset) {
    if (dmacnt==-1) dmacnt=sectores<<2;
    gotoxy (2, 18);
    if (sp)
        cputs("Valor del contador del DMA: ");
      else
        cputs("Value for DMA count: ");
    strcpy (cad, dec5strq (dmacnt));
    if (!input (cad, 1, 5, CF, CFF)) goto AbortaFormateo;
    dmacnt = atoi (cad);
    }

  for (i=0; i<sectores; i++) {   /* tabla propuesta para formatear */
    buffer[i*4]=cilindro;
    buffer[i*4+1]=cabezal;
    buffer[i*4+2]=i+1;
    buffer[i*4+3]=tsector;
    }

  if (eformat)
    if (!EditarTablaFmt (buffer, sectores))  /* permitir su alteraci¢n */
      goto AbortaFormateo;

  clrscr();
  gotoxy (16, 9);
  if (sp) cputs("Formateando..."); else cputs("Formatting...");

  MotorOn (unidad);
  SeleccionarUnidad (unidad, vunidad);
  MotorOn (unidad);

  PreparaDma(0x4A, freset? dmacnt: sectores << 2, buffer);

  outfdc (0x0D | mf_mfm << 6);     /* comando para formatear */
  outfdc (cabezal << 2 | unidad);  /* byte 1 de dicho comando */
  outfdc (tsector);
  outfdc (sectores);
  outfdc (gap);
  outfdc (pokete);                 /* byte de relleno */

  if (freset) {
      EsperaDma0 (unidad);         /* con reset precario */
      ResetUnidad (unidad);        /* reset en condiciones */
      SeleccionarUnidad (unidad, vunidad);
      }
    else EsperarInt();

  ventana (CERRAR, 23, 3, 64, 23, 0, 0);

  if (!freset) Resultados (FORMATEAR, unidad, cabezal);

  goto FinFormateo;

  AbortaFormateo: ventana (CERRAR, 23, 3, 64, 23, 0, 0);
  FinFormateo: ;
}


int EditarTablaFmt (unsigned char *buffer, int numsect)
{
  static sector=1, cil=-1, cab=-1, sec=-1, tse=-1, valor=0, subop=3;
  int    i, opcion, opc;
  char   cad[32];

  do {
    clrscr();
    if (sp) {
        gotoxy (5,1);
        cputs("OPCIONES ESPECIALES DE FORMATEO");
        }
      else {
        gotoxy (10,1);
        cputs("SPECIAL FORMAT OPTIONS");
        }
    gotoxy (2,3); if (sp) cputs("Sectores:   "); else cputs("Sectors:    ");
    for (i=0; (i<numsect) && (i<66); i++) {
      cputs (dec3str(buffer[i*4+2]));
      putch(' ');
      }
    if (numsect>65) cputs("...");

    if (sp) {
        gotoxy (2, 11); cputs("Opciones:");
        gotoxy (2, 12); cputs("    1 - Introducir manualmente los 4");
        gotoxy (2, 13); cputs("        bytes de un sector.");
        gotoxy (2, 14); cputs("    2 - Modificar un cierto byte en");
        gotoxy (2, 15); cputs("        todos los sectores.");
        gotoxy (2, 16); cputs("  ESC - Cancelar orden de formateo.");
        gotoxy (2, 17); cputs("INTRO - Proceder con el formateo.");
        gotoxy (2, 18); cputs("        Elige: ");
        }
      else {
        gotoxy (2, 11); cputs("Options:");
        gotoxy (2, 12); cputs("    1 - To introduce the 4 bytes one");
        gotoxy (2, 13); cputs("        by one.");
        gotoxy (2, 14); cputs("    2 - To modify one of the 4 bytes");
        gotoxy (2, 15); cputs("        in all sectors.");
        gotoxy (2, 16); cputs("  ESC - Cancel format command.");
        gotoxy (2, 17); cputs("INTRO - Run format command now.");
        gotoxy (2, 18); cputs("        Choose: ");
        }

    do opcion=Tecla();
      while (((opcion<'1') || (opcion>'3')) && (opcion!=27) && (opcion!=13));

    for (i=11; i<19; i++) { gotoxy (1, i); clreol(); }

    if (opcion=='1') {
        do {
          gotoxy (2, 11); if (sp) cputs("Sector a alterar: ");
                            else cputs("Sector to modify: ");
          strcpy (cad, dec3strq (sector));
          if (!input (cad, 1, 3, CF, CFF)) break;
          sector = atoi (cad); if (sector>255) sector=255;
          for (i=0; (i<numsect) && (buffer[i*4+2]!=sector); i++);
          if (buffer[i*4+2]!=sector) {
              gotoxy (2, 13);
              if (sp) cputs("Ese sector no existe.");
                else cputs("Such sector does not exist.");
              }
            else {
              gotoxy (2, 13); if (sp) cputs ("N§ Cilindro   (anterior=");
                                else cputs ("Cylinder number (previous=");
                              cputs (dec3str(buffer[i*4]));
                              cputs ("):  ");
                              if (cil==-1) cil=buffer[i*4];
                              strcpy (cad, dec3strq (cil));
                              if (!input (cad, 1, 3, CF, CFF)) break;
                              cil = atoi(cad); if (cil>255) cil=255;
              gotoxy (2, 14); if (sp) cputs ("N§ Cabezal    (anterior=");
                                else cputs ("Head     number (previous=");
                              cputs (dec3str(buffer[i*4+1]));
                              cputs ("):  ");
                              if (cab==-1) cab=buffer[i*4+1];
                              strcpy (cad, dec3strq (cab));
                              if (!input (cad, 1, 3, CF, CFF)) break;
                              cab = atoi(cad); if (cab>255) cab=255;
              gotoxy (2, 15); if (sp) cputs ("N£mero sector (anterior=");
                                else cputs ("Sector   number (previous=");
                              cputs (dec3str(buffer[i*4+2]));
                              cputs ("):  ");
                              if (sec==-1) sec=buffer[i*4+2];
                              strcpy (cad, dec3strq (sec));
                              if (!input (cad, 1, 3, CF, CFF)) break;
                              sec = atoi(cad); if (sec>255) sec=255;
              gotoxy (2, 16); if (sp) cputs ("Tama¤o Sector (anterior=");
                                else cputs ("Sector    size  (previous=");
                              cputs (dec3str(buffer[i*4+3]));
                              cputs ("):  ");
                              if (tse==-1) tse=buffer[i*4+3];
                              strcpy (cad, dec3strq (tse));
                              if (!input (cad, 1, 3, CF, CFF)) break;
                              tse = atoi(cad); if (tse>255) tse=255;
                              buffer[i*4]=cil;
                              buffer[i*4+1]=cab;
                              buffer[i*4+2]=sec;
                              buffer[i*4+3]=tse;  /* hechos cambios */
              }
          } while (0);  /* do-bobo, para poder salir con break ;-) */
        }
      else if (opcion=='2') {
        if (sp) {
            gotoxy (2, 11); cputs("Caracter¡stica a cambiar: ");
            gotoxy (2, 12); cputs("  0 - N§ Cilindro");
            gotoxy (2, 13); cputs("  1 - N§ Cabezal");
            gotoxy (2, 14); cputs("  2 - N§ Sector");
            gotoxy (2, 15); cputs("  3 - Tama¤o sector");
            gotoxy (2, 16); cputs("      Elige: ");
            }
          else {
            gotoxy (2, 11); cputs("Characteristic to modify: ");
            gotoxy (2, 12); cputs("  0 - Cylinder number");
            gotoxy (2, 13); cputs("  1 - Head number");
            gotoxy (2, 14); cputs("  2 - Sector number");
            gotoxy (2, 15); cputs("  3 - Sector size");
            gotoxy (2, 16); cputs("      Choose: ");
            }
        strcpy (cad, dec2strq (subop));
        if (input (cad, 1, 3, CF, CFF)) {
         subop = atoi (cad);
         if (subop>3)
             subop=3;
           else {
             for (i=11; i<17; i++) { gotoxy (1, i); clreol(); }
             gotoxy (2, 11); if (sp) cputs("Nuevo valor general: ");
                               else cputs("New general value: ");
             strcpy (cad, dec3strq (valor));
             if (input (cad, 1, 3, CF, CFF)) {
               valor = atoi (cad); if (valor>255) valor=255;
               for (i=0; i<numsect; i++) buffer[i*4+subop]=(char) valor;
               }
            }
          }
        }
  } while ((opcion!=27) && (opcion!=13));

  return (opcion==13);
}


void LeerIds (unidad, mf_mfm, cabezal, vunidad)
{
  unsigned long tmp[24], acu;
  int nec[24][7];
  unsigned i, j, lectura, antlectura, cnth, primeravez=0, tec;

  do {
    ventana (ABRIR, 23, 11, 58, 15, CIW, CFIW);

    gotoxy (3, 2);
    if (sp)
        cputs("Leyendo ID's... [ESC-Cancelar]");
      else
        cputs("Reading ID's... [ESC - Aborts]");
    CursorOff();

    while (inportb (0x20) & 0x40) EsperarInt();  /* IRQ6 pendiente */

    MotorOn (unidad);  /* asegurar que el motor est  en marcha */
    SeleccionarUnidad (unidad, vunidad);
    MotorOn (unidad);

    outportb (0x61, inportb(0x61) & 0xFD | 1);  /* inhibir sonido */
    outportb (0x43, 0xB4);                      /* contador 2 */
    outportb (0x42, 0xFF); outportb (0x42, 0xFF);  /* cuenta 0xFFFF */

    for (i=0; i<24; i++) {

      outfdc (0x0A | mf_mfm << 6);        /* comando 'Leer ID' */
      outfdc (cabezal << 2 | unidad);     /* byte 1 del comando */

      lectura=0xFFFF; cnth=0;    /* cuenta inicial */

      do {                       /* esperar interrupci¢n */
        antlectura=lectura;
        outportb (0x43, 0x84);        /* enclavamiento */
        lectura=inportb(0x42);        /* parte baja de la cuenta */
        lectura|=inportb(0x42) << 8;  /* parte alta de la cuenta */
        if (lectura>antlectura) if (cnth++>8) break;  /* timeout */
      } while (!(peekb(0x40, 0x3E) & 0x80));

      pokeb (0x40, 0x3E, peekb (0x40, 0x3E) & 0x7F); /* reset int. */

      outportb (0x61, inportb(0x61) & 0xFE);  /* bajar GATE */
      outportb (0x61, inportb(0x61) | 1);     /* subir GATE */

      if (kbhit()) {
         tec = getch(); if (!tec) tec=getch() << 8;
         if (tec==27) {                             /* tecla ESC */
           ventana (CERRAR, 23, 11, 58, 15, 0, 0);
           goto fin_ids;
           }
         }

      for (j=0; j<7; j++)
        if ((nec[i][j]=infdc())==-1) break;

      if (cnth<9)
          tmp[i]=cnth*65535L + (65535-lectura);
        else {
          tmp[i]=0L;                           /* error */
          nec[i][0]=-1;                        /* no informar */
          pokeb (0x40, 0x40, 0xFF);     /* asegurar motor en marcha */
          }  /* porque probablemente se est  perdiendo mucho tiempo */
    }

    outportb (0x61, inportb(0x61) & 0xFC);

    ventana (CERRAR, 23, 11, 58, 15, 0, 0);
    textcolor (CIMS); textbackground(CFI);
    if (!primeravez++) clrscr(); gotoxy (4,1);
    textcolor (CIC);
    textbackground (CFIC); if (sp) cputs("  Milisegundos   "); else cputs("  Milliseconds   ");
    textbackground (CFI); putch(' ');
    textbackground (CFIC); cputs(" Sector ");
    textbackground (CFI); putch('  ');
    textbackground (CFIC); if (sp) cputs("  Tama¤o   "); else cputs("   Size    ");
    textbackground (CFI); putch('  ');
    textbackground (CFIC); if (sp) cputs(" Cilindro "); else cputs(" Cylinder ");
    textbackground (CFI); putch('  ');
    textbackground (CFIC); if (sp) cputs(" Cara "); else cputs(" Side ");
    textbackground (CFI); putch('  ');
    textbackground (CFIC); cputs(" ST0 ");
    textbackground (CFI); putch('  ');
    textbackground (CFIC); cputs(" ST1 ");
    textbackground (CFI); putch('  ');
    textbackground (CFIC); cputs(" ST2 ");

    textbackground (CFI);
    acu=0;
    for (j=0; j<23; j++) {  /* rechazar primera muestra */
      gotoxy (4, j+2); textcolor (CIMS);

      if (tmp[j+1] && tmp[j]) {
          acu+=tmp[j+1];
          cprintf("[%8.2f]%7.2f ", acu/1193.18, tmp[j+1]/1193.18);
          }
        else
          cprintf("       N.D.       ");

      if (nec[j][0]>=0) {
        textcolor (CIS);
        cputs("   "); cputs(dec3str(nec[j][5]));
        textcolor (CIT); cputs("   ");
        if (nec[j][6]<9)
            cputs(dec5str(128 << nec[j][6]));
          else
            cputs ("    ?");
        cputs(" ("); cputs(dec3str(nec[j][6])); putch(')');

        textcolor (CICC);
        cputs("    "); cputs(dec3str(nec[j][3]));
        cputs("      "); cputs(dec3str(nec[j][4]));
        textcolor (CIST);
        cputs ("    0x"); cputs(hex2str(nec[j][0]));
        cputs ("  0x"); cputs(hex2str(nec[j][1]));
        cputs ("  0x"); cputs(hex2str(nec[j][2]));
        }
        else {
          textcolor (CIMS);
          cputs("    ??       ??          ??       ??     ??    ??    ?? ");
          }
      }

      textcolor (CIB);
      if (sp) {
          gotoxy (17, 25);
          cputs("Pulse la BARRA ESPACIADORA para leer m s ID's");
          }
        else {
          gotoxy (23, 25);
          cputs("Press SPACE BAR to read more ID's");
          }
      CursorOff();
      tec = Tecla();
  } while ((tec==32) || (tec=='5'));

  fin_ids:  MotorOff();
}


void Resultados (operacion, unidad, cabezal)
{
  int  xx, yy, res, t, st[3], str, bit;
  char sterr[2][10][40] =
    { {  "MA (Missing Address Mark)",
         "NW (Non Writable: write protect error)",
         "ND (No data)",
         "",
         "OR (Overrun)",
         "DE (Data Error)",
         "",
         "EN (End of cylinder)",
         },
      {  "MD (Missing Address Mark in Data Field",
         "BC (Bad Cylinder)",
         "",
         "",
         "WC (Wrong Cylinder)",
         "DD (Data Error in Data Field)",
         "CM (Control Mark)",
         "",
         }
    };

  ventana (ABRIR, 12, 7, 69, 22, CRS, CFRS);

  gotoxy (2, 1);
  switch (operacion) {
    case LEER:      if (sp) cputs ("RESULTADO LECTURA");
                      else cputs ("READ RESULTS");
                    break;
    case ESCRIBIR:  if (sp) cputs ("RESULTADO ESCRITURA");
                      else cputs ("WRITE RESULTS");
                    break;
    case FORMATEAR:  if (sp) cputs ("RESULTADO FORMATEO");
                      else cputs ("FORMAT RESULTS");
                    break;
    }

  xx=wherex(); yy=wherey();

  gotoxy (2, 3); cputs("[ST0 0x"); st[0]=res=infdc();
    if (res>=0) cputs(hex2str(res)); else cputs("??"); putch(']');
  if (res<0) {
      if (sp) {
          cputs ("   ­El FDC no responde!");
          gotoxy (xx, yy);
          cputs (": ­ERROR!");
          }
        else {
          cputs ("  FDC does not respond!");
          gotoxy (xx, yy);
          cputs (": ERROR!");
          }
      }
    else {
      gotoxy (2, 4); cputs("[ST1 0x");
        st[1]=res=infdc(); if (res<0) st[1]=0; else st[1] &= 0xB7;
        if (res>=0) cputs(hex2str(res)); else cputs("??"); putch(']');
      gotoxy (xx, yy);
      if ((st[0] & 0xC0)==0)
          cputs (sp?": CORRECTO":": OK");
        else
          cputs (sp?": ­ERROR!":": ERROR!");
      gotoxy (14, 3); cputs("[ST2 0x");
        st[2]=res=infdc(); if (res<0) st[2]=0; else st[2] &= 0x73;
        if (res>=0) cputs(hex2str(res)); else cputs("??"); putch(']');
      gotoxy (26, 3); if (sp) cputs("[Cilindro "); else cputs("[Cylinder ");
        res=infdc();
        if (res>=0) cputs(dec3str(res)); else cputs("??"); putch(']');
      gotoxy (26, 4); if (sp) cputs("[Cabezal  "); else cputs("[Head     ");
        res=infdc();
        if (res>=0) cputs(dec3str(res)); else cputs("??"); putch(']');
      gotoxy (42, 3); cputs("[Sector   ");
        res=infdc();
        if (res>=0) cputs(dec3str(res)); else cputs("??"); putch(']');
      gotoxy (42, 4); if (sp) cputs("[Tama¤o   "); else cputs("[Size     ");
        res=infdc();
        if (res>=0) cputs(dec3str(res)); else cputs("??"); putch(']');

      outfdc (4);                      /* Leer ST3 */
      outfdc (cabezal << 2 | unidad);  /* byte 1 */
      gotoxy (14, 4); cputs("[ST3 0x"); res=infdc();
        if (res>=0) cputs(hex2str(res)); else cputs("??"); putch(']');

      gotoxy (2, wherey()+1);

      for (str=1; str<=2; str++)
        for (bit=0, t=1; bit<8; bit++, t <<= 1)
          if (st[str] & t) {
             gotoxy (2, wherey()+1);
             cputs ("ST"); putch(str+'0'); putch('(');
             putch(bit+'0'); cputs("): ");
             cputs (sterr[str-1][bit]);
             }
      }

  MotorOff(); CursorOff();
  while (kbhit()) getch(); while (!kbhit());  /* dejar tecla en buffer */
  if ((bioskey(1) & 0xFF)==27) getch();       /* excepto si es ESC */
  ventana (CERRAR, 12, 7, 69, 22, 0, 0);
}


void MostrarBuffer (char huge *buffer, unsigned *bytes)
{
  static   linea=0, cx=0, cy=0, asc=0, nibble=0, primeravez=1;
  char     *pantalla;
  char     huge *auxbuff;
  unsigned char huge *p;
  unsigned register x, off;
  unsigned y, t, i, auxlong=0;
  int      lineas, my;
  char     cad[32];
  char     hexa[16]={'0', '1', '2', '3', '4', '5', '6', '7',
                     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

  textbackground (CFV); textcolor (CVMI); clrscr();

  window (1,19,80,25); textbackground (0); textcolor (CVMM); clrscr();
  window (1,20,80,25); textbackground (CFVM); clrscr();
  putch('Ú'); for (x=1; x<79; x++) putch ('Ä'); putch('¿');
  for (y=2; y<6; y++) {
    gotoxy (1, y); putch('³'); gotoxy (80, y); putch('³');
    }
  putch('À'); for (x=1; x<79; x++) putch ('Ä');
  _wscroll=0; putch('Ù'); _wscroll=1;

  if ((auxbuff = malloc (*bytes)) != NULL) {
    auxlong = *bytes;
    MoveMem (buffer, auxbuff, auxlong);  /* copia de seguridad */
    }

  textcolor (CVMI);
  gotoxy ( 3,2); cputs("F1:     "); cputs(sp?"Ayuda":"Help");
  if (auxlong) {
    gotoxy ( 3,3);
    cputs("F2:     "); cputs(sp?"Deshacer":"Undo");
    }
  gotoxy ( 3,4); if (sp) cputs("Intro:  Nueva secci¢n");
		 else cputs("Enter:  New section");
  gotoxy ( 3,5); cputs("Tab:    Hex\021-\020ASCII    ");

  gotoxy (30,2); if (sp) cputs("F5:  Poner buffer a 0");
		 else cputs("F5:  Set buffer to 0");
  gotoxy (30,3); if (sp) cputs("F6:  Buffer con trama");
		 else cputs("F6:  Fixed grid buffer");
  gotoxy (30,4); if (sp) cputs("F7:  Buffer aleatorio");
		 else cputs("F7:  Random grid buffer");
  gotoxy (30,5); if (sp) cputs("F8:  Tama¤o de buffer");
		 else cputs("F8:  Set buffer size");

  textcolor (CVMD);
  gotoxy (58,2); if (sp) cputs("Secci¢n "); else cputs("Section ");
  if (sp) {
      gotoxy (58,3); cputs("Buffer de       bytes"); }
    else {
      gotoxy (64,3); cputs("bytes buffer"); }

  textcolor (CVMS);
  gotoxy (58,5); if (sp) cputs("ESC/ALT-X  Abandonar");
		 else cputs("  ESC/ALT-X  Exit");

  window (1, 1,80,25);

  lineas = (*bytes >> 4); if (*bytes & 0xF) lineas++;

  if (linea>=lineas) linea=0;

  textbackground (CFV);  /* atributos de fondo */
  for (y=2; y<=17; y++) {
    gotoxy (3, y);
    textcolor (CVD); cputs ("      ");
    textcolor (CVH); cputs ("                        ");
    textcolor (CVS); cputs ("  ");
    textcolor (CVH); cputs ("                           ");
    textcolor (CVA); cputs ("                ");
    }

  do {
    p = &buffer[linea << 4];
    for (y=16; y<272; y += 16, p += 16) {
      pantalla = MK_FP ((peekb(0x40, 0x49) & 0x7F) == 7 ? 0xB000:0xB800, 0);
      textcolor (CVD); textbackground (CFV);
      off = (p-buffer);
      if (off >= *bytes) {
	  y >>= 4; for (i=0; i < (18-y)*80; i++) pantalla [(y*80+i) << 1]=' ';
	  my=y-2; if (my<0) my=0;
          break;
          }
        else my=15;
      pantalla += ((y/16) * 80 + 2 ) << 1;
      *pantalla=hexa[off >> 12];       pantalla+=2;
      *pantalla=hexa[(off >> 8) & 15]; pantalla+=2;
      *pantalla=hexa[(off >> 4) & 15]; pantalla+=2;
      *pantalla=hexa[off & 15];        pantalla+=2;
      *pantalla=':';                   pantalla+=6;
      for (x=0; x < 8; x++) {
        *pantalla=hexa[p[x] >> 4];     pantalla+=2;
        *pantalla=hexa[p[x] & 15];     pantalla+=4;
        }
      *pantalla='-';                   pantalla+=4;
      for (x=8; x < 16; x++) {
        *pantalla=hexa[p[x] >> 4];     pantalla+=2;
        *pantalla=hexa[p[x] & 15];     pantalla+=4;
        }
      pantalla+=4;
      for (x=0; x < 16; x++) { *pantalla=p[x]?p[x]:32; pantalla+=2; }
      }
    textcolor (CVMD); textbackground (CFVM); gotoxy (70,21);
    strcpy (cad, dec3strq((linea >> 4) + 1));
    cputs(cad); putch('/');
    strcpy (cad, dec3strq(lineas >> 4));
    cputs(cad); cputs("   ");
    if (sp) gotoxy (68,22); else gotoxy (58, 22);
    cputs(dec5str(*bytes));
    if (cy>my) cy=my;
    if (primeravez && *bytes) {
      ventana (ABRIR, 18, 6, 62, 14, CVA, CFVA);
      if (sp) {
          gotoxy (10,1); cputs ("Editor compatible Borland");
          gotoxy (2,3); cputs ("Cursores:             \033 \032 \030 \031");
          gotoxy (2,4); cputs ("Retroceder/Avanzar:   REPAG/AVPAG");
          gotoxy (2,5); cputs ("Inicio/Fin de l¡nea:  INICIO/FIN");
          gotoxy (2,6); cputs ("Inicio/Fin pantalla:  CONTROL-INICIO/FIN");
          gotoxy (2,7); cputs ("Inicio/Fin buffer:    CONTROL-REPAG/AVPAG");
          window (1,1,80,25);
          }
        else {
          gotoxy (10,1); cputs ("Borland compatible editor");
          gotoxy (3,3); cputs ("Cursor keys:             \033 \032 \030 \031");
          gotoxy (3,4); cputs ("Backwards/Forward:       PGUP/PGDN");
          gotoxy (3,5); cputs ("Begin/End of line:       HOME/END");
          gotoxy (3,6); cputs ("Begin/End of screen:     CTRL-HOME/END");
          gotoxy (3,7); cputs ("Begin/End of buffer:     CTRL-PGUP/PGDN");
          }
      }
    if (lineas && !(primeravez && *bytes))
        gotoxy (asc ? cx+62: cx<8? cx*3+10+nibble: cx*3+12+nibble, cy+2);
      else
        CursorOff();
    t=Tecla();
    if (primeravez && *bytes) {
       ventana (CERRAR, 18, 6, 62, 14, 0, 0);
       primeravez = 0;
       if ((t==13) || (t==27) || (t==32)) t = 0xFFFF; /* INTRO, ESC ¢ SP */
       }
    switch (t) {
      case 0xFFFF: break;
      case 0x4800: if (cy>0)                                    /* UP */
                       cy--;
                     else
                       if (linea>0) linea--;
		   nibble=0;
		   break;
      case 0x5000: if ((cy<15) && (cy+linea<lineas-1))          /* DOWN */
		       cy++;
		     else
		       if (linea<lineas-1) linea++;
		   nibble=0;
		   break;
      case 0x4D00: if (cx<15)                                   /* RIGHT */
                       cx++;
                     else
                       if (cy+linea<lineas-1) {
                         cx=0;
                         if (cy<15) cy++; else linea++;
                         }
                   nibble=0;
                   break;
      case 8:                                                   /* <-- */
      case 0x4B00: if (cx>0)                                    /* LEFT */
                       cx--;
                     else
                       if (cy>0)
                         { cy--; cx=15; }
                       else
                         if (linea>0) { linea--; cx=15; }
                   nibble=0;
		   break;
      case 0x4700: cx=0;  break;                                /* HOME */
      case 0x4F00: cx=15; break;                                /* END */
      case 0x7700: cy=0;  break;                                /* CTRL-HOME */
      case 0x7500: if (cy+linea+15<lineas)                      /* CTRL-END */
                       cy=15;
                     else
                       cy = lineas-linea;
                   break;
      case 0x4900: if (linea>15) linea -= 16; else linea=cy=0;  /* PGUP */
		   nibble=0;
		   break;
      case 0x5100: if (linea+16<lineas-1) linea += 16;          /* PGDN */
                       else {
                         if (lineas>15)
                             cy=linea=lineas-16;
                           else
                             { linea=0; cy = lineas-1; }
                         }
		   nibble=0;
		   break;
      case 0x8400: linea=cx=cy=nibble=0; break;                 /* CTRL-PGUP */
      case 0x7600: if (lineas>15)                               /* CTRL-PGDN */
                       cy=linea=lineas-16;
                     else
                       { linea=0; cy = lineas-1; }
                   cx=15;
                   nibble=0; break;
      case 0x3B00: primeravez=1; break;                         /* F1 */
      case 0x3C00: if (auxlong) {                               /* F2 */
                     ventana (ABRIR, 18, 8, 58, 12, CVA, CFVA);
                     gotoxy (4,2);
                     if (sp)
                         cputs ("Anulados los cambios en el buffer");
                       else
                         cputs ("Buffer restored to initial state");
                     CursorOff();
                     *bytes = auxlong;
                     MoveMem (auxbuff, buffer, auxlong);
                     lineas = (*bytes >> 4); if (*bytes & 0xF) lineas++;
                     if (linea>=lineas) linea=lineas-1; if (linea==-1) linea++;
                     delay (1000);
                     ventana (CERRAR, 18, 8, 58, 12, 0, 0);
                   }
                   break;
      case 0x3F00: t = lineas << 4;                             /* F5 */
		   for (i=0; i < t; i++) buffer[i]=0; break;
      case 0x4000: t = lineas << 4;                             /* F6 */
                   for (i=0; i < t; i++) buffer[i]=i; break;
      case 0x4100: t = lineas << 4;                             /* F7 */
                   for (i=0; i < t; i++) buffer[i]=rnd(256); break;
      case 13:     ventana (ABRIR, 29, 9, 50, 11, CVNS, CFVNS);
                   if (sp) cputs (" Nueva secci¢n: ");
                     else cputs (" New section: ");
                   strcpy (cad, dec3strq((linea >> 4) + 1));
                   if (input (cad, 1, 3, CVNS, CFVNS))
                     linea = (atoi(cad)-1) << 4;
                   if (linea>=lineas) linea=lineas-1; if (linea==-1) linea++;
                   ventana (CERRAR, 29, 9, 50, 11, 0, 0);
                   break;
      case 0x4200: ventana (ABRIR, 21, 11, 56, 13, CVNB, CFVNB);     /* F8 */
                   if (sp) cputs (" Nuevo tama¤o del buffer: ");
                     else cputs (" New buffer size: ");
                   strcpy (cad, dec5strq(*bytes));
                   t=*bytes; /* bytes previos */
                   if (input (cad, 1, 5, CVNB, CFVNB)) *bytes = atol(cad);
                   if (*bytes > SMAX) *bytes=SMAX;
                   lineas = (*bytes >> 4); if (*bytes & 0xF) lineas++;
                   if (linea>=lineas) linea=lineas-1; if (linea==-1) linea++;
                   ventana (CERRAR, 21, 11, 56, 13, 0, 0); CursorOff();
                   for (i=t; i < *bytes; i++) buffer[i]=0;
                   cx=cy=asc=nibble=0;
                   break;
      case 9:      asc ^= 1; break;  /* TAB */
      case 0x1B:
      case 0x2D00: break;
      default:     if (((t>='0') && (t<='9')) ||
                       ((t>='A') && (t<='F')) ||
                       ((t>='a') && (t<='f')) || asc) {
                     if (asc) {
                         buffer[linea*16+cy*16+cx] = t;
                     if (cx<15)
                         cx++;
                       else
                         if (cy+linea<lineas-1) {
                           cx=0;
                           if (cy<15) cy++; else linea++;
                           }
                         }
                       else {
                         if ((t>='a') && (t<='z')) t -= 32+7;
                         if ((t>='A') && (t<='Z')) t -= 7;
                         t -= '0';
                         buffer[linea*16+cy*16+cx] &= 0xF0 >> ((1-nibble)*4);
                         buffer[linea*16+cy*16+cx] |= t << ((1-nibble)*4);
                         nibble++;
                         if (nibble>1) {
                           if (cx<15)
                               cx++;
                             else
                               if (cy+linea<lineas-1) {
                                 cx=0;
                                 if (cy<15) cy++; else linea++;
                                 }
                           nibble=0;
                           }
                         }
                     }
                   break;
      }
    if (linea<0) linea=0;
  } while ((t != 0x1B) && (t != 0x2D00));

  if (auxlong) free ((void *) auxbuff);
}


void MotorOn (unidad)
{
  int i;

      /**** Evitar que la BIOS pare el motor (al menos en 14") ****/

  pokeb(0x40,0x40,0xFF);

      /**** Si no lo est , ponerlo en marcha y esperar 1 segundo ****/

  if (((i=peekb(0x40, 0x3F)) & (1 << unidad))==0) {
    outportb (FD_DOR, 1<<(unidad+4) | 4+8 | unidad);
    pokeb (0x40, 0x3F, i | (1 << unidad));
    delay (1000);
    pokeb(0x40,0x40,0xFF);
    }
}


void MotorOff()
{
  pokeb(0x40,0x40,55);  /* la BIOS lo detendr  en 55/18.2 segundos */
}


void outfdc (unsigned char dato)     /* enviar byte al FDC */
{                                    /* no esperando m s de 440 ms */
  int t, i=0, rd;

  do {
    i++; t=peekb(0x40, 0x6C);
    while ((t==peekb(0x40, 0x6C)) && ((rd=inportb(FD_STATUS)>>7)==0));
  } while ((i<8) && !rd);

  if (rd) outportb (FD_DATA, dato);
}


int infdc()         /* leer byte del FDC */
{                   /* no esperando m s de 440 ms */
  int t, i=0, rd;

  do {
    i++; t=peekb(0x40, 0x6C);
    while ((t==peekb(0x40, 0x6C)) && ((rd=inportb(FD_STATUS)>>7)==0));
  } while ((i<8) && !rd);

  if (rd) return (inportb (FD_DATA)); else return (-1);  /* fallo */
}


int EsperarInt()    /* Esperar interrupci¢n no m s de 2 seg. */
{
  int t, i=0;

  do {
    i++; t=peekb(0x40, 0x6C);
    while ((t==peekb(0x40, 0x6C)) && (!(peekb(0x40, 0x3E) & 0x80)));
  } while ((i<37) && (!(peekb(0x40, 0x3E) & 0x80)));

  pokeb (0x40, 0x3E, peekb (0x40, 0x3E) & 0x7F);
  return (i<37);
}


void PreparaDma (rmodo, bytes, buffer)
unsigned rmodo, bytes;
char huge *buffer;
{
  unsigned long dir;
  unsigned dmapag, dmaoff;

  dir = ((unsigned long) FP_SEG(buffer) <<4) + FP_OFF(buffer);
  dmapag = dir >> 16;  dmaoff = dir & 0xFFFF;

  outportb (0x81, dmapag);      /* registro de p gina del canal 2 */
  outportb (0xB, rmodo);        /* programar registro de modo */
  outportb (0xC, 0);            /* clear first/last flip-flop */
  outportb (4,dmaoff & 0xFF);   /* direcci¢n base (parte baja) */
  outportb (4,dmaoff >> 8);     /* direcci¢n base (parte alta) */
  outportb (5,(bytes-1) % 256); /* n§ de bytes menos 1 (parte baja) */
  outportb (5,(bytes-1) / 256); /* n§ de bytes menos 1 (parte alta) */
  outportb (0xA, 2);            /* habilitar canal 2 */
}


char *hex2str (int num)
{
  static char cad[32];
  char   hexa[16]={'0', '1', '2', '3', '4', '5', '6', '7',
                   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

  cad[0]=hexa[num >> 4];
  cad[1]=hexa[num & 15];
  cad[2]=0;

  return (cad);
}


int hextoi (char *cad)
{
  unsigned num=0, error=0;
  char     *p;

  p=cad; while (*p) { if (*p > '9') *p = (*p & 0xDF) - 7; p++; }
  p=cad; while (*p && !error) if ((*p<'0') || (*p>'?')) error++; else p++;
  p=cad; while (*p) { num <<= 4; num |= *p-'0'; p++; }

  if (!error) return (num); else return(0);
}


int input (char *cad, int modo, int maxcar, int tinta, int papel)
{
  int x, y, t, i, px, primeravez=1;
  static insercion=1;

  x=wherex(); y=wherey(); px = strlen (cad);

  gotoxy (x, y);
  textcolor (tinta+BLINK); textbackground (papel);
  cputs (cad);
  textcolor (tinta); for (i=px; i<maxcar; i++) putch(' ');
  gotoxy (x+px, y);

  do {
    if (insercion)
        _setcursortype (_NORMALCURSOR);
      else
        _setcursortype (_SOLIDCURSOR);
    if (!(t=getch())) t = getch() << 8;
    if ((primeravez) && (t!=8) && (t!=13) && (t!=27) && (t<256)) cad[0]=px=0;
    primeravez=0;
    switch (t) {
      case 0x4B00: if (px) px--; break;
      case 0x4D00: if (px < strlen(cad)) px++; break;
      case 8:      if (px)
                     for (i=--px; i<=strlen(cad); i++) cad[i]=cad[i+1];
                   break;
      case 0x5300: for (i=px; i<=strlen(cad); i++) cad[i]=cad[i+1]; break;
      case 0x4700: px=0; break;
      case 0x4F00: px = strlen(cad); break;
      case 0x5200: insercion ^= 1;
      default:     if(
                       (t>=32) && (t<256) && (
                         (modo==0) ||
                         ((modo==1) && (t>='0') && (t<='9')) ||
                         ((modo==2) && (
                           ((t>='0') && (t<='9')) ||  /* Modo 0: Asc */
                           ((t>='a') && (t<='f')) ||  /* Modo 1: Dec */
                           ((t>='A') && (t<='F'))     /* Modo 2: Hex */
                           )
                         )
                       )
                     )
                     if (!insercion) {
                         if (px < maxcar) {
                           cad[strlen(cad)+1]=0;
                           cad[px++]=t;
                           }
                         }
                       else
                         if (strlen(cad) < maxcar) {
                           for (i=strlen(cad); i>=px; i--) cad[i+1]=cad[i];
                           cad[i+1]=t; px++;
                           }
                   break;
      }
    gotoxy (x, y); cputs (cad);
    for (i=strlen(cad); i<maxcar; i++) putch(' ');  gotoxy (x+px, y);
  } while ((t!=13) && (t!=27));

  if ((t==13) && (strlen(cad)==0)) t=27;  /* campo vac¡o -> ESC */

  return (t==13);
}


char *dec2str (unsigned num)
{
  static char cad[32];

  if (num >= 10) { cad[0]=num/10+'0'; num %= 10; } else cad[0]='0';
  cad[1]=num+'0';
  cad[2]=0;

  return (cad);
}


void CursorOff (void)
{
  union REGS regs;

  regs.x.ax=0x200; regs.x.bx=peekb(0x40,0x62)<<8;
  regs.h.dl=1; regs.h.dh=63; int86 (0x10, &regs, &regs);
}


void BrilloOn (void)
{
  union REGS regs;

  if (peekb(0x40, 0x49)!=7) {       /* evitarlo en MDA */
    regs.x.ax=0x1003; regs.x.bx=0;
    int86 (0x10, &regs, &regs);
    }
}


int TipoDrive (int unidad)
{
  union REGS r;

  r.h.ah=8; r.h.dl=unidad; r.h.bl = 255;
  int86 (0x13, &r, &r);

  if ((r.x.flags & 1) || (r.h.bl==255)) return (-1);
  else return ((unsigned char) r.h.bl);
}


void ventana (operacion, x0, y0, x1, y1, tinta, papel)
{
  static void *memoria;
  char   far *pantalla;
  int    i, t;

  if (operacion==ABRIR) {
       if ((memoria = farmalloc (2L*(x1-x0+3)*(y1-y0+2)))!=NULL)
         gettext (x0, y0, x1+2, y1+1, memoria);
       textbackground (papel); if (tinta>7) t=tinta-8; else t=tinta;
       textcolor (t+8);
       gotoxy (x0, y0); putch('Ú');
       gotoxy (x0, y1); putch('À');
       textcolor (t);
       gotoxy (x1, y0); putch('¿');
       gotoxy (x1, y1); putch('Ù');
       for (i=1; i<x1-x0; i++) {
         textcolor (t+8); gotoxy (x0+i, y0); putch('Ä');
         textcolor (t);   gotoxy (x0+i, y1); putch('Ä');
         }
       for (i=1; i<y1-y0; i++) {
         textcolor (t+8); gotoxy (x0, y0+i); putch('³');
         textcolor (t); gotoxy (x1, y0+i); putch('³');
         }
       window  (x0+1, y0+1, x1-1, y1-1);
       textcolor (tinta); clrscr();
       pantalla = MK_FP ((peekb(0x40, 0x49) & 0x7F) == 7 ? 0xB000:0xB800, 0);
       t = ((y1*80 + x0) << 1) + 1;
       for (i=1; i<x1-x0+2; i++) pantalla[t+(i<<1)]=7;
       t = ((y0*80 + x1) << 1) + 1;
       for (i=0; i<y1-y0; i++)
         pantalla[t+i*160]=pantalla[t+i*160+2]=7;
       }
     else {
       puttext (x0, y0, x1+2, y1+1, memoria);
       if (memoria != NULL) { farfree (memoria); memoria=NULL; }
       window (1, 1, 80, 25);
       }
}


void v3dg (cx, cy, lx, ly, foco, dis, tin, pap, banda)
{
  int  x, y, i;
  char txt[81];

  Marco3dg (cx, cy, lx, ly, foco, tin, pap, banda);
  Marco3dg (cx+dis*2, cy+dis, lx-dis*4, ly-dis*2, foco^1, tin, pap, banda);
  textbackground (banda);

  *txt=0;
  for (x=0; x < lx-2; x++) strcat (txt, " ");
  for (i=0; i<dis-1; i++) {
    gotoxy (cx+1, cy+1+i); cputs(txt);
    gotoxy (cx+1, cy+ly-dis+i); cputs(txt);
    }
  *txt=0;
  for (i=0; i < dis*2-1; i++) strcat (txt, " ");
  for (y=cy+1; y < ly; y++) {
    gotoxy (cx+1, y); cputs(txt);
    gotoxy (cx+lx-2*dis, y); cputs(txt);
    }
  textbackground (pap);
}


void v3d (cx, cy, lx, ly, dis)
{
  window (cx+dis*2+1, cy+dis+1, cx+lx-dis*2-2, cy+ly-dis-2);
}


void v3df (cx, cy, lx, ly, foco, dis, tin, pap)
{
  Marco3df (cx, cy, lx, ly, foco, tin, pap);
  Marco3df (cx+dis*2, cy+dis, lx-dis*4, ly-dis*2, foco^1, tin, pap);
}


int Tecla()
{
  int t;

  while (kbhit()) getch(); t=getch(); if (!t) t=getch() << 8;
  return (t);
}


void boton (int cx, int cy, int borde, int ttex, int t, int p, char *texto)
{
  int tb, tt;

  if (borde<8) { tb=borde+8; tt=borde;} else { tb=borde; tt=borde-8; }

  gotoxy (cx, cy);
  textbackground (tt); textcolor (tb);
  if (CodePage437()) putch('Ý'); else putch('Û');
  textcolor (ttex); cputs (texto);
  textcolor (8); if (CodePage437()) putch('Þ'); else putch('Û');
  textcolor (t); textbackground (p);
}


char *dec5strq (unsigned num)
{
  char *p;

  p=dec5str(num);
  while (*p==' ') p++;
  return (p);
}


int EsOS2 (void)   /* Devolver 1 si corriendo bajo OS/2 */
{
  union REGS r;

  if (_osmajor >= 3) {
      r.x.ax=0x4010;
      int86 (0x2F, &r, &r);
      return (r.x.ax != 0x4010);
      }
    else return (0);   /* DOS 2.X, evitar llamada a INT 2Fh */
}


int Hay2M3()     /* devolver 1 si 2M 3.X est  instalado */
{
  int entrada, instalado=0;
  union REGS r; struct SREGS s;

  for (entrada=0xc0; (entrada<=0xff) && (!instalado); entrada++) {
    r.x.ax=entrada << 8; s.es=0x1492; r.x.di=0x1992;
    int86x (0x2f, &r, &r, &s);
    if (r.x.ax==0xFFFF)
      if ((peek(s.es,r.x.di-4)==9002) && (peek(s.es,r.x.di-2)==10787))
        if (strstr (MK_FP(s.es, r.x.di),"2M:3.")) instalado=1;
        if (strstr (MK_FP(s.es, r.x.di),"2MX:3.")) instalado=1;
    }
  return (instalado);
}


char *dec2strq (unsigned num)
{
  char *p;

  p=dec2str(num);
  if (*p=='0') p++;
  return (p);
}


char *dec3strq (unsigned num)
{
  char *p;

  p=dec3str(num);
  while (*p==' ') p++;
  return (p);
}


char *dec3str (unsigned num)
{
  static char cad[32];
  int    i;

  if (num >= 100) { cad[0]=num/100+'0'; num %= 100; } else cad[0]='0';
  if (num >= 10) { cad[1]=num/10+'0'; num %= 10; } else cad[1]='0';
  cad[2]=num+'0';
  cad[3]=0;

  for (i=0; (i<2) && (cad[i]=='0'); i++) cad[i]=' ';

  return (cad);
}


char *dec5str (unsigned num)
{
  static char cad[32];
  int    i;

  if (num >= 10000) { cad[0]=num/10000+'0'; num %= 10000; } else cad[0]='0';
  if (num >= 1000) { cad[1]=num/1000+'0'; num %= 1000; } else cad[1]='0';
  if (num >= 100) { cad[2]=num/100+'0'; num %= 100; } else cad[2]='0';
  if (num >= 10) { cad[3]=num/10+'0'; num %= 10; } else cad[3]='0';
  cad[4]=num+'0';
  cad[5]=0;

  for (i=0; (i<4) && (cad[i]=='0'); i++) cad[i]=' ';

  return (cad);
}


int HablaSp()        /* devolver 1 si mensajes en castellano */
{
  union REGS r; struct SREGS s;
  char info[64];
  int i, idioma, spl[]={54, 591, 57, 506, 56, 593, 503, 34, 63, 502,
             504, 212, 52, 505, 507, 595, 51, 80, 508, 598, 58, 3, 0};

  idioma=0;          /* supuesto el ingl‚s */

  if (_osmajor>=3) {
    r.x.ax=0x3800; s.ds=FP_SEG(info); r.x.dx=FP_OFF(info);
    intdosx (&r, &r, &s);
    i=0; while (spl[i++]) if (spl[i-1]==r.x.bx) idioma=1;
    }

  return (idioma);
}


void Marco3dg (cx, cy, lx, ly, foco, tin, pap, banda)
{
  int tb, x, y, w;

  w = _wscroll; _wscroll=0;

  if (tin<8) tb=tin+8; else tb=tin;

  if (foco) {
      textbackground (pap); textcolor (tb);

      for (y=cy; y < cy+ly; y++) {
        textbackground (pap); gotoxy (cx, y);
        if (CodePage437()) putch('Þ'); else putch('Û');
        }
      for (x=cx+1; x < cx+lx-1; x++) {
        textbackground (banda); gotoxy (x, cy); putch('ß');
        }

      textcolor (8);

      for (y=cy; y < cy+ly; y++) {
        textbackground (pap); gotoxy (cx+lx-1, y);
        if (CodePage437()) putch('Ý'); else putch('Û');
        }
      for (x=cx+1; x < cx+lx-1; x++) {
        textbackground (banda); gotoxy (x, cy+ly-1); putch('Ü');
        }
      }
    else {
      textbackground (pap); textcolor (8);

      for (y=cy; y < cy+ly; y++) {
        textbackground (banda); gotoxy (cx, y);
        if (CodePage437()) putch('Þ'); else putch('Û');
        }
      for (x=cx+1; x < cx+lx-1; x++) {
        textbackground (pap); gotoxy (x, cy); putch('ß');
        }

      textcolor (tb);

      for (y=cy; y < cy+ly; y++) {
        textbackground (banda); gotoxy (cx+lx-1, y);
        if (CodePage437()) putch('Ý'); else putch('Û');
        }
      for (x=cx+1; x < cx+lx-1; x++) {
        textbackground (pap); gotoxy (x, cy+ly-1); putch('Ü');
        }
      }

  _wscroll = w;
}


int CodePage437 (void)   /* Devolver TRUE si p gina de c¢digos 437 */
{
  union  REGS r;
  static pg437=-1;

  if (pg437 == -1) {
    if ((_osmajor*100 + _osminor) >= 330) {
        r.x.ax=0x6601;
        int86 (0x21, &r, &r);
        pg437 = (r.x.bx == 437);
        }
      else pg437 = 1;   /* DOS < 3.30, suponer p gina 437 */
    }

  return (pg437);
}


void Marco3df (cx, cy, lx, ly, foco, tin, pap)
{
  int tb, x, y, w;

  w = _wscroll; _wscroll=0;

  if (tin<8) tb=tin+8; else tb=tin;

  textbackground (pap); textcolor (foco?tb:8);

  gotoxy (cx, cy); putch('Ú');
  for (y=cy+1; y < cy+ly-1; y++) {
    gotoxy (cx, y); putch('³');
    }
  gotoxy (cx, y); putch('À');

  for (x=cx+1; x < cx+lx-1; x++) {
    gotoxy (x, cy); putch('Ä');
    }

  textcolor (foco?8:tb);

  gotoxy (cx+lx-1, cy); putch('¿');
  for (y=cy+1; y < cy+ly-1; y++) {
    gotoxy (cx+lx-1, y); putch('³');
    }
  gotoxy (cx+lx-1, y); putch('Ù');

  for (x=cx+1; x < cx+lx-1; x++) {
    gotoxy (x, cy+ly-1); putch('Ä');
    }

  _wscroll = w;
}
