├── .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 |
--------------------------------------------------------------------------------