3 msgErr0 db 'Later!',13,10,'$'
\r
5 msgErr1 db 'Error 001: Out of memory?',13,10,'$'
\r
7 msgErr2 db 'Error 002: CPU must be at least an 80386.',13,10,'$'
\r
9 msgErr3 db 'Error 003: File error.',13,10,'$'
\r
10 ERR_FILENOTFOUND EQU 4
\r
11 msgErr4 db 'Error 004: File not found.',13,10,'$'
\r
12 msgtblError dw offset msgErr0, offset msgErr1, offset msgErr2,
\r
13 offset msgErr3, offset msgErr4
\r
17 CPUName86 DB "8088/8086$"
\r
18 CPUName286 DB "80286DX/SX$"
\r
19 CPUName386 DB "80386DX/SX$"
\r
20 CPUName486 DB "80486DX/SX or better$"
\r
21 CPUNameTable DW CPUName86,CPUName286,CPUName386,CPUName486
\r
24 msgCPUTypeIs DB "Your CPU type: $"
\r
26 msgCPUTypeIsEnd DB 13,10,'$'
\r
30 msgPages DB 'Pages displayed: '
\r
31 strNumPages DB 6 dup (?),13,10,'$'
\r
34 bufText DW 80*50 DUP (?) ; Needs this much to hold
\r
35 ; a 50-line screen...
\r
40 fnMap1 db 'DIAGONAL.MAP',0
\r
41 fnTiles1 db 'DIAGONAL.TIL',0
\r
42 fnPalette db 'DIAGONAL.PAL',0 ; only one allowed, for now
\r
43 fnMap2 db 'SCROLL.MAP',0
\r
44 fnTiles2 db 'SCROLL.TIL',0
\r
46 fntblMap dw offset fnMap1,offset fnMap2
\r
47 fntblTiles dw offset fnTiles1,offset fnTiles2
\r
50 ;; CPUType routine snatched from Ray Duncan's _Power Programming MASM_
\r
51 ;; chapter 14. Reformatted to my style, but I left the code alone
\r
52 ;; except that it used to push a bunch of stuff, but doesn't any more
\r
53 ;; because I don't care what gets destroyed.
\r
55 pushf ; now try to clear bits 12-15
\r
56 pop ax ; of CPU flags
\r
58 push ax ; set modified CPU flags
\r
61 pop ax ; get flags again
\r
62 and ax,0f000h ; if bits 12-15 are still
\r
63 cmp ax,0f000h ; set, this is 8086/88
\r
64 jne cpu1 ; jump, not 8086/88
\r
65 mov nCPU,CPU8086 ; set nCPU = 86/88 CPU type
\r
68 cpu1: or ax,0f000h ; must be 286 or later,
\r
69 push ax ; now try to set bits 12-15
\r
72 pop ax ; if bits 12-15 can't be
\r
73 and ax,0f000h ; set, this is a 286
\r
74 jnz cpu2 ; jump, not 80286
\r
75 mov nCPU,CPU80286 ; set nCPU = 286 CPU type
\r
78 cpu2: mov bx,sp ; 386 or later, save SP
\r
79 and sp,not 3 ; avoid stack alignment fault
\r
80 pushfd ; get value of EFLAGS
\r
82 mov ecx,eax ; save copy of EFLAGS
\r
83 xor eax,40000h ; flip AC bit in EFLAGS
\r
84 push eax ; try and force EFLAGS
\r
86 pushfd ; get back EFLAGS value
\r
88 mov sp,bx ; restore old stack pointer
\r
89 xor eax,ecx ; can AC bit be changed?
\r
90 jnz cpu3 ; no, jump, not a 386
\r
91 mov nCPU,CPU80386 ; set nCPU = 386 CPU type
\r
94 cpu3: mov nCPU,CPU80486 ; set nCPU = 486 CPU type
\r
99 DOSPRINT <offset msgCPUTypeIs>
\r
100 DOSPRINT CPUNameTable[bx]
\r
101 DOSPRINT <offset msgCPUTypeIsEnd>
\r
102 ret ; return with nCPU = CPU type
\r
105 ;; Initialize: So far, all it does is make sure you have a 386 +
\r
106 ;; (because that's what I assembled the code for).
\r
107 Initialize PROC near
\r
108 ; Set DS = CS in this program, since data is local
\r
110 mov segCode,ax ; Store the Code Segment
\r
112 mov segPSP,bx ; Store the PSP Segment
\r
113 mov ds,ax ; Set DS = CS
\r
115 ; Resize code to 64K
\r
116 CODE_SIZE EQU 64 ; <- this is arbitrary.
\r
117 ; ES already -> allocated segment
\r
119 mov bx,64*CODE_SIZE
\r
124 ;; I've chosen not to implement sprites yet so that I can get this out
\r
126 ;; ; 320x200 buffer for sprite drawing. To draw sprites, first draw them
\r
127 ;; ; into this buffer, adding rectangles to the current rectangle list.
\r
128 ;; ; Then, use BUFFER_COPY to put out the buffers with the current
\r
129 ;; ; rectangle list to the screen. BUFFER_COPY will ensure minimal VGA
\r
131 ;; ; Create a buffer segment
\r
132 ;; mov bx,(320 * 200) / 16
\r
135 ;; mov nError,ERR_MEM
\r
136 ;; jc TerminateError
\r
137 ;; mov segBuffer,ax
\r
145 mov dx,offset fnPalette
\r
146 call LoadPaletteFile
\r
154 call Beginning ; Can display an entry screen here
\r
156 ; This is linked in from Michael Abrash's zen timer code.
\r
157 ; (But I wrote the Click myself)
\r
166 call Ending ; Can display an exit screen here
\r
170 Terminate: mov nError,ERR_OK
\r
172 mov ax,cs ;DOS functions require that DS point
\r
173 mov ds,ax ; to text to be displayed on the screen
\r
177 DOSPRINT msgtblError[bx]
\r
181 mov si,offset strNumPages
\r
183 DOSPRINT <offset msgPages>
\r
188 mov ah,4ch ; DOS Terminate
\r
190 ; Don't need to RET! We're outta here
\r
193 ;; Clicks the internal speaker. I use this to indicate that page timing
\r
201 mov cx,5000 ; (this is an arbitrary delay!)
\r
202 spkr_on: loop spkr_on
\r
208 ;; Copied from an old 8088 "Learn Assembly" book and changed a bit
\r
211 mov byte ptr cs:[si],' '
\r
212 mov byte ptr cs:[si+1],'0'
\r
213 mov byte ptr cs:[si+2],'0'
\r
214 mov byte ptr cs:[si+3],'0'
\r
215 mov byte ptr cs:[si+4],'0'
\r
216 mov byte ptr cs:[si+5],'0'
\r
222 mov byte ptr cs:[si-6],'-'
\r
223 clear_divide: mov dx,0
\r
233 ;; Given a filename at DS:DX, reads the file into memory and returns
\r
234 ;; a pointer to it as DX:0000.
\r
235 ;; Note that this routine obviously will only work correctly for
\r
236 ;; a file < 640k in size, but you can bring in files bigger than 64k.
\r
237 ;; This code comes from Future Crew's STMIK sampler "Mental Surgery"
\r
238 ;; and I commented it up to make it fit in with my stuff a little better.
\r
239 ;; Thank you, FC, for releasing that code! Several of the routines
\r
240 ;; in this program were inspired or helped along by having that source...
\r
241 ;; Most recently, added in error codes.
\r
244 ;set: DX=offset to filename
\r
245 ;return: DX=segment of file
\r
247 ; Open the datafile at DS:DX.
\r
248 mov ax,3D00h ; 3D,00 -> Open file, read only
\r
249 ; DS:DX already points at filename
\r
250 int 21h ; returns AX=file handle
\r
251 mov cl,ERR_FILENOTFOUND
\r
253 mov bx,ax ; Store file handle in BX
\r
254 mov si,bx ; and also in a variable
\r
256 ; Get the length of the file so we know how much to allocate
\r
257 mov ax,4202h ; 42,02 -> Seek, signed from end
\r
258 mov cx,0 ; CX:DX is a long file offset,
\r
259 ; BX is already set as file handle
\r
260 mov dx,0 ; zero in this case = end of file
\r
261 int 21h ; (returns long offset in DX:AX)
\r
265 ;;; shr dx,1 ; This is original FC code,
\r
266 ;;; rcr ax,1 ; which I removed because the
\r
267 ;;; shr dx,1 ; 386 has a nice instruction
\r
268 ;;; rcr ax,1 ; to do this all!
\r
269 ;;; shr dx,1 ; But 286 users will want to
\r
270 ;;; rcr ax,1 ; return to this code, instead
\r
271 ;;; shr dx,1 ; of SHRD dx,ax,4
\r
274 ; Now turn that long DX:AX into a number of paragraphs to allocate
\r
275 ; for when we read the file.
\r
276 shrd ax,dx,4 ; Divides long DX:AX by 4,
\r
277 mov bx,ax ; and stores this in BX
\r
278 inc bx ; HHMMMM? One more needed for small #'s
\r
279 mov ah,48h ; 48 -> Allocate memory
\r
280 ; BX already = # of paragraphs
\r
284 mov di,ax ; store this in a variable
\r
286 ; Seek the file back to the beginning in order to read it into
\r
287 ; the memory we just allocated.
\r
288 mov ax,4200h ; 42,00 -> Seek, absolute offset
\r
289 mov bx,si ; BX is the file handle.
\r
290 mov cx,0 ; CX:DX is a long offset
\r
295 ; Now read the file into memory
\r
296 mov ds,di ; DS points at alloc'd memory
\r
297 ReadBlock: mov ah,3fh ; 3F -> Read file
\r
298 mov cx,32768 ; read 32768 bytes at a time
\r
299 mov dx,0 ; DS:DX points at beginning of
\r
300 int 21h ; this block of memory.
\r
303 mov dx,ds ; Offset DS by (32768/16), which
\r
304 add dx,800h ; is the number of paragraphs in
\r
305 mov ds,dx ; each block of 32768 bytes.
\r
306 cmp ax,32768 ; Did we actually read 32768 bytes?
\r
307 je ReadBlock ; If so, there's more to read...
\r
308 ; Otherwise, we've read all the
\r
309 ; data in the file.
\r
311 ; So now, close the file handle.
\r
312 mov ah,3Eh ; 3E -> Close file
\r
313 ; BX still is the file handle
\r
316 ; Everything went ok. Return the segment in DX.
\r
320 ferror: mov nError,cl
\r
324 ;; Eventually, this should load in an index of all data files to
\r
325 ;; allow for filenames to be specified outside of the program. The
\r
326 ;; goal is to make the program have no hardcoded filenames...
\r
327 ;; Of course, the structure of this index and its entries will be
\r
328 ;; hardcoded, as will the structures of all of the files it includes.
\r
329 LoadIndex PROC near
\r
333 ;; Save the current video mode and cursor position with standard
\r
335 SaveVideo PROC near
\r
337 int 10h ; Get current display Mode
\r
347 mov di,offset bufText
\r
353 ;; Restore the current video mode and cursor position with standard
\r
355 RestoreVideo PROC near
\r
358 int 10h ; Get current display Mode
\r
364 PAL_UPDATE ; When flipping into text mode, re-do the
\r
365 ; palette because the BIOS changes it.
\r
370 mov si,offset bufText
\r