I made a new App for the Multiface 3

Show us what you're working on, (preferably with screenshots).
User avatar
djnzx48
Manic Miner
Posts: 453
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: I made a new App for the Multiface 3

Post by djnzx48 » Sun May 05, 2019 5:49 am

Nice! I typed it in and it worked fine. If you want a few minor comments about your code:

You don't need to use CALLs so often. In particular, the CALL after the attribute check could be changed to a JR NZ that skips over the LD (HL), $F3 instruction. The other CALL at $2266 could also be inlined. Subroutines are great for organizing your program, but they are slower than simple jumps, so it can be better to avoid them whenever practical, especially in tight loops like this one.

Also, you can try to limit the number of redundant memory accesses. When checking the colours, the attribute value is read twice for example. Here's another version which is very slightly quicker.
SpoilerShow
ld a, (hl)
and $3f
ld b, a
rra
rra
rra
xor b
and 7
Keep going on these programs. I like seeing the Multiface used to extend functionality like this.
0 x

User avatar
druellan
Manic Miner
Posts: 362
Joined: Tue Apr 03, 2018 6:19 pm
Location: Argentina
Contact:

Re: I made a new App for the Multiface 3

Post by druellan » Sun May 05, 2019 10:38 pm

Nice x2. I didn't type it, but copy&paste the code in ZX-Editor (yep, I'm lazy).
If you don't mind I'm probably going to stream my findings at some point, using you tool live :)
0 x
Un buen día me puse a probar juegos de ZX Spectrum a ciegas en Youtube, terminó siendo:
📺 Retroarqueología en Youtube

User avatar
djnzx48
Manic Miner
Posts: 453
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: I made a new App for the Multiface 3

Post by djnzx48 » Mon May 06, 2019 5:55 am

Is there a way to batch download .scr loading screens from somewhere? I'd like to try searching for hidden elements on a larger range of screens.
0 x

User avatar
druellan
Manic Miner
Posts: 362
Joined: Tue Apr 03, 2018 6:19 pm
Location: Argentina
Contact:

Re: I made a new App for the Multiface 3

Post by druellan » Mon May 13, 2019 12:45 pm

djnzx48 wrote:
Mon May 06, 2019 5:55 am
Is there a way to batch download .scr loading screens from somewhere? I'd like to try searching for hidden elements on a larger range of screens.
If you're still looking for this I can try and extract all the .scr from a WOS mirror, many files have the .scr inside the ZIP.
0 x
Un buen día me puse a probar juegos de ZX Spectrum a ciegas en Youtube, terminó siendo:
📺 Retroarqueología en Youtube

User avatar
druellan
Manic Miner
Posts: 362
Joined: Tue Apr 03, 2018 6:19 pm
Location: Argentina
Contact:

Re: I made a new App for the Multiface 3

Post by druellan » Mon May 13, 2019 12:50 pm

I was playing with this the weekend, and works pretty well, but for some reason was a pain to make it work on ZXSpin/FUSE, I ended using EightyOne.

Image

Image

@Hobes128 one small improvement, if you're still working on this, could be to pause or halt the execution of the code, since many games just plain reset, and you need to quick-pause the emulator to have the chance to reed something.
0 x
Un buen día me puse a probar juegos de ZX Spectrum a ciegas en Youtube, terminó siendo:
📺 Retroarqueología en Youtube

User avatar
djnzx48
Manic Miner
Posts: 453
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: I made a new App for the Multiface 3

Post by djnzx48 » Mon May 13, 2019 1:03 pm

druellan wrote:
Mon May 13, 2019 12:45 pm
djnzx48 wrote:
Mon May 06, 2019 5:55 am
Is there a way to batch download .scr loading screens from somewhere? I'd like to try searching for hidden elements on a larger range of screens.
If you're still looking for this I can try and extract all the .scr from a WOS mirror, many files have the .scr inside the ZIP.
Thanks, that would be helpful. I did think about trying that mirror but it was too large to download the whole thing.
0 x

Hobes128
Berk
Posts: 10
Joined: Sun Apr 14, 2019 5:13 pm

Re: I made a new App for the Multiface 3

