This aricle will explain a new (?) technique to poke subroutines using Applesoft (without using POKEs at ALL :D ) and actually spare several characters if you're into 2-liners.
## Generating various sounds in 2-liners.
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 activate the speaker in two nested loops. The inner loop controls the pitch while the outer loop controls the duration ...
This is usually done with some 6502 using delays between accesses to memory 49200 ($C030) ...
One of those routines is provided in Assembly Lines book by Roger Wagner (p. 57).
The routine is the following:
```
0300- A6 07 LDX $07 ; load value in X from byte $07 as duration
0302- A4 06 LDY $06 ; load value in Y from byte $06 as pitch
But 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 usually there nonetheless in most 2-liners.
If we have a 80-column card we can also use the following code:
* ``?"<CTRL-D>PR#3"`` activates the 80-column card. A carriage return is needed for this command, that's why it's separated from the rest of the code. Once this has been executed, we are in 80 columns mode, it means every other character is actually in auxialary memory, so we need to deactivate 80-columns mode ASAP but not the 80-column hardware.
* ``<CTRL-Q>`` CTRL-Q goes back to 40 columns mode with the 80-column hardware still active.
* ``<CTRL-O>`` every CTRL-O activates INVERSE mode
* ``<CTRL-N>`` while CTRL-N brings back NORMAL mode. Don't forget to end your string with a CTRL-N or you'll be in INVERSE mode after running the code
* ``<CTRL-O>`<CTRL-N>`` there is no FLASH when using the 80-columns hardware, but instead we have an extended INVERSE mode. That's why we print the equivalent of that normally flashing space.
As you can see the routine has been perfectly PRINTed into memory. This technique is perfect if you have a 80-column card.
However, one must admit that typing all these CTRLs every time you modify your code is arduous. And of course, it means never scrolling the TEXT page or the routine would disappear. And also, TEXT display is noticeably slower than 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. Still 43 characters is still outstanding.
Hexadecimal representation of bytes take only two characters, so it would be only 28 characters in the end. This is the way to explore/experiment.
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 + one space like "300:16 07 14 06 AD 30 C0 88 D0 FD CA D0 F5 60".
Such a program would be like this (see http://nparker.llx.com/a2/shlam.html for more info)
The simple fact that we need 3 characters to "express" one byte is intolerable ...
What if we could use hexadecimal (without spaces) right in Applesoft code ?
Here's a possibility:
The technique here is a double trick: first it uses the string stored in A$ directly from the Applesoft code (in $800), reading the bytes in A$ as stored in the program in $809 (decimal 2057).
The second trick is that A$ contains "coded" hexadecimal code. An "A" equals to a "0" and a "P" equals to an "F" (ascii of "A" + 15).
So, A6 07 A4 06 AD 30 C0 88 D0 FD CA D0 F5 60 translates to KG AH KE AG KN DA MA II NA PN MK NA PF GA
And then we have :
0 A$="KGAHKEAGKNDAMAIINAPNMKNAPFGA" : REM 33+1 chars
1 S=768: FOR I = 2057 TO 2084 STEP 2: POKE S+N, (PEEK(I)-65)*16 + PEEK(I+1) - 65: N=N+1: NEXT: REM +74 chars = 108 chars
That could be further reduced to
0 A$="KGAHKEAGKNDAMAIINAPNMKNAPFGA" : REM 33+1 chars
1 S=768: FOR I = 2057 TO 2084 STEP 2: POKE S+N, 16*PEEK(I) + PEEK(I+1) - 1105: N=N+1 : NEXT: REM +71 chars = 105 chars
Not really helping us !
So here comes the technique I've developed for this particular case.
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)
This new (?) technique involves using four very simple instructions: PRINT, SCRN, COLOR and PLOT.
We will be using the GR/TEXT capabilities of Applesoft to poke a program in TEXT page 1.
How does it work ?
First we start from our 14 routine bytes: A6 07 A4 06 AD 30 C0 88 D0 FD CA D0 F5 60
We leave every number as it is, but we replace all the letters with new letters according to this:
A becomes J, B becomes K, C->L, D->M, E->N and F becomes O.
We now have J6 07 J4 06 JM 30 L0 88 M0 OM LJ M0 O5 60
We will now take advantage of the GR/TEXT screen and the SCRN function.
We are going to print every low-nibble (4 bits) of each char on line 1 of TEXT (which is line 0 of GR), this means we print "6746M0080MJ050"
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"
Then, we will move/copy line 2 of GR, using SCRN to get its "value" (color), to line 1 of GR.
The result is that in line 0 of GR, we'll have our sound routine.
Here's the resulting code:
0 HOME: REM 4 chars (not counted)
1 ?"6746M0080MJ050": REM 17+1 chars
2 ?"J0J0J3L8MOLMO6": REM +17+1 chars = 36 chars
3 FOR I = 0 TO 13 : REM +10+1 chars = 47 chars we're going to SCRN that TEXT line in VTAB 2
4 COLOR = SCRN(I,2): REM +15+1 chars = 63 chars we have a value 0-15
5 PLOT I,1: REM +7+1 chars = 71 chars we PLOT it on line 1, actually adding 16*color to the byte in $400+I
6 NEXT : REM +4 chars = 75 chars
Even with the "HOME" statement at first (which is needed but might already be included in your 2-liner), we have 75+4+1 chars = 80 chars
This is still better than the traditional POKE technique ...
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 you
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" !
Of course, if you need line 1 of TEXT or line 0 of GR, you'll see the routine ... it's probably better using Hires 2-liners....
One last thing.
Roger Wagner's assembly lines contains another routine to handle sound that might be very useful for 2-liners.
Instead of using
POKE 6,P: POKE 7,D: CALL 1024
it's using
CALL 1024, P, D
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 !
10 HOME : REM 4 (not counted)
20 ? "0L7660L7676746M0080MJ050" : REM 27+1
30 ? "24N8024N80J0J0J3L8MOLMO6" : REM +27+1 = 56
40 FOR I = 0 TO 23 : REM +10+1 chars = 67
50 COLOR = SCRN(I,2) : REM +15+1 chars = 83
60 PLOT I,1 : REM +7+1 chars = 91
70 NEXT: REM +4 chars = 95
100 REM LET'S HEAR SOMETHING
120 FOR I = 0 TO 255
130 CALL 1024 , I, 10
140 NEXT
I hope you enjoyed this little tutorial on "how I did it" ! ....
Can you do better than this ? If so, don't hesitate to post, I'd be happy to see what you've come up with !