1 ;=======================================================
\r
2 ;=== C_UTILS.ASM - Asm Utilities for C/C++ ===
\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
67 RND_Seed DW 7397, 29447, 802
\r
68 RND_Mult DW 179, 183, 182
\r
69 RND_ModV DW 32771, 32779, 32783
\r
71 CR_LF DB 13, 10 ; the CRLF data
\r
75 ;===========================================
\r
76 ;void far pascal dos_print (far char *Text)
\r
77 ;===========================================
\r
79 ; - Print Text Directly to DOS console w/ CR/LF
\r
85 DW ?x4 ; DI, SI, DS, BP
\r
87 DP_Text DD ? ; Far Address of Text to print
\r
93 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
94 mov BP, SP ; Set up Stack Frame
\r
96 lds DX, [BP].DP_Text ; Get Addr of Text$ descriptor
\r
98 ; Compute Length of string
\r
100 CLR CX ; Length = 0
\r
101 mov SI, DX ; DS:SI = String data
\r
105 cmp b [SI], 0 ; Null Byte found?
\r
106 je @@DP_Got_Len ; exit loop if so
\r
109 inc SI ; Point to next char
\r
110 jmp s @@DP_Scan_it ; check again...
\r
114 jcxz @No_Print ; Don't Print if empty
\r
116 mov BX, 1 ; 1= DOS Handle for Display
\r
117 mov AH, 40h ; Write Text Function
\r
118 int 21h ; Call DOS to do it
\r
121 mov AX, SEG DGROUP ; Restore DGroup
\r
124 mov DX, o CR_LF ; Get Addr of CR/LF pair
\r
125 mov CX, 2 ; 2 Characters to Write
\r
126 mov BX, 1 ; 1= DOS Handle for Display
\r
128 mov AH, 40h ; Write Text Function
\r
129 int 21h ; Call DOS to do it
\r
131 cld ; Reset Direction Flag
\r
132 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
133 ret 4 ; Exit & Clean Up Stack
\r
138 ;===========================================
\r
139 ;void far pascal dos_prints (char far *Text)
\r
140 ;===========================================
\r
142 ; Print Text Directly to DOS console
\r
143 ; without a trailing CR/LF
\r
148 DOS_PRINTS PROC FAR
\r
150 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
151 mov BP, SP ; Set up Stack Frame
\r
153 lds DX, [BP].DP_Text ; Get Addr of Text$ descriptor
\r
155 ; Compute Length of string
\r
157 CLR CX ; Length = 0
\r
158 mov SI, DX ; DS:SI = String data
\r
162 cmp b [SI], 0 ; Null Byte found?
\r
163 je @@DPS_Got_Len ; exit loop if so
\r
166 inc SI ; Point to next char
\r
167 jmp s @@DPS_Scan_it ; check again...
\r
171 jcxz @DPS_Exit ; Don't Print if empty
\r
173 mov BX, 1 ; 1= DOS Handle for Display
\r
174 mov AH, 40h ; Write Text Function
\r
175 int 21h ; Call DOS to do it
\r
178 cld ; Reset Direction Flag
\r
179 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
180 ret 2 ; Exit & Clean Up Stack
\r
185 ;=========================================
\r
186 ;void far pascal set_video_mode (int Mode)
\r
187 ;=========================================
\r
189 ; Sets the Video Mode through the BIOS
\r
192 PUBLIC SET_VIDEO_MODE
\r
195 DW ?x4 ; DI, SI, DS, BP
\r
197 SVM_Mode DB ?,? ; Desired Video Mode
\r
201 SET_VIDEO_MODE PROC FAR
\r
203 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
204 mov BP, SP ; Set up Stack Frame
\r
206 CLR AH ; Function 0
\r
207 mov AL, [BP].SVM_Mode ; Get Mode #
\r
209 int 10H ; Change Video Modes
\r
212 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
213 ret 2 ; Exit & Clean Up Stack
\r
215 SET_VIDEO_MODE ENDP
\r
218 ;===================================
\r
219 ;int far pascal scan_keyboard (void)
\r
220 ;===================================
\r
222 ; Function to scan keyboard for a pressed key
\r
225 PUBLIC SCAN_KEYBOARD
\r
227 SCAN_KEYBOARD PROC FAR
\r
229 PUSHx BP, DS, SI, DI ; Preserve Important Registers
\r
231 mov AH, 01H ; Function #1
\r
232 INT 16H ; Call Keyboard Driver
\r
233 JZ @SK_NO_KEY ; Exit if Zero flag set
\r
235 mov AH, 00H ; Remove Key from Buffer
\r
236 INT 16H ; Get Keycode in AX
\r
238 OR AL, AL ; Low Byte Set (Ascii?)
\r
239 JZ @SK_Exit ; if not, it's a F-Key
\r
241 CLR AH ; Clear ScanCode if Ascii
\r
242 JMP s @SK_Exit ; Return Key in AX
\r
245 CLR AX ; Return Nil (no Keypress)
\r
248 cld ; Reset Direction Flag
\r
249 POPx DI, SI, DS, BP ; Restore Saved Registers
\r
250 ret ; Exit & Clean Up Stack
\r
255 ;========================================
\r
256 ;int far pascal random_int (int MaxValue)
\r
257 ;========================================
\r
259 ; Returns a pseudo-random number in the range of (0.. MaxInt-1)
\r
268 RI_MaxVal DW ? ; Maximum Value to Return + 1
\r
272 RANDOM_INT PROC FAR
\r
274 push BP ; Preserve Important Registers
\r
275 mov BP, SP ; Set up Stack Frame
\r
277 CLR BX ; BX is the data index
\r
278 CLR CX ; CX is the accumulator
\r
281 mov AX, RND_Seed[BX] ; load the initial seed
\r
282 mul RND_Mult[BX] ; multiply it
\r
283 div RND_ModV[BX] ; and obtain the Mod value
\r
284 mov RND_Seed[BX], DX ; save that for the next time
\r
286 add CX, DX ; add it into the accumulator
\r
288 inc BX ; point to the next set of values
\r
291 mov AX, CX ; AX = Random #
\r
293 div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder
\r
297 pop BP ; Restore BP
\r
298 ret 2 ; back to BASIC with AX holding the result
\r
303 ;==================================
\r
304 ;void far pascal init_random (void)
\r
305 ;==================================
\r
307 ; Scrambles the psuedo-random number sequence
\r
308 ; (XOR's the seed value with the timer)
\r
313 INIT_RANDOM PROC FAR
\r
315 CLR AX ; Segment = 0000
\r
317 mov AX, ES:[046Ch] ; Get Timer Lo Word
\r
319 xor RND_Seed, AX ; Scramble 1st Seed
\r
321 ret ; Exit & Clean Up Stack
\r
325 ;=========================================
\r
326 ;int far pascal int_sqr (int X, int Round)
\r
327 ;=========================================
\r
329 ; Returns the Integer Square Root of (X)
\r
330 ; Round allows the return value to be rounded to the
\r
331 ; nearest integer value by passing 0x80. Passing 0
\r
332 ; return the Integer Portion only. The rounding amound is
\r
333 ; a number from 0 to 1 multiplied by 256, thus
\r
334 ; 0.5 * 0x100 = 0x80!
\r
340 ISQ_Round DW ? ; Amount to Round Result * 256
\r
348 PUSHx BP, DI ; Save BP
\r
349 mov BP, SP ; Set up Stack Frame
\r
351 xor AX, AX ; {xor eax,eax}
\r
352 xor DX, DX ; {xor edx,edx}
\r
353 mov DI, [BP].ISQ_X ; {mov edi,x}
\r
355 mov CX, 16 ; {mov cx, 32}
\r
359 shl DI, 1 ; {shl edi,1}
\r
360 rcl DX, 1 ; {rcl edx,1}
\r
361 shl DI, 1 ; {shl edi,1}
\r
362 rcl DX, 1 ; {rcl edx,1}
\r
363 shl AX, 1 ; {shl eax,1}
\r
364 mov BX, AX ; {mov ebx,eax}
\r
365 shl BX, 1 ; {shl ebx,1}
\r
367 cmp DX, BX ; {cmp edx,ebx}
\r
370 sub DX, BX ; {sub edx,ebx}
\r
376 add ax, [BP].ISQ_Round ; {add eax,$00008000}
\r
377 ; {*round* result in hi word: ie. +0.5}
\r
378 shr ax, 8 ; {shr eax,16} {to ax (result)}
\r
380 POPx DI, BP ; Restore Registers
\r
385 ;=================================
\r
386 ;int far pascal timer_count (void)
\r
387 ;=================================
\r
389 ; Returns the current timer value as an integer/long integer
\r
394 TIMER_COUNT PROC FAR
\r
396 CLR AX ; Segment = 0000
\r
398 mov AX, ES:[046Ch] ; Get Timer Lo Word
\r
399 mov DX, ES:[046Eh] ; Get Timer Hi Word
\r
400 ret ; Exit & Clean Up Stack
\r