/******************************************************************************
 SOURCE NAME : COM.CC
 part of the Atari File Designer (AFD) source files
 for the Communication module.
------------------------------------------------------------------------------
 Written by Philippe VUILLERME (c) PVBest 2001
 Based on "RS-232 Interrupts The C Way" from Mark R. Nelson (c)1990
------------------------------------------------------------------------------
MAIN FEATURES:
  Programmation of the UART (PC serial Chip)
  Setting of the COM Port Parameters
  Monitoring of the Serial Com Port signals (RTS)
  Serial Interrupt Handler coding
  Atari Communication Protocol Functions   

HISTORY:
  COM.CC REV 2.00 :
    COMMUNICATION MODULE for the AFD.

  COM.CC REV 1.00 :
    ATARI DISK DRIVE TO PC COMMUNICATION (c) Philippe & PVBEST 2001
    Listing principal - Programme de transfer simple : 1050 vers file PC
    Nom de fichier destination unique IMAGE.ADB - a renomer après chargement.
    Premiere version ''fully'' fonctionnelle...
    Based on "RS-232 Interrupts The C Way" from Mark R. Nelson (c)1990 
    First Compiled on GNU DGJPP C++ compilator (gxx com.cc -o - com.exe)

******************************************************************************/


/*--- Librairies necessaires : -----------------------------------------*/
#include <conio.h>         // getch(),gets, etc...
#include <stdio.h>         // printf()

#include <pc.h>            // inportb(), outportb()
#include <bios.h>          // biostime()

#include <dpmi.h>
#include <go32.h>

#include <string.h>       // memset
//#include <stdlib.h>       // malloc, free
#include <unistd.h>       // read/write
#include <fcntl.h>        // disk IO
#include <sys/stat.h>     /* disk IO : for mode definitions */

#include "com.h"
#include "prompts.h"

/***********************************************************************
 * This macro was used by Shawn Hargreaves in Allegro to lock all of its 
 * data: it's great to have around.
 */

#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void *)&x,(long)sizeof(x));
#define END_OF_FUNCTION(x)  static void x##_End(){};
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x, (long)x##_End - (long)x);

/****************************************************************
 * Various constants used only in this program.
 */
#define INT_CONTROLLER   0x20  /* The address of the 8259*/
#define EOI              0x20  /* The end of int command */
#define BREAK_VECTOR     0x23  /* The CTRL-BREAK vector  */

#define EN_BLEU      0x09
#define EN_VERT      0x0A
#define EN_TURQUOISE 0x0B
#define EN_ROUGE     0x0C
#define EN_VIOLET    0x0D
#define EN_JAUNE     0x0E
#define EN_BLANC     0x0F


/***************************************************************
* Declaration of the program variable data
*/

/* Ancien Vecteurs */
_go32_dpmi_seginfo old_com_handler, new_com_handler;
_go32_dpmi_seginfo old_break_handler, new_break_handler;

/* Valeurs de TimeOut. Unite = 1 microseconde */
long int TCLOCK;
float TO1 = 60*1E06;   /* valeur du time out : 1 min*/
float TO2 = 50*1E03;   /* valeur du time out : 40 ms = TIMOUT*/
//on laisse un peu plus de 40 ms car base de temps pas fixe
float TO3 = 180*1E06;  /* valeur du time out : 3 min = DTIMLO*/
float TO4 = 240*1E06;  /* valeur du time out : 4 min */
float TO5 = 960*1E06;  /* valeur du time out : 16 min = DTIMLO*/

/* Sauvegarde de l'ancien port COM */
UBYTE old_dlm, old_dll;
UBYTE old_mcr, old_lcr, old_ier;

/*****************Data to be locked****************************/
/* Variables du module COM utilisees dans le programme */

UWORD uart_base;
UWORD interrupt_number;
UBYTE irq_mask;
UBYTE ier_mask;
UBYTE mcr_out;
UBYTE serial_int;
UBYTE serial_data;
BUFFER in;
BUFFER out;

/* Variables pour Interrruption com UART */
UWORD cks;
int xit;

/* Variables generales du programme principale */
const DISK_LENGHT = 1040*128;          //Taille maximale d'une disquette
UBYTE atari_disk_buffer[DISK_LENGHT];  //Image disk en memoire (taille maximale)
UBYTE temp_buffer[DISK_LENGHT];
UBYTE sector_buffer[256];              //buffer pour secteur entre PC file et memoire
char temp_filename[13];                //taille strlen() = 12
char image_filename[13];               //taille strlen() = 12
int fileflag;

UWORD   sector_nbr; //Numero du secteur en cours
UWORD   firstsect;  //Premier secteur pour operation
UWORD   lastsect;   //Dernier secteur pour operation
UWORD   DSIZE;      //Number of sector per disk (donne par drive status)  
UWORD   DFRLEN;     //Longueur totale de la trame recue en lecture atari
int     DERROR;     //Erreur des procedures de commandes

UBYTE   DSTATUS;    //Status et erreur de communications 
UBYTE   DCOMND;     //Commande en cours
UBYTE   DAUX1;
UBYTE   DAUX2;
UBYTE   DACKCODE;   //Acknowledge code read
UBYTE   DVSTATS;    //Octet de status drive software
UBYTE   DVSTATH;    //Octet de status drive hardware

/****************************************************************
 * This routine intercepts the CTRL-BREAK vector.  This
 * prevents the program from terminating before having a
 * chance to turn off COM interrupts.
 * This handler set the .ctrl flag indicating it is time to abort
 * and it returns an error in .error
 */
void break_handler(void)
{
 asm("cli;pusha");
 in.ctrl = 0; // end of communication
 out.ctrl = 0; // end of communication
 in.error = 249; // abort by user error
 out.error = 249; // abort by user error
 outportb( INT_CONTROLLER, EOI ); /*outportb(0x20,0x20)*/
 asm("popa;sti");
}
END_OF_FUNCTION(break_handler);

/**************************************************************
 * This is the interrupt service routine for the COM port.
 * It sits in a loop reading the interrrupt ID register, then
 * servicing one of the four different types of interrupts.
 */
