home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
apple
/
pcpigrfx.lbr
/
LISTINGS.ZZZ
/
LISTINGS.
Wrap
Text File
|
1987-02-26
|
40KB
|
1,340 lines
.HE APPLE GRAPHICS FROM CP/M----LISTINGS----------------TED CARNEVALE
.FO COPYRIGHT 1/3/85 BY TED CARNEVALE #
Copyrigh⌠ 1/3/8╡ b∙ T.Carnevale« Permissioε granteΣ fo≥ ì
nonprofi⌠ persona∞ use. All other rights reserved.
INDEX OF LISTINGS
File Purpose Page
------------------------------------------------------------
PCP.INC Data transfer between Z80 and 6502 2
APLGR/G.INC Constants, types and routines shared
by low- and high-resolution graphics 5
APLGR/L.INC Register-loading routines for low-
resolution graphics 7
APLGR/H.INC Register-loading routines for high-
resolution graphics 11
LORES.A65 6502 assembly language source for low-
resolution register-loading routines 16
LOWRES.PAS Demonstration of low-resolution
graphics functions 18
SINES.PAS Demonstration of high-resolution
graphics functions 19
PLOTTER.INC Routines for mapping "world coordinates"
onto the high-res display--used by
SINES and other high-resolution
programs 22
DUMPSCRN.PAS Dumps high-resolution screen to dot-
matrix printer 25
SAVSCRN.PAS Saves contents of a high-resolution
screen in a file on disk 27
GETSCRN.PAS Fills a high-resolution screen with
the contents of a file produced by
SAVSCRN 29
.PAèLISTING 1. PCP.INC
{PCP.INC contains primitive routines to communicate between
the PCPI Z80 card (Applicard) and the Franklin or Apple.
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
{Include before any APLGR file.
Variables and constants which are to be hidden from the
user's programs start with an underscore}
CONST
{ports}
_HOSTOUT=0;
_HOSTIN=$20;
_HOSTSTAT=$40;
{commands to transfer more than one byte}
_RDHOST=1; {Z80 -> 6502}
_WRHOST=2; {6502 -> Z80}
{commands for single byte transfers--vers.9 of PCPI ROM}
_RDBYTE=6;
_WRBYTE=7;
{command for 6502 to execute a procedure}
_CALL=3;
{**********************************************************
These three routines are low level "primitives" that should
probably never be called from procedures outside this file}
FUNCTION _recvbyte:byte; {get a byte from the 6502}
CONST READY=$80;
BEGIN
WHILE (READY AND port[_HOSTSTAT]) = 0 DO ; {wait til ready}
_recvbyte:=port[_HOSTIN]; {get byte}
END;
PROCEDURE _sendbyte(datum:byte); {send a byte to 6502}
CONST BUSY=1;
BEGIN
WHILE (BUSY AND port[_HOSTSTAT]) <> 0 DO ; {wait til ready}
port[_HOSTOUT]:=datum; {send byte}
END;
PROCEDURE _sendword(data:integer);
{send a word (low byte first) to the 6502}
VAR a:RECORD CASE boolean OF
TRUE: (i:integer);
FALSE: (b:array [1..2] of byte);
END;
BEGIN
a.i:=data;
_sendbyte(a.b[1]); _sendbyte(a.b[2]);
END;
èLISTING 1 CONTINUED--PCP.INC
{**********************************************************
Now the blocks which may be referenced by other ones}
FUNCTION _rdhostbyte(apladdr:integer):byte;
{get a byte from the 6502's RAM at address apladdr}
BEGIN
_sendbyte(_RDBYTE); _sendword(apladdr);
_rdhostbyte:=_recvbyte; {get data}
END;
PROCEDURE _wrhostbyte(apladdr:integer; datum:byte);
{send a byte to address apladdr in the 6502's RAM}
BEGIN
_sendbyte(_WRBYTE); _sendword(apladdr);
_sendbyte(datum);
END;
PROCEDURE _rdhostdata(sourceaddr,destaddr,bufsize:integer);
{transfers bufsize bytes from the 6502's RAM to the Z80's RAM.
Arguments are the starting addresses of the source and
destination, and length of the buffer area which is to
receive the data. Call thusly:
_rdhostdata(apladdr,ADDR(buffer),SIZEOF(buffer));
}
VAR
i:integer;
b:^byte;
BEGIN
_sendbyte(_RDHOST);
_sendword(sourceaddr);
_sendword(bufsize);
b:=PTR(destaddr);
FOR i:=bufsize DOWNTO 1 DO BEGIN
b^:=_recvbyte;
b:=PTR(ORD(b)+1);
END;
END;
PROCEDURE _wrhostdata(sourceaddr,destaddr,bufsize:integer);
{transfers bufsize bytes from the Z80's RAM to the 6502's RAM.
Arguments are the starting addresses of the source and
destination, and length of the buffer area which is to
receive the data. Call thusly:
_wrhostdata(ADDR(buffer),apladdr,SIZEOF(buffer));
}
VAR
i:integer;
b:^byte;
.cp 13èLISTING 1 CONTINUED--PCP.INC
BEGIN
_sendbyte(_WRHOST);
_sendword(destaddr);
_sendword(bufsize);
b:=PTR(sourceaddr);
FOR i:=bufsize DOWNTO 1 DO BEGIN
_sendbyte(b^);
b:=PTR(ORD(b)+1);
END;
END;
PROCEDURE _callapl(apladdr:integer);
{executes routine in the 6502's RAM starting at apladdr.
This routine must end with a "return" command.
NOTE: for locations > 32K, either use negative integers
or hex constants}
BEGIN
_sendbyte(_CALL);
_sendword(apladdr);
END;
{end of PCP.INC}
.PAèLISTING 2. APLGR/G.INC
{APLGR/G.INC enables calling graphics routines in Apple's
ROMs from Turbo Pascal programs.
Requires the PCPI Z80 card (Applicard).
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
{Include after PCP and before APLGR/L or APLGR/H}
{contains these routines used by both hi- & lores graphics:
PROCEDURE _setpartition(part:partition);
PROCEDURE _selectpage(pagenum:integer);
PROCEDURE textscreen(pagenum:integer);
}
CONST
_BPL=40; {# bytes/line of hi or lo res display}
{software switches for control of graphics features}
_GRFX=$C050; _TXT=$C051;
_FULSCRN=$C052; _MXD=$C053;
_PG1=$C054; _PG2=$C055;
_LRS=$C056; _HRS=$C057;
{temporary storage for parameters}
_AREG=$9000; _YREG=$9001; _LOCXX=$9002; _XREG=$9002;
TYPE
partition=(FULLSCREEN,MIXED);
_screenmode=(TEXT,GRAPHICS);
(*********************************************************)
PROCEDURE _setpartition(part:partition);
{switch between full screen graphics and mixed text/graphics}
BEGIN
CASE part OF
FULLSCREEN: _wrhostbyte(_FULSCRN,0);
MIXED: _wrhostbyte(_MXD,0);
END;
END;
PROCEDURE _selectpage(pagenum:integer);
{switch to specified graphics page}
BEGIN
IF pagenum=1 THEN _wrhostbyte(_PG1,0)
ELSE IF pagenum=2 THEN _wrhostbyte(_PG2,0)
ELSE writeln('There is no page ',pagenum);
END;
.CP 9èLISTING 2 CONTINUED--APLGR/G.INC
PROCEDURE textscreen(pagenum:integer);
{switch to specified text screen}
BEGIN
_selectpage(pagenum);
_wrhostbyte(_TXT,0);
END;
FUNCTION _inrange(n,lolimit,hilimit:integer):boolean;
{test for value outside of limits--used to prevent drawing
outside the screen boundaries}
BEGIN
IF (n>=lolimit) AND (n<=hilimit) THEN _inrange:=TRUE
ELSE _inrange:=FALSE;
END;
{end of APLGR/G}
.PAèLISTING 3. APLGR/L.INC
{APLGR/L.INC enables calling low resolution Apple graphics
routines from Turbo Pascal programs.
Requires the PCPI Z80 card (Applicard).
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
{contains these routines:
PROCEDURE lorespatch;
--installs the register-loading routines needed by setcolor,
plot, hlin and vlin
PROCEDURE loresgr(pagenum:integer; part:partition);
--invokes lores graphics
PROCEDURE clear_lores_screen(page:integer);
--clears specified lores page
PROCEDURE setcolor(color:loreshues);
--selects color for drawing
PROCEDURE plot(column,row:byte);
--puts a point on the screen
PROCEDURE hlin(row,col1,col2:byte);
--draws a horizontal line
PROCEDURE vlin(col,row1,row2:byte);
--draws a vertical line
Some of these procedures call lores graphics routines
at the following ROM locations:
SETCOL = 0F864H set color
PLOT = 0F800H plot a point
HLIN = 0F819H draw a horizontal line
VLIN = 0F828H " vertical line
This requires "poking" a few short machine language (6502)
routines into the 6502's RAM starting at location 9003H.
The parameters needed by these routines are "poked" into
locations 9000-9002H (bytes destined for the A and Y registers
and locations 2CH or 2DH).
}
TYPE
loreshues=(BLACK,MAGENTA,DARKBLUE,PURPLE,DARKGREEN,GREY1,
MEDIUMBLUE,LIGHTBLUE,BROWN,ORANGE,GREY2,PINK,
LIGHTGREEN,YELLOW,AQUA,WHITE); {lores colors}
CONST
{low resolution constants}
LOHRES=40; {# of pixels across the screen}
LOVRES=48; {full screen vertical resolution}
LOMIXVRES=40; {mixed mode vert res}
_LORESPAGE1=$400; {start of lores page 1}
_LORESPAGE2=$800; {but can't use page 2 with Applicard!
Overlaps with vital drivers!!!!}
.CP 6èLISTING 3 CONTINUED--APLGR/L.INC
{easily accessible ROM routines for lores graphics}
_LOCLRSCR=$F832; {clears whole lores screen}
_LOCLRTOP=$F836; {spares four text lines at bottom}
{The following addresses in the 6502's RAM are used by
setcolor, plot, hlin and vlin}
_ASETCOL=$9003; {set color}
_APLOT=$900A; {plot a point at column,row}
_AHLIN=$9014; {plot horiz. line at row v between col1 and col2}
_AVLIN=$9023; {plot vert. line at col h between row1 and row2}
(**********************************************************)
{Next are routines to be patched into motherboard's RAM at
$9003-$9033 so that setcolor, plot, hlin and vlin can be used}
PROCEDURE lorespatch;
{installs the register-loading routines needed by
setcolor, plot, hlin and vlin}
CONST
LORESTUFF: array [$01..$2F] of byte=(
{_ASETCOL (9003-9009)--set color}
$AD,$00,$90,$20,$64,$F8,$60,
{_APLOT (900A-9013)--plot a point at column h, row v}
$AD,$00,$90,$AC,$01,$90,$20,$00,$F8,$60,
{_AHLIN (9014-9022)--plot horiz. line at row v between
col1 and col2}
$AD,$02,$90,$85,$2C,$AD,$00,$90,$AC,$01,$90,$20,$19,$F8,$60,
{_AVLIN (9023-9031)--plot vert. line at col h between
row1 and row2}
$AD,$02,$90,$85,$2D,$AD,$00,$90,$AC,$01,$90,$20,$28,$F8,$60
);
{Borland Pascal's "structured constants" feature is nonstandard.
So, for that matter, is any hardware-specific code that might be
generated even with standard syntax! This just happens to be
a quick and dirty way to define a table of bytes that represents
6502 instructions}
VAR source,dest,lnth:integer;
BEGIN
source:=ADDR(LORESTUFF[1]); {starting address of data to send}
dest:=$9003; {where in the 6502's RAM to put it}
lnth:=$2F; {how many bytes to send}
_wrhostdata(source,dest,lnth);
END;
(***********************************************************)
.CP 11èLISTING 3 CONTINUED--APLGR/L.INC
PROCEDURE loresgr(pagenum:integer; part:partition);
{switch to low resolution graphics on specified page}
BEGIN
_selectpage(pagenum);
_setpartition(part);
_wrhostbyte(_LRS,0);
_wrhostbyte(_GRFX,0);
END;
(***********************************************************)
{Elementary lores graphics procedures}
PROCEDURE clear_lores_screen;
BEGIN
_callapl(_LOCLRSCR); {_callapl is in the file PCP.INC}
END;
PROCEDURE setcolor(color:loreshues);
{specify color to use for drawing}
VAR kolor:byte;
BEGIN
kolor:=ORD(color);
_wrhostbyte(_AREG,kolor);
_callapl(_ASETCOL);
END;
PROCEDURE plot(column,row:byte);
{draw a point at specified location}
BEGIN
IF _inrange(column,0,LOHRES) THEN
IF _inrange(row,0,LOVRES) THEN BEGIN
_wrhostbyte(_AREG,row);
_wrhostbyte(_YREG,column);
_callapl(_APLOT);
END;
END;
FUNCTION _loresclip(n,lolimit,hilimit:integer):integer;
{called by hlin & vlin to prevent drawing outside screen margins}
BEGIN
IF n<lolimit THEN _loresclip:=lolimit
ELSE IF n>hilimit THEN _loresclip:=hilimit
ELSE _loresclip:=n;
END;
PROCEDURE hlin(row,col1,col2:byte);
{draw horizontal line at "row" from col1 to col2}
VAR temp:byte;
.CP 20èLISTING 3 CONTINUED--APLGR/L.INC
BEGIN
IF _inrange(row,0,LOVRES) THEN
IF _inrange(col1,0,LOHRES) OR _inrange(col2,0,LOHRES)
THEN BEGIN
col1:=_loresclip(col1,0,LOHRES);
col2:=_loresclip(col2,0,LOHRES);
IF col1>col2 THEN BEGIN
temp:=col1;
col1:=col2;
col2:=temp;
END;
_wrhostbyte(_AREG,row);
_wrhostbyte(_YREG,col1);
_wrhostbyte(_LOCXX,col2);
_callapl(_AHLIN);
END;
END;
PROCEDURE vlin(col,row1,row2:byte);
{draw vertical line at col from row1 to row2}
VAR temp:byte;
BEGIN
IF _inrange(col,0,LOHRES) THEN
IF _inrange(row1,0,LOVRES) OR _inrange(row2,0,LOVRES)
THEN BEGIN
row1:=_loresclip(row1,0,LOVRES);
row2:=_loresclip(row2,0,LOVRES);
IF row1>row2 THEN BEGIN
temp:=row1;
row1:=row2;
row2:=temp;
END;
_wrhostbyte(_AREG,row1);
_wrhostbyte(_YREG,col);
_wrhostbyte(_LOCXX,row2);
_callapl(_AVLIN);
END;
END;
{end of APLGR/L}
.PAèLISTING 4. APLGR/H.INC
{APLGR/H.INC enables calling hi resolution Apple graphics
routines from Turbo Pascal programs.
Requires the PCPI Z80 card (Applicard).
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
{contains these routines:
PROCEDURE hirespatch;
--installs the register-loading routines to be patched
into motherboard's RAM at $9032-$9058 so ROM hires routines
can be used
PROCEDURE hiresgr(pagenum:integer; part:partition);
--invokes hires mode with specified page and partition
PROCEDURE clear_hires_screen(page:integer);
--clears specified hires page
PROCEDURE hgrselect(scrn:integer);
--select and clear specified page
PROCEDURE hgrclear;
--clear hires screen
PROCEDURE hisetcolor(color:hireshues);
--set color for drawing
PROCEDURE hplot(column,row:integer);
--plot a point at specified location
PROCEDURE hline(destcol,destrow:integer);
--draw from present cursor to destination
PROCEDURE setbackground(tint:hireshues);
--specify color of background
PROCEDURE setcursor(column,row:integer);
--put cursor at a location
Some of these procedures invoke some of the following ROM
hires graphics routines:
HGR = 0F3E2H invoke hires display page 1 with 4 text lines
HGR2 = 0F3D8H invoke hires display page 2 (full screen)
HCLR = 0F3F2H clear current hires page
BKGND = 0F3F4H set background color
HCOLOR = 0F6F0H set color for hires drawing
HPLOT = 0F457H position cursor & plot a point
HLINE = 0F53AH plot a line
HPOSN = 0F411H set cursor at h,v without plotting
--call before "draw"
SHPTR = 0F730H sets up shape pointers
(Reference: pp.69-71 in Apple Graphics & Arcade Game Design,
by J.Stanton (The Book Co., Los Angeles) 1982.)
This requires "poking" a few short routines into the 6502's RAM
starting at location 9003H. The parameters needed by these
routines are "poked" into locations 9000-9002H (bytes destined
for the A and Y registers and location 45H).
}
.CP 12èLISTING 4 CONTINUED--APLGR/H.INC
{------------Other Hires graphics locations-----------------
COLRTBL = 0F6F6H start of color table
HLCOORD = 00E0H two byte horizontal coordinate
VCOORD = 00E2H vertical coordinate
CLRMASK = 00E4H color masking word from color table
PAGENUM = 00E6H $20 for page 1, $40 for page 2
SCALE = 00E7H scale factor for shape drawing
SHAPTABL = 00E8H two byte address of shape table
-----------------------------------------------------------}
TYPE
hireshues=(BLACK1,GREEN,VIOLET,WHITE1,BLACK2,ORANGE,BLUE,WHITE2);
CONST
{hires constants}
HIHRES=280; {# of pixels across the screen}
HIVRES=192; {full screen vertical resolution}
HIMIXVRES=160; {mixed mode vert res}
HIRESPAGE1=$2000; {start of hires page 1}
HIRESPAGE2=$4000; {start of hires page 2}
{easy ROM routines to call--no parameters needed}
_HGR=$F3E2; {invoke hires display page 1 with 4 text lines}
_HGR2=$F3D8; {invoke hires display page 2 (full screen)}
_HCLR=$F3F2; {clear current hires page}
(**********************************************************)
{The following 6502 RAM locations will be patched to hold
routines that allow access to the ROM graphics functions,
such as setcolor, hplot, hline etc.}
_AHCOLOR=$9032; {PURPOSE: set color for hires drawing
SETUP: poke color into XREG}
_AHPLOT=$9039; {PURPOSE: draw a point at location h,v
SETUP: poke v into AREG, lo byte of h into
XREG, hi byte of h into YREG}
_AHLINE=$9046; {PURPOSE: draw a line from initial cursor
location to specified point
SETUP: poke v into YREG, lo byte of h into
AREG, and hi byte of h into XREG}
_ABKGND=$9053; {PURPOSE: set background color
SETUP: set color before calling, then poke
color mask into AREG}
_AHPOSN=$9059; {PURPOSE: put cursor at location h,v
without plotting
SETUP: poke v into AREG, lo byte of h
into XREG, hi byte of h into YREG
--same as for _AHPLOT}
.CP 6èLISTING 4 CONTINUED--APLGR/H.INC
PROCEDURE hirespatch;
{installs the routines to be patched into the 6502's RAM.}
CONST
{where this patch starts and how long it is}
CODESTART=$9032;
CODELENGTH=$34;
{Borland Pascal's "structured constants" feature is nonstandard.
So, for that matter, is any hardware-specific code that might
be generated even with standard syntax! This just happens to
be a quick and dirty way to define a table of bytes that
represents 6502 instructions}
HIRESTUFF: array [$01..CODELENGTH] of byte=(
{_AHCOLOR}
$AE,$02,$90,$20,$F0,$F6,$60,
{_AHPLOT}
$AE,$02,$90,$AD,$00,$90,$AC,$01,$90,$20,$57,$F4,$60,
{_AHLINE}
$AE,$02,$90,$AD,$00,$90,$AC,$01,$90,$20,$3A,$F5,$60,
{_ABKGND}
$A5,$E4,$20,$F4,$F3,$60,
{_AHPOSN}
$AE,$02,$90,$AD,$00,$90,$AC,$01,$90,$20,$11,$F4,$60
);
VAR source,dest,lnth:integer;
BEGIN
source:=ADDR(HIRESTUFF[1]);
dest:=CODESTART;
lnth:=CODELENGTH;
_wrhostdata(source,dest,lnth);
END;
(***********************************************************)
PROCEDURE hiresgr(pagenum:integer; part:partition);
{invoke hires mode with specified page and partition}
BEGIN
_selectpage(pagenum);
_setpartition(part);
_wrhostbyte(_HRS,0);
_wrhostbyte(_GRFX,0);
END;
(***********************************************************)
{Elementary hires graphics procedures}
PROCEDURE clear_hires_screen;
BEGIN
writeln('dummy routine to clear hires screen');
END;
.CP 10èLISTING 4 CONTINUED--APLGR/H.INC
PROCEDURE hgrselect(scrn:integer);
{select and clear specified page}
BEGIN
IF scrn=1 THEN _callapl(_HGR)
ELSE IF scrn=2 THEN _callapl(_HGR2)
ELSE writeln('There is no page ',scrn);
END;
PROCEDURE hgrclear;
{clear hires screen}
BEGIN
_callapl(_HCLR);
END;
PROCEDURE hisetcolor(color:hireshues);
{set color for drawing}
BEGIN
_wrhostbyte(_XREG,ORD(color));
_callapl(_AHCOLOR);
END;
PROCEDURE hplot(column,row:integer);
{plot a point at specified locus}
BEGIN
IF _inrange(column,0,HIHRES) THEN
IF _inrange(row,0,HIVRES) THEN BEGIN
_wrhostbyte(_AREG,lo(row));
_wrhostbyte(_XREG,lo(column));
_wrhostbyte(_YREG,hi(column));
_callapl(_AHPLOT);
END;
END;
PROCEDURE hline(destcol,destrow:integer);
{Draw from present cursor to dest. Uses truly crude clipping!}
BEGIN
IF _inrange(destcol,0,HIHRES) THEN
IF _inrange(destrow,0,HIVRES) THEN BEGIN
_wrhostbyte(_AREG,lo(destcol));
_wrhostbyte(_XREG,hi(destcol));
_wrhostbyte(_YREG,lo(destrow));
_callapl(_AHLINE);
END;
END;
PROCEDURE setbackground(tint:hireshues);
{specify color of background}
BEGIN
hisetcolor(tint);
_callapl(_ABKGND);
END;
èLISTING 4 CONTINUED--APLGR/H.INC
PROCEDURE setcursor(column,row:integer);
{put cursor at a specific location}
BEGIN
_wrhostbyte(_AREG,lo(row));
_wrhostbyte(_XREG,lo(column));
_wrhostbyte(_YREG,hi(column));
_callapl(_AHPOSN);
END;
{end of APLGR/H}
.PAèLISTING 5. LORES.A65
;LORES.A65
;Purpose: enable calling Apple low resolution graphics
;routines from CP/M using the PCPI Z80 card (Applicard).
;Copyright 1984 by N.T.Carnevale.
;Permission granted for nonprofit use.
;
;
;Assemble with A65, then use hex codes of the .PRN file to
;generate the code which will be written to the motherboard.
;
;---------------ROM Lores graphics routines------------------
SETCOL: .EQU 0F864H ;set color
PLOT: .EQU 0F800H ;plot a point
HLIN: .EQU 0F819H ;draw a horizontal line
VLIN: .EQU 0F828H ; " vertical line
;-------------Other Lores graphics locations-----------------
H2: .EQU 002CH ;rightmost end of horizontal line
V2: .EQU 002DH ;bottom end of vertical line
;------------------------------------------------------------
;
;
.BLOCK 9000H ;put this above the driver area
;
;first, loci for temporary storage--
AREG: .BLOCK 1 ;the scratchpad to which the Appli-
YREG: .BLOCK 1 ;card writes data destined for
LOCXX: .BLOCK 1 ;A, Y, and 2C or 2DH
;
CODESTART: ;beginning of patch area
;
;************************
;ROUTINE: ASETCOL
;PURPOSE: set the color used for drawing
;SETUP: poke "color" byte into AREG before calling
;************************
ASETCOL:
LDA AREG
JSR SETCOL
RTS
;
;************************
;ROUTINE: APLOT
;PURPOSE: plot a point at column h, row v
;SETUP: poke h into YREG, v into AREG before calling
;************************
APLOT:
LDA AREG
LDY YREG
JSR PLOT
RTS
;
;************************èLISTING 5 CONTINUED--LORES.A65
;ROUTINE: AHLIN
;PURPOSE: draw a horizontal line at row v between
; columns h1 and h2, where h1<h2
;SETUP: poke v into AREG, h1 into YREG,
; and h2 into LOCXX before calling
;************************
AHLIN:
LDA LOCXX
STA H2
LDA AREG
LDY YREG
JSR HLIN
RTS
;
;************************
;ROUTINE: AVLIN
;PURPOSE: draw a vertical line at column h between
; rows v1 and v2, where v1<v2
;SETUP: poke h into YREG, v1 into AREG,
; and v2 into LOCXX before calling
;************************
AVLIN:
LDA LOCXX
STA V2
LDA AREG
LDY YREG
JSR VLIN
RTS
;
;
FINISH:
;
;how long the whole works is:
LENGTH: .EQU FINISH-CODESTART
;
.END
;
;end of LORES.A65
.PAèLISTING 6. LOWRES.PAS
PROGRAM lowres; {lores routine test}
{Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
{$I PCP.INC}
{$I APLGR/G.INC}
{$I APLGR/L.INC}
VAR
ans:char; {for keyboard responses}
scrn:integer; {which lo-res graphics screen to use}
h,v:integer; {horizontal and vertical coordinates
--top left = 0,0}
tint:loreshues; {color used for drawing}
PROCEDURE delay; {about 4 second delay}
VAR i,j:integer;
BEGIN
FOR i:=0 TO 500 DO
FOR j:=1 TO 500 DO;
END;
BEGIN
lorespatch; {install the register-loading routines}
writeln('Low-resolution graphics exerciser');
write('Press return to clear and fill screen 1: ');
readln(ans); {can't use lores screen 2}
loresgr(1,FULLSCREEN); {display lores screen 2}
clear_lores_screen; {clear it}
tint:=BLACK; {start with black}
FOR v:=0 TO LOVRES-1 DO BEGIN
setcolor(tint); {use specified color}
hlin(v,0,LOHRES-1); {draw horiz line across screen}
IF tint=WHITE THEN tint:=BLACK
ELSE tint:=SUCC(tint); {next color to use}
END;
delay;
FOR h:=0 TO LOHRES-1 DO BEGIN
setcolor(tint);
vlin(h,0,LOVRES-1); {draw vert line down screen}
IF tint=WHITE THEN tint:=BLACK
ELSE tint:=SUCC(tint);
END;
delay;
textscreen(1); {return to the text display before exit}
END. {end of PROGRAM lowres}
.PAèLISTING 7. SINES.PAS
PROGRAM sines; {demonstrates plot of sine function}
{Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
CONST
GRAFSCREEN=2; {use only hires screen 2 with PCPI v.2 CP/M}
BELL=7;
TYPE
{these are used to map the "real world" onto the display}
realdata=RECORD
x,y:real; {x&y world coordinates, that is, "real data"}
END;
screendata=RECORD
x,y:integer; {x&y display coordinates}
END;
realscalefactors=RECORD
mx,my,bx,by:real; {used to map world into display}
END;
{$I PCP.INC}
{$I APLGR/G.INC}
{$I APLGR/H.INC}
VAR
ans:char;
frameloc,framesize:screendata;
lowerleft,upperright:realdata;
frame:realscalefactors;
hue:hireshues;
{$I PLOTTER.INC}
{PLOTTER.INC contains the following:
PROCEDURE setframe--sets up the coefficients ("magnifications"
and "shifts") that are used to transform or map "real data"
to the display. Parameters are:
lowerleft,upperright:realdata--the opposite corners of
a rectangular area that contains the range of "real
data" to be plotted ("corners of the real world").
frameloc:screendata--left upper corner of area on the
screen where the data is to go (where to put the
picture).
framesize:screendata--dimensions of the area on the
screen where the data is to go (how big to make the
picture).
VAR frame:realscalefactors--this record contains the
coefficients (calculated by setframe) that will be
used by other procedures to map "real data" to the
display.
èLISTING 7 CONTINUED--SINES.PAS
PROCEDURE plot--draws a point on the hires page using
specified scale factors. Parameters are:
point:realdata--x,y coordinates of the point in the
"real world."
frame:scalefactors--the coefficients used to map
the point onto the display.
PROCEDURE plotline--starting from present cursor location,
draws a line to the point on the screen that corresponds
to a specified endpoint in the "real world," using
specified scale factors. Parameters are:
endpoint:realdata--x,y coordinates of the end of the
line in the "real world."
frame:scalefactors--the coefficients used to map the
point onto the display.
}
PROCEDURE genplot;
{generate and plot one cycle of a sine wave}
CONST PI=3.1415926;
VAR
i:integer;
point:realdata;
dx:real;
BEGIN
point.x:=0.0;
dx:=0.02*pi;
point.y:=sin(point.x);
plot(point,frame); {plot the first point}
FOR i:=1 TO 100 DO BEGIN
point.x:=point.x+dx; {calculate the next point}
point.y:=sin(point.x);
plotline(point,frame); {and draw a line to it}
END;
END;
BEGIN
hirespatch; {install register-loading routines}
writeln('Sine plotter');
write('First, screen ',GRAFSCREEN,
' will be cleared--press return to proceed');
readln(ans);
hgrselect(GRAFSCREEN); {select screen to use}
hiresgr(GRAFSCREEN,FULLSCREEN); { and clear it}
textscreen(1); {restore text display}
writeln;
writeln('Press return to plot sine function.');
writeln('After the bell rings, press return again');
writeln(' to leave graphics mode.');
readln(ans);
èLISTING 7 CONTINUED--SINES.PAS
{specify limits of "real world" data}
lowerleft.x:=0.0; lowerleft.y:=-1.0;
upperright.x:=2*PI; upperright.y:=1.0;
{set up size of display area}
framesize.x:=HIHRES - 90; framesize.y:=HIVRES DIV 2;
{put first frame at top left-hand corner of display}
frameloc.x:=0; frameloc.y:=0;
hiresgr(GRAFSCREEN,FULLSCREEN); {go back to graphics}
hue:=BLACK1; {first "color" to use}
REPEAT
hue:=succ(hue); {advance to the next color}
hisetcolor(hue);
frameloc.y:=frameloc.y+10; { and shift the frame}
frameloc.x:=frameloc.x+12;
setframe(lowerleft,upperright,frameloc,framesize,frame);
genplot; {plot one sine wave}
UNTIL hue=WHITE2;
writeln(chr(BELL)); {ring the bell}
readln(ans); {wait until return key is pressed}
textscreen(1); {restore text display before exit!}
END. {end of PROGRAM sines}
.PAèLISTING 8. PLOTTER.INC
{PLOTTER.INC--what it takes to set up a frame
and plot data into it. Written for floating point data.
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
(*This file must be included after PCP, APLGR/G and APLGR/H.
The following types (and corresponding variables) must be
defined in the main file before PLOTTER is included:
TYPE
realdata=RECORD
x,y:real; {x&y world coordinates, that is, "real data"}
END;
screendata=RECORD
x,y:integer; {x&y display coordinates}
END;
realscalefactors=RECORD
mx,my,bx,by:real; {used to map world into display}
END;
PLOTTER contains the following procedures:
setframe--sets up the coefficients ("magnifications"
and "shifts") that are used to transform or map "real data"
to the display. Parameters are:
lowerleft,upperright:realdata--the opposite corners of
a rectangular area that contains the range of "real
data" to be plotted ("corners of the real world").
frameloc:screendata--left upper corner of area on the
screen where the data is to go (where to put the
picture).
framesize:screendata--dimensions of the area on the
screen where the data is to go (how big to make the
picture).
VAR frame:realscalefactors--this record contains the
coefficients (calculated by setframe) that will be
used by other procedures to map "real data" to the
display.
plot--draws a point on the hires page using
specified scale factors. Parameters are:
point:realdata--x,y coordinates of the point in the
"real world."
frame:scalefactors--the coefficients used to map
the point onto the display.
.CP 11èLISTING 8 CONTINUED--PLOTTER.INC
plotline--starting from present cursor location,
draws a line to the point on the screen that corresponds
to a specified endpoint in the "real world," using
specified scale factors. Parameters are:
endpoint:realdata--x,y coordinates of the end of the
line in the "real world."
frame:scalefactors--the coefficients used to map the
point onto the display.
Procedures not in this file that would be nice to have:
--"moveto" a specific location without drawing a point
(unlike plot, which moves the cursor to a point and
draws a point there)
--"relative" cursor moves (plot and plotline put the
cursor at a specific or "absolute" location on the
display
--true clipping, so that, if one or both endpoints of a
line lies outside the defined frame, only the portion
of it that is within the frame will be drawn
--a circle drawing procedure
*)
{sets up the scale factors used by the plot routines}
PROCEDURE setframe
(lowerleft,
upperright:realdata; {data limits}
frameloc:screendata; {left upper corner of display area}
framesize:screendata; {dimensions of display area}
VAR frame:realscalefactors {calculated by setframe}
);
BEGIN
WITH frame DO BEGIN
mx:=(framesize.x-1)/(upperright.x-lowerleft.x);
bx:=frameloc.x-mx*lowerleft.x;
my:=(framesize.y-1)/(lowerleft.y-upperright.y);
{note: Apple's screen is "upside-down"}
by:=frameloc.y-my*upperright.y;
END;
END;
{put cursor and plot a point at a specified location}
PROCEDURE plot(point:realdata; frame:realscalefactors);
VAR h,v:integer; {actual display coords}
BEGIN
WITH frame DO BEGIN
h:=round(mx*point.x+bx);
v:=round(my*point.y+by);
hplot(h,v);
END;
END;
èLISTING 8 CONTINUED--PLOTTER.INC
{draw a line from present cursor location to specified endpoint}
PROCEDURE plotline(endpoint:realdata; frame:realscalefactors);
VAR h,v:integer; {actual display coords}
BEGIN
WITH frame DO BEGIN
h:=round(mx*endpoint.x+bx);
v:=round(my*endpoint.y+by);
hline(h,v);
END;
END;
{end of PLOTTER.INC}
.PAèLISTING 9. DUMPSCRN.PAS
PROGRAM dumpscrn; {dumps a hires screen to the printer.
Assumes the printer card is software-compatible with
the GRAPPLER.
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
CONST GRAFSCREEN=2; {use only hires screen 2 with PCPI v.2 CP/M}
{$I PCP.INC}
{$I APLGR/G.INC}
{$I APLGR/H.INC}
TYPE string70=string[70];
VAR
size:(single,double); {specifies 1:1 or 2:1 screen dump}
ans:char;
controlstring:string[4]; {used for printer card commands}
i,numlf,copynum:integer;
scrn:integer;
PROCEDURE delay;
VAR i,j:integer;
BEGIN
FOR i:=0 TO 500 DO
FOR j:=1 TO 500 DO;
END;
FUNCTION promptans(prompt:string70):char;
{display the prompt on the console,
get a single uppercase response from the keyboard}
VAR ans:char;
BEGIN
write(prompt);
readln(ans);
promptans:=upcase(ans);
END;
PROCEDURE dumpit; {tell Grappler to do the screen dump}
VAR i:integer;
BEGIN
ans:=promptans('Adjust top edge of paper, then press RETURN');
FOR i:=1 TO numlf DO writeln(lst); {blank lines for centering}
writeln(lst,chr(0),chr(25),controlstring); {null is for safety's sake}
FOR i:=1 TO numlf DO writeln(lst); {more blank lines after dump}
END;
.CP 10èLISTING 9 CONTINUED--DUMPSCRN.PAS
BEGIN
textscreen(1); {insure text display at start of program}
hirespatch; {install register-loading routines}
REPEAT
write('Dumping screen ',GRAFSCREEN,'--');
scrn:=GRAFSCREEN;
hiresgr(scrn,FULLSCREEN); {shows the screen without clearing it}
delay;
textscreen(1); {return to text display}
ans:=promptans('P)roceed or Q)uit? ');
UNTIL ans IN ['P','Q'];
IF ans='P' THEN BEGIN
ans:=promptans('D)ouble or S)tandard size? ');
IF ans='D' THEN BEGIN
numlf:=9;
controlstring:='GDR2'; {command for magnified screen dump}
END ELSE BEGIN
numlf:=19;
controlstring:='GR2'; {standard screen dump}
END;
writeln;
write('Number of copies to make: ');
readln(copynum);
FOR i:=1 TO copynum DO dumpit; {do the screen dump}
writeln('Check top edge of paper and reset printer');
END;
END. {of PROGRAM dumpscrn}
.PAèLISTING 10. SAVSCRN.PAS
PROGRAM savscrn; {saves a hi res screen to disk.
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
CONST GRAFSCREEN=2; {use only hires screen 2 with PCPI v.2 CP/M}
{$I PCP.INC}
{$I APLGR/G.INC}
{$I APLGR/H.INC}
TYPE
string70=string[70];
byte=char;
screenline=array [1.._BPL] of byte; {_BPL is defined in APLGR/G}
figfile=FILE of screenline;
VAR
ans:char;
scrn:integer;
PROCEDURE delay;
VAR i,j:integer;
BEGIN
FOR i:=0 TO 500 DO
FOR j:=1 TO 500 DO;
END;
FUNCTION promptans(prompt:string70):char;
{display prompt on monitor,
get uppercase single character from keyboard}
VAR ans:char;
BEGIN
write(prompt);
readln(ans);
promptans:=upcase(ans);
END;
FUNCTION rowstart(row,page:integer):integer;
{calculate the starting address corresponding a line or row number}
VAR pagebase:integer;
BEGIN
IF page=1 THEN pagebase:=HIRESPAGE1 ELSE pagebase:=HIRESPAGE2;
rowstart:=pagebase + $28*(row SHR 6) + (((row SHR 3) MOD 8) SHL 7)
+ ((row MOD 8) SHL 10);
END;
PROCEDURE doit; {simple read and save a screen to disk}
VAR
filnam:string[12];
f:figfile;
linenum:integer;
temp:screenline; {temporary array to hold a line from the screen}èLISTING 10 CONTINUED--SAVSCRN.PAS
BEGIN
write('File to receive picture: ');
readln(filnam);
assign(f,filnam);
rewrite(f);
FOR linenum:=0 TO (HIVRES-1) DO BEGIN
{read _BPL bytes from the display memory, starting at
the address that corresponds to the line number,
into the array temp[]}
_rdhostdata(rowstart(linenum,GRAFSCREEN),addr(temp[1]),_BPL);
{save the array of bytes in the file}
write(f,temp);
END;
close(f);
END;
BEGIN
textscreen(1); {guarantee text display at program start}
hirespatch; {install register-loading routines}
REPEAT
write('Saving screen ',GRAFSCREEN,'--');
scrn:=GRAFSCREEN;
hiresgr(scrn,FULLSCREEN); {shows the screen without clearing it}
delay;
textscreen(1); {return to text display}
ans:=promptans('P)roceed or Q)uit? ');
UNTIL ans IN ['P','Q'];
IF ans='P' THEN doit;
END. {end of PROGRAM savscrn}
.PAèLISTING 11. GETSCRN.PAS
PROGRAM getscrn; {fills a hi res display with data from
a file that was saved to disk by savscrn.
Copyright 1984 by N.T.Carnevale.
Permission granted for nonprofit use.}
CONST GRAFSCREEN=2; {use only hires screen 2 with PCPI v.2 CP/M}
{$I PCP.INC}
{$I APLGR/G.INC}
{$I APLGR/H.INC}
TYPE
string70=string[70];
byte=char;
screenline=array [1.._BPL] of byte;
figfile=FILE of screenline;
VAR
ans:char;
scrn:integer;
PROCEDURE delay;
VAR i,j:integer;
BEGIN
FOR i:=0 TO 500 DO
FOR j:=1 TO 500 DO;
END;
FUNCTION promptans(prompt:string70):char;
VAR ans:char;
BEGIN
write(prompt);
readln(ans);
promptans:=upcase(ans);
END;
FUNCTION rowstart(row,page:integer):integer;
{calculate the starting address corresponding a line or row number}
VAR pagebase:integer;
BEGIN
IF page=1 THEN pagebase:=HIRESPAGE1 ELSE pagebase:=HIRESPAGE2;
rowstart:=pagebase + $28*(row SHR 6) + (((row SHR 3) MOD 8) SHL 7)
+ ((row MOD 8) SHL 10);
END;
PROCEDURE doit; {simple read a screen from disk
& write to specified screen}
VAR
filnam:string[12];
f:figfile;
linenum:integer;
temp:screenline; {temporary array to hold a line from the screen}èLISTING 11 CONTINUED--GETSCRN.PAS
BEGIN
write('File to read: ');
readln(filnam);
assign(f,filnam);
reset(f);
hiresgr(scrn,FULLSCREEN); {shows the screen without clearing it}
FOR linenum:=0 TO (HIVRES-1) DO BEGIN
{read _BPL bytes from the file into temporary storage}
read(f,temp);
{write the bytes to the display memory, starting at
the address that corresponds to the line number}
_wrhostdata(addr(temp[1]),rowstart(linenum,GRAFSCREEN),_BPL);
END;
close(f);
END;
BEGIN
textscreen(1); {start in text display}
hirespatch; {install register-loading routines}
scrn:=GRAFSCREEN;
REPEAT
hiresgr(scrn,FULLSCREEN); {shows the screen without clearing it}
delay;
textscreen(1); {return to the text display}
ans:=promptans('Replace that with data from a file? ');
IF ans='Y' THEN BEGIN
doit;
delay;
textscreen(1);
ans:=promptans('Do it again? ');
END;
UNTIL ans<>'Y';
END. {end of PROGRAM getscrn}
===============
END OF LISTINGS
===============