Jump to content
IGNORED

Porting paged TI games to the Geneve


mizapf

Recommended Posts

So here is a dedicated thread for discussions about porting games like those from Rasmus to the Geneve.

 

Things to consider:

  • Paging is done by writing to a memory address; this won't work with the Geneve mapper. Instead, these operations need to be replaced (possibly in the binary code). Maybe XOP? (not 0, which is used for the system calls) Or an MID-based approach?
  • Keyboard control: The Geneve keyboard is very different to the TI keyboard, but if needed, keyboard access can be written on a low level as well. See https://www.ninerpedia.org/wiki/Geneve_keyboard_control
  • As long as standard VDP (9918/9929) operations are used, there are only few (if any) problems on the 9938. F18A features are out of scope.

 

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

44 minutes ago, mizapf said:

So here is a dedicated thread for discussions about porting games like those from Rasmus to the Geneve.

 

Things to consider:

  • Paging is done by writing to a memory address; this won't work with the Geneve mapper. Instead, these operations need to be replaced (possibly in the binary code). Maybe XOP? (not 0, which is used for the system calls) Or an MID-based approach?

 

The Geneve memory can be paged by writing to a memory address.  One writes a byte with the range of >00 to >FF to any of the 8 memory addresses below to swap in a new 8K page:

 

Mapper Address     Page Bank

>8000                  >0000 to >1FFF

>8001                  >2000 to >3FFF

>8002                  >4000 to >5FFF

>8003                  >6000 to >7FFF

>8004                  >8000 to >9FFF

>8005                  >A000 to >BFFF

>8006                  >C000 to >DFFF

>8007                  >E000 to >FFFF

 

An instruction as simple as:

 

MOVB   @MEM_LIST+4,@>8007 would change the memory page on >E000 to >FFFF. 

 

Now, if you are in MDOS mode, you use the XOP to request a free page of memory, then either retrieve a buffered list of memory pages available, or use another XOP call to map the page into memory.  

 

What I was proposing was to use a modified EXEC as the launch program to load the .bin file into range of free memory pages since the PINBALL game is a 256K file.  Then, somewhere within memory, those pages where the .bin was loaded into, would be accessible or passed to the .bin program with the Geneve mapping code being used rather than the FinalGrom mapping code. Memory pages would then be indexed across those 32 x 8K pages. If there was an address to toggle the Geneve mapping code into use rather than using the FinalGrom mapping code, that would be the simplest.

 

57 minutes ago, mizapf said:

Fully agree.  KSCAN code should be easy to resolve.

57 minutes ago, mizapf said:
  • As long as standard VDP (9918/9929) operations are used, there are only few (if any) problems on the 9938. F18A features are out of scope.

Fully agree here as well. If the code works on a TI-99/4A running a 9938 chip, then there should be no issue at all.  I seriously doubt there would be an issue with the 9918 either.  And the F18A, no way.

 

I looked through Rasmus's PINBALL code and it was not immediately obvious to me where the mapping was occurring.  

 

Beery

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, 9640News said:

I looked through Rasmus's PINBALL code and it was not immediately obvious to me where the mapping was occurring. 

 

These SETO/MOV operations cause a bank switch. Obviously, parts of the code were copied to RAM.

 

2CFC: MOV  *R0+,R2
2CFE: SETO *R2
2D00: MOV  *R0,R0

 

2D5C: AI   R1,>6008
2D60: SETO *R1
2D62: ANDI R2,>001F

 

3634: AI   R0,>6000

3638: SETO @>6034
363C: MOV  *R0+,@>83EE

 

367C: B  *R11

367E: SETO @>6034
3682: MOV @>83EE,R1

 

833A: MOV @>0006(R12),R1

833E: MOV *R3,*R3      (bank switch)

8340: MOVB *R0+,*R1+

 

8354: JMP >833E

8356: SETO @>6000

835A: B  *R11

 

6038: SETO @>6000

603C: LIMI >0000

Link to comment
Share on other sites

Unfortunately, /4A mode does not allow you to map pages into the >6000 space besides those that are locked into place by the gate array.  You can perform 4096 MOV instructions to copy a page into the >6000 space during each trampoline operation; this works ok unless the program is doing a lot of bank switching. 

 

A reasonable, workable approach is to assemble the game/program with the following changes:

 

1) Use the /9640 mode VDP addresses

2) Use the /9640 mode sound address

3) Replace the trampoline code with an equivalent mapper routine and

4) Use the Geneve Keyscan routine or equivalent; keys may be buffered, which can lead to issues with repeat characters.

5) Check video registers for 9938 compatibility

 

I worked with @PeteE a few years ago to create a wrapper proof-of-concept for one of his games using this method.  The wrapper loaded the full ROM image into RAM and used the modified trampoline to index the pages.   Perhaps this idea could be expanded and a suitable simple assembly toolkit created to simplify porting for this style of ROM image. 

 

  • Like 2
