An attempt was made to implement the gameplay of the original SUPER MARIO. For this in-browser demo, use the arrow keys and hold A to run, S to jump.
When you are done playing with it, if you want to have the arrow keys back for horizontal scrolling, click this button:
This obviously isn't a full mario game but I wanted to at least get the running and jumping to feel right. Most of the rest of the stuff – bad guys, power-ups, moving platforms, the rest of the levels and so on – all feels individually achievable, with enough time. But the jumping behaviour is so easy to get wrong, it takes more than just grinding to implement it.
There are disassemblies of the game we're trying to copy. It was written in plain 6502 assembly, of course, and the community have studied it and come up with sensible labels for most of it. Although I looked through this a bit, it wasn't particularly useful. I tried a few other community-made references, I don't remember them all, but my main reference for the implementation was actually the following image, which despite the disclaimer at the start is far more useful for getting an overview of how things work than trying to study disassemblies.
This is a very large image, brace yourself for some scrolling.
If you read through most of that, you should hopefully have a pretty good idea of what we're going for.
The author of the above image is Jdaster64, who now has a website called The Super Mario Files. Interesting stuff.
The vast number of comparisons suggests we need a really solid understanding of the AVR status codes and its conditional branch logic. Our main instruction is cpi, compare immediate, and its siblings cp (compare) and cpc (compare with carry), although these are certainly not the only instructions to update the status register. To "compare" means to perform a hypothetical subtraction. We're asking, what would the status register be, if I subtracted this number from that one?
Once we've done that, we then use a conditional branch instruction. Each of these mnemonics is a permutation of "branch if the status flags are in this particular combination."
The status register on an AVR looks like this.
I is the interrupt enable flag, unrelated to conditions. T is the "bit copy storage" which I think they added because there was a spare bit available. Again, it's unrelated to conditions.
H is the half-carry flag, which is only used for BCD arithmetic, we'll ignore it here.
S, V and N are called Signed, oVerflow, and Negative, and they tell us about the results of a two's complement addition or subtraction. That is, assuming that the arguments to the compare were signed. If they weren't, the S and V flags will contain junk. N will always be equal to the highest bit of the result. Remember, these status flags are updated by combinational logic, they have no idea what type of data is flowing through the ALU.
Z and C are Zero and Carry, and these are, quite frankly, the most important flags. Zero is set if the result is zero. In other words, if you want to know if two numbers are equal, you compare them (hypothetical subtraction) and the Z flag will be set if they are the same value. Carry is the 9th bit of the 8-bit operation, so if adding things causes an overflow, the carry bit is set. Or, in the case of a subtraction, even a hypothetical one, the carry bit is set if the result underflows.
That's a summary. These aren't the only things that update the flags, but the important thing is once we've got those status flags, we need to worry about which branch to use. The following table explains it.
What a nightmare! This is taken from the AVR Instruction Set PDF, and one of the things I struggled with while I was trying to teach myself this is that they don't define the mathematical symbols they're using, you're just expected to know them.
⊕ (the plus within a circle, in case your font doesn't support it) represents exclusive or. This is a bit confusing because there are also + symbols in the table, which you'd think would represent addition. And when we talk about bitwise addition, that's the same as an exclusive or. In binary, 1 + 1 is 10, which if we only care about the lowest bit, is the same as doing XOR. I am pretty sure that + represents bitwise OR. By the process of elimination, the · (dot) must represent AND.
The mnemonics are bit ridiculous, there's GE (greater or equal), SH (same or higher), LT (less than), LO (lower), MI (minus (not to be confused with NE, which is Not Equal, not "Negative")), PL (plus)... it's like they were trying to make it extra difficult. The difference between GE and SH is that GE operates on signed data, SH operates on unsigned. Why they couldn't have chosen more helpful mnemonics is beyond me.
The only conditional branches that are easy to understand are the last four in the above table. BRCS is branch-if-carry-set, BRCC is branch-if-carry-clear, and so on. The only way to stay sane while implementing the fractal-like condition tree of mario's jumping behaviour is pick a method and stick to it. For the conditions I just consider all the numbers to be unsigned, and only worry about whether it's zero or if there was a carry. Pretty much all of the logic is done with BRNE, BREQ, BRCC, and BRCS.
After performing the head-bang, we send that object through an animation of Y positions, and if we'd gotten further, this would be where the block smashes or releases a mushroom or whatever.
Left and right wall collisions are not as easy. Like with Retro Racer, we want to give an impulse outwards from the block, to move the character away from it but stop when they no longer overlap, which is complex if speeds are at more than one pixel per frame. Mario physics extends this weirdly, by only impulsing up to once per frame. This is important, because if we jump into the corner of a block, we want to be smoothly pushed outwards, not suddenly kicked.
The last bit of logic here is to push upwards if we're standing on a block. Like with head-banging, we want to round up our position to the tile boundary. Not only does this correctly catch us when we're falling, but it's crucial for landing horizontal jumps. As you probably know if you're a player of mario, when there are a series of closely spaced blocks, you can run along the top without needing to jump, and Mario doesn't fall because each block corner catches him and gives an upwards impulse.
Each row of the level data corresponds to one column of tile data. The part of the level shown on screen is held in SRAM, and when our X position crosses a threshold, an offset is increased, which visually shifts the screen along. When that offset reaches 16 pixels, the leftmost column of level data is deleted, every column is shifted along by one, and a new column of level data is loaded onto the right.
The tiles themselves are drawn the same way as the sprites in other games. The delay routines clearly aren't optimal, since when the screen is full the framerate visably drops, and the music slowing down makes it even more noticeable. Part of the reason I didn't persue mario much further is that it became obvious the hardware was at its limit. With faster op-amps, which aren't even that expensive, we could have sped up the drawing routines considerably, and recovered plenty of time for the game logic. Remember that the NES processor only ran at about 1.8MHz, compared to our cushy 8MHz.
OK – that's about as much as I'll explain here. What follows is the highlighted source code to the javascript prototype above, and then the assembly source of the end result. If you've read through everything so far (congrats) then I'm sure you won't want to miss the conclusion at the bottom of this page.
<canvas width=256 height=224 id=cnv ></canvas> <script> c=document.getElementById('cnv') ctx=c.getContext('2d'); c.width*=2;c.height*=2;ctx.scale(2,2) ctx.mozImageSmoothingEnabled=false; ctx.webkitImageSmoothingEnabled=false; ctx.imageSmoothingEnabled=false; levelData=[0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,26,27,1,0,0,0,0,0,0,0,0,0,0,24,27,32,1,0,0,0,0,0,0,0,0,0,0,0,28,33,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,2,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,24,27,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1,0,0,9,13,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,3,0,0,0,1,0,0,11,15,0,0,0,0,0,2,0,0,0,1,0,0,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,34,1,0,0,0,0,0,0,0,0,0,3,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,9,13,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,5,7,1,0,0,0,10,14,0,0,0,0,0,0,6,8,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,11,15,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,9,13,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,0,5,7,7,1,0,0,11,15,0,0,0,0,0,0,6,8,8,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,5,7,7,7,1,0,0,0,0,0,0,0,0,0,6,8,8,8,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,26,27,1,0,0,0,0,0,0,0,0,0,0,24,27,32,1,0,0,0,0,0,0,0,0,0,0,0,28,33,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,9,13,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,5,7,7,7,1,0,0,0,11,15,0,0,0,0,6,8,8,8,1,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,24,27,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1,0,0,9,13,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,0,0,0,0,1,0,0,11,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,9,13,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,3,0,0,0,1,0,0,0,10,14,0,0,0,0,2,0,0,0,1,0,0,0,11,15,0,0,0,0,3,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,9,13,0,3,0,0,0,0,0,0,0,1,0,0,10,14,0,3,0,0,0,0,0,0,0,1,0,0,10,14,0,3,0,0,0,0,0,0,0,0,0,0,11,15,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,3,0,0,0,0,0,0,35,1,0,0,0,0,0,3,0,0,0,0,0,0,36,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,26,27,1,0,0,0,0,0,0,0,0,0,0,24,27,32,1,0,0,0,0,0,0,0,0,0,0,0,28,33,1,0,0,0,0,0,0,0,0,0,3,0,0,28,1,0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,9,13,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,11,15,0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,2,0,0,0,2,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,37,1,0,0,0,0,0,0,0,0,0,2,0,29,38,1,0,0,0,0,0,0,0,0,0,0,0,30,39,1,0,0,0,0,0,0,0,0,0,0,0,0,40,1,0,0,9,13,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,0,0,0,0,1,0,0,11,15,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,3,0,0,0,0,0,0,36,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,9,13,3,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,11,15,0,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,1,0,0,9,13,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,0,0,0,0,1,0,0,10,14,0,0,0,0,0,0,0,0,4,1,0,0,11,15,0,0,0,0,0,0,0,4,4,1,0,0,0,0,0,0,0,0,0,0,4,4,4,1,0,0,0,0,0,0,0,0,0,4,4,4,4,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,0,0,0,35,1,0,0,0,0,0,0,0,0,0,4,4,4,4,1,0,0,0,0,0,0,0,0,0,0,4,4,4,1,0,0,0,0,0,0,0,0,0,0,0,4,4,1,0,0,0,0,0,0,0,0,0,0,0,0,4,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,26,27,1,0,0,0,0,0,0,0,0,0,0,24,27,32,1,0,0,0,0,0,0,0,0,0,0,0,28,33,1,0,0,0,0,0,0,0,0,0,0,0,0,4,1,0,0,0,0,0,0,0,0,0,0,0,4,4,1,0,0,0,0,0,0,0,0,0,0,4,4,4,1,0,0,0,0,0,0,0,0,0,4,4,4,4,1,0,0,0,9,13,0,0,0,0,4,4,4,4,1,0,0,0,10,14,0,0,0,0,0,0,0,0,0,0,0,0,11,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,1,0,0,0,0,0,0,0,0,0,0,4,4,4,1,0,0,0,0,0,0,0,0,0,0,0,4,4,1,0,0,0,0,0,0,0,0,0,0,0,0,4,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,24,27,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1,0,0,9,13,0,0,0,0,0,0,0,5,7,1,0,0,10,14,0,0,0,0,0,0,0,6,8,1,0,0,11,15,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,34,1,0,0,0,0,0,0,0,0,0,3,0,0,35,1,0,0,0,0,0,0,0,0,0,3,0,0,36,1,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,9,13,0,0,0,0,3,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,11,15,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,5,7,1,0,0,9,13,0,0,0,0,0,0,0,6,8,1,0,0,10,14,0,0,0,0,0,0,0,0,4,1,0,0,10,14,0,0,0,0,0,0,0,4,4,1,0,0,11,15,0,0,0,0,0,0,4,4,4,1,0,0,0,0,0,0,0,0,0,4,4,4,4,1,0,0,0,0,0,0,0,0,4,4,4,4,4,1,0,0,0,0,0,0,0,4,4,4,4,4,4,1,0,0,0,0,0,0,4,4,4,4,4,4,4,1,0,0,0,0,0,4,4,4,4,4,4,4,4,1,0,0,0,0,0,4,4,4,4,4,4,4,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,26,27,1,0,0,0,0,0,0,0,0,0,0,24,27,32,1,0,0,0,0,0,0,0,0,0,0,0,28,33,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1,0,0,0,16,0,0,0,0,0,0,0,0,0,1,0,0,12,17,18,19,19,19,19,19,19,19,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,9,13,0,0,0,0,0,0,0,0,1,0,0,0,10,14,0,0,0,0,0,0,0,0,1,0,0,0,11,15,0,0,0,0,0,20,22,22,1,0,0,0,0,0,0,0,0,20,21,25,22,22,1,0,0,0,0,0,0,0,0,20,22,25,31,41,1,0,0,0,0,0,0,0,0,20,23,25,22,22,1,0,0,0,0,0,0,0,0,0,0,20,22,22,1,0,0,0,0,0,0,0,0,0,0,0,0,36,1,0,0,0,0,0,0,0,0,0,0,0,0,26,1,0,0,0,0,0,0,0,0,0,0,0,24,27,1,0,0,0,0,0,0,0,0,0,0,0,0,28,1] levelCols=211; levelRows=14; objects=[] blockAnim=[-1,0,1,2,3,4,5,6,6,5,4,3,2,1] //constant for (var j=levelRows;j--;) for (var i=levelCols;i--;) if (object(levelData[j + levelRows*i])) objects.push([i,j,levelData[j + levelRows*i],0]) frame=0 //animation backgroundFrame=0; scroll=0 m_x=50 m_y=128 m_xspeed=0 m_yspeed=0 skidding=false fastjump=false //Jump started at > maxWalkSpeed fasterjump=false //Jump started at > airspeedCutoff fastVjump=false //Jump started at > jumpCutoff1 fasterVjump=false //Jump started at > jumpCutoff2 facingLeft=false runcount=0 //Constants minWalkSpeed = 1/16 + 3/256 walkAccel = 9/256 + 8/(16*16*16) maxWalkSpeed = 1 + 9/16 releaseDecel = 13/256 skidDecel = 1/16 + 10/256 turnSpeed = 9/16 maxRunSpeed = 2 + 9/16 runAccel = 14/256 + 4/(16*16*16) airspeedCutoff = 1 + 13/16 airSlowGain = 9/256 + 8/(16*16*16) airFastGain = 14/256 + 4/(16*16*16) airFastDrag = 13/256 airSlowDrag = 9/256 + 8/(16*16*16) jumpSpeed = 4 bigJumpSpeed = 5 smallUpDrag = 2/16 mediumUpDrag = 1/16 + 14/256 bigUpDrag = 2/16 + 8/256 smallGravity = 7/16 medGravity = 6/16 bigGravity = 9/16 jumpCutoff1 = 1 jumpCutoff2 = 2 + 5/16 maxVspeed = 4 function draw(){ var standingOn= solid(m_x,m_y+8) || (solid(m_x+4, m_y+8) && ((m_y+8)%16) <1+m_yspeed) || (solid(m_x-4, m_y+8) && ((m_y+8)%16) <1+m_yspeed) ; if (standingOn) { //On the ground // if (m_yspeed>0) { m_y=16*Math.floor((m_y+8)/16)-8 m_yspeed=0; // } var accel; if (keys[65]) { runcount=10; accel = runAccel } else { if (runcount>0) runcount-=1; accel = walkAccel; } if (keys[39]){ //Right if (m_xspeed<0) { //Skid skidding=true if (m_xspeed>-turnSpeed) m_xspeed=0 else m_xspeed+=skidDecel } else { skidding=false facingLeft=false if (m_xspeed==0) {m_xspeed=minWalkSpeed} else {m_xspeed+= accel} if (m_xspeed> maxRunSpeed) m_xspeed= maxRunSpeed if (m_xspeed> maxWalkSpeed &&runcount==0) m_xspeed= maxWalkSpeed; } } else if (keys[37]){ //left if (m_xspeed>0) { //Skid skidding=true if (m_xspeed<turnSpeed) m_xspeed=0 else m_xspeed-=skidDecel } else { skidding=false facingLeft=true if (m_xspeed==0) {m_xspeed=-minWalkSpeed} else {m_xspeed-= accel} if (m_xspeed<-maxRunSpeed) m_xspeed= -maxRunSpeed if (m_xspeed<-maxWalkSpeed &&runcount==0) m_xspeed= -maxWalkSpeed; } } else { //No direction pressed - decelerate var decel = skidding ? skidDecel : releaseDecel; if (m_xspeed> decel) m_xspeed-=decel else if (m_xspeed< -decel) m_xspeed+=decel else m_xspeed=0 } var absxspeed = m_xspeed if (absxspeed<0) absxspeed=-absxspeed; if (absxspeed > jumpCutoff2) { fasterVjump=true } else if (absxspeed > jumpCutoff1) { fastVjump=true } else { fasterVjump=false fastVjump=false } fastjump = false fasterjump=false if (absxspeed > maxWalkSpeed) fastjump=true if (absxspeed > airspeedCutoff) fasterjump=true if (keys[83] && !prevkeys[83]) { if (fasterVjump) m_yspeed = -bigJumpSpeed else m_yspeed = -jumpSpeed } } else { //In midair if (keys[39]){ //Right if (m_xspeed >= maxWalkSpeed || m_xspeed <= -maxWalkSpeed) { m_xspeed += airFastGain } else { if (m_xspeed>0) { //If going forward - use facing var? m_xspeed+=airSlowGain } else { if (fasterjump) { m_xspeed+=airFastDrag } else { m_xspeed+=airSlowDrag } } } } else if (keys[37]) { //Left if (m_xspeed >= maxWalkSpeed || m_xspeed <= -maxWalkSpeed) { m_xspeed -= airFastGain } else { if (m_xspeed<0) { //If going forward - use facing var? m_xspeed-=airSlowGain } else { if (fasterjump) { m_xspeed-=airFastDrag } else { m_xspeed-=airSlowDrag } } } } if (fastjump) { if (m_xspeed<-maxRunSpeed) m_xspeed= -maxRunSpeed if (m_xspeed> maxRunSpeed) m_xspeed= maxRunSpeed } else { if (m_xspeed<-maxWalkSpeed) m_xspeed= -maxWalkSpeed if (m_xspeed> maxWalkSpeed) m_xspeed= maxWalkSpeed } if (m_yspeed<0 && keys[83]) { if (fasterVjump) { m_yspeed += bigUpDrag; } else if (fastVjump) { m_yspeed += mediumUpDrag; } else { m_yspeed += smallUpDrag; } } else { if (fasterVjump) { m_yspeed += bigGravity; } else if (fastVjump) { m_yspeed += medGravity; } else { m_yspeed += smallGravity; } } if (m_yspeed>maxVspeed) m_yspeed=maxVspeed } m_x+=m_xspeed m_y+=m_yspeed // Wall collisions - need improvement? var solidLeft=solid(m_x-7,m_y), solidRight=solid(m_x+7,m_y); if (solidLeft &&!solidRight) { if (facingLeft) { m_xspeed=0 } m_x+=1; } if (solidRight &&!solidLeft) { if (!facingLeft) { m_xspeed=0 } m_x-=1 } // Headbang var i=Math.floor((scroll+m_x)/16), j=Math.floor((m_y-4)/16); if( solid(m_x,m_y-4)) { m_yspeed=0 m_y=16*Math.floor((m_y-4)/16+1)+4 for (var k in objects) if (objects[k][0]==i&&objects[k][1]==j) { if (objects[k][2]==2||objects[k][2]==3) objects[k][3]=blockAnim.length; break; } } //Scroll view if (m_x<8) {m_x=8;m_xspeed=0} if (m_x>90) {var a= (m_x-90)/2; scroll+=a;m_x-=a;} //Needs work frame=m_xspeed||keys[37]||keys[39]?(frame+1+Math.abs(m_xspeed*2))%48:0 backgroundFrame=(backgroundFrame+1) %80 for (var j=14;j--;) for (var sx=Math.floor(scroll/16),i=sx+18;i>=sx;i--) { if (!object(levelData[j + levelRows*i])) ctx.drawImage(tiles, levelData[j + levelRows*i]*16,0,16,16, i*16-Math.round(scroll),j*16, 16,16); else ctx.drawImage(tiles, 0,0,16,16, i*16-Math.round(scroll),j*16, 16,16); } for (var i in objects) { var x=objects[i][0]*16-scroll,y=objects[i][1]*16; if (x>-16 || x<256+16){ ctx.drawImage(sprites, Math.floor(backgroundFrame/16)*16,objects[i][2]*16, 16,16, x,y -(objects[i][3]?blockAnim[--objects[i][3]]:0), 16,16); } } ctx.drawImage(sprites, (!standingOn)?80: m_xspeed||keys[37]||keys[39]?(skidding?64:16+Math.floor(frame/16)*16):0, facingLeft?16:0 ,16,16, m_x-8,m_y-7, 16,16) prevkeys=keys.slice() requestAnimationFrame(draw); } function solid(x,y){n=levelData[Math.floor((scroll+x)/16)*levelRows +Math.floor(y/16)];return (n&&n<9)} function object(n){return (n==2||n==3)} (tiles=new Image()).src="Tileset1.png"; tiles.onload=function(){ (sprites=new Image()).src="MarioSprites.png"; sprites.onload=function(){draw();} } keys=[]; keys[37]=keys[39]=keys[65]=keys[83]=0; document.onkeydown=function(e){ keys[e.keyCode]=true; } document.onkeyup = function(e){ keys[e.keyCode]=false; } </script>
### Mario.asm ### ; Constants... lots of them. Some of these are only approximate. .equ minWalkSpeed = $0013 .equ walkAccel = $0009 ; + .5 .equ maxWalkSpeed = $0190 .equ releaseDecel = $000D .equ skidDecel = $001A .equ turnSpeed = $0090 .equ maxRunSpeed = $0290 .equ runAccel = $000E ; + .25 .equ airspeedCutoff = $01D0 .equ airSlowGain = $0009 ; + .5 .equ airFastGain = $000E ; + .25 .equ airFastDrag = $000D .equ airSlowDrag = $0009 ; + .5 .equ jumpSpeed = $0400 .equ bigJumpSpeed = $0500 .equ smallUpDrag = $0020 .equ mediumUpDrag = $001E .equ bigUpDrag = $0028 .equ smallGravity = $0070 .equ medGravity = $0060 .equ bigGravity = $0090 .equ jumpCutoff1 = $0100 .equ jumpCutoff2 = $0250 .equ maxVspeed = $0400 ; Variables .def ctrlButtons = r2 .def prevButtons = r3 .def runcount = r4 ; r5 .def scroll = r6 .def blockAnim = r7 .def m_x_L = r8 .def m_x_H = r9 .def m_y_L = r10 .def m_y_H = r11 .def m_xspeed_L = r12 .def m_xspeed_H = r13 .def m_yspeed_L = r14 .def m_yspeed_H = r15 .def m_flags = r25 ; m_flags bit numbers .equ skidding = 0 .equ fastjump = 1 ; Jump started at > maxWalkSpeed .equ fasterjump = 2 ; Jump started at > airspeedCutoff .equ fastVjump = 3 ; Jump started at > jumpCutoff1 .equ fasterVjump = 4 ; Jump started at > jumpCutoff2 .equ facingLeft = 5 .equ standingOn = 6 ; more constants (for now) .equ levelCols = 211 .equ levelRows = 14 .equ levelColsInBuffer = 15 .equ levelBufferSize = (levelRows*levelColsInBuffer) ; Memory locations .equ frame = D_START .equ backgroundFrame = D_START + 1 .equ levelPointerH = D_START + 2 .equ levelPointerL = D_START + 3 .equ objectsLength = D_START + 4 .equ blockAnimX = D_START + 5 .equ blockAnimY = D_START + 6 .equ levelBuffer = D_START + 7 .equ objects = levelBuffer + levelBufferSize ; object table columns .equ obj_x = 0 .equ obj_y = 1 .equ obj_timer = 2 ;///////////////// ;init frame counter ldi r16,0 out TCNT0, r16 ldi r16, (1<<CS02)|(1<<CS01)|(1<<CS00) out TCCR0, r16 ldi r16, 128 out OCR0, r16 ; Init music ldi r16, HIGH(Music*2) sts MUSIC_START_H, r16 ldi r16, LOW(Music*2) sts MUSIC_START_L, r16 call resetMusic ; Load first bit of level into ram ldi r16, HIGH(levelData1*2) sts levelPointerH, r16 ldi r16, LOW(levelData1*2) sts levelPointerL, r16 ldi r18, levelColsInBuffer loadLevelDataLoop: rcall loadLevelCol dec r18 brne loadLevelDataLoop ; load all(?) objects into ram ; clear registers clr scroll clr prevButtons clr runcount clr m_x_L clr m_y_L clr m_xspeed_L clr m_xspeed_H clr m_yspeed_L clr m_yspeed_H clr m_flags clr blockAnim ldi r16,200 mov m_x_H,r16 ldi r16,128 mov m_y_H,r16 ldi r16,0 sts backgroundFrame,r16 sts frame,r16 Main: rcall readController ;////////// ;rjmp standingOnSolid ;//////// ; Are we standing on something? cbr m_flags, (1<<standingOn) ; assume not mov r16,m_x_H mov r17,m_y_H subi r17, -8 rcall checkSolid ; check solid 8 pixels underneath breq standingOnSolid ; check to either side as well. mov r16,m_x_H mov r17,m_y_H subi r17, -8 subi r16, -4 rcall checkSolid breq standingOnSolidSide mov r16,m_x_H mov r17,m_y_H subi r17, -8 subi r16, 4 rcall checkSolid breq standingOnSolidSide rjmp inMidair standingOnSolidSide: ; Additionally check our alignment with the tile and our vertical speed ; - check this. mov r16, m_y_H subi r16,-8 andi r16, $F0 mov r17, m_yspeed_H inc r17 cp r16,r17 brcc standingOnSolid rjmp inMidair standingOnSolid: sbr m_flags, (1<<standingOn) ; flag that we are on solid ground clr m_yspeed_H ; empty vertical speed clr m_yspeed_L clr m_y_L mov r16, m_y_H subi r16,-8 ; is there a faster way (better mask) to do this? andi r16, $F0 ; align with tiles subi r16,8 mov m_y_H,r16 ; temp var .def accel = r24 ; check "run" button sbrs ctrlButtons, ctrlB rjmp notHoldingRun ldi r16,10 mov runcount, r16 ldi accel, LOW(runAccel) rjmp checkWalkRight notHoldingRun: clr r16 cpse runcount,r16 dec runcount ldi accel, LOW(walkAccel) ; Handle moving left and right checkWalkRight: sbrs ctrlButtons, ctrlLeft rjmp checkWalkLeft sbrs m_xspeed_H,7 ; check sign bit of xspeed rjmp xSpeedPositive ; moving left when holding right means we're skidding sbr m_flags, (1<<skidding) ldi r16, LOW(-turnspeed) ldi r17,HIGH(-turnspeed) cp m_xspeed_L, r16 cpc m_xspeed_H, r17 brcs aboveTurnspeedRight clr m_xspeed_L clr m_xspeed_H aboveTurnspeedRight: ldi r16, LOW(skidDecel) ldi r17,HIGH(skidDecel) add m_xspeed_L,r16 adc m_xspeed_H,r17 rjmp checkJump xSpeedPositive: cbr m_flags, (1<<skidding)|(1<<facingLeft) ; not skidding, facing right clr r16 cp m_xspeed_L, r16 cpc m_xspeed_H, r16 brne speedNonZeroRight ; if xspeed=0, start at minimum speed ldi r16, LOW(minWalkSpeed) mov m_xspeed_L,r16 ldi r16,HIGH(minWalkSpeed) mov m_xspeed_H,r16 rjmp checkMaxRunSpeedRight speedNonZeroRight: clr r16 add m_xspeed_L,accel adc m_xspeed_H,r16 checkMaxRunSpeedRight: ;if (m_xspeed> maxRunSpeed) m_xspeed= maxRunSpeed ldi r16, LOW(maxRunSpeed) ldi r17,HIGH(maxRunSpeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcc belowMaxRunSpeedRight mov m_xspeed_L, r16 ; above max run speed - set to max run speed. mov m_xspeed_H, r17 belowMaxRunSpeedRight: ;if (m_xspeed> maxWalkSpeed &&runcount==0) m_xspeed= maxWalkSpeed; tst runcount brne belowMaxWalkSpeedRight ldi r16, LOW(maxWalkSpeed) ldi r17,HIGH(maxWalkSpeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcc belowMaxWalkSpeedRight mov m_xspeed_L, r16 mov m_xspeed_H, r17 belowMaxWalkSpeedRight: rjmp checkJump ; end of checkWalkRight checkWalkLeft: sbrs ctrlButtons, ctrlRight rjmp groundNoDirection sbrc m_xspeed_H,7 ; check sign bit of xspeed rjmp xSpeedNegative ; moving right when holding left means we're skidding sbr m_flags, (1<<skidding) ldi r16, LOW(turnspeed) ldi r17,HIGH(turnspeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcs aboveTurnspeedLeft clr m_xspeed_L clr m_xspeed_H aboveTurnspeedLeft: ldi r16, LOW(skidDecel) ldi r17,HIGH(skidDecel) sub m_xspeed_L,r16 sbc m_xspeed_H,r17 rjmp checkJump xSpeedNegative: cbr m_flags, (1<<skidding) ; not skidding sbr m_flags, (1<<facingLeft) ; we are indeed facing left clr r16 cp m_xspeed_L, r16 cpc m_xspeed_H, r16 brne speedNonZeroLeft ; if xspeed=0, start at minimum speed ldi r16, LOW(-minWalkSpeed) mov m_xspeed_L,r16 ldi r16,HIGH(-minWalkSpeed) mov m_xspeed_H,r16 rjmp checkMaxRunSpeedLeft speedNonZeroLeft: clr r16 sub m_xspeed_L,accel sbc m_xspeed_H,r16 checkMaxRunSpeedLeft: ; if (m_xspeed<-maxRunSpeed) m_xspeed= -maxRunSpeed ldi r16, LOW(-maxRunSpeed) ldi r17,HIGH(-maxRunSpeed) cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc belowMaxRunSpeedLeft mov m_xspeed_L, r16 ; above max run speed - set to max run speed. mov m_xspeed_H, r17 belowMaxRunSpeedLeft: ;if (m_xspeed<-maxWalkSpeed &&runcount==0) m_xspeed= -maxWalkSpeed; tst runcount brne belowMaxWalkSpeedLeft ldi r16, LOW(-maxWalkSpeed) ldi r17,HIGH(-maxWalkSpeed) cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc belowMaxWalkSpeedLeft mov m_xspeed_L, r16 mov m_xspeed_H, r17 belowMaxWalkSpeedLeft: rjmp checkJump ; end of checkWalkLeft groundNoDirection: ; if we have started to skid, continue at skidding deceleration ldi accel, LOW(skidDecel) sbrs m_flags, (1<<skidding) ldi accel, LOW(releaseDecel) ; if xspeed > decel amount, subtract it, else set xspeed=0. sbrc m_xspeed_H,7 rjmp gndNoDirNegative clr r16 cp accel,m_xspeed_L cpc r16,m_xspeed_H brcc gndNoDirZero sub m_xspeed_L,accel sbc m_xspeed_H,r16 rjmp checkJump gndNoDirNegative: clr r16 clr r17 sub r16,accel sbci r17,0 cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc gndNoDirZero sub m_xspeed_L,r16 sbc m_xspeed_H,r17 rjmp checkJump gndNoDirZero: clr m_xspeed_L clr m_xspeed_H checkJump: .def abs_xspeed_H = r26 .def abs_xspeed_L = r27 mov abs_xspeed_H, m_xspeed_H mov abs_xspeed_L, m_xspeed_L sbrs abs_xspeed_H,7 rjmp checkJumpCutoffs com abs_xspeed_H ; 16 bit negation neg abs_xspeed_L sbci abs_xspeed_H,255 checkJumpCutoffs: ldi r16, LOW(jumpCutoff2) ldi r17,HIGH(jumpCutoff2) cp r16, abs_xspeed_L cpc r17, abs_xspeed_H brcc belowJumpCutoff2 sbr m_flags, (1<<fasterVjump) rjmp checkFastJumpCutoffs belowJumpCutoff2: ldi r16, LOW(jumpCutoff1) ldi r17,HIGH(jumpCutoff1) cp r16, abs_xspeed_L cpc r17, abs_xspeed_H brcc belowJumpCutoff1 sbr m_flags, (1<<fastVjump) rjmp checkFastJumpCutoffs belowJumpCutoff1: cbr m_flags, (1<<fastVjump)|(1<<fasterVjump) checkFastJumpCutoffs: cbr m_flags, (1<<fastjump)|(1<<fasterjump) ldi r16, LOW(maxWalkSpeed) ldi r17,HIGH(maxWalkSpeed) cp r16, abs_xspeed_L cpc r17, abs_xspeed_H brcc belowJumpCutoff3 sbr m_flags,(1<<fastjump) belowJumpCutoff3: ldi r16, LOW(airspeedCutoff) ldi r17,HIGH(airspeedCutoff) cp r16, abs_xspeed_L cpc r17, abs_xspeed_H brcc belowJumpCutoff4 sbr m_flags,(1<<fasterjump) belowJumpCutoff4: sbrs ctrlButtons,ctrlA rjmp actuallyMove sbrc prevButtons,ctrlA rjmp actuallyMove sbrc m_flags, (1<<fasterjump) rjmp startFasterJump ldi r16, LOW(-jumpSpeed) ldi r17, HIGH(-jumpSpeed) mov m_yspeed_L, r16 mov m_yspeed_H, r17 rjmp actuallyMove startFasterJump: ldi r16, LOW(-bigJumpSpeed) ldi r17,HIGH(-bigJumpSpeed) mov m_yspeed_L, r16 mov m_yspeed_H, r17 rjmp actuallyMove inMidair: ; check float right sbrs ctrlButtons, ctrlLeft rjmp checkFloatLeft ; if m_xspeed >= maxWalkSpeed or m_xspeed<=-maxWalkSpeed ldi r16, LOW(maxWalkSpeed) ldi r17,HIGH(maxWalkSpeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcc belowMaxWalkSpeedFloat ldi r16, LOW(-maxWalkSpeed) ldi r17,HIGH(-maxWalkSpeed) cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc belowMaxWalkSpeedFloat rjmp aboveMaxWalkSpeedFloat belowMaxWalkSpeedFloat: ldi r16, LOW(airFastGain) ldi r17,HIGH(airFastGain) add m_xspeed_L,r16 adc m_xspeed_H,r17 rjmp checkFastJump aboveMaxWalkSpeedFloat: sbrc m_xspeed_H,7 ; if negative rjmp floatingBackwards ldi r16, LOW(airSlowGain) ldi r17,HIGH(airSlowGain) add m_xspeed_L,r16 adc m_xspeed_H,r17 rjmp checkFastJump floatingBackwards: sbrs m_flags, fasterjump rjmp notFasterJump ldi r16, LOW(airFastDrag) ldi r17,HIGH(airFastDrag) add m_xspeed_L,r16 adc m_xspeed_H,r17 rjmp checkFastJump notFasterJump: ldi r16, LOW(airSlowDrag) ldi r17,HIGH(airSlowDrag) add m_xspeed_L,r16 adc m_xspeed_H,r17 rjmp checkFastJump ; Now do all that again but with signs reversed... checkFloatLeft: sbrs ctrlButtons, ctrlRight rjmp checkFastJump ldi r16, LOW(maxWalkSpeed) ldi r17,HIGH(maxWalkSpeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcc belowMaxWalkSpeedFloatLeft ldi r16, LOW(-maxWalkSpeed) ldi r17,HIGH(-maxWalkSpeed) cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc belowMaxWalkSpeedFloatLeft rjmp aboveMaxWalkSpeedFloatLeft belowMaxWalkSpeedFloatLeft: ldi r16, LOW(airFastGain) ldi r17,HIGH(airFastGain) sub m_xspeed_L,r16 sbc m_xspeed_H,r17 rjmp checkFastJump aboveMaxWalkSpeedFloatLeft: sbrs m_xspeed_H,7 ; if negative rjmp floatingBackwardsLeft ldi r16, LOW(airSlowGain) ldi r17,HIGH(airSlowGain) sub m_xspeed_L,r16 sbc m_xspeed_H,r17 rjmp checkFastJump floatingBackwardsLeft: sbrs m_flags, fasterjump rjmp notFasterJumpLeft ldi r16, LOW(airFastDrag) ldi r17,HIGH(airFastDrag) sub m_xspeed_L,r16 sbc m_xspeed_H,r17 rjmp checkFastJump notFasterJumpLeft: ldi r16, LOW(airSlowDrag) ldi r17,HIGH(airSlowDrag) sub m_xspeed_L,r16 sbc m_xspeed_H,r17 rjmp checkFastJump ; Limit maximum floating speeds ; (constants are same as whatever they were when we were on the ground) checkFastJump: sbrs m_flags, fastjump rjmp notFastJump sbrc m_xspeed_H,7 rjmp cFJnegative ldi r16, LOW(maxRunSpeed) ldi r17,HIGH(maxRunSpeed) cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc cFJaboveMaxRunSpeed rjmp endCheckFastJump cFJaboveMaxRunSpeed: mov m_xspeed_L,r16 mov m_xspeed_H,r17 rjmp endCheckFastJump cFJnegative: ldi r16, LOW(-maxRunSpeed) ldi r17,HIGH(-maxRunSpeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcc cFJaboveMaxRunSpeed rjmp endCheckFastJump notFastJump: sbrc m_xspeed_H,7 rjmp cFJnegativeSlow ldi r16, LOW(maxWalkSpeed) ldi r17,HIGH(maxWalkSpeed) cp m_xspeed_L,r16 cpc m_xspeed_H,r17 brcc cFJaboveMaxWalkSpeed rjmp endCheckFastJump cFJaboveMaxWalkSpeed: mov m_xspeed_L,r16 mov m_xspeed_H,r17 rjmp endCheckFastJump cFJnegativeSlow: ldi r16, LOW(-maxWalkSpeed) ldi r17,HIGH(-maxWalkSpeed) cp r16,m_xspeed_L cpc r17,m_xspeed_H brcc cFJaboveMaxWalkSpeed rjmp endCheckFastJump ; Now handle vertical movement endCheckFastJump: ; reduce gravity if holding A and moving up sbrs ctrlButtons,ctrlA rjmp movingDown sbrs m_yspeed_H,7 ; if yspeed<0, moving up rjmp movingDown sbrs m_flags, fasterVjump rjmp notFasterVjumpUp ldi r16, LOW(bigUpDrag) ldi r17,HIGH(bigUpDrag) add m_yspeed_L,r16 adc m_yspeed_H,r17 rjmp belowMaxVspeed notFasterVjumpUp: sbrs m_flags, fastVjump rjmp notFastVjumpUp ldi r16, LOW(mediumUpDrag) ldi r17,HIGH(mediumUpDrag) add m_yspeed_L,r16 adc m_yspeed_H,r17 rjmp belowMaxVspeed notFastVjumpUp: ldi r16, LOW(smallUpDrag) ldi r17,HIGH(smallUpDrag) add m_yspeed_L,r16 adc m_yspeed_H,r17 rjmp belowMaxVspeed movingDown: sbrs m_flags, fasterVjump rjmp notFasterVjumpDown ldi r16, LOW(bigGravity) ldi r17,HIGH(bigGravity) add m_yspeed_L,r16 adc m_yspeed_H,r17 rjmp limitMaxVspeed notFasterVjumpDown: sbrs m_flags, fastVjump rjmp notFastVjumpDown ldi r16, LOW(medGravity) ldi r17,HIGH(medGravity) add m_yspeed_L,r16 adc m_yspeed_H,r17 rjmp limitMaxVspeed notFastVjumpDown: ldi r16, LOW(smallGravity) ldi r17,HIGH(smallGravity) add m_yspeed_L,r16 adc m_yspeed_H,r17 limitMaxVspeed: sbrc m_yspeed_H,7 rjmp belowMaxVspeed ldi r16, LOW(maxVspeed) ldi r17,HIGH(maxVspeed) cp r16,m_yspeed_L cpc r17,m_yspeed_H brcc belowMaxVspeed mov m_yspeed_L,r16 mov m_yspeed_H,r17 belowMaxVspeed: ;end of inMidair actuallyMove: add m_x_L, m_xspeed_L adc m_x_H, m_xspeed_H add m_y_L, m_yspeed_L adc m_y_H, m_yspeed_H ; Wall collisions mov r16,m_x_H mov r17,m_y_H subi r16, 7 rcall checkSolid brne checkSolidRight sbrs m_flags, facingLeft rjmp solidLeftFacingRight clr m_xspeed_H clr m_xspeed_L solidLeftFacingRight: inc m_x_H rjmp endWallCollisions checkSolidRight: mov r16,m_x_H mov r17,m_y_H subi r16, -7 rcall checkSolid brne endWallCollisions sbrc m_flags, facingLeft rjmp solidRightFacingRight clr m_xspeed_H clr m_xspeed_L solidRightFacingRight: dec m_x_H endWallCollisions: ; Headbang mov r16,m_x_H mov r17,m_y_H subi r17, 4 rcall checkSolid brne notBangingHead clr m_yspeed_H clr m_yspeed_L clr m_y_L ldi r16,4 sub m_y_H,r16 ldi r17,$F0 and m_y_H,r17 ldi r16,20 add m_y_H,r16 ldi r16,16 mov blockAnim,r16 mov r16,m_x_H sub r16,scroll subi r16,-16 andi r16,$F0 add r16, scroll sts blockAnimX,r16 mov r16,m_y_H subi r16,4 andi r16,$F0 sts blockAnimY,r16 notBangingHead: ; limit moving backwards offscreen ldi r16, 232 cp m_x_H, r16 brcs dontLimitMovBack clr m_xspeed_H clr m_xspeed_L mov m_x_H, r16 dontLimitMovBack: ; Scroll view ; if m_x < 150, {scroll++, m_x--} scrollView: ldi r16,150 cp m_x_H,r16 brcc doneScrollView inc scroll inc m_x_H lds r17,blockAnimX inc r17 sts blockAnimX,r17 rjmp scrollView doneScrollView: ; if scroll>=16, loadLevelCol & scroll-=16 ldi r16,16 cp scroll, r16 brcs doneScrollLevel sub scroll, r16 rcall loadLevelCol doneScrollLevel: ; mario animation frame ;if xspeed or holding left or right... sbrc ctrlButtons, ctrlLeft rjmp increaseMarioFrame sbrc ctrlButtons, ctrlRight rjmp increaseMarioFrame tst m_xspeed_H brne increaseMarioFrame ;set frame to 0 clr r16 sts frame,r16 rjmp endIncreaseFrame increaseMarioFrame: ;increment frame (by xspeed?) mov r17,m_xspeed_H sbrc m_xspeed_H,7 neg r17 ; get absolute xspeed lsr r17 lds r16,frame inc r16 add r16,r17 ; animate faster if moving fast andi r16, 0b00111111 ; mod 64 cpi r16,48 brcs dontResetMarioFrame ;subi r16,48 clr r16 dontResetMarioFrame: sts frame,r16 endIncreaseFrame: lds r16,backgroundFrame inc r16 cpi r16,(4*16) brne endIncBackgroundFrame clr r16 endIncBackgroundFrame: sts backgroundFrame,r16 tst blockAnim breq noBlockAnimFrame dec blockAnim noBlockAnimFrame: ;draw mario sbrc m_flags, standingOn ;check if in midair rjmp drawMarioGround ldi ZH, HIGH(MarioSprites*2+32*5) ldi ZL, LOW(MarioSprites*2+32*5) rjmp drawMarioCheckFacing drawMarioGround: ldi ZH, HIGH(MarioSprites*2) ldi ZL, LOW(MarioSprites*2) ldi r16,0 cp m_xspeed_L,r16 cpc m_xspeed_H,r16 breq drawMarioCheckFacing ;if not moving, draw standing lds r16,frame andi r16,0b00011000 lsl r16 ; sprites are 32 bytes apart lsl r16 add ZL,r16 clr r16 adc ZH, r16 sbrs m_flags, skidding rjmp drawMarioCheckFacing ldi ZH, HIGH(MarioSprites*2+32*4) ldi ZL, LOW(MarioSprites*2+32*4) drawMarioCheckFacing: ; if facingleft, add 6*32=192 sbrc m_flags, facingLeft rjmp drawMarioNotLeft ldi r16,192 add ZL, r16 clr r16 adc ZH,r16 drawMarioNotLeft: mov r16,m_x_H mov r17,m_y_H subi r16,-8 subi r17,-7 rcall drawSprite16 ; draw background ldi YH, HIGH(levelBuffer) ldi YL, LOW(levelBuffer) ldi r16,16 add r16, scroll ldi r22, levelColsInBuffer drawLevelRowLoop: ldi r17,16 ldi r21, levelRows drawLevelColLoop: ld r18, Y+ cpi r18, 0 breq dontDraw push r16 push r17 tst blockAnim breq noHeadBang lds r19,blockAnimX ;add r19,scroll cp r19,r16 brne noHeadBang lds r19,blockAnimY cp r19,r17 brne noHeadBang ldi ZH, HIGH(BlockAnimData*2) ldi ZL, LOW(BlockAnimData*2) add ZL, blockAnim ldi r19,0 adc ZH,r19 lpm r19,Z sub r17,r19 ; adjust Y position by loaded amount ;dec blockAnim noHeadBang: cpi r18,2 ; question block - animate. brne drawStatic ldi ZH, HIGH(QuestionSprite*2) ldi ZL, LOW(QuestionSprite*2) lds r19, backgroundFrame andi r19, $F0 ; mod 16 lsl r19 add ZL,r19 ldi r19,0 adc ZH,r19 rjmp endDrawTile drawStatic: ldi ZH, HIGH(Tileset*2) ldi ZL, LOW(Tileset*2) ldi r19,32 mul r18,r19 add ZL, r0 adc ZH, r1 endDrawTile: rcall drawTile pop r17 pop r16 dontDraw: subi r17,-16 dec r21 brne drawLevelColLoop subi r16,-16 dec r22 brne drawLevelRowLoop mov prevButtons, ctrlButtons ; remember controller state call processAudio ldi r16,0 out PORTA,r16 out PORTC,r16 waitforframe: in r16, TIFR sbrs r16, OCF0 rjmp waitforframe ldi r16, (1<<OCF0) out TIFR, r16 ldi r16, 0 out TCNT0, r16 rjmp Main ; Function checkSolid (x=r16 pixels, y=r17 pixels) checkSolid: sub r16,scroll ldi YH, HIGH(levelBuffer) ldi YL, LOW(levelBuffer) andi r16, $F0 ; Faster than four shifts swap r16 ; round? ldi r18, levelRows mul r16,r18 add YL, r0 adc YH, r1 andi r17,$F0 swap r17 clr r18 add YL, r17 adc YH, r18 ld r16, Y cpi r16,0 breq cNotSolid cpi r16,9 brcc cNotSolid sez ret cNotSolid: clz ret ;function isObject - what tiles are interactive? isObject: cpi r16,2 ; question block breq yesObject cpi r16,3 ; bricks breq yesObject sez yesObject: clz ret ; Function loadLevelCol ; Pushes a row of level data into the sram loadLevelCol: lds ZH, levelPointerH lds ZL, levelPointerL ldi YH, HIGH(levelBuffer + levelBufferSize-levelRows) ldi YL, LOW(levelBuffer + levelBufferSize-levelRows) ; shift all rows down by one ldi r16,((levelColsInBuffer-1)*levelRows) loadLevelShiftLoop: ld r17, -Y std Y+levelRows, r17 dec r16 brne loadLevelShiftLoop ldi YH, HIGH(levelBuffer) ldi YL, LOW(levelBuffer) ldi r16, levelRows ; now add the new row at the beginning loadLevelColLoop: lpm r17, Z+ st Y+, r17 dec r16 brne loadLevelColLoop sts levelPointerH, ZH sts levelPointerL, ZL ret ; Function readController readController: sbis SPSR,SPIF rjmp readController ; Wait for reception complete in ctrlButtons,SPDR ; Read received data com ctrlButtons push r16 ldi r16,0b00000000 ; init controller read again out SPDR,r16 pop r16 ret ; Function drawSprite16 drawSprite16: ldi r20,16 drawSpriteWordRow: push r16 lpm r19,Z+ lpm r18,Z+ drawSpriteWordLoop: sbrs r18,0 rjmp drawSpriteBit out PORTA, r16 out PORTC, r17 rcall delaySprite16 drawSpriteBit: dec r16 lsr r19 ror r18 brne drawSpriteWordLoop cpi r19,0 brne drawSpriteWordLoop inc r17 pop r16 dec r20 brne drawSpriteWordRow ret delaySprite16: push r16 ldi r16,7 delSprite16Loop: dec r16 brne delSprite16Loop pop r16 ret ; Function drawTile ; Identical to drawSprite but with reduced delay drawTile: ldi r20,16 drawTileWordRow: push r16 lpm r19,Z+ lpm r18,Z+ drawTileWordLoop: sbrs r18,0 rjmp drawTileBit out PORTA, r16 out PORTC, r17 rcall delayTile drawTileBit: dec r16 lsr r19 ror r18 brne drawTileWordLoop cpi r19,0 brne drawTileWordLoop inc r17 pop r16 dec r20 brne drawTileWordRow ret delayTile: push r16 ldi r16,1 delTileLoop: dec r16 brne delTileLoop pop r16 ret MarioSprites: ; left .db 3,224, 31,240, 5,240, 29,216, 59,152, 17,248, 15,224, 3,240, 31,248, 63,252, 63,252, 63,252, 63,252, 14,112, 28,56, 60,60, 0,0, 7,192, 63,224, 11,224, 59,176, 119,48, 35,240, 31,192, 23,224, 63,240, 31,248, 15,248, 15,248, 7,124, 3,132, 7,128, 3,224, 31,240, 5,240, 29,216, 59,152, 17,248, 15,224, 3,240, 7,248, 14,248, 15,248, 15,248, 7,240, 7,224, 15,224, 1,224, 3,224, 31,240, 5,240, 29,248, 59,248, 17,248, 15,224, 3,252, 127,255, 127,119, 39,243, 63,248, 63,252, 62,62, 0,14, 0,28, 3,224, 15,248, 31,208, 62,252, 62,102, 31,244, 15,248, 31,252, 31,252, 31,252, 15,248, 15,240, 7,240, 47,224, 63,0, 30,0, 224,0, 231,192, 255,224, 235,224, 251,176, 247,48, 67,240, 63,192, 31,252, 159,254, 159,255, 255,247, 255,250, 255,252, 7,254, 0,242 ;right .db 7,192, 15,248, 15,160, 27,184, 25,220, 31,136, 7,240, 15,192, 31,248, 63,252, 63,252, 63,252, 63,252, 14,112, 28,56, 60,60, 0,0, 3,224, 7,252, 7,208, 13,220, 12,238, 15,196, 3,248, 7,232, 15,252, 31,248, 31,240, 31,240, 62,224, 33,192, 1,224, 7,192, 15,248, 15,160, 27,184, 25,220, 31,136, 7,240, 15,192, 31,224, 31,112, 31,240, 31,240, 15,224, 7,224, 7,240, 7,128, 7,192, 15,248, 15,160, 27,184, 25,220, 31,136, 7,240, 63,192, 255,254, 238,254, 207,228, 31,252, 63,252, 124,124, 112,0, 56,0, 7,192, 31,240, 11,248, 63,124, 103,124, 47,248, 31,240, 63,248, 63,248, 63,248, 31,240, 15,240, 15,224, 7,244, 0,252, 0,120, 0,7, 3,231, 7,255, 7,215, 13,223, 12,239, 15,194, 3,252, 63,248, 127,249, 255,249, 239,255, 95,255, 63,255, 127,224, 79,0 BrickSprite: .db 255,255, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 128,128, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255 QuestionSprite: .db 127,254, 128,1, 160,5, 131,225, 134,49, 134,49, 134,49, 135,1, 129,129, 129,129, 128,1, 129,129, 129,129, 160,5, 128,1, 127,254, 127,254, 255,255, 223,251, 255,255, 254,63, 247,191, 247,191, 247,159, 241,255, 253,255, 252,255, 255,255, 253,255, 220,251, 255,255, 127,254, 127,254, 255,255, 223,251, 252,31, 248,15, 241,143, 241,143, 240,159, 240,127, 252,127, 252,255, 254,127, 252,127, 220,251, 255,255, 127,254, 127,254, 255,255, 223,251, 255,255, 254,63, 247,191, 247,191, 247,159, 241,255, 253,255, 252,255, 255,255, 253,255, 220,251, 255,255, 127,254 BlockAnimData: .db -1,0,1,2,3,4,5,6,6,5,4,3,2,1,0 Tileset: .db 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 123,254, 134,1, 134,1, 134,1, 142,1, 122,1, 254,1, 134,1, 134,1, 134,1, 131,3, 131,15, 129,253, 129,241, 193,129, 127,126, 127,254, 128,1, 160,5, 131,225, 134,49, 134,49, 134,49, 135,1, 129,129, 129,129, 128,1, 129,129 .db 129,129, 160,5, 128,1, 127,254, 255,255, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 128,128, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 127,255, 63,255, 31,255, 15,255, 8,15, 8,15, 8,15, 8,15, 8,15, 8,15, 8,15, 15,255, 16,7, 32,3, 64,1, 128,0, 255,255, 255,255, 15,193, 111,207, 111,207 .db 111,207, 111,207, 111,207, 111,207, 111,207, 111,207, 111,207, 111,207, 111,207, 255,255, 255,252, 255,255, 255,255, 128,0, 234,0, 244,0, 234,0, 244,0, 234,0, 244,0, 234,0, 244,0, 234,0, 244,0, 234,0, 255,255, 63,255, 223,60, 223,60, 223,60, 223,60, 223,60, 223,60, 223,60, 223,60, 223,60, 223,60, 223,60 .db 223,60, 223,60, 223,60, 223,60, 223,60, 61,0, 58,0, 61,0, 58,0, 61,0, 58,0, 61,0, 58,0, 61,0, 58,0, 61,0, 58,0, 61,0, 58,0, 61,0, 58,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 224,0, 16,0, 8,0, 0,0, 6,0, 1,0, 1,0, 2,0, 3,192, 4,32, 8,24, 40,4, 80,4, 130,4, 132,98, 128,17, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 .db 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,9, 0,21, 0,18, 0,80, 0,160, 0,128, 0,128, 0,64, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 3,192, 4,96, 8,48, 8,48, 8,16, 8,16, 4,32, 3,192, 36,0, 72,0, 144,0, 16,0, 224,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 2,0, 1,2, 67,199, 62,252, 28,48 .db 0,129, 195,70, 60,56, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,32, 0,64, 0,128, 0,0, 0,96, 0,25, 0,6, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 255,0, 254,0, 252,0, 248,0, 240,0, 224,0, 192,0, 128,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 1,128, 1,255, 1,193, 1,148, 1,182, 1,162, 1,136, 1,128, 1,227 .db 1,255, 1,254, 1,252, 1,248, 1,240, 1,224, 1,192, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 248,15, 8,8, 8,8, 8,8, 8,8, 8,8 .db 8,8, 255,255, 128,128, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 0,128, 0,128, 0,128, 0,255, 0,8, 0,8, 0,8, 0,255, 0,128, 0,128, 0,128, 0,255, 0,8, 0,8, 0,8, 0,255, 128,128, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 128,128, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 128,0, 128,0 .db 128,0, 255,0, 8,0, 8,0, 8,0, 255,0, 128,0, 128,0, 128,0, 255,0, 8,0, 8,0, 8,0, 255,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 7,224, 56,28, 192,3, 248,143, 8,136, 8,136, 15,248, 8,8, 8,8, 8,8, 255,255, 128,128, 128,128, 128,128, 255,255, 8,8, 8,8, 8,8, 255,255, 128,0, 64,0, 32,0, 16,0 .db 8,0, 4,0, 2,0, 1,0, 0,128, 0,64, 0,32, 0,16, 0,8, 0,4, 0,2, 0,1, 32,0, 112,0, 112,0, 112,0, 118,0, 38,0, 6,0, 6,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,1, 0,2, 0,4, 0,8, 0,16, 0,32, 0,64, 0,128, 1,0, 2,0, 4,0, 8,0, 16,0, 32,0, 64,0, 128,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 .db 0,0, 128,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 7,240, 60,14, 224,1, 120,31, 96,7, 64,3, 0,0, 128,1, 128,1, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,32, 0,112, 0,112, 0,112, 0,118, 0,38, 0,6 .db 0,6, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 224,0, 16,0, 8,0, 0,0, 6,0, 1,0, 1,0, 2,0, 3,192, 4,32, 8,24, 40,4, 80,4, 130,4, 132,98, 128,17, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,9, 0,21, 0,18, 0,80, 0,160, 0,128, 0,128 .db 0,64, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,9, 0,21, 0,18, 0,80, 0,160, 0,128, 0,128, 128,64, 64,0, 32,0, 16,0, 8,0, 4,0, 2,0, 1,0, 0,128, 0,64, 0,32, 0,16, 0,8, 0,4, 0,2, 0,1, 0,0, 144,0, 56,0, 56,0, 56,0, 59,0, 19,0, 3,0, 3,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,1, 0,2, 0,4, 0,8, 0,16, 0,32 .db 0,64, 0,128, 1,0, 2,0, 4,0, 8,0, 16,0, 32,0, 64,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 levelData1: .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,26,27,1 .db 0,0,0,0,0,0,0,0,0,0,24,27,32,1 .db 0,0,0,0,0,0,0,0,0,0,0,28,33,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,1,1 ; end of temp .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,2,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,24,27,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .db 0,0,9,13,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,3,0,0,0,1 .db 0,0,11,15,0,0,0,0,0,2,0,0,0,1 .db 0,0,0,0,0,2,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,2,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,3,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,9,13,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,5,7,1 .db 0,0,0,10,14,0,0,0,0,0,0,6,8,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,11,15,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,9,13,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,0,5,7,7,1 .db 0,0,11,15,0,0,0,0,0,0,6,8,8,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,5,7,7,7,1 .db 0,0,0,0,0,0,0,0,0,6,8,8,8,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,26,27,1 .db 0,0,0,0,0,0,0,0,0,0,24,27,32,1 .db 0,0,0,0,0,0,0,0,0,0,0,28,33,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,9,13,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,5,7,7,7,1 .db 0,0,0,11,15,0,0,0,0,6,8,8,8,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,24,27,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .db 0,0,9,13,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,0,0,0,0,1 .db 0,0,11,15,0,0,0,0,0,0,0,0,0,0 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,0 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,9,13,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,3,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,2,0,0,0,1 .db 0,0,0,11,15,0,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,9,13,0,3,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,3,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,3,0,0,0,0,0,0,0,0 .db 0,0,11,15,0,3,0,0,0,0,0,0,0,0 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,0 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,2,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,26,27,1 .db 0,0,0,0,0,0,0,0,0,0,24,27,32,1 .db 0,0,0,0,0,0,0,0,0,0,0,28,33,1 .db 0,0,0,0,0,0,0,0,0,3,0,0,28,1 .db 0,0,0,0,0,0,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,9,13,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,11,15,0,0,0,0,2,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,2,0,0,0,2,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,37,1 .db 0,0,0,0,0,0,0,0,0,2,0,29,38,1 .db 0,0,0,0,0,0,0,0,0,0,0,30,39,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,40,1 .db 0,0,9,13,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,0,0,0,0,1 .db 0,0,11,15,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,0,9,13,3,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,11,15,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,2,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,2,0,0,0,3,0,0,0,1 .db 0,0,0,0,0,3,0,0,0,0,0,0,0,1 .db 0,0,9,13,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,0,0,0,0,1 .db 0,0,10,14,0,0,0,0,0,0,0,0,4,1 .db 0,0,11,15,0,0,0,0,0,0,0,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,4,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,4,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,26,27,1 .db 0,0,0,0,0,0,0,0,0,0,24,27,32,1 .db 0,0,0,0,0,0,0,0,0,0,0,28,33,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,4,4,4,4,1 .db 0,0,0,9,13,0,0,0,0,4,4,4,4,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,0 .db 0,0,0,11,15,0,0,0,0,0,0,0,0,0 .db 0,0,0,0,0,0,0,0,0,4,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,24,27,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .db 0,0,9,13,0,0,0,0,0,0,0,5,7,1 .db 0,0,10,14,0,0,0,0,0,0,0,6,8,1 .db 0,0,11,15,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,34,1 .db 0,0,0,0,0,0,0,0,0,3,0,0,35,1 .db 0,0,0,0,0,0,0,0,0,3,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,2,0,0,0,1 .db 0,0,0,9,13,0,0,0,0,3,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,11,15,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,5,7,1 .db 0,0,9,13,0,0,0,0,0,0,0,6,8,1 .db 0,0,10,14,0,0,0,0,0,0,0,0,4,1 .db 0,0,10,14,0,0,0,0,0,0,0,4,4,1 .db 0,0,11,15,0,0,0,0,0,0,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,4,4,4,4,1 .db 0,0,0,0,0,0,0,0,4,4,4,4,4,1 .db 0,0,0,0,0,0,0,4,4,4,4,4,4,1 .db 0,0,0,0,0,0,4,4,4,4,4,4,4,1 .db 0,0,0,0,0,4,4,4,4,4,4,4,4,1 .db 0,0,0,0,0,4,4,4,4,4,4,4,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,26,27,1 .db 0,0,0,0,0,0,0,0,0,0,24,27,32,1 .db 0,0,0,0,0,0,0,0,0,0,0,28,33,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .db 0,0,0,16,0,0,0,0,0,0,0,0,0,1 .db 0,0,12,17,18,19,19,19,19,19,19,19,4,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,0,1 .db 0,0,0,9,13,0,0,0,0,0,0,0,0,1 .db 0,0,0,10,14,0,0,0,0,0,0,0,0,1 .db 0,0,0,11,15,0,0,0,0,0,20,22,22,1 .db 0,0,0,0,0,0,0,0,20,21,25,22,22,1 .db 0,0,0,0,0,0,0,0,20,22,25,31,41,1 .db 0,0,0,0,0,0,0,0,20,23,25,22,22,1 .db 0,0,0,0,0,0,0,0,0,0,20,22,22,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,36,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,26,1 .db 0,0,0,0,0,0,0,0,0,0,0,24,27,1 .db 0,0,0,0,0,0,0,0,0,0,0,0,28,1 .include "audio.asm" Music: .include "Music/MarioMusic.asm" .include "Bootloader.asm"
In the four years it's taken me to document this two-month project, I have incidentally authored a masterclass in procrastination.
I have improved slightly since creating this, and there is a temptation to go back and re-do it better. I suspect it would take a further four years to document though, so you won't be hearing about it any time soon.
I shall end by saying that this is not the only old project I have on my shelf which I never documented. Stay tuned for more absurdities, only on mitxela.com!