

.MODEL    SMALL
 
BS   EQU  8                   ;Car cter de retroceso
CR   EQU  13                  ;Car cter de retorno de carro
ESCAPE    EQU  27             ;Car cter escape
 
 
.DATA
 
KEYBOARD_INPUT LABEL BYTE
CHAR_NUM_LIMIT DB      0           ;Longitud del buffer de entrada
NUM_CHARS_READ DB      0           ;N£mero de caracteres le¡dos
CHARS          DB      80 DUP (0)  ;Un buffer para entrada del teclado
 
.CODE
 
     PUBLIC    STRING_TO_UPPER
;-----------------------------------------------------------------------;
; Este procedimiento convierte la secuencia, usando el formato DOS      ;
; de secuencias de caracteres, a letras may£sculas.                     ;
;                                                                       ;
;    DS:DX     Direcci¢n del buffer de secuencia de caracteres.         ;
;-----------------------------------------------------------------------;
STRING_TO_UPPER          PROC
     PUSH AX
     PUSH BX
     PUSH CX
     MOV  BX,DX
     INC  BX                  ;Apunta el n£mero de caracteres
     MOV  CL,[BX]             ;N£mero de caracteres en el segundo byte del
                              ;buffer
     XOR  CH,CH               ;Pone a 0 el byte superior del n£mero
UPPER_LOOP:
     INC  BX                  ;Apunta al siguiente car cter del buffer
     MOV  AL,[BX]
     CMP  AL,'a'              ;Mira si es una letra min£scula
     JB   NOT_LOWER           ;No
     CMP  AL,'z'
     JA   NOT_LOWER
     ADD  AL,'A'-'a'          ;Convierte en letra may£scula
     MOV  [BX],AL
NOT_LOWER:
     LOOP UPPER_LOOP
     POP  CX
     POP  BX
     POP  AX
     RET
STRING_TO_UPPER          ENDP
;-----------------------------------------------------------------------;
; Este procedimiento convierte un car cter de ASCII (hex) en un nibble  ;
; (4 bits).                                                             ;
;                                                                       ;
;               AL   El car cter que ha de convertirse.                 ;
; Devuelve:     AL   Nibble.                                            ;
;               CF   1 si hay error, 0 si no lo hay.                    ;
;-----------------------------------------------------------------------;
CONVERT_HEX_DIGIT      PROC
     CMP  AL,'0'              ;¨Es un d¡gito legal?
     JB   BAD_DIGIT           ;No
     CMP  AL,'9'              ;A£n no est  seguro
     JA   TRY_HEX             ;Puede ser un d¡gito hexadecimal
     SUB  AL,'0'              ;Convertir d¡gito decimal en nibble
     CLC                      ;Poner a 0 el indicador de acarreo
     RET
TRY_HEX:
     CMP  AL,'A'              ;A£n no est  seguro
     JB   BAD_DIGIT           ;No es hexadecimal
     CMP  AL,'F'              ;A£n no est  seguro
     JA   BAD_DIGIT           ;No es hexadecimal
     SUB  AL,'A'-10           ;Convertir hexadecimal en nibble
     CLC                      ;Poner a 0 el indicador de acarreo
     RET
BAD_DIGIT:
     STC                      ;Activa el indicador de acarreo, hay
     RET                      ; un error
CONVERT_HEX_DIGIT      ENDP
 
     PUBLIC    HEX_TO_BYTE
;-----------------------------------------------------------------------;
; Este procedimiento convierte de hexadecimal en un byte, los dos ca-   ;
; racteres que est n en DS:DX.                                          ;
;                                                                       ;
;    DS:DX     Direcci¢n de los dos caracteres del n£mero hexadecimal.  ;
; Devuelve:                                                             ;
;    AL   Byte.                                                         ;
;    CF   1 si hay error, 0 si no hay error.                            ;
;-----------------------------------------------------------------------;
HEX_TO_BYTE    PROC
     PUSH BX
     PUSH CX
     MOV  BX,DX               ;Pone la direcci¢n en BX para direcc.
                              ; indirecto
     MOV  AL,[BX]             ;Obtiene el primer d¡gito
     CALL CONVERT_HEX_DIGIT
     JC   BAD_HEX             ;D¡gito hexadecimal err¢neo si el
                              ; indicador de acarreo est  activado
     MOV  CX,4                ;Multiplica por 16
     SHL  AL,CL
     MOV  AH,AL               ;Retiene una copia
     INC  BX                  ;Obtiene el segundo d¡gito
     MOV  AL,[BX]
     CALL CONVERT_HEX_DIGIT
 
     JC   BAD_HEX          ;D¡gito hexadecimal err¢neo si el indicador
                           ; de acarreo est  activado
     OR   AL,AH            ;Combina dos nibbles
     CLC                   ;Pone a 0 el indicador de acarreo si no hay error

DONE_HEX:
     POP  CX
     POP  BX
     RET

BAD_HEX:
     STC                   ;Activa el indicador de acarreo si hay error
     JMP  DONE_HEX
HEX_TO_BYTE    ENDP
 
     PUBLIC    READ_STRING
     EXTRN     WRITE_CHAR:PROC
     EXTRN     UPDATE_REAL_CURSOR:PROC
;-----------------------------------------------------------------------;
; Este procedimiento efect£a una funci¢n muy similar a la funci¢n 0Ah   ;
; del DOS. Pero esta funci¢n devuelve un car cter especial si se pulsa  ;
; una tecla de funci¢n o una tecla de flecha. Con estas teclas no es    ;
; necesario pulsar Intro. ESC borra lo que se ha tecleado y comienza de ;
; nuevo.                                                                ;
;                                                                       ;
;    DS:DX     Direcci¢n del buffer de teclado. El primer byte ha de    ;
;              contener el n£mero m ximo que hay que leer (m s uno      ;
;              para el retorno de carro). Y el segundo byte usa este    ;
;              procedimiento para devolver el n£mero de caracteres      ;
;              que realmente se han le¡do.                              ;
;              0       No se han le¡do caracteres.                      ;
;             -1       Se ha le¡do un car cter especial. En             ;
;                      caso contrario, el n£mero de caracteres          ;
;                      le¡dos (sin incluir la tecla Intro).             ;
;                                                                       ;
; Usa:         BACK_SPACE, WRITE_CHAR, READ_KEY                         ;
;-----------------------------------------------------------------------;
READ_STRING    PROC
     PUSH AX
     PUSH BX
     PUSH SI
     MOV  SI,DX               ;Usa SI como registro de ¡ndice y
START_OVER:
     CALL UPDATE_REAL_CURSOR  ;Pasa a la posici¢n del cursor virtual
     MOV  BX,2                ;BX para desplazamiento al comienzo del buffer
     CALL READ_KEY            ;Lee una tecla del teclado
     OR   AH,AH               ;¨Es el car cter ASCII ampliado?
     JNZ  EXTENDED            ;S¡, entonces lo procesa
STRING_NOT_EXTENDED:          ;Car cter ampliado es un error a no ser
                              ; que el buffer est‚ vac¡o
     CMP  AL,CR               ;¨Es ‚ste un retorno de carro?
     JE   END_INPUT           ;S¡, se ha terminado con la entrada
     CMP  AL,BS               ;¨Es ‚ste un car cter de retroceso?
     JNE  NOT_BS              ;No
 
     CALL BACK_SPACE         ;S¡, borra el car cter
     CMP  BL,2               ;¨Est  el buffer vac¡o?
     JE   START_OVER         ;S¡, ahora puede leer de nuevo ASCII ampliado
     JMP  SHORT READ_NEXT_CHAR ;No, contin£a leyendo caracteres normales
NOT_BS:   CMP  AL,ESCAPE     ;¨Es ‚ste un buffer ESC--purga?
     JE   PURGE_BUFFER       ;S¡, entonces purga el buffer
     CMP  BL,[SI]            ;Mira si el buffer est  lleno
     JA   BUFFER_FULL        ;El buffer est  lleno
     MOV  [SI+BX],AL         ;En caso contrario guarda el car cter en el
                              ;buffer
     INC  BX                  ;Apunta al siguiente car cter libre del buffer
     PUSH DX
     MOV  DL,AL               ;Escribe el car cter en la pantalla
     CALL WRITE_CHAR
     POP  DX
READ_NEXT_CHAR:
     CALL UPDATE_REAL_CURSOR  ;Mueve el cursor real al cursor virtual
     CALL READ_KEY
     OR   AH,AH               ;Un car cter ASCII ampliado no es v lido
                              ; cuando el buffer no est  vac¡o
     JZ   STRING_NOT_EXTENDED ;El car cter es v lido
 
;-----------------------------------------------;
; Avisa que se ha producido un error enviando   ;
; un car cter de pitido a la pantalla: chr$(7). ;
;-----------------------------------------------;
SIGNAL_ERROR:
     PUSH DX
     MOV  DL,7           ;Produce un pitido escribiendo chr$(7)
     MOV  AH,2
     INT  21h
     POP  DX
     JMP  SHORT READ_NEXT_CHAR     ;Ahora lee el siguiente car cter
 
;-----------------------------------------------;
; Vacia el buffer de caracteres y borra todos   ;
; los caracteres que aparecen en la pantalla.   ;
;-----------------------------------------------;
PURGE_BUFFER:
     PUSH CX
     MOV  CL,[SI]        ;Retrocede el m ximo n£mero de caracteres que
     XOR  CH,CH
