Machine Language 10 PRINT Size-Optimized for Commodore 64

แชร์
ฝัง
  • เผยแพร่เมื่อ 20 พ.ย. 2024

ความคิดเห็น • 200

  • @peterszabo3584
    @peterszabo3584 2 ปีที่แล้ว +40

    9 byte version.
    Hi. I just sent a detailed explanation yesterday about my 11 byte version (with full legal opcodes) to you Robin,
    but since yesterday I further optimized it into 9 bytes:
    .C:2f00 68 PLA
    .C:2f01 6e 04 dc ROR $dc04
    .C:2f04 69 87 ADC #$87
    .C:2f06 20 17 e7 JSR $e717
    Some notes:
    The location of the code is important. That last JSR will push the "$2f, $08" sequence to the stack. I jumped into the KERNEL routine in a way that I skip the first PHA there.
    This way the last PLA before the RTS will remove the "$08" from the stack.
    The original sys routine around $e12f pushes the address $e1, $46 to the stack (return address to the sys command).
    With he first PLA I remove the 0x46 from the stack and using it also as a constant value to initialize the addition (adc #$87 will give you 205 or 206 this way).
    After jumping into the JSR the stack looks like this: $e1, $2f, $08. (Leftmost is the top of the stack). Inside the JSR $08 will be removed by pla before RTS so RTS will return
    back to the original SYS just before it calls our routine. ($e130).

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +9

      Fantastic work!!

    • @peterszabo3584
      @peterszabo3584 2 ปีที่แล้ว +4

      @@8_Bit Thanks. I really love your videos, inspiring and thorough content! Keep it up!

    • @paulkocyla1343
      @paulkocyla1343 2 ปีที่แล้ว +2

      That´s genious - that´s really really good!

    • @PSL1969
      @PSL1969 2 ปีที่แล้ว +4

      Wow, that's amazing trickery. I think we have a winner 🥇😎👍

    • @paulkocyla1343
      @paulkocyla1343 2 ปีที่แล้ว +8

      8 BYTES - using color ram as random source
      ----------------------------------------
      Slight modification to Peter´s version:
      Using the colorram address under the cursor ($F3/$F4) to randomly change carry, the ZP access saves one byte.
      The colorram has no upper nibble - the four upper bits are floating and give random values when read!
      Tried on VICE, would be nice to know if it works on real hardware :)
      adc ($f3),y ;2b
      pla ;1b
      adc #$87 ;2b
      jsr $e717 ;3b

  • @thedigitalemotion
    @thedigitalemotion 2 ปีที่แล้ว +94

    Thank you for keeping Commodore alive. Being 45 and in a stressful grown up world full of doubt, it’s so nice to relax with a bottle of wine and come back to my 80s youth. That’s always been my happiest place. I’m so pleased people like you exist. 🙏🏼🙌🏼🙏🏼🙌🏼

    • @CubicleNate
      @CubicleNate 2 ปีที่แล้ว +7

      I agree. These things take us back to a more golden age, I would say. Though, I try to stay fixed on the good things out there to keep my head in a positive place. Watching things like this is good for my soul.

    • @stephenelliott7071
      @stephenelliott7071 2 ปีที่แล้ว +3

      I completely agree, the early 80's were an exciting time for the first home computers. In the UK computers like the Sinclair ZX81, ZX Spectrum, Commodore 64 and BBC Micro were the most popular. It's great to grab a drink and fire-up those old computers plugged into time appropriate CRT's (thanks to Ebay) and remember those times fondly. Happy times!

    • @G1itcher
      @G1itcher 2 ปีที่แล้ว +4

      I'm 34 and I think computers were better in the 80s!

    • @d-h4ck401
      @d-h4ck401 2 ปีที่แล้ว +1

      @@G1itcher thats correct. Computers of 80s had a standing and were built to keep running for long years..not like today's technology which is developed to self-destroy in at most 3 or 4 years to make people always consume more.

    • @bob-ny6kn
      @bob-ny6kn 2 ปีที่แล้ว +1

      @@G1itcher I began the rest of my life from PET, Apple][e and C64. It always has been my playground. I am now playing with Arduino. Small cost and footprint. Tinkerland, robust. They even have cloud programming and simulators (drag-drop resistors, sensors, LEDs, et c.) similar to C64 User port. Keep having fun! Never grow up! Pleased to meet you, all.

  • @MichaelDoornbos
    @MichaelDoornbos 2 ปีที่แล้ว +12

    22:46 oh that one is clever, I was trying to figure out what was left to do at this point and hadn't thought of this
    24:30 always something to learn from things that don't work, glad you included it

  • @Qba474
    @Qba474 2 ปีที่แล้ว +8

    Hey! Just wanted to say, that in my country C64 and Amigas were still quite popular in the early 2000's. As a child back then, the C64 was my first computer, even though some of my friends already had a modern PC. Unfortunately, my C64 from back then didn't last. I managed to buy well preserved C64C last friday. Your videos are very informative and really inspiring! It just makes me to have some productive time with my Commodore like I've never had before. :D Thanks!

  • @LeftoverBeefcake
    @LeftoverBeefcake 2 ปีที่แล้ว +13

    Having gone through a couple books and having watched a couple other videos on ML, I could follow along and knew what most of the stuff was doing. (yay!) The extra knowledge doesn't diminish my amazement with what people can squeeze out of this machine with the relative handful of instructions and registers, if anything, I have even more respect. Some good stuff shown here and I can't wait to learn more!

  • @bald_engineer
    @bald_engineer 2 ปีที่แล้ว +5

    Thanks Robin. I really like these in-depth "step-by-step" episodes. I do not pick up many details on my first viewing. But these are the kind I come back to several times because I learn something on each repeat!

  • @espenskog8745
    @espenskog8745 2 ปีที่แล้ว +7

    Loved the optimizations and your step by step walk through. It's like we're 16 again :)

  • @JosipRetroBits
    @JosipRetroBits 2 ปีที่แล้ว +4

    Nice video, I really like 10 print maze, also did a video about it myself. People still develop different versions of this one-liner (orthogonal version) and make it smaller and smaller... and it is amazing how much amusement we get from this fascinating small piece of code.

  • @DavidYoud
    @DavidYoud 2 ปีที่แล้ว +6

    This is a completely on-brand episode!
    24:00 I had a moment of false hope for your divide-by-two approach, by using equivalent petscii values 109/110 instead of 205/206, but alas, it's still odd/even, not even/odd.

  • @geehaf
    @geehaf 2 ปีที่แล้ว +3

    Great video - I really like the way you go from the BASIC solution and iterate through the ML alternatives, including the "if it wasn't for bad luck" attempt too. I've tried but cannot optimise it any smaller. Cheers.

  • @paulvanderlaak700
    @paulvanderlaak700 2 ปีที่แล้ว +1

    Love to see this play with random in machine code. I have been doing this a few years ago with ‘random runner’. Thanx for sharing Robin. 👍

  • @NotaWizard
    @NotaWizard 2 ปีที่แล้ว +1

    awesome. And love the maze behind the patron list in the outro. Classic Robin subtle brilliance. :)

  • @lordanthrax2417
    @lordanthrax2417 2 ปีที่แล้ว +1

    Great video as always! I also like your "learning with music" stuff. Something me and my family did when i was younger! I like it a lot!

  • @phlogicali
    @phlogicali 2 ปีที่แล้ว +9

    I think I found a 11-byte-solution, which should work after a coldstart or reset:
    loop: jsr $e0be
    txa
    sbc $25
    jsr $ffd2
    bcc loop
    explanation:
    - calling subroutine $e0be of the BASIC RND() routine creates a pseudo-random value while the carry flag is also set randomly
    - the routine always returns with X-register = 139 (this value is set at address $e0f2 of the RND() routine)
    - txa transferes the value in X to the accumulator
    - after a reset the value at zeropage address $25 is always 189 (this is set at address $be0d when printing the " BYTES FREE" value using a subroutine call at $e43a)
    - sbc performs the following subtraction: 139-189-carryflag, which results in 205 or 206
    The machine code has 11 bytes: 20 BE E0 8A E5 25 20 D2 FF 90 F5
    Greetings
    Michael

    • @PSL1969
      @PSL1969 2 ปีที่แล้ว

      Interesting!

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +2

      Nice, I will give this a try!

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +7

      This seems like a great solution! I think the SBC $25 could just be SBC #189 or even ADC #66, couldn't it? The real magic is discovering that $E0BE always returns a constant number in X, that's great!

    • @phlogicali
      @phlogicali 2 ปีที่แล้ว +2

      @@8_BitYes, immediate addressing mode is the better option. I was too fixated on finding a zeropage solution and have not seen this much easier solution. :-)

    • @paulkocyla1343
      @paulkocyla1343 2 ปีที่แล้ว +1

      WOW, respect! That´s damn smart!

  • @ZXAtari
    @ZXAtari 2 ปีที่แล้ว +2

    Love this episode. Imagine somebody (more likely thousands of somebodys) spending time to optimize windows 10 code or rewrite it in pure machine code. Whole system on several 3.5 inch floppies again.....

  • @IsaacKuo
    @IsaacKuo 2 ปีที่แล้ว +4

    Huh, my optimizations mostly revolved around figuring out how the heck to get the thing to autorun and ... actually work and not crash. The most compact form of autorun I figured out stuck the code on the stack, so it was really touchy preventing other stuff from accidentally overwriting it eventually.
    (A BASIC stub with SYS to execute the machine code took up way too many bytes.)

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      Yes, making it either RUNnable or auto-running is pretty much its own topic which I just avoided here. I think I fretted enough over it in the video about 10 PRINT Orthogonal :)

  • @agranero6
    @agranero6 2 ปีที่แล้ว +1

    There is a book about this program. It is called 10 PRINT ... (in fact the title is the entire program).

  • @PSL1969
    @PSL1969 2 ปีที่แล้ว +1

    Hey Robin, great episode! 😎 Cool to see all the attempts, and how you explained it all was great! 👍👍👍

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      Thanks Peter for your great ideas!

    • @PSL1969
      @PSL1969 2 ปีที่แล้ว

      @@8_Bit I only had them because of your own great ideas :) And a great challenge to everyone, to improve upon it 👍👍👍

  • @michaelstoliker971
    @michaelstoliker971 ปีที่แล้ว

    Just for fun I cobbled up the 10 Print program on the Atari. You really only have to change the 205.5 to 6.5 and away it goes. The speedup tricks don't work on the Atari due to differences in the Basics, but it's pretty close. The forward and back slashes are ATASCII characters so they look nearly identical to the C64's PETSCII characters.

  • @warmCabin
    @warmCabin 2 ปีที่แล้ว

    You could speed that RAM scanner up by a lot with a sliding window. First, compute the cumulative score for $E000-$E0FF, then for each subsequent byte, you only have to add its score and subtract the score of the previous byte. 2 operations instead of 256.
    So for example, you start with the score from $E000-$E0FF, then you can easily find the score from $E001-$E100 by just subtracting score($E000) and adding score($E100).

  • @paulkocyla1343
    @paulkocyla1343 2 ปีที่แล้ว +3

    I made a 10 byter that uses values in stack as randomness and the illegal opcode RLA to lower the range of numbers.
    Couldn´t get the classic maze pattern to work, but two other ones that look kind of good:
    byte $2f, $f3, $01 ;RLA {adr}:={adr}rol A:=A and {adr} absolute
    jsr $e716
    sbc #$1A ;$1a for random waves, $16 for random grid
    bcc loop

    • @PSL1969
      @PSL1969 2 ปีที่แล้ว +1

      Interesting. I had an 11 byte one, that used "-" and "|" 👍
      Edit: I tried your code, but am I doing something wrong here?
      .C:0900 2F F3 01 RLA $01F3
      .C:0903 20 16 E7 JSR $E716
      .C:0906 E5 1A SBC $1A
      .C:0908 50 F6 BCC $0900 (tried with BVC as well).
      I just get a cursor.

    • @paulkocyla1343
      @paulkocyla1343 2 ปีที่แล้ว +1

      @@PSL1969 I assembled to $080d, maybe it´s that. And I run on VICE emulator by injecting into memory.
      However, Peter Szabo ^^ made a 9 byte version that displays the original - that´s black magic to get this idea :)

    • @PSL1969
      @PSL1969 2 ปีที่แล้ว +1

      @@paulkocyla1343 I'll try that 👍 9 byte that's crazy. I'll have to check that out as well.

    • @paulkocyla1343
      @paulkocyla1343 2 ปีที่แล้ว +2

      ​@@PSL1969 Hi, I just got an 8-byter to work. Posted it in the pinned comment.
      The print routine writes a color-ram address to ZP, where you can read it out with a 2-bytes command.
      The read will give a random value, because the colorram is connected with only 4-bits instead of eight.

    • @PSL1969
      @PSL1969 2 ปีที่แล้ว +1

      @@paulkocyla1343 Nice, I will check it out. This is getting crazy! :)

  • @gwivongalois6169
    @gwivongalois6169 2 ปีที่แล้ว

    Loved the implementation that used the SID as a source of randomness.

  • @byteforever7829
    @byteforever7829 2 ปีที่แล้ว

    love this that you do the assembly editing and run on real C64, and great that we can follow along using the disk file :) hope for more like this

  • @HannesEder
    @HannesEder 2 ปีที่แล้ว +1

    Re: 17:00 Random ROM: you could use a sliding window to not scan over the same 255 bytes again, i.e. inspect the first 255 bytes and then in a loop add the next byte, but remove the first byte that goes out of the window. that should be a lot faster.

  • @mrmimeisfunny
    @mrmimeisfunny 2 ปีที่แล้ว +5

    In the ml2 you could save 2 bytes by doing
    LDA #$80
    STA #$d40f
    STA #$d412
    Considering the character printing routine is slow. halving the frequency won't change much.
    In ML3 the zeropage is filled with random OS junk. So if you don't care about having exactly 128 1s to 128 0s (which true randomness won't do anyway) you can save a byte using lda 00,x or even (00,x)

  • @argoneum
    @argoneum 2 ปีที่แล้ว +3

    (not only) for Linux users, works best in xterm:
    while :; do printf "\e[44;36;1m\e#6" && for i in {1..40}; do printf "\\$((57+77*($RANDOM % 2)))" && sleep 0.01; done; echo; done
    Not sure if other terminal emulators support double-width characters or DEC sequences for them. Slightly off-topic 😸

  • @igorwollersheim464
    @igorwollersheim464 ปีที่แล้ว

    Again a great video with lots of information. Again learned a lot from your video..

  • @midtskogen
    @midtskogen 2 ปีที่แล้ว

    A really fast implementation would scroll more than a full screen for each screen refresh, so you would get the same result if you store the character directly to screen memory, simply looping over it, and you would get an "optimised" version with just a few extra instructions.

  • @AmstradExin
    @AmstradExin 2 ปีที่แล้ว +1

    I wonder if that 10 Print program is useful for finding more of these 'pleasing shapes' for making games with randomized levels. They still have some structure in them without a tailor-made randomizer for each level.

  • @CityXen
    @CityXen 2 ปีที่แล้ว

    Always proving there is more than one way to do something. Excellent vid. Cheers

  • @andreroussel
    @andreroussel ปีที่แล้ว +1

    I'm really enjoying how well you explain things in your videos. Thanks for making those for us. One question I have is what is the deal with using characters 205 and 206? I find using characters such as 182 and 184 to look much more like a traditional maze. If you wanted to use your 11 Byte method you could use characters 182 and 183 which I personnaly still find looks more like a traditional maze. Just curious as to the appeal of using diagonal lines. Thanks

    • @TheUtuber999
      @TheUtuber999 ปีที่แล้ว +1

      I tried your suggestion and you are so right, it really *does* look more like a real maze using those two chars.

  • @d.j.peters
    @d.j.peters 2 ปีที่แล้ว +2

    Well done, you could poke the char in the video ram and make the last row hidden and do a softscroll pixel row by row :-)

  • @eekee6034
    @eekee6034 2 ปีที่แล้ว +1

    I like the adc #0 instead of a jcc. Haha! of course you could get rid of the lda though. :)
    Every time I see 10print running, I want to write a maze solver for it. :) Perhaps the solver should try every entrance until it finds the longest (most interesting) path.
    Optimizing for speed doesn't seem hard to me. Write straight to the screen RAM with an indexed address. Don't even try to scroll, just limit the number of iterations. Ignore the cursor, just pick the starting address to avoid the next prompt. (I'm assuming that the C64 text mode maps RAM to screen without any gaps.) EDIT: I'm also assuming you can ignore color RAM, wherever that is. I'm an Atari guy, our mode 0 is monochrome. :-J
    The reason I bring it up is really for my own sake. I've often thought "I don't want to rewrite that" without realising the only reason "that" is complex is because it's featureful. Only recently have I learned that taking a different approach, changing the requirements a little, can make huge simplifications. I've been limiting myself unnecessarily. :)

  • @paulkocyla1343
    @paulkocyla1343 2 ปีที่แล้ว +2

    That´s a great brain-squeezer. Your solution looks pretty optimal, I don´t think it can be done any smaller.
    But maybe there´s a voodoo trick 😀.

  • @cairsahrstjoseph996
    @cairsahrstjoseph996 2 ปีที่แล้ว

    This routine can be used to generate mazes for a game. Perhaps it can be a 16 screens in a 4x4 grid which can scroll in all directions (but a "wall" would have to be drawn around it). One might start at the bottom left and make your way to the top right. Sprites can be used to make tentacled Cthulu things going about randomly looking for you; perhaps there can be a few rocks here and there in the maze which your character can pick up and throw at them to stop them for a time so you can go past them. It's a silly idea, but why not : )

    • @BillAnt
      @BillAnt ปีที่แล้ว +2

      For games an LFSR (Linear-Feedback-Shift-Register) routine works better. It's random, but allows you to go back and forth different rooms or scrolling panes while maintaining the previously random generated data. ;)

  • @karlramberg
    @karlramberg 2 ปีที่แล้ว +5

    Would be cool to see a episode of this where you optimize for speed

    • @MrRobbyvent
      @MrRobbyvent 2 ปีที่แล้ว

      very unlikely: the routine at $ffD2 has a lot of stuff to do!

    • @DavidYoud
      @DavidYoud 2 ปีที่แล้ว +2

      @@MrRobbyvent Pretty sure we could make a special-purpose print routine that's faster than $FFD2

    • @MrRobbyvent
      @MrRobbyvent 2 ปีที่แล้ว

      @@DavidYoud I'm pretty sure you could'nt

    • @DavidYoud
      @DavidYoud 2 ปีที่แล้ว

      Tell ya what, if Robin makes a video optimizing for speed, I'll submit a faster maze draw routine (which I'm sure clever people could even further optimize).

    • @DavidYoud
      @DavidYoud 2 ปีที่แล้ว

      TH-cam ate my post with a link, but check out disassembly of $E716, lots of unnecessary processing to be removed

  • @bgone5520
    @bgone5520 2 ปีที่แล้ว +2

    Write a version writing directly to screen memory at $0400. You will need to also write color ram too and handle scrolling to and last position wrote to the screen. Should be much faster than kernal rountines. Won't be smaller.

  • @LynxSnowCat
    @LynxSnowCat 2 ปีที่แล้ว +2

    Didn't realise that you needed to recount the low bits incase the ROM changes when incrementing the address. 😼
    I think I spotted a repeating club shape formed by an "e" shaped head on two perpendicular dominoes. That might be avoidable by switching through address windows, or finding one larger than the screen size.
    Would it be practical to do an initial count, then { decrement if a low bit is leaving the address window, and increment when one enters the address window } while scanning for a larger window ?

  • @andresbravo2003
    @andresbravo2003 2 ปีที่แล้ว

    Well, I know about machine language because it’s quite an impressive way to build and compile faster.

  • @JamsterJules
    @JamsterJules 2 ปีที่แล้ว +2

    Love it! How about getting it to run as fast as you can by rewriting the system library?

  • @saganandroid4175
    @saganandroid4175 2 ปีที่แล้ว

    Thank you for remembering us decimal guys sometimes. 13:58 Pseudo-random: What if instead of starting the indexed reading from ROM, maybe a screen RAM address on the left edge of the screen? There will be a feedback effect.

  • @VulpisFoxfire
    @VulpisFoxfire 2 ปีที่แล้ว

    Just as a side note..the jiffy clock is useful for random numbers, but in order to do so, it needs the human element...if you check it after a pause for input of some sort. then usually the variation in delay is enough to be 'sufficiently' random for many purposes. Of course, in an application like this where there's no outside input at all, it's pretty much useless. (and if you're one of those who is, pun intended, inhumanly fast/consistant with your keypress speed, that also defeats it).

  • @eugenetswong
    @eugenetswong 2 ปีที่แล้ว

    Hello, from British Columbia!
    6:30 Wow! I can't believe that I understood all/most of the explanation of the rotating and the carry and the kernel routine.
    18:14 If I understand you, the raster register and CIA are changing in real time, whereas the $F002 technique is a static list of numbers, right?

  • @indiocolifa
    @indiocolifa 2 ปีที่แล้ว

    Unmatched wisdom, as always. Top notch!

  • @antonnym214
    @antonnym214 2 ปีที่แล้ว +1

    On the Z80, the R register was the 16-bit address of the Dynamic RAM address being refreshed. When we needed a random number on the Z80, we just loaded A with the R register and AND with the mask for the bits we wanted from that. But I think that's clever, you pre-surveyed that section of ROM to get your result. Does not the 6502 have a RAM refresh register?

    • @fnjesusfreak
      @fnjesusfreak 2 ปีที่แล้ว

      Nope, only A, X, Y, P, S and the instruction pointer.

  • @Eightbitswide
    @Eightbitswide 2 ปีที่แล้ว +1

    Hey Robin, Really enjoying the exploration into simple assembly code programs! Question, I'm pretty sure that you covered the use of the Assembler (commands, differences between the REU version and the one you used today.) you are using fairly extensively in another video. Do you remember offhand which video?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      Hi Jeff, nice to hear from you! I've got a playlist of the assembly videos here, and you're probably thinking of the first two videos in the list: th-cam.com/play/PLvW2ZMbxgP9z9Un4LXivII_D1Hh5gZ7r9.html

    • @MrWaalkman
      @MrWaalkman 2 ปีที่แล้ว

      OBC! :)

  • @8BitNaptime
    @8BitNaptime 2 ปีที่แล้ว +1

    I wonder if reading unmapped memory is random enough? Like if there's nothing in the expansion port, and you read a IO2 address? I suspect the data bus has enough capacitance to simply hold the last value. Not so random after all I guess. Or what about the upper nybble of color RAM?

  • @TheUtuber999
    @TheUtuber999 ปีที่แล้ว

    21:46 Wouldn't rotating the CIA timer low byte mess up stability of the system?

  • @daveloomis
    @daveloomis 2 ปีที่แล้ว +1

    "Since last year!" Instant like.

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      Gotta use that dad joke every year.

  • @londongaz2
    @londongaz2 2 ปีที่แล้ว +1

    Love the end song!

  • @SteveGuidi
    @SteveGuidi 2 ปีที่แล้ว

    I was recently brushing up on ML and using random numbers, when this video came to mind! I'd like to generate a two-byte random number, but as you noted, using the SID is not suitable without introducing a delay. There is an example in Compute!'s "Programing the Commodore 64" of a routine at $E0BE that produces 4 bytes, but this appears to be slower than calling RND() in BASIC! Maybe I'm doing something wrong, but do you have a suggestion on how to generate two random bytes without introducing a delay?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว

      If you set the SID frequency to $FFFF then you only have to wait 17 cycles to guarantee it changes, so I'd suggest just grabbing a byte, do some initialization or whatever (you can probably think of something useful your code could be doing) and then grab the second byte. Even if you just waste the 17 cycles, it's probably not all that bad.
      If that's not acceptable, then I'd suggest using a large-ish software LFSR, maybe 16 bits or more. But that would probably take 17 cycles or more anyway to generate the next number in the sequence.

    • @SteveGuidi
      @SteveGuidi 2 ปีที่แล้ว

      @@8_Bit These are great suggestions -- thank you! I was goofing around and cheated: I moved the LDA $D41B instruction to a subroutine with an NOP before the RTS -- the total time to call it with JSR is (i think) 16 cycles, which is good enough for my academic exercise.

  • @properjob2311
    @properjob2311 2 ปีที่แล้ว +1

    what it you use a lookup table for the characters and then use the lower bit of the sound oscillator as the index offset? would that be quicker/smaller?

  • @williamsquires3070
    @williamsquires3070 2 ปีที่แล้ว

    Also, one thing you could do would be to make an inner loop in which you ROR 8 times, thus using all the bits in the byte (wherever you get it from). You could also try this in a machine with only - say - 16k of RAM and pull a byte from an address where there’s no RAM, ROM, or hardware and see if it reads “noise” values. So, maybe a VIC-20. Also, what happens if you try to read the joystick and none is connected? Or read the cassette input? Are bits other than bit 0 any more/less “random” than bit 0? Will this work on a C128?

  • @stefanguhren1036
    @stefanguhren1036 2 ปีที่แล้ว +1

    A bit off topic, but anyway I wanted to ask. What's the smallest possible code, in basic, to program a TRUE random generator on the C64?
    I remember back in the days when I tried to create a D&D character generator program, using the standard rnd command, the program would always give back the same values from run to run, if you get my point. The only documentation I had available at the time (the standard C64 manual) didn't give any clues to this as far as I can remember. I can see some things that would more or less allow for a 'true' rnd generator based on timings, as explained by you in this video.
    How would you write a simple basic program that would allow for TRUE rnd generation without any specific inputs and such?

    • @r3jjs
      @r3jjs 2 ปีที่แล้ว

      Short answer:
      You don't get true random numbers out of a computer without specialized hardware.
      Your choices are:
      * Do some math involving a seed value. Start with the same seed, get the same random values. Try to randomize the seed.
      * Do something that involves hardware. A box to detect radiation counts and feeds that back to the computer, for instance.

  • @dwhxyz
    @dwhxyz 2 ปีที่แล้ว +3

    For small (256 bytes) machine code programs which includes some "randomness" this still amazes me - th-cam.com/video/sWblpsLZ-O8/w-d-xo.html . The code and how it works could make for an interesting video.

  • @saganandroid4175
    @saganandroid4175 ปีที่แล้ว

    11:34 Int Disable and Print Char during Int code... very scary.

  • @JohnnyWednesday
    @JohnnyWednesday 2 ปีที่แล้ว +1

    In regards to the 204/205 bad luck? how about putting character 206 into 204? then you could use that alternate technique - but how expensive would it be to replace the char? alternatively replace character 0 and 1 then do away with any addition? can you 'offset' the pointer to the start of character ROM? perhaps shift it up so 205 is 0?

    • @JeremyNasmith
      @JeremyNasmith 2 ปีที่แล้ว +1

      I'm not sure about this, but my intuition says that the pointer to the charset would need to point to the start of a page (xx00), in which case offsetting it by one wouldn't work. Also, since the goal was to optimize for size the preparation needed to move the charset pointer adds more bytes than it saves. But, if optimizing for speed, redefining a character is fine as an initial setup cost if it saves cycles by tightening the loop.

    • @JohnnyWednesday
      @JohnnyWednesday 2 ปีที่แล้ว

      @@JeremyNasmith - I was reading and you can change the pointer to where the character glyphs start but I am unaware if it's restricted to the start of a page - it's a VICII register that's set so I don't know if that hints at any indication.
      Well if the pointer isn't fixed to the start of a page? you could get rid of combining the random value with the 205/206 and print 0 or 1 directly - but you sound like you know it'd still be more expensive :P

  • @HeffeJeffe78
    @HeffeJeffe78 2 ปีที่แล้ว

    A friend of mine used to joke that a true test for randomness is to sample if a man is spitting on the sidewalk in Tulsa on that cycle.

  • @RixtronixLAB
    @RixtronixLAB 2 ปีที่แล้ว

    Nice video, like it, thanks for sharing :)

  • @MattKasdorf
    @MattKasdorf 2 ปีที่แล้ว

    Impressive tutorial, thank you.

  • @rotordave81
    @rotordave81 2 ปีที่แล้ว

    I enjoy the smart alec self deprecation :) What if you pointed it at video memory for randomness?
    You made it a really interesting video with the boundary pushing approach.

  • @saveddijon
    @saveddijon 2 ปีที่แล้ว +4

    You can optimize the ROM searcher program as well. After summing the LSBs of $F000-$F0FF, you can sum $F001-$F100 by subtracting the LSB at $F000 and adding the LSB at $F100, etc.

  • @paulkocyla1343
    @paulkocyla1343 2 ปีที่แล้ว

    8 BYTES
    ----------------------------------------
    Using the colorram address under the cursor ($F3/$F4) to randomly change carry, the ZP access saves one byte.
    The colorram has no upper nibble - the four upper bits are floating and give random values when read!
    Modified version of Peter Szabo.
    adc ($f3),y ;2b
    pla ;1b
    adc #$87 ;2b
    jsr $e717 ;3b

    • @peterszabo3584
      @peterszabo3584 2 ปีที่แล้ว

      Sadly it only works with a poke 780,128 before the sys. The original routine resets the A register from this memory address at each iteration. (0 by default I guess)

    • @paulkocyla1343
      @paulkocyla1343 2 ปีที่แล้ว

      @@peterszabo3584 Thanx for the info. I only tried it on VICE. It works out of the box there.
      When I get some more time, I´ll try to tinker it on a real C64.

    • @peterszabo3584
      @peterszabo3584 2 ปีที่แล้ว

      @@paulkocyla1343 I also tried on VICE and it did not work after reset. But in the pinned post I added a modification that will solve the issue.

  • @BlackGymkhana
    @BlackGymkhana 2 ปีที่แล้ว

    I wish this type of skills and optimizations applied to modern operating systems...

  • @theosib
    @theosib 9 หลายเดือนก่อน

    How do you make sure basic doesn't stomp on the memory used by the assembler or you asm program?

  • @csbruce
    @csbruce 2 ปีที่แล้ว +4

    1:30 Is Turbo Macro Pro free to redistribute?
    5:12 (At this point in watching the video): Is this the final version? The main loop could be shortened to: - LDA $D41B : AND #1 : ADC #205 : JSR $FFD2 : BCC - after putting a CLC in the initialization code.
    8:15 It'd be fun to "POKE" the characters on the screen (do the screen codes have a similar pattern?) with STA (ptr),Y and then go back to the start of the screen RAM instead of scrolling. That would show how fast Machine Language is compared to BASIC.
    In BASIC, you could PRINT "{HOME}" : FOR I=1 TO 999 : print char : NEXT.
    9:55 Okay, this looks familiar!
    11:31 You could also read the CHROUT documentation: .CS=error, .CC=success.
    12:56 (At this point in watching the video): You could also use the low byte of the CIA IRQ countdown timer. This is effectively what +RND(.) is doing (except that's using the high bit).
    21:25 Why are you using ROR instead of LDA : AND #1? ROR will write the rotated result back out to the timer register, which will presumably change the range of the countdown.
    21:59 When you were doing that Transactor memory-scan program, you found some areas apparently in the I/O space that seemed to show continuously changing random values. Would one of those locations be usable?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      I don't know if Turbo Macro Pro is truly free to distribute. TMP, and its predecessor Turbo Assembler, were both commercial products of a German company called Omikron in the 1980s. Both programs have been modified and distributed by probably dozens of different scene groups over the last 30-whatever years without any challenge that I'm aware of; this version by Style has been "re-sourced" and re-assembled with many bug fixes and new features. For example, the REU support wasn't in the original at all.

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      re: CHROUT .CS=error, .CC=success, I actually went looking for that in the C64 Programmer's Reference Guide while preparing for this episode, as I thought there was a success/error indicator. However, on page 278, the description of CHROUT, says "Error returns: 0 (See READST)" and doesn't mention carry at all. Any ideas on where this carry functionality is documented?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว

      re: using ROR instead of LDA : AND #1, it was part of that (unsuccessful) multiply-by-two approach I show at the end and since it didn't seem to negatively affect the apparent randomness in this application, it stuck around in this version. But yes, it very likely is messing with the countdown. It just didn't seem to make the bits that end up in carry any less "random-ish" which is all I was concerned about.

    • @wChris_
      @wChris_ 2 ปีที่แล้ว

      @@8_Bit you might be in luck, as computer programs (or 1:1 'programs for data processing'), were added after 1985, which means they did not have any copyright before (but the source was protected!). But dont quote me, im not a lawyer!

    • @csbruce
      @csbruce 2 ปีที่แล้ว +2

      @@8_Bit: C64 PRG page 271: "ERROR RETURNS: A return from a KERNAL routine with the CARRY set indicates that an error was encountered in processing. The accumulator will contain the number of the error."

  • @gertlynge
    @gertlynge 2 ปีที่แล้ว

    What about bit 0 from the pot pin in the jjoystick port. That mest be kind of random?

  • @raoullangner-macmillan7655
    @raoullangner-macmillan7655 2 ปีที่แล้ว

    Thanks for the great video.

  • @bob-ny6kn
    @bob-ny6kn 2 ปีที่แล้ว

    205/206 dec is 1100 1101 bin. Would NOTing the two LSBs reduce cycles of transferring bytes?

  • @dougjohnson4266
    @dougjohnson4266 2 ปีที่แล้ว

    Do all the Kernals act the same for CHROUT? (Jiffydos, Keernal v1,2,3 etc) Can you call the BASIC RND function and save any bytes? Kinda slow?

  • @retrobytes.v65
    @retrobytes.v65 2 ปีที่แล้ว +1

    Hello
    Does anyone recognise the Disk Emulator / SD Card adaptor that Robin is using ?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      I do! :)
      It's the uIEC/SD, sold by RETRO Innovations.

  • @TheBookaroo
    @TheBookaroo 2 ปีที่แล้ว

    Hi, one though would be to search in rom for part of the code with a subroutine return just after and just called subroutines?

  • @neurogate459
    @neurogate459 2 ปีที่แล้ว

    Cool, thank you

  • @damienretro4416
    @damienretro4416 2 ปีที่แล้ว +1

    Love it

  • @JustWasted3HoursHere
    @JustWasted3HoursHere 2 ปีที่แล้ว +2

    Robin, do you know why the 'Restore' button has to be tapped quickly for the 'Run/Stop Restore' break function to work? In other words, if you just press Run/Stop and the Restore button normally the break won't happen. Is this a function of how the Restore button is read or is that button actually somehow physically different from the others so that it ONLY works if it's tapped quickly? Always wondered about that.

    • @Curt_Sampson
      @Curt_Sampson 2 ปีที่แล้ว +2

      The RESTORE key is certainly very different from the rest of the keys on the keyboard. It's not part of the standard keyboard matrix that's scanned via the PIO lines of CIA1 but is instead connected to the NMI line. (It goes through a 555 circuit that debounces it.) So unlike any other key, where the computer knows about it when it decides to scan the keyboard, pressing RESTORE immediately halts whatever the computer is doing and jumps to the NMI routine. (That routine scans the keyboard to see if RUN/STOP is being held down as this routine executes.)
      I'm not clear on why you're having issues with what sounds like different lengths of key press, though. I wonder if it could be a problem related to the debouncing of that switch? It would be interesting to put an oscilloscope on the relevant signals on a C64 known to be working properly and on yours and see if there's any difference.

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว

      In addition to what Curt said, I've heard that many (mostly breadbin?) C64s have the incorrect value of capacitor (or resistor?) on the Restore key line and so they need a sharper thwack to get the signal through?

    • @JustWasted3HoursHere
      @JustWasted3HoursHere 2 ปีที่แล้ว

      @@8_Bit Yep, the C64 I had back in the day was indeed the old breadbin style and I remember on several occasions trying to just hold the Run/Stop and Restore keys like a normal combination (such as Shift-Return or whatever) and it did not have the desired effect. Had to thwack it hard and quick to do the deed. I had no idea that other C64 versions didn't need that sort of thwacking. I just figured it was designed that way to prevent accidental resets, etc. Interesting!

    • @JustWasted3HoursHere
      @JustWasted3HoursHere 2 ปีที่แล้ว

      @@Curt_Sampson I guess Commodore modified the motherboard slightly in later versions to fix this issue?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว

      ​@@JustWasted3HoursHere Yeah, it seems like it's by design to me too, though some people say it was the incorrect value. I'm not sure what proof they have of this, maybe just that it doesn't match the schematics included in the C64 Programmer's Reference Guide? Well, those schematics have several errors, so I don't see it as fully authoritative. And even if the schematic says something else, it's possible they deliberately changed the value during production.

  • @kurtreber9813
    @kurtreber9813 ปีที่แล้ว

    How would one get the output to scroll smoothly?

  • @josefjelinek
    @josefjelinek 2 ปีที่แล้ว +1

    Are the end songs available somewhere to listen to?

    • @8_Bit
      @8_Bit  2 ปีที่แล้ว +1

      This episode's song has a video here: th-cam.com/video/lHOxmXCSqAs/w-d-xo.html Some of the other songs are on that channel too. Thanks for the interest, I added the link to the video description now too, which I often forget to do.

    • @josefjelinek
      @josefjelinek 2 ปีที่แล้ว

      @@8_Bit very cool, thanks; subscribed

  • @lordanthrax2417
    @lordanthrax2417 2 ปีที่แล้ว

    Do you know 10 print racer revamp?
    Lovely little game

  • @stevethepocket
    @stevethepocket 2 ปีที่แล้ว

    That's a lot slower than I was expecting a machine-language program to be. And you say most of that is CHROUT being the bottleneck? That's... actually not surprising now that I think about what all has to be done between reading an ASCII code and actually getting it onto the screen. In particular, the process of checking for every possible control character and converting what's left from ASCII to a character code probably requires a laundry list of compares (I wonder if 205/206 was picked over 109/110 initially because it somehow bypasses some of them), all of which could be done away with when you know you're only going to by outputting one of two characters. Even the value of register 646 could possibly be stored permanently in a CPU register since you only need to retrieve it at the beginning.

    • @csbruce
      @csbruce 2 ปีที่แล้ว +2

      The bulk of the overall CHROUT time is probably spent scrolling the screen.

    • @Curt_Sampson
      @Curt_Sampson 2 ปีที่แล้ว +1

      As well as the character to screen code conversion process and scrolling, there's also the overhead of figuring out which channels have been opened for output and sending the character to each one. There's really a fair amount of overhead in that CHROUT routine as compared to simpler microcomputer ROMs where the commonly used routine simply prints to the screen and that's it.

    • @stevethepocket
      @stevethepocket 2 ปีที่แล้ว +2

      @@Curt_Sampson You'd think the part that outputs to the screen specifically would be a subroutine that coders could just JSR to directly and cut out the middleman. I remember _Mapping the C64_ mentioning that some Kernal routines work like that.
      EDIT: OK, yeah, it turns out there is, at $E716. Glad I went to the trouble of converting that whole book to searchable text now.

    • @Curt_Sampson
      @Curt_Sampson 2 ปีที่แล้ว

      @@stevethepocket Yup, and nice work on digging that up. The only thing to check there is whether the code that delays after a newline when you're holding down CTRL is part of the generic routine or the $E716 routine.

    • @stevethepocket
      @stevethepocket 2 ปีที่แล้ว

      @@Curt_Sampson Already tried it and yes it is. But I think csbruce was right about the scrolling being the real bottleneck because I didn't notice any difference (and I can't imagine scrolling the entire screen would be something you could easily speed up, nor would you really want to). I'd have to record the footage and go over it frame by frame to see if the initial screen fill is any faster; they both go by too fast to really tell.

  • @rosiefay7283
    @rosiefay7283 2 ปีที่แล้ว

    Cute. But how small can you golf a program which outputs a maze where there is guaranteed exactly one route between any two locations? No walls that completely wall off one area from another. No loops.

  • @wasserwasser5555
    @wasserwasser5555 2 ปีที่แล้ว

    i think it would be nice to have this in graphics mode without the character routine

  • @cairsahrstjoseph996
    @cairsahrstjoseph996 2 ปีที่แล้ว

    Has it really been that long ? Not since last year ? I've forgotten whatever the program was about.

  • @GoatTheGoat
    @GoatTheGoat 2 ปีที่แล้ว

    7:24 Eighty or forty characters per line?

    • @GoatTheGoat
      @GoatTheGoat 2 ปีที่แล้ว

      @Mr Guru Ah, it scrolls two lines at a time, therefore eighty characters per scroll event. Got it.

  • @DasIllu
    @DasIllu 2 ปีที่แล้ว +3

    That is simply "a maze thing"
    ...
    sorry

  • @DAVIDGREGORYKERR
    @DAVIDGREGORYKERR 2 ปีที่แล้ว

    can the rnd() not be based on DES64

  • @stoif76
    @stoif76 2 ปีที่แล้ว

    Now do it with the Super CPU 👍😁👍

  • @nathanjohnson9715
    @nathanjohnson9715 ปีที่แล้ว

    does anyone know how to get back to bank 0 in TMP w/ REU after you've switched to another bank? I've looked through the docs, and there doesn't seem to be a way to do it. There's a bank switching option, but it only allows you to switch to banks 2-7 for whatever reason.

    • @8_Bit
      @8_Bit  ปีที่แล้ว

      I just tried it and you're right. That seems pretty bizarre; not sure if that's a bug or oversight or something by design that I don't understand the reason for. A potential work-around is to exit to basic with back arrow 1, type new, load"TMP+REU*",8,1 then sys32768 to re-enter. You should be back in source bank 0 and your code should still be there. Worked for me just now, anyway.

    • @nathanjohnson9715
      @nathanjohnson9715 ปีที่แล้ว

      @@8_Bit Thanks for the response! So that didn't quite work. I got an "out of memory" error. (I'm guessing I must've overwritten something I shouldn't have in my code). Luckily I took your advice in one of your other videos and I have a reset button at the ready, and after resetting, I was able to run TMP again and get my code back on bank 0. Wheww. Thanks for your help!

    • @8_Bit
      @8_Bit  ปีที่แล้ว

      @@nathanjohnson9715 Did you do the NEW command before the LOAD? Capitalized this time to stand out more :)

    • @nathanjohnson9715
      @nathanjohnson9715 ปีที่แล้ว

      @@8_Bit ha, nope, I sure didn't. Worked beautifully once I actually followed instructions.

  • @jesperlundbyrasmussen7844
    @jesperlundbyrasmussen7844 2 ปีที่แล้ว

    Anyone up for making this to a vertical scroll with a sweet SID tune?

  • @gettingpast4391
    @gettingpast4391 2 ปีที่แล้ว

    For the basic version, try this: 0 PRINT CHR$(RND(.)+I);:GOTO (set i to 205.5 then type goto 0). Notice INT() has been removed and 205.5 is a variable

  • @dr.ignacioglez.9677
    @dr.ignacioglez.9677 2 ปีที่แล้ว

    I LOVE C64 👍🥂🎩

  • @williamsquires3070
    @williamsquires3070 2 ปีที่แล้ว +1

    Hi. What happens if you eliminate the “LDA #$80” line in the initialization section? Since you still have 0xFF in the accumulator, bit 7 is still set, which is what you want in 0xD412. Would the extra ‘1’ bits (bits 0-6) cause a problem here?

  • @royalestel
    @royalestel ปีที่แล้ว

    Call me dumb, but after this assembles to machine code, isn't it 14 bytes? I'm going to noodle over this. Not sure I know enough to do anything

  • @EmilOppelnBronikowski
    @EmilOppelnBronikowski 2 ปีที่แล้ว

    I knew this thing is coming. What's the next power play from Robin? 10 PRINT in IC form?

  • @SIDCIAVIC
    @SIDCIAVIC 2 ปีที่แล้ว

    You can't read the raster register during badlines.

  • @TheUtuber999
    @TheUtuber999 ปีที่แล้ว

    On the Commander X16, the page of memory starting at address $C000 looks pretty "random" while viewing its contents in the monitor. The pattern generated from the following code looks fairly good, too...
    _Standard version..._
    .,8000 A0 00 LDY #$00
    .,8002 B9 00 C0 LDA $C000,Y
    .,8005 29 01 AND #$01
    .,8007 69 CD ADC #$CD
    .,8009 20 D2 FF JSR $FFD2
    .,800c C8 INY
    .,800d 20 E4 FF JSR $FFE4
    .,8010 F0 F0 BEQ $8002
    .,8012 60 RTS
    _Slanted version..._
    .,8000 A0 00 LDY #$00
    .,8002 A2 CD LDX #$CD
    .,8004 B9 00 C0 LDA $C000,Y
    .,8007 C8 INY
    .,8008 C9 55 CMP #$55
    .,800a 90 01 BCC $800D
    .,800c E8 INX
    .,800d 8A TXA
    .,800e 20 D2 FF JSR $FFD2
    .,8011 20 E4 FF JSR $FFE4
    .,8014 F0 EC BEQ $8002
    .,8016 60 RTS

  • @donwald3436
    @donwald3436 2 ปีที่แล้ว

    I bet you'd get a significant speed-up by inlining the print subroutine. All that pushing and popping state from stack, yuck.

  • @christianknechtel8683
    @christianknechtel8683 2 ปีที่แล้ว

    Is using a different Charset cheating?

    • @tmilker
      @tmilker 2 ปีที่แล้ว

      If you're going for size, the setup to change the character set would make the program exceed 12 bytes.

    • @christianknechtel8683
      @christianknechtel8683 2 ปีที่แล้ว

      @@tmilker Makes sense.I forgot it´s about size.

  • @melanierhianna
    @melanierhianna 2 ปีที่แล้ว

    BBC BASIC Version
    10REPEAT:PRINTCHR$(47+INT(0.5+RND(1))*45);:UNTILFALSE
    The entire program comes in at 32 bytes because REPEAT, PRINT, CHR$(, INT(, RND(, UNTIL and FALSE come out as byte each as they are tokenised.
    BBC Assembler Version
    10 FOR N%=0 TO 3 STEP 3
    20 P%=&2000
    30 [
    40 OPT N%
    50 .loop
    60 jsr &AF51 ; &AF80 for BASIC 1
    65 lda &2A
    70 ror A
    80 lda #47
    90 bcs print
    100 adc #45
    110 .print
    120 jsr &FFEE
    130 jmp loop
    140 ]
    150 NEXT
    CALL &2000
    Routine AF51 is the BASIC RND variable function. RND without braces or an argument returns a random signed 32 bit integer in zero page 2A, 2B, 2C and 2D (The Integer Work Area). We just take the lowest byte. Routine FFEE is OSWRCH i.e. OS write character. Its the operating system equivalent of PRINT CHR$(A).

    • @melanierhianna
      @melanierhianna 2 ปีที่แล้ว

      The BBC Micro, apart from Mode 7 (Teletext mode), does not have character mapped screen memory so you have to use the OS to write characters. Mode 7 has different characters in the character set to \ and / and modes 3 and 6 have 25 line screens and not 32 line screens and space them out with scan lines which are not backed by memory. So best to use one of the other screen modes.

    • @melanierhianna
      @melanierhianna 2 ปีที่แล้ว

      My optimised version. Rather than using the \ and / characters which, in ASCII, are quite far apart, I've defined a couple of user characters 128, and 129.
      10 VDU23,128,&80,&40,&20,&10,8,4,2,1
      20 VDU23,129,1,2,4,8,&10,&20,&40,&80
      30 FOR N%=0 TO 3 STEP 3
      40 P%=&2000
      50 [
      60 OPT N%
      70 .loop
      80 jsr &AF51 ; &AF80 for BASIC 1
      90 ror &2A
      100 lda #128
      110 adc #0
      120 jsr &FFEE
      130 jmp loop
      140 ]
      150 NEXT
      Since the $ as a prefix is already used in BBC BASIC (As a way of storing strings in memory, e.g. $1234="Hello world"), then & is used at the 'HEX' indicator. Also the OSWRCH routine, unlike the C64 equivalent, leaves carry in an unknown state, hence jmp.

    • @melanierhianna
      @melanierhianna 2 ปีที่แล้ว

      Okay I got it smaller! 12 bytes...
      10 VDU23,128,&80,&40,&20,&10,8,4,2,1
      20 VDU23,129,1,2,4,8,&10,&20,&40,&80
      30 FOR N%=0 TO 3 STEP 3
      40 P%=&2000
      50 [
      60 OPT N%
      70 .loop
      80 jsr &AF51
      90 lda #&81
      100 and &2A
      120 jsr &FFEE
      130 jmp loop
      140 ]
      150 NEXT
      It basically ANDs the random value with &81 which results in either &00, &01, &80 or &81. This is a cheat. 0 is the null character which does nothing. 1 means send next character to printer (so it doesn't appear on the screen). The other two are characters 128 and 129 i.e. our maze characters.

  • @deblauweschicht
    @deblauweschicht 2 ปีที่แล้ว

    Another kind of maze can be made with
    10 A=RND(1)*4:PRINTCHR$(A+171-(A>1)*5);:GOTO10
    Not quite as elegant a one-liner, as the four characters involved are not all adjacent in the PETSCII table.

  • @petesapwell
    @petesapwell 8 หลายเดือนก่อน

    Incredible ideas there and a useful tip about $ffd2