Purpose: accept keyboard input and print messages on the printer 
  ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  ;eg8-6.asm
  ;Purpose: accept keyboard input and print messages on the printer
  ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *


     .model tiny
;-------------------------------------------------------------------------
     .stack
;-------------------------------------------------------------------------
     .data
  old_ip09   dw ?
  old_cs09    dw ?
  old_ip0f   dw ?
  old_cs0f    dw ?
  count     dw ?
  buffer     db 20h dup(' ')
  buf_p     dw ?
  start_msg   db 0ah, 0dh, 'RUN', 0ah, 0dh, '$'
  end_msg    db 0ah, 0dh, 'END', 0ah, 0dh, '$'
  full_msg    db 0ah, 0dh, 'Buffer full!$'

;-------------------------------------------------------------------------
     .code
; Main   program
  main    proc far
start:
  mov    ax, @data           ;ds<=data segment
  mov    ds, ax

; initialize
  lea    ax, buffer          ;buf_p<=buffer address
  mov    buf_p, ax
  mov    count, 0           ;count<=0

; save old interrupt 09h
  mov    al, 09h            ;al<=vector#
  mov    ah, 35h            ;to get interrupt vector
  int    21h              ;call DOS
  mov    old_cs09, es          ;save registers for restore
  mov    old_ip09, bx
  push    ds               ;save ds


;set new interrupt 09h
  lea    dx, kbdint           ;dx<=offset of procedure kbdint
  mov    ax, seg kbdint        ;ax<=segment of procedure kbdint
  mov    ds, ax            ;ds<=ax
  mov    al, 09h            ;al<=intrrupt number
  mov    ah, 25h            ;to set interrupt vector
  int    21h              ;call DOS
  pop    ds               ;restore ds

;set keyboard interrupt mask bits
  in     al, 21h
  and    al, 0fdh
  out    21h, al

;save old interrupt 0fh
  mov    al, 0fh            ;al<=vector#
  mov    ah, 35h            ;to get interrupt vector
  int    21h               ;call DOS
  mov    old_cs0f, es          ;save registers for restore
  mov    old_ip0f, bx
  push    ds               ;save ds

;set new interrupt 0fh
  lea    dx, prtint           ;dx<=offset of procedure prtint
  mov    ax, seg prtint         ;ax<=segment of procedure prtint
  mov    ds, ax            ;ds<=ax
  mov    al, 0fh            ;al<=vector#
 
  mov    ah, 25h            ;to set interrupt vector
  int    21h              ;call DOS
  pop    ds              ;restore ds

  mov    ah, 09h            ;print start message
  lea    dx, start_msg
  int    21h
  
  sti
  mov    di,20000            ;main process
mainp: mov   si,30000
mainp1:dec  si
  jnz    mainp1
  dec    di
  jnz    mainp

  mov    ah, 09h            ;print end msg of main process
  lea    dx, end_msg
  int    21h

  cli
;restore old interrupt 09h
  push    ds              ;save ds
  mov    dx, old_ip09          ;ds:dx<=old handler address
  mov    ax, old_cs09
  mov    ds, ax
  mov    al, 09h            ;al<=vector#
  mov    ah, 25h            ;to restore interrupt vector
  int    21h              ;call DOS
  pop    ds               ;restore ds

; restore old interrupt 0fh
  push   ds               ;save ds
  mov    dx, old_ip0f          ;ds:dx<=old address
  mov    ax, old_cs0f
  mov    ds, ax
  mov    al, 0fh            ;al<=vector#
  mov    ah, 25h            ;to restore interrupt
  int    21h              ;call DOS
  pop    ds              ;restore ds

; enable keyboard interrupt
  in    al, 21h
  and    al, 0fdh

; enable keyboard interrupt
  in     al, 21h
  and    al, 0fdh
  out    21h, al

  sti
  mov   ax, 4c00h
  int   21h
main endp


;-----------------------------------------------------
; Interrupt Handler kbd
;  Purpose: fill buffer until full when substituted for interrupt 09h
  kbdint proc near
  push   ax            ; save registers

  push   bx

  cld                ;direction: forward
  in    al, 60h         ;read a character
  push   ax            ;save it
  in    al, 61h          ;get the control port
  mov    ah, al          ;save the value in ah
  or    al, 80h          ;reset bits for kbd
  out    61h, al         ;send out
  xchg   ah, al          ;restore control value
  out    61h, al          ;kdb has been reset

  pop   ax            ;restore scan code
  test   al, 80h          ;press or release?
  jnz    return1         ;ignore when release

  mov    bx, buf_p        ;bx<=buffer pointer
  mov    [bx], al         ;store in buffer
  call   display_hex        ;display in hex
  inc    bx            ;move pointer
  inc    count          ;count characters
  mov    buf_p, bx        ;save the pointer

check:
  cmp    count, 20h ;judge whether full
  jb    return1
  in    al, 21h
  or    al, 02          ;mask kdb bits
  and   al, 7fh         ;enable prt bits
  out   21h, al
  call   init_prt         ;initiate printer

return1:
  cli
  mov   al, 20h         ;end of interrupt
  out    20h, al

;restore registers
  pop   bx
  pop   ax
  iret               ;interrupt return
kbdint endp

;------------------------------------------------------------------------

; Interrupt Handler prtint
; Purpose: print characters when substituted for interrupt 0fh
  prtint proc near
  push   ax            ;save registers
  push   bx
  push   dx

  mov    bx, buf_p        ;bx<=buffer pointer
  mov    al, [bx]         ;get from buffer
  mov    dx, 378h         ;printer data port
  out    dx, al          ;output a character

  push   ax            ;save ax
  mov    dx, 37ah         ;printer control port
  mov    al, 1dh          ;al<=control code
  out    dx, al          ;send out
  jmp    $+2           ;slight delay
  mov    al, 1ch          ;al<=control code
  out    dx, al          ;send out
  pop    ax            ;restore ax

  inc    bx            ;move pointer
  mov    buf_p, bx        ;save the pointer
  cmp    al, 0ah         ;end of message?
  jnz    return2
  in     al, 21h         ;disable printer
  or    al, 80h         ; interrupt
  out    21h, al

return2:
  mov    al, 20h         ;end of interrupt
  out    20h, al

  pop    dx            ;restore registers
  pop    bx
  pop    ax
  iret                ;interrupt return
prtint endp

;------------------------------------------------------------------------

; Procedure init_prt
  init_prt proc near
  push    ax            ;save registers
  push   bx
  push   dx

  cli
  lea    bx, full_msg       ;bx<=offset of full_msg
  mov    buf_p, bx         ;save full_msg address

  mov    dx, 378h         ;printer data port
  mov    al, 0dh         ;CR
  out    dx, al          ;output a character

  mov    dx, 37ah         ;printer control port
  mov    al, 1dh         ;al<=control code
  out    dx, al          ;send out
  jmp    $+2            ;slight delay
  mov    al, 1ch          ;al<=control code
  out    dx, al          ;send out

  pop    dx            ;restore registers
  pop    bx
  pop    ax

  ret
init_prt endp
;------------------------------------------------------------------------


display_hex proc   near       ;display char with hex
    push     ax 
    push     cx
    push     dx
    mov     ch, 2       ;number of digits
    mov      cl, 4
nextb:
    rol      al, cl      ;highest of bits to lowest
    push     ax
    mov     dl, al
    and     dl, 0fh     ;mask off left digit

   or      dl, 30h     ;convert to ASCII
    cmp     dl, 3ah
    jl      dispit
    add      dl,7h       ;digit is A to F

dispit:
    mov      ah, 2       ;display character
    int     21h
    pop     ax
    dec      ch        ;done 2 digits?
    jnz      nextb      ;not yet
    mov      ah, 2
    mov     dl,','      ;display ','
    int      21h
    pop      dx
    pop     cx
    pop      ax
    ret              ;return from display_hex
display_hex endp

;------------------------------------------------------------------------
    end start