Rising Pacman demo

Basic and Machine Language

Moderator: Moderators

User avatar
SNB
Vic 20 Newbie
Posts: 8
Joined: Wed Apr 11, 2018 7:27 am
Website: http://csdb.dk/scener/?id=27205
Location: Italy
Occupation: sw engineer

Rising Pacman demo

Post by SNB »

Ciriciao,
this is my first release for Vic 20, it works on unexpanded PAL and NTSC machines.
I've tested it on a real PAL hardware (see video here).
NTSC is tested only on Vice.

I must say that on Vice sometimes I get a cpu jam when starting, I might not have disabled the interrupts properly, I'll need to fix that eventually.
risingpacman5k.zip
(2.36 KiB) Downloaded 211 times
sshot.gif
sshot.gif (57.99 KiB) Viewed 6795 times
edit:

Some technical information.
This demo uses a double buffer for video matrix and color matrix, both buffers use the same set of tall (8×16) tiles.
The Pacman animation is made of 3 hires frames, each consisting of 9 rows of 12 tall (8×16) tiles, yelding 96×144 pixels. The frames are "map-compressed" so they use only 122 tiles (1952 bytes) instead of 324.
The ghosts are each 2×2 multicolor tiles (8×32 multicolor pixels), 4 ghosts, 16 tiles total.

Memory layout at run time:
$0000-$01F7 video matrix buffer 1
$00F8-$01FF processor stack
$0200-$03F7 video matrix buffer 2
$03F8-$03FF variables

$1000-$101F empty tile and full tile
$1020-$177E code, frame maps, other data, more variables
$1780-$1EFF Pacman tiles
$1F00-$1FFF Ghost tiles

$9400-$95F7 color matrix buffer 1
$95F8-$95FF color variables
$9600-$97F7 color matrix buffer 2
$97F8-$97FF color variables
hasseapa
Vic 20 Devotee
Posts: 264
Joined: Thu Oct 12, 2006 4:09 am

Re: Rising Pacman demo

Post by hasseapa »

Very stylish! It also looks like it could evolve into some kind of Match-3 puzzle game :D
prowlr
Vic 20 Amateur
Posts: 46
Joined: Thu Oct 19, 2017 3:59 am
Location: Australia

Re: Rising Pacman demo

Post by prowlr »

Nice. Add a catchy little tune to it and it would be a decent little intro/demo.
User avatar
polluks
Vic 20 Amateur
Posts: 45
Joined: Sat Apr 29, 2017 4:53 pm
Website: http://www.bilskaja.de
Location: Germany
Occupation: FI

Re: Rising Pacman demo

Post by polluks »

I like the combination of hires and multicolor :)
VC20 [WGA103574] + UltiMem
User avatar
SNB
Vic 20 Newbie
Posts: 8
Joined: Wed Apr 11, 2018 7:27 am
Website: http://csdb.dk/scener/?id=27205
Location: Italy
Occupation: sw engineer

Re: Rising Pacman demo

Post by SNB »

prowlr wrote:Nice. Add a catchy little tune to it and it would be a decent little intro/demo.
I wish I could, but I used all available ram and eded with only 1 free byte for the code!
I'm not saying that some extra bytes can't be freed by optimizing here and there, but I doubt I can fit music and code to play it.

SNB
User avatar
Mike
Herr VC
Posts: 4901
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Rising Pacman demo

Post by Mike »

Cute demo! :)
SNB wrote:I wish I could, but I used all available ram [...]
What keeps you from using RAM expansions with the VIC-20?

They are a legit peripheral as are tape and disk drives. Or are you going to type in your demo each time you want to show it? :wink:
User avatar
SNB
Vic 20 Newbie
Posts: 8
Joined: Wed Apr 11, 2018 7:27 am
Website: http://csdb.dk/scener/?id=27205
Location: Italy
Occupation: sw engineer

Re: Rising Pacman demo

Post by SNB »

