/*
   CyberRax Programming (C) 1999 - 2001
   TITLE:   JSTUFF.H--
   DESCRIPTION:   Some useful (and lots of useless) procedures
   LAST MODIFIED:  30 Nov 2001
   PROCEDURES DEFINED IN THIS FILE:
       : word COMMANDLINE()
       : byte PARAMETERS()
       : word COMMANDLINESTRING(number_of_parameter)
       : void FFLUSH(,filehandle)
       : void SETFTIME( ,filehandle,time,date)
       : void FNSPLIT2(fullname,drv,dir,name,ext)
       : void RESETDISK()
       : void DOSWRITESTRING(string)
       : void DWRITEHEX(word_value)
       : word STRINGUP(string)
       : word STRINGLEN(string)
       : word STRINGCMP(string1,string2)
       : word ADVANCEDSTRLEN(string)
       : char ADVANCEDSTRCMP(string1,string2)
       : word INSTCHECK(pointer_to_NAME,size_of_NAME)
       : void FINDPATH()
       : word COMPAREWORDFAR(buf1_offset,buf2_segment,buf2_offset,words)
       : word COMPAREBYTES(buffer1,buffer2,bytes)
       : word SETINT(interrupt_number,address)
       : byte GETINT(interrupt_number)
       : byte GETLASTPRESSEDKEY()
       : void RELOCATE(#LastVariable)    // see extended text below
       : void CHECK(MainNum, BigNum, SmallNum)
       : word ASCII2DEC(string,segment_of_string)
       : word ASCII2HEX(string,segment_of_string)
       : word JDISASM(address_of_command)
       : void RESTORE_DS()
       : void RESTORE_ES()
*/


? define OK 0
? define BAD 0
? define EMPTY 0

? define TOO_BIG 1
? define NO_NUMBER 2

? define NONE 0
? define ZERO 0
? define ONE 1
? define FIVE 5

? define START 0x100


// Declaring procedures
void CHECK (BX,CX,DX);
word COMPAREWORDFAR (SI,AX,DI,CX);
word COMMANDLINESTRING (CX);
void DOSWRITESTRING (DX);
word SETINT (AX,DX);
word JDISASM (SI);
void DWRITEHEX (BX);
char ADVANCEDSTRCMP (SI,BX);
word COMPAREBYTES (SI,DI,CX);
word STRINGLEN (DI);
word STRINGUP (SI);
word STRINGCMP (SI,DI);



: void CHECK ()   // ver. 2.02, optimized by 1 byte by M. Sheker
/* Checks if unsigned MainNum is smaller than unsigned BigNum and bigger than
     unsigned SmallNum.
   Input: BX = MainNum
          CX = BigNum
          DX = SmallNum
   Size: 29 bytes
*/
{
	AX=0;
	IF(BX>=CX){
		IF(NOTZEROFLAG)AH++;
		AH++;
	}
	IF(BX<=DX){
		IF(NOTZEROFLAG)AX++;
		AX++;
	}
}
/* Returns: BX, CX, DX = not changed
            AH = 0 if smaller, AH = 1 if equal, AH = 2 if bigger to BigNum
            AL = 0 if bigger, AL = 1 if equal, AL = 2 if smaller to SmallNum
*/



: void SETFTIME ()
/* Sets file date and time.
   Input: BX = filehandle
          CX = time
          DX = date
   Size: 5 bytes
*/
{
? DOSrequired 0x0200
	AX = 0x5701;
	$INT 0x21
}
/* Returns: NOTCARRYFLAG & AX = undefined on success
            CARRYFLAG set and errorcode in AX on error
*/



: void DOSWRITESTRING ()
/* Prints the string to the screen using DOS interrupt. A replacement for
     standard DOSWRITESTR() in DOS.H--, which is 2 bytes larger.
   Input: DS:DX = address of '$'-terminated string to print
   Size: 7 bytes
*/
{
? DOSrequired 0x0100
	AH = 9;
	$INT 0x21
}
/* Returns: AH = 0x0924(?)
*/



: void FINDPATH ()
/* Determines program's path, this means the coplete commandline like
     C:\..\FILE.EXT, INCLUDING the filename and extension. Analog to
     STARTPATH() in SYSTEM.H-- and ?argc directive.
   Input: nothing
   Size: 21 bytes
*/
{
? DOSrequired 0x0300
	SI = 0;
	CX = 0x8001;
	DS = DSWORD[0x2C];
	loop(CX){
		$lodsw
		IF(AX==1)GOTO MACRO_END;
		$DEC SI
	}
	SI = 0;
MACRO_END:
}
/* Returns:  CX = destroyed
             Pathname in DS:SI if success
             DS = destroyed!, SI= 0 if error occoured
*/