void com_handler (void)
{
  asm("cli;pusha");
  xit = FALSE;
  cks = 0;
  while (xit == FALSE)
  {
     serial_int = inportb( uart_base + IIR );
     switch (serial_int)
     {
       /* MODEM STATUS INTERRUPT
       * "If the present interrupt is due to a modem status line
       * change, the MSR is read to clear the interrupt, but
       * nothing else is done."
       */
      case IIR_MODEM_STATUS :
        
        inportb( uart_base + MSR );
        break;
        

      /* TRANSMITTER HOLDING REGISTER EMPTY
       * "If the interrupt is due to the transmit holding register
       * being ready for another character, I first check to see
       * if any characters are left in the output buffer.  If
       * not, Transmit interrupts are disabled.  Otherwise, the
       * next character is extracted from the buffer and written
       * to the UART."
       * Nota : ''Lenght'' must not include the checksum.
       * ie : for a command frame  : lenght = 4,
       * total number of transmitted data is 5 with checksum
       * Maximum Data_index = lenght - 1 = 4 (data_index starts from 0)
       */
      case IIR_TRANSMIT :
        
        if ( out.ctrl == 255)  
           /*Last byte (=checksum) transmitted : close out port*/
           {
            ier_mask = ier_mask & (~IER_TX_THRE);
            outportb( uart_base + IER , ier_mask);
            out.ctrl = 0; /*Transmission over*/
            break;
           }
        if ( out.data_index == out.lenght )
           /*It's time to send the checksum*/
           { 
            serial_data = out.checksum;
            outportb( uart_base + THR , serial_data );
            out.ctrl = 255; /*255 = Checksum sending in progress*/
           }
        else 
           /*Data_index < lenght : normal data sending operation*/
           {
            serial_data = out.buffer[ out.data_index++ ];
            outportb( uart_base + THR, serial_data );
            cks = (out.checksum) + serial_data;
            if (cks > 255) {cks = cks - 256 + 1;}
            out.checksum = (UBYTE) (cks);
           }
        break;
        

      /* RECEIVED DATA AVAILABLE INTERRUPT
       * When a new character comes in and generates an
       * interrupt, it is read in.  If there is room in the input
       * buffer, it is stored, otherwise it is discarded.
       * Nota : ''lenght'' must include the checksum and 2 ack code.
       * ie : sector read frame = $41 + $43 + 128 data + 1 checksum : 
       * ($41 and $43 are ack codes before frame) so lenght = 131.
       * For sending frame lenght = 3 ($41+$41+$43 : no checksum nor data)
       * Nota : data_index starts from 0 and stop at lenght -1
       */
      case IIR_RECEIVE :
        
        serial_data = (UBYTE) inportb( uart_base + RBR ); /*Data reading*/
        /*Is it the last data of frame?*/
        if ((in.data_index+1 ) != in.lenght)
               { /*no*/
                if (in.data_index >= 2) //skip the 2 first bytes from checksum
                  {
                     cks = in.checksum + serial_data;
                     if (cks > 255) {cks = cks - 256 + 1;}           
                     in.checksum = (UBYTE) (cks); //checksum calculation OK
                  }
                in.buffer[ in.data_index++ ] =  serial_data;
               }
        else /*Yes*/
            /*data_index +1 = total lenght : this is the last input data :
             * the checksum byte for receiving or an $43 code for sending*/
               {
                in.buffer[ in.data_index ] = serial_data;
                in.ctrl = 0; /*Bytes are received : end of communication*/
               }
        break;

      /* LINE STATUS RECEIVER REGISTER INTERRUPT
       * All this code does is read the Line Status register, to
       * clear the source of the interrupt.
       */
      case IIR_LINE_STATUS :

        serial_data = (UBYTE) inportb( uart_base + LSR );
        if ((serial_data & 0x17) == LSR_OVERRUN )
         {
          in.ctrl = 0; // end of com
          in.error = 254; /* erreur : pas lu le caratere entrant */
         }
        if ((serial_data & 0x17) == LSR_FRAMING )
          {
           in.ctrl = 0; // end of com
           in.error = 251; /* erreur : pas de bit stop */
          }
          else
          {
           in.ctrl = 0; // end of com
           in.error = 248; /* autre erreur */
          }
        break;

      /* NO MORE INTERRUPT PENDING IIR = 1
       * If there are no more pending interrupts, exit xit = TRUE
       */
      case 1 :
        xit = TRUE;
        break;

      /* UNKNOWN DEFAULT INTERRUPT
       * If there are no valid interrupts left to service, the
       * routine exits, IER is reset to default and exit xit = TRUE
       */
      default :
        outportb( uart_base + IER , 0);
        asm(" jmp 0f
            0:jmp 1f
            1:
            "); /* Temporisation */
        outportb( uart_base + IER , ier_mask);
        xit = TRUE;
     }
  }
  outportb( INT_CONTROLLER, EOI ); /*outportb(0x20,0x20)*/
  asm("popa;sti");
}
END_OF_FUNCTION(com_handler);

/*************************************************************
 * This routine opens an RS-232 port up : it initializes the
 * input and output buffers, stores the uart address and
 * the interrupt number.  It then gets and stored the
 * interrupt vector presently set up for the UART, then
 * installs its own.  It also sets up the handler to
 * intercept the CTRL-BREAK handler.  Finally, it tells the
 * 8259 interrupt controller to begin accepting interrupts
 * on the IRQ line used by this COM port.
 ************************************************************/
int port_open (void)
{
  UBYTE temp;

  /* Define the IRQ mask . Mask = 0x10 for COM1*/
  irq_mask = (UBYTE) 1 << (interrupt_number % 8 );

  /* Save the old interrupt handler */
  _go32_dpmi_get_real_mode_interrupt_vector
       (interrupt_number, &old_com_handler);
  _go32_dpmi_get_real_mode_interrupt_vector
       (BREAK_VECTOR, &old_break_handler);

  /* Set the new COM interrupt handler */
  new_com_handler.pm_offset = (int) com_handler;
  new_com_handler.pm_selector = _go32_my_cs();
  _go32_dpmi_allocate_iret_wrapper(&new_com_handler);
  _go32_dpmi_set_protected_mode_interrupt_vector
       (interrupt_number, &new_com_handler);

  /*Set the new Break interrupt handler */
  new_break_handler.pm_offset = (int) break_handler;
  new_break_handler.pm_selector = _go32_my_cs();
  _go32_dpmi_allocate_iret_wrapper(&new_break_handler);
  _go32_dpmi_set_protected_mode_interrupt_vector
       (BREAK_VECTOR, &new_break_handler);

  /* Enable the IRQ COM interrupts . Relevant bit is set to 0*/
  temp = (UBYTE) inportb ( INT_CONTROLLER + 1 );
  outportb( INT_CONTROLLER + 1, ~irq_mask & temp );
  return( 0 );
}

/*************************************************************
 * This routine establishes the operating parameters for a
 * port after it has been opened.  This means it sets the
 * baud rate, parity, number of data bits, and number of
 * stop bits.  Interrupts are disabled before the routine
 * starts changing registers, and are then reenabled after
 * the changes are complete.
 */
void port_set( long speed,
               char parity,
               int data,
               int stopbits )
{
  UBYTE lcr_out;

  UBYTE low_divisor;
  UBYTE high_divisor;
  /* Disable all IRQ interrupt and the UART interrupt*/
  disable(); 
  outportb( uart_base + IER, 0 );
  /* Default value */
  ier_mask = 0;
  mcr_out = 0;
  /* Read the incoming data in UART if present */
  inportb( uart_base );
  port_save(); /* Save the old port structure */
  /*
   * Writing the baud rate means first enabling the divisor
   * latch registers, then writing the 16 bit divisor int
   * two steps, then disabling the divisor latch so the other
   * registers can be accessed normally.
   */
  low_divisor = (BYTE) (115200L / speed ) & 0xff;
  high_divisor = (BYTE) ((115200L /  speed ) >> 8);
  outportb( uart_base + LCR, LCR_DLAB );
  asm("nop;nop");
  outportb( uart_base + DLL, low_divisor );
  asm("nop;nop");
  outportb( uart_base + DLM, high_divisor );
  asm("nop;nop");
  outportb( uart_base + LCR, 0 );
  /*
   * Setting up the line control register establishes the
   * parity, number of bits, and number of stop bits.
   */
  if ( parity== 'E' )
    lcr_out = LCR_EVEN_PARITY;
  else if ( parity == 'O' )
    lcr_out = LCR_ODD_PARITY;
  else
    lcr_out = LCR_NO_PARITY;

  if ( stopbits == 2 )
    lcr_out |= LCR_2_STOP_BITS;

  if ( data == 6 )
    lcr_out |= LCR_6_DATA_BITS;
  else if ( data == 7 )
    lcr_out |= LCR_7_DATA_BITS;
  else if ( data == 8 )
    lcr_out |= LCR_8_DATA_BITS;

  outportb( uart_base + LCR, lcr_out );
   
  /* Switch on the OUT2 output interrupt line */
  /* OUT2 is needed to allow interrupts on PC compatible cards */
  mcr_out = mcr_out | MCR_OUT2;
  outportb( uart_base + MCR, mcr_out );
  asm("nop;nop");
  // ier_mask to be set to default here if needed
  /* No UART interrupt for the moment */
  outportb( uart_base + IER, ier_mask ); 
  enable(); /* Enable IRQ interrupts */
 }

/***********************************************************
 * In order to close the port, I first disable interrupts
 * at the UART, then disable interrupts from the UART's IRQ
 * line at the 8259 interrupt controller.  DTR, RTS, and
 * OUT2 are all dropped. The UART's previous interrupt
 * handler is restored, and the old break handler
 * is restored.  Finally, the port data structure is freed,
 * and things should be completely back to normal.
 */
void port_close( void )
{
  UBYTE temp;

  cprintf("Restore old UART configuration...\n\r");
  /* Disable UART interrupt */
  outportb( uart_base + IER, 0 );
  /* Disable IRQ interrupt */ 
  disable();

  /* Switch off the OUT2 interrupt output line */
  outportb( uart_base + MCR, 0 );
  /* Restore the old UART COM parameters */
  port_restore();
  /* Disable the IRQ COM interrupt. Relevant bit is set to 1 */
  temp = (UBYTE) inportb( INT_CONTROLLER + 1 );
  outportb( INT_CONTROLLER + 1, (irq_mask | temp) );

  cprintf("Restore old interrupts...\n\r");
  /* Restore the old interrupt handlers */
  _go32_dpmi_set_real_mode_interrupt_vector
       (interrupt_number, &old_com_handler);
  _go32_dpmi_free_iret_wrapper(&new_com_handler);

  _go32_dpmi_set_real_mode_interrupt_vector
       (BREAK_VECTOR, &old_break_handler);
  _go32_dpmi_free_iret_wrapper(&new_break_handler);
  
  enable();

}

/************************************************************
 *   Procedures de communications avec Atari Disk Drive
 ************************************************************/
UBYTE port_sendcommand()
{ 
  float TimeOut;
  
  /* Sending of the command frame */
  /* Out com. values initialization */
  out.buffer[0] = 0x31;
  out.buffer[1] = DCOMND;
  out.buffer[2] = DAUX1;
  out.buffer[3] = DAUX2;
  out.lenght = 4;
  out.data_index = 0;
  out.ctrl = 1; 
  out.checksum = 0;

  /* In com. values initialization */
  switch (DCOMND)
    {
      case 0x21:
      case 0x22:
       DFRLEN = 131;
       break;
      case 0x53:
       DFRLEN = 7;
       break;
      case 0x52:
       DFRLEN = 131;
       break;
      case 0x57:
       DFRLEN = 3;
       break;
      default:
       cprintf("Command $%02X not supported ?!?!\n\r",DCOMND);
       return (0);
    }

  raz_in_buffer();
  in.data_index = 0;
  in.lenght = DFRLEN;  
  in.error = 0; 
  in.ctrl = 1;
  in.checksum = 0;

  /* Timeout for the sending bytes*/
  TimeOut = TO1; /* environ 1 minute : pas d'importance*/

  /* (1) RTS output on : low output*/
  mcr_out = mcr_out | MCR_RTS;
  outportb( uart_base + MCR, mcr_out );

  /* (2) Wait 1.275 milisecond*/
  delais_cycle (1275);

  /* (3) Transmiter and receiver interupt activation */
  ier_mask = ier_mask | IER_RX_DATA | IER_LINE_STATUS | IER_TX_THRE;
  outportb( uart_base + IER, ier_mask );

  //printf ("Envoi...Valeur de mcr_out : %u\n\r", mcr_out);
  //printf ("Valeur de ier_mask : %x\n\r", ier_mask);
  //printf ("Valeur de cks : %u\n\r", cks);
  //printf ("Valeur de b : %u\n\r", b);

  while (( out.ctrl != 0) && (TimeOut != 0))
   {
    delais_cycle(1);
    TimeOut--;
   }

  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }

  /* (4) RTS output off : high output*/
  mcr_out = mcr_out & (~MCR_RTS);
  outportb( uart_base + MCR, mcr_out );

  /* (5) Acknowledge code byte receiving */
  /* Timeout for the receiving ACK byte*/
  TimeOut = TO2; /* 40 miliseconds */

  //printf ("Reception ACK... \n\r");
  //printf ("Valeur de ier_mask : %x\n\r", ier_mask);

  while (( in.data_index < 1) && (TimeOut != 0) && (in.error == 0))
   {
    delais_cycle(1);
    TimeOut--;
   }

  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }
   else   // Verifier in.error si c'est pas le TIMEOUT
   {
    if (in.error != 0) return (in.error); //error on LSR line...
   }
 
  /*Test and display the acknowledge code and return*/
  //printf("Check the ack ACK code :\n\r");
  DACKCODE = in.buffer[0];
  if ( DACKCODE != 0x41)
   { 
    return (253); /* ack error display code*/
   }
  //display_ack_code(); /*Display the right 0x41 ACK code*/
  return(0);  /* 0 means that the things went well*/
}


