commit e77442b840627d0127fbcad289a4e2d65b7e613e Author: empathicqubit Date: Fri Oct 8 19:54:21 2021 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60f9f45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.tab +*.o +*.s?? +*~ +*.dblite +build/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..880a75e --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Main", + "includePath": [ + "${HOME}/.vscode/extensions/entan-gl.cc65-vice-4.8.1/dist/cc65/include", + "${workspaceFolder}/src" + ], + "intelliSenseMode": "gcc-x64", + "compilerPath": "${HOME}/.vscode/extensions/entan-gl.cc65-vice-4.8.1/dist/cc65/bin/cc65", + "cStandard": "c99", + "cppStandard": "c++98" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0e1c476 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + // Verwendet IntelliSense zum Ermitteln möglicher Attribute. + // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen. + // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + + + + + + + + + + + + + { + "type": "cc65-vice", + "request": "launch", + "name": "CC65 Vice Launch", + "program": "${workspaceFolder}/build/msprite.d64", + "build": { + "cwd": "${workspaceFolder}", + "command": "scons", + "args": [] + }, + "debugFile": "${workspaceFolder}/build/msprite.dbg", + "mapFile": "${workspaceFolder}/build/msprite.map", + "labelFile": "${workspaceFolder}/build/msprite.lbl", + "stopOnEntry": true, + "stopOnExit": true, + "viceArgs": [ + "-model", + "ntsc" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ced7acc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,44 @@ +{ + "files.associations": { + "string.h": "c", + "memory.h": "c", + "cstdlib": "c", + "*.tcc": "c", + "memory": "c", + "new": "c", + "array": "c", + "string": "c", + "vector": "c", + "string_view": "c", + "functional": "c", + "istream": "c", + "limits": "c", + "ostream": "c", + "ratio": "c", + "sstream": "c", + "streambuf": "c", + "stdio.h": "c", + "atomic": "c", + "hash_map": "c", + "deque": "c", + "list": "c", + "iterator": "c", + "memory_resource": "c", + "type_traits": "c", + "typeinfo": "c", + "algorithm": "c", + "numeric": "c", + "random": "c", + "fstream": "c", + "iosfwd": "c", + "map": "c", + "queue": "c", + "utility": "c", + "stdbool.h": "c", + "errno.h": "c", + "_vic2.h": "c", + "c64.h": "c", + "cstring": "c", + "6502.h": "c" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..6f7375e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "type": "shell", + "group": { + "kind": "build", + "isDefault": true + }, + "command": "sh scons.sh" + } + ] +} \ No newline at end of file diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..739c68d --- /dev/null +++ b/SConstruct @@ -0,0 +1,60 @@ +# vim: syntax=python +import os + +screen_start = 'C000' +sprite_start = 'C400' +character_start = 'D800' + +if 'CC65_HOME' in os.environ: + cc65_home = os.environ['CC65_HOME'] +else: + cc65_home = '' + +if 'DISPLAY' in os.environ: + display = os.environ['DISPLAY'] +else: + display = '' + +print(cc65_home) +print(os.environ['PATH']) + +env = Environment( + BUILDERS = {}, + ENV = { + 'PATH': os.environ["PATH"], + 'CC65_HOME': cc65_home, + 'DISPLAY': display, + }, + AS = 'ca65', + ASFLAGS = ['-t', 'c64', '-g'], + CC = 'cl65', + CFLAGS = ['-DSCREEN_START=0x'+screen_start, '-DSPRITE_START=0x'+sprite_start, '-DCHARACTER_START=0x'+character_start, '-O', '-Osir', '-t', 'c64', '-C', 'c64.cfg', '-g', '-Wc', '--debug-tables', '-Wc', '${SOURCE}.tab'], + LINK = 'cl65', + LINKFLAGS = ['-g', '-C', 'c64.cfg', '-D__HIMEM__=$' + screen_start, '-Wl', '--dbgfile,build/msprite.dbg', '-Wl', '-Lnbuild/msprite.lbl', '-Wl', '--mapfile,build/msprite.map'] +) + +prg = env.Program(target=["build/msprite.prg", "build/msprite.map", "build/msprite.dbg", "build/msprite.lbl"], source=[Glob('src/*.c'), Glob('src/*_asm.s')]) + +sprites = Glob('res/sprites/*.spd') + +disk_files = [] +disk_files.append(prg[0]) +disk_files.append(sprites) + +def disk_func(target, source, env): + if not target[0].exists(): + env.Execute('c1541 -format "canada,01" d64 "%s"' % target[0]) + changes = [] + for src in source: + basename = os.path.basename(str(src)) + typename = 's' + if basename.endswith('prg'): + typename = 'p' + changes.append(""" -delete '%s' -write '%s' '%s,%s'""" % (basename, str(src), basename, typename)) + env.Execute("""c1541 -attach '%s' %s """ % (str(target[0]), ''.join(changes))) + +disk_image = env.Command(target=["build/msprite.d64"], source=disk_files, action=disk_func) + +env.Alias('build', disk_image) + +Default(disk_image) diff --git a/scons.sh b/scons.sh new file mode 100644 index 0000000..d76e379 --- /dev/null +++ b/scons.sh @@ -0,0 +1,9 @@ +DIR="$( cd "$( dirname "$(readlink -f "$0")" )" > /dev/null && pwd )" +SHPATH="$(ls -t "$HOME/.vscode/extensions/entan-gl.cc65-vice-"*"/build.sh" | head -1)" +if test -e "$DIR/../../../build.sh"; then + sh "$DIR/../../../build.sh" scons "$@" +elif test -e "$SHPATH"; then + sh "$SHPATH" scons "$@" +else + scons "$@" +fi diff --git a/src/c64.h b/src/c64.h new file mode 100644 index 0000000..6d334a3 --- /dev/null +++ b/src/c64.h @@ -0,0 +1,284 @@ +// From cc65/cc65/asminc/c64.inc +// +// C64 generic definitions. Stolen from Elite128 +// +// I prefer using this over some of the builtin structs because it is easier to +// match to assembly code docs. + +// --------------------------------------------------------------------------- +// Zero page, Commodore stuff + +#define CPU_PORT 0x01 + +#define CPU_PORT_BANK_MASK 0x07 + +#define CPU_PORT_BANK_ALL_RAM_VISIBLE_IO_INVISIBLE 0x00 +#define CPU_PORT_BANK_LORAM_HIRAM_VISIBLE 0x01 +#define CPU_PORT_BANK_LORAM_VISIBLE_KERNAL_VISIBLE 0x02 +#define CPU_PORT_BANK_BASIC_ROM_VISIBLE_KERNAL_VISIBLE 0x03 + +#define CPU_PORT_BANK_IO_VISIBLE_CHARACTER_ROM_INVISIBLE 0x04 + +#define VARTAB 0x2D +#define MEMSIZE 0x37 // Pointer to highest BASIC RAM location (+1) +#define TXTPTR 0x7A // Pointer into BASIC source code +#define TIME 0xA0 // 60 HZ clock +#define FNAM_LEN 0xB7 // Length of filename +#define SECADR 0xB9 // Secondary address +#define DEVNUM 0xBA // Device number +#define FNAM 0xBB // Pointer to filename +#define KEY_COUNT 0xC6 // Number of keys in input buffer +#define RVS 0xC7 // Reverse flag +#define CURS_FLAG 0xCC // 1 cursor off +#define CURS_BLINK 0xCD // Blink counter +#define CURS_CHAR 0xCE // Character under the cursor +#define CURS_STATE 0xCF // Cursor blink state +#define SCREEN_PTR 0xD1 // Pointer to current char in text screen +#define CURS_X 0xD3 // Cursor column +#define CURS_Y 0xD6 // Cursor row +#define CRAM_PTR 0xF3 // Pointer to current char in color RAM +#define FREKZP 0xFB // Five unused bytes + +#define SCREEN_BYTES 1000 + +#define SCREEN_BITMAP_WIDTH 320 +#define SCREEN_BITMAP_HEIGHT 200 +#define SCREEN_BITMAP_SIZE 8000 + +#define SCREEN_SPRITE_WIDTH 512 +#define SCREEN_SPRITE_HEIGHT 255 + +#define SCREEN_SPRITE_BORDER_Y_START 50 +#define SCREEN_SPRITE_BORDER_Y_END 250 + +#define SCREEN_SPRITE_BORDER_X_START 20 +#define SCREEN_SPRITE_BORDER_X_END 350 + +#define SCREEN_SPRITE_BORDER_WIDTH (SCREEN_SPRITE_BORDER_X_END - SCREEN_SPRITE_BORDER_X_START) + +#define BASIC_BUF 0x200 // Location of command-line +#define BASIC_BUF_LEN 89 // Maximum length of command-line + +#define CHARCOLOR 0x286 +#define CURS_COLOR 0x287 // Color under the cursor +#define SCREEN_IO_HI_PTR 0x288 +#define PALFLAG 0x2A6 // 0x01 PAL, 0x00 NTSC + +#define KBDREPEAT 0x28a +#define KBDREPEATRATE 0x28b +#define KBDREPEATDELAY 0x28c + +// --------------------------------------------------------------------------- +// Vector and other locations + +#define IRQVec 0x0314 +#define BRKVec 0x0316 +#define NMIVec 0x0318 + +#define IRQVec_DEFAULT_ISR 0xEA31 + +// --------------------------------------------------------------------------- +// Screen size + +#define XSIZE 40 +#define YSIZE 25 + +// --------------------------------------------------------------------------- +// I/O: VIC + +#define CHARACTER_ROM 0xD000 +#define CHARACTER_ROM_SIZE 0x1000 + +#define VIC_SPR0_X 0xD000 +#define VIC_SPR0_Y 0xD001 +#define VIC_SPR1_X 0xD002 +#define VIC_SPR1_Y 0xD003 +#define VIC_SPR2_X 0xD004 +#define VIC_SPR2_Y 0xD005 +#define VIC_SPR3_X 0xD006 +#define VIC_SPR3_Y 0xD007 +#define VIC_SPR4_X 0xD008 +#define VIC_SPR4_Y 0xD009 +#define VIC_SPR5_X 0xD00A +#define VIC_SPR5_Y 0xD00B +#define VIC_SPR6_X 0xD00C +#define VIC_SPR6_Y 0xD00D +#define VIC_SPR7_X 0xD00E +#define VIC_SPR7_Y 0xD00F +#define VIC_SPR_HI_X 0xD010 +#define VIC_SPR_ENA 0xD015 +#define VIC_SPR_EXP_Y 0xD017 +#define VIC_SPR_EXP_X 0xD01D +#define VIC_SPR_MCOLOR 0xD01C +#define VIC_SPR_BG_PRIO 0xD01B + +#define VIC_SPR_MCOLOR0 0xD025 +#define VIC_SPR_MCOLOR1 0xD026 + +#define VIC_SPR0_COLOR 0xD027 +#define VIC_SPR1_COLOR 0xD028 +#define VIC_SPR2_COLOR 0xD029 +#define VIC_SPR3_COLOR 0xD02A +#define VIC_SPR4_COLOR 0xD02B +#define VIC_SPR5_COLOR 0xD02C +#define VIC_SPR6_COLOR 0xD02D +#define VIC_SPR7_COLOR 0xD02E + +#define VIC_CTRL1 0xD011 +#define VIC_CTRL2 0xD016 + +#define VIC_SPR_SIZE 0x40 +#define VIC_SPR_COUNT 0x08 + +#define VIC_SPR_WIDTH 24 +#define VIC_SPR_HEIGHT 21 + +#define JOY_ANY_MASK (JOY_UP_MASK | JOY_DOWN_MASK | JOY_LEFT_MASK | JOY_RIGHT_MASK | JOY_BTN_1_MASK) + +#define VIC_CTRL1_BITMAP_ON 0x20 +#define VIC_CTRL1_HLINE_MSB 0x80 +#define VIC_CTRL2_MULTICOLOR_ON 0x10 + +#define VIC_HLINE 0xD012 + +#define VIC_LPEN_X 0xD013 +#define VIC_LPEN_Y 0xD014 + +#define VIC_VIDEO_ADR 0xD018 + +// Character memory pointer bits MSB-LSB +#define VIC_VIDEO_ADR_CHAR_PTR_MASK 0x0E +#define VIC_VIDEO_ADR_CHAR_PTR0 0x08 +#define VIC_VIDEO_ADR_CHAR_PTR1 0x04 +#define VIC_VIDEO_ADR_CHAR_PTR2 0x02 + +#define VIC_VIDEO_ADR_CHAR_DIVISOR 0x800 +#define VIC_VIDEO_ADR_SCREEN_DIVISOR 0x400 + +#define VIC_VIDEO_ADR_CHAR_BANK_GRAPHICS_OFFSET 0x1000 +#define VIC_VIDEO_ADR_CHAR_BANK_SMALLCASE_OFFSET 0x1800 + +// Screen memory pointer bits MSB-LSB +#define VIC_VIDEO_ADR_SCREEN_PTR_MASK 0xF0 +#define VIC_VIDEO_ADR_SCREEN_PTR0 0x80 +#define VIC_VIDEO_ADR_SCREEN_PTR1 0x40 +#define VIC_VIDEO_ADR_SCREEN_PTR2 0x20 +#define VIC_VIDEO_ADR_SCREEN_PTR3 0x10 + +#define VIC_BANK_SIZE 0x4000 + +#define VIC_IRR 0xD019 // Interrupt request register +#define VIC_IMR 0xD01A // Interrupt mask register + +#define VIC_IRQ_RASTER 0x01 +#define VIC_IRQ_SPRITE_BG 0x02 +#define VIC_IRQ_SPRITE_SPRITE 0x04 +#define VIC_IRQ_LIGHT_PEN 0x08 + +#define COLOR_RAM_SIZE 1000 + +#define VIC_BORDERCOLOR 0xD020 +#define VIC_BG_COLOR0 0xD021 +#define VIC_BG_COLOR1 0xD022 +#define VIC_BG_COLOR2 0xD023 +#define VIC_BG_COLOR3 0xD024 +// 128 stuff: +#define VIC_KBD_128 0xD02F // Extended kbd bits (visible in 64 mode) +#define VIC_CLK_128 0xD030 // Clock rate register (visible in 64 mode) + +// I/O: SID + +#define SID_S1Lo 0xD400 +#define SID_S1Hi 0xD401 +#define SID_PB1Lo 0xD402 +#define SID_PB1Hi 0xD403 +#define SID_Ctl1 0xD404 +#define SID_AD1 0xD405 +#define SID_SUR1 0xD406 + +#define SID_S2Lo 0xD407 +#define SID_S2Hi 0xD408 +#define SID_PB2Lo 0xD409 +#define SID_PB2Hi 0xD40A +#define SID_Ctl2 0xD40B +#define SID_AD2 0xD40C +#define SID_SUR2 0xD40D + +#define SID_S3Lo 0xD40E +#define SID_S3Hi 0xD40F +#define SID_PB3Lo 0xD410 +#define SID_PB3Hi 0xD411 +#define SID_Ctl3 0xD412 +#define SID_AD3 0xD413 +#define SID_SUR3 0xD414 + +#define SID_FltLo 0xD415 +#define SID_FltHi 0xD416 +#define SID_FltCtl 0xD417 +#define SID_Amp 0xD418 +#define SID_ADConv1 0xD419 +#define SID_ADConv2 0xD41A +#define SID_Noise 0xD41B +#define SID_Read3 0xD41C + +// I/O: VDC (128 only) + +#define VDC_INDEX 0xD600 +#define VDC_DATA 0xD601 + +// I/O: Complex Interface Adapters + +#define CIA1_PRA 0xDC00 // Port A +#define CIA1_PRB 0xDC01 // Port B +#define CIA1_DDRA 0xDC02 // Data direction register for port A +#define CIA1_DDRB 0xDC03 // Data direction register for port B +#define CIA1_TA 0xDC04 // 16-bit timer A +#define CIA1_TB 0xDC06 // 16-bit timer B +#define CIA1_TOD10 0xDC08 // Time-of-day tenths of a second +#define CIA1_TODSEC 0xDC09 // Time-of-day seconds +#define CIA1_TODMIN 0xDC0A // Time-of-day minutes +#define CIA1_TODHR 0xDC0B // Time-of-day hours +#define CIA1_SDR 0xDC0C // Serial data register +#define CIA1_ICR 0xDC0D // Interrupt control register +#define CIA1_CRA 0xDC0E // Control register for timer A +#define CIA1_CRB 0xDC0F // Control register for timer B + +#define CIA1_CR_START_STOP 0x01 + +#define CIA2_PRA_VIC_BANK0 0x02 +#define CIA2_PRA_VIC_BANK1 0x01 + +#define CIA2_PRA 0xDD00 +#define CIA2_PRB 0xDD01 +#define CIA2_DDRA 0xDD02 +#define CIA2_DDRB 0xDD03 +#define CIA2_TA 0xDD04 +#define CIA2_TB 0xDD06 +#define CIA2_TOD10 0xDD08 +#define CIA2_TODSEC 0xDD09 +#define CIA2_TODMIN 0xDD0A +#define CIA2_TODHR 0xDD0B +#define CIA2_SDR 0xDD0C +#define CIA2_ICR 0xDD0D +#define CIA2_CRA 0xDD0E +#define CIA2_CRB 0xDD0F + +// Super CPU +#define SCPU_VIC_Bank1 0xD075 +#define SCPU_Slow 0xD07A +#define SCPU_Fast 0xD07B +#define SCPU_EnableRegs 0xD07E +#define SCPU_DisableRegs 0xD07F +#define SCPU_Detect 0xD0BC + +// Processor Port at 0x01 + +#define LORAM 0x01 // Enable the basic rom +#define HIRAM 0x02 // Enable the kernal rom +#define IOEN 0x04 // Enable I/O +#define CASSDATA 0x08 // Cassette data +#define CASSPLAY 0x10 // Cassette: Play +#define CASSMOT 0x20 // Cassette motor on +#define TP_FAST 0x80 // Switch Rossmoeller TurboProcess to fast mode + +#define RAMONLY 0xF8 // (~(LORAM | HIRAM | IOEN)) & 0xFF diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ecb35a4 --- /dev/null +++ b/src/main.c @@ -0,0 +1,669 @@ +#include +#include +#include +#include +#include <6502.h> +#include +#include +#include "c64.h" +#include + +#define IRQ_STACK_SIZE 128 + +extern void updatepalntsc(void); + +/* Check if system is PAL + */ +void pal_system(void) { + updatepalntsc(); +} + +unsigned char* irq_stack; + +#define SCREEN_LAST_RASTER SCREEN_SPRITE_BORDER_Y_START + SCREEN_BITMAP_HEIGHT +#define SPRITE_BUFFER_LINE_COUNT 8 +#define SPRITE_BUFFER_LINE_HEIGHT (SCREEN_BITMAP_HEIGHT / SPRITE_BUFFER_LINE_COUNT) +#define SCREEN_FIRST_RASTER SCREEN_SPRITE_BORDER_Y_START - SPRITE_BUFFER_LINE_HEIGHT + +/** Disable the IO page + */ +void __fastcall__ hide_io(void) { + *(unsigned char *)CIA1_CRA &= ~CIA1_CR_START_STOP; + + *(unsigned char *)CPU_PORT &= ~CPU_PORT_BANK_IO_VISIBLE_CHARACTER_ROM_INVISIBLE; +} + +/** Enable the IO page + */ +void __fastcall__ show_io(void) { + *(unsigned char *)CPU_PORT |= CPU_PORT_BANK_IO_VISIBLE_CHARACTER_ROM_INVISIBLE; + + *(unsigned char *)CIA1_CRA |= CIA1_CR_START_STOP; +} + +/** Copies character ROM to RAM. + * @param use_graphics_charset - Use fancy graphics chars with no lowercase + */ +void character_init(bool use_graphics_charset) { + hide_io(); + memcpy(CHARACTER_START, CHARACTER_ROM + (!use_graphics_charset * VIC_VIDEO_ADR_CHAR_DIVISOR), CHARACTER_ROM_SIZE); + show_io(); +} + +/** Reset the screen to VIC bank #3 + * @param clear - Clear the screen before switching to it + */ +void screen_init (bool clear) { + unsigned char screen_ptr = ((SCREEN_START % VIC_BANK_SIZE) / VIC_VIDEO_ADR_SCREEN_DIVISOR) << 4; + + // Update kernal + *(unsigned char *)SCREEN_IO_HI_PTR = SCREEN_START >> 8; + + // Switch to bank 3 + *(unsigned char *)CIA2_PRA &= ~3; + + // Switch to screen memory + VIC.addr &= ~(VIC_VIDEO_ADR_SCREEN_PTR_MASK); + VIC.addr |= screen_ptr; + + // Switch to character memory + VIC.addr &= ~(VIC_VIDEO_ADR_CHAR_PTR_MASK); + VIC.addr |= ((CHARACTER_START % VIC_BANK_SIZE) / VIC_VIDEO_ADR_CHAR_DIVISOR) << 1; + + // Fix HLINE IRQ + VIC.rasterline = SCREEN_FIRST_RASTER; + VIC.ctrl1 &= ~VIC_CTRL1_HLINE_MSB; + + // Switch off bitmap mode + VIC.ctrl1 &= ~VIC_CTRL1_BITMAP_ON; + VIC.ctrl2 &= ~VIC_CTRL2_MULTICOLOR_ON; + + // Enable raster interrupts + VIC.imr |= VIC_IRQ_RASTER; + + if(clear) { + clrscr(); + printf("hallo!"); + + VIC.bgcolor0 = COLOR_BLACK; + VIC.bgcolor1 = COLOR_BLACK; + VIC.bgcolor2 = COLOR_BLACK; + VIC.bgcolor3 = COLOR_BLACK; + VIC.bordercolor = COLOR_BLACK; + bordercolor(COLOR_BLACK); + } +} + +bool is_pal = false; + +unsigned char setup_irq_handler(unsigned char (*handler)(void)) { + if(!irq_stack) { + if(!(irq_stack = malloc(IRQ_STACK_SIZE))) { + return EXIT_FAILURE; + } + } + + set_irq(handler, irq_stack, IRQ_STACK_SIZE); + + return EXIT_SUCCESS; +} + +unsigned char raster_clock = 0; +unsigned int game_clock = 0; +unsigned int last_updated = 0; + +void fatal(unsigned char* message) { + while(true); +} + +#define SPD_PADDING 55 + +struct spd_sprite { + unsigned char sprite_data[63]; + unsigned char metadata; +}; +typedef struct spd_sprite spd_sprite; + +struct spd { + unsigned char padding[SPD_PADDING]; + unsigned char magic[3]; + unsigned char version; + unsigned char sprite_count; + unsigned char animation_count; + unsigned char background_color; + unsigned char multicolor_0; + unsigned char multicolor_1; + spd_sprite sprites[]; +}; +typedef struct spd spd; + +#define SPRITE_MAX 80 + +/* Load a sprite sheet in SpritePad format + * @param filename - The filename on disk + * @return - Whether the sheet successfully loaded into memory. + */ +unsigned char spritesheet_load(unsigned char* filename) { + static FILE* fp; + static spd* spd_data; + static unsigned char* header; + + fp = fopen(filename, "rb"); + if(!fp) { + if(errno != 0) { + return errno; + } + else { + return EXIT_FAILURE; + } + } + + // Used to write this directly to the memory area, but we can't read it if we do. + if(!(header = calloc(1, VIC_SPR_SIZE)) + || !fread(header + SPD_PADDING, VIC_SPR_SIZE - SPD_PADDING, 1, fp)) { + fclose(fp); + return EXIT_FAILURE; + } + + spd_data = (spd*)header; + + if(spd_data->sprite_count + 1 > SPRITE_MAX) { + free(header); + fclose(fp); + return EXIT_FAILURE; + } + + memcpy(SPRITE_START, header, VIC_SPR_SIZE); + + if(!fread(SPRITE_START + VIC_SPR_SIZE, VIC_SPR_SIZE, spd_data->sprite_count + 1, fp)) { + fclose(fp); + return EXIT_FAILURE; + } + + // FIXME tf is background? The background is transparent! + + VIC.spr_mcolor0 = spd_data->multicolor_0; + VIC.spr_mcolor1 = spd_data->multicolor_1; + + free(header); + fclose(fp); + + return EXIT_SUCCESS; +} + +#define SPD_SPRITE_MULTICOLOR_ENABLE_MASK 0x80 +#define SPD_SPRITE_COLOR_VALUE_MASK 0x0F + +struct sprite_data { + unsigned char color; + unsigned char pointer; + unsigned char lo_x; + unsigned char lo_y; + + unsigned char ena; + unsigned char hi_x; + unsigned char dbl; + unsigned char multi; +}; +typedef struct sprite_data* sprite_handle; + +#define SPRITE_POOL_SIZE 32 +struct sprite_data _sprite_pool[SPRITE_POOL_SIZE]; +sprite_handle _sprite_list[SPRITE_POOL_SIZE]; +unsigned char sprite_count; + +void init_sprite_pool(void) { + memset(&_sprite_pool, 0x00, sizeof(struct sprite_data) * SPRITE_POOL_SIZE); + memset(&_sprite_list, NULL, sizeof(sprite_handle) * SPRITE_POOL_SIZE); +} + +void set_sprite_pointer(sprite_handle handle, unsigned char sprite_pointer) { + static spd_sprite* sprite; + + sprite = (spd_sprite*)(SCREEN_START + sprite_pointer * VIC_SPR_SIZE); + + handle->pointer = sprite_pointer; + handle->color = sprite->metadata & SPD_SPRITE_COLOR_VALUE_MASK; + if(sprite->metadata & SPD_SPRITE_MULTICOLOR_ENABLE_MASK) { + handle->multi = 1<<((handle - _sprite_pool)%VIC_SPR_COUNT); + } + else { + handle->multi = 0; + } +} + +void set_sprite_graphic(sprite_handle handle, unsigned char sheet_index) { + static spd* s = (spd*)SPRITE_START; + set_sprite_pointer(handle, ((unsigned int)(&s->sprites[sheet_index]) % VIC_BANK_SIZE) / VIC_SPR_SIZE); +} + +void set_sprite_position(sprite_handle a, unsigned int x, unsigned char y) { + static sprite_handle *start_handle, *comp_handle, *current_handle; + static sprite_handle arg, comp; + static unsigned char index, last_index, hi_mask, comp_y, yarg; + static bool direction; + + yarg = y; + arg = a; + + comp_y = arg->lo_y; + hi_mask = 1<<(index%VIC_SPR_COUNT); + if(x>>8) { + arg->hi_x = hi_mask; + } + else { + arg->hi_x = 0; + } + + arg->lo_x = (unsigned char)x; + if(yarg == comp_y) { + return; + } + arg->lo_y = yarg; + + index = 0; + for( + start_handle = _sprite_list; + *start_handle != arg; + start_handle++ + ) { + index++; + } + + if(yarg > comp_y) { + last_index = sprite_count - 1; + if(last_index == index) { + return; + } + direction = true; + } + else { + if(index == 0) { + return; + } + direction = false; + } + + current_handle = start_handle; + do { + if(direction) { + comp_handle = current_handle + 1; + } + else { + comp_handle = current_handle - 1; + } + comp = *comp_handle; + if(( + direction + ? (yarg <= comp->lo_y + || index == last_index) + : (comp->lo_y <= yarg + || index == 0) + ) + ) { + if(current_handle == start_handle) { + break; + } + + comp = arg; + } + + hi_mask = 1<<(index%VIC_SPR_COUNT); + + __asm__("lda %v", comp); + __asm__("ldx %v+1", comp); + __asm__("ldy #%b", offsetof(struct sprite_data, ena)); + __asm__("sta ptr1"); + __asm__("stx ptr1+1"); + + __asm__("ldx %v", hi_mask); + __asm__("loop: lda (ptr1),Y"); + __asm__("beq done"); + __asm__("txa"); + __asm__("sta (ptr1),Y"); + __asm__("done: iny"); + __asm__("tya"); + __asm__("sbc #%b", offsetof(struct sprite_data, multi)); + __asm__("bne loop"); + + if(comp == arg) { + *current_handle = comp; + break; + } + + *current_handle = comp; + *comp_handle = arg; + + if(direction) { + index++; + } + else { + index--; + } + + if(direction) { + current_handle++; + } + else { + current_handle--; + } + } while (true); +} + +void discard_sprite(sprite_handle handle) { + set_sprite_position(handle, 0xff, 0xff); + sprite_count--; + _sprite_list[sprite_count] = NULL; +} + +sprite_handle new_sprite(bool dbl) { + static sprite_handle handle; + + handle = &_sprite_pool[sprite_count]; + _sprite_list[sprite_count] = handle; + handle->dbl = dbl<<(sprite_count%VIC_SPR_COUNT); + handle->ena = 1<<(sprite_count%VIC_SPR_COUNT); + handle->lo_x = 0xfe; + handle->lo_y = 0xfe; + sprite_count++; + + return handle; +} + +#define SPR_POINTERS SCREEN_START + 0x3F8 + +unsigned char main_raster_irq(void) { + static unsigned char sprite_index = 0xff; + static unsigned char vic_sprite = 0; + static unsigned char current_y = 0; + static unsigned char new_y = 0; + static unsigned char hi_mask = 0; + static sprite_handle cs; + + if(!(VIC.irr & VIC_IRQ_RASTER)) { + return IRQ_NOT_HANDLED; + } + + VIC.irr |= VIC_IRQ_RASTER; + if(sprite_index == 0xff) { + sprite_index = 0; + vic_sprite = 0; + + // NTSC frame skip + if(is_pal) { + game_clock++; + } + else if(++raster_clock < 6) { + game_clock++; + } + else { + raster_clock = 0; + } + } + + cs = _sprite_list[sprite_index]; + new_y = cs->lo_y; + + do { + current_y = new_y; + + hi_mask = ~(1<= VIC_SPR_COUNT) { + vic_sprite = 0; + } + + // sprite_index++ + __asm__("ldx %v", sprite_index); + __asm__("inx"); + __asm__("txa"); + + // if(sprite_index == sprite_count) + __asm__("cmp %v", sprite_count); + + // else + __asm__("bne moarcs"); + + // then + __asm__("lda #$ff"); + __asm__("sta %v", sprite_index); + __asm__("sta %w", VIC_HLINE); + return IRQ_HANDLED; + + // store the new sprite index + __asm__("moarcs:"); + __asm__("sta %v", sprite_index); + + __asm__("asl"); + __asm__("tax"); + + // cs = _sprite_list[sprite_index] + __asm__("ldy %v,X", _sprite_list); + __asm__("sty %v", cs); + __asm__("sty ptr1"); + + __asm__("inx"); + __asm__("ldy %v,X", _sprite_list); + __asm__("sty %v+1", cs); + __asm__("sty ptr1+1"); + + // new_y = cs->lo_y + __asm__("ldy #%b", offsetof(struct sprite_data, lo_y)); + __asm__("lda (ptr1),Y"); + __asm__("sta %v", new_y); + + // if new_y >= current_y + buffer + __asm__("lda %v", current_y); + __asm__("clc"); + __asm__("adc #%b", VIC_SPR_HEIGHT - 2); + __asm__("cmp %v", new_y); + __asm__("bcc exitloop"); + } while(true); + __asm__("exitloop: "); + + return IRQ_HANDLED; +} + +#define WAW_SPRITE_COUNT 9 +#define WAW_SPRITE_OFFSET 0 +#define WAW_COLUMNS 3 +#define WAW_ROWS 3 +#define WAW_MINMOUTH -(VIC_SPR_HEIGHT / 2) +#define WAW_MAXMOUTH VIC_SPR_HEIGHT +#define WAW_MAXFLOAT VIC_SPR_HEIGHT * 2 * WAW_ROWS +#define WAW_MOUTHINDEX 7 +#define WAW_MOUTHSPEED 5 +#define WAW_MOVESPEED 3 + +struct waw { + unsigned int x; + unsigned char y; + signed char mouth_offset; + bool mouth_direction; + bool float_direction; + sprite_handle sprites[WAW_SPRITE_COUNT]; +}; +typedef struct waw waw; + +void init_waw(waw* w) { + static unsigned char i, j, x, y, sprite_x, sprite_y, idx; + static waw* waw; + static sprite_handle sprite; + static sprite_handle* sprites; + waw = w; + + x = waw->x + SCREEN_SPRITE_BORDER_X_START; + y = waw->y + SCREEN_SPRITE_BORDER_Y_START; + waw->y = y; + + idx = 0; + sprites = waw->sprites; + for(i = 0; i < WAW_COLUMNS; i++) { + sprite_y = y + i * VIC_SPR_HEIGHT * 2; + for(j = 0; j < WAW_ROWS; j++) { + sprite_x = x + j * VIC_SPR_WIDTH * 2; + + sprite = new_sprite(true); + set_sprite_graphic(sprite, WAW_SPRITE_OFFSET + idx); + if(idx == WAW_MOUTHINDEX) { + set_sprite_position(sprite, sprite_x, sprite_y + waw->mouth_offset); + } + else { + set_sprite_position(sprite, sprite_x, sprite_y); + } + sprites[idx] = sprite; + idx++; + } + } +} + +void update_waw(waw* w) { + static unsigned int sprite_x; + static unsigned char y, sprite_y; + static unsigned char idx; + static signed char change_y, mouth_offset; + static sprite_handle* current_sprite; + static sprite_handle sprite; + static waw* waw; + waw = w; + mouth_offset = waw->mouth_offset; + if(waw->mouth_direction) { + mouth_offset+=WAW_MOUTHSPEED; + if(mouth_offset > WAW_MAXMOUTH) { + mouth_offset = WAW_MAXMOUTH; + waw->mouth_direction = false; + } + } + else { + mouth_offset-=WAW_MOUTHSPEED; + if(mouth_offset < WAW_MINMOUTH) { + mouth_offset = WAW_MINMOUTH; + waw->mouth_direction = true; + } + } + + waw->mouth_offset = mouth_offset; + + y = waw->y; + + if(waw->float_direction) { + change_y = WAW_MOVESPEED; + if(change_y + y > WAW_MAXFLOAT) { + change_y = 0; + waw->float_direction = false; + } + } + else { + change_y = -WAW_MOVESPEED; + if(change_y + y < SCREEN_SPRITE_BORDER_Y_START) { + change_y = 0; + waw->float_direction = true; + } + } + + waw->y = y + change_y; + + current_sprite = waw->sprites; + for(idx = 0; idx < WAW_SPRITE_COUNT; idx++) { + sprite = *current_sprite; + ((unsigned char*)&sprite_x)[0] = sprite->lo_x; + ((unsigned char*)&sprite_x)[1] = sprite->hi_x; + sprite_y = sprite->lo_y + change_y; + // Mouth + if(idx == WAW_MOUTHINDEX) { + set_sprite_position(sprite, sprite_x, current_sprite[-1]->lo_y + mouth_offset); + } + else { + set_sprite_position(sprite, sprite_x, sprite_y); + } + + current_sprite++; + } +} + +unsigned char main(void) { + static unsigned char err; + //static waw waw2 = {VIC_SPR_WIDTH * WAW_COLUMNS * 2,VIC_SPR_HEIGHT * 2,0,true,true}; + static waw waw = {0,0,0,true,true}; + + updatepalntsc(); + is_pal = get_tv(); + + if(err = spritesheet_load("sprites.spd")) { + printf("Spritesheet failed to load: %d", errno); + return EXIT_FAILURE; + } + + init_sprite_pool(); + init_waw(&waw); + //init_waw(&waw2); + + character_init(true); + setup_irq_handler(&main_raster_irq); + screen_init(true); + + do { + if(last_updated == game_clock) { + continue; + } + + update_waw(&waw); + //update_waw(&waw2); + + last_updated++; + } while(true); + + while(true); + + return 0; +} \ No newline at end of file diff --git a/src/palntsc_asm.s b/src/palntsc_asm.s new file mode 100644 index 0000000..3729daa --- /dev/null +++ b/src/palntsc_asm.s @@ -0,0 +1,47 @@ +.export _updatepalntsc + ; Reliable PAL/NTSC-Detector by Ninja/The Dreams/TempesT + ; for Go64!/CW issue 06/2000 (detailed description there) + ; This routine can't be fooled like $02a6 and works also with a SCPU + +.include "c64.inc" + +nmivec = $0318 ; NMI-vector + +_updatepalntsc: + jsr palntsc ; perform check + sta PALFLAG ; update KERNAL-variable + rts + +palntsc: + sei ; disable interrupts + ldx nmivec + ldy nmivec+1 ; remember old NMI-vector + lda #<(done) + sta nmivec + lda #>(done) ; let NMI-vector point to + sta nmivec+1 ; a RTI +wait: + lda VIC_HLINE + bne wait ; wait for rasterline 0 or 256 + lda #$37 + sta VIC_HLINE + lda #$9b ; write testline $137 to the + sta VIC_CTRL1 + lda #$01 + sta VIC_IRR ; clear IMR-Bit 0 +wait1: + lda VIC_CTRL1 ; Is rasterbeam in the area + bpl wait1 ; 0-255? if yes, wait +wait2: + lda VIC_CTRL1 ; Is rasterbeam in the area + bmi wait2 ; 256 to end? if yes, wait + lda VIC_IRR ; read IMR + and #$01 ; mask Bit 0 + sta VIC_IRR ; clear IMR-Bit 0 + stx nmivec + sty nmivec+1 ; restore old NMI-vector + cli ; enable interrupts + rts ; return + +done: + rti