: word INSTCHECK ()
/* Checks whether the program with name "name" is already resident in
     memory.
   This is almost a 100% copy of instcheck() in TSR.H--. I converted and
     included it only because I needed this REG procedure in my REx-trick.
   NOTES: "name" points to a null-terminated string containing the name of
             the program. "len" is equal to strlen(name)+1 if strlen(name)<8,
             and 8 otherwise.
          This routine will only work correctly  under DOS 4+, unless your
             program writes its name in its Memory Control Block.  Moreover,
             it will fail to detect programs loaded high.
          Written by Jean-Marc Lasgouttes (Jean-Marc.Lasgouttes@inria.fr).
          Modified by SPHINX.
          Converted to REG procedure by CyberRax
  Input: AX = name
         BX = size of name
  Size: 64-66 bytes
*/
{
?DOSrequired 0x0200
	$PUSH BX,AX
	AH = 0x52;
	$INT 0x21;
#ifdef codesize
	BX--;
	BX--;
#else
	BX -= 2;
#endif
	ES = ESWORD[BX];
	do {
		$CLD
		$POP CX
		DI = 8;
		$POP SI
		SP -= 4;
		$REPZ $CMPSB
		IF (ZEROFLAG){
			DX = ES+1;
			AX = CS;
			IF (AX <> DX ) && (DX == ESWORD[1]){
#ifdef codesize
				AX><DX;
#else
				AX = DX;
#endif
				$JMP SHORT ENDED
			}
		}
		ES = ES+ESWORD[3]+1;
	} while (ESBYTE[0] <> 0x5A);
	AX = 0;
ENDED:
}
/* Returns: AX = segment of the first block in memory that:
               - has the name "name"
               - owns itself
               - is not the current program.
            AX = 0 if no such block exists.
            BX, CX, DX, SI, DI, ES, destroyed
*/



: word ANTIINSTCHECK ()
/* Replacement for INSTCHECK(), simulates a checking (and finding)
   Size: 71 bytes
*/
{
AX = 0x1234;
$XOR BX,AX
$XOR CX,AX
$XOR DX,AX
$XOR SI,AX
$XOR DI,AX
ES = 1233;
$DB 46 dup 0x90
#ifdef speed
   $NOP
#endif
}
/* Returns: AX, BX, CX, DX, SI, DI, ES, SP destroyed
*/



: word COMPAREWORDFAR ()   // ver. 1.03, optimized by 11 bytes by M. Sheker
/* Compares X words at DS:SI with words at ES:DI.
   Basically STRCMP(), just doesn't need the 0 byte as end-of-string identifier
   NOTES: If you need different DS then you have to set it before calling
            this procedure.
   Input: AX = segment of 2nd buffer
          CX = amount of words to compare
          SI = offset of 1st buffer
          DI = offset of 2nd buffer
   Size: 11 bytes
*/
{
	ES = AX;
	AX=0;
	$CLD
	$REP $CMPSW
	IF(NOTZEROFLAG)AX++;
}
/* Returns: AX = CX = 0 if success
            AX = 1, CX undetermined by failure
            SI, DI, ES destroyed
*/


: byte GETINT ()
/* Gets the address of wanted interrupt vector
   Basically GETINTVECT() but uses system call to perform the action.
   Input: AL = interrupt number
   Size: 7 bytes
*/
{
? DOSrequired 0x0200
	AH = 0x35;
	$INT 0x21
}
/* Returns: address of vector in ES:BX
*/


: word SETINT ()
/* Hooks the handler onto the interrupt
   Basically SETINTVECT() but let's DOS perform the action.
   NOTE:  DS must equal to the segment of the code which should be set to the
            vector (usually CS)
   Input: AL = interrupt number
          DS:DX = new interrupt handler
   Size: 10 bytes
*/
{
? DOSrequired 0x0100
	AH = 0x25;
	$INT 0x21
}
/* Returns: address of vector in ES:BX
*/


