This repository has been archived by the owner on Dec 24, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathL30-2.ASM
419 lines (419 loc) · 12.2 KB
/
L30-2.ASM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
; Demonstrates the interaction of the split screen and
; horizontal pel panning. On a VGA, first pans right in the top
; half while the split screen jerks around, because split screen
; pel panning suppression is disabled, then enables split screen
; pel panning suppression and pans right in the top half while the
; split screen remains stable. On an EGA, the split screen jerks
; around in both cases, because the EGA doesn't support split
; screen pel panning suppression.
;
; The jerking in the split screen occurs because the split screen
; is being pel panned (panned by single pixels--intrabyte panning),
; but is not and cannot be byte panned (panned by single bytes--
; "extrabyte" panning) because the start address of the split screen
; is forever fixed at 0.
;*********************************************************************
IS_VGA equ 1 ;set to 0 to assemble for EGA
;
VGA_SEGMENT equ 0a000h
LOGICAL_SCREEN_WIDTH equ 1024 ;# of pixels across virtual
; screen that we'll pan across
SCREEN_HEIGHT equ 350
SPLIT_SCREEN_START equ 200 ;start scan line for split screen
SPLIT_SCREEN_HEIGHT equ SCREEN_HEIGHT-SPLIT_SCREEN_START-1
CRTC_INDEX equ 3d4h ;CRT Controller Index register
AC_INDEX equ 3c0h ;Attribute Controller Index reg
OVERFLOW equ 7 ;index of Overflow reg in CRTC
MAXIMUM_SCAN_LINE equ 9 ;index of Maximum Scan Line register
; in CRTC
START_ADDRESS_HIGH equ 0ch ;index of Start Address High register
; in CRTC
START_ADDRESS_LOW equ 0dh ;index of Start Address Low register
; in CRTC
HOFFSET equ 13h ;index of Horizontal Offset register
; in CRTC
LINE_COMPARE equ 18h ;index of Line Compare reg (bits 7-0
; of split screen start scan line)
; in CRTC
AC_MODE_CONTROL equ 10h ;index of Mode Control reg in AC
PEL_PANNING equ 13h ;index of Pel Panning reg in AC
INPUT_STATUS_0 equ 3dah ;Input Status 0 register
WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;*********************************************************************
; Macro to output a word value to a port.
;
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
out dx,al
inc dx
xchg ah,al
out dx,al
dec dx
xchg ah,al
endif
endm
;*********************************************************************
MyStack segment para stack 'STACK'
db 512 dup (0)
MyStack ends
;*********************************************************************
Data segment
SplitScreenLine dw ? ;line the split screen currently
; starts after
StartAddress dw ? ;display memory offset at which
; scanning for video data starts
PelPan db ? ;current intrabyte horizontal pel
; panning setting
Data ends
;*********************************************************************
Code segment
assume cs:Code, ds:Data
;*********************************************************************
Start proc near
mov ax,Data
mov ds,ax
;
; Select mode 10h, 640x350 16-color graphics mode.
;
mov ax,0010h ;AH=0 is select mode function
;AL=10h is mode to select,
; 640x350 16-color graphics mode
int 10h
;
; Set the Offset register to make the offset from the start of one
; scan line to the start of the next the desired number of pixels.
; This gives us a virtual screen wider than the actual screen to
; pan across.
; Note that the Offset register is programmed with the logical
; screen width in words, not bytes, hence the final division by 2.
;
mov dx,CRTC_INDEX
mov ax,(LOGICAL_SCREEN_WIDTH/8/2 shl 8) or HOFFSET
OUT_WORD
;
; Set the start address to display the memory just past the split
; screen memory.
;
mov [StartAddress],SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
call SetStartAddress
;
; Set the split screen start scan line.
;
mov [SplitScreenLine],SPLIT_SCREEN_START
call SetSplitScreenScanLine
;
; Fill the split screen portion of display memory (starting at
; offset 0) with a choppy diagonal pattern sloping left.
;
mov ax,VGA_SEGMENT
mov es,ax
sub di,di
mov dx,SPLIT_SCREEN_HEIGHT
;fill all lines in the split screen
mov ax,0FF0h ;starting fill pattern
cld
RowLoop:
mov cx,LOGICAL_SCREEN_WIDTH/8/4
;fill 1 scan line
ColumnLoop:
stosw ;draw part of a diagonal line
mov word ptr es:[di],0 ;make vertical blank spaces so
; panning effects can be seen easily
inc di
inc di
loop ColumnLoop
rol ax,1 ;shift pattern word
dec dx
jnz RowLoop
;
; Fill the portion of display memory that will be displayed in the
; normal screen (the non-split screen part of the display) with a
; choppy diagonal pattern sloping right.
;
mov di,SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
mov dx,SCREEN_HEIGHT ;fill all lines
mov ax,0c510h ;starting fill pattern
cld
RowLoop2:
mov cx,LOGICAL_SCREEN_WIDTH/8/4
;fill 1 scan line
ColumnLoop2:
stosw ;draw part of a diagonal line
mov word ptr es:[di],0 ;make vertical blank spaces so
; panning effects can be seen easily
inc di
inc di
loop ColumnLoop2
ror ax,1 ;shift pattern word
dec dx
jnz RowLoop2
;
; Pel pan the non-split screen portion of the display; because
; split screen pel panning suppression is not turned on, the split
; screen jerks back and forth as the pel panning setting cycles.
;
mov cx,200 ;pan 200 pixels to the left
call PanRight
;
; Wait for a key press (don't echo character).
;
mov ah,8 ;DOS console input without echo function
int 21h
;
; Return to the original screen location, with pel panning turned off.
;
mov [StartAddress],SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
call SetStartAddress
mov [PelPan],0
call SetPelPan
;
; Turn on split screen pel panning suppression, so the split screen
; won't be affected by pel panning. Not done on EGA because both
; readable registers and the split screen pel panning suppression bit
; aren't supported by EGAs.
;
if IS_VGA
mov dx,INPUT_STATUS_0
in al,dx ;reset the AC Index/Data toggle to
; Index state
mov al,20h+AC_MODE_CONTROL
;bit 5 set to 1 to keep video on
mov dx,AC_INDEX ;point to AC Index/Data register
out dx,al
inc dx ;point to AC Data reg (for reads only)
in al,dx ;get the current AC Mode Control reg
or al,20h ;enable split screen pel panning
; suppression
dec dx ;point to AC Index/Data reg (Data for
; writes only)
out dx,al ;write the new AC Mode Control setting
; with split screen pel panning
; suppression turned on
endif
;
; Pel pan the non-split screen portion of the display; because
; split screen pel panning suppression is turned on, the split
; screen will not move as the pel panning setting cycles.
;
mov cx,200 ;pan 200 pixels to the left
call PanRight
;
; Wait for a key press (don't echo character).
;
mov ah,8 ;DOS console input without echo function
int 21h
;
; Return to text mode and DOS.
;
mov ax,0003h ;AH=0 is select mode function
;AL=3 is mode to select, text mode
int 10h ;return to text mode
mov ah,4ch
int 21h ;return to DOS
Start endp
;*********************************************************************
; Waits for the leading edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncStart proc near
mov dx,INPUT_STATUS_0
WaitNotVerticalSync:
in al,dx
test al,08h
jnz WaitNotVerticalSync
WaitVerticalSync:
in al,dx
test al,08h
jz WaitVerticalSync
ret
WaitForVerticalSyncStart endp
;*********************************************************************
; Waits for the trailing edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncEnd proc near
mov dx,INPUT_STATUS_0
WaitVerticalSync2:
in al,dx
test al,08h
jz WaitVerticalSync2
WaitNotVerticalSync2:
in al,dx
test al,08h
jnz WaitNotVerticalSync2
ret
WaitForVerticalSyncEnd endp
;*********************************************************************
; Sets the start address to the value specifed by StartAddress.
; Wait for the trailing edge of vertical sync before setting so that
; one half of the address isn't loaded before the start of the frame
; and the other half after, resulting in flicker as one frame is
; displayed with mismatched halves. The new start address won't be
; loaded until the start of the next frame; that is, one full frame
; will be displayed before the new start address takes effect.
;
; Input: none
;
; Output: none
;
; Registers altered: AX, DX
;
SetStartAddress proc near
call WaitForVerticalSyncEnd
mov dx,CRTC_INDEX
mov al,START_ADDRESS_HIGH
mov ah,byte ptr [StartAddress+1]
cli ;make sure both registers get set at once
OUT_WORD
mov al,START_ADDRESS_LOW
mov ah,byte ptr [StartAddress]
OUT_WORD
sti
ret
SetStartAddress endp
;*********************************************************************
; Sets the horizontal pel panning setting to the value specified
; by PelPan. Waits until the start of vertical sync to do so, so
; the new pel pan setting can be loaded during non-display time
; and can be ready by the start of the next frame.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
SetPelPan proc near
call WaitForVerticalSyncStart ;also resets the AC
; Index/Data toggle
; to Index state
mov dx,AC_INDEX
mov al,PEL_PANNING+20h
;bit 5 set to 1 to keep video on
out dx,al ;point the AC Index to Pel Pan reg
mov al,[PelPan]
out dx,al ;load the new Pel Pan setting
ret
SetPelPan endp
;*********************************************************************
; Sets the scan line the split screen starts after to the scan line
; specified by SplitScreenLine.
;
; Input: none
;
; Output: none
;
; All registers preserved
;
SetSplitScreenScanLine proc near
push ax
push cx
push dx
;
; Wait for the leading edge of the vertical sync pulse. This ensures
; that we don't get mismatched portions of the split screen setting
; while setting the two or three split screen registers (register 18h
; set but register 7 not yet set when a match occurs, for example),
; which could produce brief flickering.
;
call WaitForVerticalSyncStart
;
; Set the split screen scan line.
;
mov dx,CRTC_INDEX
mov ah,byte ptr [SplitScreenLine]
mov al,LINE_COMPARE
cli ;make sure all the registers get set at once
OUT_WORD ;set bits 7-0 of the split screen scan line
mov ah,byte ptr [SplitScreenLine+1]
and ah,1
mov cl,4
shl ah,cl ;move bit 8 of the split split screen scan
; line into position for the Overflow reg
mov al,OVERFLOW
if IS_VGA
;
; The Split Screen, Overflow, and Line Compare registers all contain
; part of the split screen start scan line on the VGA. We'll take
; advantage of the readable registers of the VGA to leave other bits
; in the registers we access undisturbed.
;
out dx,al ;set CRTC Index reg to point to Overflow
inc dx ;point to CRTC Data reg
in al,dx ;get the current Overflow reg setting
and al,not 10h ;turn off split screen bit 8
or al,ah ;insert the new split screen bit 8
; (works in any mode)
out dx,al ;set the new split screen bit 8
dec dx ;point to CRTC Index reg
mov ah,byte ptr [SplitScreenLine+1]
and ah,2
mov cl,3
ror ah,cl ;move bit 9 of the split split screen scan
; line into position for the Maximum Scan
; Line register
mov al,MAXIMUM_SCAN_LINE
out dx,al ;set CRTC Index reg to point to Maximum
; Scan Line
inc dx ;point to CRTC Data reg
in al,dx ;get the current Maximum Scan Line setting
and al,not 40h ;turn off split screen bit 9
or al,ah ;insert the new split screen bit 9
; (works in any mode)
out dx,al ;set the new split screen bit 9
else
;
; Only the Split Screen and Overflow registers contain part of the
; Split Screen start scan line and need to be set on the EGA.
; EGA registers are not readable, so we have to set the non-split
; screen bits of the Overflow register to a preset value, in this
; case the value for 350-scan-line modes.
;
or ah,0fh ;insert the new split screen bit 8
; (only works in 350-scan-line EGA modes)
OUT_WORD ;set the new split screen bit 8
endif
sti
pop dx
pop cx
pop ax
ret
SetSplitScreenScanLine endp
;*********************************************************************
; Pan horizontally to the right the number of pixels specified by CX.
;
; Input: CX = # of pixels by which to pan horizontally
;
; Output: none
;
; Registers altered: AX, CX, DX
;
PanRight proc near
PanLoop:
inc [PelPan]
and [PelPan],07h
jnz DoSetStartAddress
inc [StartAddress]
DoSetStartAddress:
call SetStartAddress
call SetPelPan
loop PanLoop
ret
PanRight endp
;*********************************************************************
Code ends
end Start