Jump to content
IGNORED

Assembly on the 99/4A


matthew180

Recommended Posts

I asked Karl Guttag about what happens if you read the status register at the same moment when the flag is being sent in the 9918A. He said he did not know specifically about that condition, but he was pretty sure that the 9918A did not guard against those kinds of hazards.

 

I suspect the code with the consecutive conditional jumps is polling fast enough to expose the hazard of setting and clearing the frame-flag at the same time. Actually, the MOVB loop is also capable of exposing the hazard, but probably not as often as the conditional jump loop. The F18A used to specifically guard against this hazard, but in trying to be 100% compatible with the 9918A, the F18A now models the hazard, i.e. if you read the status register at the same time the frame-flag is being set, the flag will be cleared and reported as clear.

 

Basically, polling the VDP directly risks dropping frames. IMO, the best method is to use the interrupt associated with the VDP and use the ISR to just set a flag to true. Your polling loop checks your flag, and when it is true, exit the loop, clear the flag, and continue as usual. Or on the 99/4A, poll the 9901 as mentioned earlier.

 

You could also use a counter in your ISR instead of a true/false flag, which allows you to know if you missed frames. The ISR increments the counter, and your polling loop checks for a value greater-than zero; and if true, exit the loop, *copy the ISR count*, reset ISR count to 0. Now you have the number of frames since your last loop, and if greater than 1 you can decide what to do about it (if anything).

 

 

Then, as far as I remember, it also fails on the F18A where the 5th sprite bits can be zero.

 

In the V1.6 (in 2014) firmware I made a compatibility change for reporting 5th sprite values. I decoupled the status register n-th sprite reporting from the actual number of sprites displayed. The original problem was due to the F18A's ability to show all 32 sprites on a line, the 5th-sprite flag was always clear (as well as the sprite-number bits).

 

The change I made was to report the 5th sprite, regardless of how many sprites are being displayed, when the F18A is locked. When unlocked, the flag is set when the sprite_max value (VR30) is reached, which never happens if VR30 is 31 (i.e. max-sprites is all 32 sprites).

 

I also changed the sprite-bits in the status register to follow the current sprite being processed during a scan line as long as the 5th-sprite flag is clear. This appears to be the behavior of the 9918A. Once the 5th sprite is detected, the 5th-sprite flag is set and sprite processing stops, which provides the 5th-sprite value number to the status register. The F18A models this behavior.

  • Like 1
Link to comment
Share on other sites

I suspect the code with the consecutive conditional jumps is polling fast enough to expose the hazard of setting and clearing the frame-flag at the same time. Actually, the MOVB loop is also capable of exposing the hazard, but probably not as often as the conditional jump loop.

I specifically tested for it years ago and was able to prove it can happen.. I'd expect the same thing.

 

On the TI we don't need to enable the ISR if you don't mind polling. Since the VDP interrupt is wired through the CRU, you can just test CRU bit 2 to see if the line is high. Setting a flag in a user ISR is also good - I tend to do the latter on the ColecoVision where the ISR is an NMI. ;)

 

(edit: all of which Matt already said.. never mind. ;) )

Edited by Tursi
Link to comment
Share on other sites

Since the VDP interrupt is wired through the CRU, you can just test CRU bit 2 to see if the line is high.

Safe to say there’s a consensus here regarding CRU bit 2 testing vs polling VDP Status Register for previously stated reasons?

 

Added bonus is no interrupts enabled so scratchpad RAM isn’t “trampled upon.”

 

 

Sent from my iPhone using Tapatalk Pro

  • Like 1
Link to comment
Share on other sites

Safe to say there’s a consensus here regarding CRU bit 2 testing vs polling VDP Status Register for previously stated reasons?

 

Yes. Still, the only problem is that it doesn't work on the Dijit AVPC card.

 

Does anyone have a list of the scratchpad addresses you cannot use if you use the ISR?

Link to comment
Share on other sites

 

Does anyone have a list of the scratchpad addresses you cannot use if you use the ISR?

There is a listing in the E/A manual on pages 404-406. The description of the ISR, DSR, etc. usage of this memory, as described on these pages, has such a large foot print that I have thrown out any idea of allowing the current interrupt system any access. I don't know of any other list, and I've seen this one referenced every time I've seen the subject come up, as far as I can recall anyway. :)

 

