.286
.model huge, stdcall  ; meomory and calling convation
option casemap :none  ; case sensitive
include MyMacro.inc
QUEUE_MAX_BUFFER    EQU    4    ; 최대 큐 버퍼 사이즈
init_serial proto near
EnQueue proto :byte
DeQueue proto 
;-----------------------------------------------------------
SgIntVact   segment at 0
    org   30h
    V_Com1VactOfs   dw  ?
    V_Com1VactSeg   dw  ?
SgIntVact   ends
;-----------------------------------------------------------
SgBiosData  segment at 40h
    org   0
    V_Com1Port  dw  ?
    V_Com2Port  dw  ?
SgBiosData  ends
;-----------------------------------------------------------
data    segment
    V_OldComVactOfs dw  ?
    V_OldComVactSeg dw  ?
    V_ComDataPort       dw  ?
    V_ComIntEnablePort  dw  ?
    V_ComIntStatPort    dw  ?
    V_ComLineCtrlPort   dw  ?
    V_ComModemCtrlPort  dw  ?
    V_ComLineStatPort   dw  ?
    V_ComModemStatPort  dw  ?
    V_InputChar db  ?
    
    Queue    db  4 dup(0)    ; 실제 큐
    QueueSize    db  0        ; 큐 사용량
    TempPos    db  0        ; 가상 인덱스
    Position    db  0        ; 실제 인덱스
data    ends
stackseg segment stack
        db      100h    dup('stack')