Link to comment
Share on other sites

Petee gave me the green light to share info from my ROM wrapper that was written for Tilda.

 

The original wrapper performed a brute-force address search & replace at run-time and made use of /4A mode.  The trampoline code copied each bank into >6000 as suggested above.  This made for very slow gameplay but it did work.  Ultimately, I felt /4A mode was too complicated.

 

We successfully tested a /9640 mode brute-force change to the video/sound/mapper addresses in the ROM image.  Later, I identified the changes needed for Pete to assemble his code base with the /9640 mode addresses and a few support routines.  The attached source should be the latest version of the wrapper that ran Tilda on real hardware.

 

tilda wrapper 2019 source.txt

 

Feel free to use, abuse, and augment.

 

Edit:  here is an early version with key/joystick and other info.  I'm not sure where my notes are for what Petee integrated into his code directly but this has most of the necessary elements.

 

tilda wrapper original TI mode 2019

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

18 hours ago, InsaneMultitasker said:

You can perform 4096 MOV instructions to copy a page into the >6000 space during each trampoline operation; this works ok unless the program is doing a lot of bank switching. 

Pinball 99 is bank switching at least two times per frame (at 60 FPS), so that would never work, but it doesn't have to be the >6000 region - the >0000 region or the >4000 region should work fine.

  • Like 3
Link to comment
Share on other sites

20 minutes ago, Asmusr said:

Pinball 99 is bank switching at least two times per frame (at 60 FPS), so that would never work, but it doesn't have to be the >6000 region - the >0000 region or the >4000 region should work fine.

Executing that cart from the 4000 space instead of the 6000 space is another good approach, and is one that could leverage the /4A mode relatively easily so long as the trampoline and keyboard scan (if raw) were modified and no DSR access was required.  The wrapper was originally written to test whether I could take any ROM image and patch it on the fly without knowing much about the program.  What's fun about these challenges is there are often many ways to do the same thing within the limitations of both systems.

  • Like 3
Link to comment
Share on other sites

I’ve successfully made a 32K cartridge by prototyping on the 9640.  I used GENPROG. There were minimal differences between the binary for a 4A cart and a 9640 image for GPL. 

 

First, the base address: the GENMAKE file creates two images, one for >A000 , one for >6000. 
 

There were trampolines common to all banks. I don’t recall how I chose the 9640 page numbers. The trampoline code was 3 words in either machine, so I could feel confident in the test. 

 

The 4A worked like the Guidry cart where address bits are latched. 

 

* 4A

PAGE0 BYTE 0

PAGE1 BYTE 0  *   Don’t care 

BANK0 EQU >6000

BANK1 EQU >6002

MOVB @PAGE1,@BANK1

 

* 9640

PAGE0 BYTE >9A or whichever

PAGE1 BYTE >9B 

BANK0 EQU >8005  * page goes at >A000

BANK1 EQU BANK0

MOVB @PAGE1,@BANK1 * 9640

 

 

  • Like 4
Link to comment
Share on other sites

  • 1 month later...
On 5/28/2022 at 10:53 AM, Asmusr said:

Pinball 99 is bank switching at least two times per frame (at 60 FPS), so that would never work, but it doesn't have to be the >6000 region - the >0000 region or the >4000 region should work fine.

I used the >4000 region to emulate the bank switching for the Geneve version of your Pinball 99 game.  This works well because the Geneve's >6000 space is locked to two 8K pages by design (or by design error) when operating in /4A mode. 

 

 

On 5/27/2022 at 2:35 PM, 9640News said:

What I was proposing was to use a modified EXEC as the launch program to load the .bin file into range of free memory pages since the PINBALL game is a 256K file.  Then, somewhere within memory, those pages where the .bin was loaded into, would be accessible or passed to the .bin program with the Geneve mapping code being used rather than the FinalGrom mapping code.

This is basically the approach that I used for the Pinball conversion, though a few enhancements are needed to minimize manual build intervention.  Simple was easiest for me but I'd prefer to make the loader "aware" of the ROM header so that execution is contained within the image, and so that more than Pinball 99 can be ported.  I had intended to use the Tilda approach to the wrapper then wondered what happens when the programmer uses the interrupt routine or doesn't roll their own keyboard/joystick scan. (There is also merit to using GPL though its overhead is too high for a stock system to emulate a 256K rom).

 

 All things considered, here is what I've done so far, at a high level: 

 