HH

Link to comment
Share on other sites

I did read through the TI-Intern ISR disassembly at some point long ago, but I don't think I made any notes about scratch pad memory use. :-(

 

Posts #38 and #40 (back on pg. 2 of this thread) explain a little about the ISR and what parts of it can be disabled. Then there is my post 2-years later where I reluctantly embrace the ISR:

 

#232, pg 10: http://atariage.com/forums/topic/162941-assembly-on-the-994a/page-10?do=findComment&comment=2797433

 

That gives a code example of how to use the the ISR is the most minimal form, which I probably tested (I usually don't post code without testing it... usually...). With most of the ISR services disabled, there is probably not a lot of scratch pad that you need to avoid. Of course there is the ISR hook address, and I think one workspace close to the end of scratch pad. Of course this assumes you are not planning on calling any GPL or ROM routines, and you can be sure no other devices have triggered an interrupt (that would allow a DSR to run, for example).

  • Like 1
Link to comment
Share on other sites

So I just did a review of the ISR again, only paying attention to the code flow when the interrupt is caused by the VDP, and the VDP "allowed services" are all disabled. Here is the summary:

ISR Vector WP >83C0

ISR in ROM at: >0900
First thing:
LIMI 0
LWPI >83E0  <--- Change workspace to GPL

Test if interrupt was cassette: No.
Test if interrupt was for VDP: Yes (in this case), jump to >094A

>094A, VDP interrupt: 
  MOVB @>83C2,R1
  Shift R1 left 1-bit, see if *any* VDP services are allowed
  If set, branch to >0A84, no services allowed

  Otherwise, check each service:
  >0958: Shift R1 left, JOC >09E8 (no sprite processing allowed)
  >09E8: Shift R1 left, JOC >0A66 (no sound processing allowed)
  >0A66: Shift R1 left, JOC >0A84 (no QUIT allowed)
  
  >0A84:
    Read VDP status byte into >837B  ** R15 in GPLWS contains VDP address vector >8C02
    LWPI >83C0, initial ISR vector workspace
    INC R11, which is address >83D6, if zero blank screen
    LWPI >83E0, back to GPLWS
    Updates >8379, screen timeout counter value
    Check ISR Hook >83C4, if not zero, BL @>83C4
    LWPI >83C0, back to ISR vector workspace
    RTWP, end the ISR

Notable addresses used:
>8379, copy of screen timout value
>83C2, accessed directly, VDP ISR allowed services
>83D4, accessed via R10 with WP set to >83C0, current VDP VR1 value
>83D6, accessed via R11 with WP set to >83C0, screen blank timeout
>83FE, accessed via R14 with WP set to >83E0, VDP access address >8C02

Observations:

 

* The workspace is ping-ponged back and forth between the GLPWS and the ISRWS. This means the scratch pad from >83C0 .. >83FF is pretty much off limits.

 

* There are assumed values in some of the GPLWS, notably R15 which contains >8C02 (the VDP write address), and that register is used as a base value to calculate the other VDP addresses (like when reading the VDP status register).

 

* The ROM commonly accesses specific memory through registers, similar to how my VDP routines send the MSB of R1 by knowing it will be at a specific memory address.

 

* There seems to be no consistency in the ROM for accessing specific memory directly, i.e. as an address like >83C0, or via mapped registers. It really makes it a pain in the ass to follow sometimes.

 

* The ISR does not seem to issue a LIMI 2 when it is done. The interrupt mask is stored in the calling workspace, which is restored when the ISR returns with RTWP.

 

* Don't let >83D6 even be zero to avoid having the ISR blank the VDP.

 

* Keep a copy of your VR1 in >83D4.

 

So, basically to use the ISR without any of the services:

 

* Don't use >83C0 .. >83FF in scratch pad

* Know that >8379 will be overwritten every time the ISR is called (contains a copy of the timeout value)

* Set your hook-routine address in >83C4, load your own workspace, restore the GLPWS before return, and return from your routine with B *R11.

* Disable the ISR services by writing >FF to >83C2

* Write >FF to >83D6 Edit: write >0001 to address >83D6 every time through your game loop to avoid screen blank

* Put a copy of your VDP VR1 into >83D4

 

I think that is about it. Let me know how it goes. ;-)

 