Mike wrote:What keeps you from using RAM expansions with the VIC-20?
Making the demo fit the 5k of a stock Vic-20 was one of my goals, just for the fun of it.
I have a final expansion, so no problem using any memory expansion configuration otherwise.

SNB
User avatar
Mike
Herr VC
Posts: 4901
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Rising Pacman demo

Post by Mike »

SNB wrote:Making the demo fit the 5k of a stock Vic-20 was one of my goals, just for the fun of it.
That's fine.

Besides the tech info about the memory layout, it would be interesting to know what tools you use to do the map compression. For myself, I use a small C program, which emits a screen *.bin file and a character set *.bin file from an arbitrary sized source bitmap, and which not only fills in completely filled and empty tiles but also eliminates doubles.

I've put an example of this technique into another thread (download, also for unexpanded VIC-20).
I have a final expansion, so no problem using any memory expansion configuration otherwise.
So we can expect further prods from you beyond the realm of unexpanded VIC. 8)
User avatar
SNB
Vic 20 Newbie
Posts: 8
Joined: Wed Apr 11, 2018 7:27 am
Website: http://csdb.dk/scener/?id=27205
Location: Italy
Occupation: sw engineer

Re: Rising Pacman demo

Post by SNB »

Mike wrote:Besides the tech info about the memory layout, it would be interesting to know what tools you use to do the map compression. For myself, I use a small C program [...] which not only fills in completely filled and empty tiles but also eliminates doubles.
For each project I end up writing new tools instead of maitaining general tools to be reused.
In this case I wrote a C# console application that reads a PNG image and emits two files, a binary file with the tiles and a text file with the maps to be included in the source code. It takes care of duplicate tiles.

It's actually very simple, so simple that can be inlined here:

Code: Select all

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;

namespace PMStarConverter
{
	class Program
	{
		static void Main(string[] args)
		{
			if (args.Length != 3)
			{
				Console.Out.WriteLine("usage: PMStarConverter <input file.png> <maps.s> <tiles.raw>");
				return;
			}

			try
			{
				// tile vuota e tile piena
				__tiles.Add(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
				__tiles.Add(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });

				var src = new Bitmap(Image.FromFile(args[0]));

				for (int nFrame = 0; nFrame < 3; ++nFrame)
				{
					int nFrameY = nFrame * 9 * 16;

					for (int nCol = 0; nCol < 12; ++nCol)
					{
						int nColX = nCol * 8;

						for (int nRow = 0; nRow < 9; ++nRow)
						{
							int nRowY = nFrameY + nRow * 16;
							byte[] tile = new byte[16];

							for (int nLine = 0; nLine < 16; ++nLine)
							{
								int nLineY = nRowY + nLine;
								tile[nLine] = 0x00;

								for (int nPix = 0; nPix < 8; ++nPix)
								{
									int nPixX = nColX + nPix;

									tile[nLine] = (byte)(tile[nLine] << 1);
									var pix = src.GetPixel(nPixX, nLineY);
									if (pix.R == 0xFF)
										tile[nLine] |= 1;
								}
							}

							// cerco occorrenza precedente
							int nTile;
							for (nTile = 0; nTile < __tiles.Count; ++nTile)
							{
								bool bFound = true;
								for (int nTileLine = 0; nTileLine < 16 && bFound; ++nTileLine)
								{
									if (__tiles[nTile][nTileLine] != tile[nTileLine])
										bFound = false;
								}

								if (bFound)
									break;
							}
							if (nTile == __tiles.Count)
								__tiles.Add(tile);

							__maps[nFrame, nCol, nRow] = (byte)(nTile);
						}
					}
				}

				Console.Out.WriteLine("# tiles = {0}", __tiles.Count);

				// export maps
				{
					var dst = File.CreateText(args[1]);

					for (int nFrame = 0; nFrame < 3; ++nFrame)
					{
						dst.WriteLine("\t; map of frame {0}", nFrame);
						for (int nRow = 0; nRow < 9; ++nRow)
						{
							if (nRow == 0)
								dst.Write("frame{0}:", nFrame);
							dst.Write("\t.byte ");

							for (int nCol = 0; nCol < 12; ++nCol)
							{
								byte nTile = __maps[nFrame, nCol, nRow];
								if (nTile >= 2)
									nTile += (byte)(256 - __tiles.Count - 16);

								dst.Write("${0:X2}", nTile);
								dst.Write(nCol < 11 ? ", " : "\n");
							}
						}
					}

					dst.Close();
				}

				// export tiles
				{
					var dst = File.Create(args[2]);

					for (int nTile = 2; nTile < __tiles.Count; ++nTile)
						dst.Write(__tiles[nTile], 0, 16);

					dst.Close();
				}
			}
			catch (Exception ex)
			{
				Console.Out.WriteLine(ex);
			}
		}