/***********************************************************
 *
 */
UBYTE port_getframe()

{ 
  float TimeOut;

  /* Complete code byte receiving */
  TimeOut = TO3; /*DTIMLO = 16 ou 3 minutes*/

  /* (1) Input interrupt activation and complete code receiving*/
  //ier_mask = ier_mask | IER_RX_DATA | IER_LINE_STATUS; //deja active
  //outportb( uart_base + IER, ier_mask ); //deja active
  while ((in.data_index < 2) && ( in.error == 0) && (TimeOut != 0))
   {
    delais_cycle(1);
    TimeOut--;
   }
  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }
  else   // Verifier in.error si c'est pas le TIMEOUT
   {
    if (in.error != 0) return (in.error); //error on LSR line...
   }

  DACKCODE = in.buffer[1];
  /*Check the acknowledge COMPLETE code and return*/
  if ( DACKCODE != 0x43)
   { 
    return (253); /* ack error display code*/
   }
  // display_ack_code(); /*Display the 0x43 COMPLETE code*/
  
  /* (2) Receiving of the data frame */
  while ((in.ctrl != 0) && (TimeOut != 0) && (in.error ==0))
   {
    delais_cycle(1);
    TimeOut--;
   }
  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }
  else   // Verifier in.error si c'est pas le TIMEOUT
   {
    if (in.error != 0) return (in.error); //error on LSR line...
   }

  /* (3) Test of the received checksum */
  if (in.buffer[in.lenght-1] != in.checksum)
   {
    return (252); /* Checksum not valid !*/
   }
   
  /* Shut off the receiver and the transmitter... */
  ier_mask = 0;
  outportb ( uart_base+IER, ier_mask );
  return (0);
   
}

/***********************************************************
 *
 */
UBYTE port_sendframe()

{ 
  float TimeOut;

  //raz_out_buffer(); ????

  /*Sending of the command frame*/
  TimeOut = TO4; /* 4 minutes */
  out.lenght = 128; //On envoie un secteur de 128 octets
  out.data_index = 0;
  out.ctrl = 1; 
  out.checksum = 0; 

  /* (1) Wait 1275 microsecond*/
  delais_cycle (1275);

  /* (2) Output interuption activation and frame sending*/
  ier_mask = ier_mask | IER_TX_THRE ;
  outportb( uart_base+IER, ier_mask );

  while (( out.ctrl != 0) && (TimeOut != 0))
   {
    delais_cycle(1);
    TimeOut--;
   }
  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }
 
  /* (3) Acknowledge code byte receiving */
  TimeOut = TO2; /* 40 miliseconds */

  //ier_mask = ier_mask | IER_RX_DATA | IER_LINE_STATUS;
  //outportb( uart_base+IER, ier_mask );

   while (( in.data_index < 2) && (TimeOut != 0) && (in.error == 0))
   {
    delais_cycle(1);
    TimeOut--;
   }

  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }
   else   // Verifier in.error si c'est pas le TIMEOUT
   {
    if (in.error != 0) return (in.error); //error on LSR line...
   }
 
  /*Test and display the acknowledge code and return*/
  //printf("Check the ack ACK code :\n\r");
  DACKCODE = in.buffer[1];
  if ( DACKCODE != 0x41)
   { 
    return (253); /* ack error display code*/
   }
  // display_ack_code(); /*Display the 0x41 ACK code*/


  /* (4) Complete code byte receiving */
  TimeOut = TO3; /*DTIMLO = 16 ou 3 minutes*/

  while (( in.ctrl != 0) && (TimeOut != 0))
   {
    delais_cycle(1);
    TimeOut--;
   }
  if (TimeOut == 0)
   { 
    //timeout reach !!!
    return (250);
   }
  else   // test de in.error des fois que...
   {
    if (in.error != 0) return (in.error); //error on LSR line...
   }

  DACKCODE = in.buffer[2];
  /*Display the acknowledge code and return*/
  if ( DACKCODE != 0x43)
   { 
    return (253); /* ack error display code*/
   }

  /* Shut off the receiver and the transmitter... */
  ier_mask = 0;
  outportb( uart_base+IER, ier_mask );

  /*Display the 0x43 COMPLETE code*/
  //display_ack_code(); 
  return (0);

}

/******************************************************************
---------------      Traitement des erreurs    --------------------
*******************************************************************/