Edit: See above. I forgot the ISR *increments* the 16-bit value at >83D6. Actually, it increments by 2 (INCT), and if the key routine that resets it to >0000 could be avoided, then you could set the value to >0001 once and never have to worry about it again (since the value will never actually be >0000).

Edited by matthew180
  • Like 2
Link to comment
Share on other sites

Edit: Sorry Matthew, I didn't see your last post before I did this myself.

 

Looking at the disassembly in TI Intern, the ISR with as much a possible turned off seems to be using at least these addresses:

 

GPL WS (>83E0) R1,R8,R12,R14,R15
INT WS (>83C0) R13-15
>83C2 ISR flags
>83C4 ISR address
>837B VDP status
>83D6-7 Screen timeout counter
>8379 VDP interrupt timer
Actually it would probably be best not to use any address above >83C0. This means I cannot use the ISR for my latest project.
Edited by Asmusr
  • Like 2
Link to comment
Share on other sites

Two reviews is better than one. :-)

 

I guess I would fall back to checking the CRU directly then, like the ISR is doing. I don't know what the "Dijit AVPC card" is, but it has to trigger the ISR somehow, no? If supporting that card is important, then maybe detect the card and use an alternative method for the ISR in that case?

  • Like 1
Link to comment
Share on other sites

Edit:

Actually it would probably be best not to use any address above >83C0. This means a cannot use the ISR for my latest project.

Cost:

 

CPU RAM PAD is located at addresses >8300 through >83FF.

 

256 measly BYTES!

 

ISR uses the high memory locations at addresses >83CO through >83FF.

 

64 BYTES! (25% hit)

 

Conclusion: LIMI 0

 

 

 

 

Sent from my iPhone using Tapatalk Pro

  • Like 1
Link to comment
Share on other sites

A game like TI Scramble runs the entire main loop between two vsyncs, so you have more time than you may think.

 

You have 50000 clock cycles in NTSC between two vsyncs, and you can use Classic99 to measure how much time you have used: Add a breakpoint with the start and end addresses of you main loop, e.g. T(A024-A038).

 

Updates to the display you should try to finish as soon as possible after vsync. If you use two name tables as a double buffer you can update alternating tables each frame and just do a page flip right after vsync. I have even double buffered the sprite attribute table this way.

 

Timing of sound playing is less important, I often do it after all the other work has been done.

 

If you run over the frame, perhaps deliberately, remember to read the VDP status register once to clear the interrupt before you wait for the next one. Otherwise your polling loop will terminate immediately in the middle of a frame. You must also read the VDP status after detecting the interrupt, like this:

vsync:
       movb @vdpsta,r12
       clr  r12
vsync_1:
       tb   2                          ; Test CRU bit for VDP interrupt
       jeq  vsync_1
       movb @vdpsta,r12
       rt

In Flying Shark the main loop takes up to 4 frames depending of the complexity of the graphics. In that game I had to count the frames and add delays in order to obtain an even pace, which was a real pain. If possible try to do approximately the same amount of work each frame. That doesn't mean you have to do the same things each frame. One frame can move the sprites while another scrolls the screen, for instance.

 

I am using your vsync loop with success. My game loop is:

 

LOOP

LIMI 0

wait for vsync

draw 500 bytes to VDP for scrolling bitmap playing field and sprites

 

increment counters

test counters to see if sprites should go to next animation

update playing field in CPU RAM

tell music players to go 1 tick

KSCAN

 

The music is playing at regular speed and my tick counter is advancing at 60 Hz so all's well. I'm not giving the ISR any time.

  • Like 2
Link to comment
Share on other sites

 

I am using your vsync loop with success. My game loop is:

 

LOOP

LIMI 0

wait for vsync

draw 500 bytes to VDP for scrolling bitmap playing field and sprites

 

increment counters

test counters to see if sprites should go to next animation

update playing field in CPU RAM

tell music players to go 1 tick

KSCAN

 

