;----------------------------------------------------------------------------
;ca1dcpm.asm
;1-Dimensional Cellular Automaton
;For CP/M, 8080
;Use CA1D.ASM for the Intellec MDS888 (8080) running ISIS/OSIRIS
;By Roger Arrick
;9/4/2020
;Edit 23
;V1.1
;----------------------------------------------------------------------------
;To build in CP/M 2.2:
;  asm  ca1dcpm
;  load ca1dcpm
;----------------------------------------------------------------------------
;Operation:
;There are two 1-dim arrays - 
;  ROW 1 is the new one being calculated and for display
;  ROW 2 is the previous row used for calculation.
;Starts with a single * in the middle of ROW 1.
;ROW 2 is built by applying the rule to ROW 1.
;ROW 2 is transferred to ROW 1 then sent to the console.
;ROW 2 is cleared for the next loop.
;Pressing SPACE pauses/continues display, any other character stops program.
;
;A rule is a 2 character hexidecimal byte value.
;
;Interesting rules:
;  1E Wolfram's rule 30
;  12 stacked pyramids
;  16 pyramids
;  99 right triangles
;  89 small upside triangles
;  E1, C3, 3C, 55, 66
;----------------------------------------------------------------------------

	ORG	0100H		;CP/M transient program area.

COLS	EQU	78		;# of columns on screen.  
				;Usually 80 or 132. -2 if terminal wraps automatically.

BDOS	EQU	0005h		;CP/M BDOS calls.
	
CR	EQU	0DH
LF	EQU	0AH
STOP	EQU	0		;String terminator for type routine.
BS	EQU	08H
SPACE	EQU	20H


main: 	;Setup stack.
	lxi	sp, stack	

	;Sign-on message.
	call	type
	DB	CR,LF,'One-Dimensional Cellular Automaton'
	DB	'for CP/M-80 by Roger Arrick 2020 V1.1'
	DB	CR,LF,'Try 1E, 12, 16, 55, 66, 89, 99, E1, C3',STOP

	;Set blob char.
	mvi	a,'*'		;Default blob.
	sta	BLOB
	;call	blobg		;Ask user to enter blob.
	
	;User enters rule.
	call	ruleg

	;Load initial conditions into Row 1.
	call	r1clr
	lda	BLOB
	sta	ROW1+(COLS/2)	;Put blob in middle of line.

;Main Loop -----------------------------------------------------
;
main1:
	;Display Row 1.
	call	rout
	
	;Calculate Row 2 from rule.
	call	rulee

	;Move Row 2 into Row 1.
	call	rmov

	;If user enters a space then pause, any other key then quit, else loop.
	call	const		;Has user pressed a key?
	cpi	0
	jz	main1		;No - loop.
	call	conin		;Yes - Get character.
	cpi	SPACE		;Space bar pauses.
	jnz	main		;Any other char stops.
	call	conin		;Wait for another char to resume.
	jmp	main1		;Loop forever.

;----------------------------------------------------------------------------
;Rule Execute.  Calculate Row 2 by using rule on Row 1.
;Pseudo code.
;b=column counter, c=pattern, hl=row1, de=row2 being built.
;If b counter zero then done
;Get row 1 left into bit 2 of c.
;Get row 1 center into bit 1 of c.
;Get row 1 right into bit 0 of c.
;If pattern = 000 then
;  If rule bit 0 = 1 then set blob in row 2 
;If pattern = 001 then
;  If rule bit 1 = 1 then set blob in row 2 
;If pattern = 010 then
;  If rule bit 2 = 1 then set blob in row 2 
;If pattern = 011 then
;  If rule bit 3 = 1 then set blob in row 2 
;If pattern = 100 then
;  If rule bit 4 = 1 then set blob in row 2 
;If pattern = 101 then
;  If rule bit 5 = 1 then set blob in row 2 
;If pattern = 110 then
;  If rule bit 6 = 1 then set blob in row 2 
;If pattern = 111 then
;  If rule bit 7 = 1 then set blob in row 2 
;Increment row1 pointer and row2 pointer, decrement column counter.  
;Loop
;
rulee:	;Setup variables.
	lxi	h,ROW1		;HL = Row 1 cell pointer.
	lxi	d,ROW2		;DE = Row 2 cell pointer.
	mvi	b,COLS		;B = Column count-down counter. 

