+Vorticon Posted December 17, 2015 Author Share Posted December 17, 2015 You can do what @Tursi suggested by using TF's SOUND for each channel and devising a VDP-interrupt counting mechanism for the duration times. ...lee Yes that may actually work quite well since duration is not specified as a parameter and is left to the programmer to do it manually. There are also supplementary sound words, namely Volume, Tone and Noise which provide an even finer level of control. Definitely worth a look as there is no point in reinventing the wheel here The question will be how fast will these execute... Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted December 17, 2015 Share Posted December 17, 2015 Considering some of the regularity of the sounds we are creating, and the fact that the sounds generated by the Spectrum have tones with a higher resolution than 1/60s, I postulated on the idea of a custom sound routine which would just throw the requisite data at the 9919. It looks like resolution used for Jetpac on the Spectrum is somewhere between 1/90s and 1/120s. As a result the sounds from the game are more complex than can be easily recreated in an interrupt-driven player. What can TF do toward this end? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 17, 2015 Share Posted December 17, 2015 Considering some of the regularity of the sounds we are creating, and the fact that the sounds generated by the Spectrum have tones with a higher resolution than 1/60s, I postulated on the idea of a custom sound routine which would just throw the requisite data at the 9919. It looks like resolution used for Jetpac on the Spectrum is somewhere between 1/90s and 1/120s. As a result the sounds from the game are more complex than can be easily recreated in an interrupt-driven player. What can TF do toward this end? According to the source code, TF services sound every other frame (1/30 sec). I couldn't figure out how it does it, however. @Willsy will shed some light on it ere long. ...lee Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 17, 2015 Share Posted December 17, 2015 Considering some of the regularity of the sounds we are creating, and the fact that the sounds generated by the Spectrum have tones with a higher resolution than 1/60s, I postulated on the idea of a custom sound routine which would just throw the requisite data at the 9919. It looks like resolution used for Jetpac on the Spectrum is somewhere between 1/90s and 1/120s. As a result the sounds from the game are more complex than can be easily recreated in an interrupt-driven player. What can TF do toward this end? It's a bit of an un-known quantity really. I decided to bite the bullet and write my own sound list processor/driver. Here it is: asm: soundListPlayer ( -- ) \ re-entrant. Uses r0 to remember state: \ 0 = doing nothing. Waiting for a sound list to be loaded \ 1 = loading sound bytes to sound chip \ 2 = counting off duration counter $2000 lwpi, r0 r0 mov, \ have a sound list to process? ne if, r0 1 ci, \ loading sound bytes? eq if, \ yes r1 *+ r2 movb, \ get number of bytes to send to sound chip r2 8 srl, \ move to low byte eq if, r0 clr, \ if 0 then we're finished else, begin, r1 *+ $8400 @@ movb, \ send a byte to the sound chip r2 dec, \ decrement counter eq until, \ loop until finished r1 *+ r3 movb, \ get duration byte r3 8 srl, \ move to low byte eq if, \ processed everything? r0 clr, \ reset state to 0 (idle) else, r0 2 li, \ set state flag to counting endif, endif, else, \ we're counting off duration r3 dec, \ decrement duration` eq if, \ hit zero? r0 1 li, \ set state to load sound bytes endif, endif, endif, $83e0 lwpi, \ restore GPL's workspace r11 ** b, \ return via R11 ;asm It occupies a whopping ( ) 86 bytes. How cool is that. It plays regular sound lists in the TI format. 'Yall probably don't know, but TF has built into it a user ISR hook. When an interrupt fires, it first checks to see if there's any data to pipe out to the speech synth. After that, it checks CPU ram location >A008 for an address. If there is an address in there (i.e. it's non-zero) it branches to the code at that address, hence you can run your own machine code on the VDP interrupt. So, the little sound driver above is pretty cool. It's re-entrant, and stateful. It manages it's own state. You can analyse the code to see how it works, but very briefly, the driver has 3 states: 0 - idle - nothing happening 1 - loading sound bytes into the sound chip 2 - counting off duration ticks To play a sound, put an address of a sound list on the stack, and call playSoundList - as long as there is some vdp access going on, it'll play the sounds. : playSoundList ( address -- ) $2000 @ 0= if \ if r0 of ISR=0 (not playing a sound list) $2002 ! \ set R1 of user ISR workspace with sound list address 1 $2000 ! \ kick off sound processing then ; Note: As you can see, playSoundList as written will only play a sound list if it's idle. You can query the driver with soundBusy? to see if it's idle. You might want to do this before initiating a sound. : soundBusy? ( -- flag ) $2000 @ \ load R0 of user ISR workspace ; So, boot TF, load the assembler from the blocks file (9 LOAD) and paste the text in the attached text file in, and then try: airBurstSnd playSoundList and groundBurstSnd playSoundList and inFlightSnd playSoundList I cannot take credit for the example sound list data. I took it from The Art Of Assembly, from the chapters on sound lists, which is a lot easier to understand than TI's load of old cock. The code is my own though. Oh, one thing to note: The driver above does NOT require the sound list to be placed in VDP ram. It reads it directly out of the compiled data list. Much more efficient. I've tried it from the command line and it's perfect. However, when you enter something on the command line TF just sits there flashing the cursor. That is quite a short loop, and interrupts are enabled and disabled in that loop. I would expect it to be more jittery in running code. Walid, I was thinking, wrt to Jetpac: Do you call the delay routine in the main loop of your game anywhere? If so, it would be a great place to do some dummy vdp access to fire the vdp interrupts. So, instead of: : delay ( n -- ) 0 do loop ; You could have: : delay ( n -- ) 0 do 0 v@ drop loop ; You would have to shorten your delay loops in your code, but interrupts would fire the delay loop without a doubt. That might be all you need to remove the jitter. Note that JOYST also fires the interrupts. Entire source code attached. I have't produced a CODE: version of the assembler code, but 'yall know how to do that. Rock on. Time for bed! sound list driver.txt 3 Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 17, 2015 Share Posted December 17, 2015 Or (Walid) In your delay routine, you could enable interrupts explicitly with your InterruptsOn machine code routine: : delay ( n -- ) InterruptsOn 0 do loop InterruptsOff ; I can't see any reason why that wouldn't work. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted December 18, 2015 Author Share Posted December 18, 2015 Very very cool Willsy. I will try to test this code in Jet Pac after I get back in town, or sooner if I can find the time. I hope it works out as expected. 1 Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted February 7, 2016 Share Posted February 7, 2016 I was able to get TurboForth running on my Geneve, just had to add GramKracker headers to the bin files. Cool... I noted that there is a KDEL setting to deal with keyboard repeat rates, which makes me wonder how well my USB keyboard works in TurboForth. So my TurboForth question is: Can the roms be used on a 512k red board if I flood it with the first 8k page, and then put the 2nd 8k page in the right spot on the 512k eprom? Why, cause I have 512k uv eproms... -M@ Quote Link to comment Share on other sites More sharing options...
Willsy Posted February 7, 2016 Share Posted February 7, 2016 I think if you just fill it with alternating upper and lower 8k banks it should work fine. For what it's worth, TF writes to 0x6002 to select bank 0 and 0x6000 to select bank 1. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 7, 2016 Share Posted February 7, 2016 ... my TurboForth question is: Can the roms be used on a 512k red board if I flood it with the first 8k page, and then put the 2nd 8k page in the right spot on the 512k eprom? You can copy either or both ROMs in any order to flood the unused ROM space; but, you must get the first two in the correct order, which, for TF, is backwards for the red board. That is, you want TF bank 0 in the second 8KiB and TF bank 1 in the first 8KiB. ...lee Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted February 7, 2016 Share Posted February 7, 2016 You can copy either or both ROMs in any order to flood the unused ROM space; but, you must get the first two in the correct order, which, for TF, is backwards for the red board. That is, you want TF bank 0 in the second 8KiB and TF bank 1 in the first 8KiB. ...lee Yep, that worked... I skipped splitting the .eprom and used the two bank files that are in the classic99 folder of the distribution. My red board always powers on at the last bank, so I loaded TurboForthC.bin at 00000, TurboForthD.bin at 02000, and TurboForthC.bin again at 7E000 in the minipro eprom programmer. Double negatives like non-inverted cause brain freeze Thanks! Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 7, 2016 Share Posted February 7, 2016 ,,, Double negatives like non-inverted cause brain freeze Yeah, that was, believe it or not, necessary to avoid confusion because of how the 379 chip was first implemented—even though the 379 can be implemented both ways! ...lee Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted February 7, 2016 Share Posted February 7, 2016 So, I'm looking at getting the DSK1/BLOCKS file out of the TurboForth.net download onto DSSD80 disks, but I don't recognize the 3.2meg file size... and TI99DIR nor TiImageTool think it is a disk image. Looking inside, it looks like the actual blocks file, just way to big. -M@ Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 8, 2016 Share Posted February 8, 2016 So, I'm looking at getting the DSK1/BLOCKS file out of the TurboForth.net download onto DSSD80 disks, but I don't recognize the 3.2meg file size... and TI99DIR nor TiImageTool think it is a disk image. Looking inside, it looks like the actual blocks file, just way to big. -M@ It should not be that large, for sure. Mark will have to weigh in on this. However, it looks like mostly spaces after the first 80KiB—except for a few hundred bytes at the very end, which were actually duplicated from much earlier in the file. In a hex editor, I clipped the TIFILES header (128 bytes) and all of the tail end except for 80KiB (81920 bytes)—I assumed that was the size it should be. I then converted it in TI99Dir from PC to TI file (DF128, with no data conversion), which produced a V9T9 file. Though I have not tested it, here it is in a ZIP file: BLOCKS.zip ...lee Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted February 8, 2016 Share Posted February 8, 2016 It should not be that large, for sure. Mark will have to weigh in on this. However, it looks like mostly spaces after the first 80KiB—except for a few hundred bytes at the very end, which were actually duplicated from much earlier in the file. In a hex editor, I clipped the TIFILES header (128 bytes) and all of the tail end except for 80KiB (81920 bytes)—I assumed that was the size it should be. I then converted it in TI99Dir from PC to TI file (DF128, with no data conversion), which produced a V9T9 file. Though I have not tested it, here it is in a ZIP file: BLOCKS.zip ...lee Thanks, that blocks file worked. And the turboforth.net site does say it should be an 80 block file. So that is probably good. Things seemed to load fine. Mark, you probably want to fix the archive on your website. -M@ Quote Link to comment Share on other sites More sharing options...
Willsy Posted February 8, 2016 Share Posted February 8, 2016 Thanks for the heads up. Looks like I boobooed somewhere. I'll fix it on Wednesday. I'm in Oslo at the moment. Quote Link to comment Share on other sites More sharing options...
Willsy Posted April 6, 2016 Share Posted April 6, 2016 Thanks for the heads up. Looks like I boobooed somewhere. I'll fix it on Wednesday. I'm in Oslo at the moment. I finally got around to fixing the disk image! 1 Quote Link to comment Share on other sites More sharing options...
ckoba Posted April 6, 2016 Share Posted April 6, 2016 I finally got around to fixing the disk image! 22:08:44] elan:~/ti/forth/newforth$ ls -l Downloadable\ Zip/DSK1/ total 168 -rw-r--r--@ 1 wileyc staff 82048 Mar 29 10:04 BLOCKS Thank you! Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted May 13, 2016 Share Posted May 13, 2016 I thought I would play around with the SAMS bank switching library in Turboforth. I have found one gotcha: the data and enhanced data ( data[ ]data ) words They don't return the address of the data when compiling into a paged bank. : testdata data[ 1 2 3 4 ]data ; : test testdata 0 do CR @++ . loop drop ; Running test prints out the expected 1 2 3 4 But if you do these first: 2 banks 0 setbank Then it prints out entirely different values. I suspect values I get are the machine code leading up to jumping into the bank. -M@ Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 13, 2016 Share Posted May 13, 2016 That's strange. It works fine for me: 51 load 5 banks : testdata data[ 1 2 3 4 ]data ;: test testdata 0 do CR @++ . loop drop ; test 0 setbank test Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted May 13, 2016 Share Posted May 13, 2016 Did you setbank before compiling the testdata word? -M@ Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 13, 2016 Share Posted May 13, 2016 (edited) No, I deliberately didn't do that! If I do a setbank first, it still works: Edited May 13, 2016 by Willsy 1 Quote Link to comment Share on other sites More sharing options...
+jedimatt42 Posted May 13, 2016 Share Posted May 13, 2016 Ok, that is working for me now... I must have been doing something wrong last night. This has 'huge' potential. -M@ Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 13, 2016 Share Posted May 13, 2016 Yeah it's pretty cool. Just don't let your banked definitions spill over the 4k boundary. That's why it shows you how many bytes are left after each banked definition! Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 1, 2018 Share Posted February 1, 2018 (edited) It's a bit of an un-known quantity really. I decided to bite the bullet and write my own sound list processor/driver. Here it is: asm: soundListPlayer ( -- ) \ re-entrant. Uses r0 to remember state: \ 0 = doing nothing. Waiting for a sound list to be loaded \ 1 = loading sound bytes to sound chip \ 2 = counting off duration counter $2000 lwpi, r0 r0 mov, \ have a sound list to process? ne if, r0 1 ci, \ loading sound bytes? eq if, \ yes r1 *+ r2 movb, \ get number of bytes to send to sound chip r2 8 srl, \ move to low byte eq if, r0 clr, \ if 0 then we're finished else, begin, r1 *+ $8400 @@ movb, \ send a byte to the sound chip r2 dec, \ decrement counter eq until, \ loop until finished r1 *+ r3 movb, \ get duration byte r3 8 srl, \ move to low byte eq if, \ processed everything? r0 clr, \ reset state to 0 (idle) else, r0 2 li, \ set state flag to counting endif, endif, else, \ we're counting off duration r3 dec, \ decrement duration` eq if, \ hit zero? r0 1 li, \ set state to load sound bytes endif, endif, endif, $83e0 lwpi, \ restore GPL's workspace r11 ** b, \ return via R11 ;asm It occupies a whopping ( ) 86 bytes. How cool is that. It plays regular sound lists in the TI format. <SNIP> While looking for stories about sound I found this one. 86 Bytes is pretty small for sure. This one in Forth adds only 124 bytes to the system and that includes about 31 bytes of names and linked-list stuff. So not too shabby. HEX : SILENT ( --) 9F SND! BF SND! DF SND! FF SND! ; \ turn off all sounds : PLAY$ ( sound_string -- ) \ play 1 sound string COUNT \ -- addr len 2DUP + C@ >R \ get duration at end of string, Rpush BOUNDS \ convert addr/len to end-addr. start-addr. DO I C@ SND! LOOP \ feed bytes to sound chip R> JIFFS ; \ use the delay from Rstack (JIFF=1/60) : PLAYLIST ( addr -- ) \ play a TI sound list BEGIN DUP C@ WHILE \ while the length is not 0 PAUSE \ give somebody else some time DUP PLAY$ \ play a single string COUNT + 1+ \ advance to the next sound string REPEAT SILENT DROP ; \ mom said always clean up after yourself Edited February 1, 2018 by TheBF 1 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.