Jump to content
IGNORED

F18A programming, info, and resources


matthew180

Recommended Posts

I'm having a problem. I've got the f18a installed and working great in a colecovision. No problems there.

 

With programming a scroll. I can scroll 1 pixel no problem left & right. when I get past 7 pixels I do a character screen shift and need to reset the f18a vr27 back to 0.

 

Now it does work but I see the character shift as a hiccup & then the reset of the vr27 offset to 0 as the second hickup before they align again.

 

can I update the scroll register during a disabled nmi and not have it display til the character update is complete & the nmi is re-enabled?

Link to comment
Share on other sites

I'm having a problem. I've got the f18a installed and working great in a colecovision. No problems there.

 

With programming a scroll. I can scroll 1 pixel no problem left & right. when I get past 7 pixels I do a character screen shift and need to reset the f18a vr27 back to 0.

 

Now it does work but I see the character shift as a hiccup & then the reset of the vr27 offset to 0 as the second hickup before they align again.

 

can I update the scroll register during a disabled nmi and not have it display til the character update is complete & the nmi is re-enabled?

 

Why not set up the horizontal page size in VR29 so you have two full pages to scroll within? Then you can add new character columns outside the visible viewport. This means you only have to update perhaps 24 characters for each 8 pixels scrolled instead of 768. You can even set up the increment for vram addresses in VR48 to 32 so you only have to set up the write address once before updating a column.

 

If you want to keep the model you're working with you probably need to double buffer your name table. You should always set up the VRs during vertical refresh to prevent screen tearing problems/hickups.

 

Edit: Another idea is to maintain a single name table page only but to cover the edges with a frame using tile layer 2. Then you can secretly update characters under the frame and scroll them in afterwards.

Link to comment
Share on other sites