stackseg ends
;-----------------------------------------------------------
code    segment
  ;-----------------------------------------------------------
  main  proc  far
        invoke init_serial  ;serial port initialize
        ;set segment
        assume  cs:code,ds:data
        mov   ax , data
        mov   ds , ax
        ;input loop
        mov   V_InputChar , 0
        .while  V_InputChar != '@'
          mov   ah , 06h
          mov   dl , 0FFh
          int   21h
          .if !zero?
            mov   V_InputChar , al
            ;send to serial
            mov   dx , V_ComDataPort
            out   dx , al
          .endif
      
      ;큐에서 꺼내 출력하기
      .if QueueSize == 4
        mov cx, 4
          CharLoop:
        invoke DeQueue
        mov ah, 2
        int 21h
        loop CharLoop
      .endif
        .endw
        ;serial off
        cli
        in    al , 21h    ;interupt off
        or    al , 10h
        out   21h , al
        ;restore interupt vactor
        assume  es:SgIntVact
        mov   ax , SgIntVact
        mov   es , ax
        mov   ax , V_OldComVactOfs
        mov   cx , V_OldComVactSeg
        mov   V_Com1VactOfs , ax
        mov   V_Com1VactSeg , cx
        sti
        ;serial reset
        mov   dx , V_ComIntEnablePort
        xor   ax , ax
        out   dx , al
        mov   dx , V_ComModemCtrlPort
        out   dx , al
        ;exist
        mov   ah , 4Ch
        int   21h
  main  endp
  ;-----------------------------------------------------------
  init_serial proc near
        ;com 1 initialize
        ;set interupt handler
        ;set segment
        assume  cs:code,ds:data,es:SgIntVact
        mov   ax , data
        mov   cx , SgIntVact
        mov   ds , ax
        mov   es , cx
        ;backup old interupt vactor
        mov   ax , V_Com1VactOfs
        mov   cx , V_Com1VactSeg
        mov   V_OldComVactOfs , ax
        mov   V_OldComVactOfs , cx
        ;set interupt vactor
        cli
        mov   ax , offset Irq_Serial
        mov   cx , cs
        mov   V_Com1VactOfs , ax
        mov   V_Com1VactSeg , cx
        ;set interupt mask
        in    al , 21h
        and   al , 0EFh
        out   21h , al
        sti
        ;serial prot initialize
        assume  es:SgBiosData   ;segment set
        mov   ax , SgBiosData
        mov   es , ax
        ;set com port set
        mov   ax , V_Com1Port
        mov   bx , offset V_ComDataPort
        mov   cx , 7
        .repeat
          mov   word ptr ds:[bx] , ax
          add   bx , 2    ;next register pointer
          inc   ax        ;next port address
        .untilcxz
        ;set boud rate , parrity , data length , stop bit
        mov   dx , V_ComLineCtrlPort
        mov   al , 83h    ;boud rate setting and none parrity ,
        out   dx , al     ;1 stop bit , 8bit data
        mov   dx , V_ComDataPort
        mov   al , 0Ch    ;9600 bps
        out   dx , al
        mov   dx , V_ComIntEnablePort
        xor   ax , ax
        out   dx , al
        ;set interupt
        mov   dx , V_ComLineCtrlPort
        mov   al , 3
        out   dx , al   ;data io mode
        mov   dx , V_ComIntEnablePort
        mov   al , 1    ; data receved only
        out   dx , al
        ;set  modem controler
        mov   dx , V_ComModemCtrlPort
        mov   al , 1011b    ;enable interupt and RTS,DTR
        out   dx , al
        ret
  init_serial endp
  ;-----------------------------------------------------------
  Irq_Serial  proc far
        ;protect register
        push  ax
        push  dx
        push  ds
        ;set segment
        assume  ds:data
        mov   ax , data
        mov   ds , ax
        ;when recive do set input charactor
        mov   dx , V_ComIntStatPort
        in    al , dx
        .if al & 100b    ;interupt generate and recive data?
          ;set data
          mov   dx , V_ComDataPort
          in    al , dx
          invoke EnQueue, al
        .endif
        ;set EOI
        mov   al , 20h
        out   20h , al
        ;restore register
        pop   ds
        pop   dx
        pop   ax
        iret
  Irq_Serial  endp
  EnQueue PROC MyValue:BYTE
    pusha
    
    assume ds:data        
    mov ax, data        ;
    mov ds, ax            ; 데이터 세그먼트 설정
    cmp QueueSize, QUEUE_MAX_BUFFER    ; 현재버퍼크기 >= 최대버퍼크기
    jge LABEL_BufferFull        ; 버퍼가 꽉 찻으므로 종료
    cmp Position, QUEUE_MAX_BUFFER    ; 
    jl  LABEL_PositionNext        ; 데이터인덱스 < 최대버퍼크기
    mov Position, 0            ; Postion = 0
    LABEL_PositionNext:
    mov ah, Position        ;---------------------------------- 
    mov al, QueueSize        ; TempPos = Position + QueueSize
    add ah, al            ;
    mov TempPos, ah            ;----------------------------------
    cmp TempPos, QUEUE_MAX_BUFFER    ;
    jl  LABEL_TempPosChange        ; TempPos < 최대버퍼크기
    sub TempPos, QUEUE_MAX_BUFFER    ; TempPos = TempPos - 최대버퍼크기
    LABEL_TempPosChange:
    mov ah, MyValue            ;----------------------------------         
    xor bx, bx            ; 지정된 위치에 데이터 넣기
    mov bl, byte ptr TempPos    ;
    mov Queue[bx], ah        ;----------------------------------
        
    inc QueueSize            ; 현재버퍼크기 1 증가
    LABEL_Exit:
    
    popa
    ret
    LABEL_BufferFull:
        
    ;버퍼가 가득 차면 현재큐내용을 보여주고 초기화
    ShowAllQueueDebugString
    mov QueueSize, 0
    mov TempPos, 0
    mov Position, 0
    NextLine
            ;
        
    popa
    ret
    EnQueue ENDP
    ;-------------------------------------------------------------------
    ;
    ;    큐에서 값 하나를 꺼낸다.
    ;    
    ;    인자 :
    ;    리턴 : DL -> 큐에서 꺼낸 값
    ;
    ;-------------------------------------------------------------------
    DeQueue PROC 
    push ax                ;-----------------------------
    push ds                ; 레지스터 보호
    push bx                ; ax, ds, bx, si, cx
    push si                ;
    push cx                ;-----------------------------
        
    assume ds:data            
    mov ax, data        ;
    mov ds, ax            ; 데이터 세그먼트 초기화
        
    cmp QueueSize, 0        ;
    jz  LABEL_BufferEmpty        ; 큐에 내용이 없으면 Buffer UnderFlow
    xor bx, bx            ;
    mov bl, byte ptr Position    ; 가져 올 값의 인덱스 구하기 -> BL
                
    
        
    mov dl, Queue[bx]        ; 구한 인덱스릐 큐 값을 DL에 넣는다. -> DL
    mov Queue[bx], 0        ; 값을 빼고 빈자리에 'x'를 넣는다.
    inc Position            ; 인덱스를 다음 값의 위치로 셋팅
    dec QueueSize            ; 큐 사이즈를 하나 줄인다.
    
    pop cx                ;---------------------------------
    pop si                ; 레지스터 복원
    pop bx                ; cx, si, bx, ds, ax
    pop ds                ;
    pop ax                ;---------------------------------
                
    ret
    LABEL_BufferEmpty:
    
        
    ShowAllQueueDebugString
    mov QueueSize, 0
    mov TempPos, 0
    mov Position, 0
    NextLine
    pop cx                ;---------------------------------
    pop si                ; 레지스터 복원
    pop bx                ; cx, si, bx, ds, ax
    pop ds                ;
    pop ax                ;---------------------------------
                
    ret
    DeQueue ENDP    
;-----------------------------------------------------------
code    ends
;-----------------------------------------------------------
        end   main