		static private List<byte[]> __tiles = new List<byte[]>();
		static private byte[,,] __maps = new byte[4, 12, 9]; // frame, colonna, riga
	}
}
The emitted map file looks like this:

Code: Select all

	; map of frame 0
frame0:	.byte $78, $7C, $7C, $7C, $8B, $00, $00, $00, $A0, $7C, $7C, $AD
	.byte $00, $7D, $81, $86, $8C, $00, $00, $9A, $A1, $01, $01, $AE
	.byte $00, $00, $82, $87, $8D, $8F, $93, $9B, $A2, $A7, $01, $AE
	.byte $00, $00, $00, $88, $01, $01, $94, $9C, $A3, $A8, $AB, $AF
	.byte $79, $7E, $00, $01, $01, $90, $95, $00, $00, $00, $00, $00
	.byte $7A, $7F, $83, $89, $01, $01, $96, $9D, $A4, $00, $00, $00
	.byte $7A, $01, $84, $8A, $8E, $91, $97, $9E, $A5, $A9, $00, $00
	.byte $7A, $01, $85, $00, $00, $00, $98, $9F, $A6, $AA, $AC, $B0
	.byte $7B, $80, $00, $00, $00, $92, $99, $99, $99, $99, $99, $B1
	; map of frame 1
frame1:	.byte $00, $00, $00, $BA, $7C, $7C, $7C, $C3, $00, $00, $00, $00
	.byte $00, $00, $00, $BB, $BD, $BF, $C1, $C4, $00, $C8, $CD, $D0
	.byte $B2, $B4, $B7, $87, $8D, $8F, $93, $9B, $A2, $C9, $01, $AE
	.byte $7A, $B5, $B8, $88, $01, $01, $94, $9C, $A3, $CA, $01, $AE
	.byte $7A, $AE, $00, $01, $01, $90, $95, $00, $C7, $CB, $01, $AE
	.byte $7A, $B6, $00, $89, $01, $01, $96, $9D, $A4, $CC, $CE, $AE
	.byte $B3, $00, $00, $8A, $8E, $91, $97, $9E, $A5, $00, $CF, $D1
	.byte $00, $00, $00, $BC, $BE, $C0, $C2, $C5, $00, $00, $00, $00
	.byte $00, $00, $B9, $99, $99, $99, $99, $C6, $00, $00, $00, $00
	; map of frame 2
frame2:	.byte $D2, $D5, $D9, $00, $00, $00, $E1, $7C, $7C, $E5, $EC, $00
	.byte $7A, $01, $DA, $DD, $00, $00, $C1, $E2, $E3, $E6, $00, $00
	.byte $7A, $01, $DB, $87, $8D, $8F, $93, $9B, $A2, $00, $00, $00
	.byte $D3, $D6, $B8, $88, $01, $01, $94, $9C, $A3, $00, $00, $00
	.byte $00, $00, $00, $01, $01, $90, $95, $00, $C7, $E7, $ED, $EE
	.byte $00, $00, $00, $89, $01, $01, $96, $9D, $A4, $E8, $01, $AE
	.byte $00, $D7, $DC, $8A, $8E, $91, $97, $9E, $A5, $E9, $01, $AE
	.byte $D4, $D8, $01, $DE, $DF, $00, $00, $00, $E4, $EA, $01, $AE
	.byte $7B, $99, $99, $99, $E0, $00, $00, $00, $00, $EB, $99, $EF