: word COMMANDLINE ()   // ver. 1.1, 100% '?pasecommandline' compatible
/* Sets the commandline up by removing spaces and TABs and determining the
     length.
   Almost a byte-match of code which is added when ?parsecommandline was set to
     TRUE.
     Differences: commandline is being set up at CS:81 (original place) not at
                    the end of program
                  treats TABs as spaces, thus removing them
                  returns the number of parameters
                  is not necessary for other 'commandline handling procedures'
     So like '?pasecommandline' and PARAMSTR() all in one
     This code was added because it can be used inside the program (and not only
        at the beginning).
   Input: nothing
   Size: 51-53 bytes
*/
{
	DX=0;
	ES=CS;
	DI = 0x81;
	SI = DI;
	BL = 1;
	$LODSB
	WHILE(AL!=0xD){
		WHILE(AL<=0x20)$LODSB
		do{
			$STOSB
			$LODSB
			DL+=BL;
			BX=0;
		}while(AL>0x20);
		ESBYTE[DI]=BL;
		BL = 1;
		DI++;
	}
	AX=0;
	$STOSW
	DSBYTE[0x80] = DL;
	AX = DX;
}
/* Returns: AX = DX = number of parameters, BX = 0 if any parameters
            AX = DX = 0, BX = 1 if no parameters
            SI, DI undetermined
            ES = CS
*/



: byte PARAMETERS ()
/* Returns the number of parameters on commandline
   The same as PARAMSTR(), just doesn't need '?pasecommandline'
   NOTE: DS must equal to segment of PSP (usually CS)
   Input: nothing
   Size: 4 bytes
*/
{
	AL=DSBYTE[0x80];
}



: word COMMANDLINESTRING ()
/* Returns the address of parameter
   The same as PARAMSTR(x), just doesn't need '?parsecommandline TRUE'
   NOTE: DS must equal to CS
   Input: AX = number of parameter, starting with 0
   Size: 22-23 bytes
*/
{
	BX = 0x81;
	LOOPNZ(CX){
		IF(DSBYTE[BX]==0)BREAK;
		do{
			BX++;
		}while(DSBYTE[BX]!=0);
		BX++;
	}
	AX = BX;
}
/* Returns: AX = BX = address of parameter string
                      (so COMMANDLINESTRING(0) will return 0x81)
            CX = 0
*/



: void RELOCATE ()   // ver. 1.11, uses advantages of C-- 0.235
/* Relocates the program from CS:100h to CS:40h saving so 192 bytes. This is
     mainly ment for TSRs to make them smaller.
   NOTES: Thanks to this procedure the program might overwrite among other
            things the original commandline at CS:81h (if the program is big
            enough and most likely it is), so the parameters should be
            examined first (if anything is needed from them) if
            ?parsecommandline or COMMANDLINE() is used
          if you use the segment address given by the TSR for FREEMEM() then
            you have to add 0Ch (12) to it (else FREEMEM() will fail)
         by KEEP() you better substract 0xC0 from the end-address to get
           the effect of RELOCATE()
   Input: AX = address of LastVariable
            For that add line
                         byte LastVariable;
            as the LAST line of program and then call this procedure with
            the address of this parameter (like  @RELOCATE(#LastVariable); )
   Size: 49 bytes
*/
{
CX = AX - 0x100;
AX += 16;
SP >< AX;
$PUSH 0xCB50,0x1E58,0xC08E,0xD88E,0x0CE8,0x83D8,0x8CA4,0xF3FC

/***
The above code pushes the following into the end of program:

	$CLD
	$REP $MOVSB
	AX = DS;
	AX -= 0xC;
	DS = AX;
	ES = AX;
	$POP AX
	$PUSH DS
	$PUSH AX
	$RETF
***/

AX >< SP;
SI = 0x100;
DI = 0x40;
$CALL AX;
}
/* Returns: CS = DS = ES = new (relocated) segment
            BX = IP
            DI = SI = destroyed
            CX = 0
*/



: void RESTORE_DS ()
/* Sets DS to CS
   NOTE: Use as a macro (by calling @RESTORE_DS();)
   Input: nothing
   Size: 2 bytes
*/
{
	$PUSH CS
	$POP DS
}
/* Returns: DS = CS
*/



: void RESTORE_ES ()
/* Sets ES to CS
   NOTE:  Use as a macro (by calling @RESTORE_ES();)
   Input: nothing
   Size: 2 bytes
*/
{
	$PUSH CS
	$POP ES
}
/* Returns: ES = CS
*/


: word ADVANCEDSTRLEN ()   // ver. 1.01, returns type of terminator
/* Determines the length of a string terminating with 0 or ','
   Differs from standard STRLEN() by accepting ',' also as terminator
   Input: AX = string
   Size: 25 bytes
*/
{
	DX=0;
	BX = AX;
	BX--;

LENGTH_CHECK_LOOP:

	BX++;

	$CMP DSBYTE[BX],0
	$JE ASL_END
	$CMP DSBYTE[BX],','
	$JE ASL_ENDING

	$JMP SHORT LENGTH_CHECK_LOOP

ASL_ENDING:
	DX++;

ASL_END:
	BX -= AX;
	AX >< BX;
}
/* RETURNS:  AX = length of string, not including 0 or ',' terminator.
             BX = start adress string
             DX = type of terminator: 0 - '0'
                                      1 - ','
*/