1) Modify EXEC (a TI program environment and loader)

    - Request 256K of ram for the cartridge

    - Check for /4A "TIMODE", if disabled, exit. (I removed this limitation to allow the stock Geneve to run the game)

    - Store the page map starting at cart ram >7000 (we will not use the cart space pages so this is ok)

    - Load the 256K cartridge image into the requested pages starting at virtual page address >10000.  

    - Load the program, in this case, the PINBALL program files for the >2000 and >A000->FFFF space.

    - Adjust the EXEC keyscan routine. The blanking code was writing to PAD ram, in turn destroying Pinball variables.

 

2) Update PINBALL 99 code:

    - Remove cleanup/setup code for F18A 

    - Locate and replace bank switch code

    - Replace keyboard scan routine with Geneve keyboard scan, using Pinball 99 CRU table to match the key indirectly.  I spent a long time on this yesterday before realizing that the EXEC key scan changed PAD ram in multiple places.  I also mistakenly masked out the space bar by applying an "ANDI R1,>DF00" to mask out lower case.  So much for cleverness.  Also, I had forgotten that values in the range of >8370->8377 are used for the TI keyboard/joystick values, >837C is changed to reflect GPL status, and somewhere along the way EXEC restores the DSR page >07 to the mapper which blows away our ROM page, so I had to save/restore the mapper.

 

3) Example of Geneve banking code: 

The following Pinball 99 routine banks a requested collision map into the >6000 space.  SETO *R1 maps the bank and there is an offset applied to the result a few lines later.

 

We use the computed cartridge bank address [6000, 6002, 6004...] in R1 to generate an 8-bit Geneve mapper offset.  To do this, we mask the high order byte, divide the remaining byte by two to obtain an index into our Geneve page map, and add offset 8 to point to the first virtual page. We then load the mapper (>8002) with the page number.  The correct ROM bank is now presented at >4000. 

get_coll_value:
       mov  r1,r2                      ; Copy y
       andi r1,>01e0
       srl  r1,4                       ; Bank
       ai   r1,coll_map_bank           ; Add base bank offset  (>6008)

;;       seto *r1                        ; Select bank

;----------
       mov r1,@savettr1    ;7.2.2022
       andi r1,>00FF       ;mask it
       srl  r1,1           ;divide by 2
       ai   r1,>7000+8     ;location of our page map
       movb *r1,@>8002     ;set the mapper to our page
       mov  @savettr1,r1

;---------------

 

It is also necessary to correct the bank base from >6000 to >4000.  Instead of applying an offset correction, I should probably change the EQUate "coll_bank_base" directly.  

 

       andi r2,>001f                   ; Line
       swpb r2                         ; Line offset
       a    r0,r2                      ; Add X
       ai   r2,coll_bank_base          ; Add ROM bank base address (>6000)
       ai   r2,->2000    ;fix offset for >4000  7.2.22

       clr  r1                         ; Clear result reg
       movb *r2,r1                     ; Get byte
       swpb r1
       rt
*// get_coll_value

 

 

  • Like 2
Link to comment
Share on other sites

Yesterday, I noticed that if I ran Pinball 99 multiple times, every other attempt resulted in incorrect collisions.  Turns out that the memory routine was grabbing page >36 and using it for the 8K of ram at address >2000-3FFF.  This only happened every other time, so it took me a bit to recognize the 'pattern'. TIMODE2 effectively blocks page >36 from the memory allocation request and resolves the issue. 

 

I believe there is a good case for adding a third TIMODE option that would block out page >36 and the three FAST ram pages for optimal ROM cart emulation.   Since we are using the >4000 space for the banking, file access is inhibited.   If disk access is desired, the programmer or porter could bank in the DSR at the start of the DSRLNK routine, which would require 2-3 instructions.  

 

Is anyone else consider porting a game to the Geneve?

Link to comment
Share on other sites

I've attached a functioning loader with the v1.0 PINBALL 99 ROM and modified game program files.  

 

The joystick and keyboard are enabled and function the same as the TI version.  To exit the game, press ESCAPE.  I am able to run this on my stock Geneve with the extra 32K SRAM installed.   Speech has not been tested.

 

If you download the loader and play PINBALL 99, please consider leaving feedback as to whether the loader/simulation works on your system.  In the meantime, I'll work on a few remaining enhancements/ideas and figure out how to publish the changes I made to Rasmus' code.

 

PINBALL99-Geneve v91.zip

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

Tim,

 

I just played the game.  Thanks so much for converting the program to run on the Geneve.  RASMUS did a great game.  I had to pull the Rave Speech card as it did not like it in my system. I am not sure I would say the program was locked up, but there were random, rather quiet noises coming from the speaker.  This is with PFM Geneve and Myarc 512K combo that gives 172 total 8K pages.

 

Beery

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, 9640News said:

I had to pull the Rave Speech card as it did not like it in my system. I am not sure I would say the program was locked up, but there were random, rather quiet noises coming from the speaker.  This is with PFM Geneve and Myarc 512K combo that gives 172 total 8K pages.

