Here's an updated version of the program that's better written, at least from a batari Basic standpoint. And here are some general notes in case anyone wants to use executable RAM code (whether or not it's self-modifying). Since the sample program is in batari Basic (except for the actual RAM code, which is in machine code), this post really belongs in the batari Basic forum, but I'm leaving it here since the notes also apply to assembly programming.
(1) If you want to put executable code in RAM, be sensible about it. If the code can be put in ROM instead, don't waste time copying it to RAM just so you can execute it in RAM. For instance, my sample code could have accomplished the same result more easily by simply using a variable to store and update the value for COLUBK. The *only* reason I used self-modifying code in this case was to create a simple test that can be used to see if code can be stored and executed in Superchip RAM. In general, the main benefit of putting code in RAM is so you can modify it as needed when you want the code to change depending on the situation. And in many cases you'll really need only a small portion of code to be in RAM, in which case you can run regular ROM code, jump to the small bit of changeable RAM code, then jump back to ROM. Remember, you have to copy the code into RAM before you can execute it, and the more code you need to copy, the longer it will take to copy. Nevertheless, there may be times when it's beneficial to put code in RAM so you can modify it, such as if you want to use "LDA #immediate," "LDX #immediate," or "LDY #immediate" instructions for greater speed, but the values need to be variable. (Then again, you could use the Harmony's "fast fetch" mode to accomplish the same thing.)
(2) If you're putting code in RIOT (zero-page) RAM, you can read from and write to a RAM location using the same address. But for most on-cart expansion RAM, you need to read from RAM using one set of addresses, and write to RAM using another set of addresses. (One notable exception-- perhaps the only exception-- is the RAM in supercat's "4A50" bank-switched cart format.) From a practical standpoint, this means there will be certain things you can't do in Superchip RAM code, such as "INC absolute" or "DEC absolute" to modify the contents of RAM, since those instructions would require the CPU to read from and write to the same memory address. Also, remember that the CPU must fetch each instruction from memory so it can be executed, so you need to run the RAM code using the read addresses.
(3) If you want to add RAM code to a batari Basic program, be aware that the batari Basic compiler prefixes each line label with a dot (period), so you won't be able to "goto" or "gosub" to a RAM label unless you've defined the RAM label as having a dot prefix. Then you should leave off the dot prefix when you use the RAM label in a "goto" or "gosub," since batari Basic will automatically add the dot prefix when you compile your program. In my sample program, I "dim" a subroutine name with a dot prefix, equating it to one of the default Superchip RAM variable labels ("w000" through "w127" for the write addresses, and "r000" through "r127" for the read addresses).
(4) In my first sample program, I loaded the RAM code by essentially "poking" each hex value (or machine code instruction) into a Superchip write address, one at a time, without using a loop. That's okay for very short code, but for longer code it will be more efficient to load the code into RAM using a loop, as I've done in this revised sample program. Note that I've put the code into a "data" statement, and even included the equivalent assembly code as comments. Since batari Basic automatically creates a constant with the length of the data table by appending "_length" to the end of the data table name, I was able to use the length constant to help me load the code in a "for-next" loop without having to count the number of bytes first.
rem * Superchip RAM Test for Executable Code
set romsize 8kSC
dim .Cycle_COLUBK = r000
a = SC_RAM_code_length - 1
for b = 0 to a
w000[b] = SC_RAM_code[b]
next
loop
gosub Cycle_COLUBK
drawscreen
goto loop
data SC_RAM_code
$A2,$00 ; LDX #0
$86,$09 ; STX COLUBK
$E8 ; INX
$8E,$01,$F0 ; STX $F001
$60 ; RTS
end
Michael