I have p[layed with the 2 page setup today with smooth scrolling. The reason to maintain the chunky scroll was for compatibility with an unmodified colecovision. It's (The 8 pixel chunky scroll modified with vr27 offsetting) is working reasonably well but glitchy.

 

The 2 page scroll looks great but then all my tile collisions are messed up. The collisions are happening right where they used to be on the screen but are displayed else where such as a rock or bridge or something I bang into.

 

I've uploaded a sample to youtube if you want to check it out.

 

0:00-1:00 just 2 page scrolling

1:00-1:22 a weird screwup that happens after awhile.

1:22-2:00 the planes with the combined character shifting scroll and vr27 offset 0-7 scroll.

 

  • Like 1
Link to comment
Share on other sites

Thanks for the suggestions. I was trying them out today.

 

I understand what you mean by the black cloumn to mask the updating on the screen edges so I got the bitmap layer sorta working but I have no idea how to load tiles into it yet so it was just see through garbage

 

The game is mostly done, someone suggested I look into f18a scrolling so I thought it was worth trying. I could also potentially later on convert it to work on a ti99/4a as most of it is compatible it's all built in sdcc so it would be mostly changing over the libraries i'm using.

 

- I don't know why the tiles become corrupted after ahwile in the tank section, doesn't happen in the planes so something in my program doing it. It looks like 2x2 version of fthe tile scren in 1 bit colour.

- with 2 or 4 page scrolling how do compensate for the tiles not being where they used to be. ex. if a tree is normally at tile position 10,10. But the scroll is offset by 128 pixels are you just calculating that into every collision manually? so (10 * 8 =80 pixels +128 the actual location visually is 208 pixels.

I guess I could scroll the sprites along with the bg.

Whoa, that looks great.

 

 

Whoa, that looks great.Sorry I messed up the threads before.

Link to comment
Share on other sites

Your chunky scroll is pretty much there... and with the complexity of your game already, I think that'd be the avenue I'd pursue. (On the other hand, you're amazingly quick at large reworks!! ;) )

 

The only thing that's really missing is timing when you update the screen for that 8th scroll where the characters need to move, you want that to happen offscreen. So you need to do the scroll as soon as possible after a vertical interrupt, so it's hidden. If the draw itself is taking too long, one of the suggestions was to double-buffer -- this means having a second screen in memory that you draw to while displaying the first one. Then updating the characters on the screen is just another register write - you swap which screen you're drawing to as well. That presumes you have enough VDP RAM free to do two screens there.

Link to comment
Share on other sites

yeah that's pretty much my concllusion but I wanted to look into it.

 

I'd have to rework too many things to use it anywhere else. Would be better to start in a new project and plan out from the beginning how to incorporate the scrolling.

 

Your chunky scroll is pretty much there... and with the complexity of your game already, I think that'd be the avenue I'd pursue. (On the other hand, you're amazingly quick at large reworks!! ;) )

The only thing that's really missing is timing when you update the screen for that 8th scroll where the characters need to move, you want that to happen offscreen. So you need to do the scroll as soon as possible after a vertical interrupt, so it's hidden. If the draw itself is taking too long, one of the suggestions was to double-buffer -- this means having a second screen in memory that you draw to while displaying the first one. Then updating the characters on the screen is just another register write - you swap which screen you're drawing to as well. That presumes you have enough VDP RAM free to do two screens there.

Link to comment
Share on other sites

An alternative when using the F18A would be to use the GPU or the GPU's DMA to scroll the name table tiles. Once you reach the 7th scroll pixel, the next time through the loop you would just trigger the GPU to run a simple tile adjust routine and reset the horizontal scroll register. You would only need to reset your own CPU-side counter and carry on like normal.

 

The GPU scroll routine would be stored as binary data in your code and loaded to the VRAM during game initialization. The GPU would just sits idle until you trigger it. Something like this: (untested, maybe Rasmus will give it a whirl... ;-) )

**
* F18A GPU code to use the DMA for left and right tile scrolling
*

* >6000 to >603F VDP regs
*
* The DMA src, dst, width, height, stride are copied to dedicated counters when
* the DMA is triggered, thus the original values remain unchanged.

NTBA        EQU >6002            ; Name table base address
DMA_SRC     EQU >8000            ; DMA 16-bit src address, MSB first
DMA_DST     EQU >8002            ; DMA 16-bit dst address, MSB first
DMA_W       EQU >8004            ; DMA width
DMA_H       EQU >8005            ; DMA height
DMA_STRIDE  EQU >8006            ; DMA stride
DMA_CMD     EQU >8007            ; DMA command: 0..5 | !INC/DEC | !COPY/FILL
DMA_TRIG    EQU >8008            ; DMA trigger, write any value to address

       AORG >3F00

GPU_ROUTINES
       IDLE
       B    @GPU_SCROLL_LT       ; >3F02 trigger vector
       B    @GPU_SCROLL_RT       ; >3F06 trigger vector


**
* Scroll the name table tiles left.
*
GPU_SCROLL_LT
*      Get the NTBA and calculate the table's address.
       CLR  R1
       MOVB @NTBA,R1
       SLA  R1,2                ; Adjust NTBA bits to make the 14-bit VRAM address

*      Set up the DMA.
       MOV  R1,@DMA_DST         ; Start of the name table is the destination
       INC  R1                  ; The second tile address is the source
       MOV  R1,@DMA_SRC

       LI   R1,31*256+24        ; Fancy way to put 31 (width) in the MSB of R1 and 24 (height) in the LSB
       MOV  R1,@DMA_W           ; Writes both Width and Height

       LI   R1,32*256           ; Set the Stride to 32, set the command to INC & COPY
       MOV  R1,@DMA_STRIDE      ; Write both Stride and Command

       SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes

       B    @GPU_ROUTINES       ; Done, so return to idle
*// GPU_SCROLL_LT


**
* Scroll the name table tiles right.
*
GPU_SCROLL_RT
*      Get the NTBA and calculate the table's address.
       CLR  R1
       MOVB @NTBA,R1
       SLA  R1,2                ; Adjust NTBA bits to make the 14-bit VRAM address

*      Set up the DMA.
       AI   R1,767              ; The bottom-right tile address is the destination
       MOV  R1,@DMA_DST
       DEC  R1                  ; The destination-1 tile address is the source
       MOV  R1,@DMA_SRC

       LI   R1,31*256+24        ; Fancy way to put 31 (width) in the MSB of R1 and 24 (height) in the LSB
       MOV  R1,@DMA_W           ; Writes both Width and Height

       LI   R1,32*256+2         ; Set the Stride to 32, set the command to DEC & COPY
       MOV  R1,@DMA_STRIDE      ; Write both Stride and Command

       SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes

       B    @GPU_ROUTINES       ; Done, so return to idle
*// GPU_SCROLL_RT

GPU_ROUTINES_END
       END

You can compile this with something like asm994a. Make sure to generate a listing file and copy the hex machine code bytes into your CV program as a block of data. Then during your game's startup, copy that block to VRAM >3F00. Trigger the GPU at VRAM address >3F02 for left scrolling, and VRAM address >3F06 for right scrolling. The operation should only take around 16us. You will have to fill in the left or right column with new data when the routine is done. Alternatively the routines could be easily modified to copy the new column from a known location in VRAM after the scroll.

Link to comment
Share on other sites

I gave it a try but I didn't get too far. the asm994a didn't assemble it. There were a few errors reported. I don't know assembler so I'm not sure what's wrong.

 

This sounds interesting.

 

Any idea what might have caused the screen to go wonky in the video at the 1:00 minute mark? It seems like it goes to full vga resolution 640x480 and everything is in 1 bit colour. The game is still running and the srpites are fine but the tiles went crazy. It must be a memory write problem in my program I guess that is interfering. When I do a vdp_init if I reach a certain point in the game it does correct itself.

 

 

>>>>>You can compile this with something like asm994a. Make sure to generate a listing file and copy the hex machine code bytes into your CV program as a block of data. Then during your game's startup, copy that block to VRAM >3F00. Trigger the GPU at VRAM address >3F02 for left scrolling, and VRAM address >3F06 for right scrolling. The operation should only take around 16us. You will have to fill in the left or right column with new data when the routine is done. Alternatively the routines could be easily modified to copy the new column from a known location in VRAM after the scroll.

 

This sounds very interesting though.

Link to comment
Share on other sites

See the attached image for using asm994a. You typically need to check the box "Def Regs (R0 - R15)". You also want to check the box to produce a listing file which will contain the assembled machine code in ASCII hex, which is what you want to embed into your C program.

post-24952-0-34421700-1475436730_thumb.png


Here is the listing file:

 

Asm994a TMS99000 Assembler - v3.010

                * Asm994a Generated Register Equates
                *
      0000 0000 R0      EQU     0 
      0000 0001 R1      EQU     1 
      0000 0002 R2      EQU     2 
      0000 0003 R3      EQU     3 
      0000 0004 R4      EQU     4 
      0000 0005 R5      EQU     5 
      0000 0006 R6      EQU     6 
      0000 0007 R7      EQU     7 
      0000 0008 R8      EQU     8 
      0000 0009 R9      EQU     9 
      0000 000A R10     EQU     10
      0000 000B R11     EQU     11
      0000 000C R12     EQU     12
      0000 000D R13     EQU     13
      0000 000E R14     EQU     14
      0000 000F R15     EQU     15
                *
   1            **
   2            * F18A GPU code to use the DMA for left and right tile scrolling
   3            *
   4            
   5            * >6000 to >603F VDP regs
   6            *
   7            * The DMA src, dst, width, height, stride are copied to dedicated counters when
   8            * the DMA is triggered, thus the original values remain unchanged.
   9            
  10  0000 6002 NTBA        EQU >6002            ; Name table base address
  11  0000 8000 DMA_SRC     EQU >8000            ; DMA 16-bit src address, MSB first
  12  0000 8002 DMA_DST     EQU >8002            ; DMA 16-bit dst address, MSB first
  13  0000 8004 DMA_W       EQU >8004            ; DMA width
  14  0000 8005 DMA_H       EQU >8005            ; DMA height
  15  0000 8006 DMA_STRIDE  EQU >8006            ; DMA stride
  16  0000 8007 DMA_CMD     EQU >8007            ; DMA command: 0..5 | !INC/DEC | !COPY/FILL
  17  0000 8008 DMA_TRIG    EQU >8008            ; DMA trigger, write any value to address
  18            
  19                   AORG >3F00
  20            
  21            GPU_ROUTINES
  22  3F00 0340        IDLE
  23  3F02 0460        B    @GPU_SCROLL_LT       ; >3F02 trigger vector
  23  3F04 3F0A  
  24  3F06 0460        B    @GPU_SCROLL_RT       ; >3F06 trigger vector
  24  3F08 3F34  
  25            
  26            
  27            **
  28            * Scroll the name table tiles left.
  29            *
  30            GPU_SCROLL_LT
  31            *      Get the NTBA and calculate the table's address.
  32  3F0A 04C1        CLR  R1
  33  3F0C D060        MOVB @NTBA,R1
  33  3F0E 6002  
  34  3F10 0A21        SLA  R1,2                ; Adjust NTBA bits to make the 14-bit VRAM address
  35            
  36            *      Set up the DMA.
  37  3F12 C801        MOV  R1,@DMA_DST         ; Start of the name table is the destination
  37  3F14 8002  
  38  3F16 0581        INC  R1                  ; The second tile address is the source
  39  3F18 C801        MOV  R1,@DMA_SRC
  39  3F1A 8000  
  40            
  41  3F1C 0201        LI   R1,31*256+24        ; Fancy way to put 31 (width) in the MSB of R1 and 24 (height) in the LSB
  41  3F1E 1F18  
  42  3F20 C801        MOV  R1,@DMA_W           ; Writes both Width and Height
  42  3F22 8004  
  43            
  44  3F24 0201        LI   R1,32*256           ; Set the Stride to 32, set the command to INC & COPY
  44  3F26 2000  
  45  3F28 C801        MOV  R1,@DMA_STRIDE      ; Write both Stride and Command
  45  3F2A 8006  
  46            
  47  3F2C 0720        SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes
  47  3F2E 8008  
  48            
  49  3F30 0460        B    @GPU_ROUTINES       ; Done, so return to idle
  49  3F32 3F00  
  50            *// GPU_SCROLL_LT
  51            
  52            
  53            **
  54            * Scroll the name table tiles right.
  55            *
  56            GPU_SCROLL_RT
  57            *      Get the NTBA and calculate the table's address.
  58  3F34 04C1        CLR  R1
  59  3F36 D060        MOVB @NTBA,R1
  59  3F38 6002  
  60  3F3A 0A21        SLA  R1,2                ; Adjust NTBA bits to make the 14-bit VRAM address
  61            
  62            *      Set up the DMA.
  63  3F3C 0221        AI   R1,767              ; The bottom-right tile address is the destination
  63  3F3E 02FF  
  64  3F40 C801        MOV  R1,@DMA_DST
  64  3F42 8002  
  65  3F44 0601        DEC  R1                  ; The destination-1 tile address is the source
  66  3F46 C801        MOV  R1,@DMA_SRC
  66  3F48 8000  
  67            
  68  3F4A 0201        LI   R1,31*256+24        ; Fancy way to put 31 (width) in the MSB of R1 and 24 (height) in the LSB
  68  3F4C 1F18  
  69  3F4E C801        MOV  R1,@DMA_W           ; Writes both Width and Height
  69  3F50 8004  
  70            
  71  3F52 0201        LI   R1,32*256+2         ; Set the Stride to 32, set the command to DEC & COPY
  71  3F54 2002  
  72  3F56 C801        MOV  R1,@DMA_STRIDE      ; Write both Stride and Command
  72  3F58 8006  
  73            
  74  3F5A 0720        SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes
  74  3F5C 8008  
  75            
  76  3F5E 0460        B    @GPU_ROUTINES       ; Done, so return to idle
  76  3F60 3F00  
  77            *// GPU_SCROLL_RT
  78            
  79            GPU_ROUTINES_END
  80  3F62 0000        END
  80            


 Assembly Complete - Errors: 0,  Warnings: 0


 ------ Symbol Listing ------

 DMA_CM ABS:8007 DMA_CMD
 DMA_DS ABS:8002 DMA_DST
 DMA_H  ABS:8005 DMA_H
 DMA_SR ABS:8000 DMA_SRC
 DMA_ST ABS:8006 DMA_STRIDE
 DMA_TR ABS:8008 DMA_TRIG
 DMA_W  ABS:8004 DMA_W
 GPU_RO ABS:3F00 GPU_ROUTINES
 GPU_RO ABS:3F62 GPU_ROUTINES_END
 GPU_SC ABS:3F0A GPU_SCROLL_LT
 GPU_SC ABS:3F34 GPU_SCROLL_RT
 NTBA   ABS:6002 NTBA
 R0     ABS:0000 R0
 R1     ABS:0001 R1
 R10    ABS:000A R10
 R11    ABS:000B R11
 R12    ABS:000C R12
 R13    ABS:000D R13
 R14    ABS:000E R14
 R15    ABS:000F R15
 R2     ABS:0002 R2
 R3     ABS:0003 R3
 R4     ABS:0004 R4
 R5     ABS:0005 R5
 R6     ABS:0006 R6
 R7     ABS:0007 R7
 R8     ABS:0008 R8
 R9     ABS:0009 R9

 



Take the third column where the code begins, i.e. the second column is >3F00 which is where we told the assembler to start generating code:

  Line Number
  |   Address
  \/  |    Machine Code
  18  |    |
  19  |    |           AORG >3F00
  20  |    |
  21  \/   \/   GPU_ROUTINES
  22  3F00 0340        IDLE
  23  3F02 0460        B    @GPU_SCROLL_LT       ; >3F02 trigger vector
  .
  .
  . copy the machine code column down to the end of the code...
  .
  .
  74  3F5A 0720        SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes
  74  3F5C 8008  
  75            
  76  3F5E 0460        B    @GPU_ROUTINES       ; Done, so return to idle
  76  3F60 3F00  
  77            *// GPU_SCROLL_RT

Then take the machine code hex and stuff it into an array in the C code. Copy the array to VRAM at the address specified by the AORG statement in the assembly code. Here is some pseudo code of how it might work:

unsigned char f18a_gpu_code[] = {
    0x03, 0x40,
    0x04, 0x60,
    0x3F, 0x0A,
    0x04, 0x60,
    0x3F, 0x34,
    0x04, 0xC1,
    0xD0, 0x60,
    0x60, 0x02,
    0x0A, 0x21,
    0xC8, 0x01,
    0x80, 0x02,
    0x05, 0x81,
    0xC8, 0x01,
    0x80, 0x00,
    0x02, 0x01,
    0x1F, 0x18,
    0xC8, 0x01,
    0x80, 0x04,
    0x02, 0x01,
    0x20, 0x00,
    0xC8, 0x01,
    0x80, 0x06,
    0x07, 0x20,
    0x80, 0x08,
    0x04, 0x60,
    0x3F, 0x00,
    0x04, 0xC1,
    0xD0, 0x60,
    0x60, 0x02,
    0x0A, 0x21,
    0x02, 0x21,
    0x02, 0xFF,
    0xC8, 0x01,
    0x80, 0x02,
    0x06, 0x01,
    0xC8, 0x01,
    0x80, 0x00,
    0x02, 0x01,
    0x1F, 0x18,
    0xC8, 0x01,
    0x80, 0x04,
    0x02, 0x01,
    0x20, 0x02,
    0xC8, 0x01,
    0x80, 0x06,
    0x07, 0x20,
    0x80, 0x08,
    0x04, 0x60,
    0x3F, 0x00
};


// Something like this to load the GPU code into VRAM during game initialization.
interrupts_off();
vdp_set_write_address(0x3F00);  // Code must be written to address used during assembly.

for ( i = 0 ; i < sizeof(f18a_gpu_code) ; i++ )
    vdp_write(f18a_gpu_code[i]);

interrupts_on();


// To trigger the GPU, set the address to begin execution.  The
// scroll left vector is VRAM address 0x3F02, scroll right is 0x3F06.
interrupts_off();
vdp_set_reg(54, 0x3F);  // Write GPU PC MSB.
vdp_set_reg(55, 0x02);  // Write GPU PC LSB.  scroll left routine.  writing VR55 triggers the GPU
interrupts_on();

interrupts_off();
vdp_set_reg(54, 0x3F);  // Write GPU PC MSB
vdp_set_reg(55, 0x06);  // Write GPU PC LSB.  scroll right routine.  writing VR55 triggers the GPU
interrupts_on();

If you need to move the code somewhere else in VRAM, change the AORG statement in the assembly code, re-assemble, copy the machine code to your array, and modify your GPU trigger calls accordingly. I picked >3F00 here for no other reason than it is 256 bytes from the end of VRAM.

Any idea what might have caused the screen to go wonky in the video at the 1:00 minute mark?

My guess would be that you have code somewhere that is writing to a VR (VDP Register) above 7 when it is not supposed to. On the real hardware, or a locked F18A, those VR writes are masked to 0..7. When an unlocked F18A, those writes are going to a random register. Or it might be that a write to a register is being interrupted. It looks like maybe 80-column mode is being set, but I'll have to look closer. On the F18A, sprites work in all screen modes when the F18A is unlocked.

post-24952-0-34421700-1475436730_thumb.png

Edited by matthew180
Link to comment
Share on other sites

well i was able to assemble and converted the code and it seemed to match up to the bytes in your sample below so it looks right. I was able to use your psqudo code and convert it to coleco equvalent and it runs. But the result on the first go was not good. So I guess I need to try a different ram location

 

https://www.youtube.com/watch?v=csYi0iyIfq4&feature=youtu.be

 

If you need to move the code somewhere else in VRAM, change the AORG statement in the assembly code, re-assemble, copy the machine code to your array, and modify your GPU trigger calls accordingly. I picked >3F00 here for no other reason than it is 256 bytes from the end of VRAM.

 

My guess would be that you have code somewhere that is writing to a VR (VDP Register) above 7 when it is not supposed to. On the real hardware, or a locked F18A, those VR writes are masked to 0..7. When an unlocked F18A, those writes are going to a random register. Or it might be that a write to a register is being interrupted. It looks like maybe 80-column mode is being set, but I'll have to look closer. On the F18A, sprites work in all screen modes when the F18A is unlocked.

Link to comment
Share on other sites

Well, that is clearly not working. I guess I better test my code to make sure it is working as expected.

 

 

So I guess I need to try a different ram location

 

You should know your VRAM utilization and where you might have a spare hole of memory to put the routine. It is very important that the GPU code does not get overwritten. As written the routine needs 96 bytes.

Link to comment
Share on other sites

I was just play testing my regular tank mission without any code modification for the f18a and had the same vga 80 column mode happened several times. I built in a keypad option to press 3 if this happens and it will set the screen mode back to colecovision colour mode 2 and the game continues without any hitch.

 

So this has been very useful as if I had not ordered the f18a I would not even be aware that it was happening on f18a systems. I believe it might have to do with the music program I'm using I think as it might have something to do the the vdp ports according to the lines below. I'm guessing. I'm going to try your program in another game I made without any sound at all as a test .

 

// audio port (needed to mute audio before playback)
volatile __sfr __at 0xff SOUND;
inline void MUTE_SOUND() { SOUND=0x9f; SOUND=0xbf; SOUND=0xdf; SOUND=0xff; }
// vdp port (needed to enable interrupts)
static volatile __sfr __at 0xbf vdpaddr;
EDIT>>
I tried it (the gpu scroll program above) in my mr turtle game with no sound and got the same corrupt video. However the other problem with the 80 column mode seems to have gone away when testing just the regular smooth scrolling so i suspect that is related to the music player. I'll have to try it for a longer period to be sure.
Double EDIT>> I noticed an error in my logic. I'll go try to fix that. I may have loaded garbage into memory
...and it made no difference.

Well, that is clearly not working. I guess I better test my code to make sure it is working as expected.

 

 

You should know your VRAM utilization and where you might have a spare hole of memory to put the routine. It is very important that the GPU code does not get overwritten. As written the routine needs 96 bytes.

Link to comment
Share on other sites

I don't think it's my music player... it doesn't touch video RAM or video registers, and it works fine in my own game.

 

More than likely you still have a place that's hitting VDP RAM incorrectly. You can't get timing overruns with the F18A (So make sure you test on a standard ColecoVision before you release the game!), but if you wrap around and write to register space, some of the previously ignored bits have meaning now. Check your VDP address calculation code, for instance, and make SURE when you are moving things on the screen that the address never goes negative - a negative address turns into a register write. :)

Link to comment
Share on other sites

OK, I tested the GPU DMA code on real iron (and in js99er) and it works as written. These are the possible problems I can come up with, but someone more familiar with CV programming in C might have to chime in.

 

1. Copying the GPU code to VRAM is incomplete or getting corrupt somehow.

2. VRAM addresses >3F00 .. >3F60 are not safe and are getting overwritten during game play, which corrupts the code.

3. You are not triggering the GPU at the correct address.

4. The F18A is not actually unlocked when you try to trigger the scrolling.

 

In the case of #2, remember that if you want to load the code somewhere else in VRAM, you need to change the AORG statement in the GPU source and recompile. Actually, I can probably make the code relocatable without having to recompile. I'll check on that.

 

 

I was just play testing my regular tank mission without any code modification for the f18a and had the same vga 80 column mode happened several times. I built in a keypad option to press 3 if this happens and it will set the screen mode back to colecovision colour mode 2 and the game continues without any hitch.

 

That tells me that somewhere VDP registers over VR7 are being written to, or VR0 bit >04 and VR1 bit >10 are getting set (which is how 80-column mode is enabled). Maybe it is something with the music player, as you mentioned. To be compatible with the 9938's 80-column mode, the F18A allows you to set 80-column mode even when the F18A is locked (the 9938/58 do not have any lock-out of the VRs over VR7 and require software to behave and be properly written). This is why you are seeing this problem even on games that do not unlock the F18A, and is caused when software does not obey the VDP register bits marked as "reserved" in the datasheet (all reserved bits in VDP registers should be set to 0). 80-column mode also displays pixels at the VGA resolution in the X direction.

Link to comment
Share on other sites

Here is a version that can be loaded into VRAM anywhere. NOTE, the trigger addresses are now xx02 and xx04 (instead of xx06). Since the code is short, I replaced the branch instructions with jumps which are relative. For example, if you load to VRAM at address >2600, then triggering the GPU with >2602 is scroll left, and >2604 is scroll right.

 

0x03, 0x40,
0x10, 0x01,
0x10, 0x14,
0x04, 0xC1,
0xD0, 0x60,
0x60, 0x02,
0x0A, 0x21,
0xC8, 0x01,
0x80, 0x02,
0x05, 0x81,
0xC8, 0x01,
0x80, 0x00,
0x02, 0x01,
0x1F, 0x18,
0xC8, 0x01,
0x80, 0x04,
0x02, 0x01,
0x20, 0x00,
0xC8, 0x01,
0x80, 0x06,
0x07, 0x20,
0x80, 0x08,
0x10, 0xE9,
0x04, 0xC1,
0xD0, 0x60,
0x60, 0x02,
0x0A, 0x21,
0x02, 0x21,
0x02, 0xFF,
0xC8, 0x01,
0x80, 0x02,
0x06, 0x01,
0xC8, 0x01,
0x80, 0x00,
0x02, 0x01,
0x1F, 0x18,
0xC8, 0x01,
0x80, 0x04,
0x02, 0x01,
0x20, 0x02,
0xC8, 0x01,
0x80, 0x06,
0x07, 0x20,
0x80, 0x08,
0x10, 0xD3
The listing in case you want it:

 

 

 

Asm994a TMS99000 Assembler - v3.010

                * Asm994a Generated Register Equates
                *
      0000 0000 R0      EQU     0 
      0000 0001 R1      EQU     1 
      0000 0002 R2      EQU     2 
      0000 0003 R3      EQU     3 
      0000 0004 R4      EQU     4 
      0000 0005 R5      EQU     5 
      0000 0006 R6      EQU     6 
      0000 0007 R7      EQU     7 
      0000 0008 R8      EQU     8 
      0000 0009 R9      EQU     9 
      0000 000A R10     EQU     10
      0000 000B R11     EQU     11
      0000 000C R12     EQU     12
      0000 000D R13     EQU     13
      0000 000E R14     EQU     14
      0000 000F R15     EQU     15
                *
   1            **
   2            * F18A GPU code to use the DMA for left and right tile scrolling
   3            *
   4            
   5            * >6000 to >603F VDP regs
   6            *
   7            * The DMA src, dst, width, height, stride are copied to dedicated counters when
   8            * the DMA is triggered, thus the original values remain unchanged.
   9            
  10  0000 6002 NTBA        EQU >6002            ; Name table base address
  11  0000 8000 DMA_SRC     EQU >8000            ; DMA 16-bit src address, MSB first
  12  0000 8002 DMA_DST     EQU >8002            ; DMA 16-bit dst address, MSB first
  13  0000 8004 DMA_W       EQU >8004            ; DMA width
  14  0000 8005 DMA_H       EQU >8005            ; DMA height
  15  0000 8006 DMA_STRIDE  EQU >8006            ; DMA stride
  16  0000 8007 DMA_CMD     EQU >8007            ; DMA command: 0..5 | !INC/DEC | !COPY/FILL
  17  0000 8008 DMA_TRIG    EQU >8008            ; DMA trigger, write any value to address
  18            
  19                   AORG >3F00
  20            
  21            GPU_ROUTINES
  22  3F00 0340        IDLE
  23  3F02 1001        JMP  GPU_SCROLL_LT       ; >xx02 trigger vector
  24  3F04 1014        JMP  GPU_SCROLL_RT       ; >xx04 trigger vector
  25            
  26            
  27            **
  28            * Scroll the name table tiles left.
  29            *
  30            GPU_SCROLL_LT
  31            *      Get the NTBA and calculate the table's address.
  32  3F06 04C1        CLR  R1
  33  3F08 D060        MOVB @NTBA,R1
  33  3F0A 6002  
  34  3F0C 0A21        SLA  R1,2                ; Adjust NTBA bits to make the 14-bit VRAM address
  35            
  36            *      Set up the DMA.
  37  3F0E C801        MOV  R1,@DMA_DST         ; Start of the name table is the destination
  37  3F10 8002  
  38  3F12 0581        INC  R1                  ; The second tile address is the source
  39  3F14 C801        MOV  R1,@DMA_SRC
  39  3F16 8000  
  40            
  41  3F18 0201        LI   R1,31*256+24        ; Fancy way to put 31 (width) in the MSB of R1 and 24 (height) in the LSB
  41  3F1A 1F18  
  42  3F1C C801        MOV  R1,@DMA_W           ; Writes both Width and Height
  42  3F1E 8004  
  43            
  44  3F20 0201        LI   R1,32*256           ; Set the Stride to 32, set the command to INC & COPY
  44  3F22 2000  
  45  3F24 C801        MOV  R1,@DMA_STRIDE      ; Write both Stride and Command
  45  3F26 8006  
  46            
  47  3F28 0720        SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes
  47  3F2A 8008  
  48            
  49  3F2C 10E9        JMP  GPU_ROUTINES        ; Done, so return to idle
  50            *// GPU_SCROLL_LT
  51            
  52            
  53            **
  54            * Scroll the name table tiles right.
  55            *
  56            GPU_SCROLL_RT
  57            *      Get the NTBA and calculate the table's address.
  58  3F2E 04C1        CLR  R1
  59  3F30 D060        MOVB @NTBA,R1
  59  3F32 6002  
  60  3F34 0A21        SLA  R1,2                ; Adjust NTBA bits to make the 14-bit VRAM address
  61            
  62            *      Set up the DMA.
  63  3F36 0221        AI   R1,767              ; The bottom-right tile address is the destination
  63  3F38 02FF  
  64  3F3A C801        MOV  R1,@DMA_DST
  64  3F3C 8002  
  65  3F3E 0601        DEC  R1                  ; The destination-1 tile address is the source
  66  3F40 C801        MOV  R1,@DMA_SRC
  66  3F42 8000  
  67            
  68  3F44 0201        LI   R1,31*256+24        ; Fancy way to put 31 (width) in the MSB of R1 and 24 (height) in the LSB
  68  3F46 1F18  
  69  3F48 C801        MOV  R1,@DMA_W           ; Writes both Width and Height
  69  3F4A 8004  
  70            
  71  3F4C 0201        LI   R1,32*256+2         ; Set the Stride to 32, set the command to DEC & COPY
  71  3F4E 2002  
  72  3F50 C801        MOV  R1,@DMA_STRIDE      ; Write both Stride and Command
  72  3F52 8006  
  73            
  74  3F54 0720        SETO @DMA_TRIG           ; Trigger the DMA, about 16us for all 768 bytes
  74  3F56 8008  
  75            
  76  3F58 10D3        JMP  GPU_ROUTINES        ; Done, so return to idle
  77            *// GPU_SCROLL_RT
  78            
  79            GPU_ROUTINES_END
  80  3F5A 0000        END
  80            


 Assembly Complete - Errors: 0,  Warnings: 0


 ------ Symbol Listing ------

 DMA_CM ABS:8007 DMA_CMD
 DMA_DS ABS:8002 DMA_DST
 DMA_H  ABS:8005 DMA_H
 DMA_SR ABS:8000 DMA_SRC
 DMA_ST ABS:8006 DMA_STRIDE
 DMA_TR ABS:8008 DMA_TRIG
 DMA_W  ABS:8004 DMA_W
 GPU_RO ABS:3F00 GPU_ROUTINES
 GPU_RO ABS:3F5A GPU_ROUTINES_END
 GPU_SC ABS:3F06 GPU_SCROLL_LT
 GPU_SC ABS:3F2E GPU_SCROLL_RT
 NTBA   ABS:6002 NTBA
 R0     ABS:0000 R0
 R1     ABS:0001 R1
 R10    ABS:000A R10
 R11    ABS:000B R11
 R12    ABS:000C R12
 R13    ABS:000D R13
 R14    ABS:000E R14
 R15    ABS:000F R15
 R2     ABS:0002 R2
 R3     ABS:0003 R3
 R4     ABS:0004 R4
 R5     ABS:0005 R5
 R6     ABS:0006 R6
 R7     ABS:0007 R7
 R8     ABS:0008 R8
 R9     ABS:0009 R9

 

Link to comment
Share on other sites

You guys are both clearly more technical than me. So thanks for the help. I'll try this new version.

 

I was guessing. The turtle game probably used less memory so it could be that too. I'm going to put the music player (Which I love by the way) into mr. turtle to improve the music and f18a scrolling as well so I'll try that out later and see. I thought it might be the music routine as I don't know what's going on behind the curtain.

 

The standard colecovision was my testing platform before and an adam. So this may have been there for some time and I didn't know.

 

It could be as you say writing to a negative vdp value but all the screwups are gone on the regular colecovision but then again now there 64 vr's when using f18a rather than 8 so perhaps it was previosuly unnoticed.

 

I'm glad i'm aware of it and I'll make sure that it;s corrected.

 

I don't think it's my music player... it doesn't touch video RAM or video registers, and it works fine in my own game.

More than likely you still have a place that's hitting VDP RAM incorrectly. You can't get timing overruns with the F18A (So make sure you test on a standard ColecoVision before you release the game!), but if you wrap around and write to register space, some of the previously ignored bits have meaning now. Check your VDP address calculation code, for instance, and make SURE when you are moving things on the screen that the address never goes negative - a negative address turns into a register write. :)

Link to comment
Share on other sites

I know it's hard to troubleshoot when you have a lot of unknowns. I'm impressed that you are pushing on so strongly!

 

The music player uses a large amount of RAM (relatively - a little over 100 bytes) and can use a lot of CPU, depending on the music. But what it does is very simple. There are twelve compressed "streams" in the music, representing 4 channels each of note, volume, and timing. The RAM is used to track state of each stream as well as the output state of each channel, the song data is in ROM. Each time you call STPLAY (presumably on the vertical blank but there is no requirement on that, so long as you call it roughly once every 60th of a second), it counts down the timing stream for each channel. When the countdown expires, it gets the next timing byte which tells it whether to loads notes, volumes, or both, and processes those streams as needed. The updates are written to the sound chip. The unpacking works through a combination of run-length encoding and string encoding.

 

Although it has proved stable for me, it is possible for there to be bugs, of course. One likely candidate is that the string encoding can pull data from anywhere in a 64k space. You would hear this very clearly as audio corruption, but on the Coleco, it still can't touch the VDP. Because the VDP is accessed via an I/O port and not via memory mapping (like on the TI), CPU issues have a much harder time scrambling it.

 

You should be able to tell in a debugger like BlueMSX has whether Matt's theory about the GPU program getting corrupted is true -- when the corruption happens, just pause the emulator and open the debugger. View video memory at the address you loaded the GPU program to and see if the bytes are still correct. You might be able to do similar to help troubleshoot the 80-column glitch -- when it happens, breakpoint the emulator and look at the VDP registers. The values they contain might have some clues.

Link to comment
Share on other sites

Good idea,

 

I was trying different address and this particular upload of the vram is to the starting address 2800 , it was all zeros before. So it doesn't look right. using the below code.

 

void load_gpu_code()
{
byte i=0;
BLOCK_NMI;
for ( i = 0 ; i < sizeof(f18a_gpu_code) ; i++ )
fill_vram (0x2800+i,f18a_gpu_code,1);
RELEASE_NMI;
}//end gpu code

 

vram

//fill_vram
//NMI*
/* Fill VRAM area with specified value */
//void fill_vram (unsigned offset,byte value,unsigned count);
EDIT!! I think I got it. the data array was unsigned char so I switched it to const byte and now the vram looks right
const byte

I know it's hard to troubleshoot when you have a lot of unknowns. I'm impressed that you are pushing on so strongly!

The music player uses a large amount of RAM (relatively - a little over 100 bytes) and can use a lot of CPU, depending on the music. But what it does is very simple. There are twelve compressed "streams" in the music, representing 4 channels each of note, volume, and timing. The RAM is used to track state of each stream as well as the output state of each channel, the song data is in ROM. Each time you call STPLAY (presumably on the vertical blank but there is no requirement on that, so long as you call it roughly once every 60th of a second), it counts down the timing stream for each channel. When the countdown expires, it gets the next timing byte which tells it whether to loads notes, volumes, or both, and processes those streams as needed. The updates are written to the sound chip. The unpacking works through a combination of run-length encoding and string encoding.

Although it has proved stable for me, it is possible for there to be bugs, of course. One likely candidate is that the string encoding can pull data from anywhere in a 64k space. You would hear this very clearly as audio corruption, but on the Coleco, it still can't touch the VDP. Because the VDP is accessed via an I/O port and not via memory mapping (like on the TI), CPU issues have a much harder time scrambling it.

You should be able to tell in a debugger like BlueMSX has whether Matt's theory about the GPU program getting corrupted is true -- when the corruption happens, just pause the emulator and open the debugger. View video memory at the address you loaded the GPU program to and see if the bytes are still correct. You might be able to do similar to help troubleshoot the 80-column glitch -- when it happens, breakpoint the emulator and look at the VDP registers. The values they contain might have some clues.

  • Like 1
Link to comment
Share on other sites

That was it. The sdcc 3.6 changed something about unsigned char so I had to convert a bunch of stuff to using a regular byte a few months ago. I remembered and that was the trick. See below. Seems to be working now. Thanks for the help. viewing the memory in the debugger really helped. I always forget about that.

 

This is load to 3F00

 

https://www.youtube.com/watch?v=uDvGASmK0SE

  • Like 1
Link to comment
Share on other sites

Awesome. I'm glad you got it sorted. I'm curious about what the compiler is doing with "const byte" vs "unsigned char"? That seems like it would be a very frustrating quirk.

 

The video looks good, now you just need to fill in the left or right column with the new data after tile scrolling via the GPU. The GPU can do that too, if you store the new left/right column data in a known location in VRAM? Let me know, I can modify the routines easy enough to include that. But, I figured since you have to send the new column of data in your existing code, that part of your code would not have to change.

Link to comment
Share on other sites

I agree, that's weird unsigned char vs byte -- I will have to watch out for that in my own code. If I see it, we'll figure out what it is. ;) const definitely matters, though, without it the compiler will copy the structure to RAM and that will almost certainly overflow RAM. ;)

 

Very cool when you get a breakthrough like that!

Link to comment
Share on other sites

I could have just added it to my makefile and it would use the old method. wish I had of noticed that before.

 

* char type is now unsigned by default (old behaviour can be restored
using --fsigned-char)
* Character constants are now of type int instead of char.

 

http://atariage.com/forums/topic/253705-sdcc-360-released/?hl=%2Bsdcc&do=findComment&comment=3544300

 

Next I got to track down that 80 column mode problem & figure out how to load tiles into the tilelayer 2 flor blanking out the screen edges. Or perhaps some nice clouds

 

BTW here is the combined effect with the tile shift & the vr offset now:

 

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