: byte GETLASTPRESSEDKEY ()
/* Gets the keycode of last pressed key from keyboard port 60h
   Input: nothing
   Size: 2 bytes
*/
{
$IN AL,0x60
}
/* Returns: AL = keycode
*/



: word JDISASM()
/* Determines the size of given command in machine language. Mainly ment for
    dissembling/debugging purposes
   NOTES: !!!  possibly faulty, use on your own risk  !!!
          DS must equal to the segment of opcode
   Input: SI = address of command
   Size: 711 bytes
*/
{
$JMP SHORT THA_BEGIN

RIDA:
IF (DSBYTE[SI + 1] < 0x40)
   {
   BL = DSBYTE[SI + 1];
   DL = BL & 0b110;
   DH = BL & 0b1110;
   IF (DL == 0x6) || (DL == 0xE)
      AL = 4;
   ELSE
      AL = 2;
   }
IF (DSBYTE[SI + 1] > 0x3F) && (DSBYTE[SI + 1] < 0x80)
   AL = 3;
IF (DSBYTE[SI + 1] > 0x7F) && (DSBYTE[SI + 1] < 0xC0)
   AL = 4;
IF (DSBYTE[SI + 1] > 0xBF)
   AL = 2;
$RET


THA_BEGIN:

AX = 0;
BX = 0;
DX = 0;

AH = DSBYTE[SI];

IF (AH < 4)
   $CALL RIDA

IF (AH == 4) || (AH == 0xC) || (AH == 14h) || (AH == 1Ch) || (AH == 24h) || (AH == 2Ch) || (AH == 0x34) || (AH == 3Ch) || (AH == 6Ah) || (AH == 0A8h) || (AH == 0CDh) || (AH == 0D4h) || (AH == 0D5h) || (AH == 0EBh)
   AL = 2;

IF (AH > 6Fh) && (AH < 80h)
   AL = 2;
IF (AH > 0AFh) && (AH < 0B8h)
   AL = 2;
IF (AH > 0DFh) && (AH < 0E8h)
   AL = 2;

IF (AH == 5) || (AH == 0Dh) || (AH == 15h)|| (AH == 1Dh)|| (AH == 25h) || (AH == 2Dh) || (AH == 35h) || (AH == 3Dh) || (AH == 68h) || (AH == 0A9h) || (AH == 0C2h) || (AH == 0C9h) || (AH == 0CAh) || (AH == 0E8h) || (AH == 0E9h) 
   AL = 3;

IF (AH > 9Fh) && (AH < 0A4h)
   AL = 3;
IF (AH > 0B7h) && (AH < 0C0h)
   AL = 3;

IF (AH == 6) || (AH == 7) || (AH == 0Eh) || (AH == 16h) || (AH == 17h) || (AH == 1Eh)|| (AH == 1Fh) || (AH == 27h) || (AH == 2Eh) || (AH == 2Fh) || (AH == 37h) || (AH == 3Fh) || (AH == 0C3h) || (AH == 0CBh) || (AH == 0CCh) || (AH == 0D6h) || (AH == 0D7h)
   AL = 1;

IF (AH > 3Fh ) && (AH < 0x62)
   AL = 1;
IF (AH > 6Bh ) && (AH < 0x70)
   AL = 1;
IF (AH > 8Fh ) && (AH < 0x9A)
   AL = 1;
IF (AH > 9Ah ) && (AH < 0xA0)
   AL = 1;
IF (AH > 0A3h ) && (AH < 0xA8)
   AL = 1;
IF (AH > 0A9h ) && (AH < 0xB0)
   AL = 1;
IF (AH > 0EBh ) && (AH < 0xF6)
   AL = 1;
IF (AH > 0F7h ) && (AH < 0xFE)
   AL = 1;

IF (AH == 26h) || (AH == 2Eh) || (AH == 36h) || (AH == 3Eh) || (AH == 64h) || (AH == 65h) || (AH == 0CEh) || (AH == 0CFh) 
   {
   AL = 1;
   BH = 1;
   }

IF (AH > 7) && (AH < 0xC)
   $CALL RIDA;
IF (AH > 0Fh) && (AH < 0x14)
   $CALL RIDA;
IF (AH > 17h) && (AH < 0x1C)
   $CALL RIDA;
IF (AH > 1Fh) && (AH < 0x24)
   $CALL RIDA;
IF (AH > 27h) && (AH < 0x2C)
   $CALL RIDA;
IF (AH > 2Fh) && (AH < 0x34)
   $CALL RIDA;
IF (AH > 37h) && (AH < 0x3C)
   $CALL RIDA;
IF (AH > 83h) && (AH < 0x8E)
   $CALL RIDA;
IF (AH > 0D7h) && (AH < 0xE0)
   $CALL RIDA;
IF (AH == 62h) || (AH == 0x63) || (AH == 0C4h) || (AH == 0xC5)
   $CALL RIDA;
IF (AH == 69h)
   {
   $CALL RIDA;
   AL += 2;
   }
IF (AH == 6Bh)
   {
   $CALL RIDA;
   AL++;
   }

IF (AH == 0C8h)
   AL = 4;

IF (AH == 9Ah)
   AL = 5;
IF (AH == 0EAh)
   AL = 5;
}
/* Returns: AH = opcode
            AL = 0 if opcode unsupported (currently 66h, 67h, 80h, 81h, 82h,
                   83h, 8Ch, 8Eh, 8Fh, C0h, C1h, C6h, C7h, D0h, D1h, D2h, D3h,
                   F6h, F7h, FEh and FFh)
            AL = size of command if opcode supported
            BX, DX undetermined
*/

