|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.