News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_ervin

3D-Maze (written using ccz80 and machine code)

Started by ervin, 06:11, 11 December 09

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ervin

Hi all.

I've finally fulfilled a 24-year dream of writing a machine code program for the Amstrad.
I've converted Nigel Sharp's 1985 3D-Maze type-in game (Amstrad Computer User, October 1985).

I initially wrote it using the fantastic ccz80 compiler, by converting the BASIC program line-by-line.
But the aim was to then change as much of it as possible to machine code.

It has been an excellent experience (although no one seems to understand why on earth I would want to write a program in machine code for a long-dead platform ;D), and I'm pretty chuffed with the results.

For more info, please have a look here:
http://ccz80.foroactivo.com/ccz80-f1/3d-maze-game-for-amstrad-cpc-t28.htm#67

The dsk file (containing both the original as well as my re-write) can be downloaded from here:
http://www.cpcwiki.eu/imgs/f/f1/3d-maze.zip

Now I can finally get back to the ACU Type-ins project!

redbox

Hey, that's really cool!

I had a play in WinApe and it really is fast.  I had some graphical glitches over the right panel whilst playing though (not tried it on a real CPC yet though)...?

And how did you write the code for the display to be double-buffered?

fano

Very nice work , it is now very pleasant to play  :D
"NOP" is the perfect program : short , fast and (known) bug free

Follow Easter Egg products on Facebook !

ervin

Thanks guys.
I really enjoyed writing it, and I've got some more ideas I'd like to put in when I get some time.

redbox - what sort of graphical glitches are you getting?
Are you running winape in cpc6128 mode or cpc464 mode?
The program was written using the cpc6128 library for ccz80.

Can you upload a screenshot of the sort of thing you are seeing?

The double-buffering was very tricky, as this is my first machine code game, so I kind of stumbled around and tried lots of things by trial and error.

Here's a rough summary of how it works:

- I use the IYL register (IY low byte) as a flag, so that the program always knows which "screen" is being printed and drawn to.

- the clear screen routine (using the famous stack push method for fast memory filling) checks IYL.
if IYL is 0, &8000 is subtracted from the normal screen address, and then that new location is cleared.
Otherwise the normal screen location is cleared.
ie. I am using &4000 and &c000 as the screen memory locations.

- once all plotting/drawing has been completed, IYL is checked to see which screen buffer is being drawn to.
I then switch the location of the screen memory from &4000 to &c000 (or vice versa) to display the buffered screen data, using SCR SET BASE.
Then I switch the screen-buffer-to-draw-to using SCR SET POSITION.
So if the buffer being displayed is &c000, SCR SET POSITION points to &4000, and vice versa.

I hope that made sense!

I'm sure there are several improvements that could be made to the logic I've used (and I'd love to learn as much as I can to improve it), but for a first effort I'm pretty pleased.  :)

redbox

Quote from: ervin on 00:05, 14 December 09
redbox - what sort of graphical glitches are you getting?

I just tried to replicate it to take a screenshot and couldn't...  I think probably the problem was the PC I was using at the time (my crappy work one) and not your program / WinApe - sorry to worry you!  :)

Quote from: ervin on 00:05, 14 December 09
The double-buffering was very tricky, as this is my first machine code game, so I kind of stumbled around and tried lots of things by trial and error.
Here's a rough summary of how it works:

That's a good technique and explanation, thank you.  I will try and incorporate it into my program.

We were discussing the stack push method for drawing to the screen RAM in the programming thread, so it's good to see someone else recommending it!

ervin

#5
Yes, the stack push method seems to be considerably quicker than the ldir method.

I actually stumbed across the stack push method by accident after I found this article about the spectrum version of Starion.
http://www.users.globalnet.co.uk/~jg27paw4/yr15/yr15_36.htm

Have a look at the Hacker's Guide section near the bottom of the page. Very interesting.
That lead to more research about the technique, until I was able to implement it successfully.

