Megadrive First Person Effect in Toy Story Video Game

Discussion in 'General Sega Discussion' started by Leonkh, Sep 13, 2017.

  1. Leonkh


    Not sure if this is the right thread. The channel GameHut has brought us some footage of early Sonic R and Sonic 3D Blast footage. Not too long ago, an explanation of how the first person effect in the Megadrive Toy Story game was released after another video where he shows various effects intended for Mickey Mania 2.


    Assembly Code is beyond me at the moment :rolleyes:/> ... but it was still interesting to see.
  2. MarkeyJester


    A D V A N C E Resident Jester
    There's something dodgy about this...

    At 3:06 in the video and beyond, he referenced assembly code of the rendering process, but, has made some obvious optimisation mistakes, some clear ones that a professional game programmer would never have made on the 68k. For example:

    Code (Text):
    1. loop:
    2.     move.b  (a1),(a0)
    3.     add.l   #1,a0
    4.     add.l   #1,a1
    He could've used auto increment on a1 and a0, resulting in:

    Code (Text):
    1. loop:
    2.     move.b  (a1)+,(a0)+
    Additionally, situations where he couldn't have used auto increment (like skipping a pixel or more), e.g:

    Code (Text):
    1. loop:
    2.     move.b  (a1),(a0)
    3.     add.l   #1,a0
    4.     add.l   #2,a1
    Could've been condensed to:

    Code (Text):
    1. loop:
    2.     move.b  (a1),(a0)+
    3.     add.w   #2,a1
    Where a0 has auto increment, and a1 has word added rather than long, since it sign extends...

    At around 5:05 there's a series of indexing, now the source of a1 is understandable, but the destination of a0 is not, it's always in order from 1 to 34. They could've used auto increment for all of those and save don both CPU time and space.

    Don't get me wrong, it was nice to have a good insight into the professional world, I'm not one to argue with an old aged programmer, I wouldn't expect them to remember how to program in the language by any means, but if he's referencing old code written back in the day, then these mistakes were made at the time, thus, something just doesn't add up. I really cannot stress how odd this is, I'm surprised no other programmers working with him at the time even pointed this out.
  3. ICEknight


    Researcher Researcher
    Maybe you could tell him about it in the comments? Perhaps he had a reason for that.
  4. Covarr


    Sentient Cash Register Member
    Trapped in my own thoughts.
    Two stageplays, a screenplay, and an album
    I would assume it was more about explaining the idea in a way that's as clear as possible to people who may not know any ASM, such as myself.
  5. Black Squirrel

    Black Squirrel

    shaving is boring Wiki Sysop
    Northumberland, UK
    hey wiki you're so fine
    The objective is to make it work - to make it work well is secondary, and is dictated by how much time you have left (or knowledge).

    Not everyone can work your magic!
  6. MarkeyJester


    A D V A N C E Resident Jester
    I'm sorry, it appears I've (once again) given the wrong impression. Wrong choice of words, and certainly wrong way of expression, a mistake on my part (and probably one that I'll make again without realising).

    I didn't mean to offend anyone, nor imply that "they're shit, I'm better than them", or anything like that. I was just taken off guard. This type of lack of optimisation, is not one of those "strict" type of optimisations, where you'd have to very heavily think about the way you're going about it. It's one of those "common and obvious" types of optimisation, that's what it's taken me off guard. I would not have expected a professional team of developers to make quite the modest of mistakes. Being honest, I thought the effect was brilliant, and quite well laid out for what it was.

    If I've offended any of you, my apologies entirely. I am just speculating what they could've added to it, if they had free'd up CPU time from optimising.

    Natsumi suggested the same, and encouraged me to disassembly Toy Story. Unfortunately, this is not the case, at least, not in aspect that I was addressing with the auto-increment, but it doesn't matter. I realise I could've worded it better.
  7. Xiao Hayes

    Xiao Hayes

    Classic Eggman art Member
    I think you worded it perfectly, there's no arrogance in what you said, and you shouldn't be ashamed on being good at what you do, even if you haven't worked professionally in mega Drive games, becuase, let's face it, you'd need a time machine to try get that job. I'll probably would have said the same if I knew as much as you do, and probably with a worse choice of words. It's true, however, people used to do things in the best way possible hardly find that kind of mistakes as something possible coming from a professional, but I can assure you there's a lot of "professional" people doing sub-optimal work and getting away with it, so, as long as the only fault in their code is forgetting the auto-increment, everything's quite fine. :v:

    Byt the way, I've always seen you as the most polite person in this forum; I guess thinking you're easily offending someone has something to do with that, but you're truly a respectful and respectable man, don't forget it. :thumbsup:
  8. Flygon


    Not having seen the video, it's worth remembering that Travelers Tales often suffered severely short development times for the projects they outputted. I understand that Toy Story only got around 6-7 months from very beginning to release.

    Even with a game that pulls the crazy stuff it does, and with the incredibly skilled work crew behind it, all sorts of small stuff will fall through the cracks.

    Just look at any movie that's been given carte blanche with the budget in particular areas, but also been given a very strict timeline. Throwing enough money at a problem won't fix it if nobody notices the mistakes made into the final cut on time.
  9. flamewing


    Emerald Hunter Tech Member
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    I was thinking about that when I watched the video to. But then, when he described what the engine was supposed to be doing, I realized that these initial parts had to be code he reconstructed from memory.

    Think about it: tiles are rows of pixels. He needs to skip 3 bytes every write in order to write columns as he describes.

    But what is perhaps most relevant is that he does not talk about VDP bandwidth. Drawing half of H32 mode and reflecting vertically means 32x14 tiles, or 32x32x14 = 14,336 bytes.

    This takes almost all bandwidth of the VDP for 1 full frame and for almost all of vblank on the next frame, meaning you have very little left for everything else. This on top of the time needed to tender the frame.

    So there is something else missing in the description, some other trick they used. Probably line doubling (using hblank to change scroll every other line to double each line), which would halve the number of tiles needed.
  10. Clownacy


    Tech Member
    I can't help but find this idea that you have to be on par with Markey to know about something as simple as post-increment a little... insulting, I guess is the word? It's basic 68k. Sure, not everyone knows about the 'self-additions are faster than small left-shifts' trick, but increments? They're not just some obscure optimisation. The only explanation I can think of is the dev getting too used to Z80 ASM, where you actually do need to write code like that, unless you want to resort to gimmicky instructions like 'ldi'. And even then, I don't expect that from a company that's been working with the Mega Drive for four years. It's simply not a mistake you should even make in the first place, let alone miss during cleanup and revision.

    I would have assumed the above quote meant that the code isn't reconstructed, and is actually in the game.
  11. FireRat


    Nah. Fuck it. Misfit
    Mobius Evolution 2
    Professionally, not only the workflow, but also the environment itself are also very different than a lone and calm room at home. There's a short timeline, have to constantly deal with good and bad people, write a bunch of different and complicated code, yet everything needs to work and play, at least in a good enough way depending the capability and how overall presentation should be. If there's not much time left, and the code got to work, it's time to move on; can't stay stuck around checking and perfecting everything over and over again. Not to mention any extra charges within the team. Distractions and pression, especially if programmer is nervous or anxious person, ARE a true handicap (to what I learnt from college in the most part).

    Overall Sonic 3 & Knuckles has useful engine reorganization, but certain objects are still written like shit, some say. For instance, the way Perform_DPLC is called could have been grouped into a macro and avoid reading an array from ROM, so addresses get set using inmediate values instead
    To date, most if not every game's source code I have seen has weird oddities at times as well, including but not limited to hacks and commercial games. It seems rare to see cycle-perfect code.

    I'm talking for myself here, but who knows if other "programmers" have a similar mindset to this?:
    comparatively I'm not really much of a logician, but if the goal is to write something fast, I begin with 3/4 of an idea and expand upon it while working; avoiding the temptation to priorize on how should it "look" than how should it "work". It begins with quick prototyping, more like a rough, perhaps "lazy" version of what things are aiming to be, and then either optimize/cleanup the current or (less likely but possibly) continue expanding through the branches inmediately, following a similar process. Messy, perhaps?. And in the end sometimes I'd miss a couple of tiny things, even after a final proof-read, which I'd only end up fixing months later, in a random ocassional read.
  12. Black Squirrel

    Black Squirrel

    shaving is boring Wiki Sysop
    Northumberland, UK
    hey wiki you're so fine
    I'm a C++ programmer by trade. We're not allowed to use the standard library for reasons unknown to man - that standard feature of the language that's quite literally part the second line of code most people write when starting C++ from scratch. My boss probably doesn't know how most of the library works.

    Our products keep planes in the sky and let Lewis Hamilton win trophies. This codebase has been actively worked on since 2002 (and parts of it since the 90s) and is used to produce world-class software.

    But internally, it sucks.
  13. MarkeyJester


    A D V A N C E Resident Jester
    Auto-increment for the 68k is nothing to do with a standard library of code/routines. Your argument would work if the discussion was about accusing them of not using routines supplied by SEGA, but that is not the issue.

    The issue is about a benefitially supplied functionality of the CPU. If you were to say, that your company forbid you to use auto-increment with your C/C++ pointers, as in:

    Code (Text):
    1.     *ptr1++ = *ptr2++;
    And you HAD to do it this way:

    Code (Text):
    1.     *ptr1 = *ptr2;
    2.     ptr1 += 1;
    3.     ptr2 += 2;
    Then fair enough, you'd have a point. But I highly doubt that, and I highly (, I 100% extremely) doubt their company forbid them to use auto-increment in their code, no matter how pathetically stupid their management may or may not have been. I think you might be misunderstanding the situation.

    Edit: Furthermore, auto-increment was used in various other placed in the game's code, just not in this place where it equally really matters.

    --- --- --- --- --- --- --- ---

    Anyways, GameHut responded to my comment, claims the code was generated by a macro, hence the lack of increment. While it's not impossible for a macro to use auto-increment, at least this makes sense, and I see it as a fair enough point. When you make a macro, you often don't see the code result, thus, you generally don't think about optimising it, even a standard auto-increment. So, mystery solved?
  14. Black Squirrel

    Black Squirrel

    shaving is boring Wiki Sysop
    Northumberland, UK
    hey wiki you're so fine
    The point was what you consider to be common knowledge is not always the case for others. I didn't know you could get so far in life without knowing how an std::vector works but here we are.

    Okay simpler example:


    maybe you came from a Python background and didn't know "++" was a thing, so you wrote all your code like

    *ptr1 += 1;

    it's not outside the realms of possibility.
  15. Clownacy


    Tech Member
    That's where I was going with my 'they might have been thinking of Z80' point. But like I said back then, four years is a long time to figure that out (assuming they've always had the same programmer, anyway). The macro thing definitely clears it up for me, though. It wasn't until after I'd already made my post that I thought about the code possibly being copy-pasted from elsewhere and then edited by hand, explaining how it came to be in the first place, but a macro makes even more sense.
  16. Fred


    Taking a break Oldbie
    Sonic 3 Unlocked
    Of course it helps to look at the actual code before passing judgment.

    The function responsible for drawing the 3D stage appears to be at $77C74. The expanded macro code is at the very end of the ROM.

    [68k]ROM:003E0796 move.b 0(a0),0(a1)
    ROM:003E079C move.b 3(a0),4(a1)
    ROM:003E07A2 move.b 6(a0),8(a1)
    ROM:003E07A8 move.b 9(a0),$C(a1)
    ROM:003E07AE move.b $C(a0),$10(a1)
    ROM:003E07B4 move.b $F(a0),$14(a1)
    ROM:003E07BA move.b $13(a0),$18(a1)
    ROM:003E07C0 move.b $16(a0),$1C(a1)
    ROM:003E07C6 move.b $19(a0),$20(a1)
    ROM:003E07CC move.b $1C(a0),$24(a1)
    ROM:003E07D2 rts[/68k]

    For the first few passes which skip pixels from the source texture, direct offsets are applied to a0.

    [68k]ROM:003E3634 move.b (a0)+,0(a1)
    ROM:003E3638 move.b (a0)+,4(a1)
    ROM:003E363C move.b (a0)+,8(a1)
    ROM:003E3640 move.b (a0)+,$C(a1)
    ROM:003E3644 move.b (a0)+,$10(a1)
    ROM:003E3648 move.b (a0)+,$14(a1)
    ROM:003E364C move.b (a0)+,$18(a1)
    ROM:003E3650 move.b (a0)+,$1C(a1)
    ROM:003E3654 move.b (a0)+,$20(a1)
    ROM:003E3658 move.b (a0)+,$24(a1)
    ROM:003E365C move.b (a0)+,$28(a1)
    ROM:003E3660 move.b (a0)+,$2C(a1)
    ROM:003E3664 move.b (a0)+,$30(a1)
    ROM:003E3668 move.b (a0)+,$34(a1)
    ROM:003E366C move.b (a0)+,$38(a1)
    ROM:003E3670 move.b (a0)+,$3C(a1)
    ROM:003E3674 move.b (a0)+,$40(a1)
    ROM:003E3678 move.b (a0)+,$44(a1)
    ROM:003E367C move.b (a0)+,$48(a1)
    ROM:003E3680 move.b (a0)+,$4C(a1)
    ROM:003E3684 move.b (a0)+,$50(a1)
    ROM:003E3688 move.b (a0)+,$54(a1)
    ROM:003E368C move.b (a0)+,$58(a1)
    ROM:003E3690 move.b (a0)+,$5C(a1)
    ROM:003E3694 move.b (a0)+,$60(a1)
    ROM:003E3698 move.b (a0)+,$64(a1)
    ROM:003E369C move.b (a0)+,$68(a1)
    ROM:003E36A0 move.b (a0)+,$6C(a1)
    ROM:003E36A4 move.b (a0)+,$70(a1)
    ROM:003E36A8 move.b (a0)+,$74(a1)
    ROM:003E36AC move.b (a0)+,$78(a1)
    ROM:003E36B0 move.b (a0)+,$7C(a1)
    ROM:003E36B4 move.b (a0)+,$80(a1)
    ROM:003E36B8 move.b (a0)+,$84(a1)
    ROM:003E36BC move.b (a0)+,$88(a1)
    ROM:003E36C0 move.b (a0)+,$8C(a1)
    ROM:003E36C4 move.b (a0)+,$90(a1)
    ROM:003E36C8 move.b (a0)+,$94(a1)
    ROM:003E36CC move.b (a0)+,$98(a1)
    ROM:003E36D0 move.b (a0)+,$9C(a1)
    ROM:003E36D4 move.b (a0)+,$A0(a1)
    ROM:003E36D8 move.b (a0)+,$A4(a1)
    ROM:003E36DC move.b (a0)+,$A8(a1)
    ROM:003E36E0 move.b (a0)+,$AC(a1)
    ROM:003E36E4 move.b (a0)+,$B0(a1)
    ROM:003E36E8 move.b (a0)+,$B4(a1)
    ROM:003E36EC move.b (a0)+,$B8(a1)
    ROM:003E36F0 move.b (a0)+,$BC(a1)
    ROM:003E36F4 move.b (a0)+,$C0(a1)
    ROM:003E36F8 move.b (a0)+,$C4(a1)
    ROM:003E36FC move.b (a0)+,$C8(a1)
    ROM:003E3700 move.b (a0)+,$CC(a1)
    ROM:003E3704 move.b (a0)+,$D0(a1)
    ROM:003E3708 move.b (a0)+,$D4(a1)
    ROM:003E370C move.b (a0)+,$D8(a1)
    ROM:003E3710 move.b (a0)+,$DC(a1)
    ROM:003E3714 move.b (a0)+,$E0(a1)
    ROM:003E3718 move.b (a0)+,$E4(a1)
    ROM:003E371C move.b (a0)+,$E8(a1)
    ROM:003E3720 move.b (a0)+,$EC(a1)
    ROM:003E3724 move.b (a0)+,$F0(a1)
    ROM:003E3728 move.b (a0)+,$F4(a1)
    ROM:003E372C move.b (a0)+,$F8(a1)
    ROM:003E3730 move.b (a0)+,$FC(a1)
    ROM:003E3734 rts[/68k]

    In the first pass where all the pixels from the source texture are read, autoincrement is used.

    [68k]ROM:003E677C move.b (a0),0(a1)
    ROM:003E6780 move.b (a0)+,4(a1)
    ROM:003E6784 move.b (a0),8(a1)
    ROM:003E6788 move.b (a0)+,$C(a1)
    ROM:003E678C move.b (a0),$10(a1)
    ROM:003E6790 move.b (a0)+,$14(a1)
    ROM:003E6794 move.b (a0)+,$18(a1)
    ROM:003E6798 move.b (a0),$1C(a1)
    ROM:003E679C move.b (a0)+,$20(a1)
    ROM:003E67A0 move.b (a0),$24(a1)
    ROM:003E67A4 move.b (a0)+,$28(a1)
    ROM:003E67A8 move.b (a0)+,$2C(a1)
    ROM:003E67AC move.b (a0),$30(a1)
    ROM:003E67B0 move.b (a0)+,$34(a1)
    ROM:003E67B4 move.b (a0),$38(a1)
    ROM:003E67B8 move.b (a0)+,$3C(a1)
    ROM:003E67BC move.b (a0)+,$40(a1)
    ROM:003E67C0 move.b (a0),$44(a1)
    ROM:003E67C4 move.b (a0)+,$48(a1)
    ROM:003E67C8 move.b (a0),$4C(a1)
    ROM:003E67CC move.b (a0)+,$50(a1)
    ROM:003E67D0 move.b (a0),$54(a1)
    ROM:003E67D4 move.b (a0)+,$58(a1)
    ROM:003E67D8 move.b (a0)+,$5C(a1)
    ROM:003E67DC move.b (a0),$60(a1)
    ROM:003E67E0 move.b (a0)+,$64(a1)
    ROM:003E67E4 move.b (a0),$68(a1)
    ROM:003E67E8 move.b (a0)+,$6C(a1)
    ROM:003E67EC move.b (a0)+,$70(a1)
    ROM:003E67F0 move.b (a0),$74(a1)
    ROM:003E67F4 move.b (a0)+,$78(a1)
    ROM:003E67F8 move.b (a0),$7C(a1)
    ROM:003E67FC move.b (a0)+,$80(a1)
    ROM:003E6800 move.b (a0)+,$84(a1)
    ROM:003E6804 move.b (a0),$88(a1)
    ROM:003E6808 move.b (a0)+,$8C(a1)
    ROM:003E680C move.b (a0),$90(a1)
    ROM:003E6810 move.b (a0)+,$94(a1)
    ROM:003E6814 move.b (a0)+,$98(a1)
    ROM:003E6818 move.b (a0),$9C(a1)
    ROM:003E681C move.b (a0)+,$A0(a1)
    ROM:003E6820 move.b (a0),$A4(a1)
    ROM:003E6824 move.b (a0)+,$A8(a1)
    ROM:003E6828 move.b (a0),$AC(a1)
    ROM:003E682C move.b (a0)+,$B0(a1)
    ROM:003E6830 move.b (a0)+,$B4(a1)
    ROM:003E6834 move.b (a0),$B8(a1)
    ROM:003E6838 move.b (a0)+,$BC(a1)
    ROM:003E683C move.b (a0),$C0(a1)
    ROM:003E6840 move.b (a0)+,$C4(a1)
    ROM:003E6844 move.b (a0)+,$C8(a1)
    ROM:003E6848 move.b (a0),$CC(a1)
    ROM:003E684C move.b (a0)+,$D0(a1)
    ROM:003E6850 move.b (a0),$D4(a1)
    ROM:003E6854 move.b (a0)+,$D8(a1)
    ROM:003E6858 move.b (a0)+,$DC(a1)
    ROM:003E685C move.b (a0),$E0(a1)
    ROM:003E6860 move.b (a0)+,$E4(a1)
    ROM:003E6864 move.b (a0),$E8(a1)
    ROM:003E6868 move.b (a0)+,$EC(a1)
    ROM:003E686C move.b (a0),$F0(a1)
    ROM:003E6870 move.b (a0)+,$F4(a1)
    ROM:003E6874 move.b (a0)+,$F8(a1)
    ROM:003E6878 move.b (a0),$FC(a1)
    ROM:003E687C move.b (a0)+,$100(a1)
    ROM:003E6880 move.b (a0),$104(a1)
    ROM:003E6884 move.b (a0)+,$108(a1)
    ROM:003E6888 move.b (a0)+,$10C(a1)
    ROM:003E688C move.b (a0),$110(a1)
    ROM:003E6890 move.b (a0)+,$114(a1)
    ROM:003E6894 move.b (a0),$118(a1)
    ROM:003E6898 move.b (a0)+,$11C(a1)
    ROM:003E689C rts[/68k]

    For the passes where source pixels are duplicated, the macro generated a pattern of increment and non-increment operations.

    Also, the screen is mirrored by writing the same nine rows twice to the Plane A pattern table, except the first nine are in reverse order and with the V flip bit set. That's not really what I was expecting but then again, I dunno what I was expecting.
  17. flamewing


    Emerald Hunter Tech Member
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    That is pretty much what I expected to see, to be honest; I have also written macros to generate exactly this kind of code for Natsumi (before dumping to macros and writing a program that does the same thing, because the macros took minutes to assemble in modern hardware).

    I think I will disassemble those portions of the game because I am interested in how they dealt with the bandwidth issue I mentioned.
  18. MarkeyJester


    A D V A N C E Resident Jester
    I did look at the code, in fact, if you read my comment on that very video that was linked, you would've seen:

    I did look at the code, and I did also mention that I did notice increment versions below. I did do my research, thank you very much.

    I did also note that I made a comment on the video in this thread, and that he responded to it:

    It often helps to read the thread before passing judgement.

    ...being serious though, everything to aside, you didn't need to be rude about it =(
  19. nineko


    I am the Holy Cat Tech Member
    Since you guys are disassembly Toy Story, make sure to extract the MOD player while that you're at it, it would make for awesome title screens in hacks :U
  20. Fred


    Taking a break Oldbie
    Sonic 3 Unlocked
    I didn't see your full reply, thanks to YouTube's shitty comment system showing me a "View all replies" link, which doesn't look like a link, but at least has a chevron next to it, except then each individual reply has a "Read more" link, which also doesn't look like a link, this time without the chevron. Stellar web design.

    If I knew you had already looked at code, I wouldn't have stayed up until 3 AM disassembling it myself. Why didn't you post the code here?

    Markey, be reasonable. You were the one to raise an eyebrow, and to transcribe the obviously inaccurate code from the YouTube video. From there, what I saw was a full page of baseless speculation without one single person checking if the code in the video was accurate to the one in the ROM. If you thought there was something fishy about it, and had the motivation to disassemble the ROM to figure out what was going on, then how come didn't you do that vital research before you brought up the subject?

    I don't think I was rude, nor did I single you out. I was just commenting on the fact that somehow we ended up with a full page discussing the post-increment operator in C/C++ without anyone touching the actual 68k code. I now realize that was an incorrect observation, but I didn't know it at the time of my post.