/* Programa de correccion de examenes. Necesita subrutinas en   
   ensamblador para gestionar las interrupciones de la puerta     
   serial. Escrito para funcionar con una lectora Kaiser OMR-15 
   Version 1.00 -- (C) Fernando TUSELL, 19-Junio-1.989           */

#include "stdio.h"

/*-------------------------------------------------------------------------
          DECLARACION DE VARIABLES, PARAMETROS Y FUNCIONES EXTERNAS
--------------------------------------------------------------------------*/

#define buf_len         16384  /* Longitud buffer      */
#define fold            15184  /* Longitud tras de la cual un fin de
                                  documento provoca el recomienzo         */
#define long_ex           505  /* Longitud examen + 1  */
#define long_lin           12  /* Longitud linea; long_ex=42*long_lin +1  */

#define hextodec(x) ((('0'<= x) && ( x <='9')) ? (x - '0') : (x-55))
#define min(A,B)    ((A) < (B)   ? (A)        : (B)      )
#define pliega      if (i<= fold) asc_out[0]++; else asc_out[0] = 0;

int     beg,         /* Comienzo en asc_buf[] de un examen                */
        bien,
        blanco,
        mal,
        punt[9][120],/* Puntos de las distintas preguntas                 */
        result,      /* Resultado llamada a funciones                     */
        numexam,     /* Numero de tipos de examen diferentes              */
        numpreg;     /* Numero de preguntas (comun a todos los examenes)  */

double  puntos = 0.0,
        penal  = 0.0;

char    dni[9],
        anex[5],
        tipex,
        expe[6],
        acierto[121];/* 1 para pregunta bien, 0 para pregunta mal         */

unsigned
  char  resp[9][120];/* Respuestas a las distintas preguntas              */

extern 
  int   cls(),       /* Subrutina limpieza pantalla                       */
        asc_enb(),   /* Captura interrupcion puerta serial COM1:          */
        asc_dsb(),   /* Restauracion vector interrupciones                */
        asc_int();   /* Servicio interrupcion puerta serial               */

extern 
  char  asc_buf[];   /* Buffer circular (de 16384) de recepcion de 
                        caracteres procedentes de la lectora.             */
extern 
unsigned asc_in[],   /* Lugar del buffer para proximo caracter            */
         asc_out[];  /* Lugar del buffer con el primer caracter a leer    */

FILE     *ptr;
struct   regval      {int ax,bx,cx,dx,si,di,ds,es;};

/*          FUNCIONES STANDARD DE LIBRERIA COMPILADOR C                   */

  extern char fgets();
  extern int  crt_srcp(), crt_mode(), exit(), fputs();
  
pide_inst() {
  struct   regval      call_regs, ret_regs;
  call_regs.ax = 0x0800;
  sysint(0x21,&call_regs,&ret_regs);
  return((ret_regs.ax & 0x00FF));
};

termina() {
  crt_srcp(15,0,0);
  fputs("Programa terminado sin incidencias.",stdout);
  fclose(ptr);
  asc_dsb();
  exit(0);
};

wait_char() {
  struct   regval      call_regs, ret_regs;
  call_regs.ax = 0x0b00;
  sysint(0x21,&call_regs,&ret_regs);
  return((0x00FF == (ret_regs.ax & 0x00FF)));
};

