Jump to content
IGNORED

Adventure Cartridge 80 column development and GPL


Recommended Posts

I added GPL code to set VR02 to >03. One thing I have not considered is the F18A 80 column mode as that might require being cautious with the register order. I forget whether or not the F18a must be unlocked to enable 80 column mode; if not, then VR8-15 order becomes more important.

 

You don't have to unlock the F18A to use the 80 columns mode.

  • Like 2
Link to comment
Share on other sites

Writing to VR8..VR14 will not bother the F18A while it is locked, and when unlocked only VR10 and VR11 are used; and then only if Tile-Layer 2 is enabled. Even VR15 might be ignored while the F18A is locked, but I need to check the HDL. But writing 0 to VR15 is always a safe value.

  • Like 1
Link to comment
Share on other sites

Writing to VR8..VR14 will not bother the F18A while it is locked, and when unlocked only VR10 and VR11 are used; and then only if Tile-Layer 2 is enabled. Even VR15 might be ignored while the F18A is locked, but I need to check the HDL. But writing 0 to VR15 is always a safe value.

Thanks Matthew. I took a quick look at TIMXT's routines today and came to the realization that my F18A/V9938 detection routine is flawed. When used with a V9938 system, TIMXT sets VR#2 to the unlocked value, which results in a weird split screen effect similar to what Michael posted a few days ago in this thread. Now it all makes sense (again).

 

I learned how to set each VR value individually using GPL, so my next challenge is to figure out how to loop through the registers. GPL is sort of like a cross between BASIC and Assembly; once you learn syntax its not all that bad either :)

  • Like 2
Link to comment
Share on other sites

One obviously safe detection method is to query status register 4, at least to tell apart TMS9918A and v9938. SR4 returns one of two values, 0xFE or 0xFF. Since the 9918A only has one status register, it will return its value, and it is highly unlikely that you get FE or FF from it, and impossible if you have less than 5 sprites on the screen. You need to check with the F18A, of course.

 

To read SR4, you have to set video register 15 to 4 and then read via the status register port. Remember to reset VR15 to 0 afterwards.

  • Like 1
Link to comment
Share on other sites

One obviously safe detection method is to query status register 4, at least to tell apart TMS9918A and v9938. SR4 returns one of two values, 0xFE or 0xFF. Since the 9918A only has one status register, it will return its value, and it is highly unlikely that you get FE or FF from it, and impossible if you have less than 5 sprites on the screen. You need to check with the F18A, of course.

 

To read SR4, you have to set video register 15 to 4 and then read via the status register port. Remember to reset VR15 to 0 afterwards.

 

I don't recall ever seeing (or trying) this detection method. Seems nice and simple. Typically, I have used VR14 to flip between two different VRAM banks, writing values then comparing the two banks. In TIMXT I am trying to detect the F18A, which used to work, so maybe I goofed up elsewhere. More research required.

 

fortunately, there is little need to detect the 80 column support type in the Adventure cartridge. I had considered adding some color to the status area but unlike Infocom, the status window is dynamic. And this is Adventure after all... :)

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

I reviewed the TI Writer GPL source and found the lines that should perform a CALL FILES(1) using the GPL subroutine >16. I understand the CALL and the moves in memory. I don't quite understand how >8381 plays into the call. I'm thinking that for my purposes, all I need to do is call the routine and let the chips fall where they may. If there is an error, I don't care, and it wouldn't really make any difference.

 

Anyone have insight into 8381? The next question is how do I turn this into GPL code? I also included what looks like an efficient way to set all the VDP registers.

 

