Hello World on Pac-Man hardware

cmonkey

Active member
vacBacker
Feedback
4 (100%)
Credits
1,658CR
Just for RGP
smiley2.gif


Here's the source and binary for a very simple HELLO WORLD app running on Pac-Man hardware. There's a certain amount of hardware bring up that you need to do to code for an arcade board. Probably the best way to do this is to reverse the boot process of a game running on that hardware (as I did with L System).

This is only tested in MAME as I don't have a Pac-Man board.

Assembled by the zasm assembler, although should pretty much port to any Z80 assembler without too much trouble. The resulting binary is a whopping 243 bytes in size!

This code DOES NOT wait for a vblank before blitting the text strings to vram. This is NOT the way you'd do it if you were writing a real game as, if you change the contents of vram/colour ram whilst the raster is half way through displaying the screen, you'll get flickering. You'd normally wait until the vblank routine had finished and then blit text strings to screen whilst the raster beam was turned off and flying back.

Starting homebrew coding on a vertical screen game is more challenging than a horizontal screen game due to the weird layout of vertical screens in vram, but once you've got a text blitting routine working then it's not so bad.

Have fun James!

https://www.ukvac.com/forum/data/uploads/1497/hello_world_pacman.zip

hello_world_pacman.png
 

RGP

Meeter & Greeter
Feedback
5 (100%)
Credits
2,039CR
Thanks Adrian, I went into it this evening, and ended up with this.

Screen_Shot_2014-12-30_at_00.57.20.png


I was going to try and do exactly what you've written up but I went for a bit more exploratory as I wasn't familiar with the hardware etc.

Firstly, Pac-Man has some weird screen RAM addressing going on.

0x4022 is actually the far bottom right of the screen where the fruits go.

0x4040 is top right of the playfield area and then 0x4041 is the character below and so on so the screen goes top to bottom, right to left.

Anyway, just as a basic get me up to speed hacking the hardware I wrote a text rendering routine.

Very basic:

Code:
org 00000h

toprt:   equ 04040h

topleft: equ 04300h+160

hoffset: equ 32

ld  sp, 05000h

		call init

call topbot

ld  a, 0

		ld  bc, 0

		ld  de, 0

		ld  iy, 0

		ld  ix, 0

		ld  hl, topleft

chrwr:	ld  a, (IX+str)

		ld  b, 0

		cp  b            ; check for end of text

		jp  z, wdlp

		ld  b, 0ffh      ; check for newline (saves memory)

		cp  b

		jp  z, nline

		ld  b, 32        ; check for ASCII space

		cp  b

		jp  nz, nospc

		ld  a, 47

nospc:	ld  (hl), a      ; write character to video ram

		ld  a, l

		ld  b, hoffset

		sub b

		ld  l, a

		jp  nc, checkc

dec h            ; if we set our carry bit (went below 0 on the subtract) then dec h

checkc:	inc c

		ld  a,c

		ld  b,28

		cp  b

		jp  nz, cont

nline:  ld  c, 0

		inc d

		ld  hl, topleft

		ld  a, l

		add d

		ld  l, a

cont:	inc ix           ; increment our input block offset

		jp  chrwr

wdlp:	nop

		xor a

		ld	(050c0h), a

		jp wdlp

str:    

		db  "JAMES RGP WOZ ERE",0ffh

        db  0ffh

        db  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0ffh

        db  0ffh

        db  "MARY HAD A LITTLE LAMB", 0ffh

        db  "SHE ALSO HAD A DUCK", 0ffh

        db  "SHE PUT THEM ON THE MANTLE", 0ffh

        db  "HOPING THEY MIGHT ", 16,16,16,16, 0ffh

        db  0ffh

        db  209,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,208

        db  211,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,210

   		db  213,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,212

        db  0ffh

        db  1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,0ffh

        db  33,34,35,36,37,38,39,40,41,42,43,44,45,46,0ffh

        db  91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118

        db  119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144

        db  145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170

        db  171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196

        db  197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221

        db  222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246

        db  247,248,249,250,251,252,253,254

        db  0

init:	ld  hl, 04400h

		ld  de, 04401h

		ld  bc, 003ffh

		ld  (hl), 07fh

		ldir

		ld  hl, 04000h

		ld  de, 04001h

		ld  bc, 003ffh

		ld  (hl), 47    ; 47 is space

		ldir

		ret

topbot: ld  a, 209

		ld  (0401dh), a  ; top left of fruit area

		ld  a, 208

		ld  (04002h), a  ; top right of fruit area

		ld  a, 213

		ld  (0403dh), a  ; bottom left of fruit area

		ld  a, 212

		ld  (04022h), a  ; bottom right of fruit area