Incidentally, I'd like to change the way the program works.
I'd love to remove the need for double-buffering, so that I can reclaim 16K and add all sorts of ideas into the game.
The best way I can think of to do this, and to not have any flicker, is to clear one pixel row, and then plot the new frame's pixels just for that one row.
Repeat this for each pixel row (ie. 200 times), so that the entire new frame is painted onto the screen from top to bottom, in one swipe.

But I just can't figure out how to do this.
I'm leaning towards this technique:
- clear screen at start of game
- draw first frame
- player moves forward

- instead of clearing the screen, repeat this 200 times:
- plot black pixels over the lit pixels in a row
- then plot the new frame's pixels for that row

This technique would need some way of storing the old frame's lit pixels for each row, and the new frame's lit pixels for each row.

But it's proving very difficult to figure out!

redbox

Quote from: ervin on 01:38, 14 December 09
This technique would need some way of storing the old frame's lit pixels for each row, and the new frame's lit pixels for each row.

This is very similar to what I am doing for my program (which is an animation) and I am doing it using look-up tables.  However, it uses loads of memory and wouldn't be suitable for your project.

You could try and use tiles to draw the screen area?  Each one could be stored and only the ones that change need to be drawn for that frame.  I've drawn an image to show you roughly what I mean...!



Also, you could have some look-up tables to plot the differences between the most common frames which could speed it up a bit?

ervin

Very interesting idea!
Thanks for that.

I'll give it some thought and see if I can think of a way to implement it.

Axelay

Quote from: ervin on 01:38, 14 December 09

Incidentally, I'd like to change the way the program works.
I'd love to remove the need for double-buffering, so that I can reclaim 16K and add all sorts of ideas into the game.


If colour is not too important, what about reducing the screen to 2 colours only and using hidden inks?  Then all you'd need to do is draw your new frame as normal, but XORed over the visible frame with the new frames ink matching the background.  When complete you make it's ink visible, the old frames ink to the background colour, then redraw the previous frame, again using XOR, to clear it ready for the next frame.

If you are able to change the screen dimensions (I guess probably not if using screen related firmware calls) you could perhaps make the screen narrower and taller and put the map and text under the maze, and use interrupts to still have 4 colours on the status/map display part.

Another thought.  If it is not possible to be compatible with the 464, what about using the 6128s extra memory, assuming you are not already using it?

ervin

Thanks Axelay - some great ideas there.

Can bank switching be used for a double-buffered display?
would there be a speed hit when switching banks?

arnoldemu

Quote from: ervin on 07:23, 15 December 09
Thanks Axelay - some great ideas there.

Can bank switching be used for a double-buffered display?
would there be a speed hit when switching banks?
no. amstrad video hardware can only fetch from main 64k.
But you can store your code in the extra ram :)
No, there is no hit to switch banks.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ervin

#11
Ah, I see. Thanks.

A few more annoying questions if I may...

I'm trying to figure out how to switch banks and then map the contents of a bank back to &c000.
How would I do this?

Would I use the z80 OUT command?
I don't understand this command at all - I've been trying to research it but I just don't get it.

Or would I use the firmware jumpblock KL BANK SWITCH?
If so, what would I set register A to?

It all sounds like the ideal solution, but I just can't get my head around it.

Even just storing (in the extra 64k) the addresses of the pixels to plot would be really helpful, but even that is beyond me at this stage.

Axelay

Quote from: ervin on 23:55, 15 December 09
Ah, I see. Thanks.

A few more annoying questions if I may...

I'm trying to figure out how to switch banks and then map the contents of a bank back to &c000.
How would I do this?

Would I use the z80 OUT command?
I don't understand this command at all - I've been trying to research it but I just don't get it.

Or would I use the firmware jumpblock KL BANK SWITCH?
If so, what would I set register A to?

It all sounds like the ideal solution, but I just can't get my head around it.

