digress Posted September 30, 2016 Share Posted September 30, 2016 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? Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 30, 2016 Share Posted September 30, 2016 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. Quote Link to comment Share on other sites More sharing options...
digress Posted September 30, 2016 Share Posted September 30, 2016 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. 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 30, 2016 Share Posted September 30, 2016 Whoa, that looks great. Sorry I mixed up the threads before. Quote Link to comment Share on other sites More sharing options...
digress Posted September 30, 2016 Share Posted September 30, 2016 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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted October 1, 2016 Share Posted October 1, 2016 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. Quote Link to comment Share on other sites More sharing options...
digress Posted October 1, 2016 Share Posted October 1, 2016 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. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 2, 2016 Author Share Posted October 2, 2016 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. Quote Link to comment Share on other sites More sharing options...
digress Posted October 2, 2016 Share Posted October 2, 2016 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. Quote Link to comment Share on other sites More sharing options...
TheMole Posted October 2, 2016 Share Posted October 2, 2016 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. You can also use gcc to create the F18A GPU program, with a bit if finicking around. Quote Link to comment Share on other sites More sharing options...
digress Posted October 2, 2016 Share Posted October 2, 2016 is this what you are using in your alex the kidd program? To adjust for the shifting of the scroll and still be able to land on the platforms. You can also use gcc to create the F18A GPU program, with a bit if finicking around. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 2, 2016 Author Share Posted October 2, 2016 (edited) 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. 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. Edited October 2, 2016 by matthew180 Quote Link to comment Share on other sites More sharing options...
digress Posted October 3, 2016 Share Posted October 3, 2016 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. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 3, 2016 Author Share Posted October 3, 2016 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. Quote Link to comment Share on other sites More sharing options...
digress Posted October 3, 2016 Share Posted October 3, 2016 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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted October 4, 2016 Share Posted October 4, 2016 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. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 4, 2016 Author Share Posted October 4, 2016 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. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 4, 2016 Author Share Posted October 4, 2016 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 Quote Link to comment Share on other sites More sharing options...
digress Posted October 4, 2016 Share Posted October 4, 2016 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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted October 4, 2016 Share Posted October 4, 2016 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. Quote Link to comment Share on other sites More sharing options...
digress Posted October 4, 2016 Share Posted October 4, 2016 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 //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. 1 Quote Link to comment Share on other sites More sharing options...
digress Posted October 4, 2016 Share Posted October 4, 2016 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 1 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 4, 2016 Author Share Posted October 4, 2016 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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted October 4, 2016 Share Posted October 4, 2016 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! Quote Link to comment Share on other sites More sharing options...
digress Posted October 4, 2016 Share Posted October 4, 2016 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 restoredusing --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: 7 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.