The music is playing at regular speed

A. I’d like to see how your music player(s) work.

 

B. KSCAN? Won’t that mess with the Scratchpad too?

 

 

 

 

Sent from my iPhone using Tapatalk Pro

Link to comment
Share on other sites

A. I’d like to see how your music player(s) work.

 

B. KSCAN? Won’t that mess with the Scratchpad too?

 

 

 

Right.. KSCAN. I'm not using PAD aggressively, just my WS in >8300. I must re-read this discussion if I want to take over more of PAD (like, storing my VDP write loop code).

 

The music player is based on FORTI in pure assembly (not FORTH this time). I'll be posting all the source here shortly.

Link to comment
Share on other sites

KSCAN is not only messing with scratchpad but it's also quite slow because it's doing far more than you usually need to check a key in a game.

 

Here is a routine I use to check a key. Just load r0 with one of the equates, e.g. li r0,KEY_A and bl @check_key. On return jeq moves you on if the key was not pressed.

 

In specific cases where you need to check multiple keys, e.g. the joystick, you can do faster by reading multiple CRU bits, but my routine is still a lot faster than KSCAN.

*********************************************************************
*
* Check key
*
* r0: Column in MSB: >0000 - >0700, Row in LSB: >0006 - >0014
*
*       Column   0      1    2    3    4    5     6       7
*     Row
*     >0006      =      .    ,    M    N    /    Fire    Fire
*     >0008    Space    L    K    J    H    ;    Left    Left
*     >000A    Enter    O    I    U    Y    P    Right   Right
*     >000C             9    8    7    6    0    Down    Down
*     >000E    Fctn     2    3    4    5    1    Up      Up
*     >0010    Shift    S    D    F    G    A
*     >0012    Ctrl     W    E    R    T    Q
*     >0014             X    C    V    B    Z
*
* On return NEQ means that the key is pressed
*
KEY_FI EQU  >0606
KEY_LF EQU  >0608
KEY_RG EQU  >060A
KEY_DN EQU  >060C
KEY_UP EQU  >060E
KEY_CM EQU  >0206
KEY_FS EQU  >0106
KEY_0  EQU  >050C
KEY_1  EQU  >050E
KEY_2  EQU  >010E
KEY_3  EQU  >020E
KEY_4  EQU  >030E
KEY_5  EQU  >040E
KEY_6  EQU  >040C
KEY_7  EQU  >030C
KEY_8  EQU  >020C
KEY_9  EQU  >010C
KEY_A  EQU  >0510
KEY_B  EQU  >0414
KEY_C  EQU  >0214
KEY_D  EQU  >0210
KEY_E  EQU  >0212
KEY_F  EQU  >0310
KEY_G  EQU  >0410
KEY_H  EQU  >0408
KEY_I  EQU  >020A
KEY_J  EQU  >0308
KEY_K  EQU  >0208
KEY_L  EQU  >0108
KEY_M  EQU  >0306
KEY_N  EQU  >0406
KEY_O  EQU  >010A
KEY_P  EQU  >050A
KEY_Q  EQU  >0512
KEY_R  EQU  >0312
KEY_S  EQU  >0110
KEY_T  EQU  >0412
KEY_U  EQU  >030A
KEY_V  EQU  >0314
KEY_W  EQU  >0112
KEY_X  EQU  >0114
KEY_Y  EQU  >040A
KEY_Z  EQU  >0514
KEY_SP EQU  >0008
KEY_EN EQU  >000A
KEY_FN EQU  >000E
KEY_SH EQU  >0010
KEY_CT EQU  >0012
JOY_FI EQU  >0606
JOY_LT EQU  >0608
JOY_RT EQU  >060A
JOY_DN EQU  >060C
JOY_UP EQU  >060E
*
check_key:
       li   r12,>0024                  ; CRU address of the column decoder
       ldcr r0,3                       ; Select column
       andi r0,>00ff
       mov  r0,r12                     ; Select row
       tb   0                          ; Test key, EQ if not pressed
       rt
*// check_key
 
  • Like 3
Link to comment
Share on other sites

KSCAN is not only messing with scratchpad but it's also quite slow because it's doing far more than you usually need to check a key in a game.

