I am happy to have passed the midterm and can continue onwards with the project.
This past week
This week has been mostly about tidying some things up (mostly with regard to git) and getting started with the next steps. Something that I have needed to keep better track of are my git commits. I have been bundling too many things into single commits, and the last commit I had done was a prime example of this. Take a look at the commit history for the branch here. Starting at “IMMORTAL: Minor formatting in disk.h” and ending with “IMMORTAL: Add SpriteFrame enum“, was previously all one commit (I may be off by a commit, I don’t remember exactly how many were in this one). So I broke up the commit and was able to ultimately rebase it to the latest build of ScummVM, so as to keep a linear history. I decided what should be separate commits, like the formatting in disk.h and minor change in immortal.cpp, and redid them. This took longer than I wanted, because I still struggle with using git effectively. I think I’m getting better with it, but it is something I must continue to work on so I don’t have to focus on it instead of the code as much. Once this was done, I was able to add the new commits I had been wanting to add for a while to finally finish adding the logic skeleton and clean up that ‘layer’ of the engine. This involved adding story.h (an interesting part of the engine, but I’m sure I’ll discuss that more in a future post), and quite a few new functions to kernal.cpp and logic.cpp. As well, I added the outline of drawChr.cpp, which is not needed quite yet, but it is contains a number of functions related to drawing the screen that are referenced in the new functions added to kernal.cpp.
Where are things now
As it is now, the new code for level.cpp has not been added as a commit. Although I have added much of the level skeleton (which is smaller than I expected actually), I have been a little bit stuck on trying to understand a particular couple of macros used in the source code. I’m not used to the symbols and syntax used, as they are specific to the compiler and not the 65816. One of them actually helped me realize something important about the functions I was diving into however, which is that everything in the layers beyond kernal+logic (and indeed, much of logic itself) is running in the 8bit mode of the 65816. This may be because the game was being written with the NES port in mind from the start, and although the NES and the Apple IIGS use processors within the same architecture family, the 65816 (as the name would suggest, 65xxx with 8bit and 16bit modes) can run in either 8 or 16 bit modes, while the NES is limited to 8bit. As a result, code that is running in 8bit mode can be easily adapted to the NES, but anything that makes use of 16bit sized data, would need to be changed. This may not be the reason (or at least not the only reason), but it would explain why the game uses a macro called ‘lslw3’ at one point. The name stands for Logical Shift Left for Word * 3. And if you know your 80s assembly, a LSL is the same as an ASL. This is why the 65816 only has ASL and LSR, no LSL (ASR is another matter, and in fact they had a macro for this one as well, it’s quite clever actually, utilizing a PHA/PLA and ASL, followed by a ROR to copy the carry flag that is set from the ASL). So the question I was asking myself was, why on earth would they need a macro to achieve ASL #3? The answer is that ASL #3 will certainly do the job on a given word (which is a double byte on the 65816 for reference) in 16bit mode, but not in 8bit mode. Instead, if you want to get the same result on a word but can’t directly manipulate all the bits of a word at once, you have to be creative. In this case they chose to make use of what they could do, which was ASL and ROL, in a pretty neat way. Essentially, just like the ASR macro, it makes use of the ASL pushing the highest bit of the byte off the end and into the carry flag. Normally the carry flag is used in arithmetic, but it can also be used like it is here, as a way to pass a single bit from one byte to another. This is done with the ROL, which Rotates the byte Left by a given number of bits. However that description doesn’t quite explain it, because it doesn’t rotate with only the bits in the byte, it rotates with the carry flag. A ROL will use the carry flag for the new lowest bit, and send the old highest bit into the carry flag. This is what allows the macro to work. An ASL on a byte will send the highest bit into the carry. Then, a ROL right after will take that carry flag holding the bit, and use it as the low bit. This means that ASL A : ROL B, ASL A : ROL B, ASL A : ROL B, will leave you with the same data as if you did ASL #3 on the entire word as once. Pretty cool stuff. It looks like this:
Okay, that was a bit of a detour, but the point is that everything is in 8bit post Logic, which makes the code a little more annoying to work with, but it’s good to know that going in. The macros that are still giving me trouble are more to do with this compiler syntax, but I will sort it out and continue onwards. I hope to have the outline of Room done soon, which will finally bring together the files I have waiting to be included in a commit, but are not quite relevant yet.
Next up, finishing Level, working on Room, and then likely back to kernal to start getting drawing routines working so that we can see what’s going on in Room, before we get too deep into it.