void message_display()
{
    textattr(0+EN_VERT);
    switch (DSTATUS) /*code d'erreur pour echec communication*/
    {
        case 248:
            cprintf ("COM ERROR ON LINE STATUS REGISTER (serial_data = $%02X) !!!\n\r", serial_data);
            break;
        case 249:
            cprintf ("WARNING !! BREAK INTERRUPTION REQUESTED !!!\n\r");
            break;
        case 250:
            cprintf ("COM ERROR : TIME OUT REACHED !!!\n\r");
            break;
        case 251:
            cprintf ("COM ERROR : FRAMING ERROR : NO BIT STOP !!!\n\r");
            break;
        case 252:
            cprintf ("ATARI ERROR $8F (143) : NO VALID CHECK SUM !!!\n\r");
            break;
        case 253:
            cprintf ("ERROR ON ATARI DRIVE : ACKNOWLEDGE CODE RETURNED AS FOLLOWING :\n\r");
            display_ack_code();
            break;            
        case 254:
            cprintf ("ERR0R : DATA OVERRUN : INPUT DATA NOT READ !!!\n\r");
            break;
        case 255:
            cprintf ("ERR0R UNTAGGED - 255 : CHECK SUM NOT SEND!!!\n\r");
            break;
        default:
            cprintf ("An undefined error has occured... \n\r");
            cprintf ("The error code was : %u\n\r", DSTATUS);
     }
     textattr(0+EN_BLANC);
     clreol();
}

//-------------------------------------Gestion code ACK----------------
void display_ack_code()
{
    switch (DACKCODE)       /*les differents cas de code ACK retournes*/
    {
      case 0x41:
        cprintf ("ATARI Ack code 'A' $41 = OK!!!\n\r");
        break;
      case 0x43:
        cprintf ("ATARI Ack code 'C' $43 = COMPLETE!!!\n\r");
        break;
      case 0x45:
        cprintf ("ATARI Ack code 'E' $45 = DRIVE ERROR [error $90 (144)]!!!\n\r");
        break;
      case 0x4E:
        cprintf ("ATARI Ack code 'N' $4E = DRIVE NOT ACK [error $8B (139)]!!!\n\r");
        break;
      default:
        cprintf ("Code Acknowledge inconnu = $%02Xh\n\r", DACKCODE);
    }
}

//-------------------------------------------------------------------
void raz_in_buffer(void)  /*mise a 0 les octets du in.buffer*/
{
    int i;
    for (i=0;i<260;i++)
    {
        in.buffer[i] = 0x00;

    }
    in.lenght = 0;
    in.data_index = 0;
    in.checksum = 0;
    in.ctrl = 0;
    in.error = 0;
}

//-------------------------------------------------------------------
void raz_out_buffer(void)  /*mise à 0 les octets du out.buffer*/
{
    int i;
    for (i=0;i<250;i++)
    {
        out.buffer[i] = 0x00; //(UBYTE) i;//0x00;
    }
    out.lenght = 0;
    out.data_index = 0;
    out.checksum = 0;
    out.ctrl = 0;
    out.error = 0;
}

/**********************************************************************
*         PROCEDURE DE CALIBRATION BASE DE TEMPS 1 MICROSECONDE
***********************************************************************/
void calibration()
{
    int t1, t2;
    float x1, x2;
    long int cur_compt;
    x1 = x2 = 0;
    t1 = 250;       /*premier essai pour valeur TEMPO*/
    t2 = 50;        /*deuxieme essai pour valeur TEMPO*/
    //TCLOCK = 80;  //test
    //return;       //test

    /* Premier point de mesure pour calibration */
    TCLOCK = t1;
    cprintf("Premiere calibration...\n\r");
    cur_compt = biostime(0,0); /* on lit l'horloge compteur interne */
    /* */

    delais_cycle(5494505); // = 100 tick si TCLOCK donne 1 microsec.

    x1= (biostime(0,0) - cur_compt); /*calcul du temps ecoule x1*/
    cprintf ("Temps d'acces pour TCLOCK = %u : %g tick \n\r", t1,x1);
    
    /* Meme procedure pour un deuxieme point de mesure pour calibration */
    TCLOCK = t2;
    cprintf("Deuxieme calibration...\n\r");
    cur_compt = biostime(0,0);

    delais_cycle(5494505); //= 100 tick si TCLOCK donne 1 microsec.

    x2= (biostime(0,0) - cur_compt);
    cprintf ("Temps d'acces pour TCLOCK = %u : %g tick \n\r", t2,x2);

    /* Calcul de la valeur de TCLOCK (Attention : c'est un entier !!)*/
    TCLOCK = (long int)(t1 + ((100-x1)*(t1-t2)/(x1-x2)));
    /* Affichage du resultat*/
    cprintf ("Valeur de TCLOCK : %ld\n\r", TCLOCK);

    /*essai avec la valeur calculee de TCLOCK*/
    cprintf ("Verification... attendre 27 secondes = 500 ticks)\n\r");
    cur_compt = biostime(0,0);
    delais_cycle(27472527);
    x1= (biostime(0,0) - cur_compt);
    cprintf ("Temps d'acces pour TCLOCK = %ld : %g ticks\n\r", TCLOCK, x1);
    getch();
}

//------------------Procedure d'attente-----------------------------
void delais_cycle(long int duree)
/* procedure d'attente calibree par TCLOCK */
{
    int i,j;
    for (i=duree;i>0;i--)/* duree = attente en microsecondes */
        for (j=TCLOCK;j>0;j--) /*TCLOCK calcule pour 1 microsec */
             asm("nop");
}

/*************************************************************/
/*         PROCEDURE DE CONTROL DU PORT COM                  */
/*************************************************************/
/* Save the old PORT... Display it ... Restore it...         */
/*************************************************************/
void port_save( void )
{
  UBYTE temp;
  old_mcr = inportb( uart_base + MCR);
  old_lcr = inportb( uart_base + LCR);
  old_ier = inportb( uart_base + IER);
  temp = (old_lcr | LCR_DLAB);
  outportb( uart_base + LCR, temp );
  old_dll = inportb( uart_base + DLL);
  old_dlm = inportb( uart_base + DLM);
  outportb( uart_base + LCR, temp & (~LCR_DLAB) );
}

void port_display( void )
{
  cprintf ("Valeur de vitesse : %ud\n\r", (old_dlm * 256 + old_dll));
  cprintf ("Valeur de MCR modem control : %xh\n\r", old_mcr);
  cprintf ("Valeur de LCR modem control : %xh\n\r", old_lcr);
  cprintf ("Valeur de IER modem control : %xh\n\r", old_ier);
}

void port_restore( void )
{
  UBYTE temp;
  outportb( uart_base + MCR, old_mcr);
  outportb( uart_base + LCR, old_lcr);
  outportb( uart_base + IER, old_ier);
  temp = (old_lcr | LCR_DLAB);
  outportb( uart_base + LCR, temp );
  outportb( uart_base + DLL, old_dll);
  outportb( uart_base + DLM, old_dlm);
  outportb( uart_base + LCR, temp & (~LCR_DLAB) );
}

/***************************************************************
****************************************************************
*          SOUS-PROCEDURES DES PROCEDURES PRINCIPALES
****************************************************************
****************************************************************/