PURGE_LOOP:              ; hay en el buffer.  BACK_SPACE
     CALL BACK_SPACE     ; evita que el cursor retroceda m s de lo
     LOOP PURGE_LOOP     ; permitido
     POP  CX
     JMP  START_OVER     ;Ahora puede leer caracteres ASCII ampliados
                         ; puesto que el buffer est  vac¡o
 
 
;-----------------------------------------------;
; El buffer estaba lleno, por tanto no puede    ;
; leer otro car cter. Env¡a un pitido para avi- ;
; sar al usuario de que el buffer est  lleno.   ;
;-----------------------------------------------;
BUFFER_FULL:
     JMP  SHORT SIGNAL_ERROR  ;Si el buffer est  lleno, pita
 
;-----------------------------------------------;
; Lee el c¢digo ASCII ampliado y lo pone en el  ;
; buffer como £nico car cter, despu‚s devuelve  ;
; -1 como n£mero de caracteres le¡dos.          ;
;-----------------------------------------------;
EXTENDED:                ;Lee un c¢digo ASCII ampliado
     MOV  [SI+2],AL      ;Pone este car cter en el buffer
     MOV  BL,0FFh        ;N£mero de caracteres le¡dos = -1 para especial
     JMP  SHORT END_STRING
 
;-----------------------------------------------;
; Salva el total del n£mero de caracteres       ;
; le¡dos y regresa.                             ;
;-----------------------------------------------;
END_INPUT:               ;Ha terminado la entrada de datos
     SUB  BL,2           ;N£mero total de caracteres le¡dos
END_STRING:
     MOV  [SI+1],BL      ;Devuelve el n£mero de caracteres le¡dos
     POP  SI
     POP  BX
     POP  AX
     RET
READ_STRING    ENDP
 
 
     PUBLIC    BACK_SPACE
     EXTRN     WRITE_CHAR:PROC
;-----------------------------------------------------------------------;
; Este procedimiento borra caracteres, de uno en uno, del teclado y de  ;
; la pantalla cuando el buffer no est  lleno. BACK_SPACE simplemente    ;
; regresa cuando el buffer est  vac¡o.                                  ;
;                                                                       ;
;    DS:SI+BX  El £ltimo car cter que est  en el buffer.                ;
;                                                                       ;
; Usa:         WRITE_CHAR                                               ;
;-----------------------------------------------------------------------;
BACK_SPACE     PROC      ;Borra un car cter
     PUSH AX
     PUSH DX
     CMP  BX,2           ;¨Est  vac¡o el buffer?
     JE   END_BS         ;S¡, lee el car cter siguiente
     DEC  BX             ;Borra un car cter del buffer
     MOV  AH,2           ;Borra un car cter de la pantalla
     MOV  DL,BS
     INT  21h
     MOV  DL,20h         ;Deja un espacio
     CALL WRITE_CHAR
     MOV  DL,BS          ;Retrocede de nuevo
     INT  21h
END_BS:   POP  DX
     POP  AX
     RET
BACK_SPACE     ENDP
 
     PUBLIC    READ_BYTE
;-----------------------------------------------------------------------;
; Este procedimiento lee o un solo car cter ASCII, o un n£mero hexade-  ;
; cimal. Esto es s¢lo una versi¢n de prueba de READ_BYTE.               ;
;                                                                       ;
; Devuelve bytes en    AL     C¢digo del car cter (a no ser que AH      ;
;                             sea igual a 0).                           ;
;                      AH     1 si se ha le¡do un car cter ASCII.       ;
;                             0 si no se han le¡do caracteres.          ;
;                            -1 si se ha le¡do una tecla especial.      ;
;                                                                       ;
; Usa:         HEX_TO_BYTE, STRING_TO_UPPER, READ_STRING                ;
; Lee:         KEYBOARD_INPUT, etc.                                     ;
; Escribe:     KEYBOARD_INPUT, etc.                                     ;
;-----------------------------------------------------------------------;
READ_BYTE PROC
     PUSH DX
     MOV  CHAR_NUM_LIMIT,3    ;S¢lo permite dos caracteres (m s Intro)
     LEA  DX,KEYBOARD_INPUT
     CALL READ_STRING
 
     CMP  NUM_CHARS_READ,1    ;Ver cu ntos caracteres
     JE   ASCII_INPUT         ;S¢lo uno, tratarlo como car cter ASCII
     JB   NO_CHARACTERS       ;S¢lo se ha pulsado la tecla Intro
     CMP  BYTE PTR NUM_CHARS_READ,0FFh  ;¨Tecla especial de funci¢n?
     JE   SPECIAL_KEY         ;S¡
     CALL STRING_TO_UPPER     ;No, convertir caracteres en may£sculas
     LEA  DX,CHARS            ;Direcci¢n de la secuencia de caracteres
                              ; que hay que convertir
     CALL HEX_TO_BYTE         ;Convertir secuencia de decimal a byte
     JC   NO_CHARACTERS       ;Error, por tanto devuelve 'no se han
                              ; le¡do caracteres'
     XOR  AH,AH               ;Se¤al de que se ha le¡do un car cter
