I don't remember seeing this on Commodore 64 too much. Most type-in hybrid BASIC-machine language programs would have numerous DATA statements with opcodes and a loop to READ and POKE them (typically in the tape buffer area somewhere in the fist 2K of RAM or in the 4K area at 49152/$C000).
Commodore 64 BASIC's LOAD"WHATEVER",8 command would load a file of any size in the BASIC text area without validation or checking it for any type of format. So much commercial C64 software often had a one line "10 SYS2063" (or similar) stub that would simply jump into the actual code which was tacked on to that BASIC stub. But the LIST command wouldn't display anything after the stub - I guess the "next line" bytes were set to $0000 to indicate end of program.
Here's a recent example I made: an Amstrad CPC program that's both valid BASIC and machine code at the same time, and running it both ways (RUN or CALL &170 -- loading address of the BASIC program) produce the same output: https://github.com/ssg/yello
I'm persuaded that just about every language is "homoiconic" and that _bicameral syntax_ is the actually-interesting thing about Lisps : https://parentheticallyspeaking.org/articles/bicameral-not-h...
But maybe this is part of what Gerald Sussman was talking about when he called Lisp a "low level language": like machine code, Lisp has the nice property that its default data representation is used for building directly executable programs.
The general purpose computers we have today all follow that principal, yes. However it's not a fundamental fact of computing. Turing machines, cellular automata provide other paradigms. And if those aren't practical enough for you, ASIC programming is Turing complete but cannot access its code as data.
So yes, you can't modify the actual embodiment of the state transition if it does not offer you the capability to do so. But as long as it interprets data in a TM manner (input and output are colocated and the alphabet is the same), you can write code that will interpret data as code and manipulate code as data. Not by modifying the base substrate, but by adding a virtualization layer on top.
Everything else is data.
By the time you get to programming language like C and LISP, and software like vim or firefox, everything is abstracted away and the only limits are self-imposed (or imposed by others that have done the work). They are not laws of nature.
So yes there’s a definition of code and data. But for the Turing machine, there’s only the tape and its content.
Incredible to find out that typing the paper program would not have worked, though. And in such a disappointing way no less.
I sometimes wonder if there is still a lot of that magic in the tech world, but were so used to having everything abstracted away from us in multiple layers that we've lost the ability to look for it.
I think about the TLA hackers who do seemingly impossible things. Surely they must operate on this level.
One of which was some sort of mystical “reflect on the runes” program that printed ancient symbols to contemplate. It had a written comment in the docs that it would self destruct after three attempts “to guard against frivolous use”. And, sure enough, if you typed it in without understanding it - there was the BASIC command that soft reset your computer on the third iteration! I can’t remember that command now - RANDOMIZE USR 0 perhaps? Happy days.
https://zx.tr/basinc/help/topics/manual_appd.html, scroll down. "Here is a program to throw coins for the I Ching. (Unfortunately it produces the patterns upside down. but you might not worry about this.)"
Last statement: NEW.
https://bunsen.itch.io/the-snake-temple-by-rax
IN 10 LINES OF BASIC!!
I thought it was cute that it used the RND() function to seed things necessary to generate each room .. so I used that technique in my own Oric game, which generates a large blob of graphics data using the RND() seed, meaning I don't have to ship that data in the program itself. I find the pseudo-ness of the RNG infinitely resourceful in that regard ..
Another cute trick on the Oric Atmos is to seed the 14 registers of its synth chip with ROM data .. meaning the Oric Atmos has literally THOUSANDS of onboard sound presets to choose from - once you find them, of course. So I wrote a program to find the most interesting ones and have been accruing a list of "synth presets" that come onboard with the Oric, for use in my game.
Its quite rewarding to go back to these machines and use such techniques to generate nearly-infinite levels/sounds/etc.
I can't remember how the ORIC BASIC does it but a cool property of LFSRs as PRNGs is that if you pick the right taps and length they must cycle through every possible number from 1 to (2^length)-1 exactly once, excluding zero. That's how the Wolfenstein "Fizzle Fade" effect worked - set up an LFSR long enough that every possible pixel can be visited once, with a few left over, and then throw away any that don't fit.
Some examples here, as sadly the apple2twitter bot shut down and deleted their account a while ago.
Then you'd do "RANDOMISE USR 16514" to start it. Actually USR was interesting because it would pass back the contents of BC when your machine code routine returned to BASIC.
Based on this, I wonder if it's worth trying the following:
Note that you'll probably need an emulator with accurate keyboard emulation - or a real device - in order to type these in. However, with the emulator from http://www.liaquay.co.uk/sorcerer, I was able to confirm that Graphic+Shift+0 produced 201 (rendered as F4), and Graphic+Shift+= produced 255 (rendered as S), so I think this approach will work.