This Game Cheats? Hal Labs' LeMans for Commodore 64 (Part 3)

Поділитися
Вставка
  • Опубліковано 22 лис 2024

КОМЕНТАРІ • 94

  • @frestkd
    @frestkd 6 місяців тому +6

    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.

    • @8_Bit
      @8_Bit  6 місяців тому +2

      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.

  • @Jemacaza
    @Jemacaza 6 місяців тому +19

    Always nice to dive into MOS 6510 assembly. Love it. Very nice video, thank you!

  • @whitslack
    @whitslack 6 місяців тому +18

    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.

    • @8_Bit
      @8_Bit  6 місяців тому +7

      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.

  • @VulpisFoxfire
    @VulpisFoxfire 6 місяців тому +23

    When life gives you Lemans...

    • @dominikschutz6300
      @dominikschutz6300 6 місяців тому +3

      ... you port it to other platforms. :)

    • @BillAnt
      @BillAnt 6 місяців тому +4

      You make a Super-Snapshot dump lol

  • @JustWasted3HoursHere
    @JustWasted3HoursHere 6 місяців тому +11

    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).

    • @8_Bit
      @8_Bit  6 місяців тому +9

      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 :)

    • @ZipplyZane
      @ZipplyZane 6 місяців тому +1

      @@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.

  • @merman1974
    @merman1974 6 місяців тому +2

    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.

  • @NotIT
    @NotIT 6 місяців тому +7

    Dummy 0's (zeros) was a very common practice with EM pinball machines as well.

  • @pb7379-j2k
    @pb7379-j2k 6 місяців тому +8

    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!

  • @Commodore128Mode3
    @Commodore128Mode3 6 місяців тому +4

    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!

  • @sark3153
    @sark3153 6 місяців тому +5

    This game reminds me of those electro mechanical 'digital derby' toys from the 80s

    • @csbruce
      @csbruce 6 місяців тому +2

      Robin made a video about these "zero-bit games": ua-cam.com/video/NwQMBV7Td1s/v-deo.html

  • @TheHighlander71
    @TheHighlander71 6 місяців тому +4

    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.

    • @BillAnt
      @BillAnt 6 місяців тому +1

      That was a nice logical explanation.

    • @bluerizlagirl
      @bluerizlagirl 6 місяців тому +1

      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.

  • @Azuris190
    @Azuris190 6 місяців тому +2

    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

  • @steven-vn9ui
    @steven-vn9ui 6 місяців тому +2

    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

  • @kumrogames
    @kumrogames 6 місяців тому +1

    Great video as always! Its neat to see how the inner workings of some of these games were coded.

  • @DavidYoud
    @DavidYoud 6 місяців тому +11

    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. :)

    • @BillAnt
      @BillAnt 6 місяців тому

      There were clever programmers even some 40 years ago. ;)

  • @ScottHiland
    @ScottHiland 6 місяців тому +4

    Thanks, Robin! Good stuff. Bitwise ops still mystify me on first glance, I have to put my brain into a lower gear every time.

  • @mikegarland4500
    @mikegarland4500 6 місяців тому

    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.

  • @PaoloBergamo
    @PaoloBergamo 6 місяців тому

    Awesome video! Got nothing to add but a “fair” progression would be -6 -6 -5 then either -5 or -4

  • @ralphm786
    @ralphm786 6 місяців тому

    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

  • @dominikschutz6300
    @dominikschutz6300 6 місяців тому +1

    The song at the end really rocks :)

  • @lindoran
    @lindoran 6 місяців тому +2

    Love this kind of deep dive

  • @mondrus72
    @mondrus72 6 місяців тому

    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.

  • @chrisdixon5241
    @chrisdixon5241 6 місяців тому +2

    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?

  • @yadabub
    @yadabub 6 місяців тому +2

    Arcade games did need to "kill you off," because of coin detected in pocket.

    • @8_Bit
      @8_Bit  6 місяців тому +2

      Yes, and that didn't need to carry over to home video game consoles and computer games, but it did. That was my point.

  • @Phreno_Xeno
    @Phreno_Xeno 6 місяців тому +2

    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.

    • @Okurka.
      @Okurka. 6 місяців тому +6

      You wouldn't steal a car.

    • @PlasticCogLiquid
      @PlasticCogLiquid 6 місяців тому +2

      I had a pirate version on disk :D

    • @8_Bit
      @8_Bit  6 місяців тому +7

      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.

    • @PlasticCogLiquid
      @PlasticCogLiquid 6 місяців тому +3

      @@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.

    • @MichaelPohoreski
      @MichaelPohoreski 6 місяців тому +2

      @@Okurka.No but I would _copy_ a car! /s

  • @kestergreen3844
    @kestergreen3844 6 місяців тому

    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?

  • @c128stuff
    @c128stuff 6 місяців тому +2

    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).

  • @HelloKittyFanMan
    @HelloKittyFanMan 6 місяців тому

    Cool series, thanks! Happy Battle of Puebla Day Cinco de Mayo/Five of May!

  • @8-bitwallofdoom
    @8-bitwallofdoom 6 місяців тому

    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!

    • @8_Bit
      @8_Bit  6 місяців тому

      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-bitwallofdoom
      @8-bitwallofdoom 6 місяців тому

      @@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.

  • @Jacob-dn6hg
    @Jacob-dn6hg 6 місяців тому +3

    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.

    • @8_Bit
      @8_Bit  6 місяців тому +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.

    • @Jacob-dn6hg
      @Jacob-dn6hg 6 місяців тому

      @@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.

  • @csbruce
    @csbruce 6 місяців тому +10

    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.

  • @RudysRetroIntel
    @RudysRetroIntel 6 місяців тому

    Wow! A lot of work, but you did it! Thanks for sharing

  • @NeilRoy
    @NeilRoy 6 місяців тому +1

    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!!! 😎

  • @luca6819
    @luca6819 6 місяців тому

    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

  • @einblock1840
    @einblock1840 6 місяців тому

    Very well explained!

  • @gkwgeek4509
    @gkwgeek4509 6 місяців тому

    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?

  • @stuartmcconnachie
    @stuartmcconnachie 6 місяців тому +1

    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.

  • @bluerizlagirl
    @bluerizlagirl 6 місяців тому

    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.

  • @anthonybarker3483
    @anthonybarker3483 6 місяців тому

    Very interesting, why don’t you program a “fair” version? I’d love to play that

  • @murderdoggg
    @murderdoggg 6 місяців тому

    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?

    • @8_Bit
      @8_Bit  6 місяців тому

      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.

  • @sulrich70
    @sulrich70 6 місяців тому

    Pal vs ntsc timing was my first thought

  • @Clancydaenlightened
    @Clancydaenlightened 6 місяців тому

    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

  • @weedmanwestvancouverbc9266
    @weedmanwestvancouverbc9266 6 місяців тому

    I think the timer speeds up to make the difficulty go up

  • @firstsurname9893
    @firstsurname9893 6 місяців тому +1

    Has anybody typed in the program from the end credits song?

    • @mikegarland4500
      @mikegarland4500 6 місяців тому +1

      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.)

    • @mikegarland4500
      @mikegarland4500 6 місяців тому

      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

  • @granitepenguin
    @granitepenguin 6 місяців тому +4

    the 20,40 compare is sneaky; it's not just you. Subtle mask compares can do some really weird things.

  • @fnjesusfreak
    @fnjesusfreak 6 місяців тому

    I've actually done a fair bit of stuff involving using AND to perform a modulo.

    • @8_Bit
      @8_Bit  6 місяців тому +3

      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.

  • @DerekLippold
    @DerekLippold 6 місяців тому

    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.

  • @douro20
    @douro20 4 місяці тому

    I didn't know HAL wrote games for Commodore. A couple of the games remind me of Activision titles, especially Slalom...

    • @8_Bit
      @8_Bit  4 місяці тому

      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.

  • @tubeincompetence
    @tubeincompetence 6 місяців тому +1

    Something something "S in Lemans" / comment for youtube algo :)

  • @HelloKittyFanMan
    @HelloKittyFanMan 6 місяців тому

    "Read-only memory memory..." Oops, heh.

  • @OscarSommerbo
    @OscarSommerbo 6 місяців тому +2

    It isn't really cheating. It is game design 😅

    • @ShinSeikiEvan
      @ShinSeikiEvan 6 місяців тому +1

      Did the original Sega arcade game have the same "cheating" mechanic, I wonder?

    • @OscarSommerbo
      @OscarSommerbo 6 місяців тому +2

      @@ShinSeikiEvan Given the arcade machines of the era, I wouldn't be surprised.

    • @DaveF.
      @DaveF. 6 місяців тому

      Ooo - I dunno - putting more cars on the track isn't cheating - redefining what a second is - that's a bit iffy 🙂

  • @klocugh12
    @klocugh12 6 місяців тому

    It doesn't cheat. It just accelerates itself to relativistic speeds so it experiences time dilation. From its perspective time passes perfectly on point.
    /s

  • @Thiesi
    @Thiesi 6 місяців тому +2

    0:03 - The "S" shouldn't be pronounced in "Le Mans". Now I feel better.

  • @ml.2770
    @ml.2770 6 місяців тому

    oooh, your score is sooo big.

  • @archivis
    @archivis 6 місяців тому

    :))

  • @rager1969
    @rager1969 6 місяців тому

    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.

  • @rossdtool
    @rossdtool 5 місяців тому

    At least you don’t pronounce it as’lemons’.

  • @faenethlorhalien
    @faenethlorhalien 6 місяців тому +1

    It's pronounced with an "s" in most languages, as in "in most sensible languages", so don't worry at all.

  • @Okurka.
    @Okurka. 6 місяців тому +1

    It's pronounced like 'lemons' but with an 'a'.

    • @8_Bit
      @8_Bit  6 місяців тому +4

      Lamons! :)