;;==========================================================================;; ;; Joe Zbiciak's LIFE DEMO ;; ;; Copyright 2002, Joe Zbiciak, im14u2c AT primenet DOT com. ;; ;; http://www.primenet.com/~im14u2c/intv/ ;; ;;==========================================================================;; ;* ======================================================================== *; ;* TO BUILD IN BIN+CFG FORMAT: *; ;* as1600 -o life.bin -l life.lst life.asm *; ;* *; ;* TO BUILD IN ROM FORMAT: *; ;* as1600 -o life.rom -l life.lst life.asm *; ;* ======================================================================== *; ;* ======================================================================== *; ;* This program is free software; you can redistribute it and/or modify *; ;* it under the terms of the GNU General Public License as published by *; ;* the Free Software Foundation; either version 2 of the License, or *; ;* (at your option) any later version. *; ;* *; ;* This program is distributed in the hope that it will be useful, *; ;* but WITHOUT ANY WARRANTY; without even the implied warranty of *; ;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *; ;* General Public License for more details. *; ;* *; ;* You should have received a copy of the GNU General Public License *; ;* along with this program; if not, write to the Free Software *; ;* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *; ;* ======================================================================== *; ;* Copyright (c) 2002, Joseph Zbiciak *; ;* ======================================================================== *; ROMW 16 ; Use 16-bit ROM width ;------------------------------------------------------------------------------ ; Include system information ;------------------------------------------------------------------------------ INCLUDE "../library/gimini.asm" ;------------------------------------------------------------------------------ ; Set up our variables in scratch and system memory. ;------------------------------------------------------------------------------ SCRATCH ORG $100, $100, "-RWBN" ISRVEC RMB 2 GEN0 RMB 96 GEN1 RMB 96 ROWB RMB 32 _EOSCR EQU $ SYSTEM ORG $2F0, $2F0, "-RWBN" STACK RMB 32 TICKS RMB 1 RNDLO RMB 1 RNDHI RMB 1 CURGEN RMB 1 NXTGEN RMB 1 CLTMP RMB 4 ; temp space for CALCLIFE XSAVE RMB 1 ; \ YSAVE RMB 1 ; |-- for colored-square routines CSAVE RMB 1 ; / LOOP RMB 1 GENCOL RMB 1 GCNUM RMB 1 _EOSYS EQU $ ;------------------------------------------------------------------------------ ; Build options: Set HGLIDER or VGLIDER to 1 to test with the gliders. ; Otherwise the screen will be randomized at start. ;------------------------------------------------------------------------------ HGLIDER EQU 0 VGLIDER EQU 0 ;------------------------------------------------------------------------------ ; Magic numbers for ECScable polling. ;------------------------------------------------------------------------------ EC_LOC EQU $CF00 EC_MAG EQU $69 EC_POLL EQU $CF01 ORG $5000 ; Use default memory map ;------------------------------------------------------------------------------ ; EXEC-friendly ROM header. ;------------------------------------------------------------------------------ ROMHDR: BIDECLE ZERO ; MOB picture base (points to NULL list) BIDECLE ZERO ; Process table (points to NULL list) BIDECLE MAIN ; Program start address BIDECLE ZERO ; Bkgnd picture base (points to NULL list) BIDECLE ONES ; GRAM pictures (points to NULL list) BIDECLE TITLE ; Cartridge title/date DECLE $03C0 ; No ECS title, run code after title, ; ... no clicks ZERO: DECLE $0000 ; Screen border control DECLE $0000 ; 0 = color stack, 1 = f/b mode ONES: DECLE C_BLU, C_BLU ; Initial color stack 0 and 1: Blue DECLE C_BLU, C_BLU ; Initial color stack 2 and 3: Blue DECLE C_BLU ; Initial border color: Blue ;------------------------------------------------------------------------------ ;; ======================================================================== ;; ;; TITLE -- Display our modified title screen & copyright date. ;; ;; ======================================================================== ;; TITLE: PROC BYTE 102, 'Life Demo', 0 BEGIN ; Patch the title string to say '=JRMZ=' instead of Mattel. CALL PRINT.FLS ; Write string (ptr in R5) DECLE C_WHT, $23D ; White, Point to 'Mattel' in top-left STRING '=JRMZ=' ; Guess who? :-) STRING ' Productions' BYTE 0 CALL PRINT.FLS ; Write string (ptr in R1) DECLE C_WHT, $2D0 ; White, Point to 'Mattel' in lower-right STRING '2002 =JRMZ=' ; Guess who? :-) BYTE 0 ; Done. RETURN ; Return to EXEC for title screen display ENDP ;; ======================================================================== ;; ;; MAIN: Here's our main program code. ;; ;; ======================================================================== ;; MAIN: PROC ; ------------------------------------------------------------- ; ; Prepare to jettison EXEC support. Also, initialize random ; ; number generator from everything in memory. ; ; ------------------------------------------------------------- ; DIS MVII #$100, R4 MVII #$130, R3 @@rnd: ADD@ R4, R0 ADD@ R4, R1 DECR R3 BNEQ @@rnd TSTR R0 BNEQ @@r0_ok MOVR PC, R0 @@r0_ok: TSTR R1 BNEQ @@r1_ok MOVR PC, R1 @@r1_ok: MVO R0, RNDLO MVO R1, RNDHI MVII #STACK, R6 MVII #MYISR, R0 MVO R0, ISRVEC SWAP R0 MVO R0, ISRVEC+1 EIS ; ------------------------------------------------------------- ; ; Initialize the display to colored-squares mode. ; ; ------------------------------------------------------------- ; MVII #$00F0, R1 ; Initialize by filling the screen. MVII #$0200, R4 ; MVII #$37FF, R0 ; CALL FILLMEM ; Fill screen w/ grey colored squares IF (HGLIDER OR VGLIDER) = 0 ; No gliders? Do random. ; ------------------------------------------------------------- ; ; Fill our GEN buffers with random data. ; ; ------------------------------------------------------------- ; MVII #192, R1 MVII #GEN0, R4 @@rfill: MVII #8, R0 CALL RAND MVO@ R0, R4 DECR R1 BNEQ @@rfill ELSE ; do either horizontal glider or vertical glider MVII #192, R1 MVII #GEN0, R4 CALL FILLZERO IF HGLIDER NE 0 ; ------------------------------------------------------------- ; ; Put a horizontal glider near top right. ; ; ------------------------------------------------------------- ; MVII #%0100100, R0 MVO R0, GEN0 + 1 + 4*2 MVII #%0000010, R0 MVO R0, GEN0 + 1 + 4*3 MVII #%0100010, R0 MVO R0, GEN0 + 1 + 4*4 MVII #%0011110, R0 MVO R0, GEN0 + 1 + 4*5 ENDI ;HGLIDER IF VGLIDER NE 0 ; ------------------------------------------------------------- ; ; Put a vertical glider near top left. ; ; ------------------------------------------------------------- ; MVII #%0101000, R0 MVO R0, GEN0 + 3 + 4*5 MVII #%0000100, R0 MVO R0, GEN0 + 3 + 4*6 MVII #%0000100, R0 MVO R0, GEN0 + 3 + 4*7 MVII #%0100100, R0 MVO R0, GEN0 + 3 + 4*8 MVII #%0011100, R0 MVO R0, GEN0 + 3 + 4*9 ENDI ;VGLIDER ENDI ; VGLIDER OR HGLIDER ; ------------------------------------------------------------- ; ; Set up the initial "life" state. ; ; ------------------------------------------------------------- ; MVII #GEN0, R0 MVO R0, CURGEN MVII #GEN1, R0 MVO R0, NXTGEN MVII #1, R0 MVO R0, GCNUM ; ------------------------------------------------------------- ; ; Decide if we will be polling the ECScable every iteration. ; ; ------------------------------------------------------------- ; MVII #@@main_loop, R1 MVI EC_LOC, R0 CMPI #EC_MAG,R0 BNEQ @@no_poll MVII #@@ec_poll, R1 @@no_poll: MVO R1, LOOP JR R1 ; Start first loop iteration. ; ------------------------------------------------------------- ; ; If ECScable Monitor is found, we'll call EC_POLL after ; ; every generation. Otherwise, we won't. ; ; ------------------------------------------------------------- ; @@ec_poll: CALL EC_POLL ; ------------------------------------------------------------- ; ; Pause between generations if controller is pressed. ; ; ------------------------------------------------------------- ; @@main_loop: MVI $1FE, R0 AND $1FF, R0 XORI #$FF, R0 BNEQ @@main_loop ; ------------------------------------------------------------- ; ; Benchmarking: Reset the tick counter. ; ; ------------------------------------------------------------- ; CLRR R0 MVO R0, TICKS ; ------------------------------------------------------------- ; ; Increment our color selection. Cycle from 1 thru 6. ; ; ------------------------------------------------------------- ; MVI GCNUM, R1 INCR R1 CMPI #6, R1 BLE @@gcok MVII #1, R1 @@gcok: MVO R1, GCNUM ADDI #COLORS,R1 MVI@ R1, R1 MVO R1, GENCOL ; ------------------------------------------------------------- ; ; Show current generation and calculate the next one. ; ; ------------------------------------------------------------- ; CALL SHOWLIFE CALL CALCLIFE ; ------------------------------------------------------------- ; ; Swap our double-buffer pointers. ; ; ------------------------------------------------------------- ; MVI CURGEN, R0 MVI NXTGEN, R1 MVO R1, CURGEN MVO R0, NXTGEN ; ------------------------------------------------------------- ; ; Display the # of ticks it took to calculate in lower right. ; ; ------------------------------------------------------------- ; MVI TICKS, R0 MVII #C_BLK, R1 MVII #$2EE, R4 CALL HEX8 ; ------------------------------------------------------------- ; ; Loop back to either @@main_loop or @@ec_poll. ; ; ------------------------------------------------------------- ; MVI LOOP, PC @@spin DECR PC ; can't get here. ENDP ;; ======================================================================== ;; ;; MYISR -- Just keep the display on and increment the tick counter. ;; ;; ======================================================================== ;; MYISR PROC PSHR R5 MVO R0, $20 ; Keep display on MVI $21, R0 ; Keep it in color-stack mode MVII #C_GRY, R0 MVO R0, $28 ; Set color-stack, border to grey MVO R0, $2C MVI TICKS, R0 INCR R0 MVO R0, TICKS PULR PC ENDP ;; ======================================================================== ;; ;; LIFE RULES: :: ;; ;; ;; -- A dead cell with exactly three live neighbors becomes a live ;; ;; cell (birth). ;: ;; ;; ;; -- A live cell with two or three live neighbors stays alive ;; ;; (survival). ;; ;; ;; ;; -- In all other cases, a cell dies or remains dead (overcrowding ;; ;; or loneliness). ;; ;; ;; ;; OUR WORLD ;; ;; We display a 32 x 24 "world" for the cells to live in. The world ;; ;; is "toroidal", in that the top connects to the bottom, and the ;; ;; left connects to the right. This world holds 768 cells. ;; ;; ;; ;; The cell information for the current and next generations are ;; ;; held in two bitmaps in 8-bit scratch RAM. We store 1 bit per ;; ;; cell, so each bitmap occupies 96 bytes. The bitmap is stored in ;; ;; "little endian" order -- the leftmost cell onscreen is in bit 0 ;; ;; of the first byte, not bit 7 as you may expect. ;; ;; ;; ;; The engine works by calculating the new generation from the last ;; ;; generation's state according to the rules above. It then swaps ;; ;; the two generations. We don't actually move anything in memory ;; ;; when we swap -- we keep track of which world is which with ;; ;; pointers. ;; ;; ;; ;; Once a new generation is calculated, we display it. That's it. ;; ;; ======================================================================== ;; ;; ======================================================================== ;; ;; LDTBL -- Live/Dead decision table. ;; ;; ;; ;; This lookup table implements the rules for deciding whether a cell ;; ;; lives or dies. A 0 means that the cell is dead in the next generation ;; ;; and a $8000 means that it is alive. The table is indexed by a 4-bit ;; ;; value. Bits 3 thru 1 hold the neighbor+self count, and bit 0 holds ;; ;; whether the cell is alive in the present generation. ;; ;; ======================================================================== ;; LDTBL PROC DECLE 0 ; 0 neighbors, dead -> dead DECLE 0 ; na DECLE 0 ; 1 neighbors, dead -> dead DECLE 0 ; 0 neighbors, live -> dead DECLE 0 ; 2 neighbors, dead -> dead DECLE 0 ; 1 neighbors, live -> dead DECLE $8000 ; 3 neighbors, dead -> live DECLE $8000 ; 2 neighbors, live -> live DECLE 0 ; 4 neighbors, dead -> dead DECLE $8000 ; 3 neighbors, live -> live DECLE 0 ; 5 neighbors, dead -> dead DECLE 0 ; 4 neighbors, live -> dead DECLE 0 ; 6 neighbors, dead -> dead DECLE 0 ; 5 neighbors, live -> dead DECLE 0 ; 7 neighbors, dead -> dead DECLE 0 ; 6 neighbors, live -> dead DECLE 0 ; 8 neighbors, dead -> dead DECLE 0 ; 7 neighbors, live -> dead DECLE 0 ; na DECLE 0 ; 8 neighbors, live -> dead ENDP ;; ======================================================================== ;; ;; EXP -- expands an 8 1-bit fields into 8 2-bit fields. ;; ;; ;; ;; This lookup table takes an 8-bit value and expands it to a 16 bit ;; ;; value by inserting 0s between each bit. This effectively takes the ;; ;; 8 1-bit fields of the original number and turns them into 8 2-bit ;; ;; fields. ;; ;; ;; ;; We use this in two places. The first is where we add across three ;; ;; rows of cells. We load a single byte containing 8 cells, ane expand ;; ;; that to 8 2-bit fields. We add the expanded bytes for all three rows ;; ;; together, yeilding 8 sums that range from 00b through 11b (0 to 3). ;; ;; Those 8 fields are then unpacked to bytes. ;; ;; ;; ;; The second place is in the display routine. We load pixel information ;; ;; for two rows, and then interleave the pixels for those rows. This ;; ;; places pixels that will be displayed vertically relative to each other ;; ;; in adjacent bit positions. This makes it easy to then generate the ;; ;; colored-square information, since colored-squares mode stores 2x2 ;; ;; in each card and thus needs pixel information from two rows. ;; ;; ======================================================================== ;; EXP PROC DECLE $0000, $0001, $0004, $0005, $0010, $0011, $0014, $0015 DECLE $0040, $0041, $0044, $0045, $0050, $0051, $0054, $0055 DECLE $0100, $0101, $0104, $0105, $0110, $0111, $0114, $0115 DECLE $0140, $0141, $0144, $0145, $0150, $0151, $0154, $0155 DECLE $0400, $0401, $0404, $0405, $0410, $0411, $0414, $0415 DECLE $0440, $0441, $0444, $0445, $0450, $0451, $0454, $0455 DECLE $0500, $0501, $0504, $0505, $0510, $0511, $0514, $0515 DECLE $0540, $0541, $0544, $0545, $0550, $0551, $0554, $0555 DECLE $1000, $1001, $1004, $1005, $1010, $1011, $1014, $1015 DECLE $1040, $1041, $1044, $1045, $1050, $1051, $1054, $1055 DECLE $1100, $1101, $1104, $1105, $1110, $1111, $1114, $1115 DECLE $1140, $1141, $1144, $1145, $1150, $1151, $1154, $1155 DECLE $1400, $1401, $1404, $1405, $1410, $1411, $1414, $1415 DECLE $1440, $1441, $1444, $1445, $1450, $1451, $1454, $1455 DECLE $1500, $1501, $1504, $1505, $1510, $1511, $1514, $1515 DECLE $1540, $1541, $1544, $1545, $1550, $1551, $1554, $1555 DECLE $4000, $4001, $4004, $4005, $4010, $4011, $4014, $4015 DECLE $4040, $4041, $4044, $4045, $4050, $4051, $4054, $4055 DECLE $4100, $4101, $4104, $4105, $4110, $4111, $4114, $4115 DECLE $4140, $4141, $4144, $4145, $4150, $4151, $4154, $4155 DECLE $4400, $4401, $4404, $4405, $4410, $4411, $4414, $4415 DECLE $4440, $4441, $4444, $4445, $4450, $4451, $4454, $4455 DECLE $4500, $4501, $4504, $4505, $4510, $4511, $4514, $4515 DECLE $4540, $4541, $4544, $4545, $4550, $4551, $4554, $4555 DECLE $5000, $5001, $5004, $5005, $5010, $5011, $5014, $5015 DECLE $5040, $5041, $5044, $5045, $5050, $5051, $5054, $5055 DECLE $5100, $5101, $5104, $5105, $5110, $5111, $5114, $5115 DECLE $5140, $5141, $5144, $5145, $5150, $5151, $5154, $5155 DECLE $5400, $5401, $5404, $5405, $5410, $5411, $5414, $5415 DECLE $5440, $5441, $5444, $5445, $5450, $5451, $5454, $5455 DECLE $5500, $5501, $5504, $5505, $5510, $5511, $5514, $5515 DECLE $5540, $5541, $5544, $5545, $5550, $5551, $5554, $5555 ENDP ;; ======================================================================== ;; ;; CALCLIFE ;; ;; Scan CURGEN and generate NXTGEN. Treats screen as a torus. ;; ;; Requires 4 words of temporary storage for pointers, and a 32-byte ;; ;; temporary buffer (ROWB). ;; ;; ======================================================================== ;; CALCLIFE PROC PSHR R5 @@prev EQU CLTMP + 0 ; pointer to row - 1 (modulo screen height) @@curr EQU CLTMP + 1 ; pointer to current row @@next EQU CLTMP + 2 ; pointer to row + 1 (modulo screen height) @@outp EQU CLTMP + 3 ; output pointer MVI CURGEN, R0 ; Get pointer to current generation MVO R0, @@curr ; Make top row "current" row ADDI #4, R0 ; MVO R0, @@next ; Make second row "next" row ADDI #4*22, R0 ; MVO R0, @@prev ; Make last row "previous" row MVI NXTGEN, R1 ; Get pointer to next generation MVO R1, @@outp ; Make that the output pointer ; A B C ; D E F ; G H I ; ------------------------------------------------------------- ; ; The row loop iterates 24 times, once for each row's worth ; ; of output. We tally up neighbor counts on a per-row basis ; ; and then generate output for that row. ; ; ------------------------------------------------------------- ; MVII #24, R5 @@row_loop: PSHR R5 ; ------------------------------------------------------------- ; ; Tally neighbors vertically. No side-to-side summing is ; ; done here. This loop generates 32 2-bit sums in a packed ; ; manner, and expands them to 8-bit sums. ; ; ------------------------------------------------------------- ; MVII #4, R2 MVII #ROWB, R5 MVII #$0303, R0 ; Mask for pairs of sums @@clp1: ; ------------------------------------------------------------- ; ; Get 8 cells from prev row and use that to start the sum. ; ; ------------------------------------------------------------- ; MVI @@prev, R4 ; 10 Get prev ptr MVI@ R4, R3 ; 8 Read 8 bits MVO R4, @@prev ; 11 Save updated ptr ADDI #EXP, R3 ; 8 Index into EXP table MVI@ R3, R1 ; 8 Expand to 2-bit fields ;---- ; 45 ; ------------------------------------------------------------- ; ; Get 8 cells from curr row and add that to the sum. ; ; ------------------------------------------------------------- ; MVI @@curr, R4 ; 10 Get curr ptr MVI@ R4, R3 ; 8 Read 8 bits MVO R4, @@curr ; 11 Save updated ptr ADDI #EXP, R3 ; 8 Index into EXP table ADD@ R3, R1 ; 8 Accumulate 2-bit sums ;---- ; 45 ; ------------------------------------------------------------- ; ; Get 8 cells from next row and add that to the sum. ; ; ------------------------------------------------------------- ; MVI @@next, R4 ; 10 Get next ptr MVI@ R4, R3 ; 8 Read 8 bits MVO R4, @@next ; 11 Save updated ptr ADDI #EXP, R3 ; 8 Index into EXP table ADD@ R3, R1 ; 8 Accumulate 2-bit sums ;---- ; 45 MOVR R5, R4 ; 6 R4 used for sums 0..3 ADDI #4, R5 ; 8 R5 used for sums 4..7 ;---- ; 14 MOVR R1, R3 ; 6 \__ Mask out sums 4 and 0 ANDR R0, R3 ; 6 / MVO@ R3, R4 ; 9 Write out sum 0 SWAP R3, 1 ; 6 MVO@ R3, R5 ; 9 Write out sum 4 SLR R1, 2 ; 8 Advance to next 2 sums ;---- ; 44 MOVR R1, R3 ; 6 \__ Mask out sums 5 and 1 ANDR R0, R3 ; 6 / MVO@ R3, R4 ; 9 Write out sum 1 SWAP R3, 1 ; 6 MVO@ R3, R5 ; 9 Write out sum 5 SLR R1, 2 ; 8 Advance to next 2 sums ;---- ; 44 MOVR R1, R3 ; 6 \__ Mask out sums 6 and 2 ANDR R0, R3 ; 6 / MVO@ R3, R4 ; 9 Write out sum 2 SWAP R3, 1 ; 6 MVO@ R3, R5 ; 9 Write out sum 6 SLR R1, 2 ; 8 Advance to next 2 sums ;---- ; 44 ANDR R0, R1 ; 6 Mask out sums 7 and 3 MVO@ R1, R4 ; 9 Write out sum 3 SWAP R1, 1 ; 6 MVO@ R1, R5 ; 9 Write out sum 7 ;---- ; 30 ;==== ; 311 = 45*3 + (36*3+30) + 14 DECR R2 ; 6 BNEQ @@clp1 ; 9/7 ;---- ;1304 total MVI @@curr, R0 ; \ SUBI #4, R0 ; |-- Rewind current pointer MVO R0, @@curr ; / ; ------------------------------------------------------------- ; ; The neighbor count for a given cell is the sum of the vert. ; ; sums for the previous column, current column, and next ; ; column. (Note that this includes the cell itself in the ; ; count. We adjust for this later.) ; ; ; ; In this loop, we compute the horizontal sums by running a ; ; 3-entry "sliding window" over the row buffer. We start off ; ; with R0 and R1 holding the "prev" and "curr" relative to ; ; our output, and the loop starts by reading "next" into R2. ; ; The sum of these three is written to the output. ; ; ; ; The loop proceeds to allow "prev", "curr" and "next" ; ; rotate through R0, R1, and R2. The loop is unrolled 3x so ; ; that we never explicitly move values around. We peel off ; ; two loop iterations because 32 MOD 3 == 2. ; ; ; ; Notice how "prev" for the first pixel is the last pixel, ; ; and "next" for the last pixel is the first pixel. Since ; ; we overwrite pixels as we go, we need to save the first ; ; pixels initial value on the stack prior to the loop. ; ; ------------------------------------------------------------- ; MVI ROWB+31, R0 ; Read last pixel as 'prev' MVII #ROWB, R4 ; Point to first pixel (ROWB[0]) MOVR R4, R5 ; R5 is output pointer. MVI@ R4, R1 ; Read first pixel into 'curr' PSHR R1 ; will ROWB[0] for last pixel MVII #10, R3 ; Iterate 10 times (30 pixels) @@clp2: ; prev=R0 curr=R1 next=R2 MVI@ R4, R2 ; 8 ADDR R1, R0 ; 6 ADDR R2, R0 ; 6 MVO@ R0, R5 ; 9 ;---- ; 29 ; prev=R1 curr=R2 next=R0 MVI@ R4, R0 ; 8 ADDR R2, R1 ; 6 ADDR R0, R1 ; 6 MVO@ R1, R5 ; 9 ;---- ; 29 ; prev=R2 curr=R0 next=R1 MVI@ R4, R1 ; 8 ADDR R0, R2 ; 6 ADDR R1, R2 ; 6 MVO@ R2, R5 ; 9 ;---- ; 29 DECR R3 ; 6 BNEQ @@clp2 ; 9/7 ;---- ;1018 = 10 * (29*3 + 15) - 2 ; do 2nd to last pixel. ; prev=R0 curr=R1 next=R2 MVI@ R4, R2 ; 8 ADDR R1, R0 ; 6 ADDR R2, R0 ; 6 MVO@ R0, R5 ; 9 ; do last pixel on row. ; prev=R1 curr=R2 next=@SP ADDR R2, R1 ; ADD@ SP, R1 ; add w/ first pixel on row MVO@ R1, R5 ; ; ------------------------------------------------------------- ; ; Now, make live/die decision for each pixel in row. ; ; ; ; We do this by taking the "neighbor + self" count for each ; ; cell, and merging it with the cell's live/dead status. ; ; We then look this up in a lookup-table to decide whether ; ; the cell lives or dies. ; ; ; ; Because each row only has 32 cells, we store the entire row ; ; bitmap in two registers. Also, because we produce the new ; ; generation as we consume the old, we can fill in the new ; ; generation in the same two registers as we go. ; ; ; ; NOTE: Leftmost cell on screen is in bit 0. That is, the ; ; bitmap is the mirror image horizontally of what displayed ; ; on the screen. ; ; ; ; This loop is unrolled 2x for speed. ; ; ; ; Optimization note: The CLRC doesn't need to be inside the ; ; loop because none of our operations in the loop will set C. ; ; ------------------------------------------------------------- ; MVI @@curr, R4 SDBD MVI@ R4, R0 SDBD MVI@ R4, R1 MVII #LDTBL, R5 MVII #ROWB, R4 MVII #16, R2 CLRC ; clear out the carry @@clp3: RRC R1, 1 ; \__ Advance one cell. 0 to R1's MSB, RRC R0, 1 ; / leftmost cell to Carry bit MVI@ R4, R3 ; Get neighbor count (includes self) RLC R3, 1 ; LSB of index contains curr cell. ADDR R5, R3 ; Index into live/die table XOR@ R3, R1 ; Generate new cell in MSB of R1. RRC R1, 1 ; \__ Advance one cell. 0 to R1's MSB, RRC R0, 1 ; / leftmost cell to Carry bit MVI@ R4, R3 ; Get neighbor count (includes self) RLC R3, 1 ; LSB of index contains curr cell. ADDR R5, R3 ; Index into live/die table XOR@ R3, R1 ; Generate new cell in MSB of R1. DECR R2 BNEQ @@clp3 ; ------------------------------------------------------------- ; ; Write out the 32 new cells. ; ; ------------------------------------------------------------- ; MVI @@outp, R4 MVO@ R0, R4 SWAP R0 MVO@ R0, R4 NOP MVO@ R1, R4 SWAP R1 MVO@ R1, R4 MVO R4, @@outp ; ------------------------------------------------------------- ; ; Move down a row. On the last row, make the "next" pointer ; ; point to the topmost row of the current generation. ; ; ------------------------------------------------------------- ; PULR R5 MVI @@curr, R0 ; Get current pointer MVO R0, @@prev ; ... and make it the new "previous" ADDI #4, R0 ; Move current down one row MVO R0, @@curr ; ... and make it the new "current" ADDI #4, R0 ; Move current down one more row CMPI #2, R5 ; Are we starting last iteration? BNEQ @@not_last ; No: Then make current+2 new "next" MVI CURGEN, R0 ; Yes: Make top of generation "next" @@not_last MVO R0, @@next DECR R5 ; Do next iteration if there is one. BNEQ @@row_loop PULR PC ENDP ;; ======================================================================== ;; ;; PMASK -- Pixel masks for each of the four colored square pixels. ;; ;; ======================================================================== ;; PMASK PROC @@0 EQU $0007 @@1 EQU $0038 @@2 EQU $01C0 @@3 EQU $2600 ENDP ;; ======================================================================== ;; ;; COLORS -- Color masks for the four pixels. Includes "color-squares" ;; ;; bit set in bit 12. ;; ;; ======================================================================== ;; COLORS: PROC DECLE $0000 + $1000 DECLE $0249 + $1000 DECLE $0492 + $1000 DECLE $06DB + $1000 DECLE $2124 + $1000 DECLE $236D + $1000 DECLE $25B6 + $1000 DECLE $27FF + $1000 ENDP ;; ======================================================================== ;; ;; CSQDECODE -- Decodes the "interleaved bitmap" we generate into a ;; ;; colored-squares pixel AND-mask. AND this with a color to ;; ;; generate the inforation you need for BACKTAB. ;; ;; ;; ;; Pixel order in table index: 1 3 0 2. ;; ;; ======================================================================== ;; CSQDECODE PROC @@0000 DECLE $1000 + 0 @@0001 DECLE $1000 + PMASK.2 @@0010 DECLE $1000 + PMASK.0 @@0011 DECLE $1000 + PMASK.0 + PMASK.2 @@0100 DECLE $1000 + PMASK.3 @@0101 DECLE $1000 + PMASK.3 + PMASK.2 @@0110 DECLE $1000 + PMASK.0 + PMASK.3 @@0111 DECLE $1000 + PMASK.0 + PMASK.3 + PMASK.2 @@1000 DECLE $1000 + PMASK.1 @@1001 DECLE $1000 + PMASK.1 + PMASK.2 @@1010 DECLE $1000 + PMASK.1 + PMASK.0 @@1011 DECLE $1000 + PMASK.1 + PMASK.0 + PMASK.2 @@1100 DECLE $1000 + PMASK.1 + PMASK.3 @@1101 DECLE $1000 + PMASK.1 + PMASK.3 + PMASK.2 @@1110 DECLE $1000 + PMASK.1 + PMASK.0 + PMASK.3 @@1111 DECLE $1000 + PMASK.1 + PMASK.0 + PMASK.3 + PMASK.2 ENDP ;; ======================================================================== ;; ;; SHOWLIFE ;; ;; Go through the current generation's bitmap and display using colored- ;; ;; squares mode. ;; ;; ======================================================================== ;; SHOWLIFE PROC PSHR R5 MVI CURGEN, R4 ; Get pointer to current generation MVII #$202, R5 ; Point to top-left 2x2 block MVI GENCOL, R1 ; Get color for this generation ; ------------------------------------------------------------- ; ; ROW_LP generates two rows of pixels each iteration. ; ; ------------------------------------------------------------- ; @@row_lp: MVII #4, R0 ; Iterate 4 times per row. ; ------------------------------------------------------------- ; ; COL_LP outputs four rows of pixels each iteration. ; ; ------------------------------------------------------------- ; @@col_lp: MVI@ R4, R3 ADDI #EXP, R3 MVI@ R3, R3 SLL R3, 1 ADDI #3, R4 MVI@ R4, R2 SUBI #4, R4 ADDI #EXP, R2 ADD@ R2, R3 ; ------------------------------------------------------------- ; ; At this point, R3 contains 16 pixels, 8 pixels in 2 rows. ; ; Let's display them. Recall LSBs are leftmost pixels. ; ; ------------------------------------------------------------- ; MOVR R3, R2 ; Copy pixels to R2 ANDI #$F, R2 ; Get first 2x2 block ADDI #CSQDECODE, R2 ;\__ Get proper 2x2 mask MVI@ R2, R2 ;/ ANDR R1, R2 ; Convert to desired color MVO@ R2, R5 ; Store to display SLR R3, 2 ;\__ Shift to next 2x2 pixel set SLR R3, 2 ;/ MOVR R3, R2 ; Copy pixels to R2 ANDI #$F, R2 ; Get 2nd 2x2 block ADDI #CSQDECODE, R2 ;\__ Get proper 2x2 mask MVI@ R2, R2 ;/ ANDR R1, R2 ; Convert to desired color MVO@ R2, R5 ; Store to display SLR R3, 2 ;\__ Shift to next 2x2 pixel set SLR R3, 2 ;/ MOVR R3, R2 ; Copy pixels to R2 ANDI #$F, R2 ; Get 3rd 2x2 block ADDI #CSQDECODE, R2 ;\__ Get proper 2x2 mask MVI@ R2, R2 ;/ ANDR R1, R2 ; Convert to desired color MVO@ R2, R5 ; Store to display SLR R3, 2 ;\__ Shift to next 2x2 pixel set SLR R3, 2 ;/ ADDI #CSQDECODE, R3 ;\__ Get proper 2x2 mask MVI@ R3, R2 ;/ ANDR R1, R2 ; Convert to desired color MVO@ R2, R5 ; Store to display DECR R0 BNEQ @@col_lp ADDI #4, R4 ; advance one row on input ADDI #4, R5 ; skip border stuff on output CMPI #$2EE, R5 ; At end of screen yet? BLT @@row_lp PULR PC ENDP ;; ======================================================================== ;; ;; LIBRARY INCLUDES ;; ;; ======================================================================== ;; INCLUDE "../library/print.asm" ; PRINT.xxx routines INCLUDE "../library/fillmem.asm" ; CLRSCR/FILLZERO/FILLMEM INCLUDE "../library/rand.asm" ; RAND INCLUDE "../library/hexdisp.asm" ; HEX8 ;* ======================================================================== *; ;* This program is free software; you can redistribute it and/or modify *; ;* it under the terms of the GNU General Public License as published by *; ;* the Free Software Foundation; either version 2 of the License, or *; ;* (at your option) any later version. *; ;* *; ;* This program is distributed in the hope that it will be useful, *; ;* but WITHOUT ANY WARRANTY; without even the implied warranty of *; ;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *; ;* General Public License for more details. *; ;* *; ;* You should have received a copy of the GNU General Public License *; ;* along with this program; if not, write to the Free Software *; ;* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *; ;* ======================================================================== *; ;* Copyright (c) 2002, Joseph Zbiciak *; ;* ======================================================================== *;