Little Big LAN messaging API

Starting with version 2.0h, Little Big LAN has a much simpler method of providing programmers with communication between nodes than what the low level drivers provide. To send and receive message level packets you can use a DOS INT 21h call, function 37C0h.


        AX = 37C0h
        DX - 1001h
        DS:SI --> packet
        do an INT 21h

        Carry clear if sent successfully
The packet pointed to by DS:SI must have the following format:

        ; ds:si--> dw type          ;+0  master owner TSR
        ;          db target node   ;+2
        ;          dw len           ;+3  (of remainder of packet)
        ;          db ? dup(?)      ;+5  data - user specific
The byte at +2 (target node) is the node to which you are sending the packet. If it is 0 then the message will be sent to all nodes.

The word at +0 (type) is used by the system to permit different TSRs or user programs to use the same messaging system to send and receive messages meant for it only. The TSR must register its type with the system. The type number is completely arbitrary but type 0 has a special meaning so must not be used.

To register your type:

        AX = 37C0h
        DX - 1002h
        DS:SI --> type description
        do an INT 21h


Type description format:


        ; ds:si--> dw flags         ;+0
        ;          dw type          ;+2  master owner
        ;          dd code address  ;+4  address called when a packet comes
        ;          dd ring address  ;+8  where to put it
        ;          dw ring bufsize  ;+12
        ;          dd link          ;+14  (used by system)

Now, when a message packet is received, the message server (in NET21) will get the message type field, then look through the type list for a match. If found, the code address is called (far call) and DS:SI points to the message (exact format as sent), and es:di points to a buffer to be sent in answer. If, you set ax>0 then no data will be returned.

If there is no code address, the data is copied to the ring address (+6) and the least significant bit of the flags is set. The user must poll that bit and take care of the ring. The first two bytes of the ring buffer is a flags word. The least significant bit is set when a packet is put there, and the next double word points to the next ring buffer. So the actual packet is placed at ring buffer start +6. When that buffer is filled, the ring address advances to the next ring buffer. It is up to the polling program to get packets out of the ring faster than the system receives them. If there is an overflow, the second bit of ths ring's flag is set. When the buffer is emptied, those two lower bIts must be cleared by the user.

                RING example:  (four 100 byte buffers)

ringbuf0        dw 0
                dw ringbuf1
                dw seg ringbuf1
                db 100 dup(?)

ringbuf1        dw 0
                dw ringbuf2
                dw seg ringbuf2
                db 100 dup(?)

ringbuf2        dw 0
                dw ringbuf3
                dw seg ringbuf3
                db 100 dup(?)

ringbuf3        dw 0
                dw ringbuf0
                dw seg ringbuf0
                db 100 dup(?)
To unregister your type:


        AX = 37C0h
        DX - 1003h
        DS:SI --> type description
        do an INT 21h

        (the type description is unlinked from the system list)

        Now the application can terminate.

Keep going for Source Code, or...

Top of Programmer Stuff -or- Main Table of Contents -or- Top of Form


Messaging API source example

        title tmsg.asm


cseg    segment para public 'code'

        org 100h

        assume cs:cseg,ds:cseg,es:nothing

; This simple test program demonstrates how to send LBL messages
; to your program running on a remote node.
;
;
; SYNTAX:
;
;    d>TMSG targnode
;
;    targnode is the node you are communicating with 
;
;    This program will send the keys you type to the
;    target node.  It's an ultra-simple chat program.
;
;
; Batch to assemble this code:
;
;   asm tmsg,tmsg,nul,nul
;   link tmsg
;   del tmsg.obj
;   exe2bin tmsg, tmsg.com
;   del tmsg.exe



entry   proc far


su1:
  jmp init


targnode db 0  ;node to send to
gotit    db 0  ;flag indicates we got a message
gotkey   dw 0  ;message (one keystroke)
gotcnt   dw 0  ;msg len

;data stucture you must supply to LBL:

mytype dw 0 ;flags
       dw 1 ;type (user number indicates ownership if these packets)
       dw 0 ;exec ofs ;upcall routine
       dw 0 ;exec seg
       dw 0 ;ring buffer ;not used here
       dw 0 ; "
       dw 0 ; "
       dw 0 ;link  ;internal use
       dw 0 ; "

buffer1 db 100 dup(0)

;........................... got_msg .....................................

;Called by LBL when the packet type you have registered is received

got_msg:

;fill in the answer packet pointed to by es:di
;this will be sent back to the original sender

  mov word ptr es:[di],1  ;msg owner "type"
  mov al,0FFh
  mov byte ptr es:[di+2],al ;node
  mov word ptr es:[di+3],6  ;count
  mov word ptr es:[di+5],"et"  ;data (dummy)
  mov word ptr es:[di+7],"ts"

;now process the received message

  mov ax,[si+3]
  mov cs:gotcnt,ax

  mov ax,[si+5]
  mov cs:gotkey,ax
  mov es:[di+9],ax

  or cs:gotit,1 ;set a flag, handle packet later
  xor ax,ax

  ret  ;this is a far return

entry  endp



subs proc near

;........................... register ................................

;register our "type" so that all messages of this type are sent to us.

register:
  push cs
  pop ds
  mov si,offset mytype
  mov ax,offset got_msg
  mov [si+4],ax
  mov [si+6],cs
  mov dx,1002h ;register
  mov ax,37c0h
  int 21h
  ret

;........................... unregister ...................................

;stop receiving our messages (because our code will be gone!)

unregister:
  mov dx,1003h ;unregister
  mov ax,37c0h
  push cs
  pop ds
  mov si,offset mytype
  int 21h
  ret