int image_file_save()
{
    int ouvert;    
    long int status, file_lenght;
    char inyn[2];
    int fd, disk;

    if (image_filename[0] == NULL)
           strcpy (image_filename,"ABDIMAGE.XFD");

    ouvert = FALSE;

    do
      {
        GetString(" Enter the filename to save Atari disk image in: [%s]>", image_filename, 12);
        RemoveLF(image_filename);
        UpCaseStr(image_filename);

        fd = open(image_filename, O_RDWR | O_BINARY, 0777);
        if (fd != -1) 
         {   // il existe faut-il l'effacer ??
           inyn = "Y";
           YesNo("Warning ! The file already exists : overwrite ? (Y/N) [%s]>", inyn);

           if (inyn[0] == 'N') //non
             {
		   //re-entrer un nouveau nom
               close(disk);
             }
            else  // Yes overwrite : on sort de la boucle, fichier ouvert !
             {
               ouvert = TRUE;
             }
         }
        else // fd = -1 : le fichier n'existait pas !! on le cree (ou erreur)
         {
            fd = open(image_filename, O_CREAT | O_RDWR | O_BINARY, 0777);
            if (fd == -1) //Erreur a l'ouverture
             {
                cprintf("Error writing new file !!! \n\r");
                return (-3);
             }
            else // Pas d'erreur : on sort de la boucle, fichier ouvert !
             {
               ouvert = TRUE;
             }
 
         }
      }
    while (!ouvert);

    disk = fd;
    status = write (disk , &atari_disk_buffer, DISK_LENGHT);
    if (status != DISK_LENGHT)
	{ 
            cprintf("Error writting empty format file with : ");
            cprintf("status = %ld\n\r",status);
	      close(disk);
	      return (-3);
	}

    file_lenght = lseek(disk, 0L, SEEK_END); // Calcul la taille jusqu'au EOF
    lseek(disk, 0L, SEEK_SET); //Se remettre en position debut de fichier
    status = read(disk, &sector_buffer, 128 ); //test de lecture
    if (status == -1)
        {
		cprintf("Error on testing the reading of the first sector !!!\n\r");
		close(disk);
		return (-3);
	}

    cprintf("OK ! Total bytes written in file %s = %ld \n\r", image_filename, file_lenght);
    close(disk);
    // getch();
    return (0);
}

void sector_to_file_transfer()
{
    UWORD compt;
    unsigned long offset_octet;
    offset_octet = (sector_nbr - 1) * 128;
    for (compt = 0 ; compt < 128; compt ++)
    {
        atari_disk_buffer[offset_octet + compt] = in.buffer [compt + 2];
        //decalle de 2 a cause du 41 et 43 ack codes
    }
}

void transfer_file_in_buffer()
{
    UWORD compt;
    unsigned long offset_octet;
    offset_octet = (sector_nbr - 1) * 128;
    for (compt = 0 ; compt < 128; compt ++)
    {
        out.buffer [compt] = temp_buffer[offset_octet + compt];
    }
}

void write_bad_sector(void)
{
    int i;
    for (i=0;i<128;i=i+4)
    {
        //decalle de 2 a cause du 41 et 43 ack codes
        in.buffer[i+2] = 0x42; // 'B' comme BAD!
        in.buffer[i+3] = 0x41; // 'A' comme BAD!
        in.buffer[i+4] = 0x44; // 'D' comme BAD!
        in.buffer[i+5] = 0x21; // '!' comme BAD!
    }
    in.checksum = 0xFF; // dans le cas d'un bad sector
    // arbitraire mais reconnaissable ! [il faudrait le bon] 
}

int compare_sector()
{
    UWORD compt;
    unsigned long offset_byte;
    offset_byte = (sector_nbr - 1) * 128;
    for (compt = 0 ; compt < 128; compt ++)
    //128 octets, decalage de 2 pour les codes acknowledge
    {
       if (in.buffer[compt+2] != temp_buffer[offset_byte+compt])
       return TRUE; // il y a eu une difference !!
    }
    return FALSE;
}

void sector_selection(void)
{
  firstsect = 1;
  //lastsect = 1040;
  lastsect = DSIZE;
  GetNumber(" Enter the first sector on the Atari disk drive :[%u]>", &firstsect);
  if (firstsect < 1)
		firstsect = 1;
	else if (firstsect > 1040)
		firstsect = 1040;  
  GetNumber(" Enter the last sector on the Atari disk drive :[%u]>", &lastsect);
  if (lastsect < firstsect)
		lastsect = firstsect;
	else if (lastsect > 1040)
		lastsect = 1040;
}

int read_sector()
{
      DCOMND = 0x52;
      DSTATUS = 0;
      DAUX2 = (UBYTE) (sector_nbr >> 8); //high sector number
      DAUX1 = (UBYTE) (sector_nbr & 0xFF); //low sector number
      /* Envoi de la commande */
      DSTATUS = port_sendcommand();
      if (DSTATUS != 0) return (-1); //erreur sur envoi trame
      /* Reception de la trame reponse */
      DSTATUS = port_getframe();
      if (DSTATUS != 0) return (-2);//erreur sur reception trame
         
      return(0); //sortie normale, secteur lu en in.buffer[+2]
}

int drive_status_request()
{ 
  raz_in_buffer();
  sector_nbr = 0;     //pas de secteur pour le status
  DCOMND = 0x53;
  DVSTATS = 0;
  DVSTATH = 0;
  DAUX1 = 0;
  DAUX2 = 0;
  DSTATUS = port_sendcommand();
  if (DSTATUS != 0) return (-1); //erreur sur envoi trame
  /* Reception de la trame reponse */
  DSTATUS = port_getframe();
  if (DSTATUS != 0) return (-2); //erreur sur reception trame

  //Etat de l'Atari Disk Drive 'SOFTWARE'
  DVSTATS = in.buffer[2]; // 3 eme octet de la trame (apres ACK codes)
  //Etat de l'Atari Disk Drive 'HARDWARE'
  DVSTATH = in.buffer[3]; // 4 eme octet de la trame (apres ACK codes)
  // deux autres octets recu ignores

  return (0);
}

void display_status_line()
{
    int wx,wy;
    wx = wherex();
    wy = wherey();
    window(1,22,80,24);
    textattr( 0 + EN_VIOLET); // text en violet
    gotoxy(1,2);
    clreol(); //effacer la ligne
    cprintf(" ATARI DRIVE ");
    if (!(DVSTATS | DVSTATH))
      {  //les 2 octets de status sont a 0
         cprintf("NOT READY OR NOT CONNECTED");
      }
    else
      {
         if (!(DVSTATH & 0x80))
         { // if the bit #7 of second byte is 0
           cprintf("STATUS = NO DISK IN DRIVE");
         }
         else 
         {
           cprintf("STATUS = DISK READY "); 
           if (DVSTATS & 0x08) cprintf("/WRITE PROTEC ");
           else cprintf("/WRITABLE ");
           if (!(DVSTATS & 0x80)) 
              {
               DSIZE = 720;
               cprintf("/SINGLE DENSITY (720 sect)");
              }
           else
              {
               DSIZE = 1040;
               cprintf("/ENHANCED DENSITY (1040 sect)");
              }
          }
       }
    window(1,9,80,20);
    textattr(0 + EN_BLANC); // text en blanc
    gotoxy(wx,wy);
}

int format_disk()
{ 
  raz_in_buffer();
  sector_nbr = 0;     //pas de secteur pour le format
  // DCOMND initialise dans la procedure principale
  DAUX1 = 0;
  DAUX2 = 0;
  DSTATUS = port_sendcommand();
  if (DSTATUS != 0) return (-1); //erreur sur envoi trame
  /* Reception de la trame reponse */
  DSTATUS = port_getframe();
  if (DSTATUS != 0) return (-2); //erreur sur reception trame
 
  return (0);
}

int write_sector()
{
      DCOMND = 0x57;
      DSTATUS = 0;
      DAUX2 = (UBYTE) (sector_nbr >> 8); //high sector number
      DAUX1 = (UBYTE) (sector_nbr & 0xFF); //low sector number
      /* Envoi de la commande */
      DSTATUS = port_sendcommand();
      if (DSTATUS != 0) return (-1); // erreur sur envoi trame de commande
      /* Envoi de la trame a ecrire */     
      transfer_file_in_buffer();
      DSTATUS = port_sendframe();
      if (DSTATUS != 0) return (-2); //erreur sur envoi trame de donnees

      return(0); //sortie normale
}


/*********************************************************************************
**********************************************************************************
*                                  MAIN PROCEDURES
**********************************************************************************
*********************************************************************************/

