home *** CD-ROM | disk | FTP | other *** search
/ Mega CD-ROM 1 / megacd_rom_1.zip / megacd_rom_1 / MAGAZINE / DDJMAG / DDJ8910.ZIP / MISCHEL.LST < prev    next >
File List  |  1989-09-07  |  22KB  |  757 lines

  1. _A Global Variable Device Driver for MS-DOS_
  2. by Jim Mischel
  3.  
  4. [LISTING ONE]
  5.  
  6.     name    globals
  7.     page    ,132
  8.     title   'GLOVAR - global variable device driver
  9. ;
  10. ; GLOVAR.ASM
  11. ;
  12. ; Copyright 1989, Jim Mischel
  13. ;
  14. ; This MS-DOS device driver provides true global variables to application
  15. ; programs.
  16. ;
  17. ; Sample make file:
  18. ; #
  19. ; # glovar.mak - build global variable device driver glovar.sys.
  20. ; #
  21. ; glovar.sys: glovar.asm
  22. ;   tasm glovar
  23. ;   link glovar;
  24. ;   exe2bin glovar.exe glovar.sys
  25. ;
  26.     ideal
  27.     locals
  28.  
  29.     model   tiny,pascal
  30.     CodeSeg
  31.  
  32.     org 0       ;device drivers start at offset 0
  33.     
  34. max_cmd     equ 12  ;only 12 functions recognized by the driver
  35. cr      equ 0dh
  36. lf      equ 0ah
  37. buff_size   equ 1024    ;buffer size in bytes
  38. TRUE        equ -1
  39. FALSE       equ 0
  40.  
  41. ;
  42. ; Device Driver Header
  43. ;
  44. header  dd  -1      ;link to next device, -1 = end of list
  45.         dw  8000h       ;simple character device driver
  46.     dw  strat       ;device Strategy entry point
  47.     dw  intr        ;device Interrupt entry point
  48.     db  '$$GVAR$$'  ;Device name
  49.  
  50. rh_ptr  dd  ?       ;saved request header pointer
  51.  
  52. ;
  53. ; Command codes dispatch table.  Only functions 0 (initialize) and 4 (read)
  54. ; are supported.  Other functions go to a stub routine that simple returns
  55. ; a non-error status.
  56. ;
  57. dispatch:
  58.     dw  init        ; 0 = initialize driver
  59.     dw  not_imp     ; not implemented
  60.     dw  not_imp     ; not implemented
  61.     dw  not_imp     ; not implemented
  62.     dw  read        ; 4 = read from device
  63.     dw  not_imp     ; not implemented
  64.     dw  not_imp     ; not implemented
  65.     dw  not_imp     ; not implemented
  66.     dw  not_imp     ; not implemented
  67.     dw  not_imp     ; not implemented
  68.     dw  not_imp     ; not implemented
  69.     dw  not_imp     ; not implemented
  70.     dw  not_imp     ; not implemented
  71.     dw  not_imp     ; not implemented
  72. ;
  73. ; MS-DOS Request Header structure definition
  74. ;
  75. struc   request
  76. rlength db  ?       ;length of request header
  77. unit    db  ?       ;unit number for this request (block devices)
  78. command db  ?       ;request header's command code
  79. status  dw  ?       ;driver's return status word
  80. reserve db  8 dup (?)   ;reserved area
  81. media   db  ?       ;media descriptor byte (block devices)
  82. address dd  ?       ;memory address for transfer
  83. count   dw  ?       ;byte/sector count value
  84. sector  dw  ?       ;starting sector value (block devices)
  85. ends    request         ;end of request header template
  86.  
  87. ; Status codes returned by routines in AX
  88. error       equ 8000h
  89. busy        equ 0200h
  90. done        equ 0100h
  91. unknown_command equ 3
  92.  
  93. ;
  94. ; Device strategy routine -- save address of request header.
  95. ;
  96. strat:  mov [word ptr cs:rh_ptr],bx
  97.     mov [word ptr cs:rh_ptr+2],es
  98.     retf
  99.  
  100. ;
  101. ; Interrupt routine.  Dispatch to the proper routine.
  102. ;
  103. proc    intr    far uses ax bx cx dx ds es di si
  104.     pushf
  105.     push    cs
  106.     pop ds              ;point to local data
  107.     les di,[rh_ptr]         ;ES:DI = Request Header
  108.     mov bl,[es:di+request.command]  ;bx = command code
  109.     xor bh,bh
  110.     cmp bx,max_cmd          ;make sure it's legal
  111.     jle intr1
  112.     mov ax,error+unknown_command    ;Error:  unknown command
  113.     jmp short intr2
  114. intr1:  shl bx,1                ;form index to dispatch table
  115.     call    [word ptr bx+dispatch]      ;and branch to driver routine
  116.     les di,[rh_ptr]         ;ES:DI = request header
  117. intr2:  or  ax,done             ;set Done bit
  118.     mov [es:di+request.status],ax   ;store status in request header
  119.     popf
  120.     ret
  121. endp    intr
  122.  
  123. ;
  124. ; 'stub' routine for un-implemented functions.
  125. ;
  126. not_imp:
  127.     xor ax,ax           ;set good status
  128.     retn
  129.  
  130. ;
  131. ; Read -- return address of 'global_table'
  132. ;
  133. ; This routine assumes that the application program asked for 4 (no less!)
  134. ; bytes (the size of a far pointer).
  135. ;
  136. ; This function is called with ES:DI pointing to the MS-DOS request header.
  137. ;
  138. read:   lds si,[dword ptr es:di+request.address]    ;DS:SI is buffer address
  139.     mov [word ptr ds:si],offset global_table    ;store offset
  140.     mov [word ptr ds:si+2],cs           ;and segment
  141.     mov [word ptr es:di+request.count],4    ;4 bytes transferred
  142.     xor ax,ax                   ;return good status
  143.     ret
  144.  
  145. ;
  146. ; The following three routines (set_var, get_var, flush_vars) are accessed
  147. ; by applications programs through FAR calls.  The addresses of these
  148. ; functions are provided in the 'global_table' structure, the address of
  149. ; which is passed to the application when it does a read on the $$GVAR$$
  150. ; device.
  151. ;
  152.  
  153. ;
  154. ; set_var -- assign a value to the global variable pointed to by the
  155. ; 'varname' parameter.  If the 'vardef' parameter points to the null string,
  156. ; the variable is removed from the table.
  157. ;
  158. ; Returns TRUE (-1) if successful, FALSE (0) if variable table overflows.
  159. ;
  160. proc    set_var far uses si di ds, varname:far ptr, vardef:far ptr
  161.     lds si,[varname]
  162.     call    near ptr remove_var     ;try to remove variable
  163.     les di,[vardef]
  164.     cmp [byte ptr es:di],0      ;check for NULL string
  165.     jnz do_set
  166.     mov ax,-1               ;and if so, just call it quits
  167.     jmp short set_done
  168. do_set: lds si,[varname]            ;otherwise,
  169.     call    near ptr install_var        ;install the new def
  170. set_done:
  171.     ret
  172. endp    set_var
  173.  
  174. ; Return the definition of the variable pointed to by 'varname' in the
  175. ; space pointed to by 'vardef'.  Returns -1 if the variable exists, 0 if
  176. ; not.  If the variable does not exist the null string is returned in 'vardef'.
  177. ;
  178. proc    get_var far uses si di ds, varname:far ptr, vardef:far ptr
  179.     lds si,[varname]
  180.     call    find_var
  181.     jnc get1            ;if found, continue
  182.     les di,[vardef]     ;otherwise,
  183.     mov [byte ptr es:di],0  ;store NULL string
  184.     xor ax,ax           ;set failure status
  185.     jmp short @@done        ;and exit
  186. get1:   cld             ;variable found, now find '='
  187.     mov al,'='
  188.     mov cx,-1
  189.     repnz   scasb
  190.     mov si,di
  191.     push    es
  192.     pop ds          ;DS:SI now points to first character of definition
  193.     les di,[vardef]     ;ES:DI points to return vardef area
  194. @@loop: lodsb               ;copy the definition to
  195.     stosb               ;the 'vardef' string
  196.     or  al,al
  197.     jnz @@loop
  198.     mov ax,TRUE         ;set status to TRUE
  199. @@done: ret
  200. endp    get_var
  201.  
  202. ;
  203. ; flush all variables from the global variable table.  This is done by placing
  204. ; a NULL byte at the start of the table and updating the 'next_mem' variable
  205. ; so that it also points to the head of the table.
  206. ;
  207. flush_vars:
  208.     mov [word ptr cs:glovars],0
  209.     mov ax,offset glovars
  210.     mov [word ptr cs:next_mem],ax
  211.     retf
  212.  
  213. ;
  214. ; Support routines.
  215. ;
  216. ; Remove the variable (name pointed to by ds:si) and its definition from
  217. ; the global variables table.
  218. ;
  219. remove_var:
  220.     call    find_var            ;find the variable
  221.     jc  @@done
  222.     cld
  223.     mov si,di               ;SI is start of variable name
  224.     mov al,0
  225.     mov cx,-1
  226.     repnz   scasb               ;move past definition
  227.     add [word ptr cs:next_mem],cx   ;update next_mem pointer
  228.     inc [word ptr cs:next_mem]
  229.     xchg    si,di               ;si=next variable, di=old var 
  230.     push    cs
  231.     pop ds
  232.     mov cx,offset end_buff
  233.     sub cx,si               ;cx is byte count
  234.     rep movsb               ;variable has been erased
  235. @@done: ret
  236.  
  237. ;
  238. ; Called with ds:si pointing to variable name, es:di pointing to definition.
  239. ;
  240. install_var:
  241.     push    es
  242.     push    di          ;save 'vardef' address
  243.     cld
  244.     mov al,0
  245.     mov cx,-1
  246.     repnz   scasb           ;get length of definition
  247.     not cx
  248.     mov bx,cx           ;and save
  249.     push    bx          ;will be used to copy the definition
  250.     push    ds
  251.     pop es
  252.     mov di,si
  253.     mov cx,-1
  254.     repnz   scasb           ;get length of variable name
  255.     not cx
  256.     push    cx          ;will be used to copy the variable name
  257.     add bx,cx           ;bx=strlen(varname)+strlen(vardef)+2
  258.     mov ax,buff_size + offset glovars
  259.     sub ax,[word ptr cs:next_mem]
  260.     cmp ax,bx
  261.     jnc inst1           ;if not enough room
  262.     xor ax,ax           ;then fail
  263.     add sp,8            ;clean up stack
  264.     ret
  265. inst1:  push    cs
  266.     pop es
  267.     mov di,[word ptr cs:next_mem]   ;ES:DI points to buffer
  268.     pop cx          ;restore varname length
  269.     dec cx          ;don't want null terminator
  270. @@loop1:
  271.     lodsb
  272.     call    toupper         ;convert char to upper_case
  273.     stosb               ;and store
  274.     loop    @@loop1
  275. inst2:  mov al,'='
  276.     stosb               ;store the '=' separator
  277.     pop cx          ;restore definition length
  278.     pop si          ;restore 'vardef' address
  279.     pop ds
  280.     rep movsb           ;and copy definition to buffer
  281.     mov [word ptr cs:next_mem],di
  282.     stosb               ;store extra null byte for safe keeping
  283.     mov ax,TRUE
  284.     ret
  285.  
  286. ;
  287. ; find_var -- attempt to find the variable pointed to by DS:SI in the
  288. ; global variables buffer.  
  289. ; On success, AX = TRUE (-1), carry is cleared, and ES:DI points to the first
  290. ; character of the variable name in the buffer.
  291. ; If the variable is not found, AX = FALSE (0), carry flag is set, and ES:DI
  292. ; is undefined.
  293. ;
  294. find_var:
  295.     cld
  296.     push    cs
  297.     pop es
  298.     mov di,offset glovars
  299.     mov dx,si           ;save name starting address
  300. find0:  mov bx,di           ;and variable starting address
  301.     mov si,dx
  302.     dec di
  303. find1:  lodsb
  304.     call    toupper
  305.     inc di
  306.     cmp al,[byte ptr es:di]
  307.     jz  find1
  308.     or  al,al           ;didn't match, at end of string?
  309.     jnz get_next_var        ;if not, go for next variable
  310.     cmp [byte ptr es:di],'='    ;at end of varname in buffer?
  311.     jnz get_next_var        ;if not, do next variable
  312.     mov di,bx           ;variable found
  313.     mov ax,TRUE
  314.     ret
  315. get_next_var:
  316.     mov al,0
  317.     mov cx,-1
  318.     repnz   scasb           ;get start of next variable
  319.     cmp [byte ptr es:di],0  ;if not at end of buffer
  320.     jnz find0           ;then do next variable
  321.     xor ax,ax
  322.     stc             ;the variable wasn't found
  323.     ret
  324. ;
  325. ; toupper - convert the character in AL to upper-case.
  326. ;
  327. toupper:
  328.     cmp al,'a'
  329.     jc  tod
  330.     cmp al,'z'+1
  331.     jnc tod
  332.     sub al,32
  333. tod:    ret
  334.  
  335.     even                ;might as well
  336.  
  337. ;
  338. ; The address of this table is passed back to the application whenever
  339. ; a 'read' call is made on the device.
  340. ; The application uses this table to access the memory buffer and the
  341. ; driver functions.
  342. ;
  343. global_table:
  344.     dd  ?           ;address of memory buffer
  345.     dd  ?           ;address of set_var routine
  346.     dd  ?           ;address of get_var routine
  347.     dd  ?           ;address of flush_vars routine
  348.  
  349. next_mem dw offset glovars      ;pointer to end of defined variables
  350.  
  351. glovars:                ;start of glovar buffer
  352.  
  353. ;
  354. ; Initialization code.
  355. ;
  356. ; Initialize the function pointers table and the global data buffer, and 
  357. ; return the address of the first byte of free memory after all the device
  358. ; driver code.  The global variables buffer overlays this initialization code.
  359. ;
  360. ; Upon entry, ES:DI points to the MS-DOS request header.  DS contains the
  361. ; local data segment (same as CS).  This routine makes no attempt to save
  362. ; any registers.
  363. ;
  364. init:   mov ax,cs
  365.     mov es,ax
  366.     mov di,offset dhaddr
  367.     call    hexasc          ;convert load segment address to ASCII
  368.     mov ax,cs
  369.     mov es,ax
  370.     mov di,offset mbaddr
  371.     call    hexasc
  372.     push    cs
  373.     pop es
  374.     mov ax,offset glovars
  375.     mov di,offset mbaddr+5
  376.     call    hexasc
  377.     mov ah,9            ;print sign-on message and
  378.     mov dx,offset ident     ;driver load address
  379.     int 21h
  380.     les di,[rh_ptr]     ;request header address in ES:DI
  381. ;store first usable memory address in request header
  382.     mov [word ptr es:di+request.address],offset end_buff
  383.     mov [word ptr es:di+2+request.address],cs
  384. ;
  385. ; Now set up the 'global_table' structure with proper memory addresses.
  386. ;
  387.     mov [word ptr global_table],offset glovars
  388.     mov [word ptr global_table+2],cs
  389.     mov [word ptr global_table+4],offset set_var
  390.     mov [word ptr global_table+6],cs
  391.     mov [word ptr global_table+8],offset get_var
  392.     mov [word ptr global_table+10],cs
  393.     mov [word ptr global_table+12],offset flush_vars
  394.     mov [word ptr global_table+14],cs
  395.     xor ax,ax               ;return good status
  396.     mov [word ptr glovars],ax       ;and clear glovars buffer
  397.     ret
  398.  
  399. ident   db  cr,lf,lf
  400.     db  'Global variable device driver version 1.0',cr,lf
  401.     db  'Copyright 1989, Jim Mischel',cr,lf
  402.     db  'Device Header at '
  403. dhaddr  db  'XXXX:0000.  '
  404.     db  'Memory buffer at '
  405. mbaddr  db  'SSSS:OOOO',cr,lf,lf,'$'
  406.  
  407. ;
  408. ; hexasc - converts a binary 16-bit number into a hex ASCII string
  409. ;
  410. ; call with AX = value to convert, ES:DI = address to store 4-character
  411. ; string.
  412. ;
  413. hexasc: mov bx,ax       ;save value here
  414.     mov cx,4        ;initialize character counter
  415.     mov dx,cx
  416. hexasc1:
  417.     xchg    cx,dx
  418.     rol bx,cl       ;isolate next 4 bits
  419.     mov al,bl
  420.     and al,0fh
  421.     add al,'0'      ;convert to ASCII
  422.     cmp al,'9'      ;if 0-9
  423.     jbe hexasc2     ;then jump
  424.     add al,'A'-'9'-1    ;otherwise add offset for A-F
  425. hexasc2:
  426.     stosb
  427.     xchg    cx,dx
  428.     loop    hexasc1
  429.     ret
  430.  
  431. ; reserve space for rest of global variable buffer.
  432.     db  buff_size - (offset $ - offset glovars) + 1 dup (0)
  433.  
  434.     even
  435. end_buff:
  436.     end
  437.  
  438.  
  439. [LISTING TWO]
  440.  
  441.  
  442. /*
  443.  * GVAR.C - program to test global device driver interface.
  444.  * Required structure and function definitions are in the file GVAR.H
  445.  *
  446.  * Sample make file.
  447.  *
  448.  * #
  449.  * # tcgvar.mak - make C gvar test program
  450.  * #
  451.  * gvar.exe: gvar.c gvar.h
  452.  *   tcc -ms gvar
  453.  *
  454.  */
  455. #include <stdio.h>
  456. #include <conio.h>
  457. #include <fcntl.h>
  458. #include <io.h>
  459. #include "gvar.h"
  460.  
  461. void main (void) {
  462.     char buff[1024];
  463.     int fhandle;
  464.  
  465.     if ((fhandle = _open ("$$GVAR$$", O_RDONLY)) == -1) {
  466.     puts ("Error:  can't open global variables file");
  467.     return;
  468.     }
  469.     read (fhandle, &gvars, sizeof (char far *));
  470.     printf ("global table at %Fp\n", gvars);
  471.     _close (fhandle);
  472.     if (!set_gvar ("JIMSVAR", "Hi jim.!"))
  473.         puts ("Error setting variable JIMSVAR");
  474.     if (!get_gvar ("jimsvar", buff))
  475.         puts ("JIMSVAR not defined");
  476.     else
  477.         printf ("JIMSVAR=%s\n", buff);
  478.     set_gvar ("jimsvar","");
  479.     if (!get_gvar ("JIMSVAR", buff))
  480.         puts ("JIMSVAR not defined");
  481.     else
  482.         printf ("JIMSVAR=%s\n", buff);
  483. }
  484.  
  485.  
  486. [LISTING THREE]
  487.  
  488. /*
  489.  * gvar.h -- required definitions for C interface to global variable
  490.  * device driver.
  491.  *
  492.  * Copyright 1989, Jim Mischel
  493.  */
  494.  
  495. /*
  496.  * structure of table returned by glovar read call.
  497.  */
  498. typedef struct {
  499.     void far *mem_buff;     /* pointer to memory buffer */
  500. /* function pointers */
  501.     int  far pascal (far *set_var)    (char far *, char far *);
  502.     int  far pascal (far *get_var)    (char far *, char far *);
  503.     void far pascal (far *flush_vars) ();
  504. } GVAR_DEF;
  505.  
  506. GVAR_DEF far *gvars;    /* global pointer to global variables structure */
  507.  
  508. /*
  509.  * function macros
  510.  */
  511. #define set_gvar (*gvars->set_var)
  512. #define get_gvar (*gvars->get_var)
  513. #define flush_gvars (*gvars->flush_vars)
  514.  
  515.  
  516.  
  517. [LISTING FOUR]
  518.  
  519. {*
  520.  *  GVAR.PAS - Test global variable device driver.
  521.  * 
  522.  * This program depends on the routines in TPGVAR.ASM to provide the interface
  523.  * to the device driver.
  524.  *
  525.  * Make sure the file tpgvar.obj is in the current directory 
  526.  * (or Turbo's /Object directory) before compiling this program.
  527.  *
  528.  * Sample make file for compiling this program:
  529.  * #
  530.  * # tpgvar.mak - make pascal gvar test program
  531.  * #
  532.  * gvar.exe: gvar.pas tpgvar.obj
  533.  *   tpc gvar
  534.  *
  535.  * tpgvar.obj: tpgvar.asm
  536.  *   tasm tpgvar
  537.  *
  538.  *}
  539.  
  540. {$F+}           { asm routines require far calls }
  541. {$L tpgvar}
  542. program test_globals;
  543. type
  544.     varstr  = string[255];
  545.  
  546. var
  547.     buff : varstr;
  548.  
  549. function gvar_init : boolean; external;
  550. function set_gvar (varname : varstr; vardef : varstr) : boolean; external;
  551. function get_gvar (varname : varstr; var vardef : varstr) : boolean; external;
  552. procedure flush_gvars; external;
  553.  
  554. begin
  555.     if (not gvar_init) then begin
  556.     writeln ('Can''t open gvars file.  Program aborted.');
  557.     halt;
  558.     end;
  559.     if (not set_gvar ('JIMSVAR', 'HELLO THERE'))
  560.         writeln ('Error setting variable JIMSVAR');
  561.     if (not get_gvar ('jimsvar', buff))
  562.         writeln ('JIMSVAR not defined')
  563.     else
  564.         writeln ('JIMSVAR=', buff);
  565.     trash := set_gvar ('jimsvar','');
  566.     if (not get_gvar ('jimsvar', buff))
  567.         writen ('JIMSVAR not defined')
  568.     else
  569.         writeln ('JIMSVAR=', buff);
  570. end.
  571.  
  572.  
  573. [LISTING FIVE]
  574.  
  575.  
  576.     page    ,132
  577.     name    tpgvar
  578.     title   'TPGVAR -- Turbo Pascal global variable interface routines'
  579. ;
  580. ; TPGVAR.ASM
  581. ; These routines provide the Turbo Pascal interface to the global variable 
  582. ; device driver.
  583. ;
  584. ; The routines provided are:
  585. ; GVAR_INIT
  586. ; function gvar_init : boolean; external;
  587. ;   Returns TRUE if successful, FALSE otherwise.  MUST be called before using
  588. ;   any of the other functions.
  589. ;
  590. ; SET_GVAR
  591. ; function set_gvar (varname : varstr; vardef : varstr) : boolean; external;
  592. ;   Defines the variable 'varname' with the value 'varstr'.  Returns FALSE
  593. ;   if inserting this variable will over-run the global variables buffer.
  594. ;
  595. ; GET_GVAR
  596. ; function get_gvar (varname : varstr; var vardef : varstr) : boolean; external;
  597. ;   Return the definition of the variable 'varname' in the string 'vardef'.
  598. ;   Returns FALSE if 'varname' doesn't exist.
  599. ;
  600. ; FLUSH_GVARS   procedure flush_gvars; external;
  601. ;   Removes all global variables from the buffer.
  602. ;
  603. ; Copyright 1989, Jim Mischel
  604. ;
  605.     model   tpascal
  606.     ideal
  607.     locals
  608.  
  609. ;
  610. ; global variable interface structure
  611. ; A pointer to this type of structure is returned by the $$GVAR$$ device
  612. ; in response to a read call.
  613. ;
  614. struc gvars
  615. membuff_ptr dd  ?
  616. set_var_ptr dd  ?
  617. get_var_ptr dd  ?
  618. flush_vars_ptr  dd  ?
  619. ends
  620.  
  621.     CodeSeg
  622. ;
  623. ; This has to go into the code segment because 'TPascal' model won't allow
  624. ; initialization in the data segment.
  625. ;
  626. gvar_filename   db  '$$GVAR$$',0    ;global variable device name
  627. gvar_ptr    dd  ?       ;pointer to global variables structure
  628. glovar_name db  256 dup (?) ;passed to driver routines
  629. glovar_def  db  256 dup (?)
  630.  
  631. public  GVAR_INIT
  632. public  SET_GVAR
  633. public  GET_GVAR
  634. public  FLUSH_GVARS
  635.  
  636. ;
  637. ; open the $$GVAR$$ file and read the address of the header into gvar_ptr
  638. ; returns TRUE if successful, FALSE if not.
  639. ;
  640. proc    gvar_init uses ds
  641.     push    cs
  642.     pop ds
  643.     mov dx,offset gvar_filename
  644.     mov ax,3d00h
  645.     int 21h         ;open the file
  646.     jc  @@error         ;error opening file
  647.     push    ax          ;save file handle
  648.     mov bx,ax           ;file handle in BX
  649.     mov dx,offset gvar_ptr
  650.     mov ah,3fh
  651.     mov cx,4
  652.     int 21h         ;read address of gvar structure
  653.     pop bx          ;file handle in BX
  654.     mov ah,3eh
  655.     int 21h         ;close the file
  656.     mov ax,-1
  657.     jmp short @@done
  658. @@error:
  659.     xor ax,ax
  660. @@done: ret
  661. endp    gvar_init
  662.  
  663. ;
  664. ; This macro converts the Turbo Pascal string in the 'from' parameter to an
  665. ; ASCIIZ string in the 'to' parameter.
  666. ;
  667. macro @make_asciiz from, to
  668.     lds si,[&from&]
  669.     push    cs
  670.     pop es
  671.     mov di,offset &to&
  672.     push    cs          ;these values are pushed now, but not
  673.     push    di          ;used until the gvar call below      
  674.     call    near ptr make_asciiz
  675. endm
  676.  
  677. ;
  678. ; Macro calls the specified routine.
  679. ;
  680. macro @gvar_call routine
  681.     les bx,[cs:gvar_ptr]
  682.     call    [dword ptr es:bx+gvars.&routine&]
  683. endm
  684.  
  685. ;
  686. ; Define 'varname' with value 'vardef'.  Returns TRUE if successful, FALSE
  687. ; if the global variables buffer overflows.
  688. ;
  689. proc    set_gvar uses ds, varname:far ptr, vardef:far ptr
  690.     @make_asciiz varname, glovar_name
  691.     @make_asciiz vardef, glovar_def
  692.     @gvar_call set_var_ptr
  693.     ret
  694. endp    set_gvar
  695.  
  696. ;
  697. ; Return definition of 'varname' in 'vardef'.  Returns TRUE if successful,
  698. ; FALSE if 'varname' does not exist.  If 'varname' does not exist, 'vardef'
  699. ; is set to the null string.
  700. ;
  701. ; CAUTION:  if the returned variable definition is longer than 255 characters,
  702. ; many strange and not-so-wonderful things will happen.
  703. ;
  704. proc    get_gvar uses ds, varname:far ptr, vardef:far ptr
  705.     @make_asciiz varname, glovar_name
  706.     push    cs
  707.     mov ax,offset glovar_def
  708.     push    ax
  709.     @gvar_call get_var_ptr      ;get the variable
  710.     push    ax          ;save return status
  711.     push    cs          ;now convert definition to TP string
  712.     pop ds
  713.     mov si,offset glovar_def
  714.     les di,[vardef]
  715.     push    di          ;save to set length
  716.     inc di          ;start string at second byte
  717.     xor cx,cx           ;cx is byte count
  718.     cld             ;better safe than sorry
  719. @@loop: lodsb
  720.     or  al,al
  721.     jz  @@done
  722.     stosb
  723.     inc cx          ;bump length
  724.     jmp short @@loop
  725. @@done: pop di          ;restore pointer to length byte
  726.     mov [byte ptr es:di],cl ;and store length there
  727.     pop ax          ;restore return status
  728.     ret
  729. endp    get_gvar
  730.  
  731. ;
  732. ; flush all global variables
  733. proc    flush_gvars
  734.     @gvar_call flush_vars_ptr
  735.     ret
  736. endp    flush_gvars
  737.  
  738. ;
  739. ; convert Turbo Pascal string into ASCIIZ string.
  740. ; Call with ds:si pointing to source string (TP format)
  741. ;           es:di pointing to destination buffer
  742. ; Make sure this is accessed through a near call.
  743. ;
  744. make_asciiz:
  745.     lodsb               ;get length
  746.     mov cl,al
  747.     xor ch,ch           ;in CX
  748.     cld
  749.     rep movsb           ;move the string
  750.     mov [byte ptr es:di],0  ;and place the null terminator
  751.     retn
  752.     end
  753.  
  754.  
  755.