This page (revision-22) was last changed on 03-Feb-2023 15:21 by Gromit 

This page was created on 14-Mar-2010 18:17 by Carsten Strotmann

Only authorized users are allowed to rename pages.

Only authorized users are allowed to delete pages.

Page revision history

Version Date Modified Size Author Changes ... Change note
22 03-Feb-2023 15:21 23 KB Gromit to previous
21 01-Feb-2011 12:53 23 KB Gromit to previous | to last

Page References

Incoming links Outgoing links

Version management

Difference between version and

At line 1 added 7 lines
!General Information
Author: Clinton Parker\\
Language: ACTION! \\
Compiler/Interpreter: ACTION \\
Published: ANALOG Computing #17 + #18\\
----
[{TableOfContents }]
At line 9 added one line
!! Part 1
At line 3 changed one line
by Clinton Parker
This is the first of a 2-part series that will introduce you to the Action! programming language, using a short example program that draws kaleidoscopic patterns on the screen. There's an old saying about barling people which, unfortunately, holds true for trying to please people as well. The problem in my case is that different readers have different levels of experience. I hope this series will please all of you at least some of the time.
At line 5 removed 10 lines
ANALOG Computing
[{Image src='introinaction.gif' width='..' height='..' align='left|center|right' style='..' class='..' }]
PDF: [An Introduction to ACTION/introinaction.PDF]
[{TableOfContents }]
This is the first of a 2-part series that will introduce you to the Action! programming language, using a short example program that draws kaleidoscopic patterns on the screen. There's an old saying about fooling people which, unfortunately, holds true for trying to please people as well. The problem in my case is that different readers have different levels of experience. I hope this series will please all of you at least some of the time.
At line 58 changed one line
!! PROCedures.
! PROCedures.
At line 75 changed one line
SetCo1or(1,8,14) : SetColor(2,8,8)
SetColor(1,8,14) : SetColor(2,8,8)
At line 77 changed one line
are simiiar to the BASIC statements:
are similar to the BASIC statements:
At line 106 changed one line
!!Walking through.
! Walking through.
At line 113 changed one line
generate new values for ax and ay (fields of record r, passed to the Gen() PROCedure). These values are used to calculate xO and yO as follows:
generate new values for ax and ay (fields of record r, passed to the Gen() PROCedure). These values are used to calculate x0 and y0 as follows:
At line 168 changed one line
Graphics C24)
Graphics (24)
At line 203 changed one line
to be executed. This sets [CH] back to 255 so that the keyboard handler won't think a key has been pressed and restores graphics mode 0 before returning to the Action! monitor.
to be executed. This sets [CH] back to 255 so that the keyboard handler won't think a key has been depressed and restores graphics mode 0 before returning to the Action! monitor.
At line 213 changed 2 lines
it's there for a reason. If you execute the loop only one set of ... time.
Although this is somewhat interesting, it isn't what I intended. The FOR loop causes "persistence" sets of points to be generated without any being erased (note that only Gen(p) is called, with color equal to one). So when the WHILE loop below this is reached, the call to Gen(e) will erase points that were plotted "persistence" interactions earlier.The values of p will always be"persistence" interactions ahead of e. Thus, you'll always have at most "persistence" sets of points on the screen at any given time.
yet. It's there for a reason. If you execute the loop below it, only one set of points will be displayed at a time. Although this is somewhat interesting, it isn't what I intended. The FOR loop causes "persistence" sets of points to be generated without any being erased (note that only Gen(p) is called, with color equal to one). So when the WHILE loop below this is reached, the call to Gen(e) will erase points that were plotted "persistence" interactions earlier.The values of p will always be "persistence" interactions ahead of e. Thus, you'll always have at most "persistence" sets of points on the screen at any given time.
At line 218 changed one line
Those of you who have an Action! cartridge should try this program. It's very small and easy to enter. The first thing you'll notice is that it doesn't run especially fast, This is mainly due to the fact that it is using the Atari operating system's PLOT subroutine. In Part II of this series, I' ll discuss some things you can do to speed it up. You may a!so wish ~[....] the best looking patterns.
Those of you who have an Action! cartridge should try this program. It's very small and easy to enter. The first thing you'll notice is that it doesn't run especially fast. This is mainly due to the fact that it is using the Atari operating system's PLOT subroutine. In Part II of this series, I'll discuss some things you can do to speed it up. You may also wish to adjust the colors on your TV set or monitor to get the best looking patterns.
Action! listing 1:
{{{
; KAL.ACT
: ANALOG Computing #17
; Copyright 1984 BY Clinton Parker
; All Rights Reserved
; last modified January 11, 1984
; Global variables
TYPE REC=[CARD cnt,ax,bx,cx,ay,by,cy]
REC p, e
CARD period, npts, persistence
PROC Gen(REC POINTER r)
BYTE x0, y0, x1, y1, ATRACT=77
; get new a
r.ax = (r.ax + r.bx) ! r.bx
r.ay = (r.ay + r.by) ! r.by
r.cnt = -1
IF r.cnt = 0 THEN ; get new b
r.bx = (r.bx + r.cx) ! r.cx
r.by = (r.by + r.cy) ! r.cy
r.cnt = period
ATRACT = 0 ; turn off attact mode
FI
x0 = r.ax RSH 9
y0 = r.ay RSH 9
IF x0 <= y0 AND y0 < 96 THEN
x1 = 191 - x0
y1 = 191 - y1
Plot(x0 + 64, y0) : Plot(x0 + 64, y1)
Plot(y0 + 64, x0) : Plot(y0 + 64, x1)
Plot(x1 + 64, y0) : Plot(x1 + 64, y1)
Plot(y1 + 64, x0) : Plot(y1 + 64, x1)
FI
RETURN
PROC Kal()
CHAR CH=764
Graphics(24)
SetColor(1,0,14) : SetColor(2,0,0)
; change for different patterns
persistence = 2500
period = 10000
p.cnt = period
p.ax = 5221
p.bx = 64449
p.cx = 3
p.ay = 57669
p.by = 64489
p.cy = 3
; copy plot record to erase record
MoveBlock(e, p, REC)
; handle persistence
color = 1
FOR npts = 1 TO persistence DO
Gen(p)
UNTIL CH # 255
OD
; draw patterns until key depressed
WHILE CH = 255 DO
color = 1 : Gen(p)
color = 0 : Gen(e)
OD
; ignore key and restore screen
CH = 255 : Graphics(0)
RETURN
}}}
----
!!Part 2.
Part 1 of this series presented a brief introduction of Action! data types and control structures using a small example program. In this part, I will expand on that example to demonstrate the use of ARRAYs in the Action! language, and increase the speed at which it runs.
This increase in speed is accomplished by providing a specialized PLOT routine instead of using the one provided in the cartridge library. The PLOT routine in the cartridge (the same one used by the OS) was written to be very flexible so that it could handle all the different graphics modes and check for illegal values. The problem with this generality is that it doesn't plot points on the screen all that fast. Since all the points plotted in KAL are in graphics mode 24, it seems reasonable to write a PLOT routine just for that mode.
All right, we now see that having our own PLOT routine would be useful, but how do we go about writing one? First, we'll start by looking at how the Atari represents graphics mode 24 data by means of a simple example. Imagine a small piece of graph paper 24 by 12. Label the top left square 0,0 and the bottom right square 23,11. Draw a line from top to bottom between squares 7 & 8 and 15 & 16, and then number these divisions starting with 0, 1, 2 for the First line; 3, 4, 5 for the next line (1) and ending with 33, 34, 35 for the last line (11). What you should have is __Figure 1.__ Except for the screen being much larger, this is exactly how the Atari generates a graphic 24 display. Each 8 square division on the graph paper represents an 8-bit byte of memory.
[{Image src='Fig1.png' align='center'}]
If we plot point 10,10 on our sheet of graph paper, we note that it is in division 31 and is the 2nd square of that division (first square of a division is 0). The computer does a similar calculation when we tell it to plot point 10,10. It first determines which byte of the screen memory we want and then it determines which bit in that byte is to be set.
Now this isn't as hard as it looks, because there are several tricks that can be used to make these calculations simple. We can calculate the offset of the first division (byte) of each line by multiplying the number of divisions (3 for our example, 40 for a graphics 24 display) by the line number. We can then calculate which division (byte) we want on that line by dividing the column by 8 (8 spaces per section, 8 bits per byte). Finally, we can compute which square (bit) is to be changed by the remainder of this division. Thus, for 10,10 example we have:
{{{
line offset = 30(10*3)
division offset = 1 (10/8)
square offset = 2 (10 MOD 8)
}}}
We now have enough information to design our PLOT routine. Remember that we are writing our own routine to increase the speed of plotting points. Multiplication and division are slow operations, so if we avoid doing these operations when we are plotting, it will greatly increase the speed of our plot routine. As turns out, we can avoid doing these operations by precomputing the line offsets and byte offsets at the beginning of the program and then use
those offsets in our plot routine. We do this by storing the precomputed offsets in ARRAYs. In the plot routine, we'll use Y as an index into the line Offset ARRAY (line) and X as an index into the byte offset ARRAY (div8).
!Walking through.
The PROCedure __Init()__ is responsible for generating the precomputed line and byte offsets. It starts by setting up the display with:
{{{
Graphics (24)
SetColor(1,0,14) : SetColor(2,0,0)
}}}
The next block of code computes the line offsets (192 of them for graphics mode 24). The variable __scrstart__ is defined to be location 88. This location contains the starting address of the screen. The variable __lineloc__ is used for computing the address of each line. Initially it is set to the value of __scrstart__ (address of first line), and is incremented by 40 each time through the loop (remember, there are 40 byte per line in graphics mode 24) to compute the address of the next line. The ARRAY line is used to store each value of __lineloc__. The next loop computes the byte offsets for all possible values of __X__ (0 to 319), and saves them in the ARRAY __div8__.
PROC __Plot()__ is passed two arguments. __X__ and __Y__, which define the point to be plotted. The byte that is to be modified on the screen is computed by adding the line address of __Y__ to the byte offset of __X__ as follows:
{{{pos = line(Y) + div8(X)}}}
The BYTE POINTER __pos__ now contains the address of the byte we want to modify. Next, we determine if we are plotting a point or erasing one by:
{{{IF color 0 THEN}}}
If __color__ is non-zero, we want to plot a point. This is done by setting the correct bit of the byte pointed to by __pos__. This is what
{{{pos^ == % m1(X&7)}}}
does. This may look very complicated, but it isn't. X&7 computes which bit is to be modified (same as X MOD 8, but much faster). This is used as the index for the ARRAY __m1__. ARRAY __m1__ is declared to contain a set of 8 masks. Each mask represents the bit to be modified for that index. Thus, when m1(X&7) is or'ed into the byte pointed to by __pos__, it sets only the bit to be plotted without affecting the other bits of that byte.
In a similar manner, if color is zero
{{{pos^ == & m2(X&7)}}}
erases point __X,Y__ on the screen. ARRAY __m2__ is declared to contain 8 masks which, when and'ed with the byte pointed to by __pos__, erase a single bit without effecting the other bits of that byte.
Using this __Plot__ routine instead of the built-in routine increases the execution speed of __Kal__ by about a factor of 3. Since none of the X values used in __Kal__ exceeds 255, you can change the declaration of __Plot__ to be:
{{{PROC Plot (BYTE x, y)}}}
This will make this version of __Kal__ run about 4 times faster than using the built-in Plot routine, but it will no longer work for all legal values of __X__.
If you haven't followed all of this, don't worry. I didn't go into any details about bit-wise operations (& and %) to keep the description brief. You can still enjoy the results (assuming you have an Action! cartridge). You can even use these two PROCs (__Init__ and __Plot__) in other programs that you write yourself.
Listing 1.
{{{
; KAL.ACT
; Copyright 1984 BY Clinton Parker
; All rights reserved
; last modified February 18, 1984
TYPE REC=[CARD (nt,ax,bx,cx,ay,by,cy]
REC p, e
CARD period, npts, persistence
CARD ARRAY line(192)
BYTE ARRAY diV8(320)
BYTE ARRAY m1=[128 64 32 16 8 4 2 1]
BYTE ARRAY m2=[$7F $BF $DF $EF $F7
$FB $FD $FE]
PROC Plot(CARD x, BYTE y)
BYTE POINTER pos
; get address of byte to modify
pos = line(y) + div8(x)
; modify only one bit of that byte
IF color#0 THEN ; plot
pos^ ==% m1(x & 7)
ELSE ; erase
pos^ ==& m2(x & 7)
FI
RETURN
PROC Init()
CARD i, scrstart=88
BYTE POINTER lineloc
Graphics(24)
SetColor(1,0,14):SetColor(2,0,0)
; get starting address of each line
; on graphics 24 screen
lineloc = scrstart
FOR i : 0 TO 191 DO
line(i) = lineloc
lineloc ==+ 40
OD
; pre-calculate small values
; divided by eight
FOR i = 0 TO 319 DO
div8(i) = i / 8
OD
RETURN
PROC Gen(REC POINTER r)
BYTE x0, y0, x1, y1, ATRACT=77
; get new a
r.ax = (r-ax + r.bx) ! r.bx
r.ay = (r.ay + r.by) ! r.by
r.cnt ==— 1
IF r.cnt=0 THEN ; get new b
r.bx = (r.bx + r.cx) ! r.cx
r.by = (r.by + r.cy) ! r.cy
r.cnt = period
ATRACT= 0 ; turn off attract mode
FI
x0 = r.ax RSH 9
y0 = r.ay RSH 9
IF x0 <= y0 and y0 < 96 THEN
x1 = 191 – x0
y1 = 191 — y0
Plot(x0+64, y0):Plot(x0+64, y1)
Plot(y0+64, x0):Plot(y0+64, x1)
Plot(x1+64, y0):Plot(x1+64, y1)
Plot(y1+64, x0):Plot(y1+64, x1)
FI
RETURN
PROC Kal()
CHAR CH=764
Init()
; change for different patterns:
persistence = 2500
period = 10000 p.cnt = period
p.ax = 5221 p.bx = 64449 p.cx=3
p.ay = 57669 p.by = 64489 p.cy=3
; copy plot record to erase record
MoveBlock(e, p, REC)
; handle persistence
color = 1
FOR npts = 1 TO persistence DO
Gen(p)
UNTIL CH#255 OD
; draw patterns until key depressed
WHILE CH=255 DO
color = 1 Gen(p)
color = 0 Gen(e)
OD
; ignore key and restore screen
CH = 255 : Graphics(0)
RETURN
}}}
----
PDF: [An Introduction to ACTION/introinaction.PDF]