I2C Monitor

Many years ago I was experimenting with the I2C bus and associated chips. Over a two line serial bus, you could control many chips with lots and lots of functions. To name just a few:

Luckily I only had the chips and a few good handbooks. No prior knowledge and no dedicated software. We are talking the DOS era.
Philips may have produced some kind of software, but it would have been so costly that I was no prepared to shell it out.

This forced me to create a program such that I could learn to use the I2C chips to the full, while developing that same program. The program was called the I2C monitor. It ran off an LPT port and it showed the I2C bus in real time. On the right is a screenshot of the program. It runs in DosBox on my Slackware 14.0 computer. Hence I cannot (as such) control the associated LPT port bits...

You'll have to take my word for it that it worked.


MANUAL mode

In fact, it worked so good that only 10 people on the world used it. Me, some friends and 5 field engineers at the german branch of MicroChip Inc. Yes, the creator of the IC processors. The field engineers normally worked with their million dollar DSO's but occasionally ran into serious trouble and their expensive machines could not give the right clues.

For these special cases, the german engineers had a small plastic case with the electronics and a floppy disk with this software. And when they sent the customer to get some coffee they started my interface/software and could start using this Monitor. It has a lot of functions for trouble shooters. To mention some:

ESC returns to command mode.

SEEK mode

When you press the "S" key, SEEK mode is started. The monitor now scans the bus to see which addresses return an ACK. When an ACK is received the monitor sets a tick in the cyan coloured table you can see in the picture on the right.

SEEK mode is especially handy to find out

Just using SEEK mode already makes a big difference. When the connected chips show up at the right addresses, this means that the hardware is OK. And if the hardwired I2C address was wrong the SEEK table shows it immediately. ESC returns to command mode.


Change LPT port

By prssing the P key you could change the number of the LPT port and hence the related I/O port addresses. ESC returns to command mode.


CHIP mode

Here you could enter the kind of memory chip at which address. SO the software could lookup how big the memory was. ESC returns to command mode.


The source code

Below is the full source code for the monitor. You can download a ZIP file with everything inside it that you may need. The source is in A86 format.

title    I2C experimenteer programma met IO support
page     80, 120

; version 07   : added SEEK function, File I/O          OK: 30-01-1998
; version 07a  : measures INT line via Timer Tick.      OK: 02-02-1998
; version 07b  : fixed bug in CHIP function             OK: 11-02-1998
;              : added GERMAN dialogs                   OK: 10-03-1998
;              : fixed bug in INT-line checking         OK: 10-03-1998

DEBUG   = 1             ; are we testing?
PAYWARE = 1             ; commercial version?
ENGLISH = 1             ; English dialogs?
GERMAN  = 0             ; German language?

SDA    = 0              ; I2C Serial DAta
SCL    = 1              ; I2C Serial CLock

stdout = 1              ; console output

tab    = 9              ; horizontal TAB
lf     = 10             ; line feed
cr     = 13             ; carriage return
escape = 27

seek_chr = ''          ; character and it's ...
seek_col = 03F          ; ... colour in I2C present detect

DEBUG_IO = 0278         ; standard I/O port while debugging
DEBUG_LP = 2            ; standard LPTx while debugging

#IF ENGLISH              ; determine first letter of acknowledgement
  OK_chr = 'Y'
#ELSE
  OK_chr = 'J'
#ENDIF

window_information_block STRUC
  win_size        dw  ?       ; number of bytes in window
  win_topleft_x   dw  ?       ; top left coordinates
  win_topleft_y   dw  ?
  win_width       dw  ?       ; width in chars (including boxmarks)
  win_height      dw  ?       ; height
  win_box_attrib  db  ?       ; attributes of boxmarks
  win_txt_attrib  db  ?       ; and of windowtext
  win_box_border  dw  ?       ; pointer to box information table
  win_text        dw  ?       ; address of text for this window
ENDS

justification_data  STRUC     ; in Menu-balk: kleur van de 
  normal    db  ?             ; ... normale letters,
  shortcut  db  ?             ; ... hoofdletters,
  activity  db  ?             ; ... actief menu         (not yet in use)
ENDS

input_0A STRUC          ; Int 021/0A input buffer
  max    db  ?
  count  db  ?
  tekst  db  ?
ENDS

ranges STRUC            ; range qualifiers
  from   dw  ?
  until  dw  ?
ENDS

venster MACRO                   ; set up window information structure
    dw   #4*#3*2, #1, #2, #3, #4
    db   #5, #6
    dw   #7, #8
    #EM

clr MACRO                       ; zero argument
    xor  #1, #1
    #EM

set_x MACRO                     ; set X-coordinate (horizontal)
    mov  #1, [bx.win_topleft_x]
    add  #1, #2
    mov  [scr_x], #1
    #EM

set_y MACRO                     ; set Y-coordinate (vertical)
    mov  #1, [bx.win_topleft_y]
    add  #1, #2
    mov  [scr_y], #1
    #EM

