Add files via upload

This commit is contained in:
wavemotion-dave 2025-04-25 14:34:33 -04:00 committed by GitHub
commit 71fbc3a2ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 19973 additions and 0 deletions

BIN
C64_icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

47
Makefile Normal file
View File

@ -0,0 +1,47 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
endif
include $(DEVKITARM)/ds_rules
export TARGET := GimliDS
export TOPDIR := $(CURDIR)
export VERSION := 0.6
ICON := -b $(CURDIR)/C64_icon.bmp "GimliDS $(VERSION);wavemotion-dave;https://github.com/wavemotion-dave/GimliDS"
#---------------------------------------------------------------------------------
# path to tools - this can be deleted if you set the path in windows
#---------------------------------------------------------------------------------
export PATH := $(DEVKITARM)/bin:$(PATH)
.PHONY: checkarm7 checkarm9 clean
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all: checkarm7 checkarm9 $(TARGET).nds
# $(TARGET).ds.gba : $(TARGET).nds
#---------------------------------------------------------------------------------
$(TARGET).nds : checkarm7 checkarm9
ndstool -c $(TARGET).nds -7 arm7/$(TARGET).arm7.elf -9 arm9/$(TARGET).arm9.elf $(ICON)
#---------------------------------------------------------------------------------
checkarm7:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
checkarm9:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
clean:
$(MAKE) -C arm9 clean
$(MAKE) -C arm7 clean
rm -f $(TARGET).nds
#rm -f $(TARGET).ds.gba $(TARGET).arm7 $(TARGET).arm9

130
arm7/Makefile Normal file
View File

@ -0,0 +1,130 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := source
INCLUDES := include build
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb-interwork
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM7
CXXFLAGS := $(CFLAGS)
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map
LIBS := -lmm7 -lnds7
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM7ELF := $(CURDIR)/$(TARGET).arm7.elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
export CC := $(PREFIX)gcc
export CXX := $(PREFIX)g++
export AR := $(PREFIX)ar
export OBJCOPY := $(PREFIX)objcopy
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM7ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

106
arm7/source/arm7main.c Normal file
View File

@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------
Derived from the default ARM7 core
Copyright (C) 2005 - 2010
Michael Noland (joat)
Jason Rogers (dovoto)
Dave Murphy (WinterMute)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
#include <nds.h>
#include <maxmod7.h>
#include <stdlib.h>
//---------------------------------------------------------------------------------
void VcountHandler() {
//---------------------------------------------------------------------------------
inputGetAndSend();
}
//---------------------------------------------------------------------------------
void VblankHandler(void) {
//---------------------------------------------------------------------------------
}
volatile bool exitflag = false;
//---------------------------------------------------------------------------------
void powerButtonCB() {
//---------------------------------------------------------------------------------
exitflag = true;
}
//---------------------------------------------------------------------------------
int main(int argc, char ** argv) {
//---------------------------------------------------------------------------------
// clear sound registers
dmaFillWords(0, (void*)0x04000400, 0x100);
REG_SOUNDCNT |= SOUND_ENABLE;
writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP );
powerOn(POWER_SOUND);
readUserSettings();
ledBlink(0);
irqInit();
// Start the RTC tracking IRQ
initClockIRQ();
fifoInit();
touchInit();
mmInstall(FIFO_MAXMOD);
SetYtrigger(80);
installSoundFIFO();
installSystemFIFO();
/*
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;
irqSet(IRQ_FIFO_NOT_EMPTY, FiFoHandler);
irqEnable(IRQ_VBLANK | IRQ_VCOUNT|IRQ_FIFO_NOT_EMPTY);
SoundSetTimer(0);
*/
irqSet(IRQ_VCOUNT, VcountHandler);
irqSet(IRQ_VBLANK, VblankHandler);
irqEnable(IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK);
setPowerButtonCB(powerButtonCB);
// Keep the ARM7 mostly idle
while (!exitflag) {
if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) {
exitflag = true;
}
swiWaitForVBlank();
}
return 0;
}

148
arm9/Makefile Normal file
View File

@ -0,0 +1,148 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := source
INCLUDES := source
DATA := data
GRAPHICS := gfx
BACKGRD := gfx_data
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -marm -mthumb-interwork
CFLAGS := -g -O3 -flto -Wall -Wformat=0 -Wno-sequence-point -Wno-delete-non-virtual-dtor -Wno-pointer-arith -Wno-register -Wno-narrowing -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math $(ARCH) -falign-functions=4 -frename-registers -finline-functions $(ARCH)
CFLAGS += $(INCLUDE) -DARM9 -D__NDS__=1 -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=0 -DGLOBAL_VARS=1
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lmm9 -lfat -lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM9ELF := $(CURDIR)/$(TARGET).arm9.elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \
$(foreach dir,$(BACKGRD),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
OSPECIALS := keyboard.o mainmenu_bg.o diskmenu_bg.o intro.o
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(OSPECIALS) $(addsuffix .o,$(BINFILES)) \
$(PNGFILES:.png=.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf *.nds* *.bin
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM9ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
@echo $(notdir $<)
@$(bin2o)
%.wav.o : %.wav
@echo $(notdir $<)
@$(bin2o)
keyboard.s keyboard.h : keyboard.png
grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl
mainmenu_bg.s mainmenu_bg.h : mainmenu_bg.png
grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl
diskmenu_bg.s diskmenu_bg.h : diskmenu_bg.png
grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl
intro.s intro.h : intro.png
grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

BIN
arm9/data/floppy.wav Normal file

Binary file not shown.

BIN
arm9/data/keyclick.wav Normal file

Binary file not shown.

BIN
arm9/data/mus_intro.wav Normal file

Binary file not shown.

BIN
arm9/data/soundbank.bin Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
arm9/gfx_data/intro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
arm9/gfx_data/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

2176
arm9/source/1541d64.cpp Normal file

File diff suppressed because it is too large Load Diff

170
arm9/source/1541d64.h Normal file
View File

@ -0,0 +1,170 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* 1541d64.h - 1541 emulation in disk image files (.d64/.x64/zipcode)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _1541D64_H
#define _1541D64_H
#include "sysdeps.h"
#include "IEC.h"
/*
* Definitions
*/
// Constants
const int NUM_SECTORS_35 = 683; // Number of sectors in a 35-track image
const int NUM_SECTORS_40 = 768; // Number of sectors in a 40-track image
// Disk image types
enum {
TYPE_D64, // D64 file
TYPE_ED64, // Converted zipcode file (D64 with header ID)
TYPE_X64 // x64 file
};
// Channel descriptor
struct channel_desc {
int mode; // Channel mode
bool writing; // Flag: writing to file (for file channels)
int buf_num; // Buffer number for direct access and file channels
uint8 *buf; // Pointer to start of buffer
uint8 *buf_ptr; // Pointer to current position in buffer
int buf_len; // Remaining bytes in buffer
int track, sector; // Track and sector the buffer will be written to (for writing to file channels)
int num_blocks; // Number of blocks in file (for writing to file channels)
int dir_track; // Track...
int dir_sector; // ...and sector of directory block containing file entry
int entry; // Number of entry in directory block
};
// Disk image file descriptor
struct image_file_desc {
int type; // See definitions above
int header_size; // Size of file header
int num_tracks; // Number of tracks
uint8 id1, id2; // Block header ID (as opposed to BAM ID)
uint8 error_info[NUM_SECTORS_40]; // Sector error information (1 byte/sector)
bool has_error_info; // Flag: error info present in file
};
// Disk image drive class
class ImageDrive : public Drive {
public:
ImageDrive(IEC *iec, const char *filepath);
virtual ~ImageDrive();
virtual uint8 Open(int channel, const uint8 *name, int name_len);
virtual uint8 Close(int channel);
virtual uint8 Read(int channel, uint8 &byte);
virtual uint8 Write(int channel, uint8 byte, bool eoi);
virtual void Reset(void);
private:
void close_image(void);
bool change_image(const char *path);
uint8 open_file(int channel, const uint8 *name, int name_len);
uint8 open_file_ts(int channel, int track, int sector);
uint8 create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite = false);
uint8 open_directory(const uint8 *pattern, int pattern_len);
uint8 open_direct(int channel, const uint8 *filename);
void close_all_channels(void);
int alloc_buffer(int want);
void free_buffer(int buf);
bool find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont);
bool find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry);
bool find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry);
bool alloc_dir_entry(int &track, int &sector, int &entry);
bool is_block_free(int track, int sector);
int num_free_blocks(int track);
int alloc_block(int track, int sector);
int free_block(int track, int sector);
bool alloc_block_chain(int track, int sector);
bool free_block_chain(int track, int sector);
bool alloc_next_block(int &track, int &sector, int interleave);
bool read_sector(int track, int sector, uint8 *buffer);
bool write_sector(int track, int sector, uint8 *buffer);
void write_error_info(void);
virtual void block_read_cmd(int channel, int track, int sector, bool user_cmd = false);
virtual void block_write_cmd(int channel, int track, int sector, bool user_cmd = false);
virtual void block_allocate_cmd(int track, int sector);
virtual void block_free_cmd(int track, int sector);
virtual void buffer_pointer_cmd(int channel, int pos);
virtual void mem_read_cmd(uint16 adr, uint8 len);
virtual void mem_write_cmd(uint16 adr, uint8 len, uint8 *p);
virtual void copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len);
virtual void rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len);
virtual void scratch_cmd(const uint8 *files, int files_len);
virtual void initialize_cmd(void);
virtual void new_cmd(const uint8 *name, int name_len, const uint8 *comma);
virtual void validate_cmd(void);
FILE *the_file; // File pointer for image file
image_file_desc desc; // Image file descriptor
bool write_protected; // Flag: image file write-protected
uint8 ram[0x800]; // 2k 1541 RAM
uint8 dir[258]; // Buffer for directory blocks
uint8 *bam; // Pointer to BAM in 1541 RAM (buffer 4, upper 256 bytes)
bool bam_dirty; // Flag: BAM modified, needs to be written back
channel_desc ch[18]; // Descriptors for channels 0..17 (16 = internal read, 17 = internal write)
bool buf_free[4]; // Flags: buffer 0..3 free?
};
/*
* Functions
*/
// Check whether file with given header (64 bytes) and size looks like one
// of the file types supported by this module
extern bool IsImageFile(const char *path, const uint8 *header, long size);
// Read directory of disk image file into (empty) c64_dir_entry vector
extern bool ReadImageDirectory(const char *path, vector<c64_dir_entry> &vec);
// Create new blank disk image file
extern bool CreateImageFile(const char *path);
#endif

506
arm9/source/1541fs.cpp Normal file
View File

@ -0,0 +1,506 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* 1541fs.cpp - 1541 emulation in host file system
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Notes:
* ------
*
* - If the directory is opened (file name "$"), a temporary file
* with the structure of a 1541 directory file is created and
* opened. It can then be accessed in the same way as all other
* files.
*
* Incompatibilities:
* ------------------
*
* - No "raw" directory reading
* - No relative/sequential/user files
* - Only "I" and "UJ" commands implemented
*/
#include "sysdeps.h"
#include "1541fs.h"
#include "IEC.h"
#include "main.h"
#include "Prefs.h"
// Prototypes
static bool match(const char *p, const char *n);
/*
* Constructor: Prepare emulation
*/
FSDrive::FSDrive(IEC *iec, const char *path) : Drive(iec)
{
strcpy(orig_dir_path, path);
dir_path[0] = 0;
if (change_dir(orig_dir_path)) {
for (int i=0; i<16; i++)
file[i] = NULL;
Reset();
Ready = true;
}
}
/*
* Destructor
*/
FSDrive::~FSDrive()
{
if (Ready) {
close_all_channels();
Ready = false;
}
}
/*
* Change emulation directory
*/
bool FSDrive::change_dir(char *dirpath)
{
DIR *dir;
if ((dir = opendir(dirpath)) != NULL) {
closedir(dir);
strcpy(dir_path, dirpath);
strncpy(dir_title, dir_path, 16);
return true;
} else
return false;
}
/*
* Open channel
*/
uint8 FSDrive::Open(int channel, const uint8 *name, int name_len)
{
set_error(ERR_OK);
// Channel 15: Execute file name as command
if (channel == 15) {
execute_cmd(name, name_len);
return ST_OK;
}
// Close previous file if still open
if (file[channel]) {
fclose(file[channel]);
file[channel] = NULL;
}
if (name[0] == '#') {
set_error(ERR_NOCHANNEL);
return ST_OK;
}
if (name[0] == '$')
return open_directory(channel, name + 1, name_len - 1);
return open_file(channel, name, name_len);
}
/*
* Open file
*/
uint8 FSDrive::open_file(int channel, const uint8 *name, int name_len)
{
char plain_name[NAMEBUF_LENGTH];
int plain_name_len;
int mode = FMODE_READ;
int type = FTYPE_PRG;
int rec_len = 0;
parse_file_name(name, name_len, (uint8 *)plain_name, plain_name_len, mode, type, rec_len, true);
// Channel 0 is READ, channel 1 is WRITE
if (channel == 0 || channel == 1) {
mode = channel ? FMODE_WRITE : FMODE_READ;
if (type == FTYPE_DEL)
type = FTYPE_PRG;
}
bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND);
// Expand wildcards (only allowed on reading)
if (strchr(plain_name, '*') || strchr(plain_name, '?')) {
if (writing) {
set_error(ERR_SYNTAX33);
return ST_OK;
} else
find_first_file(plain_name);
}
// Relative files are not supported
if (type == FTYPE_REL) {
set_error(ERR_UNIMPLEMENTED);
return ST_OK;
}
// Select fopen() mode according to file mode
const char *mode_str = "rb";
switch (mode) {
case FMODE_WRITE:
mode_str = "wb";
break;
case FMODE_APPEND:
mode_str = "ab";
break;
}
// Open file
if (chdir(dir_path))
set_error(ERR_NOTREADY);
else if ((file[channel] = fopen(plain_name, mode_str)) != NULL) {
if (mode == FMODE_READ || mode == FMODE_M) // Read and buffer first byte
read_char[channel] = fgetc(file[channel]);
} else
set_error(ERR_FILENOTFOUND);
return ST_OK;
}
/*
* Find first file matching wildcard pattern and get its real name
*/
// Return true if name 'n' matches pattern 'p'
static bool match(const char *p, const char *n)
{
if (!*p) // Null pattern matches everything
return true;
do {
if (*p == '*') // Wildcard '*' matches all following characters
return true;
if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
return false;
p++; n++;
} while (*p);
return !*n;
}
void FSDrive::find_first_file(char *pattern)
{
DIR *dir;
struct dirent *de;
// Open directory for reading and skip '.' and '..'
if ((dir = opendir(dir_path)) == NULL)
return;
de = readdir(dir);
while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name)))
de = readdir(dir);
while (de) {
// Match found? Then copy real file name
if (match(pattern, de->d_name)) {
strncpy(pattern, de->d_name, NAMEBUF_LENGTH);
closedir(dir);
return;
}
// Get next directory entry
de = readdir(dir);
}
closedir(dir);
}
/*
* Open directory, create temporary file
*/
uint8 FSDrive::open_directory(int channel, const uint8 *pattern, int pattern_len)
{
char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
char str[NAMEBUF_LENGTH];
char *p, *q;
int i;
DIR *dir;
struct dirent *de;
struct stat statbuf;
// Special treatment for "$0"
if (pattern[0] == '0' && pattern[1] == 0) {
pattern++;
pattern_len--;
}
// Skip everything before the ':' in the pattern
uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len);
if (t)
pattern = t + 1;
// Convert pattern to ASCII
char ascii_pattern[NAMEBUF_LENGTH];
petscii2ascii(ascii_pattern, pattern, NAMEBUF_LENGTH);
// Open directory for reading and skip '.' and '..'
if ((dir = opendir(dir_path)) == NULL) {
set_error(ERR_NOTREADY);
return ST_OK;
}
de = readdir(dir);
while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name)))
de = readdir(dir);
// Create temporary file
if ((file[channel] = fopen("ramdiskfs:/ram/test4.txt","wb")) == NULL) {
closedir(dir);
return ST_OK;
}
// Create directory title
p = &buf[8];
for (i=0; i<16 && dir_title[i]; i++)
*p++ = ascii2petscii(dir_title[i]);
fwrite(buf, 1, 32, file[channel]);
// Create and write one line for every directory entry
while (de) {
// Include only files matching the ascii_pattern
if (match(ascii_pattern, de->d_name)) {
// Get file statistics
chdir(dir_path);
stat(de->d_name, &statbuf);
// Clear line with spaces and terminate with null byte
memset(buf, ' ', 31);
buf[31] = 0;
p = buf;
*p++ = 0x01; // Dummy line link
*p++ = 0x01;
// Calculate size in blocks (254 bytes each)
i = (statbuf.st_size + 254) / 254;
*p++ = i & 0xff;
*p++ = (i >> 8) & 0xff;
p++;
if (i < 10) p++; // Less than 10: add one space
if (i < 100) p++; // Less than 100: add another space
// Convert and insert file name
strcpy(str, de->d_name);
*p++ = '\"';
q = p;
for (i=0; i<16 && str[i]; i++)
*q++ = ascii2petscii(str[i]);
*q++ = '\"';
p += 18;
// File type
if (S_ISDIR(statbuf.st_mode)) {
*p++ = 'D';
*p++ = 'I';
*p++ = 'R';
} else {
*p++ = 'P';
*p++ = 'R';
*p++ = 'G';
}
// Write line
fwrite(buf, 1, 32, file[channel]);
}
// Get next directory entry
de = readdir(dir);
}
// Final line
fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]);
// Rewind file for reading and read first byte
rewind(file[channel]);
read_char[channel] = fgetc(file[channel]);
#ifndef __riscos
// Close directory
closedir(dir);
#endif
return ST_OK;
}
/*
* Close channel
*/
uint8 FSDrive::Close(int channel)
{
if (channel == 15) {
close_all_channels();
return ST_OK;
}
if (file[channel]) {
fclose(file[channel]);
file[channel] = NULL;
}
return ST_OK;
}
/*
* Close all channels
*/
void FSDrive::close_all_channels(void)
{
for (int i=0; i<15; i++)
Close(i);
cmd_len = 0;
}
/*
* Read from channel
*/
uint8 FSDrive::Read(int channel, uint8 &byte)
{
int c;
// Channel 15: Error channel
if (channel == 15) {
byte = *error_ptr++;
if (byte != '\r')
return ST_OK;
else { // End of message
set_error(ERR_OK);
return ST_EOF;
}
}
if (!file[channel]) return ST_READ_TIMEOUT;
// Read one byte
byte = read_char[channel];
c = fgetc(file[channel]);
if (c == EOF)
return ST_EOF;
else {
read_char[channel] = c;
return ST_OK;
}
}
/*
* Write to channel
*/
uint8 FSDrive::Write(int channel, uint8 byte, bool eoi)
{
// Channel 15: Collect chars and execute command on EOI
if (channel == 15) {
if (cmd_len >= 58)
return ST_TIMEOUT;
cmd_buf[cmd_len++] = byte;
if (eoi) {
execute_cmd(cmd_buf, cmd_len);
cmd_len = 0;
}
return ST_OK;
}
if (!file[channel]) {
set_error(ERR_FILENOTOPEN);
return ST_TIMEOUT;
}
if (putc(byte, file[channel]) == EOF) {
set_error(ERR_WRITE25);
return ST_TIMEOUT;
}
return ST_OK;
}
/*
* Execute drive commands
*/
// INITIALIZE
void FSDrive::initialize_cmd(void)
{
close_all_channels();
}
// VALIDATE
void FSDrive::validate_cmd(void)
{
}
/*
* Reset drive
*/
void FSDrive::Reset(void)
{
close_all_channels();
cmd_len = 0;
set_error(ERR_STARTUP);
}

74
arm9/source/1541fs.h Normal file
View File

@ -0,0 +1,74 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* 1541fs.h - 1541 emulation in host file system
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _1541FS_H
#define _1541FS_H
#include "sysdeps.h"
#include "IEC.h"
class FSDrive : public Drive {
public:
FSDrive(IEC *iec, const char *path);
virtual ~FSDrive();
virtual uint8 Open(int channel, const uint8 *name, int name_len);
virtual uint8 Close(int channel);
virtual uint8 Read(int channel, uint8 &byte);
virtual uint8 Write(int channel, uint8 byte, bool eoi);
virtual void Reset(void);
private:
bool change_dir(char *dirpath);
uint8 open_file(int channel, const uint8 *name, int name_len);
uint8 open_directory(int channel, const uint8 *pattern, int pattern_len);
void find_first_file(char *pattern);
void close_all_channels(void);
virtual void initialize_cmd(void);
virtual void validate_cmd(void);
char dir_path[256]; // Path to directory
char orig_dir_path[256]; // Original directory path
char dir_title[16]; // Directory title
FILE *file[16]; // File pointers for each of the 16 channels
uint8 read_char[16]; // Buffers for one-byte read-ahead
};
#endif

493
arm9/source/1541job.cpp Normal file
View File

@ -0,0 +1,493 @@
/*
* 1541job.cpp - Emulation of 1541 GCR disk reading/writing
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Notes:
* ------
*
* - This is only used for processor-level 1541 emulation.
* It simulates the 1541 disk controller hardware (R/W head,
* GCR reading/writing).
* - The preferences settings for drive 8 are used to
* specify the .d64 file
*
* Incompatibilities:
* ------------------
*
* - No GCR writing possible (WriteSector is a ROM patch)
* - Programs depending on the exact timing of head movement/disk
* rotation don't work
* - The .d64 error info is unused
*/
#include <nds.h>
#include "sysdeps.h"
#include "1541job.h"
#include "CPU1541.h"
#include "Prefs.h"
// Number of tracks/sectors
const int NUM_TRACKS = 35;
const int NUM_SECTORS = 683;
// Size of GCR encoded data
const int GCR_SECTOR_SIZE = 5 + 10 + 9 + 5 + 325 + 12; // SYNC + Header + Gap + SYNC + Data + Gap
const int GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has 21 sectors
const int GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS;
// Job return codes
const int RET_OK = 1; // No error
const int RET_NOT_FOUND = 2; // Block not found
const int RET_NOT_READY = 15; // Drive not ready
// Number of sectors of each track
const int num_sectors[36] = {
0,
21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
19,19,19,19,19,19,19,
18,18,18,18,18,18,
17,17,17,17,17
};
// Sector offset of start of track in .d64 file
const int sector_offset[36] = {
0,
0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
357,376,395,414,433,452,471,
490,508,526,544,562,580,
598,615,632,649,666
};
/*
* Constructor: Open .d64 file if processor-level 1541
* emulation is enabled
*/
Job1541::Job1541(uint8 *ram1541) : ram(ram1541)
{
the_file = NULL;
gcr_data = gcr_ptr = gcr_track_start = new uint8[GCR_DISK_SIZE];
gcr_track_end = gcr_track_start + GCR_TRACK_SIZE;
current_halftrack = 2;
disk_changed = true;
motor_on = false;
if (ThePrefs.TrueDrive)
open_d64_file(ThePrefs.DrivePath[0]);
}
/*
* Destructor: Close .d64 file
*/
Job1541::~Job1541()
{
close_d64_file();
delete[] gcr_data;
}
/*
* Preferences may have changed
*/
void Job1541::NewPrefs(Prefs *prefs)
{
// 1541 emulation turned off?
if (!prefs->TrueDrive)
close_d64_file();
// 1541 emulation turned on?
else if (!ThePrefs.TrueDrive && prefs->TrueDrive)
open_d64_file(prefs->DrivePath[0]);
// .d64 file name changed?
else
{
if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0]))
{
close_d64_file();
open_d64_file(prefs->DrivePath[0]);
disk_changed = true;
}
}
}
/*
* Open .d64 file
*/
void Job1541::open_d64_file(char *filepath)
{
long size;
uint8 magic[4];
static uint8 bam[256];
//printdbg(filepath);
// Clear GCR buffer
memset(gcr_data, 0x55, GCR_DISK_SIZE);
// Try opening the file for reading/writing first, then for reading only
write_protected = false;
the_file = fopen(filepath, "rb+");
if (the_file == NULL) {
write_protected = true;
the_file = fopen(filepath, "rb");
}
if (the_file != NULL) {
// Check length
fseek(the_file, 0, SEEK_END);
if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
fclose(the_file);
the_file = NULL;
return;
}
// x64 image?
fseek(the_file, 0, SEEK_SET);
fread(&magic, 4, 1, the_file);
if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
image_header = 64;
else
image_header = 0;
// Preset error info (all sectors no error)
memset(error_info, 1, NUM_SECTORS);
// Load sector error info from .d64 file, if present
if (!image_header && size == NUM_SECTORS * 257) {
fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
fread(&error_info, NUM_SECTORS, 1, the_file);
};
// Read BAM and get ID
read_sector(18, 0, bam);
id1 = bam[162];
id2 = bam[163];
// Create GCR encoded disk data from image
disk2gcr();
}
}
/*
* Close .d64 file
*/
void Job1541::close_d64_file(void)
{
if (the_file != NULL) {
fclose(the_file);
the_file = NULL;
}
}
void Job1541::Reset(void)
{
current_halftrack = 2;
disk_changed = true;
motor_on = false;
}
/*
* Write sector to disk (1541 ROM patch)
*/
void Job1541::WriteSector(void)
{
int track = ram[0x18];
int sector = ram[0x19];
uint16 buf = ram[0x30] | (ram[0x31] << 8);
if (buf <= 0x0700)
if (write_sector(track, sector, ram + buf))
sector2gcr(track, sector);
}
/*
* Format one track (1541 ROM patch)
*/
void Job1541::FormatTrack(void)
{
int track = ram[0x51];
// Get new ID
uint8 bufnum = ram[0x3d];
id1 = ram[0x12 + bufnum];
id2 = ram[0x13 + bufnum];
// Create empty block
uint8 buf[256];
memset(buf, 1, 256);
buf[0] = 0x4b;
// Write block to all sectors on track
for(int sector=0; sector<num_sectors[track]; sector++) {
write_sector(track, sector, buf);
sector2gcr(track, sector);
}
// Clear error info (all sectors no error)
if (track == 35)
memset(error_info, 1, NUM_SECTORS);
// Write error_info to disk?
}
/*
* Read sector (256 bytes)
* true: success, false: error
*/
bool Job1541::read_sector(int track, int sector, uint8 *buffer)
{
int offset;
floppy_soundfx(); // Play floppy SFX if needed
// Convert track/sector to byte offset in file
if ((offset = offset_from_ts(track, sector)) < 0)
return false;
fseek(the_file, offset + image_header, SEEK_SET);
fread(buffer, 256, 1, the_file);
return true;
}
/*
* Write sector (256 bytes) !! -> GCR
* true: success, false: error
*/
bool Job1541::write_sector(int track, int sector, uint8 *buffer)
{
int offset;
floppy_soundfx(); // Play floppy SFX if needed
// Convert track/sector to byte offset in file
if ((offset = offset_from_ts(track, sector)) < 0)
return false;
fseek(the_file, offset + image_header, SEEK_SET);
fwrite(buffer, 256, 1, the_file);
return true;
}
/*
* Convert track/sector to offset
*/
int Job1541::secnum_from_ts(int track, int sector)
{
return sector_offset[track] + sector;
}
int Job1541::offset_from_ts(int track, int sector)
{
if ((track < 1) || (track > NUM_TRACKS) ||
(sector < 0) || (sector >= num_sectors[track]))
return -1;
return (sector_offset[track] + sector) << 8;
}
/*
* Convert 4 bytes to 5 GCR encoded bytes
*/
const uint16 gcr_table[16] = {
0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17,
0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15
};
void Job1541::gcr_conv4(uint8 *from, uint8 *to)
{
uint16 g;
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
*to++ = g >> 2;
*to = (g << 6) & 0xc0;
from++;
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
*to++ |= (g >> 4) & 0x3f;
*to = (g << 4) & 0xf0;
from++;
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
*to++ |= (g >> 6) & 0x0f;
*to = (g << 2) & 0xfc;
from++;
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
*to++ |= (g >> 8) & 0x03;
*to = g;
}
/*
* Create GCR encoded disk data from image
*/
void Job1541::sector2gcr(int track, int sector)
{
uint8 block[256];
uint8 buf[4];
uint8 *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE;
read_sector(track, sector, block);
// Create GCR header
*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff; // SYNC
buf[0] = 0x08; // Header mark
buf[1] = sector ^ track ^ id2 ^ id1; // Checksum
buf[2] = sector;
buf[3] = track;
gcr_conv4(buf, p);
buf[0] = id2;
buf[1] = id1;
buf[2] = 0x0f;
buf[3] = 0x0f;
gcr_conv4(buf, p+5);
p += 10;
memset(p, 0x55, 9); // Gap
p += 9;
// Create GCR data
uint8 sum;
*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff; // SYNC
buf[0] = 0x07; // Data mark
sum = buf[1] = block[0];
sum ^= buf[2] = block[1];
sum ^= buf[3] = block[2];
gcr_conv4(buf, p);
p += 5;
for (int i=3; i<255; i+=4) {
sum ^= buf[0] = block[i];
sum ^= buf[1] = block[i+1];
sum ^= buf[2] = block[i+2];
sum ^= buf[3] = block[i+3];
gcr_conv4(buf, p);
p += 5;
}
sum ^= buf[0] = block[255];
buf[1] = sum; // Checksum
buf[2] = 0;
buf[3] = 0;
gcr_conv4(buf, p);
p += 5;
memset(p, 0x55, 12); // Gap
}
void Job1541::disk2gcr(void)
{
// Convert all tracks and sectors
for (int track=1; track<=NUM_TRACKS; track++)
for(int sector=0; sector<num_sectors[track]; sector++)
sector2gcr(track, sector);
}
/*
* Move R/W head out (lower track numbers)
*/
void Job1541::MoveHeadOut(void)
{
if (current_halftrack == 2)
return;
current_halftrack--;
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
}
/*
* Control spindle motor
*/
void Job1541::SetMotor(bool on)
{
motor_on = on;
}
bool Job1541::ByteReady(void)
{
return motor_on;
}
/*
* Move R/W head in (higher track numbers)
*/
void Job1541::MoveHeadIn(void)
{
if (current_halftrack == NUM_TRACKS*2)
return;
current_halftrack++;
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
}
/*
* Get state
*/
void Job1541::GetState(Job1541State *state)
{
state->current_halftrack = current_halftrack;
state->gcr_ptr = gcr_ptr - gcr_data;
state->write_protected = write_protected;
state->disk_changed = disk_changed;
}
/*
* Set state
*/
void Job1541::SetState(Job1541State *state)
{
current_halftrack = state->current_halftrack;
gcr_ptr = gcr_data + state->gcr_ptr;
gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
write_protected = state->write_protected;
disk_changed = state->disk_changed;
}

