Sergioz82 Posted October 15, 2022 Author Share Posted October 15, 2022 1 hour ago, FarmerPotato said: I see, I had never imagined the situation where you want the console to do interrupt handling , but also wait on VDPSTA yourself. VDP is not the only interrupt source which the console has to handle. Therefore it tries to identify the source. Your loop read VDPSTA (clears the interrupt so TB 2 is clear) so the console thinks it can’t be VDP , and doesn’t advance the sound list. If you leave VDPSTA alone, the console routine will act. I think you could do this: LIMI 0 … everything else * wait for VSYNC: TB 2 JNE $-2 really tight loop * VDP interrupt has now arrived * leave VDPSTA alone! * do your screen updates LIMI 2 now console handles the interrupt My preference is to leave interrupts off mostly. You never know when you might get smacked otherwise. For instance, if you were updating 83CC and 83CE you want to eliminate the risk of interrupt happening in between. (seems harmless though) In the end I applied @Asmusr solution. It didn't work at first because I still had LIMI 0 and LIMI 2 in my VDP read/write routines plus an unwanted call of WVDP. Now both sounds and graphics are ok and I can move on Honestly I'm not sure I understood it 100% though: if I call WVDP at the beginning of the game loop (I update vdp attribute list in the last BL instruction inside the loop) the graphics glitch, instead if i call it just before updating the AL the glitching is gone. I don't get it: since it waits synchronization I expected it to work almost anywhere, especially after the VDP AL update, not before. This said thank you FarmerPotato and thanks to everyone. One last question: I wanted to celebrate the achievement so I decided to create a bin file for the FinalGrom and play the game on the real TI but.. where do I find the save utility (and in general the E/A disks image) to create the E/A 5 file? I looked here and on the internet but with no luck 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted October 15, 2022 Share Posted October 15, 2022 (edited) It's called save on the original disks I believe. If you don't have the disks it's probably on the ftp whtech site (I forget the full site name..so sorry). Edited October 15, 2022 by GDMike 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted October 15, 2022 Author Share Posted October 15, 2022 2 hours ago, GDMike said: It's called save on the original disks I believe. If you don't have the disks it's probably on the ftp whtech site (I forget the full site name..so sorry). Found it! ftp://ftp.whtech.com/Diskettes/Cartridge_Disks/Editor_Assembler/ But of course I have new problems: Spac Man E/A 3 object file uncompressed is 24Kb. I followed instructions, added SFIRST,SLOAD and SLAST. I don't get any error but I end up with a single 7kb E/A 5 file which of course doesn't work 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted October 15, 2022 Share Posted October 15, 2022 You didn't forget the start name did you? Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted October 15, 2022 Author Share Posted October 15, 2022 19 minutes ago, GDMike said: You didn't forget the start name did you? You mean the program name as in DEF? If so I actually removed it, I thought SFIRST had to replace it. First the source was DEF SPACM REF KSCAN,VWTR SPACM MYWSP BSS 32 LWPI MYWSP ... Now it's DEF SFIRST, SLOAD, SLAST REF KSCAN, VWTR SFIRST SLOAD LWPI MYWSP *moved MYWSP BSS 32 elsewhere .... SLAST END 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted October 15, 2022 Share Posted October 15, 2022 (edited) It's been a long time since I've done anything with that.. but I think you're close, someone will chime in soon and help you. I thought we are to leave the start name alone. Anyway, it's documented early in the EA book and I'm at work doing something else... but someone will chime in. Edited October 15, 2022 by GDMike 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 16, 2022 Share Posted October 16, 2022 2 hours ago, Sergioz82 said: But of course I have new problems: Spac Man E/A 3 object file uncompressed is 24Kb. I followed instructions, added SFIRST,SLOAD and SLAST. I don't get any error but I end up with a single 7kb E/A 5 file which of course doesn't work Not that this information will solve your problem, but the object file is not a memory image as is the case with the E/A5 file. Examining your listing should tell you how big the memory image file(s) will be. If you do not have multiple AORG directives, it should be a simple matter of the difference between the last and first memory addresses. If it is all relocatable, the last address will be that number. As an example, one of my programs has an uncompressed object file size of 1555 bytes. Its memory image is 368 bytes. Your numbers are not that different, so I would not consider your size difference to indicate a problem. ...lee 1 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 16, 2022 Share Posted October 16, 2022 3 hours ago, Sergioz82 said: But of course I have new problems: Spac Man E/A 3 object file uncompressed is 24Kb. I followed instructions, added SFIRST,SLOAD and SLAST. I don't get any error but I end up with a single 7kb E/A 5 file which of course doesn't work When you say that it doesn’t work, you are talking about running it with E/A Option 5, “RUN PROGRAM FILE”, right? ...lee 2 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 16, 2022 Share Posted October 16, 2022 13 hours ago, Sergioz82 said: At the moment I need to understand how basic sound generation works, that is, why mine does not and what I'm doing wrong.. then I can explore better solutions like yours. By "understand how basic sound generation works", do you mean understanding how to use the console ROM sound-list player routine, or do you mean actually learning how to interact with the 99/4A's sound chip? Most of the time, just deciding to not use the console's ISR can make things much simpler for game programming, and you can use all of the scratchpad RAM as you see fit. The link I posted covers some of the basics of the console ISR as it relates to sound. Also, there is a large Assembly Language thread here on the forum that covers the ISR in detail, as well as good ways to do game-loops on the 99/4A. However, generally, there are three ways to do assembly game loops on the 99/4A: 1. Easy: Disable interrupts and check for the VDP interrupt yourself in your main game-loop. All your VDP writing is safe since no other code will be writing to the VDP. You can use all of scratchpad as you want. You can use any kind of sound player / routines you want. Your game code can easily be decoupled from the VDP interrupt (60Hz). 2. Medium: Use the console ISR by allowing system interrupts (LIMI 2 / LIMI 0) in your game-loop. You need to know what the ISR is doing, you need to follow rules for using scratchpad RAM, you must reset any expected setting of the VDP's address register. You can use the console sound-list player, but you have to follow its rules and your sound data must be in VRAM or GROM. You can use sprite auto-motion, but this is usually *not* desirable in assembly games (you are generally already controlling sprite motion yourself). IMO, allowing the console ISR to run is a PITA that is more headache that it is worth. 3. Hard: Mixing the console ISR, to use some of its capabilities, with your own VDP interrupt checking or sound playing, etc. This requires total knowledge of the console ISR, what it does, disabling certain components of it. There are examples of this with all the console ISR routines disabled to the extent that they can be disabled, and it can be useful to use the VDP interrupt rather than polling the hell out of the VDP. As much as I dislike the console ISR, IIRC this is the method I think I settled on for games, however my own ISR consists of incrementing a counter that the game-loop checks for change. 4. Harder: Polling for the VDP interrupt via the 9901. This method polls the 9901 rather than the 9918A, since the VDP interrupt goes to the 9901, you can still detect the interrupt at the 9901 directly (with LIMI 0 set in the CPU). The basic reason is to not poll the 9918A for interrupts, since it has been shown that the 9918A does not deal with the internal hazard of the status register being read at the same time the frame-flag is being set. 3 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted October 16, 2022 Share Posted October 16, 2022 I am not sure if anyone has done what I did but it seems to work well. I made a sub-routine for setting the VDP address, with one entry for write mode and another entry for read mode. When that sub-routine is called interrupts are disabled. Each VDP routine that calls the address setter code, ends by enabling interrupts. Not sure if that is too inefficient for games but it means that I can run anything including automotion sprites interactively from the console which is great for testing. Example code in RPN Assembler Spoiler \ VDP set-address sub-routines L: WMODE ( Vaddr -- Vaddr) TOS 4000 ORI, L: RMODE R1 STWP, \ avoids 2 SWPB instructions R3 8C02 LI, 0 LIMI, 9 (R1) *R3 MOVB, \ write odd byte from TOS (ie: R4) TOS *R3 MOV, \ MOV writes the even byte to the port address RT, L: _VMBR R3 VDPRD LI, \ put VDP read port address in R3. 12% faster BEGIN, *R3 *R2+ MOVB, \ READ from VDP RAM into CPU RAM , autoINC R0 DEC, EQ UNTIL, 2 LIMI, RT, L: _VMBW R3 VDPWD LI, \ vdp addr. in a reg. makes this 12.9% faster BEGIN, *R2+ *R3 MOVB, \ write byte to vdp write port R0 DEC, EQ UNTIL, 2 LIMI, RT, 3 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 16, 2022 Share Posted October 16, 2022 @TheBF You are guarding every VDP call, which can introduce a lot of overhead depending on what you are doing. Game would typically want to avoid this. You also lose the advantage of being able to set the VDP address register and take advantage of its auto-increment feature between your VDP calls. 1 hour ago, TheBF said: Each VDP routine that calls the address setter code, ends by enabling interrupts. If you run with interrupts usually enabled (LIMI 2), then *every* VDP routine would need to call the address-setter code. Setting the address is pretty low-overhead code, but calling a subroutine - not so much. Performance wise you would be better off in-lining the VDP address-setting in each VDP routine. That is also a lot of LIMI 2, LIMI 0 scattered around, which would make debugging much harder. 2 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted October 16, 2022 Share Posted October 16, 2022 5 hours ago, matthew180 said: 4. Harder: Polling for the VDP interrupt via the 9901. This method polls the 9901 rather than the 9918A, since the VDP interrupt goes to the 9901, you can still detect the interrupt at the 9901 directly (with LIMI 0 set in the CPU). The basic reason is to not poll the 9918A for interrupts, since it has been shown that the 9918A does not deal with the internal hazard of the status register being read at the same time the frame-flag is being set. I'm not sure why you rank this 'harder', because to me it's just as easy as option 1 with only a few instructions changed when polling for the interrupt? Otherwise I agree completely, and I never use the ISR myself. I only suggested that Sergioz82 could use the ISR sound player because he already mentioned enabling and disabling interrupts. I didn't even remember that the sound list had to be stored in GROM or VDP RAM. 2 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 16, 2022 Share Posted October 16, 2022 21 minutes ago, Asmusr said: I'm not sure why you rank this 'harder'... Since programming is just pushing keys, there is really nothing hard about any of it. I should have said "difficult" (rocks are hard, tasks are difficult). The reason for putting that option as #4 is because it requires more in-depth knowledge of the 99/4A and the 9901, which could be considered more difficult to learn all those details and correctly apply them in an assembly language program. For you, certainly not difficult. For someone new to the 99/4A and/or 9900 assembly, probably really confusing (which makes it difficult). 26 minutes ago, Asmusr said: I only suggested that Sergioz82 could use the ISR sound player because I was not trying to question or contradict your, or anyone else's, suggestions. I was not following as closely as I probably should have before posting, so my apologizes if it came across like that. 29 minutes ago, Asmusr said: I didn't even remember that the sound list had to be stored in GROM or VDP RAM. I didn't remember either, until I re-read my post to Owen. Good thing we write this stuff down, and the forum search works as well as it does! 2 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted October 16, 2022 Author Share Posted October 16, 2022 7 hours ago, Lee Stewart said: Not that this information will solve your problem, but the object file is not a memory image as is the case with the E/A5 file. Examining your listing should tell you how big the memory image file(s) will be. If you do not have multiple AORG directives, it should be a simple matter of the difference between the last and first memory addresses. If it is all relocatable, the last address will be that number. As an example, one of my programs has an uncompressed object file size of 1555 bytes. Its memory image is 368 bytes. Your numbers are not that different, so I would not consider your size difference to indicate a problem. ...lee I don't use AORG. I haven't got memory allocation peculiarities so I let E/A choose. Now I wonder, is it a good practice to always specify AORG? Using the debugger the first byte is at A000 and the last at B899, that makes >1899 bytes or approximately 6Kb. Loading the E/A 5 file it fills exactly the same locations so ok, that's one problem less. I didn't think the E/A 5 would reduce so much the size since the code E/A 3 compressed is 11Kb and I expected something around that size. Your explanation let me make a leap forward Answering to your other post, yes, I'm talking about E/A option 5. If I select option 5 after option 3 + save utility the game runs. If I reset the emulator and I select option 5 I see a flash then it goes back to E/A blue screen with "Press enter to continue" and nothing happens. I read in another thread that this might have something to do with unitialized VDP values but I have to deepen this. @matthew180 I mean how to interact with PSG: how to write sound lists, how to write a player, when to silence PSG, when and where call the player.. in general how to make a game sound good without glitches or slow downs. I prefer to learn the basis and their downsides then move to better solutions, otherwise I wouldn't understand them very much. Now I'm using solution 2. It's the first one I learned fromCompute!'s Beginners Guide to Assembly Language. The second one I learned is from Munch man code: interrupts are always on. They're disabled only inside VDP access routines and then re-enabled, something like LIMI 0 - read/write VDP code - LIMI 2 - Return. Sound routine forces interrupts for what I understood: it's only a LIMI 2 instruction. I tried both in my code and in the end I used Compute!'s solution. 2 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted October 16, 2022 Share Posted October 16, 2022 (edited) My Short answer: use my program SUPERSAVE not SAVE. Find SUPER.BIN here: One thing about the EA SAVE utility: it doesn’t give your program the EA support routines. Any REFs that you expected were provided like KSCAN and VMBW will point to empty memory at runtime. With SAVE, Your PROGRAM file must be completely self-sufficient. You even need to define the character set, besides setting the registers with VWTR. EA might have left things set up for you but it’s pure chance. One way around that is you include the utilities as source! That is, rewrite them. (SuperSave does that for you when you want to make a PRIGRAM file. ) LOAD AND RUN would put the utilities in low memory for you (>2000 or so.) RUN PRIGRAM FILE only loads whatever SAVE wrote out: your relocatable code from A000. The stuff in >2000 is lost. The result at run time is unpredictable when your program jumps there with BLWP @KSCAN: it might go straight to the “press enter” thing you are seeing. I wrote the utility SuperSave to cure this problem once and for all. It replaces LOAD AND RUN. It asks if you want the EA support utilities linked in, and puts them at A000 before loading your object files. With SuperSave, you do not need to DEF SFIRST/SLAST/SLOAD. The loader tracks all the relocatable memory used so far, and writes that to the PROGRAM file(s) (always 8K segments—you might get 2 or 3) SuperSave doesn’t work on anything that loads code with AORG. My motivation was to convert other people’s object code to program files, so I could save disk space. One more thing: SuperSave has the genuine TI source of the E/A support routines. It walked out the door of TI Lubbock one day when an employee was bored and expected to be laid off. I wish more source had walked out the door! (You can see their KSCAN source for instance.) Edited October 16, 2022 by FarmerPotato 4 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 16, 2022 Share Posted October 16, 2022 15 minutes ago, FarmerPotato said: One thing about the EA SAVE utility: it doesn’t give your program the EA support routines. Any REFs that you expected were provided like KSCAN and VMBW will point to empty memory at runtime. With SAVE, Your PROGRAM file must be completely self-sufficient. You even need to define the character set, besides setting the registers with VWTR. EA might have left things set up for you but it’s pure chance. One way around that is you include the utilities as source! That is, rewrite them. Wow! I totally forgot about that—probably because I was really never interested in making E/A5 programs from SAVE. I always wrote my own binary load routines. That stems from the fact that TI Forth loads the system binaries from one program file and I used that system in fbForth 1.0 before developing fbForth 2.+. ...lee 3 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 16, 2022 Share Posted October 16, 2022 7 hours ago, Sergioz82 said: I mean how to interact with PSG: how to write sound lists, how to write a player, when to silence PSG, when and where call the player.. in general how to make a game sound good without glitches or slow downs. That being the case, then understanding that sound generation is not necessarily coupled to the console's interrupts (but why it is for the console sound list player), would be an important concept to lock-in. The PSG (SN76489) in the 99/4A, like many PSGs of the era, uses built-in oscillators (3 in this case, plus 1 noise LFSR) that once set, will continuously output the specified tone at the set volume. If you want to make music or sound effects, you need to vary the tone and/or volume (of the oscillators and noise) over time. The human ear is very sensitive to audio, so changing the tone and volume of a channel on a regular periodic basis is key to good sound. A typical practice on retro-computers is to use the VSYNC signal from the display system as a periodic timer to update tone and volume values in the PSG. With this technique the smallest duration for a tone is going to be about 16.6ms (or 20ms for PAL), which is usually sufficient for most music and sound effects. Using VSYNC as an easy (and sometimes only) way to get a periodic timer, if you want a tone duration to last longer than a single video frame, then you have to keep a counter of how many frames to hold the current tone before moving on to the next tone. Usually the duration is counted down for each frame. For example, a starting duration count of 60 would cause a 1-second tone (60 * 16.6ms = 1 second). Complex music and sound effects are made up of one or more varying tones and noise, duration, and volume. If you make a simple array of tone and duration values, decrement the current duration every VSYNC, then change to the next tone/duration when the current duration count reaches zero, you have the basis of the console's sound-list player. The only reason you have to enable interrupts to use the console sound-list player is because the sound-list routine is called from the console ISR (which is triggered by the 9918A's interrupt output, which happens once per frame). However, rolling your own player is not difficult, and you can remove some of the limitations and side-effects of the console's sound-list player. I too learned from the Lottrup book back in the day, and I have a love-hate relationship with the book. It absolutely blew open assembly language for me and let me use assembly for any programs I wanted to write. It was awesome and life was great. My only other resource had been the E/A manual, which as we all know is *not* a text for beginners. However, the Lottrup book always left me wondering "how does that work?" and "where do these numbers come from?", etc.. It also taught me a lot of bad habits, and a lot of misunderstanding, that took a while to undo. In Lottrup's book the reader is never shown how to work with the VDP (9918A) or PSG directly, instead the console routines are always used. This is unfortunate because sometimes the routines are more complicated than just using the chips directly, and does not allow the reader to understand the relationship between the routines and the hardware. Likewise, the reader does not learn what the console routines are, or are not, doing for them. I didn't really know the chips inside the computer (other than the 9900), or have access to the datasheets like we do today (I probably did not even know what a datasheet was, let alone how to get them). It was not until years later when I started messing with the 99/4A again that I came to realize what was really going on, and I had a lot of "ah ha!" moments in later years. For retro computers, IMO, learning from the bottom-up is better, i.e. learn the chips first, before any ROM-based abstractions. The retro computers are not that complicated, and represent an era of computing where one person can hold in their mind, and understand, the entire system. I suspect that is why many of us still mess with them; they have a kind of enjoyment that is completely lost with modern computers. 7 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted October 18, 2022 Author Share Posted October 18, 2022 @FarmerPotato I used your SuperSave and it worked perfecty! Thank you very much! One question: I have my own VDP read/write routines but I use KSCAN. In SuperSave I can only choose to include both so could this be a conflict or since I don't REF them nothing happens? BTW I also implemented your previous trick to use equations and labels to let E/A calculate the lenght of the sound list. I re-used it to calculate the address of each sound in VDP: Sound list definition: SNDLST BYTE xx .... BYTE yy EATEND BYTE.. ... NXTEND ... SNEAT EQU >1300 SNNXT EQU >1300+EATEND-SNDLST It saved my a lot of time and stress especially now that I'm adding and testing different sounds @matthew180 I printed your comment and added it to my physical copy of Compute!'s manual. Thanks a lot for the explanation. 3 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 18, 2022 Share Posted October 18, 2022 (edited) 13 hours ago, Sergioz82 said: One question: I have my own VDP read/write routines but I use KSCAN. In SuperSave I can only choose to include both so could this be a conflict or since I don't REF them nothing happens? The following code is all there is to the E/A KSCAN utility—just include it in your program: UTILWS BSS 32 ; Utility workspace KSCAN DATA UTILWS,MYKSCN ; KSCAN vector * Copy of E/A KSCAN routine (must be called with BLWP @KSCAN) MYKSCN LWPI >83E0 ; switch to GPL workspace MOV R11,@UTILWS+22 ; save GPL return to old R11 BL @>000E ; branch to keyboard scanner in console ROM LWPI UTILWS ; back to utility workspace MOV R11,@>83F6 ; restore GPL R11 RTWP The above version requires calling it with BLWP @KSCAN You can use an existing workspace for UTILWS as long as you can afford changes to its R11, R13, R14, R15. If you prefer a version with BL @KSCAN, the following will do it: GPLRET BSS 2 ; storage for GPL return * Modified copy of E/A KSCAN routine (must be called with BL @KSCAN) KSCAN LWPI >83E0 ; switch to GPL workspace MOV R11,@GPLRET ; save GPL return BL @>000E ; branch to keyboard scanner in console ROM LWPI MYWSP ; back to our workspace MOV @GPLRET,@>83F6 ; restore GPL return RT ...lee Edited October 19, 2022 by Lee Stewart Added BL version + correction & clarification 2 2 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 18, 2022 Share Posted October 18, 2022 Has Thierry's page already been mentioned? The TI Tech Pages cover a lot of detail on the keyboard and the console routines: https://unige.ch/medecine/nouspikel/ti99/titechpages.htm See the Keyboard and 9901 sections. 3 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted October 19, 2022 Author Share Posted October 19, 2022 On 10/18/2022 at 4:59 PM, Lee Stewart said: The following code is all there is to the E/A KSCAN utility—just include it in your program: UTILWS BSS 32 ; Utility workspace KSCAN DATA UTILWS,MYKSCN ; KSCAN vector * Copy of E/A KSCAN routine (must be called with BLWP @KSCAN) MYKSCN LWPI >83E0 ; switch to GPL workspace MOV R11,@UTILWS+22 ; save GPL return to old R11 BL @>000E ; branch to keyboard scanner in console ROM LWPI UTILWS ; back to utility workspace MOV R11,@>83F6 ; restore GPL R11 RTWP The above version requires calling it with BLWP @KSCAN You can use an existing workspace for UTILWS as long as you can afford changes to its R11, R13, R14, R15. If you prefer a version with BL @KSCAN, the following will do it: GPLRET BSS 2 ; storage for GPL return * Modified copy of E/A KSCAN routine (must be called with BL @KSCAN) KSCAN LWPI >83E0 ; switch to GPL workspace MOV R11,@GPLRET ; save GPL return BL @>000E ; branch to keyboard scanner in console ROM LWPI MYWSP ; back to our workspace MOV @GPLRET,@>83F6 ; restore GPL return RT ...lee Again, thank you very much! I replaced my keyscan REF with your routine (BLWP version). Since I was doing this I also removed the last REF to VWTR and replaced it with a custom routine by @matthew180 I found in another thread so now I can run SUPERSAVE without utilities. I'm confident I can soon post you some updates of the game with sounds and more mazes. 4 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.