This article will explain a new (?) technique to poke ASM (machine language) subroutines using Applesoft (without using ``POKE``s at ALL) and actually spare several characters if you're into 2-liners.
Because 2-liners have to be short (maximum 239 characters per line of code), 6502 subroutines used in 2-liners are usually short too. These routines might solve in a few bytes complex problems like generating a tune, scrolling a graphics screen or do a repetitive task best handled by machine code.
Because every character count, sometimes it's best to create "&" routines, USR() routines or routines that can be called in the form of ``CALL addr, param1, param2, ...``
Using a sound generating routine, we are going to see different techniques to interface assembly routines with Applesoft in the context of 2-liners.
The routines will be taken of the book "Assembly Lines" by Roger Wagner. The book is available freely as a PDF. It is a goldmine. I'm not sure if this is legal or not but whatever, here's the link to download a copy.
The latest technique is called 1-bit sound. It sends a voltage signal to the Apple speaker that will just produce a click because it has been "activated". It is 1-bit because it's either on or off: we're sending voltage or not.
To generate different tones we need to rapidly activate the speaker in two nested loops. The inner loop controls the pitch while the outer loop controls the duration ...
If we want this routine available for Applesoft, we have to either load it from disk (which is not allowed in a 2-liner) or to POKE it into memory before usage.
If this code was in $400 (TEXT page 1), it would display like this:
![screen capture](img/printsoundroutine.gif)
There are 3 INVERSE characters (byte value <64),oneFLASHcharacter(bytevaluebetween64and127),9NORMALcharacters(bytevaluebetween160and255)and1non-``PRINT``ablecharacter(valuebetween128and159).
To reproduce this we would have to do the following:
This is still 82 characters, not counting the ``HOME`` instruction. This instruction is needed for the code to work but is usually there nonetheless in most 2-liners.
* ``?"<CTRL-D>PR#3"`` activates the 80-column card. A carriage return is needed for this command, that's why it's a separated statement. Once this has been executed, we are in 80 columns mode, it means every other character is actually in auxiliary memory, so we need to deactivate 80-columns mode ASAP but not the 80-column hardware.
* ``<CTRL-O>`<CTRL-N>`` there is no FLASH when using the 80-columns hardware, but instead we have an extended INVERSE mode (that will print lower-case characters in INVERSE for example). The "flashing space" is actually represented by that apostrophe in INVERSE.
* ``POKE 1031,136`` so far we saved a lot of characters, unfortunately some characters cannot be ``PRINT``ed. That's the case of every value between 128 and 159 as well as the value 255. That's why we need to directly ``POKE`` these values in the appropriate memory spot.
(all these CTRL codes are explained in the 80-column cards manuals, for example [here on Asimov]( https://www.apple.asimov.net/documentation/hardware/video/Apple%20IIe%20Extended%2080-Column%20Text%20Card%20%28Rev%20B%29.pdf) )
This technique is great if you have a 80-column card.
However, one must admit that typing all these CTRLs every time you modify your code is arduous.
Of course as with every other ``PRINT`` instead of ``POKE`` technique, it means never scrolling the TEXT page or the routine would disappear.
Also, TEXT display is noticeably slower when the 80-column card is activated. You could deactivate it by adding ``CHR$(21)`` at the end of the ``PRINT`` statement but it costs you 8 more characters.
Well ... 48+8 = 56 characters. This is still outstanding.
Apart from needing a 80-column card, the main drawback to this technique is that for some routines, you'll need the help of a ``POKE`` or two because characters from $80->$9F (128 to 159) and $FF (255) cannot be ``PRINT``ed.
In fact, we we're lucky that our routine does not have more bytes with a value between $80 and $9F because this is where all the ``STA``, ``STX`` and ``STY`` 6502 opcodes are and these are very common instructions.
It will then take 11-12 additional characters per un``PRINT``able character to ``POKE`` it plus one for the separating colon !
If you have two un``PRINT``able characters it will take 23-25 additional characters. This brings us to a total of 78-80 bytes ! A third un``PRINT``able character and we are reaching the 92 characters of the "classic" DATA/READ technique.
Hexadecimal representation of bytes take only two characters, so it would be only 28 characters in the end. Of course Applesoft doesn't handle hexadecimal but maybe there are workarounds ?
There are ways to send monitor commands via Applesoft but even the monitor does not accept less than 3 characters per byte, that is two characters for the byte plus one space like "300:16 07 14 06 AD 30 C0 88 D0 FD CA D0 F5 60".
Sending monitor commands via Applesoft works like this (see http://nparker.llx.com/a2/shlam.html for more info)
Starting with our coded ``A$`` string, instead of using ``MID$()`` and ``ASC()`` to get the value to ``POKE``, we could "read" the value directly from it's location in the code.
The Applesoft code begins in \$800 (2048). The first letter ("K") after ``A$="`` is in location $809 (2057) in memory. We simply read those values directly from there.
Here's a screenshot of the code and how it's stored in memory.
![screen capture](img/hexstring_incode.gif)
* \$800: always $00
* $801-$802: memory location where the program's second line begins (the first line always begins in $803). The second line begins in $0827.
* $803-$804: line number (zero)
* $805-$826: the code of line zero
* $805: equals $41, that is ASCII #65, that is "A"
* $806: equals $24, that's ASCII #36, that's "\$"
* $807: equals $D0, this is not an ascii code, it's an Applesoft token representing "="
* $808: equals $22, that's ASCII #34, this is the quote sign (")
* $809-$824: our string in ASCII
* $825: the closing quote
* $826: $00 means this is the end of the line
* $827-$828: memory location for the third line (does not exist)
* $829-$830: line number (1)
* $831-$860: code for line 1
* $861-$862: memory location for the 4th line. As it's ``00 00`` it means there are no fourth line (and no third line either).
Anyway, all in all, our Applesoft code still takes 105 characters long !
There are variations to this technique: instead of having an assignment to a variable we could have a ``DATA``or a ``REM`` and access its precise memory location in the code. ``DATA`` is in fact shorter of 1 character (because you don't need quotes. ``REM``has the inconvenient of having to be used as the last statement of the line.
Notice that it can be used for all kinds of subroutines .... just be aware that we're "printing" routines and that the TEXT page lines are not sequential (line 1 is not in $400+40 chars). It works with any value from 0 to 255 and it's almost as easy as to type the actual hexadecimal values in the monitor.
And we will print every high-nibble of each char on line 2 of TEXT (which is line 2 of GR), this means we print "J0J0J3L8MOLMO6"
The result is the following:
![screen capture](img/printplotsound1.gif)
If you watch closely, you'll notice that the low nibble is already in place in $400 for our first byte (we have the "6" of $A6) and that the low nibble of the value in $480 ("A" from $CA) is the value we need to place as the high nibble of our first byte.
We must find a way to leave unchanged the low nibble of each byte in line 1 and use the low nibble of each byte in line 2 as the high nibble of each byte in line 1.
### SCRN, COLOR & PLOT
As you know, SCRN will return the color of a "point" in Lo-res. You also know that one TEXT character is represented as 2 "vertical" points of various colors in lores.
Here's what our two PRINTs look like in GR:
![screen capture](img/printplotsound2.gif)
You see 4 lines of colored points because we printed 2 lines of text.
The colors in line 0 correspond to the low-nibble (4 bits) of the bytes in $400->$40D while the colors in line 1 corresponds to the high-nibble of the bytes in the same range.
And of course, the same goes for lines 2 & 3... Now all we have to do is copy the points in GR line 2 (low-nibbles of bytes in $480-$48D) to line 1 (high nibbles of bytes in $400-$40D).
The result is that in line 0 and 1 of GR, we'll have our sound routine.
This method can be used to POKE/PLOT longer routines ... just make sure to take into account the fact that one line is 40 chars max, so if you need to handle more bytes, simply add a embracing loop to repeat as needed ... don't forget you can do "NEXT I,J" instead of "NEXT:NEXT" !
This means saving 12 characters every time you want to emit a sound.
However, the routine itself (see page 148 of the book) is not 14 bytes but 24 bytes. That's 10 bytes more.
If you want to output just one "unsual" sound (not CHR$(7) and not PEEK(49200)), use the 14 bytes routine.
But if you need more tunes, use the 24 bytes routine !
The 24 bytes routine uses 95 characters by itself ... it's almost as good as the "usual" routine we presented first that had 92 chars but it will take 9 characters less to call it !