home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1992 / 06 / i2c.asc < prev    next >
Text File  |  1992-05-18  |  24KB  |  676 lines

  1. _PROGRAMMING THE I2C INTERFACE_
  2. by Mitchell Kahn
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. $pagelength (30)
  8. $mod186
  9. $debug
  10. $xref
  11.  
  12. NAME     i2c_transmit;
  13.  
  14. $include (\include\pcp_io.inc)
  15.  
  16. PUBLIC i2c_xmit
  17.  
  18. ;******   EQUates  ******    
  19. BUS_FREE_MIN            EQU    2       ; Loop counter for free bus delay.
  20. MAXIMUM_MESSAGE_LEN     EQU     255
  21.  
  22. CODE_ILLEGAL_ADDR       EQU     020H
  23. CODE_MSG_LEN            EQU     040H
  24.  
  25. ;****** STACK FRAME STRUCTURE ******
  26. stack_frame     STRUC
  27. ret_ip          DW      ?
  28. ret_cs          DW      ?
  29. buffer_offset   DW      ?
  30. buffer_segment  DW      ?
  31. count           DW      ?
  32. address         DW      ?
  33. stack_frame     ENDS
  34.  
  35. %*DEFINE(Drive_SCL_Low)(
  36.         mov     dx, P2LTCH
  37.         in      al, dx
  38.         and     al, 10111111B           ; SCL is bit 6
  39.         out     dx, al
  40.         )
  41. %*DEFINE(Release_SCL_High)(
  42.         mov     dx, P2LTCH
  43.         in      al, dx
  44.         or      al, 01000000B
  45.         out     dx, al
  46.         )
  47. %*DEFINE(Drive_SDA_Low)(
  48.         mov     dx, P2LTCH
  49.         in      al, dx
  50.         and     al, 01111111B           ; SDA is bit 6
  51.         out     dx, al
  52.         )
  53. %*DEFINE(Release_SDA_High)(
  54.         mov     dx, P2LTCH
  55.         in      al, dx
  56.         or      al, 10000000B
  57.         out     dx, al
  58.         )
  59. %*DEFINE(Wait_4_7_uS)(
  60.         mov     cx, 5
  61.         loop    $
  62.         nop
  63.         nop
  64.         )
  65. %*DEFINE(Wait_Half_Bit_Time)(
  66.         mov     cx, 3
  67.         loop    $
  68.         )
  69. %*DEFINE(Wait_SCL_Low_Time)(
  70.         mov     cx, 5
  71.         loop    $
  72.         nop
  73.         nop
  74.         )
  75. %*DEFINE(Wait_SCL_High_Time)(
  76.         mov     cx, 5
  77.         loop    $
  78.         nop
  79.         nop
  80.         )
  81. %*DEFINE(Wait_For_SCL_To_Go_Low)LOCAL wait(
  82.         mov     dx, P2PIN
  83. %wait:  in      al, dx
  84.         test    al, 01000000B
  85.         jne     %wait
  86.         )
  87. %*DEFINE(Wait_For_SCL_To_Go_High)LOCAL wait(
  88.         mov     dx, P2PIN
  89. %wait:  in      al, dx
  90.         test    al, 01000000B
  91.         je      %wait
  92. %*DEFINE(Wait_For_SDA_To_Go_High)LOCAL wait(
  93.         mov     dx, P2PIN
  94. %wait:  in      al, dx
  95.         test    al, 10000000B
  96.         je      %wait
  97.         )
  98.         )
  99. %*DEFINE(Get_SDA_Bit)(
  100.         mov     dx, P2PIN
  101.         in      al, dx
  102.         and     al, 0080H
  103.         )
  104. %*DEFINE(Check_For_Bus_Free)(
  105.         mov    dx, P2PIN
  106.     in    al, dx
  107.         mov     bl, 0C0H         ; Mask for SCL and SDA.
  108.         and     al, bl           ; If SCL and SDA are high
  109.         xor     al, bl           ; this sequence will leave a zero in AX.
  110.         )
  111.  
  112. ;*****************************************************************************
  113. ;**  Revision History: 0.0 (7/90): First frozen working verion. No slave wait 
  114. ;**    timeout. No arbitration turn around.  Inefficient register usage. 
  115. ;**    0.1 (7/16/90): 8-bit registers used (improves  80C188EB. Use STRUCT for
  116. ;**    stack frame clarity. Implements slave wait timeout. Saves ES.  
  117. ;*****************************************************************************
  118.  
  119. ;*****************************************************************
  120. ;**                     Procedure I2C_XMIT                      **
  121. ;**             Call Type:      FAR                             **
  122. ;**             Uses     :      All regs.                       **
  123. ;**             Saves    :      DS and ES only.                 **
  124. ;**             Stack Frame:                                    **
  125. ;**             [bp]=   ip                                      **
  126. ;**             [bp+2]= cs                                      **
  127. ;**             [bp+4]= message offset                          **
  128. ;**             [bp+6]= message segment                         **
  129. ;**             [bp+8]= message count                           **
  130. ;**             [bp+10]= slave adress                           **
  131. ;**             Return Codes in AX register:                    **
  132. ;**             XX00 = Transmisiion completed without error     **
  133. ;**             XX01 = Bus unavailable                          **
  134. ;**             XX02 = Addressed slave not responding           **
  135. ;**             nn04 = Addressed slave aborted during xfer      **
  136. ;**                    (nn= number of bytes transferred before  **
  137. ;**                     transfer aborted)                       **
  138. ;**             XX08 = Arbitration loss (note 1)                **
  139. ;**             XX10 = Bus wait timeout                         **
  140. ;**             XX20 = Illegal address                          **
  141. ;**             XX40 = Illegal message count                    **
  142. ;**             note 1: Arbitration loss requires that the      **
  143. ;**                     I2C unit switch to slave receive        **
  144. ;**                     mode.  This is not implemented.         **
  145. ;*****************************************************************
  146.  
  147. code        segment  public
  148.                 assume cs:code
  149. i2c_xmit    proc    far
  150.                 mov     bp, sp
  151.                 push    ds
  152.                 push    es
  153.                 test    word ptr [bp].address,01H       ; Check for illegal
  154.                                                         ; address (a READ).
  155.                 jz      addr_ok
  156.                 mov     ax, CODE_ILLEGAL_ADDR           ; Illegal addr
  157.                 pop     es
  158.                 pop     ds
  159.                 ret     8                       ; Tear down stack frame
  160. addr_ok:    
  161.                 mov    cx, [bp].count          ; Get message length.
  162.                 cmp     cx, MAXIMUM_MESSAGE_LEN
  163.                 jle     message_len_ok          ; Message is 256 or less
  164.                                                 ; characters.
  165.                 mov     ax, CODE_MSG_LEN        ; Bad length return code.
  166.                 pop     es
  167.                 pop     ds
  168.                 ret     8
  169. message_len_ok:
  170.                 mov     si, [bp].buffer_offset  ; Get message offset.
  171.                 mov     ax, [bp].buffer_segment ; Get message segment
  172.                 mov     ds, ax                  ; and put in DS.
  173.         ; Test for I2C bus free condition.
  174.                 ; SCL and SDA must be high at least 4.7uS
  175.                 mov     cx, BUS_FREE_MIN        ; initialize free time counter.
  176.                 
  177.                 ; The following loop takes 48 clocks while cx>1 and 33 clocks 
  178.                 ; on the last iteration. To insure that bus is free, samples 
  179.                 ; of bus must span at least 4.7uS. At 16Mhz: 48*(62.5ns)=3uS 
  180.                 ; The first sample is at 0us, the second at 3us, and the 
  181.                 ; third will be at 6.  Although this exceeds the 4.7us
  182.                 ; spec, it is better safe than sorry.
  183. bus_free_wait:
  184.                 %Check_For_Bus_Free
  185.                 jz     i2c_bus_free
  186.                 ; At this point the bus is not available.
  187.                 mov     ax, 01H                 ; 01= return code for
  188.                 pop     es                      ; a busy bus.
  189.                 pop     ds
  190.                 ret     8                       ; return and tear down
  191.                                                 ; stack frame.
  192. i2c_bus_free:   loop    bus_free_wait        ; bus may be free but wait 
  193.                                         ; the 4.7uS required!
  194.                 ; I2C bus is available, generate a START condition
  195.                 %Drive_SDA_Low
  196.                 %Wait_4_7_uS
  197.                 mov     ax, [bp].address
  198.                 xchg    ah, al                  ; ah = address
  199. next_byte:    mov    di, 8            ; set up bit counter
  200. next_bit:    %Drive_SCL_Low
  201.                 %Wait_Half_Bit_Time        
  202.         mov    bl, ah          ; get current data
  203.         and    bl, 080H        ; strip MSB
  204.         mov    dx, P2LTCH
  205.         in    al, dx
  206.         and    al, 7fh
  207.         or    al, bl            ; set bit 7 to reflect
  208.                         ; data bit
  209.         out    dx, al            ; xmit data bit
  210.                 %Wait_Half_Bit_Time
  211.                 %Release_SCL_High
  212.                 %Wait_For_SCL_To_Go_High
  213.  
  214.                 ; At this point SCL is high so if there is another master 
  215.                 ; attempting to gain the bus, it's data would be valid here.  
  216.                 ; We need only check when our data is "1"...
  217.  
  218.                 test    bl, 80H            ; Is data a "1"?
  219.                 jz      won_arbitration    ; If not -> don't check arbitration.
  220.  
  221.                 mov     dx, P2PIN
  222.                 in      al, dx
  223.                 test    al, 80H                 ; Is SDA high?
  224.                 jnz     won_arbitration
  225.                 jmp     lost_arbitration        ; If SDA != 1 then we lost
  226.                                                 ; arbitration....
  227. won_arbitration:
  228.                 %Wait_SCL_High_Time
  229.         shl    ah, 1                   ; shift current byte
  230.                 dec    di            ; tick down bit counter
  231.         jne    next_bit        ; continue bits
  232. ; a byte has been completed.  Time to get an ACKNOWLEDGE.
  233.                 %Drive_SCL_Low
  234.                 %Wait_Half_Bit_Time
  235.                 %Release_SDA_High
  236.                 %Wait_Half_Bit_Time
  237.                 %Release_SCL_High
  238.                 %Wait_For_SCL_TO_Go_High
  239.                 ; SCL is now high.  We must loop while checking SDA for 4.7us.  
  240.                 ; With a count of 3 we have a delay of 89 clocks (5.5uS). This 
  241.                 ; could be find tuned with NOPs when performance is critical.
  242.                 mov     cx, 3
  243. check_4_ack:
  244.                 %Get_SDA_Bit                    ; Is SDA a "0"
  245.         jnz    abort_no_ack        ; if so -> abort
  246.         loop    check_4_ack 
  247. ; if we've gotten to here, then an acknowledge was received.
  248.                 mov     ah, byte ptr [si]
  249.         inc    si            ; point to next byte
  250.                 dec    word ptr [bp].count    ; dec string counter
  251.         js      xfer_done
  252.                 jmp    next_byte
  253. ; END OF MESSAGE: Issue a STOP condition
  254. xfer_done:      
  255.                 mov     di, 0                   ; Normal completion code.
  256.                 jmp     i2c_bus_stop
  257. abort_no_ack:
  258.                 cmp     si, [bp].buffer_offset  ; Check if this is the
  259.                 je      slave_did_not_respond   ; first byte (the address ).
  260.                 mov     di, 4H                  ; Abort during xfer code.
  261.                 jmp     i2c_bus_stop
  262. slave_did_not_respond:
  263.                 mov     di, 02H                 ;
  264. i2c_bus_stop:
  265.                 %Drive_SCL_Low
  266.                 %Wait_Half_Bit_Time
  267.         %Drive_SDA_Low
  268.             %Wait_4_7_uS
  269.                 %Release_SCL_High
  270.                 %Wait_For_SCL_To_Go_High
  271.                 %Wait_4_7_uS
  272.                 %Release_SDA_High
  273.                 %Wait_For_SDA_To_Go_High
  274.  
  275.                 mov     ax, di
  276.                 pop     es
  277.                 pop     ds
  278.                 ret     8                       ; Return and tear
  279.                                                 ; down stack frame.
  280. lost_arbitration:
  281.                 mov     dx, P2LTCH
  282.                 in      al, dx                  ; Release SDA and SCL
  283.                 or      al, 0C0H
  284.                 out     dx, al 
  285.                 mov     ax, 08H                 ; Lost arbitration code.
  286.                 pop     es
  287.                 pop     ds
  288.                 ret     8
  289. i2c_xmit    endp
  290. code         ends
  291. end
  292.  
  293.  
  294. [LISTING TWO]
  295.  
  296. $pagelength (30)
  297. $mod186
  298. $debug
  299. $xref
  300.  
  301. NAME    i2c_receive;
  302.  
  303. $include (/include/pcp_io.inc)
  304.  
  305. PUBLIC i2c_recv
  306.  
  307. ;****** EQUates ******
  308. BUS_FREE_MIN     EQU     1H             ; Loop counter for free bus delay.
  309. MAXLEN           EQU     255
  310.  
  311. ;****** STACK FRAME STRUCTURE  ******
  312. stack_frame     STRUC
  313. ret_ip          DW      ?
  314. ret_cs          DW      ?
  315. buffer_offset   DW      ?
  316. buffer_segment  DW      ?
  317. count           DW      ?
  318. address         DW      ?
  319. stack_frame     ENDS
  320.  
  321. %*DEFINE(Drive_SCL_Low)(
  322.         mov     dx, P2LTCH
  323.         in      al, dx
  324.         and     al, 10111111B           ; SCL is bit 6
  325.         out     dx, al
  326.         )
  327. %*DEFINE(Release_SCL_High)(
  328.         mov     dx, P2LTCH
  329.         in      al, dx
  330.         or      al, 01000000B
  331.         out     dx, al
  332.         )
  333. %*DEFINE(Drive_SDA_Low)(
  334.         mov     dx, P2LTCH
  335.         in      al, dx
  336.         and     al, 01111111B           ; SDA is bit 6
  337.         out     dx, al
  338.         )
  339. %*DEFINE(Release_SDA_High)(
  340.         mov     dx, P2LTCH
  341.         in      al, dx
  342.         or      al, 10000000B
  343.         out     dx, al
  344.         )
  345. %*DEFINE(Wait_4_7_uS)(
  346.         mov     cx, 5
  347.         loop    $
  348.         nop
  349.         nop
  350.         )
  351. %*DEFINE(Wait_Half_Bit_Time)(
  352.         mov     cx, 3
  353.         loop    $
  354.         )
  355. %*DEFINE(Wait_SCL_Low_Time)(
  356.         mov     cx, 5
  357.         loop    $
  358.         nop
  359.         nop
  360.         )
  361. %*DEFINE(Wait_SCL_High_Time)(
  362.         mov     cx, 5
  363.         loop    $
  364.         nop
  365.         nop
  366.         )
  367. %*DEFINE(Wait_For_SCL_To_Go_Low)LOCAL wait(
  368.         mov     dx, P2PIN
  369. %wait:  in      al, dx
  370.         test    al, 01000000B
  371.         jne     %wait
  372.         )
  373. %*DEFINE(Wait_For_SCL_To_Go_High)LOCAL wait(
  374.         mov     dx, P2PIN
  375. %wait:  in      al, dx
  376.         test    al, 01000000B
  377.         je      %wait
  378. %*DEFINE(Wait_For_SDA_To_Go_High)LOCAL wait(
  379.         mov     dx, P2PIN
  380. %wait:  in      al, dx
  381.         test    al, 10000000B
  382.         je      %wait
  383.         )
  384.         )
  385. %*DEFINE(Get_SDA_Bit)(
  386.         mov     dx, P2PIN
  387.         in      al, dx
  388.         and     al, 0080H
  389.         )
  390. %*DEFINE(Check_For_Bus_Free)(
  391.         mov dx, P2PIN
  392.     in  al, dx
  393.         mov     bl, 0C0H                ; Mask for SCL and SDA.
  394.         and     al, bl                  ; If SCL and SDA are high
  395.         xor     al, bl                  ; this sequence will leave
  396.         )                               ; a zero in AX.
  397. code    segment  public
  398.         assume cs:code
  399.  
  400. i2c_recv    proc    far
  401.  
  402.               ; The LSB of the address for a READ always has a "1" in the LSB.
  403.               ; The first step is to check for a legal address....
  404.                 mov     bp, sp
  405.                 push    ds
  406.                 push    es
  407.                 test    word ptr [bp].address,01H    ; Check for illegal
  408.                                                      ; address (an XMIT).
  409.                 jnz     addr_ok
  410.                 ; The address passed was for a transmit (WRITE). This is 
  411.                 ; illegal in this procedure....    
  412.                 mov     ax, 20H                 ; Illegal addr
  413.                 pop     es
  414.                 pop     ds
  415.                 ret     8                       ; Tear down stack frame
  416. addr_ok: 
  417.                 cmp     word ptr [bp].count, MAXLEN
  418.                 jg      message_wrong_len
  419.                 cmp     word ptr [bp].count, 1  ; check message length
  420.                 jge     len_ok
  421. message_wrong_len:
  422.                 mov     ax, 40H                 ; error code
  423.                 pop     es
  424.                 pop     ds
  425.                 ret     8                       ; tear down frame
  426. len_ok:
  427.                 ; Test for I2C bus free condition.
  428.                 ; SCL and SDA must be high at least 4.7uS
  429.                 mov     cx, BUS_FREE_MIN        ; initialize free time counter.
  430. ; Following loop takes 48 clocks while cx>1 and 33 clocks on last iteration. 
  431. ; To insure that bus is free, samples of bus must span at least 4.7uS. At 16Mhz
  432. ; 48*(62.5ns)= 3uS. First sample is at 0us, second at 3us, and third will be at
  433. ; 6. Although this exceeds 4.7us spec, it is better safe than sorry.
  434. bus_free_wait:
  435.                 %Check_For_Bus_Free
  436.                 jz     i2c_bus_free
  437.                 ; At this point the bus is not available.
  438.                 mov     ax, 01H                 ; 01= return code for
  439.                 pop     es                      ; a busy bus.
  440.                 pop     ds
  441.                 ret     8             ; return and tear down stack frame.
  442. i2c_bus_free:   loop    bus_free_wait ; bus may be free but wait 4.7uS required
  443.                 ; I2C bus is available, generate a START condition
  444.                 %Drive_SDA_Low
  445.                 %Wait_4_7_uS
  446.                 ; A receive begins with transmission of the ADDRESS
  447.             mov di, 8           ; set up bit counter
  448. next_bit:   
  449.         %Drive_SCL_Low
  450.         %Wait_Half_Bit_Time
  451.                 mov     bx, [bp].address
  452.         and bl, 080H        ; strip MSB
  453.         mov dx, P2LTCH
  454.         in  al, dx
  455.         and al, 7fh
  456.         or  al, bl          ; set bit 7 to reflect data bit
  457.         out dx, al          ; xmit data bit
  458.         sal [bp].address,1          ; shift current byte
  459.                 %Wait_Half_Bit_Time
  460.                 %Release_SCL_High
  461.                 %Wait_For_SCL_To_Go_High
  462.                 ; At this point SCL is high so if there is another master 
  463.                 ; attempting to gain the bus, it's data would be valid here. 
  464.                 ; We need only check when our data is a "1"...
  465.                 test    bl, 10000000B      ; Is data a "1"?
  466.                 je      won_arbitration    ; If not -> don't check arbitration.
  467.                 mov     dx, P2PIN
  468.                 in      al, dx
  469.                 test    al, 10000000B           ; Is SDA high?
  470.                 jnz     won_arbitration
  471.                 jmp     lost_arbitration
  472. won_arbitration:
  473.                 %Wait_4_7_uS                ; count off high time.
  474.         dec di          ; tick down bit counter
  475.         jne next_bit        ; continue bits
  476. ; The address has been completed.  Time to get an ACKNOWLEDGE.
  477.                 %Drive_SCL_Low
  478.                 %Wait_Half_Bit_Time
  479.                 %Release_SDA_High
  480.                 %Wait_Half_Bit_Time
  481.                 %Release_SCL_High
  482. ; Here we are expecting to see an acknowledge from addressed slave receiver:
  483.         %Wait_For_SCL_To_Go_High        ; a wait state
  484.         mov cx, 3
  485. check_4_ack:
  486.         mov dx, P2PIN
  487.         in  al, dx          ; get SDA value
  488.         and al, 10000000B       ; is it high?
  489.         jnz abort_no_ack        ; if so -> abort
  490.         nop
  491.         nop
  492.         nop                             ; NOPs for timing at 16Mhz
  493.                 loop    check_4_ack 
  494. ; if we've gotten to here, then an acknowledge was received.
  495. ; At this point in the code, slave receiver has acknowledged
  496. ; receipt of its address. SCL has just been driven low, SDA is floating.
  497.                 jmp     start_recv
  498. abort_no_ack:
  499.                 %Drive_SCL_Low
  500.                 mov     di, 02H                 ; Code for unresponsive slave.
  501.                 jmp     i2c_bus_stop
  502. ; Now the master transmitter switches to master receiver....
  503. start_recv:
  504.                 mov     di, [bp].buffer_offset
  505.                 mov     ax, [bp].buffer_segment
  506.                 mov     es, ax
  507. next_byte_r:    mov     bx, 0
  508.                 mov     si, 8
  509. next_bit_r:    
  510.                 %Drive_SCL_Low
  511.                 %Wait_4_7_uS
  512.                 %Release_SCL_High
  513.                 %Wait_For_SCL_To_Go_High
  514.                 %Get_SDA_Bit
  515.                 shr     al, 7                   ; move SDA value to LSB
  516.                 or      bl, al                  ; drop in lsb of bl
  517.                 %Wait_4_7_uS
  518.         dec si              ; tick down bit counter
  519.         je  byte_Recv_comp      ; continue bits
  520.                 shl     bl, 1                   ; shift bl for next bit
  521.                 jmp     next_bit_r
  522. ; The word has been completed.  Time to send an ACKNOWLEDGE.
  523. byte_Recv_comp:
  524.                 mov     al, bl
  525.                 stosb
  526.         %Drive_SCL_Low
  527.                 %Wait_Half_Bit_Time
  528. ; Here we need to decide whether or not to transmit an acknowledge. If this is 
  529. ; last byte required from slave, we do not send an ack; otherwise we do....
  530.                 dec     [bp].count              ; decrement the message count
  531.                 cmp     [bp].count, 0
  532.                 jne     send_ack
  533.                 %Release_SDA_High
  534.                 jmp     do_ack
  535. send_ack:       %Drive_SDA_Low
  536.  
  537. do_ack:     
  538.         %Wait_Half_Bit_Time
  539.                 %Release_SCL_High
  540.         %Wait_For_SCL_To_Go_High
  541.                 %Wait_4_7_uS
  542.                 %Drive_SCL_Low
  543.                 %Wait_Half_Bit_Time
  544.                 %Release_SDA_High
  545.                 cmp     [bp].count, 0
  546.                 je      recv_done
  547.                 jmp     next_byte_r
  548. recv_done:      mov     di, 00
  549.  
  550. i2c_bus_stop:
  551.                 %Wait_Half_Bit_Time
  552.         %Drive_SDA_Low
  553.             %Wait_4_7_uS
  554.                 %Release_SCL_High
  555.                 %Wait_For_SCL_To_Go_High
  556.                 %Wait_4_7_uS
  557.                 %Release_SDA_High
  558.                 %Wait_For_SDA_To_Go_High
  559.                 mov     ax, di
  560.                 pop     es
  561.                 pop     ds
  562.                 ret     8              ; Return and tear down stack frame.
  563. lost_arbitration:
  564.                 mov     dx, P2LTCH
  565.                 in      al, dx                  ; Release SDA and SCL
  566.                 or      al, 0C0H
  567.                 out     dx, al 
  568.                 pop     es
  569.                 pop     ds
  570.                 ret     8
  571. i2c_recv    endp
  572. code        ends
  573. end
  574.  
  575.  
  576.  
  577. [LISTING THREE]
  578.  
  579. $mod186        
  580. $debug
  581. $xref
  582.  
  583. $include (\include\pcp_io.inc)  ; a file of EQUates for 186EB register names
  584.  
  585. NAME    i2c_example
  586. EXTRN   i2c_recv:far, i2c_xmit:far
  587.  
  588. %*DEFINE(XMIT(ADDR,COUNT,MESSAGE))(
  589.         push    %ADDR
  590.         push    %COUNT
  591.         push    seg %MESSAGE
  592.         push    offset %MESSAGE
  593.         call    i2c_xmit
  594.         )
  595. %*DEFINE(RECV(ADDR,COUNT,BUFFER))(
  596.         push    %ADDR
  597.         push    %COUNT
  598.         push    seg %BUFFER
  599.         push    offset %BUFFER
  600.         call    i2c_recv
  601.         )
  602. stack   segment stack
  603.         DW      20 DUP (?)
  604.  t_o_s  DW      0
  605. stack   ends
  606. data    segment para public 'RAM'
  607. bus_msg     db  00h,77h,01h,02h,04h,08h  ; the LED I2C message 
  608. recv_buff       db      255 dup(?)
  609. data            ends
  610. usr_code        segment para 'RAM'
  611.                 assume cs:usr_code
  612. start:          mov     ax, data                ; data segment init
  613.                 mov     ds, ax
  614.                 cli
  615.                 assume ds:data
  616.                 mov     ax, stack               ; set up stack
  617.                 mov     ss, ax
  618.                 assume ss:stack
  619.                 mov     sp, offset t_o_s
  620.                 mov     dx, P2DIR               ; set up open-drain 
  621.                 in      ax, dx                  ; port pins on 186EB
  622.                 and     ax, 3FH         
  623.                 out     dx, ax
  624.                 mov     dx, P2CON
  625.                 in      ax, dx
  626.                 and     ax, 03FH
  627.                 out     dx, ax
  628.                 ; The I2C address of the LED driver is 70H for a transmit.
  629.                 %XMIT(70H,6,bus_msg)            ; send "bus" message
  630.                 ; The address for the clock is 0xA3 for a receive.
  631.                 %RECV(0A3H,15,recv_buff)   ; read first 15 bytes in clock chip.
  632. usr_code        ends
  633.                 end     start
  634.  
  635.  
  636.  
  637.  
  638.  
  639. Example 1: (a) 80C186 implementation of 4.7uS wait macro; (b) 80960CA 
  640. implementation of 4.7uS wait macro.
  641.  
  642.  
  643. (a)
  644.  
  645.  
  646. %*DEFINE(Wait_4_7_uS)(
  647.         mov     cx, 5           ; 4 clocks 
  648.         loop    $               ; 4*15+5 = 65 clocks
  649.         nop                     ; 3 clocks
  650.         nop                     ; 3 clocks
  651.                                 ; total = 75 clocks
  652.                                 ; 75 * 62.5ns = 4.69uS (close enough)
  653.         )
  654.  
  655.  
  656. (b)
  657.  
  658.  
  659. define(Wait_4_7_uS,'            
  660.         
  661.         lda     0x17, r4        # instruction may be issued in parallel
  662.                                 # so assume no clocks. 
  663. 0b:     cmpdeco 0, r4           # compare and decrement counter in r4
  664.         bne.t   0b              # if !=0 branch back (predict taken
  665.                                 # branch)
  666.                                 #
  667.                                 # The cmpdeco and bne.t together take 3
  668.                                 # clocks in parallel minimum.
  669.                                 #
  670.                                 # 0x17 (25 decimal) * 3 = 75 clocks
  671.                                 # at 16MHz this is 4.69uS
  672. ')
  673.  
  674.  
  675.  
  676.