mirror of https://github.com/LemonBoy/grape.git
first commit
This commit is contained in:
commit
33af1795f4
|
@ -0,0 +1,7 @@
|
|||
*.txt
|
||||
*.py
|
||||
*.pl
|
||||
*.nds
|
||||
*.elf
|
||||
fs/
|
||||
build/
|
|
@ -0,0 +1,134 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# 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
|
||||
# MAXMOD_SOUNDBANK contains a directory of music and sound effect files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(shell basename $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA :=
|
||||
INCLUDES := include
|
||||
NITRODATA := fs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -marm -mthumb-interwork -march=armv5te -mtune=arm946e-s
|
||||
|
||||
CFLAGS := -save-temps \
|
||||
-funroll-loops -ftree-vectorize \
|
||||
-Wall -Ofast\
|
||||
-fomit-frame-pointer\
|
||||
-ffast-math \
|
||||
$(ARCH)
|
||||
#-finstrument-functions -mpoke-function-name \
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM9 -DEMU_BUILD
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := $(ARCH)
|
||||
LDFLAGS = -specs=ds_arm9.specs $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project (order is important)
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lfilesystem -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 OUTPUT := $(CURDIR)/$(TARGET)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
ifneq ($(strip $(NITRODATA)),)
|
||||
export NITRO_FILES := $(CURDIR)/$(NITRODATA)
|
||||
endif
|
||||
|
||||
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)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
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) \
|
||||
$(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) $(TARGET).elf $(TARGET).nds
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).nds : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
$(bin2o)
|
||||
|
||||
-include $(DEPSDIR)/*.d
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
|
@ -0,0 +1,24 @@
|
|||
grape
|
||||
=====
|
||||
|
||||
An Apple II emulator for Nintendo DS, powered by devkitPRO, libnds and lots of
|
||||
sweat. This is how I spent most of my summer nights when I couldn't sleep
|
||||
because of the heat, debugging the asm core and reading the Apple II manuals.
|
||||
|
||||
|
||||
What is emulated
|
||||
================
|
||||
* Apple II/II+
|
||||
* Language card
|
||||
* 5.25 floppy disk
|
||||
* Speaker
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Consider it as a WIP, it lacks some stuff like configurable keymaps and
|
||||
extensive testing.
|
||||
The video generation takes ~50% of the CPU time and would definitely benefit
|
||||
from optimization, beside the fact that it doesn't emulate color bleeding.
|
||||
When playing games that make extensive use of disk IO disable vblank to make it
|
||||
load faster (eg. Karateka and Pacman)
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
int cpu_run (int run_cycles);
|
||||
void cpu_reset ();
|
||||
void cpu_nmi ();
|
||||
extern u32 cpu_regs[6];
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,278 @@
|
|||
#include <nds.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "emu.h"
|
||||
|
||||
// Working buffer
|
||||
static u8 diskbuf[35*6656];
|
||||
|
||||
static int Q6, Q7;
|
||||
static int motor_on;
|
||||
static int phase;
|
||||
static int last_stepper;
|
||||
static int track_start, track_pos;
|
||||
static int sel_disk;
|
||||
|
||||
void disk_save (FILE *f)
|
||||
{
|
||||
fwrite(&Q6, 1, 4, f);
|
||||
fwrite(&Q7, 1, 4, f);
|
||||
fwrite(&motor_on, 1, 4, f);
|
||||
fwrite(&phase, 1, 4, f);
|
||||
fwrite(&last_stepper, 1, 4, f);
|
||||
fwrite(&track_start, 1, 4, f);
|
||||
fwrite(&track_pos, 1, 4, f);
|
||||
fwrite(&sel_disk, 1, 4, f);
|
||||
}
|
||||
|
||||
void disk_load (FILE *f)
|
||||
{
|
||||
fread(&Q6, 1, 4, f);
|
||||
fread(&Q7, 1, 4, f);
|
||||
fread(&motor_on, 1, 4, f);
|
||||
fread(&phase, 1, 4, f);
|
||||
fread(&last_stepper, 1, 4, f);
|
||||
fread(&track_start, 1, 4, f);
|
||||
fread(&track_pos, 1, 4, f);
|
||||
fread(&sel_disk, 1, 4, f);
|
||||
}
|
||||
|
||||
u8 disk_io_read (u16 addr) ITCM_CODE;
|
||||
u8 disk_io_read (u16 addr)
|
||||
{
|
||||
int set;
|
||||
int stepper;
|
||||
int delta;
|
||||
|
||||
set = addr&1;
|
||||
stepper = (addr>>1)&3;
|
||||
|
||||
switch (addr&0xf) {
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
case 0x5:
|
||||
case 0x7:
|
||||
if (motor_on) {
|
||||
delta = 0;
|
||||
if (stepper == ((last_stepper + 1)&3))
|
||||
delta += 1;
|
||||
if (stepper == ((last_stepper + 3)&3))
|
||||
delta -= 1;
|
||||
last_stepper = stepper;
|
||||
if (delta) {
|
||||
phase += delta;
|
||||
if (phase < 0)
|
||||
phase = 0;
|
||||
if (phase > 70)
|
||||
phase = 70;
|
||||
/*track_pos = 0;*/
|
||||
track_start = (phase >> 1) * 6656;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
case 0x6:
|
||||
return 0xff;
|
||||
// ENABLE
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
motor_on = set;
|
||||
return 0xff;
|
||||
// SELECT
|
||||
case 0xa:
|
||||
case 0xb:
|
||||
sel_disk = set;
|
||||
return 0xff;
|
||||
// Q6L
|
||||
case 0xc:
|
||||
Q6 = 0;
|
||||
if (!Q7) {
|
||||
// The tracks are circular, so wrap around
|
||||
if (track_pos >= 6656) track_pos = 0;
|
||||
return diskbuf[track_start + (track_pos++)];
|
||||
}
|
||||
// Handshake register
|
||||
return 0x80;
|
||||
// Q6H
|
||||
case 0xd:
|
||||
Q6 = 1;
|
||||
return 0;
|
||||
// Q7
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
Q7 = set;
|
||||
// The status register is read from Q7L
|
||||
return 0x20|0x80; // Write protect and ready
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
int load_nib_disk (const char *file)
|
||||
{
|
||||
FILE *f;
|
||||
size_t size;
|
||||
|
||||
f = fopen(file, "rb");
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (size != 232960) {
|
||||
iprintf("Size mismatch\n");
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
fread(diskbuf, 1, 232960, f);
|
||||
fclose(f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DOS 3.3 format 35 T 16 S (343 nybbles)
|
||||
// http://www.umich.edu/~archive/apple2/misc/hardware/disk.encoding.txt
|
||||
// http://mirrors.apple2.org.za/ground.icaen.uiowa.edu/MiscInfo/Programming/c600.disasm
|
||||
// http://www.mac.linux-m68k.org/devel/iwm.php
|
||||
// http://www.textfiles.com/apple/ANATOMY/t.dos.b800.bcff.txt
|
||||
|
||||
const u8 dos33table[] = {
|
||||
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
|
||||
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
|
||||
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
|
||||
0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
|
||||
0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
|
||||
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
|
||||
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
||||
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
|
||||
};
|
||||
const int dos_order[] = {
|
||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
|
||||
};
|
||||
const int prodos_order[] = {
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF
|
||||
};
|
||||
|
||||
void write_address (u8 *b, u8 track, u8 sector, u8 volume)
|
||||
{
|
||||
// Handy macros for 4 and 4 encoding
|
||||
#define X1(x) (((x)>>1)|0xAA)
|
||||
#define X2(x) ((x)|0xAA)
|
||||
b[0] = 0xD5;
|
||||
b[1] = 0xAA;
|
||||
b[2] = 0x96;
|
||||
b[3] = X1(volume);
|
||||
b[4] = X2(volume);
|
||||
b[5] = X1(track);
|
||||
b[6] = X2(track);
|
||||
b[7] = X1(sector);
|
||||
b[8] = X2(sector);
|
||||
b[9] = X1(volume^track^sector);
|
||||
b[10] = X2(volume^track^sector);
|
||||
b[11] = 0xDE;
|
||||
b[12] = 0xAA;
|
||||
b[13] = 0xEB;
|
||||
#undef X1
|
||||
#undef X2
|
||||
}
|
||||
|
||||
// GAP + (ADDRESS + GAP + DATA + GAP)
|
||||
#define GAP1_LEN 48
|
||||
#define GAP2_LEN 6
|
||||
#define GAP3_LEN 27
|
||||
#define ADDRESS_LEN 14 // 3 + 2 + 2 + 2 + 2 + 3
|
||||
#define DATA_LEN 349 // 3 + 342 + 1 + 3
|
||||
|
||||
int load_dsk_disk (const char *file, const int *sec_order)
|
||||
{
|
||||
FILE *f;
|
||||
size_t size;
|
||||
u8 sec_buf[256];
|
||||
u8 nib_buf[343];
|
||||
u8 *p, chksum;
|
||||
int i, j, k;
|
||||
|
||||
f = fopen(file, "rb");
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (size > 143360) {
|
||||
iprintf("Size mismatch\n");
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = diskbuf;
|
||||
|
||||
memset(p, 0xff, sizeof(diskbuf));
|
||||
|
||||
// Crunch all the tracks
|
||||
for (i = 0; i < 35; i++) {
|
||||
p += 64;
|
||||
// Crunch all the sectors
|
||||
for (j = 0; j < 16; j++) {
|
||||
fseek(f, 256 * sec_order[j] + (i * 16 * 256), SEEK_SET);
|
||||
fread(sec_buf, 1, 256, f);
|
||||
// 0xFE is the default volume
|
||||
write_address(p, i, j, 0xFE);
|
||||
p += 14;
|
||||
// Second sync gap
|
||||
p += 4;
|
||||
// Data marker
|
||||
p[0] = 0xD5; p[1] = 0xAA; p[2] = 0xAD;
|
||||
p += 3;
|
||||
// Convert to 6bit, the stashed bits start at 0
|
||||
memset(nib_buf, 0, sizeof(nib_buf));
|
||||
for (k = 0; k < 256; k++) {
|
||||
u8 v = sec_buf[k];
|
||||
nib_buf[k+86] = v >> 2;
|
||||
nib_buf[k%86] |= ((v&1) << 1 | (v&2) >> 1) << (2 * (k/86));
|
||||
}
|
||||
chksum = 0;
|
||||
for (k = 0; k < 342; k++) {
|
||||
*p++ = dos33table[chksum^nib_buf[k]];
|
||||
chksum = nib_buf[k];
|
||||
}
|
||||
// Write the checksum
|
||||
*p++ = dos33table[chksum];
|
||||
// Write the last marker
|
||||
p[0] = 0xDE; p[1] = 0xAA; p[2] = 0xEB;
|
||||
p += 3;
|
||||
p += 45;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int load_disk (const char *path)
|
||||
{
|
||||
char *ext;
|
||||
|
||||
ext = strrchr(path, '.');
|
||||
if (!ext)
|
||||
return 0;
|
||||
|
||||
if (!stricmp(ext + 1, "dsk") || !stricmp(ext + 1, "do"))
|
||||
return load_dsk_disk(path, dos_order);
|
||||
|
||||
if (!stricmp(ext + 1, "po"))
|
||||
return load_dsk_disk(path, prodos_order);
|
||||
|
||||
if (!stricmp(ext + 1, "nib"))
|
||||
return load_nib_disk(path);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef DISK_H
|
||||
#define DISK_H
|
||||
|
||||
static const u8 disk_rom[] = {
|
||||
0xA2, 0x20, 0xA0, 0x00, 0xA2, 0x03, 0x86, 0x3C, 0x8A, 0x0A, 0x24, 0x3C, 0xF0, 0x10, 0x05, 0x3C,
|
||||
0x49, 0xFF, 0x29, 0x7E, 0xB0, 0x08, 0x4A, 0xD0, 0xFB, 0x98, 0x9D, 0x56, 0x03, 0xC8, 0xE8, 0x10,
|
||||
0xE5, 0x20, 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0x0A, 0x0A, 0x0A, 0x0A, 0x85, 0x2B, 0xAA, 0xBD,
|
||||
0x8E, 0xC0, 0xBD, 0x8C, 0xC0, 0xBD, 0x8A, 0xC0, 0xBD, 0x89, 0xC0, 0xA0, 0x50, 0xBD, 0x80, 0xC0,
|
||||
0x98, 0x29, 0x03, 0x0A, 0x05, 0x2B, 0xAA, 0xBD, 0x81, 0xC0, 0xA9, 0x56, 0x20, 0xA8, 0xFC, 0x88,
|
||||
0x10, 0xEB, 0x85, 0x26, 0x85, 0x3D, 0x85, 0x41, 0xA9, 0x08, 0x85, 0x27, 0x18, 0x08, 0xBD, 0x8C,
|
||||
0xC0, 0x10, 0xFB, 0x49, 0xD5, 0xD0, 0xF7, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0xAA, 0xD0, 0xF3,
|
||||
0xEA, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0x96, 0xF0, 0x09, 0x28, 0x90, 0xDF, 0x49, 0xAD, 0xF0,
|
||||
0x25, 0xD0, 0xD9, 0xA0, 0x03, 0x85, 0x40, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0x2A, 0x85, 0x3C, 0xBD,
|
||||
0x8C, 0xC0, 0x10, 0xFB, 0x25, 0x3C, 0x88, 0xD0, 0xEC, 0x28, 0xC5, 0x3D, 0xD0, 0xBE, 0xA5, 0x40,
|
||||
0xC5, 0x41, 0xD0, 0xB8, 0xB0, 0xB7, 0xA0, 0x56, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10, 0xFB, 0x59,
|
||||
0xD6, 0x02, 0xA4, 0x3C, 0x88, 0x99, 0x00, 0x03, 0xD0, 0xEE, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10,
|
||||
0xFB, 0x59, 0xD6, 0x02, 0xA4, 0x3C, 0x91, 0x26, 0xC8, 0xD0, 0xEF, 0xBC, 0x8C, 0xC0, 0x10, 0xFB,
|
||||
0x59, 0xD6, 0x02, 0xD0, 0x87, 0xA0, 0x00, 0xA2, 0x56, 0xCA, 0x30, 0xFB, 0xB1, 0x26, 0x5E, 0x00,
|
||||
0x03, 0x2A, 0x5E, 0x00, 0x03, 0x2A, 0x91, 0x26, 0xC8, 0xD0, 0xEE, 0xE6, 0x27, 0xE6, 0x3D, 0xA5,
|
||||
0x3D, 0xCD, 0x00, 0x08, 0xA6, 0x2B, 0x90, 0xDB, 0x4C, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
int load_disk (const char *path);
|
||||
void disk_load (FILE *f);
|
||||
void disk_save (FILE *f);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,82 @@
|
|||
#include <nds.h>
|
||||
#include "emu.h"
|
||||
|
||||
static u32 frames_done;
|
||||
static u32 vblanks;
|
||||
|
||||
void irq_vblank ()
|
||||
{
|
||||
if (vblanks++ == 60) {
|
||||
consoleClear();
|
||||
iprintf("FPS %i\n", frames_done);
|
||||
// iprintf("keybd_latch : %02x\n", keybd_latch);
|
||||
frames_done = 0;
|
||||
vblanks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void emu_reset ()
|
||||
{
|
||||
sound_reset();
|
||||
mem_reset();
|
||||
cpu_reset();
|
||||
}
|
||||
|
||||
void emu_init ()
|
||||
{
|
||||
keyboardShow();
|
||||
|
||||
// Set some sane defaults
|
||||
emu_vsync = 1;
|
||||
|
||||
// Setup the video hardware
|
||||
video_init();
|
||||
|
||||
// Load the appropriate bios
|
||||
u16 crc;
|
||||
/*load_bin("APPLE2.ROM", 0xB000, 0x5000, &crc);*/
|
||||
if (load_bin("BASIC.ROM", 0xD000, 0x3000, &crc)) {
|
||||
iprintf("BIOS CRC16 %04x (%s)\n", crc, (crc == 0xAC8F) ? "Valid" : "Invalid");
|
||||
// Refuse to load a wrong bios
|
||||
if (crc != 0xAC8F)
|
||||
while (1);
|
||||
} else {
|
||||
iprintf("No BASIC.ROM found. Halting.");
|
||||
while (1);
|
||||
}
|
||||
|
||||
// Load the disk rom in place
|
||||
load_buf(disk_rom, 0xc600, 0x100);
|
||||
|
||||
basename = NULL;
|
||||
|
||||
// Used by the fps counter
|
||||
frames_done = 0;
|
||||
vblanks = 0;
|
||||
// Fire the counter
|
||||
irqSet(IRQ_VBLANK, irq_vblank);
|
||||
irqEnable(IRQ_VBLANK);
|
||||
|
||||
emu_reset();
|
||||
}
|
||||
|
||||
void emu_run ()
|
||||
{
|
||||
while (1) {
|
||||
cpu_run(17030);
|
||||
|
||||
video_draw();
|
||||
|
||||
update_input();
|
||||
frames_done++;
|
||||
|
||||
if (keysDown()&KEY_START)
|
||||
pause_menu();
|
||||
|
||||
sound_play();
|
||||
|
||||
if (emu_vsync)
|
||||
swiWaitForVBlank();
|
||||
bgUpdate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef EMU_H
|
||||
#define EMU_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "disk.h"
|
||||
#include "cpu.h"
|
||||
#include "video.h"
|
||||
#include "mem.h"
|
||||
#include "state.h"
|
||||
|
||||
#define INPUT_JSTK 0
|
||||
#define INPUT_KBD 1
|
||||
|
||||
int emu_vsync;
|
||||
int emu_input;
|
||||
|
||||
static char *basename;
|
||||
|
||||
void emu_init ();
|
||||
void emu_run ();
|
||||
void emu_reset ();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
#include <nds.h>
|
||||
#include "emu.h"
|
||||
|
||||
static u8 key_map[12] = {
|
||||
0x0d, // A
|
||||
0x20, // B
|
||||
0x80, // SELECT
|
||||
0x80, // START
|
||||
0x4C, // RIGHT
|
||||
0x4A, // LEFT
|
||||
0x4B, // UP
|
||||
0x80, // DOWN
|
||||
0x80, // R
|
||||
0x80, // L
|
||||
0x80, // X
|
||||
0x80, // Y
|
||||
};
|
||||
|
||||
void update_input () ITCM_CODE;
|
||||
void update_input ()
|
||||
{
|
||||
u32 keys;
|
||||
int kbd_key;
|
||||
|
||||
scanKeys();
|
||||
keys = keysHeld()&0xfff;
|
||||
|
||||
// Send keyboard scancodes when a key is pressed
|
||||
if (emu_input == INPUT_KBD && keys) {
|
||||
keybd_latch = 0x80 ^ key_map[__builtin_ctz(keys)];
|
||||
}
|
||||
|
||||
if (emu_input == INPUT_JSTK) {
|
||||
jstk_btn[0] = (keys&KEY_X) ? 0x80 : 0x00;
|
||||
jstk_btn[1] = (keys&KEY_Y) ? 0x80 : 0x00;
|
||||
jstk_btn[2] = (keys&KEY_B) ? 0x80 : 0x00;
|
||||
}
|
||||
|
||||
kbd_key = keyboardUpdate();
|
||||
|
||||
if (kbd_key > 0) {
|
||||
switch (keybd_latch) {
|
||||
case DVK_ENTER:
|
||||
keybd_latch = 0x8d;
|
||||
return;
|
||||
default:
|
||||
if (kbd_key >= '`') keybd_latch = 0x80 | (kbd_key - 32);
|
||||
else keybd_latch = 0x80 | kbd_key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
#include <nds.h>
|
||||
#include <filesystem.h>
|
||||
#include <fat.h>
|
||||
#include <stdio.h>
|
||||
#include "emu.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
defaultExceptionHandler();
|
||||
consoleDebugInit(DebugDevice_NOCASH);
|
||||
|
||||
consoleDemoInit();
|
||||
keyboardDemoInit();
|
||||
|
||||
fatInitDefault();
|
||||
/*nitroFSInit(NULL);*/
|
||||
|
||||
soundEnable();
|
||||
|
||||
iprintf("-- grape\n");
|
||||
|
||||
emu_init();
|
||||
|
||||
if (argc != 2) {
|
||||
// Slow loading
|
||||
/*load_disk("PACMAN.DSK");*/
|
||||
/*load_disk("Karateka (1984)(Broderbund).dsk");*/
|
||||
// Awesome!
|
||||
/*load_disk("lode.dsk");*/
|
||||
// AppleII+
|
||||
/*load_disk("Prince of Persia (1989)(Broderbund)(Disk 1 of 3)[cr].dsk");*/
|
||||
// Gfx heavy
|
||||
/*load_disk("Starglider (19xx)(Rainbird).dsk");*/
|
||||
// Undocumented OPs
|
||||
/*load_disk("Ms. Pacman (19xx)(-)[cr].dsk");*/
|
||||
/*load_disk("Round About (1983)(Datamost).dsk");*/
|
||||
/*load_disk("Bug Attack (1981)(Cavalier Computer).dsk");*/
|
||||
// Scroller
|
||||
load_disk("TetrisII.DSK");
|
||||
// Mixed mode
|
||||
/*load_disk("tetris48k.nib");*/
|
||||
// Lowres
|
||||
/*load_disk("Fracas (1980)(Quality Software).dsk");*/
|
||||
/*load_disk("Colorix.dsk");*/
|
||||
/*load_disk("LoRes Games.DSK");*/
|
||||
// SP Crash
|
||||
/*load_disk("Apple II Business Graphics 1.0 (1981).nib");*/
|
||||
} else {
|
||||
load_disk(argv[1]);
|
||||
}
|
||||
|
||||
emu_run();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef MEM_H
|
||||
#define MEM_H
|
||||
|
||||
// mem.s
|
||||
extern u8 page_dirty[64];
|
||||
extern u8 keybd_latch;
|
||||
extern u32 jstk_axis[4];
|
||||
extern u32 jstk_btn[3];
|
||||
extern u32 jstk_rst_cycle;
|
||||
extern u8 mainram[0x10000];
|
||||
extern u8 lcram[0x4000];
|
||||
extern u32 memmap_r[0x10];
|
||||
extern u32 memmap_w[0x10];
|
||||
|
||||
u8 readb (u16 addr);
|
||||
void writeb (u16 addr, u8 val);
|
||||
void mem_reset (void);
|
||||
|
||||
int load_bin (char *file, u16 addr, u16 len, u16 *crc);
|
||||
int load_buf (const u8 *buf, u16 addr, u16 len);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,255 @@
|
|||
.arm
|
||||
.section .itcm,"ax",%progbits
|
||||
|
||||
.global readb
|
||||
.global writeb
|
||||
.global mem_reset
|
||||
|
||||
.global mainram
|
||||
.global lcram
|
||||
.global jstk_btn
|
||||
.global jstk_axis
|
||||
.global memmap_w
|
||||
.global memmap_r
|
||||
.global keybd_latch
|
||||
.global jstk_rst_cycle
|
||||
.global page_dirty
|
||||
|
||||
// uint8_t readb (uint16_t addr)
|
||||
readb:
|
||||
mov r1, r0, lsr #8
|
||||
cmp r1, #0xc0
|
||||
beq 1f
|
||||
mov r1, r1, lsr #4
|
||||
ldr r2, =memmap_r
|
||||
ldr r3, [r2, r1, lsl #2]
|
||||
ldrb r0, [r3, r0]
|
||||
bx lr
|
||||
// IO Read @ 0xc000
|
||||
1: and r1, r0, #0xf0
|
||||
ldr pc, [pc, r1, lsr #2]
|
||||
nop
|
||||
io_read_tbl:
|
||||
.word io_keybd
|
||||
.word io_keybdl
|
||||
.word io_null
|
||||
.word io_spkr
|
||||
.word io_null
|
||||
.word video_io_read
|
||||
.word io_jstk
|
||||
.word io_jstk_rst
|
||||
.word io_lc
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word disk_io_read
|
||||
.word io_null
|
||||
|
||||
.ltorg
|
||||
|
||||
// void writeb (uint16_t addr, uint8_t val)
|
||||
writeb:
|
||||
mov r2, r0, lsr #8
|
||||
cmp r2, #0xc0
|
||||
// IO Read
|
||||
beq 1f
|
||||
mov r2, r2, lsr #2
|
||||
// Mark the page as dirty
|
||||
mov r3, #1
|
||||
ldr r12, =page_dirty
|
||||
strb r3, [r12, r2]
|
||||
mov r2, r2, lsr #2
|
||||
ldr r12, =memmap_w
|
||||
ldr r3, [r12, r2, lsl #2]
|
||||
strb r1, [r3, r0]
|
||||
bx lr
|
||||
1: and r1, r0, #0xf0
|
||||
ldr pc, [pc, r1, lsr #2]
|
||||
nop
|
||||
io_write_tbl:
|
||||
.word io_null
|
||||
.word io_keybdl
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word video_io_read
|
||||
.word io_jstk_rst
|
||||
.word io_null
|
||||
.word io_lc
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word io_null
|
||||
.word disk_io_read
|
||||
.word io_null
|
||||
|
||||
.ltorg
|
||||
|
||||
// IO handlers
|
||||
|
||||
io_keybd:
|
||||
ldr r2, =keybd_latch
|
||||
ldr r0, [r2]
|
||||
bx lr
|
||||
|
||||
io_keybdl:
|
||||
ldr r2, =keybd_latch
|
||||
ldr r0, [r2]
|
||||
// Clear the latch
|
||||
and r0, #0x7f
|
||||
str r0, [r2]
|
||||
bx lr
|
||||
|
||||
io_jstk:
|
||||
and r0, #0xf
|
||||
ldr r1, =(jstk_btn-4)
|
||||
cmp r0, #0x4
|
||||
ldr r0, [r1, r0, lsl #2]
|
||||
// Handle the pushbutton status
|
||||
bxlo lr
|
||||
ldr r1, [r1, #32] // jstk_rst_cycle
|
||||
// Check how many cycles have passed
|
||||
subs r2, r1, r5
|
||||
ldrmi r3, =17030
|
||||
addmi r2, r3
|
||||
// Should the paddle be sent low ?
|
||||
cmp r2, r0
|
||||
movls r0, #0x80
|
||||
movhi r0, #0x00
|
||||
bx lr
|
||||
|
||||
io_jstk_rst:
|
||||
ldr r0, =jstk_rst_cycle
|
||||
str r5, [r0]
|
||||
// Read KEYINPUT
|
||||
ldr r3, =0x4000130
|
||||
ldrh r3, [r3]
|
||||
// Find how many cycles the joystick counter takes to return to 0
|
||||
mvn r3, r3, lsr #4
|
||||
adr r2, axis_to_cycles
|
||||
and r3, #0xf
|
||||
add r2, r3, lsl #2
|
||||
ldrh r1, [r2, #0]
|
||||
str r1, [r0, #-16]
|
||||
ldrh r1, [r2, #2]
|
||||
str r1, [r0, #-12]
|
||||
bx lr
|
||||
// The pdlread routine takes 11 cycles
|
||||
axis_to_cycles:
|
||||
.short 0x7f*11,0x7f*11 //
|
||||
.short 0xff*11,0x7f*11 // R
|
||||
.short 0x00*11,0x7f*11 // L
|
||||
.short 0x00*11,0x7f*11 // R+L
|
||||
.short 0x7f*11,0x00*11 // U
|
||||
.short 0xff*11,0x00*11 // R+U
|
||||
.short 0x00*11,0x00*11 // L+U
|
||||
.short 0x00*11,0x00*11 // R+L+U
|
||||
.short 0x7f*11,0xff*11 // D
|
||||
.short 0xff*11,0xff*11 // R+D
|
||||
.short 0x00*11,0xff*11 // L+D
|
||||
.short 0x00*11,0xff*11 // R+L+D
|
||||
.short 0x7f*11,0xff*11 // U+D
|
||||
.short 0xff*11,0xff*11 // R+U+D
|
||||
.short 0x00*11,0xff*11 // L+U+D
|
||||
.short 0x00*11,0xff*11 // R+L+U+D
|
||||
|
||||
io_lc:
|
||||
ldr r12, =memmap_w
|
||||
ldr r3, =(mainram+0x4000)
|
||||
// Do it backwards so we can reuse r3
|
||||
str r3, [r12, #0xf<<2]
|
||||
str r3, [r12, #0xe<<2]
|
||||
// Select between bank 1/2
|
||||
tst r0, #8
|
||||
mov r2, r3
|
||||
subne r2, #0x1000
|
||||
str r2, [r12, #0xd<<2]
|
||||
// Check if rom is mapped on read
|
||||
ldr r1, =map_rom_tbl
|
||||
and r0, #3
|
||||
ldr r0, [r1, r0, lsl #2]
|
||||
ldr r12, =memmap_r
|
||||
tst r0, #1 // this is lame
|
||||
bne 1f // map the rom
|
||||
str r2, [r12, #0xd<<2]
|
||||
str r3, [r12, #0xe<<2]
|
||||
str r3, [r12, #0xf<<2]
|
||||
bx lr
|
||||
1: sub r3, #0x4000
|
||||
str r3, [r12, #0xd<<2]
|
||||
str r3, [r12, #0xe<<2]
|
||||
str r3, [r12, #0xf<<2]
|
||||
bx lr
|
||||
|
||||
io_spkr:
|
||||
mov r0, r5
|
||||
ldr r1, =sound_spkr_flip
|
||||
bx r1
|
||||
|
||||
io_null:
|
||||
mov r0, #0
|
||||
bx lr
|
||||
|
||||
mem_reset:
|
||||
ldr r0, =memmap_r
|
||||
ldr r1, =memmap_w
|
||||
ldr r2, =mainram
|
||||
mov r3, #0x10
|
||||
1: str r2, [r0], #4
|
||||
str r2, [r1], #4
|
||||
subs r3, #1
|
||||
bne 1b
|
||||
bx lr
|
||||
|
||||
.ltorg
|
||||
|
||||
.bss
|
||||
.align 4
|
||||
mainram:
|
||||
.space 0x10000
|
||||
lcram:
|
||||
.space 0x1000*4
|
||||
|
||||
.section .dtcm
|
||||
.align 4
|
||||
memmap_r:
|
||||
.rept 16
|
||||
.word mainram
|
||||
.endr
|
||||
|
||||
memmap_w:
|
||||
.rept 16
|
||||
.word mainram
|
||||
.endr
|
||||
|
||||
page_dirty:
|
||||
.rept 64
|
||||
.byte 0
|
||||
.endr
|
||||
|
||||
// A button is active when the 7th bit is set
|
||||
jstk_btn:
|
||||
.word 0 // PB1
|
||||
.word 0 // PB2
|
||||
.word 0 // PB3 (shift)
|
||||
// Axis are 0 <= x <= 255, where 0x80 is center
|
||||
jstk_axis:
|
||||
.word 0 // P1 X
|
||||
.word 0 // P1 Y
|
||||
.word 0 // P2 X
|
||||
.word 0 // P2 Y
|
||||
// io_jstk_rst relies on this to be after the joystick
|
||||
// data for correct indexing. Don't move.
|
||||
jstk_rst_cycle:
|
||||
.word 0
|
||||
|
||||
keybd_latch:
|
||||
.word 0
|
||||
|
||||
.section .rodata
|
||||
map_rom_tbl:
|
||||
.word 0,1,1,0
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#include <nds.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
|
||||
int load_buf (const u8 *buf, u16 addr, u16 len)
|
||||
{
|
||||
if (addr + len > 0x10000) {
|
||||
iprintf("oob\n");
|
||||
return 0;
|
||||
}
|
||||
memcpy(mainram + addr, buf, len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int load_bin (char *file, u16 addr, u16 len, u16 *crc)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
if (addr + len > 0x10000) {
|
||||
iprintf("oob\n");
|
||||
return 0;
|
||||
}
|
||||
f = fopen(file, "rb");
|
||||
if (!f) {
|
||||
iprintf("Can't open %s\n", file);
|
||||
return 0;
|
||||
}
|
||||
fread(mainram + addr, 1, len, f);
|
||||
fclose(f);
|
||||
|
||||
if (crc)
|
||||
*crc = swiCRC16(0xffff, mainram + addr, len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#include <nds.h>
|
||||
#include <stdio.h>
|
||||
#include "emu.h"
|
||||
|
||||
typedef struct entry_t {
|
||||
const char *label;
|
||||
const int opts_no;
|
||||
const char *opts[10];
|
||||
int (*cb)(int);
|
||||
} entry_t;
|
||||
|
||||
typedef struct page_t {
|
||||
const char *title;
|
||||
const int entries_no;
|
||||
const entry_t *entries;
|
||||
} page_t;
|
||||
|
||||
#define OPT(n) int opt_##n (int sel) { emu_##n = sel; return 1; }
|
||||
|
||||
OPT(vsync);
|
||||
OPT(input);
|
||||
|
||||
int opt_screen (int sel)
|
||||
{
|
||||
(sel) ? lcdMainOnBottom() : lcdMainOnTop();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const static struct page_t paused_pg = {
|
||||
"Paused", 7, (const entry_t []){
|
||||
{ "Vsync", 2, { "No", "Yes" }, opt_vsync },
|
||||
{ "Scale", 2, { "No", "Yes" }, video_set_scale },
|
||||
{ "Screen", 2, { "Top", "Bottom" }, opt_screen },
|
||||
{ "Map keys to", 2, { "joystick", "keyboard" }, opt_input },
|
||||
{ "Save state", 9, { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, state_save },
|
||||
{ "Load state", 9, { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, state_load },
|
||||
{ "Exit", 0, { }, exit },
|
||||
}
|
||||
};
|
||||
static int paused_pg_opt[6] = { 1, 0, 0, 0, 0, 0 };
|
||||
|
||||
void menu_print_page (const page_t *page, int *opts)
|
||||
{
|
||||
int i;
|
||||
int cur;
|
||||
int keys;
|
||||
|
||||
cur = 0;
|
||||
|
||||
while (1) {
|
||||
consoleClear();
|
||||
|
||||
iprintf("-- %s\n\n", page->title);
|
||||
|
||||
const entry_t *sel_entry = &page->entries[cur];
|
||||
for (i = 0; i < page->entries_no; i++) {
|
||||
const entry_t *entry = &page->entries[i];
|
||||
iprintf("%c %s %s\n", (i == cur) ? '>' : ' ', entry->label,
|
||||
(entry->opts_no) ? entry->opts[opts[i]] : "");
|
||||
}
|
||||
|
||||
scanKeys();
|
||||
keys = keysDownRepeat();
|
||||
|
||||
if (keys&KEY_UP) {
|
||||
cur--;
|
||||
if (cur < 0)
|
||||
cur = page->entries_no - 1;
|
||||
}
|
||||
if (keys&KEY_DOWN) {
|
||||
cur++;
|
||||
if (cur == page->entries_no)
|
||||
cur = 0;
|
||||
}
|
||||
if (sel_entry->opts_no) {
|
||||
if (keys&KEY_LEFT && opts[cur] > 0) {
|
||||
opts[cur]--;
|
||||
if (sel_entry->cb) sel_entry->cb(opts[cur]);
|
||||
}
|
||||
if (keys&KEY_RIGHT && opts[cur] < sel_entry->opts_no - 1) {
|
||||
opts[cur]++;
|
||||
if (sel_entry->cb) sel_entry->cb(opts[cur]);
|
||||
}
|
||||
}
|
||||
|
||||
if (keys&KEY_A) {
|
||||
if (sel_entry->cb)
|
||||
sel_entry->cb(opts[cur]); // TODO : Handle errors
|
||||
return;
|
||||
}
|
||||
|
||||
// Use keysDown to avoid bouncing
|
||||
if (keysDown()&KEY_START)
|
||||
return;
|
||||
|
||||
swiWaitForVBlank();
|
||||
}
|
||||
}
|
||||
|
||||
void pause_menu () { menu_print_page(&paused_pg, (int *)&paused_pg_opt); }
|
|
@ -0,0 +1,46 @@
|
|||
#include <nds.h>
|
||||
#include "emu.h"
|
||||
|
||||
static u8 sound_buf[44100];
|
||||
static int buf_pos;
|
||||
static int last_cycles;
|
||||
static u8 spkr_phase;
|
||||
|
||||
void sound_reset ()
|
||||
{
|
||||
memset(&sound_buf, 0, sizeof(sound_buf));
|
||||
buf_pos = 0;
|
||||
last_cycles = 0;
|
||||
spkr_phase = 0x80;
|
||||
}
|
||||
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
void sound_spkr_flip (u32 cycles_left)
|
||||
{
|
||||
int ts, i;
|
||||
|
||||
// Check how many cycles have passed since last flip
|
||||
ts = (last_cycles - cycles_left);
|
||||
if (ts < 0)
|
||||
ts += 17030;
|
||||
last_cycles = cycles_left;
|
||||
|
||||
int len;
|
||||
len = MIN(ts/23, sizeof(sound_buf) - buf_pos);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
sound_buf[buf_pos++] = spkr_phase;
|
||||
}
|
||||
|
||||
spkr_phase = ~spkr_phase;
|
||||
}
|
||||
|
||||
void sound_play ()
|
||||
{
|
||||
if (buf_pos) {
|
||||
memset(sound_buf + buf_pos, 0, sizeof(sound_buf) - buf_pos);
|
||||
soundPlaySample(sound_buf, SoundFormat_8Bit, buf_pos, 44100, 127, 0x40, 0, 0);
|
||||
buf_pos = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
#include <stdio.h>
|
||||
#include <nds.h>
|
||||
#include "emu.h"
|
||||
|
||||
typedef struct state_hdr_t {
|
||||
u32 magic;
|
||||
u8 ver;
|
||||
u8 flags;
|
||||
u16 resv;
|
||||
} __attribute__((packed)) state_hdr_t;
|
||||
|
||||
#define STATE_MAGIC (0x47525033)
|
||||
|
||||
int state_save (int slot)
|
||||
{
|
||||
FILE *f;
|
||||
state_hdr_t h;
|
||||
|
||||
f = fopen("game.sav", "w+");
|
||||
if (f)
|
||||
return 0;
|
||||
|
||||
h.magic = STATE_MAGIC;
|
||||
h.ver = 1;
|
||||
h.flags = 0;
|
||||
h.resv = 0;
|
||||
|
||||
fwrite(&h, 1, sizeof(state_hdr_t), f);
|
||||
|
||||
fwrite(&cpu_regs, 6, 4, f);
|
||||
fwrite(&mainram, 1, sizeof(mainram), f);
|
||||
fwrite(&lcram, 1, sizeof(lcram), f);
|
||||
fwrite(&memmap_r, 0x10, 4, f);
|
||||
fwrite(&memmap_w, 0x10, 4, f);
|
||||
fwrite(&page_dirty, 1, 64, f);
|
||||
fwrite(&jstk_axis, 4, 4, f);
|
||||
fwrite(&jstk_btn, 3, 4, f);
|
||||
fwrite(&jstk_rst_cycle, 1, 4, f);
|
||||
fwrite(&keybd_latch, 1, 4, f);
|
||||
|
||||
video_save(f);
|
||||
disk_save(f);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state_load (int slot)
|
||||
{
|
||||
FILE *f;
|
||||
state_hdr_t h;
|
||||
|
||||
f = fopen("game.sav", "w+");
|
||||
if (f)
|
||||
return 0;
|
||||
|
||||
h.magic = STATE_MAGIC;
|
||||
h.ver = 1;
|
||||
h.flags = 0;
|
||||
h.resv = 0;
|
||||
|
||||
fread(&h, 1, sizeof(state_hdr_t), f);
|
||||
|
||||
fread(&cpu_regs, 6, 4, f);
|
||||
fread(&mainram, 1, sizeof(mainram), f);
|
||||
fread(&lcram, 1, sizeof(lcram), f);
|
||||
fread(&memmap_r, 0x10, 4, f);
|
||||
fread(&memmap_w, 0x10, 4, f);
|
||||
fread(&page_dirty, 1, 64, f);
|
||||
fread(&jstk_axis, 4, 4, f);
|
||||
fread(&jstk_btn, 3, 4, f);
|
||||
fread(&jstk_rst_cycle, 1, 4, f);
|
||||
fread(&keybd_latch, 1, 4, f);
|
||||
|
||||
video_load(f);
|
||||
disk_load(f);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef STATE_H
|
||||
#define STATE_H
|
||||
|
||||
int state_load (int slot);
|
||||
int state_save (int slot);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,371 @@
|
|||
#include <stdio.h>
|
||||
#include <nds.h>
|
||||
#include "mem.h"
|
||||
|
||||
const u8 apple_font[] = {
|
||||
0x00, 0x70, 0x88, 0xa8, 0xe8, 0x68, 0x08, 0xf0, 0x00, 0x20, 0x50, 0x88, 0x88, 0xf8, 0x88, 0x88,
|
||||
0x00, 0x78, 0x88, 0x88, 0x78, 0x88, 0x88, 0x78, 0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x88, 0x70,
|
||||
0x00, 0x78, 0x88, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0xf8, 0x08, 0x08, 0x78, 0x08, 0x08, 0xf8,
|
||||
0x00, 0xf8, 0x08, 0x08, 0x78, 0x08, 0x08, 0x08, 0x00, 0xf0, 0x08, 0x08, 0x08, 0xc8, 0x88, 0xf0,
|
||||
0x00, 0x88, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x88, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70,
|
||||
0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x70, 0x00, 0x88, 0x48, 0x28, 0x18, 0x28, 0x48, 0x88,
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x88, 0xd8, 0xa8, 0xa8, 0x88, 0x88, 0x88,
|
||||
0x00, 0x88, 0x88, 0x98, 0xa8, 0xc8, 0x88, 0x88, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70,
|
||||
0x00, 0x78, 0x88, 0x88, 0x78, 0x08, 0x08, 0x08, 0x00, 0x70, 0x88, 0x88, 0x88, 0xa8, 0x48, 0xb0,
|
||||
0x00, 0x78, 0x88, 0x88, 0x78, 0x28, 0x48, 0x88, 0x00, 0x70, 0x88, 0x08, 0x70, 0x80, 0x88, 0x70,
|
||||
0x00, 0xf8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70,
|
||||
0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00, 0x88, 0x88, 0x88, 0xa8, 0xa8, 0xd8, 0x88,
|
||||
0x00, 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88, 0x00, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20,
|
||||
0x00, 0xf8, 0x80, 0x40, 0x20, 0x10, 0x08, 0xf8, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
|
||||
0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0xf8, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf8,
|
||||
0x00, 0x00, 0x00, 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20,
|
||||
0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0xf8, 0x50, 0xf8, 0x50, 0x50,
|
||||
0x00, 0x20, 0xf0, 0x28, 0x70, 0xa0, 0x78, 0x20, 0x00, 0x18, 0x98, 0x40, 0x20, 0x10, 0xc8, 0xc0,
|
||||
0x00, 0x10, 0x28, 0x28, 0x10, 0xa8, 0x48, 0xb0, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00, 0x20, 0x40, 0x80, 0x80, 0x80, 0x40, 0x20,
|
||||
0x00, 0x20, 0xa8, 0x70, 0x20, 0x70, 0xa8, 0x20, 0x00, 0x00, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00,
|
||||
0x00, 0x70, 0x88, 0xc8, 0xa8, 0x98, 0x88, 0x70, 0x00, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x70,
|
||||
0x00, 0x70, 0x88, 0x80, 0x60, 0x10, 0x08, 0xf8, 0x00, 0xf8, 0x80, 0x40, 0x60, 0x80, 0x88, 0x70,
|
||||
0x00, 0x40, 0x60, 0x50, 0x48, 0xf8, 0x40, 0x40, 0x00, 0xf8, 0x08, 0x78, 0x80, 0x80, 0x88, 0x70,
|
||||
0x00, 0xe0, 0x10, 0x08, 0x78, 0x88, 0x88, 0x70, 0x00, 0xf8, 0x80, 0x40, 0x20, 0x10, 0x10, 0x10,
|
||||
0x00, 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70, 0x00, 0x70, 0x88, 0x88, 0xf0, 0x80, 0x40, 0x38,
|
||||
0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x20, 0x10,
|
||||
0x00, 0x40, 0x20, 0x10, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00,
|
||||
0x00, 0x10, 0x20, 0x40, 0x80, 0x40, 0x20, 0x10, 0x00, 0x70, 0x88, 0x40, 0x20, 0x20, 0x00, 0x20
|
||||
};
|
||||
|
||||
static const u16 video_addr[] DTCM_DATA = {
|
||||
0x000, 0x080, 0x100, 0x180,
|
||||
0x200, 0x280, 0x300, 0x380,
|
||||
0x028, 0x0a8, 0x128, 0x1a8,
|
||||
0x228, 0x2a8, 0x328, 0x3a8,
|
||||
0x050, 0x0d0, 0x150, 0x1d0,
|
||||
0x250, 0x2d0, 0x350, 0x3d0
|
||||
};
|
||||
|
||||
static const u16 palette[] = {
|
||||
RGB15(0, 0, 0),
|
||||
RGB15(28, 4, 12),
|
||||
RGB15(12, 9, 23),
|
||||
RGB15(31, 8, 31),
|
||||
RGB15(0, 20, 12),
|
||||
RGB15(19, 19, 19),
|
||||
RGB15(2, 25, 31),
|
||||
RGB15(25, 24, 31),
|
||||
RGB15(12, 14, 0),
|
||||
RGB15(31, 13, 7),
|
||||
RGB15(19, 19, 19),
|
||||
RGB15(31, 19, 25),
|
||||
RGB15(2, 30, 7),
|
||||
RGB15(25, 27, 17),
|
||||
RGB15(14, 31, 25),
|
||||
RGB15(31, 31, 31),
|
||||
};
|
||||
|
||||
static int text_bg;
|
||||
static int gfx_bg;
|
||||
|
||||
static int text_mode;
|
||||
static int mixed_mode;
|
||||
static int sel_page;
|
||||
static int hires;
|
||||
|
||||
void video_set_mode ();
|
||||
|
||||
void video_save (FILE *f)
|
||||
{
|
||||
fwrite(&text_mode, 1, 4, f);
|
||||
fwrite(&mixed_mode, 1, 4, f);
|
||||
fwrite(&sel_page, 1, 4, f);
|
||||
fwrite(&hires, 1, 4, f);
|
||||
}
|
||||
|
||||
void video_load (FILE *f)
|
||||
{
|
||||
fread(&text_mode, 1, 4, f);
|
||||
fread(&mixed_mode, 1, 4, f);
|
||||
fread(&sel_page, 1, 4, f);
|
||||
fread(&hires, 1, 4, f);
|
||||
|
||||
video_set_mode();
|
||||
}
|
||||
|
||||
void video_set_mode ()
|
||||
{
|
||||
if (mixed_mode) {
|
||||
bgShow(gfx_bg);
|
||||
bgShow(text_bg);
|
||||
REG_DISPCNT |= DISPLAY_WIN0_ON;
|
||||
} else {
|
||||
bgShow(text_mode ? text_bg : gfx_bg);
|
||||
bgHide(text_mode ? gfx_bg : text_bg);
|
||||
REG_DISPCNT &= ~DISPLAY_WIN0_ON;
|
||||
}
|
||||
}
|
||||
|
||||
u8 video_io_read (u16 addr) ITCM_CODE;
|
||||
u8 video_io_read (u16 addr)
|
||||
{
|
||||
switch (addr&0xf) {
|
||||
case 0x0:
|
||||
text_mode = 0;
|
||||
break;
|
||||
case 0x1:
|
||||
text_mode = 1;
|
||||
break;
|
||||
case 0x2:
|
||||
mixed_mode = 0;
|
||||
break;
|
||||
case 0x3:
|
||||
mixed_mode = 1;
|
||||
break;
|
||||
case 0x4:
|
||||
sel_page = 1;
|
||||
break;
|
||||
case 0x5:
|
||||
sel_page = 2;
|
||||
break;
|
||||
case 0x6:
|
||||
hires = 0;
|
||||
break;
|
||||
case 0x7:
|
||||
hires = 1;
|
||||
break;
|
||||
// Annunciators
|
||||
case 0x8:
|
||||
case 0xa:
|
||||
case 0xc:
|
||||
case 0xe:
|
||||
break;
|
||||
}
|
||||
|
||||
video_set_mode();
|
||||
|
||||
// Duh, some scrollers wont work if we don't return 0x80
|
||||
// http://rich12345.tripod.com/aiivideo/vbl.html
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
void draw_lores_scr (u16 *map) ITCM_CODE;
|
||||
void draw_lores_scr (u16 *map)
|
||||
{
|
||||
int last_line;
|
||||
int i, j;
|
||||
u8 *ptr;
|
||||
|
||||
if (!page_dirty[sel_page])
|
||||
return;
|
||||
|
||||
last_line = mixed_mode ? 168 : 192;
|
||||
|
||||
for (i = 0; i < last_line; i++) {
|
||||
ptr = (u8 *)(mainram + (sel_page << 10) + video_addr[i/8]);
|
||||
for (j = 0; j < 40/2; j++) {
|
||||
const u8 col_a = ((i&0x7)>=4) ? (*ptr>>4) : (*ptr&0xf);
|
||||
ptr++;
|
||||
const u8 col_b = ((i&0x7)>=4) ? (*ptr>>4) : (*ptr&0xf);
|
||||
ptr++;
|
||||
|
||||
*map++ = col_a << 8 | col_a;
|
||||
*map++ = col_a << 8 | col_a;
|
||||
*map++ = col_a << 8 | col_a;
|
||||
*map++ = col_b << 8 | col_a;
|
||||
*map++ = col_b << 8 | col_b;
|
||||
*map++ = col_b << 8 | col_b;
|
||||
*map++ = col_b << 8 | col_b;
|
||||
}
|
||||
map += 232/2;
|
||||
}
|
||||
}
|
||||
|
||||
/*odd_color = (b1&0x80) ? 6 : 3;*/
|
||||
/*even_color = (b1&0x80) ? 9 : 12;*/
|
||||
void draw_hires_scr_1 (u16 *map)
|
||||
{
|
||||
int last_line;
|
||||
int i, j, k;
|
||||
u8 *ptr, tmp;
|
||||
static u8 tmp_line[0x200];
|
||||
static const u8 color_lut[] DTCM_DATA = { 0, 12, 0, 15, 0, 9, 0, 15, 0, 3, 0, 15, 0, 6, 0, 15 };
|
||||
|
||||
last_line = mixed_mode ? 168 : 192;
|
||||
|
||||
for (i = 0; i < last_line; i++, map += 0x100) {
|
||||
if (!page_dirty[8 + (i&7)])
|
||||
continue;
|
||||
|
||||
ptr = mainram + (sel_page << 13) + ((i&7) * 0x400) + video_addr[i>>3];
|
||||
|
||||
for (j = 0, tmp = 0; j < 280; j += 7) {
|
||||
const u8 b1 = *ptr++;
|
||||
|
||||
tmp = (b1&0x7f) << 1 | tmp;
|
||||
|
||||
// embb
|
||||
// e = even / odd
|
||||
// m = msb
|
||||
// b = data
|
||||
for (k = 0; k <= 7; k++)
|
||||
tmp_line[j+k] = color_lut[(((j+k)&1) ? 0x8 : 0x0) + ((b1&0x80)>>5) + ((tmp>>k)&3)];
|
||||
|
||||
tmp >>= 7;
|
||||
}
|
||||
|
||||
dmaCopyAsynch(tmp_line, map, 0x200);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void draw_hires_scr (u16 *map) ITCM_CODE;
|
||||
void draw_hires_scr (u16 *map)
|
||||
{
|
||||
int last_line;
|
||||
int i, j, k, x;
|
||||
u8 *xptr, *ptr, tmp;
|
||||
u16 *omap;
|
||||
static u8 tmp_line[0x200];
|
||||
static const u8 color_lut[] DTCM_DATA = { 0, 12, 0, 15, 0, 9, 0, 15, 0, 3, 0, 15, 0, 6, 0, 15 };
|
||||
|
||||
last_line = mixed_mode ? 168/8 : 192/8;
|
||||
|
||||
for(x = 0; x < 8; x++, map += 0x100) {
|
||||
if (!page_dirty[8+x])
|
||||
continue;
|
||||
xptr = mainram + (sel_page << 13) + (x << 10);
|
||||
|
||||
for(omap = map, i = 0; i < last_line; i++, omap += 0x800) {
|
||||
ptr = xptr + video_addr[i];
|
||||
|
||||
for (j = 0, tmp = 0; j < 280; j += 7) {
|
||||
const u8 b1 = *ptr++;
|
||||
|
||||
tmp = (b1&0x7f) << 1 | tmp;
|
||||
const u8 *lut_b = color_lut + ((b1&0x80)>>5);
|
||||
|
||||
for (k = 0; k < 7; k+=2)
|
||||
{
|
||||
tmp_line[j+k+0] = lut_b[((j&1) << 3) + (tmp&3)];
|
||||
tmp >>= 1;
|
||||
if(k == 6)
|
||||
break;
|
||||
tmp_line[j+k+1] = lut_b[((~j&1) << 3) + (tmp&3)];
|
||||
tmp >>= 1;
|
||||
}
|
||||
}
|
||||
dmaCopyAsynch(tmp_line, omap, 0x200);
|
||||
/*memcpy(omap, tmp_line, 0x200);*/
|
||||
}
|
||||
page_dirty[8+x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_text_scr (u16 *map) ITCM_CODE;
|
||||
void draw_text_scr (u16 *map)
|
||||
{
|
||||
int start_line;
|
||||
int i, j;
|
||||
u16 *ptr;
|
||||
|
||||
if (!page_dirty[sel_page])
|
||||
return;
|
||||
|
||||
if ((start_line = mixed_mode ? 21 : 0))
|
||||
map += 32 * start_line;
|
||||
|
||||
for (i = start_line; i < 24; i++) {
|
||||
ptr = (u16 *)(mainram + (sel_page << 10) + video_addr[i]);
|
||||
|
||||
for (j = 0; j < 5; j++) {
|
||||
*map++ = (*ptr++)&0x3f3f;
|
||||
*map++ = (*ptr++)&0x3f3f;
|
||||
*map++ = (*ptr++)&0x3f3f;
|
||||
*map++ = (*ptr++)&0x3f3f;
|
||||
}
|
||||
|
||||
map += 12;
|
||||
}
|
||||
}
|
||||
|
||||
void video_draw () ITCM_CODE;
|
||||
void video_draw ()
|
||||
{
|
||||
u16 *text_ptr = (u16 *)0x6002000;
|
||||
u16 *gfx_ptr = (u16 *)0x6020000;
|
||||
|
||||
if (mixed_mode) {
|
||||
draw_text_scr(text_ptr);
|
||||
(hires) ? draw_hires_scr(gfx_ptr) : draw_lores_scr(gfx_ptr);
|
||||
} else {
|
||||
if (text_mode)
|
||||
draw_text_scr(text_ptr);
|
||||
else
|
||||
if (hires)
|
||||
draw_hires_scr(gfx_ptr);
|
||||
else
|
||||
draw_lores_scr(gfx_ptr);
|
||||
|
||||
}
|
||||
|
||||
// Clear this here otherwise we won't be able to render a lores
|
||||
// screen after the text one
|
||||
page_dirty[sel_page] = 0;
|
||||
}
|
||||
|
||||
int video_set_scale (int mode)
|
||||
{
|
||||
int scale_x;
|
||||
int scale_y;
|
||||
|
||||
switch (mode) {
|
||||
default:
|
||||
case 0:
|
||||
scale_x = floatToFixed(1., 8);
|
||||
scale_y = floatToFixed(1., 8);
|
||||
break;
|
||||
case 1:
|
||||
// 280 / 256 = 1.09
|
||||
scale_x = floatToFixed(1.09, 8);
|
||||
scale_y = floatToFixed(1., 8);
|
||||
break;
|
||||
}
|
||||
|
||||
bgSetScale(gfx_bg, scale_x, scale_y);
|
||||
bgSetScale(text_bg, scale_x, scale_y);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void video_init ()
|
||||
{
|
||||
videoSetMode(MODE_4_2D);
|
||||
|
||||
vramSetBankA(VRAM_A_MAIN_BG_0x06000000);
|
||||
vramSetBankB(VRAM_B_MAIN_BG_0x06020000);
|
||||
|
||||
// http://mtheall.com/vram.html#T2=3&NT2=128&MB2=4&TB2=0&S2=2&T3=5&NT3=32&MB3=8&TB3=1&S3=3
|
||||
text_bg = bgInit(2, BgType_Rotation, BgSize_R_512x512, 4, 0);
|
||||
gfx_bg = bgInit(3, BgType_Bmp8, BgSize_B8_512x256, 8, 0);
|
||||
|
||||
REG_DISPCNT &= ~DISPLAY_WIN0_ON;
|
||||
// Setup the window used for mixed mode
|
||||
WIN0_X0 = 0;
|
||||
WIN0_X1 = 255;
|
||||
WIN0_Y0 = 0;
|
||||
WIN0_Y1 = 160;
|
||||
// BG3 inside
|
||||
WIN_IN = 0x8;
|
||||
// BG2 outside
|
||||
WIN_OUT = 0x4;
|
||||
|
||||
memcpy(BG_PALETTE, palette, sizeof(palette));
|
||||
|
||||
struct UnpackStruct unpack_bit;
|
||||
unpack_bit.dataOffset = 14;
|
||||
unpack_bit.destWidth = 8;
|
||||
unpack_bit.sourceSize = 64*8;
|
||||
unpack_bit.sourceWidth = 1;
|
||||
swiUnpackBits((u8 *)&apple_font, (u32 *)bgGetGfxPtr(text_bg), &unpack_bit);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef VIDEO_H
|
||||
#define VIDEO_H
|
||||
|
||||
int video_set_scale (int mode);
|
||||
void video_draw ();
|
||||
void video_init ();
|
||||
|
||||
void video_save (FILE *f);
|
||||
void video_load (FILE *f);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue