mirror of
https://github.com/wavemotion-dave/GimliDS.git
synced 2025-06-18 13:55:32 -04:00
Add files via upload
This commit is contained in:
commit
71fbc3a2ca
BIN
C64_icon.bmp
Normal file
BIN
C64_icon.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 630 B |
47
Makefile
Normal file
47
Makefile
Normal 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
130
arm7/Makefile
Normal 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
106
arm7/source/arm7main.c
Normal 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
148
arm9/Makefile
Normal 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
BIN
arm9/data/floppy.wav
Normal file
Binary file not shown.
BIN
arm9/data/keyclick.wav
Normal file
BIN
arm9/data/keyclick.wav
Normal file
Binary file not shown.
BIN
arm9/data/mus_intro.wav
Normal file
BIN
arm9/data/mus_intro.wav
Normal file
Binary file not shown.
BIN
arm9/data/soundbank.bin
Normal file
BIN
arm9/data/soundbank.bin
Normal file
Binary file not shown.
BIN
arm9/gfx_data/diskmenu_bg.png
Normal file
BIN
arm9/gfx_data/diskmenu_bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
arm9/gfx_data/intro.png
Normal file
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
BIN
arm9/gfx_data/keyboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
arm9/gfx_data/mainmenu_bg.png
Normal file
BIN
arm9/gfx_data/mainmenu_bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
2176
arm9/source/1541d64.cpp
Normal file
2176
arm9/source/1541d64.cpp
Normal file
File diff suppressed because it is too large
Load Diff
170
arm9/source/1541d64.h
Normal file
170
arm9/source/1541d64.h
Normal 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 §or, 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 §or, 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
506
arm9/source/1541fs.cpp
Normal 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
74
arm9/source/1541fs.h
Normal 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
493
arm9/source/1541job.cpp
Normal 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
151
arm9/source/1541job.h
Normal 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
1203
arm9/source/C64.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
arm9/source/C64.h
Normal file
110
arm9/source/C64.h
Normal 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
595
arm9/source/CIA.cpp
Normal 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
199
arm9/source/CIA.h
Normal 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
756
arm9/source/CPU1541.cpp
Normal 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
288
arm9/source/CPU1541.h
Normal 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
640
arm9/source/CPUC64.cpp
Normal 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
190
arm9/source/CPUC64.h
Normal 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
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
870
arm9/source/Display.cpp
Normal 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
76
arm9/source/Display.h
Normal 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
433
arm9/source/FixPoint.h
Normal 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
989
arm9/source/IEC.cpp
Normal 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
275
arm9/source/IEC.h
Normal 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
53
arm9/source/Prefs.cpp
Normal 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
58
arm9/source/Prefs.h
Normal 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
1331
arm9/source/SID.cpp
Normal file
File diff suppressed because it is too large
Load Diff
178
arm9/source/SID.h
Normal file
178
arm9/source/SID.h
Normal 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
1675
arm9/source/VIC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
250
arm9/source/VIC.h
Normal file
250
arm9/source/VIC.h
Normal 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
686
arm9/source/diskmenu.cpp
Normal 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
21
arm9/source/diskmenu.h
Normal 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
2324
arm9/source/lzav.h
Normal file
File diff suppressed because it is too large
Load Diff
283
arm9/source/main.cpp
Normal file
283
arm9/source/main.cpp
Normal 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
58
arm9/source/main.h
Normal 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
661
arm9/source/mainmenu.cpp
Normal 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
48
arm9/source/mainmenu.h
Normal 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
6
arm9/source/soundbank.h
Normal 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
129
arm9/source/sysconfig.h
Normal 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
200
arm9/source/sysdeps.h
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user