Post by Hobes128 » Thu May 16, 2019 10:22 pm

Sorry, it's been a while since i checked in here, and it seems you guys sent a load of great replies :-) I'm really happy that you've actually found my daft little routine interesting, and taken the time to play with it and even provide feedback, so thank you so much for that!

Guesser - Thanks for the encouragement. I think you're absolutely right really. The fact that i had a bit of fun with it, and learned quite a bit about machine code programming is what it was all about really.


djnzx - Thanks so much for the coding tips. This was the 1st machine code program I've ever attempted so your constructive criticism is very much appreciated! Your code looks much more efficient and elegant than my own! It also occured to me that avoiding calls and absolute jumps has the advantage of making the code portable (i.e. using Jump relative instead, i could load the routine into any memory address, and it would still run). This was something that caused me a bit of a saw head when i was working on it, as the code needed to run from the multiface ram which pages in over the top of the spectrum rom at something like 8100 (I don't remember the exact address right now). Because I hand compiled the code (i used a google drive spreadsheet to work out the dec values i needed in the data statements!), I had to remember that even though I was poking it into quite high memory addresses (~40000) I needed the call commands to point the the low addresses so it would actually work once i'd LDIR'ed it into the multiface RAM. I crashed the spectrum emulator lots of times before I actually got all the addresses right.

Anyway, I've given up on hand assembling code now, and moved onto using the assembler build into zxspin. This does give me a new problem though…

In the assembler, I tend to start my code with ORG 32768 (or similar) to tell the assembler where in memory I want the code to end up. When i'm writing multiface routines, I have to ask the assembler to dump the code into high memory, as it can't write it directly to the MF ram (as it isn't paged in at the time i assemble the code). But, if i use any jumps or calls (i.e. if my routine is not portable) I need some way to ask the assembler to dump the code in a high memory address, but set the absolute jump/call memory addresses based on the low memory address i'll actually be moving the code into before running it. I have no idea how i would tell the assembler that i needed to do that. Maybe it’s not even possible?
I think in the short term, the best solution is probably to just make sure all my routines are fully portable by sticking with relative jumps.


druellan - It really made me smile to see you actually captured some hidden loading screen messages with my code - thats really cool! And yes, I would love for you to use my code in a stream so feel free to do that :-)

Your suggestion to add a pause at the end of the routine would make sense i guess (i.e. press any key to continue running the game). After doing a bit more reading, i realised that i didn't really put much effort into making sure the game could continue running after i exit my routine. That was mostly because i was focused on loading screens, and i figured that triggering an NMI in the middle of the tape loading would almost always cause a crash (or at least an r-tape loading error). When you trigger my routine from within a running game though, there's no reason it shouldn't continue running normally afterwards (all be it with a bit of screen colour corruption). To make that a bit more reliable I should really have some code at the start to push all the registers to the stack, then disable interrupts, then at the end i should have the code wait for a key press, then recover the registers from the stack, and enable interrupts before returning to the game code.
<strike>Also I think I'm using the wrong return command at the end of my routine. I used 201 which is just RET, but i realised recently that there’s another op code for RETI (i.e. return from interrupt) so i guess i should be using that one really. (I have no idea what difference it makes though to be honest).</strike> Actually, the ret command in my code is just returning from one of my pointless sub-routines. I'm acutally using RST 0 to exit my routine, which i think is correct?




I've been pretty busy with other stuff lately, so I've not had much time to keep tinkering on the spectrum, but your replies have definitely inspired me, so i'll try and find time sometime soon, and will try and post an improved version.
1 x

Hobes128
Berk
Posts: 10
Joined: Sun Apr 14, 2019 5:13 pm

Re: I made a new App for the Multiface 3

Post by Hobes128 » Thu May 16, 2019 11:15 pm

Hmmm, could you please help me understand your code djnzx...

ld a, (hl) ; this loads the attribute data i want to check - straight forward.

Spectrum attributes are in the format...
Bit 8 = flash on/off
Bit 7 = Bright on / off
Bits 6, 5, 4 = Ink Colour
Bits 3, 2, 1 = Paper Color
So let's imagine this specific attribute square is set to have blue paper and blue ink with no flash and no bright, so it's value will be...
FB INK PAP
00 001 001