int Init_Com_Port()
{
  uart_base = COM1_UART;
  irq_mask = 0;
  interrupt_number = COM1_INTERRUPT;

  /* Ensure we can get through the code without GPFing the system */
  //variable a LOCKER 
  LOCK_VARIABLE (uart_base);
  LOCK_VARIABLE (irq_mask);
  LOCK_VARIABLE (interrupt_number);
  LOCK_VARIABLE (ier_mask);
  LOCK_VARIABLE (mcr_out);
  LOCK_VARIABLE (in);
  LOCK_VARIABLE (out);
  LOCK_FUNCTION (com_handler);
  LOCK_FUNCTION (break_handler);
  LOCK_VARIABLE (serial_int);
  LOCK_VARIABLE (serial_data);
  LOCK_VARIABLE (cks);
  LOCK_VARIABLE (xit);

  window(1,9,80,20);
  textattr(EN_ROUGE);
  cprintf("WELLCOME TO ATARI TO PC FILE DESIGNER (c) Phil 2001\n\r");
  calibration();
  // initialize_disk_buffer();
  // UBYTE *disk_buffer = (UBYTE *) malloc (DISK_LENGHT);
  memset( &atari_disk_buffer, 0x00, DISK_LENGHT );
  memset( &temp_buffer, 0x00, DISK_LENGHT );
  //test...
  //port_display();
  //getch();
  //...fin test

  cprintf(" Ouverture du port...\n\r");
  port_open();

  cprintf(" Initialisation du port...\n\r");
  port_set( 19200L, 'N', 8, 1 );  /*speed, parity, data, stopbits */

  /*Initializing buffers... */
  cprintf (" Initialisation du in.buffer...\n\r");
  raz_in_buffer();
  cprintf (" Initialisation du out.buffer...\n\r");
  raz_out_buffer();

  temp_filename[0] = '\0';
  image_filename[0] = '\0';
  fileflag = FALSE;
  DSIZE = 1040; //par defaut

  cprintf("OK !!! Press any key to return >\n\r");

  window(1,22,80,24);
  textattr( 0 + EN_VIOLET); // text en violet
  gotoxy(1,1);
  cprintf(" PORT = COM1 at %04X, Speed: 19200Bauds, 8 bits data, No parity, 1 bit Stop",uart_base);
  window(1,9,80,20);
  textattr(0 + EN_BLANC); // text en blanc
  gotoxy(1,1);
  getch();  
  clrscr();
  return 0;
  //test...
  //printf ("Valeur de uart_base : %u\n\r", uart_base);
  //printf ("Valeur de irq_mask : %u\n\r", irq_mask);
  //printf ("Valeur de test : %u\n\r", LCR_ODD_PARITY);
  //port_save();
  //port_display();
  //...fin test


}

int Exit_Com_Port ()
{
  window(1,9,80,20);
  textattr(0 + EN_ROUGE);
  clrscr();
  port_close();
  cprintf("Port Ferme!!\n\r");
  cprintf("Press any key to exit Atari File Designer \n\r");
  getch();
  return 0; 
}

int Read_Sector_To_PC_File()
{
  char inkey[2];
  UWORD badflag;

  cprintf("Read sectors from Atari Drive to memory command: \n\r");
  memset( &atari_disk_buffer, 0x00, DISK_LENGHT );
  sector_selection();

  cprintf("Reading sectors %u to %u : \n\r", firstsect, lastsect);
  cprintf(" Install Atari disk and press anykey when ready >\n\r");
  getch();
  sector_nbr = firstsect;
  badflag = 0;
  while ( sector_nbr <= lastsect )
  { 
      cprintf(" Reading sector %u - ", sector_nbr);
      DERROR = read_sector();
      switch (DERROR)
      {        
        case 0:
           cprintf("OK ! read with checksum = $%02X\n\r", in.checksum);
           sector_to_file_transfer();
           sector_nbr++; //tout est OK, secteur suivant !
           break; // fin du switch()
        case -1: 
           cprintf("Error on sending the command frame !!!\n\r");
           return(DERROR);
        case -2:  
           cprintf("Error on receiving the data frame !!!\n\r");
           message_display();
           cprintf(" On the reading of sector %u : ", sector_nbr);
           strcpy(inkey,"R"); 
           RetryAbortSkip("Retry/Abort/Skip bad sector ? (R/A/S)[%s]>", inkey);
           if (inkey[0] == 'A') //Abort = abandon
               {
                 return (DERROR); // retourne l'erreur et sortir !
               }
           else if (inkey[0] == 'S') // skip and write bad sector
               {
                 write_bad_sector(); //simuler la lecture
                 cprintf ("OK : BAD! sector at %u written !!!\n\r", sector_nbr);
                 sector_to_file_transfer(); //sauvegarder le bad secteur
                 badflag = sector_nbr;
                 sector_nbr++; // on passe au secteur suivant !
                 break; //fin du switch()
               }
          //sinon inkey = 'R' : pas de transfert et relire le meme secteur
      }
  }   
  //boucle terminee !
  cprintf("Sector reading completed");
  if (badflag) cprintf(" with last error on sector %u !\n\r", badflag);
     else cprintf(" !!\n\r");   
  return (0); //Pas d'erreur non resolues DERROR = 0
}

void Display_Error_Log (int finerr) // For debugging only
{
  int i;

  if (finerr < 0)
  {
     /* Here in case of error... */
     textattr(0+EN_VERT);
     cprintf ("\n\r**ERREUR SUR LA TRANSMISSION / RECEPTION**\n\r");
     cprintf ("\n\r**ERROR LOG AS FOLLOWING : **\n\r");
     message_display();
     textattr(0+EN_VERT);
     getch();
     cprintf("Resultat de la derniere commande lecture/ecriture en ERREUR, \n\r");
     cprintf("Valeur de (nb octets lus-1)in.data_index : $%02X\n\r", in.data_index);
     cprintf("ERREUR sur lecture secteur %d ($%04X) :\n\r", sector_nbr, sector_nbr);
     cprintf("ACK codes = $%02X-$%02X, Data Frame =\n\r", in.buffer[0], in.buffer[1]);
     getch();
     for (i=2;i<(DFRLEN-1);i++)
     {
           cprintf(" %02X", in.buffer[i]);
           if (((i-2) & 0x0007) == 0x0007)  {cprintf(" -");}
           if (((i-2) & 0x000F) == 0x000F)  {cprintf(" \n\r");}
     }
     cprintf ("\n\rAvec la checksum recue  : $%02X\n\r", in.buffer[DFRLEN-1]);
     getch();
     cprintf ("Valeur de in.lenght     : $%02X\n\r", in.lenght);     
     cprintf ("Valeur de in.ctrl       : $%02X\n\r", in.ctrl);
     cprintf ("Valeur de in.checksum   : $%02X\n\r", in.checksum);
     cprintf ("Valeur de in.error      : $%02X\n\r", in.error);
     getch();
     cprintf ("Trame de commande vers Atari Drive\n\r");
     cprintf ("Valeur de out.buffer    :");
     cprintf(" $%02X ", out.buffer[0]);
     cprintf("- $%02X ", out.buffer[1]);
     cprintf("- $%02X ", out.buffer[2]);
     cprintf("- $%02X\n\r", out.buffer[3]);
     cprintf ("Valeur de out.ctrl      : $%02X\n\r", out.ctrl);
     cprintf ("Valeur de out.checksum  : $%02X\n\r", out.checksum);
     cprintf ("Valeur de mcr_out       : $%02X\n\r", mcr_out);
     cprintf ("Valeur de ier_mask      : $%02X\n\r", ier_mask);
     cprintf ("Valeur de cks           : $%02X\n\r", cks);
     cprintf ("Valeur de serial_int    : $%02X\n\r", serial_int);
     cprintf ("Valeur de serial_data   : $%02X\n\r", serial_data);
     cprintf ("Valeur de xit           : $%02X\n\r", xit);
     textattr(0+EN_BLANC);
     clreol();
  }
}

int PC_File_Writing(char *seldef)
{
  char inkeyb[2];
  DSTATUS = 0;

  strcpy(inkeyb, seldef);
  YesNo("Do you want to save the Atari disk image file ? (Y/N) [%s]>", inkeyb);
  if (inkeyb [0] == 'N' ) {return 1; } //non pas de sauvegarde

  DERROR = image_file_save();

  if (DERROR == 0 )    // 0 = c'est OK !
  {
      cprintf("Successful !!!\n\r");
  }
  else // si -3 = pas bon  !
  {
      cprintf("Error sorry !!!\n\r");
  } 
  return (DERROR); 
}

