home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
mbug
/
mbug017.arc
/
GRAPHICS.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1979-12-31
|
12KB
|
300 lines
program graphics;
{This is my first attempt at graphics on a Microbee from TurboPascal.
What we have here is a set of procedures for lores graphics: LoRes, Dot,
Point, Line, Arc, and Box. For a description of each, read the comments
within the procedures. Included with them are: RestorePCG (which is pretty
well self-explanatory), Swap (which is used by the Line procedure), and
Rad (which is in there because I'm too lazy to reach for a calculator!).
Everything seems to work well, but the Arc procedure is pretty slow; any
speed improvements without any loss in flexibility (and without resorting to
machine code!) I really would like to see. The Line procedure could probably
be improved, but it performs quite well as it is.
Eric Reaburn 6 November 1985 7:20 pm
}
var
x,y:byte;
ans:char;
procedure RestorePCG;
{Restore PCG characters by looking at the data for normal characters and
inverting this data. Data is hidden "under" screen RAM, so set the ROMREAD
latch to get at it.}
const
chardat = $F000;
pcg = $F800;
var
k:integer;
begin
port [11] := 1; {set bit 0 (ROMREAD latch) to read character data}
for k:=0 to $7FF do
mem [pcg+k] := mem [chardat+k] xor 255;
port [11] := 0; {reset ROMREAD latch to let screen be read if need be}
end; {RestorePCG}
procedure LoRes;
{Set up PCG characters for lores graphics use (only last 64 used). PCG
character is 2 lores pixels wide and 3 high, i.e. pcg character is split
into 6 blocks, top four being 4 bits wide and 4 hires pixels high each &
bottom two being 3 hires pixels high (character is 8 by 11 pixels;
4+4+3 = 11 high, 4+4 = 8 wide).}
const
start_PCG = $FC00; {change this to use any 64 characters in PCG RAM}
needed = 64; {number of programmable characters needed}
leftnybble = $F0; {value of left nybble of byte }
rightnybble = $0F; { " " right " " " }
var
chstart:integer;
k,used,part1,part2,part3,section,num:byte;
{__________________________________}
procedure Fill (from:integer; amount,valu:byte);
{Fill an area of memory with "valu" starting from "from" and going for
"amount" bytes (ie "from" to "from" + "amount" - 1}
var
k:integer;
begin
for k := from to from + amount - 1 do
mem [k] := valu;
end; {Fill}
{__________________________________}
function BiggestDivisor (var num:byte) :byte;
{Return largest value divisor for "num", reducing "num" by this value in the
process. Values tested for are 32, 16, 8, 4, 2, & 1.}
var
test,t,orgnum:byte;
begin
orgnum := num;
test := 64;
repeat
test := test div 2;
t := num div test;
if t <> 0 then begin
BiggestDivisor := test;
num := num - test;
end;
until num <> orgnum;
end; {BiggestDivisor}
{__________________________________}
begin {LoRes}
used := 0;
repeat
chstart := start_PCG + used * 16;
if used = 0
then Fill(chstart,11,0)
else begin
num := used; part1 := 0; part2 := 0; part3 := 0;
repeat
section := BiggestDivisor(num);
case section of
1: part1 := part1 or leftnybble;
2: part1 := part1 or rightnybble;
4: part2 := part2 or leftnybble;
8: part2 := part2 or rightnybble;
16: part3 := part3 or leftnybble;
32: part3 := part3 or rightnybble;
end; {case}
until num=0;
Fill(chstart, 4, part1); {fill first 4 bytes of character}
Fill(chstart+4, 4, part2); {...next 4 bytes... }
Fill(chstart+8, 3, part3); {...last 3 bytes. }
end;
used := used + 1;
until used = needed;
end; {LoRes}
{____________________________________________________________________________}
const
scrstart = $F000; {first location of screen memory}
firstlores = 192; {ASCII code of first lores character (which is a space)}
{change this along with start_PCG above to use any 64 }
{characters in PCG RAM }
procedure Dot (on:boolean; x,y:byte);
{If "on" is true then set a lores pixel at coordinates (x,y), else erase it.
X is 1 to 160, Y is 1 to 72.}
var
scrpos:integer; {screen position}
charcode,scrx,scry,tx,ty,testbyte,nybble:byte;
begin
if (x in [1..160]) and (y in [1..72])
then begin
x := x - 1; y := y - 1;
scrx := x shr 1; scry := y div 3; {scrx: 0-79, scry: 0-23}
scrpos := scrstart + scry * 80 + scrx;
charcode := mem [scrpos];
if charcode < firstlores then charcode := firstlores;
tx := x - scrx - scrx; {tx: 0-1}
ty := y - scry - scry - scry; {ty: 0-2}
testbyte := mem [scrstart + charcode * 16 + (ty shl 2)]; {sample byte from lores pixel}
if tx = 0 then nybble := $F0 {look at left side}
else nybble := $0F; {look at right side}
if on
then begin
if (testbyte and nybble) = 0 {turn pixel on if currently off}
then mem [scrpos] := charcode + (1 shl (ty+ty+tx));
end
else begin
if (testbyte and nybble) <> 0 {turn pixel off if currently on}
then mem [scrpos] := charcode - (1 shl (ty+ty+tx));
end;
end;
end; {Dot}
{____________________________________________________________________________}
function Point (x,y:byte) :boolean;
{Test to see whether a lores pixel is on at coordinates (x,y). True if it is.}
var
scrx,scry,charcode,tx,ty,testbyte,nybble:byte;
begin
Point := false;
if (x in [1..160]) and (y in [1..72])
then begin
x := x - 1; y := y - 1;
scrx := x shr 1; scry := y div 3; {scrx: 0-79, scry: 0-23}
charcode := mem [scrstart + scry * 80 + scrx];
if charcode < firstlores then charcode := firstlores;
tx := x - scrx - scrx; {tx: 0-1}
ty := y - scry - scry - scry; {ty: 0-2}
testbyte := mem [scrstart + charcode * 16 + (ty shl 2)]; {sample byte from lores pixel}
if tx = 0 then nybble := $F0 {look at left side}
else nybble := $0F; {look at right side}
if (testbyte and nybble) <> 0
then Point := true;
end;
end; {Point}
{____________________________________________________________________________}
procedure Swap (var a,b:byte);
{Swap the values "a" and "b".}
var
temp:byte;
begin
temp := a; a := b; b := temp;
end; {Swap}
{____________________________________________________________________________}
procedure Line (draw:boolean; x1,y1,x2,y2:byte);
{If "draw" is true, then draw a line from (x1,y1) to (x2,y2); else erase a
line from (x1,y1) to (x2,y2).}
var
x,y:byte;
xreal,yreal,incx,incy:real;
lx,ly:integer;
equiv:boolean;
begin
lx := x2 - x1; ly := y2 - y1; {length horizontal; length vertical}
if (lx <> 0) or (ly <> 0)
then begin {only if BOTH lx AND ly aren't zero}
if (lx <> 0) xor (ly <> 0)
then begin {only if EITHER lx OR ly is zero}
if lx = 0
then begin {vertical line}
if ly < 0
then Swap(y1,y2);
for y:=y1 to y2 do
dot(draw,x1,y);
end
else begin {horizontal line}
if lx < 0
then Swap(x1,x2);
for x:=x1 to x2 do
dot(draw,x,y1);
end;
end
else begin {NEITHER ly NOR lx are zero}
if ((lx < 0) and (ly < 0)) or ((lx > 0) and (ly > 0))
then equiv := true {if equivalent in sign, i.e. diagonal sloping from top left to bottom right}
else equiv := false; {else it's a diagonal sloping from bottom left to top right}
if abs(lx) >= abs(ly)
then begin {horizontal length => vertical length}
incy := abs(ly / lx);
if lx < 0
then begin {swap coordinates}
Swap(x1,x2); Swap(y1,y2);
end;
yreal := y1;
for x:=x1 to x2 do
begin
dot(draw,x,round(yreal));
if equiv then yreal := yreal + incy
else yreal := yreal - incy;
end;
end
else begin {vertical length > horizontal length}
incx := abs(lx / ly);
if ly < 0
then begin
Swap(x1,x2); Swap(y1,y2);
end;
xreal := x1;
for y:=y1 to y2 do
begin
dot(draw,round(xreal),y);
if equiv then xreal := xreal + incx
else xreal := xreal - incx;
end;
end;
end;
end
else dot(draw,x1,y1);
end; {Line}
{____________________________________________________________________________}
procedure Arc (draw:boolean; centrex,centrey:integer; xradius,yradius:byte;
startangle,finishangle:real);
{If "draw" is true an arc will be drawn, else it will be erased. "centrex" and
"centrey" are the coordinates of the focus of the arc. "xradius" is half
the horizontal distance from the focus, "yradius" is half the vertical
distance from the focus."startangle" is the angle from which the arc will
start, "finishangle" is the angle at which the arc ends. Angles are measured
from the positive x-axis, as in the Unit Circle.}
var
theta,inc:real;
x,y:integer;
begin
if (startangle >= 0) and (finishangle > startangle)
then begin
inc := 1 / ((xradius shl 1) + yradius);
theta := startangle;
repeat
y := round(sin(theta) * yradius); x := round(cos(theta) * xradius);
dot(draw,centrex+x,centrey-y);
theta := theta + inc;
until theta > finishangle;
end;
end; {Arc}
{____________________________________________________________________________}
procedure Box (draw:boolean; x,y,lx,ly:byte);
{If "draw" is true draw a box, else erase it. (x,y) are coordinates of the
upper left corner of the box, "lx" is width of the box, "ly" is height of the
box.}
begin
line(draw,x,y,x+lx,y);
line(draw,x,y,x,y+ly);
line(draw,x+lx,y,x+lx,y+ly);
line(draw,x,y+ly,x+lx,y+ly);
end; {Box}
{____________________________________________________________________________}
function Rad (degrees:integer) :real;
{Convert "degrees" to radians.}
begin
Rad := (degrees / 180) * pi;
end; {Rad}
begin
clrscr; lores;
x := 1;
repeat
line(true,x,1,161-x,72);
x := x + 2;
until x > 160;
y := 1;
repeat
line(true,1,y,160,73-y);
y := y + 2;
until y > 72;
arc(false,80,36,20,13,0,2*pi);
read(kbd,ans);
restorepcg;
end.
until y > 72;
arc(false,80,36,20,13,0,2*pi);
read(kbd,ans);
restorepcg;
end.