home *** CD-ROM | disk | FTP | other *** search
- ;GRAPH is a package of subroutines to allow graphs
- ; to be printed on an MX80 with GRAFTRAX
- ;Using programs written either in Microsoft Basic or Fortran
- ;
- ; Written Dec 1981 by Trevor Marshall,
- ; SYSOP, Thousand Oaks Tech RCPM
- ; 3423 Hill Canyon Ave, TO CA 91360
- ;
- ; (C) Copyright 1981 by Trevor Marshall
- ; Permission is given for this package to be
- ; distributed and used, but not for profit.
- ;
- ; This version is for 400 dots x 200 dots (25 Lines)
- ; The complete buffer array is stored in memory so that
- ; multiple plots and other graphics may be facilitated
- ;Although this takes up a lot of memory the multiple plot
- ; ability is extremely useful
- ;
- ; There are 6 Subroutines.
- ; INITAX initializes the 400x200 array and fills in axes at 0,0
- ; INITGR initializes the 400x200 array but does not fill in axes
- ; calls to SCALEX or SCALEY are used for axes
- ; MARK marks a point with a square around it,given the coordinates
- ; PLOT plots a single point in the array, given its coordinates
- ; ANNOTY prints 5 condensed chars at the left of the Y axis
- ; PUTIT Prints the stored array, together with axis names
- ; (It is left to the calling program to print any descriptive
- ; header information.)
- ; As the EPSON normally loses count of top of form position
- ; (it only counts the number of lines/ page), when the line
- ; is other than 12 dots high; PUTIT readjusts TOF
- ;
- ; The calling sequence from the MICROSOFT BASIC COMPILER is:
- ; CALL INITAX or CALL INITGR (no params)
- ; CALL MARK(X%,Y%), 0 <= X% <= 399; 0 <= Y <= 199
- ; CALL PLOT(X%,Y%)
- ; CALL ANNOTY(VALUE$,Y%)
- ; ; VALUE$ is a string of 5 chars (may be blank)
- ; ; to identify the axis, Y% is the coordinate it is to be placed at
- ; CALL PUTIT(XALPHA$,YALPHA$), where
- ; ; XALPHA is a string to annotate the X axis ( <= 79 chars)
- ; ; YALPHA is a string to annotate the Y axis ( <= 25 chars)
- ; ; either of them may be blank but not null
- ;
- ;If the Conditional FORTRAN is TRUE (for Microsoft Fortran),
- ; All the Alpha strings must be type LOGICAL, and are called thus
- ; LOGICAL VALUE(5), XALPHA(79), YALPHA(25)
- ; INTEGER X,Y
- ; CALL INITAX or CALL INITGR (no params)
- ; CALL MARK(X,Y)
- ; CALL PLOT(X,Y)
- ; CALL ANNOTY(VALUE,Y)
- ; CALL PUTIT(XALPHA,YALPHA)
- ;
- BDOS EQU 5
- ;
- FALSE EQU 0
- TRUE EQU -1
- ;
- TESTING EQU FALSE ;Set true if stand alone
- FORTRAN EQU FALSE ;Set true if compiling for Microsoft Fortran
- ;
- GLOBAL INITAX,INITGR,MARK,PLOT,ANNOTY,PUTIT
- ;
- ;------------------------------------------------------------------------------
- IF TESTING ;If Testing we use as a stand-alone package
- ;
- ORG 100H ;To reduce compilation/linking time
- ;
- CALL INITAX
- ;Now plot an example line
- LD HL,99 ;X from 100 -> 299
- LD DE,200 ;Y from 199 -> 0
- LP8: INC HL
- DEC DE
- LD A,E
- OR D
- JR Z,NXT78 ;DONE
- PUSH HL
- PUSH DE
- ;CALL PLOT(X%,Y%):
- CALL PUT.PARAM ;Into MICROSOFT format
- CALL PLOT
- POP DE
- POP HL
- JR LP8
- ;
- NXT78:
- ;CALL MARK(20,100)
- LD HL,20
- LD DE,100
- CALL PUT.PARAM
- CALL MARK
- ;
- ;CALL MARK(0,0) ;To check if marking is only performed within correct limits
- LD HL,0
- LD DE,0
- CALL PUT.PARAM
- CALL MARK
- ;
- ;CALL MARK(399,199) ;as above
- LD HL,399
- LD DE,199
- CALL PUT.PARAM
- CALL MARK
- ;
- ;CALL ANNOTY(STRING$,Y%)
- LD HL,S3
- LD DE,190
- LD (P2),DE
- LD DE,P2
- CALL ANNOTY
- LD HL,S4
- LD DE,10
- LD (P2),DE
- LD DE,P2
- CALL ANNOTY
- ;
- ;CALL PUTIT(XALPHA$,YALPHA$)
- LD HL,S1 ;XALPHA$
- LD DE,S2 ;YALPHA$
- CALL PUTIT
- JP 0 ;Done
- ;
- PUT.PARAM: ;Store passed variables
- LD (P1),HL
- LD HL,P1
- LD (P2),DE
- LD DE,P2
- RET
- ;
- P1: DW 0
- P2: DW 0
- S1: DB 79
- DW ST1
- S2: DB 24
- DW ST2
- S3: DB 5
- DW ST3
- S4: DB 5
- DW ST4
- ST1: DB 'This is the string which can be '
- db 'used to annotate the X axis for coordinates,etc.'
- ST2: DB 'THIS IS THE Y AXIS STRING'
- ST3: DB 'ts1.1'
- ST4: DB 'ts2.2'
- ;
- ;
- ENDIF ;TESTING
- ;------------------------------------------------------------------------------
- ;Firstly a S/R to get the passed parameters
- GET@HL: LD A,(HL)
- INC HL
- LD H,(HL)
- LD L,A ;Return with @HL in HL
- RET
- ;
- ;LIST.STRING is a subroutine to send a string to the LIST device
- ; HL points to the string on entry, B contains its length
- LIST.STRING: LD C,5 ;BDOS LIST function
- LP5: LD E,(HL)
- PUSH HL
- PUSH BC
- CALL BDOS
- POP BC
- POP HL
- INC HL
- DJNZ LP5
- RET
- ;
- CRLF: LD E,0DH ;Put out a CRLF
- LD C,5
- CALL BDOS
- LD E,0AH
- LD C,5
- CALL BDOS
- RET
- ;
- ;
- ; INITGR merely initializes the array
- INITGR: LD HL,BUFFER ;HL points at start of buffer
- LD DE,BUFFER+1 ;Get ready for block Fill
- LD BC,BUFF.LENGTH-1
- LD (HL),0 ;Zero the first locn
- LDIR ;and now the rest
- ; Now initialize the ANNOT.BUFFER
- LD HL,ANNOT.BUFFER
- LD DE,ANNOT.BUFFER+1
- LD BC,25*5-1
- LD (HL),' '
- LDIR
- RET
- ;
- ; INITAX initializes the array and fills in axes at 0,0
- INITAX:
- CALL INITGR
- ; Now Fill in Vertical axis
- LD DE,400 ;Row increment
- LD B,25 ;The number of rows
- LD HL,BUFFER ;Get where the Y axis will be
- LP1: LD (HL),0FFH ;Fill in solid left axis
- ADD HL,DE
- DJNZ LP1
- ;Fill in Horizontal axis
- LD HL,BUFFER+10000-400 ;Point at first char of axis row
- LD BC,400 ;The length of the axis
- LP6: LD A,(HL) ;Set the bottom bit
- OR A,1 ;Set the bottom bit
- LD (HL),A
- INC HL
- DEC BC
- LD A,C
- OR B ;Is BC=0?
- JR NZ,LP6
- ;Now put in the 'pips' on the vertical axis***********************
- RET
- ;
- ;Output the buffer & strings
- PUTIT:
- IF NOT FORTRAN ;FORTRAN does not have a length byte
- INC HL ;Point past the string length byte
- ENDIF
- CALL GET@HL
- LD (X.COORD),HL ;Save address of HL string
- EX DE,HL
- IF NOT FORTRAN
- INC HL ;as above
- ENDIF
- CALL GET@HL
- LD (Y.COORD),HL ;Y string
- ;
- LD HL,BUFFER ;Initialize the output pointer
- LD (BUFF.PTR),HL
- LD A,25 ;Number of rows
- LD (ROW),A
- LD A,-1 ;Point at first Y axis char
- LD (STRING.PTR),A
- ;Set to 8 dot linefeed
- LD HL,SPACE.8
- LD B,3 ;Number of chars O/P
- CALL LIST.STRING
- LD B,8
- LD HL,NORMAL ;cancel special print
- CALL LIST.STRING
- CALL CRLF ;Clean up format
- ;
- ;First put out the ALPHA label ID
- LP3: LD A,(STRING.PTR) ;Point at next char in Y string
- INC A
- LD (STRING.PTR),A
- ;
- LD B,4
- LD HL,EXP.ON ;set expanded emphasized mode
- CALL LIST.STRING
- ;
- LD HL,(Y.COORD)
- LD B,0 ;Calculate the index of the current string char
- LD C,A
- ADD HL,BC
- ;
- LD E,(HL)
- LD C,5
- CALL BDOS ;Send it to printer
- ;
- LD B,8
- LD HL,NORMAL ;Reset to standard chars
- CALL LIST.STRING
- ;
- LD B,2
- LD HL,COMPRESSED
- CALL LIST.STRING ;Set compressed chars
- ; LD B,6 ;Now send 6 spaces
- ; LD HL,SPACE
- ; CALL LIST.STRING
- ; Print a space, plus the 5 ANNOTated chars (if non-blank)
- LD E,' '
- LD C,5
- CALL BDOS
- LD A,(ROW) ;Calculate the string in the ANNOT buffer
- DEC A ;make 0 to 24
- SCF
- CCF
- LD B,A ;save *1
- RL A ; *2
- RL A ; *4
- ADD B ; *4 + *1 = *5
- LD C,A
- LD B,0
- LD HL,ANNOT.BUFFER
- ADD HL,BC ;Now we are pointing at
- LD B,5 ;Character count
- LP2: LD E,(HL) ;Get the char
- LD C,5
- PUSH BC
- PUSH HL
- CALL BDOS ;Output it
- POP HL
- POP BC
- INC HL
- DJNZ LP2 ;Loop until 5 chars done
- ;
- LD B,8 ;Set printer chars back to normal
- LD HL,NORMAL
- CALL LIST.STRING
- ; print the graphics preamble
- LD HL,START.GRAPHICS
- LD B,4 ;The number of chars
- CALL LIST.STRING
- ;Now print one row
- LD HL,400 ;Number of chars
- LD (CHAR),HL ;Save it
- LP4: LD HL,(BUFF.PTR)
- LD C,5
- LD E,(HL)
- INC HL
- LD (BUFF.PTR),HL
- CALL BDOS
- LD HL,(CHAR)
- DEC HL
- LD (CHAR),HL
- LD A,L
- OR H ;Is it zero yet?
- JR NZ,LP4
- ;
- CALL CRLF ;Terminate row
- ;
- LD A,(ROW)
- DEC A
- LD (ROW),A
- JP NZ,LP3 ;and output the remaining rows
- ;Whilst we were outputting the array we had 26 narrow (8 dot) lines
- ;Normally 12 dot lines are used
- ;We have 2 more short ones next,so we must compensate for
- ; the lost dots by feeding
- ; a 12 + 26*4 + 11 + 1*4 = 119 dot line (We have to use 85+34+12)
- ;First print X axis description
- LD HL,SPACE.1 ;Don't cramp description
- LD B,3
- CALL LIST.STRING
- CALL CRLF
- ;Now output the X axis string
- LD HL,(X.COORD)
- LD B,79
- CALL LIST.STRING
- CALL CRLF
- ;Do a 119 dot line feed
- LD HL,SPACE.85
- LD B,3
- CALL LIST.STRING
- CALL CRLF
- LD HL,SPACE.46
- LD B,3
- CALL LIST.STRING
- CALL CRLF
- ;**************I HAF MADE A MISTAKE (I THINK)
- ; We appear to be 1.5 lines short (approx),
- ; so let's fudge the correct solution by doing
- LD HL,FUDGE
- LD B,3
- CALL LIST.STRING
- CALL CRLF
- ;Exit after resetting to 12 dot lines
- LD HL,SPACE.12
- LD B,2
- CALL LIST.STRING
- ;
- RET
- ;
- ;
- ; ANNOTY Stores strings for annotating the rows of the Y axis
- ANNOTY:
- IF NOT FORTRAN
- INC HL ;Point past string length byte
- ENDIF
- CALL GET@HL
- LD (X.COORD),HL ;Actually a string address is saved
- EX DE,HL
- CALL GET@HL
- ;Now check that the Y coordinate (in HL) is < 200 and compensate
- ; for the fact that the top graph row is printed first
- LD DE,199 ;The Y axis goes from 0 to 199
- XOR A ;Clear carry
- EX DE,HL ;Get Ycoord to DE, subtract it from 199
- SBC HL,DE ;If Y coord was > 199 will get a carry
- RET C ;So we will exit if out of range
- ;
- ;We now get the row count into HL
- ; by dividing HL by 8
- XOR A
- RR H ; divide HL by 2
- RR L
- RR H ; /4
- RR L
- RR H ;/8
- RR L
- ; DEC L ;make row go 0 to 24, not 1 to 25
- LD A,L
- SCF ;And compute the offset into the ANNOT.BUFFER
- CCF
- RL A ; *2
- RL A ; *4
- ADD L ; *4 + *1 = *5
- LD C,A
- LD B,0
- LD HL,ANNOT.BUFFER
- ADD HL,BC ;Addr of where string goes is in HL
- LD DE,(X.COORD) ;Get pointer to string
- LD B,5 ;Bytes to copy
- LP9: LD A,(DE) ;copy them
- LD (HL),A
- INC DE
- INC HL
- DJNZ LP9 ;Loop till done
- RET
- ;
- ;
- ;
- ; MARK, A subroutine to mark a square around a dot in the array
- MARK: CALL GET@HL ;Get the first passed parameter
- LD (X.COORD),HL
- EX DE,HL ;Now get the second passed param
- CALL GET@HL ;into HL
- ;Now check that the Y coordinate (in HL) is < 200 and compensate
- ; for the fact that the top graph row is printed first
- LD DE,199 ;The Y axis goes from 0 to 199
- XOR A ;Clear carry
- EX DE,HL ;Get Ycoord to DE, subtract it from 199
- SBC HL,DE ;If Y coord was > 199 will get a carry
- RET C ;So we will exit if out of range
- LD (Y.COORD),HL
- ; Processed parameters, now mark the array thus:
- ; ..... Start 2 rows and 2 dots backwards, mark 5
- ; . . inc Y by 1, decr X by 4, dot, inc X by 4, dot
- ; . . . etc
- ; . . etc
- ; ..... 5 final dots
- ;
- CALL PLOT2 ;And plot the wanted point
- LD DE,-2 ;Now the top left of square
- CALL INCX
- CALL INCY
- CALL PLOT2
- ;
- LD B,4 ;4 more dots
- LP10: LD DE,1
- CALL INCX
- PUSH BC
- CALL PLOT2
- POP BC
- DJNZ LP10
- ;
- LD B,3 ;Three lines of fence
- LP11: PUSH BC
- LD DE,1
- CALL INCY
- LD DE,-4
- CALL INCX
- CALL PLOT2
- LD DE,4
- CALL INCX
- CALL PLOT2
- POP BC
- DJNZ LP11
- ;
- LD DE,1
- CALL INCY
- LD DE,-5
- CALL INCX
- LD B,5 ;5 dots
- LP12: LD DE,1
- CALL INCX
- PUSH BC
- CALL PLOT2
- POP BC
- DJNZ LP12
- ;
- RET ;Done marking
- ;
- PLOT2: ;Before jumping to PLOT routine check that coords
- ;are within the correct range
- LD HL,(Y.COORD)
- LD DE,-199-1
- ADD HL,DE
- RET C ;Exit if out of range
- JR PLOT3
- ;
- ; S/Rs to increment the coordinates by the amount in DE
- INCX: LD HL,(X.COORD)
- ADD HL,DE
- LD (X.COORD),HL
- RET
- INCY: LD HL,(Y.COORD)
- ADD HL,DE
- LD (Y.COORD),HL
- RET
- ;
- ;
- ; PLOT, A Subroutine to put a dot in the array,
- ; given the X coordinate in HL
- ; & Y coordinate in DE
- PLOT:
- CALL GET@HL ;Get the first passed parameter
- LD (X.COORD),HL
- EX DE,HL ;Now get the second passed param
- CALL GET@HL ;into HL
- ;Now check that the Y coordinate (in HL) is < 200 and compensate
- ; for the fact that the top graph row is printed first
- LD DE,199 ;The Y axis goes from 0 to 199
- XOR A ;Clear carry
- EX DE,HL ;Get Ycoord to DE, subtract it from 199
- SBC HL,DE ;If Y coord was > 199 will get a carry
- RET C ;So we will exit if out of range
- LD (Y.COORD),HL
- ;
- PLOT3: LD HL,(X.COORD) ;Get X coord to DE
- LD DE,-399-1 ;Check it is less than 399
- ADD HL,DE
- RET C ;Exit if out of range
- ;We now get the row count into HL and the dot count into E
- ; by dividing HL by 8 and putting the remainder into E
- LD HL,(Y.COORD) ; get data again
- XOR A
- LD E,A ;Clear carry and E
- RR H ; divide HL by 2
- RR L
- RR E ;E contains the carry bit mask
- RR H ; /4
- RR L
- RR E
- RR H ;/8
- RR L
- RR E ;Top 3 bits contain the remainder
- LD (Y2.COORD),HL ;Save our row count
- RR E
- RR E
- RR E
- RR E
- RR E ;Now E is fully adjusted with remainder in 3 LSBs
- LD A,7 ;We need to adjust remainder rel to 7
- SUB E ;So that the interpolation is correct
- LD (BIT.MASK),A ;Save it
- ;The coordinate of the byte to be marked is (Y/8)*400 + X
- LD A,8 ;Set up the multiply S/R
- LD (POKE1),A ; to be 8 x 8
- LD DE,400 ;chars/row
- LD BC,(Y2.COORD) ;Get scaled Y coordinate to BC
- CALL MULTIPLY ;The result will be in HL
- LD DE,(X.COORD) ;Add the X coordinate
- ADD HL,DE
- LD DE,BUFFER ;and the buffer base address
- ADD HL,DE ;This is the address of the wanted byte
- LD A,(BIT.MASK)
- LD B,A ;Ready for the DJNZ
- INC B ;Make shifts 1-8, not 0-7
- SCF ;set carry
- LD A,0 ;If remainder = 0 then LSB is set
- LP7: RL A ;On first shift LSB is set
- DJNZ LP7 ;And so on
- ; A now contains a mask we OR with memory to set the required bit
- OR A,(HL)
- LD (HL),A
- RET
- ;
- ;
- ;
- ; MATHLIB: a 16 Bit Arithmetic Package for Z80
- ; Sourced 26 Nov '80 by Trevor Marshall
- ;
- MULTIPLY: ; 16 x 16 Bit multiplication
- ;
- ;result (HL) = multiplicand (DE) x multiplier (BC)
- ;
- ; The multiplicand is in DE
- ; The multiplier is in BC
- ; The result will be in HL
- ; It overflows safely, but without indication
- ; Registers are destroyed
- ;
- ; Example: 5 x 3
- ; 101
- ; x 011
- ; ----------------
- ; 101 (Shifted LSBit=1)
- ; 101 (Shifted LSBit=1)
- ; 000 (Shifted LSBit=0,no add)
- ; ----------------
- ; 01111 (Result)
- ;
- ; Multiplier is in BC
- LD A,16 ; Loop count in A
- ;count must be >= max number of bits used in BC
- POKE1 EQU $-1 ;address of loop count byte
- ;Allows SELF-MODIFYING code
- LD HL,0 ;Clear result
- ZZMULT:
- ; is multiplier LSBit = 1 ?
- SRL B ;Right shift multiplier MSB
- ; 0 -> MSBit, LSBit -> Carry
- RR C ;Rotate right multiplier LSB
- ;Carry -> MSBit, LSBit -> Carry
- JR NC,ZZNOADD ;LSBit not 1, Dont add
- ; Could test for overflow by using this here:
- ; CCF ;Carry will be 1, C -> 0
- ; ADC HL,DE
- ; JR C,OVERFLOW.ROUTINE
- ; But will use the simpler
- ADD HL,DE ;LSBit = 1, so add multiplicand
- ; to (shifted) result
- ZZNOADD:
- ;Now we shift left the multiplicand
- SLA E ; 0 -> LSBit, MSBit -> Carry
- RL D ; Carry -> LSBit, MSBit -> Carry
- ;
- DEC A ;Loop cntr
- JR NZ,ZZMULT
- ;
- RET ; ***** DONE *****
- ;
- ;
- ;
- X.COORD: DW 0
- Y.COORD: DW 0
- Y2.COORD: DW 0
- BIT.MASK: DB 0
- Y.ADDR: DW 0
- X.ADDR: DW 0
- ;
- START.GRAPHICS: DB 1BH,'K'
- DW 400 ;The number of bytes
- SPACE.1: DB 1BH,'3',3
- SPACE.8: DB 1BH,'3',24
- SPACE.12: DB 1BH,'2'
- SPACE.85: DB 1BH,'3',255
- SPACE.46: DB 1BH,'3',138
- FUDGE: DB 1BH,'3',90
- SPACE: DB ' '
- NORMAL: DB 1BH,'F',1BH,'H',1BH,'Q',1BH,'T' ;Cancel all special modes
- EXP.ON: DB 1BH,'S',1BH,'E'
- COMPRESSED: DB 1BH,'P'
- ;
- CHAR: DW 0
- BUFF.PTR: DW 0
- STRING.PTR: DB 0
- ROW: DB 0
- ;
- ;
- ;
- IF NOT TESTING
- DATA
- ENDIF
- ;
- ; The main data array buffer:
- BUFFER: DS 400*25 ;10k Locs of 8 bytes = 80k bits
- BUFF.LENGTH EQU $-BUFFER
- ; The buffer is organized as 25 rows each of 400 bytes,
- ; each row being printed in turn (descending)
- ;
- ; The buffer for Y axis strings:
- ANNOT.BUFFER: DS 25*5 ;25 rows of 5 chars
- ;
- END