;........................... sendmsg ................................

;send the message in buffer1

sendmsg:
  push cs
  pop ds
  mov si,offset buffer1
  mov dx,1001h
  mov ax,37c0h
  int 21h
  ret


;........................... init .......................................


init:
  call register ;start receiving messages

  call parms  ;get the targnode

  push cs
  pop ds
  mov dx,offset string1  ;signon
  mov ah,9
  int 21h

;. . . . . . . . . . . . . . MAIN LOOP:

init2:
  sti
  mov ah,1                   ;get kb status
  int 16h                    ;any key waiting?
  jnz init3                  ;no:    yes-->

  cli                        ;ints off for test, etc...
  test cs:gotit,1            ;got a packet?
  jz init2                   ;yes:     no--> loop back

;. . . . . . . . . . . . . . got a packet

  mov cs:gotit,0             ;for next
  push cs:gotkey             ;save key
  sti                        ;ints okay

  mov ax,cs:gotkey           ;print key
  mov ah,14
  int 10h

;  mov ax,cs:gotcnt
;  call hexax
;  call crlf


  pop ax                     ;restore the key
  cmp al,13                  ;was a CR?
  jne init2                  ;yes:   no-->

  mov ah,14                  ;do a LF too
  mov al,10
  int 10h

  jmp init2                  ;loop back for more-->


;. . . . . . . . . . . . . . got a keystroke

                             ;1st, print it locally
init3:
  mov ah,0                   ;get the key
  int 16h

  cmp al,27                  ;is esc key?
  je init9                   ;no:   yes-->

  push ax                    ;save key
  mov ah,14                  ;print it
  int 10h
  pop ax
  push ax
  cmp al,13                  ;is CR?
  jne init4                  ;no-->

  mov ah,14                  ;do LF
  mov al,10
  int 10h

;. . . . . . . . . . . . . . send the keystroke

init4:
  push cs
  pop ds
  mov si,offset buffer1
  mov ax,1                   ;our type
  mov [si],ax
  mov al,targnode            ;to ...
  mov [si+2],al
  mov cx,22                   ;message length
  mov [si+3],cx

  pop ax                     ;keystroke
  mov word ptr [si+5],ax
  
  call sendmsg               ;send it

; (you could check return packet for same key)

  jmp init2                  ;loop back



init5:

init9:
  call unregister
  mov ax,4c00h
  int 21h


;........................... parms ....................................

parms:
  cld
  push cs
  pop ds
  xor cx,cx                  ;cx will = # of chars in line
  mov si,81h                 ;ds:si --> command line
  mov cl,[si-1]              ;get count

parms2:
  call nxtchar
  jc parms9
  cmp al," "
  je parms2

  dec si
  inc cx

  call getnum
  mov cs:targnode,dl


parms9:
  ret



nulsep db " "
;........................... Next char ........................................

nxtchar:
  jcxz nc10                  ;if no more chars in buffer -->
  lodsb                      ;get next char
  cmp al,0                   ;fix:1/3/90,  toshiba 2.11 like ti
  jne nc1
  mov al,0
  xchg al,cs:nulsep

nc1:
  dec cx                     ;decrement count remaining
  cmp al,13                  ;is CR ?
  jz nc9                     ;no:               yes-->
  cmp al,10                  ;is LF ?
  jz nc9                     ;no:               yes-->
  cmp al,9                   ;is tab?
  je nc8a                    ;no:   yes-->

  cmp al,"a"                 ;force to CAPs
  jc nc8
  cmp al,"z"+1
  jnc nc8
  xor al,20h

nc8:
  clc                        ;return good
  ret

nc8a:
  mov al," "                 ;use space
  clc                        ;return good
  ret

nc9:
  dec si                     ;don't go passed CR
  xor cx,cx                  ;no more chars
  mov al," "

nc10:
  stc                        ;return, none found
  ret

;page
;........................... Get number .......................................

getnum:
  xor dx,dx                  ;init number = 0

gn1:
  jcxz gn9                   ;are any chars left?  no-->
  lodsb                      ;yes, get next one
  dec cx                     ;dec # left
  mov ah,al                  ;save char
  sub ah,"0"                 ;adjust ascii to number
  jc gn8                     ;was char < "0" ?   yes-->
  cmp ah,10                  ;is char  > "9" ?   
  jnc gn8                    ;                   yes-->

  push ax
  mov ax,10                  ;decimal shift left
  mul dx
  mov dx,ax
  pop ax
  add dl,ah                  ;add in ones
  adc dh,0
  jmp gn1                    ;see if more

gn8:
  dec si                     ;restore buffer ptr
  inc cx                     ;restore # remaining

gn9:
  ret


string1 db "Waiting for keystrokes, ESC will exit ...",13,10,"$"

;.......................... video ............................

video:
  push ax
  mov ah,14
  push si
  push di
  int 10h
  pop di
  pop si
  pop ax
  ret

hexax:
  push ax
  mov al,ah
  call hexal
  pop ax
  call hexal
  ret

hexal:
  push ax
  push cx
  mov ch,al
  mov cl,4
  shr al,cl
  add al,"0"
  cmp al,3Ah
  jc hxal2
  add al,7

hxal2:
  call video
  mov al,ch
  and al,0fh
  add al,"0"
  cmp al,3Ah
  jc point4
  add al,7

point4:
  call video
  pop cx
  pop ax
  ret


crlf:
  push ax
  mov al,0Dh
  call video
  mov al,0Ah
  call video
  pop ax
  ret

subs endp


cseg ends
     end su1


Click here to download the assembled program.



Top of Programmer Stuff -or- Main Table of Contents -or- Top of Form