
;                            FILE COUNTER2.ASM
;
;              Sample Program Demonstrating Setup and Use of
;              a Timer and Counter Using Timers 0 and 1.
;              Timer 0 is used as a Timer in Mode 1 to generate
;              pulses on Port 1.0.  Timer 1 is used as a counter
;              to count the number of pulses in a fixed time
;              interval.
;              THIS PROGRAM MODIFIES COUNTER1 TO DISPLAY RESULT
;                      DIRECTLY AS A FREQUENCY IN HZ.
;
;                     Written By S.C.T.  1/24/99
             

  $MOD52                        ; Include 8052 Definitions
  
  SendChar      EQU     3F06h   ; Entry Point of Monitor Routine
  SendStr       EQU     3F09h   ; Entry Point of Monitor Routine

  TimerLow      EQU     0FEh    ; Setup Timer to Gate Counter for 
  TimerHigh     EQU     4Bh     ; a 50 millisecond window
                                ; (NOTE-We are not correcting for
                                ; jump routine latency so this is
                                ; an approximate value)

  CounterLow    EQU     00h     ; Reset Counter Count Value 
  CounterHigh   EQU     00h

  LeadZero      BIT     00h     ; Define Bit Variable For Leading Zero


  ORG  4000h                    ; Start Program at SDK Code Space Base

  MOV   SP,#7Fh                 ; Move Stack to Upper RAM (8052 ONLY)
  CLR   EA                      ; Disable Global Interrupts
  MOV   TMOD, #51h              ; Set Timer 0 in Mode 1 as a Timer
                                ; and Timer 1 in Mode 1 as a Counter
  SETB  P3.5                    ; Set Timer 1 input
  CLR   LeadZero                ; Clear Leading Zero Display Bit

startmainloop:
  MOV   TL0, #TimerLow          ; Load Timer 0 for 1 millisecond pulses
  MOV   TH0, #TimerHigh

  MOV   TL1, #CounterLow        ; Load Counter setup Value
  MOV   TH1, #CounterHigh

  CLR   TF0                     ; Clear Timer 0 Overflow flag
  CLR   TF1                     ; Clear Timer 1 Overflow flag

  SETB  TR0                     ; Start Timer 0
  SETB  TR1                     ; Start Counter

timerloop1:
  JB    TF0,measurecount        ; Loop Until Timeout
  JMP   timerloop1              ; repeat loop until bit set

measurecount:
  CLR   TR1                     ; Stop Count
  CLR   TR0                     ; Stop Timer
  CLR   TF0                     ; Clear Timeout Flag
  JB    TF1,countoverflow       ; Jump if Counter Overflowed


; --------------  DISPLAY THE MEASURED RESULTS --------------

  MOV   DPTR,#CRLF              ; Send a CR/LF
  CALL  SENDSTR

; - NOTE - To convert to Hz, we need to multiply by 20.  We
; take a shortcut by multiplying by 2 then adding a trailing
; zero at the end of the decimal result when we display it.

  MOV   A,TL1                   ; Move Count Low Byte to A
  RLC   A                       ; Multiply by 2
  MOV   R3,A                    ; Store in R3
  MOV   A,TH1                   ; Move Count High Byte to A
  RLC   A                       ; Multiply High byte by 2
  MOV   R2,A                    ; Store in R2

; The count result is now stored in R2/R3

; Divide by 10,000d
  MOV   R4, #27h                ; Load 10,000d as Divisor
  MOV   R5, #10h                
  CALL  DIVIDE                  ; Call Divide Subroutine

; Result is in R2,R3 (R2 = 0)

  MOV   A,R3                    ; Get 10,000's place value
  JZ    Thousands               ; Jump to Next Place if Zero
  SETB  LeadZero                ; Not a Leading Zero
  CALL  bcd2ascii               ; Send the byte out

; Divide by 1000d

Thousands:
  MOV   A,R4                    ; Move Remainder to Dividend
  MOV   R2,A
  MOV   A,R5
  MOV   R3,A

  MOV   R4,#03h                 ; Move 1000d to Divisor
  MOV   R5,#0E8h
  CALL  DIVIDE                  ; Call Divide Subroutine

; Result is in R2,R3 (R2 = 0)

  MOV   A,R3                    ; Get 1000's place value
  JNZ   Cont1000s               ; Test for leading zero
  JNB   LeadZero,Hundreds       ; Jump to Hundreds if not lead zero

Cont1000s:
  SETB  LeadZero                ; Not a Leading Zero
  CALL  bcd2ascii               ; Send the byte out

; Divide by 100d

Hundreds:
  MOV   A,R4                    ; Move Remainder to Dividend
  MOV   R2,A
  MOV   A,R5
  MOV   R3,A

  MOV   R4,#00h                 ; Move 100d to Divisor
  MOV   R5,#64h
  CALL  DIVIDE                  ; Call Divide Subroutine

; Result is in R2,R3 (R2 = 0)

  MOV   A,R3                    ; Get 100's place value
  JNZ   Cont100s                ; Test for leading zero
  JNB   LeadZero,Tens           ; Jump to Tens if not lead zero

Cont100s:
  SETB  LeadZero                ; Not a Leading Zero
  CALL  bcd2ascii               ; Send the byte out

; Divide by 10d

Tens:
  MOV   A,R4                    ; Move Remainder to Dividend
  MOV   R2,A
  MOV   A,R5
  MOV   R3,A

  MOV   R4,#00h                 ; Move 10d to Divisor
  MOV   R5,#0Ah
  CALL  DIVIDE                  ; Call Divide Subroutine