6342 CALL G@>6663 |call files routine
BYTE >01 | (1 file)
BS G@>6686 error (maybe for Adventure we don't care if this works or not)

6663 FETC @>834C # of files (from CALL)
; ST >01,@>834C (can use this instead of byte after CALL)
DST >0116,V@>077E "pab" for these two bytes
DST >077E,@>8356 pointer for subroutine call
ST >EE,@>8381 not sure why we set this? what is 8381?
CALL G@>10 |link sub [16]
BYTE >0A |

BS G@>6682 error
ST @>8350,@>8381
CZ @>8381
BR G@>6682 status error
RTN
6682 CEQ @>8300,@>8300 return with error
RTNC

; VDP registers set all at once (versus one register at a time)

60EA MOVE >07,G@>68BB,#>01 set VDP regs 1-7
68BB BYTE >20,>00,>0E,>01 VDP registers 1-7
BYTE >01,>06,>00,>F5

Link to comment
Share on other sites

I reviewed the TI Writer GPL source and found the lines that should perform a CALL FILES(1) using the GPL subroutine >16. I understand the CALL and the moves in memory. I don't quite understand how >8381 plays into the call. I'm thinking that for my purposes, all I need to do is call the routine and let the chips fall where they may. If there is an error, I don't care, and it wouldn't really make any difference.

 

Anyone have insight into 8381? The next question is how do I turn this into GPL code? I also included what looks like an efficient way to set all the VDP registers.

 

>8381 is the second byte of the default subroutine stack space, >8380 – >839F, with LSB of stack pointer at >8373 (followed by the default data stack space, >83A0 – >83BF, with LSB of stack pointer at >8372).

 

Re >8381, it could be that TI Writer uses the first stack position as free space and views the bottom of the stack as >8382 It certainly looks as though the second use of >8381 is viewed as free space. I cannot figure out the storage of >EE at >8381, either.

 

...lee

  • Like 1
Link to comment
Share on other sites

TI Intern page 80:

"GPL uses essentially the area of the CPU-RAM's >8372 through

>83FF. The work space for the GPL interpreter is located at 8370.
The pointer for the GPL data stack is located at >8370 and the
pointer subroutine stack is located at 8372. The complete address
for the stack consists of the pointer plus >8300. Usually the ROM
area >8380 through >83BF is used for the stacks."
>8380 is Register 0 of the GPL Stack space.and >8381 is the LSB of Register 0
The OS ROM will scan update R0 and mask the byte at >8381 and when it returns to GPL
CZ @>8381 looks for zero and if not jumps to >6682 CEQ @>8300,@>8300
this resets the GPL STATUS >837C
RTNC is GPL code to return with no change to >837C to the caller.
Now if >8381 is zero it just returns normally back to caller and >837C is reset.
Edited by RXB
Link to comment
Share on other sites

Thanks Rich and Lee. I think it is safe not to worry about the >EE stored in >8381. My next challenge is how to get the code to assemble. The TI Writer GPL code syntax seems to be different than the disassembly of the Adventure cartridge. Is there a GPL toolkit available? I have some things RXB sent me but I'm still confused on how to turn this into a properly assembled image file.

  • Like 1
Link to comment
Share on other sites

  • 9 months later...

Was nibbling at this code again a few nights ago. Does someone know the GPL bytes necessary to branch and execute to a memory location equivalent to "B @>A000"?

 

Secondly, if there is such an instruction, how would I then return execution to GPL at a specific location?

 

I want to jump out of the GPL interpreter to run some assembly code then return to GPL. The return gpl address may or may not be the one directly after the branch to my assembly routine.

Link to comment
Share on other sites

I have made GPL to ASSY branch, for a power-up cartridge header.

I,d use a GPL XML op-code "0F" followed by table 12 "b" procedure 0 "0".

So the answer is "0fb0".

I don't know if I ever did a return. I think maybe it's on the return stack?

GPL Stacks...

 

GPL uses two stacks, located in the scratch-pad memory: the subroutine stack and the data stack.
Subroutine stack

The first one serves to store the return address of procedures called with CALL. The address will be retrieved from the stack by a RTN or RTNC instruction and used to return to the calling point. SWGR and RTGR also make use of this stack but store the GROM base in addition to the return address.

The stack pointer is found in byte >8373. It points to the current return address, i.e. CALL must increment it by two before to save a new return address, whereas RTN reads the address directly and decrement >8373 by two afterwards.

The subroutine stack normally grows upwards from >8380, but this is not an absolute requirement: theoretically it could be located anywhere in the scratch-pad. Note that the GPL interpreter does not check for stack overflow, nor for empty stack: a long serie of nested CALL may therefore crash the interpreter...

Data stack

This stack is meant for use by the programmer. Its pointer is to be found in byte >8372 and the user can push a byte on it with the GPL opcode PUSH. Here also, >8372 points at the current byte, i.e. the pointer is incremented (by one) before saving the byte. For some strange reason, the symetrical POP instruction does not exist (although my assembler simulates it). You'll have to retrieve the byte "manually" with ST *>8372,dest and then decrement the pointer with DEC @>8372.

This stack is normally located at >83A0 and grows upwards with no check for overflow. Be carefull with that one: bytes >83C4-C5 contain the interrupt routine hook. Placing a non-zero value in there will cause the interrupt service routine to branch at the corresponding address in CPU memory (now there's an idea...). Again, it is possible to relocate this stack by just changing the value of the pointer in >8372, but it has to be in the scratch-pad since the pointer is a byte.

 

 

from:

 

http://www.unige.ch/medecine/nouspikel/ti99/gpl2.htm#XML

 

http://www.unige.ch/medecine/nouspikel/ti99/gpl.htm

Link to comment
Share on other sites

  • 2 years later...

Gnawed on this old bone this weekend:

1) Implemented call to GPL DSRLNK to set CALL FILES(1); the primary purpose for this was so that I could move the PAB out of the way of the screen/character set/game that reside in memory.  A secondary, unintended effect is that the maximum game size is now 0x3700 (14,080) bytes, though I don't know that we have anything larger than the Adventure Editor allows.  This same limit is now "imposed" on cassette-based games.  

2) The Save and Restore game option changes are working now, though there is a visible screen twitch when Adventure flips the screen modes for the Cassette operation. 

