iain's development activities. May contain z80, Cocoa, or whatever.
29 January 2023
I put a poll up on Mastodon for what bit I should do next and the results were for character movement. So before I start planning on how to do that, I thought I’d tidy up some of the graphics code a bit. The tiles and the character sprites were all using the same tilesheet, so I split them out, and now that they’re generated separately I don’t have to shift the tile data. That saves about 1.5k memory, and slightly reduces the t-count for drawing the tiles. Every little helps. I also filled the background colour in the correct colour, so no more pink.
Now, onto character movement. Guess the first thing to do is get my PS4 controller working with Sim Coupé.
27 January 2023
Final touches at the moment to the scrolling code was to move the map data and the drawing code into the graphics page so I didn’t have to switch pages and change stack twice for every tile. Now it changes to the graphics page when it wants to draw the map and back to the main page once the map is drawn. This is about 150t less per tile. So it’s worked out as a pretty big performance boost.
And here it is - it doesn’t tear on the screen just in the gif, thanks giphy capture.
Next I think I’ll tackle either animation or character movement
25 January 2023
Victory. Solved the memory corruption bug.
The problem was in the interrupt handling code which I borrowed along with most of the rest of the basic setup code to get things going from Howard Price’s Flappy Bird clone.
The interrupt handlers are stored as a linked list of three values: the line number for a line interrupt, the address of the handler, and the address of the next entry in the list. As I’m not using the line interrupts (yet) this list is a single entry that points back to the start of itself.
At initialisation int.reset
is set to the start of this linked list, and after every interrupt the list moves to the next value and sets two memory locations int.jump
to the address that contains the address of the jump handler in the list and int.next
to the address that contains the address of the next entry in the list. Because there’s only one entry in this list these values never change. I wonder if the extra level of indirection here is going to be important later.
The problematic code in question is this routine
@check_int: ; Make sure this is the frame interrupt
int.status: ld a,0
bit FrameIntBit,a
jp z,@+save_regs
@reset_int_manager: ; If not, reset to start properly next time
ld hl,(int.reset)
ld (int.next),hl
ret
What this routine does is load the status value into a
(the 0 has been replaced at runtime by the actual value) and checks if the status is a frame interrupt. If it is not, it resets the int.next
to point at the start of the interrupt handler linked list to go back to the start. But that’s not what int.next
is supposed to contain: int.next
is supposed to contain the address of the memory containing the address, not the address itself.
So the correct thing is to set int.next
to int.reset + 3
@reset_int_manager:
ld hl,(int.reset)
FOR 3, inc hl
ld (int.next),hl
The only question I have is why bit FrameIntBit, a
is failing and putting us into the @reset_int_manager
code in the first place. There’s no non-frame interrupts set, and the status value is FF
which my vague reading of the Sam Coupé development manual would be invalid. So, I dunno. It works now and that’s all that matters.
The hardest part of it was to find a simple way to reproduce the bug: at one point the reproduction steps were to add a breakpoint and skip it 157 times, but even then there were still thousands of instuctions to step through before the bug triggered. Once I worked out the bug was happening inside the interrupt handler, I added a frame interrupt breakpoint and could step the code there and saw funny things happening after the @reset_int_manager
code ran. Then lots more staring trying to work out how the indirection worked.
I could delete this code and hardcode it all, but I like having the flexibility of having different interrupt handlers available if I need them. Now I can work on something more interesting now I have scrolling working again.
22 January 2023
Been tracking down a weird memory corruption bug related to the interrupts. Because of the use of pop/push to copy the tiles from memory to the screen, I’ve disabled interrupts while that is happening. This shouldn’t really be necessary as the interrupts use their own stack, but if I don’t disable interrupts then memory corruption appears on the screen, which is confusing due to the separate stack.
In the working version, if I disable interrupts once for the whole map drawing then it works fine, if I only disable interrupts around the code that manipulates the stack for each tile then the interrupt handler eventually gets messed up and jumps into invalid memory.
Been getting to know the SimCoupe debugger much better, but haven’t tracked the bug down yet.
20 January 2023
My time has been split between various different things, so I’ve not had as much time as I’d have liked to work on the scrolling, but what time I did have has been spent trying to fix niggling little bugs in the various different odd and even scrolling routines, sharing code and things like that. I got one pixel per update working, but it still felt slow and the code was very complex with the start of each routine having to set up the different values and subroutines necessary to share some code with lots of SMC code.
So, I had a think and put in some profiling code, and I think it was taking a frame and a half to draw the screen each time. If this code is to ever become something more than an experiment, if it would turn into a game, then that’s far too much, because we still need all the other sprites drawn, all the animation handled, all the game logic handled, keyboard processing, music and sfx processing… and there doesn’t seem like there’s time left for that.
The outcome is that I’ve simplified the code so now it only draws the even frames: 2 pixels per update. This makes things so much simpler, the odd offset code means it never needs to merge nibbles from screen with sprite nibbles and one path for drawing every time. It also reduces the memory usage as the map tiles don’t need to be shifted. It also makes the scrolling move at double speed, so I can make it only update the screen once every 2 frames to get the same speed as 1 pixel per update and suddenly I’ve regained an entire screen refresh for logic stuff.