151
arm9/source/1541job.h Normal file
View File

@ -0,0 +1,151 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* 1541job.h - Emulation of 1541 GCR disk reading/writing
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _1541JOB_H
#define _1541JOB_H
#include <nds.h>
#include "sysdeps.h"
class MOS6502_1541;
class Prefs;
struct Job1541State;
class Job1541 {
public:
Job1541(uint8 *ram1541);
~Job1541();
void GetState(Job1541State *state);
void SetState(Job1541State *state);
void NewPrefs(Prefs *prefs);
void MoveHeadOut(void);
void MoveHeadIn(void);
bool SyncFound(void);
bool ByteReady(void);
uint8 ReadGCRByte(void);
uint8 WPState(void);
void WriteSector(void);
void FormatTrack(void);
void Reset(void);
void SetMotor(bool on);
private:
void open_d64_file(char *filepath);
void close_d64_file(void);
bool read_sector(int track, int sector, uint8 *buffer);
bool write_sector(int track, int sector, uint8 *buffer);
void format_disk(void);
int secnum_from_ts(int track, int sector);
int offset_from_ts(int track, int sector);
void gcr_conv4(uint8 *from, uint8 *to);
void sector2gcr(int track, int sector);
void disk2gcr(void);
uint8 *ram; // Pointer to 1541 RAM
FILE *the_file; // File pointer for .d64 file
int image_header; // Length of .d64/.x64 file header
uint8 id1, id2; // ID of disk
uint8 error_info[683]; // Sector error information (1 byte/sector)
uint8 *gcr_data; // Pointer to GCR encoded disk data
uint8 *gcr_ptr; // Pointer to GCR data under R/W head
uint8 *gcr_track_start; // Pointer to start of GCR data of current track
uint8 *gcr_track_end; // Pointer to end of GCR data of current track
int current_halftrack; // Current halftrack number (2..70)
bool write_protected; // Flag: Disk write-protected
bool disk_changed; // Flag: Disk changed (WP sensor strobe control)
bool motor_on;
};
// 1541 GCR state
struct Job1541State {
int current_halftrack;
uint32 gcr_ptr;
bool write_protected;
bool disk_changed;
};
/*
* Check if R/W head is over SYNC
*/
inline bool Job1541::SyncFound(void)
{
if (*gcr_ptr == 0xff && gcr_ptr[1] != 0xff)
return true;
else {
gcr_ptr++; // Rotate disk
if (gcr_ptr == gcr_track_end)
gcr_ptr = gcr_track_start;
return false;
}
}
/*
* Read one GCR byte from disk
*/
inline uint8 Job1541::ReadGCRByte(void)
{
extern void floppy_soundfx(void);
floppy_soundfx(); // Play floppy SFX if needed
uint8 byte = *gcr_ptr++; // Rotate disk
if (gcr_ptr == gcr_track_end)
gcr_ptr = gcr_track_start;
return byte;
}
/*
* Return state of write protect sensor
*/
inline uint8 Job1541::WPState(void)
{
if (disk_changed) { // Disk change -> WP sensor strobe
disk_changed = false;
return write_protected ? 0x10 : 0;
} else
return write_protected ? 0 : 0x10;
}
#endif

1203
arm9/source/C64.cpp Normal file

File diff suppressed because it is too large Load Diff

110
arm9/source/C64.h Normal file
View File

@ -0,0 +1,110 @@
/*
* C64.h - Put the pieces together
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _C64_H
#define _C64_H
// Sizes of memory areas
const int C64_RAM_SIZE = 0x10000;
const int COLOR_RAM_SIZE = 0x400;
const int BASIC_ROM_SIZE = 0x2000;
const int KERNAL_ROM_SIZE = 0x2000;
const int CHAR_ROM_SIZE = 0x1000;
const int DRIVE_RAM_SIZE = 0x800;
const int DRIVE_ROM_SIZE = 0x4000;
class Prefs;
class C64Display;
class MOS6510;
class MOS6569;
class MOS6581;
class MOS6526_1;
class MOS6526_2;
class IEC;
class MOS6502_1541;
class Job1541;
class CmdPipe;
class C64 {
public:
C64();
~C64();
void Run(void);
void Quit(void);
void Pause(void);
void Resume(void);
void Reset(void);
void NMI(void);
void VBlank(bool draw_frame);
void NewPrefs(Prefs *prefs);
void PatchKernal(bool fast_reset, bool true_drive);
void SaveRAM(char *filename);
bool SaveSnapshot(char *filename);
bool LoadSnapshot(char *filename);
int SaveCPUState(FILE *f);
int Save1541State(FILE *f);
bool Save1541JobState(FILE *f);
bool SaveVICState(FILE *f);
bool SaveSIDState(FILE *f);
bool SaveCIAState(FILE *f);
bool LoadCPUState(FILE *f);
bool Load1541State(FILE *f);
bool Load1541JobState(FILE *f);
bool LoadVICState(FILE *f);
bool LoadSIDState(FILE *f);
bool LoadCIAState(FILE *f);
uint8 *RAM, *Basic, *Kernal,
*Char, *Color; // C64
uint8 *RAM1541, *ROM1541; // 1541
C64Display *TheDisplay;
MOS6510 *TheCPU; // C64
MOS6569 *TheVIC;
MOS6581 *TheSID;
MOS6526_1 *TheCIA1;
MOS6526_2 *TheCIA2;
IEC *TheIEC;
MOS6502_1541 *TheCPU1541; // 1541
Job1541 *TheJob1541;
private:
void c64_ctor1(void);
void c64_ctor2(void);
void c64_dtor(void);
uint8 poll_joystick(int port);
void thread_func(void);
bool thread_running; // Emulation thread is running
bool quit_thyself; // Emulation thread shall quit
bool have_a_break; // Emulation thread shall pause
int joy_minx[2], joy_maxx[2], joy_miny[2], joy_maxy[2]; // For dynamic joystick calibration
uint8 joykey; // Joystick keyboard emulation mask value
uint8 orig_kernal_1d84, // Original contents of kernal locations $1d84 and $1d85
orig_kernal_1d85; // (for undoing the Fast Reset patch)
};
extern void floppy_soundfx(void);
#endif

595
arm9/source/CIA.cpp Normal file
View File

@ -0,0 +1,595 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* CIA.cpp - 6526 emulation
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Notes:
* ------
*
* - The EmulateLine() function is called for every emulated raster
* line. It counts down the timers and triggers interrupts if
* necessary.
* - The TOD clocks are counted by CountTOD() during the VBlank, so
* the input frequency is 50Hz
* - The fields KeyMatrix and RevMatrix contain one bit for each
* key on the C64 keyboard (0: key pressed, 1: key released).
* KeyMatrix is used for normal keyboard polling (PRA->PRB),
* RevMatrix for reversed polling (PRB->PRA).
*
* Incompatibilities:
* ------------------
*
* - The TOD clock should not be stopped on a read access, but
* latched
* - The SDR interrupt is faked
*/
#include "sysdeps.h"
#include "CIA.h"
#include "CPUC64.h"
#include "CPU1541.h"
#include "VIC.h"
#include "Prefs.h"
/*
* Constructors
*/
MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {}
MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC) {}
MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541) {}
/*
* Reset the CIA
*/
void MOS6526::Reset(void)
{
pra = prb = ddra = ddrb = 0;
ta = tb = 0xffff;
latcha = latchb = 1;
tod_10ths = tod_sec = tod_min = tod_hr = 0;
alm_10ths = alm_sec = alm_min = alm_hr = 0;
sdr = icr = cra = crb = int_mask = 0;
tod_halt = ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false;
tod_divider = 0;
}
void MOS6526_1::Reset(void)
{
MOS6526::Reset();
// Clear keyboard matrix and joystick states
for (int i=0; i<8; i++)
KeyMatrix[i] = RevMatrix[i] = 0xff;
Joystick1 = Joystick2 = 0xff;
prev_lp = 0x10;
}
void MOS6526_2::Reset(void)
{
MOS6526::Reset();
// VA14/15 = 0
the_vic->ChangedVA(0);
// IEC
IECLines = 0x38; // DATA, CLK, ATN high
}
/*
* Get CIA state
*/
void MOS6526::GetState(MOS6526State *cs)
{
cs->pra = pra;
cs->prb = prb;
cs->ddra = ddra;
cs->ddrb = ddrb;
cs->ta_lo = ta & 0xff;
cs->ta_hi = ta >> 8;
cs->tb_lo = tb & 0xff;
cs->tb_hi = tb >> 8;
cs->latcha = latcha;
cs->latchb = latchb;
cs->cra = cra;
cs->crb = crb;
cs->tod_10ths = tod_10ths;
cs->tod_sec = tod_sec;
cs->tod_min = tod_min;
cs->tod_hr = tod_hr;
cs->alm_10ths = alm_10ths;
cs->alm_sec = alm_sec;
cs->alm_min = alm_min;
cs->alm_hr = alm_hr;
cs->sdr = sdr;
cs->int_data = icr;
cs->int_mask = int_mask;
}
/*
* Restore CIA state
*/
void MOS6526::SetState(MOS6526State *cs)
{
pra = cs->pra;
prb = cs->prb;
ddra = cs->ddra;
ddrb = cs->ddrb;
ta = (cs->ta_hi << 8) | cs->ta_lo;
tb = (cs->tb_hi << 8) | cs->tb_lo;
latcha = cs->latcha;
latchb = cs->latchb;
cra = cs->cra;
crb = cs->crb;
tod_10ths = cs->tod_10ths;
tod_sec = cs->tod_sec;
tod_min = cs->tod_min;
tod_hr = cs->tod_hr;
alm_10ths = cs->alm_10ths;
alm_sec = cs->alm_sec;
alm_min = cs->alm_min;
alm_hr = cs->alm_hr;
sdr = cs->sdr;
icr = cs->int_data;
int_mask = cs->int_mask;
tod_halt = false;
ta_cnt_phi2 = ((cra & 0x21) == 0x01);
tb_cnt_phi2 = ((crb & 0x61) == 0x01);
tb_cnt_ta = ((crb & 0x41) == 0x41);
}
/*
* Read from register (CIA 1)
*/
uint8 MOS6526_1::ReadRegister(uint16 adr)
{
switch (adr) {
case 0x00: {
uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1;
if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns
if (!(tst & 0x02)) ret &= RevMatrix[1];
if (!(tst & 0x04)) ret &= RevMatrix[2];
if (!(tst & 0x08)) ret &= RevMatrix[3];
if (!(tst & 0x10)) ret &= RevMatrix[4];
if (!(tst & 0x20)) ret &= RevMatrix[5];
if (!(tst & 0x40)) ret &= RevMatrix[6];
if (!(tst & 0x80)) ret &= RevMatrix[7];
return ret & Joystick2;
}
case 0x01: {
uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2;
if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows
if (!(tst & 0x02)) ret &= KeyMatrix[1];
if (!(tst & 0x04)) ret &= KeyMatrix[2];
if (!(tst & 0x08)) ret &= KeyMatrix[3];
if (!(tst & 0x10)) ret &= KeyMatrix[4];
if (!(tst & 0x20)) ret &= KeyMatrix[5];
if (!(tst & 0x40)) ret &= KeyMatrix[6];
if (!(tst & 0x80)) ret &= KeyMatrix[7];
return (ret | (prb & ddrb)) & Joystick1;
}
case 0x02: return ddra;
case 0x03: return ddrb;
case 0x04: return ta;
case 0x05: return ta >> 8;
case 0x06: return tb;
case 0x07: return tb >> 8;
case 0x08: tod_halt = false; return tod_10ths;
case 0x09: return tod_sec;
case 0x0a: return tod_min;
case 0x0b: tod_halt = true; return tod_hr;
case 0x0c: return sdr;
case 0x0d: {
uint8 ret = icr; // Read and clear ICR
icr = 0;
the_cpu->ClearCIAIRQ(); // Clear IRQ
return ret;
}
case 0x0e: return cra;
case 0x0f: return crb;
}
return 0; // Can't happen
}
/*
* Read from register (CIA 2)
*/
uint8 MOS6526_2::ReadRegister(uint16 adr)
{
switch (adr) {
case 0x00:
return ((pra | ~ddra) & 0x3f) | (IECLines & the_cpu_1541->IECLines);
case 0x01: return prb | ~ddrb;
case 0x02: return ddra;
case 0x03: return ddrb;
case 0x04: return ta;
case 0x05: return ta >> 8;
case 0x06: return tb;
case 0x07: return tb >> 8;
case 0x08: tod_halt = false; return tod_10ths;
case 0x09: return tod_sec;
case 0x0a: return tod_min;
case 0x0b: tod_halt = true; return tod_hr;
case 0x0c: return sdr;
case 0x0d: {
uint8 ret = icr; // Read and clear ICR
icr = 0;
the_cpu->ClearNMI(); // Clear NMI
return ret;
}
case 0x0e: return cra;
case 0x0f: return crb;
}
return 0; // Can't happen
}
/*
* Write to register (CIA 1)
*/
// Write to port B, check for lightpen interrupt
inline void MOS6526_1::check_lp(void)
{
if (((prb | ~ddrb) & 0x10) != prev_lp)
the_vic->TriggerLightpen();
prev_lp = (prb | ~ddrb) & 0x10;
}
void MOS6526_1::WriteRegister(uint16 adr, uint8 byte)
{
switch (adr) {
case 0x0: pra = byte; break;
case 0x1:
prb = byte;
check_lp();
break;
case 0x2: ddra = byte; break;
case 0x3:
ddrb = byte;
check_lp();
break;
case 0x4: latcha = (latcha & 0xff00) | byte; break;
case 0x5:
latcha = (latcha & 0xff) | (byte << 8);
if (!(cra & 1)) // Reload timer if stopped
ta = latcha;
break;
case 0x6: latchb = (latchb & 0xff00) | byte; break;
case 0x7:
latchb = (latchb & 0xff) | (byte << 8);
if (!(crb & 1)) // Reload timer if stopped
tb = latchb;
break;
case 0x8:
if (crb & 0x80)
alm_10ths = byte & 0x0f;
else
tod_10ths = byte & 0x0f;
break;
case 0x9:
if (crb & 0x80)
alm_sec = byte & 0x7f;
else
tod_sec = byte & 0x7f;
break;
case 0xa:
if (crb & 0x80)
alm_min = byte & 0x7f;
else
tod_min = byte & 0x7f;
break;
case 0xb:
if (crb & 0x80)
alm_hr = byte & 0x9f;
else
tod_hr = byte & 0x9f;
break;
case 0xc:
sdr = byte;
TriggerInterrupt(8); // Fake SDR interrupt for programs that need it
break;
case 0xd:
if (ThePrefs.CIAIRQHack) // Hack for addressing modes that read from the address
icr = 0;
if (byte & 0x80) {
int_mask |= byte & 0x7f;
if (icr & int_mask & 0x1f) { // Trigger IRQ if pending
icr |= 0x80;
the_cpu->TriggerCIAIRQ();
}
} else
int_mask &= ~byte;
break;
case 0xe:
cra = byte & 0xef;
if (byte & 0x10) // Force load
ta = latcha;
ta_cnt_phi2 = ((byte & 0x21) == 0x01);
break;
case 0xf:
crb = byte & 0xef;
if (byte & 0x10) // Force load
tb = latchb;
tb_cnt_phi2 = ((byte & 0x61) == 0x01);
tb_cnt_ta = ((byte & 0x41) == 0x41);
break;
}
}
// Write to port A, check for VIC bank change and IEC lines
inline void MOS6526_2::write_pa(uint8_t byte)
{
the_vic->ChangedVA(byte & 3);
uint8 old_lines = IECLines;
IECLines = ((byte << 2) & 0x80) // DATA
| ((byte << 2) & 0x40) // CLK
| ((byte << 1) & 0x10); // ATN
if ((IECLines ^ old_lines) & 0x10) { // ATN changed
the_cpu_1541->NewATNState();
if (old_lines & 0x10) // ATN 1->0
the_cpu_1541->TriggerIECInterrupt();
}
}
/*
* Write to register (CIA 2)
*/
void MOS6526_2::WriteRegister(uint16 adr, uint8 byte)
{
switch (adr) {
case 0x0:
pra = byte;
write_pa(~pra & ddra);
break;
case 0x1:
prb = byte;
break;
case 0x2:
ddra = byte;
write_pa(~pra & ddra);
break;
case 0x3:
ddrb = byte;
break;
case 0x4:
latcha = (latcha & 0xff00) | byte;
break;
case 0x5:
latcha = (latcha & 0xff) | (byte << 8);
if (!(cra & 1)) // Reload timer if stopped
ta = latcha;
break;
case 0x6: latchb = (latchb & 0xff00) | byte; break;
case 0x7:
latchb = (latchb & 0xff) | (byte << 8);
if (!(crb & 1)) // Reload timer if stopped
tb = latchb;
break;
case 0x8:
if (crb & 0x80)
alm_10ths = byte & 0x0f;
else
tod_10ths = byte & 0x0f;
break;
case 0x9:
if (crb & 0x80)
alm_sec = byte & 0x7f;
else
tod_sec = byte & 0x7f;
break;
case 0xa:
if (crb & 0x80)
alm_min = byte & 0x7f;
else
tod_min = byte & 0x7f;
break;
case 0xb:
if (crb & 0x80)
alm_hr = byte & 0x9f;
else
tod_hr = byte & 0x9f;
break;
case 0xc:
sdr = byte;
TriggerInterrupt(8); // Fake SDR interrupt for programs that need it
break;
case 0xd:
if (ThePrefs.CIAIRQHack)
icr = 0;
if (byte & 0x80) {
int_mask |= byte & 0x7f;
if (icr & int_mask & 0x1f) { // Trigger NMI if pending
icr |= 0x80;
the_cpu->TriggerNMI();
}
} else
int_mask &= ~byte;
break;
case 0xe:
cra = byte & 0xef;
if (byte & 0x10) // Force load
ta = latcha;
ta_cnt_phi2 = ((byte & 0x21) == 0x01);
break;
case 0xf:
crb = byte & 0xef;
if (byte & 0x10) // Force load
tb = latchb;
tb_cnt_phi2 = ((byte & 0x61) == 0x01);
tb_cnt_ta = ((byte & 0x41) == 0x41);
break;
}
}
/*
* Count CIA TOD clock (called during VBlank)
*/
void MOS6526::CountTOD(void)
{
uint8 lo, hi;
// Decrement frequency divider
if (tod_divider)
tod_divider--;
else {
// Reload divider according to 50/60 Hz flag
if (cra & 0x80)
tod_divider = 4;
else
tod_divider = 5;
// 1/10 seconds
tod_10ths++;
if (tod_10ths > 9) {
tod_10ths = 0;
// Seconds
lo = (tod_sec & 0x0f) + 1;
hi = tod_sec >> 4;
if (lo > 9) {
lo = 0;
hi++;
}
if (hi > 5) {
tod_sec = 0;
// Minutes
lo = (tod_min & 0x0f) + 1;
hi = tod_min >> 4;
if (lo > 9) {
lo = 0;
hi++;
}
if (hi > 5) {
tod_min = 0;
// Hours
lo = (tod_hr & 0x0f) + 1;
hi = (tod_hr >> 4) & 1;
tod_hr &= 0x80; // Keep AM/PM flag
if (lo > 9) {
lo = 0;
hi++;
}
tod_hr |= (hi << 4) | lo;
if ((tod_hr & 0x1f) > 0x11)
tod_hr = (tod_hr & 0x80) ^ 0x80;
} else
tod_min = (hi << 4) | lo;
} else
tod_sec = (hi << 4) | lo;
}
// Alarm time reached? Trigger interrupt if enabled
if (tod_10ths == alm_10ths && tod_sec == alm_sec &&
tod_min == alm_min && tod_hr == alm_hr)
TriggerInterrupt(4);
}
}
/*
* Trigger IRQ (CIA 1)
*/
void MOS6526_1::TriggerInterrupt(int bit)
{
icr |= bit;
if (int_mask & bit) {
icr |= 0x80;
the_cpu->TriggerCIAIRQ();
}
}
/*
* Trigger NMI (CIA 2)
*/
void MOS6526_2::TriggerInterrupt(int bit)
{
icr |= bit;
if (int_mask & bit) {
icr |= 0x80;
the_cpu->TriggerNMI();
}
}

199
arm9/source/CIA.h Normal file
View File

@ -0,0 +1,199 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* CIA.h - 6526 emulation
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CIA_H
#define _CIA_H
#include "Prefs.h"
class MOS6510;
class MOS6502_1541;
class MOS6569;
struct MOS6526State;
class MOS6526 {
public:
MOS6526(MOS6510 *CPU);
void Reset(void);
void GetState(MOS6526State *cs);
void SetState(MOS6526State *cs);
void EmulateLine(int cycles);
void CountTOD(void);
virtual void TriggerInterrupt(int bit)=0;
protected:
MOS6510 *the_cpu; // Pointer to 6510
uint8 pra, prb, ddra, ddrb;
uint16 ta, tb, latcha, latchb;
uint8 tod_10ths, tod_sec, tod_min, tod_hr;
uint8 alm_10ths, alm_sec, alm_min, alm_hr;
uint8 sdr, icr, cra, crb;
uint8 int_mask;
int tod_divider; // TOD frequency divider
bool tod_halt, // Flag: TOD halted
ta_cnt_phi2, // Flag: Timer A is counting Phi 2
tb_cnt_phi2, // Flag: Timer B is counting Phi 2
tb_cnt_ta; // Flag: Timer B is counting underflows of Timer A
};
class MOS6526_1 : public MOS6526 {
public:
MOS6526_1(MOS6510 *CPU, MOS6569 *VIC);
void Reset(void);
uint8 ReadRegister(uint16 adr);
void WriteRegister(uint16 adr, uint8 byte);
virtual void TriggerInterrupt(int bit);
uint8 KeyMatrix[8]; // C64 keyboard matrix, 1 bit/key (0: key down, 1: key up)
uint8 RevMatrix[8]; // Reversed keyboard matrix
uint8 Joystick1; // Joystick 1 AND value
uint8 Joystick2; // Joystick 2 AND value
private:
void check_lp(void);
MOS6569 *the_vic;
uint8 prev_lp; // Previous state of LP line (bit 4)
};
class MOS6526_2 : public MOS6526{
public:
MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541);
void Reset(void);
uint8 ReadRegister(uint16 adr);
void WriteRegister(uint16 adr, uint8 byte);
virtual void TriggerInterrupt(int bit);
uint8 IECLines; // State of IEC lines (bit 7 - DATA, bit 6 - CLK, bit 4 - ATN)
private:
MOS6569 *the_vic;
MOS6502_1541 *the_cpu_1541;
void write_pa(uint8_t byte);
};
// CIA state
struct MOS6526State {
uint8 pra;
uint8 ddra;
uint8 prb;
uint8 ddrb;
uint8 ta_lo;
uint8 ta_hi;
uint8 tb_lo;
uint8 tb_hi;
uint8 tod_10ths;
uint8 tod_sec;
uint8 tod_min;
uint8 tod_hr;
uint8 sdr;
uint8 int_data; // Pending interrupts
uint8 cra;
uint8 crb;
// Additional registers
uint16 latcha; // Timer latches
uint16 latchb;
uint8 alm_10ths; // Alarm time
uint8 alm_sec;
uint8 alm_min;
uint8 alm_hr;
uint8 int_mask; // Enabled interrupts
};
/*
* Emulate CIA for one cycle/raster line
*/
inline void MOS6526::EmulateLine(int cycles)
{
unsigned long tmp;
// Timer A
if (ta_cnt_phi2) {
ta = tmp = ta - cycles; // Decrement timer
if (tmp > 0xffff) { // Underflow?
ta = latcha; // Reload timer
if (cra & 8) { // One-shot?
cra &= 0xfe;
ta_cnt_phi2 = false;
}
TriggerInterrupt(1);
if (tb_cnt_ta) { // Timer B counting underflows of Timer A?
tb = tmp = tb - 1; // tmp = --tb doesn't work
if (tmp > 0xffff) goto tb_underflow;
}
}
}
// Timer B
if (tb_cnt_phi2) {
tb = tmp = tb - cycles; // Decrement timer
if (tmp > 0xffff) { // Underflow?
tb_underflow:
tb = latchb;
if (crb & 8) { // One-shot?
crb &= 0xfe;
tb_cnt_phi2 = false;
tb_cnt_ta = false;
}
TriggerInterrupt(2);
}
}
}
#endif

756
arm9/source/CPU1541.cpp Normal file
View File