and is used in the source code like this:

Code: Select all

;
; maps of central animation frames
;
	.include "../obj/centermaps.s"
I use ca65 as assembler.
Last edited by SNB on Wed Apr 18, 2018 1:26 am, edited 1 time in total.
User avatar
Mike
Herr VC
Posts: 4901
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Rising Pacman demo

Post by Mike »

SNB wrote:For each project I end up writing new tools instead of maitaining general tools to be reused.
In this case I wrote a C# console application that reads a PNG image and emits two files, a binary file with the tiles and a text file with the maps to be included in the source code. It takes care of duplicate tiles.
:D

Your filter is actually quite similar to the filter I implemented with an own small image processing suite. It reads in Un*x *.pgm files instead and - for the moment - only outputs the *.bin files. Those could be included directly or (depending on my mood) be transcoded into a bunch of .byte directives, as with your code:

Code: Select all

/*
 *  sentoria.c - create sparse bitmap using "text" screen and charset up to 256 8x8 characters.
 *
 *        creation date: 2015-12-25  Michael Kircher
 *  date of last change: 2018-04-14  Michael Kircher
 */

#include <stdio.h>
#include <stdlib.h>
#include "image.h"
#include "matrix.h"

#define START_CHAR 2

TEMPLATE_MATRIX(byte,unsigned char)

typedef enum {false,true} boolean;

int main(int argc, char *argv[])
{
  int cols,rows; /* dimensions of text screen */
  int x,y,y2;
  int i,j,max;
  unsigned char charset[256][8];
  unsigned char **screen;
  IMAGE *src;
  FILE *stream;

  if(       NULL == (src=image_load("input"))
     || BYTE_IMG != src->type
     ||        0 != src->rows%8
     ||        0 != src->cols%8) exit(EXIT_FAILURE);

  rows=src->rows/8;
  cols=src->cols/8;

  if(NULL==(screen=byte_alloc_matrix(rows,cols)))
  {
    image_free(src);
    exit(EXIT_FAILURE);
  }

  max=START_CHAR;
  for(y=0; y<rows; y++)
    for(x=0; x<cols; x++)
    {
      unsigned char glyph[8];
      /* extract bitmap data into 8 bytes */
      for(y2=0; y2<8; y2++)
        glyph[y2]=128*(b_pixel(src,x*8+0,y*8+y2) != 0)
                 + 64*(b_pixel(src,x*8+1,y*8+y2) != 0)
                 + 32*(b_pixel(src,x*8+2,y*8+y2) != 0)
                 + 16*(b_pixel(src,x*8+3,y*8+y2) != 0)
                 +  8*(b_pixel(src,x*8+4,y*8+y2) != 0)
                 +  4*(b_pixel(src,x*8+5,y*8+y2) != 0)
                 +  2*(b_pixel(src,x*8+6,y*8+y2) != 0)
                 +  1*(b_pixel(src,x*8+7,y*8+y2) != 0);

      /* match against all current glyphs in character set: */
      j=max; /* default for non-match */
      for(i=START_CHAR; i<max; i++)
        if(   glyph[0] == charset[i][0]
           && glyph[1] == charset[i][1]
           && glyph[2] == charset[i][2]
           && glyph[3] == charset[i][3]
           && glyph[4] == charset[i][4]
           && glyph[5] == charset[i][5]
           && glyph[6] == charset[i][6]
           && glyph[7] == charset[i][7]) {j=i; break;}

      screen[y][x]=j; /* fan-out to screen */

      /* add glyph to character set if no duplicate was found: */
      if(j == max && max<256)
      {
        for(i=0; i<8; i++)
          charset[j][i]=glyph[i];
        max++;
      }
    }
  
  /* write out screen binary */
  if(NULL==(stream=fopen("screen.bin","wb")))
  {
    byte_free_matrix(screen);
    image_free(src);
    exit(EXIT_FAILURE);
  }
  for(y=0; y<rows; y++)
    for(x=0; x<cols; x++)
      fputc(screen[y][x],stream);
  fclose(stream);

  /* write out character set binary */
  if(NULL==(stream=fopen("charset.bin","wb")))
  {
    byte_free_matrix(screen);
    image_free(src);
    exit(EXIT_FAILURE);
  }
  for(j=START_CHAR; j<max; j++)
    for(i=0; i<8; i++)
      fputc(charset[j][i],stream);
  fclose(stream);

  byte_free_matrix(screen);
  image_free(src);

  exit(EXIT_SUCCESS);
}
Matching duplicates is probably more successful with 8x8 tiles, as done here. YMMV.

