A/Rexx PoC: using a compound variable as both associative and indexed
Actually, I am not really certain what to call what I am doing here. Essentially, I need a table of values which can be referenced both by number (ordinal) and the value itself (associative.)
I reach back to the TI-99 ISR (interrupt service routine) sound lists for a practical example. I want to create a transform which will increase or decrease tone values by semitones or octaves (12 semitones.) To do that, I have to load the channel sound command, which consists of two bytes, from the list. Some manipulation has to be done to convert it from (for example) >8C1F to >01FC, A-1 on channel 1, but that is another exercise.
Now I have my tone value. Say I want to increase this by an octave to A-2, >00FE. While I know an octave is 12 steps, I have no way of knowing that >00FE is 12 steps away from >01FC unless I have it in an indexed table in which I can count the 12 steps to find the value. But I also need to know at what index the initial value resides. I have not used a language with associative arrays which can actually produce an index as the elements are usually stored as some kind of hash. You can step through all of the elements, but not in any useful order.
To accomplish this I only need one variable but two ways of storing information. The first will convert a given value to an index, and the second which can then convert an index to a value.
In the example below I am doing this, as well as something else I consider neat. I am actually storing the list of values I need indexed in the script I am running as a long comment. Sure I could have dome some kind of Fu to get a long monotonous list of assignments (in this case 150,) but this method allows me an easy (and did I mention neat?) way to build the stem variable at run-time and more simply maintenance the table.
The script scans through itself looking for its table values. In this case I am indicating a wanted value with the ">", which is the hexadecimal notation prefix in 9900 assembler. As the routine takes in values it increments the index variable. Each value is stored in two ways using the same stem: the index is stored as an associative leaf, then the value is stored with the index as its leaf. TONE.0 is used to store the length of the table for walking.
Thus, the script is
If value is 01FC and index is 13, A/Rexx executes this:
Remember, I want to increase this tone by an octave which is 12 semitones. Because I know the value I can now find the index. The new index is 25, and TONE.25 gives 00FE. Done!
I can use this one stem variable to work in both up and down directions, as well as between index and value, for whatever purpose.
In the script below, the table is read from the comments at the end of the file. To save time in longer scripts I also facilitate a termination value to stop reading the file any further once the end of the table is read. For some reason i was unable to get BREAK within the DO loop to work in Regina Rexx and I did not try it in ARexx, though the script does otherwise work in ARexx if you comment out or remove the OPTIONS line. As well, since the script was created in Windows it contains carriage-returns at the end of lines, so I had to PARSE that out. If the build routine is fed a file which lacks carriage-returns, the last character of the pattern simply does not get matched and it still executes as expected.
Once the table is read into the stem variable, it is then walked and output to the screen, thus proving its might.
/* tone_value_table.rexx This will read a table within its own script into a stem variable and a related stem holding index information. */OPTIONS AREXX_BIFS AREXX_SEMANTICSparse source . . myselfif ~open('input_file', myself, 'r') then exit 30index = 0do until eof('input_file') | value == '@' /* couldn't get BREAK to work in the loop */ inline = readln('input_file') if substr(inline,1,1)~='>' then iterate parse var inline '>' value '0d'x if value ~= '@' then do index = index + 1 TONE.value = index TONE.index = value endendcall close 'input_file'TONE.0 = indexdo index = 1 to tone.0 sound = tone.index say 'Tone # ' || index || ' is ' || sound 'and this tone value is index ' || tone.soundendexit/* TMS-9919 tone value table>03F9>03C0>038A>0357>0327>02FA>02CF>02A7>0281>025D>023B>021B>01FC>01E0>01C5>01AC>0194>017D>0168>0153>0140>012E>011D>010D>00FE>00F0>00E2>00D6>00CA>00BE>00B4>00AA>00A0>0097>008F>0087>007F>0078>0071>006B>0065>005F>005A>0055>0050>004C>0047>0043>0040>003C>0039>0035>0032>0030>002D>002A>0028>0026>0024>0022>0020>001E>001C>001B>0019>0018>0016>0015>0014# Editor assembler manual ends here>0013>0012>0011>0010>000F>000E>@*/
This routine could be adapted to handle more than a single table in a file. For that matter each table could be stored in separate files, but storing the table or tables in the executing script just makes less clutter in the file system. Plus it is kind-of neat.
For my purpose, and ease of constructing the two sound channel command bytes, I can and most likely will change the order of the nibbles in the table. Done this way, building the sound channel command is a simple BITOR() instead of a more involved process of swapping nibbles around in the script.
0 Comments
Recommended Comments
There are no comments to display.