From 80ab2fddfdf30f09f0a0a637654cbb3cd5c7baa6 Mon Sep 17 00:00:00 2001 From: Rich Turner Date: Fri, 12 Aug 1983 17:53:34 -0700 Subject: MS-DOS v2.0 Release --- v2.0/source/XENIX.ASM | 907 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 907 insertions(+) create mode 100644 v2.0/source/XENIX.ASM (limited to 'v2.0/source/XENIX.ASM') diff --git a/v2.0/source/XENIX.ASM b/v2.0/source/XENIX.ASM new file mode 100644 index 0000000..dd20b50 --- /dev/null +++ b/v2.0/source/XENIX.ASM @@ -0,0 +1,907 @@ +; +; xenix file calls for MSDOS +; + +INCLUDE DOSSEG.ASM + +IFNDEF KANJI +KANJI EQU 0 ;FALSE +ENDIF + +CODE SEGMENT BYTE PUBLIC 'CODE' + ASSUME SS:DOSGROUP,CS:DOSGROUP + +.xlist +.xcref +INCLUDE DOSSYM.ASM +INCLUDE DEVSYM.ASM +.cref +.list + +TITLE XENIX - IO system to mimic UNIX +NAME XENIX + + i_need NoSetDir,BYTE + i_need CURDRV,BYTE + i_need IOCALL,BYTE + i_need IOMED,BYTE + i_need IOSCNT,WORD + i_need IOXAD,DWORD + i_need DIRSTART,WORD + i_need ATTRIB,BYTE + i_need THISFCB,DWORD + i_need AuxStack,BYTE + i_need Creating,BYTE + i_need ThisDRV,BYTE + i_need NAME1,BYTE + i_need LastEnt,WORD + i_need ThisDPB,DWORD + i_need EntLast,WORD + i_need CurrentPDB,WORD + i_need sft_addr,DWORD ; pointer to head of table + i_need CURBUF,DWORD ; pointer to current buffer + i_need DMAADD,DWORD ; pointer to current dma address + +BREAK + +CODE ENDS +DATA SEGMENT BYTE PUBLIC 'DATA' + +open_name DW ? + DW ? +open_access DB ? +open_jfn DW ? ; accessed as DD +open_jfn_b DW ? ; accessed as DD with above +open_sfn DW ? +open_sfoff DW ? ; accessed as DD +open_sfn_b DW ? ; accessed as DD with above +open_devid DB ? +Cr_read_only DB ? +rename_source DD ? +rename_dest DD ? + +DATA ENDS +CODE SEGMENT BYTE PUBLIC 'CODE' + +BREAK + +; +; Input: DS:DX is an ASCIZ path +; Output: Carry set if meta-characters present or path malformed and +; Zero is set if the only problem is that meta-characters +; are present in the last element of the path +procedure Validate_path,near + ASSUME DS:NOTHING,ES:NOTHING + PUSH AX + PUSH CX + PUSH SI + MOV SI,DX + MOV CX,0FFH ;No path seps yet + MOV AX,[SI] ; Get first two bytes + OR AL,AL + JZ validate_malformed ; NUL path + CMP AH,':' + JNZ validate_loop ; OK so far + CMP BYTE PTR [SI+2],0 + JZ validate_malformed ; NUL path (just d:) +validate_loop: + LODSB +validate_loop1: + + IF KANJI + invoke TESTKANJ + JZ NOTKANJ6 + INC SI + JMP validate_loop + +NOTKANJ6: + ENDIF + + OR AL,AL + JZ validate_end + CMP AL,"?" + JZ validate_error + CMP AL,"*" + JZ validate_error + invoke PathChrCmp + JNZ validate_loop + JCXZ validate_malformed ;If path sep, cannot have meta yet + LODSB ;Look ahead one char + OR AL,AL + JZ validate_checktslsh ;Trailing path sep + invoke PathChrCmp + JNZ validate_loop1 ;Double path sep? +validate_malformed: + INC CX + OR CX,CX ;Reset zero + JMP SHORT validate_set_carry + +validate_error: + XOR CX,CX ;Flag metas found + JMP validate_loop + +validate_checktslsh: +;A bizarre case, "/" is OK, "d:/" is OK, anything else is an error + SUB SI,DX + CMP SI,2 + JZ validate_end ;Two chars, the '/' and the NUL + CMP SI,4 + JNZ validate_malformed ;Four chars, "D:/" + MOV SI,DX + CMP BYTE PTR [SI+1],':' + JNZ validate_malformed ;Second char must be a ':' + +validate_end: + OR CX,CX ;Clears carry + JNZ validate_ok ;No metas found, leave carry clear +validate_set_carry: + STC +validate_ok: + POP SI + POP CX + POP AX + return +validate_path ENDP + +BREAK + +; +; Input: DS:DX point to a path +; Output: Carry reset - outputs of GetPath +; carry set - AL has error code +; + procedure Access_path,NEAR + ASSUME DS:NOTHING,ES:NOTHING + CALL Validate_path + JC access_no_path + MOV SI,DX + invoke GetPath + retnc + MOV AL,error_file_not_found + OR CL,CL + JNZ access_ret +access_no_path: + MOV AL,error_path_not_found +access_ret: + STC + return +access_path ENDP + +BREAK +; +; system file table data +; + +; +; The system file table is two linear tables. The first table is the +; DOS initialization table containing a default number of FCBs. The +; first word in the table is a link to the second table, which +; SYSINIT sets up, the second word is the number of FCBs in the table. +; + +; +; find_free_jfn +; input: none +; output: JNC +; ES:DI is pointer to free JFN +; JC +; ES,DI indeterminate +; + procedure Find_free_jfn,NEAR + ASSUME DS:NOTHING,ES:NOTHING + PUSH AX + PUSH CX + MOV AL,0FFh + MOV ES,[CurrentPDB] + MOV DI,PDB_JFN_Table + MOV CX,FilPerProc + REPNE SCASB + STC + JNZ Find_jfn_ret + DEC DI + CLC +Find_jfn_ret: + POP CX + POP AX + return +Find_free_jfn ENDP + +BREAK +; +; find_free_sfn +; input: none +; output: JNC +; ES:DI is free sf entry +; SI is sfn +; JC +; ES,DI,SI indeterminate +; +; sft_addr --> (link) count (fcbs) +; links = -1 means end of list +; + procedure Find_free_sfn,NEAR + ASSUME DS:NOTHING,ES:NOTHING + PUSH BX + PUSH CX + LES BX,sft_addr ; head of chain of tables + XOR SI,SI ; count of sfn + + ; ES:BX points to table... search through table +Find_sfn_in_table: + CMP BX,-1 ; end of chain + JZ Find_no_free_sfns + MOV DI,sft_table ; offset to sf entry + MOV CX,ES:[BX].sft_count ; count of fcbs in table + +Find_sfn: + CMP ES:BYTE PTR [BX+DI].sf_ref_count,0h + JZ Find_got_sfn ; ref count is 0 -> free entry + ADD DI,SIZE sf_entry ; look to next entry + INC SI ; bump sfn + LOOP Find_sfn + LES BX,ES:[BX].sft_link ; link to next + JMP SHORT Find_sfn_in_table ; look for more + +Find_no_free_sfns: + STC + JMP SHORT find_ret +Find_got_sfn: + ADD DI,BX + CLC +Find_ret: + POP CX + POP BX + RET +Find_free_sfn ENDP + +BREAK <$Open - open a file handle> +; +; Assembler usage: +; LDS DX, Name +; MOV AH, Open +; MOV AL, access +; INT int_command +; +; ACCESS Function +; ------ -------- +; open_for_read file is opened for reading +; open_for_write file is opened for writing +; open_for_both file is opened for both reading and writing. +; +; Error returns: +; AX = error_invalid_access +; = error_file_not_found +; = error_access_denied +; = error_too_many_open_files +; + + procedure $Open,NEAR + ASSUME DS:NOTHING,ES:NOTHING + MOV [Cr_read_only],0 +Open_create: + CMP AL,open_for_both ; validate access + JBE OPEN_get_jfn + error error_invalid_access + +OPEN_get_jfn: + MOV [open_name+2],DS + context DS + MOV open_name,DX + MOV open_access,AL + + invoke Find_free_jfn ; scan through user's area + ; ES:DI is the jfn entry + JNC OPEN_get_sfn +OPEN_too_many: + error error_too_many_open_files + +OPEN_get_sfn: + MOV OPEN_jfn_b,ES + MOV OPEN_jfn,DI + invoke Find_free_sfn ; get a free sft entry + ; ES:DI is the SFT entry that's free, SI is the sfn + JC OPEN_too_many + +OPEN_file: + MOV OPEN_sfn,SI + MOV OPEN_sfoff,DI + MOV OPEN_sfn_b,ES +; +; open the file +; + PUSH DS + LDS DX,DWORD PTR [open_name] + ASSUME DS:NOTHING + CALL access_path + POP DS + ASSUME DS:DOSGROUP + JNC open_check_access ; carry set -> error + transfer SYS_RET_ERR + +open_check_access: + MOV ES,WORD PTR [CURBUF+2] ; get buffer location + MOV open_devid,AH + TEST AH,080h + JNZ open_set_FCB_dev ;is a device + MOV AL,ES:[BX].dir_attr + TEST AL,attr_directory ; can't open directories + JZ open_try_volid + +open_bad_access: + error error_access_denied + +open_try_volid: + TEST AL,attr_volume_id ; can't open volume ids + JNZ open_bad_access + TEST AL,attr_read_only ; check write on read only + JZ open_set_FCB + CMP [Cr_read_only],0 + JNZ open_set_FCB ; ok if creating read only file + CMP open_access, open_for_read + JNZ open_bad_access ; writing on a read only file + JMP SHORT open_set_FCB + +open_set_FCB_dev: + PUSH SS + POP ES ;Device opens are DOSGROUP relative + +open_set_FCB: + MOV CX,11 ; copy name into FCB... + PUSH SI ; ES:BX is source, must change + MOV SI,BX ; ES:SI is source + MOV DI,open_sfoff ; ??:DI is dest + PUSH DS + PUSH ES + MOV ES,open_sfn_b ; ES:DI is dest + POP DS ; DS:SI is source + ASSUME DS:NOTHING +; +; need to save attribute for the close operation +; + MOV AH,DS:[BX.dir_attr] ; save attribute for close + MOV ES:[DI.sf_attr],AH + + ADD DI,sf_fcb+1 ; point to name + + IF KANJI + MOVSB + CMP BYTE PTR ES:[DI-1],5 + JNZ NOTKTRAN + MOV BYTE PTR ES:[DI-1],0E5H +NOTKTRAN: + DEC CX + ENDIF + + REP MOVSB ; move in parsed name + POP DS + ASSUME DS:DOSGROUP + POP SI + LES DI,DWORD PTR [open_sfoff] + ADD DI,sf_fcb ; offset on fcb in sf entry + MOV AH,open_devid + invoke DOOPEN ; let open code fill in blanks + context DS + LES DI,DWORD PTR [open_sfoff] + INC ES:[DI].sf_ref_count ; reference this FCB + MOV AL,open_access ; stash the access + MOV ES:BYTE PTR [DI].sf_mode,AL + XOR AX,AX + MOV ES:WORD PTR [DI.sf_FCB.fcb_RR],AX ; beginning of file + MOV ES:WORD PTR [DI.sf_FCB.fcb_RR+2],AX + INC AX + MOV ES:WORD PTR [DI.sf_FCB.fcb_RECSIZ],AX ; byte io only + LES DI,DWORD PTR [open_jfn] + MOV AX,open_sfn + MOV ES:BYTE PTR [DI],AL ; stash sfn in PDB + SUB DI,PDB_jfn_table ; get jfn for user + MOV AX,DI + transfer SYS_RET_OK +$Open ENDP + + +BREAK <$UNLINK - delete a file entry> +; +; Assembler usage: +; LDS DX, name +; MOV AH, Unlink +; INT 21h +; +; Error returns: +; AX = error_file_not_found +; = error_access_denied +; + procedure $UNLINK,NEAR + ASSUME DS:NOTHING,ES:NOTHING + CALL access_path + JNC unlink_check_attr + transfer SYS_RET_ERR + +unlink_check_attr: + JZ unlink_dir + LDS DI,DWORD PTR [CURBUF] ; get directory entry + TEST DS:[BX.dir_attr],attr_read_only + JZ unlink_doit + +unlink_dir: + error error_access_denied + +unlink_doit: + MOV BYTE PTR DS:[BX.dir_name],0E5h ; delete dir entry + MOV BYTE PTR DS:[DI.BUFDIRTY],1 ; dirty the buffer + LODSW + MOV BX,AX + AND BX,0FFFh + context DS + JZ unlink_flush + invoke RELEASE +unlink_flush: + MOV AL,BYTE PTR ES:[BP.DPB_drive] + invoke FLUSHBUF + transfer SYS_RET_OK +$UNLINK ENDP + +BREAK <$CREAT - creat a new file and open him for input> +; +; Assembler usage: +; LDS DX, name +; MOV AH, Creat +; MOV CX, access +; INT 21h +; ; AX now has the handle +; +; Error returns: +; AX = error_access_denied +; = error_path_not_found +; = error_too_many_open_files +; + + + procedure $CREAT,NEAR + ASSUME DS:NOTHING,ES:NOTHING + CALL Validate_path + JNC unlink_do_make + error error_path_not_found +unlink_do_make: + PUSH DX + PUSH DS + context DS + MOV WORD PTR [CREATING],0E5FFh + MOV WORD PTR [ThisFCB+2],SS + MOV WORD PTR [ThisFCB],OFFSET DOSGROUP:AUXSTACK-40 + MOV SI,DX + MOV AL,CL + AND CL,attr_read_only + MOV [Cr_read_only],CL + POP DS + PUSH DS +ASSUME DS:NOTHING + invoke MakeNode + POP DS + POP DX + OR AL,AL + JZ creat_open + CMP AL,3 + JZ creat_open +creat_no_access: + error error_access_denied +creat_open: + MOV AL,open_for_both + JMP Open_create + +$CREAT ENDP + + +BREAK <$DUP - duplicate a jfn> +; +; Assembler usage: +; MOV BX, fh +; MOV AH, Dup +; INT int_command +; AX has the returned handle +; Errors: +; AX = dup_invalid_handle +; = dup_too_many_open_files + procedure $DUP,NEAR + ASSUME DS:NOTHING,ES:NOTHING + context DS + invoke Find_free_jfn + JC dup_no_free_handles + +dup_force: + PUSH ES + PUSH DI + invoke Get_sf_from_jfn + POP SI + POP DS + JC dup_bad_handle + ; ES:DI is pointer to sf entry + ; DS:DI is pointer to jfn + INC ES:[DI].sf_ref_count ; another jfn reference... + MOV AL,[BX].PDB_JFN_table ; get old sfn + MOV [SI],AL ; store in new place + SUB SI,PDB_JFN_table ; get jfn + MOV AX,SI + transfer SYS_RET_OK + +dup_no_free_handles: + error error_too_many_open_files + +dup_bad_handle: + error error_invalid_handle +$DUP ENDP + +BREAK <$DUP2 - force a dup on a particular jfn> +; +; Assembler usage: +; MOV BX, fh +; MOV CX, newfh +; MOV AH, Dup2 +; INT int_command +; Error returns: +; AX = error_invalid_handle +; + procedure $DUP2,NEAR + ASSUME DS:NOTHING,ES:NOTHING + XCHG BX,CX ; BX < destination jfn + PUSH BX + PUSH CX + invoke $CLOSE ; close BX + context DS + POP CX + POP BX + invoke Get_jfn_pointer + XCHG BX,CX + JNC dup_force +lseek_bad_handle: + error error_invalid_handle +$DUP2 ENDP + + +BREAK <$CHMOD - change file attributes> +; +; Assembler usage: +; LDS DX, name +; MOV CX, attributes +; INT 21h +; Error returns: +; AX = error_path_not_found +; AX = error_access_denied +; + procedure $CHMOD,NEAR + ASSUME DS:NOTHING,ES:NOTHING + CMP AL,1 + JBE chmod_save + error error_invalid_function +chmod_save: + JB chmod_try_file + MOV BX,CX + AND BX,NOT attr_changeable + JZ chmod_try_file + +chmod_bad: + error error_access_denied + +chmod_bye: + transfer SYS_RET_ERR +chmod_try_file: + PUSH CX + PUSH AX + CALL access_path + POP DX + POP CX + JC chmod_bye + LES DI,[CURBUF] + context DS + OR DL,DL + JZ chmod_fetch + AND BYTE PTR ES:[BX].dir_attr,NOT attr_changeable + OR BYTE PTR ES:[BX].dir_attr,CL + MOV ES:[DI.BUFDIRTY],1 + MOV AL,-1 + invoke FlushBuf + transfer SYS_RET_OK +chmod_fetch: + XOR CX,CX + MOV CL,BYTE PTR ES:[BX].dir_attr + invoke Get_user_stack + MOV [SI.user_CX],CX + transfer SYS_RET_OK +$chmod ENDP + +BREAK <$CURRENT_DIR - dump the current directory into user space> +; +; Assembler usage: +; LDS SI,area +; MOV DL,drive +; INT 21h +; ; DS:SI is a pointer to 64 byte area that contains drive +; ; current directory. +; Error returns: +; AX = error_invalid_drive +; +procedure $CURRENT_DIR,NEAR + ASSUME DS:NOTHING,ES:NOTHING + PUSH DS + PUSH BX + PUSH SI + invoke $get_DPB +; +; ES:BP points to DPB. DS:SI points to user stack, unless error +; + CMP AL,0FFh + JNZ current_copy + POP AX ; Clean Stack + POP AX + POP AX + error error_invalid_drive + +current_copy: + POP DI ; where to move to + POP [SI.user_BX] ; restore old BX + POP BX + MOV [SI.user_DS],BX ; and restore old DS +; +; ES:BP is pointer to DPB. BX:DI is pointer to destination +; + CMP ES:[BP.dpb_current_dir],-1 + JNZ current_ok + PUSH BX + PUSH DI + MOV [ATTRIB],attr_all + invoke GETCURRDIR + POP DI + POP BX +current_ok: + MOV SI,BP ; ES:SI is source + PUSH ES + POP DS ; DS:SI is source + MOV ES,BX ; ES:DI is destination + CMP [SI.dpb_current_dir],0 + JNZ current_move + MOV BYTE PTR [SI.dpb_dir_text],0 + +current_move: + ADD SI,dpb_dir_text + MOV CX,DIRSTRLEN +current_loop: + LODSB + STOSB + OR AL,AL + LOOPNZ current_loop + transfer SYS_RET_OK +$CURRENT_DIR ENDP + + +BREAK <$RENAME - move directory entries around> +; +; Assembler usage: +; LDS DX, source +; LES DI, dest +; MOV AH, Rename +; INT 21h +; +; Error returns: +; AX = error_file_not_found +; = error_not_same_device +; = error_access_denied +procedure $RENAME,near + + MOV WORD PTR [rename_source],DX + MOV WORD PTR [rename_source+2],DS + MOV WORD PTR [rename_dest],DI + MOV WORD PTR [rename_dest+2],ES + CALL Access_path + JNC rename_check_dir + transfer SYS_RET_ERR + +rename_check_dir: + JZ rename_no_access + MOV DS,WORD PTR [CurBuf+2] + PUSH [BX.dir_date] + PUSH [BX.dir_first] + PUSH [BX.dir_size_h] + PUSH [BX.dir_size_l] + PUSH [BX.dir_time] + PUSH WORD PTR [BX.dir_attr] + PUSH WORD PTR [ThisDrv] + LDS SI,[rename_dest] + invoke GetPath + POP AX + JC rename_check_drives +rename_bad_access: + ADD SP,12 +rename_no_access: + error error_access_denied +rename_check_drives: + CMP AL,[ThisDrv] + JZ rename_create + ADD SP,12 + error error_not_same_device +rename_create: + LDS SI,[rename_dest] + POP AX + PUSH AX + MOV WORD PTR [Creating],0E5FFh + MOV WORD PTR [ThisFCB+2],SS + MOV WORD PTR [ThisFCB],OFFSET DOSGROUP:AUXStack-40 + invoke MakeNode + JC rename_bad_access + LDS SI,[CurBuf] + POP AX + MOV [BX.dir_attr],AL + POP [BX.dir_time] + POP [BX.dir_size_l] + POP [BX.dir_size_h] + POP [BX.dir_first] + POP [BX.dir_date] + MOV [SI.BUFDIRTY],1 + LDS SI,[rename_source] + invoke GetPath + LDS SI,[CurBuf] + MOV BYTE PTR [BX],0E5h + MOV [SI.BUFDIRTY],1 + context DS + MOV AL,0FFh + invoke FlushBuf + transfer SYS_RET_OK + +$RENAME ENDP + +BREAK <$FIND_FIRST - find first matching xenix filename> +; +; Assembler usage: +; MOV AH, FindFirst +; LDS DX, name +; MOV CX, attr +; INT 21h +; ; DMA address has datablock +; +; Error Returns: +; AX = error_file_not_found +; = error_no_more_files +; +procedure $FIND_FIRST,near + ASSUME DS:NOTHING,ES:NOTHING + CALL Validate_path + JNC find_get + JZ find_get + error error_file_not_found +find_get: + MOV SI,DX + PUSH CX + INC BYTE PTR [NoSetDir] ; if we find a dir, don't change to it + MOV WORD PTR [Creating],0E500h + CALL GetPath + POP CX + MOV [Attrib],CL +find_check: + JNC find_check_attr +find_no_more: + error error_no_more_files +find_check_attr: + MOV DS,WORD PTR [CURBUF+2] + MOV CH,[BX.dir_attr] + invoke MatchAttributes + JZ found_it + PUSH [LastEnt] + MOV BX,[DirStart] + JMP find_it_next +found_it: + LES DI,[DMAADD] + MOV AL,[Attrib] + STOSB ; find_buf 0 = attribute in search + MOV AL,[ThisDrv] + STOSB ; find_buf 1 = drive + MOV CX,11 + PUSH BX + MOV SI,OFFSET DOSGROUP:NAME1; find_buf 2 = formatted name + PUSH DS + PUSH SS + POP DS + + IF KANJI + MOVSB + CMP BYTE PTR ES:[DI-1],5 + JNZ NOTKANJB + MOV BYTE PTR ES:[DI-1],0E5H +NOTKANJB: + DEC CX + ENDIF + + REP MOVSB + POP DS + MOV AX,[LastEnt] + STOSW ; find_buf 13 = LastEnt + MOV AX,WORD PTR [ThisDPB] + STOSW ; find_buf 15 = ThisDPB + MOV AX,WORD PTR [ThisDPB+2] + STOSW + MOV AX,[DirStart] + STOSW ; find_buf 19 = DirStart + MOV AL,[BX].dir_attr + STOSB ; find_buf 21 = attribute found + MOV AX,[BX].dir_time + STOSW ; find_buf 22 = time + MOV AX,[BX].dir_date + STOSW ; find_buf 24 = date + MOV AX,[BX].dir_size_l + STOSW ; find_buf 26 = low(size) + MOV AX,[BX].dir_size_h + STOSW ; find_buf 28 = high(size) + POP SI + MOV CX,8 ; find_buf 30 = packed name +find_loop_name: + LODSB + STOSB + CMP AL," " + LOOPNZ find_loop_name + JNZ find_check_dot + DEC DI +find_check_dot: + ADD SI,CX + CMP BYTE PTR [SI]," " + JZ find_done + MOV AL,"." + STOSB + MOV CX,3 +find_loop_ext: + LODSB + STOSB + CMP AL," " + LOOPNZ find_loop_ext + JNZ find_done + DEC DI +find_done: + XOR AL,AL + STOSB + transfer SYS_RET_OK +$FIND_FIRST ENDP + +BREAK <$FIND_NEXT - scan for match in directory> +; +; Assembler usage: +; ; dma points at area returned by find_first +; MOV AH, findnext +; INT 21h +; ; next entry is at dma +; +; Error Returns: +; AX = error_no_more_files +; +procedure $FIND_NEXT,near + ASSUME DS:NOTHING,ES:NOTHING + LDS SI,[DMAADD] + MOV DX,SI + INC DX + PUSH SI + invoke MOVNAMENOSET + POP SI + JNC find_load +findnext_no_more: + error error_no_more_files +find_load: + MOV AX,[SI.find_buf_LastEnt] + LES BP,[SI.find_buf_ThisDPB] + OR AX,AX + JS findnext_no_more + MOV BX,[SI.find_buf_DirStart] + MOV DL,[SI.find_buf_sattr] + MOV [Attrib],DL + PUSH AX + MOV WORD PTR [ThisDPB],BP + MOV WORD PTR [ThisDPB+2],ES +find_it_next: + invoke SetDirSrch + ASSUME DS:DOSGROUP + POP AX + MOV [ENTLAST],-1 + invoke GetEnt + invoke NextEnt + JMP find_check +$find_next ENDP + +do_ext + +CODE ENDS + END + \ No newline at end of file -- cgit v1.2.3