HOME AUTOMATION Posted May 27, 2020 Share Posted May 27, 2020 13 minutes ago, speccery said: Mini Memory does implement DSRs, and it even does that in GPL code. GPL has veered me away from that DSR, as well. I believe TERMINAL EMULATOR II, also uses a device DSR, "SPEECH". 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted May 27, 2020 Share Posted May 27, 2020 The thing with GPL DSRs is that they only work from BASIC because assembly programs generally don't look for them. 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted May 27, 2020 Share Posted May 27, 2020 19 minutes ago, Asmusr said: The thing with GPL DSRs is that they only work from BASIC because assembly programs generally don't look for them. Assembly sucks at DSR unless you have a GPL DSRLNK DSR from Miller Graphics. This has been stated for over 20 years as a fact. Assembly DSR only find ROM DSR and nothing else, hence why the suck. 2 Quote Link to comment Share on other sites More sharing options...
speccery Posted May 27, 2020 Author Share Posted May 27, 2020 1 hour ago, Asmusr said: The thing with GPL DSRs is that they only work from BASIC because assembly programs generally don't look for them. I have written the SD card DSR for my ET-PEB board (and before that for the FPGA system). I suppose that has been the 'easy' part. The receiving end of a DSR is just parsing the PAB, and trying to do the right thing. My DSR does not properly deal with all the weird single character named DSR routines (named hex 10 to hex 16) for sector I/O. But calling the DSRs is another matter. A related matter is that I still don't know how to call machine code from GPL, or vice versa, and this is something I should learn at some point rather soon. If I have understood correctly, all the various DSRLNK routines are simple header walking routines, resolving a string to an address. If the address is in ROM, it's all good to go and ready for a branch to that address, but if it is a GROM address I suspect some setup is needed for scratchpad variables before jumping to somewhere in ROM to start the GPL interpreter. And I don't know how to return from there. Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted May 27, 2020 Share Posted May 27, 2020 Possibly the only 2 things I know in GPL. Learned here: XML instruction here: GPL: the GROM language (I) (Opcode: >0F) jump table here: Part II Return: B @>6A or >70 or use R11, I think? 2 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted May 27, 2020 Share Posted May 27, 2020 I never found out where the DSRLNK that BASIC uses is located, and why we don't use that from assembly. Is it because it's written in GPL? 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 27, 2020 Share Posted May 27, 2020 It's because it returns into GPL. It would have been so easy for TI to do it properly, but no. 3 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 27, 2020 Share Posted May 27, 2020 (edited) 2 hours ago, Asmusr said: I never found out where the DSRLNK that BASIC uses is located, and why we don't use that from assembly. Is it because it's written in GPL? I use it in fbForth 2.0 via the MG DSRLNK/GPLLNK. The trick is to get the GPL code to return to ALC, which is what the MG code does. The GPL DSRLNK is located in the console GROM 0 at >03D9 (see Heiner Martin’s TI99/4A Intern, p. 104). Not only does it check through all the DSRs in CRU space (>4000), but, if it exhausts CRU space, it drops through to GSRLNK, which checks every GROM (0 – 15?) at the GROM base address in GPLWS R13, which includes the cassette tape DSR in console GROM 0. I do not know whether it tries to check other GROM bases—I have not analyzed it that far. FYI, here is the MG DSRLNK/GPLLNK I modified slightly to use in fbForth 2.0: Spoiler LLVSPT ; <--This is the source copy location for the rest of this code. $BUFF EQU >2010 * 4 I/O buffers below ($LO = >3020) * Change '4' to number of buffers needed and for which there is room. $LO EQU 4*>404+$BUFF start of low-level routines after I/O buffers * EQUating labels below to $LO+$-LLVSPT insures those labels refer to the * addresses where they get copied. * . * . * . * ________ __ __ _ ____ __ __ ________ * / ___/ _ \/ / / / / |/ / //_/ / |/ / ___/ * / (_ / ___/ /__/ /__/ / ,< _ _ _ / /|_/ / (_ / * \___/_/ /____/____/_/|_/_/|_| (_|_|_)_/ /_/\___/ * *-----------------------------------------------------------------------* ;[*== GPLLNK- A universal GPLLNK - 6/21/85 - MG ========================= * {LES NOTE: Some labels have been modified for fbForth compatibility.} * * * * This routine will work with any GROM library slot since it is * * indexed off of R13 in the GPLWS. (It does require Mem Expansion) * * This GPLLNK does NOT require a module to be plugged into the * * GROM port so it will work with the Editor/Assembler, * * Mini Memory (with Mem Expansion), Extended Basic, the Myarc * * CALL LR("DSKx.xxx") or the CorComp Disk Manager Loaders. * * It saves and restores the current GROM Address in case you want * * to return back to GROM for Basic or Extended Basic CALL LINKs * * or to return to the loading module. * * * * ENTER: The same way as the E/A GPLLNK, i.e., BLWP @GPLLNK * * DATA >34 * * * * NOTES: Do Not REF GPLLNK when using this routine in your code. * * * * 70 Bytes - including the GPLLNK Workspace * *-----------------------------------------------------------------------* GPLWS EQU >83E0 GPL workspace G_R4 EQU GPLWS+8 GPL workspace R4 G_R6 EQU GPLWS+12 GPL workspace R6 SUBSTK EQU >8373 GPL Subroutine stack pointer LDGADR EQU >60 Load & Execute GROM address entry point XTAB27 EQU >200E Low Mem XML table location 27 * ..Will contain XMLRTN at startup GETSTK EQU >166C *++------------------------------------------------------------------------++* *++---ADDRESS: >3784 in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* GPLLNK EQU $LO+$-LLVSPT DATA GLNKWS R7 Set up BLWP Vectors DATA GLINK1 R8 * RTNADR EQU $LO+$-LLVSPT <---don't think we need this label DATA XMLRTN R9 address where GPL XML returns to us... * ...this address will already be in XTAB27,... * ...>200E, so don't really need it here} GXMLAD EQU $LO+$-LLVSPT DATA >176C R10 GROM Address for GPL 'XML >27' (>0F27 Opcode) DATA >50 R11 Initialized to >50 where PUTSTK address resides *++------------------------------------------------------------------------++* *++---ADDRESS: >3776 in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* GLNKWS EQU $LO+$-LLVSPT->18 GPLLNK's workspace of which only... BSS >08 R12-R15 ...registers R7 through R15 are used *++------------------------------------------------------------------------++* *++---ADDRESS: >3796 in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* GLINK1 EQU $LO+$-LLVSPT MOV *R11,@G_R4 Put PUTSTK Address into R4 of GPL WS MOV *R14+,@G_R6 Put GPL Routine Address in R6 of GPL WS LWPI GPLWS Load GPL WS BL *R4 Save current GROM Address on stack MOV @GXMLAD,@>8302(R4) Push GPL XML Address on stack for GPL Return INCT @SUBSTK Adjust the stack pointer B @LDGADR Execute our GPL Routine XMLRTN EQU $LO+$-LLVSPT MOV @GETSTK,R4 Get GETSTK pointer BL *R4 Restore GROM address off the stack LWPI GLNKWS Load our WS RTWP All Done - Return to Caller ;] * ___ _______ __ _ ____ __ __ ________ * / _ \/ __/ _ \/ / / |/ / //_/ / |/ / ___/ * / // /\ \/ , _/ /__/ / ,< _ _ _ / /|_/ / (_ / * /____/___/_/|_/____/_/|_/_/|_| (_|_|_)_/ /_/\___/ * *-----------------------------------------------------------------------* ;[*== DSRLNK - A Universal Device Service Routine Link - MG ============= * {LES NOTE: Some labels have been modified for fbForth compatibility.} * * * * (Uses console GROM 0's DSRLNK routine) * * (Do not REF DSRLNK or GPLLNK when using these routines) * * (This DSRLNK will also handle Subprograms and CS1, CS2) * * * * ENTER: The same way as the E/A DSRLNK, i.e., BLWP @DSRLNK * * DATA 8 * * * * NOTES: Must be used with a GPLLNK routine * * Returns ERRORs the same as the E/A DSRLNK * * EQ bit set on return if error * * ERROR CODE in caller's MSB of Register 0 on return * * * * 186 Bytes total - including GPLLNK, DSRLNK and both Workspaces * *-----------------------------------------------------------------------* PUTSTK EQU >50 Push GROM Address to stack pointer TYPE$ EQU >836D DSRLNK Type byte for GPL DSRLNK NAMLEN EQU >8356 Device name length pointer in VDP PAB VWA EQU >8C02 VDP Write Address location VRD EQU >8800 VDP Read Data byte location G_R4LB EQU >83E9 GPL Workspace R4 Lower byte GSTAT EQU >837C GPL Status byte location *++------------------------------------------------------------------------++* *++---ADDRESS: >37BE in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* DSRLNK EQU $LO+$-LLVSPT DATA DSRWS,DLINK1 Set BLWP Vectors *++------------------------------------------------------------------------++* *++---ADDRESS: >37C2 in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* DSRWS EQU $LO+$-LLVSPT Start of DSRLNK workspace DR3LB EQU DSRWS+7 lower byte of DSRLNK workspace R3 *++------------------------------------------------------------------------++* *++---ADDRESS: >37C2 in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* DLINK1 EQU $LO+$-LLVSPT MOV R12,R12 R0 Have we already looked up the LINK address? JNE DLINK3 R1 YES! Skip lookup routine *<<-------------------------------------------------------------------------->>* * This section of code is only executed once to find the GROM address * * for the GPL DSRLNK - which is placed at DSRADR and R12 is set to >2000 * * to indicate that the address is found and to be used as a mask for EQ & CND * *------------------------------------------------------------------------------* LWPI GPLWS R2,R3 else load GPL workspace MOV @PUTSTK,R4 R4,R5 Store current GROM address on the stack BL *R4 R6 LI R4,>11 R7,R8 Load R4 with address of LINK routine vector MOVB R4,@>402(R13) R9,R10 Set up GROM with address for vector ***les*** Note on above instruction: ***les*** 1. R13 of GPLWS has >9800=GRMRD (GROM Read Data) ***les*** 2. >402 added to GRMRD yields >9C02=GRMWA (GROM Write Address) JMP DLINK2 R11 Jump around R12-R15 DATA 0 R12 contains >2000 flag when set DATA 0,0,0 R13-R15 contains WS, PC & ST for RTWP DLINK2 MOVB @G_R4LB,@>402(R13) Finish setting up GROM address MOV @GETSTK,R5 Take some time & set up GETSTK pointer MOVB *R13,@DSRAD1 Get the GPL DSR LINK vector INCT @DSRADR Adjust it to get past GPL FETCH instruction BL *R5 Restore the GROM address off the stack LWPI DSRWS Reload DSRLNK workspace LI R12,>2000 Set flag to signify DSRLNK address is set *<<-------------------------------------------------------------------------->>* DLINK3 INC R14 Adjust R14 to point to caller's DSR Type byte MOVB *R14+,@TYPE$ Move it into >836D for GPL DSRLNK MOV @NAMLEN,R3 Save VDP address of Name Length AI R3,-8 Adjust it to point to PAB Flag byte BLWP @GPLLNK Execute DSR LINK *++------------------------------------------------------------------------++* *++---ADDRESS: >3810 in fbForth 2.0:12-------------------------------------++* *++------------------------------------------------------------------------++* DSRADR EQU $LO+$-LLVSPT BYTE >03 High byte of GPL DSRLNK address DSRAD1 EQU $LO+$-LLVSPT BYTE >00 Lower byte of GPL DSRLNK address *----Error Check & Report to Caller's R0 and EQU bit------------------------- MOVB @DR3LB,@VWA Set up LSB of VDP Address for Error Flag MOVB R3,@VWA Set up MSB of VDP Address for Error Flag SZCB R12,R15 Clear EQ bit for Error Report MOVB @VRD,R3 Get PAB Error Flag SRL R3,5 Adjust it to 0-7 error code MOVB R3,*R13 Put it into Caller's R0 (msb) JNE SETEQ If it's not zero, set EQ bit COC @GSTAT,R12 Else, test CND bit for Link Error (00) JNE DSREND No Error, Just return SETEQ SOCB R12,R15 Error, so set Caller's EQ bit DSREND RTWP All Done - Return to Caller ...lee Edited May 27, 2020 by Lee Stewart RESOLVED REFERENCES IN CODE SNIPPET... 4 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 27, 2020 Share Posted May 27, 2020 Just in case anyone is interested, in a few minutes, I will edit the MG code snippet I posted above to resolve references. For fbForth 2.0, that code is copied from ROM to low expansion RAM at startup. ...lee 2 Quote Link to comment Share on other sites More sharing options...
speccery Posted May 27, 2020 Author Share Posted May 27, 2020 1 hour ago, Lee Stewart said: Just in case anyone is interested, in a few minutes, I will edit the MG code snippet I posted above to resolve references. For fbForth 2.0, that code is copied from ROM to low expansion RAM at startup. ...lee Thanks Lee! Need to take a look at this, but not now as it is late here where I live. And thanks to all the other responses by others as well! It is a shame that TI left this half baked on their part. Not that I like GPL, but it is part of what makes the 99/4A what it is, part of it’s character. One more thing to fix if we ever get to building the TI-99/4A Next ? Quote Link to comment Share on other sites More sharing options...
speccery Posted May 27, 2020 Author Share Posted May 27, 2020 2 hours ago, mizapf said: It's because it returns into GPL. It would have been so easy for TI to do it properly, but no. Thanks. Things like these make this machine hard to understand. Unnecessary one way streets. Quote Link to comment Share on other sites More sharing options...
speccery Posted May 31, 2020 Author Share Posted May 31, 2020 I have been having fun programming in TMS9900 assembly. I've been thinking about how to demo the strange cart, which somehow evolved into seeing for instance how Parsec works (i.e. the scrolling there). As a result I wrote some miscellaneous code: - I wrote an assembler routine to convert material on the screen in graphics mode 1 to graphics mode 2. This was a good reminder how these modes work. I wrote the routine since my previous test cartridge code wrote some text on the screen in GM1, and I wanted to use that as test material. This conversion uses the 32K extended memory. It simply first reads VRAM contents to memory extension, and then from that it writes the same stuff back in GM2 memory layout (and it also sets VDP to GM2 of course). At the end the same content appears but in GM2. This was a good reminder that the CPU is not very fast, as it takes surprisingly long, something like a second for this operation to complete. - I then proceed to wrote some support code to get back to GM1. - As part of the GM1 to GM2 conversion I wrote the character indices so that for the top 8 rows it runs linearly from 0..255, which is pretty much the default for GM2. I did the same for the next 8 rows. But for the bottom third of the screen, I used a character layout similar to Parsec: this way the pattern definitions (i.e. contents of character cells) are so that each strip of 64 vertical pixels are in consecutive addresses. So if character x position is a 5 bit number from 0 to 31, and character y position is 3 bit number from 0 to 7 (to be able to address all 256 character cells in the bottom 3rd of the screen), I calculated the character cell values like this: (x << 3) | y. Below is the assembly code: ; Fill the last third with Parsec style scrolling arrangement. ; First column 0, second 8, third 16. Thus the data written is as follows ; X = 5 bit column value, Y = 3 bit row value ; Char codes: X4 X3 X2 X1 X0 Y2 Y1 Y0 CLR R1 ! MOV R1,R2 SLA R2,3 ; Shift left by three, making bottom three bits of higher byte vacant MOV R1,R3 SRL R3,5 ; Shift right by 5, move top 3 bits to three lsbs of higher byte SOC R3,R2 ; set ones corresponding, i.e. OR MOVB R2,@VDPWD AI R1,>0100 JNE -! - I then went on to test Parsec style scrolling routines. My code was in cartridge ROM area, workspace in scratchpad memory. I set a side a Parsec style buffer of 128 bytes for 2 strips of 8 pixels wide times 64 pixels tall. Then, I read two columns into this buffer, so that bytes from first and second column alternate. With this arrangement the actual scrolling goes the same way as in Parsec (although in parsec source code the shift instruction on line 3050 of the PDF I found is SRC R1,0 which makes no sense to me at first glance). *--------------------------------------------- * Write a strip of 64 pixels while scrolling. * Parsec style. VDP write pointer must have been set. SCROLLSTRIP: mov @STRIPBU2(R5),r1 ; read first and second column bytes sla r1,1 ; shift left by 1 bit (i.e. pixel) movb r1,@>8c00 ; write to VRAM inct R5 ; inc R5 by 2 (i.e. skip the other column) jlt SCROLLSTRIP ; loop back RT This turned out to be quite slow for the entire 64 pixel high area, far away from instantenous. My first attempt was to use an algorithm which on every loop iteration reads two strips, and then writes and shifts one strip, repeated 32 times for every column. This is kind of how parsec works, except it only scrolls a small horizontal section at a time, and the height is 30 pixel rows. My scroll was not going fast. I then modified the SCROLLSTRIP routine above to perform byteswapping and storing the swapped bytes, that way I only needed to read one strip and write one strip, except on first iteration. This was a bit better but still slow. As the last test I limited my code to only scroll 16 scanlines, and moved the "strip buffer" to scratchpad memory (my workspace is at >8300 and the buffer, now just 32 bytes, is at >8320). It seems like scrolling 16 vertical lines of all 256 pixels takes about 10 seconds, or goes at about 25fps. I could copy the code to scratchpad memory too, but did not bother at this point. This was a pretty interesting exercise, as a lead in to what would happen if I used the faster processor on the cartridge to compute the scrolls, and have the TMS9900 just copy the data to VDP memory. Or something like that. Overall it was fun to do some assembly programming after a while. One question which came to mind was that how would one easily do cycle counting, does classic99 for example support it? I.e. reset cycle counter at a break point, and read the count at another breakpoint? 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted May 31, 2020 Share Posted May 31, 2020 Sweeeeet 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted May 31, 2020 Share Posted May 31, 2020 1 hour ago, speccery said: Overall it was fun to do some assembly programming after a while. One question which came to mind was that how would one easily do cycle counting, does classic99 for example support it? I.e. reset cycle counter at a break point, and read the count at another breakpoint? Just add a "breakpoint" like T(start-end), e.g. T(A000-A030) to count the cycles between >a000 and >a030. 3 Quote Link to comment Share on other sites More sharing options...
speccery Posted May 31, 2020 Author Share Posted May 31, 2020 25 minutes ago, Asmusr said: Just add a "breakpoint" like T(start-end), e.g. T(A000-A030) to count the cycles between >a000 and >a030. Nice, thanks for the tip! 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 31, 2020 Share Posted May 31, 2020 6 hours ago, speccery said: With this arrangement the actual scrolling goes the same way as in Parsec (although in parsec source code the shift instruction on line 3050 of the PDF I found is SRC R1,0 which makes no sense to me at first glance). You say “at first glance”, which could mean you figured it out, but, just in case you did not, “SRC R1,0” means to shift R1 by the number of bits found in the low-order 4 bits of R0. If they are 0, then 16 bits are shifted. ...lee Quote Link to comment Share on other sites More sharing options...
speccery Posted June 2, 2020 Author Share Posted June 2, 2020 13 hours ago, Lee Stewart said: You say “at first glance”, which could mean you figured it out, but, just in case you did not, “SRC R1,0” means to shift R1 by the number of bits found in the low-order 4 bits of R0. If they are 0, then 16 bits are shifted. ...lee Thanks Lee, good comment. A quick breakpoint in Parsec loads R0 with 6, so a shift right of 6. Which I suppose in practice means a shift of 2 pixels to the left, if the bytes are organised in memory the way I think. I did not pay too much attention to this instruction not matching my expectation, as I was more focused on getting the framework in place. I have now done that. Quote Link to comment Share on other sites More sharing options...
speccery Posted June 2, 2020 Author Share Posted June 2, 2020 On 5/31/2020 at 9:14 PM, speccery said: Nice, thanks for the tip! Thanks to the tip from @Asmusr I used classic99 to measure the speed of my code. With some simple changes I was able to get the time of a one bit left shift for 16 scanlines down from around 135K cycles to about 97K cycles. Only modifying the pattern tables, not touching color tables. There are in total 32*16=512 VDP RAM reads and and the same amount of writes. Come to think go it, my code actually probably reads one extra strip at the end. Anyway, in the big picture that's around 100 cycles per each VDP access and processing. I still find it mind boggling that instructions such as "MOVB *R7,@STRIPBUF" take 38 clock cycles (over 10 microseconds) when running from cartridge ROM space. In this example R7 points to a VDP register, workspace and STRIPBUF are in scratchpad. Of course there are 4 opcode bytes to fetch, and the store is a read-modify-write cycle, so there are quite a few memory cycles involved in the execution of that instruction. If I did my math properly there are 6 memory cycles in total, each 16-bits. Since two of these are to ROM memory and broken up with wait states to four byte wide fetches, the total is in a way 8 memory cycles. This actually makes me wonder what would be the slowest MOV instruction one could have? I suppose it would mean placing code and operands into 8-bit wide memory, and then perhaps accessing GROM to get the maximum penalty... Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 2, 2020 Share Posted June 2, 2020 (edited) 4 hours ago, speccery said: This actually makes me wonder what would be the slowest MOV instruction one could have? I suppose it would mean placing code and operands into 8-bit wide memory, and then perhaps accessing GROM to get the maximum penalty... The speech ROM is slower for reads, and the sound chip is slower for writes. (Although I can't remember the exact speed of speech ROM, I am pretty sure Classic99 doesn't emulate it.. but I had notes somewhere). But I'd expect a mov from speech to sound would do it. (Edit: oh, it might! I have it in my measured docs. Reading speech ROM takes 54 cycles, but write speech was 70! So I guess MOV from speech to speech, and you should be looking at about 150 cycles?) Edited June 2, 2020 by Tursi 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted June 2, 2020 Share Posted June 2, 2020 On 5/27/2020 at 2:53 PM, Lee Stewart said: Just in case anyone is interested, in a few minutes, I will edit the MG code snippet I posted above to resolve references. For fbForth 2.0, that code is copied from ROM to low expansion RAM at startup. ...lee Interested party here This is a really nice piece of code which I was not able to understand back when I started trying to make a DSRLNK. The fog has lifted a little. I may attempt to "borrow" another fine piece of FbForth in future because this version is small and has more functionality than what I currently use. I think I can see my way to translating it into Forth Assembler because my cross-compiler assembler has labelled jumps. Questions: 1. Does this code assume the presence of and/or use any system routines in low RAM? 2. Since you copy from ROM to RAM is it safe to say that I should be able to leave it in place under Camel99 Forth because I am loading the kernel into RAM? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted June 2, 2020 Share Posted June 2, 2020 2 hours ago, TheBF said: Interested party here This is a really nice piece of code which I was not able to understand back when I started trying to make a DSRLNK. The fog has lifted a little. I may attempt to "borrow" another fine piece of FbForth in future because this version is small and has more functionality than what I currently use. I think I can see my way to translating it into Forth Assembler because my cross-compiler assembler has labelled jumps. Questions: 1. Does this code assume the presence of and/or use any system routines in low RAM? 2. Since you copy from ROM to RAM is it safe to say that I should be able to leave it in place under Camel99 Forth because I am loading the kernel into RAM? The code I slightly modified is from MG’s The Smart Programmer, V2(2), July, 1986. Here is my transcription of it: Spoiler *--------------------------------------------------------------------* * GPLLNK- A universal GPLLNK - 6/21/85 - MG * * This routine will work with any GROM library slot since it is * * indexed off of R13 in the GPLWS. (It does require Mem Expansion) * * This GPLLNK does NOT require a module to be plugged into the * * GROM port so it will work with the Editor/Assembler, * * Mini Memory (with Mem Expansion), Extended Basic, the Myarc * * CALL LR("DSKx.xxx") or the CorComp Disk Manager Loaders. * * It saves and restores the current GROM Address in case you want * * to return back to GROM for Basic or Extended Basic CALL LINKs * * or to return to the loading module. * * * * ENTER: The same way as the E/A GPLLNK, i.e., BLWP @GPLLNK * * DATA >34 * * * * NOTES: Do Not REF GPLLNK when using this routine in your code. * * * * 70 Bytes - including the GPLLNK Workspace * *--------------------------------------------------------------------* GPLWS EQU >83E0 GPL workspace GR4 EQU GPLWS+8 GPL workspace R4 GR6 EQU GPLWS+12 GPL workspace R6 STKPNT EQU >8373 GPL Stack pointer LDGADD EQU >60 Load & ECUTE GROM address entry point XTAB27 EQU >200E Low Mem XML table location 27 GETSTK EQU >166C GPLLNK DATA GLNKWS R7 Set up BLWP Vectors DATA GLINK1 R8 RTNAD DATA XMLRTN R9 address where GPL XML returns to us GXMLAD DATA >176C R10 GROM Address for GPL XML (0F 27 Opcode) DATA >50 R11 Initialized to >50 where PUTSTK address resides GLNKWS EQU $->18 GPLLNK's workspace of which only BSS >08 R12-R15 registers R7 through R15 are used GLINK1 MOV *R11,@GR4 Put PUTSTK Address into R4 of GPL WS MOV *R14+,@GR6 Put GPL Routine Address in R6 of GPL WS MOV @XTAB27,R12 Save the value at >200E MOV R9,@XTAB27 Put XMLRTN Address into >200E LWPI GPLWS Load GPL WS BL *R4 Save current GROM Address on stack MOV @GXMLAD,@>8302(R4) Push GPL XML Add on stack for GPL ReTurn INCT @STKPNT Adjust the stack pointer B @LDGADD Execute our GPL Routine XMLRTN MOV @GETSTK,R4 Get GETSTK pointer BL *R4 Restore GROM address off the stack LWPI GLNKWS Load our WS MOV R12,@XTAB27 Restore >200E RTWP All Done - Return to Caller *--------------------------------------------------------------------* * DSRLNK - A Universal Device Service Routine Link - MG * * (Uses console GROM 0's DSRLNK routine) * * (Do not REF DSRLNK or GPLLNK when using these routines) * * (This DSRLNK will also handle Subprograms and CS1, CS2) * * * * ENTER: The same way as the E/A DSRLNK, i.e., BLWP @DSRLNK * * DATA 8 * * * * NOTES: Must be used with a GPLLNK routine * * Returns ERRORs the same as the E/A DSRLNK * * EQ bit set on return if error * * ERROR CODE in caller's MSB of Register 0 on return * * * * 186 Bytes total - including GPLLNK, DSRLNK and both Workspaces * *--------------------------------------------------------------------* PUTSTK EQU >50 Push GROM Add to stack pointer TYPE EQU >836D DSRLNK Type byte for GPL DSRLNK NAMLEN EQU >8356 Device name length pointer in VDP PAB VWA EQU >8C02 VDP Write Address location VRD EQU >8800 VDP Read Data byte location GR4LB EQU >83E9 GPL Workspace R4 Lower byte GSTAT EQU >837C GPL Status byte location DSRLNK DATA DSRWS,DLINK1 Set BLWP Vectors DSRWS EQU $ Start of DSRLNK workspace DR3LB EQU $+7 R3 lower byte of DSRLNK workspace DLINK1 MOV R12,R12 R0 Have we already looked up the LINK address? JNE DLINK3 R1 YES! Skip lookup routine *<<-------------------------------------------------------------------------->>* * This section of code is only executed once to find the GROM address * * for the GPL DSRNK - which is placed at DSRADD and R12 is set to >2000 * * to indicate that the address is found and to be used as a mask for EQ & CND * *------------------------------------------------------------------------------* LWPI GPLWS R2,R3 else load GPL workspace MOV @PUTSTK,R4 R4,R5 Store current GROM address on the stack BL *R4 R6 LI R4,>11 R7,R8 Load R4 with address of LINK routine vector MOVB R4,@>402(R13) R9,R10 Set up GROM with address for vector ***les*** ***les*** Note on above instruction: ***les*** 1. R13 of GPLWS has 9800h=GRMRD (GROM Read Data) ***les*** 2. 402h added to GRMRD yields 9C02h=GRMWA (GROM Write Address) ***les*** JMP DLINK2 R11 Jump around R12-R15 DATA 0 R12 contains >2000 flag when set DATA 0,0,0 R13-R15 contains WS, PC & ST for RTWP DLINK2 MOVB @GR4LB,@>402(R13) Finish setting up GROM address MOV @GETSTK,R5 Take some time & set up GETSTK pointer MOVB *R13,@DSRAD1 Get the GPL DSR LINK vector INCT @DSRADD Adjust it to get past GPL FETCH instruction BL *R5 Restore the GROM address off the stack LWPI DSRWS Reload DSRLNK workspace LI R12,>2000 Set flag to signify DSRLNK address is set *<<-------------------------------------------------------------------------->>* DLINK3 INC R14 Adjust R14 to point to caller's DSR Type byte MOVB *R14+,@TYPE Move it into >836D for GPL DSRLNK MOV @NAMLEN,R3 Save VDP address of Name Length AI R3,-8 Adjust it to point to PAB Flag byte BLWP @GPLLNK Execute DSR LINK DSRADD BYTE >03 High byte of GPL DSRLNK address DSRAD1 BYTE >00 Lower byte of GPL DSRLNK address *------Error Check & Report to Caller's R0 and EQU bit------------------------- MOVB @DR3LB,@VWA Set up LSB of VDP Add for Error Flag MOVB R3,@VWA Set up MSB of VDP Add for Error Flag SZCB R12,R15 Clear EQ bit for Error Report MOVB @VRD,R3 Get PAB Error Flag SRL R3,5 Adjust it to 0-7 error code MOVB R3,*R13 Put it into Caller's R0 (msb) JNE SETEQ If it's not zero, set EQ bit COC @GSTAT,R12 Else, test CND bit for Link Error (00) JNE DSREND No Error; Just return SETEQ SOCB R12,R15 Error, so set Caller's EQ bit DSREND RTWP All Done - Return to Caller Re question (1), the code does not assume the presence of and/or use any system routines in low RAM. However, you cannot use the address, >200E (XTAB27), for anything else. Craig Miller found the only occurrence in console GROM 0 of code that could be used as the GPL XML instruction (>0F27) that would execute a return to RAM from GPL. >0F is the machine code for the GPL XML instruction and >27 decodes to address >200E, which will hold the RAM return address (XMLRTN) to the GSRLNK/DSRLNK code in RAM. Re question (2), except for location >200E mentioned above, your Camel99 Forth RAM code can safely be left wherever your Assembler puts it in RAM. ...lee 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted June 2, 2020 Share Posted June 2, 2020 26 minutes ago, Lee Stewart said: Craig Miller found the only occurrence in console GROM 0 of code that could be used as the GPL XML instruction (>0F27) that would execute a return to RAM from GPL. >0F is the machine code for the GPL XML instruction and >27 decodes to address >200E, which will hold the RAM return address (XMLRTN) to the GSRLNK/DSRLNK code in RAM. Wow that is some good cyber-sleuthing. For some reason I am thinking of a guy named Job ( not Jobs ) I will have to move my Heap pointer forward a little but it is workable. Thanks for the great info as always. Quote Link to comment Share on other sites More sharing options...
RXB Posted June 2, 2020 Share Posted June 2, 2020 On 5/27/2020 at 8:21 AM, Asmusr said: The thing with GPL DSRs is that they only work from BASIC because assembly programs generally don't look for them. INCORRECT! TI Basic, XB, Basic Support Module, TEII, Editor Assembler, TI Writer and Mini Memory plus many others all support GPL DSR. Quote Link to comment Share on other sites More sharing options...
speccery Posted June 2, 2020 Author Share Posted June 2, 2020 5 hours ago, Tursi said: The speech ROM is slower for reads, and the sound chip is slower for writes. (Although I can't remember the exact speed of speech ROM, I am pretty sure Classic99 doesn't emulate it.. but I had notes somewhere). But I'd expect a mov from speech to sound would do it. (Edit: oh, it might! I have it in my measured docs. Reading speech ROM takes 54 cycles, but write speech was 70! So I guess MOV from speech to speech, and you should be looking at about 150 cycles?) Wow that is a lot! The best I could do (on a very quick attempt, and I don't have the speech module here in my temporary apartment) was 77 cycles, with MOV @>9800(R1),@>A000(R1). Measured with your fantastic classic99. BTW: is there a way to save / restore breakpoints? I think you already answered to me in some message before, but can I reload a cartridge image (during development) without exiting classic? Quote Link to comment Share on other sites More sharing options...
Asmusr Posted June 2, 2020 Share Posted June 2, 2020 9 hours ago, speccery said: Thanks to the tip from @Asmusr I used classic99 to measure the speed of my code. With some simple changes I was able to get the time of a one bit left shift for 16 scanlines down from around 135K cycles to about 97K cycles. Only modifying the pattern tables, not touching color tables. There are in total 32*16=512 VDP RAM reads and and the same amount of writes. Come to think go it, my code actually probably reads one extra strip at the end. Anyway, in the big picture that's around 100 cycles per each VDP access and processing. If you want to speed things up you should look into how to avoid any VDP reads and how to avoid settings up the VDP write address more than once. 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.