int Display_Atari_DriveStatus()
{
  UWORD i;
  char bs[9];

  cprintf("Display Atari Drive Status :\n\r");
  cprintf(" Press any key when ready to start >\n\r");
  getch();
  DERROR = drive_status_request();
  display_status_line();
  if (DERROR == -1)
     {
        cprintf("Error on sending the command frame !!!\n\r");
        return (DERROR); //erreur sur envoi trame
     }

   else if (DERROR == -2)
     {  
        cprintf("Error on receiving the data frame !!!\n\r");
        return (DERROR); //erreur sur envoi trame
     }

   cprintf("Status frame received with ");
   cprintf("checksum = $%02X\n\r", in.checksum);
   for (i=2;i<(DFRLEN-1);i++)
     {
        BinaryStr(bs, in.buffer[i]); //transformation en binaire
        cprintf(" Byte %d = $%02X ( Binary : %s )\n\r",(i-1),in.buffer[i], bs);
     }   
   return 0;

}

int Load_Memory_With_PC_File()
{
    int ouvert, wx,wy;    
    long int status, file_lenght;
    char inyn[2];
    int fd;
    
    //if (temp_filename[0] == NULL)
    strcpy (temp_filename,image_filename);

    ouvert = FALSE;
    cprintf("Read the reference PC File into memory :\n\r");
  
    do
    {
        GetString(" Please enter the filename to read: [%s]>", temp_filename, 12);
        RemoveLF(temp_filename);
        UpCaseStr(temp_filename);

        fd = open(temp_filename, O_RDONLY | O_BINARY, 0777);
        if (fd == -1)  // Erreur
         {
            inyn = "Y";
            YesNo("Error opening File ! Continue ? (Y/N) [%s]>", inyn);
            if (inyn[0] == 'N') //non
              {
		    return (-3);
              }
         }

        else ouvert = TRUE;
    }
    while (!ouvert);

    file_lenght = lseek(fd, 0L, SEEK_END); // Calcul la taille jusqu'au EOF
    if ((file_lenght != DISK_LENGHT) && (file_lenght != (720 * 128)))
      {
         cprintf("ERROR Total bytes in file (%ld) not equal to 133120 bytes !!! \n\r", file_lenght);
         close(fd);
         return (-3);
      }
    memset( &temp_buffer, 0x00, DISK_LENGHT );
    lseek(fd, 0L, SEEK_SET); //Se remettre en position debut de fichier
    status = read(fd, &temp_buffer, file_lenght );
    if (status == -1)
      {
         cprintf("Error reading disk file !!!\n\r");
	 close(fd);
	 return (-3);
      }
    close(fd);
    fileflag = TRUE;
    cprintf("OK : File %s read !!! (see below displayed parameters)\n\r", temp_filename);
    //cprintf("Parameters displayed ! Press any key to continue...\n\r");
    wx = wherex();
    wy = wherey();
    window(1,22,80,24);
    textattr( 0 + EN_VIOLET); // text en violet
    gotoxy(1,3);
    clreol();
    cprintf(" PC File into memory : %s",temp_filename);
    cprintf(" (%ld bytes / %ld sectors)", file_lenght, file_lenght/128);
    window(1,9,80,20);
    textattr(0 + EN_BLANC); // text en blanc
    //getch();
    gotoxy(wx,wy);
    return (0);
}
  
int Compare_Atari_Disk_To_Memory()
{
   UWORD lasterrsect, firsterrsect;
   char inkey[2];
   int diff, flg;

   flg = FALSE;
   cprintf("Read and compare an Atari disk with PC file into memory : \n\r");
   inkey = "N"; // = charger un nouveau fichier
   if (fileflag) // = il existe un fichier : tester si il faut le remplacer
   {
       //inkey = "Y"; // oui, ne pas charger de nouveau fichier, pas de remplacement
       YesNo(" Would you like to use the PC file already in memory ? (Y/N) [%s]>", inkey);
   }
   if (inkey[0] == 'N') //non
   {
           DERROR = Load_Memory_With_PC_File();
           if (DERROR != 0) //-3 si erreur
           return (DERROR);
   }
   memset( &atari_disk_buffer, 0x00, DISK_LENGHT ); //mise a zero du buffer disk reception
   sector_selection();
   cprintf("Reading sector %u to %u : \n\r", firstsect, lastsect);
   cprintf(" Install Atari disk and press anykey when ready >\n\r");
   getch();
   sector_nbr = firstsect;
   diff = FALSE;
   while ( sector_nbr <= lastsect )
   {
      cprintf(" Reading and comparing sector %u - ", sector_nbr);
      DERROR = read_sector();       
      if (DERROR == -1) 
      {
          //-1 : erreur en lecture et abandon Abort
          cprintf("Error on sending the command frame !!!\n\r");
          cprintf("ERROR... EXITING THE COMPARISON PROCESS...\n\r");
          break; // sortir de la boucle while de lecture de secteurs
      }
      if (DERROR == -2)
      {
          cprintf("Error on receiving the data frame !!!\n\r");
          message_display();
          cprintf(" On the reading of sector %u : ", sector_nbr);
          strcpy(inkey,"R"); 
          RetryAbortSkip("Retry/Abort/Skip bad sector ? (R/A/S)[%s]>", inkey);
          if (inkey[0] == 'A') //Abort = abandon
              {
                break; // retourne l'erreur et sortir !
              }
          else if (inkey[0] == 'S') // skip and write bad sector
              {
                write_bad_sector(); //simuler la lecture d'un secteur
                cprintf ("OK : BAD! sector at %u written !!!\n\r", sector_nbr);
                DERROR = 0; //effacer l'erreur car on a ecrit un bad sector
                // et on peut lancer une comparaison (paragraphe suivant)
              }
          //sinon inkey = 'R' : pas de transfert et relire le meme secteur

      }
      if (DERROR == 0)  //tout est OK, secteur lu, on lance la comparaison...
      {
          if (compare_sector()) // = TRUE si il y a une difference
          {
              // il y a une difference !!!
              lasterrsect = sector_nbr; //noter le dernier secteur different (encours)
              if (!flg) (firsterrsect = sector_nbr); // et noter le premier
              flg = TRUE;  //pour le fichier entier non identique
              cprintf("Warning : Sector %d is different !!!\n\r", sector_nbr);
          }
          else
          {
              cprintf("OK same sector! (checksum = $%02X)\n\r", in.checksum);
          }
          sector_to_file_transfer(); // Sauvegarde du secteur lu sur Atari drive
          sector_nbr++; // passer au secteur suivant !             
      }
     
   }  
   //boucle de lecture secteurs terminee ! Afficher le resultat

   if (flg) //test du flag du fichier pour l'erreur
   {
       textattr(0+EN_VERT);
       cprintf("WARNING ! THE 2 FILES ARE NOT IDENTICAL !!\n\r");
       cprintf(" first different sector is %u\n\r", firsterrsect);
       cprintf(" last different sector is %u\n\r", lasterrsect);
       textattr(0+EN_BLANC);
       clreol();
   }
   else if (sector_nbr != 1) //on a lu au moins un secteur 
   {
       cprintf("OK ! THE 2 FILES ARE IDENTICAL (checked up to sector %u)\n\r", sector_nbr-1);
   }
   // else sinon ne rien afficher
  return (DERROR);
}

int Format_Atari_Disk()
{
  int i;
  char sed[4];

  cprintf("Format an Atari disk on the Atari disk drive :\n\r");

  //status ??? ?????????????????????

  strcpy(sed,"E");
  GetString(" Format in Single (720) or Enhanced density (1040 sectors)? (S/E) [%s]>", sed, 1);
  UpCaseStr(sed);
  
  if (sed[0] == 'S') DCOMND = 0x21;
  else DCOMND = 0x22;

  cprintf("Warning ! All the data on the Atari drive disk will be lost !!\n\r");
  if (DCOMND == 0x21) cprintf("Enter <YES> to format single density - 720 sectors size");
  else 
    {
      cprintf("Enter <YES> to format enhanced density - 1040 sectors size\n\r");
      cprintf("(Nota: enhanced density is for 1050 Atari Disk Drive only!) ");
    }
  strcpy(sed,"NO");
  GetString("(YES/NO) [%s]>", sed, 3);
  RemoveLF(sed);
  UpCaseStr(sed);

  if (strcmp(sed,"YES")) return 1; //1 = abort by user...
  
  DERROR = format_disk();

  if (DERROR == -1)
     {
        cprintf("Erreur sur l'envoi de la commande !!!\n\r");
        return (DERROR); //erreur sur envoi trame
     }

   else if (DERROR == -2)
     {  
        cprintf("Erreur sur la reception de la trame reponse !!!\n\r");
        return (DERROR); //erreur sur envoi trame
     }
   cprintf("Formating process completed !! Press any key to view status block\n\r");
   getch();
   cprintf("Format status block received with :");
   cprintf("ACK codes = $%02X-$%02X\n\r", in.buffer[0], in.buffer[1]);

   for (i=2;i<(DFRLEN-1);i++)
     {
           cprintf(" %02X", in.buffer[i]);
           if (((i-2) & 0x0007) == 0x0007)  {cprintf(" -");}
           if (((i-2) & 0x000F) == 0x000F)  {cprintf(" \n\r");}
     }
   cprintf ("\n\rAvec la checksum recue  : $%02X\n\r", in.checksum);
     
   return 0;
}

