1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
|
How to Port Snes9x to a New Platform
====================================
Version: 1.01
Date: 23-December-1998
(c) Copyright 1998 Gary Henderson (gary@daniver.demon.co.uk)
Introduction
============
This is brief description of the steps involved in porting Snes9x, the Super
Nintendo Entertainment System emulator, to new hardware which is at least
similar to Workstation or PC. It describes what code you have to write and
what functions exist that you can make use of. It also gives some insights as
to how Snes9x actually works, although that will be subject of another
document yet to be written.
Host System Requirements
========================
A C++ compiler, so you can compile the emulator! Snes9x really isn't written
in C++, it just uses the C++ compiler as a 'better C' compiler to get inline
functions and so on. With some modification, it could be converted to be
compiled with an ordinary C compiler. Snes9x isn't very C type safe and
will probably not work on a system who's integers are less than 32-bits wide
without lots of editing.
If the host system uses a CPU that implements the i386 instruction set then
you will also want to use the three assembler CPU cores, although I recently
scrapped the SPC700 assembler code (too many bugs) and replaced it with
compiler generated assembler code that I haven't got around to optimising
yet. The 65c816 and SPC700 code needs to be assembled using the GNU
assembler that comes with gcc and the Super FX code assembled with NASM
v0.97 or higher. gcc is available from lots of sites. NASM is available from
http://www.cryogen.com/Nasm
A fast CPU. SNES emulation is very compute intensive: two, or sometimes three
CPUs to emulate, an 8-channel 16-bit stereo sound digital signal processor
with real-time sample decompression, filter and echo effects, two custom
graphics processor chips that can produce transparency, scaling, rotation
and window effects in 32768 colors, and finally hardware DMA all take their
toll on the host CPU.
Lots of RAM. The SNES itself has 128k work RAM, 64k V-RAM and 64k sound CPU
RAM. If a Super FX game is being emulated, that usually comes with another
64k inside the game pack. Snes9x itself needs 4Mb to load SNES ROM images
into (or 6Mb if I ever figure out the SNES memory map of the 48Mbit ROM
images out there), 256k to cache decompressed sound samples in, 512k to
cache converted SNES tiles in, and another 64k for S-RAM emulation. And
that's not counting a few large lookup tables that the graphics code needs
for speeding up transparency effects plus few other tables used by the ZSNES
Super FX code. It all adds up to 7Mb (ish). Add to that RAM needed to
store the actual emulator code and RAM required by the host operating system
and any other process that is running; that's lots of RAM. Well, it is if
your host system only has a few mega-bytes of RAM available.
An 8-bit, 256 color (one byte per pixel) or deeper display, at least 256x239
pixels in resolution, or 512x478 if you're going to support the SNES'
hi-res. background screen modes. Ideally, a 16-bit, 65536 color screen mode
is required if you want to support transparency at speed, as that is what the
code renders internally. Any other format screen, with transparency enabled,
will require picture format conversion before you can place the rendered
SNES image on to the screen.
Sound output requires spooling 8-bit or 16-bit, mono or stereo digital sound
data to the host computer's sound hardware. The DOS port uses interrupts
from the sound card to know when more sound data is required, most other
ports have to periodically poll the host sound hardware to see if more data
is required; if it is then the SNES sound mixing code provided by Snes9x is
called to fill an area of system memory with ready mixed SNES sound data,
which then can be passed on to the host sound hardware. Sound data is
generated as an array of bytes (uint8) for 8-bit sound or shorts (int16) for
16-bit data. Stereo sound data generates twice as many samples, with each
channel's samples interleaved, first left's then right's.
For the user to be able to control and play SNES games, some form of input
device is required, a joystick or keyboard, for example. The real SNES can
have 2 eight-button digital joy-pads connected to it or 5 joy-pads when an
optional multi-player adaptor was purchased, although most games only require
a single joy-pad. Access to all eight buttons and the direction pad, of
course, are usually required by most games. Snes9x does emulate the
multi-player adaptor hardware, if you were wondering, but its still up to
you to provide the emulation of the individual joy-pads.
The SNES also had a mouse and light gun available as optional extras,
Snes9x can emulate both of these using some form of pointing device,
usually the host system's mouse.
If an accurate, constant SNES play rate is required, then a real-time timer
will be needed that can time intervals of 16.7ms (NTSC frame time) or 20ms
(PAL frame time).
Some SNES game packs contained a small amount of extra RAM and a battery so
ROMs could save a player's progress through a game for games that takes many
hours to play from start to finish. Snes9x simulates this S-RAM by saving
the contents of the area of memory normally occupied by the S-RAM into file
then automatically restoring it again the next time the user plays the same
game. If the hardware you're porting to doesn't have a hard disk available
then you could be in trouble.
Snes9x also implements freeze-game files which can record the state of the
SNES hardware and RAM at a particular point in time and can restore it to
that exact state at a later date - the result is that users can save a game
at any point, not just at save-game or password points provided by the
original game coders. Each freeze file is over 400k in size. To help save
disk space, Snes9x can be compiled with zlib, which is used to compress the
freeze files, reducing the size to typically below 100k. Download zlib from
its homepage at http://www.cdrom.com/pub/infozip/zlib/, compile Snes9x with
ZLIB defined and link with zlib. zlib is also used to load any compressed
ROM images Snes9x my encounter, compressed with gzip or compress.
Porting
=======
In theory you will only need to edit port.h, then in a separate file write
all the initialisation code and interface routines that Snes9x expects the
you to implement. You, no doubt, will discover otherwise....
There are several compile-time only options available:
DEBUGGER
--------
Enables extra code to assist me in debugging SNES ROMs. The debugger has only
ever been a quick-hack by me and user-interface to debugger facilities is
virtually non-existent. Most of the debugger information is output via
stdout and enabling the compile-time options slows the whole emulator down
slightly. However, the debugger options available are very powerful; you
could use it to help get your port working. You probably still want to ship
the finished version with the debugger disabled, it will only confuse
non-technical users.
VAR_CYCLES
----------
I recommend you define this. The main CPU in the SNES actually varies in
speed depending on what area of memory its accessing and the ROM access
speed of the game pack; defining VAR_CYCLES causes Snes9x to emulate this,
using a good approximation, rather than fixed cycle length as ZSNES does. The
resultant code is slightly slower. Leaving it undefined results in many more
emulation timing errors appearing while playing games.
CPU_SHUTDOWN and SPC700_SHUTDOWN
--------------------------------
Again I recommend defining both of these. They are both speed up hacks.
When defined, Snes9x starts watching for when either the main or sound CPUs
are in simply loops waiting for a known event to happen - like the end of
the current scan-line, and interrupt or a sound timer to reach a particular
value. If Snes9x spots either CPU in such a loop it uses its insider
knowledge to simply skip the emulation of that CPU's instructions until the
event happens. It can be a big win with lots of SNES games.
I'm constantly amazed at the ingenuity of some programmers who are able to
produce complex code to do simple things: some ROM's wait loops are so
complex Snes9x fails to spot the CPU is in such a loop and the shutdown
speed up hacks don't work.
You might be wondering why VAR_CYCLES, and the two SHUTDOWN options have to
be enabled with defines, well, in the past they sometimes introduced
problems with some ROMs, so I kept them as options. I think I've fixed all
the problems now, but you never know...
SPC700_C
--------
Define this if you are using the C/C++ version of the SPC700 CPU core. It
enables a ROM compatibility feature that executes SPC700 instructions during
SNES DMA, it allows several games to start that would otherwise lock up and
fixes music pauses when ROMs do lots of DMA, usually when switching between
game screens.
ZLIB
----
Define this if you have the zlib library available and you want it to
compress freeze-game files to save disk space. The library is also used to
support compressed ROM images.
NO_INLINE_SET_GET
-----------------
Define this to stop several of the memory access routines from being
defined in-line. Whether the C++ compiler actually in-lines when this symbol
is not defined is up to the compiler itself. In-lines functions can speed up
the C++ CPU emulations on some architectures at the cost of increased code
size. Try fiddling with this option once you've got port working to see if
it helps the speed of your port.
EXECUTE_SUPERFX_PER_LINE and ZSNES_FX
-------------------------------------
Define these if you're going to be using the ZSNES Super FX i386 assembler
code, otherwise leave them both undefined. In theory,
EXECUTE_SUPERFX_PER_LINE can also be defined when using the C++ Super FX
emulation code, but the code is still buggy and enabling the option
introduces more problems than it fixes. Any takers for fixing the C++ code?
JOYSTICK_SUPPORT, SIDEWINDER_SUPPORT and GRIP_SUPPORT
-----------------------------------------------------
These options enable support for various input devices in the UNIX and MS-DOS
port code. They're only of interest if you're able to use the existing UNIX
or MS-DOS port specific code.
port.h
======
If the byte ordering of the target system is least significant byte first,
make sure LSB_FIRST is defined in this header, otherwise, make sure its not
defined.
If you're going to support 16-bit screen rendering (required if you want
transparency effects) and your system doesn't use RGB 565 - 5 bits for red,
6 bits for green and 5 bits for blue - then you'll need make sure RGB555,
BGR565 or BGR555 is defined instead. You might want to take a look at the
*_LOW_BIT_MASKs, *_HI_BIT_MASKs and BUILD_PIXEL macros to make sure they're
correct, because I've only every tested the RGB565 version, though the Mac
port uses the RGB555 option. If your system is 24 or 32-bit only, then
don't define anything; instead write a conversion routine that will take a
complete rendered 16-bit SNES screen in RGB565 format and convert to the
format required to be displayed on your hardware.
port.h also typedefs some types, uint8 for an unsigned, 8-bit quantity,
uint16 for an unsigned, 16-bit quantity, uint32 for a 32-bit, unsigned
quantity and bool8 for a true/false type. Signed versions are also
typedef'ed.
The CHECK_SOUND macro can be defined to invoke some code that polls the
host system's sound hardware to see if it can accept any more sound data.
Snes9x makes calls to this macro several times when it is rendering the SNES
screen, during large SNES DMAs and after every emulated CPU instruction.
Since this CHECK_SOUND macro is invoked often, the code should only take a
very small amount of time to execute or it will slow down the emulator's
performance. The Linux and UNIX ports use a system timer and set a variable
when it has expired; the CHECK_SOUND only has to check to see if the
variable is set. On the MS-DOS and Mac ports, the sound hardware is not
polled at all, instead it is driven by interrupts or callbacks and the
CHECK_SOUND macro is defined to be empty.
Initialisation Code
-------------------
This is what the Linux, UNIX and MS-DOS ports do, I suspect your code
might be similar:
- The Settings structure is initialised to some sensible default values -
check the main function in unix.cpp for the values it uses.
- The command line is parsed, options specified override default values in
the Settings structure and specify a ROM image filename that the user
wants loaded. Your port could load user preferences from a file or some
other source at this point. Most values, with a little care, can be changed
via a GUI once the emulator is running.
- Some Settings structure value validation takes place, for example if
transparency effects are requested the code also makes sure 16-bit
screen rendering is turned on as well.
- Memory.Init() and S9xInitAPU() are called, checking neither failed. The
only reason they would fail is if memory allocation failed.
- Memory.LoadROM (filename) is called to load the specified ROM image into
memory. If that worked Memory.LoadSRAM (sram_filename) is called to load
the ROM's S-RAM file, if one exists. The all current ports base the
sram_filename on the filename of the ROM image, changing the file's
extension (the .smc or whatever bit) and changing the directory where its
located - you won't be able to save S-RAM files onto a CD if that's where
the ROM image is located!
If your port has a GUI, you can delay this step until the user picks an
image to load.
SNES roms images come in all shapes and sizes, some with headers, some
without, some have been mangled by the copier device in one of two ways, and
some split into several pieces; plus the SNES itself has several different
memory map models. The code tries to auto-detect all these various types,
but sometimes the SNES ROM header information has been manually edited by
someone at some stage and the code guesses wrong. To help it out it these
situations, the Settings structure contains several options to force a
particular ROM image format; these values must be initialised prior to each
call to Memory.LoadROM(filename).
- The Linux and UNIX ports now do some more operating system initialisation
ready for a system timer to be started.
- The host display hardware is now initialised. The actual screen depth and
resolution should be picked based on the user preferences if possible.
The X Window System port can't control the screen depth or resolution, if
the user requests transparency effects but the display hardware is only
set to 8-bit, it has to invoke an extra step of converting the 16-bit SNES
rendered screen to a fixed palette 8-bit display just before the SNES
screen is copied to the display hardware.
The GFX.Screen pointer needs to be initialised to point to an array of
uint8 for 8-bit screen rendering or uint16 for 16-bit rendering, cast to
an array of uint8. The array needs to be at least 256x239 bytes or shorts
in size for lo-res only support (Settings.SupportHiRes = FALSE) or
512x478 for lo-res and hi-res support. If transparency effects are
required, the GFX.SubScreen array also needs to be initialised to another
identically sized array of the same type, otherwise it can be just
initialised to NULL.
The GFX.Pitch variable needs to be set to the number of bytes on each line
of the arrays, e.g. 256 for lo-res only support, up to 1024 for 16-bit
hi-res support. If GFX.Screen is pointing into an existing array, one
created by the library function rather than just calling malloc or new,
then set GFX.Pitch to the number of bytes per line of that array,
including any padding the library function may have added.
If the target hardware supports fast access to video RAM, the screen is in
16-bit format supported by the SNES rendering code and you can double
buffer the display, you might want to point GFX.Screen directly at the
video buffer RAM. You will need to recompute the GFX.Delta value every
time you change the GFX.Screen value to double-buffer the rendering and
display.
- A call to S9xGraphicsInit() is made; make sure all your graphics rendering
options are setup correctly by now. If later, you want to change some
settings, for example 16-bit to 8-bit rendering, call S9xGraphicsDeinit()
first, change your settings, GFX.Screen and GFX.SubScreen arrays, etc.,
then call S9xGraphicsInit() again.
- S9xInitSound(int playbackrate, bool8 stereo, int sound_buffer_size)
is now called, which in turn will call your S9xOpenSoundDevice function -
see below.
- The display is switched to graphics mode using a call to S9xGraphicsMode().
- The system timer is started; its used for keeping the emulator speed
relatively constant on the MS-DOS port and noting when the sound hardware
sound should be able to accept more sound data on the Linux and UNIX ports.
- A main loop is entered which is just a loop constantly calling
S9xMainLoop() then polling the operating system for any pending events
such as key presses and releases, joystick updates, mouse position
updates, GUI user interaction, etc.
Pause functionality can be implemented by skipping the call to S9xMainLoop
and muting the sound output by calling S9xSetSoundMute (TRUE).
Don't enter the main loop until a SNES ROM image has been loaded, or at
least skip calling S9xMainLoop inside the loop until one is and make sure
S9xReset is called instead before entering the main loop. The Mac port
implements this technique by starting in pause mode and refusing to unpause
until a ROM image is loaded.
S9xMainLoop processes SNES CPU emulation, SNES screen rendering, DMA and
H-DMA emulation, until emulated scan-line 0 is reached, then it returns.
Now is your chance to process any system events pending, scan the
keyboard, read joystick values, etc.
If DEBUGGER compile-time support is enabled and the CPU emulation has hit
a break point or single-stepping is switched on, or the DEBUG_MODE_FLAG is
set in the CPU.Flags variable, then the S9xMainLoop routine returns early,
allowing you to act on the event in some way. The Linux, DOS and UNIX ports
respond to the DEBUG_MODE_FLAG being set by calling S9xDoDebug(), which in
turn outputs the current instruction and loops reading commands from stdin
and outputting debug information, currently via stdout. The debugger
desperately needs rewriting to support a GUI interface, more descriptive
commands and better error handling; maybe one day...
Existing Interface Routines
---------------------------
These are routines already written that you will either need to call or
might find useful.
-> bool8 Memory.Init ()
Allocates and initialises several major lumps of memory, for example
the SNES ROM and RAM arrays, tile cache arrays, etc. Returns FALSE if
memory allocation fails.
-> void Memory.Deinit ()
Undoes the memory allocations made by Memory.Init.
-> bool8 S9xGraphicsInit ()
Allocated and initialises several lookup tables used to speed up SNES
graphics rendering. Call after you have initialised the GFX.Screen,
GFX.SubScreen and GFX.Pitch values. If Settings.Transparency is false it
does not allocate tables used to speed up transparency effects. If you
want to provide the user with option to turn the effects on and off during
game play, make sure Settings.Transparency is true when this function is
called, it can later be set to FALSE.
Returns FALSE if memory allocation fails.
-> void S9xGraphicsDeinit ()
Undoes the memory allocations made by S9xGraphicsInit.
-> bool8 S9xInitAPU ()
Allocates and initialises several arrays used by the sound CPU and sound
generation code.
-> void S9xDeinitAPU ()
Undoes the allocations made by S9xInitAPU.
-> bool8 S9xInitSound (int mode, bool8 stereo, int buffer_size)
Does more sound code initialisation and opens the host system's sound hardware
by calling the S9xOpenSoundDevice function provided by you.
-> void S9xReset ()
Resets the SNES emulated hardware back to the state it was in at 'switch-on'
except the S-RAM area is presevered. The effect is it resets the current game
back to the start. This function is automatically called by Memory.LoROM.
-> bool8 Memory.LoadROM (const char *filename)
Attempts to load the specified ROM image filename into the emulated ROM area.
There are many different SNES ROM image formats and the code attempts to
auto-detect as many different types as it can and in a vast majority of the
cases gets it right. However, some ROM images have been edited by someone at
some stage or have been mangled by the ROM copier that produced them and
LoadROM needs help. Inparticular, it can't auto-detect the odd way in which
some Super FX games have been mangled and needs to be told, via
Settings.Interleaved2, that the ROM image is in that format, or that
odd-sized ROM images have a 512 byte copier header.
There are several other ROM image options in the Settings structure;
allow the user to set them before calling LoadROM, or make sure they all
reset to default values before each call to LoadROM.
-> bool8 Memory.LoadSRAM (const char *filename)
Call this routine to load the associated S-RAM save file (if any). The
filename should be based on the ROM image name to allow easy linkage.
The current ports change the directory and the filename extension of the ROM
filename to derive the S-RAM filename.
-> bool8 Memory.SaveSRAM (const char *filename)
Call this routine to save the emulated S-RAM area into a file so it can
be restored again the next time the user wants to play the game. Remember
to call this when just before the emulator exits or when the user has been
playing a game and is about to load another one.
-> void S9xMainLoop()
The emulator main loop. Call this from your own main loop that calls this
function (if a ROM image is loaded and the game is not paused), processes
any pending host system events, then goes back around the loop again until
the emulator exits.
S9xMainLoop normally returns control to your main loop once every emulated
frame, when it reaches the start of scan-line zero. However, the routine
can return more often if the DEBUGGER compile-time flag is defined and the
CPU has hit a break point, or the DEBUG_MODE_FLAG bit is set in CPU.Flags
or instruction single-stepping is enabled.
-> void S9xMixSamples (uint8 *buffer, int sample_count)
Call this routine from your host sound hardware handling code to fill the
specified buffer with ready mixed SNES sound data. If 16-bit sound mode is
choosen, then the buffer will be filled with an array of sample_count int16,
otherwise an array of sample_count uint8. If stereo sound generation is
selected the buffer is filled with the same number of samples, but in pairs,
first a left channel sample followed by the right channel sample.
There is a limit on how much data S9xMixSamples can deal with in one go and
hence a limit on the sample_count value; the limit is the value of the
MAX_BUFFER_SIZE symbol, normally 4096 bytes.
-> bool8 S9xSetSoundMute (bool8 mute)
Call with a TRUE parmeter to prevent S9xMixSamples from processing SNES
sample data and instead just filling the return buffer with silent sound
data. Useful if your sound system is interrupt or callback driven and the
game has been paused either directly or indirectly because the user
interacting with the emulator's user interface in some way.
-> bool8 S9xFreezeGame (const char *filename)
Call this routine to record the current SNES hardware state into a file,
the file can be loaded back using S9xUnfreezeGame at a later date effectively
restoring the current game to exact same spot. Call this routine while
you're processing any pending system events when S9xMainLoop has returned
control to you in your main loop.
-> bool8 S9xUnfreezeGame (const char *filename)
Restore the SNES hardware back to the exactly the state it was in when
S9xFreezeGame was used to generate the file specified. You have to arrange
the correct ROM is already loaded using Memory.LoadROM, an easy way to
arrange this is to base freeze-game filenames on the ROM image name. The
Linux, UNIX and DOS ports load freeze-game files when the user presses a
function key, with the names romfilename.000 for F1, romfilename.001 for F2,
etc. Games are frozen in the first place when the user presses Shift-function
key. You could choose some other scheme.
-> void S9xNextController ()
The real SNES allows several different types of devices to be plugged into
the game controller ports. The devices Snes9x emulates are a joy-pad,
multi-player adaptor (allowing a further 4 joy-pads to be plugged in),
a 2-button mouse and a light gun known as the SuperScope.
Each call to S9xNextController will step the current emulated device on to
the next device in the sequence multi-player, joy-pad, mouse on port 1,
mouse on port 2, light gun then back to multi-player again. Defines
allocating a number of each device type are in snes9x.h. The currently
selected device is stored in IPPU.Controller if you want to give some
feedback to the user. The initial value of IPPU.Controller (set when
S9xReset is called) is obtained from Settings.ControllerOption based on
currently enabled options.
Some ROMs object to certain non-joy-pad devices being plugged into the real
SNES while they are running, all Super FX games should only allow joy-pads to
be plugged in because the Super FX chip and any other device would overload
the SNES power supply. Tetris and Dr. Mario also objects for reasons best
known to itself. For this reason there are switches in the Settings
structure to enable and display the emulation of the various devices.
const char *S9xGameGenieToRaw (const char *code, uint32 &address, uint8 &byte)
const char *S9xProActionReplayToRaw (const char *code, uint32 &address,
uint8 &byte)
const char *S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram,
uint8 &num_bytes, uint8 bytes[3])
void S9xApplyCheats (bool8 apply)
void S9xRemoveCheats ()
void S9xAddCheat (uint32 address, bool8 cpu_address, bool8 sram, uint8 num_bytes,
uint8 byte1, uint8 byte2, uint8 byte3)
void S9xDeleteCheats ()
void S9xDoDebug ()
Interface Routines You Need to Implement
----------------------------------------
bool8 S9xOpenSnapshotFile (const char *base, bool8 read_only, STREAM *file)
***************************************************************************
void S9xCloseSnapshotFile (STREAM file)
***************************************
Routines to open and close freeze-game files. STREAM is defined as a
gzFile if ZLIB is defined else its defined as FILE *. The read_only parameter
is set to TRUE when reading a freeze-game file and FALSE when writing a
freeze-game file.
void S9xExit ()
***************
Called when some fatal error situation arises or when the 'q' debugger
command is used. The Mac port just beeps and drops back to the GUI when
S9xExit is called, the MS-DOS, Linux and Solaris ports all call exit () to
terminate the emulator process.
void S9xParseArg (char **argv, int &index, int argc)
****************************************************
void S9xExtraUsage ()
*********************
If you're going to be using the simple command line parser, when it
encounters an unknown option it calls S9xUsage which is supposed to report
all options the generic parse knows about (I haven't been keeping it up to
date of late). S9xUsage then, in turn calls S9xExtraUsage which you
implement to report any port-specific options available.
void S9xGraphicsMode ()
***********************
void S9xTextMode ()
*******************
The SNES debugger calls these routines to switch from a graphics screen
mode used to display the SNES game to a debugger screen used to display
debugger output. If the SNES screen can be displayed at the same time as
a text display, as would be the case when the host system implements a
graphical window system, or you're not going to support the SNES debugger,
then these routines should do nothing.
On the X Window System UNIX/Linux port, these routines do nothing where as
on the MS-DOS port they switch between a graphics screen mode and a text-only
screen mode.
bool8 S9xInitUpdate ()
**********************
Called just before Snes9x starts to render a SNES screen. The Windows port
uses this call to lock Direct X screen area to allow exclusive access; on
other existing ports its implemented as an empty function.
bool8 S9xDeinitDisplay (int width, int height, bool8 sixteen_bit)
*****************************************************************
Called once a complete SNES screen has been rendered into the GFX.Screen
memory buffer, now is your chance to copy the SNES rendered screen to the
host computer's screen memory. The problem is that you have to cope with
different sized SNES rendered screens. Width is always 256, unless you're
supporting SNES hi-res. screen modes (Settings.SupportHiRes is TRUE), in
which case it can be 256 or 512. The height parameter can be either 224 or
239 if you're only supporting SNES lo-res. screen modes, or 224, 239, 448 or
478 if hi-res. SNES screen modes are being supported.
All current ports support scaling the SNES screen to fill the host system's
screen, the many ports even supports interpolation - blending the colours of
adjacent pixels to help hide the fact they've been scaled - and scan-line
simulation - slightly darkening every other horizontal line.
Don't forget that if you're just placing the SNES image centerally in the
screen then you might need to clear areas of the screen if the SNES image
changes size between calls to S9xDeinitDisplay. The MS-DOS and UNIX ports
currently don't do this which results in junk being left on the screen if
the ROM changes SNES screen modes.
The sixteen_bit is just a copy of the Settings.SixteenBit setting and if
TRUE indicates a 16-bit SNES screen image has been rendered, 8-bit otherwise.
void S9xMessage (int type, int number, const char *message)
***********************************************************
I've started work on converting all the old printfs into calls to this
routine. When Snes9x wants to display an error, information or warning
message, it calls this routine. Check in messages.h for the types and
individual message numbers that Snes9x currently passes as parameters.
The idea is display the message string so the user can see it, but you
choose not to display anything at all, or change the message based on the
message number or message type.
Eventually all debug output will also go via this function, trace information
already does.
bool8 S9xOpenSoundDevice(int mode, bool8 stereo, int buffer_size)
*****************************************************************
S9xInitSound calls this function to actually open the host operating system's
sound device, or initialise the sound card in MS-DOS port.
The mode parameter is the value passed in on the command line with the -r
command line flag, assuming you're using the Snes9x parser. Its meant to
indicate what playback the sound hardware should be set to, value 1 to 7.
I think the real SNES sound chip playback rate is 30kHz, but such high
playback rates take a lot of native CPU power to emulate. The default
playback rate is 22kHz for the MS-DOS and UNIX ports.
The stereo flag indicates if the user wants stereo sound. Again, stereo
sound takes more CPU to power to emulate compared to mono sound.
The buffer_size value indicates what sample buffer size the user wants,
usually zero, meaning you should pick the value best suited to the current
playback rate. Sound data is normally passed to the sound hardware in
blocks, the smaller the block the less latency between the SNES game playing
a sound and it being heard by the user. But if you pick a too smaller value,
and you're having to periodically poll the operating system to see if it can
accept more sound data, then the sound output will break up because other
actions such as rendering the SNES screen can prevent you from polling the
hardware often enough and the operating system runs out of sound data to
play.
The MS-DOS port uses a buffer size of 128 samples since the sound card
sends an interrupt when more data is required which is acted upon promptly,
where as the Linux and Solaris ports use a buffer size of 512 samples or
more depending on the playback rate. Stereo and 16-bit sound both double the
actual size of the buffer in bytes.
uint32 S9xReadJoypad (int which1_0_to_4)
****************************************
This function is called to return a bit-wise mask of the state of one of the
five emulated SNES controllers. Return 0 if you're not supporting controllers
past a certain number or return the mask representing the current state of
the controller number passed as a parameter or'ed with 0x80000000.
Symbolic constants are defined in snes9x.h indicating the bit positions of
the various SNES buttons and direction indicators; they're all in the form
SNES_X_MASK where X is the SNES controller button name.
The MS-DOS and X Window System ports record what keys are currently pressed
and use that to build up a mask, the Windows port polls the operating system
when S9xReadJoypad is called to find out what keys are pressed. All ports
also implement host joysticks and joy-pads via this interface.
bool8 S9xReadMousePosition (int which1_0_to_1, int &x, int &y, uint32 &buttons)
*******************************************************************************
Used by Snes9x to get the current position of the host pointing device,
usually a mouse, used to emulated the SNES mouse. Snes9x converts the x and
y values to delta values required by the SNES mouse, so the actual x and y
values are unimportant, only the change in value since the last call to
this function is used.
Graphical windowing systems normally restrict the movement of the pointer on
the screen, if you're porting to such an environment you might want to make
a note of the change in position in the mouse since the last time you asked
the operating system the mouse position, add this change in value to some
saved x and y value, the reposition the pointer back to the centre of the
SNES display window. The saved x and y values will be the values returned
by this function.
The buttons return value is a bit-wise mask of the two SNES mouse buttons,
bit 0 for button 1 (left) and bit 1 for button 2 (right).
bool8 S9xReadSuperScopePosition (int &x, int &y, uint32 &buttons)
*****************************************************************
void S9xSetPalette ()
*********************
void S9xSyncSpeed ()
S9xUnixProcessSound
void _makepath(char *, char const *, char const *, char const *, char const *)
void _splitpath(char const *, char *, char *, char *, char *)
Sound Generation
----------------
Settings
--------
|