/*************************************************************
Command      OpCode   IsPrefix   Needs2nd       2nd      Size

*ADD          0 - 3       -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4


*ADD          4           -           -                     2

*ADD          5           -           -                     3

*PUSH ES      6           -           -                     1

*POP ES       7           -           -                     1

*OR           8 - Bh      -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*OR           Ch          -           -                     2

*OR           Dh          -           -                     3

*PUSH CS      Eh          -           -                     1

*ADC          10h - 13h   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*ADC          14h         -           -                     2

*ADC          15h         -           -                     3

*PUSH SS      16h         -           -                     1

*POP SS       17h         -           -                     1

*SBB          18h - 1Bh   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*SBB          1Ch         -           -                     2

*SBB          1Dh         -           -                     3

*PUSH DS      1Eh         -           -                     1

*POP DS       1Fh         -           -                     1

*AND          20h - 23h   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*AND          24h         -           -                     2

*AND          25h         -           -                     3

*ES:          26h         +           -                     1

*DAA          27h         -           -                     1

*SUB          28h - 2Bh   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*SUB          2Ch         -           -                     2

*SUB          2Dh         -           -                     3

*CS:          2Eh         +           -                     1

*DAS          2Fh         -           -                     1

*XOR          30h - 33h   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*XOR          34h         -           -                     2

*XOR          35h         -           -                     3

*CS:          36h         +           -                     1

*AAA          37h         -           -                     1

*CMP          38h - 3Bh   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*CMP          3Ch         -           -                     2

*CMP          3Dh         -           -                     3

*DS:          3Eh         +           -                     1

*AAS          3Fh         -           -                     1

*INC x        40h - 47h   -           -                     1

*DEC x        48h - 4Fh   -           -                     1

*PUSH x       50h - 57h   -           -                     1

*POP x        58h - 5Fh   -           -                     1

*PUSHA        60h         -           -                     1

*POPA         61h         -           -                     1

*BOUND        62h         -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*ARPL         63h         -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*FS:          64h         +           -                     1

*GS:          65h         +           -                     1



32bit        66h         +           +      x5/xD < 4      6
                                            else undetermined


*PUSH x       68h         -           -                     3

*IMUL         69h         -           +         0 - 3Fh     4
                                              40h - 7Fh     5
                                              80h - BFh     6
                                              C0h - FFh     4
                                             x6/xE < 4x     6

*PUSH x       6Ah         -           -                     2

*IMUL         6Bh         -           +         0 - 3Fh     3
                                              40h - 7Fh     4
                                              80h - BFh     5
                                              C0h - FFh     3
                                             x6/xE < 4x     5

*INSB         6Ch         -           -                     1

*INS          6Dh         -           -                     1

*OUTSB        6Eh         -           -                     1

*OUTS         6Fh         -           -                     1

*Jxx          70h - 7Fh   -           -                     2


*TEST         84h - 85h   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*XCHG         86h - 87h   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*MOV          88h - 8Bh   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*LEA          8Dh         -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*NOP          90h         -           -                     1

*XCHG x       91h - 97h   -           -                     1

*CBW          98h         -           -                     1

*CWD          99h         -           -                     1

*CALL FAR     9Ah         -           -                     5

*WAIT         9Bh         -           -                     1

*PUSHF        9Ch         -           -                     1

*POPF         9Dh         -           -                     1

*SAFH         9Eh         -           -                     1

*LAFH         9Fh         -           -                     1

*MOV          A0h - A3h   -           -                     3

*MOVSB        A4h         -           -                     1

*MOVS         A5h         -           -                     1

*CMPSB        A6h         -           -                     1

*CMPS         A7h         -           -                     1

*TEST X       A8h         -           -                     2

*TEST X       A9h         -           -                     3

*STOSx        AAh - ABh   -           -                     1

*LODSx        ACh - ADh   -           -                     1

*SCASx        AEh - AFh   -           -                     1

*MOV x        B0h - B7h   -           -                     2

*MOV x        B8h - BFh   -           -                     3

*RET add      C2h         -           -                     3

*RET          C3h         -           -                     1

*LES          C4h         -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*LDS          C5h         -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*ENTER        C8h         -           -                     4

*LEAVE        C9h         -           -                     1

*RETF add     CAh         -           -                     3

*RETF         CBh         -           -                     1

*INT 3        CCh         -           -                     1

*INT x        CDh         -           -                     2

*INTO         CEh         -           -                     1

*IRET         CFh         -           -                     1

*AAM          D4h         -           -                     2

*AAD          D5h         -           -                     2

*SETALC       D6h         -           -                     1

*XLAT         D7h         -           -                     1

*Fx           D8h - DFh   -           +         0 - 3Fh     2
                                              40h - 7Fh     3
                                              80h - BFh     4
                                              C0h - FFh     2
                                             x6/xE < 4x     4

*LOOPx        E0h - E2h   -           -                     2

*JCXZ         E3h         -           -                     2

*IN           E4h - E5h   -           -                     2

*OUT          E6h - E7h   -           -                     2

*CALL NEAR    E8h         -           -                     3

*JMP NEAR     E9h         -           -                     3

*JMP FAR      EAh         -           -                     5

*JMP SHORT    EBh         -           -                     2

*IN           ECh - EDh   -           -                     1

*OUT          EEh - EFh   -           -                     1

*LOCK         F0h         +           -                     1

*SMI          F1h         -           -                     1

*REPx         F2h - F3h   +           -                     1

*HLT          F4h         -           -                     1

*CMC          F5h         -           -                     1

CLC          F8h         -           -                      1

STC          F9h         -           -                      1

CLI          FAh         -           -                      1

STI          FBh         -           -                      1

CLD          FCh         -           -                      1

STD          FDh         -           -                      1
***************************************************************/



