Seeking alternatives to DATA statements

edited August 2010 in Sinclair Basic
Anyone know of any memory saving alternatives to storing a list of numbers, other than using DATA statements or poking data into addresses?...

Im tinkering with Gasman's mid2beeper utility and looking for alternative ways of outputing the basic other than producing repetative listings... Iv managed to output an alterative style of listing, using DATA statements for more efficiency, but am aware that DATA statements aren't all that efficient for single byte numbers... Since its a BASIC output Im aiming for (my alternative utility already outputs Assembly), I wondered if there was some clever way of generating the data within the listing that takes up less room, that could still technically be called BASIC (rather than crossing over into what is technically the domain of machine code)... (Has to be allowable in any BASIC crapmo competitions & readable for amatuer programmers)...

Any ideas?...

Here is what a typical listing looks like so far...

eg:

1 REM Mid2ASM (7th August 2010)
10 LET PITCH=60 + (0)
11 LET MININ=1/(51+0)
20 READ L: IF L=0 THEN RESTORE 30: GO TO 20
25 READ P: BEEP MININ*L,P-PITCH: IF INKEY$="" THEN GO TO 20

30 DATA 23,70,23,71,1,69,1,72
31 DATA 21,54,23,57,1,71,22,63
32 DATA 23,60,23,59,23,57,1,70
33 DATA 22,55,1,71,22,54,1,67
34 DATA 1,72,21,52,23,64,1,71
35 DATA 22,67,23,66,23,64,23,62
36 DATA 1,71,22,59,1,76,22,55
37 DATA 1,76,22,54,23,58,1,75
38 DATA 22,71,23,69,23,67,23,66
39 DATA 1,78,10,63,11,71,1,79
40 DATA 22,59,1,71,1,78,21,64
41 DATA 23,67,1,76,22,72,23,71
42 DATA 23,69,23,67,1,79,10,64
43 DATA 11,71,1,81,22,60,1,71
44 DATA 1,79,21,64,11,66,11,78
45 DATA 1,72,22,69,23,67,11,66
46 DATA 11,66,23,64,1,78,10,62
47 DATA 11,71,1,79,22,59,1,74
48 DATA 1,78,21,62,11,64,11,76
49 DATA 1,71,22,67,23,66,23,64
50 DATA 23,59,1,70,22,52,1,71
51 DATA 22,55,1,69,1,72,21,54
52 DATA 23,57,1,71,22,63,23,60
53 DATA 23,59,23,58,1,66,22,57
54 DATA 1,71,22,55,1,64,1,69
55 DATA 21,52,23,54,1,67,22,55
56 DATA 23,59,23,60,11,62,11,79
57 DATA 11,64,11,71,1,81,22,60
58 DATA 1,71,1,79,21,64,11,66
59 DATA 11,78,1,72,22,69,23,67
60 DATA 11,66,11,66,23,64,1,78
61 DATA 10,62,11,71,1,79,22,59
62 DATA 1,74,1,78,21,62,11,64
63 DATA 11,76,1,71,22,67,23,66
64 DATA 23,64,11,59,11,70,23,52
65 DATA 1,71,22,55,1,69,1,72
66 DATA 21,54,23,57,1,71,22,63
67 DATA 23,60,23,59,23,55,1,69
68 DATA 1,78,21,57,1,67,1,79
69 DATA 21,58,1,63,1,78,45,59
70 DATA 1,67,1,76,45,64,47,60
71 DATA 47,59,1,67,1,71,45,52
72 DATA 1,67,1,71,33,55,11,71
73 DATA 1,69,1,72,9,57,11,74
74 DATA 11,72,11,71,1,69,46,60
75 DATA 1,69,1,74,21,54,1,69
76 DATA 22,74,23,62,1,69,22,74
77 DATA 1,74,1,59,9,67,11,76
78 DATA 11,74,11,72,1,71,1,55
79 DATA 45,67,1,72,1,76,45,60
80 DATA 1,72,34,76,11,76,1,76
81 DATA 22,61,23,69,47,69,1,69
82 DATA 1,74,1,78,20,62,1,69
83 DATA 1,74,45,78,1,70,1,74
84 DATA 21,78,1,78,22,63,23,71
85 DATA 47,71,1,79,10,52,11,71
86 DATA 11,67,11,71,11,79,23,78
87 DATA 11,76,1,79,10,57,11,72
88 DATA 11,69,11,72,11,64,11,69
89 DATA 11,72,11,69,1,78,10,62
90 DATA 11,72,11,69,11,72,11,78
91 DATA 23,76,11,74,1,78,10,55
92 DATA 11,71,11,67,11,71,23,70
93 DATA 23,71,1,76,10,60,11,72
94 DATA 11,67,11,72,11,76,23,74
95 DATA 11,72,1,76,10,57,11,72
96 DATA 11,69,11,72,1,76,10,54
97 DATA 23,74,11,72,1,71,22,59
98 DATA 23,59,23,61,23,63,23,64
99 DATA 29,59,1,70,28,52,1,71
100 DATA 28,55,1,69,1,72,17,54
101 DATA 19,57,1,71,18,63,19,60
102 DATA 19,59,19,57,1,70,18,55
103 DATA 1,71,18,54,1,67,1,72
104 DATA 17,52,19,64,1,71,18,67
105 DATA 19,66,19,64,19,62,1,71
106 DATA 18,59,1,76,18,55,1,76
107 DATA 18,54,19,58,1,75,18,71
108 DATA 19,69,19,67,19,66,1,78
109 DATA 8,63,9,71,1,79,18,59
110 DATA 1,71,1,78,17,64,19,67
111 DATA 1,76,18,72,19,71,19,69
112 DATA 19,67,1,79,8,64,9,71
113 DATA 1,81,18,60,1,71,1,79
114 DATA 17,64,9,66,9,78,1,72
115 DATA 18,69,19,67,9,66,9,66
116 DATA 19,64,1,78,8,62,9,71
117 DATA 1,79,18,59,1,74,1,78
118 DATA 17,62,9,64,9,76,1,71
119 DATA 18,67,19,66,19,64,19,59
120 DATA 1,70,18,52,1,71,18,55
121 DATA 1,69,1,72,17,54,19,57
122 DATA 1,71,18,63,19,60,19,59
123 DATA 19,58,1,66,18,57,1,71
124 DATA 18,55,1,64,1,69,17,52
125 DATA 19,54,1,67,18,55,19,59
126 DATA 19,60,9,62,9,79,9,64
127 DATA 9,71,1,81,18,60,1,71
128 DATA 1,79,17,64,9,66,9,78
129 DATA 1,72,18,69,19,67,9,66
130 DATA 9,66,19,64,1,78,8,62
131 DATA 9,71,1,79,18,59,1,74
132 DATA 1,78,17,62,9,64,9,76
133 DATA 1,71,18,67,19,66,19,64
134 DATA 9,59,9,70,19,52,1,71
135 DATA 18,55,1,69,1,72,17,54
136 DATA 19,57,1,71,18,63,19,60
137 DATA 19,59,19,55,1,69,1,78
138 DATA 17,57,1,67,1,79,17,58
139 DATA 1,63,1,78,35,59,1,67
140 DATA 1,64,16,76,0