ld  a, 209

		ld  (043ddh), a  ; top left of score area

		ld  a, 208

		ld  (043c2h), a  ; top right of score area

		ld  a, 213

		ld  (043fdh), a  ; bottom left of score area

		ld  a, 212

		ld  (043e2h), a  ; bottom right of score area

		ret
 

RGP

Meeter & Greeter
Feedback
5 (100%)
Credits
2,039CR
I spotted the thread and decided to write my reply before reading what you'd written Ade.

Its very interesting how we both head for zero terminated strings, i've implemented newlines in mine - mine turns out at 594 bytes but i've got a big data block in there for what I want on the screen.

My Z80 is seriously seriously rusty right now but this was fun - a little painful but still fun though.
 

cmonkey

Active member
vacBacker
Feedback
4 (100%)
Credits
1,658CR
I literally couldn't be happier right now James! I'm sitting here feeling almost like a proud father, knowing that I've started the ball rolling on something big here.

I'm so happy that you've got your code running, well done!
smiley32.gif


I said that the vram layout of vertical screens was weird, this is why I prefer to code for horizontal screens.

Just a couple of things that I noticed in your code which are worth a mention :-

1) your init code only clears vram and colour ram and DOESN'T clear main cpu ram ($4c00-$4fef). It's considered bad practise to use uninitialised ram (even though, at the moment, your code isn't actually using any main cpu ram, it will as the program grows). MAME lulls you in to a false sense of security in this respect by automatically initialising all ram areas to zero when a game starts. In reality uninitialised ram contents can contain non-null values. Try to get into the habit of clearing main cpu ram in your init routine.

2) You've set your SP to $5000 which is actually the final byte of sprite ram! This is not a problem if you're not using sprites but as soon as you start using them you'll find they're doing weird things when you call a subroutine and the stack changes value right in the middle of sprite ram! You should set your stack to $4ff0, which is the top of main cpu ram.

3) You've not created a vblank routine but as you're running with interrupts disabled this isn't a problem. Obviously as your code grows you'll need to implement one, not least for music playing, as this is pretty much ALWAYS done in the vblank in any game.

I'm not criticising your code in any way so please don't take my comments as any kind of criticism, your code works, as is, and that's the single most important thing to you, me or anyone else who looks at it.

I'm off to have a celebratory cuppa now, happy in the knowledge that the ball is well and truly rolling....
smiley4.gif
 

philmurr

Active member
vacBacker
Feedback
46 (100%)
Credits
2,344CR
Interesting use of the IX register and the offset, (which will only work if the start of the text to be displayed is in the first 256 bytes of memory).

Not a criticism, just I've never seen it used that way before.
 

cmonkey

Active member
vacBacker
Feedback
4 (100%)
Credits
1,658CR
philmurr said:
Interesting use of the IX register and the offset, (which will only work if the start of the text to be displayed is in the first 256 bytes of memory).

Not a criticism, just I've never seen it used that way before.

That's the joy of seeing other people's code. Occasionally you come across stuff that causes you to rethink how you've done things for years. It's awesome, isn't it.

It will actually only work if the string to be displayed is in the first 128 bytes of memory because indexed addressing uses a two's complement displacement (-127 to +128).
 

RGP

Meeter & Greeter
Feedback
5 (100%)
Credits
2,039CR
I was looking for a good equivalent of

LDA ($0677),X

I've not written Z80 in a very long time and written way more 6502/6510/68000 than Z80 altogether but as a lot of the hardware I'd initially be interested in is Z80 I'll have to beef it up.

What's the right way?

No offence taken, I was looking for the fastest way to get the job done.
 

philmurr

Active member
vacBacker
Feedback
46 (100%)
Credits
2,344CR
cmonkey said:
philmurr said:
Interesting use of the IX register and the offset, (which will only work if the start of the text to be displayed is in the first 256 bytes of memory).

Not a criticism, just I've never seen it used that way before.

It will actually only work if the string to be displayed is in the first 128 bytes of memory because indexed addressing uses a two's complement displacement (-127 to +128).

Of course, that shows the last time I wrote some Z80 code, I stand very much corrected
smiley9.gif


RGP said:
I was looking for a good equivalent of

LDA ($0677),X

Now that makes sense why you did it that way ! Nothing wrong with what you did, when I used to write code I always set IX to the base address of the data I wanted to use, and refer to IX+offset (generally IX+0) and INC/DEC IX which gets over the "page" issue
 

