Initial commit

This commit is contained in:
Empathic Qubit 2021-10-08 19:54:21 +02:00
commit e77442b840
10 changed files with 1192 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
*.tab
*.o
*.s??
*~
*.dblite
build/

16
.vscode/c_cpp_properties.json vendored Normal file
View file

@ -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
}

41
.vscode/launch.json vendored Normal file
View file

@ -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"
]
}
]
}

44
.vscode/settings.json vendored Normal file
View file

@ -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"
}
}

16
.vscode/tasks.json vendored Normal file
View file

@ -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"
}
]
}

60
SConstruct Normal file
View file

@ -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)

9
scons.sh Normal file
View file

@ -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

284
src/c64.h Normal file
View file

@ -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

669
src/main.c Normal file
View file

@ -0,0 +1,669 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <6502.h>
#include <conio.h>
#include <c64.h>
#include "c64.h"
#include <errno.h>
#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_sprite);
#define himasker(dest) { \
__asm__("lda %w", dest); \
__asm__("and %v", hi_mask); \
__asm__("ora (ptr1),Y"); \
__asm__("sta %w", dest); \
}
__asm__("lda %v", cs);
__asm__("ldx %v+1", cs);
__asm__("sta ptr1");
__asm__("stx ptr1+1");
__asm__("ldy #%b", offsetof(struct sprite_data, color));
__asm__("ldx %v", vic_sprite);
__asm__("lda (ptr1),Y");
__asm__("sta %w,X", VIC_SPR0_COLOR);
__asm__("iny");
__asm__("lda (ptr1),Y");
__asm__("sta %w,X", SPR_POINTERS);
__asm__("txa");
__asm__("asl");
__asm__("tax");
__asm__("iny");
__asm__("lda (ptr1),Y");
__asm__("sta %w,X", VIC_SPR0_X);
__asm__("iny");
__asm__("lda (ptr1),Y");
__asm__("sta %w,X", VIC_SPR0_Y);
__asm__("tax");
__asm__("inx");
__asm__("stx %w", VIC_HLINE);
__asm__("iny");
himasker(VIC_SPR_ENA);
__asm__("iny");
himasker(VIC_SPR_HI_X);
__asm__("iny");
himasker(VIC_SPR_EXP_X);
__asm__("sta %w", VIC_SPR_EXP_Y);
__asm__("iny");
himasker(VIC_SPR_MCOLOR);
vic_sprite++;
if(vic_sprite >= 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;
}

47
src/palntsc_asm.s Normal file
View file

@ -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