;---------------------------------------;
; RC224ATF single chip modem interface  ;
;---------------------------------------;
; First we need to initialize the 8051
;
; Timer 0 will generate a 200mS (5Hz) internal interrupt
; to be used as a general timer for any purpose, ex: check
; and generate timeout for no modem answers, etc.
;
; Suppose we use a crystal 3.6864 MHz, so to calculate
; Timer 0 and Timer 1 (baud rate for serial port), we use
; the following formula for 2400 bps:
;
; Timer 1 baud rate:   100h - (Clock/16/12/Speed)
;                      100h - 3686400/16/12/2400
;                      256 - 8 = 248 or 0F8h
;                      so, TH1 and TL1 = 0F8h
;
; Original Philips formula for mode1: 
;
;                                    2 x Osc.Freq
;                      TH1 = 256 -  ---------------
;                                   384 x baud rate
;
;               or 
;
;                                     2 x Osc.Freq
;                      baud rate = ---------------------
;                                  32 x 12 x (256 - TH1)  
;
;
; *** for the formulas above we consider SMOD bit set at PCON.
;
; Timer 0 internal 200ms interrupt:
;                      10000h - (Clock/12)/Rate
;                       65536 - (3686400/12)/5Hz
;                       65536 - 61440 = 4096 or (1000h)
;                       so, TH0 = 10h and TL0 = 00h
;
;
; Timer1 will work in 8+8 bit auto reload,
; so, TMOD ..1. ...1 bits must on
; so, TCON .1.1 ...1 bits must on
; so, SCON .1.1 ..1. bits must on
;
; At power on reset, all interrupts are off, so we need to activate
; Timer 0 interrupt for the 200ms internal clock with the command
;
;            Setb  ET0    ; Activate Timer0 Interrupt
;
; But to deal with this 200ms interrupt we need an interrupt
; routine at code address 000Bh, so we need to have this:
;
;            ORG 000Bh
;            Jmp Inter0
;
; So, at every 200ms, this interrupt will launch the program
; counter to run at address 000B, and the Jump instruction will
; run the routine Inter0 that will ...
;
; When the RX pin receive a complete character it will fill the
; SBUF with that data, and it can generate an interrupt.
; If the interrupt for the serial port is active, the program
; counter will jump to the address 0023h, that is the serial
; interrupt address. At that address we will have another jump
; to our reception routine:
;
;            ORG 0023h
;            Jmp InterS
;
; So, the InterS routine will just get the byte from SBUF and
; reset the interrupt flag to be able to interrupt again at
; the next received byte.
;
; We will use this routine just for the first time we talk
; to the modem chip, after that, we will use "polling" to
; check if there is another received byte at SBUF.
;
; You can change this routine and receive everything based on
; interruptions.  The reason why we need to use interrupt at
; the first received byte is that it can happens very fast
; and our polling routine would not be able to get it before
; arrives the second byte, so we lost the first one that its
; important.
;
; Well, let's start some code here:
;

ES           BIT  0ACh    ;  IE.4  Serial Port Interrupt Enable
                          ;  Your assembler can complain and ask you
                          ;  to remove the ES designation for bit,
                          ;  since it can be already defined.

             ORG  0000h                ;
             Jmp  Start                ;
                                       ;
             ORG  000Bh                ;
             Jmp  Inter0               ;
                                       ;
             ORG  0023h                ;
             Jmp  InterS               ;
                                       ;