@ -0,0 +1,756 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* CPU1541.cpp - 6502 (1541) emulation (line based)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Notes:
* ------
*
* - The EmulateLine() function is called for every emulated
* raster line. It has a cycle counter that is decremented
* by every executed opcode and if the counter goes below
* zero, the function returns.
* - Memory map (1541C, the 1541 and 1541-II are a bit different):
* $0000-$07ff RAM (2K)
* $0800-$0fff RAM mirror
* $1000-$17ff free
* $1800-$1bff VIA 1
* $1c00-$1fff VIA 2
* $2000-$bfff free
* $c000-$ffff ROM (16K)
* - All memory accesses are done with the read_byte() and
* write_byte() functions which also do the memory address
* decoding. The read_zp() and write_zp() functions allow
* faster access to the zero page, the pop_byte() and
* push_byte() macros for the stack.
* - The PC is either emulated with a 16 bit address or a
* direct memory pointer (for faster access), depending on
* the PC_IS_POINTER #define. In the latter case, a second
* pointer, pc_base, is kept to allow recalculating the
* 16 bit 6502 PC if it has to be pushed on the stack.
* - The possible interrupt sources are:
* INT_VIA1IRQ: I flag is checked, jump to ($fffe)
* INT_VIA2IRQ: I flag is checked, jump to ($fffe)
* INT_RESET: Jump to ($fffc)
* - Interrupts are not checked before every opcode but only
* at certain times:
* On entering EmulateLine()
* On CLI
* On PLP if the I flag was cleared
* On RTI if the I flag was cleared
* - The z_flag variable has the inverse meaning of the
* 6502 Z flag
* - Only the highest bit of the n_flag variable is used
* - The $f2 opcode that would normally crash the 6502 is
* used to implement emulator-specific functions
* - The 1541 6502 emulation also includes a very simple VIA
* emulation (enough to make the IEC bus and GCR loading work).
* It's too small to move it to a source file of its own.
*
* Incompatibilities:
* ------------------
*
* - If PC_IS_POINTER is set, neither branches accross memory
* areas nor jumps to I/O space are possible
* - Extra cycles for crossing page boundaries are not
* accounted for
*/
#include "sysdeps.h"
#include "CPU1541.h"
#include "CPUC64.h"
#include "1541job.h"
#include "C64.h"
#include "CIA.h"
#include "Display.h"
#include "mainmenu.h"
extern uint8 myRAM1541[DRIVE_RAM_SIZE];
/*
* 6502 constructor: Initialize registers
*/
MOS6502_1541::MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom)
: ram(Ram), rom(Rom), the_c64(c64), the_display(disp), the_job(job)
{
a = x = y = 0;
sp = 0xff;
n_flag = z_flag = 0;
v_flag = d_flag = c_flag = false;
i_flag = true;
borrowed_cycles = 0;
rom = Rom - 0xC000; // So we don't have to mask the ROM when reading
via1_t1c = via1_t1l = via1_t2c = via1_t2l = 0;
via1_sr = 0;
via2_t1c = via2_t1l = via2_t2c = via2_t2l = 0;
via2_sr = 0;
Idle = false;
}
/*
* Reset CPU asynchronously
*/
void MOS6502_1541::AsyncReset(void)
{
interrupt.intr[INT_RESET] = true;
Idle = false;
}
/*
* Read a byte from I/O space
*/
inline uint8 MOS6502_1541::read_byte_io(uint16 adr)
{
if ((adr & 0xfc00) == 0x1800) // VIA 1
switch (adr & 0xf) {
case 0:
return ((via1_prb & 0x1a)
| ((IECLines & TheCIA2->IECLines) >> 7) // DATA
| (((IECLines & TheCIA2->IECLines) >> 4) & 0x04) // CLK
| ((TheCIA2->IECLines << 3) & 0x80)) ^ 0x85; // ATN
case 1:
via1_ifr &= 0xfd; // Clear CA1 interrupt
interrupt.intr[INT_VIA1IRQ] = false;
return 0xff; // Keep 1541C ROMs happy (track 0 sensor)
case 15:
return 0xff; // Keep 1541C ROMs happy (track 0 sensor)
case 2:
return via1_ddrb;
case 3:
return via1_ddra;
case 4:
via1_ifr &= 0xbf;
return via1_t1c;
case 5:
return via1_t1c >> 8;
case 6:
return via1_t1l;
case 7:
return via1_t1l >> 8;
case 8:
via1_ifr &= 0xdf;
return via1_t2c;
case 9:
return via1_t2c >> 8;
case 10:
return via1_sr;
case 11:
return via1_acr;
case 12:
return via1_pcr;
case 13:
return via1_ifr | (via1_ifr & via1_ier ? 0x80 : 0);
case 14:
return via1_ier | 0x80;
default: // Can't happen
return 0;
}
else if ((adr & 0xfc00) == 0x1c00) // VIA 2
switch (adr & 0xf) {
case 0:
if (the_job->SyncFound())
return (via2_prb & 0x7f) | the_job->WPState();
else
return (via2_prb | 0x80) | the_job->WPState();
case 1:
case 15:
return the_job->ReadGCRByte();
case 2:
return via2_ddrb;
case 3:
return via2_ddra;
case 4:
via2_ifr &= 0xbf;
interrupt.intr[INT_VIA2IRQ] = false; // Clear IRQ
return via2_t1c;
case 5:
return via2_t1c >> 8;
case 6:
return via2_t1l;
case 7:
return via2_t1l >> 8;
case 8:
via2_ifr &= 0xdf;
return via2_t2c;
case 9:
return via2_t2c >> 8;
case 10:
return via2_sr;
case 11:
return via2_acr;
case 12:
return via2_pcr;
case 13:
return via2_ifr | (via2_ifr & via2_ier ? 0x80 : 0);
case 14:
return via2_ier | 0x80;
default: // Can't happen
return 0;
}
else
return adr >> 8;
}
/*
* Read a byte from the CPU's address space
*/
// This can be used when we know we're reading an 'immediate' byte from ROM
inline uint8 MOS6502_1541::read_byte_fast(uint16 adr)
{
if (adr & 0xC000)
return rom[adr];
return myRAM1541[adr];
}
// The more general read has to handle RAM, ROM and IO space
uint8 MOS6502_1541::read_byte(uint16 adr)
{
if (adr & 0xC000)
return rom[adr];
else if ((adr & 0x1800) == 0x0000)
return myRAM1541[adr & 0x07ff];
else
return read_byte_io(adr);
}
/*
* Read a word (little-endian) from the CPU's address space
*/
inline uint16 MOS6502_1541::read_word(uint16 adr)
{
return read_byte(adr) | (read_byte(adr+1) << 8);
}
/*
* Write a byte to I/O space
*/
void MOS6502_1541::write_byte_io(uint16 adr, uint8 byte)
{
if ((adr & 0xfc00) == 0x1800) // VIA 1
switch (adr & 0xf) {
case 0:
via1_prb = byte;
byte = ~via1_prb & via1_ddrb;
IECLines = ((byte << 6) & (((~byte ^ TheCIA2->IECLines) << 3) & 0x80)) | ((byte << 3) & 0x40);
break;
case 1:
case 15:
via1_pra = byte;
break;
case 2:
via1_ddrb = byte;
byte &= ~via1_prb;
IECLines = ((byte << 6) & (((~byte ^ TheCIA2->IECLines) << 3) & 0x80)) | ((byte << 3) & 0x40);
break;
case 3:
via1_ddra = byte;
break;
case 4:
case 6:
via1_t1l = (via1_t1l & 0xff00) | byte;
break;
case 5:
via1_t1l = (via1_t1l & 0xff) | (byte << 8);
via1_ifr &= 0xbf;
via1_t1c = via1_t1l;
break;
case 7:
via1_t1l = (via1_t1l & 0xff) | (byte << 8);
break;
case 8:
via1_t2l = (via1_t2l & 0xff00) | byte;
break;
case 9:
via1_t2l = (via1_t2l & 0xff) | (byte << 8);
via1_ifr &= 0xdf;
via1_t2c = via1_t2l;
break;
case 10:
via1_sr = byte;
break;
case 11:
via1_acr = byte;
break;
case 12:
via1_pcr = byte;
break;
case 13:
via1_ifr &= ~byte;
break;
case 14:
if (byte & 0x80)
via1_ier |= byte & 0x7f;
else
via1_ier &= ~byte;
break;
}
else if ((adr & 0xfc00) == 0x1c00) // VIA 2
switch (adr & 0xf) {
case 0:
if ((via2_prb ^ byte) & 8) // Bit 3: Drive LED
the_display->UpdateLEDs(byte & 8 ? 1 : 0, 0);
if ((via2_prb ^ byte) & 0x04) // Bit 2: Spindle motor
the_job->SetMotor(byte & 0x04);
if ((via2_prb ^ byte) & 3) // Bits 0/1: Stepper motor
{
if ((via2_prb & 3) == ((byte+1) & 3))
the_job->MoveHeadOut();
else if ((via2_prb & 3) == ((byte-1) & 3))
the_job->MoveHeadIn();
}
via2_prb = byte;
break;
case 1:
case 15:
via2_pra = byte;
break;
case 2:
via2_ddrb = byte;
break;
case 3:
via2_ddra = byte;
break;
case 4:
case 6:
via2_t1l = (via2_t1l & 0xff00) | byte;
break;
case 5:
via2_t1l = (via2_t1l & 0xff) | (byte << 8);
via2_ifr &= 0xbf;
via2_t1c = via2_t1l;
break;
case 7:
via2_t1l = (via2_t1l & 0xff) | (byte << 8);
break;
case 8:
via2_t2l = (via2_t2l & 0xff00) | byte;
break;
case 9:
via2_t2l = (via2_t2l & 0xff) | (byte << 8);
via2_ifr &= 0xdf;
via2_t2c = via2_t2l;
break;
case 10:
via2_sr = byte;
break;
case 11:
via2_acr = byte;
break;
case 12:
via2_pcr = byte;
break;
case 13:
via2_ifr &= ~byte;
break;
case 14:
if (byte & 0x80)
via2_ier |= byte & 0x7f;
else
via2_ier &= ~byte;
break;
}
}
/*
* Write a byte to the CPU's address space
*/
inline void MOS6502_1541::write_byte(uint16 adr, uint8 byte)
{
if (adr & 0xF800) write_byte_io(adr, byte);
else myRAM1541[adr] = byte;
}
/*
* Read a byte from the zeropage
*/
inline uint8 MOS6502_1541::read_zp(uint16 adr)
{
return myRAM1541[adr];
}
/*
* Read a word (little-endian) from the zeropage
*/
inline uint16 MOS6502_1541::read_zp_word(uint16 adr)
{
return myRAM1541[adr & 0xff] | (myRAM1541[(adr+1) & 0xff] << 8);
}
/*
* Write a byte to the zeropage
*/
inline void MOS6502_1541::write_zp(uint16 adr, uint8 byte)
{
myRAM1541[adr] = byte;
}
/*
* Jump to address
*/
inline void MOS6502_1541::jump(uint16 adr)
{
pc = adr;
}
/*
* Adc instruction
*/
void MOS6502_1541::do_adc(uint8 byte)
{
if (!d_flag) {
uint16 tmp;
// Binary mode
tmp = a + byte + (c_flag ? 1 : 0);
c_flag = tmp > 0xff;
v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80);
z_flag = n_flag = a = tmp;
} else {
uint16 al, ah;
// Decimal mode
al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble
if (al > 9) al += 6; // BCD fixup for lower nybble
ah = (a >> 4) + (byte >> 4); // Calculate upper nybble
if (al > 0x0f) ah++;
z_flag = a + byte + (c_flag ? 1 : 0); // Set flags
n_flag = ah << 4; // Only highest bit used
v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80);
if (ah > 9) ah += 6; // BCD fixup for upper nybble
c_flag = ah > 0x0f; // Set carry flag
a = (ah << 4) | (al & 0x0f); // Compose result
}
}
/*
* Sbc instruction
*/
void MOS6502_1541::do_sbc(uint8 byte)
{
uint16 tmp = a - byte - (c_flag ? 0 : 1);
if (!d_flag) {
// Binary mode
c_flag = tmp < 0x100;
v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
z_flag = n_flag = a = tmp;
} else {
uint16 al, ah;
// Decimal mode
al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble
ah = (a >> 4) - (byte >> 4); // Calculate upper nybble
if (al & 0x10) {
al -= 6; // BCD fixup for lower nybble
ah--;
}
if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble
c_flag = tmp < 0x100; // Set flags
v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
z_flag = n_flag = tmp;
a = (ah << 4) | (al & 0x0f); // Compose result
}
}
/*
* Get 6502 register state
*/
void MOS6502_1541::GetState(MOS6502State *s)
{
s->a = a;
s->x = x;
s->y = y;
s->p = 0x20 | (n_flag & 0x80);
if (v_flag) s->p |= 0x40;
if (d_flag) s->p |= 0x08;
if (i_flag) s->p |= 0x04;
if (!z_flag) s->p |= 0x02;
if (c_flag) s->p |= 0x01;
s->pc = pc;
s->sp = sp | 0x0100;
s->intr[INT_VIA1IRQ] = interrupt.intr[INT_VIA1IRQ];
s->intr[INT_VIA2IRQ] = interrupt.intr[INT_VIA2IRQ];
s->intr[INT_RESET] = interrupt.intr[INT_RESET];
s->instruction_complete = true;
s->idle = Idle;
s->via1_pra = via1_pra; s->via1_ddra = via1_ddra;
s->via1_prb = via1_prb; s->via1_ddrb = via1_ddrb;
s->via1_t1c = via1_t1c; s->via1_t1l = via1_t1l;
s->via1_t2c = via1_t2c; s->via1_t2l = via1_t2l;
s->via1_sr = via1_sr;
s->via1_acr = via1_acr; s->via1_pcr = via1_pcr;
s->via1_ifr = via1_ifr; s->via1_ier = via1_ier;
s->via2_pra = via2_pra; s->via2_ddra = via2_ddra;
s->via2_prb = via2_prb; s->via2_ddrb = via2_ddrb;
s->via2_t1c = via2_t1c; s->via2_t1l = via2_t1l;
s->via2_t2c = via2_t2c; s->via2_t2l = via2_t2l;
s->via2_sr = via2_sr;
s->via2_acr = via2_acr; s->via2_pcr = via2_pcr;
s->via2_ifr = via2_ifr; s->via2_ier = via2_ier;
}
/*
* Restore 6502 state
*/
void MOS6502_1541::SetState(MOS6502State *s)
{
a = s->a;
x = s->x;
y = s->y;
n_flag = s->p;
v_flag = s->p & 0x40;
d_flag = s->p & 0x08;
i_flag = s->p & 0x04;
z_flag = !(s->p & 0x02);
c_flag = s->p & 0x01;
jump(s->pc);
sp = s->sp & 0xff;
interrupt.intr[INT_VIA1IRQ] = s->intr[INT_VIA1IRQ];
interrupt.intr[INT_VIA2IRQ] = s->intr[INT_VIA2IRQ];
interrupt.intr[INT_RESET] = s->intr[INT_RESET];
Idle = s->idle;
via1_pra = s->via1_pra; via1_ddra = s->via1_ddra;
via1_prb = s->via1_prb; via1_ddrb = s->via1_ddrb;
via1_t1c = s->via1_t1c; via1_t1l = s->via1_t1l;
via1_t2c = s->via1_t2c; via1_t2l = s->via1_t2l;
via1_sr = s->via1_sr;
via1_acr = s->via1_acr; via1_pcr = s->via1_pcr;
via1_ifr = s->via1_ifr; via1_ier = s->via1_ier;
via2_pra = s->via2_pra; via2_ddra = s->via2_ddra;
via2_prb = s->via2_prb; via2_ddrb = s->via2_ddrb;
via2_t1c = s->via2_t1c; via2_t1l = s->via2_t1l;
via2_t2c = s->via2_t2c; via2_t2l = s->via2_t2l;
via2_sr = s->via2_sr;
via2_acr = s->via2_acr; via2_pcr = s->via2_pcr;
via2_ifr = s->via2_ifr; via2_ier = s->via2_ier;
}
/*
* Reset CPU
*/
void MOS6502_1541::Reset(void)
{
the_job->Reset();
// IEC lines and VIA registers
IECLines = 0xc0;
via1_pra = via1_ddra = via1_prb = via1_ddrb = 0;
via1_acr = via1_pcr = 0;
via1_ifr = via1_ier = 0;
via2_pra = via2_ddra = via2_prb = via2_ddrb = 0;
via2_acr = via2_pcr = 0;
via2_ifr = via2_ier = 0;
// Clear all interrupt lines
interrupt.intr_any = 0;
// Read reset vector
jump(read_word(0xfffc));
// Wake up 1541
Idle = false;
}
/*
* Illegal opcode encountered
*/
void MOS6502_1541::illegal_op(uint8 op, uint16 at)
{
char illop_msg[80];
sprintf(illop_msg, "1541: Illegal opcode %02x at %04x.", op, at);
if (ShowRequester(illop_msg, "Reset 1541", "Reset C64"))
the_c64->Reset();
Reset();
}
/*
* Stack macros
*/
// Pop a byte from the stack
#define pop_byte() myRAM1541[(++sp) | 0x0100]
// Push a byte onto the stack
#define push_byte(byte) (myRAM1541[((sp--) & 0xff) | 0x0100] = (byte))
// Pop processor flags from the stack
#define pop_flags() \
n_flag = tmp = pop_byte(); \
v_flag = tmp & 0x40; \
d_flag = tmp & 0x08; \
i_flag = tmp & 0x04; \
z_flag = !(tmp & 0x02); \
c_flag = tmp & 0x01;
// Push processor flags onto the stack
#define push_flags(b_flag) \
tmp = 0x20 | (n_flag & 0x80); \
if ((via2_pcr & 0x0e) == 0x0e && the_job->ByteReady()) v_flag = true; \
if (v_flag) tmp |= 0x40; \
if (b_flag) tmp |= 0x10; \
if (d_flag) tmp |= 0x08; \
if (i_flag) tmp |= 0x04; \
if (!z_flag) tmp |= 0x02; \
if (c_flag) tmp |= 0x01; \
push_byte(tmp);
void MOS6502_1541::ext_opcode(void)
{
if (pc < 0xc000) {
illegal_op(0xf2, pc-1);
}
switch (read_byte(pc++)) {
case 0x00: // Go to sleep in DOS idle loop if error flag is clear and no command received
Idle = !(myRAM1541[0x26c] | myRAM1541[0x7c]);
jump(0xebff);
break;
case 0x01: // Write sector
the_job->WriteSector();
jump(0xf5dc);
break;
case 0x02: // Format track
the_job->FormatTrack();
jump(0xfd8b);
break;
default:
illegal_op(0xf2, pc-1);
break;
}
}
/*
* Emulate cycles_left worth of 6502 instructions
* Returns number of cycles of last instruction
*/
int MOS6502_1541::EmulateLine(int cycles_left, int cpu_cycles)
{
uint8 tmp, tmp2;
uint16 adr;
int last_cycles = 0;
// Any pending interrupts?
if (interrupt.intr_any) {
handle_int:
if (interrupt.intr[INT_RESET])
{
Reset();
}
else if ((interrupt.intr[INT_VIA1IRQ] || interrupt.intr[INT_VIA2IRQ]) && !i_flag)
{
push_byte(pc >> 8); push_byte(pc);
push_flags(false);
i_flag = true;
jump(read_word(0xfffe));
last_cycles = 7;
}
}
#define IS_CPU_1541
#undef PRECISE_CPU_CYCLES
#define PRECISE_CPU_CYCLES 0
#include "CPU_emulline.h"
#undef PRECISE_CPU_CYCLES
#define PRECISE_CPU_CYCLES 1
return last_cycles;
}

288
arm9/source/CPU1541.h Normal file
View File

@ -0,0 +1,288 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* CPU1541.h - 6502 (1541) emulation (line based)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CPU_1541_H
#define _CPU_1541_H
#include "CIA.h"
#include "C64.h"
// Set this to 1 for more precise CPU cycle calculation
#ifndef PRECISE_CPU_CYCLES
#define PRECISE_CPU_CYCLES 0
#endif
// Interrupt types
enum {
INT_VIA1IRQ,
INT_VIA2IRQ,
INT_RESET
};
class C64;
class Job1541;
class C64Display;
struct MOS6502State;
// 6502 emulation (1541)
class MOS6502_1541 {
public:
MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom);
int EmulateLine(int cycles_left, int cpu_cycles); // Emulate until cycles_left underflows
void ext_opcode(void);
void Reset(void);
void AsyncReset(void); // Reset the CPU asynchronously
void GetState(MOS6502State *s);
void SetState(MOS6502State *s);
void CountVIATimers(int cycles);
void NewATNState(void);
void TriggerIECInterrupt(void);
void trigger_via1_irq(void);
void trigger_via2_irq(void);
bool InterruptEnabled(void);
MOS6526_2 *TheCIA2; // Pointer to C64 CIA 2
uint8 IECLines; // State of IEC lines (bit 7 - DATA, bit 6 - CLK)
bool Idle; // true: 1541 is idle
private:
uint8 read_byte(uint16 adr);
uint8 read_byte_fast(uint16 adr);
uint8 read_byte_io(uint16 adr);
uint16 read_word(uint16 adr);
void write_byte(uint16 adr, uint8 byte);
void write_byte_io(uint16 adr, uint8 byte);
uint8 read_zp(uint16 adr);
uint16 read_zp_word(uint16 adr);
void write_zp(uint16 adr, uint8 byte);
void jump(uint16 adr);
void illegal_op(uint8 op, uint16 at);
void illegal_jump(uint16 at, uint16 to);
void do_adc(uint8 byte);
void do_sbc(uint8 byte);
uint8 *ram; // Pointer to main RAM
uint8 *rom; // Pointer to ROM
C64 *the_c64; // Pointer to C64 object
C64Display *the_display; // Pointer to C64 display object
Job1541 *the_job; // Pointer to 1541 job object
union { // Pending interrupts
uint8 intr[4]; // Index: See definitions above
unsigned long intr_any;
} interrupt;
uint8 z_flag, n_flag;
uint8 v_flag, d_flag, i_flag, c_flag;
uint8 a, x, y, sp;
uint16_t pc;
int borrowed_cycles; // Borrowed cycles from next line
uint8 via1_pra; // PRA of VIA 1
uint8 via1_ddra; // DDRA of VIA 1
uint8 via1_prb; // PRB of VIA 1
uint8 via1_ddrb; // DDRB of VIA 1
uint16 via1_t1c; // T1 Counter of VIA 1
uint16 via1_t1l; // T1 Latch of VIA 1
uint16 via1_t2c; // T2 Counter of VIA 1
uint16 via1_t2l; // T2 Latch of VIA 1
uint8 via1_sr; // SR of VIA 1
uint8 via1_acr; // ACR of VIA 1
uint8 via1_pcr; // PCR of VIA 1
uint8 via1_ifr; // IFR of VIA 1
uint8 via1_ier; // IER of VIA 1
uint8 via2_pra; // PRA of VIA 2
uint8 via2_ddra; // DDRA of VIA 2
uint8 via2_prb; // PRB of VIA 2
uint8 via2_ddrb; // DDRB of VIA 2
uint16 via2_t1c; // T1 Counter of VIA 2
uint16 via2_t1l; // T1 Latch of VIA 2
uint16 via2_t2c; // T2 Counter of VIA 2
uint16 via2_t2l; // T2 Latch of VIA 2
uint8 via2_sr; // SR of VIA 2
uint8 via2_acr; // ACR of VIA 2
uint8 via2_pcr; // PCR of VIA 2
uint8 via2_ifr; // IFR of VIA 2
uint8 via2_ier; // IER of VIA 2
};
// 6502 state
struct MOS6502State {
uint8 a, x, y;
uint8 p; // Processor flags
uint16 pc, sp;
uint8 intr[4]; // Interrupt state
bool instruction_complete;
bool idle;
uint8 via1_pra; // VIA 1
uint8 via1_ddra;
uint8 via1_prb;
uint8 via1_ddrb;
uint16 via1_t1c;
uint16 via1_t1l;
uint16 via1_t2c;
uint16 via1_t2l;
uint8 via1_sr;
uint8 via1_acr;
uint8 via1_pcr;
uint8 via1_ifr;
uint8 via1_ier;
uint8 via2_pra; // VIA 2
uint8 via2_ddra;
uint8 via2_prb;
uint8 via2_ddrb;
uint16 via2_t1c;
uint16 via2_t1l;
uint16 via2_t2c;
uint16 via2_t2l;
uint8 via2_sr;
uint8 via2_acr;
uint8 via2_pcr;
uint8 via2_ifr;
uint8 via2_ier;
};
/*
* Trigger VIA1 IRQ
*/
inline void MOS6502_1541::trigger_via1_irq(void)
{
interrupt.intr[INT_VIA1IRQ] = true;
Idle = false;
}
/*
* Trigger VIA2 IRQ
*/
inline void MOS6502_1541::trigger_via2_irq(void)
{
interrupt.intr[INT_VIA2IRQ] = true;
Idle = false;
}
/*
* Count VIA timers
*/
inline void MOS6502_1541::CountVIATimers(int cycles)
{
unsigned long tmp;
via1_t1c = tmp = via1_t1c - cycles;
if (tmp > 0xffff) {
via1_t1c = via1_t1l; // Reload from latch
via1_ifr |= 0x40;
}
if (!(via1_acr & 0x20)) { // Only count in one-shot mode
via1_t2c = tmp = via1_t2c - cycles;
if (tmp > 0xffff)
via1_ifr |= 0x20;
}
via2_t1c = tmp = via2_t1c - cycles;
if (tmp > 0xffff) {
via2_t1c = via2_t1l; // Reload from latch
via2_ifr |= 0x40;
if (via2_ier & 0x40)
trigger_via2_irq();
}
if (!(via2_acr & 0x20)) { // Only count in one-shot mode
via2_t2c = tmp = via2_t2c - cycles;
if (tmp > 0xffff)
via2_ifr |= 0x20;
}
}
/*
* ATN line probably changed state, recalc IECLines
*/
inline void MOS6502_1541::NewATNState(void)
{
uint8 byte = ~via1_prb & via1_ddrb;
IECLines = ((byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80) // DATA (incl. ATN acknowledge)
| ((byte << 3) & 0x40); // CLK
}
/*
* Interrupt by negative edge of ATN on IEC bus
*/
inline void MOS6502_1541::TriggerIECInterrupt(void)
{
if (via1_pcr & 0x01) // CA1 positive edge (1541 gets inverted bus signal)
{
via1_ifr |= 0x02;
if (via1_ier & 0x02) // CA1 interrupt enabled?
{
trigger_via1_irq();
}
}
}
/*
* Test if interrupts are enabled (for job loop)
*/
inline bool MOS6502_1541::InterruptEnabled(void)
{
return !i_flag;
}
#endif

640
arm9/source/CPUC64.cpp Normal file
View File

@ -0,0 +1,640 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* CPUC64.cpp - 6510 (C64) emulation (line based)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Notes:
* ------
*
* - The EmulateLine() function is called for every emulated
* raster line. It has a cycle counter that is decremented by every
* executed opcode and if the counter goes below zero, the function
* returns.
* - All memory accesses are done with the read_byte() and
* write_byte() functions which also do the memory address decoding. The
* read_zp() and write_zp() functions allow faster access to the zero
* page, the pop_byte() and push_byte() macros for the stack.
* - If a write occurs to addresses 0 or 1, new_config() is called to check
* whether the memory configuration has changed
* - The possible interrupt sources are:
* INT_VICIRQ: I flag is checked, jump to ($fffe)
* INT_CIAIRQ: I flag is checked, jump to ($fffe)
* INT_NMI: Jump to ($fffa)
* INT_RESET: Jump to ($fffc)
* - Interrupts are not checked before every opcode but only at certain
* times:
* On entering EmulateLine()
* On CLI
* On PLP if the I flag was cleared
* On RTI if the I flag was cleared
* - The z_flag variable has the inverse meaning of the 6510 Z flag.
* - Only the highest bit of the n_flag variable is used.
* - The $f2 opcode that would normally crash the 6510 is used to implement
* emulator-specific functions, mainly those for the IEC routines.
*/
#include <nds.h>
#include "sysdeps.h"
#include "CPUC64.h"
#include "C64.h"
#include "VIC.h"
#include "SID.h"
#include "CIA.h"
#include "IEC.h"
#include "Display.h"
#include "mainmenu.h"
enum {
INT_RESET = 3
};
extern uint8 myRAM[];
/*
* 6510 constructor: Initialize registers
*/
MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color)
: the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color)
{
a = x = y = 0;
sp = 0xff;
n_flag = z_flag = 0;
v_flag = d_flag = c_flag = false;
i_flag = true;
kernal_rom_offset = Kernal - 0xe000; // For faster indexing during emulation...
interrupt.intr[INT_VICIRQ] = false;
interrupt.intr[INT_CIAIRQ] = false;
interrupt.intr[INT_NMI] = false;
interrupt.intr[INT_RESET] = false;
nmi_state = false;
borrowed_cycles = 0;
dfff_byte = 0x55;
}
/*
* Reset CPU asynchronously
*/
void MOS6510::AsyncReset(void)
{
interrupt.intr[INT_RESET] = true;
}
/*
* Raise NMI asynchronously (Restore key)
*/
void MOS6510::AsyncNMI(void)
{
if (!nmi_state)
interrupt.intr[INT_NMI] = true;
}
/*
* Memory configuration has probably changed
*/
void MOS6510::new_config(void)
{
if ((ram[0] & 0x10) == 0) {
ram[1] |= 0x10; // Keep cassette sense line high
}
uint8 port = ~ram[0] | ram[1];
basic_in = (port & 3) == 3;
kernal_in = port & 2;
char_in = (port & 3) && !(port & 4);
io_in = (port & 3) && (port & 4);
}
/*
* Read a byte from I/O / ROM space
*/
__attribute__ ((noinline)) uint8 MOS6510::read_byte_io(uint16 adr)
{
switch (adr >> 12) {
case 0xa:
case 0xb:
if (basic_in)
{
return basic_rom[adr & 0x1fff];
}
else
return myRAM[adr];
case 0xc:
return myRAM[adr];
case 0xd:
if (io_in)
switch ((adr >> 8) & 0x0f) {
case 0x0: // VIC
case 0x1:
case 0x2:
case 0x3:
return TheVIC->ReadRegister(adr & 0x3f);
case 0x4: // SID
case 0x5:
case 0x6:
case 0x7:
return TheSID->ReadRegister(adr & 0x1f);
case 0x8: // Color RAM
case 0x9:
case 0xa:
case 0xb:
return color_ram[adr & 0x03ff] | (rand() & 0xf0);
case 0xc: // CIA 1
return TheCIA1->ReadRegister(adr & 0x0f);
case 0xd: // CIA 2
return TheCIA2->ReadRegister(adr & 0x0f);
case 0xe: // REU/Open I/O
case 0xf:
return rand();
}
else if (char_in)
return char_rom[adr & 0x0fff];
else
return myRAM[adr];
case 0xe:
case 0xf:
if (kernal_in)
{
return kernal_rom_offset[adr];
}
else
return myRAM[adr];
default: // Can't happen
return 0;
}
}
/*
* Read a byte from the CPU's address space
*/
inline __attribute__((always_inline)) uint8 MOS6510::read_byte(uint16 adr)
{
if (adr < 0xa000)
return myRAM[adr];
else
return read_byte_io(adr);
}
/*
* Read a word (little-endian) from the CPU's address space
*/
inline uint16 MOS6510::read_word(uint16 adr)
{
return read_byte(adr) | (read_byte(adr+1) << 8);
}
/*
* Write byte to I/O space
*/
__attribute__ ((noinline)) void MOS6510::write_byte_io(uint16 adr, uint8 byte)
{
if (adr >= 0xe000)
{
myRAM[adr] = byte;
} else if (io_in)
switch ((adr >> 8) & 0x0f) {
case 0x0: // VIC
case 0x1:
case 0x2:
case 0x3:
TheVIC->WriteRegister(adr & 0x3f, byte);
return;
case 0x4: // SID
case 0x5:
case 0x6:
case 0x7:
TheSID->WriteRegister(adr & 0x1f, byte);
return;
case 0x8: // Color RAM
case 0x9:
case 0xa:
case 0xb:
color_ram[adr & 0x03ff] = byte & 0x0f;
return;
case 0xc: // CIA 1
TheCIA1->WriteRegister(adr & 0x0f, byte);
return;
case 0xd: // CIA 2
TheCIA2->WriteRegister(adr & 0x0f, byte);
return;
case 0xe: // REU/Open I/O
case 0xf:
return;
}
else
{
myRAM[adr] = byte;
}
}
/*
* Write a byte to the CPU's address space
*/
inline void MOS6510::write_byte(uint16 adr, uint8 byte)
{
if (adr < 0xd000)
{
myRAM[adr] = byte;
if (!(adr & 0xFFFE)) new_config(); // First two bytes are special...
} else write_byte_io(adr, byte);
}
/*
* Read a byte from the zeropage
*/
inline uint8 MOS6510::read_zp(uint16 adr)
{
return myRAM[adr];
}
/*
* Read a word (little-endian) from the zeropage
*/
inline uint16 MOS6510::read_zp_word(uint16 adr)
{
// !! zeropage word addressing wraps around !!
return myRAM[adr & 0xff] | (myRAM[(adr+1) & 0xff] << 8);
}
/*
* Write a byte to the zeropage
*/
inline void MOS6510::write_zp(uint16 adr, uint8 byte)
{
myRAM[adr] = byte;
// Check if memory configuration may have changed.
if (adr < 2)
new_config();
}
/*
* Jump to address
*/
#define jump(adr) pc = (adr)
/*
* Adc instruction
*/
void MOS6510::do_adc(uint8 byte)
{
if (!d_flag) {
uint16 tmp = a + (byte) + (c_flag ? 1 : 0);
c_flag = tmp > 0xff;
v_flag = !((a ^ (byte)) & 0x80) && ((a ^ tmp) & 0x80);
z_flag = n_flag = a = tmp;
} else {
uint16 al, ah;
al = (a & 0x0f) + ((byte) & 0x0f) + (c_flag ? 1 : 0);
if (al > 9) al += 6;
ah = (a >> 4) + ((byte) >> 4);
if (al > 0x0f) ah++;
z_flag = a + (byte) + (c_flag ? 1 : 0);
n_flag = ah << 4;
v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ (byte)) & 0x80);
if (ah > 9) ah += 6;
c_flag = ah > 0x0f;
a = (ah << 4) | (al & 0x0f);
}
}
/*
* Sbc instruction
*/
void MOS6510::do_sbc(uint8 byte)
{
uint16 tmp = a - (byte) - (c_flag ? 0 : 1);
if (!d_flag) {
c_flag = tmp < 0x100;
v_flag = ((a ^ tmp) & 0x80) && ((a ^ (byte)) & 0x80);
z_flag = n_flag = a = tmp;
} else {
uint16 al, ah;
al = (a & 0x0f) - ((byte) & 0x0f) - (c_flag ? 0 : 1);
ah = (a >> 4) - ((byte) >> 4);
if (al & 0x10) {
al -= 6;
ah--;
}
if (ah & 0x10) ah -= 6;
c_flag = tmp < 0x100;
v_flag = ((a ^ tmp) & 0x80) && ((a ^ (byte)) & 0x80);
z_flag = n_flag = tmp;
a = (ah << 4) | (al & 0x0f);
}
}
/*
* Get 6510 register state
*/
void MOS6510::GetState(MOS6510State *s)
{
s->a = a;
s->x = x;
s->y = y;
s->p = 0x20 | (n_flag & 0x80);
if (v_flag) s->p |= 0x40;
if (d_flag) s->p |= 0x08;
if (i_flag) s->p |= 0x04;
if (!z_flag) s->p |= 0x02;
if (c_flag) s->p |= 0x01;
s->ddr = ram[0];
s->pr = ram[1] & 0x3f;
s->pc = pc;
s->sp = sp | 0x0100;
s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ];
s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ];
s->intr[INT_NMI] = interrupt.intr[INT_NMI];
s->intr[INT_RESET] = interrupt.intr[INT_RESET];
s->nmi_state = nmi_state;
s->dfff_byte = dfff_byte;
s->instruction_complete = true;
}
/*
* Restore 6510 state
*/
void MOS6510::SetState(MOS6510State *s)
{
a = s->a;
x = s->x;
y = s->y;
n_flag = s->p;
v_flag = s->p & 0x40;
d_flag = s->p & 0x08;
i_flag = s->p & 0x04;
z_flag = !(s->p & 0x02);
c_flag = s->p & 0x01;
ram[0] = s->ddr;
ram[1] = s->pr;
new_config();
jump(s->pc);
sp = s->sp & 0xff;
interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ];
interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ];
interrupt.intr[INT_NMI] = s->intr[INT_NMI];
interrupt.intr[INT_RESET] = s->intr[INT_RESET];
nmi_state = s->nmi_state;
dfff_byte = s->dfff_byte;
}
/*
* Reset CPU
*/
void MOS6510::Reset(void)
{
// Delete 'CBM80' if present
if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd
&& ram[0x8007] == 0x38 && ram[0x8008] == 0x30)
ram[0x8004] = 0;
// Initialize extra 6510 registers and memory configuration
ram[0] = ram[1] = 0;
new_config();
// Clear all interrupt lines
interrupt.intr_any = 0;
nmi_state = false;
interrupt.intr[INT_VICIRQ] = false;
interrupt.intr[INT_CIAIRQ] = false;
interrupt.intr[INT_NMI] = false;
interrupt.intr[INT_RESET] = false;
// Read reset vector
jump(read_word(0xfffc));
}
/*
* Illegal opcode encountered
*/
void MOS6510::illegal_op(uint8 op, uint16 at)
{
char illop_msg[80];
sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at);
ShowRequester(illop_msg, "Reset");
the_c64->Reset();
Reset();
}
/*
* Jump to illegal address space (PC_IS_POINTER only)
*/
void MOS6510::illegal_jump(uint16 at, uint16 to)
{
char illop_msg[80];
sprintf(illop_msg, "Jump to I/O space at %04x to %04x.", at, to);
ShowRequester(illop_msg, "Reset");
the_c64->Reset();
Reset();
}
/*
* Stack macros
*/
// Pop a byte from the stack
#define pop_byte() myRAM[(++sp) | 0x0100]
// Push a byte onto the stack
#define push_byte(byte) (myRAM[((sp--) & 0xff) | 0x0100] = (byte))
// Pop processor flags from the stack
#define pop_flags() \
n_flag = tmp = pop_byte(); \
v_flag = tmp & 0x40; \
d_flag = tmp & 0x08; \
i_flag = tmp & 0x04; \
z_flag = !(tmp & 0x02); \
c_flag = tmp & 0x01;
// Push processor flags onto the stack
#define push_flags(b_flag) \
tmp = 0x20 | (n_flag & 0x80); \
if (v_flag) tmp |= 0x40; \
if (b_flag) tmp |= 0x10; \
if (d_flag) tmp |= 0x08; \
if (i_flag) tmp |= 0x04; \
if (!z_flag) tmp |= 0x02; \
if (c_flag) tmp |= 0x01; \
push_byte(tmp);
void MOS6510::ext_opcode(void)
{
if (pc < 0xe000) {
illegal_op(0xf2, pc-1);
}
switch (read_byte(pc++)) {
case 0x00:
ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80);
c_flag = false;
jump(0xedac);
break;
case 0x01:
ram[0x90] |= TheIEC->OutATN(ram[0x95]);
c_flag = false;
jump(0xedac);
break;
case 0x02:
ram[0x90] |= TheIEC->OutSec(ram[0x95]);
c_flag = false;
jump(0xedac);
break;
case 0x03:
ram[0x90] |= TheIEC->In(a);
z_flag = n_flag = a;
c_flag = false;
jump(0xedac);
break;
case 0x04:
TheIEC->SetATN();
jump(0xedfb);
break;
case 0x05:
TheIEC->RelATN();
jump(0xedac);
break;
case 0x06:
TheIEC->Turnaround();
jump(0xedac);
break;
case 0x07:
TheIEC->Release();
jump(0xedac);
break;
default:
illegal_op(0xf2, pc-1);
break;
}
}
/*
* Emulate cycles_left worth of 6510 instructions
* Returns number of cycles of last instruction
*/
ITCM_CODE int MOS6510::EmulateLine(int cycles_left)
{
uint8 tmp, tmp2;
uint16 adr; // Used by read_adr_abs()!
int last_cycles = 0;
// Any pending interrupts?
if (interrupt.intr_any)
{
handle_int:
if (interrupt.intr[INT_RESET])
{
Reset();
}
else if (interrupt.intr[INT_NMI])
{
interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input
push_byte(pc >> 8); push_byte(pc);
push_flags(false);
i_flag = true;
adr = read_word(0xfffa);
jump(adr);
last_cycles = 7;
}
else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && !i_flag)
{
push_byte(pc >> 8); push_byte(pc);
push_flags(false);
i_flag = true;
adr = read_word(0xfffe);
jump(adr);
last_cycles = 7;
}
}
#include "CPU_emulline.h"
return last_cycles;
}