ruleel:	;Check for done.
	mov	a,b		;Column counter.		
	cpi	0		;If B=0 then done.
	rz

	;Create bit pattern in C according to row 1 cells.
	mvi	c,0		;C = bit2=left, bit1=center, bit0=right

	;Get row 1 left into bit 2 of c.
	;If left is less than zero then wrap around and get far right.
	mov	a,b		;If counter = cols then at 0.
	cpi	COLS
	jz	ruleex		;At left edge.
	dcx	h		;Get left of current cell.
	mov	a,m
	inx	h
	jmp	ruleez
	;If left is beyond zero then wrap around and get far right edge.
ruleex:	lda	ROW1+COLS-1	;Get far right edge (wraparound).
	;Now set the bit.
ruleez:	cpi	SPACE
	jz	rulee2		;If space then empty, don't set bit.
	mvi	a,00000100b	;Set the bit.
	ora	c
	mov	c,a		;Save in c.
	
rulee2:	;Get row 1 center into bit 1 of c.
	mov	a,m
	cpi	SPACE
	jz	rulee3		;If space then empty, don't set bit.
	mvi	a,00000010b	;Set the bit.
	ora	c
	mov	c,a		;Save in c.

rulee3:	;Get row 1 right into bit 0 of c.
	;If right is beyond cols then wrap around and get far left edge.
	mov	a,b		;if counter = 1 then at end.
	cpi	1
	jz	ruleey
	inx	h		;Get right of current cell.
	mov	a,m
	dcx	h
	jmp	ruleew
	;At far right edge, get far left edge (wrap around).
ruleey:	lda	ROW1		;Get far left edge (wraparound).
	;Now set the bit
ruleew:	cpi	SPACE
	jz	rulee4		;If space then empty, don't set bit.
	mvi	a,00000001b	;Set the bit.
	ora	c
	mov	c,a		;Pattern bits in c.

	;Set row 2 cells according to row 1 pattern and rule bits.
rulee4:	
	;Start by clearing the cell.
	mvi	a,SPACE
	stax	d
	
	;If cell pattern 000 then 
	mov	a,c		;C=pattern bits.
	cpi	00000000b
	jnz	rulee5
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	00000001b
	jz	rulee5
	;Set row 2 cell
	lda	BLOB
	stax	d

rulee5:	;If cell pattern 001 then 
	mov	a,c		;C=pattern bits.
	cpi	00000001b
	jnz	rulee6
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	00000010b
	jz	rulee6
	;Set row 2 cell
	lda	BLOB
	stax	d
	
rulee6:	;If cell pattern 010 then 
	mov	a,c		;C=pattern bits.
	cpi	00000010b
	jnz	rulee7
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	00000100b
	jz	rulee7
	;Set row 2 cell
	lda	BLOB
	stax	d

rulee7:	;If cell pattern 011 then 
	mov	a,c		;C=pattern bits.
	cpi	00000011b
	jnz	rulee8
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	00001000b
	jz	rulee8
	;Set row 2 cell
	lda	BLOB
	stax	d

rulee8:	;If cell pattern 100 then 
	mov	a,c		;C=pattern bits.
	cpi	00000100b
	jnz	rulee9
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	00010000b
	jz	rulee9
	;Set row 2 cell
	lda	BLOB
	stax	d
	
rulee9:	;If cell pattern 101 then 
	mov	a,c		;C=pattern bits.
	cpi	00000101b
	jnz	ruleea
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	00100000b
	jz	ruleea
	;Set row 2 cell
	lda	BLOB
	stax	d
	
