digress Posted August 18, 2016 Share Posted August 18, 2016 I've been running into this video corruption from time to time. The entire screen at once. I've been moving all vdp access inside the nmi routine. Still hasn't improved though. I'm wondering does any of these statements actually access VRAM which are not inside the nmi_routine. In other words do I need to move them inside the nmi routine. sprites[1].x=100; sprites[1].y=100; sprites[1].colour=4; sprites[1].pattern=(25*4); or does only updatesprites(0,16); access vram which I already have inside the nmi routine. My understanding is they are just change data in an array which is only written to the vram once updatesprites happens so it shouldn;t matter. Thanks for any replies. Quote Link to comment Share on other sites More sharing options...
youki Posted August 18, 2016 Share Posted August 18, 2016 I don't think the Sprites[] instruction touch the VRAM , it is only when you do the updatesprites that the data are transfered. Are you sure you don't overrun the Nmi routine? I mean ,are you sure of what you do in the routine takes less than 1/60 of seconds ? You can check that by for instance changing the border color at start of the nmi and at end. Like that you will see how much time your routine consume on screen . Quote Link to comment Share on other sites More sharing options...
digress Posted August 18, 2016 Author Share Posted August 18, 2016 Yeah that is my understanding that sprites[1].x or anything shouldn't cause video corruption as it's not changing anything only updatesprites does. Ok, i'll try the colour method. But I was getting the same video corruption before I had anything in the nmi_routine. The only solution that has worked is to put in delay(1) several times but it slows down the game too much as I needed 3- delay(1) in the main loop to make it stable. I'd say that video corruption is the biggest problem in programming colecovision. I've solved it in the first 2 games I made but it was a similar hassle to get a balanced program that was stable. I don't think the Sprites[] instruction touch the VRAM , it is only when you do the updatesprites that the data are transfered. Are you sure you don't overrun the Nmi routine? I mean ,are you sure of what you do in the routine takes less than 1/60 of seconds ? You can check that by for instance changing the border color at start of the nmi and at end. Like that you will see how much time your routine consume on screen . Quote Link to comment Share on other sites More sharing options...
Kiwi Posted August 18, 2016 Share Posted August 18, 2016 (edited) I believe the main problem your main bank is overfilling, so I would check with an Hexediter to see how many FF is in a bank. EDIT: Or stack overflow. I just remembered from Knightmare. It is caused by doing too many things at once or program GOTO out of the sub routine. Having everything in nmi{} will overflow the stack if the tasks isn't going to be finish once the CPU run out of cycle. I don't have anything in nmi{} unless there's something that need to run while in pause(); like Star Ocean sprite animation. To prevent nmi corruption. while(gameloop==1){ delay(1); updatesprites(0,64);//(so it doesn't tear midpoint of the screen) video stuff like doing small video write to tiles. then sprite table stuff and game logic that can take CPU time without bugging the video chip. I did see the scene with there's a chest in one of your video, and there was part of the chest printed else where due to video corruption. It tells me that the chest been drawn every frame instead of on queue. During the scene drawing routine, you can put_frame(chestclose); or put_frame(chestopen); on that scene, and the turn on screen. Once the player open the chest, change chest from 0 to 1, call chest drawing routine to change the chest from close to open. Something like void DrawChest(void){ if (chest==0){delay(1);put_frame(chestclose,24,20,4,4); if (chest==1){delay(1);put_frame(chestopen,24,20,4,4); } delay(1) will makes sure that you're on the fresh frame and corruption shouldn't happen. Edited August 18, 2016 by Kiwi Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 18, 2016 Share Posted August 18, 2016 Kiwi's got the best suggestions there... but to control video corruption you just need to understand the NMI. That may require you to dig below the libraries and look at your CRT0 and the code it calls. The trick is to remember that at the end of a video frame, that NMI code will execute, no matter what else you were doing. In the middle of a memory copy? Too bad! Changing a video register? Not anymore! So it's important to be aware of what the possible race conditions in your code actually are. I took a different approach in libti99Coleco, since I was coming from a different mindset. I refuse to let the NMI own my code. It took a few iterations to get the race conditions out, but it does what I intended now... I can trivially enable and disable the interrupts in my code to ensure that anything I'm doing that might touch the VDP can't be interrupted. Note that I don't disable interrupts in the VDP itself -- that's also a racey operation since it takes two writes, but instead my NMI function doesn't touch the VDP unless it's allowed to by the flags set in the system. A normal NMI, besides the processing that you request, also clears the interrupt from the VDP by reading the VDP status register. Reading the VDP status register has the side effect of changing the internal VDP address. This is why, even if you don't do anything else, the NMI can corrupt your VDP access. I do away with that except when the program explicitly permits it. The net effect of this similar to changing the NMI to a maskable interrupt (by design, that was my goal). If the status register is NOT read (because the program forbid the NMI to do it), then the interrupt will not retrigger until it is. This means my program can delay handling the NMI during critical sections. (Of course, if it delays for longer than 1/60th of a second, this is still safe, but the program will slow down). To see what I did, have a look here. The concept is portable to other CRT0s. https://github.com/tursilion/libti99coleco/blob/master/crt0.s -- look at nmi: https://github.com/tursilion/libti99coleco/blob/master/vdp.h -- scroll down and look at VDP_INT_ENABLE and VDP_INT_DISABLE -- ENABLE is particularly important because it needs to check for missed interrupt. For stack overflow, you just have to watch what you're doing. In a debugger you can tell how roughly much stack you're using by checking the RAM that seems changed. Quote Link to comment Share on other sites More sharing options...
digress Posted August 18, 2016 Author Share Posted August 18, 2016 Hmmm. a little over my head. If I replace part of my crt0.s with your nmi code and get it working I would call the nmi or disable the nmi by: VDP_INT_ENABLE; VDP_INT_DISABLE; similar to: SWITCH_IN_BANK7; I would: VDP_INT_DISABLE; put_char(x,y,c); VDP_INT_ENABLE; <<--this doesn't actually call the nmi but would allow it to be called like normal. and for good measure here is my current crt0.s ; crt0.s for Colecovision cart ;; external routines ;;.globl set_snd_table ;; global from this code .globl _buffer32 ;;.globl snd_areas .globl _no_nmi .globl _vdp_status .globl _nmi_flag .globl _joypad_1 .globl _keypad_1 .globl _joypad_2 .globl _keypad_2 .globl spinner_enabled .globl _spinner_1 .globl _spinner_2 ;; global from C code .globl _main .globl _nmi .globl l__INITIALIZER .globl s__INITIALIZER .globl s__INITIALIZED ;; Ordering of segments for the linker - copied from sdcc crt0.s .area _HOME .area _CODE .ascii "LinkTag:Fixed\0" ; also to ensure there is data BEFORE the banking LinkTags .area _main ; to work around a bug that drops the first AREA: tag in the ihx .area _data_arrays_rom ; the data arrays for room data in tanks area .area _sound2 ;sound fx .area _INITIALIZER .area _GSINIT .area _GSFINAL ;; banking (must be located before the RAM sections) .area _bank1 .ascii "LinkTag:Bank1\0" .area _main2 ;more tanks routines .area _bank2 .ascii "LinkTag:Bank2\0" .area _main_init_bank2 ;inits for tanks .area _GRAPHICS_TABLES ;tanks tables .area _room_graphics ;room designs tanks / four_GOALS_004 tank castle picture .area _bank3 .ascii "LinkTag:Bank3\0" .area _cake ;four_GOALS_002 - escape prison picture ;four_GOALS_003 - the plane picture .area _titlescreen_bank3 .area _data_messages_rom .area _bank4 .ascii "LinkTag:Bank4\0" .area _storming_building_arrays .area _storming_building .area _bank5 .ascii "LinkTag:Bank5\0" .area _storming_water .area _storming_water_arrays .area _scroll_bg_array .area _bank6 .ascii "LinkTag:Bank6\0" .area _storming_init_bank6 ;storming inits & level drawing .area _storming_building_graphics ;storming graphics tables .area _bank7 .ascii "LinkTag:Bank7\0" .area _sound7 ;pMusic_7 pMusic_2 = 4 songs & dog fx .area _player ;the music player ;; end of list - needed for makemegacart. Must go before RAM areas. ; This isn't used by anything else and should not contain data .area _ENDOFMAP .area _DATA .area _INITIALIZED .area _BSEG .area _BSS .area _HEAP ;; TABLE OF VARIABLES (IN RAM) .area _DATA _buffer32:: .ds 32 ; buffer space 32 [7000-701F] snd_addr:: .ds 11 ; sound addresses [7020-702A] snd_areas:: .ds 61 ; 5 sound slots + NULL (00h) [702B-...] _no_nmi:: .ds 1 _vdp_status:: .ds 1 _nmi_flag:: .ds 1 _joypad_1:: .ds 1 _keypad_1:: .ds 1 _joypad_2:: .ds 1 _keypad_2:: .ds 1 spinner_enabled:: .ds 1 _spinner_1 = 0x73eb _spinner_2 = 0x73ec .module crt0 ;; CARTRIDGE HEADER (IN ROM) .area _HEADER(ABS) .org 0x8000 .db 0xaa, 0x55 ; no default colecovision title screen => 55 AA .dw 0 ; no copy of sprite table, etc. .dw 0 ; all unused .dw _buffer32 ; work buffer .dw 0 ; ?? .dw start_program ; start address for game coding .db 0xc9,0,0 ; no RST 08 support .db 0xc9,0,0 ; no RST 10 support .db 0xc9,0,0 ; no RST 18 support .db 0xc9,0,0 ; no RST 20 support .db 0xc9,0,0 ; no RST 28 support .db 0xc9,0,0 ; no RST 30 support jp spinner_int ; RST38 - spinner interrupt jp _nmi_asm ;; CODE STARTS HERE WITH NMI .area _CODE _nmi_asm: push af ld a,#1 ld (_nmi_flag),a ; set NMI flag ;;; call 0x1fdc ; get VDP status ld (_vdp_status),a ;;; ld a,(_no_nmi) ; check if nmi() should be or a ; called jp nz,nmi_exit inc a ld (_no_nmi),a push bc push de push hl push ix push iy ex af,af' push af exx push bc push de push hl call 0x1f76 ; update controllers ld a,(0x73ee) and #0x4f ld (_joypad_1),a ld a,(0x73ef) and #0x4f ld (_joypad_2),a ld a,(0x73f0) and #0x4f ld (_keypad_1),a ld a,(0x73f1) and #0x4f ld (_keypad_2),a call decode_controllers call _nmi ; call C function ;;call 0x1f61 ; play sounds ;;call 0x1ff4 ; update snd_addr with snd_areas pop hl pop de pop bc exx pop af ex af,af' pop iy pop ix pop hl pop de pop bc xor a ld (_no_nmi),a nmi_exit: ld a,(spinner_enabled) or a jr z,nmi_end ei nmi_end: pop af ret keypad_table:: .db 0xff,8,4,5,0xff,7,11,2,0xff,10,0,9,3,1,6,0xff ; joypads will be decoded as follows: ; bit ; 0 left ; 1 down ; 2 right ; 3 up ; 4 button 4 ; 5 button 3 ; 6 button 2 ; 7 button 1 ; keypads will hold key pressed (0-11), or 0xff decode_controllers: ld ix, #_joypad_1 call decode_controller inc ix inc ix decode_controller: ld a,0(ix) ld b,a and #0x40 rlca ld c,a ld a,b and #0x0f or c ld b,a ld a,1(ix) ld c,a and #0x40 or b ld 0(ix),a ld b,a ld a,c cpl and #0x0f cp #8 jr nz,no_button_3 ex af,af' ld a,b or #0x20 ld b,a ex af,af' no_button_3: cp #4 jr nz,no_button_4 ex af,af' ld a,b or #0x10 ld b,a ex af,af' no_button_4: ld 0(ix),b ;;;; ld a,c cpl and #0x0f ld e,a ld d,#0 ld hl,#keypad_table add hl,de ld a,(hl) ld 1(ix),a ret spinner_int: push af push hl call 0x1f88 pop hl pop af ei reti start_program: im 1 ; interrupt mode -> rst 38h di xor a ; clear carry ld bc,#0x3b8 ; ram size left ld hl,#0x7000 ; starting from 7000 ld de,#0x7001 ld (hl),a ldir ; zero-fill bss ld bc,#0xFFFE ; switch in code bank ld a,(bc) ; note that this does NOT set the local pBank call gsinit ; Initialize global variables. ld h,#0 ; set dummy sound table ; update snd_addr with snd_areascall set_snd_table ld hl,#0x0033 ; initialise random generator ld (0x73c8),hl ; set screen mode 2 text call 0x1f85 ; set default VDP regs 16K ld de,#0x4000 ; clear VRAM xor a ld l,a ld h,a call 0x1f82 ; call main rountine jp _main .area _GSINIT gsinit:: ld bc, #l__INITIALIZER ld a,b or a,c jr z, gsinit_next ld de, #s__INITIALIZED ld hl, #s__INITIALIZER ldir gsinit_next: .area _GSFINAL ret ; Kiwi's got the best suggestions there... but to control video corruption you just need to understand the NMI. That may require you to dig below the libraries and look at your CRT0 and the code it calls. The trick is to remember that at the end of a video frame, that NMI code will execute, no matter what else you were doing. In the middle of a memory copy? Too bad! Changing a video register? Not anymore! So it's important to be aware of what the possible race conditions in your code actually are. I took a different approach in libti99Coleco, since I was coming from a different mindset. I refuse to let the NMI own my code. It took a few iterations to get the race conditions out, but it does what I intended now... I can trivially enable and disable the interrupts in my code to ensure that anything I'm doing that might touch the VDP can't be interrupted. Note that I don't disable interrupts in the VDP itself -- that's also a racey operation since it takes two writes, but instead my NMI function doesn't touch the VDP unless it's allowed to by the flags set in the system. A normal NMI, besides the processing that you request, also clears the interrupt from the VDP by reading the VDP status register. Reading the VDP status register has the side effect of changing the internal VDP address. This is why, even if you don't do anything else, the NMI can corrupt your VDP access. I do away with that except when the program explicitly permits it. The net effect of this similar to changing the NMI to a maskable interrupt (by design, that was my goal). If the status register is NOT read (because the program forbid the NMI to do it), then the interrupt will not retrigger until it is. This means my program can delay handling the NMI during critical sections. (Of course, if it delays for longer than 1/60th of a second, this is still safe, but the program will slow down). To see what I did, have a look here. The concept is portable to other CRT0s. https://github.com/tursilion/libti99coleco/blob/master/crt0.s -- look at nmi: https://github.com/tursilion/libti99coleco/blob/master/vdp.h -- scroll down and look at VDP_INT_ENABLE and VDP_INT_DISABLE -- ENABLE is particularly important because it needs to check for missed interrupt. For stack overflow, you just have to watch what you're doing. In a debugger you can tell how roughly much stack you're using by checking the RAM that seems changed. Quote Link to comment Share on other sites More sharing options...
+nanochess Posted August 18, 2016 Share Posted August 18, 2016 You should move this: call 0x1fdc ; get VDP status ld (_vdp_status),a After the nmi_exit: label, this way: nmi_exit: call 0x1fdc ; get VDP status ld (_vdp_status),a This would ensure your internal code DOESN'T get interrupted by another NMI. And if you already moved all VDP code into the NMI it means you're trying to do too much screens updates, maybe you could consider doing some things in odd frames and some in even frames. Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 18, 2016 Share Posted August 18, 2016 That will certainly help -- the VDP can't start a "new" NMI until the status is cleared, so doing it right at the end guarantees your work is done first, even if you run long. If you're doing all your work in the NMI that will probably save you faster than restructuring your code for my approach. For what it's worth, in the TI community we tend to run with interrupts disabled /all the time/, and just briefly enable them at set times (such as during the main loop) to let interrupt code happen. It's just doing the same thing the other way in order to guarantee the code is more deterministic. You'll see in my library I even have a "VDP_INT_POLL" which just called enable/disable one after the other. If an interrupt is pending, that lets it happen, and if not, we'll check later. (In effect it turns it into a polling situation rather than an interrupt at all.) When you're worried about vertical blank, though, you need to make sure you respond quickly, so generally my code will execute one loop and then wait for the interrupt to do its job (this also frame-locks you nicely.) So.. yeah.. Nanochess for the win. That should fix your corruption issue as long as you aren't doing fast VDP writes - if you run over a frame it will just slow down. If you just replace that code with mine you'll lose controller and spinner reads. The other possible cause of corruption is doing fast writes to the VDP - this is not legal except immediately after the NMI. If you have any (hopefully your library calls them out), then make sure they are at the top of your NMI function and don't copy too many bytes, 1500-2000 bytes is about the top end since your NMI is doing other things before giving you control. Otherwise they tend to drop bytes. 1 Quote Link to comment Share on other sites More sharing options...
digress Posted August 18, 2016 Author Share Posted August 18, 2016 Thanks I Just tried moving this after the nmi_exit:. was a little more stable for longer but the corruption happens again after awhile. Trying a mixture of things including additional delays, splitting some updates to every second frame etc. I think I need to simplify what is going on on the screen. A liitle too much at times. I already had to cut back quite a bit. I originally had 10 soldiers & 10 cannons & a tank & a plane all shooting at the same time. You should move this: call 0x1fdc ; get VDP status ld (_vdp_status),a After the nmi_exit: label, this way: nmi_exit: call 0x1fdc ; get VDP status ld (_vdp_status),a This would ensure your internal code DOESN'T get interrupted by another NMI. And if you already moved all VDP code into the NMI it means you're trying to do too much screens updates, maybe you could consider doing some things in odd frames and some in even frames. That will certainly help -- the VDP can't start a "new" NMI until the status is cleared, so doing it right at the end guarantees your work is done first, even if you run long. If you're doing all your work in the NMI that will probably save you faster than restructuring your code for my approach. For what it's worth, in the TI community we tend to run with interrupts disabled /all the time/, and just briefly enable them at set times (such as during the main loop) to let interrupt code happen. It's just doing the same thing the other way in order to guarantee the code is more deterministic. You'll see in my library I even have a "VDP_INT_POLL" which just called enable/disable one after the other. If an interrupt is pending, that lets it happen, and if not, we'll check later. (In effect it turns it into a polling situation rather than an interrupt at all.) When you're worried about vertical blank, though, you need to make sure you respond quickly, so generally my code will execute one loop and then wait for the interrupt to do its job (this also frame-locks you nicely.) So.. yeah.. Nanochess for the win. That should fix your corruption issue as long as you aren't doing fast VDP writes - if you run over a frame it will just slow down. If you just replace that code with mine you'll lose controller and spinner reads. The other possible cause of corruption is doing fast writes to the VDP - this is not legal except immediately after the NMI. If you have any (hopefully your library calls them out), then make sure they are at the top of your NMI function and don't copy too many bytes, 1500-2000 bytes is about the top end since your NMI is doing other things before giving you control. Otherwise they tend to drop bytes. You should move this: call 0x1fdc ; get VDP status ld (_vdp_status),a After the nmi_exit: label, this way: nmi_exit: call 0x1fdc ; get VDP status ld (_vdp_status),a This would ensure your internal code DOESN'T get interrupted by another NMI. And if you already moved all VDP code into the NMI it means you're trying to do too much screens updates, maybe you could consider doing some things in odd frames and some in even frames. Quote Link to comment Share on other sites More sharing options...
+nanochess Posted August 18, 2016 Share Posted August 18, 2016 Corruption shouldn't happen now if you already moved all the display code inside the NMI routine, unless you read the VDP status inside your subroutines or call a subroutine that reads the VDP status. Simplify your code. 1 Quote Link to comment Share on other sites More sharing options...
digress Posted August 19, 2016 Author Share Posted August 19, 2016 I found another possible thing I overlooked. get_char(x,y,c) reads from the vram and wasn't inside the nmi. I'll round them up and see if that helps. Corruption shouldn't happen now if you already moved all the display code inside the NMI routine, unless you read the VDP status inside your subroutines or call a subroutine that reads the VDP status. Simplify your code. Quote Link to comment Share on other sites More sharing options...
Pixelboy Posted August 19, 2016 Share Posted August 19, 2016 Corruption shouldn't happen now if you already moved all the display code inside the NMI routine, unless you read the VDP status inside your subroutines or call a subroutine that reads the VDP status. Really? It's a bad idea to interact with the VDP this way from inside the NMI routine? Quote Link to comment Share on other sites More sharing options...
+nanochess Posted August 19, 2016 Share Posted August 19, 2016 Really? It's a bad idea to interact with the VDP this way from inside the NMI routine? Absolutely no. Mecha-9 works with all VDP updates inside the NMI routine, at most 288+128 = 416 bytes are handled in each frame. Most games can be simplified to this kind of screen update. The games which don't require constant screen update can make it outside the NMI routine using a kind of disable_nmi/enable_nmi routines (semaphore-style routines) or waiting for a screen update before doing any VDP usage. Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 19, 2016 Share Posted August 19, 2016 Right.. the thing you want to be careful of is as soon as you read the VDP status register, that unlocks the VDP and allows a new NMI to start -- it doesn't matter if you are still "in" the NMI code. It matters only that you acknowledged it to the VDP. It shouldn't be a problem to have lots of moving objects... it's only VDP access that can cause corruption. Perhaps you can move your game logic to after the VDP has been updated (to ensure the VDP access happens during the blank), and have the game itself work on CPU RAM-based variables instead. In my own stuff I double-buffer the sprite table that way, for instance. The NMI loads the sprite table but the CPU version is free to be accessed at any time. Quote Link to comment Share on other sites More sharing options...
youki Posted August 19, 2016 Share Posted August 19, 2016 Really? It's a bad idea to interact with the VDP this way from inside the NMI routine? You have just to take care of what kind of console you target.... That 's not a real issue for non european developper. But on european consoles you can do more between 2 NMI , and then if you try your game on a U.S console... there is chance you got lot of VDP Ram corruptions. In fact , that's frustrating for me... for Battle of Hoth and Smurf Challenge, i had to remove few effects from the games to make it work on 60hz consoles. Except that, it is the method the more safe i found to avoid VDP issues. Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 19, 2016 Share Posted August 19, 2016 At least you're lucky on the Coleco, once you have a ratio that works, it's likely to work on all systems. The TI-99 has separate clocks for the VDP and the 9900, so if you push it right to the edge on that machine variances in the timing will still cause problems on some people's computers. (The saving grace is it's very hard to push to the limit on that machine ). But seriously, there are only two things that cause VDP corruption: either the NMI interrupts a VDP operation in your code and corrupts the VDP state, or you read/write to the VDP too quickly during active screen display. (Besides actual bugs, as I found in my own code ). You can defeat the first by ensuring that NMIs can not happen while you are doing VDP access. This is called a race condition because you are racing the NMI - hoping that you finish what you're doing before it triggers. You can use a flag to keep the NMI handler off the VDP, or you can make sure your VDP access happens in the NMI before you clear the VDP status, so it can't retrigger. Code with the mentality that every single statement can be interrupted in the middle unless you have done something to prevent it. While VDP interrupts are the most blatant, if you have any other structures that are handled in both your NMI and your main code, remember to take care of those too. The second is just a hardware limitation - the Z80 is faster than the VDP, and if you access the VDP too quickly, it will lose data. This goes for both reads and writes, as well as turning around a read after setting an address. The VDP needs a random value up to 8 microseconds to access memory on behalf of the CPU (it's not really random, but from the perspective of the CPU it can't be predicted). If you read or write faster, the data simply doesn't transfer (ie: bytes are lost). However, this is only true when the VDP is drawing the screen, so during the vertical blank (ie: immediately after the NMI triggers), or if the screen is disabled, all VDP cycles are available for CPU access, and you can read or write at full speed. The easy solution to this is just always use the slower access method, always delay between accesses. This works fine, but sometimes you need the performance. In that case, you can use faster access methods immediately after the NMI fires, while the blank is still active. I don't know what the rough limit is on the Coleco, but you have to finish that fast copy before the VDP starts drawing again to avoid losing data. The tricky part to remember is you won't lose data all the time, it entirely depends on when in the VDP state machine your byte lands. Alternately, for large things like loading character sets or such, just blank the VDP screen and you can write at full speed for the entire frame. As for the PAL/NTSC issues... it's true that PAL has more time in the blank and in the frame itself. Assuming you saw corruption, and not just slowdown, you should be able to get a lot of data to the VDP even at 60hz... Make sure you're doing your VDP accesses first in the NMI and save calculations/sound until after the VDP is taken care of, where timing is less important. Some emulators like BlueMSX will display the scanline in the debugger - I'm not sure if it's dead on but it seems close enough to use as a check. Just breakpoint after your VDP work is done and see what scanline you're at. Or, again, just use the slower VDP copies. They will always work. I'll shut up and go away now. Quote Link to comment Share on other sites More sharing options...
digress Posted August 23, 2016 Author Share Posted August 23, 2016 No luck. My new theory is it is excessive bank switching with no delays captured these 2 separate crashes, one on a real adam & 1 on coolcv. I overlaid the screens and the character pattern on crash is identical! everytime. Like the video memory location got switched and always to the same new position. other than a black or green tank obscuring a couple of tiles the pattern is identical. Screen from real adam (above photo) overlaid on the same character screen from coolcv actual crash in coolcv Quote Link to comment Share on other sites More sharing options...
newcoleco Posted August 23, 2016 Share Posted August 23, 2016 This is just my guess based on my own experience. We all experience video corruption from time to time. This one reminds me of three cases I experienced long time ago in my projects : many times I just typed the parameters in the wrong order or typed the wrong data table name to be uploaded in VRAM, I also saw what updating VRAM in a bad timing can do and now I tend to use NMI for updating VRAM in batch, and I also saw corruption when uploading the sprites table in VRAM without the byte at the end saying where that table ends ($E0 aka 240) which normally is taking care of by using a library routine but I forgot that time this detail mentioned in the TMS 9918/9928 documentation. It's all good frustration to see a problem like this but so satisfying when it's finally fixed. Quote Link to comment Share on other sites More sharing options...
digress Posted August 23, 2016 Author Share Posted August 23, 2016 It happens it the middle of scene not during vram loading of the tiles or sprites. There is sprite patterns being changed but no new ones being loaded just changed to another already predefined pattern. I had a problem on my first project with sprite 48 for some reason. That always came in corrupted. I think it specifically happens when my tank is moving and changing sprite patterns. in fact if i cycle though the sprites in your ICVGM303 it'll crash when I get to sprite 48 often times. ?? I have no idea why. It's gremlin. I'm going to try a couple more things such as no sprite pattern changeing and see if it does any better. sure is frustrating. But like you said I'll be happy when it's solved and won't give up on it. This is just my guess based on my own experience. We all experience video corruption from time to time. This one reminds me of three cases I experienced long time ago in my projects : many times I just typed the parameters in the wrong order or typed the wrong data table name to be uploaded in VRAM, I also saw what updating VRAM in a bad timing can do and now I tend to use NMI for updating VRAM in batch, and I also saw corruption when uploading the sprites table in VRAM without the byte at the end saying where that table ends ($E0 aka 240) which normally is taking care of by using a library routine but I forgot that time this detail mentioned in the TMS 9918/9928 documentation. It's all good frustration to see a problem like this but so satisfying when it's finally fixed. Quote Link to comment Share on other sites More sharing options...
digress Posted August 23, 2016 Author Share Posted August 23, 2016 i'm impressed with myself... I managed to make it crash even worse. Quote Link to comment Share on other sites More sharing options...
Kiwi Posted August 23, 2016 Share Posted August 23, 2016 I know I mentioned before, you may want to check your ROM size using hexeditor to see if the data are being overlapped. I don't know if the linker will throw a warning or error if one of the bank is overfilled. Quote Link to comment Share on other sites More sharing options...
digress Posted August 23, 2016 Author Share Posted August 23, 2016 good point but i'm in the clear there. # SWITCH ROM_AD COL_AD FREE NAME = ====== ======= ====== ===== =============== 7 0xFFF8 0x00000 0xC000 3759 Bank7 6 0xFFF9 0x04000 0xC000 1857 Bank6 5 0xFFFA 0x08000 0xC000 482 Bank5 4 0xFFFB 0x0C000 0xC000 401 Bank4 3 0xFFFC 0x10000 0xC000 249 Bank3 2 0xFFFD 0x14000 0xC000 312 Bank2 1 0xFFFE 0x18000 0xC000 1681 Bank1 0 0xFFFF 0x1C000 0x8000 538 Fixed I know I mentioned before, you may want to check your ROM size using hexeditor to see if the data are being overlapped. I don't know if the linker will throw a warning or error if one of the bank is overfilled. Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 23, 2016 Share Posted August 23, 2016 That looks very much like a VDP register being altered... are you sure you are not trying at some point to write to a negative VDP address (either by accident or on purpose?) I did this in Super Space Acer, on purpose, naively thinking that the address register would be masked anyway, and forgetting that a negative VDP address would be interpreted as register writes. Quote Link to comment Share on other sites More sharing options...
newcoleco Posted August 23, 2016 Share Posted August 23, 2016 It happens it the middle of scene not during vram loading of the tiles or sprites. There is sprite patterns being changed but no new ones being loaded just changed to another already predefined pattern. I had a problem on my first project with sprite 48 for some reason. That always came in corrupted. I think it specifically happens when my tank is moving and changing sprite patterns. in fact if i cycle though the sprites in your ICVGM303 it'll crash when I get to sprite 48 often times. ?? I have no idea why. It's gremlin. I'm going to try a couple more things such as no sprite pattern changeing and see if it does any better. sure is frustrating. But like you said I'll be happy when it's solved and won't give up on it. I would be very happy to reproduce this error message with ICVGM on my computer. I've never seen this before. Quote Link to comment Share on other sites More sharing options...
Serguei2 Posted August 23, 2016 Share Posted August 23, 2016 I would be very happy to reproduce this error message with ICVGM on my computer. I've never seen this before. It could be the origin of the problem if ICVGM gives this message in the first place. 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.