├── .gitignore ├── Makefile ├── Unununium.app └── Contents │ ├── Info.plist │ └── Resources │ └── Unununium.icns ├── audio.c ├── audio.h ├── board-BAT.c ├── board-VII.c ├── board-V_X.c ├── board-W60.c ├── board-WAL.c ├── board-dummy.c ├── board.c ├── board.h ├── config.example ├── dialog-cocoa.m ├── dialog.h ├── disas.c ├── disas.h ├── emu.c ├── emu.h ├── i2c-bus.c ├── i2c-eeprom.c ├── i2c.h ├── io.c ├── io.h ├── major.c ├── platform-sdl.c ├── platform.h ├── render-gl.c ├── render-soft.c ├── render.c ├── render.h ├── timer.c ├── timer.h ├── types.h ├── un-disas.c ├── un-dump.c ├── uuu-sdl.c ├── uuu.xcf ├── video.c └── video.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | un-disas 3 | uuu-sdl 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include config 2 | 3 | 4 | CFLAGS += -std=gnu99 -Wall -W -O2 -g -Wmissing-declarations -ffast-math 5 | CFLAGS += -DRENDER_$(RENDER) 6 | LDFLAGS := -g 7 | 8 | ifeq ($(USE_DEBUG),yes) 9 | CFLAGS += -DUSE_DEBUG 10 | endif 11 | 12 | 13 | # Default targets. 14 | .PHONY: all 15 | all: un-disas uuu-$(PLATFORM) 16 | 17 | # The disassembler. 18 | un-disas: un-disas.o disas.o 19 | 20 | # The emulator. 21 | uuu-$(PLATFORM): uuu-%: uuu-%.o platform-%.o \ 22 | disas.o emu.o timer.o video.o render.o render-$(RENDER).o \ 23 | audio.o io.o i2c-bus.o i2c-eeprom.o board.o \ 24 | board-VII.o board-W60.o board-WAL.o board-BAT.o board-V_X.o board-dummy.o 25 | 26 | # Is this compiler targetting MacOSX? 27 | is-osx = $(shell set -e; \ 28 | if ($(CC) -E -dM - | grep __APPLE__) /dev/null 2>&1; \ 29 | then echo "yes" ; fi; ) 30 | 31 | # SDL needs special compiler flags and some libraries. 32 | platform-sdl.o uuu-sdl.o: CFLAGS += $(shell sdl-config --cflags) 33 | uuu-sdl: LDFLAGS += $(shell sdl-config --libs) 34 | 35 | ifeq ($(RENDER),gl) 36 | ifeq ($(is-osx),yes) 37 | uuu-sdl: LDFLAGS += -Wl,-framework,OpenGL 38 | else 39 | uuu-sdl: LDFLAGS += -lGL 40 | endif 41 | endif 42 | 43 | # If MacOSX, use Cocoa dialogs, and create an application bundle. 44 | ifeq ($(is-osx),yes) 45 | uuu-sdl: dialog-cocoa.o 46 | 47 | %.o: %.m 48 | $(CC) $(CFLAGS) -o $@ -c $< 49 | 50 | all: .stamp-bundle 51 | 52 | .stamp-bundle: uuu-sdl 53 | -mkdir -p Unununium.app/Contents/Resources/ROMs 54 | -mkdir -p Unununium.app/Contents/MacOS 55 | cp uuu-sdl Unununium.app/Contents/MacOS/ 56 | touch $@ 57 | endif 58 | 59 | # Laziness rules, and lazy rules rule most of all. 60 | *.o: *.h Makefile config 61 | 62 | # Clean up. 63 | .PHONY: clean 64 | clean: 65 | rm -f un-disas un-disas.o disas.o 66 | rm -f emu.o timer.o video.o audio.o io.o 67 | rm -f i2c-bus.o i2c-eeprom.o 68 | rm -f board.o board-VII.o board-W60.o board-WAL.o board-BAT.o board-V_X.o board-dummy.o 69 | rm -f render.o render-gl.o render-soft.o 70 | rm -f uuu-sdl uuu-sdl.o platform-sdl.o dialog-cocoa.o 71 | -------------------------------------------------------------------------------- /Unununium.app/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | Unununium 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | uuu-sdl 11 | CFBundleIconFile 12 | Unununium.icns 13 | CFBundleIdentifier 14 | segher.unununium 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | Uuu! 21 | CFBundleVersion 22 | 0.1 23 | NSHumanReadableCopyright 24 | Copyright 2008,2009 Segher Boessenkool <segher@kernel.crashing.org> 25 | 26 | 27 | -------------------------------------------------------------------------------- /Unununium.app/Contents/Resources/Unununium.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RebeccaRGB/unununium/e43804bc52ce1ec1c758946b67bea62201e52bae/Unununium.app/Contents/Resources/Unununium.icns -------------------------------------------------------------------------------- /audio.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | #include "emu.h" 10 | #include "platform.h" 11 | 12 | #include "audio.h" 13 | 14 | 15 | int mute_audio; 16 | 17 | 18 | void audio_store(u16 val, u32 addr) 19 | { 20 | if (addr == 0x340b) 21 | mem[addr] &= ~val; 22 | else 23 | mem[addr] = val; 24 | 25 | if (addr < 0x3200) { // XXX 26 | return; 27 | } else if (addr < 0x3400) { // XXX 28 | return; 29 | } else { // XXX control regs 30 | return; 31 | } 32 | 33 | debug("AUDIO STORE %04x to %04x\n", val, addr); 34 | } 35 | 36 | u16 audio_load(u32 addr) 37 | { 38 | u16 val = mem[addr]; 39 | 40 | if (addr >= 0x3000 && addr < 0x3200) { // audio something 41 | //debug("LOAD %04x from %04x\n", val, addr); 42 | return val; 43 | } 44 | if (addr >= 0x3200 && addr < 0x3300) { // audio something 45 | //debug("LOAD %04x from %04x\n", val, addr); 46 | return val; 47 | } 48 | if (addr >= 0x3400 && addr < 0x3500) { // audio something 49 | //debug("LOAD %04x from %04x\n", val, addr); 50 | return val; 51 | } 52 | 53 | debug("UNKNOWN AUDIO LOAD from %04x\n", addr); 54 | return val; 55 | } 56 | 57 | static u32 get_channel_bit(u32 ch, u32 reg) 58 | { 59 | reg += 0x3400; 60 | if (ch >= 16) { 61 | reg += 0x0100; 62 | ch -= 16; 63 | } 64 | return (mem[reg] >> ch) & 1; 65 | } 66 | 67 | static u32 n_left[24]; 68 | static u16 bits_left[24]; 69 | static void do_next_sample(u32 ch) 70 | { 71 | loop: 72 | if (n_left[ch] == 0) { 73 | u32 addr = ((mem[0x3001 + 16*ch] & 0x3f) << 16) | mem[0x3000 + 16*ch]; 74 | bits_left[ch] = mem[addr]; 75 | if (bits_left[ch] == 0xffff) { 76 | mem[0x3000 + 16*ch] = mem[0x3002 + 16*ch]; 77 | mem[0x3001 + 16*ch] &= 0xffc0; 78 | mem[0x3001 + 16*ch] |= (mem[0x3001 + 16*ch] >> 6) & 0x3f; 79 | goto loop; 80 | } 81 | n_left[ch] = 2; 82 | mem[0x3000 + 16*ch]++; 83 | if (mem[0x3000 + 16*ch] == 0) 84 | mem[0x3001 + 16*ch]++; // XXX: mask? 85 | //debug("--> addr = %06x\n", addr); 86 | } 87 | 88 | mem[0x300b + 16*ch] = bits_left[ch] << 8; 89 | bits_left[ch] >>= 8; 90 | n_left[ch]--; 91 | } 92 | 93 | #define FREQ 440 94 | static u16 next_channel_sample(u32 ch) 95 | { 96 | // if (ch) 97 | // return 0x8000; 98 | 99 | // static u32 xxx[24] = {0}; 100 | // u16 sample = 0x8000 + (int)(20000*sin(2*M_PI*xxx[ch]*FREQ/281250)); 101 | // xxx[ch]++; 102 | // if (xxx[ch] == 281250) 103 | // xxx[ch] = 0; 104 | // return sample; 105 | 106 | u16 sample = mem[0x300b + 16*ch]; 107 | u32 acc = (mem[0x3201 + 16*ch] << 16) | mem[0x3205 + 16*ch]; 108 | u32 inc = (mem[0x3200 + 16*ch] << 16) | mem[0x3204 + 16*ch]; 109 | acc += inc; 110 | if (acc >= 0x80000) { 111 | do_next_sample(ch); 112 | acc -= 0x80000; 113 | } 114 | mem[0x3201 + 16*ch] = acc >> 16; 115 | mem[0x3205 + 16*ch] = acc; 116 | 117 | return sample; 118 | } 119 | 120 | static u16 next_sample(void) 121 | { 122 | static u32 ch = 0; 123 | u16 sample; 124 | if (get_channel_bit(ch, 0)) 125 | sample = next_channel_sample(ch); 126 | else 127 | sample = 0x8000; 128 | ch++; 129 | if (ch == 24) 130 | ch = 0; 131 | return sample; 132 | } 133 | 134 | static s16 next_host_sample(void) 135 | { 136 | if (mem[0x3d20] & 0x0002) // audio DAC disable 137 | return 0; 138 | 139 | return 0; 140 | u32 acc = 0; 141 | u32 i; 142 | for (i = 0; i < 153; i++) 143 | acc += next_sample(); 144 | return (acc / 153) - 0x8000; 145 | } 146 | 147 | void audio_render(s16 *data, u32 n) 148 | { 149 | u32 i; 150 | for (i = 0; i < n; i++) 151 | data[i] = next_host_sample(); 152 | } 153 | 154 | void audio_init(void) 155 | { 156 | } 157 | -------------------------------------------------------------------------------- /audio.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008,2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _AUDIO_H 6 | #define _AUDIO_H 7 | 8 | #include "types.h" 9 | 10 | 11 | extern int mute_audio; 12 | 13 | 14 | void audio_store(u16 val, u32 addr); 15 | u16 audio_load(u32 addr); 16 | 17 | void audio_render(s16 *data, u32 n); 18 | 19 | void audio_init(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /board-BAT.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | 7 | #include "types.h" 8 | #include "emu.h" 9 | #include "platform.h" 10 | #include "i2c.h" 11 | 12 | #include "board.h" 13 | 14 | 15 | static struct i2c_bus *i2c_bus; 16 | 17 | static u16 gpio(u32 n, u16 what, __unused u16 push, __unused u16 pull, __unused u16 special) 18 | { 19 | // debug("---> PORT %c what=%04x push=%04x pull=%04x\n", 20 | // 'A' + n, what, push, pull); 21 | 22 | if (n == 0) { 23 | if (button_up) 24 | what |= 0x8000; 25 | if (button_down) 26 | what |= 0x4000; 27 | if (button_left) 28 | what |= 0x2000; 29 | if (button_right) 30 | what |= 0x1000; 31 | if (button_A) 32 | what |= 0x0800; 33 | if (button_menu) 34 | what |= 0x0400; 35 | if (button_B) 36 | what |= 0x0200; 37 | if (button_C) 38 | what |= 0x0100; 39 | } 40 | 41 | if (n == 2) { 42 | int sda = what & 1; 43 | int scl = (what >> 1) & 1; 44 | //debug("SDA=%d SCL=%d\n", sda, scl); 45 | sda = i2c_bitbang(i2c_bus, sda, scl); 46 | what = (what & ~1) | sda; 47 | } 48 | 49 | return what; 50 | } 51 | 52 | static void init(void) 53 | { 54 | i2c_bus = i2c_bitbang_bus_create(); 55 | i2c_eeprom_create(i2c_bus, 0x200, 0xa0, "BAT"); 56 | } 57 | 58 | struct board board_BAT = { 59 | .idle_pc = 0x5ce1, 60 | .use_centered_coors = 1, 61 | 62 | .init = init, 63 | .gpio = gpio 64 | }; 65 | -------------------------------------------------------------------------------- /board-VII.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | #include "emu.h" 10 | #include "platform.h" 11 | 12 | #include "board.h" 13 | 14 | 15 | static u32 current_bank = -1; 16 | static int controller_should_be_rotated; 17 | 18 | static void switch_bank(u32 bank) 19 | { 20 | if (bank == current_bank) 21 | return; 22 | current_bank = bank; 23 | 24 | render_kill_cache(); 25 | 26 | read_rom(N_MEM*bank); 27 | // memset(ever_ran_this, 0, N_MEM); 28 | 29 | board->idle_pc = 0; 30 | if (mem[0x19792] == 0x4311 && mem[0x19794] == 0x4e43) // VII bank 0 31 | board->idle_pc = 0x19792; 32 | if (mem[0x21653] == 0x4311 && mem[0x21655] == 0x4e43) // VII bank 2 33 | board->idle_pc = 0x21653; 34 | if (mem[0x42daa] == 0x4311 && mem[0x42dac] == 0x4e43) { // VC1 35 | board->idle_pc = 0x42daa; 36 | controller_should_be_rotated = 1; 37 | //mem[0x38ecf] = 0; // no life lost in LP 38 | //mem[0x38eb1] = mem[0xb8eb0]; // no upgrades lost in LP 39 | } 40 | if (mem[0x3ecb9] == 0x4311 && mem[0x3ecbb] == 0x4e43) { // VC2 41 | board->idle_pc = 0x3ecb9; 42 | controller_should_be_rotated = 1; 43 | } 44 | } 45 | 46 | static void init(void) 47 | { 48 | switch_bank(0); 49 | } 50 | 51 | static u16 gpio(u32 n, u16 what, __unused u16 push, __unused u16 pull, __unused u16 special) 52 | { 53 | if (n == 1) { 54 | debug("STORE %04x to port B\n", what); 55 | u32 bank = ((what & 0x80) >> 7) | ((what & 0x20) >> 4); 56 | switch_bank(bank); 57 | debug("switched to bank %x\n", bank); 58 | } 59 | 60 | return what; 61 | } 62 | 63 | static void uart_send(__unused u8 x) 64 | { 65 | //debug("--- uart_send(%02x)\n", x); 66 | } 67 | 68 | static u32 controller_in_count; 69 | static u8 controller_input[8]; 70 | 71 | static u8 uart_recv(void) 72 | { 73 | if (controller_in_count == 0) { 74 | u8 buttons = 0; 75 | if (controller_should_be_rotated) { 76 | if (button_up) 77 | buttons |= 0x08; 78 | if (button_down) 79 | buttons |= 0x04; 80 | if (button_left) 81 | buttons |= 0x01; 82 | if (button_right) 83 | buttons |= 0x02; 84 | } else { 85 | if (button_up) 86 | buttons |= 0x01; 87 | if (button_down) 88 | buttons |= 0x02; 89 | if (button_left) 90 | buttons |= 0x04; 91 | if (button_right) 92 | buttons |= 0x08; 93 | } 94 | if (button_A) 95 | buttons |= 0x10; 96 | if (button_B) 97 | buttons |= 0x20; 98 | 99 | controller_input[0] = buttons; 100 | 101 | u32 x = random() & 0x3ff; 102 | u32 y = random() & 0x3ff; 103 | u32 z = random() & 0x3ff; 104 | 105 | controller_input[1] = x; 106 | controller_input[2] = y; 107 | controller_input[3] = z; 108 | 109 | x >>= 8; 110 | y >>= 8; 111 | z >>= 8; 112 | 113 | controller_input[4] = 0; 114 | controller_input[5] = (z << 4) | (y << 2) | x; 115 | 116 | controller_input[6] = 0xff; 117 | controller_input[7] = 0; 118 | 119 | controller_in_count = 8; 120 | } 121 | 122 | u8 x = controller_input[8 - controller_in_count]; 123 | controller_in_count--; 124 | 125 | return x; 126 | } 127 | 128 | struct board board_VII = { 129 | .use_centered_coors = 1, 130 | 131 | .init = init, 132 | .gpio = gpio, 133 | .uart_send = uart_send, 134 | .uart_recv = uart_recv 135 | }; 136 | -------------------------------------------------------------------------------- /board-V_X.c: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | 6 | // IOA not connected 7 | 8 | // IOB0 CS#1 = a3; b3 = ROMCS#; o3 = cart CS1# 9 | // IOB1 CS#2 = cart CS2# 10 | // IOB2 CS#3 = a0; b0 = cart det (high if cart, pull to ROMCS#); o0 = sys CS# 11 | // IOB3 reset switch 12 | // IOB4 cart front 24? 13 | // IOB5 power off 14 | // IOB6 power off switch 15 | // IOB7 power on switch 16 | 17 | // IOC0 JP13 \ version jumper 18 | // IOC1 JP12 | 19 | // IOC2 JP11 | 20 | // IOC3 JP10 / 21 | // IOC4 JP14 logo 22 | // IOC5 test point 23 | // IOC6 amp power 24 | // IOC7 system reset 25 | // IOC8 controller pin 8 controller select, high active 26 | // IOC9 controller pin 5 " " 27 | // IOC10 controller pin 9 (also extint1) 28 | // IOC11 N/C? 29 | // IOC12 controller pin 6 (also extint2) 30 | // IOC13 controller pins 4 and 7 ANDed together, pullups 31 | // IOC14 controller pin 2, through a buffer 32 | // IOC15 power / charger / whatever enable (top left) 33 | 34 | // controller: 35 | // pin1 ground 36 | // pin2 system->controller 37 | // pin3 power 38 | // pin4 controller->system 39 | // pin5 * 40 | // pin6 * 41 | // pin7 controller->system 42 | // pin8 * 43 | // pin9 * 44 | 45 | // controller messages: 46 | // 90+X: colour keys: 8=red 4=yellow 2=blue 1=green 0=release 47 | // a0+X: other keys: 1=ok 2=quit 3=help 4=abc 0=release (1 for pen as well) 48 | // 80+X: up: 3..7, 0=release stick (keypad uses 3) 49 | // 88+X: down: 3..7 50 | // c8+X: left: 3..7 51 | // c0+X: right: 3..7 52 | 53 | 54 | #include 55 | 56 | #include "types.h" 57 | #include "emu.h" 58 | #include "platform.h" 59 | 60 | #include "board.h" 61 | 62 | 63 | static int trace_gpio = 0; 64 | 65 | static void init(void) 66 | { 67 | if (mem[0x5675c] == 0x9311 && mem[0x5675e] == 0x4240 && // Winnie 68 | mem[0x5675f] == 0x4e44) 69 | board->idle_pc = 0x5675c; 70 | 71 | // else 72 | // board->idle_pc = 0x4003a; // studio, FIXME 73 | } 74 | 75 | static u32 bit(u16 x, u32 n) 76 | { 77 | return (x >> n) & 1; 78 | } 79 | 80 | static u16 gpio(u32 n, u16 what, u16 push, u16 pull, u16 special) 81 | { 82 | //if (n == 2) what &= ~0x1400; 83 | //if (n == 2) what |= 0x1400; 84 | 85 | if (trace_gpio) { 86 | static u16 old[3][4]; 87 | 88 | u32 i; 89 | for (i = 0; i < (n == 1 ? 8 : 16); i++) 90 | if (bit(what, i) != bit(old[n][0], i) 91 | || bit(push, i) != bit(old[n][1], i) 92 | || bit(pull, i) != bit(old[n][2], i) 93 | || bit(special, i) != bit(old[n][3], i)) 94 | debug("IO%c%d data=%x push=%x pull=%x special=%x\n", 95 | 'A' + n, i, bit(what, i), bit(push, i), bit(pull, i), bit(special, i)); 96 | 97 | old[n][0] = what; 98 | old[n][1] = push; 99 | old[n][2] = pull; 100 | old[n][3] = special; 101 | } 102 | 103 | // debug("IO%c data=%04x push=%04x pull=%04x special=%04x\n", 104 | // 'A' + n, what, push, pull, special); 105 | 106 | return what; 107 | } 108 | 109 | struct board board_V_X = { 110 | .use_centered_coors = 1, 111 | 112 | .init = init, 113 | .gpio = gpio 114 | }; 115 | -------------------------------------------------------------------------------- /board-W60.c: -------------------------------------------------------------------------------- 1 | // Wireless 60 board by Rebecca Bettencourt 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | #include "emu.h" 10 | #include "platform.h" 11 | 12 | #include "board.h" 13 | 14 | static u32 current_bank = -1; 15 | 16 | static void switch_bank(u32 bank) { 17 | if (bank == current_bank) return; 18 | current_bank = bank; 19 | render_kill_cache(); 20 | read_rom(N_MEM * bank); 21 | // printf("bank: %d\n", bank); 22 | 23 | board->idle_pc = 0; 24 | if (bank) { 25 | if (mem[0x3ff1c] == 0x4311 && mem[0x3ff1e] == 0x4e43) // Wireless 60 26 | board->idle_pc = 0x3ff1c; 27 | if (mem[0x7e5a8] == 0x4311 && mem[0x7e5aa] == 0x4e43) // Zone 60 28 | board->idle_pc = 0x7e5a8; 29 | } 30 | } 31 | 32 | static void init(void) { 33 | switch_bank(0); 34 | } 35 | 36 | static int controller_input = 0; 37 | 38 | static u16 gpio(u32 n, u16 what, __unused u16 push, __unused u16 pull, __unused u16 special) { 39 | // printf("--> port %c what %04x push %04x pull %04x\n", 'A'+n, what, push, pull); 40 | // printf("\rbank: %d", current_bank); 41 | switch (n) { 42 | case 1: 43 | switch_bank(what & 7); 44 | break; 45 | case 0: 46 | switch (what & 0x300) { 47 | case 0x300: 48 | controller_input = 0; 49 | break; 50 | case 0x200: 51 | controller_input++; 52 | break; 53 | default: 54 | switch (controller_input) { 55 | case 1: if (button_A) what ^= 0x400; break; /* what ^= 0x800; for player 2 */ 56 | case 2: if (button_B) what ^= 0x400; break; 57 | case 3: if (button_menu) what ^= 0x400; break; 58 | case 4: if (button_C) what ^= 0x400; break; /* button C = start */ 59 | case 5: if (button_up) what ^= 0x400; break; 60 | case 6: if (button_down) what ^= 0x400; break; 61 | case 7: if (button_left) what ^= 0x400; break; 62 | case 8: if (button_right) what ^= 0x400; break; 63 | } 64 | break; 65 | } 66 | break; 67 | } 68 | return what; 69 | } 70 | 71 | struct board board_W60 = { 72 | .use_centered_coors = 1, 73 | .init = init, 74 | .gpio = gpio 75 | }; 76 | -------------------------------------------------------------------------------- /board-WAL.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include "types.h" 6 | #include "platform.h" 7 | #include "i2c.h" 8 | #include 9 | 10 | #include "board.h" 11 | 12 | 13 | static struct i2c_bus *i2c_bus; 14 | 15 | static u16 gpio(u32 n, u16 what, __unused u16 push, __unused u16 pull, __unused u16 special) 16 | { 17 | // debug("---> PORT %c what=%04x push=%04x pull=%04x\n", 18 | // 'A' + n, what, push, pull); 19 | 20 | if (n == 0) { 21 | if (button_up) 22 | what |= 0x8000; 23 | if (button_down) 24 | what |= 0x4000; 25 | if (button_left) 26 | what |= 0x2000; 27 | if (button_right) 28 | what |= 0x1000; 29 | if (button_A) 30 | what |= 0x0800; 31 | if (button_B) 32 | what |= 0x0400; 33 | if (button_menu) 34 | what |= 0x0020; 35 | } 36 | 37 | if (n == 2) { 38 | int sda = what & 1; 39 | int scl = (what >> 1) & 1; 40 | //debug("SDA=%d SCL=%d\n", sda, scl); 41 | sda = i2c_bitbang(i2c_bus, sda, scl); 42 | what = (what & ~1) | sda; 43 | } 44 | 45 | return what; 46 | } 47 | 48 | static void init(void) 49 | { 50 | i2c_bus = i2c_bitbang_bus_create(); 51 | i2c_eeprom_create(i2c_bus, 0x200, 0xa0, "WAL"); 52 | } 53 | 54 | struct board board_WAL = { 55 | .idle_pc = 0xb1c6, 56 | 57 | .init = init, 58 | .gpio = gpio 59 | }; 60 | -------------------------------------------------------------------------------- /board-dummy.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | 7 | #include "types.h" 8 | #include "emu.h" 9 | #include "platform.h" 10 | 11 | #include "board.h" 12 | 13 | 14 | static u16 gpio(__unused u32 n, u16 what, __unused u16 push, __unused u16 pull, __unused u16 special) 15 | { 16 | return what; 17 | } 18 | 19 | struct board board_dummy = { 20 | // .init = init, 21 | .gpio = gpio 22 | }; 23 | -------------------------------------------------------------------------------- /board.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | 7 | #include "types.h" 8 | #include "emu.h" 9 | #include "platform.h" 10 | 11 | #include "board.h" 12 | 13 | struct board *board; 14 | 15 | static struct board *board_detect(void) 16 | { 17 | if (mem[0x19792] == 0x4311 && mem[0x19794] == 0x4e43) // VII 18 | return &board_VII; 19 | if (mem[0x42daa] == 0x4311 && mem[0x42dac] == 0x4e43) // VC1 20 | return &board_VII; 21 | if (mem[0x3ecb9] == 0x4311 && mem[0x3ecbb] == 0x4e43) // VC2 22 | return &board_VII; 23 | 24 | if (mem[0x3ff1c] == 0x4311 && mem[0x3ff1e] == 0x4e43) // Wireless 60 25 | return &board_W60; 26 | if (mem[0x7e5a8] == 0x4311 && mem[0x7e5aa] == 0x4e43) // Zone 60 27 | return &board_W60; 28 | 29 | if (mem[0xb1c6] == 0x9311 && mem[0xb1c8] == 0x4501 && 30 | mem[0xb1c9] == 0x5e44) 31 | return &board_WAL; 32 | 33 | if (mem[0x5ce1] == 0x42c2 && mem[0x5ce2] == 0x5e42) 34 | return &board_BAT; 35 | 36 | if (mem[0x5675c] == 0x9311 && mem[0x5675e] == 0x4240 && // Winnie 37 | mem[0x5675f] == 0x4e44) 38 | return &board_V_X; 39 | 40 | return &board_V_X; 41 | 42 | return 0; 43 | } 44 | 45 | void board_init(void) 46 | { 47 | board = board_detect(); 48 | if (board == 0) { 49 | warn("couldn't detect board\n"); 50 | board = &board_dummy; 51 | } 52 | else if (board == &board_VII) printf("detected Vii\n"); 53 | else if (board == &board_W60) printf("detected Wireless60\n"); 54 | else if (board == &board_WAL) printf("detected Wall-E\n"); 55 | else if (board == &board_BAT) printf("detected Batman\n"); 56 | else if (board == &board_V_X) printf("detected V.Smile\n"); 57 | else printf("detected unknown board\n"); 58 | 59 | if (board->init) 60 | board->init(); 61 | } 62 | -------------------------------------------------------------------------------- /board.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _BOARD_H 6 | #define _BOARD_H 7 | 8 | #include "types.h" 9 | 10 | struct board { 11 | u32 idle_pc; // Address of code that waits for (retrace) IRQ 12 | int use_centered_coors; // This is some video reg actually, FIXME 13 | 14 | void (*init)(void); 15 | u16 (*gpio)(u32 n, u16 what, u16 push, u16 pull, u16 special); 16 | void (*uart_send)(u8 x); 17 | u8 (*uart_recv)(void); 18 | }; 19 | 20 | extern struct board board_VII, board_W60, board_WAL, board_BAT, board_V_X, board_dummy; 21 | extern struct board *board; 22 | 23 | void board_init(void); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /config.example: -------------------------------------------------------------------------------- 1 | # Build for what platform? 2 | PLATFORM := sdl 3 | 4 | # What video renderer? (gl/soft) 5 | RENDER := gl 6 | 7 | # Debugging spew? (yes/no) 8 | USE_DEBUG := no 9 | 10 | # Compiler, compiler flags. 11 | CC := gcc 12 | CFLAGS := -march=native 13 | -------------------------------------------------------------------------------- /dialog-cocoa.m: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #import 6 | 7 | #include "dialog.h" 8 | 9 | static char path[256]; 10 | 11 | const char *dialog_rom_file(void) 12 | { 13 | NSOpenPanel *panel = [NSOpenPanel openPanel]; 14 | 15 | NSString *dir = [[NSBundle mainBundle] resourcePath]; 16 | dir = [dir stringByAppendingPathComponent:@"ROMs"]; 17 | 18 | NSArray *types = nil;//[NSArray arrayWithObject:@"uuu"]; 19 | //[panel setAllowsOtherFileTypes:YES]; 20 | 21 | int result = [panel runModalForDirectory:dir file:nil types:types]; 22 | 23 | if (result != NSOKButton) 24 | return 0; 25 | 26 | [[panel filename] getCString:path maxLength:(sizeof path)]; 27 | 28 | return path; 29 | } 30 | -------------------------------------------------------------------------------- /dialog.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008,2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _DIALOG_H 6 | #define _DIALOG_H 7 | 8 | const char *dialog_rom_file(void); 9 | 10 | #endif 11 | 12 | -------------------------------------------------------------------------------- /disas.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | 10 | #include "disas.h" 11 | 12 | 13 | static const char *regs[] = { "sp", "r1", "r2", "r3", "r4", "bp", "sr", "pc" }; 14 | 15 | static const char *jumps[] = { 16 | "jb", "jae", "jge", "jl", "jne", "je", "jpl", "jmi", 17 | "jbe", "ja", "jle", "jg", "jvc", "jvs", "jmp" 18 | }; 19 | 20 | static void print_alu_op_start(u8 op0, u8 opA) 21 | { 22 | static const char *alu_op_start[] = { 23 | "%s += ", "%s += ", "%s -= ", "%s -= ", 24 | "cmp %s, ", "", "%s =- ", "", 25 | "%s ^= ", "%s = ", "%s |= ", "%s &= ", 26 | "test %s, " 27 | }; 28 | 29 | printf(alu_op_start[op0], regs[opA]); 30 | } 31 | 32 | static void print_alu_op3(u8 op0, u8 opB) 33 | { 34 | static const char *alu_op3[] = { 35 | "%s + ", "%s + ", "%s - ", "%s - ", 36 | "cmp %s, ", "", "-", "", 37 | "%s ^ ", "", "%s | ", "%s & ", 38 | "test %s, " 39 | }; 40 | 41 | printf(alu_op3[op0], regs[opB]); 42 | } 43 | 44 | static void print_alu_op_end(u8 op0) 45 | { 46 | if (op0 == 1 || op0 == 3) 47 | printf(", carry"); 48 | printf("\n"); 49 | } 50 | 51 | static void print_indirect_op(u8 opN, u8 opB) 52 | { 53 | const char *forms[] = { "[%s]", "[%s--]", "[%s++]", "[++%s]" }; 54 | 55 | if (opN & 4) 56 | printf("ds:"); 57 | printf(forms[opN & 3], regs[opB]); 58 | } 59 | 60 | u32 disas(const u16 *mem, u32 offset) 61 | { 62 | u8 op0, opA, op1, opN, opB, opimm; 63 | u16 op, ximm = 0x0bad; 64 | u32 len = 1; 65 | 66 | printf("%04x: ", offset); 67 | 68 | op = mem[offset++]; 69 | 70 | 71 | // the top four bits are the alu op or the branch condition, or E or F 72 | op0 = (op >> 12); 73 | 74 | // the next three are usually the destination register 75 | opA = (op >> 9) & 7; 76 | 77 | // and the next three the addressing mode 78 | op1 = (op >> 6) & 7; 79 | 80 | // the next three can be anything 81 | opN = (op >> 3) & 7; 82 | 83 | // and the last three usually the second register (source register) 84 | opB = op & 7; 85 | 86 | // the last six sometimes are a single immediate number 87 | opimm = op & 63; 88 | 89 | 90 | // some insns need a second word: 91 | if ((op0 < 14 && op1 == 4 && (opN == 1 || opN == 2 || opN == 3)) 92 | || (op0 == 15 && (op1 == 1 || op1 == 2))) { 93 | ximm = mem[offset++]; 94 | printf("%04x %04x ", op, ximm); 95 | len = 2; 96 | } else 97 | printf("%04x ", op); 98 | 99 | printf("%x %x %x %x %x ", op0, opA, op1, opN, opB); 100 | 101 | 102 | // all-zero and all-one are invalid insns: 103 | if (op == 0 || op == 0xffff) { 104 | printf("--\n"); 105 | return 1; 106 | } 107 | 108 | 109 | // first, check for the conditional branch insns 110 | if (op0 < 15 && opA == 7 && op1 == 0) { 111 | printf("%s %04x\n", jumps[op0], offset+opimm); 112 | return 1; 113 | } 114 | if (op0 < 15 && opA == 7 && op1 == 1) { 115 | printf("%s %04x\n", jumps[op0], offset-opimm); 116 | return 1; 117 | } 118 | 119 | 120 | switch ((op1 << 4) | op0) { 121 | 122 | case 0x05: case 0x15: case 0x25: case 0x35: 123 | case 0x45: case 0x55: case 0x65: case 0x75: 124 | case 0x85: case 0x95: case 0xa5: case 0xb5: 125 | case 0xc5: case 0xd5: 126 | case 0x07: case 0x17: case 0x27: case 0x37: 127 | case 0x47: case 0x57: case 0x67: case 0x77: 128 | case 0x87: case 0x97: case 0xa7: case 0xb7: 129 | case 0xc7: case 0xd7: 130 | case 0x1d: case 0x5d: case 0x6d: 131 | case 0x20: case 0x21: case 0x22: case 0x23: 132 | case 0x24: case 0x26: case 0x28: case 0x2a: 133 | case 0x2b: case 0x2c: 134 | bad: 135 | printf("\n"); 136 | return len; 137 | 138 | 139 | // alu, base+displacement 140 | case 0x00: case 0x01: case 0x02: case 0x03: 141 | case 0x04: case 0x06: case 0x08: case 0x09: 142 | case 0x0a: case 0x0b: case 0x0c: 143 | print_alu_op_start(op0, opA); 144 | printf("[bp+%02x]", opimm); 145 | print_alu_op_end(op0); 146 | return 1; 147 | case 0x0d: 148 | printf("[bp+%02x] = %s\n", opimm, regs[opA]); 149 | return 1; 150 | 151 | 152 | // alu, 6-bit immediate 153 | case 0x10: case 0x11: case 0x12: case 0x13: 154 | case 0x14: case 0x16: case 0x18: case 0x19: 155 | case 0x1a: case 0x1b: case 0x1c: 156 | print_alu_op_start(op0, opA); 157 | printf("%02x", opimm); 158 | print_alu_op_end(op0); 159 | return 1; 160 | 161 | 162 | // pop insns 163 | case 0x29: 164 | if (op == 0x9a90) 165 | printf("retf\n"); 166 | else if (op == 0x9a98) 167 | printf("reti\n"); 168 | else if (opA+1 < 8 && opA+opN < 8) 169 | printf("pop %s, %s from [%s]\n", 170 | regs[opA+1], regs[opA+opN], regs[opB]); 171 | else 172 | goto bad; 173 | return 1; 174 | 175 | 176 | // push insns 177 | case 0x2d: 178 | if (opA+1 >= opN && opA < opN+7) 179 | printf("push %s, %s to [%s]\n", 180 | regs[opA+1-opN], regs[opA], regs[opB]); 181 | else 182 | goto bad; 183 | return 1; 184 | 185 | 186 | // alu, indirect memory 187 | case 0x30: case 0x31: case 0x32: case 0x33: 188 | case 0x34: case 0x36: case 0x38: case 0x39: 189 | case 0x3a: case 0x3b: case 0x3c: 190 | print_alu_op_start(op0, opA); 191 | print_indirect_op(opN, opB); 192 | print_alu_op_end(op0); 193 | return 1; 194 | case 0x3d: 195 | print_indirect_op(opN, opB); 196 | printf(" = %s\n", regs[opA]); 197 | return 1; 198 | 199 | 200 | case 0x40: case 0x41: case 0x42: case 0x43: 201 | case 0x44: case 0x46: case 0x48: case 0x49: 202 | case 0x4a: case 0x4b: case 0x4c: 203 | switch (opN) { 204 | 205 | // alu, register 206 | case 0: 207 | print_alu_op_start(op0, opA); 208 | printf("%s", regs[opB]); 209 | print_alu_op_end(op0); 210 | return 1; 211 | 212 | // alu, 16-bit immediate 213 | case 1: 214 | if ((op0 == 4 || op0 == 12 || op0 == 6 || op0 == 9) 215 | && opA != opB) 216 | goto bad; 217 | if (op0 != 4 && op0 != 12) 218 | printf("%s = ", regs[opA]); 219 | print_alu_op3(op0, opB); 220 | printf("%04x", ximm); 221 | print_alu_op_end(op0); 222 | return 2; 223 | 224 | // alu, direct memory 225 | case 2: 226 | if ((op0 == 4 || op0 == 12 || op0 == 6 || op0 == 9) 227 | && opA != opB) 228 | goto bad; 229 | if (op0 != 4 && op0 != 12) 230 | printf("%s = ", regs[opA]); 231 | print_alu_op3(op0, opB); 232 | printf("[%04x]", ximm); 233 | print_alu_op_end(op0); 234 | return 2; 235 | 236 | // alu, direct memory 237 | case 3: 238 | if (op0 == 4 || op0 == 12) 239 | goto bad; 240 | if ((op0 == 6 || op0 == 9) && opA != opB) 241 | goto bad; 242 | printf("[%04x] = ", ximm); 243 | print_alu_op3(op0, opB); 244 | printf("%s", regs[opA]); 245 | print_alu_op_end(op0); 246 | return 2; 247 | 248 | // alu, with shift 249 | default: 250 | print_alu_op_start(op0, opA); 251 | printf("%s asr %x", regs[opB], (opN & 3) + 1); 252 | print_alu_op_end(op0); 253 | return 1; 254 | } 255 | 256 | case 0x4d: 257 | switch (opN) { 258 | 259 | // alu, direct memory 260 | case 3: 261 | if (opA != opB) 262 | goto bad; 263 | printf("[%04x] = %s\n", ximm, regs[opB]); 264 | return 2; 265 | default: 266 | goto bad; 267 | } 268 | 269 | 270 | // alu, with shift 271 | case 0x50: case 0x51: case 0x52: case 0x53: 272 | case 0x54: case 0x56: case 0x58: case 0x59: 273 | case 0x5a: case 0x5b: case 0x5c: 274 | print_alu_op_start(op0, opA); 275 | if ((opN & 4) == 0) 276 | printf("%s lsl %x", regs[opB], (opN & 3) + 1); 277 | else 278 | printf("%s lsr %x", regs[opB], (opN & 3) + 1); 279 | print_alu_op_end(op0); 280 | return 1; 281 | 282 | 283 | // alu, with shift 284 | case 0x60: case 0x61: case 0x62: case 0x63: 285 | case 0x64: case 0x66: case 0x68: case 0x69: 286 | case 0x6a: case 0x6b: case 0x6c: 287 | print_alu_op_start(op0, opA); 288 | if ((opN & 4) == 0) 289 | printf("%s rol %x", regs[opB], (opN & 3) + 1); 290 | else 291 | printf("%s ror %x", regs[opB], (opN & 3) + 1); 292 | print_alu_op_end(op0); 293 | return 1; 294 | 295 | 296 | // alu, direct memory 297 | case 0x70: case 0x71: case 0x72: case 0x73: 298 | case 0x74: case 0x76: case 0x78: case 0x79: 299 | case 0x7a: case 0x7b: case 0x7c: 300 | print_alu_op_start(op0, opA); 301 | printf("[%02x]", opimm); 302 | print_alu_op_end(op0); 303 | return 1; 304 | case 0x7d: 305 | printf("[%02x] = %s\n", opimm, regs[opA]); 306 | return 1; 307 | 308 | 309 | case 0x1f: 310 | if (opA == 0) { 311 | printf("call %04x\n", (opimm << 16) | ximm); 312 | return 2; 313 | } 314 | goto dunno; 315 | 316 | case 0x2f: case 0x3f: case 0x6f: case 0x7f: 317 | if (opA == 7 && op1 == 2) { 318 | printf("goto %04x\n", (opimm << 16) | ximm); 319 | return 2; 320 | } 321 | if (opA == 7 && op1 == 3) 322 | goto dunno; 323 | goto dunno; 324 | 325 | 326 | case 0x0f: 327 | switch (opN) { 328 | case 1: 329 | if (opA == 7) 330 | goto dunno; 331 | printf("mr = %s*%s, us\n", regs[opA], regs[opB]); 332 | return 1; 333 | default: 334 | goto dunno; 335 | } 336 | 337 | case 0x4f: 338 | switch (opN) { 339 | case 1: 340 | if (opA == 7) 341 | goto dunno; 342 | printf("mr = %s*%s\n", regs[opA], regs[opB]); 343 | return 1; 344 | default: 345 | goto dunno; 346 | } 347 | 348 | case 0x5f: 349 | if (opA != 0) 350 | goto dunno; 351 | switch (opimm) { 352 | case 0x00: 353 | printf("int off\n"); 354 | return 1; 355 | case 0x01: 356 | printf("int irq\n"); 357 | return 1; 358 | case 0x02: 359 | printf("int fiq\n"); 360 | return 1; 361 | case 0x03: 362 | printf("int fiq,irq\n"); 363 | return 1; 364 | case 0x04: 365 | printf("fir_mov on\n"); 366 | return 1; 367 | case 0x05: 368 | printf("fir_mov off\n"); 369 | return 1; 370 | case 0x08: 371 | printf("irq off\n"); 372 | return 1; 373 | case 0x09: 374 | printf("irq on\n"); 375 | return 1; 376 | case 0x0c: 377 | printf("fiq off\n"); 378 | return 1; 379 | case 0x0e: 380 | printf("fiq on\n"); 381 | return 1; 382 | case 0x25: 383 | printf("nop\n"); 384 | return 1; 385 | default: 386 | goto dunno; 387 | } 388 | 389 | case 0x0e: case 0x1e: case 0x2e: case 0x3e: 390 | case 0x4e: case 0x5e: case 0x6e: case 0x7e: 391 | dunno: 392 | printf("\n"); 393 | return len; 394 | 395 | default: 396 | printf(""); 397 | return len; 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /disas.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008,2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _DISAS_H 6 | #define _DISAS_H 7 | 8 | #include "types.h" 9 | 10 | u32 disas(const u16 *mem, u32 offset); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /emu.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | #include 8 | #include // for usleep 9 | 10 | #include "types.h" 11 | #include "disas.h" 12 | #include "timer.h" 13 | #include "video.h" 14 | #include "audio.h" 15 | #include "io.h" 16 | #include "platform.h" 17 | #include "render.h" 18 | #include "board.h" 19 | 20 | #include "emu.h" 21 | 22 | 23 | #define FREQ 50 24 | #define PERIOD (1000000/FREQ) 25 | 26 | 27 | u16 mem[N_MEM]; 28 | 29 | static int trace = 0; 30 | static int trace_new = 0; 31 | static int store_trace = 0; 32 | static int trace_calls = 0; 33 | static int pause_after_every_frame = 0; 34 | //static u8 trace_irq[9] = { 0, 1, 1, 1, 1, 1, 1, 1, 1 }; 35 | static u8 trace_irq[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 36 | static const u32 unsp_version = 11; // version a.b as 10*a + b 37 | static u8 ever_ran_this[N_MEM]; 38 | 39 | 40 | static int do_extint1, do_extint2; 41 | 42 | 43 | static u16 reg[8]; 44 | static u8 sb; 45 | static u8 sb_banked[3]; 46 | static u8 irq_active, fiq_active; 47 | static u8 irq_enabled, fiq_enabled; 48 | 49 | static u32 cycle_count; 50 | static u32 line_count; 51 | static const u32 cycles_per_line = 1728; // 1728 for PAL, 1716 for NTSC 52 | static const u32 lines_per_field = 312; // 312 for PAL, 262 for NTSC 53 | 54 | 55 | u32 get_ds(void) 56 | { 57 | return reg[6] >> 10; 58 | } 59 | 60 | void set_ds(u32 ds) 61 | { 62 | reg[6] = (reg[6] & 0x03ff) | (ds << 10); 63 | } 64 | 65 | static void dump(u32 addr, u32 len) 66 | { 67 | u32 off, i; 68 | 69 | for (off = addr & ~0x0f; off < addr + len; off += 0x10) { 70 | printf("%06x:", off); 71 | 72 | for (i = 0; i < 16; i++) 73 | printf(" %04x", mem[off+i]); 74 | printf("\n"); 75 | } 76 | } 77 | 78 | static void store(u16 val, u32 addr) 79 | { 80 | if (store_trace) 81 | debug("WRITE %04x TO %04x (was %04x)\n", val, addr, mem[addr]); 82 | 83 | if (addr < 0x2800) // RAM 84 | mem[addr] = val; 85 | else if (addr < 0x3000) 86 | video_store(val, addr); 87 | else if (addr < 0x3800) 88 | audio_store(val, addr); 89 | else if (addr < 0x4000) 90 | io_store(val, addr); 91 | else 92 | debug("ROM STORE %04x to %04x\n", val, addr); 93 | } 94 | 95 | static u16 load(u32 addr) 96 | { 97 | if (addr < 0x2800 || addr >= 0x4000) // RAM / ROM 98 | return mem[addr]; 99 | 100 | if (addr < 0x3000) 101 | return video_load(addr); 102 | 103 | if (addr < 0x3800) 104 | return audio_load(addr); 105 | 106 | return io_load(addr); 107 | } 108 | 109 | static inline u32 cs_pc(void) 110 | { 111 | return ((reg[6] & 0x3f) << 16) | reg[7]; 112 | } 113 | 114 | static inline void set_cs_pc(u32 x) 115 | { 116 | reg[7] = x; 117 | reg[6] = (reg[6] & ~0x3f) | ((x >> 16) & 0x3f); 118 | } 119 | 120 | static inline void inc_cs_pc(int x) 121 | { 122 | if (unsp_version < 11) 123 | reg[7] += x; 124 | else 125 | set_cs_pc(cs_pc() + x); 126 | } 127 | 128 | static u32 ds_reg(u8 r) 129 | { 130 | return (get_ds() << 16) | reg[r]; 131 | } 132 | 133 | static void set_ds_reg(u8 r, u32 x) 134 | { 135 | reg[r] = x; 136 | set_ds((x & 0x3f0000) >> 16); 137 | } 138 | 139 | static void inc_ds_reg(u8 r, s16 x) 140 | { 141 | if (unsp_version < 11) 142 | reg[r] += x; 143 | else 144 | set_ds_reg(r, ds_reg(r) + x); 145 | } 146 | 147 | static void push(u16 val, u8 b) 148 | { 149 | store(val, reg[b]--); 150 | } 151 | 152 | static u16 pop(u8 b) 153 | { 154 | return load(++reg[b]); 155 | } 156 | 157 | static void print_state(void) 158 | { 159 | int i; 160 | 161 | printf("\n"); 162 | printf(" SP R1 R2 R3 R4 BP SR PC CS NZSC DS SB IRQ FIQ\n"); 163 | for (i = 0; i < 8; i++) 164 | printf("%04x ", reg[i]); 165 | printf(" %02x", reg[6] & 0x3f); 166 | printf(" %x%x%x%x", (reg[6] >> 9) & 1, (reg[6] >> 8) & 1, 167 | (reg[6] >> 7) & 1, (reg[6] >> 6) & 1); 168 | printf(" %02x", reg[6] >> 10); 169 | printf(" %x %x %x\n", sb, irq_enabled, fiq_enabled); 170 | disas(mem, cs_pc()); 171 | } 172 | 173 | static void do_irq(int irqno) 174 | { 175 | if (fiq_active || irq_active || !irq_enabled) 176 | return; 177 | 178 | if (trace_irq[irqno]) 179 | debug("### IRQ #%x ###\n", irqno); 180 | 181 | irq_active = 1; 182 | 183 | sb_banked[0] = sb; 184 | push(reg[7], 0); 185 | push(reg[6], 0); 186 | 187 | sb = sb_banked[1]; 188 | reg[7] = load(0xfff8 + irqno); 189 | reg[6] = 0; 190 | } 191 | 192 | static void do_fiq(void) 193 | { 194 | if (fiq_active || !fiq_enabled) 195 | return; 196 | 197 | if (trace_irq[8]) 198 | debug("### FIQ ###\n"); 199 | 200 | fiq_active = 1; 201 | 202 | sb_banked[irq_active] = sb; 203 | push(reg[7], 0); 204 | push(reg[6], 0); 205 | 206 | sb = sb_banked[2]; 207 | reg[7] = load(0xfff6); 208 | reg[6] = 0; 209 | } 210 | 211 | static int get_irq(void) 212 | { 213 | // XXX FIQ 214 | 215 | if (!irq_enabled || irq_active) 216 | return -1; 217 | 218 | // video 219 | if (mem[0x2862] & mem[0x2863]) 220 | return 0; 221 | 222 | // XXX audio, IRQ1 223 | 224 | // timerA, timerB 225 | if (mem[0x3d21] & mem[0x3d22] & 0x0c00) 226 | return 2; 227 | 228 | // UART, ADC XXX: also SPI 229 | if (mem[0x3d21] & mem[0x3d22] & 0x2100) 230 | return 3; 231 | 232 | // XXX audio, IRQ4 233 | 234 | // extint1, extint2 235 | if (mem[0x3d21] & mem[0x3d22] & 0x1200) 236 | return 5; 237 | 238 | // 1024Hz, 2048HZ, 4096HZ 239 | if (mem[0x3d21] & mem[0x3d22] & 0x0070) 240 | return 6; 241 | 242 | // TMB1, TMB2, 4Hz, key change 243 | if (mem[0x3d21] & mem[0x3d22] & 0x008b) 244 | return 7; 245 | 246 | return -1; 247 | } 248 | 249 | static void maybe_enter_irq(void) 250 | { 251 | int irqno = get_irq(); 252 | 253 | if (irqno < 0) 254 | return; 255 | 256 | if (irqno == 8) 257 | do_fiq(); 258 | else 259 | do_irq(irqno); 260 | } 261 | 262 | static inline u16 fetch(void) 263 | { 264 | u16 x = load(cs_pc()); 265 | inc_cs_pc(1); 266 | 267 | return x; 268 | } 269 | 270 | static void step(void) 271 | { 272 | u16 op; 273 | u8 op0, opA, op1, opN, opB, opimm; 274 | u16 x0, x1; 275 | u32 old_cs_pc = cs_pc(); 276 | u32 x, d = 0xff0000; 277 | 278 | if (trace_calls) { 279 | op = mem[cs_pc()]; 280 | if ((op & 0xf3c0) == 0xf040 || (op & 0xfff0) == 0x9a90) 281 | print_state(); 282 | if ((op & 0xfff0) == 0x9a90) 283 | printf(" to %04x\n", (load(reg[op & 7] + 1) & 0x3f) << 16 | load(reg[op & 7] + 2)); 284 | } 285 | 286 | op = fetch(); 287 | 288 | 289 | // the top four bits are the alu op or the branch condition, or E or F 290 | op0 = (op >> 12); 291 | 292 | // the next three are usually the destination register 293 | opA = (op >> 9) & 7; 294 | 295 | // and the next three the addressing mode 296 | op1 = (op >> 6) & 7; 297 | 298 | // the next three can be anything 299 | opN = (op >> 3) & 7; 300 | 301 | // and the last three usually the second register (source register) 302 | opB = op & 7; 303 | 304 | // the last six sometimes are a single immediate number 305 | opimm = op & 63; 306 | 307 | 308 | // jumps 309 | if (opA == 7 && op1 < 2) { 310 | cycle_count += 2; 311 | 312 | switch (op0) { 313 | case 0: // JCC, JB, JNAE; C == 0 314 | if ((reg[6] & 0x40) == 0) 315 | goto do_jump; 316 | break; 317 | case 1: // JCS, JNB, JAE; C == 1 318 | if ((reg[6] & 0x40) == 0x40) 319 | goto do_jump; 320 | break; 321 | case 2: // JSC, JGE, JNL; S == 0 322 | if ((reg[6] & 0x80) == 0) 323 | goto do_jump; 324 | break; 325 | case 3: // JSS, JNGE, JL; S == 1 326 | if ((reg[6] & 0x80) == 0x80) 327 | goto do_jump; 328 | break; 329 | case 4: // JNE, JNZ; Z == 0 330 | if ((reg[6] & 0x0100) == 0) 331 | goto do_jump; 332 | break; 333 | case 5: // JE, JZ; Z == 1 334 | if ((reg[6] & 0x0100) == 0x0100) 335 | goto do_jump; 336 | break; 337 | case 6: // JPL; N == 0 338 | if ((reg[6] & 0x0200) == 0) 339 | goto do_jump; 340 | break; 341 | case 7: // JMI; N == 1 342 | if ((reg[6] & 0x0200) == 0x0200) 343 | goto do_jump; 344 | break; 345 | case 8: // JBE, JNA; not (Z == 0 && C == 1) 346 | if ((reg[6] & 0x0140) != 0x0040) 347 | goto do_jump; 348 | break; 349 | case 9: // JNBE, JA; (Z == 0 && C == 1) 350 | if ((reg[6] & 0x0140) == 0x0040) 351 | goto do_jump; 352 | break; 353 | case 10: // JLE, JNG; not (Z == 0 && S == 0) 354 | if ((reg[6] & 0x0180) != 0) 355 | goto do_jump; 356 | break; 357 | case 11: // JNLE, JG; (Z == 0 && S == 0) 358 | if ((reg[6] & 0x0180) == 0) 359 | goto do_jump; 360 | break; 361 | // JVC: N == S 362 | // JVS: N != S 363 | case 14: // JMP 364 | goto do_jump; 365 | default: 366 | goto bad; 367 | 368 | do_jump: 369 | cycle_count += 2; 370 | 371 | if (op1 == 0) 372 | inc_cs_pc(opimm); 373 | else 374 | inc_cs_pc(-opimm); 375 | } 376 | return; 377 | } 378 | 379 | 380 | // PUSH 381 | if (op1 == 2 && op0 == 13) { 382 | cycle_count += 4 + 2*opN; 383 | 384 | while (opN--) 385 | push(reg[opA--], opB); 386 | return; 387 | } 388 | 389 | // POP 390 | if (op1 == 2 && op0 == 9) { 391 | // special case for RETI 392 | if (opA == 5 && opN == 3 && opB == 0) { 393 | cycle_count += 8; 394 | 395 | reg[6] = pop(0); 396 | reg[7] = pop(0); 397 | 398 | if (fiq_active) { 399 | fiq_active = 0; 400 | sb_banked[2] = sb; 401 | sb = sb_banked[irq_active]; 402 | } else if (irq_active) { 403 | irq_active = 0; 404 | sb_banked[1] = sb; 405 | sb = sb_banked[0]; 406 | } 407 | 408 | maybe_enter_irq(); 409 | 410 | return; 411 | } 412 | 413 | cycle_count += 4 + 2*opN; 414 | 415 | while (opN--) 416 | reg[++opA] = pop(opB); 417 | return; 418 | } 419 | 420 | 421 | if (op0 == 15) { 422 | switch (op1) { 423 | case 0: // MUL US 424 | if (opN == 1) { 425 | if (opA == 7) 426 | goto bad; 427 | 428 | cycle_count += 12; 429 | 430 | x = reg[opA]*reg[opB]; 431 | if (reg[opB] & 0x8000) 432 | x -= reg[opA] << 16; 433 | reg[4] = x >> 16; 434 | reg[3] = x; 435 | return; 436 | } else 437 | goto bad; 438 | 439 | case 4: // MUL SS 440 | if (opN == 1) { 441 | if (opA == 7) 442 | goto bad; 443 | 444 | cycle_count += 12; 445 | 446 | x = reg[opA]*reg[opB]; 447 | if (reg[opB] & 0x8000) 448 | x -= reg[opA] << 16; 449 | if (reg[opA] & 0x8000) 450 | x -= reg[opB] << 16; 451 | reg[4] = x >> 16; 452 | reg[3] = x; 453 | return; 454 | } else 455 | goto bad; 456 | 457 | case 1: // CALL 458 | if ((opA & 1) != 0) 459 | goto bad; 460 | 461 | cycle_count += 9; 462 | 463 | x1 = fetch(); 464 | push(reg[7], 0); 465 | push(reg[6], 0); 466 | set_cs_pc((opimm << 16) | x1); 467 | return; 468 | 469 | case 2: // JMPF 470 | if (opA != 7) 471 | goto bad; 472 | 473 | cycle_count += 5; 474 | 475 | x1 = fetch(); 476 | set_cs_pc((opimm << 16) | x1); 477 | return; 478 | 479 | case 5: // VARIOUS 480 | cycle_count += 2; 481 | 482 | switch (opimm) { 483 | case 0: 484 | irq_enabled = 0; 485 | fiq_enabled = 0; 486 | //debug("INT OFF\n"); 487 | return; 488 | case 1: 489 | irq_enabled = 1; 490 | fiq_enabled = 0; 491 | //debug("INT IRQ\n"); 492 | return; 493 | case 2: 494 | irq_enabled = 0; 495 | fiq_enabled = 1; 496 | //debug("INT FIQ\n"); 497 | return; 498 | case 3: 499 | irq_enabled = 1; 500 | fiq_enabled = 1; 501 | //debug("INT FIQ,IRQ\n"); 502 | return; 503 | case 8: 504 | irq_enabled = 0; 505 | //debug("IRQ OFF\n"); 506 | return; 507 | case 9: 508 | irq_enabled = 1; 509 | //debug("IRQ ON\n"); 510 | return; 511 | case 12: 512 | fiq_enabled = 0; 513 | //debug("FIQ OFF\n"); 514 | return; 515 | case 14: 516 | fiq_enabled = 1; 517 | //debug("FIQ ON\n"); 518 | return; 519 | case 0x25: // NOP 520 | return; 521 | default: 522 | goto bad; 523 | } 524 | 525 | default: 526 | goto bad; 527 | } 528 | } 529 | 530 | 531 | // alu op 532 | 533 | // first, get the arguments 534 | x0 = reg[opA]; 535 | 536 | switch (op1) { 537 | case 0: // [BP+imm6] 538 | cycle_count += 6; 539 | 540 | d = (u16)(reg[5] + opimm); 541 | if (op0 == 13) 542 | x1 = 0x0bad; 543 | else 544 | x1 = load(d); 545 | break; 546 | 547 | case 1: // imm6 548 | cycle_count += 2; 549 | 550 | x1 = opimm; 551 | break; 552 | 553 | case 3: // [Rb] and friends 554 | cycle_count += 6; 555 | if (opA == 7) 556 | cycle_count++; 557 | 558 | if (opN & 4) { 559 | if ((opN & 3) == 3) 560 | inc_ds_reg(opB, 1); 561 | d = ds_reg(opB); 562 | if (op0 == 13) 563 | x1 = 0x0bad; 564 | else 565 | x1 = load(d); 566 | if ((opN & 3) == 1) 567 | inc_ds_reg(opB, -1); 568 | if ((opN & 3) == 2) 569 | inc_ds_reg(opB, 1); 570 | } else { 571 | if ((opN & 3) == 3) 572 | reg[opB]++; 573 | d = reg[opB]; 574 | if (op0 == 13) 575 | x1 = 0x0bad; 576 | else 577 | x1 = load(d); 578 | if ((opN & 3) == 1) 579 | reg[opB]--; 580 | if ((opN & 3) == 2) 581 | reg[opB]++; 582 | } 583 | break; 584 | 585 | case 4: 586 | switch(opN) { 587 | case 0: // Rb 588 | cycle_count += 3; 589 | if (opA == 7) 590 | cycle_count += 2; 591 | 592 | x1 = reg[opB]; 593 | break; 594 | 595 | case 1: // imm16 596 | cycle_count += 4; 597 | if (opA == 7) 598 | cycle_count++; 599 | 600 | x0 = reg[opB]; 601 | x1 = fetch(); 602 | break; 603 | 604 | case 2: // [imm16] 605 | cycle_count += 7; 606 | if (opA == 7) 607 | cycle_count++; 608 | 609 | x0 = reg[opB]; 610 | d = fetch(); 611 | if (op0 == 13) 612 | x1 = 0x0bad; 613 | else 614 | x1 = load(d); 615 | break; 616 | 617 | case 3: // [imm16] = ... 618 | cycle_count += 7; 619 | if (opA == 7) 620 | cycle_count++; 621 | 622 | x0 = reg[opB]; 623 | x1 = reg[opA]; 624 | d = fetch(); 625 | break; 626 | 627 | default: // ASR 628 | { 629 | cycle_count += 3; 630 | if (opA == 7) 631 | cycle_count += 2; 632 | 633 | u32 shift = (reg[opB] << 4) | sb; 634 | if (shift & 0x80000) 635 | shift |= 0xf00000; 636 | shift >>= (opN - 3); 637 | sb = shift & 0x0f; 638 | x1 = (shift >> 4) & 0xffff; 639 | } 640 | } 641 | break; 642 | 643 | case 5: 644 | cycle_count += 3; 645 | if (opA == 7) 646 | cycle_count += 2; 647 | 648 | if (opN < 4) { // LSL 649 | u32 shift = ((sb << 16) | reg[opB]) << (opN + 1); 650 | sb = (shift >> 16) & 0xf; 651 | x1 = shift & 0xffff; 652 | } else { // LSR 653 | u32 shift = ((reg[opB] << 4) | sb) >> (opN - 3); 654 | sb = shift & 0x0f; 655 | x1 = (shift >> 4) & 0xffff; 656 | } 657 | break; 658 | 659 | case 6: 660 | cycle_count += 3; 661 | if (opA == 7) 662 | cycle_count += 2; 663 | 664 | if (opN < 4) { // ROL 665 | u32 shift = ((((sb << 16) | reg[opB]) << 4) | sb) << (opN + 1); 666 | sb = (shift >> 20) & 0x0f; 667 | x1 = (shift >> 4) & 0xffff; 668 | } else { // ROR 669 | u32 shift = ((((sb << 16) | reg[opB]) << 4) | sb) >> (opN - 3); 670 | sb = shift & 0x0f; 671 | x1 = (shift >> 4) & 0xffff; 672 | } 673 | break; 674 | 675 | case 7: 676 | cycle_count += 5; 677 | if (opA == 7) 678 | cycle_count++; 679 | 680 | x1 = load(opimm); 681 | break; 682 | 683 | default: 684 | goto bad; 685 | } 686 | 687 | //debug("--> args: %04x %04x\n", x0, x1); 688 | 689 | // then, perform the alu op 690 | switch (op0) { 691 | case 0: // ADD 692 | x = x0 + x1; 693 | break; 694 | case 1: // ADC 695 | x = x0 + x1; 696 | if (reg[6] & 0x40) 697 | x++; 698 | break; 699 | case 2: case 4: // SUB, CMP 700 | x = x0 + (~x1 & 0xffff) + 1; 701 | break; 702 | case 3: // SBC 703 | x = x0 + (~x1 & 0xffff); 704 | if (reg[6] & 0x40) 705 | x++; 706 | break; 707 | case 6: // NEG 708 | x = -x1; 709 | break; 710 | case 8: // XOR 711 | x = x0 ^ x1; 712 | break; 713 | case 9: // LOAD 714 | x = x1; 715 | break; 716 | case 10: // OR 717 | x = x0 | x1; 718 | break; 719 | case 11: case 12: // AND, TEST 720 | x = x0 & x1; 721 | break; 722 | case 13: // STORE 723 | store(x0, d); 724 | return; 725 | default: 726 | goto bad; 727 | } 728 | 729 | // set N and Z flags 730 | if (op0 < 13 && opA != 7) { // not STORE 731 | reg[6] = (reg[6] & ~0x0300); 732 | 733 | if (x & 0x8000) 734 | reg[6] |= 0x0200; 735 | 736 | if ((x & 0xffff) == 0) 737 | reg[6] |= 0x0100; 738 | } 739 | 740 | // set S and C flags 741 | if (op0 < 5 && opA != 7) { // ADD, ADC, SUB, SBC, CMP 742 | reg[6] = (reg[6] & ~0x00c0); 743 | 744 | if (x != (u16)x) 745 | reg[6] |= 0x40; 746 | 747 | // this only works for cmp, not for add (or sbc). 748 | if ((s16)x0 < (s16)x1) 749 | reg[6] |= 0x80; 750 | } 751 | 752 | if (op0 == 4 || op0 == 12) // CMP, TEST 753 | return; 754 | 755 | //debug("--> result: %04x\n", x); 756 | 757 | if (op1 == 4 && opN == 3) // [imm16] = ... 758 | store(x, d); 759 | else 760 | reg[opA] = x; 761 | 762 | return; 763 | 764 | 765 | bad: 766 | set_cs_pc(old_cs_pc); 767 | print_state(); 768 | fatal("! UNIMPLEMENTED\n"); 769 | } 770 | 771 | static void do_idle(void) 772 | { 773 | static u32 last_retrace_time = 0; 774 | 775 | u32 now = get_realtime(); 776 | if (now < last_retrace_time + PERIOD) { 777 | // debug(" sleeping %dus\n", last_retrace_time + PERIOD - now); 778 | usleep(last_retrace_time + PERIOD - now); 779 | } 780 | last_retrace_time = now; 781 | } 782 | 783 | u16 get_video_line(void) 784 | { 785 | return line_count; 786 | } 787 | 788 | static void run_line(void) 789 | { 790 | int idle = 0; 791 | 792 | for (;;) { 793 | // if (timer_triggered != timer_handled) 794 | // break; 795 | 796 | if (trace) 797 | print_state(); 798 | 799 | if (trace_new && ever_ran_this[cs_pc()] == 0) { 800 | ever_ran_this[cs_pc()] = 1; 801 | print_state(); 802 | } 803 | 804 | if (cs_pc() == board->idle_pc) { 805 | idle++; 806 | if (idle == 2) { 807 | cycle_count = 0; 808 | break; 809 | } 810 | } 811 | 812 | step(); 813 | if (cycle_count >= cycles_per_line) { 814 | cycle_count -= cycles_per_line; 815 | break; 816 | } 817 | } 818 | } 819 | 820 | static void do_controller(void) 821 | { 822 | char key; 823 | 824 | do { 825 | key = update_controller(); 826 | 827 | switch (key) { 828 | case 0x1b: 829 | printf("Goodbye.\n"); 830 | exit(0); 831 | 832 | case '0' ... '7': 833 | printf("*** doing IRQ %c\n", key); 834 | do_irq(key - '0'); 835 | break; 836 | 837 | case '8': 838 | printf("*** doing FIQ\n"); 839 | do_fiq(); 840 | break; 841 | 842 | case 't': 843 | trace ^= 1; 844 | break; 845 | 846 | case 'y': 847 | trace_new ^= 1; 848 | break; 849 | 850 | case 'u': 851 | pause_after_every_frame ^= 1; 852 | break; 853 | 854 | case 'v': 855 | dump(0x2800, 0x80); 856 | break; 857 | 858 | case 'c': 859 | dump(0x2900, 0x300); 860 | break; 861 | 862 | case 'q': 863 | mute_audio ^= 1; 864 | break; 865 | 866 | case 'w': 867 | do_extint1 = 1; 868 | break; 869 | 870 | case 'e': 871 | do_extint2 = 1; 872 | break; 873 | 874 | case 'a': 875 | hide_page_1 ^= 1; 876 | break; 877 | 878 | case 's': 879 | hide_page_2 ^= 1; 880 | break; 881 | 882 | case 'd': 883 | hide_sprites ^= 1; 884 | break; 885 | 886 | case 'f': 887 | show_fps ^= 1; 888 | break; 889 | 890 | case 'x': 891 | dump(0, 0x4000); 892 | break; 893 | } 894 | } while (key); 895 | } 896 | 897 | static void run(void) 898 | { 899 | for (line_count = 0; line_count < lines_per_field; line_count++) { 900 | run_line(); 901 | 902 | timer_run(cycles_per_line); 903 | 904 | if (line_count == 240) 905 | mem[0x2863] |= 1; // trigger vblank IRQ 906 | if (line_count == mem[0x2836]) 907 | mem[0x2863] |= 2; // trigger vpos IRQ 908 | 909 | maybe_enter_irq(); 910 | } 911 | 912 | // static u32 last; 913 | // u32 now = get_realtime(); 914 | // debug("-> %uus for this field (cpu)\n", now - last); 915 | // last = now; 916 | 917 | do_idle(); 918 | 919 | render(); 920 | 921 | // now = get_realtime(); 922 | // debug("-> %uus for this field (gfx)\n", now - last); 923 | // last = now; 924 | 925 | do_controller(); 926 | 927 | mem[0x3d22] |= 0x0100; // UART FIXME 928 | 929 | if (do_extint1) { 930 | mem[0x3d22] |= 0x0200; 931 | do_extint1 = 0; 932 | } 933 | if (do_extint2) { 934 | mem[0x3d22] |= 0x1000; 935 | do_extint2 = 0; 936 | } 937 | 938 | if (pause_after_every_frame) { 939 | printf("*** paused, press a key to continue\n"); 940 | 941 | while (update_controller() == 0) 942 | ; 943 | } 944 | } 945 | 946 | void emu(void) 947 | { 948 | platform_init(); 949 | read_rom(0); 950 | board_init(); 951 | io_init(); 952 | video_init(); 953 | audio_init(); 954 | 955 | memset(reg, 0, sizeof reg); 956 | reg[7] = mem[0xfff7]; // reset vector 957 | 958 | for (;;) 959 | run(); 960 | } 961 | -------------------------------------------------------------------------------- /emu.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _EMU_H 6 | #define _EMU_H 7 | 8 | #include "types.h" 9 | 10 | #define N_MEM 0x400000 11 | 12 | extern u16 mem[N_MEM]; 13 | 14 | void emu(void); 15 | 16 | u32 get_ds(void); 17 | void set_ds(u32 ds); 18 | u16 get_video_line(void); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /i2c-bus.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include "types.h" 6 | #include 7 | 8 | #include "i2c.h" 9 | 10 | 11 | static void i2c_bus_find_device(struct i2c_bus *bus, u8 address) 12 | { 13 | struct i2c_device *dev; 14 | 15 | for (dev = bus->devices; dev; dev = dev->next) 16 | if (dev->address(dev, address) == 0) 17 | break; 18 | 19 | bus->device = dev; 20 | } 21 | 22 | static int i2c_bus_write(struct i2c_bus *bus, u8 data) 23 | { 24 | struct i2c_device *dev = bus->device; 25 | return dev->write(dev, data); 26 | } 27 | 28 | static u8 i2c_bus_read(struct i2c_bus *bus) 29 | { 30 | struct i2c_device *dev = bus->device; 31 | return dev->read(dev); 32 | } 33 | 34 | static void i2c_bus_read_ack(struct i2c_bus *bus, int ack) 35 | { 36 | struct i2c_device *dev = bus->device; 37 | if (dev->read_ack) 38 | dev->read_ack(dev, ack); 39 | } 40 | 41 | 42 | struct i2c_bitbang_bus { 43 | struct i2c_bus bus; 44 | 45 | void (*clock)(struct i2c_bitbang_bus *); 46 | 47 | int sda, scl, sda_dev; 48 | int dir; 49 | u8 bits; 50 | int nbits; 51 | }; 52 | 53 | struct i2c_bus *i2c_bitbang_bus_create(void) 54 | { 55 | return calloc(sizeof(struct i2c_bitbang_bus), 1); 56 | } 57 | 58 | static void clock_write(struct i2c_bitbang_bus *bb); 59 | 60 | static void clock_write_ack(struct i2c_bitbang_bus *bb) 61 | { 62 | if (bb->sda_dev == 0) { 63 | bb->clock = clock_write; 64 | bb->bits = 0; 65 | bb->nbits = 0; 66 | bb->sda_dev = 1; 67 | } else { 68 | bb->clock = 0; 69 | } 70 | } 71 | 72 | static void clock_write(struct i2c_bitbang_bus *bb) 73 | { 74 | bb->bits = 2*bb->bits + bb->sda; 75 | bb->nbits++; 76 | if (bb->nbits == 8) { 77 | bb->sda_dev = i2c_bus_write(&bb->bus, bb->bits); 78 | bb->clock = clock_write_ack; 79 | } 80 | } 81 | 82 | static void clock_read(struct i2c_bitbang_bus *bb); 83 | 84 | static void clock_read_ack(struct i2c_bitbang_bus *bb) 85 | { 86 | i2c_bus_read_ack(&bb->bus, bb->sda); 87 | bb->clock = clock_read; 88 | if (bb->sda == 0) { 89 | bb->bits = i2c_bus_read(&bb->bus); 90 | bb->nbits = 0; 91 | bb->sda_dev = bb->bits >> 7; 92 | bb->clock = clock_read; 93 | } else 94 | bb->clock = 0; 95 | } 96 | 97 | static void clock_read(struct i2c_bitbang_bus *bb) 98 | { 99 | bb->bits = 2*bb->bits; 100 | bb->sda_dev = bb->bits >> 7; 101 | bb->nbits++; 102 | if (bb->nbits == 8) { 103 | bb->sda_dev = 1; 104 | bb->clock = clock_read_ack; 105 | } 106 | } 107 | 108 | static void clock_address_ack(struct i2c_bitbang_bus *bb) 109 | { 110 | if (bb->dir == 0) { 111 | bb->clock = clock_write; 112 | bb->bits = 0; 113 | bb->nbits = 0; 114 | bb->sda_dev = 1; 115 | } else { 116 | bb->clock = clock_read; 117 | bb->bits = i2c_bus_read(&bb->bus); 118 | bb->nbits = 0; 119 | bb->sda_dev = bb->bits >> 7; 120 | } 121 | } 122 | 123 | static void clock_address(struct i2c_bitbang_bus *bb) 124 | { 125 | bb->bits = 2*bb->bits + bb->sda; 126 | bb->nbits++; 127 | if (bb->nbits == 8) { 128 | i2c_bus_find_device(&bb->bus, bb->bits); 129 | if (bb->bus.device == 0) { 130 | bb->clock = 0; 131 | return; 132 | } 133 | bb->sda_dev = 0; 134 | bb->dir = bb->bits & 1; 135 | bb->clock = clock_address_ack; 136 | } 137 | } 138 | 139 | int i2c_bitbang(struct i2c_bus *bus, int sda, int scl) 140 | { 141 | struct i2c_bitbang_bus *bb = subtype(struct i2c_bitbang_bus, bus, bus); 142 | int old_sda = bb->sda; 143 | int old_scl = bb->scl; 144 | bb->sda = sda; 145 | bb->scl = scl; 146 | 147 | if (old_scl == 1 && scl == 1) { 148 | if (old_sda == 1 && sda == 0) { // START 149 | bb->clock = clock_address; 150 | bb->bits = 0; 151 | bb->nbits = -1; 152 | bb->sda_dev = 1; 153 | bus->device = 0; 154 | } 155 | if (old_sda == 0 && sda == 1) { // STOP 156 | bb->clock = 0; 157 | bb->sda_dev = 1; 158 | } 159 | } 160 | 161 | if (old_scl == 1 && scl == 0 && bb->clock) 162 | bb->clock(bb); 163 | 164 | return sda & bb->sda_dev; 165 | } 166 | -------------------------------------------------------------------------------- /i2c-eeprom.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | 7 | #include "types.h" 8 | #include "platform.h" 9 | 10 | #include "i2c.h" 11 | #include 12 | 13 | 14 | struct i2c_eeprom { 15 | struct i2c_device dev; 16 | 17 | u8 *data; 18 | u32 size; // in bytes 19 | 20 | void *file_cookie; 21 | 22 | u8 address; // i2c bus address 23 | u32 current; // data pointer 24 | int address_bytes_left; 25 | }; 26 | 27 | 28 | static int i2c_eeprom_address(struct i2c_device *dev, u8 address) 29 | { 30 | struct i2c_eeprom *eeprom = subtype(struct i2c_eeprom, dev, dev); 31 | 32 | // XXX: for 24c04 only 33 | if ((address & 0xfc) != eeprom->address) 34 | return 1; 35 | 36 | if ((address & 1) == 0) { 37 | // XXX: for 24c04 only 38 | eeprom->address_bytes_left = 1; 39 | eeprom->current = (address >> 1) & 1; 40 | } 41 | 42 | return 0; 43 | } 44 | 45 | static int i2c_eeprom_write(struct i2c_device *dev, u8 data) 46 | { 47 | struct i2c_eeprom *eeprom = subtype(struct i2c_eeprom, dev, dev); 48 | 49 | if (eeprom->address_bytes_left) { 50 | eeprom->address_bytes_left--; 51 | eeprom->current = (eeprom->current << 8) | data; 52 | } else { 53 | eeprom->data[eeprom->current] = data; 54 | //debug("EEPROM: wrote %02x @ %02x, writing to file\n", data, eeprom->current); 55 | save_eeprom(eeprom->file_cookie, eeprom->data, eeprom->size); 56 | // XXX: for 24c04 only 57 | eeprom->current = (eeprom->current & ~0x0f) | 58 | ((eeprom->current + 1) & 0x0f); 59 | } 60 | return 0; 61 | } 62 | 63 | static u8 i2c_eeprom_read(struct i2c_device *dev) 64 | { 65 | struct i2c_eeprom *eeprom = subtype(struct i2c_eeprom, dev, dev); 66 | 67 | u8 data = eeprom->data[eeprom->current]; 68 | // XXX: for 24c04 only 69 | eeprom->current = (eeprom->current + 1) & 0x1ff; 70 | 71 | return data; 72 | } 73 | 74 | void i2c_eeprom_create(struct i2c_bus *bus, u32 size, u8 address, 75 | const char *file_name) 76 | { 77 | struct i2c_eeprom *eeprom = malloc(sizeof *eeprom); 78 | eeprom->dev.next = bus->devices; 79 | bus->devices = &eeprom->dev; 80 | 81 | eeprom->dev.address = i2c_eeprom_address; 82 | eeprom->dev.write = i2c_eeprom_write; 83 | eeprom->dev.read = i2c_eeprom_read; 84 | eeprom->dev.read_ack = 0; 85 | 86 | eeprom->data = calloc(size, 1); 87 | eeprom->size = size; 88 | 89 | eeprom->file_cookie = open_eeprom(file_name, eeprom->data, size); 90 | 91 | eeprom->address = address; 92 | eeprom->current = 0; 93 | } 94 | -------------------------------------------------------------------------------- /i2c.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _I2C_H 6 | #define _I2C_H 7 | 8 | #include "types.h" 9 | 10 | 11 | // Device. 12 | 13 | struct i2c_device { 14 | struct i2c_device *next; // devices on this bus 15 | 16 | int (*address)(struct i2c_device *dev, u8 address); // returns ack 17 | int (*write)(struct i2c_device *dev, u8 data); // returns ack 18 | u8 (*read)(struct i2c_device *dev); // returns data 19 | void (*read_ack)(struct i2c_device *dev, int ack); 20 | }; 21 | 22 | 23 | // Bus. 24 | 25 | struct i2c_bus { 26 | struct i2c_device *devices; // all devices on this bus 27 | struct i2c_device *device; // currently addressed device 28 | }; 29 | 30 | 31 | // EEPROM device. 32 | 33 | void i2c_eeprom_create(struct i2c_bus *bus, u32 size, u8 address, 34 | const char *file_name); 35 | 36 | 37 | // Bitbang bus. 38 | 39 | struct i2c_bus *i2c_bitbang_bus_create(void); 40 | int i2c_bitbang(struct i2c_bus *bus, int sda, int scl); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | #include "emu.h" 10 | #include "platform.h" 11 | #include "board.h" 12 | #include "timer.h" 13 | 14 | #include "io.h" 15 | 16 | int trace_unknown_io = 1; 17 | 18 | static const u16 known_reg_bits[] = { 19 | 0x001f, // 3d00 GPIO control 20 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 3d01..3d05 IOA 21 | 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0x00ff, // 3d06..3d0a IOB 22 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 3d0b..3d0f IOC 23 | 0x000f, // 3d10 timebase freq 24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3d11..3d1f 25 | 0x4006, // 3d20 system control 26 | 0x3ffb, // 3d21 IRQ control 27 | 0x7fff, // 3d22 IRQ status 28 | 0x003e, // 3d23 memory control 29 | 0xffff, // 3d24 watchdog 30 | 0x2002, // 3d25 ADC control 31 | 0, 0, // 3d26..3d27 32 | 0xffff, // 3d28 sleep 33 | 0x0080, // 3d29 wakeup source 34 | 0x00ff, // 3d2a wakeup delay 35 | 0x0001, // 3d2b PAL/NTSC 36 | 0, 0, // 3d2c..3d2d 37 | 0x0007, // 3d2e FIQ control 38 | 0x003f, // 3d2f DS 39 | 0x00ef, // 3d30 UART control 40 | 0x0003, // 3d31 UART status 41 | 0, // 3d32 42 | 0x00ff, 0x00ff, // 3d33..3d34 UART baud rate 43 | 0x00ff, // 3d35 UART TX 44 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3d36..3d3f 45 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3d40..3d4f 46 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3d50..3d5f 47 | }; 48 | 49 | static const u16 known_dma_reg_bits[] = { 50 | 0xffff, // source low 51 | 0x003f, // source high 52 | 0x3fff, // len 53 | 0x3fff, // dest 54 | }; 55 | 56 | static void trace_unknown(u32 addr, u16 val, int is_read) 57 | { 58 | if (addr >= 0x3d00 && addr < 0x3d00 + ARRAY_SIZE(known_reg_bits)) { 59 | if (val & ~known_reg_bits[addr - 0x3d00]) 60 | debug("*** UNKNOWN IO REG BITS %s: %04x bits %04x\n", 61 | is_read ? "READ" : "WRITE", addr, 62 | val & ~known_reg_bits[addr - 0x3d00]); 63 | } else if (addr >= 0x3e00 && addr < 0x3e00 + ARRAY_SIZE(known_dma_reg_bits)) { 64 | if (val & ~known_dma_reg_bits[addr - 0x3e00]) 65 | debug("*** UNKNOWN DMA REG BITS %s: %04x bits %04x\n", 66 | is_read ? "READ" : "WRITE", addr, 67 | val & ~known_reg_bits[addr - 0x3e00]); 68 | } else 69 | debug("*** UNKNOWN IO %s: [%04x] = %04x\n", 70 | is_read ? "READ" : "WRITE", addr, val); 71 | } 72 | 73 | 74 | static u8 seeprom[0x400]; 75 | 76 | static void do_i2c(void) 77 | { 78 | u32 addr; 79 | 80 | addr = (mem[0x3d5b] & 0x06) << 7; 81 | addr |= mem[0x3d5c] & 0xff; 82 | 83 | if (mem[0x3d58] & 0x40) 84 | mem[0x3d5e] = seeprom[addr]; 85 | else 86 | seeprom[addr] = mem[0x3d5d]; 87 | 88 | mem[0x3d59] |= 1; 89 | } 90 | 91 | static void do_gpio(u32 addr) 92 | { 93 | u32 n = (addr - 0x3d01) / 5; 94 | u16 buffer = mem[0x3d02 + 5*n]; 95 | u16 dir = mem[0x3d03 + 5*n]; 96 | u16 attr = mem[0x3d04 + 5*n]; 97 | u16 special = mem[0x3d05 + 5*n]; 98 | 99 | u16 push = dir; 100 | u16 pull = (~dir) & (~attr); 101 | u16 what = (buffer & (push | pull)); 102 | what ^= (dir & ~attr); 103 | what &= ~special; 104 | 105 | if (board->gpio) 106 | what = board->gpio(n, what, push, pull, special); 107 | 108 | mem[0x3d01 + 5*n] = what; 109 | } 110 | 111 | static void do_dma(u32 len) 112 | { 113 | u32 src = ((mem[0x3e01] & 0x3f) << 16) | mem[0x3e00]; 114 | u32 dst = mem[0x3e03] & 0x3fff; 115 | u32 j; 116 | 117 | for (j = 0; j < len; j++) 118 | mem[dst+j] = mem[src+j]; 119 | 120 | mem[0x3e02] = 0; 121 | } 122 | 123 | static void do_tmb1(void) 124 | { 125 | mem[0x3d22] |= 0x0001; 126 | } 127 | 128 | static void do_tmb2(void) 129 | { 130 | mem[0x3d22] |= 0x0002; 131 | } 132 | 133 | static struct timer timer_tmb1 = { 134 | .name = "TMB1", 135 | .interval = 27000000/8, 136 | .run = do_tmb1 137 | }; 138 | 139 | static struct timer timer_tmb2 = { 140 | .name = "TMB2", 141 | .interval = 27000000/128, 142 | .run = do_tmb2 143 | }; 144 | 145 | void io_store(u16 val, u32 addr) 146 | { 147 | if (trace_unknown_io) 148 | trace_unknown(addr, val, 0); 149 | 150 | switch (addr) { 151 | case 0x3d00: // GPIO special function select 152 | if ((mem[addr] & 0x0001) != (val & 0x0001)) 153 | debug("*** IOA SPECIAL set #%d\n", val & 0x0001); 154 | if ((mem[addr] & 0x0002) != (val & 0x0002)) 155 | debug("*** IOB SPECIAL set #%d\n", (val & 0x0002) >> 1); 156 | if ((mem[addr] & 0x0004) != (val & 0x0004)) 157 | debug("*** IOA WAKE %sabled\n", 158 | (val & 0x0004) ? "en" : "dis"); 159 | if ((mem[addr] & 0x0008) != (val & 0x0008)) 160 | debug("*** IOB WAKE %sabled\n", 161 | (val & 0x0008) ? "en" : "dis"); 162 | if ((mem[addr] & 0x0010) != (val & 0x0010)) 163 | debug("*** IOC WAKE %sabled\n", 164 | (val & 0x0010) ? "en" : "dis"); 165 | break; 166 | 167 | // 3d01 3d06 3d0b data 168 | // 3d02 3d07 3d0c buffer 169 | // 3d03 3d08 3d0d dir 170 | // 3d04 3d09 3d0e attrib 171 | // 3d05 3d0a 3d0f special 172 | 173 | case 0x3d01: 174 | case 0x3d06: 175 | case 0x3d0b: // port A, B, C data 176 | addr++; 177 | // fallthrough: write to buffer instead 178 | 179 | case 0x3d05: case 0x3d0a: case 0x3d0f: // GPIO special function select 180 | // fallthrough 181 | 182 | case 0x3d02 ... 0x3d04: // port A 183 | case 0x3d07 ... 0x3d09: // port B 184 | case 0x3d0c ... 0x3d0e: // port C 185 | mem[addr] = val; 186 | do_gpio(addr); 187 | return; 188 | 189 | case 0x3d10: // timebase control 190 | if ((mem[addr] & 0x0003) != (val & 0x0003)) { 191 | u16 hz = 8 << (val & 0x0003); 192 | debug("*** TMB1 FREQ set to %dHz\n", hz); 193 | timer_tmb1.interval = 27000000 / hz; 194 | } 195 | if ((mem[addr] & 0x000c) != (val & 0x000c)) { 196 | u16 hz = 128 << ((val & 0x000c) >> 2); 197 | debug("*** TMB2 FREQ set to %dHz\n", hz); 198 | timer_tmb2.interval = 27000000 / hz; 199 | } 200 | break; 201 | 202 | // case 0x3d11: timebase clear 203 | // case 0x3d12: 204 | // case 0x3d13: 205 | // case 0x3d14: timer A enable 206 | // case 0x3d15: timer A IRQ ack 207 | // case 0x3d16: 208 | // case 0x3d17: 209 | // case 0x3d18: timer B enable 210 | // case 0x3d19: timer B IRQ ack 211 | // case 0x3d1a: 212 | // case 0x3d1b: 213 | // case 0x3d1c: 214 | // case 0x3d1d: 215 | // case 0x3d1e: 216 | // case 0x3d1f: 217 | 218 | case 0x3d20: // system control 219 | if ((mem[addr] & 0x4000) != (val & 0x4000)) 220 | debug("*** SLEEP MODE %sabled\n", 221 | (val & 0x4000) ? "en" : "dis"); 222 | break; 223 | 224 | case 0x3d21: // IRQ control 225 | break; 226 | 227 | case 0x3d22: // IRQ ack 228 | mem[addr] &= ~val; 229 | return; 230 | 231 | case 0x3d23: // memory control 232 | if ((mem[addr] & 0x0006) != (val & 0x0006)) 233 | debug("*** MEMORY WAIT STATE switched to %d\n", (val & 0x0006) >> 1); 234 | if ((mem[addr] & 0x0038) != (val & 0x0038)) 235 | debug("*** BUS ARBITER switched to mode %d\n", (val & 0x0038) >> 3); 236 | break; 237 | 238 | case 0x3d24: // watchdog 239 | if (val != 0x55aa) 240 | debug("*** WEIRD WATCHDOG WRITE: %04x\n", val); 241 | break; 242 | 243 | case 0x3d25: // ADC control 244 | if ((mem[addr] & 0x0002) != (val & 0x0002)) 245 | debug("*** ADC %sabled\n", 246 | (val & 0002) ? "dis" : "en"); 247 | mem[addr] = ((val | mem[addr]) & 0x2000) ^ val; 248 | return; 249 | 250 | // case 0x3d26: 251 | // case 0x3d27: ADC data 252 | 253 | case 0x3d28: // sleep 254 | if (val != 0xaa55) 255 | debug("*** WEIRD SLEEP WRITE: %04x\n", val); 256 | break; 257 | 258 | case 0x3d29: // wakeup source 259 | if ((mem[addr] & 0x0080) != (val & 0x0080)) 260 | debug("*** WAKE ON KEY %sabled\n", 261 | (val & 0x0080) ? "en" : "dis"); 262 | break; 263 | 264 | case 0x3d2a: // wakeup delay 265 | if ((mem[addr] & 0x00ff) != (val & 0x00ff)) 266 | debug("*** WAKE DELAY %d\n", val & 0x00ff); 267 | break; 268 | 269 | // case 0x3d2b: PAL/NTSC 270 | // case 0x3d2c: PRNG #1 271 | // case 0x3d2d: PRNG #2 272 | 273 | case 0x3d2e: // FIQ select 274 | if ((mem[addr] & 0x0007) != (val & 0x0007)) 275 | debug("*** FIQ select %d\n", val & 0x0007); 276 | break; 277 | 278 | case 0x3d2f: // DS 279 | set_ds(val); 280 | break; 281 | 282 | case 0x3d30: { // UART control 283 | static const char *mode[4] = { 284 | "0", "1", "odd", "even" 285 | }; 286 | if ((mem[addr] & 0x0001) != (val & 0x0001)) 287 | debug("*** UART RX IRQ %sabled\n", 288 | (val & 0x0001) ? "en" : "dis"); 289 | if ((mem[addr] & 0x0002) != (val & 0x0002)) 290 | debug("*** UART TX IRQ %sabled\n", 291 | (val & 0x0002) ? "en" : "dis"); 292 | if ((mem[addr] & 0x000c) != (val & 0x000c)) 293 | debug("*** UART 9th bit is %s\n", 294 | mode[(val & 0x000c) >> 2]); 295 | if ((mem[addr] & 0x0020) != (val & 0x0020)) 296 | debug("*** UART 9th bit %sabled\n", 297 | (val & 0x0020) ? "en" : "dis"); 298 | if ((mem[addr] & 0x0040) != (val & 0x0040)) 299 | debug("*** UART RX %sabled\n", 300 | (val & 0x0040) ? "en" : "dis"); 301 | if ((mem[addr] & 0x0080) != (val & 0x0080)) 302 | debug("*** UART TX %sabled\n", 303 | (val & 0x0080) ? "en" : "dis"); 304 | break; 305 | } 306 | 307 | case 0x3d31: // UART status 308 | mem[addr] ^= (mem[addr] & 0x0003 & val); 309 | return; 310 | 311 | // case 0x3d32: // UART reset 312 | 313 | case 0x3d33: // UART baud rate low byte 314 | debug("*** UART BAUD RATE is %u\n", 27000000 / 16 / (0x10000 - (mem[0x3d34] << 8) - val)); 315 | break; 316 | 317 | case 0x3d34: // UART baud rate high byte 318 | debug("*** UART BAUD RATE is %u\n", 27000000 / 16 / (0x10000 - (val << 8) - mem[0x3d33])); 319 | break; 320 | 321 | case 0x3d35: // UART TX data 322 | if (board->uart_send) 323 | board->uart_send(val); 324 | else 325 | debug("UART write %02x (not hooked up)\n", val); 326 | return; 327 | 328 | 329 | // case 0x3d36: UART RX data 330 | // case 0x3d37: 331 | // case 0x3d38: 332 | // case 0x3d39: 333 | // case 0x3d3a: 334 | // case 0x3d3b: 335 | // case 0x3d3c: 336 | // case 0x3d3d: 337 | // case 0x3d3e: 338 | // case 0x3d3f: 339 | // case 0x3d40: SPI control 340 | // case 0x3d41: SPI data 341 | // case 0x3d42: SPI clock 342 | // case 0x3d43: 343 | // case 0x3d44: 344 | // case 0x3d45: 345 | // case 0x3d46: 346 | // case 0x3d47: 347 | // case 0x3d48: 348 | // case 0x3d49: 349 | // case 0x3d4a: 350 | // case 0x3d4b: 351 | // case 0x3d4c: 352 | // case 0x3d4d: 353 | // case 0x3d4e: 354 | // case 0x3d4f: 355 | // case 0x3d50: 356 | // case 0x3d51: 357 | // case 0x3d52: 358 | // case 0x3d53: 359 | // case 0x3d54: 360 | // case 0x3d55: 361 | // case 0x3d56: 362 | // case 0x3d57: 363 | 364 | case 0x3d58: // IIC command 365 | mem[addr] = val; 366 | do_i2c(); 367 | return; 368 | 369 | case 0x3d59: // IIC status/ack 370 | mem[addr] &= ~val; 371 | return; 372 | 373 | case 0x3d5a: // IIC access mode? always 80, "combined mode" 374 | break; 375 | 376 | case 0x3d5b: // IIC device address 377 | case 0x3d5c: // IIC subadr 378 | break; 379 | 380 | case 0x3d5d: // IIC data out 381 | break; 382 | 383 | case 0x3d5e: // IIC data in 384 | break; 385 | 386 | case 0x3d5f: // IIC controller mode 387 | break; 388 | 389 | case 0x3e00: // DMA source addr low 390 | case 0x3e01: // DMA source addr high 391 | break; 392 | 393 | case 0x3e02: // DMA size 394 | do_dma(val); 395 | return; 396 | 397 | case 0x3e03: // DMA target addr 398 | break; 399 | 400 | default: 401 | ; 402 | } 403 | 404 | mem[addr] = val; 405 | } 406 | 407 | u16 io_load(u32 addr) 408 | { 409 | u16 val = mem[addr]; 410 | 411 | if (trace_unknown_io) 412 | trace_unknown(addr, val, 1); 413 | 414 | switch (addr) { 415 | case 0x3d01: 416 | case 0x3d06: 417 | case 0x3d0b: // GPIO 418 | do_gpio(addr); 419 | return mem[addr]; 420 | 421 | case 0x3d02 ... 0x3d05: 422 | case 0x3d07 ... 0x3d0a: 423 | case 0x3d0c ... 0x3d0f: // GPIO 424 | break; 425 | 426 | case 0x3d1c: // video line counter 427 | val = get_video_line(); 428 | break; 429 | 430 | case 0x3d21: // IRQ control 431 | break; 432 | 433 | case 0x3d22: // IRQ status 434 | break; 435 | 436 | case 0x3d2b: // PAL/NTSC 437 | break; 438 | 439 | case 0x3d2c: case 0x3d2d: // PRNG 440 | return random(); 441 | 442 | case 0x3d2f: // DS 443 | return get_ds(); 444 | 445 | case 0x3d31: // UART status FIXME 446 | return 3; 447 | 448 | case 0x3d36: // UART RX data 449 | if (board->uart_recv) 450 | val = board->uart_recv(); 451 | else { 452 | debug("UART READ (not hooked up)\n"); 453 | val = 0; 454 | } 455 | return val; 456 | 457 | case 0x3d59: // IIC status 458 | break; 459 | 460 | case 0x3d5e: // IIC data in 461 | break; 462 | 463 | default: 464 | ; 465 | } 466 | 467 | return val; 468 | } 469 | 470 | void io_init(void) 471 | { 472 | timer_add(&timer_tmb1); 473 | timer_add(&timer_tmb2); 474 | } 475 | -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008,2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _IO_H 6 | #define _IO_H 7 | 8 | #include "types.h" 9 | 10 | void io_store(u16 val, u32 addr); 11 | u16 io_load(u32 addr); 12 | 13 | void io_init(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /major.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | 10 | int length; 11 | 12 | static u8 *map_file(const char *name) 13 | { 14 | int fd = open(name, O_RDONLY); 15 | length = lseek(fd, 0, SEEK_END); 16 | void *map = mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); 17 | close(fd); 18 | return map; 19 | } 20 | 21 | int main(int argc, const char **argv) 22 | { 23 | int n = argc - 1; 24 | u8 *data[n]; 25 | int i, j; 26 | 27 | for (i = 0; i < n; i++) 28 | data[i] = map_file(argv[i + 1]); 29 | 30 | for (j = 0; j < length; j++) { 31 | int a[256]; 32 | memset(a, 0, sizeof a); 33 | for (i = 0; i < n; i++) 34 | a[data[i][j]]++; 35 | int max = 0; 36 | u8 imax = 0; 37 | for (i = 0; i < 256; i++) 38 | if (a[i] > max) { 39 | max = a[i]; 40 | imax = i; 41 | } 42 | write(1, &imax, 1); 43 | if (max < n-1) { 44 | fprintf(stderr, "warning: at least two outliers @ %06x:", j); 45 | for (i = 0; i < n; i++) 46 | fprintf(stderr, " %02x", data[i][j]); 47 | fprintf(stderr, " (picked %02x)\n", imax); 48 | } 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /platform-sdl.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "types.h" 13 | #include "emu.h" 14 | #include "video.h" 15 | #include "audio.h" 16 | #include "render.h" 17 | 18 | #include "platform.h" 19 | 20 | 21 | #define PIXEL_SIZE 3 22 | 23 | 24 | static void *rom_file; 25 | 26 | void open_rom(const char *path) 27 | { 28 | rom_file = fopen(path, "rb"); 29 | if (!rom_file) 30 | fatal("Cannot read ROM file %s\n", path); 31 | } 32 | 33 | void read_rom(u32 offset) 34 | { 35 | u32 n; 36 | 37 | fseek(rom_file, 2*(offset + 0x4000), SEEK_SET); 38 | n = fread(mem + 0x4000, 2, N_MEM - 0x4000, rom_file); 39 | 40 | // gross, but whatever. one day i'll fix this, but not today 41 | #ifdef _BIG_ENDIAN 42 | for (u32 i = 0x4000; i < n + 0x4000; i++) 43 | mem[i] = (mem[i] << 8) | (mem[i] >> 8); 44 | #endif 45 | } 46 | 47 | 48 | void *open_eeprom(const char *name, u8 *data, u32 len) 49 | { 50 | char path[256]; 51 | char *home; 52 | FILE *fp; 53 | 54 | memset(data, 0, len); 55 | 56 | home = getenv("HOME"); 57 | if (!home) 58 | fatal("cannot find HOME\n"); 59 | snprintf(path, sizeof path, "%s/.unununium", home); 60 | mkdir(path, 0777); 61 | snprintf(path, sizeof path, "%s/.unununium/%s.eeprom", home, name); 62 | 63 | fp = fopen(path, "rb"); 64 | if (fp) { 65 | fread(data, 1, len, fp); 66 | if (ferror(fp)) 67 | fatal("error reading EEPROM file %s\n", path); 68 | fclose(fp); 69 | } 70 | 71 | fp = fopen(path, "wb"); 72 | if (!fp) 73 | fatal("cannot open EEPROM file %s\n", path); 74 | 75 | fwrite(data, 1, len, fp); 76 | fflush(fp); 77 | 78 | return fp; 79 | } 80 | 81 | void save_eeprom(void *cookie, u8 *data, u32 len) 82 | { 83 | FILE *fp = cookie; 84 | 85 | rewind(fp); 86 | 87 | fwrite(data, 1, len, fp); 88 | fflush(fp); 89 | } 90 | 91 | 92 | static SDL_Surface *sdl_surface; 93 | 94 | #ifdef RENDER_soft 95 | static inline u8 x58(u32 x) 96 | { 97 | x &= 31; 98 | return (x << 3) | (x >> 2); 99 | } 100 | 101 | u32 get_colour(u8 r, u8 g, u8 b) 102 | { 103 | return SDL_MapRGB(sdl_surface->format, r, g, b); 104 | } 105 | 106 | void render_palette(void) 107 | { 108 | u32 i; 109 | for (i = 0; i < 256; i++) { 110 | u16 p = mem[0x2b00 + i]; 111 | palette_rgb[i] = SDL_MapRGB(sdl_surface->format, 112 | x58((p >> 10) & 31), 113 | x58((p >> 5) & 31), 114 | x58(p & 31)); 115 | } 116 | } 117 | 118 | void update_screen(void) 119 | { 120 | if (SDL_MUSTLOCK(sdl_surface)) 121 | if (SDL_LockSurface(sdl_surface) < 0) 122 | fatal("oh crap\n"); 123 | 124 | u32 pitch = sdl_surface->pitch / 4; 125 | u32 *pixels = sdl_surface->pixels; 126 | 127 | u32 x, y, j; 128 | for (y = 0; y < 240; y++) { 129 | u32 *p = pixels + PIXEL_SIZE*y*pitch; 130 | u32 *s = screen + 320*y; 131 | 132 | for (x = 0; x < 320; x++) { 133 | u32 c = *s++; 134 | 135 | for (j = 0; j < PIXEL_SIZE; j++) 136 | p[PIXEL_SIZE*x + j] = c; 137 | } 138 | 139 | for (j = 1; j < PIXEL_SIZE; j++) 140 | memcpy(p + j*pitch, p, 4*PIXEL_SIZE*320); 141 | } 142 | 143 | if (SDL_MUSTLOCK(sdl_surface)) 144 | SDL_UnlockSurface(sdl_surface); 145 | 146 | SDL_UpdateRect(sdl_surface, 0, 0, 0, 0); 147 | } 148 | #endif 149 | 150 | #ifdef RENDER_gl 151 | void update_screen(void) 152 | { 153 | SDL_GL_SwapBuffers(); 154 | } 155 | #endif 156 | 157 | 158 | u8 button_up; 159 | u8 button_down; 160 | u8 button_left; 161 | u8 button_right; 162 | u8 button_A; 163 | u8 button_B; 164 | u8 button_C; 165 | u8 button_menu; 166 | 167 | static char handle_debug_key(int key) 168 | { 169 | switch (key) { 170 | case SDLK_ESCAPE: 171 | return 0x1b; 172 | case '0' ... '8': 173 | case 't': 174 | case 'y': 175 | case 'u': 176 | case 'x': 177 | case 'q': 178 | case 'w': case 'e': 179 | case 'a': case 's': case 'd': 180 | case 'f': 181 | case 'v': case 'c': 182 | return (key); 183 | } 184 | 185 | return 0; 186 | } 187 | 188 | static void handle_controller_key(int key, int down) 189 | { 190 | switch (key) { 191 | case SDLK_UP: case 'j': 192 | button_up = down; 193 | break; 194 | case SDLK_DOWN: case 'm': 195 | button_down = down; 196 | break; 197 | case SDLK_LEFT: case 'n': 198 | button_left = down; 199 | break; 200 | case SDLK_RIGHT: case ',': 201 | button_right = down; 202 | break; 203 | case SDLK_SPACE: case 'h': 204 | button_A = down; 205 | break; 206 | case 'b': case 'k': 207 | button_B = down; 208 | break; 209 | case 'g': 210 | button_C = down; 211 | break; 212 | case 'l': 213 | button_menu = down; 214 | break; 215 | } 216 | } 217 | 218 | char update_controller(void) 219 | { 220 | SDL_Event event; 221 | char key; 222 | 223 | while (SDL_PollEvent(&event)) { 224 | 225 | switch (event.type) { 226 | case SDL_KEYDOWN: 227 | key = handle_debug_key(event.key.keysym.sym); 228 | if (key) 229 | return (key); 230 | 231 | // Fallthrough. 232 | case SDL_KEYUP: 233 | handle_controller_key(event.key.keysym.sym, 234 | (event.type == SDL_KEYDOWN)); 235 | break; 236 | 237 | case SDL_QUIT: 238 | return 0x1b; 239 | 240 | case SDL_ACTIVEEVENT: 241 | case SDL_MOUSEMOTION: 242 | case SDL_MOUSEBUTTONDOWN: 243 | case SDL_MOUSEBUTTONUP: 244 | continue; 245 | 246 | default: 247 | warn("Unknown SDL event type %d\n", event.type); 248 | continue; 249 | } 250 | } 251 | 252 | return 0; 253 | } 254 | 255 | 256 | u32 get_realtime(void) 257 | { 258 | struct timeval tv; 259 | 260 | gettimeofday(&tv, 0); 261 | return 1000000*tv.tv_sec + tv.tv_usec; 262 | } 263 | 264 | 265 | void warn(const char *format, ...) 266 | { 267 | va_list ap; 268 | va_start(ap, format); 269 | vfprintf(stderr, format, ap); 270 | } 271 | 272 | #ifdef USE_DEBUG 273 | void debug(const char *format, ...) 274 | { 275 | va_list ap; 276 | va_start(ap, format); 277 | vfprintf(stderr, format, ap); 278 | } 279 | #endif 280 | 281 | void fatal(const char *format, ...) 282 | { 283 | va_list ap; 284 | va_start(ap, format); 285 | vfprintf(stderr, format, ap); 286 | exit(1); 287 | } 288 | 289 | 290 | static void mix(__unused void *cookie, u8 *data, int n) 291 | { 292 | if (mute_audio) { 293 | memset(data, 0, n); 294 | return; 295 | } 296 | 297 | audio_render((s16 *)data, n/2); 298 | } 299 | 300 | 301 | void platform_init(void) 302 | { 303 | if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0) 304 | fatal("Unable to init SDL: %s\n", SDL_GetError()); 305 | atexit(SDL_Quit); 306 | 307 | SDL_WM_SetCaption("Unununium", 0); 308 | 309 | #ifdef RENDER_soft 310 | sdl_surface = SDL_SetVideoMode(PIXEL_SIZE*320, PIXEL_SIZE*240, 0, 311 | SDL_SWSURFACE); 312 | 313 | if (!sdl_surface) 314 | fatal("Unable to initialise video: %s\n", SDL_GetError()); 315 | if (sdl_surface->format->BytesPerPixel != 4) 316 | fatal("Didn't get a 32-bit surface\n"); 317 | 318 | pixel_mask[0] = sdl_surface->format->Rmask; 319 | pixel_mask[1] = sdl_surface->format->Gmask; 320 | pixel_mask[2] = sdl_surface->format->Bmask; 321 | pixel_shift[0] = sdl_surface->format->Rshift; 322 | pixel_shift[1] = sdl_surface->format->Gshift; 323 | pixel_shift[2] = sdl_surface->format->Bshift; 324 | #endif 325 | 326 | #ifdef RENDER_gl 327 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 328 | SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); 329 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); 330 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); 331 | 332 | sdl_surface = SDL_SetVideoMode(PIXEL_SIZE*320, PIXEL_SIZE*240, 0, SDL_OPENGL); 333 | 334 | if (!sdl_surface) 335 | fatal("Unable to initialise video: %s\n", SDL_GetError()); 336 | #endif 337 | 338 | render_kill_cache(); 339 | render_init(PIXEL_SIZE); 340 | 341 | SDL_AudioSpec spec = { 342 | .freq = 44100, 343 | .format = AUDIO_S16SYS, 344 | .channels = 1, 345 | .samples = 1024, 346 | .callback = mix, 347 | .userdata = 0 348 | }, actual; 349 | int ret = SDL_OpenAudio(&spec, &actual); 350 | debug("ret: %d (%s)\n", ret, SDL_GetError()); 351 | debug("--> fr=%d ch=%d sam=%d size=%d sil=x'%04x\n", actual.freq, actual.channels, actual.samples, actual.size, actual.silence); 352 | SDL_PauseAudio(0); 353 | 354 | // First SDL input does a lot of init, do it now. 355 | update_controller(); 356 | } 357 | -------------------------------------------------------------------------------- /platform.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _PLATFORM_H 6 | #define _PLATFORM_H 7 | 8 | #include "types.h" 9 | 10 | 11 | extern u8 button_up; 12 | extern u8 button_down; 13 | extern u8 button_left; 14 | extern u8 button_right; 15 | extern u8 button_A; 16 | extern u8 button_B; 17 | extern u8 button_C; 18 | extern u8 button_menu; 19 | 20 | void platform_init(void); 21 | 22 | void open_rom(const char *path); 23 | void read_rom(u32 offset); 24 | 25 | void *open_eeprom(const char *name, u8 *data, u32 len); 26 | void save_eeprom(void *cookie, u8 *data, u32 len); 27 | 28 | extern u32 pixel_mask[3]; 29 | extern u32 pixel_shift[3]; 30 | 31 | void update_screen(void); 32 | char update_controller(void); 33 | 34 | u32 get_realtime(void); 35 | 36 | void __noreturn fatal(const char *format, ...); 37 | void warn(const char *format, ...); 38 | #ifdef USE_DEBUG 39 | void debug(const char *format, ...); 40 | #else 41 | static inline void debug(__unused const char *format, ...) { } 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /render-gl.c: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | #ifdef __APPLE__ 8 | #include 9 | #include 10 | #else 11 | #define GL_GLEXT_PROTOTYPES 12 | #include 13 | #include 14 | #endif 15 | 16 | #include "types.h" 17 | #include "emu.h" 18 | #include "platform.h" 19 | 20 | #include "render.h" 21 | 22 | 23 | static GLuint atlas_texture, palette_texture, page_coor_texture, 24 | page_colour_texture, page_hoff_texture; 25 | static GLuint tile_fragment_program, page_fragment_program; 26 | static GLuint display_list; 27 | 28 | 29 | #define error() do { int err = glGetError(); if (err) fatal("GL error 0x%04x at line %d\n", err, __LINE__); } while(0) 30 | 31 | 32 | void render_palette(void) 33 | { 34 | static u8 that[4*256]; 35 | 36 | u32 i; 37 | for (i = 0; i < 256; i++) { 38 | u16 p = mem[0x2b00 + i]; 39 | that[4*i] = 255*(p & 31) / 31.0f; 40 | that[4*i+1] = 255*((p >> 5) & 31) / 31.0f; 41 | that[4*i+2] = 255*((p >> 10) & 31) / 31.0f; 42 | that[4*i+3] = 255*(1-(p >> 15)); 43 | } 44 | 45 | glActiveTexture(GL_TEXTURE1); 46 | glBindTexture(GL_TEXTURE_1D, palette_texture); 47 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_BGRA, GL_UNSIGNED_BYTE, that); 48 | error(); 49 | } 50 | 51 | 52 | static enum render_state { 53 | state_nothing, 54 | state_page, 55 | state_tile, 56 | state_rect 57 | } current_render_state; 58 | 59 | static void set_render_state(enum render_state render_state) 60 | { 61 | if (render_state == current_render_state) 62 | return; 63 | current_render_state = render_state; 64 | 65 | switch (render_state) { 66 | case state_nothing: 67 | break; 68 | 69 | case state_page: 70 | glEnable(GL_FRAGMENT_PROGRAM_ARB); 71 | glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, page_fragment_program); 72 | glActiveTexture(GL_TEXTURE0); 73 | glBindTexture(GL_TEXTURE_2D, atlas_texture); 74 | glEnable(GL_TEXTURE_2D); 75 | glActiveTexture(GL_TEXTURE2); 76 | glEnable(GL_TEXTURE_2D); 77 | glActiveTexture(GL_TEXTURE3); 78 | glEnable(GL_TEXTURE_2D); 79 | glActiveTexture(GL_TEXTURE4); 80 | glEnable(GL_TEXTURE_1D); 81 | break; 82 | 83 | case state_tile: 84 | glEnable(GL_FRAGMENT_PROGRAM_ARB); 85 | glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, tile_fragment_program); 86 | glActiveTexture(GL_TEXTURE0); 87 | glBindTexture(GL_TEXTURE_2D, atlas_texture); 88 | glEnable(GL_TEXTURE_2D); 89 | glActiveTexture(GL_TEXTURE2); 90 | glDisable(GL_TEXTURE_2D); 91 | glActiveTexture(GL_TEXTURE3); 92 | glDisable(GL_TEXTURE_2D); 93 | glActiveTexture(GL_TEXTURE4); 94 | glDisable(GL_TEXTURE_1D); 95 | break; 96 | 97 | case state_rect: 98 | glDisable(GL_FRAGMENT_PROGRAM_ARB); 99 | glActiveTexture(GL_TEXTURE0); 100 | glDisable(GL_TEXTURE_2D); 101 | glActiveTexture(GL_TEXTURE2); 102 | glDisable(GL_TEXTURE_2D); 103 | glActiveTexture(GL_TEXTURE3); 104 | glDisable(GL_TEXTURE_2D); 105 | glActiveTexture(GL_TEXTURE4); 106 | glDisable(GL_TEXTURE_1D); 107 | } 108 | } 109 | 110 | 111 | void render_page(u32 xscroll, u32 yscroll, u32 h, u32 w) 112 | { 113 | set_render_state(state_page); 114 | 115 | u16 coors[4096]; 116 | u8 colours[4096]; 117 | u16 hoff[256]; 118 | 119 | u32 wn = 512/w, hn = 256/h; 120 | u32 x, y; 121 | 122 | u16 *coors1 = render_page_atlas_coors; 123 | u8 *colours1 = render_page_tile_colours; 124 | 125 | for (y = 0; y < hn; y++) { 126 | for (x = 0; x < wn; x++) { 127 | coors[128*y + 2*x] = 32 * *coors1++; 128 | coors[128*y + 2*x + 1] = 32 * *coors1++; 129 | colours[128*y + 2*x] = *colours1++; 130 | colours[128*y + 2*x + 1] = *colours1++; 131 | } 132 | for ( ; x < 64; x += wn) { 133 | memcpy(&coors[128*y + 2*x], &coors[128*y], 4*wn); 134 | memcpy(&colours[128*y + 2*x], &colours[128*y], 2*wn); 135 | } 136 | } 137 | memcpy(&coors[128*hn], coors, 256*(32-hn)); 138 | memcpy(&colours[128*hn], colours, 128*(32-hn)); 139 | 140 | glActiveTexture(GL_TEXTURE2); 141 | glEnable(GL_TEXTURE_2D); 142 | glBindTexture(GL_TEXTURE_2D, page_coor_texture); 143 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 64, 32, GL_LUMINANCE_ALPHA, 144 | GL_UNSIGNED_SHORT, coors); 145 | 146 | glActiveTexture(GL_TEXTURE3); 147 | glEnable(GL_TEXTURE_2D); 148 | glBindTexture(GL_TEXTURE_2D, page_colour_texture); 149 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 64, 32, GL_LUMINANCE_ALPHA, 150 | GL_UNSIGNED_BYTE, colours); 151 | 152 | for (y = 0; y < 256; y++) 153 | hoff[y] = 128*render_page_hoff[y]; 154 | 155 | glActiveTexture(GL_TEXTURE4); 156 | glEnable(GL_TEXTURE_1D); 157 | glBindTexture(GL_TEXTURE_1D, page_hoff_texture); 158 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_LUMINANCE, 159 | GL_UNSIGNED_SHORT, hoff); 160 | 161 | glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 162 | (float)xscroll/w, (float)yscroll/h, 0, 0); 163 | glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 164 | 512.0f/w, 256.0f/h, 0, 0); 165 | glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, 166 | w/2048.0f, h/2048.0f, 0, 0); 167 | 168 | float tx0 = 0.0f; 169 | float ty0 = 0.0f; 170 | float tx1 = 320.0f/512; 171 | float ty1 = 240.0f/256; 172 | 173 | // X and Y texture coordinates swapped to avoid a swizzle on 174 | // the first TEX insn (the hoff one), since that costs an extra 175 | // indirection on some cards, and then we have too many. 176 | glBegin(GL_QUADS); 177 | glTexCoord2f(ty0, tx0); 178 | glVertex2i(0, 0); 179 | glTexCoord2f(ty0, tx1); 180 | glVertex2i(320, 0); 181 | glTexCoord2f(ty1, tx1); 182 | glVertex2i(320, 240); 183 | glTexCoord2f(ty1, tx0); 184 | glVertex2i(0, 240); 185 | glEnd(); 186 | error(); 187 | } 188 | 189 | void render_texture(int x, int y, int w, int h, u16 atlas_x, u16 atlas_y, u8 pal_offset, u8 alpha) 190 | { 191 | set_render_state(state_tile); 192 | 193 | glColor4ub(pal_offset, 0, 0, alpha); 194 | error(); 195 | 196 | float tx0 = (float)atlas_x/ATLAS_W; 197 | float ty0 = (float)atlas_y/ATLAS_H; 198 | float tx1 = (float)(atlas_x + w)/ATLAS_W; 199 | float ty1 = (float)(atlas_y + h)/ATLAS_H; 200 | 201 | glBegin(GL_QUADS); 202 | glTexCoord2f(tx0, ty0); 203 | glVertex2i(x, y); 204 | glTexCoord2f(tx1, ty0); 205 | glVertex2i(x + w, y); 206 | glTexCoord2f(tx1, ty1); 207 | glVertex2i(x + w, y + h); 208 | glTexCoord2f(tx0, ty1); 209 | glVertex2i(x, y + h); 210 | glEnd(); 211 | error(); 212 | } 213 | 214 | void render_rect(int x, int y, int w, int h, u8 r, u8 g, u8 b) 215 | { 216 | set_render_state(state_rect); 217 | 218 | glBegin(GL_QUADS); 219 | glColor3ub(r, g, b); 220 | glVertex2i(x, y); 221 | glVertex2i(x + w, y); 222 | glVertex2i(x + w, y + h); 223 | glVertex2i(x, y + h); 224 | glEnd(); 225 | error(); 226 | } 227 | 228 | void render_atlas(u32 atlas_y, u32 atlas_h) 229 | { 230 | debug("atlas: %d+%d\n", atlas_y, atlas_h); 231 | 232 | glBindTexture(GL_TEXTURE_2D, atlas_texture); 233 | error(); 234 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, atlas_y, ATLAS_W, atlas_h, GL_LUMINANCE, GL_UNSIGNED_BYTE, &atlas[atlas_y*ATLAS_W]); 235 | error(); 236 | } 237 | 238 | 239 | void render_begin(void) 240 | { 241 | glClearColor(0, 0, 0, 0); 242 | glClear(GL_COLOR_BUFFER_BIT); 243 | 244 | set_render_state(state_nothing); 245 | } 246 | 247 | void render_end(void) 248 | { 249 | } 250 | 251 | 252 | static void init_textures(void) 253 | { 254 | glGenTextures(1, &palette_texture); 255 | glBindTexture(GL_TEXTURE_1D, palette_texture); 256 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 257 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 258 | glTexImage1D(GL_TEXTURE_1D, 0, 4, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); 259 | 260 | glGenTextures(1, &atlas_texture); 261 | glBindTexture(GL_TEXTURE_2D, atlas_texture); 262 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 263 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 264 | glTexImage2D(GL_TEXTURE_2D, 0, 1, ATLAS_W, ATLAS_H, 0, GL_LUMINANCE, 265 | GL_UNSIGNED_BYTE, 0); 266 | 267 | glGenTextures(1, &page_coor_texture); 268 | glBindTexture(GL_TEXTURE_2D, page_coor_texture); 269 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 270 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 271 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE16_ALPHA16, 64, 32, 0, 272 | GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT, 0); 273 | 274 | glGenTextures(1, &page_colour_texture); 275 | glBindTexture(GL_TEXTURE_2D, page_colour_texture); 276 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 277 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 278 | glTexImage2D(GL_TEXTURE_2D, 0, 2, 64, 32, 0, GL_LUMINANCE_ALPHA, 279 | GL_UNSIGNED_BYTE, 0); 280 | 281 | glGenTextures(1, &page_hoff_texture); 282 | glBindTexture(GL_TEXTURE_1D, page_hoff_texture); 283 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 284 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 285 | glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE16, 256, 0, 286 | GL_LUMINANCE, GL_UNSIGNED_SHORT, 0); 287 | 288 | error(); 289 | } 290 | 291 | static void init_fragment_program(GLuint *id, const char *program) 292 | { 293 | glGenProgramsARB(1, id); 294 | glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, *id); 295 | 296 | glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, 297 | strlen(program), program); 298 | GLint err; 299 | glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &err); 300 | if (err != -1) { 301 | printf("problem compiling fragment program:\n"); 302 | printf(" err pos = %ld\n", (long)err); 303 | printf(" err msg: %s\n", 304 | glGetString(GL_PROGRAM_ERROR_STRING_ARB)); 305 | fatal(""); 306 | } 307 | 308 | GLint n, nn; 309 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 310 | GL_PROGRAM_INSTRUCTIONS_ARB, &n); 311 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 312 | GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &nn); 313 | debug(">>> %d (%d) insns\n", n, nn); 314 | 315 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 316 | GL_PROGRAM_TEMPORARIES_ARB, &n); 317 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 318 | GL_PROGRAM_NATIVE_TEMPORARIES_ARB, &nn); 319 | debug(">>> %d (%d) temps\n", n, nn); 320 | 321 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 322 | GL_PROGRAM_PARAMETERS_ARB, &n); 323 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 324 | GL_PROGRAM_NATIVE_PARAMETERS_ARB, &nn); 325 | debug(">>> %d (%d) params\n", n, nn); 326 | 327 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 328 | GL_PROGRAM_ATTRIBS_ARB, &n); 329 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 330 | GL_PROGRAM_NATIVE_ATTRIBS_ARB, &nn); 331 | debug(">>> %d (%d) attrs\n", n, nn); 332 | 333 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 334 | GL_PROGRAM_ALU_INSTRUCTIONS_ARB, &n); 335 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 336 | GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, &nn); 337 | debug(">>> %d (%d) alu insns\n", n, nn); 338 | 339 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 340 | GL_PROGRAM_TEX_INSTRUCTIONS_ARB, &n); 341 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 342 | GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, &nn); 343 | debug(">>> %d (%d) tex insns\n", n, nn); 344 | 345 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 346 | GL_PROGRAM_TEX_INDIRECTIONS_ARB, &n); 347 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 348 | GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB, &nn); 349 | debug(">>> %d (%d) tex indirs\n", n, nn); 350 | 351 | GLint ok; 352 | glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 353 | GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &ok); 354 | if (!ok) 355 | fatal("fragment program exceeds resources\n"); 356 | } 357 | 358 | static void init_fragment_programs(void) 359 | { 360 | const char tile_frag[] = 361 | "!!ARBfp1.0\n" 362 | "OPTION ARB_precision_hint_nicest; \n" 363 | 364 | "TEMP r0; \n" 365 | 366 | "TEX r0.x, fragment.texcoord[0], texture[0], 2D; \n" //** 367 | "ADD r0.x, r0.x, fragment.color.r; \n" 368 | 369 | "TEX r0, r0, texture[1], 1D; \n" //** 370 | "MUL r0.a, r0.a, fragment.color.a; \n" 371 | "MOV result.color, r0; \n" 372 | 373 | "END"; 374 | init_fragment_program(&tile_fragment_program, tile_frag); 375 | 376 | const char page_frag[] = 377 | "!!ARBfp1.0\n" 378 | "OPTION ARB_precision_hint_nicest; \n" 379 | 380 | "TEMP coor, tile_coor, tile, tile_colour; \n" 381 | "TEMP atlas_coor, palette_coor, colour, hoff; \n" 382 | 383 | "TEX hoff, fragment.texcoord[0], texture[4], 1D; \n" //** 384 | 385 | "MOV coor, fragment.texcoord[0]; \n" 386 | "ADD coor.y, coor, hoff; \n" 387 | "MAD coor, coor.yxxx, program.local[1], program.local[0]; \n" 388 | // now coor is integer tile #, fraction coor within tile 389 | 390 | "FLR tile_coor, coor; \n" 391 | "FRC atlas_coor, coor; \n" 392 | 393 | // 1/64,1/32 394 | "PARAM tile_scale = { 0.015625, 0.03125 }; \n" 395 | "MUL tile_coor, tile_coor, tile_scale; \n" 396 | 397 | "TEX tile, tile_coor, texture[2], 2D; \n" //** 398 | "TEX tile_colour, tile_coor, texture[3], 2D; \n" 399 | "MAD atlas_coor, atlas_coor, program.local[2], tile.xwww; \n" 400 | 401 | "TEX palette_coor, atlas_coor, texture[0], 2D; \n" //** 402 | "ADD palette_coor, palette_coor, tile_colour.r; \n" 403 | 404 | "TEX colour, palette_coor, texture[1], 1D; \n" //** 405 | "MUL colour.a, colour.a, tile_colour.a; \n" 406 | "MOV result.color, colour; \n" 407 | 408 | "END"; 409 | init_fragment_program(&page_fragment_program, page_frag); 410 | } 411 | 412 | void render_init(u32 pixel_size) 413 | { 414 | debug("GL RENDERER: %s\n", glGetString(GL_RENDERER)); 415 | debug("GL VENDOR: %s\n", glGetString(GL_VENDOR)); 416 | // debug("GL EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS)); 417 | debug("GL VERSION: %s\n", glGetString(GL_VERSION)); 418 | 419 | 420 | glViewport(0, 0, pixel_size*320, pixel_size*240); 421 | glMatrixMode(GL_PROJECTION); 422 | glLoadIdentity(); 423 | glOrtho(0, 320, 240, 0, -1, 1); 424 | 425 | glMatrixMode(GL_MODELVIEW); 426 | glLoadIdentity(); 427 | 428 | glEnable(GL_BLEND); 429 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 430 | error(); 431 | 432 | init_textures(); 433 | init_fragment_programs(); 434 | 435 | display_list = glGenLists(1); 436 | } 437 | -------------------------------------------------------------------------------- /render-soft.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | //#include 6 | #include 7 | 8 | #include "types.h" 9 | #include "emu.h" 10 | #include "board.h" 11 | #include "video.h" 12 | #include "platform.h" 13 | 14 | #include "render.h" 15 | 16 | 17 | u32 pixel_mask[3]; 18 | u32 pixel_shift[3]; 19 | 20 | static inline u8 mix_channel(u8 old, u8 new, u8 alpha) 21 | { 22 | return ((255 - alpha)*old + alpha*new) / 255; 23 | } 24 | 25 | static inline void mix_pixel(u32 offset, u32 rgb, u8 alpha) 26 | { 27 | u32 x = 0; 28 | u32 i; 29 | for (i = 0; i < 3; i++) { 30 | u8 old = (screen[offset] & pixel_mask[i]) >> pixel_shift[i]; 31 | u8 new = (rgb & pixel_mask[i]) >> pixel_shift[i]; 32 | x |= (mix_channel(old, new, alpha) << pixel_shift[i]) & pixel_mask[i]; 33 | } 34 | screen[offset] = x; 35 | } 36 | 37 | void render_texture(int xoff, int yoff, int w, int h, u16 atlas_x, u16 atlas_y, u8 pal_offset, u8 alpha) 38 | { 39 | //debug("drawing at %d %d\n", atlas_x, atlas_y); 40 | u8 *p = &atlas[atlas_y*ATLAS_W + atlas_x]; 41 | 42 | for (u32 y = 0; y < (u32)h; y++) { 43 | u32 yy = (yoff + y) & 0x1ff; 44 | 45 | for (u32 x = 0; x < (u32)w; x++) { 46 | u32 xx = (xoff + x) & 0x1ff; 47 | 48 | u8 pal = p[y*ATLAS_W + x] + pal_offset; 49 | 50 | if (xx < 320 && yy < 240) { 51 | u16 rgb = mem[0x2b00 + pal]; 52 | if ((rgb & 0x8000) == 0) { 53 | if (alpha != 255) 54 | mix_pixel(xx + 320*yy, palette_rgb[pal], alpha); 55 | else 56 | screen[xx + 320*yy] = palette_rgb[pal]; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | void render_rect(int x, int y, int w, int h, u8 r, u8 g, u8 b) 64 | { 65 | u32 rgb = get_colour(r, g, b); 66 | 67 | u32 xx, yy; 68 | for (yy = y; yy < (u32)y + h; yy++) 69 | for (xx = x; xx < (u32)x + w; xx++) 70 | screen[320*yy + xx] = rgb; 71 | } 72 | 73 | void render_atlas(u32 atlas_y, u32 atlas_h) 74 | { 75 | debug("atlas: %d+%d\n", atlas_y, atlas_h); 76 | } 77 | 78 | 79 | static void render_page_tile(u32 xoff, u32 yoff, u32 h, u32 w, u16 atlas_x, u16 atlas_y, u8 pal_offset, u8 alpha) 80 | { 81 | for (u32 y = 0; y < h; y++) { 82 | int yy = yoff + y; 83 | yy &= 0x01ff; 84 | if (yy >= 0x01c0) 85 | yy -= 0x0200; 86 | 87 | int xx = xoff; 88 | if (yy < 0 || yy >= 240) 89 | continue; 90 | 91 | xx -= render_page_hoff[yy]; 92 | 93 | xx &= 0x01ff; 94 | if (xx >= 0x01c0) 95 | xx -= 0x0200; 96 | 97 | if (xx + w <= 0 || xx >= 320) 98 | continue; 99 | 100 | render_texture(xx, yy, w, 1, atlas_x, atlas_y + y, pal_offset, alpha); 101 | } 102 | } 103 | 104 | void render_page(u32 xscroll, u32 yscroll, u32 h, u32 w) 105 | { 106 | u32 hn = 256/h; 107 | u32 wn = 512/w; 108 | 109 | u32 x0, y0; 110 | for (y0 = 0; y0 < hn; y0++) 111 | for (x0 = 0; x0 < wn; x0++) { 112 | u32 index = x0 + wn*y0; 113 | 114 | u32 y = ((h*y0 - yscroll + 0x10) & 0xff) - 0x10; 115 | u32 x = (w*x0 - xscroll) & 0x1ff; 116 | 117 | u16 atlas_x = render_page_atlas_coors[2*index]; 118 | u16 atlas_y = render_page_atlas_coors[2*index+1]; 119 | u8 pal_offset = render_page_tile_colours[2*index]; 120 | u8 alpha = render_page_tile_colours[2*index+1]; 121 | 122 | render_page_tile(x, y, h, w, atlas_x, atlas_y, pal_offset, alpha); 123 | } 124 | } 125 | 126 | void render_begin(void) 127 | { 128 | memset(screen, 0, sizeof screen); 129 | } 130 | 131 | void render_end(void) 132 | { 133 | } 134 | 135 | void render_init(__unused u32 pixel_size) 136 | { 137 | } 138 | -------------------------------------------------------------------------------- /render.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include "types.h" 6 | #include "emu.h" 7 | #include "video.h" 8 | #include "board.h" // for use_centered_coors 9 | #include "platform.h" 10 | 11 | #include "render.h" 12 | 13 | 14 | u8 atlas[ATLAS_W*ATLAS_H]; 15 | static u32 atlas_x, atlas_y, atlas_h, atlas_low_x, atlas_low_y; 16 | 17 | u16 render_page_atlas_coors[4096]; 18 | u8 render_page_tile_colours[4096]; 19 | u16 render_page_hoff[256]; 20 | 21 | 22 | #define HASH_SIZE 65536 23 | #define CACHE_SIZE (HASH_SIZE + ATLAS_W*ATLAS_H/64) 24 | static struct cache { 25 | u32 key; 26 | struct cache *next; 27 | u16 atlas_x, atlas_y; 28 | } cache[CACHE_SIZE]; 29 | struct cache *cache_free_head; 30 | 31 | 32 | static const u8 sizes[] = { 8, 16, 32, 64 }; 33 | static const u8 colour_sizes[] = { 2, 4, 6, 8 }; 34 | 35 | 36 | void render_kill_cache(void) 37 | { 38 | debug("Killing atlas\n"); 39 | 40 | u32 i; 41 | for (i = 0; i < CACHE_SIZE; i++) { 42 | cache[i].key = -1U; 43 | cache[i].next = 0; 44 | } 45 | 46 | for (i = HASH_SIZE; i < CACHE_SIZE - 1; i++) 47 | cache[i].next = &cache[i + 1]; 48 | cache[CACHE_SIZE - 1].next = 0; 49 | cache_free_head = &cache[HASH_SIZE]; 50 | 51 | atlas_low_x = 0; 52 | atlas_low_y = 0; 53 | 54 | atlas_x = 0; 55 | atlas_y = 0; 56 | atlas_h = 0; 57 | } 58 | 59 | static u32 cache_key(u32 addr, u16 attr) 60 | { 61 | return (attr << 24) | addr; 62 | } 63 | 64 | static u32 cache_hash(u32 key) 65 | { 66 | return (key ^ (key >> 16)) % HASH_SIZE; 67 | } 68 | 69 | static struct cache *cache_get(u32 addr, u16 attr) 70 | { 71 | u32 key = cache_key(addr, attr); 72 | 73 | struct cache *slot; 74 | for (slot = &cache[cache_hash(key)]; slot; slot = slot->next) { 75 | if (slot->key == 0) 76 | fatal("WHOOPS, CACHE WENT BAD (get)\n"); 77 | if (slot->key == key) 78 | return slot; 79 | } 80 | 81 | fatal("WHOOPS, CACHE WENT BAD (get, slot)\n"); 82 | } 83 | 84 | static struct cache *cache_make(u32 key) 85 | { 86 | struct cache *slot = &cache[cache_hash(key)]; 87 | if (slot->key == -1U) 88 | return slot; 89 | 90 | for ( ; slot->next; slot = slot->next) { 91 | if (slot->key == key) 92 | return slot; 93 | if (slot->key == -1U) 94 | fatal("WHOOPS, CACHE WENT BAD (make)\n"); 95 | } 96 | 97 | debug("make, new slot %04x\n", cache_free_head - cache); 98 | slot->next = cache_free_head; 99 | slot = cache_free_head; 100 | cache_free_head = cache_free_head->next; 101 | 102 | slot->next = 0; 103 | return slot; 104 | } 105 | 106 | static void render_texture_into_atlas(u32 addr, u16 attr, int w, int h) 107 | { 108 | u32 key = cache_key(addr, attr); 109 | struct cache *cache = cache_make(key); 110 | 111 | if (cache->key == key) 112 | return; 113 | 114 | cache->key = key; 115 | 116 | if (atlas_x + w > ATLAS_W) { 117 | atlas_x = 0; 118 | atlas_y += atlas_h; 119 | atlas_h = 0; 120 | } 121 | 122 | if (atlas_y + h > ATLAS_H) 123 | fatal("Whoops, overran atlas\n"); 124 | 125 | u8 *p = &atlas[atlas_y*ATLAS_W + atlas_x]; 126 | 127 | //debug("rendering at %d %d\n", atlas_x, atlas_y); 128 | render_bitmap(p, ATLAS_W, addr, attr); 129 | 130 | cache->atlas_x = atlas_x; 131 | cache->atlas_y = atlas_y; 132 | 133 | atlas_x += w; 134 | 135 | if (h > atlas_h) 136 | atlas_h = h; 137 | } 138 | 139 | static void render_tile(u32 xoff, u32 yoff, u32 addr, u16 attr) 140 | { 141 | u32 h = sizes[(attr & 0x00c0) >> 6]; 142 | u32 w = sizes[(attr & 0x0030) >> 4]; 143 | 144 | u32 nc = colour_sizes[attr & 0x0003]; 145 | 146 | u8 alpha = 255; 147 | if (attr & 0x4000) 148 | alpha = mem[0x282a] << 6; 149 | 150 | u32 pal_offset = (attr & 0x0f00) >> 4; 151 | pal_offset >>= nc; 152 | pal_offset <<= nc; 153 | 154 | int yy = yoff & 0x01ff; 155 | if (yy >= 0x01c0) 156 | yy -= 0x0200; 157 | 158 | int xx = xoff & 0x01ff; 159 | if (xx >= 0x01c0) 160 | xx -= 0x0200; 161 | 162 | if (xx + w <= 0 || xx >= 320) 163 | return; 164 | if (yy + h <= 0 || yy >= 240) 165 | return; 166 | 167 | struct cache *cache = cache_get(addr, attr); 168 | u16 atlas_x = cache->atlas_x; 169 | u16 atlas_y = cache->atlas_y; 170 | 171 | render_texture(xx, yy, w, h, atlas_x, atlas_y, pal_offset, alpha); 172 | } 173 | 174 | static void render_tile_into_atlas(u32 page, u16 attr, u16 tile) 175 | { 176 | u32 h = sizes[(attr & 0x00c0) >> 6]; 177 | u32 w = sizes[(attr & 0x0030) >> 4]; 178 | 179 | u32 nc = colour_sizes[attr & 0x0003]; 180 | 181 | u32 addr = 0x40*mem[0x2820 + page] + nc*w*h/16*tile; 182 | 183 | render_texture_into_atlas(addr, attr, w, h); 184 | } 185 | 186 | static void __noinline render_page_into_atlas(u32 page) 187 | { 188 | u16 *regs = &mem[0x2810 + 6*page]; 189 | 190 | u32 attr = regs[2]; 191 | u32 ctrl = regs[3]; 192 | u32 tilemap = regs[4]; 193 | u32 palette_map = regs[5]; 194 | 195 | if ((ctrl & 8) == 0) 196 | return; 197 | 198 | u32 h = sizes[(attr & 0x00c0) >> 6]; 199 | u32 w = sizes[(attr & 0x0030) >> 4]; 200 | 201 | u32 hn = 256/h; 202 | u32 wn = 512/w; 203 | 204 | u32 x0, y0; 205 | for (y0 = 0; y0 < hn; y0++) 206 | for (x0 = 0; x0 < wn; x0++) { 207 | u32 which = (ctrl & 4) ? 0 : x0 + wn*y0; 208 | u16 tile = mem[tilemap + which]; 209 | 210 | if (tile == 0) 211 | continue; 212 | 213 | u16 palette = mem[palette_map + which/2]; 214 | if (which & 1) 215 | palette >>= 8; 216 | 217 | u32 tileattr = attr; 218 | if ((ctrl & 2) == 0) { 219 | tileattr &= ~0x000c; 220 | tileattr |= (palette >> 2) & 0x000c; // flip 221 | } 222 | 223 | render_tile_into_atlas(page, tileattr, tile); 224 | } 225 | } 226 | 227 | static void render_sprite_into_atlas(u16 *sprite) 228 | { 229 | u16 tile, attr; 230 | 231 | tile = sprite[0]; 232 | attr = sprite[3]; 233 | 234 | if (tile == 0) 235 | return; 236 | 237 | render_tile_into_atlas(2, attr, tile); 238 | } 239 | 240 | static void __noinline render_sprites_into_atlas(void) 241 | { 242 | if ((mem[0x2842] & 1) == 0) 243 | return; 244 | 245 | u32 i; 246 | for (i = 0; i < 256; i++) 247 | render_sprite_into_atlas(mem + 0x2c00 + 4*i); 248 | } 249 | 250 | static void __noinline render_page_arrays(u32 page, u32 depth) 251 | { 252 | u16 *regs = &mem[0x2810 + 6*page]; 253 | 254 | u32 attr = regs[2]; 255 | u32 ctrl = regs[3]; 256 | u32 tilemap = regs[4]; 257 | u32 palette_map = regs[5]; 258 | 259 | if ((ctrl & 8) == 0) 260 | return; 261 | 262 | if ((attr & 0x3000) >> 12 != depth) 263 | return; 264 | 265 | u32 h = sizes[(attr & 0x00c0) >> 6]; 266 | u32 w = sizes[(attr & 0x0030) >> 4]; 267 | u32 nc = colour_sizes[attr & 0x0003]; 268 | 269 | u32 hn = 256/h; 270 | u32 wn = 512/w; 271 | 272 | u32 i; 273 | for (i = 0; i < hn*wn; i++) { 274 | u32 which = (ctrl & 4) ? 0 : i; 275 | 276 | u16 tile = mem[tilemap + which]; 277 | 278 | u16 palette = mem[palette_map + which/2]; 279 | if (which & 1) 280 | palette >>= 8; 281 | 282 | u32 tileattr = attr; 283 | u32 tilectrl = ctrl; 284 | if ((ctrl & 2) == 0) { 285 | tileattr &= ~0x000c; 286 | tileattr |= (palette >> 2) & 0x000c; // flip 287 | 288 | tileattr &= ~0x0f00; 289 | tileattr |= (palette << 8) & 0x0f00; // palette 290 | 291 | tilectrl &= ~0x0100; 292 | tilectrl |= (palette << 2) & 0x0100; // blend 293 | } 294 | 295 | u16 atlas_x, atlas_y; 296 | if (tile) { 297 | u32 addr = 0x40*mem[0x2820 + page] + nc*w*h/16*tile; 298 | struct cache *cache = cache_get(addr, tileattr); 299 | atlas_x = cache->atlas_x; 300 | atlas_y = cache->atlas_y; 301 | } else { 302 | atlas_x = 0; 303 | atlas_y = 0; 304 | } 305 | 306 | u32 pal_offset = (tileattr & 0x0f00) >> 4; 307 | pal_offset >>= nc; 308 | pal_offset <<= nc; 309 | 310 | u8 alpha = 255; 311 | if (attr & 0x4000 || tilectrl & 0x0100) 312 | alpha = mem[0x282a] << 6; 313 | if (tile == 0) 314 | alpha = 0; 315 | 316 | render_page_atlas_coors[2*i] = atlas_x; 317 | render_page_atlas_coors[2*i+1] = atlas_y; 318 | render_page_tile_colours[2*i] = pal_offset; 319 | render_page_tile_colours[2*i+1] = alpha; 320 | } 321 | } 322 | 323 | static void __noinline __render_page(u32 page, u32 depth) 324 | { 325 | u16 *regs = &mem[0x2810 + 6*page]; 326 | 327 | u32 xscroll = regs[0]; 328 | u32 yscroll = regs[1]; 329 | u32 attr = regs[2]; 330 | u32 ctrl = regs[3]; 331 | 332 | if ((ctrl & 8) == 0) 333 | return; 334 | 335 | if ((attr & 0x3000) >> 12 != depth) 336 | return; 337 | 338 | u32 h = sizes[(attr & 0x00c0) >> 6]; 339 | u32 w = sizes[(attr & 0x0030) >> 4]; 340 | 341 | u32 i; 342 | for (i = 0; i < 240; i++) 343 | render_page_hoff[i] = (ctrl & 0x0010) ? mem[0x2900 + i] : 0; 344 | 345 | render_page(xscroll, yscroll, h, w); 346 | } 347 | 348 | static void render_sprite(u32 depth, u16 *sprite) 349 | { 350 | u16 tile, attr; 351 | s16 x, y; 352 | 353 | tile = sprite[0]; 354 | x = sprite[1]; 355 | y = sprite[2]; 356 | attr = sprite[3]; 357 | 358 | if (tile == 0) 359 | return; 360 | 361 | if ((u32)(attr & 0x3000) >> 12 != depth) 362 | return; 363 | 364 | u32 h = sizes[(attr & 0x00c0) >> 6]; 365 | u32 w = sizes[(attr & 0x0030) >> 4]; 366 | u32 nc = colour_sizes[attr & 0x0003]; 367 | 368 | if (board->use_centered_coors) { 369 | x = 160 + x; 370 | y = 120 - y; 371 | 372 | x -= (w/2); 373 | y -= (h/2) - 8; 374 | } 375 | 376 | x = x & 0x1ff; 377 | y = y & 0x1ff; 378 | 379 | u32 addr = 0x40*mem[0x2822] + nc*w*h/16*tile; 380 | 381 | render_tile(x, y, addr, attr); 382 | } 383 | 384 | static void __noinline render_sprites(u32 depth) 385 | { 386 | if ((mem[0x2842] & 1) == 0) 387 | return; 388 | 389 | u32 i; 390 | for (i = 0; i < 256; i++) 391 | render_sprite(depth, mem + 0x2c00 + 4*i); 392 | } 393 | 394 | static void do_show_fps(void) 395 | { 396 | static u32 fps = 0; 397 | 398 | const int per_n = 5; 399 | static int count = 0; 400 | count++; 401 | if (count == per_n) { 402 | static u32 last = 0; 403 | u32 now = get_realtime(); 404 | if (last) 405 | fps = 1000000 * per_n / (now - last); 406 | last = now; 407 | count = 0; 408 | } 409 | 410 | if (fps > 50) 411 | fps = 50; 412 | 413 | render_rect(3, 3, 7, 52, 255, 255, 255); 414 | render_rect(4, 4, 5, 50 - fps, 0, 0, 0); 415 | render_rect(4, 54 - fps, 5, fps, 0, 255, 0); 416 | } 417 | 418 | void render(void) 419 | { 420 | if (atlas_y + atlas_h + (2*512*256 + 256*64*64) / ATLAS_W > ATLAS_H) 421 | render_kill_cache(); 422 | 423 | render_begin(); 424 | 425 | if ((mem[0x3d20] & 0x0004) == 0) { // video DAC disable 426 | render_palette(); 427 | 428 | atlas_low_x = atlas_x; 429 | atlas_low_y = atlas_y; 430 | 431 | if (!hide_page_1) 432 | render_page_into_atlas(0); 433 | if (!hide_page_2) 434 | render_page_into_atlas(1); 435 | if (!hide_sprites) 436 | render_sprites_into_atlas(); 437 | 438 | if (atlas_y != atlas_low_y || atlas_x != atlas_low_x) 439 | render_atlas(atlas_low_y, atlas_y + atlas_h - atlas_low_y); 440 | 441 | u32 depth; 442 | for (depth = 0; depth < 4; depth++) { 443 | if (!hide_page_1) { 444 | render_page_arrays(0, depth); 445 | __render_page(0, depth); 446 | } 447 | if (!hide_page_2) { 448 | render_page_arrays(1, depth); 449 | __render_page(1, depth); 450 | } 451 | if (!hide_sprites) 452 | render_sprites(depth); 453 | } 454 | } 455 | 456 | if (show_fps) 457 | do_show_fps(); 458 | 459 | render_end(); 460 | 461 | update_screen(); 462 | } 463 | -------------------------------------------------------------------------------- /render.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _RENDER_H 6 | #define _RENDER_H 7 | 8 | void render_init(u32 pixel_size); 9 | void render(void); 10 | 11 | void render_kill_cache(void); 12 | 13 | void render_palette(void); 14 | void render_atlas(u32 atlas_y, u32 atlas_h); 15 | void render_begin(void); 16 | void render_end(void); 17 | 18 | 19 | extern u16 render_page_atlas_coors[4096]; 20 | extern u8 render_page_tile_colours[4096]; 21 | extern u16 render_page_hoff[256]; 22 | 23 | void render_page(u32 xscroll, u32 yscroll, u32 h, u32 w); 24 | 25 | 26 | void render_texture(int x, int y, int w, int h, u16 atlas_x, u16 atlas_y, u8 pal_offset, u8 alpha); 27 | void render_rect(int x, int y, int w, int h, u8 r, u8 g, u8 b); 28 | 29 | 30 | // ATLAS_W and ATLAS_H should be a multiple of 64 and a power of two; 31 | // We need 2*512*256 for the two video pages, and 256*64*64 for the sprites. 32 | // We also require 64 lines of slack for how we do the update. 33 | 34 | // Let's use a 2048x2048 texture, that's 4MB. 35 | #define ATLAS_W 2048 36 | #define ATLAS_H 2048 37 | 38 | extern u8 atlas[ATLAS_W*ATLAS_H]; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /timer.c: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | 7 | #include "types.h" 8 | 9 | #include "timer.h" 10 | 11 | 12 | static int trace_timer = 0; 13 | 14 | static struct timer *timers; 15 | 16 | void timer_debug(void) 17 | { 18 | struct timer *timer; 19 | for (timer = timers; timer; timer = timer->next) 20 | printf("timer \"%s\" %u ticks\n", timer->name, timer->time); 21 | } 22 | 23 | void timer_add(struct timer *timer) 24 | { 25 | if (timer->time == 0) 26 | timer->time = timer->interval; 27 | 28 | u32 time = timer->time; 29 | struct timer **p = &timers; 30 | 31 | while (*p && (*p)->time <= time) { 32 | time -= (*p)->time; 33 | p = &(*p)->next; 34 | } 35 | 36 | timer->next = *p; 37 | *p = timer; 38 | timer->time = time; 39 | if (timer->next) 40 | timer->next->time -= time; 41 | } 42 | 43 | void timer_run(u32 ticks) 44 | { 45 | //debug("going to run for %u ticks...\n", ticks); 46 | //timer_debug(); 47 | 48 | struct timer *timer; 49 | while ((timer = timers) && timer->time <= ticks) { 50 | timers = timer->next; 51 | ticks -= timer->time; 52 | 53 | if (trace_timer) 54 | printf("running timer \"%s\"\n", timer->name); 55 | 56 | if (timer->run) 57 | timer->run(); 58 | 59 | if (timer->interval) { 60 | timer->time = timer->interval; 61 | timer_add(timer); 62 | } 63 | } 64 | 65 | if (timer) 66 | timer->time -= ticks; 67 | 68 | //debug("timers done:\n"); 69 | //timer_debug(); 70 | } 71 | -------------------------------------------------------------------------------- /timer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _TIMER_H 6 | #define _TIMER_H 7 | 8 | #include "types.h" 9 | 10 | struct timer { 11 | const char *name; 12 | u32 time; 13 | u32 interval; 14 | void (*run)(void); 15 | struct timer *next; 16 | }; 17 | 18 | void timer_debug(void); 19 | 20 | void timer_add(struct timer *timer); 21 | void timer_run(u32 ticks); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _TYPES_H 6 | #define _TYPES_H 7 | 8 | #include 9 | 10 | 11 | typedef unsigned char u8; 12 | typedef unsigned short u16; 13 | typedef unsigned int u32; 14 | typedef unsigned long long u64; 15 | 16 | typedef signed short s16; 17 | typedef signed int s32; 18 | 19 | 20 | #define ARRAY_SIZE(x) (sizeof x / sizeof x[0]) 21 | 22 | 23 | #define subtype(_type, _base, _field) \ 24 | (void *)((u8 *)(_base) - offsetof(_type, _field)) 25 | 26 | 27 | #undef __unused 28 | #define __unused __attribute__((__unused__)) 29 | #define __noinline __attribute__((__noinline__)) 30 | #define __noreturn __attribute__((__noreturn__)) 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /un-disas.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008,2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "types.h" 9 | #include "disas.h" 10 | 11 | #define N_MEM 0x400000 12 | 13 | static u16 mem[N_MEM]; 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | FILE *in; 18 | u32 i, n; 19 | 20 | if (argc != 2) { 21 | fprintf(stderr, "usage: %s \n", argv[0]); 22 | exit(1); 23 | } 24 | 25 | in = fopen(argv[1], "rb"); 26 | if (!in) { 27 | perror("Cannot read ROM file"); 28 | exit(1); 29 | } 30 | 31 | n = fread(mem, 2, N_MEM, in); 32 | 33 | fclose(in); 34 | 35 | // gross, but whatever. one day i'll fix this, but not today 36 | #ifdef _BIG_ENDIAN 37 | for (i = 0; i < n; i++) 38 | mem[i] = (mem[i] << 8) | (mem[i] >> 8); 39 | #endif 40 | 41 | for (i = 0; i < n; ) 42 | i += disas(mem, i); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /un-dump.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int off, x, i; 6 | 7 | off = 0; 8 | 9 | for (;;) { 10 | printf("%06x:", off); 11 | off += 16; 12 | 13 | for (i = 0; i < 16; i++) { 14 | x = getchar(); 15 | x |= getchar() << 8; 16 | printf(" %04x", x); 17 | } 18 | printf("\n"); 19 | 20 | if (feof(stdin)) 21 | exit(0); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /uuu-sdl.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008,2009 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // We need to include this here, since it redefines main(). 10 | #include 11 | 12 | #include "types.h" 13 | #include "platform.h" 14 | #include "emu.h" 15 | #include "dialog.h" 16 | 17 | 18 | #ifdef __APPLE__ 19 | void CGSSetConnectionProperty(int, int, int, int); 20 | int _CGSDefaultConnection(); 21 | int CGSCreateCString(char *); 22 | int CGSCreateBoolean(unsigned char); 23 | void CGSReleaseObj(int); 24 | #endif 25 | 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | #ifdef __APPLE__ 30 | if (argc == 1) { 31 | const char *path = dialog_rom_file(); 32 | if (!path) 33 | return 0; 34 | open_rom(path); 35 | } 36 | #else 37 | if (0) 38 | ; 39 | #endif 40 | else { 41 | if (argc != 2) 42 | fatal("usage: %s \n", argv[0]); 43 | 44 | open_rom(argv[1]); 45 | } 46 | 47 | #ifdef __APPLE__ 48 | // Hack to speed up display refresh 49 | int propertyString = CGSCreateCString("DisableDeferredUpdates"); 50 | int cid = _CGSDefaultConnection(); 51 | CGSSetConnectionProperty(cid, cid, propertyString, CGSCreateBoolean(1)); 52 | CGSReleaseObj(propertyString); 53 | #endif 54 | 55 | srandom(time(0)); 56 | 57 | emu(); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /uuu.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RebeccaRGB/unununium/e43804bc52ce1ec1c758946b67bea62201e52bae/uuu.xcf -------------------------------------------------------------------------------- /video.c: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "types.h" 10 | #include "emu.h" 11 | #include "platform.h" 12 | 13 | #include "video.h" 14 | 15 | 16 | u32 palette_rgb[256]; 17 | u32 screen[320*240]; 18 | 19 | int hide_page_1; 20 | int hide_page_2; 21 | int hide_sprites; 22 | int show_fps = 1; 23 | int trace_unknown_video = 1; 24 | 25 | 26 | static const u8 sizes[] = { 8, 16, 32, 64 }; 27 | static const u8 colour_sizes[] = { 2, 4, 6, 8 }; 28 | 29 | static const u16 known_reg_bits[] = { 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2800..280f 31 | 0xffff, 0xffff, 0x3fff, 0x011e, 0x3fff, 0x3fff, // 2810..2815 text page 1 32 | 0xffff, 0xffff, 0x3fff, 0x011e, 0x3fff, 0x3fff, // 2816..281b text page 2 33 | 0x00ff, // 281c vcmp value 34 | 0, 0, 0, // 281d..281f 35 | 0xffff, 0xffff, 0xffff, // 2820..2822 36 | 0, 0, 0, 0, 0, 0, 0, // 2823..2829 37 | 0x0003, // 282a blend level 38 | 0, 0, 0, 0, 0, // 282b..282f 39 | 0x00ff, // 2830 fade offset 40 | 0, 0, 0, 0, 0, // 2831..2835 41 | 0xffff, 0xffff, // 2836..2837 IRQ pos 42 | 0, 0, 0, 0, // 2838..283b 43 | 0xffff, // 283c TV hue/saturation 44 | 0, 0, 0, // 283d..283f 45 | 0, 0, // 2840..2841 46 | 0x0001, // 2842 47 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2843..284f 48 | 0, 0, 0, 0, // 2850..2853 49 | 0x0007, // 2854 LCD control 50 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2855..285f 51 | 0, 0, // 2860..2861 52 | 0x0007, 0x0007, // 2862..2863 53 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2864..286f 54 | 0x3fff, 0x03ff, 0x03ff, // 2870..2872 DMA 55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2873..287f 56 | }; 57 | 58 | static const u16 known_sprite_bits[] = { 59 | 0xffff, 0xffff, 0xffff, 0xffff // actually only low 9 bits of X/Y, low 15 of attr 60 | }; 61 | 62 | static void trace_unknown(u32 addr, u16 val, int is_read) 63 | { 64 | if (addr >= 0x2800 && addr < 0x2800 + ARRAY_SIZE(known_reg_bits)) { 65 | if (val & ~known_reg_bits[addr - 0x2800]) 66 | debug("*** UNKNOWN VIDEO REG BITS %s: %04x bits %04x\n", 67 | is_read ? "READ" : "WRITE", addr, 68 | val & ~known_reg_bits[addr - 0x2800]); 69 | } else if (addr >= 0x2900 && addr < 0x2a00) { 70 | if (val & ~0xffff) 71 | debug("*** UNKNOWN VIDEO HOFFSET BITS %s: %04x bits %04x\n", 72 | is_read ? "READ" : "WRITE", addr, val & ~0x01ff); 73 | } else if (addr >= 0x2a00 && addr < 0x2b00) { 74 | if (val & ~0x00ff) 75 | debug("*** UNKNOWN VIDEO HCMP BITS %s: %04x bits %04x\n", 76 | is_read ? "READ" : "WRITE", addr, val & ~0x00ff); 77 | } else if (addr >= 0x2b00 && addr < 0x2c00) { 78 | if (val & ~0xffff) 79 | debug("*** UNKNOWN VIDEO PALETTE BITS %s: %04x bits %04x\n", 80 | is_read ? "READ" : "WRITE", addr, val & ~0xffff); 81 | } else if (addr >= 0x2c00 && addr < 0x3000) { 82 | if (val != 0xffff && val & ~known_sprite_bits[addr & 3]) 83 | debug("*** UNKNOWN VIDEO SPRITE BITS %s: %04x bits %04x\n", 84 | is_read ? "READ" : "WRITE", addr, 85 | val & ~known_sprite_bits[addr & 3]); 86 | } else 87 | debug("*** UNKNOWN VIDEO %s: [%04x] = %04x\n", 88 | is_read ? "READ" : "WRITE", addr, val); 89 | } 90 | 91 | 92 | static void do_dma(u32 len) 93 | { 94 | u32 src = mem[0x2870]; 95 | u32 dst = mem[0x2871] + 0x2c00; 96 | u32 j; 97 | 98 | for (j = 0; j < len; j++) 99 | mem[dst+j] = mem[src+j]; 100 | 101 | mem[0x2872] = 0; 102 | mem[0x2863] |= 4; // trigger video DMA IRQ 103 | } 104 | 105 | void video_store(u16 val, u32 addr) 106 | { 107 | if (trace_unknown_video) 108 | trace_unknown(addr, val, 0); 109 | 110 | if (addr < 0x2900) { // video regs 111 | switch (addr) { 112 | case 0x2810: case 0x2816: // page 1,2 X scroll 113 | val &= 0x01ff; 114 | break; 115 | 116 | case 0x2811: case 0x2817: // page 1,2 Y scroll 117 | val &= 0x00ff; 118 | break; 119 | 120 | case 0x2812 ... 0x2815: // page 1 regs 121 | case 0x2818 ... 0x281b: // page 2 regs 122 | break; 123 | 124 | case 0x281c: // vcmp value 125 | break; 126 | 127 | case 0x2820 ... 0x2822: // bitmap offsets 128 | break; 129 | 130 | case 0x282a: // blend level 131 | break; 132 | 133 | case 0x2830: // fade offset 134 | if ((mem[addr] & 0x00ff) != (val & 0x00ff)) 135 | debug("*** TV FADE set to %02x\n", val & 0x00ff); 136 | break; 137 | 138 | case 0x2836: // IRQ pos V 139 | case 0x2837: // IRQ pos H 140 | val &= 0x01ff; 141 | break; 142 | 143 | case 0x283c: // TV control 1 (hue/saturation) 144 | if ((mem[addr] & 0xff00) != (val & 0xff00)) 145 | debug("*** TV HUE set to %02x\n", val >> 8); 146 | if ((mem[addr] & 0x00ff) != (val & 0x00ff)) 147 | debug("*** TV SATURATION set to %02x\n", val & 0x00ff); 148 | break; 149 | 150 | case 0x283d: // XXX TV control 2 151 | break; 152 | 153 | case 0x283e: // XXX lightpen pos V 154 | case 0x283f: // XXX lightpen pos H 155 | break; 156 | 157 | case 0x2842: // XXX sprite enable 158 | break; 159 | 160 | case 0x2854: { // LCD control 161 | static const char *lcd_colour_mode[4] = { 162 | "B/W", "16 grey levels", "4096 colours", "BAD" 163 | }; 164 | if ((mem[addr] & 0x0003) != (val & 0x0003)) 165 | debug("*** LCD COLOUR MODE set to %s\n", 166 | lcd_colour_mode[val & 0x0003]); 167 | if ((mem[addr] & 0x0004) != (val & 0x0004)) 168 | debug("*** LCD RESOLUTION set to %s\n", 169 | (val & 0x0004) ? "320x240" : "160x100"); 170 | break; 171 | } 172 | 173 | case 0x2862: // video IRQ control 174 | break; 175 | 176 | case 0x2863: // video IRQ status 177 | mem[addr] &= ~val; 178 | return; 179 | 180 | case 0x2870: // video DMA src 181 | case 0x2871: // video DMA dst 182 | break; 183 | 184 | case 0x2872: // video DMA len 185 | do_dma(val); 186 | return; 187 | 188 | default: 189 | debug("VIDEO STORE %04x to %04x\n", val, addr); 190 | } 191 | } else if (addr < 0x2a00) { // scroll per raster line 192 | } else if (addr < 0x2b00) { // horizontal stretch 193 | } else if (addr < 0x2c00) { // palette 194 | } else { // sprites 195 | } 196 | 197 | mem[addr] = val; 198 | } 199 | 200 | u16 video_load(u32 addr) 201 | { 202 | u16 val = mem[addr]; 203 | 204 | if (trace_unknown_video) 205 | trace_unknown(addr, val, 1); 206 | 207 | if (addr < 0x2900) { 208 | switch (addr) { 209 | case 0x2810 ... 0x2815: // page 1 regs 210 | case 0x2816 ... 0x281b: // page 2 regs 211 | break; 212 | 213 | case 0x282a: // blend level 214 | break; 215 | 216 | case 0x2838: // current line 217 | return get_video_line(); 218 | 219 | case 0x2842: // XXX 220 | break; 221 | 222 | case 0x2862: // video IRQ enable 223 | break; 224 | 225 | case 0x2863: // video IRQ status 226 | break; 227 | 228 | case 0x2872: // DMA len 229 | break; 230 | 231 | default: 232 | debug("VIDEO LOAD %04x from %04x\n", val, addr); 233 | } 234 | } 235 | 236 | return val; 237 | } 238 | 239 | 240 | 241 | void render_bitmap(u8 *dest, u32 pitch, u32 addr, u16 attr) 242 | { 243 | u32 h = sizes[(attr & 0x00c0) >> 6]; 244 | u32 w = sizes[(attr & 0x0030) >> 4]; 245 | 246 | u32 yflipmask = attr & 0x0008 ? h - 1 : 0; 247 | u32 xflipmask = attr & 0x0004 ? w - 1 : 0; 248 | 249 | u32 nc = colour_sizes[attr & 0x0003]; 250 | 251 | u32 m = addr; 252 | u32 bits = 0; 253 | u32 nbits = 0; 254 | 255 | for (u32 y = 0; y < h; y++) { 256 | u32 yy = y ^ yflipmask; 257 | 258 | for (u32 x = 0; x < w; x++) { 259 | u32 xx = x ^ xflipmask; 260 | 261 | bits <<= nc; 262 | if (nbits < nc) { 263 | u16 b = mem[m++ & 0x3fffff]; 264 | b = (b << 8) | (b >> 8); 265 | bits |= b << (nc - nbits); 266 | nbits += 16; 267 | } 268 | nbits -= nc; 269 | 270 | dest[pitch*yy + xx] = bits >> 16; 271 | 272 | bits &= 0xffff; 273 | } 274 | } 275 | } 276 | 277 | 278 | void video_init(void) 279 | { 280 | mem[0x2836] = 0xffff; 281 | mem[0x2837] = 0xffff; 282 | } 283 | -------------------------------------------------------------------------------- /video.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008-2010 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _VIDEO_H 6 | #define _VIDEO_H 7 | 8 | #include "types.h" 9 | 10 | extern u32 palette_rgb[256]; 11 | extern u32 screen[320*240]; 12 | 13 | extern int hide_page_1; 14 | extern int hide_page_2; 15 | extern int hide_sprites; 16 | extern int show_fps; 17 | 18 | 19 | void video_store(u16 val, u32 addr); 20 | u16 video_load(u32 addr); 21 | 22 | void render_bitmap(u8 *dest, u32 pitch, u32 addr, u16 attr); 23 | 24 | void video_init(void); 25 | 26 | #endif 27 | --------------------------------------------------------------------------------