File:  [OS/2 SDKs] / os2sdk / demos / apps / life / life2.asm
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 12:25:56 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: os2sdk-1988, HEAD
Microsoft OS/2 SDK 03-01-1988

;***	life2.asm - assembler routines for life.c
;*
;*	Contains:	_draw_grid - draws 79x45 grid on screen
;*			_dostep    - advances the internal and screen grids
;*					 one generation
;*			scrfill    - private screen routine for _dostep
;*			scrremove  - private screen routine for _dostep
;*
;*  Created by Microsoft Corp. 1987


;***	segment definitions for the link with C
;
_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT  WORD PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT  WORD PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT  WORD PUBLIC 'BSS'
_BSS	ENDS
DGROUP	GROUP	CONST, _BSS, _DATA
	ASSUME	CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

;***	the public routines
;
	PUBLIC	_draw_grid, _dostep


;***	Global variables from LIFE.C
;
	EXTRN	_ScrSeg:WORD
	EXTRN	_ScrCol:WORD
	EXTRN	_ScrRow:WORD

;***	OS/2 functions
;
	EXTRN	VIOSCRUNLOCK:FAR
	EXTRN	VIOSCRLOCK:FAR


_TEXT	   SEGMENT
;***	_draw_grid - clears screen and draws 79x45 grid
;*
;*	Entry:	ds:_ScrSeg - screen buffer segment, from life.c
;*
;*	Exit:	None
;*
_draw_grid	PROC NEAR
	push	bp			; startup
	mov	bp,sp
	sub	sp,2			; one word variable for VIOSCRLOCK
	push	di			; save register used by C compiler

;	get screen access
	mov	ax,1
	push	ax			; wait until screen available
	lea	ax,byte ptr [bp-2]
	push	ss
	push	ax			; far address of return code
	sub	ax,ax
	push	ax			; reserved word
	call	VIOSCRLOCK		; call DOS for access

;	get parameter and set up
	mov	es,ds:_ScrSeg		; (es) = screen buffer segment
	xor	di,di			; (di) = offset in screen (start at 0)

;	loop to draw 45 rows of the tops and middles of grid squares
	mov	dx,45			; (dx) = rows of grid squares, counter
dg1:	mov	ax,5555h		; (ax) = alternating bit pattern
	mov	cx,39			; (cx) = number of full words on a line
	rep	stosw			; fill line with 5555h
	mov	es:[di],al		; finish off last odd byte
	inc	di			; (di) = start of next row
	inc	di
	mov	ax,08080h		; (ax) = left bit on
	mov	cx,40			; (cx) = number of words on a line
	rep	stosw			; fill next line with f0f0h
	dec	dx			; at screen bottom?
	jnz	dg1			; if no, loop

;	draw the last line
	mov	ax,5555h		; (ax) = alternating bits
	mov	cx,39			; (cx) = full words on last line
	rep	stosw			; do fill
	mov	es:[di],al		; finish off line with odd byte

;	clear bottom on screen on even bit plane
	inc	di			; (di) = start of last few lines
	sub	ax,ax			; (ax) = blank
	mov	cx,400			; (cx) = words left at bottom
	rep	stosw

;	draw the grid lines on the odd bit plane
	mov	di,2000h		; (di) = top of odd bit plane
	mov	ax,08080h		; (ax) = left bit on
	mov	cx,40*2*45		; (cx) = words to fill
	rep	stosw			; fill remainder with f0f0h

;	clear bottom on screen on odd bit plane
	sub	ax,ax			; (ax) = blank
	mov	cx,400			; (cx) = words left at bottom
	rep	stosw

;	return the screen
	sub	ax,ax
	push	ax			; reserved word
	call	VIOSCRUNLOCK		; call DOS to return screen

;	return
	pop	di
	mov	sp,bp
	pop	bp
	ret
_draw_grid	ENDP


;***	_dostep - advances InGrid one generation.
;*
;*	Entry:	char far *INGRID - InGrid, main internal bit map of cells
;*		char far *INGRID2 - InGrid2, working area of same size
;*		unsigned INROW - InRow, bits per row of above to areas
;*		unsigned INCOL - InCol, rows of above to areas
;*		ds:_ScrSeg	 - from life.c
;*		ds:_ScrRow	 - from life.c
;*		ds:_ScrCol	 - from life.c
;*
;*	Exit:	InGrid is updated on generation in accordance with
;*		the rules of life.
;*		Returns in AX the return code from its VIOSCRLOCK call
;*
INGRID EQU [bp+4]
INGRID2 EQU [bp+8]
INROW EQU [bp+12]
INCOL EQU [bp+14]