: void DWRITEHEX()
/* Writes HEX number on screen using DOS interrupt
   Input: BX = word to print
   Size: 83 bytes
*/
{
AH=2;
DX=BX;
$SHR DX,12
IF(DL>=10)DL+='A'-10-'0';
DL+='0';
$INT 0x21
DX=BX;
$SHR DX,8
DL&=0x0F;
IF(DL>=10)DL+='A'-10-'0';
DL+='0';
$INT 0x21
DX=BX;
$SHR DX,4
DL&=0x0F;
IF(DL>=10)DL+='A'-10-'0';
DL+='0';
$INT 0x21
DL=BL;
DL&=0x0F;
IF(DL>=10)DL+='A'-10-'0';
DL+='0';
$INT 0x21
}
/* Returns: BX = the number
            AX, DX destroyed
*/



: char ADVANCEDSTRCMP()   // ver. 1.02, another bugfix
/* Compares two strings.
   Basically STRCMP() but accepts also ',' as terminator and doesn't determine
     if string1 is greater/smaller than string2
   Input: BX = string1
          SI = string2
   Size: 47 bytes
*/
{
AH = 0;
BX--;

TOP:
BX++;
$LODSB
$CMP DSBYTE[BX],AL
$JNE CHECK_TERMINATOR
$CMP AL,0
$JE HERE
$CMP AL,0x2C
$JE HERE
$JMP SHORT TOP

CHECK_TERMINATOR:
$CMP AL,0
$JE CHECK_BX
$CMP AL,','
$JNE CHANGE_AH

CHECK_BX:
$CMP DSBYTE[BX],0
$JE HERE
$CMP DSBYTE[BX],','
$JE HERE

CHANGE_AH:
AH++;

HERE:
AH >< AL;
}
/* Returns:  AL = 0 if the strings are equal
             AL = 1 if strings not equal
             BX,SI = undefined
*/