Even just storing (in the extra 64k) the addresses of the pixels to plot would be really helpful, but even that is beyond me at this stage.

I'm afraid I cant help you on using firmware to do the bank switching, I've never used it.  But using out commands, it's as simple as:


; do bank switch
    ld bc,&7fc5 ; this swaps extended memory into &4000
    out (c),c
; get something from the extended memory
; for example ...
    ld hl,&4000
    ld de,&1b40
    ld bc,2304
    ldir

; return to base 64k config
    ld bc,&7fc0
    out (c),c


That said, I'm not using firmware with that code.  You might need to bear in mind what the firmware is doing, if you page something in over the screen memory for example.

The 0 and 5 at the end of the LD BC instructions relate directly to the configuration numbers you can find under documents, amstrad cpc ram paging, at this site:

http://www.cpctech.org.uk/

Hope arnoldemu doesn't mind me linking that, but it explains the ram paging much better than I could!

ervin

Excellent, thanks very much for that info.
I'll see how I can incorporate it.

Did you use that technique in the fabulous Star Sabre?

redbox

Yeah, this is all helpful for me too... I've already run out of memory for my demo and need to use the extra 64k!

ervin

Another question about ram paging...

If I switch 16k from the extra 64k into &4000, will any current contents of &4000 to &7fff be swapped to that bank in the extra 64k?

And vice versa?

If so, how do I then refer to what was initially at &4000, without swapping back?
In other words, can the current contents at &4000 be used at the same time as stuff in one of the extra banks?

fano

#16
Quote from: ervin on 02:25, 17 December 09
Another question about ram paging...

If I switch 16k from the extra 64k into &4000, will any current contents of &4000 to &7fff be swapped to that bank in the extra 64k?
This is not really swapping but branching , the extra RAM page replaces 'logicaly' regular RAM it until you swith another.

Quote from: ervin on 02:25, 17 December 09

If so, how do I then refer to what was initially at &4000, without swapping back?
In other words, can the current contents at &4000 be used at the same time as stuff in one of the extra banks?
there are some other combinaisons in rmr to allow to connect differently extra RAM like #C1 that connect extra RAM 4 at #C000 address.

Longshot made very clear scheme about RAM paging there : http://cpcrulez.fr/coding_logon40.htm (sorry article in French but scheme are very well done)

I'd recommend you article about gate array/pal at grimware.org
"NOP" is the perfect program : short , fast and (known) bug free

Follow Easter Egg products on Facebook !

ervin

WOW - that article at grimware.org is AMAZING.
Thank you for the link.

I'll have to get the French article translated.
That website looks really good, but I haven't really tried to use it in the past, as I can't speak French.

Axelay

Quote from: ervin on 05:26, 16 December 09
Excellent, thanks very much for that info.
I'll see how I can incorporate it.

Did you use that technique in the fabulous Star Sabre?

Yes, that code was from Star Sabre.  Well, an early version of it!  That was copying the next levels background graphics to main ram, when I was simply treating the extra ram as 'fast disk'.  But then I read about the video display 'ignoring' the extra ram, and there was one 16k bank that can be mapped to either &4000 or &c000, right where the two screens were in main ram.  So I put the current level graphics & map in that bank, and mapped it over the currently visible screen every frame so it could be written to the current 'work' screen.  Cost a little extra overhead in working out pointers, but saved a lot of memory in main ram.

ervin

Thanks Axelay.
Sounds pretty much like the ideal solution for the direction I want to take 3d-maze in.

Gryzor

Congrats on your feat, I understand how it must have felt! I'll play it tonight hopefully, but why don't you upload it here emulated? :)

ervin

Thanks Gryzor.   :)
I hope you enjoy it.

Just wondering - how do I upload it here emulated?
Do you mean through JavaCPC?

Gryzor

Yes - but it doesn't work at the moment. Good think I remembered, we'll fix it soon!

Powered by SMFPacks Menu Editor Mod