identifi(beg) {
  int i, j, k, now;
  char e, digit;
  unsigned char d;
  extern char dni[],expe[],tipex,anex[],asc_buf[];
  if(asc_buf[beg] != '@')
    {fputs(" Ha habido algun problema en la lectura\n",stdout);}

  strncpy(dni,"????????\0",9);
  strncpy(expe,"?????\0",6);
  tipex = '?';
  strncpy(anex,"???\0",4);

  k = long_lin + 4;
  for(i=0;i<=9;i++) {
    digit = '0' + i;
    now = beg + k;
    d = asc_buf[now] - '0';          /* El segundo nibble del primer byte
                                        es siempre un digito entre 0 y 7  */
    if(0x04 == (0x04 & d)) dni[0]  = digit;
    if(0x02 == (0x02 & d)) dni[1]  = digit;
    if(0x01 == (0x01 & d)) dni[2]  = digit;
    e = asc_buf[++now];   
                                     /* Ahora en cambio si nos hemos de   */
    d = hextodec(e);                 /* preocupar de si tenemos un digito */
                                     /* hexadecimal mayor o menor que 'A' */

    if(0x08 == (0x08 & d)) dni[3]  = digit;
    if(0x04 == (0x04 & d)) dni[4]  = digit;
    if(0x02 == (0x02 & d)) dni[5]  = digit;
    if(0x01 == (0x01 & d)) dni[6]  = digit;
    e = asc_buf[++now];
    d = hextodec(e); 
    if(0x08 == (0x08 & d)) dni[7]  = digit;
    if(0x02 == (0x02 & d)) expe[0] = digit;
    if(0x01 == (0x01 & d)) expe[1] = digit;
    e = asc_buf[++now];         
    d = hextodec(e); 
    if(0x08 == (0x08 & d)) expe[2] = digit;
    if(0x04 == (0x04 & d)) expe[3] = digit;
    if(0x02 == (0x02 & d)) expe[4] = digit;
    e = asc_buf[++now];
    d = hextodec(e); 
    if(0x08 == (0x08 & d)) tipex   = digit;
    if(0x04 == (0x04 & d)) anex[0] = digit;
    if(0x02 == (0x02 & d)) anex[1] = digit;
    if(0x01 == (0x01 & d)) anex[2] = digit;
    k += long_lin;                                  
  }
  return(0);
};

puntua(beg) {
  int   i, j, l, m, n, cc, k, r, offset;
  char a, b, c;
  unsigned char d, e, f;
  double p;
  k      = -30;
  offset = 5;
  puntos = 0.00;
  bien = 0;
  mal = 0;
  blanco = 0;
  n = tipex - '0';
  for(m = numpreg ; m > 0 ; m -= 30) {
    j = min(m,30);
    k += 30;                  /* Se suma al indice i para encontrar      */
                              /* el numero de pregunta en cada momento   */
    r      = 11*long_lin;

    for(i=1;i<=j;i++) {
      l =  r + offset;
      a = asc_buf[beg + l];
      e = hextodec(a); 
      b = asc_buf[beg + l + 1];
      if (b <= '8')
        d = b - '0';
      else
        d = (b - 55) & 0x08;
      f = (e << 4) + d;
      cc = k + i;
      p = (double) punt[n][cc];

      if (f == resp[n][cc]) {
        acierto[cc] = '1';
        bien++;
        puntos +=  p;
        }
      else if(f==0) {
        blanco++;
        acierto[cc] = 'X'; 
        }
      else {
        acierto[cc] = '0';
        mal++;
        puntos -= penal*p;
        }
    r += long_lin;
    }
    offset +=2;
  }                  
  return(0);

};

lee_sol(beg) {
  int i, j, k, l, m, n, r, offset;
  unsigned char d, e, f;
  char a, b, c;

  m = numpreg;
  offset = 5;
  k      = - 30;  
  n = tipex - '0';

  for(m = numpreg ; m > 0 ; m -= 30 ) {
    j = min(m,30);
    k += 30;                  /* Se suma al indice i para encontrar      */
                              /* el numero de pregunta en cada momento   */
    r = 11*long_lin;          /* Longitud de la parte de  identificacion */

    for(i=1;i<=j;i++) {
      l =  r + offset;
      a = asc_buf[beg + l];
      e = hextodec(a); 
      b = asc_buf[beg + l + 1];
      if (b <= '8')
        d = b - '0';
      else
        d = (b - 55) & 0x08;
      f = (e << 4) + d;
      resp[n][k+i] = f;
    r += long_lin;
    }
    offset +=2;
  }                               
  return(0);

};

lee_punt(beg) {
  int i, j, k, l, m, n, r, offset;
  unsigned char d, e, f;
  char a,b,c;

  offset = 5;
  k      = - 30;  
  n = tipex - '0';

  for(m = numpreg ; m > 0 ; m -= 30) {
    j = min(m,30);
    k += 30;                  /* Se suma al indice i para encontrar      */
                              /* el numero de pregunta en cada momento   */
    r = 11*long_lin;          /* Longitud de la parte de  identificacion */

    for(i=1;i<=j;i++) {
      l =  r + offset;
      a = asc_buf[beg + l];
      e = hextodec(a); 
      b = asc_buf[beg + l + 1];
      if (b <= '8')
        d = b - '0';
      else
        d = (b - 55) & 0x08;
      f = (e << 4) + d;
      f = (f >> 3);
      punt[n][k+i] = f;
    r += long_lin;
    }
    offset +=2;
  }                               
  return(0);
       
};

