=== P·HYDRA DOCUMENTATION === The "Operation Codes, With Details, Please!" File (OPS, ver L5L) 00 RETPAGE -- RETPAGE sets the low byte of the playhead's instruction address to zero, sending it back to the first instruction on the current memory page. Since program memory is blanked to zeros when P·HYDRA loads, then all sequences automatically loop. 01 - 04 SETF1...4 -- -- Sets the value of POKEY's frequency registers (AUDF1-4). These operations take the first operand as a MODE setting, and the second operand as VALUE. The MODEs are: 00 - use SECOND OPERAND value directly 01 - use " " as a scale index 02 - add " " to current AUDF value 03 - add " " to current INTERVAL value 04 - use REGISTER LOW BYTE directly 05 - use " " as scale index 06 - add " " to current AUDF value 07 - add " " to current INTERVAL value 08 - use RANDOM value directly 09 - use " " value as scale index 0A - add " " value to current AUDF value 0B - add " " value to current INTERVAL value So, the four low bits of the [mode] operand are: source----| |----method / \ / \ 0 0 0 0 0 0 0 0 05 - 08 SETC1...4 -- -- Sets the value of POKEY's voice control registers (AUDC1-4). These operations take the first operand as a MODE setting, and the second operand as a VALUE. The modes are: 00 - Overwrite the AUDCx register with VALUE 01 - Set only the distortion bits of AUDCx with VALUE 02 - Set the volume bits of AUDCx with VALUE 03 - Add VALUE to AUDCx's distortion bits 04 - Add VALUE to AUDCx's volume bits In some future version I may rewrite this to be more like the SETFx operations, using direct, register, and random sources. 09 SETCTL -- Sets the value of AUDCTL, POKEY's control register. Again, we use the first operand as a MODE value to determine how to use the second operand's VALUE. 00 - Store the VALUE directly to AUDCTL 01 - Perform XOR with VALUE and AUDCTL 02 - Perform AND with VALUE and AUDCTL 03 - Perform OR with VALUE and AUDCTL 0A HOP -- Makes the current playhead hop over the number of instructions specified by operand 1. If operand 1 is zero, then the value of the low byte of the playhead's register is used instead. Operand 2 is ignored. 0B JMP -- Set the current playhead's program counter directly. Operand 1 sets the low byte, operand 2 sets the high byte. Yes, you can crash the computer by jumping to a weird location. Should I accident-proof this a bit better? Yeah, probably. 0C STOR -- Stores the passed word value to the playhead's register. 0D MAC -- Jump to a macro sequence (aka, subroutine). Stores the playhead's current position to it's macro return address register, then performs a JMP. 0E MACRET -- Return from a macro sequence. Sets the current playhead's program counter to the value of its macro return address register. 0F OPNOP -- Does nothing. Really. But it still has a delay value, so if you need a delay of more than 60 ticks, line-up a few OPNOPs. 10 STOP -- Disables the current playhead. Leaves its register, program counter, etc. untouched. 19 - 1C SETC1R, SETC2R, SETC3R. SETC4R (SET AUDC from Register) -- -- Set the value of POKEY's voice control registers (AUDC1-4) from the value of the current playhead's register ...in theory. I'm looking at the source code right now, and all of these operations just have a comment that says "AIN'T WROTE THIS YET", so... not implemented at this time, apparently! 1D SETCTLR (SET AUDCTL from Register) -- Set the value of AUDCTL, POKEY's control register, from the value of the current playhead's register. *** NOT IMPLEMENTED : Same as the SETCxR operatons. 1E MODR (MODify Register) -- Modify the value of the current playhead's register. The value of operand 1 determines what to do, and operand 2 determines how much. Operand 1 - Result --------- ------ 0 - Perform a 16-bit adition with operand 2 1 - XOR with operand 2 2 - AND with operand 2 3 - OR with operand 2 >3 - 8-bit addition with operand 2 1F HOPNE (HOP on Not-Equal) -- Skip the next operation if the playhead's register is not equal to the passed value. 20 JMPONZ (JuMP On Zero) -- Set the current playhead's program counter to the passed address (same as a JMP) IF the playhead's register equals zero. 21 - 26 GOHED1-6 -- -- Activates a playhead, sets its program counter to the fist byte of the page given in operand 1, and sets the low byte of its register to the value of operand 2 IF operand 2 is nonzero. 30 GOPCM -- Start playback of the loaded PCM waveform on POKEY voice 4. Operand 1 sets the sample rate -- that is, operand 1 is a divisor for POKEY's main clock, so the following values give the corresponding sample rates. op1 15kHz clock (15,699.9 Hz) 64kHz clock (63,921 Hz) --- ------------------------- ----------------------- 2 7849.95 Hz 31960.5 4 3924.975 15980.25 8 1962.4875 7990.125 16 981.24375 3995.0625 32 490.621875 1997.53125 And so-on. 31 NOPCM -- Disables PCM playback on POKEY voice 4. 32 GOFM -- Enable frequency modulation on POKEY voice 4. Operands 1 and 2 do nothing here. 33 SETFMF (SET FM Frequency) -- Set the frequency of the sine oscillator/FM operator. The low nybble of operand 1 sets the target: x0 --special-- x1 OPF0 (sample skip) x2 AUDF4 (sample rate) x3 INTRVLF (scale index) x4 FMTRANSP (transposion) The high nybble determines the operation 0x --special-- 1x SET from OPERAND2 2x SET from REGISTER LOW BYTE 3x SET from RANDOM 4x ADD from OPERAND2 5x ADD from REGISTER LOW 6x ADD from RANDOM Here's how this works: there's a 256-byte PCM table of a sine wave in memory, and we use the POKEY high-resolution timers to read that table and set the volume of POKEY voice 4 to produce a sine wave sound, or we use the values of the sine table to set/modify the pitch of voice 4 to do some crude FM. The pitch of the sine operator is determined by setting a sample SKIP VALUE, and a sample RATE. The sample rate is determined by the value of AUDF4, and that value is a divider of POKEY's main clock frequency. So, with POKEY on its 15KHz clock (15,699.9 Hz), an AUDF4 value of 2 sets the sample rate to 7849.95Hz, while a value of 4 sets the sample rate to 3924.975 Hz, and so-on. The skip value, stored in a zero-page register we call OPF0, determines how many samples are read from the sine table before skipping one. So the pitch of the sine oscillator is determined by: SAMPLE_RATE / (SAMPLE_LENGTH - (SAMPLE_LENGTH / SKIP_VALUE) Put simply, if imprecisely: incrementing the sample rate raises pitch, incrementing the skip value lowers pitch. Now, for our sanity there's a table of rate/skip values representing a 5-octave chromatic scale starting at C0 (based on A=440Hz) stored in the program, and we can set the sine oscillator to one of those tones by simply setting a scale index (INTRVLF), rather than setting OPF4 and AUDF4 individually. The "--special--" function listed above is simply this: when operand 1 is set to "00", the sine/FM oscillator will match the frequency of POKEY voice 1. 34 SETFMC (SET FM Controls) -- Sets FM/sine oscillator configuration. The first operand determines what is to be done: 00 - Change FM/Sine configuration 01 - Change FM amount >01 - Set sine volume ...? When operand 1 is 0, the value of operand 2 sets the configuration: 00 - FM/Sine OFF 40 - Sine oscillator ON 80 - FM ON C0 - FM & Sine oscillator ON When setting the FM amount (operand 1 = 1), only the three lowest bits of operand 2 are used, for 8 levels of intensity. Seven, really, because a value of 0 mean no FM. When setting the sine wave volume (operand 1 > 1).... I have to experiment with this. 35 SETCVOX (SET distortion C timbre) -- The source says that this sets "distortion C voice. Toggle between 64 and 128". As I recall, when a POKEY voice is in distortion C mode, it actually produces two different timbres which are non-linearly interleaved, but with the use of a lookup table you can get a nearly equal-tempered musical scale out of it. This sets which lookup table to use, and thus which timbre is produced. 3B SETSCALE -- Set the scale table vector. ... 3C PEEK -- Load a byte from memory into the current playhead's register (low byte). ... 3D PEEKW (PEEK Word) -- Load a word from memory into the current playhead's register. ... 3E POKE -- Copy the low byte of the current playhead's register to the specified memory location. ... 3F POKEW (POKE Word) -- Copy the value of the playhead's register to the specified memory location. ...