Rising Pacman demo
Moderator: Moderators
- 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
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.
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
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.
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
Re: Rising Pacman demo
Very stylish! It also looks like it could evolve into some kind of Match-3 puzzle game
Re: Rising Pacman demo
Nice. Add a catchy little tune to it and it would be a decent little intro/demo.
- polluks
- Vic 20 Amateur
- Posts: 45
- Joined: Sat Apr 29, 2017 4:53 pm
- Website: http://www.bilskaja.de
- Location: Germany
- Occupation: FI
- 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
I wish I could, but I used all available ram and eded with only 1 free byte for the code!prowlr wrote:Nice. Add a catchy little tune to it and it would be a decent little intro/demo.
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
- Mike
- Herr VC
- Posts: 4901
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Re: Rising Pacman demo
Cute demo!
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?
What keeps you from using RAM expansions with the VIC-20?SNB wrote:I wish I could, but I used all available ram [...]
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?
- 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
Making the demo fit the 5k of a stock Vic-20 was one of my goals, just for the fun of it.Mike wrote:What keeps you from using RAM expansions with the VIC-20?
I have a final expansion, so no problem using any memory expansion configuration otherwise.
SNB
- Mike
- Herr VC
- Posts: 4901
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Re: Rising Pacman demo
That's fine.SNB wrote:Making the demo fit the 5k of a stock Vic-20 was one of my goals, just for the fun of it.
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).
So we can expect further prods from you beyond the realm of unexpanded VIC.I have a final expansion, so no problem using any memory expansion configuration otherwise.
- 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
For each project I end up writing new tools instead of maitaining general tools to be reused.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.
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
}
}
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
Code: Select all
;
; maps of central animation frames
;
.include "../obj/centermaps.s"
Last edited by SNB on Wed Apr 18, 2018 1:26 am, edited 1 time in total.
- Mike
- Herr VC
- Posts: 4901
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Re: Rising Pacman demo
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.
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);
}
Attached the complete source including the image processing library: (download).
Re: Rising Pacman demo
Nice demo!
- 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
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).Mike wrote:Matching duplicates is probably more successful with 8x8 tiles, as done here.
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
Re: Rising Pacman demo
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
You may already be aware of a new WIP-Pac-Man for C64
https://www.youtube.com/watch?time_cont ... zbpWiQtgXs
- 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
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.
The version of pacman in this video looks very good indeed, it’s a pity the maze is not shaped as the arcade original.
Re: Rising Pacman demo
Agreed.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.
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