When I was a kid I got the game I discovered that if stay on the left side of the track you can time it right and go for hours without crashing. Just modulate the throttle, and time the back and fourth cars.
You sure you're not thinking of Spy Hunter or some other similar game? I can't see how you could get through certain parts of LeMans like that, especially where the road splits into two narrow bridges that get clogged up with cars.
Using a bitwise "AND" to compute the modulus by a power-of-2 divisor is a very common trick. In fact, that's how most (all?) optimizing compilers of higher-level languages like C implement unsigned modulus by a power-of-2 divisor. Edit: I see you addressed this in some other comments. I agree that it's a little surprising to see this trick used when the divisor is notionally 2000 (decimal), which is not a power of 2, but of course it is when it's stored in BCD format.
Yes, I've seen that many times, and used it in my own games etc. What I've never seen before is using it for detecting multiples of decimal numbers, like 20,000 / 40,000 / 60,000 etc. points.
That AND is clever, it basically discards a lot of values and concentrates on the point where the (decimal) numbers roll over to the next 20,000. Neatly programmed, and clever how it is checked twice (the standard score increment, and when the bonus 1000 is added). What really blows my mind is the discipline needed to switch between decimal mode and binary mode in the same section of code; dealing with different variables in different modes isn't easy - especially considering much of this would have been done without a modern assembler/development environment. The STX/STY definitely looks like leftover code. It may well be that it was originally written to preserve values, or the coder often added that sort of code at the start of an important subroutine. It's a good habit to store and fetch variables, unless your code is absolutely time-critical. It had not occurred to me that the game was NOT using screen timing/interrupts to determine the speed. Maybe that is down to being so early in the C64/Ultimax's life that such routines were not commonplace or indeed even suggested for use. Having played it a lot (in the joystick-patched version) I noticed that it gets difficult fast. And that "difficulty table" is a fairly efficient way of doing that.
If the label said "Seconds" instead of "Time" it would be more flagrant. I think the better way to do it and still be considered "fair" would be if you got fewer and fewer extra seconds for completing a lap (instead of making those "seconds" shorter and shorter).
Yes, it's true that the game doesn't actually say "seconds", even in the manual. There's just 60 units of time that start off about second-length so I wrongly assumed they were seconds :)
@@8_Bit I mean, even if they aren't exactly 1 second, it's bizarre that they change them. I'd test putting it at 3C for all of them, and see what you get.
Another amazing video Robin! You are tenacious! (I mean that in a very good way!) I wouldn't have the patience for delving into something like this. One more reason that I love this channel!
One of my favourite games :D I was between 2-6 when i played with the C64 and this game was just accsessible to such a small kid. Later on when i was 15 i played that game agains a friend, in one round i got really lucky and played about 15 Minutes straight. You can imagine that the Highscore was absurdly high... Was the last round we played haha
I will say that if the title was the actual name of the city, "Le Mans" then it would be right to silence the "s". But since they made up a brand new word, LEMANS, then anything goes!
The AND #$1F is not an obvious solution. Very clever. I was thinking...you want to check that the least significant digit is zero, so the F in $1F makes sure of that. The other check is just to see whether or not the number is even or odd. So checking for a $1 makes sure you have an even number. It makes sense but I would have probably not thought of it. Great stuff.
The 6502 also has an instruction called BIT, which performs a logic AND between the accumulator and the contents of some location in memory (it can take a long address anywhere in memory or a short one in ZP, but not an immediate operand) *without* saving the result, except for setting the Z flag if every bit of the result was 0. If you have the value you want to AND with in memory somewhere, you can just specify its address in a BIT instruction. Although it has to take an extra clock cycle to read the operand, the accumulator remains unaltered; so you don't need to store and retrieve it, which _might_ offer a net time saving. For instance, if you need to check if or not the high nybble of the accumulator is zero, you can BIT with some address containing &F0 (which is the opcode for a BEQ instruction, so is very likely already to be present in your code). BIT also sets the N flag according to bit 7 of the operand byte, and the V flag according to bit 6 of the operand byte. This can be useful for reading status flags without altering the accumulator contents. The opcode for RTS is &60, so a BIT instruction against an RTS (which is even more likely to be present) will set V. It's still 3 bytes and 4 cycles, but might be useful if you ever need to return _two_ bits of state information from a subroutine. One more use for BIT is to "mask out" a one or two byte instruction, by prefixing it with the opcode for BIT short (=&24) or long (=&2C) respectively and conditionally branching to the byte _after_ this opcode. If the branch is not taken, the instruction is interpreted as BIT; which will read from memory and alter the flags, but not affect the contents of the registers. Otherwise, what should have been the address for the BIT instruction will be interpreted as an instruction. For instance, 2C A5 01 looks like "BIT &01A5"; but a branch to the second byte will end up loading the accumulator with 1.
Aha, yes, of course. Thanks. I was thinking of "just" using INC for the carry into locations $0B and $0C but you're right, the same problem would happen there.
@@8_Bit I figured that's what you meant. I actually meant A as in the accumulator, but I forgot that instruction was introduced later and doesn't exist on the 6510. Sorry that was ambiguous.
1:55 Sections of the code look quite space-inefficient. 3:26 If they use Decimal mode, they should disable it in their IRQ routine, since the Kernel IRQ doesn't. 12:54 Similarly, a test against $01 detects every even value. The same thing can be done for every power of 2. AND #$1F will check if the value is evenly divisible by 32, which in BCD is $20.
Great vid as usual. I always find these fascinating. I am still being amazed at all the little tricks for checking for different conditions. The 'and 1F' trick is definitely a clever one. Thanks for doing all the hard work you do for us.
18:22 Is the SEC at &EFFE strictly necessary? The BCC at EFF7 did _not_ branch; and we have done nothing since then that would affect the carry flag, so this suggests C should still be set when we get here. I guess you might need an explicit SEC if something else did a JMP to this section of code with the carry in an unknown state, but this seems like a waste of a byte and two ticks of the CPU clock.
Awesome series of videos, Robin! I love Le Mans as it's the only racing game I know of that uses the paddles, which I think are sadly underutilised. If you ever feel like coding another game for the C64, please consider Sega's Turbo with paddle support. John Champeau of Champ Games did an amazing version for the Atari 2600 & I would love to see this on the C64. Cheers
Very interesting. In my ZX Spectrum game puzzle game BlockZ there is an extra timer byte at the end of the data for each level, so each level gives a different amount of time. There are always 160 "seconds" of time to complete each level but the timer byte defines how long a second is by using the following formula (timer_byte/50). So the higher the value of the timer byte is then the slower the timer will go down because each of the 160 "seconds" will be longer. The timer is handled within the interrupt routine on my PAL machine every 50th of a second. It checks to see if the timer byte is greater than 0 and decrements it by one if it is. If it is zero it checks if the number of "seconds" are greater than zero. If it is then the number of "seconds" is decremented and the timer byte is reset to correct value for the length of a "second" for that level. If the "seconds" are zero then time is up, a life is lost and so forth.
Your reversing videos are my favorite videos. It's always fun to explore clever things the ancient ones did (like the AND mask compare). If we find their solutions clever, likely they did too. :)
Another use for BCD is the time of day clocks in the CIA chips. Why would you use those instead of the clock kept by the system interupts? Simple, you do not have to put any work into keeping the clocks in the CIA chips running. You do have to set a time, ensure they are configured for either 50hz or 60hz line power (see codebase64 for how to do this properly) and start them.. after that they'll keep time for you, also when your interupt does not run for a while, or with a 'wrong' interval for some reason (say.. loading something from disk).
Great video. The explanations of the routines were excellent. How were you able to find and identify the different routines originally? I can understand assembly code but I'd like to get into disassembly to be able to find a small routine like you have. How much disassembly did you actually have to do to locate the score counter?
13:10 or just read the current score, do a ror and compare Though this way probably save cpu cycles Since you need to load the score value, every increment, then rotate and compare Could use the rotate instruction and use the cpu flags as a compare mask, get rid of the and or try to combine the instructions Set it up to ror, and branch on carry or branch on overflow
Yay, first time I catch one of your videos on the same day it gets released! Given the RTS at $F48A, I suspect that the code block from $F462 could be some standard routine that the authors reuse across games, and are just using good practice to store registers upon entering a subroutine before changing them, and restoring them before exiting. The fact that they swapped them in the process is unfortunate! I wonder if this could be a way to fingerprint other titles from them?
I didn't try changing the value as I suspect I'd be able to play it "forever" if I made it easier. As it stands I've sometimes had 300,000 point games (maybe 15-20 minutes of gameplay, I'm not sure) so I'm not really sure the game would be better for it. It would probably need another goal or gameplay element if it didn't "cheat" like this.
In the 4th video do you patch the code to use a random # within certain bounds such that each subsequent level is more difficult or occasionally easier, or is this it? Just kidding : ) But here is a serious question: how many of the original cartridges are based on the same code-base? I assume some of the post Ultimax CBM carts were as well? Is there a complete list online somewhere? Thank you as always. I learn something every time!
That would be kind of interesting to have some variation in the difficulty levels. When luck and skill combine at the right time, you could get some very high scores. I'm not sure if any of these HAL Laboratory Max/C64 launch titles share a code-base at all, beyond probably a few re-used routines that were probably copied and shared (and modified a bit) around the office (which might have been a single bedroom with 5 guys crammed in it, from what I've read!). Oh, or do you mean across platforms? Like, some of these C64/Max titles can be traced back to the VIC-20 and even PET in their 6502 form, and they were also ported to Z80 machines in some cases. So a game like Avenger (Space Invaders) may truly have a code-base that can be traced across several platforms. But I don't think (for example) LeMans and Avenger on the C64/Max have much code in common. Each game had a single main developer from the HAL team and they would share advice and code snippets (likely on paper!) but probably nothing more organized than that.
@@8_Bit Looking forward to your adding another BCD nibble-pair of digits to account for the yet-to-be-achived higher scores and your mod to generate a random x'th of a second-second that maybe has a 1/16th probability of zonking you with an impossible 'kill screen' fate.
It would be interesting to know why they chose to go to 64(40 hex) making the second "difficulty" slightly easier. Were they trying to lull you into a false sense of security or was the initial value 64 and then later changed to 60 by someone trying to match the refresh rate?
Yes, i have. In a nasty turn of circumstances, this song is cut off at the end!! Good thing I had an intact version to draw from in the past. (j/k.. I'm sure it wasn't intentional on Robin's part.)
I had (and have) the original cartridge *and* a pirated copy on disk :) I actually preferred the pirate copy because it had been hacked to use joystick instead of paddles and I found that a bit easier to control, especially when exiting the pits.
If it was in binary, and not BCD, how would you code… Every 256 points? Every 512 bytes? etc Basically it’s very easy to check for any multiple of a power of 2 simply by checking the n low order bits are zero. Perhaps easiest to comprehend if you remember you can multiply 2^n simply by shifting left n bits. Axiomatically that shift moves zeros into the low order bits. Very similar for BCD, where you can check for powers of 10 for example 10, 100, 1000 etc. But also you can check for double those values by masking one extra bit in the next nibble in order to check the next digit is even and not odd.
Yeah, quite a few times I've covered using AND as a modulo on a binary number, such as AND #%00000111 to do a MOD 8 (remainder 0 to 7). What's new to me here is to use AND as a way of identifying decimal (Binary Coded Decimal) multiples, such as 2000, 4000, 6000 etc.
I have to agree, that AND with 1F is genius. It covers so many cases and is really fast and efficient. 👍 Also, you're a lot of things, "simple minded" isn't one of them!!! 😎
I KNEW IT! 😂 One of my first two games, with the cartridge of International Soccer. I also had the paddles, mine were darker in colour, similar to the breadbin case iirc, with a red switch
Yes, HAL got their start with Commodore. As hobbyists on the Commodore PET, then professionally on the VIC-20, Commodore Max, and Commodore 64. Then they got into MSX and of course Nintendo.
Listening to you explain it is a bit like when that guy in The Matrix claims he can see what’s going on by watching the code lol😂 even understanding, it’s still a bit mystifying. I couldn’t recall any of this material as readily.
You are definitely not simple minded, at least as it pertains to assembly ;) In all seriousness, you are clever so if you are amazed that says something.
When I was a kid I got the game I discovered that if stay on the left side of the track you can time it right and go for hours without crashing. Just modulate the throttle, and time the back and fourth cars.
You sure you're not thinking of Spy Hunter or some other similar game? I can't see how you could get through certain parts of LeMans like that, especially where the road splits into two narrow bridges that get clogged up with cars.
Using a bitwise "AND" to compute the modulus by a power-of-2 divisor is a very common trick. In fact, that's how most (all?) optimizing compilers of higher-level languages like C implement unsigned modulus by a power-of-2 divisor.
Edit: I see you addressed this in some other comments. I agree that it's a little surprising to see this trick used when the divisor is notionally 2000 (decimal), which is not a power of 2, but of course it is when it's stored in BCD format.
Yes, I've seen that many times, and used it in my own games etc. What I've never seen before is using it for detecting multiples of decimal numbers, like 20,000 / 40,000 / 60,000 etc. points.
When life gives you Lemans...
... you port it to other platforms. :)
You make a Super-Snapshot dump lol
Always nice to dive into MOS 6510 assembly. Love it. Very nice video, thank you!
That AND is clever, it basically discards a lot of values and concentrates on the point where the (decimal) numbers roll over to the next 20,000. Neatly programmed, and clever how it is checked twice (the standard score increment, and when the bonus 1000 is added). What really blows my mind is the discipline needed to switch between decimal mode and binary mode in the same section of code; dealing with different variables in different modes isn't easy - especially considering much of this would have been done without a modern assembler/development environment.
The STX/STY definitely looks like leftover code. It may well be that it was originally written to preserve values, or the coder often added that sort of code at the start of an important subroutine. It's a good habit to store and fetch variables, unless your code is absolutely time-critical.
It had not occurred to me that the game was NOT using screen timing/interrupts to determine the speed. Maybe that is down to being so early in the C64/Ultimax's life that such routines were not commonplace or indeed even suggested for use.
Having played it a lot (in the joystick-patched version) I noticed that it gets difficult fast. And that "difficulty table" is a fairly efficient way of doing that.
Dummy 0's (zeros) was a very common practice with EM pinball machines as well.
If the label said "Seconds" instead of "Time" it would be more flagrant. I think the better way to do it and still be considered "fair" would be if you got fewer and fewer extra seconds for completing a lap (instead of making those "seconds" shorter and shorter).
Yes, it's true that the game doesn't actually say "seconds", even in the manual. There's just 60 units of time that start off about second-length so I wrongly assumed they were seconds :)
@@8_Bit I mean, even if they aren't exactly 1 second, it's bizarre that they change them.
I'd test putting it at 3C for all of them, and see what you get.
Another amazing video Robin! You are tenacious! (I mean that in a very good way!) I wouldn't have the patience for delving into something like this. One more reason that I love this channel!
Great video as always! Its neat to see how the inner workings of some of these games were coded.
Fascinating, I only follow bits of what you say but it is interesting to see how things were done back then especially to save a few bytes
One of my favourite games :D I was between 2-6 when i played with the C64 and this game was just accsessible to such a small kid.
Later on when i was 15 i played that game agains a friend, in one round i got really lucky and played about 15 Minutes straight. You can imagine that the Highscore was absurdly high... Was the last round we played haha
Thanks, Robin! Good stuff. Bitwise ops still mystify me on first glance, I have to put my brain into a lower gear every time.
I will say that if the title was the actual name of the city, "Le Mans" then it would be right to silence the "s". But since they made up a brand new word, LEMANS, then anything goes!
The AND #$1F is not an obvious solution. Very clever. I was thinking...you want to check that the least significant digit is zero, so the F in $1F makes sure of that. The other check is just to see whether or not the number is even or odd. So checking for a $1 makes sure you have an even number. It makes sense but I would have probably not thought of it. Great stuff.
That was a nice logical explanation.
The 6502 also has an instruction called BIT, which performs a logic AND between the accumulator and the contents of some location in memory (it can take a long address anywhere in memory or a short one in ZP, but not an immediate operand) *without* saving the result, except for setting the Z flag if every bit of the result was 0. If you have the value you want to AND with in memory somewhere, you can just specify its address in a BIT instruction. Although it has to take an extra clock cycle to read the operand, the accumulator remains unaltered; so you don't need to store and retrieve it, which _might_ offer a net time saving.
For instance, if you need to check if or not the high nybble of the accumulator is zero, you can BIT with some address containing &F0 (which is the opcode for a BEQ instruction, so is very likely already to be present in your code).
BIT also sets the N flag according to bit 7 of the operand byte, and the V flag according to bit 6 of the operand byte. This can be useful for reading status flags without altering the accumulator contents. The opcode for RTS is &60, so a BIT instruction against an RTS (which is even more likely to be present) will set V. It's still 3 bytes and 4 cycles, but might be useful if you ever need to return _two_ bits of state information from a subroutine.
One more use for BIT is to "mask out" a one or two byte instruction, by prefixing it with the opcode for BIT short (=&24) or long (=&2C) respectively and conditionally branching to the byte _after_ this opcode. If the branch is not taken, the instruction is interpreted as BIT; which will read from memory and alter the flags, but not affect the contents of the registers. Otherwise, what should have been the address for the BIT instruction will be interpreted as an instruction. For instance, 2C A5 01 looks like "BIT &01A5"; but a branch to the second byte will end up loading the accumulator with 1.
This game reminds me of those electro mechanical 'digital derby' toys from the 80s
Robin made a video about these "zero-bit games": th-cam.com/video/NwQMBV7Td1s/w-d-xo.html
Arcade games did need to "kill you off," because of coin detected in pocket.
Yes, and that didn't need to carry over to home video game consoles and computer games, but it did. That was my point.
5:07 INC doesn't apply the BCD adjustment, so it can't be used here. If A holds $09, INC would go to $0A instead of $10, irrespective of the D flag.
Aha, yes, of course. Thanks. I was thinking of "just" using INC for the carry into locations $0B and $0C but you're right, the same problem would happen there.
@@8_Bit I figured that's what you meant. I actually meant A as in the accumulator, but I forgot that instruction was introduced later and doesn't exist on the 6510. Sorry that was ambiguous.
1:55 Sections of the code look quite space-inefficient.
3:26 If they use Decimal mode, they should disable it in their IRQ routine, since the Kernel IRQ doesn't.
12:54 Similarly, a test against $01 detects every even value. The same thing can be done for every power of 2. AND #$1F will check if the value is evenly divisible by 32, which in BCD is $20.
Great vid as usual. I always find these fascinating. I am still being amazed at all the little tricks for checking for different conditions. The 'and 1F' trick is definitely a clever one. Thanks for doing all the hard work you do for us.
The song at the end really rocks :)
18:22 Is the SEC at &EFFE strictly necessary? The BCC at EFF7 did _not_ branch; and we have done nothing since then that would affect the carry flag, so this suggests C should still be set when we get here.
I guess you might need an explicit SEC if something else did a JMP to this section of code with the carry in an unknown state, but this seems like a waste of a byte and two ticks of the CPU clock.
Awesome series of videos, Robin! I love Le Mans as it's the only racing game I know of that uses the paddles, which I think are sadly underutilised. If you ever feel like coding another game for the C64, please consider Sega's Turbo with paddle support. John Champeau of Champ Games did an amazing version for the Atari 2600 & I would love to see this on the C64. Cheers
Very interesting.
In my ZX Spectrum game puzzle game BlockZ there is an extra timer byte at the end of the data for each level, so each level gives a different amount of time.
There are always 160 "seconds" of time to complete each level but the timer byte defines how long a second is by using the following formula (timer_byte/50). So the higher the value of the timer byte is then the slower the timer will go down because each of the 160 "seconds" will be longer.
The timer is handled within the interrupt routine on my PAL machine every 50th of a second. It checks to see if the timer byte is greater than 0 and decrements it by one if it is. If it is zero it checks if the number of "seconds" are greater than zero. If it is then the number of "seconds" is decremented and the timer byte is reset to correct value for the length of a "second" for that level. If the "seconds" are zero then time is up, a life is lost and so forth.
Your reversing videos are my favorite videos. It's always fun to explore clever things the ancient ones did (like the AND mask compare). If we find their solutions clever, likely they did too. :)
There were clever programmers even some 40 years ago. ;)
Another use for BCD is the time of day clocks in the CIA chips.
Why would you use those instead of the clock kept by the system interupts? Simple, you do not have to put any work into keeping the clocks in the CIA chips running. You do have to set a time, ensure they are configured for either 50hz or 60hz line power (see codebase64 for how to do this properly) and start them.. after that they'll keep time for you, also when your interupt does not run for a while, or with a 'wrong' interval for some reason (say.. loading something from disk).
Great video. The explanations of the routines were excellent. How were you able to find and identify the different routines originally? I can understand assembly code but I'd like to get into disassembly to be able to find a small routine like you have. How much disassembly did you actually have to do to locate the score counter?
Awesome video! Got nothing to add but a “fair” progression would be -6 -6 -5 then either -5 or -4
13:10 or just read the current score, do a ror and compare
Though this way probably save cpu cycles
Since you need to load the score value, every increment, then rotate and compare
Could use the rotate instruction and use the cpu flags as a compare mask, get rid of the and or try to combine the instructions
Set it up to ror, and branch on carry or branch on overflow
Yay, first time I catch one of your videos on the same day it gets released!
Given the RTS at $F48A, I suspect that the code block from $F462 could be some standard routine that the authors reuse across games, and are just using good practice to store registers upon entering a subroutine before changing them, and restoring them before exiting.
The fact that they swapped them in the process is unfortunate! I wonder if this could be a way to fingerprint other titles from them?
Did you fix it then play it again? How much better could they have made the game if there was no timer or game over?
I didn't try changing the value as I suspect I'd be able to play it "forever" if I made it easier. As it stands I've sometimes had 300,000 point games (maybe 15-20 minutes of gameplay, I'm not sure) so I'm not really sure the game would be better for it. It would probably need another goal or gameplay element if it didn't "cheat" like this.
In the 4th video do you patch the code to use a random # within certain bounds such that each subsequent level is more difficult or occasionally easier, or is this it? Just kidding : ) But here is a serious question: how many of the original cartridges are based on the same code-base? I assume some of the post Ultimax CBM carts were as well? Is there a complete list online somewhere? Thank you as always. I learn something every time!
That would be kind of interesting to have some variation in the difficulty levels. When luck and skill combine at the right time, you could get some very high scores.
I'm not sure if any of these HAL Laboratory Max/C64 launch titles share a code-base at all, beyond probably a few re-used routines that were probably copied and shared (and modified a bit) around the office (which might have been a single bedroom with 5 guys crammed in it, from what I've read!). Oh, or do you mean across platforms? Like, some of these C64/Max titles can be traced back to the VIC-20 and even PET in their 6502 form, and they were also ported to Z80 machines in some cases. So a game like Avenger (Space Invaders) may truly have a code-base that can be traced across several platforms.
But I don't think (for example) LeMans and Avenger on the C64/Max have much code in common. Each game had a single main developer from the HAL team and they would share advice and code snippets (likely on paper!) but probably nothing more organized than that.
@@8_Bit Looking forward to your adding another BCD nibble-pair of digits to account for the yet-to-be-achived higher scores and your mod to generate a random x'th of a second-second that maybe has a 1/16th probability of zonking you with an impossible 'kill screen' fate.
Love this kind of deep dive
It would be interesting to know why they chose to go to 64(40 hex) making the second "difficulty" slightly easier. Were they trying to lull you into a false sense of security or was the initial value 64 and then later changed to 60 by someone trying to match the refresh rate?
Has anybody typed in the program from the end credits song?
Yes, i have. In a nasty turn of circumstances, this song is cut off at the end!! Good thing I had an intact version to draw from in the past. (j/k.. I'm sure it wasn't intentional on Robin's part.)
10 b=49152:c=0
20 read a:if a > -1 then poke b+c,a:c=c+1:goto 20
30 sys 49152
40 data 169,147,32,210,255,169,0,141,32,208,141
50 data 33,208,238,32,208,238,33,208,76,13,192,-1
I had a pirated copy of this game on tape turbo loader. I'd forgotten about it for over 30 years until I happened upon this video.
You wouldn't steal a car.
I had a pirate version on disk :D
I had (and have) the original cartridge *and* a pirated copy on disk :) I actually preferred the pirate copy because it had been hacked to use joystick instead of paddles and I found that a bit easier to control, especially when exiting the pits.
@@8_Bit I think my whole collection was about half and half legit/pirate :D The cracked versions always had trainers so that was nice.
@@Okurka.No but I would _copy_ a car! /s
Very interesting, why don’t you program a “fair” version? I’d love to play that
Cool series, thanks! Happy Battle of Puebla Day Cinco de Mayo/Five of May!
If it was in binary, and not BCD, how would you code…
Every 256 points?
Every 512 bytes?
etc
Basically it’s very easy to check for any multiple of a power of 2 simply by checking the n low order bits are zero. Perhaps easiest to comprehend if you remember you can multiply 2^n simply by shifting left n bits. Axiomatically that shift moves zeros into the low order bits.
Very similar for BCD, where you can check for powers of 10 for example 10, 100, 1000 etc. But also you can check for double those values by masking one extra bit in the next nibble in order to check the next digit is even and not odd.
I think the timer speeds up to make the difficulty go up
I've actually done a fair bit of stuff involving using AND to perform a modulo.
Yeah, quite a few times I've covered using AND as a modulo on a binary number, such as AND #%00000111 to do a MOD 8 (remainder 0 to 7). What's new to me here is to use AND as a way of identifying decimal (Binary Coded Decimal) multiples, such as 2000, 4000, 6000 etc.
Wow! A lot of work, but you did it! Thanks for sharing
I have to agree, that AND with 1F is genius. It covers so many cases and is really fast and efficient. 👍
Also, you're a lot of things, "simple minded" isn't one of them!!! 😎
Pal vs ntsc timing was my first thought
the 20,40 compare is sneaky; it's not just you. Subtle mask compares can do some really weird things.
Very well explained!
I KNEW IT! 😂 One of my first two games, with the cartridge of International Soccer. I also had the paddles, mine were darker in colour, similar to the breadbin case iirc, with a red switch
0:03 - The "S" shouldn't be pronounced in "Le Mans". Now I feel better.
I didn't know HAL wrote games for Commodore. A couple of the games remind me of Activision titles, especially Slalom...
Yes, HAL got their start with Commodore. As hobbyists on the Commodore PET, then professionally on the VIC-20, Commodore Max, and Commodore 64. Then they got into MSX and of course Nintendo.
Something something "S in Lemans" / comment for youtube algo :)
Listening to you explain it is a bit like when that guy in The Matrix claims he can see what’s going on by watching the code lol😂 even understanding, it’s still a bit mystifying. I couldn’t recall any of this material as readily.
It isn't really cheating. It is game design 😅
Did the original Sega arcade game have the same "cheating" mechanic, I wonder?
@@ShinSeikiEvan Given the arcade machines of the era, I wouldn't be surprised.
Ooo - I dunno - putting more cars on the track isn't cheating - redefining what a second is - that's a bit iffy 🙂
"Read-only memory memory..." Oops, heh.
oooh, your score is sooo big.
It's pronounced with an "s" in most languages, as in "in most sensible languages", so don't worry at all.
You are definitely not simple minded, at least as it pertains to assembly ;)
In all seriousness, you are clever so if you are amazed that says something.
At least you don’t pronounce it as’lemons’.
:))
It's pronounced like 'lemons' but with an 'a'.
Lamons! :)