cmonkey

Active member
vacBacker
Feedback
4 (100%)
Credits
1,658CR
That's the joy of coding James, there's isn't really a right way or a wrong way, there are just different ways to achieve the end goal. You could probably sit 10 Z80 coders down in a room and ask them to create a routine to do something and you can pretty much guarantee that you'd end up with at least half a dozen different approaches to the same end result when they'd finished.

Personally, when I do string printing routines I point HL at the start of the string and then keep retrieving bytes from the string into A until I read a zero terminator. I then inc hl after printing each byte to screen.

blit_text: ld hl, string ; point HL to the start of the string

blit_loop: ld a,(hl) ; get next byte of string

or a ; test the byte we just retrieved

ret z ; return if we read a zero

call write_A_to_vram

inc hl ; increment string pointer

jr blit_loop

I usually use the IX and IY registers to step through tables of data or data structures.

But like I said before, each person will have his or her own unique coding style, and as long as it acheives the end result and doesn't cause geothermal nuclear war in the process of doing so who cares what it looks like!
 

RGP

Meeter & Greeter
Feedback
5 (100%)
Credits
2,039CR
That's got to be the start of a good joke. 10 Z80 coders in a room and .....

And another thing that will always be different between coders is optimisation, you've used a call to a subroutine which involves a stack push, program counter change and the reverse at the end of it and internally, likely some push/pop's of registers within the routine. Save a cycle here use one there.

One thing you can't do reliably on arcade hardware which foils people is self-modifying and self-generating code because its in ROM.

In mine I didn't read until later that there's sprite addresses at the end of the ram block upto $5000
 

cmonkey

Active member
vacBacker
Feedback
4 (100%)
Credits
1,658CR
You most certainly CAN do self-modifying code on arcade hardware!
smiley2.gif
But first you've got to copy your routine to ram first so that you can modify it. Sega Pengo uses self-modifying code in the maze generation algorithm. More about that here (in fact that thread should probably be moved to this section now) :-

http://www.ukvac.com/forum/sega-pengo-bug-found-in-maze-generating-algorithm_topic335921.html

I choose to write text blitting routines like this for quickness of porting from one platform to another. The actual writing of bytes to vram is usually platform specific but the rest of the routine is generic. This way I only have to port the 'write_A_to_vram' routine to my new target and copy/paste the rest of it.

But I agree with you that optimisation between different coders is the one thing that can make some code very hard to read if not suitably commented (and let's face it, who comments their own code?!)

The truth of the matter is that arcade hardware is soooo much more powerful than the equivalent 8/16-bit computers that we're used to coding for that you don't have to worry as much about optimisation in the early stages of your demo/game. Obviously if you really want to push the hardware to its very limits then you'll be optimising like crazy by unrolling all your loops and racing the raster by doing instruction timing counting and stuff like that.

Wouldn't it be cool to have your own custom code running on a few boards for when you host your next New Frontier event.....
smiley1.gif
 

RGP

Meeter & Greeter
Feedback
5 (100%)
Credits
2,039CR
I've just had an immediate practical thought for this kind of thing as well as homebrewering a game - a monitor test pattern generator. I know ideally we want to keep tests/utilities out of this section etc but I have a bootleg Pac-Man board lurking around that would make an ideal test pattern generator.

Its something I talked to TRM about last year with a view to us doing a tech doc series on basic micro processor hardware usage, i'd always intended to use a 6502 or Z80 for the CPU with some buttons to page back/forward etc but this is almost perfect on pac hardware.

1. It has graphics pre-built for text - including blocks for a cross-hatch

2. Decent colour pallet - certainly enough to generate reasonable intensity RGB - not calibration standard but enough for doing basic setup after a repair or doing a soak test.

3. Hardware sprites - not essential but good for doing some basic animation to exercise a few parts of the display.

4. Board supports joystick inputs for paging through things and has 2 'buttons' (p1/p2 start) that can be used for sub-selections.

Hmmmmmmm........
 

RGP

Meeter & Greeter
Feedback
5 (100%)
Credits
2,039CR
cmonkey said:
Thinking outside the box, that's what I like James.
smiley1.gif

Thanks, i've mostly used a 60-in-1 on test mode to get a colour pattern but the palette is so far between the test pattern and the live side of the board that you can't remotely setup a decent picture.

I did acquire a test pattern generator a bit back and the sync output has stopped working on it - I can't figure out how to get to the part where the sync circuit is without a lot of desoldering. Andy acquired a second one from eBay and i've been using that but they could do with it back up at the unit's workshop now.
 
Top