rd_text MACRO                   ; BUFFER, LENGTH
    mov  w [#1], #2             ; set length
    mov  dx, offset #1
    mov  ah, 0A
    int  021                    ; use Int 021/010 for text input
    #EM

  DATA SEGMENT                  ; space for volatile data

         even
v_mode     dw  ?        ; video mode
v_segm     dw  ?        ; video-RAM segment
v_page     dw  ?        ; active screen page
v_lines    dw  ?        ; lines per screen
v_cols     dw  ?        ; columns per screen
lin_len    dw  ?        ; line length in BYTES, not characters!
scr_mul    dw  ?        ; use THIS multiplication routine
p_size     dw  ?        ; page size
line_24    dw  ?        ; address of last line
scr_x      dw  ?        ; current X-position
scr_y      dw  ?        ; current Y-position

rd_num     dw  ?        ; number of bytes for I2C read operation
digits     dw  ?        ; precision of digit conversion routines
b_digit    dw  ?        ; number of binary digits to show
h_digit    dw  ?        ; number of hex-digits to show
handle     dw  ?        ; handle for currently open file
old_timer  dd  ?        ; old pointer to INT 01C

PROM_size  dw  ?        ; words per device
word_size  dw  ?        ; bits per word
page_size  dw  ?        ; words per cache page
wr_speed   dw  ?        ; delay after (page) write

n_count    dw  ?        ; length of n_buffer
r_count    dw  ?        ; length of r_buffer

LPT_port   dw  ?        ; current LPT port address
LPT_number dw  ?        ; current LPT port number [1..4]
LPT_total  dw  ?        ; total number of LPT adapters in this system

FLAGS      dw  ?

comment  ^    bit    means                          bit    means                  
                              
               0     Illegal hex digit               8     Parsing error
               1     Exit to DOS requested           9     NO refresh
               2     0: read mode, 1: write mode    10     
               3     IO-mode file open              11     
               4     CFG-file open                  12     
               5     Names match                    13     
               6     CFG-file empty                 14     File specified____
               7     CFG-file not present           15     Destination = FILE / I2C
         ^

SCL_count  dw  ?        ; counts negative SCL pulses
delay      dw  ?        ; calibrate wait-loop
que_ptr    dw  ?        ; pointer into ACTion queue
store_si   dw  ?        ; storage for SI register
burn       dw  ?, ?     ; program FROM and UNTIL (in chip)
range      dw  ?, ?     ; get data from file in range FROM / UNTIL

scratchpad dw  32 dup (?)       ; general purpose notepad

number_que dw  128 dup (?)      ; buffer to store commands
scr_attr   db  ?, ?             ; colour for printing

buffer1    db  80 dup (?)
buffer2    db  80 dup (?)
n_buffer   db  16 dup (?)       ; name of device
r_buffer   db  80 dup (?)       ; receive buffer
dec_buffer db  6  dup (?)       ; buffer for HEX --> ASCII xlation
data_port  db  ?                ; contents of [Base+0]
read_port  db  ?                ; contents of [Base+1]
shift_bits db  ?                ; used in compensating for screen page
protocol   db  ?                ; protocol I2c, Microwire, Special, Onewire
wr_type    db  ?                ; write per page, byte, both
slave      db  ?                ; I2C address for File I/O

         even
file_name  db  64 dup (?)       ; buffer for storing filename
buffer3    db  1K dup (?)       ; buffer for 

last_byte  equ  $               ; needed for determining end of data, start of SS

  CODE SEGMENT                  ; program starts here
         jmp   main             ; sorry, there

         even
should_be dw   5                ; 5 s per signal edge

hex_tbl  db  '0123456789ABCDEF', cr, 0          ; BCD -> HEX conversion table
CFG_file db  'I2C01.CFG', 0

hide_cursor:                    ; set cursor
         mov   w [scr_x], 0
#IF DEBUG
         mov   w [scr_y], 24    ; if debugging, on line 24
#ELSE
         mov   w [scr_y], 25
#ENDIF                          ; else on line 25 (out of view)
         call  set_cursor       ; set cursor
         ret
;------------------------

open_2:  push  ax               ; open timer chip in mode 2
         mov   al, 034
         out   043, al
         clr   al
         out   040, al
         out   040, al
         pop   ax
         ret
;------------------------

close_2: push  ax               ; close mode 2 of timer chip
         mov   al, 036
         out   043, al
         xor   al, al
         out   040, al
         out   040, al
         pop   ax
         ret
;------------------------

rd_time: mov   al, 6            ; read timer
         out   043, al
         in    al, 040
         mov   ah, al
         in    al, 040
         xchg  al, ah
         stosw
         ret
;------------------------

get_mode: push  ax, bx, dx, bp
         mov   ah, 0F
         int   010                      ; get video mode
         clr   ah
         mov   [v_mode], ax             ; store video mode
         mov   bl, bh
         clr   bh
         mov   [v_page], bx             ; and current displaypage
         mov   [v_segm], 0B800
         cmp   ax, 7
         IF e  mov  [v_segm], 0B000     ; store video segment
         cmp   ax, 04E
         IF nb sub  ax, 04E - 8
         shl   ax, 1
         mov   bx, ax                   ; use bx as index into several tables:
         mov   ax, [bx + video_rows]
         mov   [v_lines], ax            ; store number of lines
         mov   ax, [bx + video_columns]
         mov   [v_cols], ax             ; store number of chars per line
         shl   ax, 1
         mov   [lin_len], ax            ; store number of bytes per line
         mov   ax, [bx + video_math]
         mov   [scr_mul], ax            ; store dedicated multiplication routine
         mov   ax, [bx + video_page_size]
         mov   [p_size], ax
         mov   al, [bx + video_shifters]
         mov   [shift_bits], al
         clr   ax
         mov   [scr_x], ax              ; initialize screen coordinates
         mov   [scr_y], ax
         mov   ax, [v_lines]
         dec   ax
         mul   [v_cols]
         mov   [line_24], ax            ; compare-address for printing LF's
         pop   bp, dx, bx, ax
;------------------------

mul_132: 
mul_40:  ret
;------------------------

mul_80:  push  ax, cx           ; calculate char address in 80 columns
         mov   ax, [scr_y]
         shl   ax, 4
         mov   bx, ax           ; bx = 16 x SCR_Y
         shl   ax, 2            ; ax = 64 x SCR_Y
         add   ax, bx           ; ax = 80 x SCR_Y
         add   ax, [scr_x]
         shl   ax, 1
         mov   bx, [v_page]
         mov   cl, [shift_bits]
         shl   bx, cl
         add   bx, ax
         pop   cx, ax
         ret                    ; with result in BX
;------------------------

put_char: push  bx, es          ; print one character/attribute word at (scr_x, scr_y)
         mov   es, [v_segm]
         call  w [scr_mul]
     es  mov   [bx], ax
         inc   [scr_x]
         pop   es, bx
         ret
;------------------------

L0:      pop   ax
         ret

print$:  push  ax                       ; print ASCIIZ string [si]
         mov   ah, [bx.win_txt_attrib]  ; ah = colour
L1:      lodsb                          ; al = character
         cmp   al, 0                    ; is it a NULL?
         je    L0                       ; if so, exit
         call  put_char                 ; ... else print character
         jmp   L1                       ; and loop back for next character
;------------------------

cls:     pushf                          ; clear text screen
         push  ax, cx, di, es
         mov   es, [v_segm]
         mov   di, [v_page]
         mov   cl, [shift_bits]
         shl   di, cl
         mov   ax, [v_cols]
         mul   [v_lines]
         mov   cx, ax
         mov   ah, [scr_attr]
         mov   al, ' '
         rep   stosw                    ; block move for storing video RAM
         pop   es, di, cx, ax
         popf
         ret
;------------------------

show_bin: push  ax, bx, cx      ; print the value in ax in binary form, with leading zero's
         mov   cx, 16           ; value in b_digit is number of digits to show, starting at LSB
         sub   cx, [b_digit]    ; if NOT all 16 digits to print, ...
         IF nz shl  ax, cl      ; ... else allign bits for proper processing
         mov   bh, [scr_attr]   ; initialize registers
         mov   cx, [b_digit]
S8:      mov   bl, '0'          ; try with al = ZERO
         shl   ax, 1            ;    ... but if bit = set, ...
         adc   bl, 0            ;       ... make it a ONE, ...
         xchg  ax, bx           ;       ... swap source and character to print, ...
         call  put_char         ;       ... print it,
         xchg  ax, bx           ;   ... retrieve source
         loop  S8               ;   ... and loop back
         pop   cx, bx, ax
         ret
;------------------------

show_hex: push  ax, bx, cx              ; number in ax will be printed to <scr_x, scr_y>
         mov   bx, offset hex_tbl       ; a trailing space is appended to the result
         mov   cx, 4
         sub   cx, [h_digit]    ; determine how many digits to shift out ...
         shl   cx, 2            ;    ... using 4 bits per hex digit, ...
         IF nz shl  ax, cl      ;    ... and dispose them
         mov   cx, [h_digit]
S4:      rol   ax, 4            ; rotate MSB of ax into LSB
         push  ax
         and   al, 0F           ; mask off high nibble
         xlat                   ; compose hex digit
         mov   ah, [scr_attr]
         call  put_char         ; and print it
         pop   ax               ; restore value
         loop  S4               ; and loop back
         mov   al, ' '
         mov   ah, [scr_attr]
         call  put_char         ; print the trailing space
         pop   cx, bx, ax       ; prepare for exit, and
         ret                    ; gone
;------------------------

calc_win_addr: push cx          ; calculate address of topleft window in v_page
         mov   ax, [v_page]
         mov   cl, [shift_bits]
         shl   ax, cl
         mov   cx, ax
         mov   ax, [bx.win_topleft_y]
         mul   [v_cols]
         add   ax, [bx.win_topleft_x]
         shl   ax, 1
         add   ax, cx
         pop   cx               ; result in ax
         ret
;------------------------

put_row: push  ax, bx, cx, si, es       ; print cx character/attribute words at (scr_x, scr_y)
         mov   ah, [bx.win_box_attrib]  ; bx = -> window information block
         mov   es, [v_segm]
         call  w [scr_mul]
         lodsb                          ; si = -> boxing information block
     es  mov   [bx], ax                 ; print leftmost char/attrib
         add   bx, 2                    ; bx points to next screen position
         sub   cx, 2                    ; compensate for both left- and rightmost chars
         lodsb                          ; get middle character
P1:  es  mov   [bx], ax                 ; print it, ...
         add   bx, 2                    ; ... point to next position, ...
         loop  P1                       ; ... and repeat this
         lodsb                          ; get rightmost char ...
     es  mov   [bx], ax                 ; ... and print it.
         pop   es, si, cx, bx, ax
         ret
;------------------------

store_window: pushf                     ; bx = ptr -> window information block
         push  ax, cx, dx, si, di, es, ds
         cld
         call  calc_win_addr            ; where does it start?
         mov   si, ax
         mov   di, bp                   ; where to store window contents?
         mov   dx, [v_cols]             ; how many columns per line,
         shl   dx, 1                    ; convert to bytes per line,
         mov   ax, ss
         mov   es, ax                   ; set up ES/DI pair
         mov   ax, [v_segm]
         mov   ds, ax                   ; set up DS/SI pair
      cs mov   cx, [bx.win_height]      ; load CX with height of window
S0:      push  cx                       ; store it for later use
      cs mov   cx, [bx.win_width]       ; load CX with width of window
         push  si                       ; store starting address
         rep   movsw                    ; store one box-row of text/attribute words
         pop   si                       ; retrieve previous starting address, ...
         add   si, dx                   ; ... and update it.
         pop   cx                       ; retrieve LINES counter
         loop  S0                       ; and loop back for next line
         pop   ds                       ; restore datasegment
         mov   si, bx
         mov   cx, 5
         rep   movsw                    ; add first five words of window info block to buffer
         mov   bp, di                   ; adjust bp
         pop   es, di, si, dx, cx, ax
         popf                           ; restore variables and exit
         ret
;------------------------

draw_box: push  ax, cx, dx      ; make the windowborder, starting at (scr_x, scr_y)
         mov   dx, [scr_x]      ; bx = -> window information block
         mov   cx, [bx.win_width]       ; si = -> box information block
         mov   ax, cx
         call  put_row          ; print top row
         add   si, 3            ; point to middle row
         mov   [scr_x], dx
         inc   [scr_y]
         mov   cx, [bx.win_height]
         sub   cx, 2
         jcxz  >D1
D0:      push  cx
         mov   cx, ax
         call  put_row          ; print middle row
         mov   [scr_x], dx
         inc   [scr_y]
         pop   cx
         loop  D0               ; until done
D1:      add   si, 3            ; select end-row
         mov   cx, ax
         call  put_row          ; and print it
         pop   dx, cx, ax
         ret                    ; that's all, folks!
;------------------------

make_box: push  ax, si, [scr_x], [scr_y]    ; compose box around pop-up window
         mov   ax, [bx.win_topleft_x]       ; bx = -> window information block
         mov   [scr_x], ax
         mov   ax, [bx.win_topleft_y]
         mov   [scr_y], ax
         mov   si, [bx.win_box_border]
         call  draw_box                     ; do the actual boxing
         pop   [scr_y], [scr_x], si, ax
         ret
;------------------------

pr_win:  push  ax, cx                   ; build up screen si -> table & bx -> window information block
P1:      lodsb                          ; get vertical position
         cmp   al, 0F0                  ; end of table reached?
         je    >P4                      ; if so, exit
         cbw                            ; convert AL to AX
         add   ax, [bx.win_topleft_x]   ; compensate for horizontal position of window
         mov   [scr_x], ax              ; store real position
         lodsb                          ; get horizontal position
         cbw                            ; covert AL to AX
         add   ax, [bx.win_topleft_y]   ; compensate for vertical position of window
         mov   [scr_y], ax              ; store real position
         mov   ah, [bx.win_txt_attrib]  ; get colour
P2:      lodsb                          ; get first char
         cmp   al, 0                    ; is it an END of LINE?
         je    P1                       ; if so, EXIT, ...
         call  put_char                 ; ... else print it ...
         jmp   P2                       ; ... and loop back.
P4:      pop   cx, ax                   ; exit
         ret
;------------------------

pop_up_window: push  ax, bx, si         ; bx = -> window information block
         mov   ax, sp
         sub   ax, 256                  ; minimum extra space for stack
         sub   ax, bp
         cmp   ax, [bx.win_size]        ; enough room in SS?
         ja    >P1                      ; if so, jump, ...
         stc                            ; ... else set carry, ...
P0:      pop   si, bx, ax               ; ... and get out.
         ret
P1:      call  store_window             ; store data "under" window
         call  make_box                 ; compose box
         mov   si, [bx.win_text]        ; set up pointer to window contexts
         call  pr_win                   ; ... and print it
         clc                            ; clear carry and get out
         jmp   P0
;------------------------

restore_window:                         ; close window and restore screen
         push  ax, bx, cx, dx, si, di, es
         pushf
         cmp   bp, 0
         jne   >R1
         popf
         stc
R0:      pop   es, di, si, dx, cx, bx, ax
         ret
R1:      mov   bx, offset scratchpad
         mov   ax, [bp - 8]     ; topleft x
         mov   dx, [bp - 6]     ; topleft y
         mov   cx, [bp - 4]     ; window width
         mov   si, [bp - 2]     ; window height
         add   ax, cx
         dec   ax               ; compose bottom-right x
         add   dx, si
         dec   dx               ; and bottom-right y
         mov   [bx + 2], ax     ; make dummy window information block
         mov   [bx + 4], dx
         mov   [bx + 6], cx
         mov   [bx + 8], si
         call  calc_win_addr    ; calculate address of BOTTOM-RIGHT corner
         mov   di, ax           ; point to it
         mov   es, [v_segm]
         mov   cx, si
         mov   si, bp
         sub   si, 12           ; subtract saved header
         std                    ; reverse direction
         mov   ax, [v_cols]
         shl   ax, 1
         mov   dx, [bx.win_width]
         push  ds
         mov   bx, ss
         mov   ds, bx
R2:      push  cx
         mov   cx, dx
         push  di
         rep   movsw            ; move one line of old screen back
         pop   di
         sub   di, ax           ; point one line UP
         pop   cx               ; retrieve counter
         loop  R2               ; and loop back
         pop   ds
         mov   bp, si
         add   bp, 2
         popf
         clc
         jmp   R0               ; done
;------------------------

clear_pad: push  ax, cx, di, es         ; clear scratchpad area
         pushf
         cld
         mov   di, offset scratchpad
         mov   cx, (type scratchpad)/2
         mov   ax, ds
         mov   es, ax
         clr   ax               ; fill ax with NULL, ...
         rep   stosw            ; ... and zero-out notepad
         popf
         pop   es, di, cx, ax
         ret
;------------------------

yesno:   push  ax               ; wait for Y or y, any other key will deny
L1:      mov   ah, 7
         int   021              ; get char, no echo
         cmp   al, 0            ; F-key or Alt-key pressed?
         jne   >L2              ; if not go on, else ...
         mov   ah, 7            ;    ... skip over the active part of the function key, ...
         int   21h
         jmp   L1               ;    ... and jump back

L2:      and   al, not bit 5    ; make uppercase
         cmp   al, OK_chr       ; check against language specific acknowledge char
         clc 
         IF  e cmc              ; nifty! 
         pop   ax
         ret
;------------------------

spaces:  push  ax               ; print cx spaces from <scr_x, scr_y>
         mov   al, ' '
L3:      call  put_char
         loop  L3               ; easy!
         pop   ax
         ret
;------------------------

do_menu_bar: push  bx, cx, dx, [scr_x], [scr_y], si ; put a menubar on top of the screen
         clr   cx                                   ; si = -> menu-text (ASCIIZ)
         clr   bx                                   ; CAPITALS different colour, underscores '_' are spaces
         add   si, type justification_data          ; point to start of text
D0:      lodsb                  ; fetch char
         cmp   al, 0
         je    >D2              ; if zero => end of string
         cmp   al, ' '
         jne   >D1
         inc   cx               ; increment space-counter
         jmp   D0

D1:      inc   bx               ; increment character counts
         jmp   D0

D2:      pop   si               ; string scanned. CX = items, BX = chars
         mov   ax, [v_cols]
         sub   ax, bx           ; ax = # of spaces in line
         div   cl
         mov   dx, ax           ; dl = spaces between items
         clr   ax               ; dh = spaces at end of line
         mov   [scr_x], ax
         mov   [scr_y], ax      ; point to top-left screen position
         mov   bx, si
         add   si, type justification_data
D3:      lodsb                  ; start processing menu-bar
         cmp   al, ' '          ; space? ...
         jne   >D4              ; ... if not, jump.
         push  cx               ; else ...
         mov   cl, dl
         clr   ch
         mov   ah, [bx.normal]
         call  spaces           ; ... fill in amount of spaces, ...
         pop   cx
         jmp   D3               ; ... and fetch next char

D4:      cmp   al, 0            ; is it End-Of-String?
         je    >D6              ; if so, jump
         mov   ah, [bx.normal]  ; else check for capital
         cmp   al, 'A'
         jb    D5
         cmp   al, 'Z'
         IF na mov  ah, [bx.shortcut]   ; and change colour
D5:      cmp   al, '_'          ; is it an _?
         IF  e mov  al, ' '     ; if equal, replace by space
         call  put_char         ; print char, ...
         jmp   D3               ; ... and get next char 

D6:      clr   cx               ; end of string => pad menu bar out with spaces
         mov   cl, dh           ; also end of line?
         jcxz  >D7              ; if so, skip
         mov   ah, [bx.normal]
         call  spaces           ; filling out line
D7:      pop   [scr_y], [scr_x], dx, cx, bx
         ret
;------------------------

process_keys: push  ax, dx, si  ; si = -> char array
         pushf                  ; on return, bx = index in array
         cld
         mov   dx, si     ; store si
P0:      mov   si, dx     ; restore si
         mov   ah, 7
         int   21h        ; get char, unprocessed, no echo
         cmp   al, 0      ; if F- or Alt-key, ...
         jne   >P1        ; ... discard it, ...
         mov   ah, 7      ; ... so skip them
         int   21h
         jmp   P0         ; ... and loop back

P1:      cmp   al, 01B    ; ESC pressed?
         jne   >P3        ; if not jump, ...
P2:      mov   bx, 0FFFF  ; ... else set signal, ...
         jmp   short >P6  ; ... and jump to exit.

P3:      mov   ah, al     ; save character typed by user
P4:      lodsb            ; get first array item
         cmp   al, 0FF    ; FF indicates this loop is not infinite
         je    P2         ; if finite, jump to exit.
         cmp   al, 0
         je    P0         ; if end of array, try again
         cmp   ah, al     ; stored char = array char?
         je    >P5        ; if so, jump out
         or    al, bit 5  ; change case of one char
         cmp   ah, al     ; and compare again
         jne   P4         ; if still no match, try next entry
P5:      mov   bx, si     ; char found, so find index number
         sub   bx, dx
         dec   bx         ; si was already pointing to NEXT char
P6:      popf
         clc
         pop   si, dx, ax ; bx = index in array
         ret              ; if bx = FFFF, => escape pressed
;------------------------

set_cursor: push  ax, bx, dx, bp    ; set DOS cursor to [scr_x], [scr_y]
         mov   dx, [scr_x]      ; fill dl
         mov   dh, b [scr_y]    ; fill dh
         mov   bx, [v_page]
         mov   ah, 2
         int   010              ; set cursor position via video INT
         pop   bp, dx, bx, ax
         ret
;------------------------

#IF !PAYWARE
no_go:   push  ax, bx           ; sorry, no deal. Just buy the real thing!
         mov   bx, offset no_go_box
         call  pop_up_window
         mov   ax, 0C07
         int   021              ; wait for "ANY KEY"
         call  restore_window   ; restore window
         pop   bx, ax
         ret                    ; and exit
;------------------------
#ENDIF

T1:      mov   bx, 0FFFF        ; byte not in table!
         pop   dx
         ret                    ; exit
T2:      sub   bx, dx           ; calculate position in table
         pop   dx
         ret

table_find: push  dx            ; find AL in ASCIIZ table [BX] and report position
         mov   dx, bx           ; keep value of BX
T0:      cmp   b [bx], 0        ; is it end of table?
         je    T1               ; if so, jump out
         cmp   al, [bx]         ; compare byte with table
         je    T2               ; if same, jump out
         inc   bx               ; else increment pointer
         jmp   T0               ; and loop back
;------------------------

show_error:                     ; si = -> error message
         or    [FLAGS], bit 9
         push  bx
         mov   bx, offset error_window
         call  pop_up_window
         call  pr_win
         mov   ax, 0C07
         int   021
         call  restore_window
         pop   bx               ; should be clear by now
         and   [FLAGS], not bit 9
         ret
;------------------------

L2:      call  restore_window           ; prepare to exit
         mov   al, ah
         pop   [scr_y], [scr_x], si, dx, cx, bx
         ret

L1:      or    w [FLAGS], bit 0         ; set ERROR status, ...
         push  si
         mov   si, offset error_02
         call  show_error               ; ... show error, ...
         pop   si
         jmp   L2                       ; ... and exit

get_hex_2: push  bx, cx, dx, si, [scr_x], [scr_y]   ; get 2 digit hex number
         mov   bx, offset hexin_box
         call  pop_up_window    ; show intention on-screen
         set_x ax, 7
         set_y ax, 1
         call  set_cursor       ; position cursor
         rd_text buffer1, 3     ; read text using INT 021/0A
         mov   si, dx
         call  hide_cursor      ; hide cursor
         clr   ax
         clr   cx
         inc   si               ; point to chars read in
         lodsb                  ; get length of string
         mov   cl, al
         jcxz  L2               ; if no input, jump
L0:      lodsb                  ; get char
         call  make_upper       ; make uppercase
         mov   bx, offset hex_tbl
         call  table_find       ; look up in table
         cmp   bx, 010          ; valid char?
         jnc   L1               ; if not, report so ...
         mov   al, bl           ; else get number, ...
         shl   al, 4            ; ... allign it, ...
         shl   ax, 4            ; ... and shift it into AH, ...
         loop  L0               ; ... until done.
         jmp   L2               ; then exit succesfully
;------------------------

zeker?:  push  ax, bx, [scr_x], [scr_y]         ; are we sure?-box
         mov   bx, offset zeker_box
         call  pop_up_window
         call  yesno
         pushf
         call  restore_window
         popf
         pop   [scr_y], [scr_x], bx, ax
         ret
;------------------------

chk_SDA: push  dx               ; Measure SDA-line status
         mov   dx, [LPT_port]
         inc   dx               ; dx = INPUT port
         in    al, dx           ; read port
         shl   al, 1            ; Store value in Boolean accumulator (= carry flag)
         mov   al, '0'          ; init AL
         IF  c inc  al          ; if bit was "1", increment AL
         pop   dx               ; and prepare to ...
         ret                    ; ... exit
;------------------------

rd_SDA:  push  ax               ; Determine the current state of the SDA line
         call  chk_SDA
         mov   [SDA_is], al     ; store char in string
         pop   ax
         ret
;------------------------

rd_BIT:  push  ax               ; Determine value of the last BIT processed
         call  chk_SDA
         mov   [BIT_is], al     ; store char
         pop   ax
         ret
;------------------------

rd_SCL:  push  ax, dx           ; Measure SCL-line status
         mov   dx, [LPT_port]
         inc   dx
         in    al, dx
         shl   al, 3            ; Store value in carry flag
         mov   al, '0'
         IF  c inc  al
         mov   [SCL_is], al     ; store char
         pop   dx, ax
         ret
;------------------------
         
fill_main_screen:               ; refresh main screen
         push  bx, si
         mov   bx, offset main_box      ; set pointer to ...
         call  make_box                 ; ... draw lines around the screen, ...
         mov   si, [bx.win_text]
         call  pr_win                   ; ... and print the contents
         pop   si, bx
         ret
;------------------------

fill_SDA_box:                   ; show current state of SDA-line
         push  bx, si
         mov   bx, offset SDA_box
         call  make_box                 ; make box
         mov   si, [bx.win_text]
         call  pr_win                   ; and show information
         pop   si, bx
         ret
;------------------------

fill_SCL_box:                   ; show current state of SCL line
         push  bx, si
         mov   bx, offset SCL_box
         call  make_box                 ; draw lines
         mov   si, [bx.win_text]
         call  pr_win                   ; show information
         pop   si, bx
         ret
;------------------------

fill_BIT_box:                   ; show state of last BIT received via SDA
         push  bx, si
         mov   bx, offset BIT_box
         call  make_box
         mov   si, [bx.win_text]
         call  pr_win
         pop   si, bx
         ret
;------------------------

fill_LPT_box:                   ; show which LPT number we're on
         push  bx, si
         mov   bx, offset LPT_box
         call  make_box
         mov   si, [bx.win_text]
         call  pr_win
         pop   si, bx
         ret
;------------------------

get_port: push  bx, es          ; determine I/O address of LPTx
         mov   bx, [LPT_number] ; get portnumber
         dec   bx               ; subtract 1
         shl   bx, 1            ; multiply by 2
         add   bx, 0408         ; add offset in BIOS RAM
         clr   ax
         mov   es, ax           ; point to BIOS area
     es  mov   ax, [bx]         ; get I/O address of this LPT-number
         pop   es, bx
         ret
;------------------------

do_quit: push  bx, si                   ; exit to DOS?
         mov   bx, offset Exit_box
         call  pop_up_window
         call  zeker?                   ; sure?
         IF  c or  w [FLAGS], bit 1     ; if so, set flag
         call  restore_window
         pop   si, bx
         ret
;------------------------

IO_w8:   push  cx               ; wait specified amout of time
         mov   cx, [delay]
W0:      jmp   >W1              ; clear cache
W1:      loop  W0               ; and do nothing
         pop   cx
         ret
;------------------------

SCL_hi:  push  ax, dx           ; make SCL line HIGH
         mov   dx, [LPT_port]
         in    al, dx           ; get current value
         or    al, bit SCL      ; set SCL-bit
         out   dx, al           ; and activate it
         call  IO_w8            ; wait for bus to settle
         call  rd_SCL           ; measure levels of SCL, ...
         call  rd_BIT           ; ... SDA, and ...
         pop   dx, ax
         ret
;------------------------

SCL_lo:  push  ax, dx           ; make SCL line LOW
         mov   dx, [LPT_port]
         in    al, dx           ; get current value
         and   al, not bit SCL  ; clear bit
         out   dx, al           ; and activate it
         inc   [SCL_count]      ; if so, increment counter
         call  IO_w8            ; wait for bus to settle and, ...
         call  rd_SCL           ; ... measure SCL level
         pop   dx, ax
         ret
;------------------------

SDA_hi:  push  ax, dx           ; make SDA line HIGH
         mov   dx, [LPT_port]
         in    al, dx           ; get current value
         or    al, bit SDA      ; set SDA bit, ...
         out   dx, al           ; ... and activate it
         call  IO_w8            ; wait for bus to settle, and ...
         call  rd_SDA           ; ... read in current value
         pop   dx, ax
         ret
;------------------------

SDA_lo:  push  ax, dx           ; make SDA line LOW
         mov   dx, [LPT_port]
         in    al, dx           ; get current value
         and   al, not bit SDA  ; clear bit, ...
         out   dx, al           ; ... and activate it.
         call  IO_w8            ; wait for bus to settle, ...
         call  rd_SDA           ; ... and read in current value
         pop   dx, ax
         ret
;------------------------

togl_SDA: push  ax, dx          ; switch state of SDA line
         mov   dx, [LPT_port]
         in    al, dx           ; get current value
         xor   al, bit SDA      ; ... toggle SDA-bit, ...
         out   dx, al           ; ... activate it, ...
         call  IO_w8            ; ... wait for bus to settle, ...
         call  rd_SDA           ; ... check SDA state IN HARDWARE, ...
         call  fill_SDA_box     ; ... fill it in on screen, ...
         pop   dx, ax           ; and exit
         ret
;------------------------

togl_SCL: push  ax, dx          ; switch state of SCL line
         mov   dx, [LPT_port]
         in    al, dx           ; get current value, ...
         xor   al, bit SCL      ; ... toggle state, ...
         out   dx, al           ; ... activate it, ...
         call  IO_w8            ; ... wait for bus to settle, ...
         call  rd_SDA           ; ... check SDA state IN HARDWARE, ...
         call  rd_SCL           ; ... check SCL state IN HARDWARE
         cmp   b [SCL_is], "1"  ; is SCL high?
         jne   >L1
         call  rd_BIT           ; if so, read SDA value, ...
         call  fill_BIT_box     ; ... and print it
L1:      call  fill_SCL_box     ; else show SCL value, ...
         call  fill_SDA_box     ; ... and SDA value
         inc   [SCL_count]      ; ... increment clock count
         pop   dx, ax
         ret
;------------------------

clock:   call  SCL_hi           ; give one clock cycle
         call  SCL_lo
         call  rd_SDA           ; check SDA state IN HARDWARE
         inc   [SCL_count]
         call  fill_BIT_box
         call  fill_SDA_box
         call  fill_SCL_box
         ret
;------------------------

get_ACK: call  SDA_hi           ; release SDA line
         call  SCL_hi           ; get ACK bit
         call  SCL_lo
         call  rd_SDA           ; check state of SDA-line
         call  fill_BIT_box     ; update screen
         call  fill_SDA_box
         ret
;------------------------

do_ACK:  call  SDA_lo           ; make SDA bit null
         call  SCL_hi           ; clock it out
         call  SCL_lo
         call  SDA_hi           ; release SDA-line
         call  rd_SDA           ; check state of SDA-line
         call  fill_BIT_box
         call  fill_SDA_box     ; update screen
         ret
;------------------------

P_cond:  push  bx, si           ; force STOP condition
         call  SCL_lo           ; make sure SCL is LOW
         call  SDA_lo           ; make SDA low, to prevent a start condition
         call  SCL_hi           ; make SCL high
         call  SDA_hi           ; STOP condition active, I2C bus released
         call  IO_w8
         call  IO_w8            ; let it stabilize
         call  fill_SCL_box
         call  fill_SDA_box     ; update the screen
         mov   bx, offset I2C_box
         mov   si, offset STOP_txt
         call  pr_win           ; even mention it in the I2C bus box
         mov   [SCL_count], 0   ; clear "clocks since last S-condition"
         pop   si, bx
         ret
;------------------------

S_cond:  push  bx, si           ; force START condition
         call  SCL_lo           ;  make sure there is a legal "1" on the SDA line
         call  SDA_hi           ; 
         call  SCL_hi           ; correct clock            
         call  SDA_lo           ; change state of SDA bit,  = START condition
         call  SCL_lo           ; leave SCL in defined state
         call  fill_SCL_box
         call  fill_SDA_box
         call  fill_BIT_box
         mov   bx, offset I2C_box
         mov   si, offset STRT_txt
         call  pr_win           ; update screen
         pop   si, bx
         ret
;------------------------

send_0:  call  SDA_lo           ; xfer a ZERO bit over the I2C bus
         call  clock
         call  SDA_hi           ; release the bus after the LOW pulse
         call  fill_SDA_box
         ret
;------------------------

send_1:  call  SDA_hi           ; xfer a ONE bit over the I2C bus
         call  clock
         ret
;------------------------

get_ascii: push  ax, bx, dx     ; read in ASCII to send over the bus
         mov   bx, offset asc_box
         call  pop_up_window
         set_x si, 9
         set_y si, 3
         call  set_cursor
         rd_text buffer1, 65    ; read in text via int 021/0A
         call  hide_cursor      ; remove cursor from screen
         mov   si, dx
         clr   cx
         mov   cl, [buffer1.count]      ; cx = chars read
         call  restore_window
         mov   si, offset buffer1 + 2   ; si -> chars
         pop   dx, bx, ax
         ret                    ; with cx = count and si = -> buffer1
;------------------------

i2c_out: push  cx, dx           ; send byte in AH over the bus
         mov   cx, 8            ; bits to transfer
         mov   dx, [LPT_port]
L8:      in    al, dx
         shr   al, 1            ; allign AL
         rol   ax, 1            ; shift next bit into SDA position
         out   dx, al
         call  IO_w8
         call  SCL_hi
         call  SCL_lo           ; clock it out, ...
         loop  L8               ; ... and loop back.
         call  get_ACK          ; get the ACK pulse from the slave
         pop   dx, cx
         ret
;------------------------

show_ACK: push  ax, bx, si      ; was there an ACK signal FROM THE SLAVE? 
         mov   bx, offset I2C_box
         mov   si, offset ACK_txt
         cmp   [BIT_is], "1"
         IF nc mov  si, offset NACK_txt
         call  pr_win
         pop   si, bx, ax
         ret
;------------------------

D1:      mov   ah, al           ; no errors, so, ...
         call  i2c_out          ; ... TRANSMIT it
         call  show_ACK         ; and retrieve the ACK pulse
D2:      pop   ax               ; exit
         ret

do_xmit: push  ax               ; read in byte and set it on the I2C bus
         call  get_hex_2
         test  w [FLAGS], bit 0 ; error?
         jz    D1                       ; if not, jump, ...
         and   w [FLAGS], not bit 0     ; ... else do nothing
         jmp   D2
;------------------------

do_asci: push  ax, cx, si       ; read in ASCII and transmit it across the bus
         call  get_ascii
         jcxz  >L9
         cld
L0:      lodsb                  ; fetch byte, ...
         mov   ah, al           ; ... position it, ...
         call  i2c_out          ; ... send it out, ...
         loop  L0               ; ... and loop back.
L9:      pop   si, cx, ax
         ret
;------------------------

do_rcv:  push  cx, dx           ; Receive one byte and dump it in AH. NO acknowledge!
         call  SDA_hi           ; release SDA-line
         mov   cx, 8
         mov   dx, [LPT_port]
         inc   dx
L1:      call  SCL_hi           ; clock high
         in    al, dx           ; read in value
         shl   ax, 1            ; isolate SDA bit and  shift it <FROM> AL <INTO> AH
         call  SCL_lo           ; clock low again
         loop  L1               ; loop back
         pop   dx, cx
         ret
;------------------------

ahx10:   push  bx       ; multiply AH by 10
         mov   bl, ah   ; bl = ah
         shl   bl, 1    ; bl = ah x 2
         shl   ah, 3    ; ah = ah x 8
         add   ah, bl   ; ah = ah x (8 + 2) = ah x 10
         pop   bx
         ret
;------------------------

do_read: push  ax, bx, [scr_x], [scr_y], w [scr_attr]   ; receive ONE byte from I2C peripheral
         mov   bx, offset rcv_box                       ; byte = AH 
         call  pop_up_window
D0:      call  do_rcv           ; receive one byte
         set_x dx, 8
         set_y dx, 3            ; set up cursor
         mov   [b_digit], 8     ; set: 8 bits to show
         mov   [h_digit], 2     ; set: 2 hex digits to show
         mov   al, ah           ; store byte
         mov   [scr_attr], 0F
         call  show_hex         ; AL = 2 bytes
         add   [scr_x], 10
         mov   ah, [scr_attr]
         call  put_char         ; print it
         set_x dx, 8
         inc   [scr_y]
         call  show_bin         ; print bits
D3:      mov   ax, 0C07
         int   021              ; wait for key
         cmp   al, tab          ; TAB? ...
         jnz   >D1              ; ... if not, jump
         call  do_ACK           ; send ACK pulse over the bus
         jmp   D0               ; and loop back
D1:      cmp   al, escape       ; ESC?
         jnz   D3               ; ... if not, wait for keypress.
         call  P_cond           ; send STOP condition
D2:      call  restore_window   ; and prepare to exit
         pop   w [scr_attr], [scr_y], [scr_x], bx, ax
         ret
;------------------------

make_upper:
         cmp   al, 'a'          ; less than 'a'
         jb    ret              ; if so, no lowercase
         cmp   al, 'z'          ; more than 'z'
         IF na xor  al, bit 5   ; if not, make uppercase
         ret                    ; and exit
;------------------------

#IF PAYWARE
E0:      stc            ; error in convert routines
         ret

C9:      mov   al, ah           ; normal exit of convert routine
         clr   ah               ; signal THIS IS A BYTE
         stosw                  ; store in queue
         inc   w [que_ptr]      ; adjust queue pointer
         dec   si
         clc
         ret

cvt_hex: clr   ah               ; convert ascii number (base 16) to binary
C1:      lodsb                  ; si = -> chars
         cmp   al, ' '          ; if space, ...
         je    C9               ; ... done
         cmp   al, tab          ; of TAB, ...
         je    C9               ; ... done
         cmp   al, 0            ; if NULL, ...
         je    C9               ; ... done
         call  make_upper       ; make uppercase if necessary
         mov   bx, offset hex_tbl
         call  table_find       ; look up in table
         cmp   bx, 010
         ja    E0               ; illegal char => exit with errorcode
         mov   al, bl           ; make number
         shl   al, 4
         shl   ax, 4
         jmp   C1               ; and prepare to store it in queue
;------------------------

cvt_dec: clr   ah               ; convert ascii number (base 10) to binary
C2:      lodsb
         cmp   al, ' '
         je    C9
         cmp   al, tab
         je    C9
         cmp   al, 0
         je    C9
         cmp   al, '9'
         ja    E0
         cmp   al, '0'
         jb    E0
         sub   al, '0'
         call  ahx10
         add   ah, al
         jmp   C2
;------------------------

cvt_bin: clr   ah               ; convert ascii number (base 2) to binary
C3:      lodsb
         cmp   al, ' '
         je    C9
         cmp   al, tab
         je    C9
         cmp   al, 0
         je    C9
         cmp   al, '1'
         ja    E0
         cmp   al, '0'
         jb    E0
         sub   al, '1'          ; al = al - '1' => al = '1' -> NO CARRY, al = '0' -> CARRY
         cmc                    ; correct result
         rcl   ah, 1
         jmp   C3
;------------------------

cvt_txt: lodsb                  ; convert ascii to binary
         cmp   al, '"'          ; end of string is ', ", NULL
         je    ret
         cmp   al, "'"
         je    ret
         cmp   al, 0
         je    ret
         clr   ah               ; ah = 0 => parameter = byte
         stosw                  ; store it in queue
         inc   w [que_ptr]      ; adjust queue pointer
         jmp   short cvt_txt    ; and get next
;------------------------

rd_str:  push  ax, bx, cx, si, di, [scr_x], [scr_y]         ; read in a string of characters/hex numbers
         push  w [scr_attr]
         mov   bx, offset rd_box
         call  pop_up_window                    ; show window
         mov   al, [bx.win_txt_attrib]
         mov   [scr_attr], al
R2:      mov   di, offset scratchpad
         mov   cx, [rd_num]             ; number of bytes to read
R0:      call  do_rcv                   ; get one bute from the I2C bus
         mov   al, ah                   ; copy to AL
         stosb                          ; store it
         cmp   cx, 1                    ; last byte?
         IF ne call do_ack              ; if not, send ACK
         loop  R0                       ; and loop back
         mov   si, offset scratchpad
         set_x ax, 10
         set_y ax, 3            ; set position for normal printing
         mov   cx, [rd_num]
         mov   ah, [scr_attr]
R3:      lodsb                  ; get first byte
         call  put_char         ; print it
         loop  R3               ; and loop back
         set_x ax, 10
         set_y ax, 4            ; set position for printing hax values
         mov   cx, [rd_num]
         mov   w [h_digit], 2           ; 2 hex digits to print
         mov   si, offset scratchpad
R4:      lodsb                  ; get first byte
         call  show_hex         ; convert to hexadecimal and print it
         loop  R4               ; and loop back
R1:      mov   ax, 0C07
         int   021              ; wait for keypress
         cmp   al, escape       ; is it ESC? ...
         je    >R9              ; ... if so, jump out
         cmp   al, tab          ; TAB? ...
         jne   R1               ; ... if so, ...
         call  do_ack           ; ... send ACK, and ...
         jmp   R2               ; ... receive next series of bytes
R9:      call  P_cond           ; return to caller, after ...
         call  restore_window   ; ... restoring screen, ...
         pop   w [scr_attr]     ; ... and registers
         pop   [scr_y], [scr_x], di, si, cx, bx, ax
         ret
;------------------------

wr_str:  push  ax, bx, cx, dx, si, di           ; send multiple bytes to I2C bus
         mov   bx, offset wr_box
         call  pop_up_window
         set_x ax, 10
         set_y ax, 3
         call  set_cursor
         rd_text buffer1, 41            ; read in data to send
         call  hide_cursor
         cmp   b [buffer1.count], 0     ; nothing to do?
         je    >W4                      ; if so, leave sub
         clr   cx
         mov   cl, [buffer1.count]      ; cx = bytes in buffer
         mov   si, offset buffer1 + 2
         mov   di, offset buffer2       ; copy the buffer
         cld
         rep   movsb
         clr   al
         stosb                          ; make it ASCIIZ
         mov   w [que_ptr], 0           ; clear queue
         mov   si, offset buffer2       ; read from buffer2 (the copy)
         mov   di, offset number_que    ; store results in queue
W2:      lodsb                  ; get first byte
         cmp   al, 0            ; if NULL, ...
         je    >W4              ; ... exit.
         cmp   al, ' '          ; if SPACE ...
         je    W2
         cmp   al, tab          ; ... or TAB ...
         je    W2               ; ... skip it.
         call  make_upper       ; make it uppercase
         cmp   al, 'A'          ; ASCII OUT requested?
         jne   >L2
         mov   ax, offset do_asci
W19:     stosw                  ; if so, store address of routine into queue, ...
         inc   [que_ptr]        ; ... adjust queue pointer ...
         jmp   W2               ; ... and get next byte
L2:      cmp   al, 'S'          ; is it START COND?
         jne   >W21
         mov   ax, offset S_cond
         jmp   W19              ; store it
W21:     cmp   al, 'P'          ; is it STOP COND?
         jne   >W22
         mov   ax, offset P_cond
         jmp   W19
W22:     mov   bx, offset nr_table      ; prepare for tanslation
         call  table_find       ; search table with number prefixes
         cmp   bx, 0FFFF        ; nothing found?
         je    >W3              ; if so, jump to error, else ...
         shl   bx, 1            ; ... make pointer, ...
         call  nr_act[bx]       ; ... and call indexed table
         jc    >W6              ; if carry set, jump to error, else ...
         jmp   W2               ; ... get next byte from buffer

W3:      mov   si, offset error_01      ; error in type specifier
         call  show_error
         jmp   >W5
W6:      mov   si, offset error_02      ; error in number entry
         call  show_error       ; show it, ...
         jmp   >W5              ; ... and exit

W4:      cmp   w [que_ptr], 0           ; queue empty?
         je    >W5                      ; if so, exit, else ...
         mov   si, offset number_que    ; ... prepare for interpreting existing queue
         mov   cx, [que_ptr]            ; cx = number of commands
W0:      lodsw                  ; get first queue entry
         cmp   ah, 0            ; is AH = 0? 
         je    >W00             ; if so, send data over I2C bus, else ...
         call  ax               ; ... execute function, ...
         jmp   >W01             ; ... and jump forward to LOOP
W00:     mov   ah, al           ; allign data, ...
         call  i2c_out          ; ... and send it out, ...
W01:     loop  W0               ; ... until done.
W5:      call  restore_window   ; restore screen
         pop   di, si, dx, cx, bx, ax
         ret
;------------------------

L8:      mov   si, offset error_03      ; error opening file
         call  show_error
         or    w [FLAGS], bit 8         ; signal ERROR WHILE PARSING
L9:      pop   di, dx, cx, ax
         ret

IO_prep: push  ax, cx, dx               ; prepare for I/O action
         mov   ax, offset IO_rd
         test  w [FLAGS], bit 2
         IF nz mov  ax, offset IO_wr
         stosw                          ; enter appropriate subroutine address in queue
         inc   [que_ptr]                ; adjust queue pointer
         push  di
         mov   di, offset file_name
L0:      lodsb
         cmp   al, ' '
         je    >L1
         cmp   al, tab
         je    >L1
         cmp   al, 0
         je    >L1
         stosb                  ; compose I/O filename
         jmp   L0
L1:      dec   si               ; end of filename reached
         mov   al, 0
         stosb                          ; make ASCIIZ
         mov   dx, offset file_name
         test  w [FLAGS], bit 2
         jnz   >L2
         mov   ax, 03D40
         int   021
         jc    L8
         mov   [handle], ax
         or    w [FLAGS], bit 3   ; signal I/O FILE IS OPEN
         jmp   L9
L2:      clr   cx
         mov   ah, 03C
         int   021
         jc    L8
         mov   [handle], ax
         or    w [FLAGS], bit 3
         jmp   L9
;------------------------

IO_rd:                          ; read FROM file TO i2c
         mov   bx, [handle]
         mov   ah, 03E
         int   021              ; close file
         ret
;------------------------

IO_wr:                          ; write FROM i2c TO file
         mov   bx, [handle]
         mov   ah, 03E
         int   021              ; close file
         ret
;------------------------
#ENDIF

load_block:                     ; get buffer from disk
         push  ax, bx, dx
         mov   dx, offset buffer3
         mov   cx, 1024
         mov   bx, [handle]
         mov   ah, 03F
         int   021
         mov   cx, ax
         mov   si, offset buffer3
         pop   dx, bx, ax
         ret
;------------------------

L0:      lodsb                  ; get char
         dec   cx               ; adjust counter
         ret

get_chr: cmp   cx, 0            ; get next char from buffer3
         jne   L0               ; buffer empty?
         call  load_block       ; if so, get new bufferful
         cmp   cx, 0            ; nothing read?
         jnz   L0               ; if not, continue, else ...
         or    w [FLAGS], bit 6 ; ... set error flag, and ...
         ret                    ; ... exit to caller
;------------------------

L4:      pop   di
         ret

fill_line: push  di                     ; xfer one line of text from buffer3 to r_buffer
         mov   di, offset r_buffer      ; si = -> into buffer3
         mov   [r_count], 0
L0:      call  get_chr
         test  w [FLAGS], bit 6 ; EOF found?
         jnz   L4
         cmp   al, cr           ; first char a CR?
         je    >L1              ; if so, find LF
         cmp   al, ';'          ; is this a COMMENT line?
         jne   >L2              ; if not so, move data to buffer
L1:      call  get_chr
         test  w [FLAGS], bit 6 ; EOF found?
         jnz   L4
         cmp   al, lf
         jne   L1
         jmp   L0
L2:      call  make_upper
         stosb                  ; store char
         inc   [r_count]        ; inc counter
         call  get_chr
         test  w [FLAGS], bit 6 ; EOF found?
         jnz   L4
         cmp   al, lf           ; end of line?
         je    >L3              ; if so, exit
         cmp   al, ';'          ; comment at end of line?
         jne   L2
         dec   si               ; if so, treat it as a SEPARATE LINE!
L3:      dec   di
         mov   al, 0            ; make ASCIIZ
         stosb
         jmp   L4               ; and exit
;------------------------

compare: push  si, di, cx       ; compare n_buffer with first entry of r_buffer
         or    w [FLAGS], bit 5 ; signal NAMES DO NOT MATCH
         mov   cx, [n_count]
         cmp   cx, [r_count]
         jnc   >L9                      ; if line < chipname => skip
         mov   si, offset n_buffer      ; set up pointers
         mov   di, offset r_buffer
         repz  cmpsb                    ; compare strings
         jnz   >L9                      ; if NZ, difference found, else ...
         and   w [FLAGS], not bit 5     ; ... WE HAVE A MATCH!
L9:      pop   cx, di, si               ; and exit to caller
         ret
;------------------------

skip_ws: lodsb                  ; skip delimiters
         dec   cx
         cmp   al, ' '
         je    skip_ws
         cmp   al, tab
         je    skip_ws
         ret
;------------------------

L9:      inc   si
         pop   dx, bx
         ret

base_2:  push  bx, dx           ; convert ASCII to BINary
         clr   ah               ; AL = ASCII byte
         sub   al, '0'          ; make BCD
         mov   bx, 10           ; set up multiplier
L0:      cmp   b [si], '9'      ; next byte is no digit?
         ja    L9
         cmp   b [si], '0'
         jb    L9               ; if so, exit, else ...
         mul   bx               ; ... multiply by 10
         mov   dl, [si]         ; get next byte, ...
         sub   dl, '0'          ; ... make BCD, ...
         inc   si               ; ... point to next byte, and ...
         add   ax, dx           ; ... compose number.
         jmp   L0               ; now get next digit to process.
;------------------------

showdev: push  ax, cx, si, [scr_x], [scr_y]     ; show named I2C device parameters on screen
         mov   al, b [dev_x]
         clr   ah
         mov   [scr_x], ax
         push  ax
         mov   al, b [dev_x + 1]
         add   ax, 2
         add   ax, [main_box.win_topleft_y]
         mov   [scr_y], ax                      ; set up printing position
         mov   cx, [v_cols]
         sub   cx, [scr_x]
         sub   cx, 2
         mov   ah, [main_box.win_txt_attrib]
         call  spaces                           ; wipe out old data
         pop   [scr_x]
         mov   si, offset r_buffer
         mov   cx, [r_count]
         mov   ah, [main_box.win_txt_attrib]
L0:      lodsb                                  ; get byte, ...
         call  put_char                         ; ... and print it, ...
         loop  L0                               ; ... until done.
         pop   [scr_y], [scr_x], si, cx, ax
         ret
;------------------------

get_params: push  ax, si                ; get I2C parameters from file
         mov   si, offset r_buffer
L0:      lodsb
         cmp   al, ' '              ; first skip over devicename
         je    >L1
         cmp   al, tab
         jne   L0
L1:      call  skip_ws              ; find PROTOCOL byte, ...
         call  make_upper           ; ... make uppercase, ...
         mov   [protocol], al       ;     ... and store it.
         call  skip_ws              ; find PROM SIZE, ... 
         call  base_2               ; ... make it binary, ...
         mov   [PROM_size], ax      ;     ... and store it.
         dec   ax                   ; compose "last address to burn"
         mov   [burn.until], ax
         call  skip_ws
         call  base_2
         mov   [word_size], ax      ; find and store WORD SIZE (in bits)
         call  skip_ws
         call  base_2
         mov   [page_size], ax      ; find and store PAGE SIZE (in bytes)
         call  skip_ws
         call  base_2
         mov   [wr_speed], ax       ; find and store WRITE SPEED
         call  showdev              ; and show it on screen.
         pop   si, ax
         ret
;------------------------

L3:      or    w [FLAGS], bit 7         ; signal I2C.CFG NOT FOUND
         mov   si, offset error_06      ; show it to the user
L4:      call  show_error
L5:      test  w [FLAGS], bit 4         ; I2C.CFG file still open?
         jz    >L6
         mov   bx, [handle]             ; if YES, ...
         mov   ah, 03E
         int   021                      ; ... close it.
         and   w [FLAGS], not bit 4     ; clear corresponding flag, ...
L6:      pop   si, di, dx, cx, ax       ; ... and exit
         ret

set_dev: push  ax, cx, dx, di
         mov   di, offset n_buffer
         mov   [n_count], 0             ; init length of device-name
         and   w [FLAGS], not bit 4     ; signal I2C.CFG FILE NOT OPEN
         and   w [FLAGS], not bit 6     ; signal EOF NOT DETECTED
L0:      lodsb
         cmp   al, ' '                  ; delimiter?
         je    >L7
         cmp   al, 0                    ; delimiter?
         je    >L7
         call  make_upper               ; make uppercase
         stosb                          ; and store in name-buffer
         inc   [n_count]                ; inc counter
         jmp   L0

L7:      push  si                       ; save pointer into buffer-1/-2
         mov   al, ' '
         stosb                          ; add delimiter
         inc   [n_count]
         mov   dx, offset CFG_file
         mov   ax, 03D42
         int   021                      ; open I2C.CFG file
         jc    L3
         mov   [handle], ax
         or    w [FLAGS], bit 4         ; signal I2C.CFG FILE IS OPEN
         call  load_block               ; load first block of I2C.CFG into buffer3
L1:      call  fill_line
         test  w [FLAGS], bit 6         ; if end of file reached, ...
         jz    >L2
         mov   si, offset error_04      ; ... mention UNKNOWN DEVICE
         jmp   L4
L2:      call  compare
         test  w [FLAGS], bit 5         ; do we have a MATCH?
         jnz   L1
         call  get_params               ; if so, decode data and store in RAM
         jmp   L5
;------------------------

#IF PAYWARE
do_dest: push  bx                       ; where to send data?
         mov   bx, offset IO_box        ; define window parameters
         xor   [FLAGS], bit 15          ; toggle flag
         call  FIO_fil
         pop   bx
         ret
;------------------------

L0:      pop   di, si, dx, cx, bx, ax
         ret

do_file: push  ax, bx, cx, dx, si, di           ; send data to file
         mov   bx, offset IO_box
         set_x ax, 10
         set_y ax, 5                    ; prepare to ...
         call  set_cursor               ; ... set DOS cursor
         mov   ah, [bx.win_txt_attrib]
         mov   cx, 35                   ; prepare to ...
         call  spaces                   ; ... erase old text
         rd_text file_name, 35          ; get filename
         call  hide_cursor
         mov   bl, [file_name.count]    ; bytes read 
         cmp   bl, 0                    ; nothing read?
         je    L0                       ; if so, quit, else ...
         clr   bh
         mov   [file_name + 2 + bx], bh ; ... make buffer ASCIIZ and ...
         or    [FLAGS], bit 14          ; ... mention "FILE SPECIFIED".
         jmp   L0                       ; and exit.
;------------------------

do_go:   push  ax, bx, cx, dx, si, di
         pop   di, si, dx, cx, bx, ax
         ret
;------------------------

FIO_fil: push  ax, si, [scr_x], [scr_y]         ; File I/O fill screen
         mov   ah, [IO_box.win_txt_attrib]      ; bx = -> window info block
         mov   [scr_attr], ah
         mov   si, offset dest_1        ; destination is "File"
         test  [FLAGS], bit 15          ; unless FLAGS-15 is set:
         IF nz mov  si, offset dest_2   ; then destination is "I2C "
         set_y ax, 2
         set_x ax, 10                   ; translate window to full screen
         mov   al, [slave]
         clr   ah                       ; AX = slave address
         mov   [b_digit], 7             ; show 7 ...
         call  show_bin                 ; ... binary digits
         mov   al, ' '
         call  put_char
         set_x ax, 44
         call  print$                   ; print destination
         set_y ax, 3
         set_x ax, 10                   ; prepare to print 4 hex digits ...
         mov   [h_digit], 4
         mov   ax, [burn.from]
         call  show_hex                 ; ... to BURN
         mov   ah, [IO_box.win_txt_attrib]
         mov   al, '-'
         call  put_char
         mov   al, ' '
         call  put_char
         mov   ax, [burn.until]
         call  show_hex                 ; print BURN + 2
         set_y ax, 6
         set_x ax, 10
         mov   ax, [range.from]
         call  show_hex                 ; print RANGE FROM
         mov   ah, [IO_box.win_txt_attrib]
         mov   al, '-'
         call  put_char
         mov   al, ' '
         call  put_char
         mov   ax, [range.until]
         cmp   ax, 0
         jz    >L0
         call  show_hex                 ; print RANGE UNTIL
         jmp   >L1
L0:      mov   si, offset stop_mes      ; "END "
         call  print$
L1:      set_x ax, 10
         set_y ax, 5
         mov   si, offset [file_name.tekst]
         test  [FLAGS], bit 14
         IF nz call  print$             ; if necessary print filename
         pop   [scr_y], [scr_x], si, ax
         ret
;------------------------

L0:      mov   si, offset error_02      ; "error in number entry"
         call  show_error               ; show it
L1:      stc                            ; set carryflag to signal caller
L2:      pop   di, dx, bx       ; restore and ...
         ret                    ; ... exit to caller

L3:      mov   ax, dx           ; prepare to ...
         stosw                  ; ... store it
         clc
         jmp   L2               ; and exit

parse_number:
         push  bx, dx, di       ; si = -> input, di -> output
         inc   si
         cmp   b [si], 0        ; nothing in buffer?
         je    L2               ; if so, leave, ...
         lodsb                  ; ... else get COUNT
         clr   dx               ; set up result
         mov   ch, dh
         mov   cl, al
         call  skip_ws
         dec   si               ; compensate for SKIP_WS
         inc   cx
L4:      jcxz  L3               ; if done, exit, ...
         lodsb                  ; ... else fetch char, ...
         dec   cx               ; ... and adjust counter.
         cmp   al, ' '          ; if separator, ...
         je    >L5              ; ... take action
         cmp   al, '-'
         jne   >L6
L5:      mov   ax, dx           ; store result
         stosw
         clr   dx               ; prime DX
         jmp   L4               ; and continue
L6:      call  make_upper
         mov   bx, offset hex_tbl
         call  table_find
         cmp   bx, 010
         ja    L0
         shl   dx, 4
         or    dx, bx
         jmp   L4
;------------------------

L0:      call  FIO_fil                  ; update screen and ...
L1:      pop   di, si, cx, bx, ax       ; ... exit
         ret

do_burn: push  ax, bx, cx, si, di       ; Enter burn FROM and UNTIL
         mov   bx, offset IO_box
         set_x ax, 10
         set_y ax, 3
         call  set_cursor
         mov   cx, 15
         mov   ah, [bx.win_txt_attrib]
         call  spaces                   ; clear area on screen
         rd_text scratchpad, 16         ; read from it
         call  hide_cursor
         mov   si, offset scratchpad
         mov   di, offset burn
         call  parse_number             ; parse numbers
         jc    L1                       ; if error, mention it
         mov   ax, [burn.from]          ; else check for magnitudes
         mov   cx, [burn.until]
         cmp   ax, cx                   ; if reversed,
         jbe   L0
         mov   [burn.from], cx          ; swap high and low
         mov   [burn.until], ax
         jmp   L0                       ; exit
;------------------------

L2:      mov   ax, [burn.from]          ; if same as BURN, fill it in
         mov   [range.from], ax
         mov   ax, [burn.until]
         mov   [range.until], ax
L0:      call  FIO_fil                  ; update screen
L1:      pop   di, si, cx, bx, ax       ; and get out.
         ret

do_rang: push  ax, bx, cx, si, di       ; set range to read from input-file
         mov   bx, offset IO_box
         set_x ax, 10
         set_y ax, 6
         call  set_cursor
         mov   cx, 15
         mov   ah, [bx.win_txt_attrib]
         call  spaces                   ; clear place on screen
         rd_text scratchpad, 16         ; fetch input
         call  hide_cursor
         mov   si, offset scratchpad
         mov   di, offset range
         cmp   [si.tekst], '='          ; same as BURN range?
         je    L2
         call  parse_number             ; parse numbers
         jc    L1
         call  FIO_fil
         mov   ax, [range.from]
         mov   cx, [range.until]
         cmp   ax, bx
         jbe   L0
         mov   [range.from], cx
         mov   [range.until], ax
         jmp   L0
;------------------------

L2:      mov   si, offset error_02      ; "error in number entry"
         call  show_error
L0:      call  FIO_fil                  ; update screen
         pop   si, dx, cx, bx, ax
         ret

do_slav: push  ax, bx, cx, dx, si       ; set File I/O slave address
         mov   bx, offset IO_box
         set_x ax, 10
         set_y ax, 2
         call  set_cursor
         mov   ah, [bx.win_txt_attrib]
         mov   cx, 9
         call  spaces
         rd_text n_buffer, 8
         call  hide_cursor
         mov   cl, [n_buffer.count]
         clr   ch
         jcxz  L0                       ; nothing entered => leave
         mov   si, offset n_buffer + 2
         clr   ah
L1:      lodsb
         cmp   al, '1'
         ja    L2
         cmp   al, '0'
         jb    L2               ; if out of range, mention it.
         sub   al, '1'          ; else calculate carry, ...
         cmc                    ; ... complement it, ...
         rcl   ah, 1            ; ... and rotate it into place
         loop  L1               ; until done
         mov   [slave], ah
         mov   cl, 8
         sub   cl, [n_buffer + 1]
         cmp   cl, 0
         IF ne shl  ah, cl
         jmp   L0
;------------------------

secret:  push  ax, bx                   ; secret function (joke)
         mov   bx, offset L_box
         call  pop_up_window
         mov   ax, 0C07
         int   021
         call  restore_window
         pop   bx, ax
         ret
;------------------------

L2:      mov   bx, offset IO_box2
         call  pop_up_window
         mov   ax, 0C07
         int   021
L1:      call  restore_window
         pop   si, bx
         ret

I2C_IO:  push  bx, si                   ; process File I/O with I2C hardware
         cmp   [burn.until], 0
         je    L2
         mov   si, offset IO_bar
         call  do_menu_bar              ; show menu-bar
         mov   bx, offset IO_box
         call  pop_up_window            ; show menu-window, and ...
         call  FIO_fil                  ; ... fill it in
         mov   si, offset IO_keys       ; now wait for keypresses, and ...
L0:      call  process_keys             ; ... process them.
         cmp   bx, 0FFFF                ; if ESC, then ...
         je    L1                       ; ... exit
         shl   bx, 1                    ; else, prepare for indexing, ...
         call  IO_act[bx]               ; ... and do indexed CALL before ...
         jmp   L0                       ; ... getting next command.
;------------------------
#ENDIF

plot_it: push  ax                       ; show chips that ACK'ed
         mov   ax, cx                   ; get address
         and   ax, 00F0                 ; mask off bits
         shr   ax, 3                    ; process (addr*2/16)
         add   ax, [bx.win_topleft_x]
         add   al, b [pd_boxx]
         add   ax, 2
         mov   [scr_x], ax              ; and store it
         mov   ax, cx
         and   ax, 000F
         shr   ax, 1
         add   ax, [bx.win_topleft_y]
         add   al, b [pd_boxx + 1]
         mov   [scr_y], ax              ; and store it
         mov   ax, seek_col BY seek_chr
         call  put_char
         pop   ax
         ret
;------------------------

do_seek: push  ax, bx, cx               ; scan the bus for active addresses
         push  [scr_x], [scr_y]
         mov   bx, offset pd_box
         mov   b [pd_boxc + 2], seek_chr
         call  pop_up_window
         mov   cx, 0                    ; set up address counter
L0:      call  S_cond                   ; send START condition,   ...
         mov   ah, cl
         call  i2c_out                  ; ... send address,       ...
         cmp   [BIT_is], "0"            ; ... store value of ACK, ...
         IF  e call  plot_it
         call  P_cond                   ; ... and send a STOP condition.
         add   cx, 2
         cmp   cx, 0100
         jb    L0
         mov   ax, 0C07
         int   021
         call  restore_window
         pop   [scr_y], [scr_x], cx, bx, ax
         ret
;------------------------

L0:      call  restore_window           ; done
         pop   si, bx
         ret

do_manual:                              ; control I2C bus manually
         push  bx, si
         mov   si, offset man_bar
         call  do_menu_bar              ; show menu bar
         mov   bx, offset Manl_box
         call  pop_up_window            ; show menu window
         mov   si, offset man_keys
L9:      call  process_keys             ; wait for keypress
         cmp   bx, 0FFFF                ; if ESC pressed, ...
         je    L0                       ; ... get out.
         shl   bx, 1                    ; else prepare for ...
         call  man_act[bx]              ; ... indexed CALL in table, ...
         jmp   L9                       ; ... until done.
;------------------------

do_port: push  ax, bx, si               ; toggle LPT port number
         mov   bx, offset LPT_box2
         call  pop_up_window            ; show window
         call  zeker?                   ; are we sure?
         pushf
         call  restore_window           ; restore screen
         popf
         jnc   >L71                     ; if NOT SURE, exit
         cmp   w [LPT_total], 1         ; only one LPT-port onboard?
         je    >L7                      ; if so, request is ridiculous, ...
         inc   [LPT_number]             ; ... else inc number.
         call  get_port                 ; try to get an I/O address
         cmp   ax, 0                    ; is it ZERO? ...
         je    >L8                      ; ... revert to LPT1
         mov   [LPT_port], ax           ; else store it
L7:      mov   ax, [LPT_number]         ; get LPT number, ...
         add   al, '0'                  ; ... make it ASCII, ...
         mov   [LPT_is], al             ; ... and store it to ...
         call  fill_LPT_box             ; ... show it on screen.
L71:     pop   si, bx, ax               ; prepare to exit
         call  P_cond                   ; send P-condition by default
         ret                            ; and exit to DOS

L8:      mov   w [LPT_number], 1        ; revert to LPT1
         call  get_port                 ; get I/O address
         mov   [LPT_port], ax           ; store it
         jmp   L7                       ; and process it
;------------------------

tune_w8: push  ax, bx, cx, dx, si, di   ; calibrate the wait-loop for each and every PC clone ...
         mov   w [delay], 0200          ;    ... ever made anywhere in the known part of the universe ...
         mov   di, offset scratchpad    ;    ... and beyond. Japanese PC's excluded.
         mov   si, di
         call  open_2           ; open hi res timer.
         mov   cx, 16           ; repeat 16 times
T0:      call  rd_time          ; read timer, ...
         call  IO_w8            ; ... wait specified time, and ...
         call  rd_time          ; ... read timer again
         loop  T0               ; until 16 done
         call  close_2          ; close hi res timer
         mov   cx, 16
         xor   bx, bx           ; bx = total
T1:      lodsw                  ; get "start" count,
         mov   dx, ax           ; store it
         lodsw                  ; get "end" count, 
         sub   ax, dx           ; determine "time spent"
         neg   ax               ; make positive
         add   bx, ax           ; sum it all up
         loop  T1               ; until done
         shr   bx, 4            ; make it an average
         mov   ax, [delay]      ; get current value
         mul   w [should_be]
         div   bx               ; compose better value
         inc   ax               ; compensate for very  S  L  O  W  machines....
         mov   [delay], ax      ; store new value
         pop   di, si, dx, cx, bx, ax
         ret
;------------------------

copyrights:
         mov   dx, offset copyleft      ; show copyleft message
         mov   cx, lendon
         mov   bx, stdout
         mov   ah, 040
         int   021
         ret
;------------------------

find_LPT:                       ; auto determine LPT port to which I2C card is connected
         call  P_cond           ; send STOP condition
         call  rd_SCL           ; read SCL (should be HIGH <=> carry is set)
#IF DEBUG
         mov   w [LPT_number], DEBUG_LP
         mov   w [LPT_port], DEBUG_IO
#ELSE
         jnc   >F1              ; if no carry, then also no I2C interface, ...
         call  rd_SDA           ; ... else read SDA
         jnc   >F1              ; no carry? then go to next LPT port.
#ENDIF
F0:      mov   ax, [LPT_number] ; LPT port found!
         add   al, '0'          ; make it ASCII
         mov   [LPT_is], al     ; store it
         call  fill_LPT_box     ; show it
         ret                    ; and quit.

F1:      inc   [LPT_number]     ; select next printerport
         call  get_port         ; get I/O address
         cmp   ax, 0            ; is it NULL?
         je    >F2              ; if so, show message ...
         mov   [LPT_port], ax   ; ... else store it, ...
         jmp   find_lpt         ; ... and retest I/F

F2:      call  cls
         mov   dx, offset mess002       ; = "No replying card"
         mov   cx, len002
         mov   bx, stdout
         mov   ah, 040
         int   021

#IF PAYWARE
         mov   ax, 04C02        ; commercial version exits here
         int   021
#ELSE
         mov   ax, 0C07         ; try-out version waits for key-press, ...
         int   021
         mov   [LPT_number], 1  ; ... and defaults to LPT1
         push  es
         clr   ax
         mov   es, ax
      es mov   ax, [0408]
         mov   [LPT_port], ax
         pop   es
         jmp   F0
#ENDIF
;------------------------

do_chip: push  ax, bx, dx, si, di       ; look up chipname
         mov   bx, offset chip_box
         call  pop_up_window
         or    w [FLAGS], bit 5
         set_x ax, 9
         set_y ax, 1
         call  set_cursor       ; position DOS cursor
         rd_text buffer1, 17    ; get text via INT 021/0A
         mov   si, dx
         call  hide_cursor
         clr   ah
         mov   al, [si.count]   ; ax = counter
         cmp   ax, 0            ; buffer empty?
         jz    >L0              ; if so, exit, ...
         add   si, 2            ; ... else point to data, ...
         push  si               ; ... save pointer, ...
         add   si, ax           ; ... point to end of buffer, ...
         mov   b [si], ' '      ; ... and store a SPACE.
         pop   si               ; restore pointer
         call  set_dev          ; find device, ...
L0:      call  restore_window   ; ... and exit.
         test  w [FLAGS], bit 5 + BIT 7
         IF  z call  showdev            ; if no error, show device parameters on screen
         pop   di, si, dx, bx, ax
         ret
;------------------------
#IF PAYWARE

INT_chk: push  ds, cs                   ; let TimerTick check for INT-bit
         pop   ds                       ; restore registers
         test  [FLAGS], bit 9           ; shall we refresh?
         jnz   >L0                      ; if in a mighty low window, don't refresh!
         push  ax, bx, dx, [scr_x], [scr_y]
         mov   dx, [LPT_port]
         inc   dx
         in    al, dx                   ; Measure INT-line status
         shl   al, 2                    ; Store value in carry flag
         mov   al, '0'
         adc   al, 0                    ; make it ASCII
         mov   ah, [INT_box.win_txt_attrib]
         mov   bx, [INT_box.win_text]
         mov   dx, [INT_box.win_topleft_x]
         add   dl, [bx]
         add   dx, 6
         mov   [scr_x], dx
         mov   dx, [INT_box.win_topleft_y]
         add   dl, [bx+1]
         mov   [scr_y], dx
         call  put_char
         pop   [scr_y], [scr_x], dx, bx, ax
L0:      pushf                          ; compensate for IRET
         call  [old_timer]              ; call Int routine
         pop   ds                       ; restore registers, ...
         iret                           ; ... and exit.
;------------------------
#ENDIF

init:    push  ds, es
         call  tune_w8              ; tune waitloop
         mov   cx, last_byte
         mov   di, offset v_mode
         sub   cx, di
         clr   al
         rep   stosb                ; zero all floating variables
         mov   b [scr_attr], 01A
         mov   b [slave], 0A0
         mov   w [digits], 6
         mov   w [b_digit], 8
         mov   w [h_digit], 4
         mov   w [delay], 0100
         mov   w [rd_num], 16
         mov   w [page_size], 2     ; prime important data
         mov   ah, 0F
         int   010              ; get video mode
         cmp   al, 7            ; if Hercules, ...
         je    >L4              ; do not set co80
         mov   ax, 3
         int   010              ; set mode 3: VGA colour 80 x 25
L4:      call  get_mode         ; set video parameters
         clr   ax
         mov   es, ax
         mov   bx, 0408
L0:   es cmp   w [bx], 0        ; determine number of LPT ports
         jz    >L1
         add   bx, 2
         inc   [LPT_total]      ; adjust counter
         jmp   L0
L1:      cmp   [LPT_total], 0   ; if no LPT ports present, ...
         jne   >L2
         mov   dx, offset mess001       ; ... mention it, ...
         mov   cx, len001
         mov   bx, stdout
         mov   ah, 040
         int   021

#IF PAYWARE
         call  copyrights       ; ... show the copyrights, ...
         mov   ax, 04C01
         int   021              ; ... and exit to DOS.
#ELSE
         mov   ax, 0C07         ; Try-out version waits for key, ...
         int   021
         mov   ax, 0378         ; ... and sets dummy port address.
      es mov   [0408], ax
#ENDIF

L2:   es mov   ax, [0408]       ; get data from active LPT-port
         mov   [LPT_port], ax
         mov   w [LPT_number], 1
         mov   es, ds
         call  find_LPT         ; auto detect I2C interface, ...
         call  hide_cursor      ; ... hide cursor and ...

#IF PAYWARE
         mov   ax, 0351C
         int   021                  ; get current TimerTick pointer
         mov   w [old_timer], bx
         mov   w [old_timer][2], es ; store it
         mov   ds, cs
         mov   dx, offset INT_chk
         mov   ax, 0251C
         int   021                  ; and install new one
#ENDIF
         pop   es, ds
         ret                    ; ... leave INIT procedure.
;------------------------

main:    mov   ax, cs               ; main routine
         add   ax, (last_byte + 15)/16
         mov   ss, ax               ; set up SS, ...
         mov   sp, 0FFFE            ; ... and SP
         call  init                 ; initialise system, ...
         clr   bp                   ; ... frame pointer, ...
         call  cls
         call  fill_main_screen
         call  fill_SDA_box
         call  fill_SCL_box
         call  fill_BIT_box
         mov   bx, offset INT_box   ; ... and INT box, ...
         call  make_box
         mov   si, [bx.win_text]
         call  pr_win
         mov   bx, offset I2C_box   ; ... and I2C status box, ...
         call  make_box
         mov   si, [bx.win_text]
         call  pr_win
         call  fill_LPT_box         ; ... and screen.
         call  P_cond               ; init I2C bus
L8:      mov   si, offset main_bar
         call  do_menu_bar          ; show menu bar
         mov   si, offset main_keys
         call  process_keys         ; check for keypress
         cmp   bx, 0FFFF            ; ESC pressed?
         jne   >L9
         call  do_quit              ; ask for acknowledgement
         test  w [FLAGS], bit 1
         jnz   TO_DOS
         jmp   L8                   ; repeat

L9:      shl   bx, 1                ; make pointer
         call  main_act[bx]         ; index into action-table
         test  w [FLAGS], bit 1     ; EXIT requested?
         jz    L8

TO_DOS:  call  P_cond
#IF PAYWARE
         push  ds
         mov   dx, w [old_timer]
         mov   ds, w [old_timer][2]
         mov   ax, 0251C
         int   021                  ; restore old TimerTick
         pop   ds
#ENDIF
         mov   b [scr_attr], 01E
         mov   w [scr_x], 0
         mov   w [scr_y], 0
         call  set_cursor
         call  cls
         call  copyrights
         mov   ax, 04C00
         int   021
;------------------------

box_01   db  'Ŀ '        ; define different types of boxes
box_02   db  'ͻ ͼ'
box_03   db  '͸ ;'
box_04   db  'ķ Ľ'
box_05   db  '=| |='
box_06   db  ' '
box_07   db  'ų '
box_08   db  'κ '
box_09   db  '#=# #=#'
box_10   db  '³ '
box_11   db  '³ '
box_12   db  'Ĵ '
box_14   db  'ѳ '
box_15   db  'ѳ '
box_16   db  '͸ ;'

         even
SDA_box: venster 0, 21, 11, 3, 01E, 01D, box_10, $+2
         db  2, 1, 'SDA = '
SDA_is   db  '1', 0
         db  0F0

         even
SCL_box: venster 10, 21, 11, 3, 01E, 01D, box_11, $+2
         db  2, 1, 'SCL = '
SCL_is   db  '1', 0
         db  0F0

         even
BIT_box: venster 20, 21, 11, 3, 01E, 01F, box_11, $+2
         db  2, 1, 'BIT = '
BIT_is   db  '1', 0
         db  0F0

         even
INT_box: venster 30, 21, 11, 3, 01E, 01F, box_11, $+2
         db  2, 1, 'INT = '
         db  '1', 0
         db  0F0

         even
I2C_box: venster 40, 21, 24, 3, 01E, 01D, box_11, $+2
         db   2, 1, 'I2C status = ', 0
         db  0F0

STOP_txt db  15, 1, '-STOP-', 0
         db  0F0

STRT_txt db  15, 1, 'START ', 0
         db  0F0

ACK_txt  db  15, 1, ' ACK  ', 0
         db  0F0
                
NACK_txt db  15, 1, 'no ACK', 0
         db  0F0

         even
LPT_box: venster 63, 21, 17, 3, 01E, 01D, box_12, $+2
         db  2, 1, 'LPT-port = '
LPT_is   db  '1', 0
         db  0F0

         even
hexin_box: venster 20, 8, 16, 3, 0A, 0E, box_04, $+2
         db  2, 1, 'Hex:', 0
         db  0F0

main_bar db  0E, 03, 016        ; MAIN MENU title bar
#IF ENGLISH
          db  'I2C_experiments: Manual Chip File Port Seek Esc_=_exit', 0
main_keys db  'MPFQXCS', 0
#ELSEIF GERMAN
          db  'I2C_Versuchungen: Hand Chip Datei Port Suche ESC_=_stop', 0
main_keys db  'HPDQXCS', 0
#ELSE
          db  'I2C_experimenten: Hand Chip Bestand Poort Zoek ESC_=_stop', 0
main_keys db  'HPBQXCZ', 0
#ENDIF

         even
#IF PAYWARE
main_act dw  do_manual, do_port, I2C_IO, do_quit, do_quit, do_chip
         dw  do_seek
#ELSE
main_act dw  do_manual, do_port, NO_GO, do_quit, do_quit, do_chip
         dw  do_seek
#ENDIF

         even
main_box: venster 0, 1, 80, 23, 01E, 01F, box_01, $+2           ; main screen layout
         db   2,  1, '   ', 0
         db   3,  2,  '      ', 0
         db   3,  3,  '    ', 0
         db   3,  4,  '        ', 0
         db   2,  5, '       ', 0

#IF ENGLISH
         db  20,  1, 'Control I2C logic from the LPT port.', 0
         db  20,  2, 'Knowledge of I2C chips is a must to be able to work', 0
         db  20,  3, 'succesfully with this software.', 0
#ELSEIF GERMAN
         db  20,  1, 'Meistern von I2C Bausteine ber die LPT Porte.', 0
         db  20,  2, 'Kentnis von I2C Chips ist eine Voraussetzung um mit diesen', 0
         db  20,  3, 'Programm arbeiten zu knnen.', 0
#ELSE
         db  20,  1, 'Besturen van I2C logica vanaf de LPT poort.', 0
         db  20,  2, 'Kennis van I2C chips is een vereiste om met dit programma', 0
         db  20,  3, 'te kunnen werken.', 0
#ENDIF
         db  20,  5, 'M O N I T O R  &  D E B U G G E R', 0
dev_x    db   5,  7,  'device     Pr  words   bit  page   ms  B/P', 0
         db   4,  8, '              ', 0

         db  11, 15, 'Ŀ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ ڿ   SCL', 0
         db  15, 16,     '', 0
         db  11, 17, 'Ŀ Ŀ  Ŀ     Ŀ  Ŀ               SDA', 0
         db  14, 18,    '                       ', 0
         db  13, 19,   'S    1  1  0  1  0  0  1  1 ACK 1  1  1  1  0  0  0  0   P   I2C', 0

         db   1, 23, 'Copyrights 1996-1998:', 0
         db  26, 23, 'Jan Verhoeven', 0
         db  44, 23, 'NL-5012 GH  272', 0
         db  63, 23, '[aklasse@tip.nl]', 0

         db  0F0

         even
LPT_box2: venster 30, 4, 30, 3, 0E, 0F, box_01, $+2
#IF ENGLISH
         db   5, 1, 'Change printerport?', 0
         db  0F0
#ELSEIF GERMAN
         db   5, 1, 'ndern LPT Porte?', 0
         db  0F0
#ELSE
         db   5, 1, 'Andere printerpoort?', 0
         db  0F0
#ENDIF

         even
no_go_box: venster 12, 18, 45, 4, 0E, 08C, box_08, $+2
#IF ENGLISH
         db   3, 1, 'Not implemented in shareware version.', 0
         db   3, 2, 'Please invest $25 in software.', 0
         db  0F0
#ELSEIF GERMAN
         db   3, 1, 'Nicht mglich in shareware Ausgabe.', 0
         db   3, 2, 'Bitte registrieren Sie.', 0
         db  0F0
#ELSE
         db   3, 1, 'Niet ondersteund in shareware versie.', 0
         db   3, 2, 'Registreer U daarom, AUB.', 0
         db  0F0
#ENDIF

         even
Manl_box: venster 17, 2, 54, 7, 0D, 0F, box_03, $+2
#IF ENGLISH
         db   4,  1, 'H - Hex entry', 0
         db   4,  2, 'D - Toggle SDA', 0
         db   4,  3, 'C - Toggle SCL', 0
         db   2,  4, 'TAB - Strobe SCL', 0
         db  21,  1, 'R - Read byte', 0
         db  21,  2, 'A - ASCII out', 0
         db  21,  3, 'S - force START condition', 0
         db  21,  4, 'P - force  STOP condition', 0
         db  40,  1, '0 - Send "0"', 0
         db  40,  2, '1 - Send "1"', 0
#IF PAYWARE
         db   4,  5, '@ - Read buffer', 0
         db   21, 5, '# - Write several', 0
#ENDIF
         db  40,  6, ' ESC = exit ', 0
         db  0F0
#ELSEIF GERMAN
         db   4,  1, 'H - Hex eingabe', 0
         db   4,  2, 'D - Toggle SDA', 0
         db   4,  3, 'C - Toggle SCL', 0
         db   2,  4, 'TAB - SCL Puls', 0
         db  21,  1, 'R - Byte lesen', 0
         db  21,  2, 'A - ASCII aus', 0
         db  21,  3, 'S - Gib START kondition', 0
         db  21,  4, 'P - Gib  STOP kondition', 0
         db  40,  1, '0 - Sende "0"', 0
         db  40,  2, '1 - Sende "1"', 0
#IF PAYWARE
         db   4,  5, '@ - Lese Puffer', 0
         db   21, 5, '# - Sende Reihe', 0
#ENDIF
         db  40,  6, ' ESC = Stop ', 0
         db  0F0
#ELSE
         db   4,  1, 'H - Hex invoer', 0
         db   4,  2, 'D - Flip SDA', 0
         db   4,  3, 'C - Flip SCL', 0
         db   2,  4, 'TAB - SCL puls', 0
         db  21,  1, 'R - Lees byte', 0
         db  21,  2, 'A - ASCII uit', 0
         db  21,  3, 'S - maak START conditie', 0
         db  21,  4, 'P - maak  STOP conditie', 0
         db  40,  1, '0 - Zend "0"', 0
         db  40,  2, '1 - Zend "1"', 0
#IF PAYWARE
         db   4,  5, '@ - Lees buffer', 0
         db   21, 5, '# - Stuur reeks', 0
#ENDIF
         db  40,  6, ' ESC = stop ', 0
         db  0F0
#ENDIF

man_bar  db  0E, 0B, 7B
#IF ENGLISH
         db  'MANUAL_MODE: press_the_correct_keys ESC_to_exit', 0
#ELSEIF GERMAN
         db  'HAND_BEDIENUNG: Drck_die_richtige_Taste ESC_=_Exit', 0
#ELSE
         db  'HAND_BEDIENING: Druk_de_juiste_toets ESC_is_stop', 0
#ENDIF

man_keys db  'DCSP01HRA@2#3', tab, 0
         even

#IF PAYWARE
man_act  dw  togl_SDA, togl_SCL, S_cond, P_cond, send_0, send_1, do_xmit, do_read, do_asci
         dw  rd_str, rd_str, wr_str, wr_str, clock
#ELSE
man_act  dw  togl_SDA, togl_SCL, S_cond, P_cond, send_0, send_1, do_xmit, do_read, do_asci
         dw  NO_GO, NO_GO, NO_GO, NO_GO, clock
#ENDIF

         even
Exit_box: venster 10, 5, 22, 3, 07, 0E, box_02, $+2
#IF ENGLISH
         db   2,  1, 'Back off to DOS!', 0
#ELSEIF GERMAN
         db   2,  1, 'Abhauen zum DOS!', 0
#ELSE
         db   2,  1, 'Wegwezen naar DOS!', 0
#ENDIF
         db  0F0

         even
zeker_box: venster 15, 10, 25, 3, 0Ah, 0Fh, box_04, $+2
#IF ENGLISH
         db   2, 1, 'Are we sure?', 0
         db  14, 2, ' Yes / No ', 0 
#ELSEIF GERMAN
         db   2, 1, 'Ganz sicher?', 0
         db  14, 2, ' Ja / Nein ', 0 
#ELSE
         db   2, 1, 'Weten wij dit zeker?', 0
         db  14, 2, ' Ja / Nee ', 0 
#ENDIF
         db  0F0

         even
rcv_box: venster 40, 10, 24, 7, 0E, 0D, box_04, $+2
#IF ENGLISH
         db   3, 1, 'Receive byte', 0
#ELSEIF GERMAN
         db   3, 1, 'Lese byte', 0
#ELSE
         db   3, 1, 'Lees byte', 0
#ENDIF
         db   1, 2, '----------------------', 0
         db   3, 3, 'hex:', 0
         db  14, 3, 'ASCII:', 0
         db   3, 4, 'bin:', 0
         db   2, 5, 'TAB = ACK  ESC = STOP', 0
         db  0F0

         even
asc_box: venster  3, 10, 75, 5, 02E, 02F, box_04, $+2
#IF ENGLISH
         db   2, 1, 'Enter ASCII data (max. 64 chars)', 0
#ELSEIF GERMAN
         db   2, 1, 'ASCII Eingabe (max. 64 Zeichen)', 0
#ELSE
         db   2, 1, 'Voer ASCII data in (max. 64 tekens)', 0
#ENDIF
         db   1, 2, '-----------+---+---+---+---+---+---+---+---+--------', 0
         db   2, 3,  'Text :', 0
         db  0F0

#IF PAYWARE
         even
wr_box:  venster 3, 10, 53, 5, 070, 071, box_03, $+2
         db   2, 1, 'Enter parameters: 0xx = hex, .xx = dec, Bxx = bin', 0
         db   1, 2, '---------------------------------------------------', 0
         db   2, 3,  'Input :', 0
         db  0F0

         even
rd_box:  venster 8, 8, 62, 6, 07, 0E, box_01, $+2
         db   2, 1, 'Read I2C data.', 0
         db   2, 3, 'ASCII :', 0
         db   4, 4,   'HEX :', 0
         db   4, 5, ' TAB = ACK ', 0
         db  49, 5, ' ESC = STOP ', 0
         db  0F0

nr_table db  "$0#.bB'", '"', 0
         even
nr_act   dw  cvt_hex, cvt_hex, cvt_dec, cvt_dec, cvt_bin, cvt_bin
         dw  cvt_txt, cvt_txt

IO_box:  venster 10, 11, 50, 8, 0A, 0E, box_01, $+2
         db   7, 0, ' File I/O mode ', 0
         db   2, 2, 'Slave :', 0
         db   2, 3, 'Burn  :', 0
         db   2, 5, 'File  :', 0
         db   2, 6, 'Range :', 0
         db  30, 6, 'GO', 0
         db  45, 6, 'ESC', 0
         db  30, 2, 'Destination :', 0
         db  0F0

IO_bar   db  0E, 0A, 7B
         db  'FILE_IO: Burn Dest File Go Range Slave ESC_=_exit', 0
IO_keys  db  'BDFGRSJV', 0
         even
IO_act   dw  do_burn, do_dest, do_file, do_go, do_rang, do_slav
         dw  secret, secret
dest_1   db  'FILE', 0
dest_2   db  'I2C ', 0
stop_mes db  'End ', 0

IO_box2: venster 20, 8, 50, 6, 0A, 0E, box_03, $+2
#IF ENGLISH
         db   3,  1, "You haven't yet specified a chip type!", 0
         db   2,  3, "Please do this prior to calling file I/O.", 0
#ELSEIF GERMAN
         db   4,  1, "Noch kein Chip-Typ gewhlt!", 0
         db   2,  3, "Bitte eingeben und dann file I/O starten.", 0
#ELSE
         db   4,  1, "Je hebt nog geen chipsoort opgegeven!", 0
         db   2,  3, "Doe dit, alvorens bestands I/O te starten.", 0
#ENDIF
         db  0F0
#ENDIF

pd_box:  venster 22, 5, 40, 15, 03E, 03B, box_04, $+2
#IF ENGLISH
         db  16,  1, 'HIGH nibble', 0
#ELSEIF GERMAN
         db  16,  1, 'BITS 4 bis 7', 0
#ELSE
         db  16,  1, 'HOGE bits', 0
#ENDIF
         db   6,  2,   '0 1 2 3 4 5 6 7 8 9 A B C D E F', 0
         db   5,  3,  'ѻ', 0
pd_boxx  db   4,  4, '0. . . . . . . . . . . . . . . .', 0
         db   4,  5, '2. . . . . . . . . . . . . . . .', 0
         db   4,  6, '4. . . . . . . . . . . . . . . .', 0
         db   4,  7, '6. . . . . . . . . . . . . . . .', 0
         db   4,  8, '8. . . . . . . . . . . . . . . .', 0
         db   4,  9, 'A. . . . . . . . . . . . . . . .', 0
         db   4, 10, 'C. . . . . . . . . . . . . . . .', 0
         db   4, 11, 'E. . . . . . . . . . . . . . . .', 0
         db   5, 12,  'ϼ', 0
#IF GERMAN
pd_boxc  db  20, 13, ' = Gefunden', 0
#ELSE
pd_boxc  db  20, 13, ' = present', 0
#ENDIF
         db  0F0

chip_box: venster 10, 4, 30, 3, 04E, 04F, box_03, $+2
         db   2, 1, 'CHIP :', 0
         db  0F0

L_box: venster 30, 15, 30, 6, 06E, 06D, box_08, $+2
         db   2, 2, 'This is a SECRET option', 0
         db   4, 3, 'Enjoy your R I D E ...', 0
         db  0F0

error_window: venster 10, 18, 60, 5, 07C, 07E, box_04, $+2
#IF ENGLISH
         db   2, 1, 'E R R O R ...', 0
         db  24, 4, ' Press almost any key to continue ', 0
         db  0F0

error_01 db   3, 2, '... in type specifier. Operation aborted', 0
         db  0F0
error_02 db   3, 2, '... in number entry. Operation aborted', 0
         db  0F0
error_03 db   3, 2, '... opening file', 0
         db  0F0
error_04 db   3, 2, '... device not in list', 0
         db  0F0
error_05 db   3, 2, '... wrong protocol', 0
         db  0F0
error_06 db   3, 2, '... I2C01.CFG not found', 0
         db  0F0
#ELSEIF GERMAN
         db   2, 1, 'F E H L E R', 0
         db  22, 4, ' Bitte Taste drcken ', 0
         db  0F0

error_01 db   3, 2, '... in Type-Bezeichnung. Auftrag beendet', 0
         db  0F0
error_02 db   3, 2, '... falsches Ziffer. Auftrag beendet', 0
         db  0F0
error_03 db   3, 2, '... bei ffnen Datei', 0
         db  0F0
error_04 db   3, 2, '... unbekannter Chip', 0
         db  0F0
error_05 db   3, 2, '... kein I2C chip', 0
         db  0F0
error_06 db   3, 2, '... I2C01.CFG Datei nicht da', 0
         db  0F0
#ELSE
         db   2, 1, 'F O U T ...', 0
         db  24, 4, ' Druk op een toets ', 0
         db  0F0

error_01 db   3, 2, '... in soortaanduiding. Opdracht afgebroken', 0
         db  0F0
error_02 db   3, 2, '... in soort cijfer. Operatie gestopt', 0
         db  0F0
error_03 db   3, 2, '... bij openen bestand', 0
         db  0F0
error_04 db   3, 2, '... chip onbekend', 0
         db  0F0
error_05 db   3, 2, '... geen I2C chip', 0
         db  0F0
error_06 db   3, 2, '... I2C01.CFG file ontbreekt', 0
         db  0F0
#ENDIF

copyleft db  'This software was developed by Jan Verhoeven, zipcode NL-5012 GH 272.', cr, lf
         db  'In case you want to know more about this or other programs, just write to', cr, lf, lf
         db  tab, tab, 'Jan Verhoeven', cr, lf
         db  tab, tab, 'NL-5012 GH  272         E-mail :   aklasse@tip.nl', cr, lf, lf
         db  'The address may look incomplete, but it is sufficient for the Dutch postal', cr, lf
         db  'services to succesfully deliver any mail.', cr, lf, lf
         db  'I want to thank Arizona Microchip Technology for their help ideas and samples.', cr, lf
         db  'The way in which we have worked together has assured further development.', cr, lf
         db  'Many thanks as well to Atmel and Alcom Electronics for supplying samples.', cr, lf, lf
lendon  equ $ - copyleft

#IF ENGLISH
mess001  db  'According to your BIOS tables, there are no LPT ports in this system.', cr, lf
         db  'In my infinite wisdom, I have therefore decided to abort myself.', cr, lf, lf
         db  'Bye!', cr, lf, lf
#ELSEIF GERMAN
mess001  db  'Wenn die DOS-Tabellen recht haben, gibt es gar keine LPT Schnittstellen in', cr, lf
         db  'diesen Rechner. Deshalb werde ich hier haltmachen.', cr, lf
         db  'Tschss!', cr, lf, lf
#ELSE 
mess001  db  'Volgens de tabellen van DOS zit er hier helemaal geen printerpoort in.', cr, lf
         db  'In mijn oneindige wijsheid heb ik daarom besloten om te stoppen.', cr, lf, lf
         db  'Houdoe!', cr, lf, lf
#ENDIF
len001 equ $ - mess001

#IF ENGLISH
mess002  db  'There is no replying I2C card on any LPT port in this computer system...', cr, lf
         db  'Please check all connections and the power supply of the I2C card.', cr, lf, lf
#ELSEIF GERMAN
mess002  db  'Es gibt keine antwortende I2C Schnittstellen an diesen Rechner.', cr, lf
         db  'Bitte sehen Sie alle Anschlsse und die Versorgungsspannung nach.', cr, lf, lf
#ELSE
mess002  db  'Ik krijg geen antwoord van een aangesloten I2C kaart...', cr, lf
         db  'Controleer alle aansluitingen en de voeding van de I2C kaart.', cr, lf, lf
#ENDIF

#IF PAYWARE
         db  'Good luck!', cr, lf
#ELSEIF GERMAN
         db  'Viel Spa im bung!', cr, lf
#ELSE
         db  'Enjoy your dry run!', cr, lf
#ENDIF
len002 equ $ - mess002

video_columns   dw  40, 40, 80, 80, 0, 0, 0, 80, 80, 132, 132, 132
video_rows      dw  25, 25, 25, 25, 0, 0, 0, 25, 60,  60,  25,  43
video_math      dw  mul_40, mul_40, mul_80, mul_80, 0, 0, 0
                dw  mul_80, mul_80, mul_132, mul_132, mul_132
video_page_size dw  0800, 00800, 01000, 01000, 00000, 00000
                dw  0000, 01000, 04000, 04000, 02000, 04000
video_shifters  db  11, 11, 12, 12, 0, 0, 0, 12, 14, 14, 13, 14
   


Page created on 17 May 2014 and