_dostep PROC	NEAR
	push	bp
	mov	bp,sp
	sub	sp,2			; make room for local var
	push	di
	push	si
	push	es
	push	ds

	; try to lock screen and put return code in [BP-2]
	xor	ax,ax
	push	ax			; don't wait until screen available
	lea	ax,byte ptr [bp-2]
	push	ss
	push	ax			; far address of return code
	sub	ax,ax
	push	ax			; reserved word
	call	VIOSCRLOCK		; call DOS for access

	; get pointers to the two internal grids
	les	di,INGRID2		 ; (es:di) = @InGrid2
	lds	si,INGRID		; (ds:si) = @InGrid

	; copy InGrid into InGrid2
	mov	ax,INCOL	      ; (ax) = cells per row
	mul	word ptr INROW	      ; (ax) = total cells
	mov	cl,4
	shr	ax,cl			; (ax) = cell/16 = total words
	mov	cx,ax			; (cx) = total words
	rep	movsw			; move InGrid into InGrid2


	; main step loop
	;   I use a rather odd algorithm.  "neighbors" is defined
	;   here as all cells in a 3x3 grid summed.  If neighbors < 3, the
	;   the cell in the center is turned off.  If neighbors = 3, the cell
	;   is turned on.  If neighbors = 4, the cell is left in its current
	;   state.  If neighbors > 4, the cell is turned off.
	push	ds
	pop	es			; (es) = InGrid's segment
	lds	bx,INGRID2		 ; (ds) = InGrid2's segment
					; (bx) = pointer to offset in both
	mov	di,INROW	      ; (di) = row count
	mov	si,INCOL	      ; (si) = column count
	shr	si,1
	shr	si,1
	shr	si,1			; (si) = bytes per row in grids

	; loop within the grid through all its rows
	;    here we come when at the start of a row
ds1:	mov	cx,INCOL	      ; (cx) = column count
	mov	al,80h			; (al) = bit mask for current bit
	xor	dx,dx			; (dh) = #of neighbors 1 col back
					; (dl) = #of neighbors at cur col

	; get number of neighbors on current row
ds2:	xor	ah,ah			; (ah) = count of neighbors on cur row
	test	ds:[bx],al		; is current bit on?
	jz	ds3			; if not go on
	inc	ah			; if yes, inc neighbor count
ds3:	cmp	bx,si			; are we on top row
	jb	ds4			; if yes, don't check above top
	push	bx			; save bx
	sub	bx,si			; backup bx to previous row
	test	ds:[bx],al		; is up a row bit on?
	pop	bx			; restore bx
	jz	ds4			; if not go on
	inc	ah			; if yes, inc neighbor count
ds4:	cmp	di,1			; are we on last row?
	jz	ds5			; if yes, don't read off edge
	test	ds:[bx+si],al		; is bit down a row on?
	jz	ds5			; if not, go on to final neighbor addin
	inc	ah			; if yes, inc neighbor count

	; add up the total number of neighbors and decide what to do
ds5:	cmp	cx,INCOL		; are we at far left?
	jz	ds12			; if yes, don't affect bits off edge
	add	dh,dl			; (dh) = previous two rows of neighbors
	add	dh,ah			; (dh) = total neighbor count
	cmp	dh,3
	jb	ds6			; if neighbors <3, turn off the cell
	je	ds9			; if neighbors = 3, turn on the cell
	cmp	dh,4			;
	je	ds12			; if neighbors = 4, do nothing

	; turn off the center bit
ds6:	rol	al,1			; backup to center bit
	jnc	ds7			; if no overflow, go on
	dec	bx			; else, backup to last byte
ds7:	test	es:[bx],al		; is it already off
	jz	ds8			; if yes, don't do it again
	not	al			; turn on all bits but one to blank
	and	es:[bx],al		; force off bit in InGrid
	not	al			; restore al

	pop	ds			; (ds) =  C's data ptr
	call	scrremove		; turn off on screen
	push	ds			; resave C's data ptr
	mov	ds,[bp+8+2]		; (ds) = seg of InGrid2

ds8:	ror	al,1			; put back al to foreward bit
	jnc	ds12			; if no overflow, go to next bit
	inc	bx			; if overflow, restore bx
	jmp	ds12			; go on to next bitl

	; turn on the center bit
ds9:	rol	al,1			; backup to center bit
	jnc	ds10			; if no overflow, go on
	dec	bx			; else, backup to last byte
ds10:	test	es:[bx],al		; is it already on?
	jnz	ds11			; if yes, don't do it again
	or	es:[bx],al		; force on bit in InGrid
	pop	ds			; (ds) =  C's data ptr
	call	scrfill 		; turn on on screen
	push	ds			; resave C's data ptr
	mov	ds,[bp+8+2]		; (ds) = seg of InGrid2
