home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / fwktl101.zip / HELOWRLD.1 < prev    next >
Text File  |  1996-05-28  |  22KB  |  554 lines

  1. ; This is a "Hello, world!" program for loading and execution in
  2. ; OS/2 Warp protected mode, using the FWKTL(TM) Text_program Loader,
  3. ; Version 1.01, to load it.
  4. ;
  5. ; (C)Copyright Frederick W. Kantor 1996. All rights reserved.
  6. ;
  7. ; programs loaded by FWKTL can modify themselves, and can modify
  8. ;   their source (the file from which the program was loaded)
  9. ;
  10. ;
  11. ; To assemble in a DOS session using Eric Isaacson's A386 version 0.64
  12. ; or later:
  13. ;
  14. ;     A386 helowrld.1 <enter>  makes HELOWRLD.COM and .SYM
  15. ;
  16. ;     A386 +G16 +S +L39 +H127 helowrld.1 <enter>  makes .COM and .LST.
  17. ;
  18. ; the second command line, above, is provided in MKHWA386.BAT (enclosed);
  19. ; to use it, in a DOS session type
  20. ;
  21. ;     MKHWA386 <enter>
  22. ;
  23. ; and follow the instructions on the screen;
  24. ;
  25. ; the .COM extension can then be changed or removed, to help avoid
  26. ; error.  FWKTL loads it independent of extension.
  27. ;
  28. ; if the resulting .COM program is executed directly in a DOS or OS/2
  29. ; environment, it returns to the command line: the first byte is RET.
  30. ;
  31. ; a linker is not needed.
  32. ;
  33. ; There are 3 examples in this "Hello, world." program. The only
  34. ; parameter you need to set to use them is EXAMPLE :
  35. ;   EXAMPLE EQU 1
  36. ;   EXAMPLE EQU 2
  37. ;   EXAMPLE EQU 3
  38.  
  39. EXAMPLE EQU 1   ; current setting
  40.  
  41. ;
  42. ; EXAMPLE EQU 1
  43. ;   uses PSETUP = 0 in initialization; HELOWRLD.COM is 69 bytes long;
  44. ;   this configuration shows how to get an API's address when you
  45. ;   already have the module handle; it gets a specific address for a
  46. ;   single procedure, DOS32WRITE (DOSCALLS.282) (see also P_ STRUC);
  47. ;   the handle for the DOSCALLS module is provided by FWKTL as part
  48. ;   of the initialization data.
  49. ;
  50. ; EXAMPLE EQU 2
  51. ;   sets PSETUP = 1; HELOWRLD.COM is 41 bytes long; this uses a simple
  52. ;   SHOW procedure in FWKTL to send an ASCIIZ string up to, but not
  53. ;   including, its terminal 00 to 'standard output' (defaults to
  54. ;   screen), and append a terminal carriage_return line_feed. (to save
  55. ;   space, the exit errorlevel is not set to zero: the errorlevel
  56. ;   returned is the low 16 bits of the DWORD for the program's entry
  57. ;   point.)
  58. ;
  59. ; EXAMPLE EQU 3
  60. ;   sets PSETUP = 0; HELOWRLD.COM is 142 bytes long;
  61. ;   this illustrates how functions can be easily added; it gets
  62. ;   addresses for a list of 25 APIs and uses two of them, one to write
  63. ;   "Hello, world!",0d,0a to "standard output" (the screen), and the
  64. ;   other to sound a tone. The APIs loaded in example 3 include tools
  65. ;   for loading and freeing other dynamic link library modules,
  66. ;   allocating and freeing memory, some semaphore procedures, etc.
  67.  
  68.  
  69. ; To run the assembled HELOWRLD.COM in an OS/2 session:
  70. ;
  71. ; Format:  FWKTL HELOWRLD.COM <enter>
  72.  
  73. ; Initialization:
  74. ;
  75. ; FWKTL loads a program and sets
  76. ;
  77. ;   EAX = entry point for execution
  78. ;   EBX = 0 if no free memory requested
  79. ;     ELSE EBX  DWORD aligned, after code
  80. ;   ECX = number of DWORDs in initialization requested (see P_ STRUC)
  81. ;   ESI points to start of initialization_data source (matching start
  82. ;         of P_)
  83. ;   EDI = 0 if no free memory requested
  84. ;     ELSE EDI = EBX = start of free memory, in case you wish to put
  85. ;         initialization data there
  86. ;   the "direction flag" (decrement flag) is cleared (CLD)
  87. ;
  88. ; FWKTL provides initial values, and several procedures;
  89. ;   see P_ STRUC, below, for initial values and procedure addresses.
  90. ;   These values are easily copied, with ESI, EDI, ECX, and CLD from
  91. ;   FWKTL. If free memory was requested (done in HELOWRLD.1), use
  92. ;     REP MOVSD
  93.  
  94. ;   If no free memory was requested, save values in STACK. For saving
  95. ;   EBX in STACK, I suggest you modify J_ STRUC and put J_EBX at the
  96. ;   beginning, and load EDI with the effective address of the beginning
  97. ;   of the standard FWKTL initialization values: e.g.,
  98. ;     SUB ESP,TYPE J_
  99. ;     MOV EBP,ESP
  100. ;     LEA EDI,[EBP].P_LOADEDAT ; where to put initialization data
  101. ;     REP MOVSD                ; using ESI, ECX, and CLD from FWKTL
  102.  
  103. ; FWKTL calls the program as a routine in Thread 1:
  104. ;
  105. ;   on entry, the stack will not return an exception until 0ABAh dwords
  106. ;     are pushed (= 02AE8h bytes = 10984 (decimal) bytes)
  107. ;
  108. ;   on return to FWKTL:  CS, SS, and ESP must be correct when RET
  109. ;      is executed
  110. ;
  111. ;   EAX, EBX, ECX, EDX, ESI, EDI, EBP, DS, ES, FS, GS can be changed
  112. ;
  113. ;   EAX is used to return an exit errorlevel, of which the low 16 bits
  114. ;       (AX) is returned by OS/2 Warp at the command line.
  115.  
  116. ; to display exit errorlevels, you can use  PROMPT=[$p $r]
  117.  
  118. ;-------------------------------------------------------
  119.  
  120. KEEP_EBX EQU 00   ; set to 1 to store EBX in stack, so that it can
  121.                   ; be recovered by MOV EBX,[EBP] (e.g., in case a
  122.                   ; procedure called changes it); EBP may be less
  123.                   ; likely to be changed than EBX during such calls.
  124.                   ; All calls used in these 3 examples preserve EBX,
  125.                   ; so this is not used.
  126. ;-------------------------------------------------------
  127.                   ; PSETUP is used to control P_ STRUC and to set
  128.                   ;   bit00 in second DWORD in the program header
  129.                   ; = 0 to get the shorter initial setup data;
  130.                   ; = 1 to get the longer initial setup data which
  131.                   ;   has the address for the SHOW utility
  132.  
  133. #IF EXAMPLE EQ 1 OR EXAMPLE EQ 3
  134. PSETUP EQU 00
  135. #ELSEIF EXAMPLE EQ 2
  136. PSETUP EQU 01
  137. #ENDIF
  138.  
  139. ;-------------------------------------------------------
  140.  
  141. USE32             ; for A386 flat, protected_mode, 32_bit code.
  142.                   ; conditional jumps can be short or long; so, with
  143.                   ; some attention to detail, the code can be made
  144.                   ; movable.
  145.  
  146. ;-------------------------------------------------------
  147.  
  148. ; structure for initial data:
  149.  
  150. P_ STRUC          ; these are provided by FWKTL for initialization,
  151.                   ;  according to file header settings:
  152.  
  153. P_LOADEDAT DD ?   ; start of this memory block
  154. P_PWHENCE DD ?    ; points to ASCIIZ string re where program was found
  155. P_PCOMTAIL DD ?   ; points to ASCIIZ command tail
  156.  
  157. P_GETFN DD ?      ; address for indirect call to FWKTL GETFN function:
  158.                   ; Input:
  159.                   ;     EAX = ProcedureOrdinal
  160.                   ;         (maximum permitted OS/2 ordinal <= 65533)
  161.                   ;         if EAX > 0, ESI is ignored
  162.                   ;   OR
  163.                   ;     EAX = 0
  164.                   ;     ESI points to ASCIIZ procedure_name
  165.                   ;
  166.                   ;   EDX = module handle
  167.                   ;   EDI points to target DWORD to receive
  168.                   ;         procedure address
  169.                   ;
  170.                   ; Output:
  171.                   ;  if successful,
  172.                   ;     zero_flag is set
  173.                   ;     procedure address is in target DWORD
  174.                   ;     EAX = 0
  175.                   ;
  176.                   ;  if error
  177.                   ;     zero_flag is cleared
  178.                   ;     EAX contains error number:
  179.                   ;              6  ERROR_INVALID_HANDLE
  180.                   ;            123  ERROR_INVALID_NAME
  181.                   ;          65079  ERROR_ENTRY_IS_CALLGATE
  182.                   ;
  183.                   ; all other CPU registers and flags are preserved
  184.  
  185. P_GETFNLIST DD ?  ; address for indirect call to FWKTL GETFNLIST
  186.                   ;   function, to get the addresses for a list of
  187.                   ;   procedures in the same module:
  188.                   ;
  189.                   ; EBX, EDX, EBP are preserved across this function
  190.                   ;
  191.                   ; Input:
  192.                   ;   EAX = 0 if list is ASCIIZ procedure_names
  193.                   ;         2 if list is WORD ordinals
  194.                   ;         4 if list is DWORD ordinals
  195.                   ;         (maximum permitted OS/2 ordinal = 65533)
  196.                   ;   ECX = number of items in procedure list
  197.                   ;   EDX = module handle
  198.                   ;   ESI points to start of list of WORDs, DWORDs,
  199.                   ;         or series of ASCIIZ procedure_names
  200.                   ;   EDI points to start of target DWORDs to receive
  201.                   ;         the corresponding procedure addresses
  202.                   ;
  203.                   ; Output:
  204.                   ; if no error:
  205.                   ;   zero_flag is set
  206.                   ;   each target DWORD contains its procedure address
  207.                   ;   ESI:
  208.                   ;      if EAX = 0
  209.                   ;           ESI points to terminal 00h of last ASCIIZ
  210.                   ;              string;
  211.                   ;      ELSE if EAX > 0
  212.                   ;           ESI points to first byte after source;
  213.                   ;   EDI points to first byte after last target DWORD
  214.                   ;
  215.                   ; if error:
  216.                   ;   zero_flag is cleared
  217.                   ;   EAX contains error number:
  218.                   ;            6  ERROR_INVALID_HANDLE
  219.                   ;          123  ERROR_INVALID_NAME
  220.                   ;        65079  ERROR_ENTRY_IS_CALLGATE
  221.                   ;   ECX is not decremented on failed step
  222.                   ;   ESI points to list item identifying the procedure
  223.                   ;         for which the failure occurred
  224.  
  225. P_HDOSCALLS DD ?  ; handle for DOSCALLS module as loaded by FWKTL,
  226.                   ;   can be used in EDX for P_GETFN or P_GETFNLIST.
  227.  
  228.        ;------------------------- end of setup for PSETUP=0
  229.  
  230. #IF PSETUP EQ 1
  231.  
  232.     ; two additional procedures are optionally available from FWKTL:
  233.  
  234. P_SHOW DD ?        ; address for FWKTL SHOW function;
  235.                    ;
  236.                    ; usage:
  237.                    ;
  238.                    ; ESI points to ASCIIZ string to show on screen
  239.                    ;       using 'standard error' handle=2;
  240.                    ;   this procedure drops terminal 00 and adds 0D,0A;
  241.                    ; all CPU registers and flags are preserved.
  242.  
  243. P_USWORDCAPS DD ?  ; address for FWKTL capitalization function;
  244.                    ;
  245.                    ; usage:
  246.                    ;
  247.                    ; ESI points to contiguous string > ' ' to
  248.                    ;   capitalize, US English;
  249.                    ; all CPU registers and flags are preserved.
  250.                    ;
  251.                    ; (e.g., this procedure can be used when '/' or '-'
  252.                    ; is found in command tail, for case_insensitive
  253.                    ; options when you don't capitalize whole tail)
  254.  
  255. #ENDIF  ;------------------------- end of setup for PSETUP=1
  256.  
  257. #IF EXAMPLE EQ 1 OR EXAMPLE EQ 3  ; these are used with an API:
  258.  
  259. P_WROTE DD ?       ; used with the DOS32WRITE API
  260.  
  261. P_WRITE DD ?       ; to hold address for OS/2 DOS32WRITE API
  262.                    ; Note: P_WRITE is also the first entry in the
  263.                    ;       address targets used in EXAMPLE EQ 3:
  264.                    ;       P_WROTE must not be inserted directly
  265.                    ;       below P_WRITE, because it would offset
  266.                    ;       the rest of the address targets in
  267.                    ;       EXAMPLE EQ 3
  268. #ENDIF
  269.                 ; other material can be inserted here:
  270.  
  271. #IF EXAMPLE EQ 3   ; addresses for 24 more APIs
  272.  
  273. P_SCANENV        DD ?
  274. P_SEARCHPATH     DD ?
  275. P_SLEEP          DD ?
  276. P_EXIT           DD ?
  277. P_SETFILEPTR     DD ?
  278. P_CLOSE          DD ?
  279. P_OPEN           DD ?
  280. P_READ           DD ?
  281. P_BEEP           DD ? ; see note on using BEEP for diagnostics, below
  282. P_ALLOCMEM       DD ?
  283. P_FREEMEM        DD ?
  284. P_CREATETHREAD   DD ?
  285. P_GETINFOBLOCKS  DD ?
  286. P_LOADMODULE     DD ?
  287. P_QUERYPROCADDR  DD ?
  288. P_FREEMODULE     DD ?
  289. P_CREATEEVENTSEM DD ?
  290. P_OPENEVENTSEM   DD ?
  291. P_CLOSEEVENTSEM  DD ?
  292. P_RESETEVENTSEM  DD ?
  293. P_POSTEVENTSEM   DD ?
  294. P_WAITEVENTSEM   DD ?
  295. P_QUERYEVENTSEM  DD ?
  296. P_QUERYSYSINFO   DD ?
  297.  
  298. #ENDIF ; EXAMPLE EQ 3
  299.  
  300. ENDS    ; end of P_ STRUC
  301.  
  302. ;-------------------------------------------------------
  303.  
  304.       ; Note: BEEP is convenient for simple diagnostics;
  305.       ; e.g., you can insert code blocks like this to make a tone
  306.       ; when each such place is reached, and set different values
  307.       ; for frequency and/or duration to distinguish the beeps;
  308.       ; registers and flags are preserved across this function:
  309.  
  310.  
  311.       ; put this before the first beep call you want turned on:
  312.       ;
  313.       ; rundb equ 01 ; 01 to insert them (and remove initial ";"),
  314.       ;              ; 00 (or initial semicolon) to exclude them
  315.  
  316.  
  317.       ; #if rundb                                  ;debug
  318.       ; pushfd                                     ;debug
  319.       ; pushad                                     ;debug
  320.       ; pushd 0100h ; duration, milliseconds       ;debug
  321.       ; pushd 0100h ; frequency, cycles per second ;debug
  322.       ; call [ebx].p_beep                          ;debug
  323.       ; add esp,08                                 ;debug
  324.       ; popad                                      ;debug
  325.       ; popfd                                      ;debug
  326.       ; #endif                                     ;debug
  327.  
  328. ;-------------------------------------------------------
  329.  
  330. ; Here is the code header:    ( USE32 was already set, above)
  331.  
  332. ; the first byte of the header is a RET, in case the program has
  333. ; a .COM extension and someone accidentally tries to run it directly
  334. ; in a DOS or OS/2 session.
  335.  
  336. CODESTART:   ; used as a reference point
  337.  
  338. RET          ; 4_byte header identification string
  339. DB 'FWK'     ;
  340.  
  341. DD PSETUP    ; bit00 = 0 for PSETUP=0 (see P_ STRUC)
  342.              ; bit00 = 1 for PSETUP=1 (see P_ STRUC)
  343.              ; bits 31...01 are reserved, and must be zero for use with
  344.              ;   FWKTL version 1.00.
  345.  
  346. DD TYPE P_   ; amount of free memory requested after code;
  347.              ;   in these examples, just enough to hold a P_ STRUC;
  348.              ;   PSETUP affects the size of P_ STRUC used
  349.              ;   to hold standard FWKTL initialization data;
  350.              ;   storage of any other data there also affects the
  351.              ;   size of P_ STRUC; e.g., P_ can contain buffers.
  352.              ; the free memory starts DWORD aligned, zeroed.
  353.  
  354.              ; more memory than used in P_ STRUC can be requested;
  355.              ; note that memory requested in this way is committed
  356.              ; when allocated.
  357.  
  358.              ; for efficiency and flexibility, programs which need a
  359.              ; lot of memory can use API procedures to allocate and
  360.              ; free memory, rather than asking for it as part of
  361.              ; installation. EXAMPLE EQ 3 loads the addresses for
  362.              ; calling some memory management procedures.
  363.  
  364.              ; Note that this kind of program can write new code into
  365.              ; the memory and then run it, or can relocate or modify
  366.              ; itself and continue to run.
  367.  
  368.  
  369. ; Here is where the executable code starts;
  370.  
  371. JMP >L0  ; this is the execution entry point.
  372.  
  373.          ; at this point, the STACK from FWKTL provides working space
  374.          ;       for 0400h (1024 decimal) dwords, not counting space
  375.          ;         allowed for system use;
  376.          ;       the STACK will not return an exception until more than
  377.          ;         0ABAh dwords are on the stack (2746 decimal)
  378.          ; EAX = entry point for execution
  379.          ; EBX = start of free memory, DWORD aligned, after code
  380.          ; ECX = number of initialization DWORDs requested (P_ STRUC)
  381.          ; ESI points to start of initialization_data source (matching
  382.          ;       start of P_)
  383.          ; EDI=EBX to put initialization data there (using REP MOVSD)
  384.          ; decrement flag has been cleared (CLD) (for using REP MOVSD)
  385.  
  386. MSG:
  387. OMSG EQU $-CODESTART      ; used in calculating where this message is
  388. DB 'Hello, world!'
  389.  
  390. #IF EXAMPLE EQ 2 ; the FWKTL SHOW procedure is for use with ASCIIZ strings;
  391. DB 00            ; terminal 00 to make ASCIIZ string (SHOW provides CRLF)
  392. #ELSE            ;
  393. DB 0DH,0AH       ; carriage_return line_feed (for use with DOS32WRITE API)
  394. #ENDIF           ;
  395.  
  396. LMSG EQU $-MSG
  397.  
  398.  
  399.  
  400. #IF EXAMPLE EQ 3 ; this is a flexible method, using an expandable list
  401.               ; of procedures; in this example the procedures are
  402.               ; identified using ordinal numbers (this is required for
  403.               ; DOSCALLS procedures), but this method can be used with
  404.               ; procedures identified by name, in a list of ASCIIZ
  405.               ; procedure_names.
  406.  
  407. FNLIST:       ; list of procedures (DOSCALLS ordinals).
  408. OFNLIST EQU $-CODESTART ; offset used for finding FNLIST in memory.
  409.  
  410. ; these decimal number WORDs are in the same order
  411. ; as their corresponding DD targets in P_ STRUC
  412.         ;   these are the procedures (APIs) included in EXAMPLE=3:
  413.         ;                      decimal ordinals
  414.  
  415. DW 282  ;   DOS32WRITE          (DOSCALLS.282)
  416. DW 227  ;   DOS32SCANENV        (DOSCALLS.227)
  417. DW 228  ;   DOS32SEARCHPATH     (DOSCALLS.228)
  418. DW 229  ;   DOS32SLEEP          (DOSCALLS.229)
  419. DW 234  ;   DOS32EXIT           (DOSCALLS.234)
  420. DW 256  ;   DOS32SETFILEPTR     (DOSCALLS.256)
  421. DW 257  ;   DOS32CLOSE          (DOSCALLS.257)
  422. DW 273  ;   DOS32OPEN           (DOSCALLS.273)
  423. DW 281  ;   DOS32READ           (DOSCALLS.281)
  424. DW 286  ;   DOS32BEEP           (DOSCALLS.286)
  425. DW 299  ;   DOS32ALLOCMEM       (DOSCALLS.299)
  426. DW 304  ;   DOS32FREEMEM        (DOSCALLS.304)
  427. DW 311  ;   DOS32CREATETHREAD   (DOSCALLS.311)
  428. DW 312  ;   DOS32GETINFOBLOCKS  (DOSCALLS.312)
  429. DW 318  ;   DOS32LOADMODULE     (DOSCALLS.318)
  430. DW 321  ;   DOS32QUERYPROCADDR  (DOSCALLS.321)
  431. DW 322  ;   DOS32FREEMODULE     (DOSCALLS.322)
  432. DW 324  ;   DOS32CREATEEVENTSEM (DOSCALLS.324)
  433. DW 325  ;   DOS32OPENEVENTSEM   (DOSCALLS.325)
  434. DW 326  ;   DOS32CLOSEEVENTSEM  (DOSCALLS.326)
  435. DW 327  ;   DOS32RESETEVENTSEM  (DOSCALLS.327)
  436. DW 328  ;   DOS32POSTEVENTSEM   (DOSCALLS.328)
  437. DW 329  ;   DOS32WAITEVENTSEM   (DOSCALLS.329)
  438. DW 330  ;   DOS32QUERYEVENTSEM  (DOSCALLS.330)
  439. DW 348  ;   DOS32QUERYSYSINFO   (DOSCALLS.348)
  440.  
  441. NFNLIST EQU ($-FNLIST)/2 ; number of items in FNLIST
  442.                          ;   = list_length_in_bytes / word_length
  443. #ENDIF ; EXAMPLE EQ 3
  444.  
  445. ; Note that the APIs loaded in EXAMPLE=3 include procedures for loading
  446. ; and freeing other modules besides DOSCALLS (which was loaded by
  447. ; FWKTL); the GETFN and GETFNLIST functions in FWKTL can be used with
  448. ; other modules than DOSCALLS, once they have been loaded and their
  449. ; handles made available.
  450.  
  451. L0:                       ; target for JMP from entry point
  452.  
  453. #IF KEEP_EBX              ; in general use, this step is used to
  454. PUSH EBX                  ; save EBX value; but every call used in this
  455. #ENDIF                    ; particular program preserves EBX, so this
  456.                           ; step is not needed.
  457.  
  458. MOV EBP,ESP               ; save ESP value in EBP
  459.  
  460. REP MOVSD                 ; load EBX:P_ STRUC
  461.                           ; FWKTL preset ESI, EDI, and ECX,
  462.                           ;       and did CLD
  463.  
  464.  
  465. #IF EXAMPLE EQ 1          ; get specific API: DOS32WRITE (DOSCALLS.282)
  466.  
  467. MOV EAX,282               ; 282 decimal ordinal
  468. MOV EDX,[EBX].P_HDOSCALLS ; use handle for DOSCALLS module,
  469.                           ;   already loaded by FWKTL
  470. LEA EDI,[EBX].P_WRITE     ; point EDI at DWORD for holding address
  471. CALL [EBX].P_GETFN        ; get procedure address
  472.  
  473. #ENDIF
  474.  
  475.  
  476. #IF EXAMPLE EQ 3          ; this is a flexible method, using an
  477.                           ;   expandable list of procedure ordinals
  478.  
  479. MOV ESI,[EBX].P_LOADEDAT  ; calculate position of start of FNLIST
  480. ADD ESI,OFNLIST           ;   procedure list
  481.  
  482. LEA EDI,[EBX].P_WRITE     ; point EDI at first location for storing
  483.                           ;   procedure addresses
  484. MOV EAX,02                ; FNLIST list contains 2_byte ordinals
  485. MOV ECX,NFNLIST           ; number of items in list
  486. MOV EDX,[EBX].P_HDOSCALLS ; use handle for DOSCALLS module,
  487.                           ;   already loaded by FWKTL
  488. CALL [EBX].P_GETFNLIST    ; get addresses for procedures in list
  489.  
  490. #ENDIF
  491.  
  492.  
  493. #IF EXAMPLE EQ 2          ; this is a special_case, with a simple way
  494.                           ;   to show a message on the screen
  495.                           ;   (uses "standard error" handle=2):
  496.  
  497. MOV ESI,[EBX].P_LOADEDAT  ; point ESI at start of loaded program
  498. ADD ESI,OMSG              ; add offset to point ESI at start of message
  499. CALL [EBX].P_SHOW         ; call FWKTL SHOW function
  500.  
  501. #ENDIF
  502.  
  503.  
  504. #IF EXAMPLE EQ 1 OR EXAMPLE EQ 3 ; this illustrates a flexible method,
  505.                          ; using the DOS32WRITE API loaded above
  506.                          ; (see HELOWRLD.LST):
  507.  
  508. LEA EAX,[EBX].P_WROTE    ; pDWORD for amount written
  509. PUSHD EAX                ;
  510.  
  511. PUSHD LMSG               ; amount to write
  512.  
  513. MOV EAX,[EBX].P_LOADEDAT ; calculate position of message text
  514. ADD EAX,OMSG             ;
  515. PUSHD EAX                ; push address of beginning of text
  516.  
  517. PUSHD 1                  ; handle = 'standard output'
  518.  
  519. CALL [EBX].P_WRITE       ; indirect call to DOS32WRITE to write message
  520. #ENDIF
  521.  
  522. #IF EXAMPLE EQ 3
  523.  
  524. ;MOV ESP,EBP     ; could clear the stack first, but there's enough room
  525.  
  526. #IF KEEP_EBX
  527. MOV EBX,[EBP]             ; this is a way to restore EBX
  528. #ENDIF
  529.  
  530. PUSHD 0200H               ; 512 milliseconds duration (nominal)
  531. PUSHD 0100H               ; 256 cycles per second (approx)
  532. CALL [EBX].P_BEEP         ; indirect call to DOS32BEEP API loaded above
  533.  
  534. #ENDIF ; EXAMPLE EQ 3
  535.  
  536. #IF EXAMPLE NE 2          ; cleanup omitted in EXAMPLE EQ 2 to save space
  537.  
  538. MOV ESP,EBP               ; clean up STACK
  539.                           ;
  540. #IF KEEP_EBX              ;
  541. POP EBX                   ;
  542. #ENDIF                    ;
  543.  
  544. XOR EAX,EAX               ; set exit errorlevel = 0 (low 16 bits are
  545.                           ;   used in making exit errorlevel returned
  546.                           ;   to command line in OS/2 Warp)
  547. #ENDIF ; EXAMPLE NE 2
  548.  
  549. RET                       ; return to FWKTL
  550.  
  551. ; FWKTL is a trademark of Frederick W. Kantor.
  552. ; A386 was written by Eric Isaacson, copyrighted 1995, 1996.
  553. ; OS/2 and Warp are trademarks of International Business Machines Corporation.
  554.