THE best talking hands on the whole internet keep on entertaining and surprising me. All those years later I still learn new things about the C64 and its not-so-limited BASIC thanks to Robin and his friends.
0:00 Wow - a Commodore calculator! 5:00 When doing boolean operations, Commodore BASIC uses 16-bit signed integers, where having all '1' bits = $FFFF = -1. By using -1 to mean 'true', AND, OR, and NOT are simultaneously Logical ★and★ Bitwise operations. 27:40 In principle, it generates 32 random mantissa bits, so the chance of generating exactly 0.5 is 1 in 4-billion. 36:06 Darn, I had written a note to commute «B=B=(I=1)» to «B=I=1=B»? 43:40 «POS(π)» is 4% faster than «POS(.)».
1) You are telling him something new, as opposed to summarizing, right? A lot of the Boolean operations are still so new to me, that I could understand what he was getting at, but it was hard to really absorb it. 2) Is POS(PI) the fastest known to you?
@@mycosys: You can test the timing for yourself in an emulator with a program like: 10 TI$="000000" 20 FORI=1TO1000:X=PEEK(162)AND2+1:NEXT 30 PRINTTI «RND(π)*4OR1» clocks in at 659 jiffies, «PEEK(162)AND2+1» at 593 jiffies, and «RND(.)*4OR1» at 478 jiffies. A problem with PEEK(162) is that the jiffy clock changes slowly relative to printing things on the screen, so you'll see patterns in the maze. PEEK(56324) would work better, though execute slower (730 jiffies) since the bigger number takes longer to parse. If you assign 56324 to a variable, then the test executes in 430 jiffies. Of course, Robin's goal was to make the program as ★small★ as possible. RND(.) is faster than RND(π) since it uses a simpler but lower-quality method, which is essentially the same as PEEK(56324).
@@eugenetswong: I assume Robin knows how Commodore booleans work, since he mentioned the 16-bit signed integers later in the video and said he might make a video on the subject. «π» is the fastest dummy argument I know of. Also, the POS() function is going to execute faster than the equivalent PEEK().
24:33 instead of an empty REM line to demarcate, I like to use a line with just a colon. Normally colon is used to separate multiple statements on one line, but BASIC allows a colon with no statements before or after it.
Here's an assembly version I decided to come up with. Probably not the most optimized but still fun to figure out! lda #12 sta $d021 ldy #40 loop: dey bne same_line jsr reverse ldy #40 same_line: jsr reverse lda $dc04 and #2 tax jsr print inx jsr print bcc loop reverse: lda 199 eor #$ff sta 199 rts print: lda bytes,x jsr $ffd2 rts bytes: .byte 155, 169, 151, 223
After watching this video, I was wondering how fast it would draw the pattern in ML. So I whipped out my good old trusty Merlin assembler (originally made for the Apple II but ported to the C64). Low and behold, it draws the pattern virtually instantly... nice job! :) Coincidentally someone made a video recently about the Merlin assembler. th-cam.com/video/aa74VOJALwM/w-d-xo.html
I really loved how you managed to eliminate the for loop, and then eventually all the variables!! Brillant. Really enjoyed watching this. Thanks Robin.
The use of -1 for true lets BASIC get away with just one version of the AND/OR/NOT operators, instead of having separate ones for bitwise vs logical operations like most proglangs (e.g. &/I/~ vs &&/II/! in C). As long as your "true" value has _every_ bit set (and false has every bit clear) the result of a bitwise operator will be the same as the result of a logical one.
Dunno how much smaller it would be but you could make it so much faster, direct access the line position, ditch the RND func and just use the LSB of the system timer for a pseadorandom bool, select form 2 memory adresses, write the char from the rom
@@mycosys The system timer doesn't change fast enough; you'd write many of the same choice in a row every tick. But you can write a short 8-bit linear feedback shift RNG that fits in 16 bytes of 6502 machine code. On the 64/128, you can alternatively set up the SID's noise channel and use its oscillator as a random source, but that's probably still too slow for this; the max frequency is 4 kHz, at which rate it only changes about once every 250 clock cycles.
What fun..! I had to try this on the Vic - works just as well, with screen background as a 'light' colour, and the in-line screen having first colour white, and second colour the darker shade of the background.
I am a total layman if it goes to programming 8 bit machines. For me final code looks like code written in one of those esoteric languages designed to mess with the coder.
This would be very fast in VISION BASIC, you should do a video on VISION BASIC. It's the best programming tool I have ever seen for the C64 by a long shot.
Cool video. I don't really know how to program, but I love watching other people who know this stuff so well. Very cool to see how short the program got!
It’s always a highlight when you upload a new video. I remember I wanted a C64 when I was a kid. I never got it. But I got a TI99. I had lots of fun with that
Very cool! I was into programming kaleidoscope and maze type PETSCII effects in BASIC, but I never came up with anything as cool as this 3D terrain effect.
Your Mid$ solution reminds me of an article I read in SoftSide about "String Packing" on the TRS-80 by putting your graphics into a string then using the varptr (variable pointer) command to point to where in memory that string resides and print whatever portion you want.
Changing your life for the better is amazing. I used to party every weekend. These days I watch 48 minute videos of how to proactively make things more efficient by trial and error. Thanks Robin.
A simpler solution for the rnd. At start make an array c$: c$(0)="[chr155][chr169]":c$(1)="[chr151][chr223]" Then the print is pretty simple: ?c$(rN(1)*2) Together with the pos() solution there are only 72 bytes in the line.
It never occurred to me that none of the C64 keyboards show both rows of colours on the number keys, but oddly I do I have this memory of seeing both colours on the numbers. I never owned a C128, so what was I remembering? The answer is literally right in front of me right now: the C16 has both colours printed on the number keys and it sort-of looks like a C64 🙂
Next: machine language version! I’m really impressed at how well you guys know the C64’s logic and functions inside and out in order to figure out all these shortcuts.
various assembly version can be found among these comments (I hope these direct links work): th-cam.com/video/VC-lbd8mTOs/w-d-xo.html&lc=UgyxeSL_kB9Sfd25r9Z4AaABAg th-cam.com/video/VC-lbd8mTOs/w-d-xo.html&lc=UgwT2SCwRsBvMjq83_B4AaABAg ... but also Forth: th-cam.com/video/VC-lbd8mTOs/w-d-xo.html&lc=Ugy4YWUVZMJm61R4_OB4AaABAg
Congrats Robin and Ian! That's really cool to see all the variables removed, it feels a lot more "10PRINT"-like now. Also, I would love to see you craft a 6502/6510 assembly version of this program, to see it fill up the screen blazing fast. That would be a ton of fun! 🖖🏻
I got the original code to work on the Commander X16 pretty easily after some modifications. The important thing to get right was that the maximum value of I in the FOR loop was dependent on the width of the screen. And I wanted the program to be able to run in 80-column *or* 40-column mode. So after setting up the color in the X16 way with *5 COLOR 1,12* I added in a line to use the KERNAL routine to return the dimensions of the screen, happily named SCREEN and its jump table address is $FFED. It returns the width of the screen in the X register, which after BASIC returns from a SYS call is found stored at location $030D. Therefore, *7 SYS $FFED:X=PEEK($030D)* was stuck in there. The last modification I made was to change the '40' in line 10 to 'X'. Otherwise it's the same as the original program by Ian. Unfortunately, my additions make it impossible to compact the program into a single line anymore.
you can even shrink and made a little bit faster getting rid of the first poke (53281) and changing colour scheme (use CTRL+4 and COMMODORE+7 for colours in the string text). is not looking good as grey shades, but not so bad too
A very easy solution would be to just enter it into C128 mode (excuse me if you somewhere mentioned that in the video, then I simply overheard it). C128 mode has a 160 char buffer - which is to allow 80-col mode still have 2 rows of BASIC code.
Great video! Thanks for not just jumping to the answer, but for showing all the process. Using RND(pi) for a slight speed improvement was a new one to me. Bill Gates needs to see this somehow -- he might get a kick out of how people are still exploring all the nuances of code he helped create so very long ago.
-1 is all bits set (as opposed to all bits clear) - since we are dealing with twos-complement numbers, a binary number with all bits set comes out as -1. It also means that you don’t need to distinguish between binary and logical ANDs, ORs , etc.
I learned to program on an ICL 1904 mainframe at school in the 70s. In the 8 bit days we used to create "one liners" & "five liners" . It is amazing what people came up with, the ingenuity (I had a Video Genie - clone of TRS80 model 1). We used to do the same years later on our Atari STFM/STE's too, in STOS. It really does get the old grey matter working. Basic gets a lot of stick, but without it many of us wouldn't have become programmers.
To me, it looks like crystalline growths, or the Giant's Causeway (granite columns). The B=B=0 toggle is very clever. Funny how some optimisations are faster OR shorter. No need to apologise for how long the video ended up being, because there are some very interesting moments.
How many variables can we add to B=B=B=0 until it is no longer useful? I am under the impression that we can use any twice variable, before it becomes redundant.
Back in the early 80's I looked forward every month to reading my dad's issue of SoftSide magazine and trying out the one-liners they would print every month. I think all the back issues are posted on the Wayback Machine. The TRaSh-80 ones were the best. There was a one-liner car racing game that I expanded upon to be an entire program.
I used to wonder about -1 for true also until I realized that BASIC actually has no logical operators, but only bitwise. But if you ensure that boolean results are always 0 or -1, then the bitwise operators act like logical. So if/then expressions that use boolean logic in them are actually using bitwise operations. I suppose this is an optimization that BASIC inventors chose back at Dartmouth and has been carried through to this day in BASIC implementations. PowerBASIC and FreeBASIC implement separate logical operators such as AndAlso and OrElse.
Microsoft Access/Excel also uses -1 for TRUE. Sometimes complex queries in Access don't show up as a checkbox for some reason and it shows the 0 or -1 instead.
I'm always interested in a round of Programming Golf. Get a program down to as few (key)strokes as possible. And if you can make it faster, so much the better! This was a fascinating bit, I really thought the *1.025 would be faster than *41/40, since a lot of hardware of the time had faster multiplication but slower division.
There's an old joke that goes something like, "ask a programmer to review five lines of code and they'll come up with 20 optimisations. Ask a programmer to review 200 lines of code and they'll say it's fine" 😂
I also thought that *1.025 would be faster. I'm not an expert, but I think that *41/40 is faster, because it spells out smaller tasks to do, one at a time, whereas *1.025 might calculate it all at once. I'd love find out, though.
@@eugenetswong I was surprised to find it. Both because it's an extra operation and because multiplication should be "easier" for the computer! One of my favourite optimizations (in "real life" coding too) is when imperative code can be eliminated with algebraic refactoring. That's probably why I was playing around with Robin's equation and stumbled upon this curiosity.
I expected *1.025 to be faster, too. I guess one reason it isn't is that 1.025 isn't "round" in binary, producing a (FAC) floating-point mantissa of $83333333, requiring all 32 bits to be multiplied.
A cleverly crafted, brand-new "0 PRINT" for nerds like us. However fine this one-liner came up to be, I don't think I am the only one who went straight to Turbo Macro Pro after watching the video. 🙂
Great video! Thanks for going through the whole journey of the optimization. That's the second time I've seen the MID$ trick to randomly pick some specific characters - I want to use this very cool idea! By the way, there is one c64 with all 16 colors listed on the keys - the SX-64.
And there's at least once C64 out there with keycaps lifted from a C16 that also have all colors printed on them... I did that in high school when C16 keyboards showed up in surplus stores. I don't know where that 64 ended up but I can't be the only one!
I'm still of two minds about whether the SX-64 *is* a Commodore 64. I mean, you get an error if you type LOAD and press return! I've got hundreds of C64 games on tape that won't work on it. If you're a disk user then it's highly (but not completely) C64 compatible. But a tape-focused C64 fan would say it's not compatible at all.
Both PAL and NTSC Commodore 64s say "Commodore 64" on the box they came in, on the case, and on the boot screen, so I say yes, they're undoubtedly all Commodore 64s no matter where they're located. The SX-64 doesn't call itself a Commodore 64 ever, as far as I know, though I haven't thoroughly looked into it yet.
All one needed to have understood was C=64 Basic programming to have gotten that MS Windows was an absurdly bloated, massive overwriting of code; Possibly one of the most inefficient programs ever written by man. And it still is!
@@-108- I could be wrong on this, but I remember reading that the reason Win98SE worked so well was because they deleted a lot of unused remarked out code and reworked kludgy parts too.
such clever out of the box thinking, too bad kids today don't experience the joy we had growing up in the 80's and learning to code on the greatest 8 bit platform.
I think the pattern looks like a bismuth crystal. Which raises the possibility of choosing different colors in the random color picker. Also playing with the 1 TO 40 generates visually different appearances. Like changing the loop to 1 TO 20 is different from 1 TO 30, etc
The reason that TRUE is -1 is that in binary -1 is 11111111 (for bytes). For anyone who cares, this value is how -1 is represented because adding 1 to it will return zero. (It's the 'two's complement' representation of -1)
I've been very interested in computers and computing, since 1980. And bought my first computer in 1982. An Oric-1, wich I upgraded to an Oric Atmos. But to the thing, I really would like to tell you, and to see if it's possible to get hold of a little basic program, I found in a computer magazine, in 1984, or around that time. It was basicly (he, he) just called "Animals". The program was asking the user, of what animal he was thinking of, or the characteristics of this animal. Then the program was trying to find what animal the user was thinking about. The thing is, the program wasn't longer than maybe 10 lines, or so. But every time it stored the answer, from the user, and so grew the data base, to implement more, and more animals. I was very impressed with how few lines it was possible to write such a program.
Hi, if you search for: david ahl animal game basic You will find several hits related to that game, or at least variations of it. It's a game that's been around from the 1970s for many different platforms, ported from one BASIC to the next.
OMFG!?! You absolutely delivered! Complete analysis and review with insane optimization. This is incredibly WOW! The thing I like about the original 10print was that I was able to optimize for speed in some ways because instead of printing, I was able to build strings to then dump all at once. And to prevent scrolling, the last block was always one character short. Ultimately I ended up with an array of large strings and random colors selections with the entire maze appearing in a blink while a countdown in the middle of the screen counts while at the same time generating a new maze string array. Obviously this is not a one-liner but it is a nice screensaver and was kind of fun to build and optimize. But you can't print a poke... I'll dig into this to see if I can block-array the output for faster printing.
On the c64, integer variable logic is much slower than floating point because c64 basic has no integer functions. All integers are internally converted to floats for operations and then back into integers for display.
Actually, in most BASIC dialects I’m aware of, 0 is false, and any non-zero value is true. So even just “1” is true as it has at least 1 bit set high. So if B has some non-zero value, then after “B=B=0”, B=0. Then after the second “B=B=0” (on line 10 after the For loop), B=-1, and it’ll keep toggling between 0 and -1.
-1 is interesting because you can "and" it with any non-zero and get another non-zero as you'd expect. You can also "or" it with anything and get a non-zero result. It's the only number to have those properties.
I think it was A+ magazine (for Apple Computers) that had "one liners" (for Apple IIe) on the inside of the back cover (the very last sheet of the magazine, not including the covers) - complete games, sine graphs (?), and other stuff could be seen
4:20 - B=B=0 is clever and minimal. But also B=ABS(B)-1 switches false (0) and true (-1) in one statement. -1 for True is easy to switch, +1 and 0 is far more complex. 28:00 - Nope, you saved one, 14 versus 15. 28:30 - That is astoundingly clever. And I'll bet it doesn't work in any other Basic.
Commodore basic does have B=NOT B which is faster than B=B=0. It's another reason -1 is a good choice for "true". Binary NOT -1 = 0. I had forgotten that when I wrote the first version of the program. However, after all of the refactoring, it turned out not to be needed anyway.
THE best talking hands on the whole internet keep on entertaining and surprising me. All those years later I still learn new things about the C64 and its not-so-limited BASIC thanks to Robin and his friends.
We need a face reveal video at some point... or maybe not, and just enjoy Robin's magical hands. :)
0:00 Wow - a Commodore calculator!
5:00 When doing boolean operations, Commodore BASIC uses 16-bit signed integers, where having all '1' bits = $FFFF = -1. By using -1 to mean 'true', AND, OR, and NOT are simultaneously Logical ★and★ Bitwise operations.
27:40 In principle, it generates 32 random mantissa bits, so the chance of generating exactly 0.5 is 1 in 4-billion.
36:06 Darn, I had written a note to commute «B=B=(I=1)» to «B=I=1=B»?
43:40 «POS(π)» is 4% faster than «POS(.)».
Could you possibly tell me how much faster it is to replace the 1,3 random function with PEEK(162)AND2+1 ? I would be most appreciative
1) You are telling him something new, as opposed to summarizing, right? A lot of the Boolean operations are still so new to me, that I could understand what he was getting at, but it was hard to really absorb it.
2) Is POS(PI) the fastest known to you?
@@mycosys: You can test the timing for yourself in an emulator with a program like:
10 TI$="000000"
20 FORI=1TO1000:X=PEEK(162)AND2+1:NEXT
30 PRINTTI
«RND(π)*4OR1» clocks in at 659 jiffies, «PEEK(162)AND2+1» at 593 jiffies, and «RND(.)*4OR1» at 478 jiffies.
A problem with PEEK(162) is that the jiffy clock changes slowly relative to printing things on the screen, so you'll see patterns in the maze. PEEK(56324) would work better, though execute slower (730 jiffies) since the bigger number takes longer to parse.
If you assign 56324 to a variable, then the test executes in 430 jiffies. Of course, Robin's goal was to make the program as ★small★ as possible.
RND(.) is faster than RND(π) since it uses a simpler but lower-quality method, which is essentially the same as PEEK(56324).
@@eugenetswong: I assume Robin knows how Commodore booleans work, since he mentioned the 16-bit signed integers later in the video and said he might make a video on the subject.
«π» is the fastest dummy argument I know of. Also, the POS() function is going to execute faster than the equivalent PEEK().
@@csbruce Thank you!
24:33 instead of an empty REM line to demarcate, I like to use a line with just a colon. Normally colon is used to separate multiple statements on one line, but BASIC allows a colon with no statements before or after it.
The program really needs a name. I humbly propose "Spicy 10Print"
Hi, Ian. Thank you for contributing to this.
Also, thank you for participating in the comment section.
I like 10Printian
11PRINT
Tiny
MountainScape
"Hi, It's Robin". For me, this gives me the warm fuzzies the way Mr. Roger's opening greetings did for the PBS kids in days of yore.
Yes!
I'm so glad that you decide to make a video about this amazing pattern, and a big shout-out to Ian Witham :)
Here's an assembly version I decided to come up with. Probably not the most optimized but still fun to figure out!
lda #12
sta $d021
ldy #40
loop:
dey
bne same_line
jsr reverse
ldy #40
same_line:
jsr reverse
lda $dc04
and #2
tax
jsr print
inx
jsr print
bcc loop
reverse:
lda 199
eor #$ff
sta 199
rts
print:
lda bytes,x
jsr $ffd2
rts
bytes:
.byte 155, 169, 151, 223
After watching this video, I was wondering how fast it would draw the pattern in ML. So I whipped out my good old trusty Merlin assembler (originally made for the Apple II but ported to the C64). Low and behold, it draws the pattern virtually instantly... nice job! :)
Coincidentally someone made a video recently about the Merlin assembler.
th-cam.com/video/aa74VOJALwM/w-d-xo.html
Assembling this in TMP results in the wrong characters being printed, not sure why
I really loved how you managed to eliminate the for loop, and then eventually all the variables!! Brillant. Really enjoyed watching this. Thanks Robin.
17:25 that was a very satisfying "yup"
You are so good at explaining! Everything becomes totaly easy to understand! Thank you and everyone involved in this great little oneliner :D
Yes, thank you to all the code contributors. I really needed these demonstrations in Boolean logic.
The use of -1 for true lets BASIC get away with just one version of the AND/OR/NOT operators, instead of having separate ones for bitwise vs logical operations like most proglangs (e.g. &/I/~ vs &&/II/! in C). As long as your "true" value has _every_ bit set (and false has every bit clear) the result of a bitwise operator will be the same as the result of a logical one.
Only you could make a 48 minute video about a one line program. I enjoyed every minute.
It flowed quite nicely at 1.5x speed.
Very cool! Ian's a smart cookie and this is a great collab.
And your song at the end.. haha amazing. Even more current 23 years after it was written.
What a tingly memory those inverted character codes for changing colors are.
So cool. I'd like to see an assembly version, detailed like you did here.
Dunno how much smaller it would be but you could make it so much faster, direct access the line position, ditch the RND func and just use the LSB of the system timer for a pseadorandom bool, select form 2 memory adresses, write the char from the rom
@@mycosys The system timer doesn't change fast enough; you'd write many of the same choice in a row every tick. But you can write a short 8-bit linear feedback shift RNG that fits in 16 bytes of 6502 machine code.
On the 64/128, you can alternatively set up the SID's noise channel and use its oscillator as a random source, but that's probably still too slow for this; the max frequency is 4 kHz, at which rate it only changes about once every 250 clock cycles.
@@markjreed Do you really mean
@@liorean No, I meant size of the assembled code, not the source! 16 bytes of source code is like two instructions. :)
I would love to see how to do that
What fun..! I had to try this on the Vic - works just as well, with screen background as a 'light' colour, and the in-line screen having first colour white, and second colour the darker shade of the background.
27:45 I’ve decided to find you the answer. For science.
36:30 this is starting to look like a 1 liner in Perl. Who needs readability eh?
Simply amazing. A piece of art - including the optimizations.
That was a really nice conversation. It would straight up fit at the GOTO conference lineup ;-)
Excellent, fun video and I especially like your trick to use the Signum function. Cute.
Hey Robin, wow this is really cool! 😎😎 Kudos to Ian!
There's definitely a game to be developed from that :) Great video. Learnt some new BASIC tricks.
I am a total layman if it goes to programming 8 bit machines. For me final code looks like code written in one of those esoteric languages designed to mess with the coder.
This would be very fast in VISION BASIC, you should do a video on VISION BASIC. It's the best programming tool I have ever seen for the C64 by a long shot.
The SX-64 keyboard shows all 16 colors on the number keys.
Cool video. I don't really know how to program, but I love watching other people who know this stuff so well. Very cool to see how short the program got!
Three best words on TH-cam - "Hi, it's Robin.", makes my day every time!
It’s always a highlight when you upload a new video. I remember I wanted a C64 when I was a kid. I never got it. But I got a TI99. I had lots of fun with that
Very cool! I was into programming kaleidoscope and maze type PETSCII effects in BASIC, but I never came up with anything as cool as this 3D terrain effect.
Your Mid$ solution reminds me of an article I read in SoftSide about "String Packing" on the TRS-80 by putting your graphics into a string then using the varptr (variable pointer) command to point to where in memory that string resides and print whatever portion you want.
The way you guys think amazes me.
Great video thank you
It looks like the snow bank in my driveway after the snowplow goes by. Cheers from Ottawa :-)
Changing your life for the better is amazing. I used to party every weekend. These days I watch 48 minute videos of how to proactively make things more efficient by trial and error. Thanks Robin.
Excellent video. I love those one-liners 😊
A simpler solution for the rnd.
At start make an array c$:
c$(0)="[chr155][chr169]":c$(1)="[chr151][chr223]"
Then the print is pretty simple:
?c$(rN(1)*2)
Together with the pos() solution there are only 72 bytes in the line.
It never occurred to me that none of the C64 keyboards show both rows of colours on the number keys, but oddly I do I have this memory of seeing both colours on the numbers. I never owned a C128, so what was I remembering? The answer is literally right in front of me right now: the C16 has both colours printed on the number keys and it sort-of looks like a C64 🙂
Next: machine language version! I’m really impressed at how well you guys know the C64’s logic and functions inside and out in order to figure out all these shortcuts.
various assembly version can be found among these comments (I hope these direct links work):
th-cam.com/video/VC-lbd8mTOs/w-d-xo.html&lc=UgyxeSL_kB9Sfd25r9Z4AaABAg
th-cam.com/video/VC-lbd8mTOs/w-d-xo.html&lc=UgwT2SCwRsBvMjq83_B4AaABAg
... but also Forth:
th-cam.com/video/VC-lbd8mTOs/w-d-xo.html&lc=Ugy4YWUVZMJm61R4_OB4AaABAg
Such a cool effect! I love this little piece of code. Thanks for sharing.
Hehe very cool mate! Im gonna show this to my computer science students as an example of how to work with optimisation!
Congrats Robin and Ian! That's really cool to see all the variables removed, it feels a lot more "10PRINT"-like now. Also, I would love to see you craft a 6502/6510 assembly version of this program, to see it fill up the screen blazing fast. That would be a ton of fun! 🖖🏻
Run it through an Assembly compiler and wallah!
See the comment above by @Duncan Sparks who wrote it in Assy.
I got the original code to work on the Commander X16 pretty easily after some modifications. The important thing to get right was that the maximum value of I in the FOR loop was dependent on the width of the screen. And I wanted the program to be able to run in 80-column *or* 40-column mode.
So after setting up the color in the X16 way with *5 COLOR 1,12* I added in a line to use the KERNAL routine to return the dimensions of the screen, happily named SCREEN and its jump table address is $FFED. It returns the width of the screen in the X register, which after BASIC returns from a SYS call is found stored at location $030D. Therefore, *7 SYS $FFED:X=PEEK($030D)* was stuck in there. The last modification I made was to change the '40' in line 10 to 'X'. Otherwise it's the same as the original program by Ian. Unfortunately, my additions make it impossible to compact the program into a single line anymore.
you can even shrink and made a little bit faster getting rid of the first poke (53281) and changing colour scheme (use CTRL+4 and COMMODORE+7 for colours in the string text). is not looking good as grey shades, but not so bad too
53281 and 53280 are the video addresses for foreground and background. The number afterward is the color, of which there are sixteen.
This was the best episode I have seen! I couldn't stop watching it!
A very easy solution would be to just enter it into C128 mode (excuse me if you somewhere mentioned that in the video, then I simply overheard it).
C128 mode has a 160 char buffer - which is to allow 80-col mode still have 2 rows of BASIC code.
I'm always thinking, that's it, no more chance to improve and simplify something. I'm always wrong... Your knowledge is impressive.
Great video! Thanks for not just jumping to the answer, but for showing all the process. Using RND(pi) for a slight speed improvement was a new one to me. Bill Gates needs to see this somehow -- he might get a kick out of how people are still exploring all the nuances of code he helped create so very long ago.
Absolutely amazing, I am so impressed :) will have to whip,out my C64 to try this, and the the machine code version here in the comments.
This sure was interesting and fun to watch! It was like a good plot, always a new twist (of code) behind the corner. More like this, please.
Love this :) thank you very much - hope you're well!
Nice Easter Egg at 6:07!
Great video, really appreciate you walking through the code and your great explanations. And love the "Remember When" song at the end. 👍 😀
Cool! The power of Basic V2!
-1 is all bits set (as opposed to all bits clear) - since we are dealing with twos-complement numbers, a binary number with all bits set comes out as -1. It also means that you don’t need to distinguish between binary and logical ANDs, ORs , etc.
I learned to program on an ICL 1904 mainframe at school in the 70s. In the 8 bit days we used to create "one liners" & "five liners" . It is amazing what people came up with, the ingenuity (I had a Video Genie - clone of TRS80 model 1). We used to do the same years later on our Atari STFM/STE's too, in STOS. It really does get the old grey matter working. Basic gets a lot of stick, but without it many of us wouldn't have become programmers.
To me, it looks like crystalline growths, or the Giant's Causeway (granite columns). The B=B=0 toggle is very clever. Funny how some optimisations are faster OR shorter. No need to apologise for how long the video ended up being, because there are some very interesting moments.
How many variables can we add to B=B=B=0 until it is no longer useful? I am under the impression that we can use any twice variable, before it becomes redundant.
@@eugenetswong if you had an even number of variables, you just end up with the original value. It has to be an odd number to work like that.
@@merman1974 Well, maybe I misstated. I think in terms of B=B=0 to be 2 usages of B. So, B=B=B=0 is redundant?
This is truly cool. So immersive. I can see different shapes depending on what my eyes focus on.
Back in the early 80's I looked forward every month to reading my dad's issue of SoftSide magazine and trying out the one-liners they would print every month. I think all the back issues are posted on the Wayback Machine. The TRaSh-80 ones were the best. There was a one-liner car racing game that I expanded upon to be an entire program.
It was pretty exciting to watch the evolution of this nice piece of software.
I love these 10print programs, short program but great visuals
The image reminds me of those " Stare-E-O " three dimensional images from the '90s...
This is amazing! Good job guys :)
I used to wonder about -1 for true also until I realized that BASIC actually has no logical operators, but only bitwise. But if you ensure that boolean results are always 0 or -1, then the bitwise operators act like logical. So if/then expressions that use boolean logic in them are actually using bitwise operations. I suppose this is an optimization that BASIC inventors chose back at Dartmouth and has been carried through to this day in BASIC implementations. PowerBASIC and FreeBASIC implement separate logical operators such as AndAlso and OrElse.
OrElse, the most threatening of logical operators
Microsoft Access/Excel also uses -1 for TRUE. Sometimes complex queries in Access don't show up as a checkbox for some reason and it shows the 0 or -1 instead.
I'm always interested in a round of Programming Golf. Get a program down to as few (key)strokes as possible. And if you can make it faster, so much the better! This was a fascinating bit, I really thought the *1.025 would be faster than *41/40, since a lot of hardware of the time had faster multiplication but slower division.
There's an old joke that goes something like, "ask a programmer to review five lines of code and they'll come up with 20 optimisations. Ask a programmer to review 200 lines of code and they'll say it's fine" 😂
I also thought that *1.025 would be faster. I'm not an expert, but I think that *41/40 is faster, because it spells out smaller tasks to do, one at a time, whereas *1.025 might calculate it all at once.
I'd love find out, though.
@@eugenetswong I was surprised to find it. Both because it's an extra operation and because multiplication should be "easier" for the computer! One of my favourite optimizations (in "real life" coding too) is when imperative code can be eliminated with algebraic refactoring. That's probably why I was playing around with Robin's equation and stumbled upon this curiosity.
I expected *1.025 to be faster, too. I guess one reason it isn't is that 1.025 isn't "round" in binary, producing a (FAC) floating-point mantissa of $83333333, requiring all 32 bits to be multiplied.
defiantly what I miss about my teen years with my commodore.
A cleverly crafted, brand-new "0 PRINT" for nerds like us. However fine this one-liner came up to be, I don't think I am the only one who went straight to Turbo Macro Pro after watching the video. 🙂
Thanks to Okurka for showing it is + not *. I just got back to these!!
Great video! Thanks for going through the whole journey of the optimization. That's the second time I've seen the MID$ trick to randomly pick some specific characters - I want to use this very cool idea! By the way, there is one c64 with all 16 colors listed on the keys - the SX-64.
And there's at least once C64 out there with keycaps lifted from a C16 that also have all colors printed on them... I did that in high school when C16 keyboards showed up in surplus stores. I don't know where that 64 ended up but I can't be the only one!
I'm still of two minds about whether the SX-64 *is* a Commodore 64. I mean, you get an error if you type LOAD and press return! I've got hundreds of C64 games on tape that won't work on it. If you're a disk user then it's highly (but not completely) C64 compatible. But a tape-focused C64 fan would say it's not compatible at all.
@@8_Bit Allow me to confuse the issue: is a PAL 64 a 64 in NTSC land?
Both PAL and NTSC Commodore 64s say "Commodore 64" on the box they came in, on the case, and on the boot screen, so I say yes, they're undoubtedly all Commodore 64s no matter where they're located. The SX-64 doesn't call itself a Commodore 64 ever, as far as I know, though I haven't thoroughly looked into it yet.
k19:09 B=B=(I=1) whoah, can you go over that again? 29:40 That's craazzyyyy. I hope you explore larger sets and variations applications at some point.
One of the best broadcasts (in my opinion) and by the way you have an amazing ability to explain and teach others.👍
This is awesome. I bet if Robin wrote Microsoft Windows, it would've only been like 27 lines of code. :-)
All one needed to have understood was C=64 Basic programming to have gotten that MS Windows was an absurdly bloated, massive overwriting of code; Possibly one of the most inefficient programs ever written by man. And it still is!
@@-108- I could be wrong on this, but I remember reading that the reason Win98SE worked so well was because they deleted a lot of unused remarked out code and reworked kludgy parts too.
such clever out of the box thinking, too bad kids today don't experience the joy we had growing up in the 80's and learning to code on the greatest 8 bit platform.
I think the pattern looks like a bismuth crystal. Which raises the possibility of choosing different colors in the random color picker. Also playing with the 1 TO 40 generates visually different appearances. Like changing the loop to 1 TO 20 is different from 1 TO 30, etc
Ooh, that Pi optimization is cool
The reason that TRUE is -1 is that in binary -1 is 11111111 (for bytes). For anyone who cares, this value is how -1 is represented because adding 1 to it will return zero. (It's the 'two's complement' representation of -1)
That was fun. I'm an Acorn guy myself.
I've been very interested in computers and computing, since 1980.
And bought my first computer in 1982.
An Oric-1, wich I upgraded to an Oric Atmos.
But to the thing, I really would like to tell you, and to see if it's possible to get hold of a little basic program,
I found in a computer magazine, in 1984, or around that time.
It was basicly (he, he) just called "Animals".
The program was asking the user, of what animal he was thinking of, or the characteristics of this animal.
Then the program was trying to find what animal the user was thinking about.
The thing is, the program wasn't longer than maybe 10 lines, or so.
But every time it stored the answer, from the user, and so grew the data base, to implement more, and more animals.
I was very impressed with how few lines it was possible to write such a program.
Hi, if you search for: david ahl animal game basic
You will find several hits related to that game, or at least variations of it. It's a game that's been around from the 1970s for many different platforms, ported from one BASIC to the next.
I love the challenge of finding different code to achieve the same result but with different optimizations.
Very, very nice! :D
12:10 I really think we need an exploration and clarification of why Reversing (Inverse) alternating lines creates the 3D look. What's up with that?
Your detailed knowledge is impressive.
Wow, that is real deepness of thinking!
OMFG!?! You absolutely delivered! Complete analysis and review with insane optimization. This is incredibly WOW!
The thing I like about the original 10print was that I was able to optimize for speed in some ways because instead of printing, I was able to build strings to then dump all at once. And to prevent scrolling, the last block was always one character short. Ultimately I ended up with an array of large strings and random colors selections with the entire maze appearing in a blink while a countdown in the middle of the screen counts while at the same time generating a new maze string array. Obviously this is not a one-liner but it is a nice screensaver and was kind of fun to build and optimize.
But you can't print a poke... I'll dig into this to see if I can block-array the output for faster printing.
Calm down, you'll have a stroke
I read and have this book 10 Print. I found amazing that a one-liner was a title of a book.
Kind of amusing to think every program in C is effectively a one liner, C gives no fox about line breaks or whitespace XD
Outstanding explanation and breakdown!
Wow, that’s pretty cool. It would make a great wallpaper. I wonder how hard it would be to get it to run on a PC at @ 4K.
I competely watched it and thought it would have been maybe 8 minutes... Love it.
If your prefence is speed, create a lookup-Table (datas) with random numbers and stick to integer-only operations (variable names end in "%")
On the c64, integer variable logic is much slower than floating point because c64 basic has no integer functions.
All integers are internally converted to floats for operations and then back into integers for display.
I encourage you to start exploring the VisionBasic C64 compiler and also TRSE.
Actually, in most BASIC dialects I’m aware of, 0 is false, and any non-zero value is true. So even just “1” is true as it has at least 1 bit set high. So if B has some non-zero value, then after “B=B=0”, B=0. Then after the second “B=B=0” (on line 10 after the For loop), B=-1, and it’ll keep toggling between 0 and -1.
-1 is interesting because you can "and" it with any non-zero and get another non-zero as you'd expect. You can also "or" it with anything and get a non-zero result. It's the only number to have those properties.
When I saw this on reddit I immediately knew you'd make a video about it. Maybe call it the Escher10 program?
To get the ultimate shrinkage throw the code in the pool
No variables and all math? I didn't know commodore 64 basic could do functional programming. :p
I think it was A+ magazine (for Apple Computers) that had "one liners" (for Apple IIe) on the inside of the back cover (the very last sheet of the magazine, not including the covers) - complete games, sine graphs (?), and other stuff could be seen
SoftSide Magazine was my place to get one-liners in the early 80's. Primarily geared towards the TRS-80 but also had code for the Atari and Apple.
Cool pattern. It would make a great desktop background via a screen capture jpeg, among many other possible uses.
4:20 - B=B=0 is clever and minimal. But also B=ABS(B)-1 switches false (0) and true (-1) in one statement. -1 for True is easy to switch, +1 and 0 is far more complex.
28:00 - Nope, you saved one, 14 versus 15.
28:30 - That is astoundingly clever. And I'll bet it doesn't work in any other Basic.
B=B=0 is the same as B=1-B for changing between 0 and 1... dont know if 2 comparison is faster or slower than just a simple decrement.
Basically, B=B=0 is the same as B=!B in more modern languages, which would clearly be recognized as a toggle.
Commodore basic does have B=NOT B which is faster than B=B=0. It's another reason -1 is a good choice for "true". Binary NOT -1 = 0. I had forgotten that when I wrote the first version of the program. However, after all of the refactoring, it turned out not to be needed anyway.
Great work, learnt a lot