C64 - Hiding machine language in a REM statement

Поделиться
HTML-код
  • Опубликовано: 2 окт 2024

Комментарии • 71

  • @3DPDK
    @3DPDK 3 месяца назад +12

    🔹One other limitation: You can not add or delete any basic line *before* the REM statement. When the BASIC editor moves the REM to make room for the new line, it replaces any reverse character (any number in the ML above 128) with it's non-reversed version (basically subtracting 128 from the number). You actually can add or delete a line before the REM statement, but you must re-enter the ML in the monitor and understand that the start address of the ML and any specific target address within the code will change.
    🔹If you absolutely need to LDX #$00 (or similar) use LDX #$01; DEX or use LDX #$FF; INX. Either of these adds an extra byte to the ML code. Also keep in mind the first sets the Zero flag and the second sets the Carry flag which may or may not be important to the rest of the code.
    🔹You can not STA $0400 (top left screen character) or any other memory page boundary address because of the low byte "00". You can get around it by using Indirect Indexed Y addressing and figure out a way to get a "0" in the low byte of the pointer address.
    LDX #$01
    DEX
    STX $0334 (820)
    LDX #$04
    STX $0335 (821)
    ...
    ...
    STA ($0334),y (the pointer address held at 820/821 is 1024 ($0400) or the top left character position of the screen)

    • @retrooldguy9880
      @retrooldguy9880  3 месяца назад +3

      Good point about adding lines above the REM. I forgot to mention that. If you do accidentally do that, deleting that line will fix it without having to re-enter the ML. Also if you try to add to or edit the REM or even put the cursor on the REM with the ML already placed and hit ENTER, it will likely mess up the ML and you'd have to re-enter the ML code. I'm going to PIN this at the top. Thanks for pointing that out.

    • @digitalnomad9985
      @digitalnomad9985 2 месяца назад +2

      @@retrooldguy9880 I did something similar commercially for a customer's custom program in the 80s on a Color Computer. To save memory, I embedded a bunch of binary data and a short (hand compiled) machine language program in data string literals where the memory pointer of the string variable in the "READ" statement gets pointed to the actual literal in memory in the quotes. In addition to the "end of statement" binary marker (I think CoCo BASIC also used binary zero) I had to avoid using the "quote" character or I'd destroy the syntax of the DATA statement. I PEEKed around the listing to find the DATA statement, then POKED the values into the listing by hand, as well as hand assembling the program, because I didn't have a memory monitor or an assembler. I didn't have to guess where the actual data was in memory at runtime, because the interpreter took care of that. I could edit the rest of the BASIC program with impunity because the whole thing was functionally relocatable.

    • @csbruce
      @csbruce 2 месяца назад

      There's no absolute-indirect-indexed addressing mode. The pointer address needs to be in zero-page.

    • @3DPDK
      @3DPDK 2 месяца назад +1

      @@csbruce You are correct. Indirect Indexed Y or indexed X addressing must use a zero page pointer. The code snippet should be:
      LDX #$01
      DEX
      STX $FB (251)
      LDX #$04
      STX $FC (252)
      ...
      ...
      STA ($FB),y

  • @MatsEngstrom
    @MatsEngstrom 3 месяца назад +10

    Ah yes, I did this back in 10'th grade (1982?) on a Swedish manufactured machine named ABC-80. A Z80 machine with a built-in BASIC interpreter. I remember spending most of my non-class time in school coding away at a Space Invader game. Some parts of it was in basic and some in Z80-assembly manually POKEd into long REM-statements to speed up the game to something playable. There was no assembler on the machines so I had to assemble by hand and make sure all memory references and jumps was adapted to where the REM was in memory. And of course no debugger. So either it ran or it did't and I had to figure it out "in the blind". It took quite a while but in the end I got it running.

    • @pauligrossinoz
      @pauligrossinoz 3 месяца назад +1

      😲 Wow! That's really hard-arse! 😁👍

    • @MatsEngstrom
      @MatsEngstrom 3 месяца назад +2

      @@pauligrossinoz I was a super-nerd back then, having coded in assembly (but 6502) since 5th grade. ;) Actually I'm still a super nerd today. It's not been many days since then that I've not written some code at. I probably have 10 or 15 more years to go before I give it up alltogether.

  • @csbruce
    @csbruce 3 месяца назад +7

    The character set can be turned to lowercase by PRINTing CHR$(14) and to uppercase with CHR$(142).

  • @RacerX-
    @RacerX- 3 месяца назад +10

    Fascinating. I have seen all kinds of REM tricks over the years but I have never seen this one. Excellent trick, Thanks for sharing!

  • @JustWasted3HoursHere
    @JustWasted3HoursHere 3 месяца назад +5

    I remember back in the day being completely confused by a BASIC program that only consisted of "10 SYS 49152" or something similar. Years later I discovered what was going on here. Another clever trick I remember from the old C64 days was something along the lines of this:
    10 IF A=0 THEN A=1: LOAD "PART 1",8,1
    20 IF A=1 THEN A=2: LOAD "PART 2",8,1
    30 IF A=2 THEN A=3: LOAD "PART 3",8,1
    and so on for as many parts as you needed. Essentially, when the first program exited, BASIC would always start reading from the beginning of the BASIC program, but the value of variables would be retained, so checking for and then changing the value of A would allow each section to be loaded in turn. Pretty clever!

  • @CityXen
    @CityXen 2 месяца назад +1

    Very cool vid, thx for sharing. Cool idea to cover this topic, however unpractical it is. It is still a cool reminder of the practically infinite possibilities capable of the c64.

  • @FlemmingSteffensen
    @FlemmingSteffensen 3 месяца назад +1

    Fascinating. I never thought about it. Great example.
    With hiding the ugly REM statement at 7:06, can't you insert a $0, and the address to the next line as part of the REM statement, and move the sys address a few bytes ahead in memory. It might also save you from all the delete characters in your PLOT AT example. My guess is the EOL is only actually used for listing/running, but not for saving.

  • @JxH
    @JxH 3 месяца назад +1

    Many decades ago (back in the day, early 1980s), I was able to hide BASIC in BASIC. I did this on the TRS-80 Color Computer, but the same approach would likely work on any early computer with MS-BASIC. The approach is to pack the program with an obscure character, such as "\". Then run a routine (e.g. up in line 1000) to find (PEEK) and replace (POKE) the character with backspace (08). Then delete the extra routine. The hidden backspaces overwrite what is displayed with LIST, leaving only what you wish to appear in the REM part of the line.
    End result is (for example):
    LIST
    10 PRINT "NO NO NO"
    RUN
    YES YES YES
    Explanation = 10 PRINT "YES YES YES"; REM \\\\\\\\\\\\\\\\\\\"NO NO NO"
    One can make LIST appear to RUN and RUN appear to LIST.
    Anything is possible...

  • @PlayBASIC-Developer
    @PlayBASIC-Developer 2 месяца назад +1

    This bring back so many memories !

  • @ChannelReuploads9451
    @ChannelReuploads9451 2 месяца назад +1

    I know this is C64, but I always wondered how they got Machine code in to Rem Statements in ZX Spectrum Basic, and this pretty much explains it for me, First line, Rem "

  • @TheUtuber999
    @TheUtuber999 3 месяца назад +2

    Cool technique, thanks! For the first example, you can also skip using a monitor and just copy the bytes over directly...
    10 fori=0to16:reada:poke828+i,a:next
    20 sys828
    30 data173,32,208,72,238,32,208,32,228
    40 data255,240,248,104,141,32,208,96
    run
    new
    10 rem"xxxxxxxxxxxxxxxxx"
    20 sys2055
    fori=0to16:poke2055+i,peek(828+i):next
    run

  • @91F2Z
    @91F2Z 3 месяца назад +1

    Oooo, brings back my C64/128 days. My brain is stretched.

  • @Robert08010
    @Robert08010 3 месяца назад +2

    "Super Mon... who can change the code of Basic programs... bend pins with his bare hands,
    and who disguised as a simple assembler for a great american computer company...
    Compiles the never ending stream of apps, drivers via the programmers way!

    • @retrooldguy9880
      @retrooldguy9880  3 месяца назад

      It was Jim Butterfield of course. But reading your comment, made me think of Mary Tyler Moore for some reason. Maybe it was the cadence.

    • @digitalnomad9985
      @digitalnomad9985 2 месяца назад

      @@retrooldguy9880 You're probably too young to remember the old black and white Superman TV show.

    • @retrooldguy9880
      @retrooldguy9880  2 месяца назад

      @@digitalnomad9985 that was ten years before my time. I did watch it in syndication as a kid in the early 70s though

  • @jamesslick4790
    @jamesslick4790 3 месяца назад +1

    WHOA! This is so cool! 👍👍Earned a sub. MY C64 is only an emulator, But I hope to get some real gear! I was a C64 user in the 80's and this stuff (to me is STILL more fun to mess with the "innards" than the modern stuff!)

  • @innova500
    @innova500 3 месяца назад

    Very cool stuff my friend. i built my first cnc machine with the vic20. it didnt work very well and i ended up moving to the ibm clone. This trick would have solved problems i couldnt figure out at the time... subbed,,,

  • @piggypiggypig1746
    @piggypiggypig1746 3 месяца назад +3

    Sneaky. I love it!

  • @jack002tuber
    @jack002tuber 3 месяца назад +2

    One trick is to put the ML in it last and then save it. Once you edit the basic program the work is lost. Not fun to edit but you can make it work

    • @MrChannel42
      @MrChannel42 3 месяца назад

      I saw this used for cheat prevention(?) back in the day (Hitchhikers guide to the galaxy game on the microbee iirc). They poked clear-screen characters into REMs at random points in the file for the release version so that LIST would be... inconvenient.

  • @edgeeffect
    @edgeeffect 2 месяца назад

    This was the defacto way of doing machine code on the ZX81 and Spectrum but i think the clever trick for hiding the gibberish would only work on a Commodore... nice!

  • @roberthuff3122
    @roberthuff3122 3 месяца назад +1

    Stuxnet!

  • @EelkodeVos
    @EelkodeVos 2 месяца назад

    I've seen program lists back in the 80's like this and always was triggered by the actual very short time where you can see the line is printed, then deleted and other characters are written over the same line. Sneaky, but still visible by the human eye. And of course when you notice it, you get kids like me studying it through my machine language monitor. #PowerCartridgeAllTheWay

  • @alunroberts1439
    @alunroberts1439 2 месяца назад

    Wish I did keep the 64 I did have interesting stuff

  • @microdesigns2000
    @microdesigns2000 2 месяца назад

    This feels like the kind of ingredients that viruses are made from.

  • @NoahSpurrier
    @NoahSpurrier 2 месяца назад

    Brings back old memories.

  • @Huppat
    @Huppat 19 дней назад

    nice little trick

  • @AllanFolm
    @AllanFolm 2 месяца назад

    Have you considered placing a shift-L right after the line number 10 REM?

    • @retrooldguy9880
      @retrooldguy9880  2 месяца назад

      Not when making the video. A few other mentioned that, so I tried, It will keep the garbage from appearing because it stops the listing. The program will work fine. To see the rest of the program all you have to do is LIST 11-

  • @StefanTravis
    @StefanTravis 3 месяца назад

    I used to do this on the ZX Spectrum. It was amazingly easy to get just one figure wrong, and crash the computer... or occasionally give a weird cool effect.

  • @carminone
    @carminone 3 месяца назад +1

  • @schitlipz
    @schitlipz 3 месяца назад

    You can also do special PETSCII in REM statements so when listing you can spice up the look. Or just a shift+L after a REM will stop the listing. I'm sure somebody already mentioned it.

  • @melkiorwiseman5234
    @melkiorwiseman5234 2 месяца назад

    I read about another method which was almost the same, except that it was done using a string literal. The advantage of using a string literal is that you can use the VARPTR function to find where the string is stored without having to manually search for it. Once you have POKEd the entire machine language routine into the string, you can then delete the code which loads the machine language routine into the string and save the program without those lines. But of course the same problem of having the machine code display as control characters applies, unless you save the program before the machine code has been POKEd into place and leave those instructions intact.
    While it's possible to force the string to be copied out of the literal and into string space, that rather goes against much of the reason for putting the machine code into a string literal in the first place. However what it still does is to remove the requirement to create "reserved" memory before entering or loading the program.

  • @momoandakawa
    @momoandakawa 3 месяца назад

    this is how it was done on the Spectrum as well: first BASIC line was a REM with Z80 assembly op codes. iirc you could also set the color to white-on-white with CHR$(). second line executes machine code using RANDOMIZE USR + known address of begin of basic
    =]

  • @philpeko1796
    @philpeko1796 2 месяца назад

    I'm an old guy too, I don't know much of your previous work here, but I have been writing some old Basic too, (and ASM since) your video is ... wicked, you made me laugh for the hiding idea. Peace & Prosper. ❤🖖

  • @lestersegelhorst2776
    @lestersegelhorst2776 3 месяца назад

    As a kid, I remember seeing this on some games I tried to decode. (Before I knew what machine language was) I thought it was purely a copy protection scheme.

  • @bpark10001
    @bpark10001 2 месяца назад

    Isn't there a way to link machine language program with some sort of subroutine call? In the AIM-65, you set up a USR (user function) call to the program.

  • @TheUtuber999
    @TheUtuber999 3 месяца назад

    2:30 "When the program is run, we get that ugly flashing border effect." 😆

  • @codingbloke
    @codingbloke 3 месяца назад

    So what happens if one of the machine code bytes is D6 the " character?

    • @retrooldguy9880
      @retrooldguy9880  3 месяца назад

      Nothing. It still RUNS and LISTS as normal. Try this on your computer, type 10 REM """ (3 quotes) and ENTER. It will accept them without any error. D6 is just a placeholder, to be replaced by the ml codes, if one happens to be D6 (DEC,x) that's fine.

  •  3 месяца назад

    Would it be possible to hide the binary code *in between* the basic lines? I mean, create a block of normal basic lines (with REM probably) followed by some normal basic program and then cut the first REM line short with zero and pointer to the first actual program line, which would give us space hidden in between the basic lines, which we could fill with anything.
    Is that possible or is there something, that would prevent it from working? Like some automatic cleanup of space in between the lines or something.

    • @retrooldguy9880
      @retrooldguy9880  3 месяца назад +1

      Hmm. I just tried that and it did work, but you have the same problem as before. You have to create the space with something (REM "XXXXX", works best). As soon as you start entering code you will have the same problem with garbage/control characters messing up the LIST and possibly keep the program from running, and you still can't use a zero byte as the interpreter will see that as EOL as explained in the video.
      This was meant to be a neat little hack for short routines. If you want to have a lengthy chunk of code as part of your BASIC program, it would be better to put it at the end of BASIC where there are no restriction, as I demonstrated in a previous video: ruclips.net/video/3arpo-9FbCk/видео.html

    • @melkiorwiseman5234
      @melkiorwiseman5234 2 месяца назад

      @@retrooldguy9880 Put the zero byte at the start of the REM string, followed by the address of the start of the next line. Start the machine code routine after that. That might trick the LIST command into not even looking for a zero byte between the pointer to the next line and the end of the current line.
      In other words, if your machine code routine is 10 bytes, then put 13 characters after the REM. When you're entering the machine code routine, first enter a zero byte then the address of the start of the next line followed by the machine code routine.
      Is that what you did? Because it sounds like an interesting thing to try. See whether or not it saves and loads correctly, too. Off-hand, my guess is that saving the program as an ASCII file would not work correctly, while saving it as tokenised code would work.

    •  2 месяца назад

      @@retrooldguy9880 Thank you.

  • @z352kdaf8324
    @z352kdaf8324 2 месяца назад

    @5:10 you didn't need to re-do all that, but I suspect you know that. Just enter the del key string.. Well I never had to hide anything in REM statements, I always had a Super Snapshot V3 to do my assembly in.

  • @Volker-Dirr
    @Volker-Dirr 3 месяца назад +1

    Won't there be a problem as soon as the hex 22 (so the quote character) is used?

    • @tabularasa0606
      @tabularasa0606 3 месяца назад

      I don't think so, since the interpreter has the string in a token. The double quotes around the string only exist for human readability.

    • @retrooldguy9880
      @retrooldguy9880  3 месяца назад

      Actually it doesn't. I tried it out after reading your comment. I created this line: 10 REM """ (3 quotes) in the basic editor and it was accepted with no error. The program will still list and run OK. Give a shot.
      In the video though, the interpreter was reading the bytes 22 00 ($0022) as the link address to the next BASIC line, so it would jump to a zero page address that is used for temporary pointers and calculation results, where it would likely just dead end and stop execution or possibly crash the computer.

    • @Volker-Dirr
      @Volker-Dirr 3 месяца назад

      @@retrooldguy9880 Sorry, I don't mean in the Basic. I mean "22" in assembler. So if for example you try to LDA the hex 22 (ok, probably that is very rare) or if there is an assembler command with the value 22.But i don't know if there is any one. hmm... thinking one more time: Probaly you are right, if it works with Basic, it should work with assembler also. I will try at the weekend.

  • @TechStuff365
    @TechStuff365 3 месяца назад

    I remember similar on the zx81, the basic program would be a rem statement and a loader, run it and type the machine code in hex into the loader which would place it in the rem statement. Some programs even used the 300 bytes of the tape buffer. One mistake and the whole thing would crash and be lost.

    • @Harfinou
      @Harfinou 2 месяца назад

      Looked like :
      10 rem ##{#~#~|`{[`{~
      20 rand usr xxxxx
      Am I right ?

  • @theantipope4354
    @theantipope4354 3 месяца назад

    Evil, but brilliant!

  • @denismilic1878
    @denismilic1878 3 месяца назад +1

    You go a subscriber, a very picky one. I already know the REM trick, but I never used 🆃 to tidy it up.

  • @Adrian.Rengle
    @Adrian.Rengle 3 месяца назад

    Oldies but Goldies !

  • @justinparrtech
    @justinparrtech 3 месяца назад

    Very cool!!

  • @lezbriddon
    @lezbriddon 2 месяца назад

    not trying to be pedantic but isnt it 'machine code' when its down at an executable byte/word instruction level, and anything that needs compilation or interpretation is 'language'?
    edit: I googled, I'm not keeping up with the times....... Machine code, also known as machine language, is the elemental language of computers.

  • @JanBruunAndersen
    @JanBruunAndersen 2 месяца назад

    I don't see the point of doing this. Okay, if it is important to hide the machine code from prying eyes, then maybe, but considering that it is limited to 80 bytes per REM statement, and that anyone sufficient interested would know how to enter the debugger and just look, then how much value and secret can those 80 bytes be?
    On top of that, with all those DELETE characters added in, you are using 160 bytes of memory to hide 80 bytes.
    As an intellectual exercise, sure. It's neat, and putting the instructions for calling the code into the REM statement, that is nifty too. But at the end of the day, I would just put the hex-codes into DATA statements and have a little FOR/READ loop POKE the bytes into place. It is easy to edit, no limitations on the size of the code, no problems with adding lines to the BASIC program itself, etc.

    • @retrooldguy9880
      @retrooldguy9880  2 месяца назад +1

      It's just a neat little hack. I wouldn't recommend trying to do an entirely ML program like this. But, you got to admit, the PLOT routine fit in perfectly.

    • @z352kdaf8324
      @z352kdaf8324 2 месяца назад

      It does teach how they can occupy same space in memory ..

  • @simona625
    @simona625 3 месяца назад

    Could you not use multiple rem statements ?

    • @retrooldguy9880
      @retrooldguy9880  3 месяца назад

      Theoretically yes. But I didn't try, it would be pretty impractical as you'd have to jump over the bytes containing the EOL, line address, next line number, and REM token to move from line to line. I'll leave that as an exercise for the user 😀

    • @mikechappell4156
      @mikechappell4156 3 месяца назад

      @@retrooldguy9880 It really shouldn't be that impractical. I don't recall if there is a jump n bytes instruction directly, but I recall you can jump n bytes on test. Set carry flag , jump carry set n bytes, so it's at most 3 bytes. IIRC, n bytes will be a standard size, 1 byte for quote, one for 0 indicating EOLN, 2 bytes for pointer to next line, 2 bytes for the line number, 1 byte for REM, space and quote. You wouldn't need to reevaluate the jump instruction at the end of each line, just end the lines with the same 3 characters before the ending quote. You would need to adjust if your were using the trick to hide the mess, but that would be standard size as well if you kept your blocks of code the same size and I guess NOP could be used for that to pad to length since instructions can be 1 to 3 bytes long. (Again, only a problem if you are trying to hide variable length code blocks, if not, it would be easy.