Topic: Commodore Wedge Reinvented
Programmer: Kiyote Wolf

    Most QBasic programmers remember C= 64 programming since the two were around at the same time.  If anybody used a Commodore, they are probably familiar with the term Wedge, a small assembly language routine that rides the screen update at 60 Hz to do stuff automatically, such as monitor keys, play music or update sprites, or more than likely all three at once.

    I still have my old QBasic manual, and looking over it for hours on end I finally figured out how to recreate the Wedge interrupt program in a QBasic program.  We use the PLAY statement, which creates an interrupt when the play buffer queue gets low to leapfrog out of our code into a side routine and do something useful, then let it leap back to our regular code.

    I have been searching my code for an example of this and have since lost the example, but re-coding it (with maybe one error,.. maybe not..) is quite easy.  First, set your ON PLAY statement to jump to your Wedge code (your small routine), and set PLAY ON so when the buffer goes empty it will automatically run your Wedge code.  Next, initalize your variable, and get your code up to speed, and when you get to the loop of your main routine, just before it put PLAY "T200P32P32".  With PLAY ON flagged as ON, your code will now interrupt itself every time the first delay is over, because the only "note" left in the buffer is another "rest", because it is in QBasic to jump to the ON PLAY vector when the buffer drops to only 1 note left.  Now, to interrupt this constant Wedge code jumping, simply enter PLAY OFF somewhere in your code to give you main routine full attention again, but if you want to re-enable the Wedge code, make sure you give it a push to start moving again, set PLAY ON and then another PLAY "T200P32P32" statement after to trigger the vector jump again.

    The code will still run itself until the buffer catches up, but once it has finished all the "rests" in the queue, the Wedge code will not run again until you let it and then push it into action again.  Now, this is of course going to happen very fast, and I'm quite sure that it is not a steady beat because of the haphazard way code is going to process at different speeds.  You are going to find that high priority commands such as disk access and most anything is going to run until it is done with the CPU, and then once that task is finished, then next code that will run is going to be your Wedge code.  Now because the Wedge code is not stable like a heartbeat, what you can do to moderate it's actions is tie it's reactions into the TIMER or TIME functions to see how much real time occurs between callings of itself, or just don't worry about the differences in time slices because it happens so quickly anyway.  Whatever you prefer will of course prevail. 

    Now, one major bit of information on this, is routines that access memory directly using the DEF SEG routines are drastically going to be affected, especially if your wedge code uses DEF SEG routines of it's own.  I have already ran into this and have a solution already.  Even though DEF SEG is a regular QBasic command, simply push the DEF SEG command into a SUB or FUNCTION to keep track of the code segment memory address that was fed to it, and make that value a global variable that your Wedge code can find and set the SEG back to when it's done goofing off.  That way, if your Wedge code is updating sprites while your main routine is off updating your sprite memory array, your SEG will not be left skewed off course and run the risk of crashing your program.  Getting used to a DEF SEG  .. SUB is not that hard to do, especially after getting the full use of having a Wedge code piece in your QBasic games can do.

    Lastly, the Wedge code is going on, even as it is running itself, so you have to force it not to allow recursion of itself.  How do I .. what??  Ok.. what you do is when the Wedge code first runs, have it check for a global flag to tell itself that it is running once already, and if so, return to the regular code without running.  After a test for an instance goes false, meaning this code is running only one copy of itself, right after that test, set the global flag to tell itself it's running.  The flag set now, set PLAY OFF so the remaining "rests" in the queue die off and get kicked by the "busy flag" test.  Now your Wedge code is running.  Do your fun stuff.. sing a song,.. move a pixel.. whatever..  Now, if there was a DEF SEG address saved, re-set that flag so the main code will have it's code segment set, then set PLAY ON, then right before the code goes RETURN, do another WorkingFlag = 0 then PLAY "T200P32P32". 


*** Example that works

ON PLAY(1) GOSUB STOPHERE
PLAY ON

PLAY "T200P32P32"
SCREEN 0: WIDTH 80, 25: CLS

REM MAIN LOOP
DO
  PRINT "HI WORLD"
  t = TIMER: WHILE TIMER - t < .1: WEND
  COLOR INT(RND(1) * 6) + 1
LOOP

REM WEDGE CODE
STOPHERE:
IF WORKING THEN RETURN
WORKING = -1
PLAY OFF
PRINT "INTERRUPTING COW"
PLAY ON
WORKING = 0
PLAY "T600P32P32"
RETURN

*** End of Example Code

    You can use this code to do lots of stuff in the background.  Instead of wasting precious cycles doing a blank dull timer to keep your code from running too fast, you can use this to one, slow your code down a tiny bit or more if you code it to, and two, use this code to make things simultaneous, or to watch for things without having to code complex jumping routines to make your program crazy.  You can do all this in the background without having to even reference the code in question.

    I hope people can use this, because I think it's great to have the C= 64 Wedge back again.

P.S. One drawback of this routine is it will toggle the PC speaker, so at different operating speeds, you may notice strumming of the PC speaker that sounds like a light hum, or maybe more annoying noise, but generally you shouldn't even hear a thing with it.  If you use DOSBOX to run and your code already uses the Soundblaster, simply disable the PC speaker if you do encounter this phantom noise.  You cannot use standard PLAY statements while this routine operates or even SOUND, since they use the same port to do the same function, so use a Soundblaster bit of code of some kind in your program, or just no sound at all.  Other people should be able to drop the volume of the internal speaker on their sound settings, but do not mute it.  If you mute it, it will probably enable the speaker internally which will just do the same thing, just drop the volume to zero on the slider.

KW 2/9/08