: word ASCII2DEC ()   // 2.0, shortened by 155(!) bytes
/* Converts ASCIIZ strings like "1234" to DEC numbers (1234d in this case) for
     furder processing
   Input: BX:AX = string
   Size: 80 bytes
*/
{
$PUSH DS
DS = BX;
$PUSH AX
ADVANCEDSTRLEN(AX);
IF (AX > 5)
   {
   $POP AX
   AX = TOO_BIG;
   GOTO A2D_END;
   }
$POP SI
DI = SI + AX;

AX = 0;
BX = 0;

A2D_LOOP:
$LODSB
IF (AL < 0x30) || (AL > 0x39)
   {
   AX = NO_NUMBER;
   GOTO A2D_END;
   }
AL -= 0x30;
BX += AX;

IF (SI < DI)
   {
   BX *= 10;
   GOTO A2D_LOOP;
   }
AX = 0;
A2D_END:
$POP DS
}
/* Returns: AX = 0 if number successfully converted
               BX = the number
            AX <> 0 if some error occoured
               AX = 1 if number over 5 characters (larger than 65535)
               AX = 2 if not dealing with a decimal number
               BX = undetermined
            DX, SI, DI destroyed
*/



: void FNSPLIT2()   // ver 1.01, supports slash as directory separarator
/* Splits the given filename (with path) into parts filling with them the
     given buffers.
   Basically FNSPLIT() in FILE.H-- but can handle forms like
     DIR\FILE.EXT and /FILE.EXT
   Input: AX = complete filename
          BX = buffer for drive
          CX = buffer for directory name
          DX = buffer for filename
          DI = buffer for extension
   Note: if you don't need some part (the extension for example) then '0' must
            be given instead of address
   Size: 111 - 117 bytes
*/
{
$PUSH SI,BX,CX,DX

?ifdef codesize
   DX >< AX;
?else
   DX = AX;
?endif

SI = DX;
ES = DS;

$CLD

do $LODSB
   while (AL <> 0);

SI--;
SI--;

$STD
BX = 0;
CX = 4;
loop(CX)
   {
   IF (SI < DX)
      GOTO NO_DOT_FOUND;
   $LODSB
   BX++;
   IF (AL == '.')
      GOTO DOT_FOUND;
   IF (AL == '\\') || (AL == '/') || (AL == ':')
      {
      SI++;
      GOTO NO_DOT_FOUND;
      }
   }
NO_DOT_FOUND:
IF (DI == 0)
   GOTO SCAN_FILENAME;
AL = 0;
$STOSB
GOTO SCAN_FILENAME2;

DOT_FOUND:
IF (DI == 0)
   GOTO SCAN_FILENAME;
$PUSH SI
SI++;
$CLD
CX = BX;
$REP $MOVSB
AL = 0;
$STOSB
$POP SI


SCAN_FILENAME:
BX = 0;
SCAN_FILENAME2:
$POP DI

$STD
CX = 9;
loop(CX)
   {
   IF (SI < DX)
      GOTO NO_SLASH_FOUND;
   $LODSB
   IF (AL == '\\') || (AL == '/') || (AL == ':')
      GOTO SLASH_FOUND;
   BX++;
   }
NO_SLASH_FOUND:
IF (DI == 0)
   GOTO SCAN_DIR;
AL = 0;
$STOSB
GOTO SCAN_DIR;

SLASH_FOUND:
SI++;
IF (DI == 0)
   GOTO SCAN_DIR;
$PUSH SI
SI++;
$CLD
CX = BX;
$REP $MOVSB
AL = 0;
$STOSB
$POP SI


SCAN_DIR:
$POP DI
$STD
BX = 0;
CX = 68;
loop(CX)
   {
   IF (SI < DX)
      GOTO NO_COLON_FOUND;
   $LODSB
   IF (AL == ':')
      GOTO COLON_FOUND;
   BX++;
   }
NO_COLON_FOUND:
IF (DI == 0)
   GOTO SCAN_DRIVE;
SI++;
$CLD
CX = BX;
$REP $MOVSB
AL = 0;
$STOSB
GOTO SCAN_DRIVE;

COLON_FOUND:
SI++;
IF (DI == 0)
   GOTO SCAN_DRIVE;
$PUSH SI
SI++;
$CLD
CX = BX;
$REP $MOVSB
AL = 0;
$STOSB
$POP SI

SCAN_DRIVE:
$POP DI
IF (DI == 0)
   GOTO END_SCAN;
$STD
$LODSB
$CLD
IF (AL <> ':')
   GOTO DRIVE_END;
$MOVSW
DRIVE_END:
AL = 0;
$STOSB


END_SCAN:
$POP SI
}
/* Returns: DX = address of full filename
            AX, BX, CX, DI destroyed
            SI is preserved
            Buffers filled:
                drivebuffer:       x:,0             <eg "D:">
                directory buffer:  \xxx.xxx\xxx\,0  <eg "\DIR\DIR\">
                filename buffer:   xxxxx,0          <eg "FILE">
                extension buffer:  .xxx,0           <eg ".EXT">
*/