190
arm9/source/CPUC64.h Normal file
View File

@ -0,0 +1,190 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* CPUC64.h - 6510 (C64) emulation (line based)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CPU_C64_H
#define _CPU_C64_H
#include "C64.h"
// Set this to 1 for more precise CPU cycle calculation
#ifndef PRECISE_CPU_CYCLES
#define PRECISE_CPU_CYCLES 0
#endif
// Set this to 1 for instruction-aligned CIA emulation
#ifndef PRECISE_CIA_CYCLES
#define PRECISE_CIA_CYCLES 0
#endif
// Interrupt types
enum {
INT_VICIRQ,
INT_CIAIRQ,
INT_NMI
// INT_RESET (private)
};
class MOS6569;
class MOS6581;
class MOS6526_1;
class MOS6526_2;
class IEC;
struct MOS6510State;
// 6510 emulation (C64)
class MOS6510 {
public:
MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color);
int EmulateLine(int cycles_left); // Emulate until cycles_left underflows
void Reset(void);
void AsyncReset(void); // Reset the CPU asynchronously
void AsyncNMI(void); // Raise NMI asynchronously (NMI pulse)
void GetState(MOS6510State *s);
void SetState(MOS6510State *s);
void TriggerVICIRQ(void);
void ClearVICIRQ(void);
void TriggerCIAIRQ(void);
void ClearCIAIRQ(void);
void TriggerNMI(void);
void ClearNMI(void);
int ExtConfig; // Memory configuration for ExtRead/WriteByte (0..7)
MOS6569 *TheVIC; // Pointer to VIC
MOS6581 *TheSID; // Pointer to SID
MOS6526_1 *TheCIA1; // Pointer to CIA 1
MOS6526_2 *TheCIA2; // Pointer to CIA 2
IEC *TheIEC; // Pointer to drive array
private:
void ext_opcode(void);
uint8 read_byte(uint16 adr);
uint8 read_byte_io(uint16 adr);
uint16 read_word(uint16 adr);
void write_byte(uint16 adr, uint8 byte);
void write_byte_io(uint16 adr, uint8 byte);
uint8 read_zp(uint16 adr);
uint16 read_zp_word(uint16 adr);
void write_zp(uint16 adr, uint8 byte);
void new_config(void);
void illegal_op(uint8 op, uint16 at);
void illegal_jump(uint16 at, uint16 to);
void do_adc(uint8 byte);
void do_sbc(uint8 byte);
uint8 read_emulator_id(uint16 adr);
uint16_t pc;
C64 *the_c64; // Pointer to C64 object
uint8 *ram; // Pointer to main RAM
uint8 *basic_rom, *kernal_rom, *char_rom, *color_ram; // Pointers to ROMs and color RAM
uint8 *kernal_rom_offset;
union { // Pending interrupts
uint8 intr[4]; // Index: See definitions above
unsigned long intr_any;
} interrupt;
bool nmi_state; // State of NMI line
uint8 z_flag, n_flag;
uint8 v_flag, d_flag, i_flag, c_flag;
uint8 a, x, y, sp;
int borrowed_cycles; // Borrowed cycles from next line
uint8 basic_in, kernal_in, char_in, io_in;
uint8 dfff_byte;
};
// 6510 state
struct MOS6510State {
uint8 a, x, y;
uint8 p; // Processor flags
uint8 ddr, pr; // Port
uint16 pc, sp;
uint8 intr[4]; // Interrupt state
bool nmi_state;
uint8 dfff_byte;
bool instruction_complete;
};
// Interrupt functions
inline void MOS6510::TriggerVICIRQ(void)
{
interrupt.intr[INT_VICIRQ] = true;
}
inline void MOS6510::TriggerCIAIRQ(void)
{
interrupt.intr[INT_CIAIRQ] = true;
}
inline void MOS6510::TriggerNMI(void)
{
if (!nmi_state) {
nmi_state = true;
interrupt.intr[INT_NMI] = true;
}
}
inline void MOS6510::ClearVICIRQ(void)
{
interrupt.intr[INT_VICIRQ] = false;
}
inline void MOS6510::ClearCIAIRQ(void)
{
interrupt.intr[INT_CIAIRQ] = false;
}
inline void MOS6510::ClearNMI(void)
{
nmi_state = false;
}
#endif

1387
arm9/source/CPU_emulline.h Normal file

File diff suppressed because it is too large Load Diff

870
arm9/source/Display.cpp Normal file
View File

@ -0,0 +1,870 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* Display.cpp - C64 graphics display, emulator window handling
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "Display.h"
#include "main.h"
#include "IEC.h"
#include "C64.h"
#include "Prefs.h"
#include "mainmenu.h"
#include <maxmod9.h>
#include "soundbank.h"
u8 floppy_sound_counter = 0;
// "Colodore" palette
uint8_t palette_red[16] = {
0x00, 0xff, 0x81, 0x75, 0x8e, 0x56, 0x2e, 0xed, 0x8e, 0x55, 0xc4, 0x4a, 0x7b, 0xa9, 0x70, 0xb2
};
uint8_t palette_green[16] = {
0x00, 0xff, 0x33, 0xce, 0x3c, 0xac, 0x2c, 0xf1, 0x50, 0x38, 0x6c, 0x4a, 0x7b, 0xff, 0x6d, 0xb2
};
uint8_t palette_blue[16] = {
0x00, 0xff, 0x38, 0xc8, 0x97, 0x4d, 0x9b, 0x71, 0x29, 0x00, 0x71, 0x4a, 0x7b, 0x9f, 0xeb, 0xb2
};
void floppy_soundfx(void)
{
if (myConfig.diskSFX)
{
if (floppy_sound_counter == 0) floppy_sound_counter = 250;
}
}
/*
* Update drive LED display (deferred until Update())
*/
u16 LastFloppySound = 0;
u8 last_led_states = 0x00;
void C64Display::UpdateLEDs(int l0, int l1)
{
led_state[0] = l0;
led_state[1] = l1;
last_led_states = l0;
if (led_state[0] == DRVLED_ERROR)
{
DSPrint(24, 21, 2, (char*)"CDE"); // Red Error Label
}
else
{
if (led_state[0] || led_state[1])
{
DSPrint(24, 21, 2, (char*)"@AB"); // Green Activity Label
}
else
{
DSPrint(24, 21, 2, (char*)" !\""); // White Idle Drive Label
last_led_states = 0;
}
}
}
/*
* Display_NDS.i by Troy Davis(GPF), adapted from:
* Display_GP32.i by Mike Dawson - C64 graphics display, emulator window handling,
*
* Frodo (C) 1994-1997,2002 Christian Bauer
* X11 stuff by Bernd Schmidt/Lutz Vieweg
*/
#include "C64.h"
#include "VIC.h"
#include <nds.h>
#include <fat.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <unistd.h>
#include "diskmenu.h"
#include "keyboard.h"
#include "soundbank.h"
#include <maxmod9.h>
#define MOVE_MAX 16
#define ABS(a) (((a) < 0) ? -(a) : (a))
#define ROUND(f) ((u32) ((f) < 0.0 ? (f) - 0.5 : (f) + 0.5))
#define KB_NORMAL 0
#define KB_CAPS 1
#define KB_SHIFT 2
#define F_1 0x1
#define F_2 0x2
#define F_3 0x3
#define F_4 0x4
#define F_5 0x5
#define F_6 0x6
#define F_7 0x7
#define F_8 0x18
#define MOUNT_DISK 0xFE
#define MAIN_MENU 0xFF
#define LFA 0x095 //Left arrow
#define CLR 0x147
#define PND 0x92
#define RST 0x13 // Restore
#define RET '\n' // Enter
#define BSP 0x08 // Backspace
#define CTR 0x21 // Ctrl
#define SPC 0x20// Space
#define ATT 0x22 // At@
#define UPA 0x23 //uparrow symbol
#define RUN 0x00 // RunStop
#define SLK 0x25 // Shift Lock
#define CMD 0x26 // Commodore key
#define SHF 0x27 // Shift Key
#define CUP 0x14 // Cursor up
#define CDL 0x15 // Cursor left
static int m_Mode=KB_SHIFT;
extern u8 col, row; // console cursor position
extern uint16 *screen;
uint16 * map;
uint8 *bufmem;
#define BUFMEM_SIZE ((512*(DISPLAY_Y-1))+DISPLAY_X)
uint8 *emu_screen;
uint8 *emu_buffers[2];
int emu_buf=0;
static int keystate[256];
extern u8 MainMenu(C64 *the_c64);
char str[300];
int menufirsttime =0;
int choosingfile = 1;
int bg0b, bg1b;
void ShowKeyboard(void)
{
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE); // BG0 = debug console; BG1 = keyboard
bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0);
bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0);
bgSetPriority(bg0b,1); bgSetPriority(bg1b,0);
decompress(keyboardTiles, bgGetGfxPtr(bg0b), LZ77Vram);
decompress(keyboardMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
dmaCopy((void*) bgGetMapPtr(bg0b)+32*30*2,(void*) bgGetMapPtr(bg1b),32*24*2);
dmaCopy((void*) keyboardPal,(void*) BG_PALETTE_SUB,256*2);
unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
}
void WaitForVblank();
/*
C64 keyboard matrix:
Bit 7 6 5 4 3 2 1 0
0 CUD F5 F3 F1 F7 CLR RET DEL
1 SHL E S Z 4 A W 3
2 X T F C 6 D R 5
3 V U H B 8 G Y 7
4 N O K M 0 J I 9
5 , @ : . - L P +
6 / ^ = SHR HOM ; * <EFBFBD>
7 R/S Q C= SPC 2 CTL <- 1
*/
#define MATRIX(a,b) (((a) << 3) | (b))
/*
* Display constructor: Draw Speedometer/LEDs in window
*/
C64Display::C64Display(C64 *the_c64) : TheC64(the_c64)
{
}
/*
* Display destructor
*/
C64Display::~C64Display()
{
}
/*
* Prefs may have changed
*/
void C64Display::NewPrefs(Prefs *prefs)
{
floppy_sound_counter = 50; // One seconds of no floppy sound...
}
uint8* frontBuffer;
u8 JITTER[] = {0, 64, 128};
s16 temp_offset = 0;
u16 slide_dampen=0;
void vblankIntr(void)
{
int cxBG = (myConfig.offsetX << 8);
int cyBG = (myConfig.offsetY+temp_offset) << 8;
int xdxBG = ((320 / myConfig.scaleX) << 8) | (320 % myConfig.scaleX) ;
int ydyBG = ((200 / myConfig.scaleY) << 8) | (200 % myConfig.scaleY);
REG_BG2X = cxBG;
REG_BG2Y = cyBG;
REG_BG3X = cxBG+JITTER[myConfig.jitter];
REG_BG3Y = cyBG;
REG_BG2PA = xdxBG;
REG_BG2PD = ydyBG;
REG_BG3PA = xdxBG;
REG_BG3PD = ydyBG;
if (temp_offset)
{
if (slide_dampen == 0)
{
if (temp_offset > 0) temp_offset--;
else temp_offset++;
}
else
{
slide_dampen--;
}
}
if (floppy_sound_counter)
{
if (floppy_sound_counter == 250)
{
if (myConfig.diskSFX) mmEffect(SFX_FLOPPY);
}
floppy_sound_counter--;
}
}
extern void InterruptHandler(void);
int init_graphics(void)
{
//set the mode for 2 text layers and two extended background layers
powerOn(POWER_ALL_2D);
videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE);
bgInit(3, BgType_Bmp8, BgSize_B8_512x512, 0,0);
bgInit(2, BgType_Bmp8, BgSize_B8_512x512, 0,0);
REG_BLDCNT = BLEND_ALPHA | BLEND_SRC_BG2 | BLEND_DST_BG3;
REG_BLDALPHA = (8 << 8) | 8; // 50% / 50%
//set the first two banks as background memory and the third as sub background memory
//D is not used..if you need a bigger background then you will need to map
//more vram banks consecutivly (VRAM A-D are all 0x20000 bytes in size)
vramSetPrimaryBanks(VRAM_A_MAIN_BG_0x06000000, VRAM_B_MAIN_BG_0x06020000, VRAM_C_SUB_BG , VRAM_D_LCD);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE); //sub bg 0 will be used to print text
REG_BG0CNT_SUB = BG_MAP_BASE(31);
BG_PALETTE_SUB[255] = RGB15(31,31,31);
//consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true);
frontBuffer = (uint8*)(0x06000000);
bufmem = (uint8*)malloc(BUFMEM_SIZE);
if (!fatInitDefault())
{
iprintf("Unable to initialize media device!");
return -1;
}
chdir("/roms");
chdir("c64");
ShowKeyboard();
REG_BG3CNT = BG_BMP8_512x512;
#if 0
REG_BG3PA = DISPLAY_X-54; //((DISPLAY_X / 256) << 8) | (DISPLAY_X % 256) ;//
REG_BG3PB = 0;
REG_BG3PC = 0;
REG_BG3PD = DISPLAY_X-106;//((DISPLAY_Y / 192) << 8) | ((DISPLAY_Y % 192) + (DISPLAY_Y % 192) / 3) ;//
REG_BG3X = 28<<8;//1<<8;//
REG_BG3Y = 32<<8;//1<<8;//
#else
int cxBG = (myConfig.offsetX << 8);
int cyBG = (myConfig.offsetY) << 8;
int xdxBG = ((320 / myConfig.scaleX) << 8) | (320 % myConfig.scaleX) ;
int ydyBG = ((200 / myConfig.scaleY) << 8) | (200 % myConfig.scaleY);
REG_BG2X = cxBG;
REG_BG2Y = cyBG;
REG_BG3X = cxBG;
REG_BG3Y = cyBG;
REG_BG3PA = xdxBG;
REG_BG3PD = ydyBG;
SetYtrigger(190); //trigger 2 lines before vsync
irqSet(IRQ_VBLANK, vblankIntr);
irqEnable(IRQ_VBLANK);
#endif
return TRUE;
}
int counta,firsttime;
void WaitForVblank()
{
swiWaitForVBlank();
}
/*
* Redraw bitmap
*/
void C64Display::Update(void)
{
dmaCopyWordsAsynch(3, bufmem+(512*14), frontBuffer+(512*14), BUFMEM_SIZE-(18*1024));
}
/*
* Draw speedometer
*/
//*****************************************************************************
// Displays a message on the screen
//*****************************************************************************
void DSPrint(int iX,int iY,int iScr,char *szMessage)
{
u16 *pusScreen,*pusMap;
u16 usCharac;
char *pTrTxt=szMessage;
pusScreen=(u16*) bgGetMapPtr(bg1b) + iX + (iY<<5);
pusMap=(u16*) (iScr == 6 ? bgGetMapPtr(bg0b)+24*32 : (iScr == 0 ? bgGetMapPtr(bg0b)+24*32 : bgGetMapPtr(bg0b)+26*32 ));
while((*pTrTxt)!='\0' )
{
char ch = *pTrTxt++;
if (ch >= 'a' && ch <= 'z') ch -= 32; // Faster than strcpy/strtoupper
if (((ch)<' ') || ((ch)>'_'))
usCharac=*(pusMap); // Will render as a vertical bar
else if((ch)<'@')
usCharac=*(pusMap+(ch)-' '); // Number from 0-9 or punctuation
else
usCharac=*(pusMap+32+(ch)-'@'); // Character from A-Z
*pusScreen++=usCharac;
}
}
void show_joysticks(void)
{
if (current_joystick)
{
DSPrint(1, 3, 2, (char*)"()");
DSPrint(1, 4, 2, (char*)"HI");
DSPrint(3, 3, 2, (char*)"*+");
DSPrint(3, 4, 2, (char*)"JK");
}
else
{
DSPrint(3, 3, 2, (char*)"()");
DSPrint(3, 4, 2, (char*)"HI");
DSPrint(1, 3, 2, (char*)"*+");
DSPrint(1, 4, 2, (char*)"JK");
}
}
void show_shift_key(void)
{
if (m_Mode == KB_SHIFT)
{
DSPrint(1, 17, 2, (char*)",-");
DSPrint(1, 18, 2, (char*)"LM");
}
else
{
DSPrint(1, 17, 2, (char*)"./");
DSPrint(1, 18, 2, (char*)"NO");
}
}
int i = 0;
int debug[8]={0,0,0,0,0,0,0,0};
void C64Display::Speedometer(int speed)
{
#if 1
char tmp[34];
sprintf(tmp, "%-8d", speed);
DSPrint(19, 1, 6, tmp);
sprintf(tmp, "%-8d %-8d %-6d %-6d", debug[0],debug[1],debug[2],debug[3]);
DSPrint(0, 0, 6, tmp);
#endif
show_joysticks();
show_shift_key();
}
/*
* Return pointer to bitmap data
*/
uint8 *C64Display::BitmapBase(void)
{
return (uint8 *)bufmem;
}
/*
* Return number of bytes per row
*/
int C64Display::BitmapXMod(void)
{
return 512;
}
void C64Display::KeyPress(int key, uint8 *key_matrix, uint8 *rev_matrix) {
int c64_byte, c64_bit, shifted;
if(!keystate[key]) {
keystate[key]=1;
c64_byte=key>>3;
c64_bit=key&7;
shifted=key&128;
c64_byte&=7;
if(shifted) {
key_matrix[6] &= 0xef;
rev_matrix[4] &= 0xbf;
}
key_matrix[c64_byte]&=~(1<<c64_bit);
rev_matrix[c64_bit]&=~(1<<c64_byte);
}
}
void C64Display::KeyRelease(int key, uint8 *key_matrix, uint8 *rev_matrix) {
int c64_byte, c64_bit, shifted;
if(keystate[key]) {
keystate[key]=0;
c64_byte=key>>3;
c64_bit=key&7;
shifted=key&128;
c64_byte&=7;
if(shifted) {
key_matrix[6] |= 0x10;
rev_matrix[4] |= 0x40;
}
key_matrix[c64_byte]|=(1<<c64_bit);
rev_matrix[c64_bit]|=(1<<c64_byte);
}
}
/*
* Poll the keyboard
*/
int c64_key=-1;
int lastc64key=-1;
bool m_tpActive=false;
touchPosition m_tp;
void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
{
scanKeys();
if ((lastc64key >-1) && ((keysCurrent() & KEY_TOUCH) == 0))
{
KeyRelease(lastc64key, key_matrix, rev_matrix);
}
if ((keysCurrent() & KEY_TOUCH) == 0) // No touch screen... reset the flag
{
m_tpActive = false;
}
else
if ((m_tpActive == false) && (keysCurrent() & KEY_TOUCH))
{
touchRead(&m_tp);
m_tpActive = true;
unsigned short c = 0;
int tilex, tiley;
tilex = m_tp.px;
tiley = m_tp.py;
if (tiley > 20) // We're in the keyboard area...
{
if (tiley < 44) // Big Key Row
{
if (tilex < 42)
{
current_joystick ^= 1;
extern void show_joysticks();
show_joysticks();
}
else if (tilex < 80) c = CTR;
else if (tilex < 118) c = BSP;
else if (tilex < 156) c = RST;
else if (tilex < 194) c = CMD;
else if (tilex < 255) c = RUN;
}
else if (tiley < 74) // Number Row
{
if (tilex < 23) c = '1';
else if (tilex < 42) c = '2';
else if (tilex < 61) c = '3';
else if (tilex < 80) c = '4';
else if (tilex < 99) c = '5';
else if (tilex < 118) c = '6';
else if (tilex < 137) c = '7';
else if (tilex < 156) c = '8';
else if (tilex < 175) c = '9';
else if (tilex < 194) c = '0';
else if (tilex < 213) c = '+';
else if (tilex < 233) c = '-';
else if (tilex < 256) c = UPA;
}
else if (tiley < 104) // QWERTY Row
{
if (tilex < 23) c = CUP;
else if (tilex < 42) c = 'Q';
else if (tilex < 61) c = 'W';
else if (tilex < 80) c = 'E';
else if (tilex < 99) c = 'R';
else if (tilex < 118) c = 'T';
else if (tilex < 137) c = 'Y';
else if (tilex < 156) c = 'U';
else if (tilex < 175) c = 'I';
else if (tilex < 194) c = 'O';
else if (tilex < 213) c = 'P';
else if (tilex < 233) c = '@';
else if (tilex < 256) c = '*';
}
else if (tiley < 134) // ASDF Row
{
if (tilex < 23) c = CDL;
else if (tilex < 42) c = 'A';
else if (tilex < 61) c = 'S';
else if (tilex < 80) c = 'D';
else if (tilex < 99) c = 'F';
else if (tilex < 118) c = 'G';
else if (tilex < 137) c = 'H';
else if (tilex < 156) c = 'J';
else if (tilex < 175) c = 'K';
else if (tilex < 194) c = 'L';
else if (tilex < 213) c = ':';
else if (tilex < 233) c = ';';
else if (tilex < 256) c = '=';
}
else if (tiley < 164) // ZXCV Row
{
if (tilex < 23) c = SHF;
else if (tilex < 42) c = 'Z';
else if (tilex < 61) c = 'X';
else if (tilex < 80) c = 'C';
else if (tilex < 99) c = 'V';
else if (tilex < 118) c = 'B';
else if (tilex < 137) c = 'N';
else if (tilex < 156) c = 'M';
else if (tilex < 175) c = ',';
else if (tilex < 194) c = '.';
else if (tilex < 213) c = '/';
else if (tilex < 256) c = RET;
}
else if (tiley < 192) // Bottom Row
{
if (tilex < 23) c = F_1;
else if (tilex < 42) c = F_3;
else if (tilex < 61) c = F_5;
else if (tilex < 80) c = F_7;
else if (tilex < 190) c = ' ';
else if (tilex < 222) c = MOUNT_DISK;
else if (tilex < 256) c = MAIN_MENU;
}
if (c==MAIN_MENU)
{
TheC64->Pause();
MainMenu(TheC64);
ShowKeyboard();
TheC64->Resume();
}
else if (c==MOUNT_DISK)
{
TheC64->Pause();
u8 reload = mount_disk(TheC64);
ShowKeyboard();
if (reload & 0x7F)
{
kbd_buf_reset();
// Insert the new disk into the drive...
Prefs *prefs = new Prefs(ThePrefs);
strcpy(prefs->DrivePath[0], Drive8File);
strcpy(prefs->DrivePath[1], Drive9File);
prefs->TrueDrive = myConfig.trueDrive;
TheC64->NewPrefs(prefs);
ThePrefs = *prefs;
delete prefs;
// See if we should issue a system-wide RESET
if (reload == 2)
{
TheC64->PatchKernal(ThePrefs.FastReset, ThePrefs.TrueDrive);
TheC64->Reset();
}
}
TheC64->Resume();
if (reload & 0x80)
{
extern void kbd_buf_feed(const char *s);
kbd_buf_feed("\rLOAD\"*\",8,1\rRUN\r");
}
}
else if (c != 0)
{
mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
}
if(c==RET) // Return
{
//consolePrintChar('\n');
//strcpy(text, "");
c64_key = MATRIX(0,1);
KeyPress(c64_key, key_matrix, rev_matrix);
lastc64key=c64_key;
} else
if(c==BSP) // Backspace
{
c64_key = MATRIX(0,0);
KeyPress(c64_key, key_matrix, rev_matrix);
lastc64key=c64_key;
} else
if(c==RUN)
{
mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
c64_key = MATRIX(7,7);
KeyPress(c64_key, key_matrix, rev_matrix);
lastc64key=c64_key;
} else
if(c==SLK || c==SHF)
{
if(m_Mode==KB_NORMAL) {
m_Mode = KB_SHIFT;
} else {
m_Mode = KB_NORMAL;
}
show_shift_key();
}
else
{
if(c!=0x0)
{
switch (c)
{
case 'A': c64_key = MATRIX(1,2); break;
case 'B': c64_key = MATRIX(3,4); break;
case 'C': c64_key = MATRIX(2,4); break;
case 'D': c64_key = MATRIX(2,2); break;
case 'E': c64_key = MATRIX(1,6); break;
case 'F': c64_key = MATRIX(2,5); break;
case 'G': c64_key = MATRIX(3,2); break;
case 'H': c64_key = MATRIX(3,5); break;
case 'I': c64_key = MATRIX(4,1); break;
case 'J': c64_key = MATRIX(4,2); break;
case 'K': c64_key = MATRIX(4,5); break;
case 'L': c64_key = MATRIX(5,2); break;
case 'M': c64_key = MATRIX(4,4); break;
case 'N': c64_key = MATRIX(4,7); break;
case 'O': c64_key = MATRIX(4,6); break;
case 'P': c64_key = MATRIX(5,1); break;
case 'Q': c64_key = MATRIX(7,6); break;
case 'R': c64_key = MATRIX(2,1); break;
case 'S': c64_key = MATRIX(1,5); break;
case 'T': c64_key = MATRIX(2,6); break;
case 'U': c64_key = MATRIX(3,6); break;
case 'V': c64_key = MATRIX(3,7); break;
case 'W': c64_key = MATRIX(1,1); break;
case 'X': c64_key = MATRIX(2,7); break;
case 'Y': c64_key = MATRIX(3,1); break;
case 'Z': c64_key = MATRIX(1,4); break;
case ' ': c64_key = MATRIX(7,4); break;
case '0': c64_key = MATRIX(4,3); break;
case '1': c64_key = MATRIX(7,0); break;
case '2': c64_key = MATRIX(7,3); break;
case '3': c64_key = MATRIX(1,0); break;
case '4': c64_key = MATRIX(1,3); break;
case '5': c64_key = MATRIX(2,0); break;
case '6': c64_key = MATRIX(2,3); break;
case '7': c64_key = MATRIX(3,0); break;
case '8': c64_key = MATRIX(3,3); break;
case '9': c64_key = MATRIX(4,0); break;
case '*': c64_key = MATRIX(6,1); break;
case ':': c64_key = MATRIX(5,5); break;
case ';': c64_key = MATRIX(6,2); break;
case '=': c64_key = MATRIX(6,5); break;
case '/': c64_key = MATRIX(6,7); break;
case ATT: c64_key = MATRIX(5,6); break;
case ',': c64_key = MATRIX(5,7); break;
case '.': c64_key = MATRIX(5,4); break;
case '+': c64_key = MATRIX(5,0); break;
case '-': c64_key = MATRIX(5,3); break;
case RST: c64_key = MATRIX(7,7); break;
case CLR: c64_key = MATRIX(6,3); break;
case LFA: c64_key = MATRIX(7,1); break;
case UPA: c64_key = MATRIX(6,6); break;
case PND: c64_key = MATRIX(6,0); break;
case CUP: c64_key = MATRIX(0,7); break;
case CDL: c64_key = MATRIX(0,2); break;
case F_1: c64_key = MATRIX(0,4); break;
case F_3: c64_key = MATRIX(0,5); break;
case F_5: c64_key = MATRIX(0,6); break;
case F_7: c64_key = MATRIX(0,3); break;
default : c64_key = -1; break;
}
if (c64_key < 0)
return;
if(m_Mode==KB_NORMAL)
{
c64_key= c64_key| 0x80;
}
KeyPress(c64_key, key_matrix, rev_matrix);
lastc64key=c64_key;
}
}
}
}
}
/*
* Check if NumLock is down (for switching the joystick keyboard emulation)
*/
bool C64Display::NumLock(void)
{
return false;
}
/*
* Allocate C64 colors
*/
typedef struct {
int r;
int g;
int b;
} plt;
static plt palette[256];
void C64Display::InitColors(uint8 *colors)
{
int i;
for (i = 0; i < 16; i++)
{
palette[i].r = palette_red[i]>>3;
palette[i].g = palette_green[i]>>3;
palette[i].b = palette_blue[i]>>3;
BG_PALETTE[i]=RGB15(palette_red[i]>>3,palette_green[i]>>3,palette_blue[i]>>3);
}
// frodo internal 8 bit palette
for(i=0; i<256; i++) {
colors[i] = i & 0x0f;
}
}
/*
* Show a requester (error message)
*/
char tmp[256];
long int ShowRequester(const char *a, const char *b, const char *)
{
sprintf(tmp, "%s: %s\n", a, b);
DSPrint(0, 0, 6, tmp);
return 1;
}

