Jump to content
Jet Set Willy & Manic Miner Community

Why was Manic Miner never properly optimised?


Recommended Posts

On 4/26/2024 at 10:59 PM, DigitalDuck said:

Making something run more efficiently doesn't have to mean speeding it up - it can be used to make the game run at a smooth speed (syncing to display refresh and counting frames) instead of speeding up when there are less enemies and slowing down when there are more, or add other more complex elements to the game.

That makes perfect sense. I think people who have tweaked the code usually did it to make the game run *faster*, though - or at least that's what they usually referred to or seemed to be aiming at. Hence my observation (paired with the often-heard complaints or comments about the difficulty level of various games).

Link to comment
Share on other sites

Would it help to know that usually I have ZXSpin set at either 105% or 115-120% ? As its (better) at slight speed increases compared to Specatulator.

With the increase in speed here I felt it was fine at 100% 🙂 I honestly don't think its too fast***

 

*** Running it without contention (rewrite needed) and it was a bit "fun" 😮 🙂 

Link to comment
Share on other sites

  • 2 weeks later...
On 4/26/2024 at 9:24 PM, jetsetdanny said:

One thing that has always puzzled me is why some people are so excited about speeding MM and JSW up (there have been various attempts to do it, some admittedly very successful)

I think the 'standard' MM and JSW run a bit too slow (honest thought) as with emulation I used to run them a bit faster. I do agree the third party edited ones are a lot harder and take into account (perhaps subconsciously) the erm "pace" ?

I've seen myself with my own attempt at it, how fast MM and JSW can run with just a simple modification to the main loop , aka no actual detailed optimisation of the code in itself and also the next gain -if- the 'game buffer' was moved out of contended memory (easy with some emulators)

I suppose when new on a real Speccy "back then" they seemed fast enough but these days we are spoiled a bit I suppose. 😕 I'm not sure. 🙂 

Link to comment
Share on other sites

  • 2 months later...

I'm rewriting my 2010 version and it's optimised, I will release source code once it is in a playable state.

It runs at 50fps even with the tune. Willy has 8 frames of animation instead of 4 but they will need adjusting. EDIT: Guardians only update at 25fps but if they had 8 frames of animation they could also update at 50fps, they are being redrawn each frame.

I'll be releasing a rolling demo this week (which is the attract mode from the original, just show all 20 levels, no Willy movement or collision code is done yet - which I expect will take less than 2 weeks)

Sneak preview 

hudstuff.gif

So you'll have to do a good job to beat that I think 🙂

 

+ No backbuffer or attribute buffer (draws directly to screen beating the raster)... saves at least 4K of memory

+ All the level data is currently only less than 3K (does not include graphics data though)... so can have at least 50 levels with an 8K budget for level data. I will also pack the levels into a bitstream (so something that only needs 5 bits only uses 5 bits) which should save around 25-50% on the data size.

+ With a 12K budget for level data we could be looking at MM and JSW in the same 48K of code perhaps.

- the code is large since it is written to run fast rather than care about code size (for stuff done every frame anyway)

 

EDIT: Thread about it on Spectrum Computing forum

https://spectrumcomputing.co.uk/forums/viewtopic.php?t=11662

 

I will write a level editor at some point as well (which will run on the Speccy, may require 128K, dunno), probably whilst waiting for more new graphics/auditioning extra levels

 

Edited by ParadigmShifter
Link to comment
Share on other sites

I'm interested to see this too. 🙂

I found a reasonable speed increase by a more efficient method of the four main copy routines and combined with (in my case) storing the source screen data outside contended ram also made a good increase***

 

*** To do the latter, I cheated as a test by disabling contention in emulation, but it gave me a good approximation of what it would do.

 

Must admit I'd not read that thread other than maybe a couple of posts when it first appeared as I'd assumed it was only about sprite designs nothing else. 🙂 

Edited by Spider
I can haz make typo ?
Link to comment
Share on other sites

I'm not using an off screen buffer at all, all my drawing is direct to screen.

Most of the draw routines use the stack to read the sprite data (so can read 2 bytes of data in 10T states which is as fast as you can read data on the Z80, much faster than LDI). I'm not using the stack to blit to the screen though, it turns out to be slower doing it that way because you have to keep reloading SP.

This is the body of the draw horizontal guardian routine anyway. The stack gets set to the graphics data (32 bytes) and HL points at the screen address of top left of the sprite

	    REPT 4
    pop de                                ; 10T    
    ld (hl), e                            ; 7T
    inc l                                ; 4T
    ld (hl), d                            ; 7T
    pop de                                ; 10T
    inc h                                ; 4T
    ld (hl), d                            ; 7T
    dec l                                ; 4T
    ld (hl), e                            ; 7T
    inc h                                ; 4T
    ENDR                                ; 
	    ; move to next cell down
    ld a, l            ; 4T calculate screen address to start of next 8 lines
    add #20            ; 7T add 32 to screen pointer
    ld l, a            ; 4T
    jr c, .addr_ok    ; 7T/12T jump if addition resulted in overflow (this means we are at next 64 line segment)
    ld a, h            ; 4T (nc)
    sub 8            ; 7T (nc) subtract 8*256
    ld h, a            ; 4T = 37T if jump not taken
.addr_ok            ; 27T if jump was taken
	    REPT 4, idx
    pop de                                ; 10T    
    ld (hl), e                            ; 7T
    inc l                                ; 4T
    ld (hl), d                            ; 7T
    pop de                                ; 10T
    inc h                                ; 4T
    ld (hl), d                            ; 7T
    dec l                                ; 4T
    ld (hl), e                            ; 7T
    IF idx < 3
    inc h                                ; 4T
    ENDIF
    ENDR                                ; 

I'm also drawing in a zigzag fashion which complicates the vertical guardian draw, I need 2 versions depending on whether the guardian is at an odd or even Y coordinate.

 

Edited by ParadigmShifter
Link to comment
Share on other sites

I also only save and restore the stack pointer once when rendering all the guardians, Willy, the keys, the exit and the solar power beams (which also use the stack to pop 2 entries off the erase/draw list at once - each entry is the attribute address to either set back to the background colour or set to the beam colour). More details about how I am doing the solar power beam in the thread at the Spectrum Computing forum.

 

So it's basically do this:

di ; disable interrupts while the stack pointer is changed

save sp

draw all the guardians in a loop

draw Willy

draw all the keys

draw the exit

draw the solar power beam if applicable

restore sp

enable interrupts again

Which means that I am unable to PUSH or POP any registers nor call any functions inbetween saving and restoring the stack pointer. So all the guardian draw routines are jumped to and they jump back to draw_next_guardian when they are done and such.

I will probably make the erasing of everything work in a similar way as well. (Well it does already, it has a list of stuff to erase - which is only if a guardian moved and if it moved to the right I only erase an 8x16 strip where it moved out of, and vertical guardians erase the 16xN rows they were previously covering. EDIT: So if they moved 3 pixels down they erase the 16x3 strip above them). I don't think there is much benefit of using the stack for that since it is just writing 0s to the screen. I erase everything I need to before drawing anything - otherwise the big evil priest in JSW would not work properly if I erase each guardian and then immediately draw it).

Erasing Willy is more complicated it needs to redraw the cells he is standing in front of (and it will also need to redraw any crumbly cells he is standing on).

Edited by ParadigmShifter
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.