: word COMPAREBYTES ()
/* Compares X bytes at DS:SI with words at DS:DI.
   Basically STRCMP(), just doesn't need the 0 byte as end-of-string identifier
   NOTE: If you need different DS then you have to set it before calling
           this procedure.
   Input: SI = first array
          DI = second array
          CX = length of array
   Size: 19 bytes
*/
{
$PUSH DS
$POP ES
AX = 0;
$CLD
$REP $CMPSB
IF (NOTZEROFLAG)
   AX++;
}
/* Returns: AX = CX = 0 if success
            AX = 1, CX undetermined by failure
            SI, DI, destroyed
            ES = DS
*/



: void FFLUSH ()
/* Orders DOS to write all data still in it's buffers to disk
   Input: BX = filehandle
   Size: 9 bytes
*/
{
AH = 0x68;
$INT 0x21
}
/* Returns: NOTCARRYFLAG if successful
            CARRYFLAG set by error
               AX = DOS errorcode
*/



: void RESETDISK ()
/* Writes all modified disk buffers to disk
   Input: nothing
   Size: 4 bytes
*/
{
AH = 0xD;
$INT 0x21
}
/* Returns: nothing
*/



: word STRINGLEN ()
/* Determines the length of a 0 terminating string
   Basically STRLEN() in STRING.H-- but uses DOS 3.0+ internal code
   NOTE: !!!   not supported by all DOS-clones   !!!
   Input: ES:DI = ASCIIZ string
   Size: 10 - 11 bytes
*/
{
? DOSrequired 0x0300

AX = 0x1212;
$INT 0x2F
CX--;

?ifdef codesize
   AX >< CX;
?else
   AX = CX;
?endif
}
/* Returns: AX = length of string, not including 0 terminator
            CX destroyed
*/



: void STRINGUP ()
/* Converts a 0 terminating string to uppercase
   Basically STR_UP() in STRING.H-- but uses DOS 3.0+ internal code.
   NOTES: DS must equal to ES
          !!!  '/' in string are changed to '\'   !!!
          !!!   not supported by all DOS-clones   !!!
   Input: DS:SI = ASCIZ string
   Size: 10 bytes
*/
{
? DOSrequired 0x0300

DI = SI;
AX = 0x1211;
$INT 0x2F
}
/* Returns: DI = SI
*/



: word STRINGCMP ()
/* Compares two 0 terminated strings
   Basically STRCMP() in STRING.H-- but uses DOS 3.0+ internal code.
   NOTE: DS must equal to ES
         !!!   not supported by all DOS-clones   !!!
   Input: DS:SI = first ASCIZ string
          ES:DI = second ASCIZ string
   Size: 17 - 18 bytes
*/
{
? DOSrequired 0x0300

AX = 0x121E;
BX = 0;
$INT 0x2F

IF (NOTZEROFLAG)
   BX++;

?ifdef codesize
   AX >< BX;
?else
   AX = BX;
?endif
}
/* Returns: AX = 0 if strings are equal
            AX = 1 if not equal
            BX, SI, DI undetermined
*/



: word ASCII2HEX ()
/* Converts ASCIIZ strings like "1234" to HEX numbers (1234h in this case) for
     furder processing
   Input: BX:AX = string
   Size: 94 bytes
*/
{
$PUSH DS
DS = BX;
$PUSH AX
ADVANCEDSTRLEN(AX);
IF (AX > 4)
   {
   $POP AX
   AX = TOO_BIG;
   GOTO A2H_END;
   }
$POP SI
DI = SI + AX;

AX = 0;
BX = 0;

A2H_LOOP:
$LODSB
IF (AL >= 0x30) && (AL <= 0x39)
   {
   AL -= 0x30;
   GOTO NUM_OK;
   }
IF (AL >= 0x41) && (AL <= 0x46)
   {
   AL -= 0x37;
   GOTO NUM_OK;
   }

AX = NO_NUMBER;
GOTO A2H_END;

NUM_OK:
BX += AX;

IF (SI < DI)
   {
   BX <<= 4;
   GOTO A2H_LOOP;
   }
AX = 0;
A2H_END:
$POP DS
}
/* Returns: AX = 0 if number successfully converted
               BX = the number
            AX <> 0 if some error occoured
               AX = 1 if number over 4 characters (larger than FFFFh)
               AX = 2 if not dealing with a hex number
               BX = undetermined
            DX, SI, DI destroyed
*/



/* end of JSTUFF.H-- */
