summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--blastem.h1
-rw-r--r--config.c23
-rw-r--r--menu.c85
-rw-r--r--menu.h18
-rw-r--r--menu.s6894
-rw-r--r--rom.db19
-rw-r--r--romdb.c51
-rw-r--r--util.c42
-rw-r--r--util.h9
10 files changed, 289 insertions, 55 deletions
diff --git a/Makefile b/Makefile
index 79c148d..aad5962 100644
--- a/Makefile
+++ b/Makefile
@@ -107,7 +107,7 @@ Z80OBJS=z80inst.o z80_to_x86.o
AUDIOOBJS=ym2612.o psg.o wave.o
CONFIGOBJS=config.o tern.o util.o
-MAINOBJS=blastem.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+MAINOBJS=blastem.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o menu.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
ifeq ($(CPU),x86_64)
CFLAGS+=-DX86_64 -m64
diff --git a/blastem.h b/blastem.h
index 43ca592..958f8b0 100644
--- a/blastem.h
+++ b/blastem.h
@@ -24,6 +24,7 @@ typedef struct {
psg_context *psg;
uint16_t *work_ram;
uint8_t *zram;
+ void *extra;
uint8_t *save_storage;
eeprom_map *eeprom_map;
uint32_t num_eeprom;
diff --git a/config.c b/config.c
index fa1d6cb..a4187d5 100644
--- a/config.c
+++ b/config.c
@@ -43,7 +43,7 @@ tern_node * parse_config_int(char **state, int started, int *line)
config_data = started ? NULL : *state;
while ((curline = strtok_r(config_data, "\n", state)))
{
-
+
config_data = NULL;
curline = strip_ws(curline);
int len = strlen(curline);
@@ -61,7 +61,7 @@ tern_node * parse_config_int(char **state, int started, int *line)
}
fatal_error("unexpected } on line %d\n", *line);
}
-
+
char * end = curline + len - 1;
if (*end == '{') {
*end = 0;
@@ -128,7 +128,7 @@ tern_node * parse_config_file_assets(char *config_path)
if (!config_size) {
goto config_empty;
}
-
+
char * config_data = malloc(config_size+1);
if (SDL_RWread(rw, config_data, 1, config_size) != config_size) {
goto config_read_fail;
@@ -152,12 +152,12 @@ tern_node * load_config()
if (ret) {
return ret;
}
-
+
ret = parse_config_file_assets("default.cfg");
if (ret) {
return ret;
}
-
+
fatal_error("Failed to find a config file in internal storage or in the blastem APK\n");
//this will never get reached, but the compiler doesn't know that. Let's make it happy
return NULL;
@@ -169,25 +169,26 @@ tern_node * load_config()
{
char * exe_dir;
char * home = get_home_dir();
- if (home) {
+ tern_node *ret;
+ if (home) {
char * path = alloc_concat(home, "/.config/blastem/blastem.cfg");
- tern_node * ret = parse_config_file(path);
+ ret = parse_config_file(path);
free(path);
if (ret) {
return ret;
}
}
-
+
exe_dir = get_exe_dir();
- if (exe_dir) {
- path = alloc_concat(exe_dir, "/default.cfg");
+ if (exe_dir) {
+ char *path = alloc_concat(exe_dir, "/default.cfg");
ret = parse_config_file(path);
free(path);
if (ret) {
return ret;
}
}
-
+
fatal_error("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n");
//this will never get reached, but the compiler doesn't know that. Let's make it happy
return NULL;
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..aaf45cc
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,85 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "blastem.h"
+#include "menu.h"
+#include "backend.h"
+#include "util.h"
+
+
+uint16_t menu_read_w(uint32_t address, void * context)
+{
+ //This should return the status of the last request with 0
+ //meaning either the request is complete or no request is pending
+ //in the current implementation, the operations happen instantly
+ //in emulated time so we can always return 0
+ return 0;
+}
+
+void * menu_write_w(uint32_t address, void * context, uint16_t value)
+{
+ m68k_context *m68k = context;
+ genesis_context *gen = m68k->system;
+ menu_context *menu = gen->extra;
+ if (!menu) {
+ gen->extra = menu = calloc(1, sizeof(menu_context));
+ menu->curpath = strdup(get_home_dir());
+ }
+ if (menu->state) {
+ uint32_t dst = menu->latch << 16 | value;
+ switch (address >> 2)
+ {
+ case 0: {
+ size_t num_entries;
+ dir_entry *entries = get_dir_list(menu->curpath, &num_entries);
+ for (size_t i = 0; i < num_entries; i++)
+ {
+ uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
+ if (!dest) {
+ break;
+ }
+ *(dest++) = entries[i].is_dir;
+ *(dest++) = 1;
+ dst += 2;
+ uint8_t term = 0;
+ for (char *cpos = entries[i].name; *cpos; cpos++)
+ {
+ dest[1] = *cpos;
+ dest[0] = cpos[1];
+ if (cpos[1]) {
+ cpos++;
+ } else {
+ term = 1;
+ }
+ dst += 2;
+ if (!(dst & 0xFFFF)) {
+ //we may have walked off the end of a memory block, get a fresh native pointer
+ dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
+ if (!dest) {
+ break;
+ }
+ } else {
+ dest += 2;
+ }
+ }
+ if (!term) {
+ *(dest++) = 0;
+ *dest = 0;
+ dst += 2;
+ }
+ }
+ free_dir_list(entries, num_entries);
+ break;
+ }
+ default:
+ fprintf(stderr, "WARNING: write to undefined menu port %X\n", address);
+ }
+ menu->state = 0;
+ } else {
+ menu->latch = value;
+ menu->state = 1;
+ }
+
+ return context;
+}
diff --git a/menu.h b/menu.h
new file mode 100644
index 0000000..11b5201
--- /dev/null
+++ b/menu.h
@@ -0,0 +1,18 @@
+/*
+ Copyright 2015 Michael Pavone
+ This file is part of BlastEm.
+ BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
+*/
+#ifndef MENU_H_
+#define MENU_H_
+typedef struct {
+ char *curpath;
+ uint16_t latch;
+ uint16_t state;
+} menu_context;
+
+
+uint16_t menu_read_w(uint32_t address, void * context);
+void * menu_write_w(uint32_t address, void * context, uint16_t value);
+
+#endif // MENU_H_
diff --git a/menu.s68 b/menu.s68
index 342ce85..ec84810 100644
--- a/menu.s68
+++ b/menu.s68
@@ -84,7 +84,7 @@
dc.b "Menu "
dc.b " "
dc.b " "
- dc.b "GM MK-00000-00", 0, 0
+ dc.b "MP BlstMenu-00", 0, 0
dc.b " "
dc.l $0, rom_end-1, $FF0000, $FFFFFF
dc.b " "
@@ -92,7 +92,7 @@
dc.b " "
dc.b " "
dc.b "JUE "
-
+
;register addresses
VDP_DATA equ $C00000
VDP_CTRL equ $C00004
@@ -139,21 +139,21 @@ vdpregset macro
vdpreg macro
dc.w (((\1) << 8) | $8000 | (\2))
endm
-
+
;Writes a normal VDP command to the control port
;\1 - VDP address
;\2 - Access type
vdpaccess macro
move.l #((\2) | (\1) << 16 & $3FFF0000 | (\1) >> 14 & 3), (a1)
endm
-
+
;Writes a DMA command to the control port
;\1 - Destination address
;\2 - Destination type
startdma macro
move.l #(\2 | VDP_DMA_FLAG | (\1 << 16) & $3FFF0000 | (\1 >> 14) & 3), (a1)
endm
-
+
DMA_SRC_68K equ 0
DMA_SRC_VRAM equ $C0
DMA_SRC_FILL equ $80
@@ -162,14 +162,19 @@ dmasrc macro
move.l #($95009600 + (\1) << 15 & $FF0000 + (\1) >> 9 & $FF), (a1)
move.w #($9700 + (\1) >> 17 & $7F | (\2)), (a1)
endm
-
+
+dir_buffer equ $100000
+menu_port equ $180000
+
+MAX_DISPLAY equ 24
+
rsset $FFFF8000
x_pos rs.w 1
base_cmd rs.l 1
sprite_list rs.l 160
num_sprites rs.b 1
last_pad rs.b 1
-
+
int_6:
dmasrc sprite_list, DMA_SRC_68K
;set DMA length
@@ -184,7 +189,7 @@ int_6:
move.b d1, d0
move.l d0, (a1)
startdma $C000, VDP_VRAM_WRITE
-
+
lea PAD1_DATA, a2
move.b #$40, (a2)
move.b (a2), d0
@@ -200,14 +205,14 @@ int_6:
eor.b d0, d1
and.b d0, d1
move.b d0, (last_pad).w
-
+
moveq #16, d2
btst #1, d1
bne down
btst #0, d1
bne up
rte
-
+
down:
add.w d2, (sprite_list).w
add.w d2, (sprite_list+8).w
@@ -216,11 +221,11 @@ up:
sub.w d2, (sprite_list).w
sub.w d2, (sprite_list+8).w
rte
-
+
int_4:
empty_handler:
rte
-
+
initial_regs:
@@ -241,13 +246,13 @@ end_initial_regs
start:
lea $C00000, a0
lea $C00004, a1
-
+
moveq #(end_initial_regs-initial_regs-1), d0
lea initial_regs.w, a2
.regloop
move.w (a2)+, (a1)
dbra d0, .regloop
-
+
vdpaccess $0, VDP_CRAM_WRITE
move.w #$020, (a0)
move.w #$EEE, (a0)
@@ -277,43 +282,76 @@ tloop:
ploop:
move.l d0, (a0)
dbra d1, ploop
-
+
;setup SAT
;;vdpaccess $C000, VDP_VRAM_WRITE
-
+
lea sprite_list.w, a2
;left arrow
move.l #$01080501, (a2)+
move.l #$807F0086, (a2)+
-
+
;right arrow
move.l #$01080500, (a2)+
move.l #$887F01AA, (a2)+
move.b #2, num_sprites.w
-
+
move.l #$40860002, d3
move.l d3, (a1)
move.l d3, base_cmd.w
- lea Message(pc), a6
+ lea dir_buffer, a6
+ lea menu_port, a2
+ move.l a6, (a2)
+
+wait_complete:
+ tst.w (a2)
+ bne wait_complete
+
+ moveq #MAX_DISPLAY-1, d7
+file_loop:
+ tst.b (a6)+
+ addq #1, a6 ;TODO: Do something with directory flag
+
+ cmp.b #$2E, (a6)
+ bne normal
+ cmp.b #$2E, (1, a6)
+ beq normal
+ addq #1, a6
+.skip_loop:
+ tst.b (a6)+
+ bne .skip_loop
+ addq #1, d7
+ move.l a6, d6
+ bra skip
+normal:
moveq #0, d0
bsr print_string
-
+ move.l a6, d6
+
+ lea Newline(pc), a6
+ bsr print_string
+
+skip:
+ ;word align pointer
+ addq #1, d6
+ and.w #$FFFE, d6
+ move.l d6, a6
+
+ dbra d7, file_loop
+
;setup gamepad in port 1
move.b #$40, PAD1_CTRL
-
+
move.w #$8174, (a1) ;enable display, vertical interrupts, DMA
-
+
wait_forever
stop #2500
bra wait_forever
-Message:
- dc.b "Journey From Darkness - Strider Returns (U) [c][!].bin", $A
- dc.b "Toejam & Earl in Panic on Funkotron (U) [!].bin", $A
- dc.b "Fire Shark (U) [c][!].bin", $A
- dc.b "Sonic and Knuckles (W) [!].bin", 0
+Newline:
+ dc.b $A, 0
align 1
@@ -378,5 +416,5 @@ widths:
dc.b 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1
dc.b 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
dc.b 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1
-
+
rom_end:
diff --git a/rom.db b/rom.db
index 57f448c..4e010d4 100644
--- a/rom.db
+++ b/rom.db
@@ -353,4 +353,21 @@ T-12056 {
offset 80000
}
}
-} \ No newline at end of file
+}
+BlstMenu {
+ map {
+ 0 {
+ device ROM
+ last FFFFF
+ }
+ 100000 {
+ device RAM
+ size 80000
+ last 17FFFF
+ }
+ 180000 {
+ device MENU
+ last 1FFFFF
+ }
+ }
+}
diff --git a/romdb.c b/romdb.c
index bf89f88..76ddbde 100644
--- a/romdb.c
+++ b/romdb.c
@@ -4,6 +4,7 @@
#include "romdb.h"
#include "util.h"
#include "blastem.h"
+#include "menu.h"
#define TITLE_START 0x150
#define TITLE_END (TITLE_START+48)
@@ -335,7 +336,7 @@ void * write_eeprom_i2c_b(uint32_t address, void * context, uint8_t value)
if (!map) {
fatal_error("Could not find EEPROM map for address %X\n", address);
}
-
+
uint16_t expanded, mask;
if (address & 1) {
expanded = value;
@@ -405,7 +406,7 @@ char *get_header_name(uint8_t *rom)
{
uint8_t *last = rom + TITLE_END - 1;
uint8_t *src = rom + TITLE_START;
-
+
while (last > src && (*last <= 0x20 || *last >= 0x80))
{
last--;
@@ -490,24 +491,24 @@ void add_memmap_header(rom_info *info, uint8_t *rom, uint32_t size, memmap_chunk
}
info->save_size = save_size;
info->save_buffer = malloc(save_size);
-
+
info->map_chunks = base_chunks + (ram_start >= rom_end ? 2 : 3);
info->map = malloc(sizeof(memmap_chunk) * info->map_chunks);
memset(info->map, 0, sizeof(memmap_chunk)*2);
memcpy(info->map+2, base_map, sizeof(memmap_chunk) * base_chunks);
-
+
if (ram_start >= rom_end) {
info->map[0].end = rom_end < 0x400000 ? nearest_pow2(rom_end) - 1 : 0xFFFFFF;
//TODO: ROM mirroring
info->map[0].mask = 0xFFFFFF;
info->map[0].flags = MMAP_READ;
info->map[0].buffer = rom;
-
+
info->map[1].start = ram_start;
info->map[1].mask = info->save_mask;
info->map[1].end = ram_end + 1;
info->map[1].flags = MMAP_READ | MMAP_WRITE;
-
+
if (ram_flags == RAM_FLAG_ODD) {
info->map[1].flags |= MMAP_ONLY_ODD;
} else if (ram_flags == RAM_FLAG_EVEN) {
@@ -520,7 +521,7 @@ void add_memmap_header(rom_info *info, uint8_t *rom, uint32_t size, memmap_chunk
info->map[0].mask = 0xFFFFFF;
info->map[0].flags = MMAP_READ;
info->map[0].buffer = rom;
-
+
info->map[1].start = 0x200000;
info->map[1].end = 0x400000;
info->map[1].mask = 0x1FFFFF;
@@ -531,7 +532,7 @@ void add_memmap_header(rom_info *info, uint8_t *rom, uint32_t size, memmap_chunk
info->map[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area
info->map[1].write_8 = (write_8_fun)write_sram_area_b;
info->map[1].buffer = cart + 0x200000;
-
+
memmap_chunk *last = info->map + info->map_chunks - 1;
memset(last, 0, sizeof(memmap_chunk));
last->start = 0xA13000;
@@ -545,7 +546,7 @@ void add_memmap_header(rom_info *info, uint8_t *rom, uint32_t size, memmap_chunk
info->map = malloc(sizeof(memmap_chunk) * info->map_chunks);
memset(info->map, 0, sizeof(memmap_chunk));
memcpy(info->map+1, base_map, sizeof(memmap_chunk) * base_chunks);
-
+
info->map[0].end = rom_end > 0x400000 ? rom_end : 0x400000;
info->map[0].mask = rom_end < 0x400000 ? nearest_pow2(rom_end) - 1 : 0xFFFFFF;
info->map[0].flags = MMAP_READ;
@@ -703,7 +704,7 @@ void map_iter_fun(char *key, tern_val val, void *data)
} else if (!strcmp(dtype, "EEPROM")) {
process_eeprom_def(key, state);
add_eeprom_map(node, start, end, state);
-
+
map->write_16 = write_eeprom_i2c_w;
map->write_8 = write_eeprom_i2c_b;
map->read_16 = read_eeprom_i2c_w;
@@ -719,6 +720,22 @@ void map_iter_fun(char *key, tern_val val, void *data)
map->flags |= MMAP_ONLY_EVEN;
}
map->mask = calc_mask(state->info->save_size, start, end);
+ } else if (!strcmp(dtype, "RAM")) {
+ uint32_t size = strtol(tern_find_ptr_default(node, "size", "0"), NULL, 16);
+ if (!size || size > map->end - map->start) {
+ size = map->end - map->start;
+ }
+ map->buffer = malloc(size);
+ map->mask = calc_mask(size, start, end);
+ map->flags = MMAP_READ | MMAP_WRITE;
+ char *bus = tern_find_ptr_default(node, "bus", "both");
+ if (!strcmp(dtype, "odd")) {
+ map->flags |= MMAP_ONLY_ODD;
+ } else if (!strcmp(dtype, "even")) {
+ map->flags |= MMAP_ONLY_EVEN;
+ } else {
+ map->flags |= MMAP_CODE;
+ }
} else if (!strcmp(dtype, "Sega mapper")) {
state->info->mapper_start_index = state->ptr_index++;
state->info->map_chunks+=7;
@@ -760,6 +777,12 @@ void map_iter_fun(char *key, tern_val val, void *data)
map->mask = 0xFF;
map->write_16 = (write_16_fun)write_bank_reg_w;
map->write_8 = (write_8_fun)write_bank_reg_b;
+ } else if (!strcmp(dtype, "MENU")) {
+ //fake hardware for supporting menu
+ map->buffer = NULL;
+ map->mask = 0xFF;
+ map->write_16 = menu_write_w;
+ map->read_16 = menu_read_w;
} else {
fatal_error("Invalid device type for ROM DB map entry %d with address %s\n", state->index, key);
}
@@ -778,7 +801,7 @@ rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_
break;
}
product_id[i] = rom[GAME_ID_OFF + i];
-
+
}
printf("Product ID: %s\n", product_id);
tern_node * entry = tern_find_ptr(rom_db, product_id);
@@ -794,7 +817,7 @@ rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_
} else {
info.name = get_header_name(rom);
}
-
+
char *dbreg = tern_find_ptr(entry, "regions");
info.regions = 0;
if (dbreg) {
@@ -806,7 +829,7 @@ rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_
if (!info.regions) {
info.regions = get_header_regions(rom);
}
-
+
tern_node *map = tern_find_ptr(entry, "map");
if (map) {
info.save_type = SAVE_NONE;
@@ -828,6 +851,6 @@ rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_
} else {
add_memmap_header(&info, rom, rom_size, base_map, base_chunks);
}
-
+
return info;
}
diff --git a/util.c b/util.c
index 4b1e8c7..c4b25f5 100644
--- a/util.c
+++ b/util.c
@@ -11,6 +11,7 @@
#include "blastem.h" //for headless global
#include "render.h" //for render_errorbox
+#include "util.h"
char * alloc_concat(char * first, char * second)
{
@@ -277,5 +278,46 @@ fallback:
}
return exe_dir;
}
+#include <dirent.h>
+
+dir_entry *get_dir_list(char *path, size_t *numret)
+{
+ DIR *d = opendir(path);
+ if (!d) {
+ if (numret) {
+ *numret = 0;
+ }
+ return NULL;
+ }
+ size_t storage = 64;
+ dir_entry *ret = malloc(sizeof(dir_entry) * storage);
+ size_t pos = 0;
+ struct dirent* entry;
+ while (entry = readdir(d))
+ {
+ if (entry->d_type != DT_REG && entry->d_type != DT_LNK && entry->d_type != DT_DIR) {
+ continue;
+ }
+ if (pos == storage) {
+ storage = storage * 2;
+ ret = realloc(ret, sizeof(dir_entry) * storage);
+ }
+ ret[pos].name = strdup(entry->d_name);
+ ret[pos++].is_dir = entry->d_type == DT_DIR;
+ }
+ if (numret) {
+ *numret = pos;
+ }
+ return ret;
+}
+
+void free_dir_list(dir_entry *list, size_t numentries)
+{
+ for (size_t i = 0; i < numentries; i++)
+ {
+ free(list[i].name);
+ }
+ free(list);
+}
#endif
diff --git a/util.h b/util.h
index ffa3385..24750e9 100644
--- a/util.h
+++ b/util.h
@@ -3,6 +3,11 @@
#include <stdio.h>
+typedef struct {
+ char *name;
+ uint8_t is_dir;
+} dir_entry;
+
//Utility functions
//Allocates a new string containing the concatenation of first and second
@@ -23,6 +28,10 @@ void set_exe_str(char * str);
char * get_exe_dir();
//Returns the user's home directory
char * get_home_dir();
+//Retunrs an array of normal files and directories residing in a directory
+dir_entry *get_dir_list(char *path, size_t *numret);
+//Frees a dir list returned by get_dir_list
+void free_dir_list(dir_entry *list, size_t numentries);
//Returns the contents of a symlink in a newly allocated string
char * readlink_alloc(char * path);
//Prints an error message to stderr and to a message box if not in headless mode and then exits