DONE_READ:
     POP  DX
     RET
NO_CHARACTERS:
     XOR  AH,AH               ;Indica que no se ha le¡do ning£n car cter
     NOT  AH                  ;Devuelve -1 en AH
     JMP  DONE_READ
ASCII_INPUT:
     MOV  AL,CHARS            ;Carga el car cter que se ha le¡do
     XOR  AH,AH               ;Se¤al de que se ha le¡do un car cter
     JMP  DONE_READ
SPECIAL_KEY:
 
     MOV  AL,CHARS[0]         ;Devuelve el c¢digo de exploraci¢n
     MOV  AH,1                ;Indica una tecla especial con un 1
     JMP  DONE_READ
READ_BYTE ENDP
 
 
     PUBLIC    READ_KEY
;-----------------------------------------------------------------------;
; Este procedimiento lee una tecla del teclado.                         ;
;                                                                       ;
; Devuelve:    AL      C¢digo del car cter (a no ser que AH = 1)        ;
;              AH      0 si ha le¡do un car cter ASCII                  ;
;                      1 si ha le¡do una tecla especial                 ;
;-----------------------------------------------------------------------;
READ_KEY  PROC
     XOR  AH,AH               ;Llama a la funci¢n de lectura del teclado
     INT  16h                 ;Lee c¢digo de car cter/exploraci¢n del
                              ;teclado
     OR   AL,AL               ;¨Es ‚ste un c¢digo ampliado?
     JZ   EXTENDED_CODE       ;S¡
NOT_EXTENDED:
     XOR  AH,AH               ;Devuelve £nicamente el c¢digo ASCII
DONE_READING:
     RET
 
EXTENDED_CODE:
     MOV  AL,AH               ;Pone el c¢digo de exploraci¢n en AL
     MOV  AH,1                ;Indica c¢digo ampliado
     JMP  DONE_READING
READ_KEY  ENDP
 
 
     PUBLIC    READ_DECIMAL
;-----------------------------------------------------------------------;
; Este procedimiento coge el buffer de entrada de READ_STRING y         ;
; convierte la secuencia de d¡gitos decimales en una palabra.           ;
;                                                                       ;
;    AX   Palabra convertida de decimal.                                ;
;    CF   1 si hay error, 0 si no hay error.                            ;
;                                                                       ;
; Usa:         READ_STRING                                              ;
; Lee:         KEYBOARD_INPUT, etc.                                     ;
; Escribe:     KEYBOARD_INPUT, etc.                                     ;
;-----------------------------------------------------------------------;
READ_DECIMAL   PROC
     PUSH BX
     PUSH CX
     PUSH DX
     MOV  CHAR_NUM_LIMIT,6    ;El n£mero m ximo es cinco d¡gitos (65535)
     LEA  DX,KEYBOARD_INPUT
     CALL READ_STRING
     MOV  CL,NUM_CHARS_READ   ;Obtiene n£mero de caracteres le¡dos
     XOR  CH,CH               ;Pone a 0 el byte superior del contador
     CMP  CL,0                ;Devuelve error si no se han le¡do
                              ; caracteres
     JLE  BAD_DECIMAL_DIGIT   ;No se han le¡do caracteres, avisar
                              ; del error
     XOR  AX,AX               ;Comenzar con el n£mero a 0
     XOR  BX,BX               ;Comenzar al principio de la cadena
CONVERT_DIGIT:
     MOV  DX,10               ;Multiplicar el n£mero por 10
     MUL  DX                  ;Multiplicar AX por 10
     JC   BAD_DECIMAL_DIGIT   ;Indicador de acarreo si MUL ha producido
                              ; desbordamiento
     MOV  DL,CHARS[BX]        ;Obtiene el siguiente d¡gito
     SUB  DL,'0'              ;Y lo convierte en un nibble (4 d¡gitos)
     JS   BAD_DECIMAL_DIGIT   ;D¡gito err¢neo si < 0
     CMP  DL,9                ;¨Es un d¡gito err¢neo?
     JA   BAD_DECIMAL_DIGIT   ;S¡
     ADD  AX,DX               ;No, a¤adirle al n£mero
     INC  BX                  ;Apunta al siguiente car cter
     LOOP CONVERT_DIGIT       ;Obtiene el siguiente d¡gito
DONE_DECIMAL:
     POP  DX
     POP  CX
     POP  BX
     RET
BAD_DECIMAL_DIGIT:
     STC                 ;Activa el acarreo para indicar el error
     JMP  DONE_DECIMAL
READ_DECIMAL   ENDP
 
     END


