|
|
1.1 root 1: ;*** life2.asm - assembler routines for life.c
2: ;*
3: ;* Contains: _draw_grid - draws 79x45 grid on screen
4: ;* _dostep - advances the internal and screen grids
5: ;* one generation
6: ;* scrfill - private screen routine for _dostep
7: ;* scrremove - private screen routine for _dostep
8: ;*
9:
10:
11: ;*** segment definitions for the link with C
12: ;
13: _TEXT SEGMENT BYTE PUBLIC 'CODE'
14: _TEXT ENDS
15: _DATA SEGMENT WORD PUBLIC 'DATA'
16: _DATA ENDS
17: CONST SEGMENT WORD PUBLIC 'CONST'
18: CONST ENDS
19: _BSS SEGMENT WORD PUBLIC 'BSS'
20: _BSS ENDS
21: DGROUP GROUP CONST, _BSS, _DATA
22: ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
23:
24: ;*** the public routines
25: ;
26: PUBLIC _draw_grid, _dostep
27:
28:
29: ;*** Global variables from LIFE.C
30: ;
31: EXTRN _ScrSeg:WORD
32: EXTRN _ScrCol:WORD
33: EXTRN _ScrRow:WORD
34:
35: ;*** OS/2 functions
36: ;
37: EXTRN VIOSCRUNLOCK:FAR
38: EXTRN VIOSCRLOCK:FAR
39:
40:
41: _TEXT SEGMENT
42: ;*** _draw_grid - clears screen and draws 79x45 grid
43: ;*
44: ;* Entry: ds:_ScrSeg - screen buffer segment, from life.c
45: ;*
46: ;* Exit: None
47: ;*
48: _draw_grid PROC NEAR
49: push bp ; startup
50: mov bp,sp
51: sub sp,2 ; one word variable for VIOSCRLOCK
52: push di ; save register used by C compiler
53:
54: ; get screen access
55: mov ax,1
56: push ax ; wait until screen available
57: lea ax,byte ptr [bp-2]
58: push ss
59: push ax ; far address of return code
60: sub ax,ax
61: push ax ; reserved word
62: call VIOSCRLOCK ; call DOS for access
63:
64: ; get parameter and set up
65: mov es,ds:_ScrSeg ; (es) = screen buffer segment
66: xor di,di ; (di) = offset in screen (start at 0)
67:
68: ; loop to draw 45 rows of the tops and middles of grid squares
69: mov dx,45 ; (dx) = rows of grid squares, counter
70: dg1: mov ax,5555h ; (ax) = alternating bit pattern
71: mov cx,39 ; (cx) = number of full words on a line
72: rep stosw ; fill line with 5555h
73: mov es:[di],al ; finish off last odd byte
74: inc di ; (di) = start of next row
75: inc di
76: mov ax,08080h ; (ax) = left bit on
77: mov cx,40 ; (cx) = number of words on a line
78: rep stosw ; fill next line with f0f0h
79: dec dx ; at screen bottom?
80: jnz dg1 ; if no, loop
81:
82: ; draw the last line
83: mov ax,5555h ; (ax) = alternating bits
84: mov cx,39 ; (cx) = full words on last line
85: rep stosw ; do fill
86: mov es:[di],al ; finish off line with odd byte
87:
88: ; clear bottom on screen on even bit plane
89: inc di ; (di) = start of last few lines
90: sub ax,ax ; (ax) = blank
91: mov cx,400 ; (cx) = words left at bottom
92: rep stosw
93:
94: ; draw the grid lines on the odd bit plane
95: mov di,2000h ; (di) = top of odd bit plane
96: mov ax,08080h ; (ax) = left bit on
97: mov cx,40*2*45 ; (cx) = words to fill
98: rep stosw ; fill remainder with f0f0h
99:
100: ; clear bottom on screen on odd bit plane
101: sub ax,ax ; (ax) = blank
102: mov cx,400 ; (cx) = words left at bottom
103: rep stosw
104:
105: ; return the screen
106: sub ax,ax
107: push ax ; reserved word
108: call VIOSCRUNLOCK ; call DOS to return screen
109:
110: ; return
111: pop di
112: mov sp,bp
113: pop bp
114: ret
115: _draw_grid ENDP
116:
117:
118: ;*** _dostep - advances InGrid one generation.
119: ;*
120: ;* Entry: char far *INGRID - InGrid, main internal bit map of cells
121: ;* char far *INGRID2 - InGrid2, working area of same size
122: ;* unsigned INROW - InRow, bits per row of above to areas
123: ;* unsigned INCOL - InCol, rows of above to areas
124: ;* ds:_ScrSeg - from life.c
125: ;* ds:_ScrRow - from life.c
126: ;* ds:_ScrCol - from life.c
127: ;*
128: ;* Exit: InGrid is updated on generation in accordance with
129: ;* the rules of life.
130: ;* Returns in AX the return code from its VIOSCRLOCK call
131: ;*
132: INGRID EQU [bp+4]
133: INGRID2 EQU [bp+8]
134: INROW EQU [bp+12]
135: INCOL EQU [bp+14]
136:
137: _dostep PROC NEAR
138: push bp
139: mov bp,sp
140: sub sp,2 ; make room for local var
141: push di
142: push si
143: push es
144: push ds
145:
146: ; try to lock screen and put return code in [BP-2]
147: xor ax,ax
148: push ax ; don't wait until screen available
149: lea ax,byte ptr [bp-2]
150: push ss
151: push ax ; far address of return code
152: sub ax,ax
153: push ax ; reserved word
154: call VIOSCRLOCK ; call DOS for access
155:
156: ; get pointers to the two internal grids
157: les di,INGRID2 ; (es:di) = @InGrid2
158: lds si,INGRID ; (ds:si) = @InGrid
159:
160: ; copy InGrid into InGrid2
161: mov ax,INCOL ; (ax) = cells per row
162: mul word ptr INROW ; (ax) = total cells
163: mov cl,4
164: shr ax,cl ; (ax) = cell/16 = total words
165: mov cx,ax ; (cx) = total words
166: rep movsw ; move InGrid into InGrid2
167:
168:
169: ; main step loop
170: ; I use a rather odd algorithm. "neighbors" is defined
171: ; here as all cells in a 3x3 grid summed. If neighbors < 3, the
172: ; the cell in the center is turned off. If neighbors = 3, the cell
173: ; is turned on. If neighbors = 4, the cell is left in its current
174: ; state. If neighbors > 4, the cell is turned off.
175: push ds
176: pop es ; (es) = InGrid's segment
177: lds bx,INGRID2 ; (ds) = InGrid2's segment
178: ; (bx) = pointer to offset in both
179: mov di,INROW ; (di) = row count
180: mov si,INCOL ; (si) = column count
181: shr si,1
182: shr si,1
183: shr si,1 ; (si) = bytes per row in grids
184:
185: ; loop within the grid through all its rows
186: ; here we come when at the start of a row
187: ds1: mov cx,INCOL ; (cx) = column count
188: mov al,80h ; (al) = bit mask for current bit
189: xor dx,dx ; (dh) = #of neighbors 1 col back
190: ; (dl) = #of neighbors at cur col
191:
192: ; get number of neighbors on current row
193: ds2: xor ah,ah ; (ah) = count of neighbors on cur row
194: test ds:[bx],al ; is current bit on?
195: jz ds3 ; if not go on
196: inc ah ; if yes, inc neighbor count
197: ds3: cmp bx,si ; are we on top row
198: jb ds4 ; if yes, don't check above top
199: push bx ; save bx
200: sub bx,si ; backup bx to previous row
201: test ds:[bx],al ; is up a row bit on?
202: pop bx ; restore bx
203: jz ds4 ; if not go on
204: inc ah ; if yes, inc neighbor count
205: ds4: cmp di,1 ; are we on last row?
206: jz ds5 ; if yes, don't read off edge
207: test ds:[bx+si],al ; is bit down a row on?
208: jz ds5 ; if not, go on to final neighbor addin
209: inc ah ; if yes, inc neighbor count
210:
211: ; add up the total number of neighbors and decide what to do
212: ds5: cmp cx,INCOL ; are we at far left?
213: jz ds12 ; if yes, don't affect bits off edge
214: add dh,dl ; (dh) = previous two rows of neighbors
215: add dh,ah ; (dh) = total neighbor count
216: cmp dh,3
217: jb ds6 ; if neighbors <3, turn off the cell
218: je ds9 ; if neighbors = 3, turn on the cell
219: cmp dh,4 ;
220: je ds12 ; if neighbors = 4, do nothing
221:
222: ; turn off the center bit
223: ds6: rol al,1 ; backup to center bit
224: jnc ds7 ; if no overflow, go on
225: dec bx ; else, backup to last byte
226: ds7: test es:[bx],al ; is it already off
227: jz ds8 ; if yes, don't do it again
228: not al ; turn on all bits but one to blank
229: and es:[bx],al ; force off bit in InGrid
230: not al ; restore al
231:
232: pop ds ; (ds) = C's data ptr
233: call scrremove ; turn off on screen
234: push ds ; resave C's data ptr
235: mov ds,[bp+8+2] ; (ds) = seg of InGrid2
236:
237: ds8: ror al,1 ; put back al to foreward bit
238: jnc ds12 ; if no overflow, go to next bit
239: inc bx ; if overflow, restore bx
240: jmp ds12 ; go on to next bitl
241:
242: ; turn on the center bit
243: ds9: rol al,1 ; backup to center bit
244: jnc ds10 ; if no overflow, go on
245: dec bx ; else, backup to last byte
246: ds10: test es:[bx],al ; is it already on?
247: jnz ds11 ; if yes, don't do it again
248: or es:[bx],al ; force on bit in InGrid
249: pop ds ; (ds) = C's data ptr
250: call scrfill ; turn on on screen
251: push ds ; resave C's data ptr
252: mov ds,[bp+8+2] ; (ds) = seg of InGrid2
253: ds11: ror al,1 ; put back al to foreward bit
254: jnc ds12 ; if no overflow, go to next bit
255: inc bx ; if overflow, restore bx
256:
257: ; advance to the next bit
258: ds12: ror al,1 ; (al) mask advances to next bit
259: mov dh,dl ; move done neighbor count
260: mov dl,ah ; move the new neighbor row into bl
261: jnc ds13 ; if still in same byte, jmp
262: inc bx ; if at byte end, advance to next byte
263: ds13: loop ds2 ; loop through rest of bits on line
264:
265:
266: ; at the end of the row
267: dec di ; decrement row count
268: jz dsx ; if at end of grid, goto exit routine
269: jmp ds1 ; if not at bottom, go to next row
270:
271: ; at the end of the grid
272: ; return the screen
273: dsx: cmp byte ptr [bp-2],0 ; is the screen locked?
274: jnz dsx1 ; if not, don't unlock
275:
276: sub ax,ax
277: push ax ; reserved word
278: call VIOSCRUNLOCK ; call DOS to return screen
279:
280: dsx1: xor ah,ah
281: mov al,[bp-2] ; return code from SCRLOCK
282:
283: pop ds
284: pop es
285: pop si
286: pop di
287: mov sp,bp
288: pop bp
289: ret
290: _dostep ENDP
291:
292:
293: ;*** scrfill - fills in a cell on the screen in 79x45 grid
294: ;*
295: ;* Entry: (di) = row number counted from bottom up (InRow-di=row to fill)
296: ;* (cx) = col number from right-1 (InCol-1-cx = col to fill)
297: ;* (ds) = data segment of life.c
298: ;*
299: ;* Exit: None, saves all registers
300: ;*
301: scrfill proc near
302: cmp [bp-2],byte ptr 0 ; is the screen available?
303: jnz sfxx ; if not, exit
304:
305: push es
306: push ax
307: push bx
308: push dx
309:
310: ; get screen pointer
311: mov es,ds:_ScrSeg ; (es) = screen buf segment
312: mov ax,INROW
313: sub ax,di ; (ax) = row
314: cmp ax,ds:_ScrRow ; is it on the screen?
315: jae sfx ; if off, exit
316: mul WORD PTR INCOL ; * InCol
317: shl ax,1 ; * 2
318: mov dx,INCOL
319: sub dx,cx
320: dec dx ; (dx) = col to fill
321: cmp dx,ds:_ScrCol ; is it on the screen?
322: jae sfx ; if off, exit
323: add ax,dx ; + col = scrn offset of cell
324:
325: ; fill in screen cell
326: mov bx,ax ; (es:bx) = far ptr to screen cell
327: mov BYTE PTR es:[bx+2000h],7fh ; first line of cell
328: mov BYTE PTR es:[bx+80],7fh ; second
329: mov BYTE PTR es:[bx+2000h+80],7fh ; third line of cell
330:
331: sfx: pop dx
332: pop bx
333: pop ax
334: pop es
335: sfxx: ret
336: scrfill endp
337:
338:
339: ;*** scrremove - removes a cell on the screen in 79x45 grid
340: ;*
341: ;* Entry: (di) = row number counted from bottom up (InRow-di=row to fill)
342: ;* (cx) = col number from right-1 (InCol-1-cx = col to fill)
343: ;* (ds) = data segment of life.c
344: ;*
345: ;* Exit: None, saves all registers
346: ;*
347: scrremove proc near
348: cmp [bp-2],byte ptr 0 ; is the screen available?
349: jnz srxx ; if not, exit
350:
351: push es
352: push ax
353: push bx
354: push dx
355:
356: ; get screen pointer
357: mov es,ds:_ScrSeg ; (es) = screen buf segment
358: mov ax,INROW
359: sub ax,di ; (ax) = row
360: cmp ax,ds:_ScrRow ; is it on the screen?
361: jae srx ; if off, exit
362: mul WORD PTR INCOL ; * InCol
363: shl ax,1 ; * 2
364: mov dx,INCOL
365: sub dx,cx
366: dec dx ; (dx) = col
367: cmp dx,ds:_ScrCol ; is it on the screen?
368: jae srx ; if off, exit
369: add ax,dx ; + col = scrn offset of cell
370:
371: ; remove screen cell
372: mov bx,ax ; (es:bx) = far ptr to screen cell
373: mov BYTE PTR es:[bx+2000h],80h ; first line of cell
374: mov BYTE PTR es:[bx+80],80h ; second
375: mov BYTE PTR es:[bx+2000h+80],80h ; third line of cell
376:
377: srx: pop dx
378: pop bx
379: pop ax
380: pop es
381: srxx: ret
382: scrremove endp
383:
384: _TEXT ENDS
385: END
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.