and $3f ; 3F in hex is 00 111 111 in binary, so and'ing with this mask strips off the bright and flash data, leaving just ink and paper in the A register
ld b, a ; Store that result in the B register, so b now contains 00 001 001
rra
rra
rra ; rotating A right three times means that the paper colour is lost, leaving just the ink colour in bits 3,2,1 with all other bits are zero so now a = 00 000 001
xor b ; this is the bit i'm struggling to get my head around...

This command will xor B with A and store the result in A, so...
A = 00 000 001
B = 00 001 001
Result = 00 001 000 = A
is that right?

and 7 ; 7 = 00 000 111 so this and mask will set all but the last 3 bits to zero, so now A = 00 000 000

So now if i do CP B, i'm comparing A = 00 000 000 with B = 00 001 001. Because they don't match one another, the zero flag won't be set, so my JR NZ, wont trigger.
I'm sure I'm showing up my poor coding skills here, but I must be misunderstanding something - please help.
0 x

User avatar
djnzx48
Manic Miner
Posts: 453
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: I made a new App for the Multiface 3

Post by djnzx48 » Thu May 16, 2019 11:39 pm

If you want to assemble your code at a different address from where it's run from, most assemblers support something like this. For example, it's .PHASE in Pasmo. I can't remember if the ZX Spin assembler has this feature though.

For the return instructions, there are three different opcodes (not counting conditional returns) - RET, RETI and RETN. They basically all work in the same way, but the specific one you use can affect hardware peripherals attached to the bus. A common example is the AMX mouse - it uses maskable interrupts to send mouse movements and button clicks, and if you use a regular RET to return from your mouse routine then mouse input will stop working!

RETI means 'return from maskable interrupt' and it's what should be used for IM2 routines (the ones you set up with a 257-byte jump table). In your case, you may need to use RETN (return from non-maskable interrupt) as it may be an NMI that triggers the Multiface? I suppose it depends on where your code returns to - the Multiface routine or somewhere else.

RST 0 is probably OK to exit your routine, but it depends on how the stack is balanced.
Hobes128 wrote:
Thu May 16, 2019 11:15 pm
Hmmm, could you please help me understand your code djnzx...

ld a, (hl) ; this loads the attribute data i want to check - straight forward.

Spectrum attributes are in the format...
Bit 8 = flash on/off
Bit 7 = Bright on / off
Bits 6, 5, 4 = Ink Colour
Bits 3, 2, 1 = Paper Color
Just a note - it's more common to refer to these bits as zero-indexed. FLASH is usually denoted as bit 7, BRIGHT is bit 6, PAPER is bits 5-3 and INK is bits 2-0. (I think you've got INK and PAPER mixed around in your example.) This mirrors their use in the Z80 BIT/SET/RES instructions for operating on bits.

I didn't actually test the code I posted, so it may be wrong ;) Your explanation seems mostly correct. However, the CP B should be omitted in this version and the JR placed straight after the AND 7. This is because AND sets the flags based on the result of the computation, and when the INK and PAPER match, the result will be zero, and the zero flag set. Then the jump should work correctly.

Also, with RRA the bits that fall off the end aren't completely lost - they go into the carry flag which is then shifted into the topmost bit during the next RRA instruction. (You can think of RRA as a 9-bit rotate, with the 9th bit being the carry flag.) So in your example, after the RRAs the A register should equal 00100001. In this case it doesn't matter as the extra bits will be masked off anyway.
1 x

Hobes128
Berk
Posts: 10
Joined: Sun Apr 14, 2019 5:13 pm

Re: I made a new App for the Multiface 3

Post by Hobes128 » Fri May 17, 2019 12:05 am

Thanks for the very detailed reply djnzx.

The multiface is indeed triggered via a non-mascable interuprt, but the last page of the manual states...
"to return from your program, to the program you stopped, use RST 0" so i just did what the manual said ;-)
http://www.worldofspectrum.org/pub/sinc ... Manual.pdf

Anyway, it's bed time for me now, but i'll come back to your post soon, and try and fully digest it.
Thanks again for the help!
0 x

Post Reply