76
arm9/source/Display.h Normal file
View File

@ -0,0 +1,76 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* Display.h - C64 graphics display, emulator window handling
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _DISPLAY_H
#define _DISPLAY_H
const int DISPLAY_X = 0x170;
const int DISPLAY_Y = 0x11f;
class C64Window;
class C64Screen;
class C64;
class Prefs;
// Class for C64 graphics display
class C64Display {
public:
C64Display(C64 *the_c64);
~C64Display();
void Update(void);
void UpdateLEDs(int l0, int l1);
void Speedometer(int speed);
uint8 *BitmapBase(void);
int BitmapXMod(void);
void KeyPress(int key, uint8 *key_matrix, uint8 *rev_matrix);
void KeyRelease(int key, uint8 *key_matrix, uint8 *rev_matrix);
void PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick);
bool NumLock(void);
void InitColors(uint8 *colors);
void NewPrefs(Prefs *prefs);
C64 *TheC64;
public:
int led_state[2];
};
// Exported functions
extern long ShowRequester(const char *str, const char *button1, const char *button2 = NULL);
#endif

433
arm9/source/FixPoint.h Normal file
View File

@ -0,0 +1,433 @@
/*
* FixPoint.h - Provides fixed point arithmetic (for use in SID.cpp)
*
* (C) 1997 Andreas Dehmel
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* You need to define FIXPOINT_PREC (number of fractional bits) and
* ldSINTAB (ld of the size of the sinus table) as well M_PI
* _before_ including this file.
* Requires at least 32bit ints!
*/
#define FIXPOINT_BITS 32
// Sign-bit
#define FIXPOINT_SIGN (1<<(FIXPOINT_BITS-1))
/*
* Elementary functions for the FixPoint class
*/
// Multiplies two fixpoint numbers, result is a fixpoint number.
static inline int fixmult(int x, int y)
{
register unsigned int a,b;
register bool sign;
sign = (x ^ y) < 0;
if (x < 0) {x = -x;}
if (y < 0) {y = -y;}
// a, b : integer part; x, y : fractional part. All unsigned now (for shift right)!!!
a = (((unsigned int)x) >> FIXPOINT_PREC); x &= ~(a << FIXPOINT_PREC);
b = (((unsigned int)y) >> FIXPOINT_PREC); y &= ~(b << FIXPOINT_PREC);
x = ((a*b) << FIXPOINT_PREC) + (a*y + b*x) +
((unsigned int)((x*y) + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC);
#ifdef FIXPOINT_SIGN
if (x < 0) {x ^= FIXPOINT_SIGN;}
#endif
if (sign) {x = -x;}
return(x);
}
// Multiplies a fixpoint number with an integer, result is a 32 bit (!) integer in
// contrast to using the standard member-functions which can provide only (32-FIXPOINT_PREC)
// valid bits.
static inline int intmult(int x, int y) // x is fixpoint, y integer
{
register unsigned int i,j;
register bool sign;
sign = (x ^ y) < 0;
if (x < 0) {x = -x;}
if (y < 0) {y = -y;}
i = (((unsigned int)x) >> 16); x &= ~(i << 16); // split both into 16.16 parts
j = (((unsigned int)y) >> 16); y &= ~(j << 16);
#if FIXPOINT_PREC <= 16
// This '32' is independent of the number of bits used, it's due to the 16 bit shift
i = ((i*j) << (32 - FIXPOINT_PREC)) + ((i*y + j*x) << (16 - FIXPOINT_PREC)) +
((unsigned int)(x*y + (1 << (FIXPOINT_PREC - 1))) >> FIXPOINT_PREC);
#else
{
register unsigned int h;
h = (i*y + j*x);
i = ((i*j) << (32 - FIXPOINT_PREC)) + (h >> (FIXPOINT_PREC - 16));
h &= ((1 << (FIXPOINT_PREC - 16)) - 1); x *= y;
i += (x >> FIXPOINT_PREC); x &= ((1 << FIXPOINT_PREC) - 1);
i += (((h + (x >> 16)) + (1 << (FIXPOINT_PREC - 17))) >> (FIXPOINT_PREC - 16));
}
#endif
#ifdef FIXPOINT_SIGN
if (i < 0) {i ^= FIXPOINT_SIGN;}
#endif
if (sign) {i = -i;}
return(i);
}
// Computes the product of a fixpoint number with itself.
static inline int fixsquare(int x)
{
register unsigned int a;
if (x < 0) {x = -x;}
a = (((unsigned int)x) >> FIXPOINT_PREC); x &= ~(a << FIXPOINT_PREC);
x = ((a*a) << FIXPOINT_PREC) + ((a*x) << 1) +
((unsigned int)((x*x) + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC);
#ifdef FIXPOINT_SIGN
if (x < 0) {x ^= FIXPOINT_SIGN;}
#endif
return(x);
}
// Computes the square root of a fixpoint number.
static inline int fixsqrt(int x)
{
register int test, step;
if (x < 0) return(-1);
if (x == 0) return(0);
step = (x <= (1<<FIXPOINT_PREC)) ? (1<<FIXPOINT_PREC) : (1<<((FIXPOINT_BITS - 2 + FIXPOINT_PREC)>>1));
test = 0;
while (step != 0)
{
register int h;
h = fixsquare(test + step);
if (h <= x) {test += step;}
if (h == x) break;
step >>= 1;
}
return(test);
}
// Divides a fixpoint number by another fixpoint number, yielding a fixpoint result.
static inline int fixdiv(int x, int y)
{
register int res, mask;
register bool sign;
sign = (x ^ y) < 0;
if (x < 0) {x = -x;}
if (y < 0) {y = -y;}
mask = (1<<FIXPOINT_PREC); res = 0;
while (x > y) {y <<= 1; mask <<= 1;}
while (mask != 0)
{
if (x >= y) {res |= mask; x -= y;}
mask >>= 1; y >>= 1;
}
#ifdef FIXPOINT_SIGN
if (res < 0) {res ^= FIXPOINT_SIGN;}
#endif
if (sign) {res = -res;}
return(res);
}
/*
* The C++ Fixpoint class. By no means exhaustive...
* Since it contains only one int data, variables of type FixPoint can be
* passed directly rather than as a reference.
*/
class FixPoint
{
private:
int x;
public:
FixPoint(void);
FixPoint(int y);
~FixPoint(void);
// conversions
int Value(void);
int round(void);
operator int(void);
// unary operators
FixPoint sqrt(void);
FixPoint sqr(void);
FixPoint abs(void);
FixPoint operator+(void);
FixPoint operator-(void);
FixPoint operator++(void);
FixPoint operator--(void);
// binary operators
int imul(int y);
FixPoint operator=(FixPoint y);
FixPoint operator=(int y);
FixPoint operator+(FixPoint y);
FixPoint operator+(int y);
FixPoint operator-(FixPoint y);
FixPoint operator-(int y);
FixPoint operator/(FixPoint y);
FixPoint operator/(int y);
FixPoint operator*(FixPoint y);
FixPoint operator*(int y);
FixPoint operator+=(FixPoint y);
FixPoint operator+=(int y);
FixPoint operator-=(FixPoint y);
FixPoint operator-=(int y);
FixPoint operator*=(FixPoint y);
FixPoint operator*=(int y);
FixPoint operator/=(FixPoint y);
FixPoint operator/=(int y);
FixPoint operator<<(int y);
FixPoint operator>>(int y);
FixPoint operator<<=(int y);
FixPoint operator>>=(int y);
// conditional operators
bool operator<(FixPoint y);
bool operator<(int y);
bool operator<=(FixPoint y);
bool operator<=(int y);
bool operator>(FixPoint y);
bool operator>(int y);
bool operator>=(FixPoint y);
bool operator>=(int y);
bool operator==(FixPoint y);
bool operator==(int y);
bool operator!=(FixPoint y);
bool operator!=(int y);
};
/*
* int gets treated differently according to the case:
*
* a) Equations (=) or condition checks (==, <, <= ...): raw int (i.e. no conversion)
* b) As an argument for an arithmetic operation: conversion to fixpoint by shifting
*
* Otherwise loading meaningful values into FixPoint variables would be very awkward.
*/
FixPoint::FixPoint(void) {x = 0;}
FixPoint::FixPoint(int y) {x = y;}
FixPoint::~FixPoint(void) {;}
inline int FixPoint::Value(void) {return(x);}
inline int FixPoint::round(void) {return((x + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC);}
inline FixPoint::operator int(void) {return(x);}
// unary operators
inline FixPoint FixPoint::sqrt(void) {return(fixsqrt(x));}
inline FixPoint FixPoint::sqr(void) {return(fixsquare(x));}
inline FixPoint FixPoint::abs(void) {return((x < 0) ? -x : x);}
inline FixPoint FixPoint::operator+(void) {return(x);}
inline FixPoint FixPoint::operator-(void) {return(-x);}
inline FixPoint FixPoint::operator++(void) {x += (1 << FIXPOINT_PREC); return x;}
inline FixPoint FixPoint::operator--(void) {x -= (1 << FIXPOINT_PREC); return x;}
// binary operators
inline int FixPoint::imul(int y) {return(intmult(x,y));}
inline FixPoint FixPoint::operator=(FixPoint y) {x = y.Value(); return x;}
inline FixPoint FixPoint::operator=(int y) {x = y; return x;}
inline FixPoint FixPoint::operator+(FixPoint y) {return(x + y.Value());}
inline FixPoint FixPoint::operator+(int y) {return(x + (y << FIXPOINT_PREC));}
inline FixPoint FixPoint::operator-(FixPoint y) {return(x - y.Value());}
inline FixPoint FixPoint::operator-(int y) {return(x - (y << FIXPOINT_PREC));}
inline FixPoint FixPoint::operator/(FixPoint y) {return(fixdiv(x,y.Value()));}
inline FixPoint FixPoint::operator/(int y) {return(x/y);}
inline FixPoint FixPoint::operator*(FixPoint y) {return(fixmult(x,y.Value()));}
inline FixPoint FixPoint::operator*(int y) {return(x*y);}
inline FixPoint FixPoint::operator+=(FixPoint y) {x += y.Value(); return x;}
inline FixPoint FixPoint::operator+=(int y) {x += (y << FIXPOINT_PREC); return x;}
inline FixPoint FixPoint::operator-=(FixPoint y) {x -= y.Value(); return x;}
inline FixPoint FixPoint::operator-=(int y) {x -= (y << FIXPOINT_PREC); return x;}
inline FixPoint FixPoint::operator*=(FixPoint y) {x = fixmult(x,y.Value()); return x;}
inline FixPoint FixPoint::operator*=(int y) {x *= y; return x;}
inline FixPoint FixPoint::operator/=(FixPoint y) {x = fixdiv(x,y.Value()); return x;}
inline FixPoint FixPoint::operator/=(int y) {x /= y; return x;}
inline FixPoint FixPoint::operator<<(int y) {return(x << y);}
inline FixPoint FixPoint::operator>>(int y) {return(x >> y);}
inline FixPoint FixPoint::operator<<=(int y) {x <<= y; return x;}
inline FixPoint FixPoint::operator>>=(int y) {x >>= y; return x;}
// conditional operators
inline bool FixPoint::operator<(FixPoint y) {return(x < y.Value());}
inline bool FixPoint::operator<(int y) {return(x < y);}
inline bool FixPoint::operator<=(FixPoint y) {return(x <= y.Value());}
inline bool FixPoint::operator<=(int y) {return(x <= y);}
inline bool FixPoint::operator>(FixPoint y) {return(x > y.Value());}
inline bool FixPoint::operator>(int y) {return(x > y);}
inline bool FixPoint::operator>=(FixPoint y) {return(x >= y.Value());}
inline bool FixPoint::operator>=(int y) {return(x >= y);}
inline bool FixPoint::operator==(FixPoint y) {return(x == y.Value());}
inline bool FixPoint::operator==(int y) {return(x == y);}
inline bool FixPoint::operator!=(FixPoint y) {return(x != y.Value());}
inline bool FixPoint::operator!=(int y) {return(x != y);}
/*
* In case the first argument is an int (i.e. member-operators not applicable):
* Not supported: things like int/FixPoint. The same difference in conversions
* applies as mentioned above.
*/
// binary operators
inline FixPoint operator+(int x, FixPoint y) {return((x << FIXPOINT_PREC) + y.Value());}
inline FixPoint operator-(int x, FixPoint y) {return((x << FIXPOINT_PREC) - y.Value());}
inline FixPoint operator*(int x, FixPoint y) {return(x*y.Value());}
// conditional operators
inline bool operator==(int x, FixPoint y) {return(x == y.Value());}
inline bool operator!=(int x, FixPoint y) {return(x != y.Value());}
inline bool operator<(int x, FixPoint y) {return(x < y.Value());}
inline bool operator<=(int x, FixPoint y) {return(x <= y.Value());}
inline bool operator>(int x, FixPoint y) {return(x > y.Value());}
inline bool operator>=(int x, FixPoint y) {return(x >= y.Value());}
/*
* For more convenient creation of constant fixpoint numbers from constant floats.
*/
#define FixNo(n) (FixPoint)((int)(n*(1<<FIXPOINT_PREC)))
/*
* Stuff re. the sinus table used with fixpoint arithmetic
*/
// define as global variable
FixPoint SinTable[(1<<ldSINTAB)];
#define FIXPOINT_SIN_COS_GENERIC \
if (angle >= 3*(1<<ldSINTAB)) {return(-SinTable[(1<<(ldSINTAB+2)) - angle]);}\
if (angle >= 2*(1<<ldSINTAB)) {return(-SinTable[angle - 2*(1<<ldSINTAB)]);}\
if (angle >= (1<<ldSINTAB)) {return(SinTable[2*(1<<ldSINTAB) - angle]);}\
return(SinTable[angle]);
// sin and cos: angle is fixpoint number 0 <= angle <= 2 (*PI)
static inline FixPoint fixsin(FixPoint x)
{
int angle = x;
angle = (angle >> (FIXPOINT_PREC - ldSINTAB - 1)) & ((1<<(ldSINTAB+2))-1);
FIXPOINT_SIN_COS_GENERIC
}
static inline FixPoint fixcos(FixPoint x)
{
int angle = x;
angle = ((angle + (1<<(FIXPOINT_PREC-1))) >> (FIXPOINT_PREC - ldSINTAB - 1)) & ((1<<(ldSINTAB+2))-1);
FIXPOINT_SIN_COS_GENERIC
}
static inline void InitFixSinTab(void)
{
int i;
float step;
for (i=0, step=0; i<(1<<ldSINTAB); i++, step+=0.5/(1<<ldSINTAB))
{
SinTable[i] = FixNo(sin(M_PI * step));
}
}

989
arm9/source/IEC.cpp Normal file
View File

@ -0,0 +1,989 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* IEC.cpp - IEC bus routines, 1541 emulation (DOS level)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Notes:
* ------
*
* - There are three kinds of devices on the IEC bus: controllers,
* listeners and talkers. We are always the controller and we
* can additionally be either listener or talker. There can be
* only one listener and one talker active at the same time (the
* real IEC bus allows multiple listeners, but we don't).
* - There is one Drive object for every emulated drive (8..11).
* A pointer to one of them is stored in "listener"/"talker"
* when talk()/listen() is called and is used by the functions
* called afterwards.
* - The Drive objects have four virtual functions so that the
* interface to them is independent of their implementation:
* Open() opens a channel
* Close() closes a channel
* Read() reads from a channel
* Write() writes to a channel
* - The EOI/EOF signal is special on the IEC bus in that it is
* Sent before the last byte, not after it.
*/
#include "sysdeps.h"
#include "IEC.h"
#include "1541fs.h"
#include "1541d64.h"
#include "Prefs.h"
#include "Display.h"
#include "main.h"
// IEC command codes
enum {
CMD_DATA = 0x60, // Data transfer
CMD_CLOSE = 0xe0, // Close channel
CMD_OPEN = 0xf0 // Open channel
};
// IEC ATN codes
enum {
ATN_LISTEN = 0x20,
ATN_UNLISTEN = 0x30,
ATN_TALK = 0x40,
ATN_UNTALK = 0x50
};
/*
* Constructor: Initialize variables
*/
Drive *IEC::create_drive(const char *path)
{
//printdbg("h1");
if (IsDirectory(path)) {
// Mount host directory
//printdbg((char*)path);
//printdbg("h2");
return new FSDrive(this, path);
} else {
// Not a directory, check for mountable file type
//printdbg("h3");
int type;
if (IsMountableFile(path, type)) {
//printdbg("h4");
if (type == FILE_IMAGE) {
//printdbg("h5");
// Mount disk image
return new ImageDrive(this, path);
} else {
// Mount archive type file
//printdbg("h6");
//return new ArchDrive(this, path);
}
} else {
//printdbg("h7");
// Unknown file type
// print error?
}
}
return NULL;
}
IEC::IEC(C64Display *display) : the_display(display)
{
int i;
// Create drives 8..9
for (i=0; i<2; i++)
drive[i] = NULL; // Important because UpdateLEDs is called from the drive constructors (via set_error)
if (!ThePrefs.TrueDrive) {
for (i=0; i<2; i++)
drive[i] = create_drive(ThePrefs.DrivePath[i]);
}
listener_active = talker_active = false;
listening = false;
}
/*
* Destructor: Delete drives
*/
IEC::~IEC()
{
for (int i=0; i<2; i++)
delete drive[i];
}
/*
* Reset all drives
*/
void IEC::Reset(void)
{
for (int i=0; i<2; i++)
if (drive[i] != NULL && drive[i]->Ready)
drive[i]->Reset();
UpdateLEDs();
}
/*
* Preferences have changed, prefs points to new preferences,
* ThePrefs still holds the previous ones. Check if drive settings
* have changed.
*/
void IEC::NewPrefs(Prefs *prefs)
{
// Delete and recreate all changed drives
for (int i=0; i<2; i++)
{
if (strcmp(ThePrefs.DrivePath[i], prefs->DrivePath[i]) || ThePrefs.TrueDrive != prefs->TrueDrive)
{
delete drive[i];
drive[i] = NULL; // Important because UpdateLEDs is called from drive constructors (via set_error())
if (!prefs->TrueDrive)
{
drive[i] = create_drive(prefs->DrivePath[i]);
}
}
}
UpdateLEDs();
}
/*
* Update drive LED display
*/
void IEC::UpdateLEDs(void)
{
u8 l0=0; u8 l1=0;
if (drive[0] != NULL) l0 = drive[0]->LED;
if (drive[1] != NULL) l1 = drive[1]->LED;
the_display->UpdateLEDs(l0, l1);
}
/*
* Output one byte
*/
uint8 IEC::Out(uint8 byte, bool eoi)
{
if (listener_active) {
if (received_cmd == CMD_OPEN)
return open_out(byte, eoi);
if (received_cmd == CMD_DATA)
return data_out(byte, eoi);
return ST_TIMEOUT;
} else
return ST_TIMEOUT;
}
/*
* Output one byte with ATN (Talk/Listen/Untalk/Unlisten)
*/
uint8 IEC::OutATN(uint8 byte)
{
received_cmd = sec_addr = 0; // Command is sent with secondary address
switch (byte & 0xf0) {
case ATN_LISTEN:
listening = true;
return listen(byte & 0x0f);
case ATN_UNLISTEN:
listening = false;
return unlisten();
case ATN_TALK:
listening = false;
return talk(byte & 0x0f);
case ATN_UNTALK:
listening = false;
return untalk();
}
return ST_TIMEOUT;
}
/*
* Output secondary address
*/
uint8 IEC::OutSec(uint8 byte)
{
if (listening) {
if (listener_active) {
sec_addr = byte & 0x0f;
received_cmd = byte & 0xf0;
return sec_listen();
}
} else {
if (talker_active) {
sec_addr = byte & 0x0f;
received_cmd = byte & 0xf0;
return sec_talk();
}
}
return ST_TIMEOUT;
}
/*
* Read one byte
*/
uint8 IEC::In(uint8 &byte)
{
if (talker_active && (received_cmd == CMD_DATA))
return data_in(byte);
byte = 0;
return ST_TIMEOUT;
}
/*
* Assert ATN (for Untalk)
*/
void IEC::SetATN(void)
{
// Only needed for real IEC
}
/*
* Release ATN
*/
void IEC::RelATN(void)
{
// Only needed for real IEC
}
/*
* Talk-attention turn-around
*/
void IEC::Turnaround(void)
{
// Only needed for real IEC
}
/*
* System line release
*/
void IEC::Release(void)
{
// Only needed for real IEC
}
/*
* Listen
*/
uint8 IEC::listen(int device)
{
//printdbg("IEC::listen");
if ((device >= 8) && (device <= 11)) {
//printdbg("IEC::listen2");
if ((listener = drive[device-8]) != NULL && listener->Ready) {
//printdbg("IEC::listen3");
listener_active = true;
return ST_OK;
}
}
listener_active = false;
return ST_NOTPRESENT;
}
/*
* Talk
*/
uint8 IEC::talk(int device)
{
if ((device >= 8) && (device <= 11)) {
if ((talker = drive[device-8]) != NULL && talker->Ready) {
talker_active = true;
return ST_OK;
}
}
talker_active = false;
return ST_NOTPRESENT;
}
/*
* Unlisten
*/
uint8 IEC::unlisten(void)
{
listener_active = false;
return ST_OK;
}
/*
* Untalk
*/
uint8 IEC::untalk(void)
{
talker_active = false;
return ST_OK;
}
/*
* Secondary address after Listen
*/
uint8 IEC::sec_listen(void)
{
switch (received_cmd) {
case CMD_OPEN: // Prepare for receiving the file name
name_ptr = name_buf;
name_len = 0;
return ST_OK;
case CMD_CLOSE: // Close channel
if (listener->LED != DRVLED_ERROR) {
listener->LED = DRVLED_OFF; // Turn off drive LED
UpdateLEDs();
}
return listener->Close(sec_addr);
}
return ST_OK;
}
/*
* Secondary address after Talk
*/
uint8 IEC::sec_talk(void)
{
return ST_OK;
}
/*
* Byte after Open command: Store character in file name, open file on EOI
*/
uint8 IEC::open_out(uint8 byte, bool eoi)
{
if (name_len < NAMEBUF_LENGTH) {
*name_ptr++ = byte;
name_len++;
}
if (eoi) {
*name_ptr = 0; // End string
listener->LED = DRVLED_ON; // Turn on drive LED
UpdateLEDs();
return listener->Open(sec_addr, name_buf, name_len);
}
return ST_OK;
}
/*
* Write byte to channel
*/
uint8 IEC::data_out(uint8 byte, bool eoi)
{
return listener->Write(sec_addr, byte, eoi);
}
/*
* Read byte from channel
*/
uint8 IEC::data_in(uint8 &byte)
{
return talker->Read(sec_addr, byte);
}
/*
* Drive constructor
*/
Drive::Drive(IEC *iec)
{
the_iec = iec;
LED = DRVLED_OFF;
Ready = false;
set_error(ERR_STARTUP);
}
/*
* Set error message on drive
*/
// 1541 error messages
static const char *Errors_1541[] = {
"00, OK,%02d,%02d\x0d",
"01, FILES SCRATCHED,%02d,%02d\x0d",
"03, UNIMPLEMENTED,%02d,%02d\x0d",
"20, READ ERROR,%02d,%02d\x0d",
"21, READ ERROR,%02d,%02d\x0d",
"22, READ ERROR,%02d,%02d\x0d",
"23, READ ERROR,%02d,%02d\x0d",
"24, READ ERROR,%02d,%02d\x0d",
"25, WRITE ERROR,%02d,%02d\x0d",
"26, WRITE PROTECT ON,%02d,%02d\x0d",
"27, READ ERROR,%02d,%02d\x0d",
"28, WRITE ERROR,%02d,%02d\x0d",
"29, DISK ID MISMATCH,%02d,%02d\x0d",
"30, SYNTAX ERROR,%02d,%02d\x0d",
"31, SYNTAX ERROR,%02d,%02d\x0d",
"32, SYNTAX ERROR,%02d,%02d\x0d",
"33, SYNTAX ERROR,%02d,%02d\x0d",
"34, SYNTAX ERROR,%02d,%02d\x0d",
"60, WRITE FILE OPEN,%02d,%02d\x0d",
"61, FILE NOT OPEN,%02d,%02d\x0d",
"62, FILE NOT FOUND,%02d,%02d\x0d",
"63, FILE EXISTS,%02d,%02d\x0d",
"64, FILE TYPE MISMATCH,%02d,%02d\x0d",
"65, NO BLOCK,%02d,%02d\x0d",
"66, ILLEGAL TRACK OR SECTOR,%02d,%02d\x0d",
"70, NO CHANNEL,%02d,%02d\x0d",
"71, DIR ERROR,%02d,%02d\x0d",
"72, DISK FULL,%02d,%02d\x0d",
"73, CBM DOS V2.6 1541,%02d,%02d\x0d",
"74, DRIVE NOT READY,%02d,%02d\x0d"
};
void Drive::set_error(int error, int track, int sector)
{
// Write error message to buffer
sprintf(error_buf, Errors_1541[error], track, sector);
error_ptr = error_buf;
error_len = strlen(error_buf);
current_error = error;
//DSPrint(0,1,6,error_buf);
// Set drive condition
if (error != ERR_OK && error != ERR_SCRATCHED)
if (error == ERR_STARTUP)
LED = DRVLED_OFF;
else
LED = DRVLED_ERROR;
else if (LED == DRVLED_ERROR)
LED = DRVLED_OFF;
the_iec->UpdateLEDs();
}
/*
* Parse file name, determine access mode and file type
*/
void Drive::parse_file_name(const uint8 *src, int src_len, uint8 *dest, int &dest_len, int &mode, int &type, int &rec_len, bool convert_charset)
{
// If the string contains a ':', the file name starts after that
const uint8 *p = (const uint8 *)memchr(src, ':', src_len);
if (p) {
p++;
src_len -= p - src;
} else
p = src;
// Transfer file name upto ','
dest_len = 0;
uint8 *q = dest;
while (*p != ',' && src_len-- > 0) {
if (convert_charset)
*q++ = petscii2ascii(*p++);
else
*q++ = *p++;
dest_len++;
}
*q++ = 0;
// Strip trailing CRs
while (dest_len > 0 && dest[dest_len - 1] == 0x0d)
dest[--dest_len] = 0;
// Look for mode and type parameters separated by ','
p++; src_len--;
while (src_len > 0) {
switch (*p) {
case 'D':
type = FTYPE_DEL;
break;
case 'S':
type = FTYPE_SEQ;
break;
case 'P':
type = FTYPE_PRG;
break;
case 'U':
type = FTYPE_USR;
break;
case 'L':
type = FTYPE_REL;
while (*p != ',' && src_len-- > 0) p++;
p++; src_len--;
rec_len = *p++; src_len--;
if (src_len < 0)
rec_len = 0;
break;
case 'R':
mode = FMODE_READ;
break;
case 'W':
mode = FMODE_WRITE;
break;
case 'A':
mode = FMODE_APPEND;
break;
case 'M':
mode = FMODE_M;
break;
}
// Skip to ','
while (*p != ',' && src_len-- > 0) p++;
p++; src_len--;
}
}
/*
* Execute DOS command (parse command and call appropriate routine)
*/
static void parse_block_cmd_args(const uint8 *p, int &arg1, int &arg2, int &arg3, int &arg4)
{
arg1 = arg2 = arg3 = arg4 = 0;
while (*p == ' ' || *p == 0x1d || *p == ',') p++;
while (*p >= '0' && *p < '@')
arg1 = arg1 * 10 + (*p++ & 0x0f);
while (*p == ' ' || *p == 0x1d || *p == ',') p++;
while (*p >= '0' && *p < '@')
arg2 = arg2 * 10 + (*p++ & 0x0f);
while (*p == ' ' || *p == 0x1d || *p == ',') p++;
while (*p >= '0' && *p < '@')
arg3 = arg3 * 10 + (*p++ & 0x0f);
while (*p == ' ' || *p == 0x1d || *p == ',') p++;
while (*p >= '0' && *p < '@')
arg4 = arg4 * 10 + (*p++ & 0x0f);
}
void Drive::execute_cmd(const uint8 *cmd, int cmd_len)
{
// Strip trailing CRs
while (cmd_len > 0 && cmd[cmd_len - 1] == 0x0d)
cmd_len--;
// Find token delimiters
const uint8 *colon = (const uint8 *)memchr(cmd, ':', cmd_len);
const uint8 *equal = colon ? (const uint8 *)memchr(colon, '=', cmd_len - (colon - cmd)) : NULL;
const uint8 *comma = (const uint8 *)memchr(cmd, ',', cmd_len);
const uint8 *minus = (const uint8 *)memchr(cmd, '-', cmd_len);
// Parse command name
set_error(ERR_OK);
switch (cmd[0]) {
case 'B': // Block/buffer
if (!minus)
set_error(ERR_SYNTAX31);
else {
// Parse arguments (up to 4 decimal numbers separated by
// space, cursor right or comma)
const uint8 *p = colon ? colon + 1 : cmd + 3;
int arg1, arg2, arg3, arg4;
parse_block_cmd_args(p, arg1, arg2, arg3, arg4);
// Switch on command
switch (minus[1]) {
case 'R':
block_read_cmd(arg1, arg3, arg4);
break;
case 'W':
block_write_cmd(arg1, arg3, arg4);
break;
case 'E':
block_execute_cmd(arg1, arg3, arg4);
break;
case 'A':
block_allocate_cmd(arg2, arg3);
break;
case 'F':
block_free_cmd(arg2, arg3);
break;
case 'P':
buffer_pointer_cmd(arg1, arg2);
break;
default:
set_error(ERR_SYNTAX31);
break;
}
}
break;
case 'M': // Memory
if (cmd[1] != '-')
set_error(ERR_SYNTAX31);
else {
// Read parameters
uint16 adr = uint8(cmd[3]) | (uint8(cmd[4]) << 8);
uint8 len = uint8(cmd[5]);
// Switch on command
switch (cmd[2]) {
case 'R':
mem_read_cmd(adr, (cmd_len < 6) ? 1 : len);
break;
case 'W':
mem_write_cmd(adr, len, (uint8 *)cmd + 6);
break;
case 'E':
mem_execute_cmd(adr);
break;
default:
set_error(ERR_SYNTAX31);
break;
}
}
break;
case 'C': // Copy
if (!colon)
set_error(ERR_SYNTAX31);
else if (!equal || memchr(cmd, '*', cmd_len) || memchr(cmd, '?', cmd_len) || (comma && comma < equal))
set_error(ERR_SYNTAX30);
else
copy_cmd(colon + 1, equal - colon - 1, equal + 1, cmd_len - (equal + 1 - cmd));
break;
case 'R': // Rename
if (!colon)
set_error(ERR_SYNTAX34);
else if (!equal || comma || memchr(cmd, '*', cmd_len) || memchr(cmd, '?', cmd_len))
set_error(ERR_SYNTAX30);
else
rename_cmd(colon + 1, equal - colon - 1, equal + 1, cmd_len - (equal + 1 - cmd));
break;
case 'S': // Scratch
if (!colon)
set_error(ERR_SYNTAX34);
else
scratch_cmd(colon + 1, cmd_len - (colon + 1 - cmd));
break;
case 'P': // Position
position_cmd(cmd + 1, cmd_len - 1);
break;
case 'I': // Initialize
initialize_cmd();
break;
case 'N': // New (format)
if (!colon)
set_error(ERR_SYNTAX34);
else
new_cmd(colon + 1, comma ? (comma - colon - 1) : cmd_len - (colon + 1 - cmd), comma);
break;
case 'V': // Validate
validate_cmd();
break;
case 'U': // User
if (cmd[1] == '0')
break;
switch (cmd[1] & 0x0f) {
case 1: { // U1/UA: Read block
const uint8 *p = colon ? colon + 1 : cmd + 2;
int arg1, arg2, arg3, arg4;
parse_block_cmd_args(p, arg1, arg2, arg3, arg4);
block_read_cmd(arg1, arg3, arg4, true);
break;
}
case 2: { // U2/UB: Write block
const uint8 *p = colon ? colon + 1 : cmd + 2;
int arg1, arg2, arg3, arg4;
parse_block_cmd_args(p, arg1, arg2, arg3, arg4);
block_write_cmd(arg1, arg3, arg4, true);
break;
}
case 9: // U9/UI: C64/VC20 mode switch
if (cmd[2] != '+' && cmd[2] != '-')
Reset();
break;
case 10: // U:/UJ: Reset
Reset();
break;
default:
set_error(ERR_UNIMPLEMENTED);
break;
}
break;
default:
set_error(ERR_SYNTAX31);
break;
}
}
// BLOCK-READ:channel,0,track,sector
void Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
{
set_error(ERR_UNIMPLEMENTED);
}
// BLOCK-WRITE:channel,0,track,sector
void Drive::block_write_cmd(int channel, int track, int sector, bool user_cmd)
{
set_error(ERR_UNIMPLEMENTED);
}
// BLOCK-EXECUTE:channel,0,track,sector
void Drive::block_execute_cmd(int channel, int track, int sector)
{
set_error(ERR_UNIMPLEMENTED);
}
// BLOCK-ALLOCATE:0,track,sector
void Drive::block_allocate_cmd(int track, int sector)
{
set_error(ERR_UNIMPLEMENTED);
}
// BLOCK-FREE:0,track,sector
void Drive::block_free_cmd(int track, int sector)
{
set_error(ERR_UNIMPLEMENTED);
}
// BUFFER-POINTER:channel,pos
void Drive::buffer_pointer_cmd(int channel, int pos)
{
set_error(ERR_UNIMPLEMENTED);
}
// M-R<adr low><adr high>[<number>]
void Drive::mem_read_cmd(uint16 adr, uint8 len)
{
unsupp_cmd();
error_ptr = error_buf;
error_buf[0] = 0;
error_len = 0;
set_error(ERR_OK);
}
// M-W<adr low><adr high><number><data...>
void Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
{
set_error(ERR_UNIMPLEMENTED);
}
// M-E<adr low><adr high>
void Drive::mem_execute_cmd(uint16 adr)
{
set_error(ERR_UNIMPLEMENTED);
}
// COPY:new=file1,file2,...
// ^ ^
// new_file old_files
void Drive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len)
{
set_error(ERR_UNIMPLEMENTED);
}
// RENAME:new=old
// ^ ^
// new_file old_file
void Drive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
{
set_error(ERR_UNIMPLEMENTED);
}
// SCRATCH:file1,file2,...
// ^
// files
void Drive::scratch_cmd(const uint8 *files, int files_len)
{
set_error(ERR_UNIMPLEMENTED);
}
// P<channel><record low><record high><byte>
// ^
// cmd
void Drive::position_cmd(const uint8 *cmd, int cmd_len)
{
set_error(ERR_UNIMPLEMENTED);
}
// INITIALIZE
void Drive::initialize_cmd(void)
{
set_error(ERR_UNIMPLEMENTED);
}
// NEW:name,id
// ^ ^
// name comma (or NULL)
void Drive::new_cmd(const uint8 *name, int name_len, const uint8 *comma)
{
set_error(ERR_UNIMPLEMENTED);
}
// VALIDATE
void Drive::validate_cmd(void)
{
set_error(ERR_UNIMPLEMENTED);
}
/*
* Notice user of unsupported drive command
*/
void Drive::unsupp_cmd(void)
{
}
/*
* Convert PETSCII<->ASCII
*/
uint8 ascii2petscii(char c)
{
if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')))
return c ^ 0x20;
return c;
}
void ascii2petscii(uint8 *dest, const char *src, int n)
{
while (n-- && (*dest++ = ascii2petscii(*src++))) ;
}
char petscii2ascii(uint8 c)
{
if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')))
return c ^ 0x20;
if ((c >= 0xc1) && (c <= 0xda))
return c ^ 0x80;
return c;
return c;
}
void petscii2ascii(char *dest, const uint8 *src, int n)
{
while (n-- && (*dest++ = petscii2ascii(*src++))) ;
}
/*
* Check whether file is a mountable disk image or archive file, return type
*/
bool IsMountableFile(const char *path, int &type)
{
// Read header and determine file size
uint8 header[64];
memset(header, 0, sizeof(header));
FILE *f = fopen(path, "rb");
if (f == NULL)
return false;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
fread(header, 1, sizeof(header), f);
fclose(f);
if (IsImageFile(path, header, size)) {
type = FILE_IMAGE;
return true;
}
else return false;
}
/*
* Read directory of mountable disk image or archive file into c64_dir_entry vector,
* returns false on error
*/
//bool ReadDirectory(const char *path, int type, vector<c64_dir_entry> &vec)
//{
// vec.clear();
// switch (type) {
// case FILE_IMAGE:
// return ReadImageDirectory(path, vec);
// case FILE_ARCH:
// return ReadArchDirectory(path, vec);
// default:
// return false;
// }
//}

275
arm9/source/IEC.h Normal file
View File

@ -0,0 +1,275 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* IEC.h - IEC bus routines, 1541 emulation (DOS level)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _IEC_H
#define _IEC_H
/*
* Definitions
*/
// Maximum length of file names
const int NAMEBUF_LENGTH = 256;
// C64 status codes
enum {
ST_OK = 0, // No error
ST_READ_TIMEOUT = 0x02, // Timeout on reading
ST_TIMEOUT = 0x03, // Timeout
ST_EOF = 0x40, // End of file
ST_NOTPRESENT = 0x80 // Device not present
};
// 1541 error codes
enum {
ERR_OK, // 00 OK
ERR_SCRATCHED, // 01 FILES SCRATCHED
ERR_UNIMPLEMENTED, // 03 UNIMPLEMENTED
ERR_READ20, // 20 READ ERROR (block header not found)
ERR_READ21, // 21 READ ERROR (no sync character)
ERR_READ22, // 22 READ ERROR (data block not present)
ERR_READ23, // 23 READ ERROR (checksum error in data block)
ERR_READ24, // 24 READ ERROR (byte decoding error)
ERR_WRITE25, // 25 WRITE ERROR (write-verify error)
ERR_WRITEPROTECT, // 26 WRITE PROTECT ON
ERR_READ27, // 27 READ ERROR (checksum error in header)
ERR_WRITE28, // 28 WRITE ERROR (long data block)
ERR_DISKID, // 29 DISK ID MISMATCH
ERR_SYNTAX30, // 30 SYNTAX ERROR (general syntax)
ERR_SYNTAX31, // 31 SYNTAX ERROR (invalid command)
ERR_SYNTAX32, // 32 SYNTAX ERROR (command too long)
ERR_SYNTAX33, // 33 SYNTAX ERROR (wildcards on writing)
ERR_SYNTAX34, // 34 SYNTAX ERROR (missing file name)
ERR_WRITEFILEOPEN, // 60 WRITE FILE OPEN
ERR_FILENOTOPEN, // 61 FILE NOT OPEN
ERR_FILENOTFOUND, // 62 FILE NOT FOUND
ERR_FILEEXISTS, // 63 FILE EXISTS
ERR_FILETYPE, // 64 FILE TYPE MISMATCH
ERR_NOBLOCK, // 65 NO BLOCK
ERR_ILLEGALTS, // 66 ILLEGAL TRACK OR SECTOR
ERR_NOCHANNEL, // 70 NO CHANNEL
ERR_DIRERROR, // 71 DIR ERROR
ERR_DISKFULL, // 72 DISK FULL
ERR_STARTUP, // 73 Power-up message
ERR_NOTREADY // 74 DRIVE NOT READY
};
// Mountable file types
enum {
FILE_IMAGE, // Disk image, handled by ImageDrive
FILE_ARCH // Archive file, handled by ArchDrive
};
// 1541 file types
enum {
FTYPE_DEL, // Deleted
FTYPE_SEQ, // Sequential
FTYPE_PRG, // Program
FTYPE_USR, // User
FTYPE_REL, // Relative
FTYPE_UNKNOWN
};
static const char ftype_char[9] = "DSPUL ";
// 1541 file access modes
enum {
FMODE_READ, // Read
FMODE_WRITE, // Write
FMODE_APPEND, // Append
FMODE_M // Read open file
};
// Drive LED states
enum {
DRVLED_OFF, // Inactive, LED off
DRVLED_ON, // Active, LED on
DRVLED_ERROR // Error, blink LED
};
// Information about file in disk image/archive file
struct c64_dir_entry {
c64_dir_entry(const uint8 *n, int t, bool o, bool p, size_t s, off_t ofs = 0, uint8 sal = 0, uint8 sah = 0)
: type(t), is_open(o), is_protected(p), size(s), offset(ofs), sa_lo(sal), sa_hi(sah)
{
strncpy((char *)name, (const char *)n, 17);
name[16] = 0;
}
// Basic information
uint8 name[17]; // File name (C64 charset, null-terminated)
int type; // File type (see defines above)
bool is_open; // Flag: file open
bool is_protected; // Flag: file protected
size_t size; // File size (may be approximated)
// Special information
off_t offset; // Offset of file in archive file
uint8 sa_lo, sa_hi; // C64 start address
};
class Drive;
class C64Display;
class Prefs;
// Class for complete IEC bus system with drives 8..11
class IEC {
public:
IEC(C64Display *display);
~IEC();
void Reset(void);
void NewPrefs(Prefs *prefs);
void UpdateLEDs(void);
uint8 Out(uint8 byte, bool eoi);
uint8 OutATN(uint8 byte);
uint8 OutSec(uint8 byte);
uint8 In(uint8 &byte);
void SetATN(void);
void RelATN(void);
void Turnaround(void);
void Release(void);
private:
Drive *create_drive(const char *path);
uint8 listen(int device);
uint8 talk(int device);
uint8 unlisten(void);
uint8 untalk(void);
uint8 sec_listen(void);
uint8 sec_talk(void);
uint8 open_out(uint8 byte, bool eoi);
uint8 data_out(uint8 byte, bool eoi);
uint8 data_in(uint8 &byte);
C64Display *the_display; // Pointer to display object (for drive LEDs)
uint8 name_buf[NAMEBUF_LENGTH]; // Buffer for file names and command strings
uint8 *name_ptr; // Pointer for reception of file name
int name_len; // Received length of file name
Drive *drive[2]; // 2 drives (8 and 9)
Drive *listener; // Pointer to active listener
Drive *talker; // Pointer to active talker
bool listener_active; // Listener selected, listener_data is valid
bool talker_active; // Talker selected, talker_data is valid
bool listening; // Last ATN was listen (to decide between sec_listen/sec_talk)
uint8 received_cmd; // Received command code ($x0)
uint8 sec_addr; // Received secondary address ($0x)
};
// Abstract superclass for individual drives
class Drive {
public:
Drive(IEC *iec);
virtual ~Drive() {}
virtual uint8 Open(int channel, const uint8 *name, int name_len)=0;
virtual uint8 Close(int channel)=0;
virtual uint8 Read(int channel, uint8 &byte)=0;
virtual uint8 Write(int channel, uint8 byte, bool eoi)=0;
virtual void Reset(void)=0;
int LED; // Drive LED state
bool Ready; // Drive is ready for operation
protected:
void set_error(int error, int track = 0, int sector = 0);
void parse_file_name(const uint8 *src, int src_len, uint8 *dest, int &dest_len, int &mode, int &type, int &rec_len, bool convert_charset = false);
void execute_cmd(const uint8 *cmd, int cmd_len);
virtual void block_read_cmd(int channel, int track, int sector, bool user_cmd = false);
virtual void block_write_cmd(int channel, int track, int sector, bool user_cmd = false);
virtual void block_execute_cmd(int channel, int track, int sector);
virtual void block_allocate_cmd(int track, int sector);
virtual void block_free_cmd(int track, int sector);
virtual void buffer_pointer_cmd(int channel, int pos);
virtual void mem_read_cmd(uint16 adr, uint8 len);
virtual void mem_write_cmd(uint16 adr, uint8 len, uint8 *p);
virtual void mem_execute_cmd(uint16 adr);
virtual void copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len);
virtual void rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len);
virtual void scratch_cmd(const uint8 *files, int files_len);
virtual void position_cmd(const uint8 *cmd, int cmd_len);
virtual void initialize_cmd(void);
virtual void new_cmd(const uint8 *name, int name_len, const uint8 *comma);
virtual void validate_cmd(void);
void unsupp_cmd(void);
char error_buf[256]; // Buffer with current error message
char *error_ptr; // Pointer within error message
int error_len; // Remaining length of error message
int current_error; // Number of current error
uint8 cmd_buf[64]; // Buffer for incoming command strings
int cmd_len; // Length of received command
private:
IEC *the_iec; // Pointer to IEC object
};
/*
* Functions
*/
// Convert ASCII character to PETSCII character
extern uint8 ascii2petscii(char c);
// Convert ASCII string to PETSCII string
extern void ascii2petscii(uint8 *dest, const char *src, int max);
// Convert PETSCII character to ASCII character
extern char petscii2ascii(uint8 c);
// Convert PETSCII string to ASCII string
extern void petscii2ascii(char *dest, const uint8 *src, int max);
// Check whether file is a mountable disk image or archive file, return type
extern bool IsMountableFile(const char *path, int &type);
// Read directory of mountable disk image or archive file into c64_dir_entry vector
extern bool ReadDirectory(const char *path, int type, vector<c64_dir_entry> &vec);
#endif

53
arm9/source/Prefs.cpp Normal file
View File

@ -0,0 +1,53 @@
/*
* Prefs.cpp - Global preferences
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "Prefs.h"
#include "Display.h"
#include "C64.h"
#include "main.h"
// These are the active preferences
Prefs ThePrefs __attribute__((section(".dtcm")));
/*
* Constructor: Set up preferences with defaults
*/
Prefs::Prefs()
{
// These are PAL specific values
DrawEveryN = isDSiMode() ? 1:2;
strcpy(DrivePath[0], "");
strcpy(DrivePath[1], "");
SIDType = SIDTYPE_DIGITAL;
LimitSpeed = true;
FastReset = true;
CIAIRQHack = false;
TrueDrive = false; // True Drive Emulation when TRUE (slower emulation)
SIDFilters = true;
}

58
arm9/source/Prefs.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Prefs.h - Global preferences
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PREFS_H
#define _PREFS_H
#include "sysdeps.h"
// SID types
enum {
SIDTYPE_NONE, // SID emulation off
SIDTYPE_DIGITAL, // Digital SID emulation
SIDTYPE_SIDCARD // SID card
};
// Preferences data
class Prefs {
public:
Prefs();
bool ShowEditor(bool startup, char *prefs_name);
void Check(void);
void Load(char *filename);
bool Save(char *filename);
bool operator==(const Prefs &rhs) const;
bool operator!=(const Prefs &rhs) const;
char DrivePath[2][256]; // Path for drive 8 and 9
int DrawEveryN; // Draw every n-th frame
int SIDType; // SID emulation type
bool LimitSpeed; // Limit speed to 100%
bool FastReset; // Skip RAM test on reset
bool CIAIRQHack; // Write to CIA ICR clears IRQ
bool TrueDrive; // Enable processor-level 1541 emulation
bool SIDFilters; // Emulate SID filters
};
// These are the active preferences
extern Prefs ThePrefs;
#endif

1331
arm9/source/SID.cpp Normal file

File diff suppressed because it is too large Load Diff

178
arm9/source/SID.h Normal file
View File

@ -0,0 +1,178 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* SID.h - 6581 emulation
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SID_H
#define _SID_H
#include <nds.h>
#include <stdlib.h>
// Define this if you want an emulation of an 8580
// (affects combined waveforms)
#undef EMUL_MOS8580
class Prefs;
class C64;
class SIDRenderer;
struct MOS6581State;
// Class for administrative functions
class MOS6581 {
public:
MOS6581(C64 *c64);
~MOS6581();
void Reset(void);
uint8 ReadRegister(uint16 adr);
void WriteRegister(uint16 adr, uint8 byte);
void NewPrefs(Prefs *prefs);
void PauseSound(void);
void ResumeSound(void);
void GetState(MOS6581State *ss);
void SetState(MOS6581State *ss);
void EmulateLine(void);
private:
void open_close_renderer(int old_type, int new_type);
C64 *the_c64; // Pointer to C64 object
SIDRenderer *the_renderer; // Pointer to current renderer
uint8 regs[32]; // Copies of the 25 write-only SID registers
uint8 last_sid_byte; // Last value written to SID
};
// Renderers do the actual audio data processing
class SIDRenderer {
public:
virtual ~SIDRenderer() {}
virtual void Reset(void)=0;
virtual void EmulateLine(void)=0;
virtual void WriteRegister(uint16 adr, uint8 byte)=0;
virtual void NewPrefs(Prefs *prefs)=0;
virtual void Pause(void)=0;
virtual void Resume(void)=0;
};
// SID state
struct MOS6581State {
uint8 freq_lo_1;
uint8 freq_hi_1;
uint8 pw_lo_1;
uint8 pw_hi_1;
uint8 ctrl_1;
uint8 AD_1;
uint8 SR_1;
uint8 freq_lo_2;
uint8 freq_hi_2;
uint8 pw_lo_2;
uint8 pw_hi_2;
uint8 ctrl_2;
uint8 AD_2;
uint8 SR_2;
uint8 freq_lo_3;
uint8 freq_hi_3;
uint8 pw_lo_3;
uint8 pw_hi_3;
uint8 ctrl_3;
uint8 AD_3;
uint8 SR_3;
uint8 fc_lo;
uint8 fc_hi;
uint8 res_filt;
uint8 mode_vol;
uint8 pot_x;
uint8 pot_y;
uint8 osc_3;
uint8 env_3;
};
/*
* Fill buffer (for Unix sound routines), sample volume (for sampled voice)
*/
inline void MOS6581::EmulateLine(void)
{
if (the_renderer != NULL)
the_renderer->EmulateLine();
}
/*
* Read from register
*/
inline uint8 MOS6581::ReadRegister(uint16 adr)
{
// A/D converters
if (adr == 0x19 || adr == 0x1a) {
last_sid_byte = 0;
return 0xff;
}
// Voice 3 oscillator/EG readout
if (adr == 0x1b || adr == 0x1c) {
last_sid_byte = 0;
return rand();
}
// Write-only register: Return last value written to SID
return last_sid_byte;
}
/*
* Write to register
*/
inline void MOS6581::WriteRegister(uint16 adr, uint8 byte)
{
// Keep a local copy of the register values
last_sid_byte = regs[adr] = byte;
if (the_renderer != NULL)
the_renderer->WriteRegister(adr, byte);
}
#endif

1675
arm9/source/VIC.cpp Normal file

File diff suppressed because it is too large Load Diff

250
arm9/source/VIC.h Normal file
View File

@ -0,0 +1,250 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* VIC.h - 6569R5 emulation (line based)
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VIC_H
#define _VIC_H
#include <nds.h>
// Define this if you want global variables instead of member variables
#if defined(__i386) || defined(mc68000) || defined(__MC68K__)
#define GLOBAL_VARS
#endif
// Define this if you have a processor that can do unaligned accesses quickly
#if defined(__i386) || defined(mc68000) || defined(__MC68K__)
#define CAN_ACCESS_UNALIGNED
#endif
// Total number of raster lines (PAL)
const unsigned TOTAL_RASTERS = 0x138;
// Screen refresh frequency (PAL)
const unsigned SCREEN_FREQ = 50;
class MOS6510;
class C64Display;
class C64;
struct MOS6569State;
class MOS6569 {
public:
MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color);
uint8 ReadRegister(uint16 adr);
void WriteRegister(uint16 adr, uint8 byte);
int EmulateLine(void);
void ChangedVA(uint16 new_va); // CIA VA14/15 has changed
void TriggerLightpen(void); // Trigger lightpen interrupt
void GetState(MOS6569State *vd);
void SetState(MOS6569State *vd);
private:
#ifndef GLOBAL_VARS
void vblank(void);
void raster_irq(void);
uint16 mx[8]; // VIC registers
uint8 my[8];
uint8 mx8;
uint8 ctrl1, ctrl2;
uint8 lpx, lpy;
uint8 me, mxe, mye, mdp, mmc;
uint8 vbase;
uint8 irq_flag, irq_mask;
uint8 clx_spr, clx_bgr;
uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1;
uint8 sc[8];
uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM
C64 *the_c64; // Pointer to C64
C64Display *the_display; // Pointer to C64Display
MOS6510 *the_cpu; // Pointer to 6510
uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f")
uint8 ec_color, b0c_color, b1c_color,
b2c_color, b3c_color; // Indices for exterior/background colors
uint8 mm0_color, mm1_color; // Indices for MOB multicolors
uint8 spr_color[8]; // Indices for MOB colors
uint32 ec_color_long; // ec_color expanded to 32 bits
uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines
uint8 color_line[40]; // Buffer for color line, read in Bad Lines
uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer
int xmod; // Number of bytes per row
uint16 raster_y; // Current raster line
uint16 irq_raster; // Interrupt raster line
uint16 dy_start; // Comparison values for border logic
uint16 dy_stop;
uint16 rc; // Row counter
uint16 vc; // Video counter
uint16 vc_base; // Video counter base
uint16 x_scroll; // X scroll value
uint16 y_scroll; // Y scroll value
uint16 cia_vabase; // CIA VA14/15 video base
uint16 mc[8]; // Sprite data counters
int display_idx; // Index of current display mode
int skip_counter; // Counter for frame-skipping
long pad0; // Keep buffers long-aligned
uint8 spr_coll_buf[DISPLAY_X]; // Buffer for sprite-sprite collisions and priorities
uint8 fore_mask_buf[DISPLAY_X/8]; // Foreground mask for sprite-graphics collisions and priorities
#ifndef CAN_ACCESS_UNALIGNED
uint8 text_chunky_buf[40*8]; // Line graphics buffer
#endif
bool display_state; // true: Display state, false: Idle state
bool border_on; // Flag: Upper/lower border on (Frodo SC: Main border flipflop)
bool frame_skipped; // Flag: Frame is being skipped
uint8 bad_lines_enabled; // Flag: Bad Lines enabled for this frame
bool lp_triggered; // Flag: Lightpen was triggered in this frame
uint8 *get_physical(uint16 adr);
void make_mc_table(void);
void el_std_text(uint8 *p, uint8 *q, uint8 *r);
void el_mc_text(uint8 *p, uint8 *q, uint8 *r);
void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r);
void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r);
void el_ecm_text(uint8 *p, uint8 *q, uint8 *r);
void el_std_idle(uint8 *p, uint8 *r);
void el_mc_idle(uint8 *p, uint8 *r);
void el_sprites(uint8 *chunky_ptr);
int el_update_mc(int raster);
uint16 mc_color_lookup[4];
bool border_40_col; // Flag: 40 column border
uint8 sprite_on; // 8 flags: Sprite display/DMA active
uint8 *matrix_base; // Video matrix base
uint8 *char_base; // Character generator base
uint8 *bitmap_base; // Bitmap base
#endif
};
// VIC state
struct MOS6569State {
uint8 m0x; // Sprite coordinates
uint8 m0y;
uint8 m1x;
uint8 m1y;
uint8 m2x;
uint8 m2y;
uint8 m3x;
uint8 m3y;
uint8 m4x;
uint8 m4y;
uint8 m5x;
uint8 m5y;
uint8 m6x;
uint8 m6y;
uint8 m7x;
uint8 m7y;
uint8 mx8;
uint8 ctrl1; // Control registers
uint8 raster;
uint8 lpx;
uint8 lpy;
uint8 me;
uint8 ctrl2;
uint8 mye;
uint8 vbase;
uint8 irq_flag;
uint8 irq_mask;
uint8 mdp;
uint8 mmc;
uint8 mxe;
uint8 mm;
uint8 md;
uint8 ec; // Color registers
uint8 b0c;
uint8 b1c;
uint8 b2c;
uint8 b3c;
uint8 mm0;
uint8 mm1;
uint8 m0c;
uint8 m1c;
uint8 m2c;
uint8 m3c;
uint8 m4c;
uint8 m5c;
uint8 m6c;
uint8 m7c;
// Additional registers
uint8 pad0;
uint16 irq_raster; // IRQ raster line
uint16 vc; // Video counter
uint16 vc_base; // Video counter base
uint8 rc; // Row counter
uint8 spr_dma; // 8 Flags: Sprite DMA active
uint8 spr_disp; // 8 Flags: Sprite display active
uint8 mc[8]; // Sprite data counters
uint8 mc_base[8]; // Sprite data counter bases
bool display_state; // true: Display state, false: Idle state
bool bad_line; // Flag: Bad Line state
bool bad_line_enable; // Flag: Bad Lines enabled for this frame
bool lp_triggered; // Flag: Lightpen was triggered in this frame
bool border_on; // Flag: Upper/lower border on (Frodo SC: Main border flipflop)
uint16 bank_base; // VIC bank base address
uint16 matrix_base; // Video matrix base
uint16 char_base; // Character generator base
uint16 bitmap_base; // Bitmap base
uint16 sprite_base[8]; // Sprite bases
// Frodo SC:
int cycle; // Current cycle in line (1..63)
uint16 raster_x; // Current raster x position
int ml_index; // Index in matrix/color_line[]
uint8 ref_cnt; // Refresh counter
uint8 last_vic_byte; // Last byte read by VIC
bool ud_border_on; // Flag: Upper/lower border on
};
#endif

