home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.whtech.com
/
ftp.whtech.com.tar
/
ftp.whtech.com
/
Geneve
/
9640news
/
CAT14
/
RDWRTG.ARK
< prev
next >
Wrap
Text File
|
2006-10-19
|
19KB
|
515 lines
?
A Graphics Programming Language (GPL)
Primer &
Routine to Read/Edit Text from KSCAN
by Mack McCormick
I have always avoided delving into the study of GPL because I felt
it was too difficult, cumbersome, executed too slowly, and had little
to offer. Boy, was I wrong. It makes writing routines used by BASIC a
snap in assembler. For example, I recently needed a routine to read
text from the screen which would allow full editing including erase,
insert, delete, quit, bonk tone at right margin, and enter/up
arrow/down arrow. I also wanted the neat auto-repeat feature used by
TI where there is a slight pause before the key takes off repeating. I
began to consider writing the routine but then remembered that an
identical routine resided in GPL in GROM (Graphics Read Only Memory) in
the console. I first consided using the routine from GROM but then
remembered that it added the screen offset of >60 to each character and
I didn't need that. I could have done some fancy trick to make it work
but decided to convert GPL to 9900 assembler code.
You'll find two programs here. One to link you to the routine in
console GROM from a CALL LOAD from E/A BASIC and the identical (almost)
routine in 9900 assembler code ready to link into any program you may
have that needs this utility. I've included in the 9900 routine the
actual GPL code used by the Pre-Scan routine of the monitor so you may
see what conversions were necessary. Try reading the GPL instructions
(marked with three *) to get a flavor for GPL). If the GPL gets in the
way if using the routine in your program you may delete the GPL
statements though they will have no effect if they remain.
It has really become obvious to me why TI invented GPL though I
used to condem them for it. The major reason is that it saves about
41% more code than straight assembler. GROM as you may know is only
used by TI and is a chip which supplies a byte at a time to a memory
mapped address and auto-increments to the next byte (like VDP RAM)
unless you change the address to be read from. It's a great way to
save memory. TI calls it medium speed memory. It is 6K bytes big and
resides on 8K boundaries. It is an ideal medium to hold the console
BASIC routines because the TMS9900 CPU chip in the console can only
directly address 64K. The GPL actually does not execute any code. GPL
is interpreted in console ROM beginning at >0024 and extending to >D18.
This interpreter is straight assembler code which acts as directed by
the GPL bytes coming from the GROM. Hence you see one reason TI BASIC
is slow. It is an interpreted by GPL and GPL is interpreted by
assembler. Two interpretations! Instructions in GPL usually have two
operands and most instructions can access RAM, GROM, or VDP RAM. Most
instructions are single byte operands unless the operand is preceded by
a D for double operand. GPL uses two stacks a data stack at >83A0 and
a subroutine address stack at >8380 (this allows arbitrary nesting of
subroutines). Here are a few types of instructions:
DATA TRANSFER -Single/Double Byte
-Block to Block
-Formatted Block Moves
ARITHMETIC -add, subtract, multiply, divide, negate, absolute val.
LOGICAL -AND, XOR, Shifts.
CONDITION -Arithmetic and Logical
BRANCHING -Conditional/Uncond
BIT MANIPULATION -Set, reset, test.
SUBROUTINE -Call, Return
STACK OPNS -Push and Pop
MISC -Random Number, KSCAN, Coincidence Detection, Sound, I/O
The closest language to GPL is assembler and any experienced
assembler programmer should have little difficulty learning GPL. One
major difference is the use of MACRO instructions by the assembler such
as REPEAT....UNTIL and IF....THEN....ELSE. Very similar in this
respect to 99000 assembly language.
A few words about how memory is addressed. Here are a few of the
most common ways and their syntax. 5 represents the decimal byte 5.
>33 represents hex 33. &110011 represents binary 110011. #5506
represents the decimal number 5506. :A: is the ASCII equivalent >41.
Well this has been a *very* general overview of GPL. Let's look
at some actual GPL source code and my intrepretation of the 9900
assembler equivalent. This routine could have been shortened but I
tried to keep it as close to GPL as possible. Hope you enjoy it. If
you have questions just ask. My 6 GPL manuals cover thousands of pages
and we have just skimmed the surface here. I plan to write a GPL
disassembler and interpreter to convert GPL to 9900 object code within
the next six months if my schedule permits. That should make the job
easy!
DEF START
REF KSCAN,VSBW,VSBR,GPLLNK
* JUST A LITTLE ROUTINE TO TEST SUBROUTINE *
START LWPI WS
MOVB @H00,@KEYVAL SCAN ENTIRE KEYBOARD
LOOP BL @READLN
DATA >002,>2FE START, END POSITONS
JMP LOOP
********************************************************************************
* This is the console GPL READLN routine at (>2A42 in GROM 1) converted to 9900
* assembler. Interprets BACKSPACE, INSERT, DELETE, and FORWARD. Uses SCRATCH
* PAD RAM. Total number of characters may be limited by changing the start
* value of ARG+2 (upper limit) and entering at READL1. VARW is the start address
* of the field. VARA is the current highest write address.
* Entering at READL1 allows us to pre-specify the minimum number of characters
* to be read for default creation.
* Entering at READ00 allows specification of the initial cursor position. In
* this case ARG+6 has to be set to the cursor position and ARG+4<>0.
* Programmer responsibility to insure that VARW <= ARG+6 <= VARA <= ARG+2
* ARG+4 indicates if the line has been changed. If so, ARG+4=0.
* This is a possible call:
* BL @READLN
* DATA >1DF,>35D LOWER,UPPER SCREEN LIMITS
********************************************************************************
* EQUATES *
WS EQU >8300 MY WORKSPACE
ARG EQU >835C
VARW EQU >8320 ABS LOWER LIMIT
VARA EQU >832A CURRENT END OF LINE
TEMP EQU 0 R0 USED FOR TEMP STORAGE
TEMP1 EQU 1 R1 USED FOR ADDL TEMP STORAGE
R1LB EQU WS+3
TEMP2 EQU 2
TEMP3 EQU 3
TIMER EQU >8379 VDP TIMER INC EVERY 1/60 SEC.
KEYVAL EQU >8374 KEYBOARD TO SCAN
RKEY EQU >8375 KEY CODE
STATUS EQU >837C GPL STATUS BYTE
* CONSTANTS * (Should be EQU with byte values in code to save memory.)
H00 BYTE 0
H01 BYTE 1
HFF BYTE >FF
H508 DATA 508
H60 DATA 60
H14 BYTE 14
H766 DATA 766
BREAK BYTE >02
DLETE BYTE >03
INSRT BYTE >04
CLRLN BYTE >07
BACK BYTE >08
FORW BYTE >09
DOWN BYTE >0A
MVUP BYTE >0B
CHRTN BYTE >0D
CURSOR BYTE >1E
SPACE BYTE >20
VARV BYTE 0 (This is at >8301 in GPL but I use >8300 for workspace)
VAR1 DATA 0 AUTO REPEAT COUNTER (This is 1 byte at >830D in GPL)
EVEN
READLN
* The GPL code stores >35D at ARG+2 but to give more utility replaced with the
* next two lines of code.
*** DST >35D,@ARG+2 GPL DOUBLE STORE
MOV *R11+,@VARW START ADDRESS OF THE FIELD
MOV *R11+,@ARG+2 UPPER LIMIT
*** DST @VARW,@VARA
MOV @VARW,@VARA NOTHING ENTERED YET
* VARA SHOULD POINT TO A SPACE LOCATION OR END OF FIELD
READL1
*** ST 1,@ARG+4 STORE BYTE=1 TO ARG+4
MOVB @H01,@ARG+4 MEANS NO CHANGE IN LINE
READL2
*** DST @VARW,@ARG+5 HAD TO USE ARG+6 BECAUSE OF WORD BOUNDARY PROBLEMS
MOV @VARW,@ARG+6 POSITION CURSOR AT START OF FIELD
READ00
*** CLR @VAR1 CLEAR BYTE. I HAD TO USE WORD BECAUSE 9900 IS SO MUCH
*** FASTER
CLR @VAR1 COUNTER FOR AUTO REPEAT
* This is where we return to exit INSERT mode.
READ01
*** CLR @ARG+7 USED ARG+8 BECAUSE HAD TO USE ARG+6 & ARG+7 ALREADY
MOVB @H00,@ARG+8 NORMAL OPERATION MODE
*** ST CURSOR,@VARV
MOVB @CURSOR,@VARV VARV USED FOR CURSOR/CHARACTER
READ$1
* Input 1 char and alternate cursor and character for blink
*** EX @VARV,RAM(@ARG+5) EXCHANGE @VARG WITH WHATS AT LOCATION ARG+5 IN VDP
MOV @ARG+6,TEMP EXCHANGE VARV,ARG+6
BLWP @VSBR
SWPB TEMP1
MOVB @VARV,TEMP1
BLWP @VSBW
MOVB @R1LB,@VARV
*** CLR @TIMER
MOVB @H00,@TIMER SET VDP TIMER TO ZERO
*** $REPEAT MACRO. REPEAT CODE UNTIL $UNTIL IS TRUE
L00001 LIMI 2 ENABLE INTERRUPTS SO THE VDP TIMER (>8379) CAN INC
LIMI 0 DISABLE INTERRUPTS SO THE VDP WON'T GET MESSED UP
*** SCAN SCAN THE KEYBOARD
BLWP @KSCAN SCAN FOR A CHARACTER
*** BS READ$2 BRANCH ON COND BIT (EQ) SET
MOVB @STATUS,@STATUS EQUAL BIT SET?
JNE READ$2 FOUND A NEW CHARACTER
*** INC @VAR1 INCREMENT THE BYTE @VAR1 BY ONE
INC @VAR1 INC AUTO-REPEAT COUNTER
*** $IF @RKEY .NE. >FF THEN MACRO. IF RKEY NOT EQ >FF THEN EXECUTE THE
*** FOLLOWING CODE OTHERWISE SKIP TO THE $END IF TERMINATOR
CB @RKEY,@HFF OLD KEY?
JEQ L00002 YEP
*** $IF @VAR1 .HE. 254 THEN HIGHER OR EQUAL
C @VAR1,@H508 HOLD OLD KEY FOR A WHILE
* HAD TO DOUBLE 254 TO SLOW DOWN ASSEMBLY CODE
JLT L00002 BEFORE STARTING REPEAT
*** SUB 30,@VAR1 SUBTRACT BYTE
S @H60,@VAR1 CONTROL REPEAT RATE
*** B READ$3 UNCONDITIONAL BRANCH
JMP READ$3
*** $END IF
*** $END IF
*** $UNTIL @TIMER .H. 14 TERMINATOR FOR REPEAT UNTIL HIGHER THAN 14
L00002 CB @TIMER,@H14
JLE L00001 TIME NEXT CHARACTER SWITCH
*** BR READ$1 BRANCH COND BIT RESET. USED TO SAVE ONE BYTE OF MEMORY
JMP READ$1 RESTART CHAR BLINK CYCLE
READ$2
*** CLR @VAR1
CLR @VAR1 CLEAR AUTO REPEAT COUNTER
READ$3
*** $IF @VARV .NE. CURSOR THEN
CB @VARV,@CURSOR IF NE EXCHANGE AGAIN
JEQ L00003
*** EX @VARV,RAM(@ARG+5)
MOV @ARG+6,TEMP EXCHANGE VARV,ARG+6
BLWP @VSBR
SWPB TEMP1
MOVB @VARV,TEMP1
BLWP @VSBW
MOVB @R1LB,@VARV
*** $END IF
*** $IF @RKEY .L. : : THEN IF RKEY LESS THAN SPACE THEN EXECUTE CODE
L00003 CB @RKEY,@SPACE IF .LT. SPACE THEN CONTROL CHAR
JLT L00004
B @L0000C
* THIS IS WHERE YOU WOULD TRAP ALL CONTROL CODES *
* HANDLE BREAK CHAR FIRST
* CB @RKEY,@BREAK
* JNE LABLE
* BACK ARROW - SPACE BACK ONE POSITION
*** $END IF
*** $IF @RKEY .EQ. BACK GOTO RBACK GOTO's DO NOT REQUIRE AN END IF TERM
L00004 CB @RKEY,@BACK BACK ARROW?
JNE B00002 TO FIX OUT OF RANGE ERROR
B @RBACK
* RIGHT ARROW - FORWARD SPACE
*** $IF @RKEY .EQ. FORW GOTO FORW
B00002 CB @RKEY,@FORW
JNE B00003 TO FIX OUT OF RANGE ERROR
B @RFORW
* INSERT *
*** $IF @RKEY .EQ. INSRT THEN
B00003 CB @RKEY,@INSRT
JNE L00005
*** ST 1,@ARG+8
MOVB @H01,@ARG+8 SET INSERT MODE FLAG
*** $END IF
* DELETE - DELETE THE CURRENT CHAR
*** $IF @RKEY .EQ. DLETE THEN
L00005 CB @RKEY,@DLETE
JNE L00006
*** CLR @ARG+4
MOVB @H00,@ARG+4 INDICATE A CHANGE IN LINE
*** $IF @VARA .DNE. @ARG+6 THEN THE D MEANS DOUBLE OR WORD OF MEMORY COMPARE
C @VARA,@ARG+6 EMPTY LINE?
JEQ L0001F YEP.
*** DST @VARA,@ARG
MOV @VARA,@ARG MOVE EVERYTHING FROM THE RIGHT
*** DSUB @ARG+5,@ARG DOUBLE BYTE (WORD) SUBTRACT
S @ARG+6,@ARG OF THE CURSOR TO THE LEFT
*** MOVE @ARG FROM RAM(1(ARG+6)) TO RAM(@ARG+5) THIS IS A BLOCK MOVE OF @ARG
*** BYTES OF VDP RAM FROM WHATS AT ADDR ARG+6 PLUS 1 TO WHATS AT ADDRESS
*** ARG+6. IN SHORT MOVE EVERYTHING ON SCREEN ONE BYTE LOWER.
MOV @ARG,TEMP2 COUNTER
MOV @ARG+6,TEMP
INC TEMP MOVE @ARG FROM RAM(1(ARG+6)) TO RAM(@ARG+6)
L00008 BLWP @VSBR
DEC TEMP
BLWP @VSBW
INCT TEMP
DEC TEMP2
JNE L00008
*** DDEC @VARA DECREMENT THE WORD (DOUBLE) AT VARA
DEC @VARA PRE-UPDATE END OF STRING
*** $IF RAM(@VARA) .EQ. : :+OFFSET GOTO READ01 OFFSET IS SCREEN OFFSET >60
MOV @VARA,TEMP
BLWP @VSBR
CB @TEMP1,@SPACE
JNE B00001 TO RESOLVE OUT OF RG ERROR
B @READ01
*** DINC @VARA INCREMENT THE WORD OF MEMORY AT VARA
B00001 INC @VARA
*** ST : :+OFFSET,RAM(@VARA)
L0001F MOV @VARA,TEMP
LI TEMP1,>2000
BLWP @VSBW
*** BR READ01
B @READ01
* CLEAR - Clear the entire input line
*** $IF @RKEY .EQ. CLRLN THEN
L00006 CB @RKEY,@CLRLN
JNE L00009
*** $REPEAT
*** ST : :+OFFSET,RAM(@VARA)
MOVB @SPACE,TEMP1
CLRLIN
MOV @VARA,TEMP SO WE CAN FIDDLE WITH VALUE
BLWP @VSBW
DEC @VARA PRE-UPDATE END OF LINE
*** $UNTIL @VARA .DL. @VARW DOUBLE LESS THAN
C @VARA,@VARW UP TO AND INCL FIRST POS
JHE CLRLIN
*** DINC @VARA
INC @VARA UNDO LAST SUBTRACTION
CLR @ARG+4
MOVB @H00,@ARG+4 INDICATE CHANGE
*** BR READL2
B @READL2 RESTART EVERYTHING
*** $END IF
* GENERAL EXIT POINT
*** $IF @RKEY .NE. CHRTN THEN
L00009 CB @RKEY,@CHRTN ONLY REACT ON CR/UP/DOWN
JEQ L0000A
*** $IF @RKEY .NE. MVUP THEN
CB @RKEY,@MVUP
JEQ L0000A
*** $IF @RKEY .NE. DOWN GOTO READ$1
CB @RKEY,@DOWN
JEQ L0000A
B @READ$1
*** $END IF
*** $END IF
*** $IF @VARA .DEQ. @ARG+2 THEN DOUBLE EQUAL
L0000A C @VARA,@ARG+2 CHECK FOR BLOCK ON LAST POSITION
JNE L0000B
*** $IF RAM(@VARA) .NE. : :+OFFSET THEN
MOV @VARA,TEMP
BLWP @VSBR
CB TEMP1,@SPACE BLOCKED?
JEQ L0000B
*** DINC @VARA
INC @VARA POINT BEYOND LAST CHAR IN LINE
*** $END IF
*** $END IF
L0000B RT ENTER THE CURRENT LINE
*** $END IF (THIS IS FROM THE $IF THAT CHECKED FOR CTRL CODES)
* INSERT ROUTINE *
*** $IF @ARG+8 .NE. 0 THEN INSERT
L0000C CB @ARG+8,@H00 INSERT MODE
JEQ L0000D
READ$4
*** DST @VARA,@ARG
MOV @VARA,@ARG USE ARG AS TEMP FOR INSERT
*** $WHILE @ARG .DH. @ARG+6
L0000F C @ARG,@ARG+6 MOVE EVERYTHING UP TO CURSOR LOCATION
JLE L0000E
*** DDEC @ARG
DEC @ARG COPY LOWER LOCATION TO HIGHER ONE
*** ST RAM(@ARG),RAM(1(ARG)) GO FROM HIGH TO LOW IN VDP RAM
MOV @ARG,TEMP
BLWP @VSBR
INC TEMP
BLWP @VSBW
JMP L0000F
*** $SEND WHILE TERMINATOR FOR WHILE
*** $IF @VARA .DL. @ARG+2 THEN
L0000E C @VARA,@ARG+2 ONLY UPDATE VARA AS UPPER
JHE L0000D
*** DINC @VARA
INC @VARA HASN'T BEEN REACHED YET
*** $END IF
*** $END IF
*** ST @RKEY,RAM(@ARG+6)
L0000D MOVB @RKEY,TEMP1 DISPLAY THE CHARACTER
MOV @ARG+6,TEMP
BLWP @VSBW
*** CLR @ARG+4
MOVB @H00,@ARG+4 INDICATE CHANGE IN LINE
READ05
*** $IF @ARG+5 .DEQ. @ARG+2 THEN
C @ARG+6,@ARG+2 HIT RIGHT MARGIN?
JNE L0002F
*** CALL TONE2 CALL ANOTHER GPL ROUTINE IN THIS CASE BONK
MOVB @H00,@STATUS CLEAR THE STATUS BYTE BEFORE ACCESSING GPL
BLWP @GPLLNK GIVE A BAD RESPONSE TONE
DATA >0036
*** BR READ$1
B @READ$1 STAY IN CURRENT MODE
*** $END IF
*** DINC @ARG+6
L0002F INC @ARG+6 UPDATE CURRENT ADDRESS
*** IF @ARG+6 .DH. @VARA THEN
C @ARG+6,@VARA CHECK FOR LAST NEW HIGH LIMIT
JLE L00010
*** DST @ARG+5,@VARA
MOV @ARG+6,@VARA UPDATE NEW HIGH LIMIT
*** $END IF
*** $IF @VARA .DL. >2FE GOTO READ$1
L00010 C @VARA,@H766
JHE L00011 TO FIX OUT OF RANGE PROBLEM
B @READ$1 STILL SOME SPACE TO GO
L00011
* This is where we could scroll the screen if needed
* UPDATE POINTERS IF YOU SCROLL *
*** CALL SCROLL SCROLL THE SCREEN
*** DSUB 28,@VARA
* S @H28,@VARA BACK TO START OF LINE
*** DSUB 32,@VARW
* S @H32,@VARW BACKUP START LINE ADDRESS
*** DSUB 32,@ARG+2
* S @H32,@ARG+2 ABSOLUTE HIGH LIMIT BACKS UP TOO
*** DSUB 32,@ARG+6
* S @H32,@ARG+6 CURRENT CURSOR POSITION ALSO
*** BR READ$1
B @READ$1 START WITH SOMETHING ELSE
* FORWARD CURSOR MOVE
RFORW
*** CLR @ARG+8
MOVB @H00,@ARG+8 LEAVE INSERT MODE
*** BR READ05
B @READ05 USE REST OF LOGIC
* BACK CURSOR MOVE
RBACK
*** $IF @ARG+5 .DH. @VARW
C @ARG+6,@VARW CHECK BOTTOM RANGE
JLE L00012
*** DDEC @ARG+6
DEC @ARG+6
*** $END IF
*** BR READ01
L00012 B @READ01
END
****************************************
* THIS IS A ROUTINE TO DIRECTLY ACCESS *
* THE GROM READLN ROUTINE. USE CALL *
* LOAD("DSK1.FILENAME") AND CALL LINK *
* ("DSK1.START") FROM E/A BASIC TO SEE *
* IT BECAUSE OF SCREEN OFFSET. *
****************************************
DEF START
GPLWS EQU >83E0 ADDRESS FOR GPL WORK SPACE
H00 BYTE 0
WS BSS >20 MY WORKSPACE
EVEN
START
LWPI WS POINT TO MY WORKSPACE
LI R0,>2
MOV R0,@>8320 START SCREEN ADDRESS FOR SCAN
MOVB @H00,@>837C CLEAR THE STATUS BYTE SO WE DON'T GET AN ERROR
BLWP @GPLLNK LINK TO THE ROUTINE IN GROM
DATA >2A42
MOVB @H00,@>837C RETURN TOTHE CALLING PROGRAM ON ENTER
LWPI GPLWS
B @>0070
* YOU COULD HAVE PLACED AN END STATEMENT HERE AND REF'd GPLLNK INSTEAD
* OF USING THIS ROUTINE.
* GPLLNK ROUTINE *
GPLLNK DATA UTILWS,XGPL VECTOR FOR THE GPLLNK BLWP
UTILWS EQU >2094
SUBSTK EQU >8373
FLAG2 EQU >8349
SVGPRT EQU >2030
H20 BYTE >20
EVEN
XGPL
MOVB @SUBSTK,R1
SRL R1,8
MOV *R14+,@>8304(R1)
SOCB @H20,@FLAG2
LWPI GPLWS
MOV @SVGPRT,R11
RT
END
Download complete. Turn off Capture File.