From 33af1795f47fea1fb931623af022527b44cf33e9 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 13 Oct 2013 18:31:57 +0000 Subject: [PATCH] first commit --- .gitignore | 7 + Makefile | 134 ++++ source/README.md | 24 + source/cpu.h | 10 + source/cpu.s | 1551 ++++++++++++++++++++++++++++++++++++++++++++++ source/disk.c | 278 +++++++++ source/disk.h | 27 + source/emu.c | 82 +++ source/emu.h | 23 + source/input.c | 52 ++ source/main.c | 55 ++ source/mem.h | 23 + source/mem.s | 255 ++++++++ source/memutil.c | 39 ++ source/menu.c | 100 +++ source/sound.c | 46 ++ source/state.c | 82 +++ source/state.h | 7 + source/video.c | 371 +++++++++++ source/video.h | 11 + 20 files changed, 3177 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 source/README.md create mode 100644 source/cpu.h create mode 100644 source/cpu.s create mode 100644 source/disk.c create mode 100644 source/disk.h create mode 100644 source/emu.c create mode 100644 source/emu.h create mode 100644 source/input.c create mode 100644 source/main.c create mode 100644 source/mem.h create mode 100644 source/mem.s create mode 100644 source/memutil.c create mode 100644 source/menu.c create mode 100644 source/sound.c create mode 100644 source/state.c create mode 100644 source/state.h create mode 100644 source/video.c create mode 100644 source/video.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a45155 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.txt +*.py +*.pl +*.nds +*.elf +fs/ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..117f3c9 --- /dev/null +++ b/Makefile @@ -0,0 +1,134 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=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 +#--------------------------------------------------------------------------------------- diff --git a/source/README.md b/source/README.md new file mode 100644 index 0000000..77e7611 --- /dev/null +++ b/source/README.md @@ -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) diff --git a/source/cpu.h b/source/cpu.h new file mode 100644 index 0000000..59b4eed --- /dev/null +++ b/source/cpu.h @@ -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 diff --git a/source/cpu.s b/source/cpu.s new file mode 100644 index 0000000..4370413 --- /dev/null +++ b/source/cpu.s @@ -0,0 +1,1551 @@ +.arm +.section .itcm,"ax",%progbits + +.global cpu_run +.global cpu_reset +.global cpu_regs + +.extern mainram +.extern readb +.extern writeb + +reg_pc .req r11 +reg_sp .req r10 +reg_a .req r9 +reg_x .req r8 +reg_y .req r7 +reg_f .req r6 +cycles .req r5 + +.macro fetch + ldrb r0, [reg_pc], #1 +.endm + +.macro fetchw + ldrb r0, [reg_pc], #1 + ldrb r1, [reg_pc], #1 + orr r0, r1, lsl #8 +.endm + +.macro rebase_sp r + ldr r0, =(mainram+0x100) + add \r, r0 +.endm + +.macro unbase_sp r + ldr r0, =(mainram+0x100) + sub \r, reg_sp, r0 +.endm + +.macro rebase_pc r + ldr r1, =memmap_r + mov r2, \r, lsr #12 + ldr r1, [r1, r2, lsl #2] + add \r, r1 + ldr r2, =last_page + str r1, [r2] +.endm + +.macro unbase_pc r + ldr r1, =last_page + ldr r2, [r1] + sub \r, reg_pc, r2 +.endm + +.macro push b + strb \b, [reg_sp], #-1 +.endm + +.macro pop + ldrb r0, [reg_sp, #1]! +.endm + +.macro adr_zp_z + fetch +.endm + +.macro adr_zp r + fetch + add r0, \r + and r0, #0xff +.endm + +.macro adr_abs_z + fetchw +.endm + +.macro adr_abs r + fetchw + mov r1, r0, lsr #8 + cmp r1, #0xff + subeq cycles, #1 + add r0, \r +.endm + +.macro adr_idx + fetchw + ldr r1, =memmap_r + mov r2, r0, lsr #12 + ldr r1, [r1, r2, lsl #2] + add r1, r0 + ldrb r0, [r1, #0] + ldrb r1, [r1, #1] + orr r0, r1, lsl #8 +.endm + +.macro adr_idx_x + fetch + ldr r1, =memmap_r + add r0, reg_x + ldr r1, [r1] + add r1, r0 + ldrb r0, [r1, #0] + ldrb r1, [r1, #1] + orr r0, r1, lsl #8 +.endm + +.macro adr_idx_y + fetch + ldr r3, =memmap_r + ldr r2, [r3] + add r2, r2, r0 + ldrb r0, [r2, #0] + ldrb r1, [r2, #1] + orr r0, r1, lsl #8 + add r1, r0, reg_y + eor r2, r1, r0 + tst r2, #0x100 + subne cycles, #1 + mov r0, r1 +.endm + +.macro adr_rel + ldrsb r0, [reg_pc], #1 + // Sign extend + add r2, reg_pc, r0 + eor r1, r2, reg_pc + tst r1, #0x100 + subne cycles, #2 + add reg_pc, r0 +.endm + +#define FLAG_CARRY 0x01<<24 +#define FLAG_ZERO 0x02<<24 +#define FLAG_INTR 0x04<<24 +#define FLAG_DEC 0x08<<24 +#define FLAG_OVER 0x40<<24 +#define FLAG_NEG 0x80<<24 + +.macro op_adc r + mov reg_a, reg_a, lsl #24 + movs r2, reg_f, lsr #25 + bic reg_f, #(FLAG_ZERO+FLAG_CARRY+FLAG_OVER+FLAG_NEG) + subcs \r, \r, #0x100 + adcs reg_a, \r, ror #8 + orreq reg_f, #FLAG_ZERO + orrcs reg_f, #FLAG_CARRY + orrvs reg_f, #FLAG_OVER + orrmi reg_f, #FLAG_NEG + mov reg_a, reg_a, lsr #24 +.endm + +.macro op_sbc r + mov reg_a, reg_a, lsl #24 + movs r2, reg_f, lsr #25 + bic reg_f, #(FLAG_ZERO+FLAG_CARRY+FLAG_OVER+FLAG_NEG) + sbcs reg_a, \r, lsl #24 + orrcs reg_f, #FLAG_CARRY + orrvs reg_f, #FLAG_OVER + orrmi reg_f, #FLAG_NEG + movs reg_a, reg_a, lsr #24 + orreq reg_f, #FLAG_ZERO +.endm + +.macro op_and a1 + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + lsl reg_a, #24 + ands reg_a, \a1, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 +.endm + +.macro op_asl a1 + bic reg_f, #(FLAG_CARRY+FLAG_ZERO+FLAG_NEG) + movs \a1, \a1, lsl #25 + orrcs reg_f, #FLAG_CARRY + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr \a1, #24 +.endm + +.macro op_cmp a1, a2 + bic reg_f, #(FLAG_CARRY+FLAG_ZERO+FLAG_NEG) + mov r3, \a1, lsl #24 + cmp r3, \a2, lsl #24 + orrcs reg_f, #FLAG_CARRY + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG +.endm + +.macro op_dec a1 + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + lsl \a1, #24 + subs \a1, #1<<24 + orrmi reg_f, #FLAG_NEG + movs \a1, \a1, lsr #24 + orreq reg_f, #FLAG_ZERO +.endm + +.macro op_eor a1 + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + lsl reg_a, #24 + eors reg_a, \a1, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 +.endm + +.macro op_inc a1 + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + lsl \a1, #24 + adds \a1, #1<<24 + orrmi reg_f, #FLAG_NEG + movs \a1, \a1, lsr #24 + orreq reg_f, #FLAG_ZERO +.endm + +.macro op_lsr a1 + bic reg_f, #(FLAG_CARRY+FLAG_ZERO) + movs \a1, \a1, lsr #1 + orrcs reg_f, #FLAG_CARRY + orreq reg_f, #FLAG_ZERO + //flag_neg \a1 +.endm + +.macro op_ora a1 + bic reg_f, #(FLAG_NEG+FLAG_ZERO) + lsl reg_a, #24 + orrs reg_a, \a1, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 +.endm + +.macro op_rol a1 + and r3, reg_f, #FLAG_CARRY + bic reg_f, #(FLAG_CARRY+FLAG_ZERO+FLAG_NEG) + movs \a1, \a1, lsl #25 + orrcs reg_f, #FLAG_CARRY + adds \a1, r3 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr \a1, #24 +.endm + +.macro op_ror a1 + movs r3, reg_f, lsr #25 + bic reg_f, #(FLAG_CARRY+FLAG_ZERO+FLAG_NEG) + lsl \a1, #23 + orrcs reg_f, #FLAG_NEG + orrcs \a1, #1<<31 + movs \a1, \a1, lsr #24 + orreq reg_f, #FLAG_ZERO + orrcs reg_f, #FLAG_CARRY +.endm + +.macro op_bit a1 + bic reg_f, #(FLAG_ZERO+FLAG_OVER+FLAG_NEG) + tst reg_a, \a1 + orreq reg_f, #FLAG_ZERO + tst \a1, #0x40 + orrne reg_f, #FLAG_OVER + tst \a1, #0x80 + orrne reg_f, #FLAG_NEG +.endm + +// NOP +_ea: +_7a: + mov r0, #2 + b instr_end +// ORA +_09: + fetch + op_ora r0 + mov r0, #2 + b instr_end +_05: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_ora r2 + mov r0, #3 + b instr_end +_15: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_ora r2 + mov r0, #4 + b instr_end +_0d: + adr_abs_z + bl readb + op_ora r0 + mov r0, #4 + b instr_end +_1d: + adr_abs reg_x + bl readb + op_ora r0 + mov r0, #4 + b instr_end +_19: + adr_abs reg_y + bl readb + op_ora r0 + mov r0, #4 + b instr_end +_01: + adr_idx_x + bl readb + op_ora r0 + mov r0, #6 + b instr_end +_11: + adr_idx_y + bl readb + op_ora r0 + mov r0, #5 + b instr_end +// AND +_29: + fetch + op_and r0 + mov r0, #2 + b instr_end +_25: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_and r2 + mov r0, #3 + b instr_end +_35: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_and r2 + mov r0, #4 + b instr_end +_2d: + adr_abs_z + bl readb + op_and r0 + mov r0, #4 + b instr_end +_3d: + adr_abs reg_x + bl readb + op_and r0 + mov r0, #4 + b instr_end +_39: + adr_abs reg_y + bl readb + op_and r0 + mov r0, #4 + b instr_end +_21: + adr_idx_x + bl readb + op_and r0 + mov r0, #6 + b instr_end +_31: + adr_idx_y + bl readb + op_and r0 + mov r0, #5 + b instr_end +// ASL +_0a: + op_asl reg_a + mov r0, #2 + b instr_end +_06: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_asl r2 + strb r2, [r1, r0] + mov r0, #5 + b instr_end +_16: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_asl r2 + strb r2, [r1, r0] + mov r0, #6 + b instr_end +_0e: + adr_abs_z + mov r4, r0 + bl readb + op_asl r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #6 + b instr_end +_1e: + adr_abs reg_x + mov r4, r0 + bl readb + op_asl r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #7 + b instr_end +// BCC +_90: + tst reg_f, #FLAG_CARRY + addne reg_pc, #1 + mov r0, #2 + bne instr_end + adr_rel + mov r0, #3 + b instr_end +// BCS +_b0: + tst reg_f, #FLAG_CARRY + addeq reg_pc, #1 + mov r0, #2 + beq instr_end + adr_rel + mov r0, #3 + b instr_end +// BEQ +_f0: + tst reg_f, #FLAG_ZERO + addeq reg_pc, #1 + mov r0, #2 + beq instr_end + adr_rel + mov r0, #3 + b instr_end +// BIT +_24: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_bit r2 + mov r0, #3 + b instr_end +_2c: + adr_abs_z + bl readb + op_bit r0 + mov r0, #4 + b instr_end +.ltorg +// BMI +_30: + tst reg_f, #FLAG_NEG + addeq reg_pc, #1 + mov r0, #2 + beq instr_end + adr_rel + mov r0, #3 + b instr_end +// BNE +_d0: + tst reg_f, #FLAG_ZERO + addne reg_pc, #1 + mov r0, #2 + bne instr_end + adr_rel + mov r0, #3 + b instr_end +// BPL +_10: + tst reg_f, #FLAG_NEG + addne reg_pc, #1 + mov r0, #2 + bne instr_end + adr_rel + mov r0, #3 + b instr_end +// BRK +_00: + add reg_pc, #1 + unbase_pc r0 + mov r1, r0, lsr #8 + and r2, r0, #0xff + push r1 + push r2 + mov r0, reg_f, lsr #24 + orr r0, #0x30 + push r0 + orr reg_f, #FLAG_INTR + + ldr r0, =memmap_r + ldr r0, [r0, #0xf<<2] + ldr r3, =0xfffe + add r0, r3 + ldrb r1, [r0, #0] + ldrb r2, [r0, #1] + orr reg_pc, r1, r2, lsl #8 + rebase_pc reg_pc + + mov r0, #7 + b instr_end +// RTI +_40: + pop + bic reg_f, #0xff000000 + orr reg_f, r0, lsl #24 + pop + mov reg_pc, r0 + pop + orr reg_pc, r0, lsl #8 + mov r0, #6 + b instr_end +.ltorg +// RTS +_60: + pop + mov reg_pc, r0 + pop + orr reg_pc, r0, lsl #8 + add reg_pc, #1 + rebase_pc reg_pc + mov r0, #6 + b instr_end +// CLD +_d8: + bic reg_f, #FLAG_DEC + mov r0, #2 + b instr_end +// JSR +_20: + unbase_pc r4 + add r4, #1 + mov r0, r4, lsr #8 + push r0 + and r4, #0xff + push r4 + adr_abs_z + mov reg_pc, r0 + rebase_pc reg_pc + mov r0, #6 + b instr_end + +// STX +_86: + adr_zp_z + ldr r1, =mainram + strb reg_x, [r1, r0] + mov r0, #3 + b instr_end +_96: + adr_zp reg_y + ldr r1, =mainram + strb reg_x, [r1, r0] + mov r0, #4 + b instr_end +_8e: + adr_abs_z + mov r1, reg_x + bl writeb + mov r0, #4 + b instr_end +// LDY +_a0: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + fetch + movs reg_y, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_y, #24 + mov r0, #2 + b instr_end +_a4: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_zp_z + ldr r1, =mainram + ldrb reg_y, [r1, r0] + movs reg_y, reg_y, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_y, #24 + mov r0, #3 + b instr_end +.ltorg +_b4: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_zp reg_x + ldr r1, =mainram + ldrb reg_y, [r1, r0] + movs reg_y, reg_y, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_y, #24 + mov r0, #4 + b instr_end +_ac: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_abs_z + bl readb + movs reg_y, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_y, #24 + mov r0, #4 + b instr_end +_bc: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_abs reg_x + bl readb + movs reg_y, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_y, #24 + mov r0, #4 + b instr_end +// STY +_84: + adr_zp_z + ldr r1, =mainram + strb reg_y, [r1, r0] + mov r0, #3 + b instr_end +_94: + adr_zp reg_x + ldr r1, =mainram + strb reg_y, [r1, r0] + mov r0, #4 + b instr_end +_8c: + adr_abs_z + mov r1, reg_y + bl writeb + mov r0, #4 + b instr_end +.ltorg +// BVS +_70: + tst reg_f, #FLAG_OVER + addeq reg_pc, #1 + mov r0, #2 + beq instr_end + adr_rel + mov r0, #3 + b instr_end +// BVC +_50: + tst reg_f, #FLAG_OVER + addne reg_pc, #1 + mov r0, #2 + bne instr_end + adr_rel + mov r0, #3 + b instr_end +// LDA +_a9: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + fetch + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #2 + b instr_end +_a5: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_zp_z + ldr r1, =mainram + ldrb reg_a, [r1, r0] + movs reg_a, reg_a, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #3 + b instr_end +_b5: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_zp reg_x + ldr r1, =mainram + ldrb reg_a, [r1, r0] + movs reg_a, reg_a, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #4 + b instr_end +_ad: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_abs_z + bl readb + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #4 + b instr_end +_bd: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_abs reg_x + bl readb + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #4 + b instr_end +_b9: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_abs reg_y + bl readb + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #4 + b instr_end +_a1: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_idx_x + bl readb + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #6 + b instr_end +_b1: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_idx_y + bl readb + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #5 + b instr_end +// STA +_85: + adr_zp_z + ldr r1, =mainram + strb reg_a, [r1, r0] + mov r0, #3 + b instr_end +_95: + adr_zp reg_x + ldr r1, =mainram + strb reg_a, [r1, r0] + mov r0, #4 + b instr_end +_8d: + adr_abs_z + mov r1, reg_a + bl writeb + mov r0, #4 + b instr_end +_9d: + adr_abs reg_x + mov r1, reg_a + bl writeb + mov r0, #5 + b instr_end +_99: + adr_abs reg_y + mov r1, reg_a + bl writeb + mov r0, #5 + b instr_end +_81: + adr_idx_x + mov r1, reg_a + bl writeb + mov r0, #6 + b instr_end +_91: + adr_idx_y + mov r1, reg_a + bl writeb + mov r0, #6 + b instr_end +// JMP +_4c: + adr_abs_z + mov reg_pc, r0 + rebase_pc reg_pc + mov r0, #3 + b instr_end +_6c: + adr_idx + mov reg_pc, r0 + rebase_pc reg_pc + mov r0, #5 + b instr_end +// PHA +_48: + push reg_a + mov r0, #3 + b instr_end +// PLA +_68: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + pop + movs reg_a, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #4 + b instr_end +// LSR +_4a: + op_lsr reg_a + mov r0, #2 + b instr_end +_46: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_lsr r2 + strb r2, [r1, r0] + mov r0, #5 + b instr_end +_56: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_lsr r2 + strb r2, [r1, r0] + mov r0, #6 + b instr_end +_4e: + adr_abs_z + mov r4, r0 + bl readb + op_lsr r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #6 + b instr_end +_5e: + adr_abs reg_x + mov r4, r0 + bl readb + op_lsr r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #7 + b instr_end +// ADC +_69: + fetch + op_adc r0 + mov r0, #2 + b instr_end +_65: + adr_zp_z + ldr r1, =mainram + ldrb r0, [r1, r0] + op_adc r0 + mov r0, #3 + b instr_end +_75: + adr_zp reg_x + ldr r1, =mainram + ldrb r0, [r1, r0] + op_adc r0 + mov r0, #4 + b instr_end +_6d: + adr_abs_z + bl readb + op_adc r0 + mov r0, #4 + b instr_end +_7d: + adr_abs reg_x + bl readb + op_adc r0 + mov r0, #4 + b instr_end +_79: + adr_abs reg_y + bl readb + op_adc r0 + mov r0, #4 + b instr_end +_61: + adr_idx_x + bl readb + op_adc r0 + mov r0, #6 + b instr_end +_71: + adr_idx_y + bl readb + op_adc r0 + mov r0, #5 + b instr_end +// SBC +_e9: + fetch + op_sbc r0 + mov r0, #2 + b instr_end +_e5: + adr_zp_z + ldr r1, =mainram + ldrb r0, [r1, r0] + op_sbc r0 + mov r0, #3 + b instr_end +_f5: + adr_zp reg_x + ldr r1, =mainram + ldrb r0, [r1, r0] + op_sbc r0 + mov r0, #4 + b instr_end +_ed: + adr_abs_z + bl readb + op_sbc r0 + mov r0, #4 + b instr_end +_fd: + adr_abs reg_x + bl readb + op_sbc r0 + mov r0, #4 + b instr_end +_f9: + adr_abs reg_y + bl readb + op_sbc r0 + mov r0, #4 + b instr_end +_e1: + adr_idx_x + bl readb + op_sbc r0 + mov r0, #6 + b instr_end +_f1: + adr_idx_y + bl readb + op_sbc r0 + mov r0, #5 + b instr_end +.ltorg +// LDX +_a2: + bic reg_f, #(FLAG_NEG+FLAG_ZERO) + fetch + movs reg_x, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_x, #24 + mov r0, #2 + b instr_end +_a6: + bic reg_f, #(FLAG_NEG+FLAG_ZERO) + adr_zp_z + ldr r1, =mainram + ldrb reg_x, [r1, r0] + movs reg_x, reg_x, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_x, #24 + mov r0, #3 + b instr_end +_b6: + bic reg_f, #(FLAG_NEG+FLAG_ZERO) + adr_zp reg_y + ldr r1, =mainram + ldrb reg_x, [r1, r0] + movs reg_x, reg_x, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_x, #24 + mov r0, #4 + b instr_end +_ae: + bic reg_f, #(FLAG_NEG+FLAG_ZERO) + adr_abs_z + bl readb + movs reg_x, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_x, #24 + mov r0, #4 + b instr_end +_be: + bic reg_f, #(FLAG_NEG+FLAG_ZERO) + adr_abs reg_y + bl readb + movs reg_x, r0, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_x, #24 + mov r0, #4 + b instr_end +// CPX +_e0: + fetch + op_cmp reg_x, r0 + mov r0, #2 + b instr_end +_e4: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_cmp reg_x, r2 + mov r0, #3 + b instr_end +_ec: + adr_abs_z + bl readb + op_cmp reg_x, r0 + mov r0, #4 + b instr_end +// CMP +_c9: + fetch + op_cmp reg_a, r0 + mov r0, #2 + b instr_end +_c5: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_cmp reg_a, r2 + mov r0, #3 + b instr_end +_d5: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_cmp reg_a, r2 + mov r0, #4 + b instr_end +_cd: + adr_abs_z + bl readb + op_cmp reg_a, r0 + mov r0, #4 + b instr_end +_dd: + adr_abs reg_x + bl readb + op_cmp reg_a, r0 + mov r0, #4 + b instr_end +_d9: + adr_abs reg_y + bl readb + op_cmp reg_a, r0 + mov r0, #4 + b instr_end +_c1: + adr_idx_x + bl readb + op_cmp reg_a, r0 + mov r0, #6 + b instr_end +_d1: + adr_idx_y + bl readb + op_cmp reg_a, r0 + mov r0, #5 + b instr_end +// CPY +_c0: + fetch + op_cmp reg_y, r0 + mov r0, #2 + b instr_end +_c4: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_cmp reg_y, r2 + mov r0, #3 + b instr_end +_cc: + adr_abs_z + bl readb + op_cmp reg_y, r0 + mov r0, #4 + b instr_end +// EOR +_49: + fetch + op_eor r0 + mov r0, #2 + b instr_end +_45: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_eor r2 + mov r0, #3 + b instr_end +_55: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_eor r2 + mov r0, #4 + b instr_end +_4d: + adr_abs_z + bl readb + op_eor r0 + mov r0, #4 + b instr_end +_5d: + adr_abs reg_x + bl readb + op_eor r0 + mov r0, #4 + b instr_end +_59: + adr_abs reg_y + bl readb + op_eor r0 + mov r0, #4 + b instr_end +_41: + adr_idx_x + bl readb + op_eor r0 + mov r0, #6 + b instr_end +_51: + adr_idx_y + bl readb + op_eor r0 + mov r0, #5 + b instr_end +// INC +_e6: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_inc r2 + strb r2, [r1, r0] + mov r0, #5 + b instr_end +_f6: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_inc r2 + strb r2, [r1, r0] + mov r0, #6 + b instr_end +_ee: + adr_abs_z + mov r4, r0 + bl readb + op_inc r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #6 + b instr_end +_fe: + adr_abs reg_x + mov r4, r0 + bl readb + op_inc r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #7 + b instr_end +// INY +_c8: + op_inc reg_y + mov r0, #2 + b instr_end +// INX +_e8: + op_inc reg_x + mov r0, #2 + b instr_end +// DEX +_ca: + op_dec reg_x + mov r0, #2 + b instr_end +.ltorg +// DEY +_88: + op_dec reg_y + mov r0, #2 + b instr_end +// SEC +_38: + orr reg_f, #FLAG_CARRY + mov r0, #2 + b instr_end +// SED +_f8: + orr reg_f, #FLAG_DEC + mov r0, #2 + b instr_end +// TAX +_aa: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + movs reg_x, reg_a, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_x, #24 + mov r0, #2 + b instr_end +// TAY +_a8: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + movs reg_y, reg_a, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_y, #24 + mov r0, #2 + b instr_end +// TXA +_8a: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + movs reg_a, reg_x, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #2 + b instr_end +// TYA +_98: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + movs reg_a, reg_y, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + lsr reg_a, #24 + mov r0, #2 + b instr_end +// TXS +_9a: + mov reg_sp, reg_x + rebase_sp reg_sp + mov r0, #2 + b instr_end +// TSX +_ba: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + unbase_sp reg_x + orreq reg_f, #FLAG_ZERO + movs r1, reg_x, lsl #24 + orrmi reg_f, #FLAG_NEG + mov r0, #2 + b instr_end +// DEC +_c6: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_dec r2 + strb r2, [r1, r0] + mov r0, #5 + b instr_end +_d6: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_dec r2 + strb r2, [r1, r0] + mov r0, #6 + b instr_end +_ce: + adr_abs_z + mov r4, r0 + bl readb + op_dec r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #6 + b instr_end +_de: + adr_abs reg_x + mov r4, r0 + bl readb + op_dec r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #7 + b instr_end +// CLC +_18: + bic reg_f, #FLAG_CARRY + mov r0, #2 + b instr_end +// PHP +_08: + mov r0, reg_f, lsr #24 + orr r0, #0x30 // MAGIC! + push r0 + mov r0, #3 + b instr_end +// PLP +_28: + pop + mov reg_f, r0, lsl #24 + mov r0, #4 + b instr_end +// ROL +_2a: + op_rol reg_a + mov r0, #2 + b instr_end +_26: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_rol r2 + strb r2, [r1, r0] + mov r0, #5 + b instr_end +_36: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_rol r2 + strb r2, [r1, r0] + mov r0, #6 + b instr_end +_2e: + adr_abs_z + mov r4, r0 + bl readb + op_rol r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #6 + b instr_end +_3e: + adr_abs reg_x + mov r4, r0 + bl readb + op_rol r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #7 + b instr_end +// ROR +_6a: + op_ror reg_a + mov r0, #2 + b instr_end +_66: + adr_zp_z + ldr r1, =mainram + ldrb r2, [r1, r0] + op_ror r2 + strb r2, [r1, r0] + mov r0, #5 + b instr_end +_76: + adr_zp reg_x + ldr r1, =mainram + ldrb r2, [r1, r0] + op_ror r2 + strb r2, [r1, r0] + mov r0, #6 + b instr_end +_6e: + adr_abs_z + mov r4, r0 + bl readb + op_ror r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #6 + b instr_end +_7e: + adr_abs reg_x + mov r4, r0 + bl readb + op_ror r0 + mov r1, r0 + mov r0, r4 + bl writeb + mov r0, #7 + b instr_end +// SEI +_78: + orr reg_f, #FLAG_INTR + mov r0, #2 + b instr_end +// CLI +_58: + bic reg_f, #FLAG_INTR + mov r0, #2 + b instr_end +// CLV +_b8: + bic reg_f, #FLAG_OVER + mov r0, #2 + b instr_end +// LAX +_b7: + bic reg_f, #(FLAG_ZERO+FLAG_NEG) + adr_zp reg_y + ldr r1, =mainram + ldrb reg_a, [r1, r0] + movs reg_x, reg_a, lsl #24 + orreq reg_f, #FLAG_ZERO + orrmi reg_f, #FLAG_NEG + mov reg_x, reg_a + mov r0, #4 + b instr_end +// DOP +_04: + add reg_pc, #1 + mov r0, #3 + b instr_end +// TOP +_fc: + add reg_pc, #2 + mov r0, #4 + b instr_end +// SLO +_07: + adr_zp_z + ldr r2, =mainram + ldrb r1, [r2, r0] + movs r1, r1, lsl #25 + orrcs reg_f, #FLAG_CARRY + lsr r1, #24 + strb r1, [r2, r0] + op_ora r1 + mov r0, #5 + b instr_end + +// http://emu-docs.org/CPU%2065xx/undocop.txt + +_xx: + // Stop the vblank irq +#if 1 + ldr r3, =0x4000210 + ldr r2, [r3] + bic r2, #1 + str r2, [r3] +#endif + mov r1, r0 + ldr r0, =unhandled_msg + mov r2, reg_a + mov r3, reg_x + stmfd sp!, {reg_y,reg_sp,reg_pc} + bl iprintf +k: b k + +// int cpu_run_1 (int cycles_to_do) +cpu_run: + stmfd sp!, {r4-r12, lr} + + ldr r1, =cpu_regs + ldmia r1, {reg_f-reg_pc} + + mov cycles, r0 +loop: + fetch +#if 0 + unbase_pc reg_pc + mov r3, reg_pc, lsr #12 + cmp r3, #0xf + beq e + stmfd sp!, {reg_pc,r0} + ldr r0, =debug_msg + mov r1, reg_a + mov r2, reg_x + mov r3, reg_y + bl iprintf + ldmfd sp!, {reg_pc,r0} +e: rebase_pc reg_pc +#endif + ldr pc, [pc, r0, lsl #2] + nop +op_tab: + .word _00,_01,_xx,_xx,_04,_05,_06,_07,_08,_09,_0a,_xx,_xx,_0d,_0e,_xx + .word _10,_11,_xx,_xx,_xx,_15,_16,_xx,_18,_19,_xx,_xx,_xx,_1d,_1e,_xx + .word _20,_21,_xx,_xx,_24,_25,_26,_xx,_28,_29,_2a,_xx,_2c,_2d,_2e,_xx + .word _30,_31,_xx,_xx,_xx,_35,_36,_xx,_38,_39,_xx,_xx,_xx,_3d,_3e,_xx + .word _40,_41,_xx,_xx,_xx,_45,_46,_xx,_48,_49,_4a,_xx,_4c,_4d,_4e,_xx + .word _50,_51,_xx,_xx,_xx,_55,_56,_xx,_58,_59,_xx,_xx,_xx,_5d,_5e,_xx + .word _60,_61,_xx,_xx,_xx,_65,_66,_xx,_68,_69,_6a,_xx,_6c,_6d,_6e,_xx + .word _70,_71,_xx,_xx,_xx,_75,_76,_xx,_78,_79,_7a,_xx,_xx,_7d,_7e,_xx + .word _xx,_81,_xx,_xx,_84,_85,_86,_xx,_88,_xx,_8a,_xx,_8c,_8d,_8e,_xx + .word _90,_91,_xx,_xx,_94,_95,_96,_xx,_98,_99,_9a,_xx,_xx,_9d,_xx,_xx + .word _a0,_a1,_a2,_xx,_a4,_a5,_a6,_xx,_a8,_a9,_aa,_xx,_ac,_ad,_ae,_xx + .word _b0,_b1,_xx,_xx,_b4,_b5,_b6,_b7,_b8,_b9,_ba,_xx,_bc,_bd,_be,_xx + .word _c0,_c1,_xx,_xx,_c4,_c5,_c6,_xx,_c8,_c9,_ca,_xx,_cc,_cd,_ce,_xx + .word _d0,_d1,_xx,_xx,_xx,_d5,_d6,_xx,_d8,_d9,_xx,_xx,_xx,_dd,_de,_xx + .word _e0,_e1,_xx,_xx,_e4,_e5,_e6,_xx,_e8,_e9,_ea,_xx,_ec,_ed,_ee,_xx + .word _f0,_f1,_xx,_xx,_xx,_f5,_f6,_xx,_f8,_f9,_xx,_xx,_fc,_fd,_fe,_xx +instr_end: + subs cycles, r0 + bpl loop + + mov r0, cycles + // Copy the regs back to mem + ldr r1, =cpu_regs + stmia r1, {reg_f-reg_pc} + // Return the undone cycles + ldmfd sp!, {r4-r12, pc} + +cpu_reset: + stmfd sp!, {r4-r6, lr} + ldr r4, =0xfffd + mov r0, r4 + bl readb + mov r5, r0, lsl #8 + sub r0, r4, #1 + bl readb + orr r0, r5 + ldr r6, =cpu_regs + mov r5, r0 // r5 = pc + mov r4, #0xfd // r4 = sp + rebase_pc r5 + rebase_sp r4 + mov r0, #0 // r0 = f + mov r1, #0 // r1 = y + mov r2, #0 // r2 = x + mov r3, #0 // r3 = a + stmia r6!, {r0-r5} + ldmfd sp!, {r4-r6, pc} + +cpu_regs: + .word 0,0,0,0,0,0 + +last_page: + .word 0 + +unhandled_msg: + .ascii "Unhandled op\n" +debug_msg: + .ascii "A%02x X%02x Y%02x P%04x OP %02x\n" + .byte 0 diff --git a/source/disk.c b/source/disk.c new file mode 100644 index 0000000..93d2494 --- /dev/null +++ b/source/disk.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#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; +} diff --git a/source/disk.h b/source/disk.h new file mode 100644 index 0000000..c7ff5d7 --- /dev/null +++ b/source/disk.h @@ -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 diff --git a/source/emu.c b/source/emu.c new file mode 100644 index 0000000..b54eab5 --- /dev/null +++ b/source/emu.c @@ -0,0 +1,82 @@ +#include +#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(); + } +} diff --git a/source/emu.h b/source/emu.h new file mode 100644 index 0000000..16555ad --- /dev/null +++ b/source/emu.h @@ -0,0 +1,23 @@ +#ifndef EMU_H +#define EMU_H + +#include +#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 diff --git a/source/input.c b/source/input.c new file mode 100644 index 0000000..89d85e0 --- /dev/null +++ b/source/input.c @@ -0,0 +1,52 @@ +#include +#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; + } + } +} diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..54f419f --- /dev/null +++ b/source/main.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#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; +} diff --git a/source/mem.h b/source/mem.h new file mode 100644 index 0000000..9505ed3 --- /dev/null +++ b/source/mem.h @@ -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 diff --git a/source/mem.s b/source/mem.s new file mode 100644 index 0000000..6899568 --- /dev/null +++ b/source/mem.s @@ -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 + diff --git a/source/memutil.c b/source/memutil.c new file mode 100644 index 0000000..c25309e --- /dev/null +++ b/source/memutil.c @@ -0,0 +1,39 @@ +#include +#include +#include +#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; +} + diff --git a/source/menu.c b/source/menu.c new file mode 100644 index 0000000..4d0fb42 --- /dev/null +++ b/source/menu.c @@ -0,0 +1,100 @@ +#include +#include +#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); } diff --git a/source/sound.c b/source/sound.c new file mode 100644 index 0000000..16d7f19 --- /dev/null +++ b/source/sound.c @@ -0,0 +1,46 @@ +#include +#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; + } +} diff --git a/source/state.c b/source/state.c new file mode 100644 index 0000000..84dc187 --- /dev/null +++ b/source/state.c @@ -0,0 +1,82 @@ +#include +#include +#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; +} diff --git a/source/state.h b/source/state.h new file mode 100644 index 0000000..f0fdf8a --- /dev/null +++ b/source/state.h @@ -0,0 +1,7 @@ +#ifndef STATE_H +#define STATE_H + +int state_load (int slot); +int state_save (int slot); + +#endif diff --git a/source/video.c b/source/video.c new file mode 100644 index 0000000..1b4b919 --- /dev/null +++ b/source/video.c @@ -0,0 +1,371 @@ +#include +#include +#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); +} diff --git a/source/video.h b/source/video.h new file mode 100644 index 0000000..1c6d179 --- /dev/null +++ b/source/video.h @@ -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