686
arm9/source/diskmenu.cpp Normal file
View File

@ -0,0 +1,686 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
// Diskette Menu Loading Code
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include "diskmenu.h"
#include "mainmenu.h"
#include "keyboard.h"
#include "mainmenu_bg.h"
#include "diskmenu_bg.h"
#include "Prefs.h"
#include "C64.h"
char Drive8File[MAX_FILENAME_LEN];
char Drive9File[MAX_FILENAME_LEN];
extern int bg0b, bg1b;
int countZX=0;
int ucGameAct=0;
int ucGameChoice = -1;
FIC64 gpFic[MAX_FILES];
char szName[256];
char szFile[256];
u32 file_size = 0;
char strBuf[40];
#define WAITVBL swiWaitForVBlank();swiWaitForVBlank();swiWaitForVBlank();
/*********************************************************************************
* Show The 14 games on the list to allow the user to choose a new game.
********************************************************************************/
static char szName2[40];
void dsDisplayFiles(u16 NoDebGame, u8 ucSel)
{
u16 ucBcl,ucGame;
u8 maxLen;
DSPrint(31,5,0,(NoDebGame>0 ? (char*)"<" : (char*)" "));
DSPrint(31,22,0,(NoDebGame+14<countZX ? (char*)">" : (char*)" "));
for (ucBcl=0;ucBcl<18; ucBcl++)
{
ucGame= ucBcl+NoDebGame;
if (ucGame < countZX)
{
maxLen=(int)strlen(gpFic[ucGame].szName);
strcpy(szName,gpFic[ucGame].szName);
if (maxLen>30) szName[30]='\0';
if (gpFic[ucGame].uType == DIRECTORY)
{
szName[28] = 0; // Needs to be 2 chars shorter with brackets
sprintf(szName2, "[%s]",szName);
sprintf(szName,"%-30s",szName2);
DSPrint(1,5+ucBcl,(ucSel == ucBcl ? 2 : 0),szName);
}
else
{
sprintf(szName,"%-30s",strupr(szName));
DSPrint(1,5+ucBcl,(ucSel == ucBcl ? 2 : 0 ),szName);
}
}
else
{
DSPrint(1,5+ucBcl,(ucSel == ucBcl ? 2 : 0 ),(char *)" ");
}
}
}
// -------------------------------------------------------------------------
// Standard qsort routine for the games - we sort all directory
// listings first and then a case-insenstive sort of all games.
// -------------------------------------------------------------------------
int Filescmp (const void *c1, const void *c2)
{
FIC64 *p1 = (FIC64 *) c1;
FIC64 *p2 = (FIC64 *) c2;
if (p1->szName[0] == '.' && p2->szName[0] != '.')
return -1;
if (p2->szName[0] == '.' && p1->szName[0] != '.')
return 1;
if ((p1->uType == DIRECTORY) && !(p2->uType == DIRECTORY))
return -1;
if ((p2->uType == DIRECTORY) && !(p1->uType == DIRECTORY))
return 1;
return strcasecmp (p1->szName, p2->szName);
}
/*********************************************************************************
* Find files (TAP/TZX/Z80/SNA) available - sort them for display.
********************************************************************************/
void speccySEFindFiles(u8 bTapeOnly)
{
u32 uNbFile;
DIR *dir;
struct dirent *pent;
uNbFile=0;
countZX=0;
dir = opendir(".");
while (((pent=readdir(dir))!=NULL) && (uNbFile<MAX_FILES))
{
strcpy(szFile,pent->d_name);
if(pent->d_type == DT_DIR)
{
if (!((szFile[0] == '.') && ((int)strlen(szFile) == 1)))
{
// Do not include the [sav] and [pok] directories
if ((strcasecmp(szFile, "sav") != 0))
{
strcpy(gpFic[uNbFile].szName,szFile);
gpFic[uNbFile].uType = DIRECTORY;
uNbFile++;
countZX++;
}
}
}
else {
if ((strlen(szFile)>4) && (strlen(szFile)<(MAX_FILENAME_LEN-4)) && (szFile[0] != '.') && (szFile[0] != '_')) // For MAC don't allow files starting with an underscore
{
if ( (strcasecmp(strrchr(szFile, '.'), ".D64") == 0) ) {
strcpy(gpFic[uNbFile].szName,szFile);
gpFic[uNbFile].uType = SPECCY_FILE;
uNbFile++;
countZX++;
}
}
}
}
closedir(dir);
// ----------------------------------------------
// If we found any files, go sort the list...
// ----------------------------------------------
if (countZX)
{
qsort (gpFic, countZX, sizeof(FIC64), Filescmp);
}
}
// ----------------------------------------------------------------
// Let the user select a new game (rom) file and load it up!
// ----------------------------------------------------------------
u8 speccySELoadFile(u8 bTapeOnly)
{
bool bDone=false;
u16 ucHaut=0x00, ucBas=0x00,ucSHaut=0x00, ucSBas=0x00, romSelected= 0, firstRomDisplay=0,nbRomPerPage, uNbRSPage;
s16 uLenFic=0, ucFlip=0, ucFlop=0;
u8 retVal = 0;
// Show the menu...
while ((keysCurrent() & (KEY_TOUCH | KEY_START | KEY_SELECT | KEY_A | KEY_B))!=0);
speccySEFindFiles(bTapeOnly);
ucGameChoice = -1;
nbRomPerPage = (countZX>=18 ? 18 : countZX);
uNbRSPage = (countZX>=5 ? 5 : countZX);
if (ucGameAct>countZX-nbRomPerPage)
{
firstRomDisplay=countZX-nbRomPerPage;
romSelected=ucGameAct-countZX+nbRomPerPage;
}
else
{
firstRomDisplay=ucGameAct;
romSelected=0;
}
if (romSelected >= countZX) romSelected = 0; // Just start at the top
dsDisplayFiles(firstRomDisplay,romSelected);
// -----------------------------------------------------
// Until the user selects a file or exits the menu...
// -----------------------------------------------------
while (!bDone)
{
if (keysCurrent() & KEY_UP)
{
if (!ucHaut)
{
ucGameAct = (ucGameAct>0 ? ucGameAct-1 : countZX-1);
if (romSelected>uNbRSPage) { romSelected -= 1; }
else {
if (firstRomDisplay>0) { firstRomDisplay -= 1; }
else {
if (romSelected>0) { romSelected -= 1; }
else {
firstRomDisplay=countZX-nbRomPerPage;
romSelected=nbRomPerPage-1;
}
}
}
ucHaut=0x01;
dsDisplayFiles(firstRomDisplay,romSelected);
}
else {
ucHaut++;
if (ucHaut>10) ucHaut=0;
}
uLenFic=0; ucFlip=-50; ucFlop=0;
}
else
{
ucHaut = 0;
}
if (keysCurrent() & KEY_DOWN)
{
if (!ucBas) {
ucGameAct = (ucGameAct< countZX-1 ? ucGameAct+1 : 0);
if (romSelected<uNbRSPage-1) { romSelected += 1; }
else {
if (firstRomDisplay<countZX-nbRomPerPage) { firstRomDisplay += 1; }
else {
if (romSelected<nbRomPerPage-1) { romSelected += 1; }
else {
firstRomDisplay=0;
romSelected=0;
}
}
}
ucBas=0x01;
dsDisplayFiles(firstRomDisplay,romSelected);
}
else
{
ucBas++;
if (ucBas>10) ucBas=0;
}
uLenFic=0; ucFlip=-50; ucFlop=0;
}
else {
ucBas = 0;
}
// -------------------------------------------------------------
// Left and Right on the D-Pad will scroll 1 page at a time...
// -------------------------------------------------------------
if (keysCurrent() & KEY_RIGHT)
{
if (!ucSBas)
{
ucGameAct = (ucGameAct< countZX-nbRomPerPage ? ucGameAct+nbRomPerPage : countZX-nbRomPerPage);
if (firstRomDisplay<countZX-nbRomPerPage) { firstRomDisplay += nbRomPerPage; }
else { firstRomDisplay = countZX-nbRomPerPage; }
if (ucGameAct == countZX-nbRomPerPage) romSelected = 0;
ucSBas=0x01;
dsDisplayFiles(firstRomDisplay,romSelected);
}
else
{
ucSBas++;
if (ucSBas>10) ucSBas=0;
}
uLenFic=0; ucFlip=-50; ucFlop=0;
}
else {
ucSBas = 0;
}
// -------------------------------------------------------------
// Left and Right on the D-Pad will scroll 1 page at a time...
// -------------------------------------------------------------
if (keysCurrent() & KEY_LEFT)
{
if (!ucSHaut)
{
ucGameAct = (ucGameAct> nbRomPerPage ? ucGameAct-nbRomPerPage : 0);
if (firstRomDisplay>nbRomPerPage) { firstRomDisplay -= nbRomPerPage; }
else { firstRomDisplay = 0; }
if (ucGameAct == 0) romSelected = 0;
if (romSelected > ucGameAct) romSelected = ucGameAct;
ucSHaut=0x01;
dsDisplayFiles(firstRomDisplay,romSelected);
}
else
{
ucSHaut++;
if (ucSHaut>10) ucSHaut=0;
}
uLenFic=0; ucFlip=-50; ucFlop=0;
}
else {
ucSHaut = 0;
}
// -------------------------------------------------------------------------
// They B key will exit out of the ROM selection without picking a new game
// -------------------------------------------------------------------------
if ( keysCurrent() & KEY_B )
{
bDone=true;
retVal = 0;
while (keysCurrent() & KEY_B);
}
// -------------------------------------------------------------------
// Any of these keys will pick the current ROM and try to load it...
// -------------------------------------------------------------------
if (keysCurrent() & KEY_A || keysCurrent() & KEY_Y || keysCurrent() & KEY_X)
{
if (gpFic[ucGameAct].uType != DIRECTORY)
{
bDone=true;
if (keysCurrent() & KEY_X) retVal = 2; else retVal = 1;
ucGameChoice = ucGameAct;
WAITVBL;
}
else
{
chdir(gpFic[ucGameAct].szName);
speccySEFindFiles(bTapeOnly);
ucGameAct = 0;
nbRomPerPage = (countZX>=14 ? 14 : countZX);
uNbRSPage = (countZX>=5 ? 5 : countZX);
if (ucGameAct>countZX-nbRomPerPage) {
firstRomDisplay=countZX-nbRomPerPage;
romSelected=ucGameAct-countZX+nbRomPerPage;
}
else {
firstRomDisplay=ucGameAct;
romSelected=0;
}
dsDisplayFiles(firstRomDisplay,romSelected);
while (keysCurrent() & KEY_A);
}
}
// --------------------------------------------
// If the filename is too long... scroll it.
// --------------------------------------------
if ((int)strlen(gpFic[ucGameAct].szName) > 30)
{
ucFlip++;
if (ucFlip >= 25)
{
ucFlip = 0;
uLenFic++;
if ((uLenFic+30)>(int)strlen(gpFic[ucGameAct].szName))
{
ucFlop++;
if (ucFlop >= 15)
{
uLenFic=0;
ucFlop = 0;
}
else
uLenFic--;
}
strncpy(szName,gpFic[ucGameAct].szName+uLenFic,30);
szName[30] = '\0';
DSPrint(1,5+romSelected,2,szName);
}
}
swiWaitForVBlank();
}
// Wait for some key to be pressed before returning
while ((keysCurrent() & (KEY_TOUCH | KEY_START | KEY_SELECT | KEY_A | KEY_B | KEY_R | KEY_L | KEY_UP | KEY_DOWN))!=0);
return retVal;
}
u16 nds_key;
void LoadGameConfig(void)
{
if (strlen(Drive8File) > 1)
{
file_crc = getCRC32((u8*)Drive8File, strlen(Drive8File));
FindConfig();
}
}
void BottomScreenDiskette(void)
{
decompress(diskmenu_bgTiles, bgGetGfxPtr(bg0b), LZ77Vram);
decompress(diskmenu_bgMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
dmaCopy((void*) bgGetMapPtr(bg0b)+32*30*2,(void*) bgGetMapPtr(bg1b),32*24*2);
dmaCopy((void*) diskmenu_bgPal,(void*) BG_PALETTE_SUB,256*2);
unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
}
void BottomScreenMainMenu(void)
{
decompress(mainmenu_bgTiles, bgGetGfxPtr(bg0b), LZ77Vram);
decompress(mainmenu_bgMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
dmaCopy((void*) bgGetMapPtr(bg0b)+32*30*2,(void*) bgGetMapPtr(bg1b),32*24*2);
dmaCopy((void*) mainmenu_bgPal,(void*) BG_PALETTE_SUB,256*2);
unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
}
void DisplayFileNameDiskette(void)
{
char tmp[34];
DSPrint(5,6,6, (char*)" ");
DSPrint(5,10,6, (char*)" ");
if (strlen(Drive8File) > 1)
{
DSPrint(5,5,6, (char *)"DRIVE 8 IS MOUNTED WITH:");
strncpy(tmp, Drive8File, 27); tmp[26]=0;
DSPrint(5,6,6, tmp);
}
else
{
DSPrint(5,5,6, (char *)"DRIVE 8 IS NOT MOUNTED ");
}
if (strlen(Drive9File) > 1)
{
DSPrint(5,9,6, (char *)"DRIVE 9 IS MOUNTED WITH:");
strncpy(tmp, Drive9File, 27); tmp[26]=0;
DSPrint(5,10,6, tmp);
}
else
{
DSPrint(5,9,6, (char *)"DRIVE 9 IS NOT MOUNTED ");
}
if (myConfig.trueDrive)
{
DSPrint(0,22,6, (char *)" TRUE DRIVE IS ENABLED (SLOW) ");
}
else
{
DSPrint(0,22,6, (char *)" TRUE DRIVE IS DISABLED (FAST) ");
}
}
// ----------------------------------------------------------------------
// The Disk Menu can be called up directly from the keyboard graphic
// and allows the user to rewind the tape, swap in a new tape, etc.
// ----------------------------------------------------------------------
#define MENU_ACTION_END 255 // Always the last sentinal value
#define MENU_ACTION_EXIT 0 // Exit the menu
#define MENU_ACTION_DRIVE8 1 // Mount Drive 8 File
#define MENU_ACTION_DRIVE9 2 // Mount Drive 9 File
#define MENU_ACTION_EJECT 3 // Eject all drives
#define MENU_ACTION_REBOOT_C64 4 // Force C64 Reboot
#define MENU_ACTION_TRUE_DRIVE 5 // Toggle True Drive
#define MENU_ACTION_CONFIG 6 // Configure Game
#define MENU_ACTION_SKIP 99 // Skip this MENU choice
typedef struct
{
char *menu_string;
u8 menu_action;
} MenuItem_t;
typedef struct
{
char *title;
u8 start_row;
MenuItem_t menulist[15];
} DiskMenu_t;
DiskMenu_t disk_menu =
{
(char *)" ", 10,
{
{(char *)" MOUNT DISK 8 ", MENU_ACTION_DRIVE8},
{(char *)" MOUNT DISK 9 ", MENU_ACTION_DRIVE9},
{(char *)" EJECT DISKS ", MENU_ACTION_EJECT},
{(char *)" TOGGLE TRUEDRIVE ", MENU_ACTION_TRUE_DRIVE},
{(char *)" CONFIG GAME ", MENU_ACTION_CONFIG},
{(char *)" REBOOT C64U ", MENU_ACTION_REBOOT_C64},
{(char *)" EXIT MENU ", MENU_ACTION_EXIT},
{(char *)" NULL ", MENU_ACTION_END},
},
};
static DiskMenu_t *menu = &disk_menu;
// -------------------------------------------------------
// Show the Disk Menu text - highlight the selected row.
// -------------------------------------------------------
u8 diskette_menu_items = 0;
void DiskMenuShow(bool bClearScreen, u8 sel)
{
diskette_menu_items = 0;
if (bClearScreen)
{
// -------------------------------------
// Put up the Diskette menu background
// -------------------------------------
BottomScreenDiskette();
}
// ---------------------------------------------------
// Pick the right context menu based on the machine
// ---------------------------------------------------
menu = &disk_menu;
// Display the menu title
DSPrint(15-(strlen(menu->title)/2), menu->start_row, 6, menu->title);
// And display all of the menu items
while (menu->menulist[diskette_menu_items].menu_action != MENU_ACTION_END)
{
DSPrint(16-(strlen(menu->menulist[diskette_menu_items].menu_string)/2), menu->start_row+2+diskette_menu_items, (diskette_menu_items == sel) ? 7:6, menu->menulist[diskette_menu_items].menu_string);
diskette_menu_items++;
}
// ----------------------------------------------------------------------------------------------
// And near the bottom, display the file/rom/disk that is currently loaded into memory.
// ----------------------------------------------------------------------------------------------
DisplayFileNameDiskette();
}
// ------------------------------------------------------------------------
// Handle Disk mini-menu interface... Allows rewind, swap tape, etc.
// ------------------------------------------------------------------------
u8 DisketteMenu(C64 *the_c64)
{
u8 menuSelection = 0;
u8 retVal = 0;
while ((keysCurrent() & (KEY_TOUCH | KEY_LEFT | KEY_RIGHT | KEY_A ))!=0);
// ------------------------------------------------------------------
//Show the cassette menu background - we'll draw text on top of this
// ------------------------------------------------------------------
DiskMenuShow(true, menuSelection);
u8 bExitMenu = false;
while (true)
{
nds_key = keysCurrent();
if (nds_key)
{
if (nds_key & KEY_UP)
{
menuSelection = (menuSelection > 0) ? (menuSelection-1):(diskette_menu_items-1);
while (menu->menulist[menuSelection].menu_action == MENU_ACTION_SKIP)
{
menuSelection = (menuSelection > 0) ? (menuSelection-1):(diskette_menu_items-1);
}
DiskMenuShow(false, menuSelection);
}
if (nds_key & KEY_DOWN)
{
menuSelection = (menuSelection+1) % diskette_menu_items;
while (menu->menulist[menuSelection].menu_action == MENU_ACTION_SKIP)
{
menuSelection = (menuSelection+1) % diskette_menu_items;
}
DiskMenuShow(false, menuSelection);
}
if (nds_key & KEY_B) // Treat this as selecting 'exit'
{
bExitMenu = true;
}
else
if (nds_key & KEY_A) // User has picked a menu item... let's see what it is!
{
switch(menu->menulist[menuSelection].menu_action)
{
case MENU_ACTION_EXIT:
bExitMenu = true;
break;
case MENU_ACTION_DRIVE8:
BottomScreenMainMenu();
retVal = speccySELoadFile(0);
if (ucGameChoice >= 0)
{
retVal = 1;
strcpy(Drive8File, gpFic[ucGameChoice].szName);
LoadGameConfig();
}
DiskMenuShow(true, menuSelection);
break;
case MENU_ACTION_DRIVE9:
BottomScreenMainMenu();
retVal = speccySELoadFile(0);
if (ucGameChoice >= 0)
{
retVal = 1;
strcpy(Drive9File, gpFic[ucGameChoice].szName);
}
DiskMenuShow(true, menuSelection);
break;
case MENU_ACTION_EJECT:
strcpy(Drive8File, "");
strcpy(Drive9File, "");
retVal = 1;
DiskMenuShow(true, menuSelection);
break;
case MENU_ACTION_TRUE_DRIVE:
myConfig.trueDrive ^= 1;
DiskMenuShow(true, menuSelection);
break;
case MENU_ACTION_CONFIG:
if (file_crc != 0x00000000)
{
u8 last_trueDrive = myConfig.trueDrive;
BottomScreenMainMenu();
GimliDSGameOptions();
if (last_trueDrive != myConfig.trueDrive) // Need to reload...
{
Prefs *prefs = new Prefs(ThePrefs);
prefs->TrueDrive = myConfig.trueDrive;
the_c64->NewPrefs(prefs);
ThePrefs = *prefs;
delete prefs;
}
DiskMenuShow(true, menuSelection);
}
else
{
DSPrint(0, 20, 6, (char*)" NO GAME IS LOADED ");
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
DSPrint(0, 20, 6, (char*)" ");
}
break;
case MENU_ACTION_REBOOT_C64:
retVal = 2;
bExitMenu = true;
break;
}
}
if (bExitMenu) break;
while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0);
WAITVBL;WAITVBL;WAITVBL;
}
}
while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0);
WAITVBL;WAITVBL;WAITVBL;
return retVal;
}
u8 mount_disk(C64 *the_c64)
{
strcpy(Drive8File, ThePrefs.DrivePath[0]);
strcpy(Drive9File, ThePrefs.DrivePath[1]);
u8 retVal = DisketteMenu(the_c64);
return retVal;
}