ds11:	ror	al,1			; put back al to foreward bit
	jnc	ds12			; if no overflow, go to next bit
	inc	bx			; if overflow, restore bx

	; advance to the next bit
ds12:	ror	al,1			; (al) mask advances to next bit
	mov	dh,dl			; move done neighbor count
	mov	dl,ah			; move the new neighbor row into bl
	jnc	ds13			; if still in same byte, jmp
	inc	bx			; if at byte end, advance to next byte
ds13:	loop	ds2			; loop through rest of bits on line


	; at the end of the row
	dec	di			; decrement row count
	jz	dsx			; if at end of grid, goto exit routine
	jmp	ds1			; if not at bottom, go to next row

	; at the end of the grid
	; return the screen
dsx:	cmp	byte ptr [bp-2],0	; is the screen locked?
	jnz	dsx1			; if not, don't unlock

	sub	ax,ax
	push	ax			; reserved word
	call	VIOSCRUNLOCK		; call DOS to return screen

dsx1:	xor	ah,ah
	mov	al,[bp-2]		; return code from SCRLOCK

	pop	ds
	pop	es
	pop	si
	pop	di
	mov	sp,bp
	pop	bp
	ret
_dostep ENDP


;***	scrfill - fills in a cell on the screen in 79x45 grid
;*
;*	Entry:	(di) = row number counted from bottom up (InRow-di=row to fill)
;*		(cx) = col number from right-1 (InCol-1-cx = col to fill)
;*		(ds) = data segment of life.c
;*
;*	Exit:	None, saves all registers
;*
scrfill proc   near
	cmp	[bp-2],byte ptr 0	; is the screen available?
	jnz	sfxx			; if not, exit

	push	es
	push	ax
	push	bx
	push	dx

	; get screen pointer
	mov	es,ds:_ScrSeg		; (es) = screen buf segment
	mov	ax,INROW
	sub	ax,di			; (ax) = row
	cmp	ax,ds:_ScrRow		; is it on the screen?
	jae	sfx			; if off, exit
	mul	WORD PTR INCOL		;	 * InCol
	shl	ax,1			;	 * 2
	mov	dx,INCOL
	sub	dx,cx
	dec	dx			;		(dx) = col to fill
	cmp	dx,ds:_ScrCol		; is it on the screen?
	jae	sfx			; if off, exit
	add	ax,dx			;	 + col = scrn offset of cell

	; fill in screen cell
	mov	bx,ax			; (es:bx) = far ptr to screen cell
	mov	BYTE PTR es:[bx+2000h],7fh	; first line of cell
	mov	BYTE PTR es:[bx+80],7fh 	; second
	mov	BYTE PTR es:[bx+2000h+80],7fh	; third line of cell

sfx:	pop	dx
	pop	bx
	pop	ax
	pop	es
sfxx:	ret
scrfill endp


;***	scrremove - removes a cell on the screen in 79x45 grid
;*
;*	Entry:	(di) = row number counted from bottom up (InRow-di=row to fill)
;*		(cx) = col number from right-1 (InCol-1-cx = col to fill)
;*		(ds) = data segment of life.c
;*
;*	Exit:	None, saves all registers
;*
scrremove proc	 near
	cmp	[bp-2],byte ptr 0	; is the screen available?
	jnz	srxx			; if not, exit

	push	es
	push	ax
	push	bx
	push	dx

	; get screen pointer
	mov	es,ds:_ScrSeg		; (es) = screen buf segment
	mov	ax,INROW
	sub	ax,di			; (ax) = row
	cmp	ax,ds:_ScrRow		; is it on the screen?
	jae	srx			; if off, exit
	mul	WORD PTR INCOL		;	 * InCol
	shl	ax,1			;	 * 2
	mov	dx,INCOL
	sub	dx,cx
	dec	dx			;		(dx) = col
	cmp	dx,ds:_ScrCol		; is it on the screen?
	jae	srx			; if off, exit
	add	ax,dx			;	 + col = scrn offset of cell

	; remove screen cell
	mov	bx,ax			; (es:bx) = far ptr to screen cell
	mov	BYTE PTR es:[bx+2000h],80h	; first line of cell
	mov	BYTE PTR es:[bx+80],80h 	; second
	mov	BYTE PTR es:[bx+2000h+80],80h	; third line of cell

srx:	pop	dx
	pop	bx
	pop	ax
	pop	es
srxx:	ret
scrremove endp

_TEXT	ENDS
END


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.