1 ;=======================================================
\r
2 ;=== UTILS.ASM - Asm Utilities for QuickBasic/BC7 ===
\r
3 ;=======================================================
\r
12 ; macros to PUSH and POP multiple registers
\r
14 PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8
\r
17 PUSHx R2, R3, R4, R5, R6, R7, R8
\r
21 POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8
\r
24 POPx R2, R3, R4, R5, R6, R7, R8
\r
28 ; Macro to Clear a Register to 0
\r
31 xor Register, Register ; Set Register = 0
\r
34 ; Macros to Decrement Counter & Jump on Condition
\r
36 LOOPx MACRO Register, Destination
\r
37 dec Register ; Counter--
\r
38 jnz Destination ; Jump if not 0
\r
41 LOOPjz MACRO Register, Destination
\r
42 dec Register ; Counter--
\r
43 jz Destination ; Jump if 0
\r
47 ; ==== General Constants ====
\r
65 EXTRN stringaddress:far
\r
66 EXTRN stringlength:far
\r
75 RND_Seed DW 7397, 29447, 802
\r
76 RND_Mult DW 179, 183, 182
\r
77 RND_ModV DW 32771, 32779, 32783
\r
79 CR_LF DB 13, 10 ; the CRLF data
\r
87 ; Prints Text Directly to DOS console w/ CR/LF
\r
93 DW ?x4 ; DI, SI, DS, BP
\r
95 DP_Text DW ? ; Address of Text$ Descriptor
\r
101 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
102 mov BP, SP ; Set up Stack Frame
\r
104 mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor
\r
107 push SI ; Push Addr of BC7 Decriptor Ptr
\r
108 call stringaddress ; Get Address + Len of string!!!
\r
109 ; DX:AX = Addr CX = Len
\r
110 mov DS, DX ; DS = DX = Segment of string
\r
111 mov DX, AX ; DX = AX = Offset of String
\r
113 mov CX, [SI] ; put its length into CX
\r
114 mov DX, [SI+02] ; now DS:DX points to the String
\r
117 jcxz @No_Print ; Don't Print if empty
\r
119 mov BX, 1 ; 1= DOS Handle for Display
\r
120 mov AH, 40h ; Write Text Function
\r
121 int 21h ; Call DOS to do it
\r
124 mov AX, SEG DGROUP ; Restore DGroup
\r
127 mov DX, o CR_LF ; Get Addr of CR/LF pair
\r
128 mov CX, 2 ; 2 Characters to Write
\r
129 mov BX, 1 ; 1= DOS Handle for Display
\r
131 mov AH, 40h ; Write Text Function
\r
132 int 21h ; Call DOS to do it
\r
134 cld ; Reset Direction Flag
\r
135 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
136 ret 2 ; Exit & Clean Up Stack
\r
141 ;==================
\r
142 ;DOS_PRINTS (Text$)
\r
143 ;==================
\r
145 ; Print Text$ Directly to DOS console
\r
146 ; without a trailing CR/LF
\r
151 DOS_PRINTS PROC FAR
\r
153 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
154 mov BP, SP ; Set up Stack Frame
\r
156 mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor
\r
159 push SI ; Push Addr of BC7 Decriptor Ptr
\r
160 call stringaddress ; Get Address + Len of string!!!
\r
161 ; DX:AX = Addr CX = Len
\r
162 mov DS, DX ; DS = DX = Segment of string
\r
163 mov DX, AX ; DX = AX = Offset of String
\r
165 mov CX, [SI] ; put its length into CX
\r
166 mov DX, [SI+02] ; now DS:DX points to the String
\r
169 jcxz @DPS_Exit ; Don't Print if empty
\r
171 mov BX, 1 ; 1= DOS Handle for Display
\r
172 mov AH, 40h ; Write Text Function
\r
173 int 21h ; Call DOS to do it
\r
176 cld ; Reset Direction Flag
\r
177 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
178 ret 2 ; Exit & Clean Up Stack
\r
183 ;======================
\r
184 ;SET_VIDEO_MODE (Mode%)
\r
185 ;======================
\r
187 ; Sets the Video Mode through the BIOS
\r
190 PUBLIC SET_VIDEO_MODE
\r
193 DW ?x4 ; DI, SI, DS, BP
\r
195 SVM_Mode DB ?,? ; Desired Video Mode
\r
199 SET_VIDEO_MODE PROC FAR
\r
201 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
202 mov BP, SP ; Set up Stack Frame
\r
204 CLR AH ; Function 0
\r
205 mov AL, [BP].SVM_Mode ; Get Mode #
\r
207 int 10H ; Change Video Modes
\r
210 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
211 ret 2 ; Exit & Clean Up Stack
\r
213 SET_VIDEO_MODE ENDP
\r
220 ; Function to scan keyboard for a pressed key
\r
223 PUBLIC SCAN_KEYBOARD
\r
225 SCAN_KEYBOARD PROC FAR
\r
227 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
229 mov AH, 01H ; Function #1
\r
230 int 16H ; Call Keyboard Driver
\r
231 jz @SK_NO_KEY ; Exit if Zero flag set
\r
233 mov AH, 00H ; Remove Key from Buffer
\r
234 int 16H ; Get Keycode in AX
\r
236 or AL, AL ; Low Byte Set (Ascii?)
\r
237 jz @SK_Exit ; if not, it's a F-Key
\r
239 CLR AH ; Clear ScanCode if Ascii
\r
240 jmp s @SK_Exit ; Return Key in AX
\r
243 CLR AX ; Return Nil (no Keypress)
\r
246 cld ; Reset Direction Flag
\r
247 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
248 ret ; Exit & Clean Up Stack
\r
253 ;====================
\r
254 ;RANDOM_INT (MaxInt%)
\r
255 ;====================
\r
257 ; Returns a pseudo-random number in the range of (0.. MaxInt-1)
\r
266 RI_MaxVal DW ? ; Maximum Value to Return + 1
\r
270 RANDOM_INT PROC FAR
\r
272 push BP ; Preserve Important Registers
\r
273 mov BP, SP ; Set up Stack Frame
\r
275 CLR BX ; BX is the data index
\r
276 CLR CX ; CX is the accumulator
\r
279 mov AX, RND_Seed[BX] ; load the initial seed
\r
280 mul RND_Mult[BX] ; multiply it
\r
281 div RND_ModV[BX] ; and obtain the Mod value
\r
282 mov RND_Seed[BX], DX ; save that for the next time
\r
284 add CX, DX ; add it into the accumulator
\r
286 inc BX ; point to the next set of values
\r
289 mov AX, CX ; AX = Random #
\r
291 div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder
\r
295 pop BP ; Restore BP
\r
296 ret 2 ; back to BASIC with AX holding the result
\r
305 ; Scrambles the psuedo-random number sequence
\r
306 ; (XOR's the seed value with the timer)
\r
311 INIT_RANDOM PROC FAR
\r
313 clr AX ; Segment = 0000
\r
315 mov AX, ES:[046Ch] ; Get Timer Lo Word
\r
317 xor RND_Seed, AX ; Scramble 1st Seed
\r
319 ret ; Exit & Clean Up Stack
\r
324 ;====================
\r
325 ;INT_SQR (X%, Round%)
\r
326 ;====================
\r
328 ; Returns the Integer Square Root of (X)
\r
329 ; Round allows the return value to be rounded to the
\r
330 ; nearest integer value by passing 0x80. Passing 0
\r
331 ; return the Integer Portion only. The rounding amound is
\r
332 ; a number from 0 to 1 multiplied by 256, thus
\r
333 ; 0.5 * 0x100 = 0x80!
\r
339 ISQ_Round DW ? ; Amount to Round Result * 256
\r
347 PUSHx BP, DI ; Save BP
\r
348 mov BP, SP ; Set up Stack Frame
\r
350 xor AX, AX ; {xor eax,eax}
\r
351 xor DX, DX ; {xor edx,edx}
\r
352 mov DI, [BP].ISQ_X ; {mov edi,x}
\r
354 mov CX, 16 ; {mov cx, 32}
\r
358 shl DI, 1 ; {shl edi,1}
\r
359 rcl DX, 1 ; {rcl edx,1}
\r
360 shl DI, 1 ; {shl edi,1}
\r
361 rcl DX, 1 ; {rcl edx,1}
\r
362 shl AX, 1 ; {shl eax,1}
\r
363 mov BX, AX ; {mov ebx,eax}
\r
364 shl BX, 1 ; {shl ebx,1}
\r
366 cmp DX, BX ; {cmp edx,ebx}
\r
369 sub DX, BX ; {sub edx,ebx}
\r
375 add ax, [BP].ISQ_Round ; {add eax,$00008000}
\r
376 ; {*round* result in hi word: ie. +0.5}
\r
377 shr ax, 8 ; {shr eax,16} {to ax (result)}
\r
379 POPx DI, BP ; Restore Registers
\r
389 ; Returns the current timer value as an integer/long integer
\r
395 TIMER_COUNT PROC FAR
\r
397 clr AX ; Segment = 0000
\r
398 mov ES, AX ; use ES to get at data
\r
399 mov AX, ES:[046Ch] ; Get Timer Lo Word
\r
400 mov DX, ES:[046Eh] ; Get Timer Hi Word
\r
401 ret ; Exit & Return value in DX:AX
\r