21
arm9/source/diskmenu.h Normal file
View File

@ -0,0 +1,21 @@
#define MAX_FILES 2048
#define MAX_FILENAME_LEN 256
#define SPECCY_FILE 0x01
#define DIRECTORY 0x02
class C64;
extern void DSPrint(int iX,int iY,int iScr,char *szMessage);
typedef struct {
char szName[MAX_FILENAME_LEN+1];
u8 uType;
u32 uCrc;
} FIC64;
extern char Drive8File[MAX_FILENAME_LEN];
extern char Drive9File[MAX_FILENAME_LEN];
extern FIC64 gpFic[MAX_FILES];
extern int ucGameChoice;
extern u8 mount_disk(C64 *the_c64);

2324
arm9/source/lzav.h Normal file

File diff suppressed because it is too large Load Diff

283
arm9/source/main.cpp Normal file
View File

@ -0,0 +1,283 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
/*
* main.cpp - Main program
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "main.h"
#include "C64.h"
#include "Display.h"
#include "Prefs.h"
#include "mainmenu.h"
#include "intro.h"
#include <nds.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
extern int init_graphics(void);
extern void InterruptHandler(void);
// Global variables
C64 *TheC64 = NULL; // Global C64 object
void intro_logo(void);
extern void init_maxmod(void);
extern void ShowKeyboard(void);
extern void BottomScreenMainMenu(void);
#define KERNAL_ROM_FILE "kernal.rom"
#define BASIC_ROM_FILE "basic.rom"
#define CHAR_ROM_FILE "char.rom"
#define DRIVE_ROM_FILE "1541.rom"
/*
* Load C64 ROM files
*/
char big_path[300];
u8 Frodo::load_rom(const char *which, const char *path, uint8 *where, size_t size)
{
sprintf(big_path, "/roms/bios/%s", path); // Try /roms/bios
FILE *f = fopen(big_path, "rb");
if (f)
{
size_t actual = fread(where, 1, size, f);
fclose(f);
if (actual == size) return 1;
}
else // Try /roms/c64
{
sprintf(big_path, "/roms/c64/%s", path);
FILE *f = fopen(big_path, "rb");
if (f)
{
size_t actual = fread(where, 1, size, f);
fclose(f);
if (actual == size) return 1;
}
else // Try current directory
{
FILE *f = fopen(path, "rb");
if (f)
{
size_t actual = fread(where, 1, size, f);
fclose(f);
if (actual == size) return 1;
}
}
}
return 0;
}
void Frodo::load_rom_files()
{
u8 roms_loaded = 0;
roms_loaded += load_rom("Basic", BASIC_ROM_FILE, TheC64->Basic, BASIC_ROM_SIZE);
roms_loaded += load_rom("Kernal", KERNAL_ROM_FILE, TheC64->Kernal, KERNAL_ROM_SIZE);
roms_loaded += load_rom("Char", CHAR_ROM_FILE, TheC64->Char, CHAR_ROM_SIZE);
roms_loaded += load_rom("1541", DRIVE_ROM_FILE, TheC64->ROM1541, DRIVE_ROM_SIZE);
if (roms_loaded != 4)
{
BottomScreenMainMenu();
DSPrint(0,5, 6, (char*) "ONE OR MORE BIOS ROMS NOT FOUND ");
DSPrint(0,7, 6, (char*) "THIS EMULATOR REQUIRES ORIGINAL ");
DSPrint(0,8, 6, (char*) "C64 BIOS ROMS AS FOLLOWS: ");
DSPrint(0,10,6, (char*) "KERNAL.ROM 8K CRC32:dbe3e7c7 ");
DSPrint(0,11,6, (char*) "BASIC.ROM 8K CRC32:f833d117 ");
DSPrint(0,12,6, (char*) "CHAR.ROM 4K CRC32:ec4272ee ");
DSPrint(0,13,6, (char*) "1541.ROM 16K CRC32:899fa3c5 ");
DSPrint(0,15,6, (char*) "PLACE THESE EXACTLY NAMED ROMS ");
DSPrint(0,16,6, (char*) "IN /ROMS/BIOS or /ROMS/C64 or ");
DSPrint(0,17,6, (char*) "IN THE SAME DIRECTORY AS THE EMU");
while(1) asm("nop");
}
}
/*
* Create application object and start it
*/
extern "C" {
int main(int argc, char **argv)
{
Frodo *the_app;
char *args[]={ (char*)"Gimli", NULL };
defaultExceptionHandler();
intro_logo();
if (!init_graphics())
return 0;
LoadConfig();
the_app = new Frodo();
the_app->ArgvReceived(1, args);
keysSetRepeat(15, 6);
the_app->ReadyToRun();
delete the_app;
return (1);
}
/*
* Constructor: Initialize member variables
*/
Frodo::Frodo()
{
TheC64 = NULL;
}
/*
* Process command line arguments
*/
void Frodo::ArgvReceived(int argc, char **argv)
{
}
/*
* Arguments processed, run emulation
*/
void Frodo::ReadyToRun(void)
{
// Create and start C64
TheC64 = new C64;
load_rom_files();
TheC64->Run();
delete TheC64;
}
}
bool IsDirectory(const char *path){
struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
#include <maxmod9.h>
#include "soundbank.h"
u16 vusCptVBL=0;
void vblankIntro()
{
vusCptVBL++;
}
/******************************************************************************
* Routine FadeToColor : Fade from background to black or white
******************************************************************************/
void FadeToColor(unsigned char ucSens, unsigned short ucBG, unsigned char ucScr, unsigned char valEnd, unsigned char uWait) {
unsigned short ucFade;
unsigned char ucBcl;
// Fade-out to black
if (ucScr & 0x01) REG_BLDCNT=ucBG;
if (ucScr & 0x02) REG_BLDCNT_SUB=ucBG;
if (ucSens == 1) {
for(ucFade=0;ucFade<valEnd;ucFade++) {
if (ucScr & 0x01) REG_BLDY=ucFade;
if (ucScr & 0x02) REG_BLDY_SUB=ucFade;
for (ucBcl=0;ucBcl<uWait;ucBcl++) {
swiWaitForVBlank();
}
}
}
else {
for(ucFade=16;ucFade>valEnd;ucFade--) {
if (ucScr & 0x01) REG_BLDY=ucFade;
if (ucScr & 0x02) REG_BLDY_SUB=ucFade;
for (ucBcl=0;ucBcl<uWait;ucBcl++) {
swiWaitForVBlank();
}
}
}
}
// --------------------------------------------------------------
// Intro with portabledev logo and new PHEONIX-EDITION version
// --------------------------------------------------------------
void intro_logo(void)
{
bool bOK;
// Init graphics
videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE );
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE );
vramSetBankA(VRAM_A_MAIN_BG); vramSetBankC(VRAM_C_SUB_BG);
irqSet(IRQ_VBLANK, vblankIntro);
irqEnable(IRQ_VBLANK);
// Init BG
int bg1 = bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 31,0);
REG_BLDCNT = BLEND_FADE_BLACK | BLEND_SRC_BG0 | BLEND_DST_BG0; REG_BLDY = 16;
init_maxmod();
mmEffect(SFX_MUS_INTRO);
// Show portabledev
decompress(introTiles, bgGetGfxPtr(bg1), LZ77Vram);
decompress(introMap, (void*) bgGetMapPtr(bg1), LZ77Vram);
dmaCopy((void *) introPal,(u16*) BG_PALETTE,256*2);
FadeToColor(0,BLEND_FADE_BLACK | BLEND_SRC_BG0 | BLEND_DST_BG0,3,0,3);
bOK=false;
while (!bOK) { if ( !(keysCurrent() & 0x1FFF) ) bOK=true; } // 0x1FFF = key or touch screen
vusCptVBL=0;bOK=false;
while (!bOK && (vusCptVBL<5*60)) { if (keysCurrent() & 0x1FFF ) bOK=true; }
bOK=false;
while (!bOK) { if ( !(keysCurrent() & 0x1FFF) ) bOK=true; }
memset((u8*)0x06000000, 0x00, 0x20000); // Reset VRAM to 0x00 to clear any potential display garbage on way out
}