ruleea:	;If cell pattern 110 then 
	mov	a,c		;C=pattern bits.
	cpi	00000110b
	jnz	ruleeb
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	01000000b
	jz	ruleeb
	;Set row 2 cell
	lda	BLOB
	stax	d

ruleeb:	;If cell pattern 111 then 
	mov	a,c		;C=pattern bits.
	cpi	00000111b
	jnz	ruleec
	;then if rule bit = 1 then
	lda	RULE		;Get the rule.
	ani	10000000b
	jz	ruleec
	;Set row 2 cell
	lda	BLOB
	stax	d

ruleec:	;Update pointers and counters for next loop.
	inx	h		;Next row 1 cell location.
	inx	d		;Next row 2 cell location.
	dcr	b		;Down count.
	
	jmp	ruleel		;Loop until done with all cells in row

;----------------------------------------------------------------------------
;Get rule code from console as a 2-char hex word 0-FF (0-255) into RULE.
;Backspace restarts program.
;Destroys A.
;
ruleg:	call	type		;Ask
	db	CR,LF,'Enter Rule (00-ff, Q=quit, BS=restart): ',STOP
	call	hexin		;Get 2-char hex word from console into A
	sta	RULE
	call	type
	db	CR,LF,STOP
	ret
	
;----------------------------------------------------------------------------
;Get blob char from user.
;Destroys A.
;
blobg:	call	type		;Ask
	db	CR,LF,'Enter blob char in hex (2A=*): ',STOP
	call	hexin		;Get 2-char hex word from console into A.
	sta	BLOB
	ret

;----------------------------------------------------------------------------
;Display row 1.
;Destroys A.
;
rout:	push	h		;Save regs.
	push	b
	lxi	h,ROW1		;HL = Row.
	mvi	b,COLS		;B = Columns counter.
	call	type
	db	CR,LF,STOP
routl:	mov	a,b		;Check for done.
	cpi	0
	jz	routx
	mov	c,m		;Get char.
	call	conout		;Display it.
	inx	h		;Point to next column.
	dcr	b		;Decrement column counter.
	jmp	routl		;Loop until done.
routx:	pop	b		;Restore regs.
	pop	h
	ret
	
;----------------------------------------------------------------------------
;Clear row 1.
;Destroys A.
;
r1clr:	push	h		;Save regs.
	push	b
	lxi	h,ROW1
	mvi	b,COLS		;B = Columns counter.
r1clrl:	mov	a,b		;Check for done.
	cpi	0
	jz	r1clrx
	mvi	m,SPACE		;Clear char.  
	inx	h		;Point to next column.
	dcr	b		;Decrement column counter.
	jmp	r1clrl		;Loop until done.
r1clrx:	pop	b		;Restore regs.
	pop	h
	ret

;----------------------------------------------------------------------------
;Clear row 2.
;Destroys A.
;
r2clr:	push	h		;Save regs.
	push	b
	lxi	h,ROW2
	mvi	b,COLS		;B = Columns counter.
r2clrl:	mov	a,b		;Check for done.
	cpi	0
	jz	r2clrx
	mvi	m,SPACE		;Clear char.  
	inx	h		;Point to next column.
	dcr	b		;Decrement column counter.
	jmp	r2clrl		;Loop until done.
r2clrx:	pop	b		;Restore regs.
	pop	h
	ret

;----------------------------------------------------------------------------
;Move ROW 2 to ROW 1.
;Destroys A.
;
rmov:	push	h		;Save regs.
	push	d
	push	b
	lxi	h,ROW1		;HL = Row 1.
	lxi	d,ROW2		;DE = Row 1.
	mvi	b,COLS		;B = Columns counter.
