; File : $Workfile: CSTART.ASM$ ; ; Description : ; ; Original Author : DIGITAL RESEARCH ; ; Last Edited By : $CALDERA$ ; ;-----------------------------------------------------------------------; ; Copyright Work of Caldera, Inc. All Rights Reserved. ; ; THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL, ; PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC. ; ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES ; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF ; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO ; HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE ; AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE ; AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED, ; COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED, ; CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST, ; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF ; CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT ; AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND ; CIVIL LIABILITY. ;-----------------------------------------------------------------------; ; ; *** Current Edit History *** ; *** End of Current Edit History *** ; ; $Log$ ; CSTART.ASM 1.2 97/03/21 15:01:01 ; Added /n option to disable critical error handler ; CSTART.ASM 1.38 94/12/21 10:45:05 ; Reduced Heap size to 860h just to be sure. ; CSTART.ASM 1.34 94/03/29 16:10:05 ; _docmd_int2f returns 1 or 0 depending on whether or not the command ; is accepted. ; CSTART.ASM 1.31 93/11/18 18:50:18 ; Fix HMA registration problem ; CSTART.ASM 1.30 93/11/09 00:00:14 ; Shorten _ge_config_env ; CSTART.ASM 1.28 93/11/05 00:54:25 ; move HMA registration code to where it's actually executed ; CSTART.ASM 1.27 93/11/04 23:39:11 ; Fix problem with resident data relocation code ; CSTART.ASM 1.26 93/11/04 20:06:09 ; int2E_far_entry now does CLI and STI the correct way round when loading ; SS and SP. ; CSTART.ASM 1.25 93/10/24 13:13:34 ; Added strategy 'best fit upper mem'' & link upper mem' ; put_resident_high() ; CSTART.ASM 1.24 93/09/10 15:56:46 ; CLS checks for ANSI using Int 2F/1A00 ; CSTART.ASM 1.23 93/08/26 09:40:50 ; Now use PSP for stack during func 4b exec. There's some debug ; code in case things screw up. ; CSTART.ASM 1.21 93/08/03 10:04:18 ; Stopped using memory above A000 for transient portion because the ; stack was disappearing when MEMMAX -V executed. ; CSTART.ASM 1.20 93/07/05 08:35:00 ; Now switch to a stack located at the top of conventional memory before ; calling INT 21 ah=4B. ; CSTART.ASM 1.19 93/05/24 11:38:22 ; alloc_com_memory now allocates copy buffer by allocating largest possible ; block then shrinking it to the correct size. This prevents the buffer ; being located in upper memory after a HILOAD. ; CSTART.ASM 1.16 93/02/24 17:42:01 ; int10_cls() no longer checks position of INT 29 vector. ; CSTART.ASM 1.12 92/11/17 14:26:40 ; Change to set_reload_file to allow switches on COMSPEC line. ; CSTART.ASM 1.10 92/09/17 11:31:43 ; INT 2e DS=SI=0 now works when we are relocated. ; CSTART.ASM 1.9 92/09/11 10:45:36 ; our_cmd_int2f altered so we can support multi-line macros in DOSKEY. ; CSTART.ASM 1.8 92/08/06 09:55:22 ; Correctly support INT 2F AH=AEh for EDOS. See _cmd_line_int2f. ; Int 2E DS=SI=0 causes batch processing to halt. ; CSTART.ASM 1.7 92/07/20 17:13:44 ; Added the following maintanence source code changes: ; 29 Apr 92 Now sets Novell error mode to 00 on entry, and restores ; ; original mode on exit. ; 18 May 92 Added routine get_original_envsize, to use as default if ; ; /E option not used ; ENDLOG page 62,132 title COMMAND.COM Startup Routines and Resident Section ; ; 7 Nov 87 Force the initial PATH specification to the root of the ; boot drive. Also add a new variable TEMPDRV which is ; Concurrents Temporary Drive. ; ; 10 Dec 87 If DRNET has been loaded then add the DRNET=x.x string to ; the initial environment. ; 25 Feb 88 Run the 2nd Phase RSP's before spawning the remaining TMPs ; 1 Mar 88 Initialize the AUTOLOGON and NODE variables for diskless ; workstation support. Support the NODE environment variable ; 21 Mar 88 Allow for all registers to be corrupted on return from the ; DOS EXEC function call. ; 5 Apr 88 Correctly handle incorrect command line length passed by ; FrameWork Install Program ; 13 Apr 88 Move Memory allocation code to DOSIF and initialise the ; default console in PD for SunRiver. ; 10 May 88 Clean-Up the segment grouping and force the CGROUP to be ; linked after the data for everything but the TMP. Add the ; INT10_CLS function for DOSPLUS ; 12 May 88 Cater for DesqView passing an environment segment of 0000 ; 13 May 88 Alter FCB build to giver F & RED when /FRED typed in For FS.COM ; 19 May 88 Prevent CODE being moved to high memory if loaded as an .EXE ; or no initial environment is supplied ; 20 May 88 Move reload messages into the MESSAGE.C file. ; 24 May 88 Use the internal Critical Error handler and check for ABORT ; codes while in the Command Processor Code. ; 6 Jun 88 Move Command ReadLine down to resident code - so SK+ can ; overwrite the hi-mem portion. ; 16 Jun 88 VC_DATA now returns the Physical Console Number as well. ; 21 Jun 88 Add FARPTR routine which determines the correct segment to ; return to PRINTF based on the offset of a message. ; 22 Jun 88 remove environment & PSP setup & RSP spawning code ; for banked RSP support. Kludgey CDOS_EXEC code for banking ; 29 Jun 88 Modify MASTER_ENV so it can be called from C and change the ; exec code to use the internal FCB parsing. ; 30 Jun 88 Set the default PRINTER and AUX using the information in the ; INT17 and INT14 mapping arrays. ; 20 Jul 88 Increase HEAP Size to 0A00h for the TMP ; 9 Aug 88 Terminate the command line after a CR or LF for ; BitStream Fontware. ; 31 Aug 88 Make the READLINE function call from high memory so PolyTron ; PolyWindows can be invoked from the command line. ; 22 Sep 88 Always use INT_BREAK routine for Control Break Handling ; 03 Oct 88 Invalidate the Old Environment for Novell under CDOS. ; 09 Nov 88 Select the correct DR-DOS history Buffer. ; 15 Nov 88 Re-initilaise Interrupt handlers after an EXEC for Novell ; 21 Nov 88 Install Command Processor Backdoor but just terminate caller ; 13 Dec 88 Generate the FCBs for a DOS exec internally for compatibility ; with Novell Netware. ; 15 Dec 88 Force the default INT 22, 23, 24 and 2E handlers to be set ; relative to the PSP. Update PSP copies of interrupt 22, 23, 24 ; if this is the root DOS process. ; 25 Jan 89 If new DRDOS internal data layout get the PD a new way ; 27 Feb 89 fix MAKE_FCB, use SI from F_PARSE if possible ; 15 Apr 89 INT2E handler ; 17 Apr 89 int10_cls: don't return to 25 lines if in 43 or 50 line mode ; 31 May 89 DRDOS get PD using new f4458 backdoor ; 6 Jun 89 int2e: amend our copy of command, not users command ; 19 Jun 89 Remove "Alternative" methods of getting PD address ; 14 Jul 89 SideKick Plus checksum only done if STDERR is to CON ; (so when LAPLINK does CTTY COM1 it goes quicker) ; 6 Sep 89 Call INT21/5D09,5D08 in readline ; 30 Oct 89 Throw away startup code (put it in STACK segment) ; 13 Nov 89 Relocate DRDOS resident code over command line/fcbs in PSP ; 30 Jan 90 in_exec is now incremented and decremented to allow ; novell menu program to exit successfully. ; 30 Jan 90 Added batch_seg_ptr before first occurance of string ; 'A:\COMMAND.COM' (reload_file). Novell uses it during ; remote boot. ; 31 Jan 90 restore_term_addr puts back old Int22/23/24 ready for ; an EXIT command (DESQview bug) ; If no environment make reload_file in root of default drive ; 7 Feb 90 Turn off HILOAD on DRDOS ; Add d2cgroupptr support routine (see COM.C) ; 27 Mar 90 turn history buffers to command when in readline, so COPY CON ; etc will use application buffers ; 30 Mar 90 Stop cleanly when we can't reload transient portion rather ; than overwriting other peoples memory and crashing ; 4 Apr 90 dbcs_init moved from DOSIF, use system table, ; throw away init code ; 12 Apr 90 changed for no inherited environment ; 18 Apr 90 add JW's changes to int10_cls to support JW's new VGA card ; 9 May 90 Int2E doesn't trash Int24, returns with CY clear ; 17 May 90 CLI/STI round stack swap in INT2E exit routine. ; 5 Jun 90 Int21/4458 checks CY before fetching PD, so if running on DOS ; we carry on into the C code which gives version error ; 12 Jun 90 master_env now leaves MS_M_STARTEGY alone, because Novell ; gets confused if it ends up high ; 3/Jul/90 DLS data into R_TEXT for Watcom C v7.0 (again - I originally ; did this on 20/Apr/90, but someone screwed up with the ; archive version managment). ; 2-Aug-90 RG-00- define LOGIN procedures for Stellar security ; 1-Sep-90 _msgfarptr added for DLS ; 13-Sep-90 COMSPEC=A:\COMMAND.COM even when CDOS.COM ; 21-Sep-90 Increase TMP heap size so we can save initial state for ; subsequent login's. ; 26-Sep-90 We now switch to our own psp during an INT 2Eh. ; This fixes bug experienced with NOVELL MENU.EXE on top of ; DR DOS 5.0. ; 11-Oct-90 CDOSTMP exec stashes unlinked MD in PSP for TSR auto-load ; 03-Dec-90 Stop corruption of DMD chain when allocating high memory for ; transient portion of command processor ; 11 dec 90 CDOS.COM switches to TMP history buffer ; 13 dec 90 save ES around TMP P_EXEC (DRNET sometimes trashes it) ; 17 dec 90 exec of CMD call io_pckbd with cl=40h (must be 24 line) ; 26/Feb/91 Increased _rld_msgs maximum text size from 100 to 120. ; 11/Mar/91 Added show_help function. COMMAND.COM is now really ; an EXE file with the help messages tagged onto the end. ; 25/Apr/91 Most of the resident code/data is now relocatable to high ; memory. Some Novell critical data is left in low memory. ; 15/May/91 Added dummy code to force at least one relocation item in ; .exe header so loader doesn't think file is EXEPACKed. ; 12/Jun/91 Added dummy code to allow Software Carousel to run. ; See int22_entry. ; 19 jun 91 disable control break until handler initialised ; 24/Jun/91 Changed memory allocation procedure on return from func 4B to ; allow Novell Remote Boot to work. ; 2 jul 91 our_cmd_int2f only has single parameter ; 26 jul 91 Novell dummy pipe filenames zero terminated ; 29 jul 91 A 1K far buffer is now allocated by alloc_com_memory. It is ; used by type, batch_read, and printf. ; 5 Aug 91 Call Get Extended Error (int 21 ah=59) after exec. ; 14 Aug 91 Put pointers to _batch, _batchflg, and _echoflg in low memory ; stub. This is primarily for Software Carousel. ; 4 Dec 91 Fixed problem with full environment trashing next DMD. ; 29 Apr 92 Rearranged DGROUP so that constant code and data appear after ; the stack - So that Multitasker need not save it. ; 10 Jun 92 show_help function now copes with doubled % characters. ; ;------------------------------------------------------------------------------ .xlist include msdos.equ include mserror.equ include char.def .list ; This is the offset in segment FFFF to which we will relocate. ; It is set to E0 to allow for a PCNFS bug HISEG_OFF equ 0E0h FALSE equ 0h TRUE equ not FALSE ThreeCOM equ TRUE Copy_Buffer_Size equ 0C80h ; 50k of buffer space in paras ; matches MAX_COPYBUF in COMCPY.C RLSTACK_SIZE equ 256+256 ; Reserve for ReadLine stack ; We need 260 bytes for possible ; buffer, plus a little stack ; If the stack overflows it isn't ; a disaster - we will just re-load ; COMMAND.COM ifdef CDOSTMP C_HEAP_SIZE equ 1200h ; C routine Heap Size - TMP has else ; extra to save ENV in ;C_HEAP_SIZE equ 0800h ; C routine Heap Size ;endif ; (observed sizes 500h-600h - IJ) C_HEAP_SIZE equ 0860h ; C routine Heap Size ; For safety increased that value as UNC filenames require 128 byte buffers ; allocated dynamically on tha stack. With respect to the observed sizes ; above it might be dangerous to leave that value at 0800h. I would have ; increased the value to 0A00 but then it does no longer fit into HMA. (JBM) endif ifdef DOSPLUS include f52data.def else .xlist include system.def include pd.def .list XIOS_HISTBUF equ 44 ; Get the History Buffer Address XIOS_PCKBD equ 32 ; Set keyboard/screen modes NETMOD_BIT equ 040h ; Network Module in MODULE_MAP ; Concurrent DOS System Data Page Format XIOS_ENTRY equ ds:dword ptr 0028h ; XIOS function Entry CCPMSEG equ es:word ptr 0040h ; OS Starting Paragraph MODULE_MAP equ es:byte ptr 0046h ; Concurrent Module Map NVCNS equ es:byte ptr 0047h ; Number of Virtual Consoles MFL equ es:word ptr 005Ah ; Memory Free List RLR equ es:word ptr 0068h ; Ready List Root VERSION equ es:word ptr 0078h ; OS Version String Offset MWDR equ es:word ptr 0098h ; Memory Window Decriptor BOOTDRV equ es:byte ptr 009Dh ; System Boot Drive ENVSIZE equ es:word ptr 00B8h ; Environment Size Bytes DRNET_NODE equ es:byte ptr 00BAh ; DR-NET Node Number DRNET_FLAG equ es:byte ptr 00BB ; DR-NET Flags V386_PTR equ es:word ptr 00C8h ; 386 Data pointer CCBLIST equ es:word ptr 0C3Eh ; XIOS CCB$LIST INT17_PTR equ es:word ptr 0C46h ; XIOS INT 17 Mapping Array INT14_PTR equ es:word ptr 0C48h ; XIOS INT 14 Mapping Array CCB_OWNER equ es:word ptr 0 ; CCB Owning Process CCB_PCNS equ es:byte ptr 10 ; CCB Physical Console Number CCB_VCNS equ es:byte ptr 11 ; CCB Virtual Console Number MF_CODE equ 0004h ; MD flag bit endif ; ; Standard definitions for PSP variable ; PSP_TERM_IP equ es:word ptr 000Ah PSP_PARENT equ es:word ptr 0016h PSP_ENVIRON equ es:word ptr 002ch ifndef DOSPLUS ; The following Macro takes one parameter which is the Concurrent DOS ; function number. bdos MACRO func ifnb mov cl,func endif int 224 ENDM .xlist include ccpm.equ .list endif swap MACRO reg1, reg2 push reg1 push reg2 pop reg1 pop reg2 ENDM page ifdef CDOSTMP ; ; For the Concurrent DOS TMP the CGROUP segments are defined ; first so that the CGROUP appears first in the .EXE file and ; can be converted to a .COM by "RELOC" or "EXE2BIN". This .COM ; file is then converted to a .CMD by the COM2CMD utility which ; uses the data embeded at the start of the code to generate the ; right groups. ; HGROUP GROUP HEADER HEADER SEGMENT para public 'HEADER' HEADER ENDS DGROUP GROUP RSP_SEG,PD_SEG,UDA_SEG,NULL,EXEC_CODE,_DATA,DATA,CONST,FIXED,_BSS,HEAP,c_common,STACK,DYNAMIC RSP_SEG SEGMENT para public 'CDOS_DATA' RSP_SEG ENDS PD_SEG SEGMENT para public 'CDOS_DATA' PD_SEG ENDS UDA_SEG SEGMENT para public 'CDOS_DATA' UDA_SEG ENDS NULL SEGMENT para public 'BEGDATA' NULL ENDS EXEC_CODE SEGMENT byte public 'DATA' EXEC_CODE ENDS _DATA SEGMENT byte public 'DATA' _DATA ENDS DATA SEGMENT byte public 'DATA' DATA ENDS CONST SEGMENT byte public 'CONST' CONST ENDS FIXED SEGMENT para public 'FDATA' FIXED ENDS _BSS SEGMENT word public 'BSS' _BSS ENDS HEAP SEGMENT word public 'BSS' HEAP ENDS c_common SEGMENT byte public 'BSS' c_common ENDS STACK SEGMENT para public 'BSS' STACK ENDS DYNAMIC SEGMENT para public 'DDATA' DYNAMIC ENDS endif ifndef CDOSTMP ; The following declarations declare the presence and order of ; various data segments within the DATA Group of the command ; processor. ; DGROUP GROUP R_TEXT, ED_TEXT, NULL, _DATA, DATA, CONST, FIXED, _BSS, HEAP, c_common, STACK, DYNAMIC R_TEXT SEGMENT para public 'CDOS_DATA' R_TEXT ENDS NULL SEGMENT byte public 'BEGDATA' NULL ENDS _DATA SEGMENT byte public 'DATA' _DATA ENDS DATA SEGMENT byte public 'DATA' DATA ENDS CONST SEGMENT byte public 'CONST' CONST ENDS FIXED SEGMENT byte public 'FDATA' FIXED ENDS _BSS SEGMENT byte public 'BSS' _BSS ENDS HEAP SEGMENT byte public 'BSS' HEAP ENDS c_common SEGMENT byte public 'BSS' c_common ENDS STACK SEGMENT word public 'STACK' STACK ENDS ED_TEXT SEGMENT para public 'CDATA' Public ed_text_start ed_text_start label byte ED_TEXT ENDS DYNAMIC SEGMENT para public 'DDATA' DYNAMIC ENDS endif CGROUP GROUP _TEXT, _MSG, _TEXTEND _TEXT SEGMENT para public 'CODE' ifdef DLS extrn _my_dls_init:far endif _TEXT ENDS _MSG SEGMENT byte public 'CODE' _MSG ENDS _TEXTEND SEGMENT para public 'CODE' _TEXTEND ENDS CEND GROUP ETEXT, ETEXTEND ETEXT SEGMENT para public 'XEND' ETEXT ENDS ETEXTEND SEGMENT para public 'XEND' ETEXTEND ENDS codeOFFSET equ offset CGROUP: dataOFFSET equ offset DGROUP: endOFFSET equ offset CEND: code_length equ codeOFFSET rlstack ; Total Code Length real_code equ code_length - RLSTACK_SIZE static_length equ dataOFFSET FIXED ; Static Data Length dynamic_length equ dataOFFSET ed_text_start total_length equ dataOFFSET DYNAMIC ; Total Data Length cgroup_length equ codeOFFSET _TEXTEND cend_length equ endOFFSET ETEXTEND ; help_length is an APPROXIMATE value, but it must be LARGER than the correct ; length of the help segment. ifdef DLS help_length equ 0A000h else help_length equ 05000h endif page public __acrtused ; trick to force in startup __acrtused = 9876h ; funny value not easily matched in SYMDEB ifdef CDOSTMP HEADER SEGMENT public 'HEADER' dw 0EDCh ; Header Signature dw offset HGROUP:CGROUP ; Code Group dw real_code ; Real Code Size dw offset HGROUP:DGROUP dw total_length dw static_length HEADER ENDS ; These Segments are forced into the correct order for a CDOS ; Resident System Process. First the RSP header which contains ; the regeneration information required by GENSYS. ; RSP_SEG SEGMENT sysdatseg dw 0 ; system data segment sdatvar dw 0047h ; # of system consoles defconsole db 0,0 ; console # | copy # dw 0,0 dw RSF_DYNAMIC+RSF_SPECIAL+RSF_ENVIRON dw 0 dw 0 RSP_SEG ENDS _DATA SEGMENT byte public 'DATA' extrn _gp_far_buff:word _DATA ENDS else R_TEXT SEGMENT assume cs:DGROUP, ds:nothing, es:nothing, ss:nothing ifdef WATCOMC Public _small_code_ ; Watcom C requires this label to _small_code_ label near ; be declared in the start-up module else ifdef MWC Public _mwINIT ; MetaWare requires this label _mwINIT label near ; to be declared in the start-up ; module else Public __cstart __cstart label near ; to be declared in the start-up ; module endif endif extrn _gp_far_buff:word ifdef DOSPLUS extrn com_criterr:near endif cstart: ; start address of all "C" programs call near ptr getIP ; Push the IP register and Skip the retIP: ; version control messages. dw 0EDCh ; Digital Research Marker dw code_length ; Length of the Code Group ;dw static_length ; Length of the Fixed Data Group ifdef DLS dw total_length else dw dynamic_length ; length of dynamic data endif dw total_length ; Minimum Length of the Runtime ; Data Group reloc_off dw 0 ; offset of relocated resident code/data reloc_seg dw 0 ; segment of relocated resident code/data reloc_size dw 0 ; size of relocated resident code/data public _batchptr_off public _batchflg_off public _echoflg_off _batchptr_off dw 0 ; offset of _batch variable _batchflg_off dw 0 ; offset of _batchflg variable _echoflg_off dw 0 ; offset of _echoflg variable ; These are the entry points for INT 23 and INT 24. They will have JMPF ; instructions poked into them. control_break_entry db 5 dup(90h) crit_error_entry db 5 dup(90h) ; The call to MS_X_EXEC must be made from the same segment as the PSP. ; msdos_exec does a far jump to here and then we far jump back. psp_dofunc4b: mov ax,(MS_X_EXEC*256) int DOS_INT jmp i22_entry ; Software Carousel Version 5.0 looks for the following three instructions ; and assumes it is an entry point to int 22 code. mov bx,0ffffh mov ah,48h int 21h clc i22_entry: int22_entry db 0eah dw dataOFFSET func4b_return func4b_seg dw 0 int2E_entry db 0eah dw dataOFFSET int2E_far_entry int2E_seg dw 0 ; IMPORTANT - batch_seg_ptr MUST be just before reload_file. - EJH ; ; ***** Do Not change the order of the following variables ***** ; batch_seg_ptr dw 0ffffh ; file filename. For novell remote ; boot support. ifdef DOSPLUS reload_file db 'A:\COMMAND.COM',0 else ifdef NETWARE reload_file db 'A:\NETWARE.COM',0 else reload_file db 'A:\CDOS.COM',0 endif endif db (80-15) dup ('n') ; Expanded LoadPath ;cmdline db 128 dup ('c') ; Local Copy of Initial Command Line ; dummy pipe filenames for NOVELL out_pipe db '_:/' , 0 , '_______.___',0 in_pipe db '_:/' , 0 , '_______.___',0 ; ; ***** Do Not change the order of the preceeding variables ***** ; ; This next bit forces hi_seg_start to be on a paragraph boundary org HISEG_OFF hi_seg_start label byte ; Himem Registration chain entry himem_link_next dw 0 himem_link_size dw 0 db 5 public __psp __psp dw 0 Public _batch_seg_ptr _batch_seg_ptr dw dataOFFSET batch_seg_ptr dw 0 ; segment will be set to low_seg Public _cbreak_ok _cbreak_ok db 0 ; set when ctrl-break handler initialised ; The following causes there to be at least one relocation item ; in the .exe header so the loader does not think the file is ; EXEPACKed. mov ax,seg _batch_seg_ptr R_TEXT ENDS endif _TEXT SEGMENT ifdef CDOSTMP assume cs:CGROUP, ds:DGROUP, ss:DGROUP ; ; This entry point is used when the startup is executed as an ; RSP. CS is CGROUP and DS is DGROUP. From here on we initialise ; internal data structures etc. ; ifdef WATCOMC Public _small_code_ ; Watcom C requires this label to _small_code_ label near ; be declared in the start-up module else ifdef MWC Public _mwINIT ; MetaWare requires this label _mwINIT label near ; to be declared in the start-up ; module else Public __cstart __cstart label near ; to be declared in the start-up ; module endif endif cstart: call RSP_start ; Push the IP register and Skip the retIP: ; version control messages. dw 0EDCh ; Digital Research Marker dw code_length ; Length of the Code Group dw static_length ; Length of the Fixed Data Group dw total_length ; Minimum Length of the Runtime ; Data Group else extrn _int2e_handler:far endif extrn __main:far ; C main program _TEXT ENDS _DATA SEGMENT ; ; Data held in this segment remains in the resident portion of ; the program image is is not overlayed by transient programs ; loaded by COMMAND.COM. The variables here are private to the ; startup module. ; psp_save_area dw 6 dup (?) ifndef DOSPLUS extrn _pd:dword ; Concurrent Process Descriptor extrn _sysdat_seg:word ; Concurrent System Data Page endif extrn _n_option:word ; ; The following is the offset and segment of the C routine MAIN ; which is moved up in memory in order to accomodate the ; Environment variables and Resident Data area. ; C_code_entry label dword Public code_seg,low_seg code_off dw codeOFFSET __main ; Offset of MAIN code_seg dw ? ; Segment of MAIN data_seg dw ? ; DGROUP segment alloc_seg dw ? ; the start of hi mem allocated low_seg dw ? ; segment of low memory stub. ifdef CDOSTMP Public __psp __psp dw 0 cmd_histbuf dw 0 ; Command Processor History Buffer prog_histbuf dw 0 ; Program History Buffer mpb label word mpb_start dw ? mpb_min dw ? mpb_max dw ? mpb_pdadr dw ? mpb_flags dw ? sysdat dw ? ; Concurrent System Data Page uda dw ? ; Concurrent User Data Area ; ; The following buffer is used by the P_EXEC function. Used ; by CDOS_EXEC to load DOS and CP/M programs. ; exec_block label byte exec_pathoff dw ? ; Offset of ASCIIZ Load file exec_pathseg dw ? ; Segment of ASCIIZ Load File exec_filetype db ? ; File Type Index exec_loadtype db ? ; EXEC or CHAIN to application exec_clineoff dw ? ; ASCIIZ Command line Offset exec_clineseg dw ? ; ASCIIZ Command Line Segment exec label dword ; FAR pointer to EXEC routine dw dataOFFSET cdos_exec exec_seg dw ? cmdline db 0 ; Blank Command Line EXEC_CODE SEGMENT Assume CS:DGROUP, DS:DGROUP, SS:DGROUP err_tbl db 0 ; 00 Success db -101 ; 01 System Call Not Implemented db -102 ; 02 Illegal System Call db ED_MEMORY ; 03 Cannot Find Memory db -104 ; 04 Illegal Flag Number db -105 ; 05 Flag Overrun db -106 ; 06 Flag Underrun db -107 ; 07 No Unused Queue Descriptors db -108 ; 08 No free Queue Buffer db -109 ; 09 Cannot find Queue db -110 ; 10 Queue in Use db -111 ; 11 db -112 ; 12 No Free Process Descriptors db -113 ; 13 No Queue Access db -114 ; 14 Empty Queue db -115 ; 15 Full Queue db -116 ; 16 CLI Queue missing db -117 ; 17 No 8087 in system db ED_DMD ; 18 No Unused Memory Descriptors db -119 ; 19 Illegal Console Number db -120 ; 20 No Process Descriptor Match db -121 ; 21 No Console Match db -122 ; 22 No CLI Process ?? db -123 ; 23 Illegal Disk Number db -124 ; 24 Illegal FileName db -125 ; 25 Illegal FileType db -126 ; 26 Character Not Ready db ED_BLOCK ; 27 Illegal Memory Descriptor db -128 ; 28 Bad Return from BDOS load db ED_FAIL ; 29 Bad Return from BDOS read db ED_ACCESS ; 30 Bad Return from BDOS Open db -131 ; 31 Null Command db ED_ENVIRON ; 32 Not owner of resource db -133 ; 33 No Cseg in Load File db -134 ; 34 PD exists on Thread Root db -135 ; 35 Could Not Terminate Process db -136 ; 36 Cannot ATTACH to Process db -137 ; 37 Illegal List Device Number db ED_PASSWORD ; 38 Illegal Password db -139 ; 39 db -140 ; 40 External Termination db -141 ; 41 Fixup Error on Load db -142 ; 42 Flag Set Ignored db -143 ; 43 Illegal Aux Device Number cdos2dos PROC NEAR cmp ax,0000 ; Check for Success jz c2d10 ; and skip lookup lea bx,err_tbl ; xlat the error code in AL xlat err_tbl ; into a Negated DOS compatible mov ah,0FFH ; error code c2d10: ret cdos2dos ENDP ; WORD FAR CDECL cdos_exec(BYTE *path, UWORD type, BYTE *line, BOOLEAN back); ; ; On Entry: ; back 10[bp] ; line 08[bp] ; type 06[bp] ; path 04[bp] ; ; ES = SYSDAT ; ; On Exit: ; AX = exit error code ; cdos_exec PROC FAR lea si,P_MEM[bx] ; SI -> root of MD's ce_10: ; find our code segment mov si,es:[si] ; get next memory descriptor test si,si ; end of list? jz ce_40 ; yes, we don't own any separate code test es:word ptr 6[si],MF_CODE jz ce_10 ; loop back if not code segment mov si,es:8[si] ; get MPAD for code segment lea di,P_MPAR[bx] ; get MPAD root ce_20: cmp si,es:[di] ; is this the predecessor? je ce_30 mov di,es:[di] ; else check next MPAD jmp short ce_20 ce_30: ; SI -> our MPAD, DI -> previous MPAD xor ax,ax xchg ax,es:[si] ; get next MPAD mov es:[di],ax ; unlink our MPAD from list ce_40: push di push si ; SI = 0 if no separate code alloc push ds mov ds,es:P_PSP[bx] ; point to our PSP mov ds:word ptr [5eh],si ; stash TMP mpad away in here pop ds mov dx,198 ; Raise the priority of the TMP bdos P_PRIORITY ; while we wait for the child mov dx,dataOFFSET exec_block push es ; do a P_EXEC but save /restore bdos P_EXEC ; ES around it since DRNET trashes pop es ; it sometimes. mov ax,cx ; Get the Concurrent Error Code call cdos2dos ; and convert to a standard DOS push ax ; Error Code for COMMAND.RSP test ax,ax ; If any errors occured during the jnz ce_60 ; exec or the child process is not cmp word ptr 10[bp],0 ; inheriting the console the do not jnz ce_60 ; execute a Console Attach function bdos C_ATTACH ; process to terminate ce_60: mov dx,200 ; Return to the normal priority bdos P_PRIORITY ; now that we have the console back pop ax pop si pop di test si,si ; separate code allocation? jz ce_50 ; no, don't have to un-kludge! push ax mov ax,es:[di] ; get link of previous MPAD mov es:[si],ax ; link it to our MPAD mov es:[di],si ; link our MPAD to previous MPAD mov dx,1 ; make sure code gets banked in bdos P_DELAY ; this gets us off/on RLR pop ax ce_50: ret cdos_exec ENDP EXEC_CODE ENDS _TEXT SEGMENT assume cs:CGROUP, ds:DGROUP, es:nothing, ss:nothing public _exec ; EXEC routine ; ; WORD CDECL exec(BYTE *path, UWORD type, BYTE *line, BOOLEAN back); ; ; On Entry: ; back 10[bp] ; This value is ignored for MSDOS_EXEC ; line 08[bp] ; type 06[bp] ; path 04[bp] ; ; On Exit: ; AX = exit error code ; _exec: ;----- push bp mov bp,sp push si push di mov ax,04[bp] ; Get the Full Command Name mov exec_pathoff,ax mov exec_pathseg,ds mov ax,08[bp] ; and the Command Line mov exec_clineoff,ax mov exec_clineseg,ds mov ax,06[bp] ; Get the Command Type (.CMD etc) mov exec_filetype,al ; Save the Command type mov exec_loadtype, 0 ; The default is to load the command cmp word ptr 10[bp],0 ; and for the child to inherit the jz exec_10 ; console mov exec_loadtype,2 exec_10: test al,al ; is command type a CMD jnz exec20 ; no, skip XIOS call mov ax,XIOS_PCKBD ; CMD's expect 24 lines mov cl,40h ; so tell XIOS thats what we must have mov dl,defconsole ; on this console call xios exec20: les bx,_pd ; Get the process Descriptor Address mov cx,prog_histbuf ; and force the system to use the mov es:P_SB_SEG[bx],cx ; Program Level History Buffer call exec ; do FAR call to cdos_exec les bx,_pd ; Get the process Descriptor Address mov cx,cmd_histbuf ; and force the system to use the mov es:P_SB_SEG[bx],cx ; Command Level History Buffer cmp exec_filetype,0 ; was it a CMD jne exec30 push ax mov ax,XIOS_PCKBD ; we are in must-be-24 line mov cl,0 ; mode, get back to default mode mov dl,defconsole ; for console number call xios pop ax exec30: pop di pop si pop bp ret _TEXT ENDS Assume CS:DGROUP, DS:DGROUP, SS:DGROUP else ; ; Novell 2.1 intercepts the DOSPLUS 4B00 return by updating the ; PSP USER_SS/SP and when it returns ALL registers except CS:IP ; have been corrupted. ; extrn stack_min:word ; Minimum Stack Address extrn heap_top:word readline label dword ; FAR pointer to READLINE routine dw dataOFFSET msdos_readline readline_seg dw ? critical dd ? ; Critical Error Handler Address exec_sp dw ? exec_ss dw ? exec_block label byte exec_env dw ? ; Environment Segment exec_clineoff dw ? ; ASCIIZ Command line Offset exec_clineseg dw ? ; ASCIIZ Command Line Segment exec_fcb1off dw dataOFFSET fcb1 ; FCB1 Contents Offset exec_fcb1seg dw ? ; FCB1 Contents Segment exec_fcb2off dw dataOFFSET fcb2 ; FCB2 Contents Offset exec_fcb2seg dw ? ; FCB2 Contents Segment exec label dword ; FAR pointer to EXEC routine dw dataOFFSET msdos_exec exec_seg dw ? msdos_exec_ret label dword ; FAR pointer to exit EXEC routine dw codeOFFSET _exec_ret msdos_exec_ret_seg dw ? fcb1 db 16 dup (?) ; FCB1 Buffer fcb2 db 16 dup (?) ; FCB2 Buffer crc dw ? ; COMMAND.COM crc ; For Dual Language Support... ifdef DLS ;;ED_TEXT SEGMENT para public 'CDATA' Public _rld_msgs,_rld_text _rld_msgs dw 120 ;RELOAD_LEN ; size of this message buffer _reload_msgs dw 0 ; First part of Code reload prompt _reload_msgf dw 0 ; Second part of Code reload prompt _reload_msgm dw 0 ; Unlikely part of Code reload prompt dw 0 ; end of list _rld_text db 120 dup (?) ; message text is placed here RELOAD_LEN equ $-_reload_msgs ;;ED_TEXT ENDS else extrn _reload_msgs:byte ; First part of Code reload prompt extrn _reload_msgf:byte ; Second part of Code reload prompt extrn _reload_msgm:byte ; No memory available message endif reload_flag db 0 ; Reloading Command Processor in_exec db 0 high_code dw TRUE ; Enable High Code exe_file dw FALSE ; True if COMMAND.COM is really an EXE return_code dw ? ; Exec return code net_error_mode db ? ; int2E data i2e_lock dw 0 ; mutex flag i2e_user_ss dw 0 ; users ss, sp i2e_user_sp dw 0 i2e_stack dw 0 i2e_cmd dw 0 ; offset of local copy of command line i2e_c_entry label dword i2e_c_offs dw codeOFFSET _int2e_handler i2e_c_seg dw ? i2e_i23vec label dword i2e_i23off dw ? i2e_i23seg dw ? i2e_i24vec label dword i2e_i24off dw ? i2e_i24seg dw ? endif _DATA ENDS _BSS SEGMENT Public _edata _edata label BYTE ; end of data (start of bss) _BSS ENDS ETEXT SEGMENT ; ; The RLSTACK segment holds the stack used by the READLINE routine. ; This must be in High memory for the PolyTron PolyWindows product. ; ; "ETEXT" also forces the linker to pad the CGROUP to at least ; real_code bytes. Otherwise the file length can be upto 15 bytes ; shorter then real_code + total_length. This obviously causes ; problems with CALC_CRC and the file reloading. ; db RLSTACK_SIZE dup(0CCh) rlstack label word ETEXT ENDS ifdef CDOSTMP STACK SEGMENT Public _end _end label BYTE ; end of bss (start of starup/stack) db (C_HEAP_SIZE - 6) dup (0DDh) ; C Heap Area stack_top label word stack_ip dw ? ; Initial Offset stack_cs dw ? ; Initial Code Segment (Unknown) stack_flags dw ? ; Initial Flags (Unknown) temp_buffer db 512 dup(0) ; temp far buffer for *gp_far_buff STACK ENDS else STACK SEGMENT stack_start: ; HEAP_TOP is initialised to _end so the _RELOAD_FILE and CMDLINE ; variables are allocated on the HEAP. ; Public _end _end label BYTE ; end of bss (start of startup/stack) db 255,'3771146-XXXX-654321' page ; ; The CS register is now adjusted so that this startup ; can be used in the form of an .EXE or .COM. Whatever the ; execution format of this file DS still points at the PSP ; but must be careful when resizing the RAM. ; getIP PROC FAR cld ; be sure of DIR flag... pop di ; Get the Program Counter sub di,dataOFFSET retIP ; Correct for retIP mov cl,4 ; Convert Offset values to Segments mov ax,cs ; Get the current CS shr di,cl ; Convert Initial IP to Segment add ax,di ; add to CS and save for CALLF to MAIN push ax ; Save the New CS and the offset to mov ax,dataOFFSET gotCS ; the next instruction and then execute push ax ret ; a RETF instruction to correct CS. getIP ENDP ifdef DOSPLUS ; Most of the PSP (FCB's and command buffer) is unused - we reclaim this space ; by relocating the resident part of COMMAND.COM reloc_code: ;---------- ; On Entry: ; CS = DGROUP, DS = PSP, ES = nothing ; On Exit: ; CS = relocated DGROUP ; ; We build a "REP MOVSB ! RETF 6" on the stack. We also fiddle the near return ; address into a FAR. We then setup our registers appropriately and execute ; this code. ; if 1 ret else cmp ds:byte ptr 0080h,7fh ; discard garbage cmdline lengths jb reloc_code10 ret reloc_code10: pop bx ; recover return offset mov ax,0 push ax ; 0 (part of RETF 6) mov ax,006cah push ax ; RETF 6 on stack mov ax,0a4f3h push ax ; REP MOVSB on stack mov al,ds:0080h ; Get the Command length xor ah,ah mov cl,4 ; convert AX to cmdline length shr ax,cl ; in rounded down para's add ax,9 ; keep at least this much mov si,ds add ax,si ; add in PSP mov es,ax mov cx,sp ; save address of code on stack push ax push bx ; new RETF address on stack push ss push cx ; address of CODE on stack push cs pop ds ; DS -> DGROUP xor si,si ; setup DS:SI, ES:DI and CX xor di,di ; ready to do REP MOVSB mov cx,total_length ; DGROUP length add cx,real_code ;+CGROUP length db 0cbh ; RETF to code on stack endif endif gotCS: ;out 0fdh,al ; for debug purposes mov [__psp],ds ; Save our PSP in the local variable ifdef DOSPLUS mov ax,cs ; put our stack somewhere safe mov ss,ax mov sp,dataOFFSET rlstack push ds push di call reloc_code ; relocate code over unused bit of PSP pop di pop ds endif ifdef DLS call _my_dls_init endif mov [code_seg],cs ; Initialise the DATA segment address mov [data_seg],cs ; and calculate the current address mov [exec_seg],cs ; of the Code Segment mov [readline_seg],cs ; use it to fixup some JMPFs mov [_batch_seg_ptr+2],cs mov [low_seg],cs ; we may be relocated to upper or high ; memory so remember the current segment mov bx,cs ; Path up the JMPF instructions sub bx,[__psp] ; around the MSDOS EXEC code to mov cl,4 shl bx,cl add cs:[exec_psp-2],bx mov cs:[func4b_seg],cs mov cs:[int2E_seg],cs mov cs:[exec_psp],ds cmp di,0000h ; Disable Code Relocation if we have jnz gotCS_10 ; been loaded as an .EXE file ;;mov high_code,FALSE mov exe_file,TRUE ; Remember we are an EXE gotCS_10: mov di,total_length shr di,cl add [code_seg],di push ds ; Initialise the Checksum so we can mov ds,[code_seg] ; check the integrity of the high mov si,2 ; high copy of the command processor call calc_crc ; code pop ds mov [crc],ax mov ah,0ddh ; set Novell error mode mov dl,0 ; to 00 - BAP int 21h mov net_error_mode,al ; save original error mode call get_ds ; Get DGROUP value in AX cli ; turn off interrupts mov ss,ax ; SS = DGROUP mov sp,dynamic_length ; Initialise SP ;mov sp,total_length sti ; turn interrupts back on assume ss:DGROUP push ax call handler_init ; Initialise the Control Break and pop ax ; Critical Error Interrupt Vectors mov si,total_length ; Get the DGROUP length in bytes add si,code_length ; Add in the Code Length mov cl,4 ; and convert to a paragraphs shr si,cl mov bx,ax ; Get the Current DS sub bx,__psp ; and calculate DS - PSP Seg add bx,si ; DS + Data length in Para's mov es,__psp mov ah,MS_M_SETBLOCK int DOS_INT call exec_name ; Get our Load Path from the environment cmp high_code,FALSE ; Skip Memory check if HIGH_CODE jz cstart_10 ; support has been disabled mov high_code,FALSE ; Assume Failure. call alloc_com_memory ; Allocate high memory for command.com jnc carry_on mov ax,cs ; if no memory and CS is nearing cmp ax,8000h ; transient part abort and go home. jb cstart_10 mov ah,4ch int 21h carry_on: mov high_code,TRUE ; Set HIGH_CODE flag TRUE push es ; Relocate the command processor code push ds ; into high memory mov es,ax ; es-> destination segment mov di,0 mov si,0 mov ds,code_seg ; ds-> code to be moved mov cx,real_code ; convert bytes to words shr cx,1 rep movsw ; Move code up to high memory mov code_seg,es ; update code_seg pop ds pop es ; Shrink memory containing low memory version of step aside code mov si,total_length ; Get the DGROUP length in bytes mov cl,4 ; and convert to a paragraphs shr si,cl call get_ds mov bx,ax ; Get the Current DS sub bx,__psp ; and calculate DS - PSP Seg add bx,si ; DS + Data length in Para's mov ah,MS_M_SETBLOCK int DOS_INT cstart_10: ifdef DOSPLUS mov ax,4457h ; terminate HILOAD operation mov dx,200h int DOS_INT call dbcs_init ; initialise the DBCS support endif call get_cs push ax mov ax,codeOFFSET memory_init push ax db 0CBh ; a RETF instruction to correct CS. page ; ; Build the full path and filename of this process using the ; loadpath attached to the environment. If no filename exists ; then prevent the Command Processor code from being located ; in high memory. ; exec_name: push es mov es,__psp ; Get the PSP Segment Address mov dx,PSP_ENVIRON ; Get the environment segment cmp dx,0000 ; Have we got an environment ? jz exec_n11 ; No prevent High Code Support mov es,dx ; Scan through the environment and mov di,0 ; determine the Environment size and mov al,0 ; the Load file name mov cx,7FFFh exec_n05: ; Scan through the Environment repne scasb ; searching for the 00 00 terminator jcxz exec_n10 ; Terminate On CX == 0000 cmp es:byte ptr [di],al ; If the next byte is zero then this is jnz exec_n05 ; then end of the environment cmp es:word ptr 1[di],1 ; Are we pointing at the Control Word jnz exec_n10 ; No then no Load Path exists push ds mov ds,dx ; DS -> Environment Segment ;call get_ds ;mov es,ax ; ES -> Command Processor Data Seg mov es,[low_seg] lea si,03[di] ; Fully expand the filename so the mov di,dataOFFSET reload_file ; user can SUBST drives mov ah,60h int DOS_INT exec_n15: if 0 mov di,dataOFFSET reload_file ; this ASCIIZ string is on the heap xor ax,ax ; so now we need to find out how mov cx,-1 ; much space it takes up and reserve repne scasb ; that amount of the heap mov heap_top,di ; byte after the NUL is available endif pop ds pop es ret exec_n10: mov high_code,FALSE exec_n11: mov ah,MS_DRV_GET int DOS_INT ; get default drive mov es,[low_seg] add es:reload_file,al ; and use that for the comspec push ds ;call get_ds ;mov es,ax ; ES -> Command Processor Data Seg jmp exec_n15 ifdef DOSPLUS DI_BUF_PTR equ dword ptr -4 ; pointer to DBCS lead byte table DI_BUF_ID equ byte ptr -5 ; buffer id DI_BUF equ byte ptr -5 ; buffer DI_LOCALS equ 5 ; No. bytes storage local to init_dbcs _DATA SEGMENT byte public 'DATA' Public dbcs_table_ptr dbcs_table_ptr label dword dbcs_table_off dw dataOFFSET dummy_dbcs_table dbcs_table_seg dw 0 dummy_dbcs_table dw 0 _DATA ENDS dbcs_init proc near ;-------- ; To initialise the double byte character set (DBCS) lead byte table. ; MUST be called before the first call to dbcs_lead() or dbcs_expected(). ; Entry ; none ; Exit ; none (side effect: DBCS table initialised) push bp mov bp, sp sub sp, DI_LOCALS ; allocate local variables push ds push es push di push si mov dbcs_table_seg, ds ; assume DBCS call will fail mov ax, 06507h ; Extended Country Info: get DBCS ptr mov bx, 0FFFFh ; codepage number: -1 for global cp mov cx, 00005h ; size of info. buffer mov dx, 0FFFFh ; country code: -1 for current country lea di, DI_BUF[bp] push ss pop es ; es:di -> DI_BUF int 21h ; returns with DI_BUF filled in jc di_exit ; just exit if function fails cmp DI_BUF_ID[bp], 7 ; is table for DBCS? jne di_exit ; no - exit les ax, DI_BUF_PTR[bp] ; es:ax -> system DBCS table mov dbcs_table_off,ax mov dbcs_table_seg,es ; fixup pointer to system DBCS table di_exit: pop si pop di pop es pop ds mov sp, bp pop bp ret dbcs_init endp endif ; I want to do ; db (C_HEAP_SIZE - 6 - ($ - stack_start)) dup (0DDh) ; but MASM requires dup's be absolute, so instead rept C_HEAP_SIZE if (offset $ - offset stack_start) LT (C_HEAP_SIZE - 6) db 0ddh endif endm stack_top label word stack_ip dw ? ; Initial Offset stack_cs dw ? ; Initial Code Segment (Unknown) stack_flags dw ? ; Initial Flags (Unknown) STACK ENDS ED_TEXT SEGMENT ; ; Return the CheckSum of All the C code and Static Data. DS:SI ; are initialised to point at the C_code_start. AX contains the ; CheckSum on return. ; calc_crc: mov cx,real_code ; Number of bytes to move is always shr cx,1 ; even because of the segment def. dec cx mov bx,0EDCh ; Checksum Seed cc_10: ; Sum the words of the image while lodsw ; rotating the check value add bx,ax rol bx,1 loop cc_10 xchg ax,bx ret page ; ; Return the code segment of the C main routine. ; get_cs: mov ax,cs:code_seg ret ; ; Return the Data segment of the C data area ; get_ds: mov ax,cs:data_seg ret ; Get remaining memory size, subtract code size and allocate high memory for ; transient portion of command processor ; Exit AX=Segment of high memory ; CY set if none available ; ; This used to allocate only just enough space for the code at the top of ; memory and leave the reset free for use as copy buffers. ; But then came SideKick Plus - this assumes COMMAND.COM only uses the top ; 20K of it's hi-mem partion and overwrites the rest as it gets it's ; overlays. As we are bigger the 20K the result is the system crashes when ; you exit back to the command line. ; In order to minimise this possibility we reserve some space (currently 50K) ; for use as copy buffers and allocate all the rest to the hi-mem portion ; of COMMAND.COM. Since SK starts overwriting from the bottom of memory it ; doesn't usually reach the code. ; The real solution is of course to make sure we are smaller than 20K too... ; (Or transfer the ReadLine code into the resident portion and checksum the ; hi-mem before returning to the hi-mem code ???). ; --ij public alloc_com_memory alloc_com_memory: push es mov bx,0FFFFh ; Allocate more paras than there are mov ah,MS_M_ALLOC ; to find out how many there are. int DOS_INT mov ah,MS_M_ALLOC int DOS_INT ; allocate it mov si,code_length ; then convert the CODE_LENGTH to mov cl,4 ; paragraphs and check if enough memory shr si,cl ; is available to copy the code high add si,1 ; + para for a memory descriptor sub bx,si ; Is there enough for the code ? jc acm_10 ; if not exit with error cmp bx,Copy_Buffer_Size ; allocate some memory for Copy Buffers jb acm_5 mov bx,Copy_Buffer_Size ; limit the maximum Copy Buffer size acm_5: mov es,ax mov ah,MS_M_SETBLOCK int DOS_INT ; shrink to fit push es ; save buffer address ; Now allocate remaining memory for step aside code mov bx,0FFFFh ; allocate all memory mov ah,MS_M_ALLOC ; to find out how much there is. int DOS_INT mov ah,MS_M_ALLOC ; block at the end of memory int DOS_INT ; AX=Segment of new block of memory mov alloc_seg,ax ; save address so we can free it if ThreeCOM cmp bx,1000h ; do we have at least 64k ? jb acm_6 ; if so do 3-Com fix and start mov si,1000h ; Transient portion 64k down acm_6: endif add ax,bx ; go to top of memory we allocated cmp ax,0a000h ; dont use memory above A000 as this jb acm_6a ; may disappear when user does mov ax,0a000h ; MEMMAX -V acm_6a: dec si sub ax,si ; adjust address we run cmd proc at ; Deallocate intervening block of memory pop es push ax mov ah,MS_M_FREE ; Now free it up int DOS_INT pop ax mov cs:_gp_far_buff,real_code mov cs:_gp_far_buff+2,ax ; save address of temp buffer clc ; allocated OK acm_10: pop es ret ; called to free up the cmd processor's high memory free_com_memory: push es push ax mov es,alloc_seg mov ah,MS_M_FREE int DOS_INT pop ax pop es ret ;Control_Break: ; The Break handler makes the following checks taking the ; appropriate action after each test:- ; ; 1) Is COMMAND.COM the current process (Get PSP) if NO ABORT ; 2) Is the break_flag SET if YES jump to the C break handler ; "break_handler" after insuring that the segment registers ; have all be set correctly. ; assume cs:DGROUP, ds:nothing, es:nothing ; Set up the default Control Break Handler. handler_init: ifndef DOSPLUS mov ax,4453h ; Get the address of the int DOS_INT ; internal Critical Error jc handler_i10 ; handler Ignore on Error mov word ptr critical+0,ax ; Save the handler Offset (AX) mov word ptr critical+2,bx ; and Segment (BX) endif mov al,24h ; Set the default Critical mov dx,dataOFFSET critical_error ; Error Handler mov bx,dataOFFSET crit_error_entry call set_vector ; Setup correct ISR handler_i10: mov al,23h ; Set the default Control mov dx,dataOFFSET control_break ; Break Handler mov bx,dataOFFSET control_break_entry ;; jmp set_vector ; Setup correct ISR ; ; Convert the address of the interrupt #AL service routine, whose ; address is CS:DX to be PSP:xxxx. Five NOP must be coded after the ; interrupt service routine for the JMPF instruction which corrects ; the code segment. ; set_vector: push ds push es mov es,[low_seg] mov es:byte ptr 0[bx],0EAh ; JMPF opcode mov es:word ptr 1[bx],dx ; Offset of Interrupt vector mov es:word ptr 3[bx],cs ; Insert the correct code seg mov dx,bx mov bx,es sub bx,__psp ; Calculate the correct offset for mov cl,4 ; the interrupt handler if the segment shl bx,cl ; must be that of our PSP add dx,bx mov ds,__psp mov ah,MS_S_SETINT int DOS_INT pop es pop ds ret ;extrn _int_break:near ; C Break/Error handling routine assume cs:DGROUP, ds:DGROUP, es:nothing control_break PROC FAR ;;db 5 dup(90h) ; Reserve space for JMPF CS:$+5 push ax ; Save the Users registers push bx push ds call get_ds ; Get our Local DS mov ds,ax cmp _cbreak_ok,0 ; is our handler initialised ? je break_05 ; no, don't call it cmp reload_flag,0 ; Are we part way through reloading clc ; the command processor? jnz break_05 ; if so do not abort. mov ah, MS_P_GETPSP ; Get the current PSP address int DOS_INT cmp bx,[__psp] ; Is this our address stc ; Assume not and Set the carry flag jz break_10 ; if internal, restart the command loop ; if external, abort process break_05: pop ds pop bx pop ax ret ; ; This section of code corrects the stack and jumps to the ; C code Control Break Handler _int_break. Beware that the ; stack segment need not be the DS because the READ_LINE routine ; executes on a high stack and the CED programs will emulates ; a Control-Break on the wrong Stack. ; break_10: cli ; Swap to the correct stack not mov ax,ds ; forgetting that we might be running mov es,ax ; on old 8088 or 8086 so interrupts mov ss,ax ; must be disabled mov sp,stack_min ; Get the lowest possible stack address add sp,12 ; Add a little to avoid problems with sti ; the stack check code. mov ax,0100h ; Termination Code Control-C Abort push ax ; Save on the Stack for C routine push ax ; Force a dummy return address on the ; stack (NOT USED) call get_cs ; Put the Segment and Offset address push ax ; of the C break Handler on the stack mov ax,codeOFFSET _int_break; and execute a RETF to it. push ax ret control_break ENDP critical_error PROC FAR ;;db 5 dup(90h) ; Reserve space for JMPF CS:$+5 push ds ; Save the Critical Error DS push ax call get_ds ; Get the Command Processor DS mov ds,ax ; and call the original routine ifdef DOSPLUS mov ax, _n_option ; check for /n command line option cmp ax, 0 pop ax jne skip_criterr call com_criterr ; local criterr handler jmp criterr_cont skip_criterr: mov al,3 ; /n option => always return fail criterr_cont: else pop ax pushf call critical ; stored previous handler endif cmp al,02h ; Did the user request a Terminate jne critical_e20 ; Yes so check if they are trying to push ax ; terminate the command processor push bx mov ah, MS_P_GETPSP ; Get the current PSP address int DOS_INT cmp bx,[__psp] ; Is this our address? jne critical_e10 ; no so return with Abort code cmp in_exec,0 ; are we EXECing a program ? jne critical_e10 ; then return the error cmp reload_flag,0 ; then unless we are reloading command je break_10 ; processor break here to prevent critical_e10: ; higher levels generating repeated pop bx ; critical errors (eg. when searching pop ax ; a path) critical_e20: pop ds iret critical_error ENDP ; ; INT2E is a backdoor entry to the "Permanent" Command interpreter ; which will execute the command at DS:SI. ; int2E_far_entry proc far db 5 dup(90h) ; Reserve space for JMPF CS:$+5 ; ds:si -> command line preceded by byte count ; check we're not re-entering mov ax, 1 xchg cs:i2e_lock, ax test ax, ax jz i2e_10 iret i2e_10: push bx push cx push dx push si push di push bp push ds push es ; if ds = si = 0 then set batch_seg_ptr to zero and get out. ; This is a clean way of halting batch processing. mov ax,ds cmp ax,0 jne i2e_15 cmp si,0 jne i2e_15 mov si,cs:_batch_seg_ptr mov ds,cs:_batch_seg_ptr+2 mov [si],ax jmp i2e_exit2 i2e_15: ; swap stack mov cs:i2e_user_ss, ss mov cs:i2e_user_sp, sp cli mov ss, cs:exec_ss mov sp, cs:exec_sp sti ; allocate 128 bytes for command line, since the C code needs a 128 ; byte buffer anyway mov cx, 128 ; save stack p mov cs:i2e_stack, sp ; have we got enough room for command line mov ax, sp sub ax, cx sub ax, cs:heap_top cmp ax, 256 jge i2e_25 jmp i2e_exit i2e_25: sub sp, cx mov cs:i2e_cmd, sp ; copy command line push ss pop es mov di, sp lodsb ; get line count and ax,7fh ; limit to 128 bytes add ax,sp ; terminating NULL goes here rep movsb ; copy the command line xchg ax,di ; DI -> NUL posn xor al,al stosb ; replace CR with NUL push cs pop ds ; reload non-resident code if necessary cmp high_code, TRUE jnz i2e_30 mov reload_flag, 1 call reload_code mov reload_flag, 0 i2e_30: ; install our own Break and Criterr handlers - e.g. if second copy ; of the command processor is running, install original handlers so ; that they are looking at the same data as we are. ; N.B. __psp is currently that of original command processor mov ax, (MS_S_GETINT*256) + 23h int DOS_INT mov i2e_i23seg, es mov i2e_i23off, bx mov ax, (MS_S_GETINT*256) + 24h int DOS_INT mov i2e_i24seg, es mov i2e_i24off, bx call handler_init ; save the command processor's __psp variable, and then set it to the ; psp of the process that called us. This is so that the Break and ; Criterr abort code can correctly determine whether the int2e command ; was internal or external mov ah, MS_P_GETPSP int DOS_INT push bx ; save calling process's psp - EJH mov bx, [__psp] ; Set current PSP to our own - EJH mov ah, MS_P_SETPSP int DOS_INT ; call C code mov ax, code_seg mov i2e_c_seg, ax push i2e_cmd call i2e_c_entry pop ax ; clean up stack ; Set current psp back to that of calling process - EJH pop bx mov ah, MS_P_SETPSP int DOS_INT ; restore interrupt vecs push ds lds dx, i2e_i23vec mov ax, (MS_S_SETINT*256) + 23h int DOS_INT lds dx, cs:i2e_i24vec mov ax, (MS_S_SETINT*256) + 24h int DOS_INT pop ds cmp high_code, TRUE ; If not a .EXE then free jnz i2e_ret ; any memory alocated by call free_com_memory ; the Command Processor i2e_ret: i2e_exit: ; swap back the stack cli mov ss, i2e_user_ss mov sp, i2e_user_sp sti i2e_exit2: pop es pop ds pop bp pop di pop si pop dx pop cx pop bx sub ax, ax mov cs:i2e_lock, 0 ret 2 int2E_far_entry endp _TEXT SEGMENT assume cs:CGROUP, ds:DGROUP, es:nothing, ss:nothing public _exec ; EXEC routine ; ; WORD CDECL exec(BYTE *path, UWORD type, BYTE *line, BOOLEAN back); ; ; back 10[bp] ; line 08[bp] ; type 06[bp] ; path 04[bp] ; _exec: ;----- push bp mov bp,sp push si push di push ds push es inc in_exec cmp in_exec,1 je _exec10 ; Menuing system push exec_ss ; save old stack_save if we push exec_sp ; are being re-entered _exec10: mov si,08[bp] ; the Command Line mov exec_env,0000 mov exec_clineoff,si mov exec_clineseg,ds mov exec_fcb1seg,ds mov exec_fcb2seg,ds push ds pop es ; ; Extract two valid filenames from the CR terminated ; string passed in DS:SI. The FCBs will be generated in FCB1 and FCB2. ; inc si mov di,dataOFFSET fcb1 ; Blank fill the first FCB call zap_fcb call make_fcb ; Build first FCB mov di,dataOFFSET fcb2 ; Blank fill the Second FCB call zap_fcb call make_fcb ; Build second FCB mov ax,cs jmp exec ; do FAR jmp to msdos_exec as we can't _exec_ret: ; trust the stack when CALLing dec in_exec ; Has PCTOOLS un-installed itself ? jz _exec20 ; No so the stack contents are valid js _exec_bad ; Yes, so exit by Ctrl-Break pop exec_sp ; old stack save back again pop exec_ss ; in the case of re-entry _exec20: pop es pop ds pop di pop si pop bp mov ax,return_code neg ax ret ; ; We have returned from the EXEC function un-expectedly (because ; of PCTOOLS De-installation ?). Therefore the contents of the stack ; are invalid and we generate a Control-Break. ; _exec_bad: mov ax,0100h ; Termination Code Control-C Abort push ax ; Save on the Stack for C routine push ax ; Force a dummy return address on stack mov in_exec,al ; zero in_exec count for next time extrn _int_break:near ; C Break/Error handling routine jmp _int_break ; now treat as ctrl-break ; ; Initialise the FCB entry a DX:DI and copy the Drive, FileName ; and extension form DS:SI when SI != 0FFFFh ; zap_fcb: mov ax,2000h ; Zero Fill first byte and last 4 stosb ; Drive code 0 xchg al,ah mov cx,11 ; FileName and Ext to ' ' rep stosb xchg al,ah mov cl,4 ; Last four bytes to 00 rep stosb sub di,16 ret make_fcb: call scan_filechar ; Get the Next File Character push si push di push ax ; Save Character and Pointers mov ax,(MS_F_PARSE shl 8) + 1 ; Parse the command line int DOS_INT pop ax pop di ; Restore the Character and Pointer cmp ah,'/' ; Was the arg preceeded by a '/' jne make_fcb10 ; If it was roll back the cmd line pop si ; pointer and blank all but the 1st inc si ; char in the FCB add di,2 ; Such that /FRED gives an FCB of 'F' mov cx,10 ; and si points to the 'R' mov al,' ' rep stosb ret make_fcb10: pop ax ; discard original SI, skip name jmp scan_sepchar ; skip until separator ; ; SCAN_SEPCHAR will search through the command line DS:SI ; which contains CX characters and return with SI pointing ; to the next SEPARATOR character. ; ; On Entry: DS:SI Character String ; ; On Exit: DS:SI First Non Separator ; scan_sepchar: push di ; Save DI ES points at this segment mov ah,0 ; Invalidate Separator Character scan_s10: mov al,[si] ; Get the Character to Test cmp al,CR jz scan_s20 ; Have we reached the end of the string mov cx,legal_length ; Scan the table of legal mov di,dataOFFSET legal_table ; separators repnz scasb ; Scan the List jz scan_s20 ; Separator Located inc si ; the character pointer jmp SHORT scan_s10 scan_s20: pop di ret ; ; SCAN_FILECHAR will search through the command line DS:SI ; and return with SI pointing to the next NON_SEPARATOR character. ; ; On Entry: DS:SI Character String ; ; On Exit: DS:SI First Non Separator ; AH Last Valid Separator ; scan_filechar: push di ; Save DI ES points at this segment mov ah,0 ; Invalidate Separator Character scan_f10: mov al,[si] ; Get the Character to Test cmp al,CR jz scan_f20 ; Have we reached the end of the string mov cx,legal_length ; Scan the table of legal mov di,dataOFFSET legal_table ; separators repnz scasb ; Scan the List jnz scan_f20 ; Non Separator Located mov ah,al ; Save the Separator and increment inc si ; the character pointer jmp SHORT scan_f10 scan_f20: pop di ret _TEXT ENDS legal_table db ':.;,=+',9,' /<>|',22h,'[]' legal_length equ 15 assume cs:DGROUP, ds:DGROUP, es:nothing, ss:nothing public msdos_exec ; so it shows in map file msdos_exec PROC FAR push ax cmp high_code,TRUE ; Check if the Command Processor Code jnz msdos_e10 ; has been relocated to high memory call free_com_memory ; free cmd processor memory msdos_e10: pop ax mov bx,dataOFFSET exec_block mov dx,04[bp] ; Get the Full Command Name mov exec_ss,ss ; Save SS:SP in case somebody (NOVELL) mov exec_sp,sp ; corrupts them ; The following 2 JMPF are kludged so that the INT22 vector saved in ; the child's PSP has the segment of the comand processors PSP. ; This is vital for many TSR management utilities. ; ; swap stack to conventional memory cli ; mov ax,__psp ; mov ss,ax ; mov sp,0100h ; sti ; db 0EAh ; JMPF Opcode dw dataOFFSET psp_dofunc4b ; Corrected Offset exec_psp dw 0 ; PSP Segment assume cs:DGROUP, ds:nothing, es:nothing, ss:nothing func4b_return: mov ss,exec_ss ; Restore the real SS:SP mov sp,exec_sp ; Using a CS overide jc msdos_e20 ; if no error xor ax,ax ; then zero return code jmp msdos_e21 msdos_e20: mov ah,59h ; get extended error code sub bx,bx int 21h msdos_e21: mov return_code,ax call get_ds ; Point DS & ES to CSdata mov ds,ax mov es,ax assume cs:DGROUP, ds:DGROUP, es:nothing, ss:nothing call handler_init ; Re-initialise Control-Break and ; Critical error handlers for Novell cmp high_code,TRUE ; Is the Command Code in High jnz msdos_e30 ; memory ? mov reload_flag,1 ; Reloading Command Processor call reload_code ; Reload Command Processor Code mov reload_flag,0 ; Command Processor Loaded msdos_e30: mov ax,code_seg ; Update the CS for the high code mov msdos_exec_ret_seg,ax ; in case it has moved and return jmp msdos_exec_ret msdos_exec ENDP _TEXT SEGMENT assume cs:CGROUP, ds:DGROUP, es:nothing, ss:nothing Public _readline _readline: ;--------- cld push bp mov bp,sp push si push di push es ifdef DOSPLUS mov ax,4456h ; Swap to the Command process mov dl,1 ; History Buffer in DR DOS int DOS_INT else push ds call get_history_buffers ; get history buffers mov P_SB_SEG[bx],ax ; swap to command one pop ds endif mov ax,5d09h int DOS_INT ; close remote spool files mov ax,5d08h mov dl,1 int DOS_INT ; Set truncate flag with redirected I/O mov ah,MS_P_GETPSP ; get the current PSP int DOS_INT ; then compare the STDERR entry in the mov es,bx ; XFT with the Magic Number for CON cmp es:byte ptr [18h+STDERR],1 mov dx,4[bp] ; get the buffer address and current mov ax,cs ; AX = transient code segment call readline ; do far call to msdos_readline ifdef DOSPLUS mov ax,4456h ; Swap to the Application process mov dl,0 ; History Buffer in DR DOS int DOS_INT else push ds call get_history_buffers ; get history buffers mov P_SB_SEG[bx],dx ; swap to application one pop ds endif pop es pop di pop si pop bp ret ifdef CDOS get_history_buffers proc near ; On Entry: ; None ; On Exit: ; AX = command history buffers ; DX = application history buffers ; DS:BX -> pd ; NB. both DS and ES are corrupted by this call ; mov ds,_sysdat_seg ; DS points to SYSDAT and ES to the mov bx,ds:word ptr [68h] ; current process's UDA mov dl,P_CNS[bx] ; DL = console number mov es,P_UDA[bx] push bx mov ax,XIOS_HISTBUF ; Get the History Buffer Address's call XIOS_ENTRY ; by calling ths XIOS pop bx ret get_history_buffers endp endif _TEXT ENDS assume cs:DGROUP, ds:DGROUP, es:nothing ; ; msdos_readline(BYTE *buffer); ; ; On Entry: ; flags - ZF set if we need to do Sidekick Plus check ; DS:DX -> buffer ; AX:0 -> Resident Section ; buffer ; ; The memory allocation for the hi-mem part of command.com has already been ; altered to minimise the chances of SideKick Plus overwriting the code. ; But it still can happen, say either on a machine with limited memory or ; other TSR's also loaded. In order to cater for this case we move the actual ; readline call into the resident portion and checksum things before returning ; to high memory - thus we can reload command.com if it get overwritten. ; msdos_readline PROC FAR pushf ; save the result for later mov bx,sp ; SP before swapping stacks. cli ; to RLSTACK mov ss,ax ; above the Code and Messages mov sp,codeOFFSET rlstack sti push bx ; save old SP push ds sub sp,260 ; make room for buffer on stack mov di,sp ; save the address push ds push dx ; save real BUFFER for later... push di ; and buffer on stack mov si,dx ; point DS:SI at real buffer mov dx,di ; point DX at stack buffer push ss ; now get all the seg regs pop es ; pointing to himem mov cx,256/2 rep movsw ; copy resident buffer to stack push ss ; DS:DX is stack buffer pop ds mov ax,4810h ; give DOSKEY a chance int 2Fh or ax,ax ; zero means DOSKEY has done it jz msdos_rl10 mov word ptr [di],21cdh ; poke INT 21 and mov byte ptr 2[di],0cbh ; RETF instruction push cs ; save an address for the mov ax,dataOFFSET msdos_rl10; RETF to return to push ax mov ah, MS_C_READSTR ; parameter for INT 21 readline push ds ; finally jump to the INT 21 push di ; we have poked ret msdos_rl10: pop si ; source is readline buffer pop di ; recover old address pop es ; and seg of real buffer mov cl,ds:byte ptr [si] ; get MAX length xor ch,ch ; make it a word cld rep movsb ; copy the string add sp,260 ; recover my readline buffer space pop ds pop bx ; recover old SP cli ; Now restore the normal C stack call get_ds ; which is at DS:BX. mov ss,ax mov sp,bx mov es,ax sti popf ; recover result of STDERR test jne msdos_r10 ; if not CON, then skip the check cmp high_code,TRUE ; Is the Command Code in High jnz msdos_r10 ; memory ? mov reload_flag,1 ; Reloading Command Processor call reload_code ; Reload Command Processor Code mov reload_flag,0 ; Command Processor Loaded msdos_r10: mov ax,code_seg ; Update the CS for the high code mov word ptr -08[bp],ax ; in case it has moved and return ret msdos_readline ENDP ; Cmd processor is in high memory so check it out reload_code: cmp in_exec,1 ; If we landed here from an undefined area je alloc_mem ; then free cmd processor memory call free_com_memory alloc_mem: call alloc_com_memory ; Allocate memory for the Command jc alloc_mem_error ; processor code. push ax call check_crc ; see if code has been trashed pop ax je still_there mov code_seg,ax jmp load_com ; Read in command Processor Code still_there: cmp ax,code_seg ; has the memory configuration changed? je hasnt_moved ; (it will during Novell remote boot) push ds push es mov cx,real_code mov es,ax ; move the code to the new location mov ds,code_seg ; sub si,si ; ds:si -> old location sub di,di ; es:di -> new location rep movsb ; do it mov cs:code_seg,es ; update code_seg with new location pop es pop ds call check_crc ; recalculate the crc jne load_com ; if its not valid we need to load ; from disk hasnt_moved: ret alloc_mem_error: ; We can't reload COMMAND.COM because we haven't any memory ifdef DLS mov dx,_reload_msgm else mov dx,dataOFFSET _reload_msgm endif call reload_err ; say we can't reload COMMAND.COM jmp $ ; stop forever.... check_crc: ; return with ZF set if himem crc's correctly push ds mov ds,code_seg mov si,2 call calc_crc pop ds ; Compare this crc with the one we got earlier cmp crc,ax ret ; If checksum is wrong load a new copy into high memory and patch new RET addr open_err: ifdef DLS mov dx,_reload_msgf else mov dx,dataOFFSET _reload_msgf endif call reload_err ; prompt for COMMAND.COM mov ah,MS_C_RAWIN ; Wait for the User to Press int DOS_INT ; a key load_com: push ds mov ds,[low_seg] mov dx,dataOFFSET reload_file mov ax,(MS_X_OPEN*256)+40h ; Try to open COMMAND.COM int DOS_INT ; al=40h means NONE DENIED share mode pop ds jc open_err mov bx,ax xor dx,dx ; Take account of EXE header cmp exe_file,FALSE ; if we were loaded as an EXE je load_com_10 add dx,200h ; Assume header is 200h bytes load_com_10: xor cx,cx ; Now read the command processor code add dx,total_length ; Seek to area of COMMAND.COM mov ax,(MS_X_LSEEK*256)+0 ; containing code int DOS_INT jc read_err push ds mov cx,real_code mov ds,code_seg ; DS:DX - > High memory area xor dx,dx mov ah,MS_X_READ ; Read the code into memory int DOS_INT pop ds read_err: mov ah,MS_X_CLOSE ; Close the file int DOS_INT call check_crc ; try and see if we loaded OK jne open_err ; if not complain and try again ret reload_err: ; On Entry: ; DX = offset of final part of error message ; push dx ; save final part of message mov bx,STDERR ; Display on standard Error ifdef DLS mov dx,_reload_msgs else mov dx,dataOFFSET _reload_msgs endif call reload_err10 push ds mov ds,[low_seg] mov dx,dataOFFSET reload_file ; Filename call reload_err10 pop ds pop dx ; recover msg offset ; ; Calculate the length of the message to be displayed and output using ; MS_X_WRITE. ; ; DS:DX String to Output ; BX Output Handle ; reload_err10: xor al,al ; String Terminator push es push ds ; look for terminating BYTE pop es ; in string at DS:DX mov di,dx ; and get length to display mov cx,0ffffh repnz scasb ; look for terminator not cx ; see how far we had to look dec cx ; forget about the terminator mov ah,MS_X_WRITE int DOS_INT pop es ret ED_TEXT ENDS endif page _TEXT SEGMENT assume cs:CGROUP, ds:DGROUP, ss:DGROUP ifdef CDOSTMP RSP_start: mov ax,ds mov es,ax cli ; Swap stacks with interrupts disabled mov ss,ax ; just in case we're on an 8088/86 mov sp,dataOFFSET stack_top sti mov insys,0 ; Reset the INSYS Flag mov _gp_far_buff,dataOFFSET temp_buffer mov _gp_far_buff+2,ax ; save address of temp buffer mov dl,defconsole ; Set this process's Default Console bdos C_SET bdos C_ATTACH bdos P_PDADR ; Get the address of the current mov sysdat,es ; PD and SYSDAT mov dl,BOOTDRV ; Get the BOOT Drive mov es:P_DSK[bx],dl ; Update the running process mov bx,dataOFFSET pd_seg ; Update the default drive field mov P_DSK[bx],dl ; In the P_CREATE process descriptor mov bx,es:INT17_PTR ; Initialise the default printer mov al,defconsole ; from the INT17 Mapping Array mul es:byte ptr [bx] add bx,ax mov dl,es:1[bx] bdos L_SET mov bx,es:INT14_PTR ; Initialise the default printer mov al,defconsole ; from the INT14 Mapping Array mul es:byte ptr [bx] add bx,ax mov dl,es:1[bx] bdos A_SET mov [code_seg],cs ; Update the CODE_SEG and EXEC_SEG mov [exec_seg],ds ; variables mov dl,0FFh ; Set the BDOS Error Mode to Return bdos F_ERRMODE ; with No Display mov dx,ds ; Initialise the DMA Segment bdos F_DMASEG ; Pointer to Our DS bdos P_PDADR ; Set ES:BX to the Process Descriptor mov word ptr _pd,bx ; Save these values locally mov word ptr _pd+2,es mov _sysdat_seg,es or es:P_CONMODE[bx],PCM_FCTLC mov es:P_PARENT[bx],0 ; Zero the Parent Pointer mov ax,es:P_UDA[bx] ; Finally get our UDA and save it. mov uda,ax mov ax,es:P_PSP[bx] mov [__psp],ax mov ax,XIOS_HISTBUF ; Get the History Buffer Address's mov dl,defconsole ; for console number call xios mov cmd_histbuf,ax ; AX == Command Level History Buffer mov prog_histbuf,dx ; DX == Program History Buffer les bx,_pd ; Get the Process descriptor address mov es:P_SB_SEG[bx],ax ; and use the Command Line ; History Buffer ; File Ownership stuff les bx,_pd ; Get our process descriptor address mov al,es:P_CNS[bx] ; get the current VC No. xor ah,ah mov bx,CCBLIST ; XIOS CCB$LIST add bx,ax add bx,ax mov bx,es:word ptr [bx] ; extract the correct mov ah,CCB_PCNS[bx] ; Physical Console Number. mov ch,0 mov cl,NVCNS ; now work through all consoles mov bx,CCBLIST ; looking for 1st one with this PC RSP_start10: mov di,es:word ptr [bx] ; Get the CCB address cmp ah,CCB_PCNS[di] ; Is this the same physical console je RSP_start20 ; no, ignore this one add bx,2 ; onto next CCB loop RSP_start10 jmp RSP_start60 ; impossible.... RSP_start20: cmp al,CCB_VCNS[di] ; is it the same virtual console ? je RSP_start40 ; yes, allocate new FA_ structure RSP_start30: push di mov dx,5 bdos P_DELAY ; wait a bit pop di ; then see if it has been allocated mov si,CCB_OWNER[di] ; si -> owning process mov si,es:P_FILE_ACCESS[si] ; File Access structure test es:FA_FLAGS[si],FAF_TMP_INIT ; is it the new one ? jz RSP_start30 ; no, delay again inc es:FA_COUNT[si] ; one more user for this structure les bx,_pd xchg si,es:P_FILE_ACCESS[bx] ; replace existing one dec es:FA_COUNT[si] ; one less user for old one jmp RSP_start60 RSP_start40: push ax ; save PC/VC cmp ah,0 ; is it the main box ? je RSP_start45 mov dx,5*50 ; delay PC Terminals 5 secs while bdos P_DELAY ; main box starts up RSP_start45: mov DX,FA_LENGTH ; we need this much SYSDAT memory bdos S_MEMORY ; for our file access structure pop dx ; recover PC/VC cmp ax,0FFFFh ; if no memory je RSP_start50 ; then just keep root one les di,_pd ; replace file access structure xchg ax,es:P_FILE_ACCESS[di] xchg ax,di dec es:FA_COUNT[di] ; we have stopped using this structure mov es:FA_USER[bx],0 ; user # is super user mov es:FA_GROUP[bx],0 ; and group is super group mov es:FA_DEF_ACCESS[bx],0 ; initialise the new structure mov es:FA_COUNT[bx],1 RSP_start50: les bx,_pd mov bx,es:P_FILE_ACCESS[bx] mov es:FA_FLAGS[bx],FAF_TMP_INIT RSP_start60: ; File Ownership stuff ends push ds mov es,sysdat ; Get SYSDAT mov dx,VERSION ; get OS label offset in SUP segment mov ds,CCPMSEG ; get SUP segment for signon string bdos C_WRITESTR ; print OS label on current console pop ds endif ; ; assume cs:CGROUP, ds:DGROUP, es:DGROUP ; ; zero data areas (_BSS and c_common) ; memory_init: push ss pop es cld ; set direction flag (up) mov di,dataOFFSET _edata ; beginning of bss area mov cx,dataOFFSET _end ; end of bss area sub cx,di xor ax,ax rep stosb ; zero bss ; C segmentation conventions set up here (DS=SS and CLD) push ss ; set up initial DS=ES=SS, CLD pop ds assume ds:DGROUP ; do necessary initialization BEFORE command line processing ! ifndef CDOSTMP push es mov ax,4458h ; We now have an IOCTL function int 21h ; to get our private data jc mem_init10 ifdef DOSPLUS ;les ax,DRDOS_PD ;les ax,es:dword ptr 0000h[bx] ;mov word ptr _pd,ax ;mov word ptr _pd+2,es ;mov _sysdat_seg,es else mov es:byte ptr [bx],-1 ; say COMSPEC is loaded mov cl,P_PDADR ; Set ES:BX to the Process Descriptor mov ax,4459h ; use Int 21h so we don't crash if int 21h ; under DOS and we will give an error mov word ptr _pd,bx ; from COM.C init(). mov word ptr _pd+2,es ; Save these values locally mov _sysdat_seg,es endif mem_init10: pop es endif xor bp,bp ; mark top stack frame for SYMDEB ifdef CDOSTMP mov ax,dataOFFSET cmdline ; Pass a pointer to a NULL string else call get_cmdline ; Copy the command line to a local endif push ax ; buffer and pass the address to MAIN call C_code_entry ; main ( cmd ) add sp,2 ; Restore the Stack mov ah,MS_X_EXIT ; use whatever is in ax after int DOS_INT ; returning here from main page ; ; For TURBOC and METAWARE C the majority of the Command Processor ; messages are linked with the CGROUP in the MSG segment. When the ; compiler generates DGROUP relative references large offsets are ; produced. This occurs because the DGROUP is linked before the CGROUP ; and its MSG Segment. FARPTR converts a NEAR pointer into a FAR ; by checking the value of the offset against the DGROUP length. ; Offset greater than TOTAL_LENGTH must be in MSG so the pointer ; is converted to CS:x-TOTAL_LENGTH otherwise DS:x. ; public _farptr _farptr: push bp mov bp,sp mov ax,04[bp] mov dx,ds cmp ax,total_length jbe farptr_10 ifndef WATCOMC sub ax,total_length endif mov dx,cs farptr_10: mov bx,dx pop bp ret ifdef DLS public _msgfarptr _msgfarptr: push bp mov bp,sp mov ax,04[bp] add ax,offset CGROUP:_MSG mov dx,cs mov bx,dx pop bp ret endif ; We need some additional support for the CMD_LIST - it contains near pointers ; to other items in the CGROUP. This routine turns a NEAR pointer into a FAR ; pointer. public _cgroupptr _cgroupptr: push bp mov bp,sp mov ax,04[bp] mov dx,cs mov bx,dx pop bp ret ifdef DLS ;/* Get the address of the msg_language variable in the DR BDOS. */ ;GLOBAL BYTE FAR * CDECL get_msg_lang() public _get_msg_lang _get_msg_lang: mov ax, 4458h ; IOCTL func: get ptr private data int 21h ; pointer returned in ES:BX jc _get_msg_lang_err ; skip if invalid function add bx, 9 ; DLS version byte at offset 9 cmp es:byte ptr [bx], 1 ; correct version? jne _get_msg_lang_err ; no - skip inc bx ; DLS language byte at offset 10 mov ax,bx mov dx,es ; return pointer in DX:AX ret _get_msg_lang_err: mov ax,codeOFFSET dummy_lang mov dx,cs ret dummy_lang db 0 ; default to primary language endif page ifndef CDOSTMP ; ; This following routine copies the initial command line from the PSP ; into the CMDLINE data area. Processing any special switches and ; adding a terminating null character. The offset of the copy is ; returned in AX. ; get_cmdline: if 0 mov di,dataOFFSET cmdline else mov di,heap_top ; copy cmdline onto the heap endif push ds ; Preserve DS and point to PSP mov ds,__psp ; Get the PSP address xor cx,cx ; Now copy the command line mov cl,ds:0080h ; Get the Command length mov si,0081h ; and its start location jcxz get_cmdl20 get_cmdl10: lodsb ; Terminate the copy after cmp al,0Dh ; CX bytes or earlier if a jz get_cmdl20 ; CR or LF character is found. cmp al,0Ah ; FrameWork Install Program and jz get_cmdl20 ; Bitstream FontWare stosb loop get_cmdl10 get_cmdl20: xor al,al ; Zero Terminate the command stosb ; line copy for C. pop ds if 0 mov ax,dataOFFSET cmdline ; Return the command line else mov ax,di ; new bottom of heap xchg ax,heap_top ; return old one = cmdline endif ret ; ; This routine will install the DS relative dummy INT 2E handler with ; a PSP:XXXX entry in the interrupt vector table. A JMPF is coded ; after the handler entry point inorder to correct CS. ; Public _install_perm _install_perm: push es mov ax,__psp ; Modify the INT 22 and 2E mov es,ax ; vectors if the current process cmp ax,PSP_PARENT ; is the ROOT DOS process. ie jnz inst_p10 ; PSP_PARENT == PSP mov al,2Eh ; Install the Command Processor mov dx,dataOFFSET int2E_entry ; Backdoor entry call inst_p30 mov al,22h ; Update the Command Processor mov dx,dataOFFSET int22_entry ; Terminate address call inst_p30 push ds ; When this is the ROOT process push si ; then update the PSP copies of push di ; interrupt vectors 22, 23, 24 push ds ; because some TSR management pop es ; programs examine these variables lea di,psp_save_area ; ES:DI -> save area for PSP mov ds,__psp lea si,PSP_TERM_IP ; DS:SI -> data in PSP to save mov cx,6 rep movsw ; save the data for later EXIT push ds pop es lea di,PSP_TERM_IP ; ES:DI -> PSP mov ds,cx mov si,022h * 4 ; DS:SI -> real interrupt vecs mov cx,6 rep movsw pop di pop si pop ds inst_p10: pop es ret inst_p30: push ds mov bx,[low_seg] sub bx,__psp ; Calculate the correct offset for mov cl,4 ; the interrupt handler if the segment shl bx,cl ; must be that of our PSP add dx,bx mov ds,__psp mov ah,MS_S_SETINT int DOS_INT pop ds ret page ; ; This routine will restore the Int 22 terminate address copy in ; the PSP in preperation for an EXIT (DeskView bug). ; Public _restore_term_addr _restore_term_addr: push es mov ax,__psp ; Restore the PSP we altered mov es,ax ; if the current process cmp ax,PSP_PARENT ; is the ROOT DOS process. ie jne restore_ta10 ; PSP_PARENT == PSP push si push di lea si,psp_save_area lea di,PSP_TERM_IP mov cx,6 rep movsw pop di pop si restore_ta10: pop es ret page ; ; This routine will restore the Novell error mode - BAP ; Public _restore_error_mode _restore_error_mode: mov dl,net_error_mode mov ah,0ddh ; set error mode to the value int 21h ; it was when COMMAND was ret ; executed page ; ; MASTER_ENV will create a Master Environment of 04[bp] bytes unless ; the environment is already larger in which case the environment ; is increased by 128 bytes. ; Public _master_env _master_env: push bp mov bp,sp push si push di push es ifdef DOSPLUS ; mov ax,(MS_M_STRATEGY*256)+1 ; mov bx,1 ; set memory strategy to best fit ; int DOS_INT endif mov bx,04[bp] ; Save the Specified Size add bx,15 ; and force it to be an integer and bx,not 15 ; multiple of 16 mov es,__psp ; Get the PSP Segment Address mov cx,PSP_ENVIRON ; Get the environment segemnt jcxz master_env10 ; Have we been loaded by DesQview ? mov es,cx ; Scan through the environment and xor di,di ; determine the Environment size and xor ax,ax ; the Load file name mov cx,7FFFh master_env05: repne scasb ; Scan for a ZERO byte jcxz master_env10 ; Abort if maximum size exceeded inc di ; Check if the next character was a 0 cmp al,es:-1[di] ; if YES then this is the end of the jnz master_env05 ; environment mov cx,di ; Calculate the environment Size ; ; CX contains the current environment size in bytes. 0 for a DesQview ; system or an empty environment. ; master_env10: cmp bx,cx ; Is the current environment larger jae master_env15 ; No allocate requested value mov bx,128 add bx,cx master_env15: push cx ; Save current Environment Size mov cl,4 ; convert request to paragraphs shr bx,cl mov ah,MS_M_ALLOC ; Allocate Memory int DOS_INT pop cx jc master_env30 ; Abort Memory Allocation Failed mov es,__psp ; Copy the Environemt xchg ax,PSP_ENVIRON ; Update the Environment Pointer push ds mov ds,ax ; DS -> Initial Environment mov es,PSP_ENVIRON ; ES -> Master Environment mov si,0 mov di,si jcxz master_env20 ; If this was a Desqview exec then rep movsb ; skip the environment copy and ; just initialize to 0000 if TRUE ; Invalidate the contents of the not ds:word ptr 00h ; current environment so Novell else ; Netware finds the correct Master push es ; environment under Concurrent DOS. mov es,ax ; mov ah,MS_M_FREE ; A possible alternative is to free int DOS_INT ; the old environment but this would pop es ; change the memory allocations for endif ; sub-sequent loads. master_env20: pop ds xor ax,ax stosw master_env30: ifdef DOSPLUS ; mov ax,(MS_M_STRATEGY*256)+1 ; xor bx,bx ; set memory strategy to first fit ; int DOS_INT endif pop es pop di pop si pop bp ret endif page ifndef DOSPLUS ; ; UWORD FAR *sysdat(WORD *); ; Public _sysdat _sysdat: push bp mov bp,sp push es BDOS S_SYSDAT ; Get the SYSDAT segment address mov ax,word ptr 04[bp] ; and the SYSDAT byte offset mov bx,es ; and return a FAR pointer to the mov dx,bx ; data required. MSC uses DX:AX pop es ; but cater for other compilers pop bp ; just in case. ret endif ifdef DOSPLUS ; ; BOOLEAN CDECL int10_cls(); ; ; int10_cls will return TRUE if it issued an INT10 function to ; clear the screen. ; Public _int10_cls _int10_cls: push bp push si push di push es mov ax,(MS_X_IOCTL*256)+0 ; Get the device attributes for mov bx,STDOUT ; STDOUT int DOS_INT mov ax,0000 ; Assume that the test fails jc int10_exit ; and return FALSE to the caller and dl,092h ; Check that STDOUT is the Console cmp dl,092h ; Out DEVICE and it supports INT29 jnz int10_exit ; Fast output ;;mov es,ax ; Now check that the INT29 routine ;;mov bx,es:word ptr (29h*4)+2; is below the INT20 service routine ;;cmp bx,es:word ptr (20h*4)+2; ie that this is a BIOS device driver ;; jae int10_exit ; No way Jose ; So what if it isn't? mov ax,1A00h ; ANSI.SYS installation check int 2Fh ; AL = FF on return if ANSI installed cbw ; AX = FFFF if ANSI present inc ax ; AX = 0 if ANSI present jz int10_exit ; get number of lines call cginfo ; get screen resolution push dx ; screen lines mov ah, 0fh ; get mode int 10h and al,7fh mov ah, 0 ; set mode, clear screen (al bit 7 clear) int 10h call cginfo ; has resolution changed? pop ax ; restore screen lines cmp al, dl ; has # of screen lines changed? je int10_done ; skip if still the same mov ax, 1112h ; character generator mov bl, 0 ; set 8x8 double dot font int 10h mov ax, 1103h ; set block specifier mov bl, 0 int 10h int10_done: mov ax,1 ; All done int10_exit: pop es pop di pop si pop bp ret cginfo: mov dl, 24 ; assume default # for CGA/MDA mov ax, 1130h ; character generator info mov bh, 0 int 10h ret ; dl = nlines - 1 endif ifdef CDOSTMP xios: push ds ; Save the entry segment registers push es ; then call the XIOS correctly with ; DS pointing to SYSDAT and ES to the mov es,uda ; current process's UDA mov ds,sysdat call XIOS_ENTRY pop es pop ds ret endif ifndef DOSPLUS ; ; _vc_data(&vc_base, &vc_num, &pc_num) ; ; VC_DATA returns the first Virtual Console attached to the current ; physical console, the number of virtual consoles attached and the ; physical console. ; Public _vc_data _vc_data: push bp mov bp,sp push es push si push di mov si,04[bp] ; SI == &VC_BASE mov word ptr [si],0000 ; *vc_base = 0 les bx,_pd ; Get our process descriptor address mov ah,0 mov al,NVCNS mov cx,ax mov al,es:P_CNS[bx] ; get the current VC No. mov bx,CCBLIST ; XIOS CCB$LIST mov di,ax ; Calculate the correct entry in shl di,1 ; the CCB$LIST which points to our mov di,es:word ptr [di+bx] ; CCB and then extract the correct mov al,CCB_PCNS[di] ; Physical Console Number. mov di,08[bp] ; Get the address of the PC_NUM mov word ptr [di],ax ; and save the Physical Console Number mov di,0000 ; From CCB 0 vc_d10: push di mov di,es:word ptr [di+bx] ; Get the CCB ad…ress cmp al,CCB_PCNS[di] ; Is this theY$ame physical console pop di ; Restore the original DI jz vc_d30 ; Yes then save info vc_d20: inc word ptr [si] ; *VC_BASE++ inc di ; Point to the next entry in inc di ; the CCB$LIST and try again loop vc_d10 jmp vc_exit vc_d30: mov si,06[bp] ; VC_NUM mov word ptr [si],0000 ; *VC_NUM = 0 vc_d40: push di mov di,es:word ptr [di+bx] ; Get the CCB address cmp al,CCB_PCNS[di] ; Is this the same physical console pop di ; Yes then increment the count jnz vc_exit ; and continue. inc word ptr [si] ; *VC_NUM++ inc di ; Point to the next entry in inc di ; the CCB$LIST and try again loop vc_d40 vc_exit: pop di pop si pop es pop bp ret endif _TEXT ENDS page ifdef CDOSTMP RSF_DYNAMIC equ 0001h ; create at boot time RSF_NONBANK equ 0002h ; allocate non-banked RSF_SPECIAL equ 0004h ; requires separate code RSF_ENVIRON equ 0008h ; requires large environment PD_SEG SEGMENT ; ; This is the standard process descriptor for a TMP. During ; the Concurrent P_CREATE function the contents of this process ; descriptor are copied to a full size descriptor inside SYSDAT. ; dw 0,0 ; link fields db PS_RUN ; status db 200 ; priority dw PF_SYS+PF_KEEP+PF_SPECIAL; flags db 'Tmp ' ; Process Name dw 40h/10h ; uda seg db 0,0 ; disk,user db 0,0 ; ldisk,luser dw 0FFFFh ; mem (Shared Code) dw 0,0 ; dvract,wait db 0,0 ; org,net dw 0 ; parent cns db 0,0 ; cns,abort db 0,0 ; cin,cout db 0,0 ; lst,sf3 db 0,0 ; sf4,sf5 dw 0,0 ; reserved dw 0,0 ; pret,scratch PD_SEG ENDS UDA_SEG SEGMENT uda_size dw ULEN,80h,0,0 ;0-7 dw 0,0,0,0 ;8-fh dw 0,0,0,0 ;10-17 dw 0,0,0,0 ;18-1f dw 0,0,0,0 ;20-27 dw 0,0,0,0 ;28-2f dw 0,0 ;30-33 uda_SP dw dataOFFSET uda_stack,0 ;34-37 dw 0,0,0,0 ;38-3f dw 0,0,0,0 ;40-47 dw 0,0,0,0 ;48-4f uda_CS dw 0 ;50-51 uda_DS dw 0 ;52-53 uda_ES dw 0 ;54-55 uda_SS dw 0 ;56-57 dw 0,0,0,0 ;58-5f insys db 1,0 ;60-61 dw 0,0,0 ;62-67 db (ULEN-6Eh)dup(0CCH) ; Initialise System Stack uda_stack dw codeOFFSET RSP_start ; Initial Offset dw ? ; Initial Segment (Unknown) dw ? ; Initial Flags (Unknown) UDA_SEG ENDS endif ifdef DOSPLUS _TEXT SEGMENT public _show_help public _put_resident_high public _get_config_env public _get_original_envsize ; BAP added this _show_help PROC NEAR ; ; VOID show_help(index) ; WORD index ; ;out 0fdh,al push bp mov bp,sp sub sp,4 ; require 2 WORD local variables mov ah,MS_M_ALLOC ; allocate memory for the help text mov bx,help_length ; bx = no. paragraphs mov cl,4 shr bx,cl int DOS_INT ; do it jnc show_help_05 jmp show_help_err0 ; exit on error show_help_05: mov -2[bp],ax ; save segment of allocated memory push ds mov ds,[low_seg] mov dx,dataOFFSET reload_file mov ax,(MS_X_OPEN*256)+0 ; Open the COMMAND.COM file int DOS_INT ; do it pop ds jc show_help_err1 ; exit on error mov bx,ax ; bx = file handle mov -4[bp],ax ; save handle for later xor cx,cx xor dx,dx cmp exe_file,TRUE ; if command.com is an EXE file take jne show_help_10 ; account of the EXE header add dx,200h show_help_10: add dx,total_length add dx,cgroup_length add dx,cend_length ; dx = file offset of help text mov ax,(MS_X_LSEEK*256)+0 ; seek to the right location int DOS_INT ; do it jc show_help_err2 ; exit on error push ds mov ds,-2[bp] ; ds = help text segment xor dx,dx ; dx = 0; mov ah,MS_X_READ ; read from COMMAND.COM file mov cx,help_length ; cx = no. bytes required int DOS_INT ; do it pop ds jc show_help_err2 ; exit on error cmp ax,0 ; zero bytes read means there's no je show_help_err2 ; help seg tagged to file. ifdef DLS call _get_msg_lang ; get a far pointer to msg_language var mov es,dx ; dx:ax = far pointer on return mov bx,ax ; mov bx,es:[bx] ; bx = msg_language shl bx,1 ; multiply bx by 2 else xor bx,bx ; bx = 0 endif mov es,-2[bp] ; ds = help text segment mov bx,es:[bx] ; bx -> help message offset table mov ax,4[bp] ; ax = message index shl ax,1 ; multiply by 2 add bx,ax ; bx -> offset of required message mov bx,es:[bx] ; bx -> message call write_string ; display the message show_help_err2: mov ah,MS_X_CLOSE ; close the file mov bx,-4[bp] ; bx = file handle int DOS_INT ; do it show_help_err1: mov ah,MS_M_FREE ; free the memory push es mov es,-2[bp] ; es = segment to free int DOS_INT ; do it pop es show_help_err0: add sp,4 pop bp ret write_string: ;extrn _str:byte ; es -> help segment ; bx -> message in help segment write_string_00: ;mov di,dataOFFSET _str ; di -> local buffer mov di,[heap_top] write_string_05: mov al,es:[bx] ; get a character cmp al,0ah ; check for NEWLINE... jnz write_string_10 ; ...jump if its not mov al,0dh ; replace LF with CR LF mov [di],al ; inc di ; mov al,0ah ; mov [di],al ; inc di ; mov al,0 ; mov [di],al ; terminate string call flush_buff ; display it inc bx ; jmp write_string_00 ; start again write_string_10: cmp al,0 ; check for NULL... jnz write_string_20 ; ...jump if its not mov [di],al ; store char call flush_buff ; display string jmp write_string_90 ; exit write_string_20: cmp al,'%' ; messages have doubled %'s because jnz write_string_30 ; were intended for printf originally. inc bx ; => skip next character. write_string_30: mov [di],al ; store char inc di ; inc bx ; jmp write_string_05 ; loop write_string_90: ret flush_buff: extrn strlen_:near extrn c_write_:near ;mov ax,dataOFFSET _str ; watcom C requites first param in AX mov ax,[heap_top] call strlen_ ; get length of string mov dx,ax ; watcom C requires second param in DX ;mov ax,dataOFFSET _str ; watcom C requires first param in AX mov ax,[heap_top] call c_write_ ; display the string ret _show_help ENDP try_high_memory PROC NEAR ; This function attempts to allocate some high memory at FFFF:E0 by modifying ; the HIMEM FREE CHAIN maintained by the BDOS. ; on entry: bx = no. paras required (preserved) ; on exit: Carry Set = unsuccessful ; otherwise ; ax = FFFF = segment of allocated memory ; HIMEM Free Chain is modified. push ds push es push bx mov cl,4 ; multiplying bx by 4... shl bx,cl ; ...gives memory required in bytes mov dx,bx ; put it in dx because bx is used for ; somthing else add dx,HISEG_OFF mov ax,4458h ; get private data area int DOS_INT ; do it mov si,es:10h[bx] ; ax = start of HIMEM Free Chain test si,si ; zero means there's no chain so... je thm_unsuccessful; ...return unsuccessful mov ax,0FFFFh ; mov ds,ax ; ds:si -> first free himem area cmp si,HISEG_OFF ; check if area starts at or below HISEG_OFF ja thm_unsuccessful; ...return unsuccessful mov cx,2[si] ; CX = length of free area cmp cx,dx ; check if length >= that required jb thm_unsuccessful; ...return unsuccessful mov ax,[si] ; assume we will use the entire block mov es:10h[bx],ax ; so unlink it from the chain mov di,HISEG_OFF ; generate HIMEM REGISTRATION link and mov ax,di ; update root in private data area xchg ax,es:14h[bx] mov [di],ax mov 2[di],dx ; remember how much we have used ; JBM commented this line out, I put it back in. (BAP) sub 2[di],di ; see above comment (JBM) sub cx,dx ; subtract amount used from actual size cmp cx,256 ; if less than 256 bytes are left jb thm_success ; then just forget about them mov si,di ; there is enough to be worth recyling add si,dx ; SI -> free block following allocation mov 2[si],cx ; it's this long mov ax,si xchg ax,es:10h[bx] ; put at head of HMA free chain mov [si],ax thm_success: mov ax,0ffffh ; return success clc ; jmp thm_exit ; thm_unsuccessful: stc ; return failure thm_exit: pop bx pop es pop ds ret try_high_memory ENDP _put_resident_high PROC NEAR ; ; VOID put_resident_high(param) ; WORD param; ; ; param = 0 Try HIGH memory, then UPPER memory ; param = 1 Only try HIGH memory ; param = 2 Only try UPPER memory ; PRH_PARAM equ word ptr 4[bp] push bp mov bp,sp push si push di mov ax,(MS_M_STRATEGY*256)+2 int DOS_INT ; get existing HMA link mov ah,0 push ax ; save it mov bx,total_length ; get size of resident code/data sub bx,(dataOFFSET hi_seg_start)-15 mov cl,4 ; subtract size of low memory stub shr bx,cl ; bx = block size in paras. cmp PRH_PARAM,2 ; skip the next bit if we're only je prh_upper ; looking at UPPER memory call try_high_memory ; first try to allocate high memory ; ie seg FFFF jnc prh_success ; jump if successful cmp PRH_PARAM,0 ; if we're only looking at HIGH memory jne prh_exit ; then we exit now prh_upper: push bx ; save length mov ax,(MS_M_STRATEGY*256)+1 mov bx,41h ; set memory strategy to best fit int DOS_INT ; upper only mov ax,(MS_M_STRATEGY*256)+3 mov bx,1 ; try to link in upper memory int DOS_INT pop bx ; recover length jc prh_exit ; no upper memory, stop now mov ah,MS_M_ALLOC ; allocate some memory int DOS_INT ; do it jc prh_exit ; no upper memory, stop now sub ax,HISEG_OFF/16 ; bias segment appropriately prh_success: ; AX = segment to relocate to ; mov es,ax ; es = new block mov si,dataOFFSET hi_seg_start ; start at R_TEXT segment mov di,si mov cx,total_length ; get size of resident code/data sub cx,si ; subtract size of low memory stub mov [reloc_seg],ax ; These values in the low memory mov [reloc_off],di ; stub are used by TaskMAX to find mov [reloc_size],cx ; the relocated code/data mov [data_seg],ax ; update data_seg variable mov [exec_seg],ax ; and some others... mov [readline_seg],ax mov [func4b_seg],ax mov [int2E_seg],ax mov ds:word ptr [control_break_entry+3],ax mov ds:word ptr [crit_error_entry+3],ax add si,4 ; take account of HIMEM add di,4 ; REGISTRATION link sub cx,4 rep movsb ; move'em mov bx,ds sub bx,[__psp] ; ax = difference between psp and data add bx,HISEG_OFF/16 ; add size of bit we leave behind mov es,[__psp] ; es -> old location mov ds,ax ; this is the new data seg mov ss,ax ; and also the new stack seg mov ah,MS_M_SETBLOCK ; modify old segment size int DOS_INT ; do it prh_exit: pop bx ; recover upper memory link mov ax,(MS_M_STRATEGY*256)+3 int DOS_INT ; restore to original state mov ax,(MS_M_STRATEGY*256)+1 xor bx,bx ; set memory strategy to first fit int DOS_INT pop di pop si pop bp ret _put_resident_high ENDP _get_config_env PROC NEAR ; BYTE FAR *get_config_env(); ; This function returns a pointer to the start of the config.sys ; environment. mov ax,4458h ; get pointer to private data int DOS_INT ; do it mov ax,0 ; es:bx -> private data jc get_cfg_env10 xchg ax,es:18[bx] ; ax = segment of config environment get_cfg_env10: mov bx,ax ; return FARNULL (0000:0000) mov dx,ax xor ax,ax ret _get_config_env ENDP ; BAP - This routine finds the orignal COMMAND.COM's environment size. ; This may not be the best way of doing it .. ! ; It finds the PSP of the original COMMAND.COM by checking the PSP seg ; against the parent PSP seg. If they are not the same, it repeats for ; the parent. When they are the same, it has found the original COMMAND ; and finds the environment size. _get_original_envsize PROC NEAR push bp mov bp,sp push si push di push es push cx mov ah,51h int DOS_INT ; get current PSP seg try_next: mov es,bx mov cx,bx ; move into CX mov bx,0 mov ax,es:16h[bx] ; get parent PSP seg in ax cmp ax,cx ; are they the same ? je got_org_psp ; yes - found COMMAND.COM PSP mov bx,ax ; else make this current seg and jmp try_next ; try again got_org_psp: mov es,ax ; ES = COMMAND.COM PSP seg mov bx,0 mov ax,es:2ch[bx] ; get env seg in ax cmp ax,0 ; seg = 0000 ? je bomb_out ; yes - forget it dec ax ; AX:0000 points to memory descriptor mov es,ax mov ax,es:3[bx] ; find length of seg ( in paras) mov cl,4 shl ax,cl ; convert to bytes bomb_out: mov bx,ax mov dx,ax pop cx pop es pop di pop si pop bp ret _get_original_envsize ENDP _TEXT ENDS endif ifndef CDOSTMP R_TEXT SEGMENT extrn _out_pipe:byte extrn _kbdbuf:byte R_TEXT ENDS _TEXT SEGMENT public _get_reload_file _get_reload_file: ; copy reload_file to heap push ds push es push si push di ;mov ax,0e40h ;int 10h mov ax,ds mov es,ax mov ds,[low_seg] mov di,[heap_top] mov si,offset reload_file cld grf_loop: lodsb stosb cmp al,0 jnz grf_loop pop di pop si pop es pop ds ret public _set_reload_file _set_reload_file: ; copy string on heap to reload file push es push si push di ;mov ax,0e40h ;int 10h mov es,[low_seg] mov si,[heap_top] mov di,offset reload_file cld srf_loop: lodsb stosb cmp al,20h ; BAP from here jne srf_brian ; if AL = 20h, poke a 00 xor al,al ; in instead, to terminate dec di ; the file. COMSPEC can then have stosb ; switches, but reload_file is srf_brian: ; just the file name. cmp al,0 jnz srf_loop pop di pop si pop es ret public _get_out_pipe _get_out_pipe: ; copy out_pipe filename from low_seg to data seg push ds push es push si push di ;mov ax,0e40h ;int 10h mov ax,ds mov es,ax mov di,offset DGROUP:_out_pipe mov ds,[low_seg] mov si,offset out_pipe mov cx,8 cld rep movsb pop di pop si pop es pop ds ret public _docmd_int2f ; ; BOOLEAN docmd_int2f(BYTE *cmdline, BYTE *cmd, UWORD count); ; ; cmdline db max, actual, 'COMMAND LINE', CR ; cmd db length, 'COMMAND', CR ; count db remaining length of tail, FF/00 internal/external flag ; ; _docmd_int2f: push bp mov bp,sp push bx push si mov bx,4[bp] ; bx -> original command line mov si,6[bp] ; si -> upper cased command mov cx,8[bp] mov dx,0ffffh mov ax,0AE00h int 2fh test al,al jz docmd_int2f_exit mov bx,4[bp] mov si,6[bp] mov cx,8[bp] mov dx,0ffffh mov ax,0AE01h int 2fh mov al,1 docmd_int2f_exit: cbw ; return true if handled pop si pop bx pop bp mov bx,ax mov dx,ax ret _TEXT ENDS endif end ; start address