From 4e76ac681decbe5992456afece2f4f610fde3030 Mon Sep 17 00:00:00 2001 From: Paul Gardner-Stephen Date: Thu, 20 Jun 2019 07:39:33 +0200 Subject: [PATCH] pull in our free 8x8 font so that we can make totally free C65-compatible ROMs --- Makefile | 14 +- assets/8x8font.png | Bin 0 -> 2118 bytes src/pngprepare.c | 720 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 732 insertions(+), 2 deletions(-) create mode 100644 assets/8x8font.png create mode 100644 src/pngprepare.c diff --git a/Makefile b/Makefile index e2305074..92d15cae 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,14 @@ -all: src/collect_data src/preprocess +all: Ophis src/collect_data src/preprocess + +Ophis: + git submodule init + git submodule update + +src/pngprepare: src/pngprepare.c Makefile + $(CC) $(COPT) -I/usr/local/include -L/usr/local/lib -o src/pngprepare src/pngprepare.c -lpng + +chargen: src/pngprepare assets/8x8font.png + src/pngprepare charrom assets/8x8font.png chargen src/collect_data: src/collect_data.c Makefile gcc -Wall -o src/collect_data src/collect_data.c @@ -21,7 +31,7 @@ newkern: newrom Makefile newbasic: newrom Makefile dd if=newrom bs=8192 count=1 skip=0 of=newbasic -newc65: newkern newbasic Makefile +newc65: newkern newbasic chargen Makefile dd if=/dev/zero bs=4096 count=10 of=newc65 cat newbasic chargen chargen newkern >>newc65 dd if=/dev/zero bs=65536 count=1 of=padding diff --git a/assets/8x8font.png b/assets/8x8font.png new file mode 100644 index 0000000000000000000000000000000000000000..08ffaa67990a302abb2538e9f737d0040a7341c5 GIT binary patch literal 2118 zcmZvec{J3G9>zz+pqP1S6ER$-xVDh7WRhVR+k{ZoD5S#hmKl377#iDH3K_CQiI-HC zA&hvDrIK9?*%D?LjBS2b=iYPPf9^le=Q-zl&i9Y!JbyeStQGpGkc1Ej1UhPFin2K% z7zBcVz`O?z?~Ii=06*5k&iH@;02GfAicMcl95JuOFSwr-(yvR4 z@!i_pO(rnwePlt(kq(}5W5O3h4bQ2hv4}o=D$pDf~a#un&!aTmw<> zM;KqqyRiHr4Qmf*eR*Qgp>B`+5W!y@QR20=BYsa(rfKC{WAmt37fnxHFP!qxn&w|^ zYfG~x^}Hp8)qYcrPMN>IAL9Yf&<>o*N2W{@M^Gh0*XsskvAIT7;b_&SBV=M5-FlV` z?X0O!8t}v{JFGsToXN;sa;x7Tm+LLtz7Y*uo=ywO9Vd7;TmK+s1S^$|A6uTb7w)Pq zI#+BL&pjGgE5Gpe8OUHQ6ft^6%V#I9L512(5~wdL z6P8!1GAi!kOW3*Ds#fZ5v+$Smo&>o!x~t0L(kFlE6w*?*C*r(F66>G0+5NPfwRxAk z$+U1g2dd(3$OH=9Hk;jG;XEI#E7H0bVYv5G0CS zkqWblN)e(J=r(J?qpElD3iw&Un?#6M=qG2K<%$cRCJtG@eB8-tjGx>;HN6T&B4QHo-Q1THKsP#DtMEiVHH!pz=c} z4ZOr%d?hTFQpmhz{n2sH$Bw6DW@RF+vl_TZYPdY%NuK4c5&MrZ`xY(}OSUz**wYQ+ zxsJO%++QY`QvYfWY0L6~Z^G6&&B`v6thC=o_t_r=7i7V(*(`Y+(H?ajukxO5GHNC- zyR?{V6w)v;UmcQhr<^(Wcb)mGr3Dj@y3IFpv(MVS-6|p%H-nN*-yI8)kIrbiWIeFv z#NXueG>}v_Q6l_~RU_w5VLqvZw#OoI7Y)C-=x-mOwP+#8GW)_(8h7f{40+fke- zs!|$qWl_Hb`8FXwhGkGo^ZJYxC1FM;&4f12Rez6G%3v$Swe}5Ah z2Y6DG3*Tf$h(Ki<^m*q`yicv$lAWXvk&4)BYCOIeW;fPly}@YlnDkHKO{ z2HuAZh-rn*0KfP|9k2}Mu-KJ*g2Nmn8TiUU5{GZ^QKjP!Y#$jI|DWvmCJ$h=xf+^W z4TS(^AP31qdv~&A42Dhhtzz(Q(U2(kAmWmh_NPQ`r?FK_+!6Pbn8slp`=xjfoL&X!VnMFK%&NsGKs345Gr<3oJv0Z|Qt_HaJq^^cs8uZLd;3s{-&QLk z0?b{9?MXiv%?mC}y<^1ya_RnM%bSLeRY4bizoW$gwj;~*%7dt@`x0|BC4R?qAl&|u rKcZ?V*+Yudr$MKI4jMBH#0wg4L{1JL|1xwi2ta1WR;Y^0?vMThlS4wr literal 0 HcmV?d00001 diff --git a/src/pngprepare.c b/src/pngprepare.c new file mode 100644 index 00000000..7c5b245c --- /dev/null +++ b/src/pngprepare.c @@ -0,0 +1,720 @@ +/* + * Copyright 2002-2010 Guillaume Cottenceau. + * Copyright 2015-2018 Paul Gardner-Stephen. + * + * This software may be freely redistributed under the terms + * of the X11 license. + * + */ + +/* ============================================================= */ + +#include +#include +#include +#include +#include +#include + +#define PNG_DEBUG 3 +#include + +/* ============================================================= */ + +char *vhdl_prefix= + "library IEEE;\n" + "use IEEE.STD_LOGIC_1164.ALL;\n" + "use ieee.numeric_std.all;\n" + "use work.debugtools.all;\n" + "\n" + "--\n" + "entity charrom is\n" + "port (Clk : in std_logic;\n" + " address : in integer range 0 to 4095;\n" + " -- chip select, active low \n" + " cs : in std_logic;\n" + " data_o : out std_logic_vector(7 downto 0);\n" + "\n" + " writeclk : in std_logic;\n" + " -- Yes, we do have a write enable, because we allow modification of ROMs\n" + " -- in the running machine, unless purposely disabled. This gives us\n" + " -- something like the WOM that the Amiga had.\n" + " writecs : in std_logic;\n" + " we : in std_logic;\n" + " writeaddress : in unsigned(11 downto 0);\n" + " data_i : in std_logic_vector(7 downto 0)\n" + " );\n" + "end charrom;\n" + "\n" + "architecture Behavioral of charrom is\n" + "\n" + "-- 4K x 8bit pre-initialised RAM for character ROM\n" + "\n" + "type ram_t is array (0 to 4095) of std_logic_vector(7 downto 0);\n" + "signal ram : ram_t := (\n" + "\n"; + +char *vhdl_suffix= + ");\n" + "\n" + "begin\n" + "\n" + "--process for read and write operation.\n" + "PROCESS(Clk,ram,writeclk)\n" + "BEGIN\n" + "\n" + " if(rising_edge(writeClk)) then \n" + " if writecs='1' then\n" + " if(we='1') then\n" + " ram(to_integer(writeaddress)) <= data_i;\n" + " end if;\n" + " end if;\n" + " data_o <= ram(address);\n" + " end if;\n" + "END PROCESS;\n" + "\n" + "end Behavioral;\n"; + +/* ============================================================= */ + +int x, y; + +int width, height; +png_byte color_type; +png_byte bit_depth; + +png_structp png_ptr; +png_infop info_ptr; +int number_of_passes; +png_bytep * row_pointers; + +FILE *infile; +FILE *outfile; + +/* ============================================================= */ + +void abort_(const char * s, ...) +{ + va_list args; + va_start(args, s); + vfprintf(stderr, s, args); + fprintf(stderr, "\n"); + va_end(args); + abort(); +} + +/* ============================================================= */ + +void read_png_file(char* file_name) +{ + unsigned char header[8]; // 8 is the maximum size that can be checked + + /* open file and test for it being a png */ + infile = fopen(file_name, "rb"); + if (infile == NULL) + abort_("[read_png_file] File %s could not be opened for reading", file_name); + + fread(header, 1, 8, infile); + if (png_sig_cmp(header, 0, 8)) + abort_("[read_png_file] File %s is not recognized as a PNG file", file_name); + + /* initialize stuff */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + abort_("[read_png_file] png_create_read_struct failed"); + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + abort_("[read_png_file] png_create_info_struct failed"); + + if (setjmp(png_jmpbuf(png_ptr))) + abort_("[read_png_file] Error during init_io"); + + png_init_io(png_ptr, infile); + png_set_sig_bytes(png_ptr, 8); + + // Convert palette to RGB values + png_set_expand(png_ptr); + + png_read_info(png_ptr, info_ptr); + + width = png_get_image_width(png_ptr, info_ptr); + height = png_get_image_height(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + + printf("Input-file is: width=%d, height=%d.\n", width, height); + + number_of_passes = png_set_interlace_handling(png_ptr); + png_read_update_info(png_ptr, info_ptr); + + /* read file */ + if (setjmp(png_jmpbuf(png_ptr))) + abort_("[read_png_file] Error during read_image"); + + row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); + for (y=0; y255) { + fprintf(stderr,"Too many colours in image: Must be < 256, now up to %d\n", + palette_index); + } + if (palette_index>2559) exit(-1); + + // allocate it + palette[palette_index].r=r; + palette[palette_index].g=g; + palette[palette_index].b=b; + return palette_index++; + +} + +unsigned char nyblswap(unsigned char in) +{ + return ((in&0xf)<<4)+((in&0xf0)>>4); +} + +void process_file(int mode, char *outputfilename) +{ + int multiplier=-1; + if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) + multiplier=3; + + if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGBA) + multiplier=4; + + if (multiplier==-1) { + fprintf(stderr,"Could not convert file to RGB or RGBA\n"); + } + + outfile=fopen(outputfilename,"w"); + if (outfile == NULL) { + // could not open output file, so close all and exit + if (infile != NULL) { + fclose(infile); + infile = NULL; + } + abort_("[process_file] File %s could not be opened for writing", outputfilename); + } + + + /* ============================ */ + + if (mode==0) { + printf("mode=0 (logo)\n"); + // Logo mode + + // Pre-load in C64 palette, so that those colours can be re-used if required + + palette[0]=(struct rgb){.r=0,.g=0,.b=0}; + palette[1]=(struct rgb){.r=0xff,.g=0xff,.b=0xff}; + palette[2]=(struct rgb){.r=0xab,.g=0x31,.b=0x26}; + palette[3]=(struct rgb){.r=0x66,.g=0xda,.b=0xff}; + palette[4]=(struct rgb){.r=0xbb,.g=0x3f,.b=0xb8}; + palette[5]=(struct rgb){.r=0x55,.g=0xce,.b=0x58}; + palette[6]=(struct rgb){.r=0x1d,.g=0x0e,.b=0x97}; + palette[7]=(struct rgb){.r=0xea,.g=0xf5,.b=0x7c}; + palette[8]=(struct rgb){.r=0xb9,.g=0x74,.b=0x18}; + palette[9]=(struct rgb){.r=0x78,.g=0x73,.b=0x00}; + palette[10]=(struct rgb){.r=0xdd,.g=0x93,.b=0x87}; + palette[11]=(struct rgb){.r=0x5b,.g=0x5b,.b=0x5b}; + palette[12]=(struct rgb){.r=0x8b,.g=0x8b,.b=0x8b}; + palette[13]=(struct rgb){.r=0xb0,.g=0xf4,.b=0xac}; + palette[14]=(struct rgb){.r=0xaa,.g=0x9d,.b=0xef}; + palette[15]=(struct rgb){.r=0xb8,.g=0xb8,.b=0xb8}; + + + for (y=0; y255) printf("Too many colours at (%d,%d)\n",x,y); + + /* work out where in logo file it must be written. + image is made of 8x8 blocks. So every 8 pixels across increases address + by 64, and every 8 pixels down increases pixel count by (64*8), and every + single pixel down increases address by 8. + */ + int address=0; + address+=0x300; // space for palettes + address+=(x&7)+(y&7)*8; + address+=(x>>3)*64; + // if (size==SIZE_LOGO) + address+=(y>>3)*64*(width/8); + // else + // address+=(y>>3)*64*40; + + fseek(outfile,address,SEEK_SET); + int n=fwrite(&c,1,1,outfile); + if (n!=1) { + fprintf(stderr,"Could not write pixel (%d,%d) @ $%x\n",x,y,address); + if (outfile != NULL) { + fclose(outfile); + outfile = NULL; + } + exit(-1); + } + } + } + + fprintf(stderr,"Writing out palette of %d values\n",palette_index-palette_first); + for(int i=0;i<256;i++){ + int address; + unsigned char c; + int v; + + address=i+0x000; + v=palette[i].r; + c=(v>>4)|((v&0xf)<<4); + fseek(outfile,address,SEEK_SET); + fwrite(&c,1,1,outfile); + + address=i+0x100; + v=palette[i].g; + c=(v>>4)|((v&0xf)<<4); + fseek(outfile,address,SEEK_SET); + fwrite(&c,1,1,outfile); + + address=i+0x200; + v=palette[i].b; + c=(v>>4)|((v&0xf)<<4); + fseek(outfile,address,SEEK_SET); + fwrite(&c,1,1,outfile); + } + + + if (outfile != NULL) { + fclose(outfile); + outfile = NULL; + } + + } + + + /* ============================ */ + if (mode==1) { + + unsigned char first_half[1024]; + + printf("mode=1 (charrom)\n"); + // charrom mode + + int vhdl_mode=1; + if (!strstr(outputfilename,".vhdl")) vhdl_mode=0; + + int bytes=0; + if (vhdl_mode) fprintf(outfile,"%s",vhdl_prefix); + if (width!=8) { + fprintf(stderr,"Fonts must be 8 pixels wide\n"); + } + + int spots[8][8]; + int charsets; + + + // 4KB = 2x 256 char = 2KB charsets + for(charsets = 0 ; charsets<2 ; charsets++) { + if (bytes>=4096) break; + fprintf(stderr,"yheight=%d\n",height); + for (y=0; y0x7f||g>0x7f||b>0x7f) { + byte|=(1<<(7-x)); + spots[yy][x]=1; + } else spots[yy][x]=0; + } + } + fflush(stdout); + char comma = ','; + if (bytes<1024) { + first_half[bytes]=byte; + } + bytes++; + if (bytes>=4096) { + comma=' '; + } + if (vhdl_mode) fprintf(outfile,"x\"%02x\"%c",byte,comma); + else fputc(byte,outfile); + if (vhdl_mode) { + if ((y&7)==7) { + fprintf(outfile,"\n"); + int yy; + for(yy=0;yy<8;yy++) { + fprintf(outfile,"-- ["); + for(x=0;x<8;x++) { + if (spots[yy][x]) fprintf(outfile,"*"); else fprintf(outfile," "); + } + fprintf(outfile,"]\n"); + } + } + } + } + + // Fill in any missing bytes + if (bytes<2048) { + + printf("Padding output file to 2048 after first charset\n"); + + if (vhdl_mode) { + fprintf(outfile,",\n"); + + for(;bytes<2048;bytes+=8) { + int reverse=bytes&0x400; + if (reverse) reverse=0xff; + + fprintf(outfile,"x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\"%c -- 0x%03x (set %d, char 0x%02x)\n", + first_half[(bytes+0)&0x3ff]^reverse, + first_half[(bytes+1)&0x3ff]^reverse, + first_half[(bytes+2)&0x3ff]^reverse, + first_half[(bytes+3)&0x3ff]^reverse, + first_half[(bytes+4)&0x3ff]^reverse, + first_half[(bytes+5)&0x3ff]^reverse, + first_half[(bytes+6)&0x3ff]^reverse, + first_half[(bytes+7)&0x3ff]^reverse, + ',', + bytes,bytes/2048,(bytes/8)&0xff); + + fprintf(outfile,"\n"); + int yy; + for(yy=0;yy<8;yy++) { + fprintf(outfile,"-- ["); + for(x=0;x<8;x++) { + if ((first_half[(bytes+yy)&0x3ff]^reverse)&(1<<(7-x))) fprintf(outfile,"*"); else fprintf(outfile," "); + } + fprintf(outfile,"]\n"); + } + } + } else { + // In raw mode, don't pad, or write charset twice + break; + } + + } + + } + // Fill in any missing bytes + if (bytes<4096) { + + printf("Padding output file to 4096\n"); + + if (vhdl_mode) { + fprintf(outfile,",\n"); + for(;bytes<4096;bytes+=8) { + int reverse=bytes&0x400; + if (reverse) reverse=0xff; + + fprintf(outfile,"x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\"%c -- 0x%03x (set %d, char %d)\n", + first_half[(bytes+0)&0x3ff]^reverse, + first_half[(bytes+1)&0x3ff]^reverse, + first_half[(bytes+2)&0x3ff]^reverse, + first_half[(bytes+3)&0x3ff]^reverse, + first_half[(bytes+4)&0x3ff]^reverse, + first_half[(bytes+5)&0x3ff]^reverse, + first_half[(bytes+6)&0x3ff]^reverse, + first_half[(bytes+7)&0x3ff]^reverse, + bytes<(4096-8)?',':' ', + bytes,bytes/2048,(bytes/8)&0xff); + } + } else { + // In raw mode, don't pad + } + } + if (vhdl_mode) fprintf(outfile,"%s",vhdl_suffix); + + if (outfile != NULL) { + fclose(outfile); + outfile = NULL; + } + } + + /* ============================ */ + if (mode==2) { + printf("mode=2 (hi-res prep)\n"); + // hi-res image preparation mode + + // int bytes=0; + if (width%8||height%8) { + fprintf(stderr,"Image must be multiple of 8 pixels wide and high\n"); + } + int problems=0; + int total=0; + int threes=0; + int fours=0; + int ones=0; + + int tiles[8000][8][8]; + int tile_count=0; + + int this_tile[8][8]; + + for (y=0; y=8000) { + fprintf(stderr,"Too many tiles\n"); + if (outfile != NULL) { + fclose(outfile); + outfile = NULL; + } + exit(-1); + } + } + + if (colour_count==1) ones++; + if (colour_count==3) threes++; + if (colour_count==4) fours++; + if (colour_count>2) { + printf("%d colours in card\n",colour_count); + problems++; + } + } + } + printf("%d problem tiles out of %d total tiles\n",problems,total); + printf("%d with 3, %d with 4, %d with only one colour\n",threes,fours,ones); + printf("%d unique tiles\n",tile_count); + } + + if (mode==3) { + // Output set of 16-colour sprites + int colour_count=0; + int colours[16]; + fprintf(stderr,"Scanning colour palette...\n"); + for (y=0; y=16) { + fprintf(stderr,"Too many colours. Image must be 16-colours or less.\n"); + exit(-1); + } + colours[colour_count++]=c; + } + } + } + fprintf(stderr,"%d unique colours found.\n",colour_count); + + // Avoid colour 0 if we can, to keep it for transparency + int colour_offset=0; + // if (colour_count<16) colour_offset=1; + + + int bytes_per_sprite=8*(height>>1); + while (bytes_per_sprite%64) bytes_per_sprite++; + fprintf(stderr,"Sprites are %d ($%02X) pixels high, and will occupy %d bytes (%d VIC-II sprite slots) each.\n", + height/2,height/2,bytes_per_sprite,bytes_per_sprite/64); + + + // Output palette + unsigned char red[16]; + unsigned char green[16]; + unsigned char blue[16]; + int i; + + for(i=0;i<16;i++) + { + int idx=i; + if (colour_offset) idx+=colour_offset; + if (idx>=16) idx-=16; + + red[idx]=nyblswap(colours[i]&0xff); + green[idx]=nyblswap((colours[i]>>8)&0xff); + blue[idx]=nyblswap((colours[i]>>16)&0xff); + } + // Write magic string, height of sprites in pixels, + // and the number of sprites, and how many slots per + // sprite + fwrite("M65SPRITE16",12,1,outfile); + fputc(height/2,outfile); + fputc(width/32,outfile); + fputc(bytes_per_sprite&0xff,outfile); + fputc(bytes_per_sprite>>8,outfile); + fwrite(red,16,1,outfile); + fwrite(green,16,1,outfile); + fwrite(blue,16,1,outfile); + + + // Output pixels + for (x=0; x \n"); + exit(-1); + } + + int mode=-1; + + if (!strcasecmp("logo",argv[1])) mode=0; + if (!strcasecmp("charrom",argv[1])) mode=1; + if (!strcasecmp("hires",argv[1])) mode=2; + if (!strcasecmp("sprite16",argv[1])) mode=3; + if (mode==-1) { + fprintf(stderr,"Usage: program_name \n"); + exit(-1); + } + + printf("argv[0]=%s\n", argv[0]); + printf("argv[1]=%s\n", argv[1]); + printf("argv[2]=%s\n", argv[2]); + printf("argv[3]=%s\n", argv[3]); + + printf("Reading %s\n",argv[2]); + read_png_file(argv[2]); + + printf("Processing with mode=%d and output=%s\n", mode, argv[3]); + process_file(mode,argv[3]); + + printf("done\n"); + + if (infile != NULL) { + fclose(infile); + infile = NULL; + } + + if (outfile != NULL) { + fclose(outfile); + outfile = NULL; + } + + return 0; +} + +/* ============================================================= */