rmov1:	mov	a,b		;Check for done.
	cpi	0
	jz	rmovx
	ldax	d		;get row 2.
	mov	m,a		;put in row 1.
	inx	h		;Point to next column.
	inx	d
	dcr	b		;Decrement column counter.
	jmp	rmov1		;Loop until done.
rmovx:	pop	b		;Restore regs.
	pop	d
	pop	h
	ret
	
;----------------------------------------------------------------------------
;Test c factor - unused debug.
;
testx:	xchg
	jc 0adb6h
	jmp 00b4h

;----------------------------------------------------------------------------
;Type string following call to CO.
;
type:	xthl			;Get pointer to string.
	push	b		;Don't destroy BC.
	mov	a,m		;Get the char.
	inx	h		;Point to next char.
	cpi	STOP		;Stop?
	jz	type1
	mov	c,a		;Send char.
	call	conout
	jmp	type+2		;Next char.
type1:	pop	b
	xthl			;Get return address back to caller.
	ret
		
;----------------------------------------------------------------------------
;Read 4-char hex byte from console.  
;No validation.
;Return in HL.
;
hexinw:	call	hexin		;Get hex byte
	mov	h,a		;High byte.
	call	hexin		;Get hex byte
	mov	l,a		;Low byte.
	ret
	
;----------------------------------------------------------------------------
;Read 2-char hex byte from console.  
;No validation.
;Return in A.
;
hexin:	push	b		;Don't destroy regs.
	call	hexine		;Get 1st char and echo.
	call	hexinc		;Convert char into value.
	mov	b,a		;Save temp.
	call	hexine		;Get 2nd char and echo.
	call	hexinc		;Convert char into value.
	mov	c,a		;Save temp.
	mov	a,b		;Put 1st value in high nibble.
	rlc
	rlc
	rlc
	rlc
	ani	0f0h
	ora	c		;Put 2nd value in low nibble.
	pop	b		;Don't destroy regs.
	ret			;All done.
;Get char and echo, into A.
hexine:	call	conin
	cpi	BS		;BS restarts program.
	jz	main
	cpi	'q'		;q=quit.
	jz	done
	cpi	'Q'		;Q=quit.
	jz	done
	mov	c,a
	cpi	BS		;BS restarts program.
	jz	main
	mov	a,c
	ret
;Convert ASCII char in A to a 4 bit value. 0-F = 0-15.
hexinc:	sui	'0'
	cpi	10
	rm
	sui	7
	ani	0fh
	ret

;----------------------------------------------------------------------------
;Console input.
;Returns character in A.
;
conin:	push	b		;Save registers from destruction.
	push	d
	push	h
	mvi	c,1		;Console input code.
	call	BDOS
	pop	h
	pop	d
	pop	b
	ret

;----------------------------------------------------------------------------
;Console input status.
;Returns A=0 if nothing, A=FF if something.
;
const:	push	b		;Save registers from destruction.
	push	d
	push	h
	mvi	c,11		;Console status code.
	call	BDOS
	pop	h
	pop	d
	pop	b
	ret
	
;----------------------------------------------------------------------------
;Console output.
;Character in C.
;
conout:	push	b		;Save registers from destruction.
	push	d
	push	h
	push	psw
	mov	e,c
	mvi	c,2		;Console output code.
	call	BDOS
	pop	psw
	pop	h
	pop	d
	pop	b
	ret
	
;----------------------------------------------------------------------------
;Return to OS.
;
done:	call	type		;New line.
	DB	CR,LF,STOP
	mvi	c,0		;Reboot code.
	jmp	BDOS
	
;----------------------------------------------------------------------------
;Storage
;	
BLOB:	ds	1		;Blob character.
RULE:	ds	1		;Rule low byte.
ROW1:	ds	COLS		;Row of elements displayed.
ROW2:	ds	COLS		;Row of elements being worked on.

;----------------------------------------------------------------------------
;Stack at end of program memory.
;
	ds	50		
stack:

;----------------------------------------------------------------------------
	end