Attached the complete source including the image processing library: (download).
User avatar
beamrider
Vic 20 Scientist
Posts: 1465
Joined: Sun Oct 17, 2010 2:28 pm
Location: UK

Re: Rising Pacman demo

Post by beamrider »

Nice demo!
User avatar
SNB
Vic 20 Newbie
Posts: 8
Joined: Wed Apr 11, 2018 7:27 am
Website: http://csdb.dk/scener/?id=27205
Location: Italy
Occupation: sw engineer

Re: Rising Pacman demo

Post by SNB »

Mike wrote:Matching duplicates is probably more successful with 8x8 tiles, as done here.
Yes, I definitely agree, in the same bitmap you will always find more duplicates with 8×8 than with 8×16 tiles (or the same number in the worst case).
But you also get a map that is twice as large, so the overall compression might be better or worse depending from case to case.
In any case I decided to go for 8×16 pixels tiles for other reasons.
I wanted to double buffer the screen. Not only the video matrix but also the color matrix, so I needed each matrix not to exceed 512 bytes.
I wanted to cover as much as possible of the video area, with PAL up to 28×36 8×8 cells, so I needed to go for the tall characters and use a matrix sized 28×18, 504 bytes.
In the final version the PAL matrix is sized 28×17 because the ghosts animations are not double buffered and I needed to hide them.

SNB
hasseapa
Vic 20 Devotee
Posts: 264
Joined: Thu Oct 12, 2006 4:09 am

Re: Rising Pacman demo

Post by hasseapa »

Aha, you are the guy that made the arcade perfect looking and (somewhat) playable C64-demo of Pac-Man :)
You may already be aware of a new WIP-Pac-Man for C64
https://www.youtube.com/watch?time_cont ... zbpWiQtgXs
User avatar
SNB
Vic 20 Newbie
Posts: 8
Joined: Wed Apr 11, 2018 7:27 am
Website: http://csdb.dk/scener/?id=27205
Location: Italy
Occupation: sw engineer

Re: Rising Pacman demo

Post by SNB »

Yes, it’s me, but my demo was far from playable, I consider it more like an animated mockup.
The version of pacman in this video looks very good indeed, it’s a pity the maze is not shaped as the arcade original.
hasseapa
Vic 20 Devotee
Posts: 264
Joined: Thu Oct 12, 2006 4:09 am

Re: Rising Pacman demo

Post by hasseapa »

SNB wrote: The version of pacman in this video looks very good indeed, it’s a pity the maze is not shaped as the arcade original.
Agreed.

I really hope they add the cut-scenes/interludes - they are sorely missed in both VIC and C64 versions/clones.

Edit: 2014 version on Quikman actually has interludes! Nice. Also, a cool but crazy 1983 C64-version version has them https://www.youtube.com/watch?v=_J5b8zQsKZ4
Post Reply