3) Tweaked V9938/F18A video register settings for compatibility; a little more testing is needed here and for item #2

4) I now have a much better appreciation for GPL; the syntax of the program code neatly follows the more verbose pre-assembled code. I even found it 'easy' to recognize and write some of the code by hand after a while. 

 

image.png.94a8af4d8d436d5aafb36adee3b0ce86.png   image.png.9a6168d7604dd30c90a818c9a053bc45.png

  • Like 7
Link to comment
Share on other sites

After much head scratching, the Adventure 80 column cartridge is now also working on the Geneve.

 

For the last few days, no matter what I tried the cartridge was 'stuck' in 40 column mode. I thought it was my code or the 9938, ultimately the root cause was found in the Geneve's GPL interpreter.  

 

80 column mode requires VR0 set to >04.  To rule out a problem with the interpreter's routine for VWTR (that lives in the TI ROM at >0698) I disabled the video writes in this routine and in the keyboard SCAN routine.  When I ran Adventure, the screen mode still changed; this was 'impossible'!  It led me to realize that the interpreter is being patched at run-time to inhibit video register writes that are out of range and/or incompatible with the 9938.  

 

In the commented patch below, Video Register 0 has a mask of >FC and the SZCB resets those Corresponding bits to zero. This in turn means that VR0 was masked from >04 to >00, leaving us stranded in 40 column mode.  


PAT#3  SZCB @MASK(R2),R11   ;mask the video register found in MSByte R11
       MOVB R11,*R15    ;write the register data to VDP
       ORI  R2,>80    
       MOVB @>83E5,*R15  ;write register number/mode to VDP

..code continues
MASK  BYTE >FC,>84,>F0,>00   ;register masks (0-3)
HF8    BYTE >F8,>80,>F8,>00  ;masks 4-7

 

By modifying the VR0 mask from >FC to >F8, the needed bit is now passed along to the 9938.  I don't know how many cartridges errantly set VR0, so I might create a one-off patch for the Geneve. 

 

With a little luck I'll post the updated cart later this month.  

  • Like 6
Link to comment
Share on other sites

4 hours ago, dhe said:

