TITLE HELOWRLD NAME HELOWRLD .386 .387 ; HELOWRLD.ASM can be assembled using IBM's ALP 3.00.004 assembler, ; linked to make a pseudo .DLL using LINK386, and ; stripped to leave only the FWKTL_format program using FWKTRIM. TEXT32 SEGMENT DWORD PUBLIC 'CODE' ASSUME CS:FLAT, DS:FLAT ALIGN 4 MAIN: ; This is a "Hello, world!" program for loading and execution in ; OS/2 Warp protected mode, using the FWKTL(TM) Text_program Loader, ; Version 1.00, to load it. ; ; (C)Copyright Frederick W. Kantor 1996. All rights reserved. ; ; programs loaded by FWKTL can modify themselves, and can modify ; their source (the file from which the program was loaded) ; ; ; To assemble in an OS/2 session using IBM's ALP 3.00.004, run ; ; MK_HWASM.CMD ; ; (enclosed in this package) and follow the instructions on the screen ; to make HELOWLD2.COM; ; ; the .COM extension can then be changed or removed, to help avoid ; error. FWKTL loads it independent of extension. ; ; if the resulting .COM program is executed directly in a DOS or OS/2 ; environment, it returns to the command line: the first byte is RET. ; ; There are 3 examples in this "Hello, world." program. The only ; parameter you need to set to use them is EXAMPLE : ; EXAMPLE EQU 1 ; EXAMPLE EQU 2 ; EXAMPLE EQU 3 EXAMPLE EQU 1 ; current setting ; ; EXAMPLE EQU 1 ; uses PSETUP = 0 in initialization; HELOWLD2.COM is 69 bytes long; ; this configuration shows how to get an API's address when you ; already have the module handle; it gets a specific address for a ; single procedure, DOS32WRITE (DOSCALLS.282) (see also P_ STRUC); ; the handle for the DOSCALLS module is provided by FWKTL as part ; of the initial data. ; ; EXAMPLE EQU 2 ; sets PSETUP = 1; HELOWRLD.COM is 41 bytes long; this uses a simple ; SHOW procedure in FWKTL to send an ASCIIZ string up to, but not ; including, its terminal 00 to 'standard output' (defaults to ; screen), and append a terminal carriage_return line_feed. (to save ; space, the exit errorlevel is not set to zero: the errorlevel ; returned is the low 16 bits of the DWORD for the program's entry ; point.) ; ; EXAMPLE EQU 3 ; sets PSETUP = 0; HELOWLD2.COM is 142 bytes long; ; this illustrates how functions can be easily added; it gets ; addresses for a list of 25 APIs and uses two of them, one to write ; "Hello, world!",0d,0a to "standard output" (the screen), and the ; other to sound a tone. The APIs loaded in example 3 include tools ; for loading and freeing other dynamic link library modules, ; allocating and freeing memory, some semaphore procedures, etc. ; To run the assembled HELOWLD2.COM in an OS/2 session: ; ; Format: FWKTL HELOWLD2.COM ; Initialization: ; ; FWKTL loads a program and sets ; ; EAX = entry point for execution ; EBX = 0 if no free memory requested ; ELSE EBX DWORD aligned, after code ; ECX = number of DWORDs in initialization requested (see P_ STRUC) ; ESI points to start of initialization_data source (matching start ; of P_) ; EDI = 0 if no free memory requested ; ELSE EDI = EBX = start of free memory, in case you wish to put ; initialization data there ; the "direction flag" (decrement flag) is cleared (CLD) ; ; FWKTL provides initial values, and several procedures; ; see P_ STRUC, below, for initial values and procedure addresses. ; These values are easily copied, with ESI, EDI, ECX, and CLD from ; FWKTL. If free memory was requested (done in HELOWRLD.1), use ; REP MOVSD ; If no free memory was requested, save values in STACK. For saving ; EBX in STACK, I suggest you modify J_ STRUC and put J_EBX at the ; beginning, and load EDI with the effective address of the beginning ; of the standard FWKTL initialization values: e.g., ; SUB ESP,TYPE J_ ; MOV EBP,ESP ; LEA EDI,[EBP].P_LOADEDAT ; where to put initialization data ; REP MOVSD ; using ESI, ECX, and CLD from FWKTL ; FWKTL calls the program as a routine in Thread 1: ; ; on entry, the stack will not return an exception until 0ABAh dwords ; are pushed (= 02AE8h bytes = 10984 (decimal) bytes) ; ; on return to FWKTL: CS, SS, and ESP must be correct when RET ; is executed ; ; EAX, EBX, ECX, EDX, ESI, EDI, EBP, DS, ES, FS, GS can be changed ; ; EAX is used to return an exit errorlevel, of which the low 16 bits ; (AX) is returned by OS/2 Warp at the command line. ; to display exit errorlevels, you can use PROMPT=[$p $r] ;------------------------------------------------------- KEEP_EBX EQU 00 ; set to 1 to store EBX in stack, so that it can ; be recovered by MOV EBX,[EBP] (e.g., in case a ; procedure called changes it); EBP may be less ; likely to be changed than EBX during such calls. ; All calls used in these 3 examples preserve EBX, ; so this is not used. ;------------------------------------------------------- ; PSETUP is used to control P_ STRUC and to set ; bit00 in second DWORD in the program header ; = 0 to get the shorter initial setup data; ; = 1 to get the longer initial setup data which ; has the addresses for the SHOW and USWORDCAPS ; utilities IF EXAMPLE EQ 1 OR EXAMPLE EQ 3 PSETUP EQU 0 ELSEIF EXAMPLE EQ 2 PSETUP EQU 1 ENDIF ;------------------------------------------------------- ; structure for initial data: P_ STRUC ; these are provided by FWKTL for initialization, ; according to file header settings: P_LOADEDAT DD ? ; start of this memory block P_PWHENCE DD ? ; points to ASCIIZ string re where program was found P_PCOMTAIL DD ? ; points to ASCIIZ command tail P_GETFN DD ? ; address for indirect call to FWKTL GETFN function: ; Input: ; EAX = ProcedureOrdinal ; (maximum permitted OS/2 ordinal <= 65533) ; if EAX > 0, ESI is ignored ; OR ; EAX = 0 ; ESI points to ASCIIZ procedure_name ; ; EDX = module handle ; EDI points to target DWORD to receive ; procedure address ; ; Output: ; if successful, ; zero_flag is set ; procedure address is in target DWORD ; EAX = 0 ; ; if error ; zero_flag is cleared ; EAX contains error number: ; 6 ERROR_INVALID_HANDLE ; 123 ERROR_INVALID_NAME ; 65079 ERROR_ENTRY_IS_CALLGATE ; ; all other CPU registers and flags are preserved P_GETFNLIST DD ? ; address for indirect call to FWKTL GETFNLIST ; function, to get the addresses for a list of ; procedures in the same module: ; ; EBX, EDX, EBP are preserved across this function ; ; Input: ; EAX = 0 if list is ASCIIZ procedure_names ; 2 if list is WORD ordinals ; 4 if list is DWORD ordinals ; (maximum permitted OS/2 ordinal = 65533) ; ECX = number of items in procedure list ; EDX = module handle ; ESI points to start of list of WORDs, DWORDs, ; or series of ASCIIZ procedure_names ; EDI points to start of target DWORDs to receive ; the corresponding procedure addresses ; ; Output: ; if no error: ; zero_flag is set ; each target DWORD contains its procedure address ; ESI: ; if EAX = 0 ; ESI points to terminal 00h of last ASCIIZ ; string; ; ELSE if EAX > 0 ; ESI points to first byte after source; ; EDI points to first byte after last target DWORD ; ; if error: ; zero_flag is cleared ; EAX contains error number: ; 6 ERROR_INVALID_HANDLE ; 123 ERROR_INVALID_NAME ; 65079 ERROR_ENTRY_IS_CALLGATE ; ECX is not decremented on failed step ; ESI points to list item identifying the procedure ; for which the failure occurred P_HDOSCALLS DD ? ; handle for DOSCALLS module as loaded by FWKTL, ; can be used in EDX for P_GETFN or P_GETFNLIST. ;------------------------- end of setup for PSETUP EQ 0 IF PSETUP EQ 1 ; two additional procedures are optionally available from FWKTL: P_SHOW DD ? ; address for FWKTL SHOW function; ; ; usage: ; ; ESI points to ASCIIZ string to show on screen ; using 'standard error' handle=2; ; this procedure drops terminal 00 and adds 0D,0A; ; all CPU registers and flags are preserved. P_USWORDCAPS DD ? ; address for FWKTL capitalization function; ; ; usage: ; ; ESI points to contiguous string > ' ' to ; capitalize, US English; ; all CPU registers and flags are preserved. ; ; (e.g., this procedure can be used when '/' or '-' ; is found in command tail, for case_insensitive ; options when you don't capitalize whole tail) ENDIF ;------------------------- end of setup for PSETUP EQ 1 IF EXAMPLE EQ 1 OR EXAMPLE EQ 3 ; these are used with an API: P_WROTE DD ? ; used with the DOS32WRITE API P_WRITE DD ? ; to hold address for OS/2 DOS32WRITE API ; Note: P_WRITE is also the first entry in the ; address targets used in EXAMPLE EQ 3: ; P_WROTE must not be inserted directly ; below P_WRITE, because it would offset ; the rest of the address targets in ; EXAMPLE EQ 3 ENDIF ; other material can be inserted here: IF EXAMPLE EQ 3 ; addresses for 24 more APIs P_SCANENV DD ? P_SEARCHPATH DD ? P_SLEEP DD ? P_EXIT DD ? P_SETFILEPTR DD ? P_CLOSE DD ? P_OPEN DD ? P_READ DD ? P_BEEP DD ? ; see note on using BEEP for diagnostics, below P_ALLOCMEM DD ? P_FREEMEM DD ? P_CREATETHREAD DD ? P_GETINFOBLOCKS DD ? P_LOADMODULE DD ? P_QUERYPROCADDR DD ? P_FREEMODULE DD ? P_CREATEEVENTSEM DD ? P_OPENEVENTSEM DD ? P_CLOSEEVENTSEM DD ? P_RESETEVENTSEM DD ? P_POSTEVENTSEM DD ? P_WAITEVENTSEM DD ? P_QUERYEVENTSEM DD ? P_QUERYSYSINFO DD ? ENDIF ; EXAMPLE EQ 3 P_ ENDS ; end of P_ STRUC ;------------------------------------------------------- ; Note: BEEP is convenient for simple diagnostics; ; e.g., you can insert code blocks like this to make a tone ; when each such place is reached, and set different values ; for frequency and/or duration to distinguish the beeps; ; registers and flags are preserved across this function: ; put this before the first beep call you want turned on: ; ; rundb equ 01 ; 01 to insert them (and remove initial ";"), ; ; 00 to exclude them ; if rundb ;debug ; pushfd ;debug ; pushad ;debug ; pushd 0100h ; duration, milliseconds ;debug ; pushd 0100h ; frequency, cycles per second ;debug ; call [ebx].p_beep ;debug ; add esp,08 ;debug ; popad ;debug ; popfd ;debug ; endif ;debug ;------------------------------------------------------- ; Here is the code header: ; the first byte of the header is a RET, in case the program has ; a .COM extension and someone accidentally tries to run it directly ; in a DOS or OS/2 session. CODESTART: ; used as a reference point RET ; 4_byte header identification string DB 'FWK' ; DD PSETUP ; bit00 = 0 for PSETUP=0 (see P_ STRUC) ; bit00 = 1 for PSETUP=1 (see P_ STRUC) ; bits 31...01 are reserved, and must be zero for use with ; FWKTL version 1.00. DD TYPE P_ ; amount of free memory requested after code; ; in these examples, just enough to hold a P_ STRUC; ; PSETUP affects the size of P_ STRUC used ; to hold standard FWKTL initialization data; ; storage of any other data there also affects the ; size of P_ STRUC; e.g., P_ can contain buffers. ; the free memory starts DWORD aligned, zeroed. ; more memory than used in P_ STRUC can be requested; ; note that memory requested in this way is committed ; when allocated. ; for efficiency and flexibility, programs which need a ; lot of memory can use API procedures to allocate and ; free memory, rather than asking for it as part of ; installation. EXAMPLE EQ 3 loads the addresses for ; calling some memory management procedures. ; Note that this kind of program can write new code into ; the memory and then run it, or can relocate or modify ; itself and continue to run. ; Here is where the executable code starts; JMP SHORT LL0 ; this is the execution entry point. ; at this point, the STACK from FWKTL provides working space ; for 0400h (1024 decimal) dwords, not counting space ; allowed for system use; ; the STACK will not return an exception until more than ; 0ABAh dwords are on the stack (2746 decimal) ; EAX = entry point for execution ; EBX = start of free memory, DWORD aligned, after code ; ECX = number of initialization DWORDs requested (P_ STRUC) ; ESI points to start of initialization_data source (matching ; start of P_) ; EDI=EBX to put initialization data there (using REP MOVSD) ; decrement flag has been cleared (CLD) (for using REP MOVSD) MSG: OMSG EQU $-CODESTART ; used in calculating where this message is DB 'Hello, world!' IF EXAMPLE EQ 2 ; the FWKTL SHOW procedure is for use with ASCIIZ strings; DB 00 ; terminal 00 to make ASCIIZ string (SHOW provides CRLF) ELSE ; DB 0DH,0AH ; carriage_return line_feed (for use with DOS32WRITE API) ENDIF ; LMSG EQU $-MSG IF EXAMPLE EQ 3 ; this is a flexible method, using an expandable list ; of procedures; in this example the procedures are ; identified using ordinal numbers (this is required for ; DOSCALLS procedures), but this method can be used with ; procedures identified by name, in a list of ASCIIZ ; procedure_names. FNLIST: ; list of procedures (DOSCALLS ordinals). OFNLIST EQU $-CODESTART ; offset used for finding FNLIST in memory. ; these decimal number WORDs are in the same order ; as their corresponding DD targets in P_ STRUC ; these are the procedures (APIs) included in EXAMPLE=3: ; decimal ordinals DW 282 ; DOS32WRITE (DOSCALLS.282) DW 227 ; DOS32SCANENV (DOSCALLS.227) DW 228 ; DOS32SEARCHPATH (DOSCALLS.228) DW 229 ; DOS32SLEEP (DOSCALLS.229) DW 234 ; DOS32EXIT (DOSCALLS.234) DW 256 ; DOS32SETFILEPTR (DOSCALLS.256) DW 257 ; DOS32CLOSE (DOSCALLS.257) DW 273 ; DOS32OPEN (DOSCALLS.273) DW 281 ; DOS32READ (DOSCALLS.281) DW 286 ; DOS32BEEP (DOSCALLS.286) DW 299 ; DOS32ALLOCMEM (DOSCALLS.299) DW 304 ; DOS32FREEMEM (DOSCALLS.304) DW 311 ; DOS32CREATETHREAD (DOSCALLS.311) DW 312 ; DOS32GETINFOBLOCKS (DOSCALLS.312) DW 318 ; DOS32LOADMODULE (DOSCALLS.318) DW 321 ; DOS32QUERYPROCADDR (DOSCALLS.321) DW 322 ; DOS32FREEMODULE (DOSCALLS.322) DW 324 ; DOS32CREATEEVENTSEM (DOSCALLS.324) DW 325 ; DOS32OPENEVENTSEM (DOSCALLS.325) DW 326 ; DOS32CLOSEEVENTSEM (DOSCALLS.326) DW 327 ; DOS32RESETEVENTSEM (DOSCALLS.327) DW 328 ; DOS32POSTEVENTSEM (DOSCALLS.328) DW 329 ; DOS32WAITEVENTSEM (DOSCALLS.329) DW 330 ; DOS32QUERYEVENTSEM (DOSCALLS.330) DW 348 ; DOS32QUERYSYSINFO (DOSCALLS.348) NFNLIST EQU ($-FNLIST)/2 ; number of items in FNLIST ; = list_length_in_bytes / word_length ENDIF ; EXAMPLE EQ 3 ; Note that the APIs loaded in EXAMPLE EQ 3 include procedures for ; loading and freeing other modules besides DOSCALLS (which was loaded ; by FWKTL); the GETFN and GETFNLIST functions in FWKTL can be used ; with other modules than DOSCALLS, once they have been loaded and ; their handles made available. LL0: ; target for JMP from entry point IF KEEP_EBX ; in general use, this step is used to PUSH EBX ; save EBX value; but every call used in this ENDIF ; particular program preserves EBX, so this ; step is not needed. MOV EBP,ESP ; save ESP value in EBP REP MOVSD ; load EBX:P_ STRUC ; FWKTL preset ESI, EDI, and ECX, ; and did CLD IF EXAMPLE EQ 1 ; get specific API: DOS32WRITE (DOSCALLS.282) MOV EAX,282 ; 282 decimal ordinal MOV EDX,[EBX].P_HDOSCALLS ; use handle for DOSCALLS module, ; already loaded by FWKTL LEA EDI,[EBX].P_WRITE ; point EDI at DWORD for holding address CALL [EBX].P_GETFN ; get procedure address ENDIF IF EXAMPLE EQ 3 ; this is a flexible method, using an ; expandable list of API procedure ordinals MOV ESI,[EBX].P_LOADEDAT ; calculate position of start of FNLIST ADD ESI,OFNLIST ; procedure list LEA EDI,[EBX].P_WRITE ; point EDI at first location for storing ; procedure addresses MOV EAX,02 ; FNLIST list contains 2_byte ordinals MOV ECX,NFNLIST ; number of items in list MOV EDX,[EBX].P_HDOSCALLS ; use handle for DOSCALLS module, ; already loaded by FWKTL CALL [EBX].P_GETFNLIST ; get addresses for procedures in list ENDIF IF EXAMPLE EQ 2 ; this is a special_case, with a simple way ; to show a message on the screen ; (uses "standard error" handle=2): MOV ESI,[EBX].P_LOADEDAT ; point ESI at start of loaded program ADD ESI,OMSG ; add offset to point ESI at start of message CALL [EBX].P_SHOW ; call FWKTL SHOW function ENDIF IF EXAMPLE EQ 1 OR EXAMPLE EQ 3 ; this illustrates a flexible method, ; using the DOS32WRITE API loaded above ; (see HELOWRLD.LST): LEA EAX,[EBX].P_WROTE ; pDWORD for amount written PUSHD EAX ; PUSHD LMSG ; amount to write MOV EAX,[EBX].P_LOADEDAT ; calculate position of message text ADD EAX,OMSG ; PUSHD EAX ; push address of beginning of text PUSHD 1 ; handle = 'standard output' CALL [EBX].P_WRITE ; indirect call to DOS32WRITE to write message ENDIF IF EXAMPLE EQ 3 ;MOV ESP,EBP ; could clear the stack first, but there's enough room IF KEEP_EBX MOV EBX,[EBP] ; this is a way to restore EBX ENDIF PUSHD 0200H ; 512 milliseconds duration (nominal) PUSHD 0100H ; 256 cycles per second (approx) CALL [EBX].P_BEEP ; indirect call to DOS32BEEP API loaded above ENDIF ; EXAMPLE EQ 3 IF EXAMPLE NE 2 ; cleanup omitted in EXAMPLE EQ 2 to save space MOV ESP,EBP ; clean up STACK ; IF KEEP_EBX ; POP EBX ; ENDIF ; XOR EAX,EAX ; set exit errorlevel = 0 (low 16 bits are ; used in making exit errorlevel returned ; to command line in OS/2 Warp) ENDIF ; EXAMPLE NE 2 RET ; return to FWKTL DB 'FWKEOF',0,1 ; EOF signature for use with FWKTRIM TEXT32 ENDS END MAIN ; FWKTL and FWKTRIM are trademarks of Frederick W. Kantor. ; ; IBM, OS/2, Warp, and IBM Assembly Language Processor (ALP) are trademarks ; of International Business Machines Corporation.