cabecera() {
  cls();
  crt_mode(2); 
  fputs("CORRIGE -- Programa para correccion automatica de examenes\n",
        stdout);
  fputs("           Ultima revision: 19/6/1989, (C) F. Tusell\n",
        stdout);
  return(0);
};


main(argc,argv){

/* Inicializaciones y primera pantalla */

int i, j, numord = 0;
  cabecera();
  crt_srcp(5,2,0);
  fputs("Introduzca numero de examenes diferentes :   ",stdout);
  scanf("%d",&numexam);
  crt_srcp(6,2,0);
  fputs("Introduzca numero (comun) de preguntas   :  ",stdout);
  scanf("%d",&numpreg);
  acierto[numpreg+1] = "\0";
  crt_srcp(7,2,0);
  fputs("Introduzca fraccion de penalizacion      :  ",stdout);
  scanf("%lf",&penal);
  asc_enb();
  for(j=0; j<=numexam-1; j++) {
    crt_srcp(9,2,0);
    printf("Introduzca soluciones examen     %d --->    ",j);
    while(1) { 
      if(asc_in[0] != asc_out[0]) {
        i = asc_out[0];
        if (asc_buf[i] == '$') {
          beg = i - long_ex;
          identifi(beg);
          lee_sol(beg);
          pliega;
          break;
          }
        else                   
          asc_out[0]++;
      }
    }
    crt_srcp(9,2,0);
    printf("Introduzca hoja de puntos examen %d --->    ",j);
    while(1) { 
      if(asc_in[0] != asc_out[0]) {
        i = asc_out[0];
        if (asc_buf[i] == '$') {
          beg = i - long_ex;
          lee_punt(beg);
          pliega;
          break;
          }
        else                   
          asc_out[0]++;
      }
    }
} 

/* Busca un examen. Se examinan caracteres del buffer hasta
   encontrar un '$' (fin de registro). Si es asi, se procesa.            */

  cls();
  crt_mode(2); 
  fputs("CORRIGE -- Programa para correccion automatica de examenes\n",
        stdout);
  fputs("           Ultima revision: 19/6/1989, (C) F. Tusell\n",
        stdout);
  crt_srcp(5,12,0);
  fputs("------------------  S T A T U S ------------------\n\n\n",stdout);

  ptr = fopen("notas.dat","w");
  for( ; ; ) { 
    while(asc_in[0] != asc_out[0]) { 
      i = asc_out[0];
      if (asc_buf[i] == '$') {
        beg = i - long_ex;
        identifi(beg);
        puntua(beg);
        crt_srcp(8,0,0);
        numord++;
        printf("D.N.I. = %s\nExped. = %s\nTipo   = %1c\nAnexos = %s\n",
               dni,expe,tipex,anex);
        printf("Puntos = %6.2f",puntos);             
        crt_srcp(8,35,0);
        printf("Numero de orden  = %d",numord);
        crt_srcp(9,35,0);
        printf("Primer  apellido = %s",anex);
        crt_srcp(10,35,0);
        printf("Segundo apellido = %s",anex);
        crt_srcp(11,35,0);
        printf("Nombre           = %s",anex);
        crt_srcp(12,35,0);
        printf("Bien/Blanco/Mal  = %3d, %3d, %3d",bien,blanco,mal);
        fprintf(ptr,"%5s %8s %4d%3d%3d%3d%8.2f %s %s\n",
                expe,dni,numord,bien,blanco,mal,puntos,anex,&acierto[1]);
        pliega;
        }
      else {                  
        asc_out[0]++;
        }
    }
                                  /* Comprueba si un caracter esta esperando
                                            en el buffer del teclado          */
    if (wait_char()) { 
      if ((pide_inst() == 0x0041) | (pide_inst() == 0x0061))  
        termina();                                 /* La orden era 'A' o 'a'  */
    }
  }
}