START:       Mov  SP,#050h             ; Stack Pointer to 50h, or any other.
                                       ;
             Orl  PCON,#80h            ; Set SMOD bit, necessary to the baud rate formula
                                       ;
             Mov  TH0,#010h            ; timer0 for 200ms
             Mov  TL0,#000h            ;
                                       ;
             Mov  TH1,#0F8h            ; baud rate for serial port at 2400bps
             Mov  TL1,#0F8h            ;
                                       ;
             ; Defining Timer0 and Timer1 modes
                                       ;
             Mov  TMOD,#021h           ; Timer1 = 8 bits autoloader, Timer0 = 16 bits 
             Mov  TCON,#051h           ; Run both timers 0 and 1
             Mov  SCON,#052h           ; 8 bit uart (mode1), Enable RX, Set TX Int Flag 
             CLR  ET0                  ; deactivate Timer0 interrupt
             CLR  ET1                  ; deactivate Timer1 interrupt
             CLR  EX0                  ; deactivate external T0 interrupt
             CLR  EX1                  ; deactivate external T1 interrupt
             Setb EA                   ; Set gate to All active Interrupts
             Setb ET0                  ; Activate Timer0 Interrupt 200ms
                                       ;
                                       ; Wake up modem if not awake
                                       ;
             Mov  R4,#0                ; R4 will be used at serial interrupt
             Setb ES                   ; Activate Serial Interrupt
                                       ;
             Mov  Dptr,#ATH            ; Send "+++" to modem to return to
             Call Code2Modem           ; command mode, if in data mode.
             Call Tim200ms             ;
                                       ;
             Mov  Dptr,#ATZ            ; Send "ATZ" to Software Reset Modem.
             Call Code2Modem           ;
             Call Tim200ms             ;
                                       ;
             Mov  Dptr,#MdmInit        ; Send "ATE0V0" to modem.
             Call Code2Modem           ;
             Call Tim200ms             ;
                                       ;
             Mov  Dptr,#MdmInit1       ; Send "ATX4E0Q0V0S11.... to modem.
             Call Code2Modem           ;
             Call Tim200ms             ;
                                       ;
             Clr  ES                   ; Deactivate Serial Interrupt
             Mov  A,R4                 ; Modem Should Answer letter "K"
             Cjne A,#'K',ModemError    ; If R4 is not "K", modem did not
                                       ; answered the commands ATX4E0Q0....
                                       ; and it must be dead. It should answer.
                                       ; In any case, check R4 contents.
                                       ; ModemError Routine needs to be done
                                       ; to signal modem error in some way.
                                       ;
                                       ; After this point, the modem will
                                       ; only answer numeric messages,
                                       ; and for every command you sent to
                                       ; the modem, if accepted and processed
                                       ; ok, it will answer "0" (30h).
                                       ;
             Mov  Dptr,#DIALFONE       ; Here Modem is Ok and will DIAL
             Call Code2Modem           ; Send "ATDT....number"  to modem
             ;
             ; Modem will dial and contact remote modem, they will do a
             ; handshake (noises) to stablish the communication speed
             ; between both modems. After they do that, your modem will
             ; answer a numeric byte to you know it, and both modems will
             ; switch do data mode, leaving command mode.
             ;
             ; In the data mode, everything you send to the modem, will
             ; be transmited to the other modem and it will deliver to
             ; the DTE (data terminal equipment) connected to that modem.
             ;
             ; If you need to send commands to your modem again, not data,
             ; you need to make your modem return to command mode again.
             ;
             ; To do that, wait at least 30ms, then send "+++" and wait
             ; again at least 30ms, it is almost sure that your modem
             ; will understand that and switch back to command mode,
             ; so you can send again AT commands to the modem again.
             ;
             ; This will be necessary when you want to disconnect your
             ; connection, modem release phone line, and so on.
             ;
                                       ;
             Call ModemIn              ; Wait modem answer
             Cjne A,#'1',Start1        ; answer "1" (31h) = connected 2400bps
             ;....                     ; show it in any way if necessary.
Start1:      Cjne A,#'5',Start2        ; answer "5" (35h) = connected 1200bps
             ;....                     ; show it in any way if necessary.
                                       ;
Start2:      ;....                     ; here your software goes transmitting
             ;....                     ; and receiving data as required.
             ;....                     ;
                                       ;
Code2Modem:  Clr  A                    ;
             Movc A,@A+Dptr            ; Get byte pointed by Dptr
             Inc  Dptr                 ; point to next byte
             Jnz  $+3                  ; if not zero skip the Ret instruction
             Ret                       ; Return to Caller if byte = zero.
             Call ModemOut             ; Send Data to Modem
             Jmp  Code2Modem           ; Go back for other bytes until zero.
                                       ;
ModemOut:    Jnb  TI,$                 ; Wait while TX buffer is not empty
             Clr  TI                   ; Clear Flag for next time
             Mov  SBUF,A               ; Move data to be transmited immediately
             Ret                       ; Return to Caller
                                       ;
ModemIn:     Jnb  RI,$                 ; Wait until RX Data Available
             Mov  A,SBUF               ; Get received byte
             Clr  RI                   ; Clear Data Available flag
             Ret                       ; Return to Caller
                                       ;
InterS:      Clr  A                    ;
             Jnb  RI,Inters9           ; If not data received, return A=0
             Mov  A,SBUF               ; get received data
             Mov  R4,A                 ; Save R4 with modem data
             Clr  RI                   ; Clear Receive Interrupt Flag
Inters9:     Reti                      ; Return from Interrupt
                                       ;
                                       ;
Inter0:      Mov  TH0,#010h            ; Reload Timer0 Values
             Mov  TL0,#000h            ;
             Push Acc                  ;
             Inc  R5                   ; R5 is a 100ms counter
                                       ; Do any other function
             Reti                      ; Return from Interrupt


ATH:         db  '++++',0
ATZ:         db  'ATZ'13,,0
MDMINIT:     db  'ATE0V0'13,,0
MDMINIT1:    db  'ATX4E0Q0V0S11=40S12=0S7=15'13,0

DIALFONE:    db  'ATDT 123 4567',13,0  ; <--- change number here


; Time routines.  You can change R5, R6 and R7 by any other variables
; or addresses in the internal ram memory.
;--------------------------------------;
Tim200ms:    Mov  R7,#20               ; R7 x 10ms = 200ms
             Jmp  Tim10ms              ; Call 10ms Routine & Return to Caller
Tim1000ms:   Mov  R7,#100              ;
Tim10ms:     Mov  R6,#216              ; Clock 3686400 generates this
Tim10msA:    Mov  R5,#6                ; loop routine to waste aprox 10ms
Tim10msB:    Djnz R6,$                 ; times the R7 contents.
             Djnz R5,Tim10msB          ;
             Djnz R7,Tim10msA          ; To wait 100ms, load R7 with 0Ah
             Ret                       ; and call this routine.




