Jump to content
Jet Set Willy & Manic Miner Community

Some Jet Set Willy technical questions


crem

Recommended Posts

You can speed up the Copy routines by 25% by changing to inline LDI - But that necessitates knowing z80 and the writing the code.

You can also delete
#89ad (3 bytes) animate willy along the bottom(graphics)

#89ef (3 bytes) animate conveyors(graphics)
#8a3a (3 bytes) print the time(text)
#8a46 (3 bytes) print items(text)
#944e (3bytes) animates graphic item/object

#8e20 (2bytes) and #8e23(2bytes) remove the jumping/falling  sfx


Plus make sure the tune is turned off.  When doing the most basic operation a few million times needlessly, It all starts to add up to hours.

Link to comment
Share on other sites

19 hours ago, Norman Sword said:

It all starts to add up to hours.

Thanks a lot, that helped a lot!

Without changes I could generate 2450 frames per second.

With 3 LDIRs replaced with NOPS, 3950 fps.

With your changes (and LDIRs replaced) -- whopping 5130 fps!

 

With LDIR being so slow, I'm thinking of changing the emulator to just run memset() for LDIR instead of emulating it "properly" (with fetching instruction on every iteration, incrementing all the registers and updating t-state count as it runs). Maybe then it would get even higher.

For now I'm not sure if that's worth the time to implement it, so any other micro-optimizations like that would be appreciated.

Link to comment
Share on other sites

The LDIR instruction was written to enable interrupts to occur whilst the instruction takes place. Because the Z80 system uses interrupts for a host of external devices as well as synchronisation and housekeeping tasks.  Enabling interrupts while an LDIR is taking place was important and that is why the LDIR is so slow. After every move of one byte the program counter moves back and reloads the LDIR instruction and repeats the one byte move.

JSW does not use the interrupts and therefore any external  block move sequence that can be used in place of LDIR will have absolutely no impact on what the game does.

So if you externally move the block in place of the LDIR doing it. The game will take no notice and just carry on. I don't think it even cares about T-states - used mainly for the refresh register and again not used.

Edited by Norman Sword
Link to comment
Share on other sites

5 minutes ago, Norman Sword said:

The LDIR instruction was written to enable interrupts to occur whilst the instruction takes place. Because the Z80 system uses interrupts for a host of external devices as well as synchronisation and housekeeping tasks.  Enabling interrupts while an LDIR is taking place was important and that is why the LDIR is so slow. After every move of one byte the program counter moves back and reloads the LDIR instruction and repeats the one byte move.

JSW does not use the interrupts and therefore any external  block move sequence that can be used in place of LDIR will have absolutely no impact on what the game does.

So if you externally move the block in place of the LDIR doing it. The game will not care less.

One more case where it could matter is if LDIR overwrites the memory of the instruction itself. Hopefully that doesn't happen in JSW though.

Also I'm a bit afraid that after my replacement it with a simple memcpy, I'll leave cpu in wrong state (e.g. forget to set flags which would be needed afterwards... But I think JSW doesn't rely on register/flag values after LDIR instructions). I'll give it a try anyway.

Do you think something like that would be good enough (e.g. ignoring all flags altogether)?

uint16_t hl = GET_HL();
uint16_t de = GET_DE();
uint16_t bc = GET_BC();
memmove(&memory[de], &memory[hl], bc);  // Can overflow if goes over 0xffff but should not happen in JSW
SET_HL(hl + bc);
SET_DE(de + bc);
SET_BC(0);

The current emulation code also does some magic with Y, X and V flags, and I never even heard about those Z80 flags. 🙂 Hopefully it's ok not to touch them. Although maybe it makes sense to reset S/Z/C.

Link to comment
Share on other sites

Totally offtopic for this thread, but I'm just curious.

Usually when talking about memory addresses written in hexadecimal form, I can see 3 prefixes are used: #, $ and & (#DA78, $DA78 and &DA78) (Also there's 0xDA78 and DA78h but those I know where they come from).

I wonder which of those prefixes is more common and where they all come from?

I know that in Pascal $ is a prefix for hex numbers, but weren't there a Z80 assembler which used that?

Link to comment
Share on other sites

Been writing z80 since 1982 and I have witnessed many changes. 
Originally nearly all hex was 01234h, but the format used would be forced upon you by whatever assembler you used. The assemblers did not all use the same syntax.

At present I use whatever comes to hand, and yes I will use every notation 1235h,#1234,$1234,0x1234 and my present assembler is happy with all of them

After writing a lot of code it becomes easier to use #123a for no other reason than it becomes tedious when editing a lot of hex data to remember to make sure that any hex data is preceded by a non hex digit. eg whilst 1fffh is valid d000h is not and needs changing to 0d000h. When a lot of data is being manually edited and the assembler keeps throwing out the data. It becomes easier to use a format that always complies with the syntax  so changing #8000 to #c000 will always assemble whilst the edit of 8000h to c000h is a syntax error.

 

Link to comment
Share on other sites

I was used to seeing the # prefix and sometimes a 'h' suffix. Starting on the Spectrum platform in 84, there was not really any built in hex facility.

Those who perhaps started coding on the Acorn machines may be more used to & however. For instance typing PRINT &C9 will return you with an answer of 201 , which is correct as #C9 is 201 in decimal. This is also accepted for the Amstrad CPC range too.

Can't recall which but one emulator in the "insert pokes" section seems to prefer a $ prefix for hex values too, just to add to the mix!

Personal preference is simply #xx as its quite easy to distinguish between:

LD A , #80

JP #7530

Decimal of the above:

LD A , 128

JP 30000

I do realise I'm slightly contradicting myself as today I did provide some 'pokes' using a different prefix however the prefix suited the platform they were intended for! , but in general if I was writing a post or such I'd rather just use #value, its easy to type and does not or should not lead to any confusion.

 

Link to comment
Share on other sites

  • 2 weeks later...

Quick question.

I have a breakpoint at address #8C01, and generate various playthroughs in JSW, and it sometimes happens that number of lives decreases (and guardian cycles seemingly reset) without hitting that breakpoint.

I'm fairly sure it's bug in my code, but just checking, maybe I missed something in JSW code... Could you think of some ways of this happening in JSW? (number of lives decreases without hitting breakpoint at #8C01).

That happens in the very first room, "The bathroom".

Thanks!

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.