--- /dev/null
+;=======================================================\r
+;=== UTILS.ASM - Asm Utilities for QuickBasic/BC7 ===\r
+;=======================================================\r
+\r
+ PAGE 255, 132\r
+\r
+ .MODEL Medium\r
+ .286\r
+\r
+ ; ==== MACROS ====\r
+\r
+ ; macros to PUSH and POP multiple registers\r
+\r
+PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8\r
+ IFNB <R1>\r
+ push R1 ; Save R1\r
+ PUSHx R2, R3, R4, R5, R6, R7, R8\r
+ ENDIF\r
+ENDM\r
+\r
+POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8\r
+ IFNB <R1>\r
+ pop R1 ; Restore R1\r
+ POPx R2, R3, R4, R5, R6, R7, R8\r
+ ENDIF\r
+ENDM\r
+\r
+ ; Macro to Clear a Register to 0\r
+\r
+CLR MACRO Register\r
+ xor Register, Register ; Set Register = 0\r
+ENDM\r
+\r
+ ; Macros to Decrement Counter & Jump on Condition\r
+\r
+LOOPx MACRO Register, Destination\r
+ dec Register ; Counter--\r
+ jnz Destination ; Jump if not 0\r
+ENDM\r
+\r
+LOOPjz MACRO Register, Destination\r
+ dec Register ; Counter--\r
+ jz Destination ; Jump if 0\r
+ENDM\r
+\r
+\r
+ ; ==== General Constants ====\r
+\r
+ False EQU 0\r
+ True EQU -1\r
+ nil EQU 0\r
+\r
+ b EQU BYTE PTR\r
+ w EQU WORD PTR\r
+ d EQU DWORD PTR\r
+ o EQU OFFSET\r
+ f EQU FAR PTR\r
+ s EQU SHORT\r
+ ?x4 EQU <?,?,?,?>\r
+ ?x3 EQU <?,?,?>\r
+\r
+\r
+IFDEF FARSTRINGS\r
+\r
+ EXTRN stringaddress:far\r
+ EXTRN stringlength:far\r
+\r
+ENDIF\r
+\r
+\r
+ .Data\r
+\r
+ EVEN\r
+\r
+RND_Seed DW 7397, 29447, 802\r
+RND_Mult DW 179, 183, 182\r
+RND_ModV DW 32771, 32779, 32783\r
+\r
+CR_LF DB 13, 10 ; the CRLF data\r
+\r
+ .Code\r
+\r
+;=================\r
+;DOS_PRINT (Text$)\r
+;=================\r
+;\r
+; Prints Text Directly to DOS console w/ CR/LF\r
+;\r
+\r
+ PUBLIC DOS_PRINT\r
+\r
+DP_Stack STRUC\r
+ DW ?x4 ; DI, SI, DS, BP\r
+ DD ? ; Caller\r
+ DP_Text DW ? ; Address of Text$ Descriptor\r
+DP_Stack ENDS\r
+\r
+\r
+DOS_PRINT PROC FAR\r
+\r
+ PUSHx BP, DS, SI, DI ; Preserve Important Registers\r
+ mov BP, SP ; Set up Stack Frame\r
+\r
+ mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor\r
+\r
+IFDEF FARSTRINGS\r
+ push SI ; Push Addr of BC7 Decriptor Ptr\r
+ call stringaddress ; Get Address + Len of string!!!\r
+ ; DX:AX = Addr CX = Len\r
+ mov DS, DX ; DS = DX = Segment of string\r
+ mov DX, AX ; DX = AX = Offset of String\r
+ELSE\r
+ mov CX, [SI] ; put its length into CX\r
+ mov DX, [SI+02] ; now DS:DX points to the String\r
+ENDIF\r
+\r
+ jcxz @No_Print ; Don't Print if empty\r
+\r
+ mov BX, 1 ; 1= DOS Handle for Display\r
+ mov AH, 40h ; Write Text Function\r
+ int 21h ; Call DOS to do it\r
+\r
+@No_Print:\r
+ mov AX, SEG DGROUP ; Restore DGroup\r
+ mov DS, AX\r
+\r
+ mov DX, o CR_LF ; Get Addr of CR/LF pair\r
+ mov CX, 2 ; 2 Characters to Write \r
+ mov BX, 1 ; 1= DOS Handle for Display\r
+\r
+ mov AH, 40h ; Write Text Function\r
+ int 21h ; Call DOS to do it\r
+\r
+ cld ; Reset Direction Flag \r
+ POPx DI, SI, DS, BP ; Restore Saved Registers\r
+ ret 2 ; Exit & Clean Up Stack\r
+\r
+DOS_PRINT ENDP\r
+\r
+\r
+;==================\r
+;DOS_PRINTS (Text$)\r
+;==================\r
+; \r
+; Print Text$ Directly to DOS console \r
+; without a trailing CR/LF\r
+;\r
+\r
+ PUBLIC DOS_PRINTS\r
+\r
+DOS_PRINTS PROC FAR\r
+\r
+ PUSHx BP, DS, SI, DI ; Preserve Important Registers\r
+ mov BP, SP ; Set up Stack Frame\r
+\r
+ mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor\r
+\r
+IFDEF FARSTRINGS\r
+ push SI ; Push Addr of BC7 Decriptor Ptr\r
+ call stringaddress ; Get Address + Len of string!!!\r
+ ; DX:AX = Addr CX = Len\r
+ mov DS, DX ; DS = DX = Segment of string\r
+ mov DX, AX ; DX = AX = Offset of String\r
+ELSE\r
+ mov CX, [SI] ; put its length into CX\r
+ mov DX, [SI+02] ; now DS:DX points to the String\r
+ENDIF\r
+\r
+ jcxz @DPS_Exit ; Don't Print if empty\r
+\r
+ mov BX, 1 ; 1= DOS Handle for Display\r
+ mov AH, 40h ; Write Text Function\r
+ int 21h ; Call DOS to do it\r
+\r
+@DPS_Exit:\r
+ cld ; Reset Direction Flag \r
+ POPx DI, SI, DS, BP ; Restore Saved Registers\r
+ ret 2 ; Exit & Clean Up Stack\r
+\r
+DOS_PRINTS ENDP\r
+\r
+\r
+;======================\r
+;SET_VIDEO_MODE (Mode%) \r
+;======================\r
+;\r
+; Sets the Video Mode through the BIOS\r
+;\r
+\r
+ PUBLIC SET_VIDEO_MODE\r
+\r
+SVM_Stack STRUC\r
+ DW ?x4 ; DI, SI, DS, BP\r
+ DD ? ; Caller\r
+ SVM_Mode DB ?,? ; Desired Video Mode\r
+SVM_Stack ENDS\r
+\r
+\r
+SET_VIDEO_MODE PROC FAR\r
+\r
+ PUSHx BP, DS, SI, DI ; Preserve Important Registers\r
+ mov BP, SP ; Set up Stack Frame\r
+\r
+ CLR AH ; Function 0\r
+ mov AL, [BP].SVM_Mode ; Get Mode #\r
+\r
+ int 10H ; Change Video Modes\r
+\r
+@SVM_Exit:\r
+ POPx DI, SI, DS, BP ; Restore Saved Registers\r
+ ret 2 ; Exit & Clean Up Stack\r
+\r
+SET_VIDEO_MODE ENDP\r
+\r
+\r
+;==============\r
+;SCAN_KEYBOARD%\r
+;==============\r
+;\r
+; Function to scan keyboard for a pressed key\r
+;\r
+\r
+ PUBLIC SCAN_KEYBOARD\r
+\r
+SCAN_KEYBOARD PROC FAR\r
+\r
+ PUSHx BP, DS, SI, DI ; Preserve Important Registers\r
+\r
+ mov AH, 01H ; Function #1\r
+ int 16H ; Call Keyboard Driver\r
+ jz @SK_NO_KEY ; Exit if Zero flag set\r
+\r
+ mov AH, 00H ; Remove Key from Buffer\r
+ int 16H ; Get Keycode in AX\r
+\r
+ or AL, AL ; Low Byte Set (Ascii?)\r
+ jz @SK_Exit ; if not, it's a F-Key\r
+\r
+ CLR AH ; Clear ScanCode if Ascii\r
+ jmp s @SK_Exit ; Return Key in AX\r
+\r
+@SK_NO_KEY:\r
+ CLR AX ; Return Nil (no Keypress)\r
+\r
+@SK_Exit:\r
+ cld ; Reset Direction Flag \r
+ POPx DI, SI, DS, BP ; Restore Saved Registers\r
+ ret ; Exit & Clean Up Stack\r
+\r
+SCAN_KEYBOARD ENDP\r
+\r
+\r
+;====================\r
+;RANDOM_INT (MaxInt%)\r
+;====================\r
+;\r
+; Returns a pseudo-random number in the range of (0.. MaxInt-1)\r
+;\r
+\r
+\r
+ PUBLIC RANDOM_INT\r
+\r
+RI_Stack STRUC\r
+ DW ? ; BP\r
+ DD ? ; Caller\r
+ RI_MaxVal DW ? ; Maximum Value to Return + 1\r
+RI_Stack ENDS\r
+\r
+\r
+RANDOM_INT PROC FAR\r
+\r
+ push BP ; Preserve Important Registers\r
+ mov BP, SP ; Set up Stack Frame\r
+\r
+ CLR BX ; BX is the data index\r
+ CLR CX ; CX is the accumulator\r
+\r
+REPT 3\r
+ mov AX, RND_Seed[BX] ; load the initial seed\r
+ mul RND_Mult[BX] ; multiply it\r
+ div RND_ModV[BX] ; and obtain the Mod value\r
+ mov RND_Seed[BX], DX ; save that for the next time\r
+\r
+ add CX, DX ; add it into the accumulator\r
+ inc BX\r
+ inc BX ; point to the next set of values\r
+ENDM\r
+\r
+ mov AX, CX ; AX = Random #\r
+ CLR DX ; DX = 0\r
+ div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder\r
+\r
+ mov AX, DX\r
+\r
+ pop BP ; Restore BP\r
+ ret 2 ; back to BASIC with AX holding the result\r
+\r
+RANDOM_INT ENDP\r
+\r
+\r
+;===========\r
+;INIT_RANDOM\r
+;===========\r
+;\r
+; Scrambles the psuedo-random number sequence\r
+; (XOR's the seed value with the timer)\r
+;\r
+\r
+ PUBLIC INIT_RANDOM\r
+\r
+INIT_RANDOM PROC FAR\r
+\r
+ clr AX ; Segment = 0000\r
+ mov ES, AX\r
+ mov AX, ES:[046Ch] ; Get Timer Lo Word\r
+\r
+ xor RND_Seed, AX ; Scramble 1st Seed\r
+\r
+ ret ; Exit & Clean Up Stack\r
+\r
+INIT_RANDOM ENDP\r
+\r
+\r
+;====================\r
+;INT_SQR (X%, Round%)\r
+;====================\r
+;\r
+; Returns the Integer Square Root of (X)\r
+; Round allows the return value to be rounded to the \r
+; nearest integer value by passing 0x80. Passing 0\r
+; return the Integer Portion only. The rounding amound is\r
+; a number from 0 to 1 multiplied by 256, thus \r
+; 0.5 * 0x100 = 0x80!\r
+;\r
+\r
+ISQ_Stack STRUC\r
+ DW ?,? ; BP, DI\r
+ DD ? ; Caller\r
+ ISQ_Round DW ? ; Amount to Round Result * 256\r
+ ISQ_X DW ? ; "X"\r
+ISQ_Stack ENDS\r
+\r
+ PUBLIC INT_SQR\r
+\r
+INT_SQR PROC FAR\r
+\r
+ PUSHx BP, DI ; Save BP\r
+ mov BP, SP ; Set up Stack Frame\r
+\r
+ xor AX, AX ; {xor eax,eax}\r
+ xor DX, DX ; {xor edx,edx}\r
+ mov DI, [BP].ISQ_X ; {mov edi,x}\r
+\r
+ mov CX, 16 ; {mov cx, 32}\r
+\r
+@ISQ_L:\r
+\r
+ shl DI, 1 ; {shl edi,1}\r
+ rcl DX, 1 ; {rcl edx,1}\r
+ shl DI, 1 ; {shl edi,1}\r
+ rcl DX, 1 ; {rcl edx,1}\r
+ shl AX, 1 ; {shl eax,1}\r
+ mov BX, AX ; {mov ebx,eax}\r
+ shl BX, 1 ; {shl ebx,1}\r
+ inc BX ; {inc ebx}\r
+ cmp DX, BX ; {cmp edx,ebx}\r
+ jl @ISQ_S\r
+\r
+ sub DX, BX ; {sub edx,ebx}\r
+ inc AX ; {inc eax}\r
+\r
+@ISQ_S: \r
+ loop @ISQ_L\r
+\r
+ add ax, [BP].ISQ_Round ; {add eax,$00008000} \r
+ ; {*round* result in hi word: ie. +0.5}\r
+ shr ax, 8 ; {shr eax,16} {to ax (result)}\r
+\r
+ POPx DI, BP ; Restore Registers \r
+ ret 4 ; Exit\r
+\r
+INT_SQR ENDP\r
+\r
+\r
+;============\r
+;TIMER_COUNT&\r
+;============\r
+;\r
+; Returns the current timer value as an integer/long integer\r
+;\r
+\r
+\r
+ PUBLIC TIMER_COUNT\r
+\r
+TIMER_COUNT PROC FAR\r
+\r
+ clr AX ; Segment = 0000\r
+ mov ES, AX ; use ES to get at data\r
+ mov AX, ES:[046Ch] ; Get Timer Lo Word\r
+ mov DX, ES:[046Eh] ; Get Timer Hi Word\r
+ ret ; Exit & Return value in DX:AX\r
+\r
+TIMER_COUNT ENDP\r
+\r
+\r
+ END\r