hi Toby, I have tried your little routine and it works perfectly well. Nice . ;-) In my previous post I had made a mistake entering CLC instead of CMP #10. I guess i was reading too fast. Here is my test code in relaunch64 written for 64tass: *= $801 .word (+), 10 .null $9e, "2061" + .word 0 .include "stdio.asm" tmp = $8000 ; My stdio.asm jumps directly to main ; so the next 5 lines are never executed main2 ;point to nmi lda #tmp sta $0329 main ;init screen init lda #$0 sta 53280 sta 53281 lda #147 jsr $ffd2 lda #5 jsr $ffd2 lda #142 jsr $ffd2 ldy #0 loop tya jsr printhc space lda #$20 jsr $ffd2 iny ;cpy #$10 bne loop rts printhc pha lda #11 ; light gray sta 646 ; next basic color ln pla pha and #$0f cmp #$0a bne hn lda #7 ; yellow sta 646 hn pla pha and #$f0 cmp #$a0 bne ok2 lda #7 ;yellow sta 646 ok2 pla pha lsr a lsr a lsr a lsr a jsr conv4 jsr $ffd2 pla and #$0f jsr conv4 jsr $ffd2 rts conv1 and #$0f tax lda table,x rts table .byte 48,49,50,51,52 ; 0-4 .byte 53,54,55,56,57 ; 5-9 .byte 65,66,67,68,69,70 ; a-f conv2 cmp #10 bcc number adc #6 number adc #48 rts conv3 sed clc adc #$90 adc #$40 cld rts conv4 sed cmp #10 adc #48 cld rts
@@roaddan01: I tried Toby's method and it DID work. Your code above is missing the CMP #10 in "conv4". That adds the extra 1 through the Carry flag for hexits $0A to $0F, which shifts your "@ABCDE" to the expected "ABCDEF". Of course, I can't condone requiring specific behaviour from a system for invalid inputs (values $0A to $0F are not valid BCD)!
My daughter was doing a science quiz while I was watching this video - that didn't bother her... my snoring did 😔 Don't got me wrong, COVID-19 home schooling is tiring on this old dad.
I love it but I also recall how the Terminator is running 6502 according to screenshots of the code in the movie and I can't shake the memory of that. Imagine a machine as complex as the terminator would have to be running 6502 code!
@@DevilsHandyman Programmer: "We could use a neural network to make the AI of our military bot very intelli..." Boss: "Nope! Sounds expensive. Just include a 6502 emulator, map this 4k to the hypervisor, and reuse the enemy algorithms from this game code we inherited."
Thanks! Another straightforward and easy to understand lesson in 6502 Assembly. Enjoyed that one. And I appreciate you taking the time to answer my questions on something you posted 2 years ago. I know I'm behind, but it's going to take me a while to catch up.
My favorite book so far is "MicroComputer Experimentation with the MOS Technology KIM-1" by Lance Leventhal. I am currently reading "Programming and Interfacing the 6502 with experiments" by Marvin L. De Jong. This book also had a HEX to ASCII conversion program, but it would get the input from switches on a breadboard and send the answer out to a set of LEDs on the same breadboard. I did like your explanation of BCC = BLT and BCS = BGE that will be helpful.
Nice Hex and TMP walkthrough. The disk is very helpful to try out examples. For those dishexic who cannot memorize 32768, to start TMP, the following also works: SYS2^15
bcc; that last c is like a < bcs; the s is like an = over a > (if you ignore the top loopy bit, although just remember that it wasn't a c. This is more to remember that it is = as well as a >)
Perhaps. I'm not quite sure this was by design, but I'm ready to be convinced if someone can still dig up any evidence of that. Also see my other comment: ua-cam.com/video/utb6dPUNw4I/v-deo.html&lc=UgwClTGI_XdtOSOflMh4AaABAg
Had that book 35 years ago. Couldn't connect its examples to graphics programming on the C64, but always thought it somehow taught how to draw the cover art in assembly.
Make sure you don't jump to Conversion 3 (17:10) before watching the Conversion 2 chapter (12:00), because the conv2 chapter contains an explanation why the carry adds one (from 15:50), which is also important for understanding conv3.
Yes, that would work just as well, and execute faster, so it's a very good choice. I'm in the habit of using $FFD2 when performance doesn't matter, such as a one-time initialization, as it's more portable to the other 8-bit Commodores such as the C-128 and Plus/4. I'm not saying it's a GOOD habit, but it is my habit :)
(@16:09) a bit easier to understand (but takes more bytes) is: conv2 .block CMP #10 ; Acc=[0..9]? BCC ahead ; yes ADC #54 ; 1 less than 55, the amount to shift 10 -> 65 (“A”) - A hex = 10 base 16, because carry set! BNE ahead2 ; exit out now ahead ADC #48 ; shift into ASCII ‘0’-‘9’ ahead2 RTS ; return with Acc holding the ASCII value for the hex digit. .bend
What, you expected me to know how terrible 2020 would be? :) Like I said at the beginning of the video, this one was originally released December 2019 for the patrons.
It's unfortunate the PETSCII table in the book shown e.g. at 23:17 does not include hexadecimal numbers. That makes this a little harder to follow. Btw., another way to think of what's going on with conversion 3 is that when the 6502 in BCD mode "corrects" (cf. 21:37) the numbers 9A-9F to 100-105, that action skips the distance which the numbers and uppercase characters are apart in the PETSCII (and ASCII) tables. Of course, as per the video, skipping six positions would be off by one - if it were not for the carry bit, which adds another position, so it's seven positions skipped in total.
15:58 - you must have gone up and changed the jsr conv1 to jsr conv2 at some point, but you didn't show it. Was very surprised this run didn't just repeat the original loop!
I remember having a Commodore Pet and not being able to afford an Assembler (it was 50 quid). I used to know most of the op-codes off-by-heart. You know, when Programmers were 'ard!
Funny enough, these days the table-driven version would be slower again, since there is a good chance the table is not in cache, and the cache miss penalty is so large that you can easily convert dozens of bytes to ASCII using computation in the same time. Even a single cache miss would make the computation versions worthwhile. And, of course, the branchless versions would be king, since that way the branch predictor has no way to bug out.
Fascinating and educational als always. And very good that its all done on the Commodore itself instead of some fancy schmancy incomprehensible bloated programming suite that's bound to not be supported anymore in a few years.
I never had the brain for learning programming languages, I'm too artsy-fartsy. I understood a fraction of all this but it was still fascinating because I know the game programmers at Atari had to write VCS games in 6502 Assembly but were hampered by the antiquated machines they had to code on as well as the limitations of the console itself. Given all that, it's amazing some of the stuff they were able to make.
I love your coding examples. It has been decades since I did any 6502 programming. Is there a reason you don't create labels for the functions you are going to call $ffd2 like you do for tmp?
Since $FFD2 is already cross-platform across all the Commodore 8-bits I'm just in the habit of calling it directly. But it's totally valid to do a label like CHROUT = $FFD2 and then use JSR CHROUT from then on.
@@8_Bit I figured as much. I like to try to teach programmers to make their code clear when someone else reads it but that isn't so much what you are showing here. Thanks for sharing!
@@DevilsHandyman Yeah, this particular case is a toss-up. I think a lot of 8-bit Commodore programmers find $FFD2 more clear than CHROUT (which I think is the generally-accepted label for $FFD2) - I certainly do. But for a general audience, another label would be more clear. Unfortunately I don't always know who my audience is here on UA-cam!
@@8_Bit I've always been in the habit of using labels a lot and the nice thing about them is that they don't add any size to the executable but make your own code more readable. I've gone back to some source code years later on ones I didn't label or put comments on and was scratching my head thinking, "What the heck was I doing in THIS subroutine?"
Rodnay Zacks was kinda like a god in the early 1980s... 'cus it didn't matter "which side" you were on... Rodnay wrote THE book on the Z80 and the 6502! :)
@@8_Bit it's not here yet but it looks like the first of the black cover with orangeish text. I was just trilled to find one for under $30 (they are as high as $150 right now) that's in readable shape.
Before I forget, do you prefer LDA'ing the #147 then executing the CHROUT over doing it with the built-in JSR routine (at $e544)? I can't even find an entry on the C-64 Wiki about it, so is it just not that widely known about? If you JSR $e544 in a program, it generates a Clear Screen operation, which is the same as doing SYS 58692. It's just that JSR $e544 is so much shorter than loading the Accumulator with 147 then doing a JSR anyway.
I just have #147 and $FFD2 (CHROUT) memorized so I use that most of the time. $FFD2 has the nice property of preserving the A,X,Y registers and also being an officially documented KERNAL call that works on all Commodore 8-bit computers. That said, $E544 does save a couple bytes, so it's fine to use if it suits you!
I think if you put the table in zero-page memory, it’ll be just a bit faster, as you can replace the absolute,x addressing mode with the zero page,x addressing mode, though this should be good enough for most situations! 🙂
Robin, I am using VICE. When I am following your videos and hit "S" to start the program in TMP, I get three lines of repeated "pi" characters with the desired output displayed on the last 16 characters of the 3rd line. This is the case on default VICE NTSC settings for the Breadbin and the 64C. I am doing the precise same thing you are doing with the same software. If I hook up the SuperSnapshot cart image and do a memory fill before going into TMP, when I execute the program in TMP I get white square characters instead of "pi" characters -- either way I am still getting junk output. What's going on? I've noticed this issue on multiple (recent) versions of VICE when following your assembly videos, where my screen output has a bunch of junk that yours doesn't. I don't know what older versions of VICE do.
Robin, never mind. I was going willy nilly with comments and it appears going to the line break (possibly over) was breaking the program, even if I added extra semicolons. I cleaned it up and it looks just like yours.
Yes, there is a risk of crashing there. I can think of a couple work-arounds that I'd have to test, but pretty much nobody worried about that kind of thing back then during initialization. If code like that is being executed in a loop or during interrupt handling, then it's much more likely to cause a crash and should definitely be dealt with.
@@8_Bit I learned to worry about it when I added a nmi handler to make ctrl+restore do a restore of the VDC char set on a 128. That takes a little while, and a slightly bouncy restore key means it easily gets triggered while its still running, resulting in unintentional writes to screen memory (with the default vdc ram layout) The easy solution is to have some location set to $ff and do something like this... inc .location bne + + dec .location Of course.. that should return, or take care to decrease that .location again when done. It is atomic because the inc .location cannot be interupted (irq or new nmi only happens after the instruction finishes), and for the next 255 subsequent calls, the bne will branch past the code which should be protected.
Actually, I've learned the meaning of the word nibble way before the word nipple gained some strange attractiveness to it 😉 - almost in the single digits of my life clock.
SED:CMP#10:ADC#48:CLD:RTS
7 bytes
Nice one!
Someone plays Code Golf.
hi Toby, I have tried your little routine and it works perfectly well. Nice . ;-) In my previous post I had made a mistake entering CLC instead of CMP #10. I guess i was reading too fast.
Here is my test code in relaunch64 written for 64tass:
*= $801
.word (+), 10
.null $9e, "2061"
+ .word 0
.include "stdio.asm"
tmp = $8000
; My stdio.asm jumps directly to main
; so the next 5 lines are never executed
main2 ;point to nmi
lda #tmp
sta $0329
main ;init screen
init lda #$0
sta 53280
sta 53281
lda #147
jsr $ffd2
lda #5
jsr $ffd2
lda #142
jsr $ffd2
ldy #0
loop tya
jsr printhc
space lda #$20
jsr $ffd2
iny
;cpy #$10
bne loop
rts
printhc pha
lda #11 ; light gray
sta 646 ; next basic color
ln pla
pha
and #$0f
cmp #$0a
bne hn
lda #7 ; yellow
sta 646
hn pla
pha
and #$f0
cmp #$a0
bne ok2
lda #7 ;yellow
sta 646
ok2 pla
pha
lsr a
lsr a
lsr a
lsr a
jsr conv4
jsr $ffd2
pla
and #$0f
jsr conv4
jsr $ffd2
rts
conv1 and #$0f
tax
lda table,x
rts
table .byte 48,49,50,51,52 ; 0-4
.byte 53,54,55,56,57 ; 5-9
.byte 65,66,67,68,69,70 ; a-f
conv2 cmp #10
bcc number
adc #6
number adc #48
rts
conv3 sed
clc
adc #$90
adc #$40
cld
rts
conv4 sed
cmp #10
adc #48
cld
rts
@@roaddan01: I tried Toby's method and it DID work. Your code above is missing the CMP #10 in "conv4". That adds the extra 1 through the Carry flag for hexits $0A to $0F, which shifts your "@ABCDE" to the expected "ABCDEF". Of course, I can't condone requiring specific behaviour from a system for invalid inputs (values $0A to $0F are not valid BCD)!
@@roaddan01 You have used CLC instead of CMP#10 in conv4.
Am I the only one that finds watching 6502 coding to be very relaxing? :D
My daughter was doing a science quiz while I was watching this video - that didn't bother her... my snoring did 😔
Don't got me wrong, COVID-19 home schooling is tiring on this old dad.
I love it but I also recall how the Terminator is running 6502 according to screenshots of the code in the movie and I can't shake the memory of that. Imagine a machine as complex as the terminator would have to be running 6502 code!
I love it. I'm learning TMP assembler with these
Nope you are not the only one, but there is no cure. I tried. Lol
@@DevilsHandyman Programmer: "We could use a neural network to make the AI of our military bot very intelli..." Boss: "Nope! Sounds expensive. Just include a 6502 emulator, map this 4k to the hypervisor, and reuse the enemy algorithms from this game code we inherited."
Thanks! Another straightforward and easy to understand lesson in 6502 Assembly. Enjoyed that one.
And I appreciate you taking the time to answer my questions on something you posted 2 years ago. I know I'm behind, but it's going to take me a while to catch up.
My favorite book so far is "MicroComputer Experimentation with the MOS Technology KIM-1" by Lance Leventhal. I am currently reading "Programming and Interfacing the 6502 with experiments" by Marvin L. De Jong. This book also had a HEX to ASCII conversion program, but it would get the input from switches on a breadboard and send the answer out to a set of LEDs on the same breadboard.
I did like your explanation of BCC = BLT and BCS = BGE that will be helpful.
Your voice is very soothing.
Amazing work, thank you! It's definitely inspiring for my new solo music project UV 🙏🏻
Nice Hex and TMP walkthrough. The disk is very helpful to try out examples.
For those dishexic who cannot memorize 32768, to start TMP, the following also works:
SYS2^15
I've got Leventhal's "6809 Assembly Language Programming."
Did he write a book for each of the MOS and Motorola chips?
Love the video and the credit music as well! Thank you.
bcc; that last c is like a <
bcs; the s is like an = over a > (if you ignore the top loopy bit, although just remember that it wasn't a c. This is more to remember that it is = as well as a >)
I tried it all. Great stuff. The bcd is a forgotten science. It probably is the reason behind for the gap between the ascii 9 and A. Cool stuff.
Perhaps. I'm not quite sure this was by design, but I'm ready to be convinced if someone can still dig up any evidence of that.
Also see my other comment: ua-cam.com/video/utb6dPUNw4I/v-deo.html&lc=UgwClTGI_XdtOSOflMh4AaABAg
Had that book 35 years ago. Couldn't connect its examples to graphics programming on the C64, but always thought it somehow taught how to draw the cover art in assembly.
Much needed today 🥺
Make sure you don't jump to Conversion 3 (17:10) before watching the Conversion 2 chapter (12:00), because the conv2 chapter contains an explanation why the carry adds one (from 15:50), which is also important for understanding conv3.
Assembler n00b here, wouldn't LDA #1 and STA $0286 work as well to set the cursor colour to white?
Yes, that would work just as well, and execute faster, so it's a very good choice. I'm in the habit of using $FFD2 when performance doesn't matter, such as a one-time initialization, as it's more portable to the other 8-bit Commodores such as the C-128 and Plus/4. I'm not saying it's a GOOD habit, but it is my habit :)
(@16:09) a bit easier to understand (but takes more bytes) is:
conv2
.block
CMP #10 ; Acc=[0..9]?
BCC ahead ; yes
ADC #54 ; 1 less than 55, the amount to shift 10 -> 65 (“A”) - A hex = 10 base 16, because carry set!
BNE ahead2 ; exit out now
ahead ADC #48 ; shift into ASCII ‘0’-‘9’
ahead2 RTS ; return with Acc holding the ASCII value for the hex digit.
.bend
lol. "happy new year and talk in 2020" oh well can't expect Robin to be perfect ALL the time :-P
What, you expected me to know how terrible 2020 would be? :) Like I said at the beginning of the video, this one was originally released December 2019 for the patrons.
@@8_Bit I actually missed that but it still made me chuckle and check the calendar like I was losing my mind
It's unfortunate the PETSCII table in the book shown e.g. at 23:17 does not include hexadecimal numbers. That makes this a little harder to follow.
Btw., another way to think of what's going on with conversion 3 is that when the 6502 in BCD mode "corrects" (cf. 21:37) the numbers 9A-9F to 100-105, that action skips the distance which the numbers and uppercase characters are apart in the PETSCII (and ASCII) tables.
Of course, as per the video, skipping six positions would be off by one - if it were not for the carry bit, which adds another position, so it's seven positions skipped in total.
15:58 - you must have gone up and changed the jsr conv1 to jsr conv2 at some point, but you didn't show it. Was very surprised this run didn't just repeat the original loop!
Aha, good catch. Yes, I must've been over-eager with the editing there.
I remember having a Commodore Pet and not being able to afford an Assembler (it was 50 quid). I used to know most of the op-codes off-by-heart. You know, when Programmers were 'ard!
Funny enough, these days the table-driven version would be slower again, since there is a good chance the table is not in cache, and the cache miss penalty is so large that you can easily convert dozens of bytes to ASCII using computation in the same time. Even a single cache miss would make the computation versions worthwhile. And, of course, the branchless versions would be king, since that way the branch predictor has no way to bug out.
Fascinating and educational als always. And very good that its all done on the Commodore itself instead of some fancy schmancy incomprehensible bloated programming suite that's bound to not be supported anymore in a few years.
amazing video... you should make more assambly language videos... very interesting!
There's quite a few here in my playlist: ua-cam.com/play/PLvW2ZMbxgP9z9Un4LXivII_D1Hh5gZ7r9.html
8-Bit Show And Tell yep Ive watched them all 😄
I never had the brain for learning programming languages, I'm too artsy-fartsy. I understood a fraction of all this but it was still fascinating because I know the game programmers at Atari had to write VCS games in 6502 Assembly but were hampered by the antiquated machines they had to code on as well as the limitations of the console itself. Given all that, it's amazing some of the stuff they were able to make.
I love your coding examples. It has been decades since I did any 6502 programming. Is there a reason you don't create labels for the functions you are going to call $ffd2 like you do for tmp?
Since $FFD2 is already cross-platform across all the Commodore 8-bits I'm just in the habit of calling it directly. But it's totally valid to do a label like CHROUT = $FFD2 and then use JSR CHROUT from then on.
@@8_Bit I figured as much. I like to try to teach programmers to make their code clear when someone else reads it but that isn't so much what you are showing here. Thanks for sharing!
@@DevilsHandyman Yeah, this particular case is a toss-up. I think a lot of 8-bit Commodore programmers find $FFD2 more clear than CHROUT (which I think is the generally-accepted label for $FFD2) - I certainly do. But for a general audience, another label would be more clear. Unfortunately I don't always know who my audience is here on UA-cam!
@@8_Bit I've always been in the habit of using labels a lot and the nice thing about them is that they don't add any size to the executable but make your own code more readable. I've gone back to some source code years later on ones I didn't label or put comments on and was scratching my head thinking, "What the heck was I doing in THIS subroutine?"
Rodnay Zacks was kinda like a god in the early 1980s... 'cus it didn't matter "which side" you were on... Rodnay wrote THE book on the Z80 and the 6502! :)
I had that black “6502” book way back when I was learning assembler on my Apple ][+! I wish I hadn’t lost it. 😢
Ah! This trick is also well known on x86.
That would be:
and al,0fh
add al,90h
daa
adc al,40h
daa
I finally picked up a copy of this book for less than a Bazillion dollars. Took a lot of eBay patience for it to not be highway robbery ;-)
Congrats! Which edition/printing did you end up with? Which cover?
@@8_Bit it's not here yet but it looks like the first of the black cover with orangeish text. I was just trilled to find one for under $30 (they are as high as $150 right now) that's in readable shape.
I paid $50 for a very nice copy of Butterfield's Machine Language book months ago(the second edition) so this seemed like a bargain.
Before I forget, do you prefer LDA'ing the #147 then executing the CHROUT over doing it with the built-in JSR routine (at $e544)? I can't even find an entry on the C-64 Wiki about it, so is it just not that widely known about? If you JSR $e544 in a program, it generates a Clear Screen operation, which is the same as doing SYS 58692. It's just that JSR $e544 is so much shorter than loading the Accumulator with 147 then doing a JSR anyway.
I just have #147 and $FFD2 (CHROUT) memorized so I use that most of the time. $FFD2 has the nice property of preserving the A,X,Y registers and also being an officially documented KERNAL call that works on all Commodore 8-bit computers. That said, $E544 does save a couple bytes, so it's fine to use if it suits you!
I think if you put the table in zero-page memory, it’ll be just a bit faster, as you can replace the absolute,x addressing mode with the zero page,x addressing mode, though this should be good enough for most situations! 🙂
Would love to see some 320x200 graphics mode assembly please!
Here we are in Nov 2020, we can use this to count votes with
they seem more interested in not counting them
still funny i remember left arrow 3 :)
BUG! - you comment ;a-f should read A-F to truly be correct.
Robin, I am using VICE. When I am following your videos and hit "S" to start the program in TMP, I get three lines of repeated "pi" characters with the desired output displayed on the last 16 characters of the 3rd line. This is the case on default VICE NTSC settings for the Breadbin and the 64C. I am doing the precise same thing you are doing with the same software. If I hook up the SuperSnapshot cart image and do a memory fill before going into TMP, when I execute the program in TMP I get white square characters instead of "pi" characters -- either way I am still getting junk output.
What's going on? I've noticed this issue on multiple (recent) versions of VICE when following your assembly videos, where my screen output has a bunch of junk that yours doesn't. I don't know what older versions of VICE do.
Robin, never mind. I was going willy nilly with comments and it appears going to the line break (possibly over) was breaking the program, even if I added extra semicolons. I cleaned it up and it looks just like yours.
What happens if you happen to hit the restore key between the lda/sta/lda/star block to point nmi to tmp? Is there a chance to crash the 64?
Yes, there is a risk of crashing there. I can think of a couple work-arounds that I'd have to test, but pretty much nobody worried about that kind of thing back then during initialization. If code like that is being executed in a loop or during interrupt handling, then it's much more likely to cause a crash and should definitely be dealt with.
@@8_Bit I learned to worry about it when I added a nmi handler to make ctrl+restore do a restore of the VDC char set on a 128. That takes a little while, and a slightly bouncy restore key means it easily gets triggered while its still running, resulting in unintentional writes to screen memory (with the default vdc ram layout)
The easy solution is to have some location set to $ff and do something like this...
inc .location
bne +
+ dec .location
Of course.. that should return, or take care to decrease that .location again when done.
It is atomic because the inc .location cannot be interupted (irq or new nmi only happens after the instruction finishes), and for the next 255 subsequent calls, the bne will branch past the code which should be protected.
Nice outro 🐶
That was so sweet 😂
The song at the end? :)
am i the only one who did not see the output but a black screen ?
edit: rewatched it and saw the chars .. but the fast refresh had me fooled
The decimal mode version seems interesting but I don't think I'd want to abuse an obscure feature for something as basic as this.
Could your table be define like this
.byte "0123456789ABCDEF"
?
Anyone else head 'nipple' when he says 'nybble'? :-)
Actually, I've learned the meaning of the word nibble way before the word nipple gained some strange attractiveness to it 😉 - almost in the single digits of my life clock.
You teachers PET :p
Osborne books: VASTLY superior to any Sybex/Zaks book.
December 2019...when the world hadn't gone to shit....yet
did you mean December 2000??
@@mycosys Why Dec 2000?
I guess Dec 2019 would be obvious...:-)