Link to .d64 file: 8bitshowandtell.com/downloads/tmphex.d64 To use: LOAD"TMP",8,1 to load the assembler. SYS 32768 to start it. Back arrow (tilde in emulators) L to load. hex2 is the re-source of the original program, and hex8 is my modified program. Back arrow 3 to assemble, back arrow 1 to quit to BASIC, SYS 828 to start. Alternatively, LOAD"HEX2.O",8,1 or LOAD"HEX8.O",8,1 to load pre-assembled binaries of either version, SYS 828:NEW to start. The NEW is necessary when loading the binary from disk due to a quirk in how BASIC memory pointers are set after a load.
Hey Robin, I loved your comment about “not being the best at this, just excited about it”. I really appreciate that you’re just a guy that loves what he’s talking about and not trying to convince everyone that you have all the answers! Great video!
I've written some similar evaluation routines for the C64, such as an enhanced DOS wedge that had an option for numeric base conversion. To parse hex values, rather than writing special-case code to handle one-byte values, what I did was start by clearing two bytes in zero page (you could use $62/$63) and then rotate in each new digit, with the old two-byte value being shifted left by 4 bits before ORing in the new digit at the bottom. The parser would stop when it found a character that wasn't 0-9 or A-F. This way, you could enter anywhere from 1 to 4 hex digits and it would parse it correctly without needing special code for each combination. I had the parser throw an "Illegal Quantity" error if there was a carry from the high bit of the MSB, since on the C64, you're basically never going to be using any hex values outside the $0000-$FFFF range. There's one bug in your program that's difficult (or at least annoying) to fix. I ran into it as well, and even commercial packages like Simon's BASIC often have it. If you enter PRINT $CDEF, it will give a ?SYNTAX ERROR after showing the value. That's because DEF is a keyword, and before the line is executed, it is converted into a token ($96). This won't be recognized and will give bad results. For a DOS wedge that only is needed in immediate mode, you can get around this by wedging ICRNCH ($0304-5) and parsing the line before it's tokenized. But for a general-purpose utility it's harder. You would either have to special-case DEF, or take the hex value into a buffer and "untokenize" it before parsing it, which would cause the code to be longer and a bit slower.
Excellent point about "DEF" - I now have a very distant memory of hearing about that, but didn't think of it at all while working on this. Very interesting solutions, hopefully I can explore them sometime.
I’d be really intrigued to find out if C64 BASIC could have been written to deal with integer decimal, 8 bit and 16 bit values with any appreciable speed advantage versus treating everything as a float.
@@StefanHolmes The approach I would think would have been most practical, and would like to see explored, would be treating loads of values with 00 in the exponent as a short-hand for loading two bytes into the mantissa without an implied leading 1, but allowing addition, subtraction, and multiplication of such values to be performed directly without conversion to/from float.
@@StefanHolmes Another approach I've pondered, since the floating-point accumulator already has a mantissa that's a byte longer than the storage format, would have been whether it would have been practical to shift bytes only by multiples of 8 bits, but after each operation identify the position of the highest set bit in the upper byte. If e.g. the highest set bit is bit 2 (implying an upper byte value from 4-7), clear the bottom 3 bits of the bottom byte. When storing a number, take the five remaining bits of the bottom byte along with the two bottom bits of the upper byte (the third bit from the bottom would be known to be '1') and the sign bit, and combine them. This would make processing of the upper byte of the mantissa a little awkward, but would avoid the need for long slow chains of shifts that act upon the entire floating-point accumulator.
The extensive use of jump tables in these old OS and BASIC ROMs is so cool. It means you can very easily mod almost all aspects of how the computer works.
All of those books are absolutely fantastic. Hard to believe I've had them all for decades. I'd love to see more episodes disassembling some of the other gems in the Raeto book!
That's a very nice addition to the Commodore BASIC. It was always a pain to remember the decimal values. So much easier to remember d020 than 53280. It's a shame this wasn't part of the original BASIC.
@@jack002tuber The Apple Macintosh ROM had a built-in function called "StuffHex" which would accept an address and a string, and store data represented by two hex characters per byte into memory at the indicated address. If MS-BASIC had included a similar function, that would have cut by an order of magnitude the time required to put things like machine-language programs or character tables into memory, and also cut by about half the amount of BASIC code necessary to hold them (compared to data statements). If a program has a non-trivial amount of data to store, using a read/poke loop to put a 53-byte stuff-hex function into RAM and then using that for everything else would be better than using read/poke loop for everything.
The reason he did the TAY after PLA was probably because originally he wanted to use JMP $B391 to convert the integer to floating point, and that routine wants the low byte in Y and the high byte in A, which seems to be exactly flipped to me, against all ROM listings. Here I show how I step through the rest of what I am trying right now: .C:c08a 68 PLA - A:02 X:02 Y:02 SP:f1 ..-...ZC 1374505684 .C:c08b A8 TAY - A:D2 X:02 Y:02 SP:f2 N.-....C 1374505688 .C:c08c 68 PLA - A:D2 X:02 Y:D2 SP:f2 N.-....C 1374505690 .C:c08d EA NOP - A:04 X:02 Y:D2 SP:f3 ..-....C 1374505694 .C:c08e EA NOP - A:04 X:02 Y:D2 SP:f3 ..-....C 1374505696 .C:c08f 4C 91 B3 JMP $B391 - A:04 X:02 Y:D2 SP:f3 ..-....C 1374505698 My example was ?$04D2 (decimal 1234). And you see how the low byte D2 is PLAd of the stack first, moved to Y then the high byte 04 and finally the jump. And it's showing correctly $04D2. Yet in every listing I see it's the other way round: .,B391 A2 00 LDX #$00 set type = numeric .,B393 86 0D STX $0D clear data type flag, $FF = string, $00 = numeric .,B395 85 62 STA $62 save FAC1 mantissa 1 .,B397 84 63 STY $63 save FAC1 mantissa 2 .,B399 A2 90 LDX #$90 set exponent=2^16 (integer) .,B39B 4C 44 BC JMP $BC44 Here is an example of how it's used, loading the value of an integer variable: .,AF61 A0 00 LDY #$00 clear index .,AF63 B1 64 LDA ($64),Y get integer variable low byte .,AF65 AA TAX copy to X .,AF66 C8 INY increment index .,AF67 B1 64 LDA ($64),Y get integer variable high byte .,AF69 A8 TAY copy to Y .,AF6A 8A TXA copy low byte to A .,AF6B 4C 91 B3 JMP $B391 This is the commentary from Lee Davison, and the Data Becker commentary in German is saying the same thing. Yet in my experience as shown in stepping through my program above, it is clearly the other way round. Anyway, I think the guy ultimately gave up on using JMP $B391 because (a) he was as confused as I was and/or (b) because this treats the integer as a 16-bit signed int, i.e., $C000 comes out as -16384.
Another interesting episode of Robin's Retro Classroom. Coincidentally, I am slowly working on a new video regarding a long-forgotten C64 Kernal alternative that I happened upon a while back -- it includes some Hex wedge commands.
TY for your very informative video's. I learn from you more than back in the days from the books. I recently discovered TMP and btw i start it with SYS 2^15. a little more convenient for me to remember ;-)
Kudos to Commodore for knowing that built-in C64 BASIC lacks a lot of features and designed all those vectors to be easily modified by the user so it can be improved a lot
Robin I consider myself a decent programmer with a lot of years of experience but when you hit the assembler/dissembler I realize how correct I am...just a decent programmer lol. Great video as usual. :)
I KNOW NOTHING ABOUT COMPUTER PROGRAMMING. IM JUST GETTING BY ON PROGRAMMING SYNTHESIZERS. BUT I LOVE WATCHING YOUR VIDS. THE EARLY ONE ABOUT THE GAMES ON COMMODORE WAS GREAT.. THANKS!!
nono, if you check for 2 or 4 hex digit it won't be for 1 or 3 digit hex numbers, the basic idea of good full compatible, is to (after checking for the dolar sign) 1) initialize output to zero, 2) get if character is valid HEX IF NOT (escape returning the output register),IF YES convert the value,to 4bits nibble, multiply the output *16 (or SUB A, $30 -> JR C, notvalid_esc, -> DAA -> cp a, $10 , JRNC, notvalid_esc . So, the check for valid character and the convert it to nibble is the same code sinmultaneosly. In Z80 then would be, ADD HL,HL (4 times), -> then ADD L, a - > ADC H,0 -> DJNZ, LOOPBACK is that short.
Another great video Robin, thanks :) Also, did you know the FC3 cartridge supports '$' values in basic? I'm not sure if other freezer cartridges do or not, but found it to be pretty handy
0:52 Okay, I can read the raw machine-code numbers to the 133 on line 11… 5:45 Ah, that's STA zeropage. 1:49 Can the '$' values be used in general expressions like $D000+$0020? Seems like it. 6:43 I guess they considered it worthwhile, but putting this in zero page only saves 3 clock cycles and the content of the .Y register (or .X, since LDA (zp,X) could be used here). OTOH, it wastes time doing character tests that you don't always need (you could have several different variations of this routine in ROM that only do tests that are specifically needed; I suppose you still could) and consumes 24 bytes of zero page that could have been available to the user. While putting this in zero page enabled BASIC on the PET computers to be extended, the vectors you pointed out on the VIC-20 and later computers obviates this usage. 13:52 You can see the logic in the ASCII layout, which has various character sequences starting at round binary numbers, so we can assume that $30 to $39 being digits 0 to 9 is not a coincidence, and $40+i being Roman letters 1 to 26 is not a coincidence. 16:15 I seem to recall there being a ROM routine that will more easily put a 16-bit value into FAC1. Yes, my "Mapping The VIC" book says that $D391 converts .Y (LSB), .A (MSB) to floating point. The corresponding C64 routine would be at $B391. It doesn't need an exponent value. But, as we discovered in an earlier version of this video, that routine is only for *signed* 16-bit values, and he probably wrote the TAY code to use that routine, but then cloned some of the code from $B391 to force the use of *unsigned* values after seeing some unwanted negative numbers. There doesn't appear to be any existing ROM routine that that will set up an *unsigned* 16-bit integer into FAC1. The routine at $B391 being signed is why the FRE() function on the C64 is borked: www.unusedino.de/ec64/technical/misc/c64/romlisting.html#B37D . 16:58 Well, 32 bits for the mantissa, plus 8 bits for the exponent comes to a total of 40 bits (5 bytes). One presumes Microsoft chose this odd size since they wouldn't be taken seriously if they gave full-blown computers less precision than 8-digit pocket calculators. BASIC is more ROM-efficient if everything is done in floating point. It would have been nice if they had extended their 0.0 representation to be interpreted as a 16-bit integer to allow BASIC games to run faster. 17:11 I've always found binary floating-point fascinating. Even most professional software developers don't fully understand the nuances of using it. 19:38 I'd use a 16-bit shifting method that can accept any number of hex digits and does a «val := val * 16 + digit» and stops on the first non-hexit character. $a, $00000000000ffff. You could trap the overflow of «ROL high» to check for errors. 26:39 We'll assume that these hex constants execute faster than decimal constants since no floating-point multiplication is needed to parse them. Are they faster than referencing variables? 27:58 You could also mention the 24-hour early access to get FRIST POST!!!1!
I'd probably use the variable length hex system as well, but maybe limit it to 8 hex digits because can't think of a realistic reason why you'd want to use more on an 8-bit computer! And maybe have the hex digits themselves in a lookup table, even though that would "waste" 16 bytes, because it would make the whole thing a lot more robust.
@mPky1 I think his point was that there would be no effort to handle longer values. I think eight digits would be a good limit because the largest odd number that the Commodore can represent accurately would be $FFFFFFFF. Although nine non-zero hex digits could be meaningful for even numbers up to $1FFFFFFFE, multiples of four up to $3FFFFFFFC, or multiples of eight up to $7FFFFFFF8, those usage cases would be very rare. Actually, even going beyond four would probably be useful too rarely to be worthwhile.
The use of hex, though is for expressing addresses and register values ... and those are either 1 or 2 bytes So for the 6510 processor, the faster approach also covers the real use case.
Hey Robin! Thanks for another interesting episode! I remember using 'BASIC compiler' programs to speed up my BASIC programs. It would be really interesting if you did an episode on how those worked. I'm guessing these 'compiler' programs parsed BASIC's representation of the listing and output a bunch of hardwired 'jsr' statements using constants to skip the overhead of the BASIC interpreter's parsing time.
There are SO many of the examples in that book where I wish they had just listed the Assembly source instead of these DATA statements. It's a useful book, but would be 5x as useful with that one added detail.
The way I do it is as follows. Set 16 bit value to zero Ldx #4 .loop Read char Branch not hex digit done Get nibble or value low dex asl value low rol value high asl value low rol value high asl value low rol value high ask value low rol value high Dex BNE loop .done It will read UP TO four hex digits stopping at the first non digit. The value will contain the 16 bit value
Robin. Thanks for all you do. I have a the Raeto West programmer book for the PET. I'm sure you have this as well. I have worn this thing out like it was the only bible left in the world. I used it so much and added tons of C64 comments. Did not even think to look for the C64 version. Found it at Archive.org . Had to do a little extra searching for it though. Saving a little time for others; here's the direct link to it. archive.org/details/computes-programming-the-commodore-64-the-definitive-guide-revised-edition
I noticed the discrepancy between the codebase64 write-up and the one here: www.c64-wiki.com/wiki/Floating_point_arithmetic I've also seen some conflicting information in some of my old C64 books that discuss it. I'll have to sort it all out before I make an episode about floats!
@@8_Bit c64-wiki.com is definitely right (this can be proved if one is capable of arithmetic's). Nice saying "conflicting information" - I would consider it rather as "wrong information" ;)
20:54 The CHRGOT LDA is probably misformatted because it isn't a proper instruction since it doesn't have an operand. The assembler/formatter probably interpreted it as a macro call instead of as a label and and a processor instruction. One could more properly use CHRGOT .BYTE $AD or CHRGOT LDA $ffff : TXTPTR = CHRGOT+1.
Right, but in this case this kind of poor formatting could be imagined as a try to make it clearer to the reader, not to convince an assembler to accept the source. ;)
I wish they taught useful stuff like this in high school, being able to calculate hexadecimal would be far more useful than all that shit with calculus,& differential equations.
I grew up with a Commodore 64, but was very limited in assembly programming because I didn't yet have a clear understanding of bitwise/Boolean operations. If only I had learned that in school...
It's possible, but it would take some work, finding some free space in the ROMs and then making sure the patch got installed during boot. There's so little free space in the ROMs that most mods like this require some other functionality to be disabled as a trade-off, such as cassette support. Probably more practical would be to create a cartridge image that would install it at boot and then return to BASIC, and then use that cartridge (either a stand-alone simple ROM cart or an EasyFlash).
I think, it is better to use a BASIC/KERNAL/ROM selector to switch between already known improved KERNALs or if one uses a floppy speeder, some have already such an extension (e.g. SpeedDOS+). I saw ROMs eliminating several best known bugs in BASIC, but I don't know if there is one with such a hex extension around. However, this would be a nice project.
The first thing the int-to-float ROM routine does is copy the integer value from the FAC into A and Y, from which it does the floating-point conversion. So I imagine the TAY was there because the code was originally jumping past the main entry point to where the routine expected it in A and Y already, saving a couple memory stores and loads. This seemed useful enough that I decided to port it to the VIC-20; the zero page vectors are all the same, and the ROM routines all have direct analogues in slightly different memory locations, so I expected it to be straightforward. There was one surprising difference in behavior, though: the VIC's int-to-float routine treats the integer as signed! It's somewhat less useful when POKE $900F,8 gives an ILLEGAL QUANTITY ERROR. So I added a check of the sign bit with an add of 65536.0 to negative results. My version is up at github.com/markjreed/commodore-stuff/tree/main/evalhex_vic.
Is there a way to save the assembly component with the BASIC so you don't have to manually load it before hand or if your distributing it to someone that doesn't have it? Cause otherwise it won't work after a reboot or if you don't have it.
The HEX8.O file could be included on the disk along with your BASIC program, and then the first lines of your BASIC program would be something like: 1 IF A=0 THEN A=1:LOAD"HEX8.O",8,1 2 IF A=1 THEN SYS 828
Robin, thank you. I saw how you loaded up Turbo Macro Pro. I assume this works also for the new "TheC64".?? I should be able to take my files from the "C64 Macro Assembly Development System", load them on a flash drive, and then be able to program "TheC64" with the Assembler that I know. I have the Assembler on C64 Disk, and also have migrated it to the VICE Emulator, but wanted to have it available when the new "TheC64" is sold in the USA. Can anyone confirm this will work for me?
Hi, yes, it should work. If you haven't already, check out my two videos about TheC64: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-z2GACGEucTI.html and ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-88cJVoESSps.html Both have a full timestamped index in the video description which should help you find the most relevant information.
Robin, Thank you. I can study these again now more carefully. I have the .D64 files on my Dell PC Win 10 for the C64 Emulator, and should be able to copy them to a USB stick. As soon as "The C64" becomes available in the USA, it can join my (real) Commodore64s, VIC20, and C64 Emulator!
I found a bug in hex2.o and hex8.o. I attempted to put in "10 input a$" and it freezes the computer instantly. If I do the same thing with basic hex, it will take it, but it errors on execution. I admit my C64 is a bit rusty, but if I recall correctly input a$ is required to input actual string (not numerical) input data. At least on the emulator. I didn't feel like moving this to my USB stick for my U2+ yet.
@@oqibidipoand in z80, there's no clear carry.. there's set carry and invert carry.. old CPU's are quirky and have a few first revision "bugs" that were kept for not breaking the code. At least 6502 fixed a broken rotate right after first revision
@@8_Bit Ha! Nice touch. No, I did not get it until you mentioned it, 1=white, right? Yes I'm a bit slow. It has been more than a while since I last programmed the c64 but I'm sure those more familiar will have got it straight away. Nicely done! All my programming is assembly on the 4032 ( following Raeto West too / Programming the PET/CBM ). I figure that if I can master the PET then the transition to the c64 won't be too difficult. Thanks for the upload. 👍
Just curious: could you enter even the program line numbers in hexadecimal? And when the user needs to INPUT a number, would even that work in hex as well?
I tried input and.........just trying to write 10 input a$ crashes (hex8) or errors out (basic hex). lol If I write the code ahead of time and then run extension and run the program it doesn't support it at all. $FF just returns $FF not 255.
Entering a line number in hexadecimal with this routine would not work. That's because the BASIC ROM routine that interprets the program line you entered doesn't go through IEVAL, but uses a much more limited routine, LINGET ($A96B), which only reads decimal literals in the 0-63999 range. It doesn't support variables or expressions. The LINGET routine is also used by GOTO and GOSUB, which is why you can't use a variable or formula in these commands. (You could fix that by copying BASIC ROM to RAM, patching GOTO and GOSUB to call the full expression evaluation routine instead, and banking in the RAM.)
fancollector1980 gave an excellent answer. Unfortunately the hex support doesn't work with input. Presumably it uses a different pathway to interpret the numbers, as you also can't type e.g. 5+5 into a numeric input (you get a ?REDO FROM START error) and if you do a string variable input, VAL() will not evaluate any kind of formula, it just converts a numeric constant.
Unfortunately, no - even though the VIC-II colour registers can be POKEd with an 8-bit value (whether it's up to 255 in decimal or $FF in hex), only 4 bits are connected, with 16 possible colours. Extra apparent colours can be generated by a couple techniques (flickering and mixing) and I started working on an episode about that but got stalled with some problems with capturing the video properly.
the system has a physical hard limitation of 16 colors. There is no way around that. If you exceed them.......it just cycles through the same ones again.
wait the BASIC doesn't support Hexadecimal by default? why? it seems like a really fundamental thing to have, especially with POKE/PEEKs. and what about Binary?
Sinclair BASIC doesn't support Hex natively either, but it does have a BIN keyword that lets the user enter binary numbers up to 1111111111111111 or 65535d. Locomotive BASIC, which is the version that runs on the Amstrad CPC range, supports both Hex and Bin, however.
C128 BASIC has DEC() and HEX$() functions for hexadecimal support, but the earlier V2 BASICs only supported decimal. Not sure why, but they were extremely constrained for ROM space and presumably it wasn't a priority.
@@8_Bit BASIC V2 was just a dreadful choice for the C64. Wikipedia says Commodore were pushed for time, so they just picked BASIC from the PET/VIC-20. That sounds believable. I do wonder why they didn't improve things with the 1985 release of the C64C, but I wouldn't be surprised if it was purely a cost reduction method as they were then focusing on the new Amiga released the same year. (EDIT: C64C actual release was 1986 according to Google!) Imagine though if BASIC V7 had been shoehorned into the C64C. Might have stretched its marketable life out a little more.
Now I personally like that c64 didn't have better basic, no graphics functions, "weird" way of displaying hires and all other quirks. That forced me into learning assembler. The second reason why I start programming is that for the first eight months I had only C64 without tape or disk. Imagine every morning you type program, afternoon play game that you programmed at evening turn of c64 and tomorrow repeat that process again. More hardcore was all literature that I had was in English or German and I didn't understand either of them. This channel is the reason that I bought broken c64, fixed it, build pi1541 and I will try to recover my software from old data tapes.
Excellent video. However it confirms me how BASIC is inefficient. You enter an hex value, converted from text to 16 bits value, then converted by floating point, then converted back to 16 bits to address the correct register in memory or mmio.
Yes, the inefficiency is mostly due to them squeezing the whole BASIC language into 8K or 9K of memory, and there not being enough room for full float and int support. Although there's lots I wish were better or different in CBM BASIC, the more I study how it works, the more respect I have for what they managed with the constraints.
4 года назад
@@8_Bit Exactly. It can be difficult getting younger folks to appreciate what it really meant to be working with the these 8bit computers extremely limited memory. This is very easily lost in today's world of excess of RAM, CPU and GPU speeds.
Here is another interesting idea: You could initialize the x register with #$fe, count it upwards, store the integer bytes directly with "sta $64,x" and thus get rid of the stack operations altogether! It would look something like this: ishex ldx #$fe ; 2 bytes ldy #$03 [...] onebyte lda #$00 ; high byte will be 0 sta $64,x inx ; loop only once! loop jsr $0073 ; get digit [...] sta $64,x ; store in FAC inx ; more digits? bne loop ; yes, repeat [...]
Hi, I am a new member. I have a card with 2 5V inputs and 6 outputs with relays. Everything works fine, but I would like to get 3 inputs at 5V and 5 outputs. The program they associated with the card uses POKE and PEEK. Is there someone who can help me? Peter
@@El_Grincho If the bits are added together, yes! :) There's a comparison between CBM floats and IEEE 754 (single-precision C floats) here: www.c64-wiki.com/wiki/Floating_point_arithmetic
If you do exact precision accounting, 1 is $0.01 and standard 32 bit floats can only go up to $160,000 some dollars.Commodore insisted on more, so Microsoft extended their mantissa to 32 (effective) bits (given that a normalized value always has 1 left of the binary point so the leading 1 can be omitted).
Easy and relaxing like some assembly. LOL. Sure. By the way, a thing that injects itself into the normal operating program, that's what a virus does. Great video!
On the C64, a lot of legitimate utility applications were implemented using the kind of techniques that are now found only in malware. For instance, consider the common instance where you have a program that auto-runs when you LOAD"*",8,1. That isn't a feature included in the KERNAL. Rather, you have to set the load address of your routine to make it load where it will either smash the stack or do what we would now call a buffer-overflow exploit on the vectors in page 3. No memory protection at all - it's just the way things were in the 8-bit days. Of course, the upside of this was that the "operating system", such as it was, was entirely in ROM. If anything got messed up, just turn it off and back on, and you're back to factory settings. The worst any potential malware could have done was wipe the contents of the current floppy. (I suppose it would be possible to write some sort of 1541-resident malware that would screw up other disks placed in the drive, but even then, turning the drive off and back on would wipe that out too).