; Result is in R2,R3 (R2 = 0)

  MOV   A,R3                    ; Get 10's place value
  JNZ   Cont10s                 ; Test for leading zero
  JNB   LeadZero,Ones           ; Jump to Ones if not lead zero

Cont10s:
  CALL  bcd2ascii               ; Send the byte out

; Get 1's Place Remainder
Ones:
  MOV   A,R5
  CALL  bcd2ascii               ; Send the byte out

  MOV   A,#00h                  ; Send out a trailing zero
  CALL  bcd2ascii

  MOV   A,#20h                  ; Send out a Space Char
  CALL  SENDCHAR

  MOV   A,#48h                  ; Send out Hz
  CALL  SENDCHAR
  MOV   A,#7Ah
  CALL  SENDCHAR

  CLR   LeadZero                ; Clear the Lead Zero Bit

  CALL  waitabit                ; Call Time Delay Routine

  JMP   startmainloop           ; Repeat Main Loop Forever

countoverflow:
  MOV   DPTR,#OVMessage         ; Point to Overflow Message
  CALL  SENDSTR                 ; Print It
  JMP   startmainloop           ; Go Back to Main Loop


;  *******************   BCD2ASCII Routine   *********************
;  This routine converts an unpacked BCD value in the accumulator 
;  to ASCII then sends it out using the monitor sendchar routine
;  ***************************************************************

bcd2ascii:
  ANL  A,#0Fh                   ; Mask out LSB
  MOV  DPTR,#BCDTable           ; Point to Conversion Table
  MOVC A,@A+DPTR                ; Retrieve Byte
  CALL SENDCHAR                 ; Send First Character

  RET                           ; Return to Calling Code
; --------------------------------------------------------


;  *******************    WAITABIT Routine   *******************
;  This routine uses a nested loop to generate a time delay
;  *************************************************************

WaitABit:
  MOV   R0,#04h
delay1:
  MOV   R1,#0FFh
delay2:
  MOV   R2,#0FFh
delay3:
  DJNZ  R2,delay3
  DJNZ  R1,delay2
  DJNZ  R0,delay1

  RET                   ; Return to Calling Code
; -----------------------------------------------------------

;***********************************************************************
;               SUBROUTINE DIVIDE
;  Description:
;       Unsigned Divide Of R2/3 By R4/5
;
;  Entry Requirements:
;       Divisor In R4/5
;       Dividend In R2/3
;
;  On Exit:
;       Quotient In R2/3
;       Remainder In R4/5
;
;  Affected:
;       PSW.CY, PSW.Z, PSW.P, Acc, B, R0, R1, R2, R3, R4, R5
;
;  Stack:
;       X Bytes, Not Including Space Used By Called Routines

DIVIDE:          

  CLR   A
  MOV   B,A                     ; Initialize count
  MOV   R0,A                    ; Zero quotient
  MOV   R1,A
  MOV   A,R2                    ; Check for zero dividend
  ORL   A,R3
  JZ    l?p82
  MOV   A,R4                    ; Check for zero divisor
  ORL   A,R5
  JNZ   l?p32
  RET

l?p12:            
  MOV   A,R2
  CLR   C
  SUBB  A,R4                    ; Is divisor greater than dividend yet
  JC    l?p42                   ; yes, go no further
  JNZ   l?p22
  MOV   A,R3
  SUBB  A,R5
  JC    l?p42

l?p22:            
  MOV   A,R5                    ; Shift divisor up one bit
  CLR   C
  RLC   A
  MOV   R5,A
  MOV   A,R4
  RLC   A
  MOV   R4,A
 
l?p32:            
  INC   B                       ; Increment count
  MOV   A,R4                    ; Check for safe to shift some more
  JNB   ACC.7,l?p12             ; Loop if top bit clear

l?p42:            
  MOV   A,R2
  CLR   C
  SUBB  A,R4                    ; Is divisor greater than dividend
  JC    l?p52    
  JNZ   l?p62
  MOV   A,R3
  SUBB  A,R5
  JNC   l?p62

l?p52:            
  CLR   C
  SJMP  l?p72

l?p62:            
  CLR   C                       ; Subtract divisor from dividend
  MOV   A,R3                    ; now set bit for quotient
  SUBB  A,R5
  MOV   R3,A
  MOV   A,R2
  SUBB  A,R4
  MOV   R2,A
  SETB  C                       

l?p72:            
  MOV   A,R1
  RLC   A
  MOV   R1,A
  MOV   A,R0
  RLC   A
  MOV   R0,A

  MOV   A,R4                    ; Shift divisor down and continue
  CLR   C                       ; with the rest
  RRC   A
  MOV   R4,A
  MOV   A,R5
  RRC   A
  MOV   R5,A
  DJNZ  B,l?p42                 

l?p82:            
  MOV   5,R3
  MOV   4,R2
  MOV   2,R0
  MOV   3,R1
  RET                           ; Return to Calling Code



; ******************  TEXT AND LOOKUP TABLES ****************

bcdtable:                       ; BCD2ASCII Lookup Table
  DB    30h,31h,32h,33h,34h,35h,36h,37h,38h,39h

OVMessage:
  DB    0Dh,0Ah,' Data Counter Overflow ',0Dh,0Ah,'$'

CRLF:
  DB    0Dh,0Ah,'$'

  END