int Write_Sector_From_PC_File()
{
  char inkey[2];
  UWORD errflag;

  cprintf("Write command sectors to Atari Drive from memory :\n\r");
  // Status ?? ??????????????????

  cprintf("The PC file in memory will be used for source of writing.\n\r");
  inkey = "N"; // = charger un nouveau fichier
  if (fileflag) // = il existe un fichier : tester si il faut le remplacer
     {
        inkey = "Y"; // oui, ne pas charger de nouveau fichier, pas de remplacement
        YesNo(" Would you like to use the PC file already in memory ? (Y/N) [%s]>", inkey);
     }
  if ((inkey[0] == 'N')) //non
        {
           DERROR = Load_Memory_With_PC_File();
           if (DERROR != 0) //-3 si erreur
           return (DERROR);
        }

  sector_selection();

  cprintf("Writing sectors %u to %u : \n\r", firstsect, lastsect);
  cprintf(" Warning ! The data on Atari Disk will be overwritten !!!\n\r");
  cprintf(" Install Atari disk and press enter when ready >\n\r");
  // test de ESC ou abort a rajouter ??? ????????????????????
  getch();
  errflag = 0;
  sector_nbr = firstsect;
  while ( sector_nbr <= lastsect )
  { 
      cprintf(" Writing sector %u - ", sector_nbr);
      DERROR = write_sector();       
      switch (DERROR)
      {        
        case 0:
           cprintf(" written !! (checksum = $%02X)\n\r", out.checksum);
           sector_nbr++; //tout est OK, secteur suivant !
           break;
        case -1:
           cprintf("Warning ! Command frame sending error !!!\n\r");
           return (DERROR);                        
        default: //DERROR == -2 ou autre valeur ...
           cprintf("Warning ! Data frame sending error !!!\n\r");
           message_display( );
           cprintf(" On the writing of sector %u : ", sector_nbr);
           strcpy(inkey,"R"); 
           RetryAbortSkip("Retry/Abort/or Skip writing it? (R/A/S)[%s]>", inkey);
           if (inkey[0] == 'A') //Abort = abandon
               {
                 return (DERROR); // retourne l'erreur et sortir !
               }
           else if (inkey[0] == 'S') // skip and write bad sector
               {
                 // ne rien faire !
                 cprintf ("OK : sector %u not written !!!\n\r", sector_nbr);
                 errflag = sector_nbr;
                 sector_nbr++; // on passe au secteur suivant !
                 break;
               }
          //sinon inkey = 'R' : pas de transfert et relire le meme secteur
      }
  }
  //boucle terminee !
  cprintf("Sector writing completed");
  if (errflag) cprintf(" with last error on sector %u !\n\r", errflag);
     else cprintf(" !!\n\r");   
  return (0); // DERROR = 0 : pas d'erreur non resolue
}

/******************************* ADDON 9/3/2001 REV1.70 *******************************/

int Read_Verify_Atari_Disk()
{
   UWORD lasterrsect, firsterrsect, emptysect;
   char inkey[2];
   int difflag, errflag, veriflag;
   UBYTE lastchecksum;

   difflag = FALSE;
   errflag = FALSE;
   veriflag = FALSE;

   cprintf("Read an Atari disk with PC file into memory with verifying : \n\r");

   memset( &atari_disk_buffer, 0x00, DISK_LENGHT ); //mise a zero du buffer disk reception
   sector_selection();
   cprintf("Reading sector %u to %u : \n\r", firstsect, lastsect);
   cprintf(" Install Atari disk and press anykey when ready >\n\r");
   getch();
   
   emptysect = lastsect;
   sector_nbr = firstsect;
   while ( sector_nbr <= lastsect )
   {
      DERROR = read_sector();
      if (!veriflag) cprintf(" Sector %u - ", sector_nbr);
      if (DERROR == -1) 
      {
          //-1 : erreur en lecture et abandon Abort
          textattr(EN_VERT);
          cprintf("Command frame sending error !!!\n\r");
          message_display();
          cprintf("Exiting the read and verify process\n\r");
          break; // sortir de la boucle while de lecture de secteurs
      }
      if (DERROR == -2)
      {
          textattr(EN_VERT);
          cprintf("Data frame receiving error !!!\n\r");
          message_display();
          cprintf(" Reading of sector %u : ", sector_nbr);
          strcpy(inkey,"R"); 
          RetryAbortSkip("Retry/Abort/Skip bad sector ? (R/A/S)[%s]>", inkey);
          if (inkey[0] == 'A') //Abort = abandon
              {
                cprintf("Exiting the read and verify process\n\r");
                break; // retourne l'erreur et sortir !
              }
          else if (inkey[0] == 'S') // skip and write bad sector
              {
                write_bad_sector(); //simuler la lecture d'un secteur
                cprintf ("OK : BAD! sector at %u writen !!!\n\r", sector_nbr);
                DERROR = 0; //effacer l'erreur car on a ecrit un bad sector
                // et on peut lancer une comparaison (paragraphe suivant)
              }
          //sinon inkey = 'R' : pas de transfert et relire le meme secteur

      }
      if (DERROR == 0)  //tout est OK, secteur lu, on lance la comparaison...
      {
          if (in.checksum) emptysect = sector_nbr; // marque le dernier non vide
          if (veriflag == TRUE) // 2eme cycle de lecture verification
          {
              if (lastchecksum != in.checksum)// il y a une difference !!!
              {
                  textattr(EN_VERT);
                  lasterrsect = sector_nbr; //noter le dernier secteur different (encours)
                  if (!errflag) (firsterrsect = sector_nbr); // et noter le premier
                  errflag = TRUE;  //pour le fichier entier non identique
                  cprintf("warning : error on verifying !!!\n\r");
                  cprintf("First read checksum was : $%02X\n\r",lastchecksum);
                  cprintf("Verified checksum was   : $%02X\n\r",in.checksum);
                  textattr(EN_BLANC);             
              }
              else cprintf("read and verify ! (checksum = $%02X)\n\r", in.checksum);

              veriflag = FALSE;
              sector_to_file_transfer(); // Sauvegarde du secteur lu sur Atari drive
              sector_nbr++; // passer au secteur suivant !             
          }
          else // premiere lecture veriflag == FALSE
          {
              lastchecksum = in.checksum;
              veriflag = TRUE;
              //getch(); //test uniquement pour faire un secteur dirrerent !!!
          }
      }
   }  
   //boucle de lecture secteurs terminee ! Afficher le resultat

   if (errflag) //test du flag du fichier pour l'erreur
   {
       textattr(EN_VERT); 
       cprintf("WARNING ! Disk read but errors have been encountered on verifying !!\n\r");
       cprintf(" first different sector is %u\n\r", firsterrsect);
       cprintf(" last different sector is %u\n\r", lasterrsect);
       textattr(EN_BLANC);
       clreol();
   }
   else if (sector_nbr != 1) //on a lu au moins un secteur 
   {
       cprintf("OK ! Disk read and verify up to sector %u with no errors\n\r", sector_nbr-1);
   }
   if (emptysect != lastsect) cprintf("NOTA : the last empty sector was %u\n\r",emptysect+1);

   return (DERROR);
}