58
arm9/source/main.h Normal file
View File

@ -0,0 +1,58 @@
/*
* main.h - Main program
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MAIN_H
#define _MAIN_H
class C64;
// Global variables
class Prefs;
class Frodo {
public:
Frodo();
void ArgvReceived(int argc, char **argv);
void ReadyToRun(void);
C64 *TheC64;
private:
u8 load_rom(const char *which, const char *path, uint8 *where, size_t size);
void load_rom_files();
};
// Global C64 object
extern C64 *TheC64;
/*
* Functions
*/
// Determine whether path name refers to a directory
extern bool IsDirectory(const char *path);
extern void DSPrint(int iX,int iY,int iScr,char *szMessage);
extern void kbd_buf_reset(void);
extern int current_joystick;
#endif

661
arm9/source/mainmenu.cpp Normal file
View File

@ -0,0 +1,661 @@
// =====================================================================================
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
//
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
// any copying or distribution of this emulator, its source code and associated
// readme files, with or without modification, are permitted per the original
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
// efforts to provide a clean open-source emulation base for the C64.
//
// Numerous hacks and 'unsafe' optimizations have been performed on the original
// Frodo emulator codebase to get it running on the small handheld system. You
// are strongly encouraged to seek out the official Frodo sources if you're at
// all interested in this emulator code.
//
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
// =====================================================================================
// Main Menu Loading Code
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include "C64.h"
#include "diskmenu.h"
#include "mainmenu.h"
#include "mainmenu_bg.h"
#include "Prefs.h"
extern C64 *TheC64;
extern int bg0b, bg1b;
#define WAITVBL swiWaitForVBlank();swiWaitForVBlank();swiWaitForVBlank();
static u16 nds_key;
extern void BottomScreenMainMenu(void);
// ----------------------------------------------------------------------
// The Disk Menu can be called up directly from the keyboard graphic
// and allows the user to rewind the tape, swap in a new tape, etc.
// ----------------------------------------------------------------------
#define MENU_ACTION_END 255 // Always the last sentinal value
#define MENU_ACTION_EXIT 0 // Exit the menu
#define MENU_ACTION_RESET_EMU 1 // Reset Emulator
#define MENU_ACTION_SAVE_STATE 2 //
#define MENU_ACTION_LOAD_STATE 3 //
#define MENU_ACTION_CONFIG 4 //
#define MENU_ACTION_SKIP 99 // Skip this MENU choice
typedef struct
{
char *menu_string;
u8 menu_action;
} MenuItem_t;
typedef struct
{
char *title;
u8 start_row;
MenuItem_t menulist[15];
} MainMenu_t;
MainMenu_t main_menu =
{
(char *)"MAIN MENU", 7,
{
{(char *)" CONFIG GAME ", MENU_ACTION_CONFIG},
{(char *)" SAVE STATE ", MENU_ACTION_SAVE_STATE},
{(char *)" LOAD STATE ", MENU_ACTION_LOAD_STATE},
{(char *)" RESET C64 ", MENU_ACTION_RESET_EMU},
{(char *)" EXIT MENU ", MENU_ACTION_EXIT},
{(char *)" NULL ", MENU_ACTION_END},
},
};
static MainMenu_t *menu = &main_menu;
// -------------------------------------------------------
// Show the Main Menu text - highlight the selected row.
// -------------------------------------------------------
u8 main_menu_items = 0;
void MainMenuShow(bool bClearScreen, u8 sel)
{
main_menu_items = 0;
if (bClearScreen)
{
BottomScreenMainMenu();
}
// ---------------------------------------------------
// Pick the right context menu based on the machine
// ---------------------------------------------------
menu = &main_menu;
// Display the menu title
DSPrint(15-(strlen(menu->title)/2), menu->start_row, 6, menu->title);
// And display all of the menu items
while (menu->menulist[main_menu_items].menu_action != MENU_ACTION_END)
{
DSPrint(16-(strlen(menu->menulist[main_menu_items].menu_string)/2), menu->start_row+2+main_menu_items, (main_menu_items == sel) ? 7:6, menu->menulist[main_menu_items].menu_string);
main_menu_items++;
}
}
static char theDrivePath[256];
void check_and_make_sav_directory(void)
{
// Init filename = romname and SAV in place of ROM
DIR* dir = opendir("sav");
if (dir) closedir(dir); // Directory exists... close it out and move on.
else mkdir("sav", 0777); // Otherwise create the directory...
}
// ------------------------------------------------------------------------
// Handle Main Menu interface...
// ------------------------------------------------------------------------
u8 MainMenu(C64 *the_c64)
{
u8 menuSelection = 0;
u8 retVal = 0;
while ((keysCurrent() & (KEY_TOUCH | KEY_LEFT | KEY_RIGHT | KEY_A ))!=0);
// ------------------------------------------------------------------
//Show the cassette menu background - we'll draw text on top of this
// ------------------------------------------------------------------
MainMenuShow(true, menuSelection);
u8 bExitMenu = false;
while (true)
{
nds_key = keysCurrent();
if (nds_key)
{
if (nds_key & KEY_UP)
{
menuSelection = (menuSelection > 0) ? (menuSelection-1):(main_menu_items-1);
while (menu->menulist[menuSelection].menu_action == MENU_ACTION_SKIP)
{
menuSelection = (menuSelection > 0) ? (menuSelection-1):(main_menu_items-1);
}
MainMenuShow(false, menuSelection);
}
if (nds_key & KEY_DOWN)
{
menuSelection = (menuSelection+1) % main_menu_items;
while (menu->menulist[menuSelection].menu_action == MENU_ACTION_SKIP)
{
menuSelection = (menuSelection+1) % main_menu_items;
}
MainMenuShow(false, menuSelection);
}
if (nds_key & KEY_B) // Treat this as selecting 'exit'
{
bExitMenu = true;
}
else if (nds_key & KEY_A) // User has picked a menu item... let's see what it is!
{
switch(menu->menulist[menuSelection].menu_action)
{
case MENU_ACTION_RESET_EMU:
the_c64->PatchKernal(ThePrefs.FastReset, ThePrefs.TrueDrive);
the_c64->Reset();
bExitMenu = true;
break;
case MENU_ACTION_CONFIG:
if (file_crc != 0x00000000)
{
u8 last_trueDrive = myConfig.trueDrive;
GimliDSGameOptions();
if (last_trueDrive != myConfig.trueDrive) // Need to reload...
{
Prefs *prefs = new Prefs(ThePrefs);
prefs->TrueDrive = myConfig.trueDrive;
the_c64->NewPrefs(prefs);
ThePrefs = *prefs;
delete prefs;
}
bExitMenu = true;
}
else
{
DSPrint(0, 18, 6, (char*)" NO GAME IS LOADED ");
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
DSPrint(0, 18, 6, (char*)" ");
}
break;
case MENU_ACTION_SAVE_STATE:
{
check_and_make_sav_directory();
sprintf(theDrivePath,"sav/%s", ThePrefs.DrivePath[0]);
int len = strlen(theDrivePath);
if (len > 4)
{
char *p=&theDrivePath[len-4];
strcpy(p,".GSS");
}
if (the_c64->SaveSnapshot(theDrivePath) == false)
{
DSPrint(0, 18, 6, (char*)" UNABLE TO SAVE STATE ");
}
else
{
DSPrint(0, 18, 6, (char*)" .GSS SNAPSHOT SAVED ");
}
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
DSPrint(0, 18, 6, (char*)" ");
bExitMenu = true;
}
break;
case MENU_ACTION_LOAD_STATE:
{
check_and_make_sav_directory();
sprintf(theDrivePath,"sav/%s", ThePrefs.DrivePath[0]);
int len = strlen(theDrivePath);
if (len > 4)
{
char *p=&theDrivePath[len-4];
strcpy(p,".GSS");
}
if (the_c64->LoadSnapshot(theDrivePath) == false)
{
DSPrint(0, 18, 6, (char*)" NO VALID SNAPSHOT FOUND ");
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
DSPrint(0, 18, 6, (char*)" ");
}
bExitMenu = true;
}
break;
case MENU_ACTION_EXIT:
bExitMenu = true;
break;
}
}
if (bExitMenu) break;
while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0);
WAITVBL;WAITVBL;WAITVBL;
}
}
while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0);
WAITVBL;WAITVBL;WAITVBL;
return retVal;
}
// zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
#define CRC32_POLY 0x04C11DB7
const u32 crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, // 0 [0x00 .. 0x07]
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, // 8 [0x08 .. 0x0F]
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, // 16 [0x10 .. 0x17]
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, // 24 [0x18 .. 0x1F]
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, // 32 [0x20 .. 0x27]
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, // 40 [0x28 .. 0x2F]
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, // 48 [0x30 .. 0x37]
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, // 56 [0x38 .. 0x3F]
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, // 64 [0x40 .. 0x47]
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, // 72 [0x48 .. 0x4F]
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, // 80 [0x50 .. 0x57]
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, // 88 [0x58 .. 0x5F]
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, // 96 [0x60 .. 0x67]
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, // 104 [0x68 .. 0x6F]
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, // 112 [0x70 .. 0x77]
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, // 120 [0x78 .. 0x7F]
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, // 128 [0x80 .. 0x87]
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, // 136 [0x88 .. 0x8F]
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, // 144 [0x90 .. 0x97]
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, // 152 [0x98 .. 0x9F]
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, // 160 [0xA0 .. 0xA7]
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, // 168 [0xA8 .. 0xAF]
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, // 176 [0xB0 .. 0xB7]
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, // 184 [0xB8 .. 0xBF]
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, // 192 [0xC0 .. 0xC7]
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, // 200 [0xC8 .. 0xCF]
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, // 208 [0xD0 .. 0xD7]
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, // 216 [0xD8 .. 0xDF]
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, // 224 [0xE0 .. 0xE7]
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, // 232 [0xE8 .. 0xEF]
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, // 240 [0xF0 .. 0xF7]
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, // 248 [0xF8 .. 0xFF]
};
// --------------------------------------------------
// Compute the CRC of a memory buffer of any size...
// --------------------------------------------------
u32 getCRC32(u8 *buf, int size)
{
u32 crc = 0xFFFFFFFF;
for (int i=0; i < size; i++)
{
crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ (u8)buf[i]];
}
return ~crc;
}
extern char strBuf[];
u32 file_crc = 0x00000000;
u8 option_table=0;
struct Config_t AllConfigs[MAX_CONFIGS];
struct Config_t myConfig __attribute((aligned(4))) __attribute__((section(".dtcm")));
void SetDefaultGameConfig(void)
{
myConfig.game_crc = 0; // No game in this slot yet
myConfig.key_A = 0;
myConfig.key_B = 1;
myConfig.key_Y = 2;
myConfig.key_X = 3;
myConfig.autoFire = 0;
myConfig.trueDrive = 0;
myConfig.jitter = 1; // Medium
myConfig.diskSFX = 1; // Disk sound effects on
myConfig.reserved1 = 0;
myConfig.reserved2 = 0;
myConfig.reserved3 = 0;
myConfig.reserved4 = 0;
myConfig.reserved5 = 0;
myConfig.reserved6 = 0xA5; // So it's easy to spot on an "upgrade" and we can re-default it
myConfig.cpuCycles = 0; // Normal 63 - this is the adjustment to that
myConfig.badCycles = 0; // Normal 23 - this is the adjustment to that
myConfig.offsetX = 32;
myConfig.offsetY = 35;
myConfig.scaleX = 256;
myConfig.scaleY = 200;
}
s16 CycleDeltas[] = {0,1,2,3,4,5,6,7,8,9,-9,-8,-7,-6,-5,-4,-3,-2,-1};
// ----------------------------------------------------------------------
// Read file twice and ensure we get the same CRC... if not, do it again
// until we get a clean read. Return the filesize to the caller...
// ----------------------------------------------------------------------
u32 ReadFileCarefully(char *filename, u8 *buf, u32 buf_size, u32 buf_offset)
{
u32 crc1 = 0;
u32 crc2 = 1;
u32 fileSize = 0;
// --------------------------------------------------------------------------------------------
// I've seen some rare issues with reading files from the SD card on a DSi so we're doing
// this slow and careful - we will read twice and ensure that we get the same CRC both times.
// --------------------------------------------------------------------------------------------
do
{
// Read #1
crc1 = 0xFFFFFFFF;
FILE* file = fopen(filename, "rb");
if (file)
{
if (buf_offset) fseek(file, buf_offset, SEEK_SET);
fileSize = fread(buf, 1, buf_size, file);
crc1 = getCRC32(buf, buf_size);
fclose(file);
}
// Read #2
crc2 = 0xFFFFFFFF;
FILE* file2 = fopen(filename, "rb");
if (file2)
{
if (buf_offset) fseek(file2, buf_offset, SEEK_SET);
fread(buf, 1, buf_size, file2);
crc2 = getCRC32(buf, buf_size);
fclose(file2);
}
} while (crc1 != crc2); // If the file couldn't be read, file_size will be 0 and the CRCs will both be 0xFFFFFFFF
return fileSize;
}
// ---------------------------------------------------------------------------
// Write out the GimliDS.DAT configuration file to capture the settings for
// each game. This one file contains global settings ~1000 game settings.
// ---------------------------------------------------------------------------
void SaveConfig(bool bShow)
{
FILE *fp;
int slot = 0;
if (bShow) DSPrint(6,23,0, (char*)"SAVING CONFIGURATION");
// If there is a game loaded, save that into a slot... re-use the same slot if it exists
myConfig.game_crc = file_crc;
// Find the slot we should save into...
for (slot=0; slot<MAX_CONFIGS; slot++)
{
if (AllConfigs[slot].game_crc == myConfig.game_crc) // Got a match?!
{
break;
}
if (AllConfigs[slot].game_crc == 0x00000000) // Didn't find it... use a blank slot...
{
break;
}
}
// --------------------------------------------------------------------------
// Copy our current game configuration to the main configuration database...
// --------------------------------------------------------------------------
if (myConfig.game_crc != 0x00000000)
{
memcpy(&AllConfigs[slot], &myConfig, sizeof(struct Config_t));
}
// --------------------------------------------------
// Now save the config file out to the SD card...
// --------------------------------------------------
DIR* dir = opendir("/data");
if (dir)
{
closedir(dir); // directory exists.
}
else
{
mkdir("/data", 0777); // Doesn't exist - make it...
}
fp = fopen("/data/GimliDS.DAT", "wb+");
if (fp != NULL)
{
u16 ver = CONFIG_VERSION;
fwrite(&ver, sizeof(ver), 1, fp); // Write the config version
fwrite(&AllConfigs, sizeof(AllConfigs), 1, fp); // Write the array of all configurations
fclose(fp);
} else DSPrint(4,23,0, (char*)"ERROR SAVING CONFIG FILE");
if (bShow)
{
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
DSPrint(4,23,0, (char*)" ");
}
}
// ----------------------------------------------------------
// Load configuration into memory where we can use it.
// The configuration is stored in GimliDS.DAT
// ----------------------------------------------------------
void LoadConfig(void)
{
// -----------------------------------------------------------------
// Start with defaults.. if we find a match in our config database
// below, we will fill in the config with data read from the file.
// -----------------------------------------------------------------
SetDefaultGameConfig();
u16 ver = 0x0000;
if (ReadFileCarefully((char *)"/data/GimliDS.DAT", (u8*)&ver, sizeof(ver), 0)) // Read Global Config
{
ReadFileCarefully((char *)"/data/GimliDS.DAT", (u8*)&AllConfigs, sizeof(AllConfigs), sizeof(ver)); // Read the full game array of configs
if (ver != CONFIG_VERSION)
{
memset(&AllConfigs, 0x00, sizeof(AllConfigs));
SetDefaultGameConfig();
SaveConfig(FALSE);
}
}
else // Not found... init the entire database...
{
memset(&AllConfigs, 0x00, sizeof(AllConfigs));
SetDefaultGameConfig();
SaveConfig(FALSE);
}
}
// -------------------------------------------------------------------------
// Try to match our loaded game to a configuration my matching CRCs
// -------------------------------------------------------------------------
void FindConfig(void)
{
// -----------------------------------------------------------------
// Start with defaults.. if we find a match in our config database
// below, we will fill in the config with data read from the file.
// -----------------------------------------------------------------
SetDefaultGameConfig();
for (u16 slot=0; slot<MAX_CONFIGS; slot++)
{
if (AllConfigs[slot].game_crc == file_crc) // Got a match?!
{
memcpy(&myConfig, &AllConfigs[slot], sizeof(struct Config_t));
break;
}
}
}
// ------------------------------------------------------------------------------
// Options are handled here... we have a number of things the user can tweak
// and these options are applied immediately. The user can also save off
// their option choices for the currently running game into the NINTV-DS.DAT
// configuration database. When games are loaded back up, NINTV-DS.DAT is read
// to see if we have a match and the user settings can be restored for the game.
// ------------------------------------------------------------------------------
struct options_t
{
const char *label;
const char *option[20];
u8 *option_val;
u8 option_max;
};
#define CYCLE_DELTA_STR "+0","+1","+2","+3","+4","+5","+6","+7","+8","+9","-9","-8","-7","-6","-5","-4","-3","-2","-1",
const struct options_t Option_Table[1][20] =
{
// Game Specific Configuration
{
{"TRUE DRIVE", {"DISABLE (FAST)", "ENABLED (SLOW)"}, &myConfig.trueDrive, 2},
{"AUTO FIRE", {"OFF", "ON"}, &myConfig.autoFire, 2},
{"LCD JITTER", {"NONE", "LIGHT", "HEAVY"}, &myConfig.jitter, 3},
{"DISK SOUND", {"SFX OFF", "SFX ON"}, &myConfig.diskSFX, 2},
{"A BUTTON", {"JOY FIRE", "SPACE", "RETURN", "JOY UP", "JOY DOWN", "PAN-UP", "PAN-DOWN"}, &myConfig.key_A, 7},
{"B BUTTON", {"JOY FIRE", "SPACE", "RETURN", "JOY UP", "JOY DOWN", "PAN-UP", "PAN-DOWN"}, &myConfig.key_B, 7},
{"X BUTTON", {"JOY FIRE", "SPACE", "RETURN", "JOY UP", "JOY DOWN", "PAN-UP", "PAN-DOWN"}, &myConfig.key_X, 7},
{"Y BUTTON", {"JOY FIRE", "SPACE", "RETURN", "JOY UP", "JOY DOWN", "PAN-UP", "PAN-DOWN"}, &myConfig.key_Y, 7},
{"CPU CYCLES", {CYCLE_DELTA_STR}, &myConfig.cpuCycles, 19},
{"BAD CYCLES", {CYCLE_DELTA_STR}, &myConfig.badCycles, 19},
{NULL, {"", ""}, NULL, 1},
}
};
// ------------------------------------------------------------------
// Display the current list of options for the user.
// ------------------------------------------------------------------
u8 display_options_list(bool bFullDisplay)
{
s16 len=0;
DSPrint(1,21, 0, (char *)" ");
if (bFullDisplay)
{
while (true)
{
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][len].label, Option_Table[option_table][len].option[*(Option_Table[option_table][len].option_val)]);
DSPrint(1,5+len, (len==0 ? 2:0), strBuf); len++;
if (Option_Table[option_table][len].label == NULL) break;
}
// Blank out rest of the screen... option menus are of different lengths...
for (int i=len; i<16; i++)
{
DSPrint(1,5+i, 0, (char *)" ");
}
}
DSPrint(1,22, 0, (char *)" A or B=EXIT, START=SAVE ");
return len;
}
//*****************************************************************************
// Change Game Options for the current game
//*****************************************************************************
void GimliDSGameOptions(void)
{
u8 optionHighlighted;
u8 idx;
bool bDone=false;
int keys_pressed;
int last_keys_pressed = 999;
option_table = 0;
idx=display_options_list(true);
optionHighlighted = 0;
while (keysCurrent() != 0)
{
WAITVBL;
}
while (!bDone)
{
keys_pressed = keysCurrent();
if (keys_pressed != last_keys_pressed)
{
last_keys_pressed = keys_pressed;
if (keysCurrent() & KEY_UP) // Previous option
{
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][optionHighlighted].label, Option_Table[option_table][optionHighlighted].option[*(Option_Table[option_table][optionHighlighted].option_val)]);
DSPrint(1,5+optionHighlighted,0, strBuf);
if (optionHighlighted > 0) optionHighlighted--; else optionHighlighted=(idx-1);
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][optionHighlighted].label, Option_Table[option_table][optionHighlighted].option[*(Option_Table[option_table][optionHighlighted].option_val)]);
DSPrint(1,5+optionHighlighted,2, strBuf);
}
if (keysCurrent() & KEY_DOWN) // Next option
{
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][optionHighlighted].label, Option_Table[option_table][optionHighlighted].option[*(Option_Table[option_table][optionHighlighted].option_val)]);
DSPrint(1,5+optionHighlighted,0, strBuf);
if (optionHighlighted < (idx-1)) optionHighlighted++; else optionHighlighted=0;
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][optionHighlighted].label, Option_Table[option_table][optionHighlighted].option[*(Option_Table[option_table][optionHighlighted].option_val)]);
DSPrint(1,5+optionHighlighted,2, strBuf);
}
if (keysCurrent() & KEY_RIGHT) // Toggle option clockwise
{
*(Option_Table[option_table][optionHighlighted].option_val) = (*(Option_Table[option_table][optionHighlighted].option_val) + 1) % Option_Table[option_table][optionHighlighted].option_max;
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][optionHighlighted].label, Option_Table[option_table][optionHighlighted].option[*(Option_Table[option_table][optionHighlighted].option_val)]);
DSPrint(1,5+optionHighlighted,2, strBuf);
}
if (keysCurrent() & KEY_LEFT) // Toggle option counterclockwise
{
if ((*(Option_Table[option_table][optionHighlighted].option_val)) == 0)
*(Option_Table[option_table][optionHighlighted].option_val) = Option_Table[option_table][optionHighlighted].option_max -1;
else
*(Option_Table[option_table][optionHighlighted].option_val) = (*(Option_Table[option_table][optionHighlighted].option_val) - 1) % Option_Table[option_table][optionHighlighted].option_max;
sprintf(strBuf, " %-12s : %-14s", Option_Table[option_table][optionHighlighted].label, Option_Table[option_table][optionHighlighted].option[*(Option_Table[option_table][optionHighlighted].option_val)]);
DSPrint(1,5+optionHighlighted,2, strBuf);
}
if (keysCurrent() & KEY_START) // Save Options
{
SaveConfig(TRUE);
}
if ((keysCurrent() & KEY_B) || (keysCurrent() & KEY_A)) // Exit options
{
option_table = 0; // Reset for next time
break;
}
}
swiWaitForVBlank();
}
// Give a third of a second time delay...
for (int i=0; i<20; i++)
{
swiWaitForVBlank();
}
return;
}

48
arm9/source/mainmenu.h Normal file
View File

@ -0,0 +1,48 @@
#define MAX_CONFIGS 1150
#define CONFIG_VERSION 0x0004
extern s16 CycleDeltas[];
struct __attribute__((__packed__)) Config_t
{
u32 game_crc;
u8 key_A;
u8 key_B;
u8 key_X;
u8 key_Y;
u8 trueDrive;
u8 autoFire;
u8 jitter;
u8 diskSFX;
u8 reserved1;
u8 reserved2;
u8 reserved3;
u8 reserved4;
u8 reserved5;
u8 reserved6;
u8 cpuCycles;
u8 badCycles;
s16 offsetX;
s16 offsetY;
s16 scaleX;
s16 scaleY;
};
extern struct Config_t myConfig;
#define KEY_MAP_JOY_FIRE 0
#define KEY_MAP_SPACE 1
#define KEY_MAP_RETURN 2
#define KEY_MAP_JOY_UP 3
#define KEY_MAP_JOY_DN 4
#define KEY_MAP_PAN_UP 5
#define KEY_MAP_PAN_DN 6
extern u32 getCRC32(u8 *buf, int size);
extern u32 file_crc;
void LoadConfig(void);
void SaveConfig(void);
void FindConfig(void);
void GimliDSGameOptions(void);

6
arm9/source/soundbank.h Normal file
View File

@ -0,0 +1,6 @@
#define SFX_FLOPPY 0
#define SFX_KEYCLICK 1
#define SFX_MUS_INTRO 2
#define MSL_NSONGS 0
#define MSL_NSAMPS 3
#define MSL_BANKSIZE 3

129
arm9/source/sysconfig.h Normal file
View File

@ -0,0 +1,129 @@
/* sysconfig.h.in. Generated from configure.in by autoheader. */
/* Define if you have the <dirent.h> header file, and it defines `DIR'. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 1
/* Define if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define if you have the `mkdir' function. */
#define HAVE_MKDIR 1
/* Define if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define if you have the `rmdir' function. */
#define HAVE_RMDIR 1
/* Define if you have the `select' function. */
#undef HAVE_SELECT
/* Define if you have the `statfs' function. */
#undef HAVE_STATFS
/* Define if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define if you have the `strstr' function. */
#define HAVE_STRSTR 1
/* Define if `st_blocks' is member of `struct stat'. */
#define HAVE_STRUCT_STAT_ST_BLOCKS 1
/* Define if you have the <sys/dir.h> header file, and it defines `DIR'. */
#define HAVE_SYS_DIR_H 1
/* Define if you have the <sys/mount.h> header file. */
#undef HAVE_SYS_MOUNT_H
/* Define if you have the <sys/ndir.h> header file, and it defines `DIR'. */
#undef HAVE_SYS_NDIR_H
/* Define if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define if you have the <sys/select.h> header file. */
#define HAVE_SYS_SELECT_H 1
/* Define if you have the <sys/statfs.h> header file. */
#undef HAVE_SYS_STATFS_H
/* Define if you have the <sys/statvfs.h> header file. */
#define HAVE_SYS_STATVFS_H 1
/* Define if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define if you have the <sys/vfs.h> header file. */
#undef HAVE_SYS_VFS_H
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if you have the `usleep' function. */
#undef HAVE_USLEEP
/* Define if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
/* Define if `utime(file, NULL)' sets file's timestamp to the present. */
#undef HAVE_UTIME_NULL
/* Define if you have the <values.h> header file. */
#undef HAVE_VALUES_H
/* The size of a `char', as computed by sizeof. */
#define SIZEOF_CHAR 1
/* The size of a `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* The size of a `long', as computed by sizeof. */
#define SIZEOF_LONG 4
/* The size of a `long long', as computed by sizeof. */
#define SIZEOF_LONG_LONG 8
/* The size of a `short', as computed by sizeof. */
#define SIZEOF_SHORT 2
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
// Use iprintf/iscanf on NDS to save ~50 KB
#define sscanf siscanf
#define printf iprintf
#define fprintf fiprintf
#define sprintf siprintf
#define snprintf sniprintf
#define vsnprintf vsniprintf

200
arm9/source/sysdeps.h Normal file
View File

@ -0,0 +1,200 @@
/*
* sysdeps.h - Try to include the right system headers and get other
* system-specific stuff right
*
* Frodo Copyright (C) Christian Bauer
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysconfig.h"
#include <stdio.h>
extern int debug[];
//
//#ifdef EMU
//#include <kos/kosstdio.h>
//#else
//#include "gba_nds_fat/gba_nds_fat.h"
//#endif
//#include <nds/jtypes.h>
//#include <stdio.h>
//
//#undef FILE
//#undef DIR
//#undef size_t
//#undef fopen
//#undef fread
//#undef fwrite
//#undef fclose
//#undef ftell
//#undef rewind
//#undef fseek
//#undef chdir
//#undef ftell
//#undef getc
//#undef tmpfile
//#undef rewind
//#undef putc
//#undef fputc
//#undef fscanf
//#undef feof
//#undef stdin
//#undef stdout
//#undef fflush
//
//#ifdef EMU
//#define FILE KOS_FILE
//#define fopen KOS_fopen
//#define fwrite KOS_fwrite
//#define fread KOS_fread
//#define fclose KOS_fclose
//#define fseek KOS_fseek
//#define fputs KOS_fputs
//#define fgets KOS_fgets
//#define fputc KOS_fputc
//#define getc KOS_getc
//#define putc KOS_putc
//#define fgetc KOS_fgetc
//#define fprintf KOS_fprintf
//#define vfprintf KOS_fprintf
//#define ftell KOS_ftell
//#define tmpfile KOS_tmpfile
//#define rewind KOS_rewind
//#define feof KOS_feof
//#define stdin KOS_stdin
//#define stdout KOS_stdout
//#define fscanf KOS_fscanf
//#define fflush KOS_fflush
//#else
//#define FILE FAT_FILE
//#define fopen FAT_fopen
//#define fwrite FAT_fwrite
//#define fread FAT_fread
//#define fclose FAT_fclose
//#define fseek FAT_fseek
//#define fputs FAT_fputs
//#define fgets FAT_fgets
//#define fputc FAT_fputc
//#define getc FAT_getc
//#define putc FAT_putc
//#define fgetc FAT_fgetc
//#define fprintf FAT_fprintf
//#define vfprintf FAT_fprintf
//#define ftell FAT_ftell
//#define tmpfile FAT_tmpfile
//#define rewind FAT_rewind
//#define feof FAT_feof
//#define stdin FAT_stdin
//#define stdout FAT_stdout
//#define fscanf FAT_fscanf
//#define fflush FAT_fflush
//#endif
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <vector>
using std::vector;
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_VALUES_H
#include <values.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#ifdef HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
#ifdef HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include <nds.h>
#include <assert.h>
#if EEXIST == ENOTEMPTY
#define BROKEN_OS_PROBABLY_AIX
#endif