I appreciate the feedback.  I will review the banking code this evening to confirm the speech data is being presented properly.  This may be a timing issue with how fast (or slow) the speech data is being sent to the synthesizer.  Are there any other speech-related programs that you can try running with EXEC so that we know if the issue is with the loader versus the game?  If the latter, I'll need help from those of you that have experience with speech synth coding. 

  • Like 2
Link to comment
Share on other sites

2 hours ago, InsaneMultitasker said:

I appreciate the feedback.  I will review the banking code this evening to confirm the speech data is being presented properly.  This may be a timing issue with how fast (or slow) the speech data is being sent to the synthesizer.  Are there any other speech-related programs that you can try running with EXEC so that we know if the issue is with the loader versus the game?  If the latter, I'll need help from those of you that have experience with speech synth coding. 

I can go into extended basic via the GPL interpreter and type CALL SAY("HELLO") and get speech.

 

Beery

Link to comment
Share on other sites

5 hours ago, mizapf said:

Do a CALL SPGET("HELLO",A$) and then CALL SAY(,A$); this will run a speak external command.

No issue.

 

4 hours ago, InsaneMultitasker said:

It occurred to me that the indices to the speech data are addresses, and those addresses are AORG'd to the >6000 space.  Since we are banking everything into >4000-5FFF, who knows what is being sent to the synthesizer.   @9640News I PM'd you a set of files to test.

Sent you a text message picture of the error message, invalid GPL access.

Link to comment
Share on other sites

3 hours ago, 9640News said:

Sent you a text message picture of the error message, invalid GPL access.

Gremlins.  I made one instruction change that has nothing to do with GPL, and I don't recall seeing anything related to GPL in Rasmus' code.  I will test the files tonight on my system.  Maybe there was an error during assembly that I didn't notice. 

 

@Asmusrdo you rely upon GPL or any ROM-based XML calls in your game(s)?   

Link to comment
Share on other sites

7 hours ago, Asmusr said:

No, I'm not using anything in the system ROMs or GROMs.

Thank you. 

 

( I don't understand how adjusting the speech data index (speech_addr) by ->2000 affects any jump/branch operations that would cause the error Beery is encountering.  Either I inadvertently edited/deleted something within speech.a99 or there is some instruction that isn't apparent to me. )

Link to comment
Share on other sites

1 hour ago, InsaneMultitasker said:

Thank you. 

 

( I don't understand how adjusting the speech data index (speech_addr) by ->2000 affects any jump/branch operations that would cause the error Beery is encountering.  Either I inadvertently edited/deleted something within speech.a99 or there is some instruction that isn't apparent to me. )

Did you also remember to aorg speech-data.a99 at >4000 instead of >6000?

Edited by Asmusr
  • Like 1
Link to comment
Share on other sites

1 hour ago, Asmusr said:

And adjust ROM address in speak and send_bytes_to_speech functions?

Yes, those routines looked good in the listing.  I am sitting in my car about to run a few errands and it occurred to me that I am using a BL @>E to call the modified keyboard routine.  GPLWS is the caller workspace, which means R11 is modified during the BL instruction.   R11 is address >83F6 which just so happens to be the destination address for the speech read status routine, in PAD.  I surmise the speech read status is overwriting the MSByte at the R11 address, sending the program into a tailspin. 

  • Like 3
Link to comment
Share on other sites

@9640News confirmed that R11 was the culprit; PINBALL speech is working properly now.   I posted a question about the gameplay in the pinball development topic.

 

To improve (and maybe simplify) the method for emulating this style of cartridge, applied a similar approach to the Flying Shark game.  The title and demo mode work fine, gameplay starts but I haven't tied in the joystick or keyboard routines. 

 

On 5/27/2022 at 1:28 PM, mizapf said:
  • Paging is done by writing to a memory address; this won't work with the Geneve mapper. Instead, these operations need to be replaced (possibly in the binary code). Maybe XOP? (not 0, which is used for the system calls) Or an MID-based approach?
  • Keyboard control: The Geneve keyboard is very different to the TI keyboard, but if needed, keyboard access can be written on a low level as well. See https://www.ninerpedia.org/wiki/Geneve_keyboard_control
  • As long as standard VDP (9918/9929) operations are used, there are only few (if any) problems on the 9938. F18A features are out of scope.

So far, this has all been true.  For Pinball and Flying Shark, Rasmus' banking approach made it "very easy" to change the bank location and memory mapping to suit the Geneve.  The keyboard support depends on how the programmer implements the scan.  Vsync-driven games might require activating the Geneve wait state and/or using slow ram, where gameplay is impacted by faster code execution.  I will play the games in emulation to get a feel for their operation on the /4A versus the 9640.

  • Like 4
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...