From 8a43487117b23806e478603f3c8ab72e37cd1882 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Mon, 7 Oct 2019 00:56:18 +0300 Subject: [PATCH] at last sprites "work" --- .gdbinit | 1 - .gitignore | 1 + Makefile | 21 +++- src/data.asm | 3 - src/gfx.asm | 9 +- src/gfx.inc | 18 +++ src/main.asm | 11 +- tools/csprite/Makefile | 13 +++ tools/csprite/src/image.c | 264 ++++++++++++++++++++++++++++++++++++++++++ tools/csprite/src/image.h | 45 ++++++++ tools/csprite/src/main.c | 279 +++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 643 insertions(+), 22 deletions(-) create mode 100644 src/gfx.inc create mode 100644 tools/csprite/Makefile create mode 100644 tools/csprite/src/image.c create mode 100644 tools/csprite/src/image.h create mode 100644 tools/csprite/src/main.c diff --git a/.gdbinit b/.gdbinit index 47cf5fb..63600ba 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,3 +1,2 @@ target remote localhost:1234 -set architecture i8086 disp/i $pc diff --git a/.gitignore b/.gitignore index 269e431..a04e6a2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ *.bin data/ link.map +tools/csprite/csprite diff --git a/Makefile b/Makefile index aadff36..1a2fcb4 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ name = game elf = $(name).elf bin = $(name).bin -ASFLAGS = -f elf32 +ASFLAGS = -f elf32 -i src/ LDFLAGS = -m elf_i386 -T game.ld -print-gc-sections QEMU_FLAGS = -fda floppy.img -serial file:serial.log -d guest_errors @@ -23,21 +23,20 @@ boot.img: bootldr.bin $(bin) bootldr.bin: $(elf) objcopy -O binary -j '.boot*' $< $@ -$(bin): $(elf) $(data) +$(bin): $(elf) objcopy -O binary -R '.boot*' $< $@ $(elf): $(obj) $(LD) -o $@ $(obj) -Map link.map $(LDFLAGS) +src/data.o: src/data.asm $(data) + %.o: %.asm nasm -o $@ $(ASFLAGS) $< -data/sprsheet.inc: data/sprsheet.png - img2tiles -o $@ -n -t 32x32 $< - .PHONY: clean clean: - rm -f $(bin) $(obj) bootldr.bin floppy.img boot.img + rm -f $(bin) $(obj) $(data) bootldr.bin floppy.img boot.img .PHONY: disasm disasm: bootldr.disasm $(name).disasm @@ -61,3 +60,13 @@ debug: floppy.img .PHONY: sym sym: $(name).sym + + +tools/csprite/csprite: + $(MAKE) -C tools/csprite + +data/sprsheet.inc: data/sprsheet.png + tools/csprite/csprite -n sprsheet -s 32x32 $< >$@ + +#data/sprsheet.inc: data/sprsheet.png +# img2tiles -o $@ -n -t 32x32 $< diff --git a/src/data.asm b/src/data.asm index e6eab2f..c31fb5d 100644 --- a/src/data.asm +++ b/src/data.asm @@ -1,7 +1,4 @@ ; vi:filetype=nasm ts=8 sts=8 sw=8 section .data - - global sprsheet_cmap - global sprsheet_tiles %include "data/sprsheet.inc" diff --git a/src/gfx.asm b/src/gfx.asm index ca5cf74..06ff5e4 100644 --- a/src/gfx.asm +++ b/src/gfx.asm @@ -17,8 +17,8 @@ bits 32 section .text -VIDMEM_ADDR equ 0a0000h -FRAMEBUF_ADDR equ 090000h +%define GFX_ASM_ +%include "gfx.inc" REG_CRTC_STATUS equ 3dah CRTC_VBLANK_BIT equ 08h @@ -26,9 +26,8 @@ CRTC_VBLANK_BIT equ 08h REG_DAC_ADDR equ 3c8h REG_DAC_DATA equ 3c9h + extern sprsheet extern sprsheet_cmap - extern sprsheet_tiles - global init_gfx init_gfx: @@ -119,7 +118,7 @@ slow_sprite: add edi, eax ; di <- (y - 16) * 320 + (x - 16) add edi, FRAMEBUF_ADDR - mov esi, sprsheet_tiles + mov esi, sprsheet ; calculate sprite id offset (each spr is 32*32=1k) mov eax, [ebp + 8] shl eax, 10 diff --git a/src/gfx.inc b/src/gfx.inc new file mode 100644 index 0000000..de65a12 --- /dev/null +++ b/src/gfx.inc @@ -0,0 +1,18 @@ +; vi:filetype=nasm: +%ifndef GFX_INC_ +%define GFX_INC_ + +VIDMEM_ADDR equ 0a0000h +FRAMEBUF_ADDR equ 090000h + +%ifndef GFX_ASM_ + extern init_gfx + extern clear + extern swap_buffers + extern wait_vsync + extern slow_sprite + + extern sprsheet +%endif ; GFX_ASM_ + +%endif ; GFX_INC_ diff --git a/src/main.asm b/src/main.asm index 243ff0d..1cf9d6a 100644 --- a/src/main.asm +++ b/src/main.asm @@ -1,10 +1,6 @@ ; vi:filetype=nasm ts=8 sts=8 sw=8: bits 32 - extern init_gfx - extern clear - extern slow_sprite - extern wait_vsync - extern swap_buffers +%include "gfx.inc" ; this is placed at the beginning of our binary at 1mb (see game.ld) ; and it's what gets executed directly by the boot loader @@ -19,10 +15,11 @@ main: main_loop: call clear + push dword 0 push dword 100 push dword 160 - push dword 0 - call slow_sprite + push dword FRAMEBUF_ADDR + call sprsheet add esp, 16 call wait_vsync diff --git a/tools/csprite/Makefile b/tools/csprite/Makefile new file mode 100644 index 0000000..fa1448d --- /dev/null +++ b/tools/csprite/Makefile @@ -0,0 +1,13 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +bin = csprite + +CFLAGS = -pedantic -Wall -g +LDFLAGS = -lpng -lz + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff --git a/tools/csprite/src/image.c b/tools/csprite/src/image.c new file mode 100644 index 0000000..18fae9b --- /dev/null +++ b/tools/csprite/src/image.c @@ -0,0 +1,264 @@ +/* +256boss - bootable launcher for 256byte intros +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include +#include "image.h" + +int alloc_image(struct image *img, int x, int y, int bpp) +{ + memset(img, 0, sizeof *img); + img->width = x; + img->height = y; + img->bpp = bpp; + img->scansz = img->pitch = x * bpp / 8; + + if(!(img->pixels = malloc(y * img->scansz))) { + printf("failed to allocate %dx%d (%dbpp) pixel buffer\n", x, y, bpp); + return -1; + } + + /* just a guess, assume the user will fill the details, but set up reasonable + * defaults just in case... + */ + if(bpp <= 8) { + img->nchan = 1; + img->cmap_ncolors = 1 << bpp; + } else if(bpp <= 24) { + img->nchan = 3; + } else { + img->nchan = 4; + } + return 0; +} + +int load_image(struct image *img, const char *fname) +{ + int i; + FILE *fp; + png_struct *png; + png_info *info; + int chan_bits, color_type; + png_uint_32 xsz, ysz; + png_color *palette; + unsigned char **scanline; + unsigned char *dptr; + + if(!(fp = fopen(fname, "rb"))) { + printf("failed to open: %s: %s\n", fname, strerror(errno)); + return -1; + } + + if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { + fclose(fp); + return -1; + } + if(!(info = png_create_info_struct(png))) { + fclose(fp); + png_destroy_read_struct(&png, 0, 0); + return -1; + } + if(setjmp(png_jmpbuf(png))) { + fclose(fp); + png_destroy_read_struct(&png, &info, 0); + return -1; + } + + png_init_io(png, fp); + png_read_png(png, info, 0, 0); + + png_get_IHDR(png, info, &xsz, &ysz, &chan_bits, &color_type, 0, 0, 0); + img->width = xsz; + img->height = ysz; + img->nchan = png_get_channels(png, info); + img->bpp = img->nchan * chan_bits; + img->scansz = img->pitch = xsz * img->bpp / 8; + img->cmap_ncolors = 0; + + if(color_type == PNG_COLOR_TYPE_PALETTE) { + png_get_PLTE(png, info, &palette, &img->cmap_ncolors); + memcpy(img->cmap, palette, img->cmap_ncolors * sizeof *img->cmap); + } + + if(!(img->pixels = malloc(ysz * img->scansz))) { + perror("failed to allocate pixel buffer"); + fclose(fp); + png_destroy_read_struct(&png, &info, 0); + return -1; + } + dptr = img->pixels; + + scanline = (unsigned char**)png_get_rows(png, info); + for(i=0; iscansz); + dptr += img->pitch; + } + + fclose(fp); + png_destroy_read_struct(&png, &info, 0); + return 0; +} + +int save_image(struct image *img, const char *fname) +{ + int i, chan_bits, coltype; + FILE *fp; + png_struct *png; + png_info *info; + png_text txt; + unsigned char **scanline = 0; + unsigned char *pptr; + + if(!(fp = fopen(fname, "wb"))) { + printf("save_image: failed to open: %s: %s\n", fname, strerror(errno)); + return -1; + } + + if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { + fclose(fp); + return -1; + } + if(!(info = png_create_info_struct(png))) { + png_destroy_write_struct(&png, 0); + fclose(fp); + return -1; + } + + txt.compression = PNG_TEXT_COMPRESSION_NONE; + txt.key = "Software"; + txt.text = "img2tiles"; + txt.text_length = 0; + + if(setjmp(png_jmpbuf(png))) { + png_destroy_write_struct(&png, &info); + free(scanline); + fclose(fp); + return -1; + } + + switch(img->nchan) { + case 1: + if(img->cmap_ncolors > 0) { + coltype = PNG_COLOR_TYPE_PALETTE; + } else { + coltype = PNG_COLOR_TYPE_GRAY; + } + break; + case 2: + coltype = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + case 3: + coltype = PNG_COLOR_TYPE_RGB; + break; + case 4: + coltype = PNG_COLOR_TYPE_RGB_ALPHA; + break; + } + + chan_bits = img->bpp / img->nchan; + png_set_IHDR(png, info, img->width, img->height, chan_bits, coltype, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_text(png, info, &txt, 1); + + if(img->cmap_ncolors > 0) { + png_set_PLTE(png, info, (png_color*)img->cmap, img->cmap_ncolors); + } + + if(!(scanline = malloc(img->height * sizeof *scanline))) { + png_destroy_write_struct(&png, &info); + fclose(fp); + return -1; + } + + pptr = img->pixels; + for(i=0; iheight; i++) { + scanline[i] = pptr; + pptr += img->pitch; + } + png_set_rows(png, info, scanline); + + png_init_io(png, fp); + png_write_png(png, info, 0, 0); + png_destroy_write_struct(&png, &info); + free(scanline); + fclose(fp); + return 0; +} + + +int cmp_image(struct image *a, struct image *b) +{ + int i; + unsigned char *aptr = a->pixels; + unsigned char *bptr = b->pixels; + + if(a->width != b->width || a->height != b->height || a->bpp != b->bpp || a->nchan != b->nchan) { + return -1; + } + + for(i=0; iheight; i++) { + if(memcmp(aptr, bptr, a->scansz) != 0) { + return -1; + } + aptr += a->pitch; + bptr += b->pitch; + } + return 0; +} + +void blit_image(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy) +{ + int i; + unsigned char *sptr, *dptr; + + assert(src->bpp == dst->bpp); + assert(src->nchan == dst->nchan); + + if(sx < 0) { w += sx; sx = 0; } + if(sy < 0) { h += sy; sy = 0; } + if(dx < 0) { w += dx; sx -= dx; dx = 0; } + if(dy < 0) { h += dy; sy -= dy; dy = 0; } + if(sx + w >= src->width) w = src->width - sx; + if(sy + h >= src->height) h = src->height - sy; + if(dx + w >= dst->width) w = dst->width - dx; + if(dy + h >= dst->height) h = dst->height - dy; + + if(w <= 0 || h <= 0) return; + + sptr = src->pixels + sy * src->pitch + sx * src->bpp / 8; + dptr = dst->pixels + dy * dst->pitch + dx * dst->bpp / 8; + + for(i=0; ibpp / 8); + dptr += dst->pitch; + sptr += src->pitch; + } +} + +void image_color_offset(struct image *img, int offs) +{ + int i, sz = img->height * img->pitch; + unsigned char *pptr = img->pixels; + + for(i=0; i + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef IMAGE_H_ +#define IMAGE_H_ + +struct cmapent { + unsigned char r, g, b; +}; + +struct image { + int width, height; + int bpp; + int nchan; + int scansz, pitch; + int cmap_ncolors; + struct cmapent cmap[256]; + unsigned char *pixels; +}; + +int alloc_image(struct image *img, int x, int y, int bpp); +int load_image(struct image *img, const char *fname); +int save_image(struct image *img, const char *fname); + +int cmp_image(struct image *a, struct image *b); + +void blit_image(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy); + +void image_color_offset(struct image *img, int offs); + +#endif /* IMAGE_H_ */ diff --git a/tools/csprite/src/main.c b/tools/csprite/src/main.c new file mode 100644 index 0000000..dd9be02 --- /dev/null +++ b/tools/csprite/src/main.c @@ -0,0 +1,279 @@ +#include +#include +#include +#include +#include +#include +#include "image.h" + +struct rect { + int x, y, w, h; +}; + +int csprite(struct image *img, int x, int y, int xsz, int ysz); +int proc_sheet(const char *fname); +void print_usage(const char *argv0); + +int tile_xsz, tile_ysz; +struct rect rect; +int cmap_offs; +int ckey; +int fbpitch = 320; +const char *name = "sprite"; + +int main(int argc, char **argv) +{ + int i; + char *endp; + + for(i=1; i= 256) { + fprintf(stderr, "-coffset must be followed by a valid colormap offset\n"); + return 1; + } + + } else if(strcmp(argv[i], "-fbpitch") == 0) { + fbpitch = atoi(argv[++i]); + if(fbpitch <= 0) { + fprintf(stderr, "-fbpitch must be followed by a positive number\n"); + return 1; + } + + } else if(strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "-name") == 0) { + name = argv[++i]; + if(!name) { + fprintf(stderr, "%s must be followed by a name\n", argv[i - 1]); + return 1; + } + + } else if(strcmp(argv[i], "-k") == 0 || strcmp(argv[i], "-key") == 0) { + ckey = strtol(argv[++i], &endp, 10); + if(endp == argv[i] || ckey < 0 || ckey >= 256) { + fprintf(stderr, "%s must be followed by a valid color key\n", argv[i - 1]); + return 1; + } + + } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) { + print_usage(argv[0]); + return 0; + + } else { + fprintf(stderr, "invalid option: %s\n", argv[i]); + print_usage(argv[0]); + return 1; + } + + } else { + if(proc_sheet(argv[i]) == -1) { + return 1; + } + } + } + + return 0; +} + +const char *prefixfmt = + "\tglobal %s\n" + "%s:\n" + "\tmov eax, [esp + 12]\n" + "\tmov ecx, %d\n" + "\tmul ecx\n" + "\tadd eax, [esp + 8]\n" + "\tadd eax, [esp + 4]\n" + "\tmov edx, eax\n" + "\tmov eax, [esp + 16]\n" + "\tjmp [.tiletab + eax * 4]\n\n" + ".tiletab:\n"; + +int proc_sheet(const char *fname) +{ + int i, j, num_xtiles, num_ytiles, xsz, ysz, tidx; + struct image img; + + if(load_image(&img, fname) == -1) { + fprintf(stderr, "failed to load image: %s\n", fname); + return -1; + } + if(rect.w <= 0) { + rect.w = img.width; + rect.h = img.height; + } + + if(tile_xsz <= 0) { + num_xtiles = num_ytiles = 1; + xsz = rect.w; + ysz = rect.h; + } else { + num_xtiles = rect.w / tile_xsz; + num_ytiles = rect.h / tile_ysz; + xsz = tile_xsz; + ysz = tile_ysz; + } + + printf(prefixfmt, name, name, fbpitch); + for(i=0; ipixels + y * img->scansz + x; + struct csop *ops, *optr; + + ops = optr = alloca((xsz + 1) * ysz * sizeof *ops); + + for(i=0; i 0) { + optr++->op = CSOP_ENDL; + } + + for(j=0; j= 0); + optr->op = mode; + optr->len = j - start; + optr++; + } + mode = new_mode; + start = j; + } + pptr++; + } + pptr += img->scansz - xsz; + + if(mode != -1) { + assert(start >= 0); + optr->op = mode; + optr->len = xsz - start; + optr++; + } + } + numops = optr - ops; + + pptr = img->pixels + y * img->scansz + x; + optr = ops; + skip_acc = 0; + /* edx points to dest */ + for(i=0; iop) { + case CSOP_SKIP: + skip_acc += optr->len; + pptr += optr->len; + break; + + case CSOP_ENDL: + skip_acc += fbpitch - xsz; + pptr += img->scansz - xsz; + break; + + case CSOP_COPY: + if(skip_acc) { + printf("\tadd edx, %d\n", skip_acc); + skip_acc = 0; + } + + for(j=0; jlen / 4; j++) { + printf("\tmov dword [edx + %d], 0x%x\n", j * 4, *(uint32_t*)pptr); + pptr += 4; + } + j *= 4; + switch(optr->len % 4) { + case 3: + printf("\tmov byte [edx + %d], 0x%x\n", j++, (unsigned int)*pptr++); + case 2: + printf("\tmov word [edx + %d], 0x%x\n", j, (unsigned int)*(uint16_t*)pptr); + pptr += 2; + j += 2; + break; + case 1: + printf("\tmov byte [edx + %d], 0x%x\n", j++, (unsigned int)*pptr++); + break; + } + + skip_acc = optr->len; + break; + + default: + printf("\t; invalid op: %d\n", optr->op); + } + optr++; + } + printf("\tret\n"); + + return 0; +} + +void print_usage(const char *argv0) +{ + printf("Usage: %s [options] \n", argv0); + printf("Options:\n"); + printf(" -s,-size : tile size (default: whole image)\n"); + printf(" -r,-rect : use rectangle of the input image (default: whole image)\n"); + printf(" -coffset : colormap offset [0, 255] (default: 0)\n"); + printf(" -fbpitch : target framebuffer pitch (scanline size in bytes)\n"); + printf(" -k,-key : color-key for transparency (default: 0)\n"); + printf(" -h: print usage and exit\n"); +} -- 1.7.10.4