141 REM BUG REPORT:
142 REM Note data is 884 bytes long...
143 REM Pitch ranges from 52 to 81...
Post edited by kgmcneil on

Comments

  • edited August 2010
    Since DATA statements store numbers that are about 4-5 bytes long, is there some clever way of joining maybe 4 numbers together, storing them in a data statement and then separating them prior using them as single bytes?...
  • edited August 2010
    You could store them in DATA statements as strings, then convert them when you READ using VAL. The should save some space.
  • edited August 2010
    If you put each number in quotes you'd save 4 bytes as you're not allowing the CHR$(14)+5 byte floating point number marker to be generated, but you are adding two extra quotes per value.

    Alternatively, I notice there are a lot of ones, these can be represented as SGN PI. Zero can be abbreviated to NOT PI and three can be abbreviated to INT PI.

    So a quick example would be :
    READ A$: LET L=VAL(A$)
    READ B$: LET P=VAL(B$)
    
    DATA "23","70","10","69"
    
  • edited August 2010
    Instead of:
    30 DATA 23,70,23,71,1,69,1,72
    you could use:
    30 DATA "2370237101690172"
    which would take 18 bytes instead of 61.

    Then you'd have to do something like:
    20 READ l$
    21 FOR n=1 TO 13 STEP 4
    22 LET l=VAL l$(n TO n+1): IF l=0 THEN RESTORE 30: GO TO 20
    23 LET p=VAL l$(n+2 TO n+3): BEEP minin*l,p-pitch: IF INKEY$<>"" THEN STOP
    25 NEXT n
    26 GO TO 20

    Of course, any space saving measure is always a trade-off against time, so resolving each BEEP would take longer and the overall effect wouldn't be quite the same unless all the numbers were adjusted to take account of the extra processing time.
  • edited August 2010
    Writing the numbers in hexadecimal in a single string data statement would, I imagine, be the most efficient, as you'd use two digits for each byte instead of the three if you used decimal (yes, you'd use one to three digits if you used decimal, but if you put them all together in a string data statement like that then the program would not know when any byte ended and the next began, so you'd need to use three bytes for every decimal representation of a byte, for the program to read the data correctly, whereas with hexadecimal each byte is represented by exactly two digits).
  • edited August 2010
    A proposal is that you can output a BASIC program with variables initialized. In this case, the variable would be a numeric array of N*2, with N (number of rows) being the number of beeps needed. Column 1 of each row would be duration, and column 2 the pitch. The N variable would be initialized as well.
    So, the BASIC code you have to output is always the same. Assuming your numeric array is called P (for Partiture), you would have:
    10 FOR I=1 TO N: BEEP P(I,1),P(I,2): NEXT I
    
    The advantage of this method is that you are not required to have a long listing with each number stored both in ASCII and in floating point format (and ASCII representation can be longer than floating point, as durations calculated by mid2beeper have many decimals), and the code is fast. Methods based upon coding numbers as hex, or ASCII, or whatever are slower due to the decoding process to be performed both on duration and pitch, and melodies with rapid note sucessions (to simulate more than one channel) would sound too much slow.
    The disadvatange, on the other way, is that you cannot do a CLEAR or RUN, as you will erase all your variables, and you will have to output the BASIC program directly off from your mid2beep version, as zmakebas cannot generate BASIC programs with variables embedded, afaik.
  • edited August 2010
    You can use a REM statement as DATA-dump.
    Make a line like
      1  REM XXXXdata-lengthXXXXXX
    10 LET PROG=PEEK 23635+256*PEEK 23636
    15 LET a=PROG+5
    20 FOR f= 0 to length: PEEK (a+f): NEXT f
    
    with as length of course the right a mount of X's.
    With a proper LET a=PEEK 23635+256*peek 23636 you find the Program start.
    Thats usualy 23755 and the first X is at 23760 if i am correct...
    Now you can read them with a FOR/NEXT loop.
    the +5 is to hop over the first 4 bytes for the LINE number + 1 for the REM statement it self.
    If you want it in another LINE you have to use another PEEK, for NEXT LINE.
    I can look for that but you can search for "REM and USR" to find it in basic tips and tricks tread.
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited August 2010
    I propose a 1-byte per number solution which involves still storing the listing in data statements.

    Your range goes from 0 to 72. The 96 ASCII characters from space (chr$(32)) to copyright (chr$(126)) is more tha adequate to storing each number as a different ascii character.

    Convert each number to a character representation e.g.
    (space) = chr$(32) = 0
    ! = chr$(33) = 1
    " = chr$(34) = 2
    # = chr$(35) = 3

    continue into the numbers 0-9 more punctuation until you each
    h = chr$(72+32) = 72

    To decode, read a data string and step through each character. Once you have reached the end of the character, READ another string. Space character = 0 will rerun your program.
  • edited August 2010
    Here you go.

    Bear in mind that if you are pasting in the listing below into BasIN, make sure you manually add the backslash (\) characters as these mysteriously get omitted with BasIN Paste/Add Code function.

    You can also save 8 bytes for each string you concatenate (2 bytes line no; 2 bytes length; 2 quote chars, 1 CR char, 1 DATA char)
    1 REM Mid2ASM (7th August 2010)
    2 LET a$=""
    10 LET PITCH=60 + (0)
    11 LET MININ=1/(51+0)
    20 GO SUB 26: LET L=n: IF L=0 THEN RESTORE 30: GO TO 20
    25 GO SUB 26: LET P=n: BEEP MININ*L,P-PITCH: IF INKEY$="" THEN GO TO 20
    
    26 IF a$="" THEN READ a$
    27 LET n=CODE a$(1)-32
    28 LET a$=a$(2 TO)
    29 RETURN
    
    30 DATA "7f7g!e!h"
    31 DATA "5V7Y!g6_"
    32 DATA "7\7[7Y!f"
    33 DATA "6W!g6V!c"
    34 DATA "!h5T7`!g"
    35 DATA "6c7b7`7^"
    36 DATA "!g6[!l6W"
    37 DATA "!l6V7Z!k"
    38 DATA "6g7e7c7b"
    39 DATA "!n*_+g!o"
    40 DATA "6[!g!n5`"
    41 DATA "7c!l6h7g"
    42 DATA "7e7c!o*`"
    43 DATA "+g!q6\!g"
    44 DATA "!o5`+b+n"
    45 DATA "!h6e7c+b"
    46 DATA "+b7`!n*^"
    47 DATA "+g!o6[!j"
    48 DATA "!n5^+`+l"
    49 DATA "!g6c7b7`"
    50 DATA "7[!f6T!g"
    51 DATA "6W!e!h5V"
    52 DATA "7Y!g6_7\"
    53 DATA "7[7Z!b6Y"
    54 DATA "!g6W!`!e"
    55 DATA "5T7V!c6W"
    56 DATA "7[7\+^+o"
    57 DATA "+`+g!q6\"
    58 DATA "!g!o5`+b"
    59 DATA "+n!h6e7c"
    60 DATA "+b+b7`!n"
    61 DATA "*^+g!o6["
    62 DATA "!j!n5^+`"
    63 DATA "+l!g6c7b"
    64 DATA "7`+[+f7T"
    65 DATA "!g6W!e!h"
    66 DATA "5V7Y!g6_"
    67 DATA "7\7[7W!e"
    68 DATA "!n5Y!c!o"
    69 DATA "5Z!_!nM["
    70 DATA "!c!lM`O\"
    71 DATA "O[!c!gMT"
    72 DATA "!c!gAW+g"
    73 DATA "!e!h)Y+j"
    74 DATA "+h+g!eN\"
    75 DATA "!e!j5V!e"
    76 DATA "6j7^!e6j"
    77 DATA "!j![)c+l"
    78 DATA "+j+h!g!W"
    79 DATA "Mc!h!lM\"
    80 DATA "!hBl+l!l"
    81 DATA "6]7eOe!e"
    82 DATA "!j!n4^!e"
    83 DATA "!jMn!f!j"
    84 DATA "5n!n6_7g"
    85 DATA "Og!o*T+g"
    86 DATA "+c+g+o7n"
    87 DATA "+l!o*Y+h"
    88 DATA "+e+h+`+e"
    89 DATA "+h+e!n*^"
    90 DATA "+h+e+h+n"
    91 DATA "7l+j!n*W"
    92 DATA "+g+c+g7f"
    93 DATA "7g!l*\+h"
    94 DATA "+c+h+l7j"
    95 DATA "+h!l*Y+h"
    96 DATA "+e+h!l*V"
    97 DATA "7j+h!g6["
    98 DATA "7[7]7_7`"
    99 DATA "=[!f<T!g"
    100 DATA "<W!e!h1V"
    101 DATA "3Y!g2_3\"
    102 DATA "3[3Y!f2W"
    103 DATA "!g2V!c!h"
    104 DATA "1T3`!g2c"
    105 DATA "3b3`3^!g"
    106 DATA "2[!l2W!l"
    107 DATA "2V3Z!k2g"
    108 DATA "3e3c3b!n"
    109 DATA "(_)g!o2["
    110 DATA "!g!n1`3c"
    111 DATA "!l2h3g3e"
    112 DATA "3c!o(`)g"
    113 DATA "!q2\!g!o"
    114 DATA "1`)b)n!h"
    115 DATA "2e3c)b)b"
    116 DATA "3`!n(^)g"
    117 DATA "!o2[!j!n"
    118 DATA "1^)`)l!g"
    119 DATA "2c3b3`3["
    120 DATA "!f2T!g2W"
    121 DATA "!e!h1V3Y"
    122 DATA "!g2_3\3["
    123 DATA "3Z!b2Y!g"
    124 DATA "2W!`!e1T"
    125 DATA "3V!c2W3["
    126 DATA "3\)^)o)`"
    127 DATA ")g!q2\!g"
    128 DATA "!o1`)b)n"
    129 DATA "!h2e3c)b"
    130 DATA ")b3`!n(^"
    131 DATA ")g!o2[!j"
    132 DATA "!n1^)`)l"
    133 DATA "!g2c3b3`"
    134 DATA ")[)f3T!g"
    135 DATA "2W!e!h1V"
    136 DATA "3Y!g2_3\"
    137 DATA "3[3W!e!n"
    138 DATA "1Y!c!o1Z"
    139 DATA "!_!nC[!c"
    140 DATA "!`0l "
    
    141 REM BUG REPORT:
    142 REM Note data is 884 bytes long...
    143 REM Pitch ranges from 52 to 81... 
    
  • edited August 2010
    A Z80 snapshot of the above and the XLS file which does the conversion can be found here:
    http://zxlife.net/chris/zx/tune.zip
  • edited August 2010
    Would you allow yourself to store the data in a string variable present at LOAD time, and not reentered within the program using DATA statements?

    It would still be BASIC, but would need to be started with GOTO 10, as it wouldn't survive a RUN command.
  • edited August 2010
    Guys, you are exceptional... can always count on the WOS for good info and ideas... I like quite a few of your ideas here, and will have to do some experimenting and mixing and matching... Im not quite sure how able Ill be to output embedded tokens and stuff from the Ruby script but will look into that... Im rather leaning to the notion of having the data within the listing rather than stored in an unlisted variable... I like the hexidecimal idea, but appreciate that it may have a speed penalty... Im tempted by the REM idea of storage... The use of converting the numbers to string characters also appeals, but I might not always be able to guarentee that the numbers will be in range, as my script now does autokey shift corrections by changing a constant that is added to the data rather than by editing all the data values themselves... but I might change that...

    yes... I have to say, Im overwhelmed and impressed by all your responses, and now have more than enough ideas to look into...

    Thanks guys!...

    Ill release the next version of Mid2ASM with ability to output to BASIC using some of these ideas, as soon as I can find time...

    Thanks again, for all your helpful suggestions!
  • edited August 2010
    kgmcneil wrote: »
    Im not quite sure how able Ill be to output embedded tokens and stuff from the Ruby script but will look into that...

    If it helps, zmakebas allows you to insert arbitrary bytes using the syntax: \{123} or \{0xAF}.

    There's also some direct Ruby-to-Spectrum-Basic code in tracker2ay which lets you build listings from Ruby-ish code like
    50 => [:pause, 1, ':', :randomize, :usr, 0xc005, ':', :goto, 50]
    
    ...although that doesn't support arbitrary bytes, and I'm not entirely sure how you'd do that in a neat way.
  • edited August 2010
    Thanks for the tip there Gasman... after playing around with BASin, bas2tap and looking at your referrence to zmakebas, Iv concluded that not all these tools use the same standards for encoding their tokens... I have thus settled on using the standard used in Martijn van der Heide's bas2tap, which would translate inline, for instance, something like {41} into A... (notice the absence of backslashes \ )... Iv settled on this one because:
    a) Im already using the tool anyway within the new version of Mid2ASM...
    b) Bas2Tap comes with compact source code that could be compiled for Linux or anything else (ie: simple enough even for me to compile)...

    My plan then, will be to go with something like Crisis's suggestion of having the code embedded within the REM statements... Obviously, they'll be some slow down when jumping between lines in reading the REMS, and the code won't be wonderfully readable... but it will be pure BASIC, embedded in the listing and fairly efficient...

    Incidently, out of curiousity... is zmakebas very diffferent from Martijn van der Heide's bas2tap?... is it more popular?... Would I be at a disadvantage in outputing such a format?... Perhaps I should design the script to make the format optional..?!
  • edited August 2010
    Since you are tempted with REM i have to warn for 1 or 2 importend issues with CHR$ 13 !!
    Once you have a CHR$13 as DATA the BASIC interprenter migth see it as END OF LINE.
    Especially in 128K mode you migth lose your code.
    128k Mode aint fond on long lines anyway so thats a 2nd issue to be carefull with.

    PICREMA uses a 'false chr$ 13' to make the interprenter think the line is ended and nothing more will be printed on SCREEN.
    This means you CAN NOT edit the line anymore since the FIRST chr$ 13 will be the new EOL...

    Hope i did not UN-tempt you now ;-)
    but i dont see see any DATA '13' in your data-list !!!
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited August 2010
    Darn it!... Didn't even think of that!...

    I suspect that shifting all the values up by something like 32, like in BloodBaz's idea, might help, but Iv also noticed a new problem to do with control codes...

    I was doing a cut and paste of BloodBaz's method in BASin and noticed that the "\" backslash character was filtered out of the listing... Haven't tried that with the REM method, but suspect Ill likewise hit upon a similar problem there too... I know, I know.. thats just BASin's token system at work... but since Im trying to make a tool that spits out output that works on most systems, these little details matter...

    ...you know, even though its slow, Im beginning to see some merits in the HEX approach proposed by ewgf... That would be slow, but would have the following advantages at least:

    1) Readable
    2) Its in the listing
    3) Its in the listing in a way that allows you to print and recopy it (ie: what you see is what you get)(no tokens or hidden stuff)
    4) Its pure BASIC (even more so than some of the other methods)
    5) It would include nothing that would be filtered out by any of the tools
    6) Which means, its practically universal code...

    ...just wish that method wasn't so darn slow...

    ..but heck, what can people expect if they opt for BASIC over assembly?!?!?
  • edited August 2010
    kgmcneil wrote: »
    Darn it!... Didn't even think of that!...

    (..)
    ..but heck, what can people expect if they opt for BASIC over assembly?!?!?

    50 decisions per second....:p
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited August 2010
    50 decisions a second - sounds like my wife! ;)

    Paddy
  • edited August 2010
    kgmcneil wrote: »
    Darn it!... Didn't even think of that!...

    I was doing a cut and paste of BloodBaz's method in BASin and noticed that the "\" backslash character was filtered out of the listing...

    I did warn you! It is part of a "feature" but it is a shame there isn't an option in the paste function to disable it.
    kgmcneil wrote: »

    ...you know, even though its slow, Im beginning to see some merits in the HEX approach proposed by ewgf... That would be slow, but would have the following advantages at least:

    1) Readable
    2) Its in the listing
    3) Its in the listing in a way that allows you to print and recopy it (ie: what you see is what you get)(no tokens or hidden stuff)
    4) Its pure BASIC (even more so than some of the other methods)
    5) It would include nothing that would be filtered out by any of the tools
    6) Which means, its practically universal code...

    ...just wish that method wasn't so darn slow...

    If you want to consider the HEX approach but avoid the slowness then there is a solution: Compile the HEX into actual byte data in memory BEFORE playing the tune, then you have fast access pure 0..255 values that you can call upon. You just need a "Please wait" at the start of the program. :-)
  • edited August 2010
    TO BloodBaz: Nice idea BloodBaz... Only problem with that is that if the music data is vast then Ill be making two copies of the data before playing (the original in HEX, and the buffer unpacked in memory), which might be too much for the humble speccy... Nevertheless, it might work well if the unpacked data somehow overwrites the original HEX.. perhaps if the HEX in a REM statement is overwritten during run time, the corruption might not be so problematic than if corrupted while in the speccy editor mode?...

    Iv been exploring a middle ground approach, similar to the HEX idea, but, Im hoping, faster to process... I goes something like this:

    - The data is stored in strings of two bytes, just like HEX, perhaps stored in a REM statement (but DATA is okey too)...

    eg: REM IJIJIJIJIJ

    The I represents the 1st byte, the J represents the 2nd byte.
    In this example, B will represent our resulting Byte. The combination of I & J will yield us with B like so:
    ============================
    START:
    Let B=CODE(I)
    Let X=CODE(J)
    If B=162 Then Let B=B-X: Goto ReadNextByte
    (else)
    Let B=B+X

    ReadNextByte:

    Let DURATION=B

    {Move the counters to read the next two chr$}

    Let B=CODE(I)
    Let X=CODE(J)

    If B=162 Then Let B=B-X: Goto PlayBEEP
    (else)
    Let B=B+X

    PlayBEEP:

    Let PITCH=B-60 (60 is normally added to pitch to ensure its a postive number)

    BEEP DURATION, PITCH
    ============================

    - Notice that whether the two values are added or subtracted depends upon whether the first value is 162 or not...

    - When encoding the data, values from 0-256 (actually 0-261) are encoded using the following conditons: (B=Value to translate)

    1) If B=70, or B=191 Then I=32 & J=(B-I)
    2) ElseIf B<131 Then I=162, J=(162-B)
    3) ElseIf A>130 Then I=99, J=(B-I)

    The encoding in Ruby is the slow bit (3 conditions), the decoding done on Speccy (one condition: If B=162 Then Let B=B-X: Else Let B=B+X) prior playing however should be fast...

    The advantages of this weird approach should be the following:
    a> It avoids listing chr$<32
    b> It avoids listing chr$>162
    c> It avoids listing chr$ "\"
    d> All the data is transparent to see now (although the UDG's won't be distinguishable from the alphabet to the naked eye, unless changed)...

    Anyway, thats where Im leaning... I just got to find time to write it into my Ruby script now...
  • edited August 2010
    I think Iv found my solution... A referrence by Matt B somewhere in the forums mentioned a oneliner using something like pseudoHEX encoded bytes, stored in a string (a$)... Iv adapted that one liner to store similarly encoded bytes in a REM statement... Rather than play them as I translate the bytes, Im translating and dumping them first in memory before BEEPing them... The clever bit, is where Im dumpting them... Since the REM byte codes are 2 chrs per byte, Im dumping them to overwrite the original REM encoded chrs... This makes the listing selfmodifying... I can get away with it, only because my REM statement is guarenteed to be longer than the data that Im poking into it (twice as long)...

    Here is a sample - it plays the first two lines of my original piece (encoded the bytes by hand, so you only get two lines for now - but working on a Ruby Script):

    1 REM 1746174701450148153617390147163?!!

    2 LET A=5+PEEK 23635+256*PEEK 23636: LET J=1
    3 LET BYTE=16*PEEK (A-1+(J*2-1))+PEEK (A-1+(J*2))-816: IF BYTE=-255 THEN GO TO 5
    4 POKE A+J,BYTE: LET J=J+1: GO TO 3

    5 LET PITCH=60+(0): LET MININ=1/(51+0)
    6 FOR F=1 TO J-1 STEP 2: BEEP PEEK (A+F)*MININ,PEEK (A+F+1)-PITCH: IF INKEY$<>"" THEN GO TO 8
    7 NEXT F: GO TO 6

    8 REM ...CONTINUE ONWARDS
    9 REM NB: DON'T USE RUN... TO REPLAY, USE GO TO 6


    The RubyScript part will encode each byte to a 2-chr string using something like this:


    FINAL CODE: A=DECIMAL, H=RegularHEX, Z=PseudoHEX

    eg:

    A=200

    H=A.to_s(16).upcase
    Z=""
    H.each_byte do |c|
    if c>64 then
    c=c-7
    end
    Z<<c.chr
    end

    Puts A ... should output 200
    Puts H ... should output C8
    Puts Z ... should output <8 ...which is our pseudo coded byte...!

    Just need to craft it into my Mid2ASM scripts and test it on a large file...

    Incidently, does anyone know how long a REM statement can be before the speccy goes belly up?... What is the limit for a REM statements length?...
  • edited August 2010
    There's a lot of maths and PEEKs going on there although I do appreciate the size issue.
    My original idea (apart from the "\" issue which is not actually a programming issue but a BasIn issue) was only ever 1 byte per number but does have the 96 number range issue so is out the window.

    I am wondering whether a step back to two different approaches which would be fast and simple to implement:

    Option 1: If Ruby can output binary, then it could write out the data in binary. THis option is only valid if you are happy to go down the "Ruby outputting bin data" route

    Option 2: Write a small M/C program to do the conversion for you. Generally these things are quicker and easier in M/C than BASIC, but again, I would understand if you want to keep it 100% BASIC.
  • edited August 2010
    I appreciate the suggestions there BloodBaz:

    1) Your right, the above code is quite complex, and my next challenge will be to stream line it... Most of its complexity is due to the stuff for poking or peeking in the REM locations rather than the translation stuff itself... I can save some speed by precalculating some of the values prior... Im guessing there are other things I can also do... Any ideas at streamlining would be appreciated!

    2) Your 1 byte per datum idea was brilliant... but Im not sure I can commit yet to only having values 1-96, as the beep range is something like -60 to +69, which is something like 128-9 values, and Im also allowing for the possibility of stray notes that get converted a little above that range, which will force the tool to shift the song key up or down to accomodate, without actually changing the data itself directly... I feel then somehow obliged then to be able to represent the full range of notes and maybe even a little more, to er on side of caution - hence the 2 chrs to byte approach...

    3) The Mid2Asm tool already spits out assembly listing and compiles it with pasmo prior spitting out a autorun tap file, so Im really now focusing on it spitting out BASIC listing now, to complement the assembly listing... Im thus trying to emphasis the differences between the two, rather than risk it spitting out two different outputs that have too much in common. When I focus on BASIC over MC, one of its differences I really notice is its visibility - what you see is what you get... You don't quite get that feel for MC, which has an air of mystery to it prior running at best, and has a totally unknown factor to it at worst (like if you run it from an emulator where hidden precompiled material already exists in memory before program start)... For beginner programmers, there is something reassuring and gratifying in being able to see the whole listing of a single unmodular program, clearly visible, represented on paper, in a magazine or elsewhere. Thats kind of what I want this listing to represent, hence it being important to be visible BEFORE I run the program (of course, after running it, the code in REM dissappears to mysteriously reside in memory)... So, I realise that my approach must seem very inefficient, but Im aiming to balence the efficiency issues with the issues important to amateur programmers who are used to seeing what they run and having it all in one listing, and one place...
Sign In or Register to comment.