...

Like pausing for 8mS, twice, to debounce in code.

 

@FarmerPotato, there has been a lot of keyboard and KSCAN discussion around pg. 10 in this thread. My post that dissects the delay in KSCAN is here:

 

http://atariage.com/forums/topic/162941-assembly-on-the-994a/page-10?do=findComment&comment=2840302

  • Like 1
Link to comment
Share on other sites

Like pausing for 8mS, twice, to debounce in code.

 

@FarmerPotato, there has been a lot of keyboard and KSCAN discussion around pg. 10 in this thread. My post that dissects the delay in KSCAN is here:

 

http://atariage.com/forums/topic/162941-assembly-on-the-994a/page-10?do=findComment&comment=2840302

 

OK, KSCAN will be toast. I will check for movement keys/fire, maybe Redo/Back. The column setup can be combined.

Thanks Rasmus!
KEY_FN EQU  >000E

KEY_FS EQU  >0106
KEY_9  EQU  >010C
KEY_S  EQU  >0110
KEY_X  EQU  >0114

KEY_D  EQU  >0210
KEY_8  EQU  >020C
KEY_E  EQU  >0212

KEY_Q  EQU  >0512

JOY_FI EQU  >0606
JOY_LT EQU  >0608
JOY_RT EQU  >060A
JOY_DN EQU  >060C
JOY_UP EQU  >060E
  • Like 1
Link to comment
Share on other sites

On a related yet much less technical note...

 

I’ve always felt the early computer games overused the keyboard. Lots of really poor UI out there...

 

Examples:

 

“Press J for joystick or K for keyboard”

or

“Press Enter/Return to start game.”

or

“Press 1 to play, 2 to exit.”

or

“Press Spacebar to begin”

or

“Make sure alpha-lock is....”

or

 

As a gamer I much prefer joystick based menus with button presses or Joystick movement to select options.

 

Ex: Building in a joystick-up menu movement requirement prior to starting your game assures Alpha-Lock is released on the TI console, well before gameplay begins, every time.

 

It’s understood the TI console did not come with a joystick. Is it not safe to assume each potential player has one by now? Why punish all players with zero value added keyboard driven UIs?

 

Text adventures may be the exception here. They may also be the reason bad UIs became the standard prior to action games: sports, shooters, etc.

 

Nothing worse than having to get up off the couch to restart a game. Seriously, I didn’t buy a joystick cable extender so I can now get up and press the 1 key to restart each and every game.

 

Friends don’t let friends KSCAN.

 

 

Sent from my iPhone using Tapatalk Pro

  • Like 1
Link to comment
Share on other sites

But you have to consider the times the 99/4A was released in. Most people didn't have computer exposure prior to getting one, and if you just said "Use Joystick to make selection" and you couldn't move up because the Alpha Lock key was engaged, and didn't provide a reminder message, the novice user would likely be pretty frustrated with your program very quickly. I know there was an addendum thing in the pile of docs that came with the 99/4A that mentioned the joystick/alpha lock issue, but the average person probably didn't read all that real thoroughly.

  • Like 1
Link to comment
Share on other sites

I understand you're talking about the menus, not gameplay.

 

But for gameplay, I don't like joysticks. I have more dexterity in my fingertips on the keyboard. I resent that the Atarisoft games generally don't have keyboard controls.

 

 

Late games like Barrage and SpotShot let you push Fire instead of Redo. You only needed the keyboard for Back.

  • Like 2
Link to comment
Share on other sites

But you have to consider the times the 99/4A was released in. Most people didn't have computer exposure prior to getting one, and if you just said "Use Joystick to make selection" and you couldn't move up because the Alpha Lock key was engaged, and didn't provide a reminder message, the novice user would likely be pretty frustrated with your program very quickly. I know there was an addendum thing in the pile of docs that came with the 99/4A that mentioned the joystick/alpha lock issue, but the average person probably didn't read all that real thoroughly.

 

In Bubble Plane (1988) I added a CRU bit test and a message saying "RELEASE ALPHA LOCK TO BEGIN PLAY". Despite what I just said, keyboard and joystick gameplay and menus (including joystick high score initials entry) were both supported.

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