Great job pilot! I've been waiting years to see someone writing about GPL and 80-column mode.

I certainly never thought I'd be poking around GPL. ;) There is no defined mask for registers greater than 7, so if there was a GPL program that needed to set blink mode, for example, a similar problem would occur .  I also discovered that some of the typical environment setup, such as the color table for graphics mode 1, sits right in the middle of the 80 column screen. I'll have to move that around a bit as well.  With exception of a few cosmetic items, I'm about done with the changes.

  • Like 3
  • Thanks 2
Link to comment
Share on other sites

  • 4 months later...

There were a few posts in the main forum related to Adventure 80 column mode.  This work took a back seat to family-related items late last year, and then I forgot to follow up on the effort.  I made printouts of the updates and a hex-based comparison of the code though I don't have a paper trail to tell me why I went down that rabbit hole!  It might have been related to the cassette routines?   If I have free time this weekend, I'll try to sort it out because I thought I was done....

  • Like 3
Link to comment
Share on other sites

I reviewed my notes tonight.  It seems that I had introduced a bug that incorrectly moves the cursor to position (1,1) under certain conditions, and it was most apparent after the restore game command.  

 

I also discovered some bad news & good news.  Around the same time, I installed a more recent version of Classic99 into a different location, did not realize I had done so, then later deleted the older version that contained the DSK3 folder where my work resided.  It was a miserable shock when I realized what I had done, I hope there wasn't anything important in the other DSK folders.  Fortunately and a little surprisingly, I had made a backup of the Adventure work files on Oct 10, so the work survived. 

 

For anyone curious, here is a sample of the Hex Comparison report I have been using to review changes.  The leftmost column is the updated cartridge; the next column is the original cartridge.

image.thumb.png.98c999ac8533d9cdd7f1778008fb81de.png

  • Like 2
Link to comment
Share on other sites

When I setup a project under classic99, I always create a directory called mydisk on the same level as dsk1, dsk2 .. and under mydisks\dsk1 dsk2 ...

 

That creates a high level of confidence for me nothing bad can happen with and update. I have a master directory called classic99_vms with machines like c99, funnelweb, fortran and the whole classic99_vms directory gets backed up once a week, and since the code is so small, I have a 64gb usb drive, with 44 weekly copies of everything.

  • Like 3
Link to comment
Share on other sites

OK... here's an operational, nearly finished 80 column Adventure cartridge for those interested in trying it out :)

 

adventure2022Mar12 testG.bin - binary Grom file, for classic99, js99er, etc

 

ADVGROM80 - TIFILES format, Gram cartridge format

 

There are a few things I want to update at a later date:  fix an undesired scroll that periodically obscures a line of the game's intro screen, track down and fix a cursor home position reset after a QUIT-RESTART-QUIT sequence, and make a cosmetic change to separate the items above the divider line with a blank line.  

 

The cartridge .bin file works for me in both Classic99 and js99er, though the gpl-based CALL FILES(1) subroutine doesn't seem to execute in js99er @Asmusr (?) like it does in Classic99.  This might impact the largest games and/or the save game option, if js99er is sensitive to vram in the disk buffer area.   I didn't notice any ill effects during gameplay.  fixed within js99er

 

The cart has not been tested on any real hardware besides my Geneve.   And there are no guarantees concerning cassette routines nor am I inclined to troubleshoot them if broken.

 

Lastly, there is no TIFILES GROM file provided for the Geneve (yet) because I haven't decided how to fix the GPL video register mask dilemma.  see later post for GROM file usable with HSGPL, Geneve;  see Geneve OS Development thread for PLAYE utility

 

  • Like 3
Link to comment
Share on other sites

1 hour ago, InsaneMultitasker said:

The cartridge .bin file works for me in both Classic99 and js99er, though the gpl-based CALL FILES(1) subroutine doesn't seem to execute in js99er @Asmusr (?) like it does in Classic99.

That's strange. Even though it goes through GPL it should end up calling either DSR subprogram >16 or FILES, right?

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...