TxtCol] ; print a table of text
call SetColr
L0: lodsw ; get Xpos
cmp ax, 0F000 ; end of table?
je ret ; exit, if so
mov [di.CurrX], ax
lodsw ; get Ypos
mov [di.CurrY], ax
L1: lodsb ; get text
cmp al, 0
je L0
call putchar ; and print it
jmp L1 ; until this line is done
-------- Print ---------------------------------- End ------------
Wit this approach, and starting from a working (empty) framework of routines,
you can design the userinterface of your software within the hour. And it will
look just fine.
The actual code is then the only thing you need to worry about.....
Having such routines, which have been tested and found reliable, you make the
user interface easily and are able to concentrate on the actual coding the
maximum amount of time. If the screen needs another layout (since you couldn't
realize the function you considered), just change a few entries in the table.
Many times just the X or Y values need some adjustment for better lining up,
or for regrouping. No need to worry about the order of the plotting. Just make
sure that the correct window is selected (for the colours) and that the table
is terminated by a TopicEnd.
Conclusion.
-----------
So far my elaboration on the VGA mode 12h. Again, I would rather use 800 x 600
but that mode is not standardised. VGA 12h is standard on all VGA cards, so
it's the best we can universally get and for many applications it is more than
enough.
Please try to make the BoxDrawing function. I will submit the "solution" to
the next issue. For future issues I will start working on an explanation about
mouse-usage. This little rodent is nice to control many applications. If the
screen is well layed out, you don't need the keyboard for data entry. Just drag
the mouse along the screen and poke him in the eye.
The bitmap data for the character generator can be obtained from
http://asmjournal.freeservers.com/supplements/univ-vmode.html
where the complete text of the article has been archived.
::/ \::::::.
:/___\:::::::.
/| \::::::::.
:| _/\:::::::::.
:| _|\ \::::::::::.
:::\_____\:::::::::::...........................................FEATURE.ARTICLE
Conway's Game of Life
by Laura Fairhead
I had the idea for this one day after stumbling upon a "gem" that
somebody had written to play life. It was small and fast and reminded
me of years ago when I had written many versions of this for the
BBC Master 128 (my love lost). Since I had never written a version
for the PC I thought that I would, and ended up spending some hours
trimming off the bytes until it is now :- 156 bytes long. I must admit
if it was not for the program that I found, this program would have been
MUCH slower than it is. After I had written the code I tested it against
the program that I had found and to my perplexity it was a great deal
slower. After some hours of frustration I found the reason:- my program
was accessing the video memory to do the bulk of its work. This must have
brought about a factor of 12 decrease in speed!!
Life is a classic game of cellular automata by John Conway. It is
played on an nxn grid of squares. Each square may be occuppied by a
cell or empty. Each 'go' of the game the player calculates the next
generation of a colony of cells by applying three simple rules:-
(i) a cell with less than 2 or more than 3 neighbours dies
(ii) a cell with 2 or 3 neighbours survives
(iii) a cell is born in a square with exactly 3 neighbours
A neighbouring square is one diagonally adjacent as well as the
normal horizontal/vertical so each square has 8 neighbouring squares.
Overview of the code
~~~~~~~~~~~~~~~~~~~~
First, note that if we define
S:=state of square in this generation (0=empty, 1=occupied)
N:=number of neighbours
S':=state of square in the next generation
then according to the rules
S'={0, if N<2 or N>3
{1, if (N=2 or N=3) and S=1
{1, if N=3
so S'=1 iff (N=2 and S=1) or N=3
this can be simplified using bitwise-OR to the dramatically simple:
S'= ( N|S=3 )
note: iff means "if and only if"
"A iff B" means that A => B and B => A
The code uses one big array with one byte for each square that
starts just after the program end. To save space it just assumes that it
can use this memory since this is generally okay. However this is
very bad practice really and it should use AH=04Ah/int 021h to adjust
the memory size and abort if not successful.
The big array actually serves the purpose of 2 arrays; bit0 of
a byte indicates the state of the square in the current generation. bit4
of each byte indicates the state of the square in the next generation.
After initialisation, generation 0 is calculated by filling about
1/4 of the array with 1's.
Now we do a loop to get the next generation. The screen is 0140h
bytes across and 0C8h bytes down. Therefore:-
-0141h -0140h -013Fh
-0001h . +0001h
+013Fh +0140h +0141h
If DI is the offset of the array which we are calculating for,
note that the neighbours can be summed as follows:-
MOV AX,[DI-0141h]
ADD AL,[DI-013Fh]
ADD AX,[DI+013Fh]
ADD AL,[DI+0141h]
ADD AL,[DI-1]
ADD AL,[DI+1]
ADD AL,AH
Note that if bit4 of any of the neighbours was set then we would
still have the correct total in the least significant 4 bits of AL.
So from here the new cell state can be calculated simply:-
OR AL,[DI]
AND AL,0Fh
CMP AL,3
And if ZF=1 now we have a set cell.
JNZ ko
OR BYTE PTR [DI],010h
ko:
When the next generation has been calculated we have done most of