commit 2a6bece89d16e691f22ba1432ca9bea0bf60e791 Author: simon.kagstrom Date: Wed Dec 31 16:16:24 2008 +0000 Imported orig code diff --git a/1541 ROM b/1541 ROM new file mode 100644 index 0000000..c3c6a31 Binary files /dev/null and b/1541 ROM differ diff --git a/64prgs/3fff b/64prgs/3fff new file mode 100644 index 0000000..795be3c Binary files /dev/null and b/64prgs/3fff differ diff --git a/64prgs/colorbars b/64prgs/colorbars new file mode 100644 index 0000000..df7d038 Binary files /dev/null and b/64prgs/colorbars differ diff --git a/64prgs/d011h3 b/64prgs/d011h3 new file mode 100644 index 0000000..9aa9964 Binary files /dev/null and b/64prgs/d011h3 differ diff --git a/64prgs/dadb b/64prgs/dadb new file mode 100644 index 0000000..99ab663 Binary files /dev/null and b/64prgs/dadb differ diff --git a/64prgs/de00all b/64prgs/de00all new file mode 100644 index 0000000..a10e3f2 Binary files /dev/null and b/64prgs/de00all differ diff --git a/64prgs/dycp b/64prgs/dycp new file mode 100644 index 0000000..0423839 Binary files /dev/null and b/64prgs/dycp differ diff --git a/64prgs/fld b/64prgs/fld new file mode 100644 index 0000000..83163ad Binary files /dev/null and b/64prgs/fld differ diff --git a/64prgs/lrborder b/64prgs/lrborder new file mode 100644 index 0000000..937abb0 Binary files /dev/null and b/64prgs/lrborder differ diff --git a/64prgs/sprsync b/64prgs/sprsync new file mode 100644 index 0000000..ddd13ac Binary files /dev/null and b/64prgs/sprsync differ diff --git a/64prgs/stretch b/64prgs/stretch new file mode 100644 index 0000000..a37753c Binary files /dev/null and b/64prgs/stretch differ diff --git a/64prgs/tech-tech b/64prgs/tech-tech new file mode 100644 index 0000000..810da9c Binary files /dev/null and b/64prgs/tech-tech differ diff --git a/64prgs/text26 b/64prgs/text26 new file mode 100644 index 0000000..ea3f777 Binary files /dev/null and b/64prgs/text26 differ diff --git a/Basic ROM b/Basic ROM new file mode 100644 index 0000000..9e06923 Binary files /dev/null and b/Basic ROM differ diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..8dd8dd7 --- /dev/null +++ b/CHANGES @@ -0,0 +1,203 @@ +Changes from V4.1a to V4.1b: + - Unix: Updated Tcl/Tk preferences editor [Gerard Decatrel] + - Unix: Added SDL display driver which is the default if SDL >= 1.2 is + detected by the configure script + - Unix: Increased number of sound buffers from 4 to 16 (emu10k driver doesn't + work with the smaller value) + - Unix: Fixed some small compilation problems + +Changes from V4.1 to V4.1a: + - Fixed bug in IEC::NewPrefs() + - Optimized VIC emulation for speed + - BeOS: Prefs saved in /boot/home/config/settings/Frodo_settings + - BeOS: Directory panel works again + - BeOS: Correct C64/1541 clock frequency ratio (Frodo SC) + - BeOS: Correct audio mixing/clipping + +Changes from V4.0a to V4.1: + - Integrated Win32 and RiscOS ports + - Snapshot support + - Added page-crossing and "borrowed" cycles in line-based CPU emulation (Frodo PC) + - Added precise CIA cycles for line-based emulation (Frodo PC) + - Optional fixed-point arithmetic and precomputed filters in SID.cpp + - Optional dynamic alignment checks in VIC.cpp + - Changed typedefs and constants: + BYTE -> int8 + UBYTE -> uint8 + WORD -> int16 + UWORD -> uint16 + LONG -> int32 + ULONG -> uint32 + FALSE -> false + TRUE -> true + - Unix: Better configure script + - Unix: Sound support for Solaris 2.x + - Unix: Joystick can be toggled between port 1/2 with the NumLock key + - Unix: US keyboard layouts supported + - BeOS: Fixed for BeOS AA:DR9 + - BeOS: Can now switch between window/screen mode while the emulation is running, + speeded up full screen mode + - BeOS: Prefs saved in /system/settings/Frodo_settings by default + +Changes from V4.0 to V4.0a: + - Corrected BRK, ANE, ARR, SBX and DCP instructions + - Frodo SC: Improved the CIA timers + - Frodo SC: MOS6526::EmulateCycle() split into MOS6526::EmulateCycle() + and MOS6526::CheckIRQs() + - Frodo SC: Corrected interrupt behaviour of branch instructions + - BeOS: Sound calculation is now done in stereo + +Changes from V3.1c to V4.0: + - The C64 ROM files are now included + - Unix: Added SVGAlib keyboard patches from Bernd + +Changes from V3.1b to V3.1c: + - Ported to AmigaOS + - Fixed bug in IEC::Reset() + - Fixed bug when writing to SID registers >24 + - The SID noise waveform should now sound the same on all + platforms + - Removed all calls to tolower() in SAM.cpp because of + possible side-effects if tolower() is a macro + - Drive LEDs are only updated once per frame + - .d64/.t64 files are opened with read permissions only + - Fixed bug with read_char buffering in 1541fs.cpp/1541t64.cpp + - Frodo SC: Fixed memory trashing bug in MOS6569::draw_background() + - Unix: Drive LEDs blink on error + - Unix: Added more patches from Bernd + +Changes from V3.1a to V3.1b: + - Corrected SID sustain behaviour + - Reading from write-only SID registers returns the last + byte written to the SID + - No more distortions when playing samples + - Removed the "Ignore SID Volume" prefs item again + - Combined SID waveforms now sampled from a 6581R4 + - Improved 1541 VIA timer operation + - Fixed bug in 1541 head movement + - Raster IRQs can be triggered by writing to $d011/$d012 + - Some changes for the MacOS port + - Included autoconf stuff from Bernd + - Frodo SC: Fixed some CIA timer bugs + +Changes from V3.1 to V3.1a: + - Frodo SC ported + - Processor-level 1541 emulation supports reading GCR data, + removed the faked job loop + - Corrected ISB, RRA, SBX and SHA instructions + - The last line of Y expanded sprites wasn't drawn + - Light pen registers work + - Small fixes to 1541d64/1541t64 + - CIA 2 PRA write: IEC lines respect DDRA + - Better triangle waveform (12 bits) + - SID emulation can play sampled sounds + - New "Ignore SID Volume" prefs item for better sample playing + - '*' on numerical keypad toggles speed limiter + - BeOS: '/' on numerical keypad toggles processor-level 1541 + emulation + - BeOS: Safer quitting + - BeOS: Option to use GameKit (screen) + - BeOS: Replaced srand(system_time()) with srand(real_time_clock()) + - Unix: Sun makefile + +Changes from V3.0h to V3.1: + - BeOS: Joysticks work again + +Changes from V3.0g to V3.0h: + - Implemented SID notch filter, better resonance frequency calculation + - Fixed bug with SID filter option + - CIA timer B one-shot mode stops timer when counting undeflows + of timer A + - Implemented lightpen trigger + - BeOS: Fixed for BeOS DR8, improved the GUI a bit + - Unix: Some fixes to the TkGui (T64, SIDFilters, removed speed + display) + - Unix: Main window no longer resizable + - Unix: SVGALib support works again + +Changes from V3.0f to V3.0g: + - New T64/LYNX mode for 1541 emulation + - 1541fs.cpp/match() used to treat the pattern "foo" as "foo*" + - 1541 DIR mode uses tmpfile() for opening temporary directory files + - 1541 D64 mode allows wildcards for selective directory reading + - Increased compatibility of processor-level 1541 emulation in + various places (C64<->1541 communication, VIA registers, memory + map, disk change flag) + - Inlined MOS6526::EmulateLine() and some small public functions of + MOS6502_1541 + - New prefs option to enable/disable SID filter emulation + - Joystick calibration is reset when joystick options change + - BeOS: Self-calibrating joystick routines + +Changes from V3.0e to V3.0f: + - Improved DIR/D64 drive reset, resetting the C64 resets the drives + - Implemented 'G' command for DIR/D64 drives + - Corrected translation of 0xc1..0xda characters in conv_from_64() + - BeOS: Implemented smart "Insert next disk" menu item + - Unix: Improved the speedometer/LED bar + - Unix: Self-calibrating joystick routines + - Unix: No need to enter path of 'wish' in TkGui.tcl + +Changes from V3.0d to V3.0e: + - Removed the CBOOL data type + - Unix: Sound for HP-UX + - Unix: Keyboard layout matches the picture in the docs more + closely + - Unix: Diagonal directions of keypad joystick emulation work + - Unix: +/- on numerical keypad modifies SkipFrames + - Unix: F9 invokes SAM + - Unix: Drive LEDs and speedometer implemented + - Unix: Some changes to the GUI + - Unix: Random number generator is initialized + - Unix: Name of prefs file can be given as an argument + - Unix: Calls XFlush() and XSync() in C64Display::Update() + +Changes from V3.0c to V3.0d: + - SID filter emulation implemented + - SID master volume setting works again + - Flags are recalculated in MOS6526::SetState() + - Changed CBOOL->bool in some places + - Fixed bug with char_in in MOS6510::new_config() + - BeOS: Emulation thread priority lowered + - Unix: Some changes for DEC Alpha + - Unix: Joystick support for Linux + +Changes from V3.0b to V3.0c: + - 1541 DIR mode can load directory with "$0" + - Rearranged the CPU code (more macros, less inline functions) + - SID envelope generators rewritten, envelopes are now + recalculated for every sample + - SID calc_buffer function now takes pointer to WORD buffer + - Unix: Sound for Linux + - Unix: Prefs window implemented (needs Tcl/Tk) + - Unix: Corrected x64 disk image detection on little-endian systems + - Unix: SVGAlib support works again, accesses frame buffer + directly if possible + - Unix: Added support for SHM under X11 + +Changes from V3.0a to V3.0b: + - Implemented REU emulation + - Formatting disks with ID possible under processor-level + 1541 emulation + - Corrected and optimized SID waveform/envelope calculation + (signed arithmetic) + - Corrected idle state display again (ECM text) + - 1541 D64 mode ignores drive numbers when opening the + directory + - Processor-level 1541 emulation deactivates when idle + - BeOS: Sound output quality is now 16 bits + - Unix: Quits more cleanly, reactivates key repeat + - Unix: Fixed alignment problem with text_chunky_buf in VIC.h + +Changes from V3.0 to V3.0a: + - Implemented SID test bits + - Combined SID waveforms respect pulse width + - Corrected idle state graphics display + - Processor-level 1541 emulation respects .d64 error info + - CPU emulation optimized (6510 and 6502 split) + - VIC emulation optimized (raster counter in local variable + in EmulateLine()) + - BeOS: Now exiting the audio subscriber with ExitStream(TRUE) + - Unix: Fixed missing thread_func() declaration + - Unix: getcwd(AppDirPath) was missing in main_x.i + - Unix: Speed limiter works diff --git a/Char ROM b/Char ROM new file mode 100644 index 0000000..191ac46 Binary files /dev/null and b/Char ROM differ diff --git a/Docs/Main.html b/Docs/Main.html new file mode 100644 index 0000000..6898cb7 --- /dev/null +++ b/Docs/Main.html @@ -0,0 +1,48 @@ + + +Frodo Manual + + + +

Frodo V4.1

+What's new in V4.1? +

The free, portable Commodore 64 emulator for BeOS/Unix/MacOS/AmigaOS/RiscOS/Win32

+© Copyright 1994-1997,2002 Christian Bauer et al. Freely distributable. + +
+ + + + + + + + + + + + + diff --git a/Docs/author.html b/Docs/author.html new file mode 100644 index 0000000..bec4cd3 --- /dev/null +++ b/Docs/author.html @@ -0,0 +1,37 @@ + + +The author + + + +

How to reach me:

+

Snail mail:

+
+Christian Bauer
+Max-Planck-Str.60
+55124 Mainz
+Germany

+

EMail:

+
+cbauer@iphcip1.physik.uni-mainz.de
+

+ +Questions, criticism, suggestions and bug +reports are always welcome. EMail is preferred. In fact, the probability +that physical mail to me will be answered is virtually zero (call me lazy +:-).

+ +Questions about the Unix version should go to Bernd Schmidt +(esp. Linux) and Lutz Vieweg (esp. HP-UX).

+ +Questions about the Mac version should go to Richard Bannister.

+ +Questions about the Win32 version should go to J. Richard Sladkey.

+ +Questions about the Acorn version should go to Andreas Dehmel.

+ +Frodo is not a shareware program, but I won't reject any gifts. +:-)

+ + + diff --git a/Docs/bugreports.html b/Docs/bugreports.html new file mode 100644 index 0000000..428bdbf --- /dev/null +++ b/Docs/bugreports.html @@ -0,0 +1,52 @@ + + +Bug reports + + + +

Bug reports

+ +
+ +If you find a bug or a misfeature in Frodo, or have an idea how to make +some things better, then please drop me a note so I'll be able to improve +Frodo in the future. However, I'm not interested in reports about programs +that "don't work" unless you have tested them with both Frodo SC and a +real C64. My address can be found here. +Questions about the Unix version should go to +Bernd Schmidt (esp. Linux) and +Lutz Vieweg (esp. HP-UX). +Questions about the Mac version should go to +Richard Bannister. +Questions about the Win32 version should go to +J. Richard Sladkey. +Questions about the Acorn version should go to +Andreas Dehmel.

+ +I don't think the emulation can be made much faster without changing the +concept but I'd be happy to find someone showing me how to do it.

+ +Known bugs of the BeOS version:

+ +

+ +Known bugs of the Unix version:

+ +

+ +Known bugs of the AmigaOS version:

+ +

+ + + diff --git a/Docs/demoprograms.html b/Docs/demoprograms.html new file mode 100644 index 0000000..32a3b25 --- /dev/null +++ b/Docs/demoprograms.html @@ -0,0 +1,64 @@ + + +Sample programs + + + +

Sample programs

+ +
+ +The directory "64prgs" contains some little sample programs to show the +capabilities of Frodo/Frodo SC. Except for "dadb" and "lrborder" they are all loaded with +LOAD"<name>",8,1 and started with SYS49152. +To do so, you have to set "Drive 8" in the settings to "Dir" and set the +path to '64prgs' so Frodo will find the programs.

+ +The programs were written by Pasi Ojala, Marko Mäkelä, Andreas +Boose and me. If you like, you can try them on other emulators or on a real +C64.

+ +A short description of each program:

+ +

3fff

+Opens the top and bottom border and displays swinging letters ("Cycles per +line (CPU)" should be set to 60 for this one) + +

colorbars

+Flickering colors + +

d011h3

+8-way soft scrolling without moving a single byte in memory (cursor keys +to scroll left/right, 't' for text mode, 'g' for bitmap mode), requires Frodo SC + +

dadb

+A program running in the color RAM (press space), requires Frodo SC + +

de00all

+A program running in the address space $de00-$dfff (press space), requires +Frodo SC + +

dycp

+Scrolling with different Y character position + +

fld

+Demonstrates the FLD effect (flexible line distance) + +

lrborder

+Opens the left/right border, requires Frodo SC + +

sprsync

+Stable raster routine by synchronizing with a sprite, requires Frodo SC + +

stretch

+Variably expanded sprites, requires Frodo SC + +

tech-tech

+Horizontal scrolling with large amplitude (use joystick in port 2 to +control) + +

text26

+Displays (nearly) 26 lines of text + + + diff --git a/Docs/emulwindow.html b/Docs/emulwindow.html new file mode 100644 index 0000000..021aa6c --- /dev/null +++ b/Docs/emulwindow.html @@ -0,0 +1,41 @@ + + +Emulation window + + + +

Emulation window

+ +
+ +The emulation window displays the C64 graphics and receives the keyboard +input. When this window is active and the emulation is running, the main +menu (BeOS/AmigaOS only) offers six commands (apart from About/Quit):

+ +

+ +In the bottom left corner of the window, Frodo displays how much percent of +the speed of a real C64 the emulation achieves.

+ +The four items labeled "Drive 8" to "Drive 11" are the disk activity +indicators of the 1541 emulation.

+ +Under RISC OS Frodo can be controlled via Menus and the emulator pane. Some notes on +the pane entries: + +

+ + + diff --git a/Docs/files.html b/Docs/files.html new file mode 100644 index 0000000..9ee1575 --- /dev/null +++ b/Docs/files.html @@ -0,0 +1,85 @@ + + +File access + + + +

File access

+ +
+ +Frodo offers four possibilities for the 1541 emulation: + +

1. Host system directory, setting "Dir"

+ +In this mode, the C64 programs and files are stored in a directory on your +hard disk and can be LOADed and SAVEd as usual from +the emulator. The paths to the directories of the simulated drives are +given in the settings window in the path entry fields of the "Drives" +box.

+ +You can also load the directory with LOAD"$",8. All files are +displayed as "PRG", all subdirectories as "DIR". To get into a +subdirectory, you have to open the settings window and change the path for +the drive, as the operating system of the C64 doesn't know about +subdirectories. However, it is possible to use a "/" in the C64 file name +to access these subdirectories (e.g. LOAD"GAMES/ELITE",8), +unless the '/' translation setting is turned on.

+ +For the opening of files, the file types "P" and "S" and the access modes +"R", "W" and "A" are supported. Wildcards (*,?) can be used, but you have +to remember that files in BeOS/Unix/AmigaOS directories have no determined order +and the result of LOAD"*",8 is rather random. Files are always +overwritten even if they are not opened with "@:". Floppy commands other +than "I" and "UJ", relative files and direct block access are not +implemented. You can however read the error channel.

+ + +

2. .d64/x64 disk image file, setting "D64"

+ +Most C64 programs available on the Internet and on CD-ROMs, expecially +demos and games, are stored in files with the ending ".d64". Such a file +holds all 683 blocks of a complete side of a 1541 disk, so that direct +block accesses are possible from within the emulation. However, Frodo only +supports read accesses. The path name of the disk image file must be given +in the settings window in the path entry fields of the "Drives" box.

+ +Apart from .d64 files, Frodo can also use image files of the "x64" +emulator, automatically detecting the file type.

+ + +

3. .t64/LYNX archive file, setting "T64"

+ +.t64 and LYNX (.lnx) files are archive files like "tar". .t64 files are +also often found on the Internet and on CD-ROMs; LYNX is a native C64 +archiver. Frodo's .t64 support is a bit special in that it doesn't treat +the .t64 file like a tape image (that's what the .t64 format was designed +for), but rather like a disk image file. .t64's are not accessed with +device number 1 (Frodo doesn't have any tape emulation), but with numbers +8..11 like a disk drive.

+ +When loading the directory with LOAD"$",8, Frodo creates a +listing of all files within the archive. You cannot write to .t64 or LYNX +files, they are read-only under Frodo.

+ + +

4. Processor-level 1541 emulation

+ +The 1541 is an "intelligent" disk drive bearing its own CPU and memory that +can even be programmed and execute code concurrently to the C64. Frodo is +able to emulate a 1541 on this level, but as this slows down the whole +emulation notably, the 1541 processor emulation can be turned on and off +from the settings.

+ +If the 1541 processor emulation is turned on, the "Dir"/"D64" drives 8..11 +are no longer available. They are replaced by a single drive with number 8 +that operates in "D64" mode (regardless of the state of the type setting +for drive 8). The path name of the .d64/x64 file to be used must be given +in the path entry field of drive 8.

+ +In contrast to the standard "D64" mode, the 1541 processor emulation is +able to write to the disk. There is no way to "virtually write-protect" the +disk image file, so be careful. + + + diff --git a/Docs/flavours.html b/Docs/flavours.html new file mode 100644 index 0000000..177aa96 --- /dev/null +++ b/Docs/flavours.html @@ -0,0 +1,76 @@ + + +Frodo, Frodo PC and Frodo SC + + + +

Frodo, Frodo PC and Frodo SC

+ +
+ +Frodo comes in three 'flavours' that allow you to decide between speed +and accuracy of the emulation. + +

The line-based emulation 'Frodo'

+ +Frodo is a line-based emulation, i.e. the activities that happen +in parallel during one video line in the real C64 are emulated one after +the other for the different chips. This offers a reasonable degree of +precision of the emulation at a decent speed. There are some things that +cannot be emulated with this technique, but it works fine with about +80% of all C64 programs and it is also the fastest of the three Frodo +versions. + +
+ +

The improved line-based emulation 'Frodo PC'

+ +Frodo PC is also a line-based emulation but it has some improvements +over the standard Frodo: + + + +Programs that don't work on the standard Frodo or that produce an +"Illegal jump to I/O space" message might work with Frodo PC. However, +Frodo PC is a bit slower. + +
+ +

The single-cycle emulation 'Frodo SC'

+ +Frodo SC is a special version of Frodo that doesn't work with a +line-based emulation but instead with a cycle-based one. That means that +the emulator switches between 6510 and VIC in every emulated ø2 clock +phase. By doing this, Frodo SC achieves an extreme precision (nearly all +$d011 and $d016 effects can be emulated), but at the expense of speed. + +In the settings options, Frodo SC differs from Frodo/Frodo PC in only +a few points: + + + +Apart from that, Frodo SC is operated in the same way as Frodo and also +uses the same settings. Frodo SC has only a few incompatibilities to a +real C64: + + + + + diff --git a/Docs/future.html b/Docs/future.html new file mode 100644 index 0000000..8e0b21b --- /dev/null +++ b/Docs/future.html @@ -0,0 +1,26 @@ + + +The future + + + +

The future

+ + +"Ai! Palan ú ná metta eldaloaron!" + + +
+ +These are some of the things planned for future versions:

+ +

+ + + + diff --git a/Docs/history.html b/Docs/history.html new file mode 100644 index 0000000..0b98185 --- /dev/null +++ b/Docs/history.html @@ -0,0 +1,224 @@ + + +History + + + +

Revision history

+ + +"Those days, the Third Age of Middle-earth,
+are now long past, and the shape of all lands
+has been changed." +
+ +
+ +This emulator started as a player routine for C64 music on the Amiga. +Versions V1.x were written in 68K assembly language, versions V2.x in a mix +of 68K assembly and C. V3.x/V4.x are portable versions written in C++.

+ +

V1.x/V2.x

+ + +

V3.0

+ + +

V3.0a

+ + +

V3.0b

+ + +

V3.0c

+ + +

V3.0d

+ + +

V3.0e

+ + +

V3.0f

+ + +

V3.0g

+ + +

V3.0h

+ + +

V3.1

+ + +

V3.1a

+ + +

V3.1b

+ + +

V3.1c

+ + +

V4.0

+ + +

V4.0a

+ + +

V4.1

+ + + + diff --git a/Docs/installation.html b/Docs/installation.html new file mode 100644 index 0000000..d589243 --- /dev/null +++ b/Docs/installation.html @@ -0,0 +1,64 @@ + + +Installation + + + +

Installation

+ +
+ +There are two kinds of Frodo distributions. The source distributions ("FrodoV?_?.Src.*") +contain source code that has to be compiled on your system. The binary distributions +("FrodoV?_?..*") contain an executable that can be started directly. + +

Compiling under BeOS

+ +There are BeIDE project files for Frodo ("Frodo.proj"), Frodo PC ("FrodoPC.proj") +and Frodo SC ("FrodoSC.proj"). You must rename (or copy) Src/sysconfig.h.Be +to Src/sysconfig.h first. Compilation requires the unlimited Metrowerks +linker. + +

Compiling under Unix

+ +First you have to run the program "configure". This can be done simply with +the following command:

+ +cd Src
+./configure

+ +You might want to give configure optional arguments. To use SVGAlib on Linux +systems, you have to do

+ +cd Src
+./configure --without-x

+ +To select a german keyboard layout in the X11 version instead of the usual +US keyboard, do

+ +cd Src
+./configure --enable-kbd-lang-de

+ +After running configure, you may want to edit the generated Makefile. To use the SHM +extension (highly recommended for speed), add "-DX_USE_SHM" to the definition of +CFLAGS. To get sound under Solaris 2.x, add "-DSUN". Next, type

+ +make all

+ +

Compiling under AmigaOS

+ +The makefile was written for the ADE with GCC. Other compilers have not been tested. +You must rename (or copy) Src/sysconfig.h.Amiga to Src/sysconfig.h first. Then type

+ +cd Src
+make -fMakefile.Amiga all

+ +

The C64 ROM files (all systems)

+ +Frodo looks for four ROM files named "Basic ROM", "Kernal ROM", "Char ROM" +and "1541 ROM" in the same directory Frodo is in. These files are included +in the source and binary distributions of Frodo. "Kernal ROM" is an +extended version of the C64 Kernal. + + + diff --git a/Docs/kernal.html b/Docs/kernal.html new file mode 100644 index 0000000..c22e8d5 --- /dev/null +++ b/Docs/kernal.html @@ -0,0 +1,55 @@ + + +Kernal extensions + + + +

Extensions of the included Kernal ROM

+ +
+ +The included "Kernal ROM" has some improvements/changes as compared to +an original C64 kernal: + + + + + diff --git a/Docs/keyboard.html b/Docs/keyboard.html new file mode 100644 index 0000000..1e3e4ba --- /dev/null +++ b/Docs/keyboard.html @@ -0,0 +1,140 @@ + + +Keyboard layout + + + +

Keyboard layout

+ +
+ +The keyboard layout closely resembles that of a real C64. The individual +rows of the keyboard are mapped as follows (american keyboard): + +
+<- 1 2 3 4 5 6 7 8 9 0 + -
+    Q W E R T Y U I O P @ *
+    A S D F G H J K L : ;
+     Z X C V B N M , . /
+

+ +In addition, the following keys are used: + +

+ Esc        - RUN/STOP
+ Backspace  - INS/DEL
+ Return     - RETURN
+ Enter      - RETURN
+ Shift keys - SHIFT
+ Caps lock  - SHIFT LOCK
+ F1-F8      - F1-F8
+

+ +Special keys under BeOS: + +

+ \          - ^
+ Insert     - Shift-INS/DEL
+ Delete     - INS/DEL
+ Home       - CLR/HOME
+ End        - £
+ Page Up    - £
+ Page Down  - =
+ Menu Keys  - C=
+ Ctrl Left  - CTRL
+ Ctrl Right - C=
+ F11        - RESTORE
+ F12        - C64 Reset
+

+ +Special keys under Unix: + +

+ \          - ^
+ Insert     - Shift-INS/DEL
+ Delete     - INS/DEL
+ Home       - CLR/HOME
+ End        - £
+ Page Up    - ^
+ Page Down  - =
+ Alt Keys   - C=
+ Ctrl Left  - CTRL
+ Ctrl Right - C=
+ F9         - Start SAM
+ F10        - Quit Frodo
+ F11        - RESTORE
+ F12        - C64 Reset
+

+ +Special keys under AmigaOS: + +

+ \          - £
+ Delete     - CLR/HOME
+ ( (keypad) - ^
+ ) (keypad) - =
+ Alt Keys   - C=
+ Ctrl       - CTRL
+ F9         - RESTORE
+ F10        - C64 Reset
+

+ +Special keys under RISC OS: + +

+ F5         - Toggle sound emulation mode
+ F6         - Enter SAM
+ F7         - RESTORE
+ F8         - Reset
+ Copy       - Toggle pause
+ PageUp     - Increase SkipFrames
+ PageDown   - Decrease SkipFrames
+ num/       - Toggle 1541 emulation mode
+ num*       - Toggle speed limiter
+ num+/-     - +/-
+ Alt        - CBM
+ ScrollLock - On: force single tasking, else multitasking
+

+ +Apart from that the function keys are mapped differently under RISC OS. (F1,F2,F3,F4) maps +to the C64's (F1,F3,F5,F7), you get (F2,F4,F6,F8) by pressing shift like on a real C64.

+ +So the famous key combination RUN/STOP-RESTORE must be typed as Esc-F11 (Esc-F9 under AmigaOS). +But you don't have to thrash the F11 key the same way as the RESTORE key on +the original C64. :-)

+ +The cursor (arrow) keys work as expected. I.e. "cursor up" corresponds to +"Shift-cursor down" on the C64. The same applies to the function keys F2, +F4, F6 and F8. + +The numerical keypad emulates a joystick in port 1 or 2, depending on the +state of the Num Lock (Num Lock off: port 2, Num Lock on: port 1):

+ +

+   7    8    9
+        ^
+        |
+   4    5    6
+  <-- Fire  -->
+        |
+        v
+   1    2    3
+
+   0
+ Fire
+

+ +Keyboard joysticks are handled differently under RISC OS: + +

+ +The '+' and '-' keys on the numerical keypad increase and decrease the +"Draw every n-th frame" setting on the fly. The '*' on the numerical keypad +toggles the "Limit Speed" option. The '/' on the numerical keypad toggles +the processor-level 1541 emulation. + + + diff --git a/Docs/legalmush.html b/Docs/legalmush.html new file mode 100644 index 0000000..f22be81 --- /dev/null +++ b/Docs/legalmush.html @@ -0,0 +1,37 @@ + + +Copyright and distribution + + + +

Copyright and distribution

+ +
+ +The program "Frodo", this manual and the source code may be freely +distributed as long as they remain unchanged (archiving and packing is +allowed) and all files are included. You must not make any profit by +selling Frodo, especially the price of a disk containing Frodo may not +exceed US$ 5,- (or equivalent amounts in other currencies). Please feel +free to distribute Frodo via bulletin board systems and networks and as +part of shareware/freeware CD-ROMs.

+ +Anyone using this program agrees to incur the risk of using it for himself. +In no way can the author be held responsible for any damage directly or +indirectly caused by the use or misuse of this manual and/or the program.

+ +The rights on the source code remain at the author. It may not - not even +in parts - used for commercial purposes without explicit written permission +by the author. Permission to use it for non-commercial purposes is hereby +granted als long as my copyright notice remains in the program. You are not +allowed to use the source to create and distribute a modified version of +Frodo.

+ +Frodo is not designed, intended, or authorized for use as a component in +systems intended for surgical implant within the body, or other +applications intended to support or sustain life, or for any other +application in which the failure of Frodo could create a situation where +personal injury or death may occur.

+ + + diff --git a/Docs/overview.html b/Docs/overview.html new file mode 100644 index 0000000..a785ac4 --- /dev/null +++ b/Docs/overview.html @@ -0,0 +1,50 @@ + + +Overview + + + +

Overview

+ + +"Ú-queta i yéni avánier alye,
+ú-queta i cirya ná vanwa!" +
+ +
+ +Frodo is a freeware C64 emulator for BeOS, Unix, MacOS, AmigaOS, +Win32 and RiscOS systems and the world's first C64 emulator not bearing a +"64" in its name. :-) (No, it has absolutely nothing to do with +frodo.hiof.no, that's a pure coincidence.)

+ +Frodo was developed to reproduce the graphics of games and demos better +than the existing C64 emulators. Therefore Frodo has relatively high system +requirements: It should only be run on systems with at least a +PowerPC/Pentium/68060. But on the other hand, Frodo can display raster +effects correctly that only result in a flickering mess with other +emulators.

+ +Frodo comes in three flavours: The "normal" Frodo +with a line-based emulation, the improved line-based emulation "Frodo PC", +and the single-cycle emulation Frodo SC that is slower but far more +compatible.

+ +In addition to a precise 6510/VIC emulation, Frodo features a processor-level +1541 emulation that is even able to handle about 95% of all fast loaders. +There is also a faster 1541 emulation for four drives in .d64/x64 disk +images, .t64/LYNX archives, or directories of the host system.

+ +Frodo runs on these systems:

+ +

+ + + diff --git a/Docs/sam.html b/Docs/sam.html new file mode 100644 index 0000000..f840e21 --- /dev/null +++ b/Docs/sam.html @@ -0,0 +1,282 @@ + + +SAM + + + +

SAM

+ + +"Frodo! Mr.Frodo, my dear!" cried Sam,
+tears almost blinding him. "It's Sam, I've come!" +
+ +
+ +Frodo has a built-in machine language monitor that can be activated at any +time by pressing F9 (Unix) or selecting the appropriate menu item (BeOS/AmigaOS/MacOS): +SAM (Simple Assembler and Monitor). It provides full access to the memory +and hardware of the emulated C64 and 1541 (under BeOS, you must only start +SAM if you launched Frodo from the Shell).

+ +SAM is controlled by a command-based interface, all numbers are in hex. +With the command "h" you can display a list of all commands. The command +"x" quits SAM and returns to Frodo.

+ +SAM has two modes of operation, indicated by the prompt "C64>" or "1541>". +You can switch between them with the "64" and "1541" commands. In "C64" +mode, all commands that access memory or the CPU operate on the memory/CPU +of the emulated C64. In "1541" mode, they operate on the emulated 1541 +(this is only useful if the processor-level 1541 emulation is turned +on).

+ +All commands that access the C64 memory use the memory configuration set +with the "k" command. On starting up SAM, the configuration is set to the +one the processor is in. Accesses from within SAM have the same effect as +accesses of the emulated processor. This affects especially the I/O +registers: a memory dump of $dc00-$de00 clears pending CIA interrupts as +SAM reads from $dc0d and $dd0d. With the "v" commands, you can examine the +state of the I/O chips without modifying the state of the emulation.

+ +In all places where SAM expects a number (except in the assembler) you can +also enter an expression of hex numbers containing '+', '-', '*', '/' and +parens. Commands that create a longer output can be interrupted with +Ctrl-C.

+ +Here is a description of all commands ('[]' marks a parameter than can be +left out, '{}' marks a parameter that can be repeated many times. If a +[start] parameter is left out, SAM continues at the address where the last +command stopped):

+ + +

+ a [start]           Assemble
+

+ +starts the assembler at the address "start". SAM always prints the address +where the next instruction will be written to. The syntax of the +instructions conforms to the standard except for shift/rotation +instructions in the "accumulator" addressing mode. Those have to be entered +without operand, e.g. "lsr" instead of "lsr a". Entering a blank line quits +the assembler and returns to the command mode of SAM.

+ + +

+ b [start] [end]     Binary dump
+

+ +displays the memory from "start" to "end" byte-wise binary. With this +command, you can view character sets.

+ + +

+ c start end dest    Compare memory
+

+ +compares the memory in the range from "start" to (and including) "end" +with the memory at "dest". The addresses of all different bytes and the +total number of differences (decimal) are printed.

+ + +

+ d [start] [end]     Disassemble
+

+ +disassembles the memory from "start" to "end". Undocumented opcodes are +markes with a star '*'.

+ + +

+ e                   Show interrupt vectors
+

+ +shows the currently active interrupt vectors of the 6510 (C64) or 6502 +(1541) and (in C64 mode, if the Kernal ROM is mapped in) of the Kernal.

+ + +

+ f start end byte    Fill memory
+

+ +fills the memory in the range from "start" to (and including) "end" with +the value "byte".

+ + +

+ i [start] [end]     ASCII/PETSCII dump
+

+ +shows the memory from "start" to "end" as ASCII/PETSCII characters.

+ + +

+ k [config]          Show/set C64 memory configuration
+

+ +"k" without parameters shows the memory configuration that is set for SAM, +"k" with parameter modifies it. On exiting SAM, the configuration set with +the processor port is reactivated. The memory configuration has no effect +in 1541 mode. The 8 possible configurations are: + +

+  #  $a000-$bfff  $d000-$dfff  $e000-$ffff
+  ----------------------------------------
+  0      RAM          RAM          RAM
+  1      RAM       Char ROM        RAM
+  2      RAM       Char ROM    Kernal ROM
+  3   Basic ROM    Char ROM    Kernal ROM
+  4      RAM          RAM          RAM
+  5      RAM          I/O          RAM
+  6      RAM          I/O      Kernal ROM
+  7   Basic ROM       I/O      Kernal ROM
+
+
+ + +
+ l start "file"      Load data
+

+ +loads the contents of the specified file into memory starting from address +"start". The file name must be enclosed in quotation marks even if it +contains no spaces. This command cannot be used to load C64 programs as +it doesn't respect the embedded load address in the programs.

+ + +

+ m [start] [end]     Memory dump
+

+ +displays the memory from "start" to "end" as hexadecimal numbers and ASCII +characters.

+ + +

+ n [start] [end]     Screen code dump
+

+ +displays the memory from "start" to "end" as ASCII characters, interpreting +each byte as a screen code of the standard character set.

+ + +

+ o ["file"]          Redirect output
+

+ +When a file name is specified, all following output is redirected to this +file. The file name must be enclosed in quotation marks even if it contains +no spaces. Entering "o" without parameters closes the file and directs the +output into the window of SAM again.

+ + +

+ p [start] [end]     Sprite dump
+

+ +displays the memory from "start" to "end" binary with three bytes per line. +With this command, you can display sprite data.

+ + +

+ r [reg value]       Show/set CPU registers
+

+ +"r" without parameters shows all 6510 (C64) or 6502 (1541) registers and +flags and the instruction at the address specified by the program counter. +For the 6510, "DR" and "PR" are the data direction register and data +register of the processor port. To modify a register, give its name ("reg") +and the new value ("value") as parameters.

+ + +

+ s start end "file"  Save data
+

+ +writes the memory from "start" to (and including) "end" to the specified +file. The file name must be enclosed in quotation marks even if it contains +no spaces. This command cannot be used to save C64 programs as it doesn't +save a load address in the file.

+ + +

+ t start end dest    Transfer memory
+

+ +transfers the memory from "start" to (and including) "end" to "dest". +Source and destination may overlap.

+ + +

+ vc1                 View CIA 1 state
+

+ +shows the state of CIA 1 ($dc00).

+ + +

+ vc2                 View CIA 2 state
+

+ +shows the state of CIA 2 ($dd00).

+ + +

+ vf                  View floppy state
+

+ +shows the state of the processor-level 1541 emulation.

+ + +

+ vs                  View SID state
+

+ +shows the state of the SID.

+ + +

+ vv                  View VIC state
+

+ +shows the state of the VIC.

+ + +

+ x                   Return to Frodo
+

+ +quits SAM and returns to Frodo.

+ + +

+ : addr {byte}       Modify memory
+

+ +writes the space-separated values "byte" into memory starting at "addr".

+ + +

+ 1541                Switch to 1541 mode
+

+ +switches to 1541 mode. All commands that access memory or the CPU will then +operate on the emulated 1541 (processor-level).

+ + +

+ 64                  Switch to C64 mode
+

+ +switches to C64 mode. All commands that access memory or the CPU will then +operate on the emulated C64.

+ + +

+ ? expression        Calculate expression
+

+ +calculates the value of the given expression and displays it in decimal +and hexadecimal.

+ + + diff --git a/Docs/settings.html b/Docs/settings.html new file mode 100644 index 0000000..061bac4 --- /dev/null +++ b/Docs/settings.html @@ -0,0 +1,167 @@ + + +Settings + + + +

Settings

+ +
+ +Under BeOS, the settings window appears directly after starting Frodo, or +by selecting the "Settings..." menu item in the running emulation. Under +Unix, the settings window is permanently visible.

+ +With "Sprite display", you can switch the display of sprites on and +off. Turning them off speeds up the emulation a little when there are a lot +of sprites on the screen.

+ +"Sprite collisions" determines whether collisions between sprites +and between sprites and graphics should be detected. Turning off collisions +will make you invincible in some games (sadly, your enemies are likely to +become invincible, too :-/.

+ +"Joystick on Port 1/2" specifies on which ports you have real +joysticks connected (as opposed to the joystick +emulation on the numerical keypad). Joysticks are only supported under +BeOS, Linux, RiscOS and AmigaOS (only one joystick). The port numbers relate +to the host machine ports. On the BeBox, port 1 is the upper one and port 2 +the lower one. You should only turn on the ports to which you have actually +joysticks connected, or the keyboard will behave erratically. Frodo has an +automatic joystick calibration. If you plug in a new joystick or change the +joystick settings, you should first move the joystick once in each direction.

+ +With "Swap joysticks" you can swap the assignment of the joystick +ports of the host machine to the C64 ports without having to plug out and +in your joysticks. E.g. if a C64 game is using a joystick on C64 port 1 you +can simply activate "Swap joysticks" and use a joystick in port 2 on your +machine to play the game.

+ +When the field "Limit speed" is active, the emulation is slowed down +when its relative speed exceeds 100%. If you set the value in "Every (n)th +frame" so that the speed is just over 100% and activate the speed limiter, +the emulation always runs at the original C64 speed, with the highest +possible precision.

+ +With the setting "Fast Reset" you can disable the memory test that +is normally performed by the C64 on a reset. Under emulation, the memory +test is not necessary and the reset (F12) becomes much faster when it is +disabled.

+ +The setting "Clear CIA ICR on write" is necessary to make some +programs (such as the games "Gyruss" and "Motos") run that would otherwise +hang in an endless interrupt loop because they use an unusual technique to +acknowledge CIA interrupts (sometimes even without the programmer knowing +it). It should normally be turned off.

+ +The "SID Filters" field enables the emulation of the SID filters. +The sound emulation is slightly faster, but worse, when the filters are +disabled.

+ +"Doublescan lines" is only available under BeOS for the "Screen" +display type. It removes the black lines between scanlines, but makes +the emulation a bit slower.

+ +"Cycles per line (CPU)" and "Cycles per Bad Line (CPU)" set +the number of clock cycles available to the CPU per normal raster line and +per Bad Line. If a program is showing flickering lines or graphical flaws +you should try to slightly alter both values. For "Bruce Lee" you must +enter "62" for the "Cycles per line (CPU)".

+ +With "Cycles per line (CIA)" you can control the speed of the CIA +timers. Entering a higher value increases the frequency of cursor blinking +and key repeat. Some programs don't run correcly with the default value +(e.g. "Ballblazer" which needs a value of 65).

+ +"Cycles per line (1541)" sets the number of cycles available to the +1541 processor emulation per raster line. There is normally no need to +change this value. This setting has no effect if 1541 processor emulation +is turned off.

+ +The settings for the four "cycles" coming closest to an original PAL C64 +are (63, 23, 63, 64).

+ +With "Draw every n-th frame" you can select if Frodo should skip +frames when displaying the C64 graphics. The normal setting is "1", that +is, every frame (every simulated raster beam sweep) is recalculated. If you +change this to "2", for example, then only every second frame is +calculated, immensely speeding up the display, though some raster effects +may look a bit jerky. This setting can also be changed while the emulation +is running with the '+' and '-' keys on the numerical keypad.

+ +"Display type" is only available under BeOS. You can choose between +running the emulation in a window or in full-screen mode (using the +Game Kit).

+ +The "SID emulation type" controls the sound emulation and has two +settings: "None" and "Digital". "None" means no +sound (faster), "Digital" turns on the digital sound emulation +(only available under BeOS, Linux and HP-UX). Future versions of Frodo may +support more emulation types such as the use of a real SID chip on an +expansion card or across a network.

+ +"REU size" sets the size of the REU (RAM Expansion Unit) emulated by +Frodo or turns the REU emulation off ("None"). Only few programs actually +use the REU (operating systems like ACE and GEOS, and some utilities).

+ +In the box "Drives", there are four rows, each corresponding to one +of four emulated 1541 drives with the drive numbers 8, 9, 10 and 11. For +every drive, there is a popup control, a path entry field +and a button:

+ +With the popup control, you select the emulation mode of the +respective disk drive (for more detailed information, see here). There are three choices: "Dir", +"D64" and "T64". "Dir" emulates the drive in a +directory of the BeOS/Unix file system. "D64" accesses a .d64 or x64 +disk image file. "T64" is the setting for accessing a .t64 or C64 +LYNX archive file.

+ +The path entry field holds either the path name of the directory for +the "Dir" mode, the path name of the .d64/x64 image file for the "D64" +mode, or the path name of the .t64/LYNX archive file for the "T64" mode. +Under BeOS, you may also drop Tracker icons to the entry field.

+ +The button labeled "B" opens a file panel/requester for a more +comfortable selection of directories and .d64/x64/.t64/LYNX files.

+ +With "Map '/' <-> '\' in file names" you control whether the +'/' in C64 filenames will be translated to '\' and vice versa for "Dir" +mode drives. The '/' character is used to access subdirectories under +BeOS/Unix, but as the C64 doesn't have subdirectories, it's a valid part of +a C64 file name. This is a problem if a program wants to create a file with +'/' in it as BeOS/Unix would interpret the part before the '/' as a +directory name and, finding no such directory, would return an error and +the operation would fail. Now simply activate this gadget and all '/'s will +transparently be translated into '\', so in directory listings the '/' will +still appear. If you turn off this option, you can of course use the '/' to +access files in subdirectories from the C64.

+ +If "Enable 1541 processor emulation" is turned on, the four emulated +1541s are disabled and replaced by a single 1541 emulation (drive 8) that +only operates on .d64/x64 files, but emulates the 1541 processor and is +compatible with about 50% of all fast loaders. However, it slows down the +emulation considerably. If you have a .d64 with a program that doesn't load +with the normal emulation (see above), you may have better luck with the +1541 processor emulation instead. The path name of the disk image file to +be used must be entered into the path entry field of drive 8.

+ +

BeOS/AmigaOS

+ +Clicking "Start"/"OK" will start the actual emulation (resp. return +to it) and "Quit"/"Cancel" will discard your changes to the settings +and quit Frodo (resp. discard the changes and return to the emulation).

+ +With the menu items "Open...", "Save", "Save As..." +and "Revert" you can load and save the settings from and to +arbitrary files.

+ +

Unix

+ +Clicking "Apply" applies the settings of the "Cycles" controls to +the running emulation (all other settings are applied automatically). +"Defaults" reverts to the default settings, "Quit" quits +Frodo and "Reset" resets the emulation.

+ + + diff --git a/Docs/systemspecific.html b/Docs/systemspecific.html new file mode 100644 index 0000000..34d5c0b --- /dev/null +++ b/Docs/systemspecific.html @@ -0,0 +1,102 @@ + + +System specific notes + + + +

System specific notes

+ +
+ +

BeOS

+ +Frodo may be started from the shell or from the Tracker. When started from +the shell, Frodo accepts a single argument: The name of a preferences file +to load instead of the default "Frodo Prefs".

+ +At first, the window for the emulation settings +appears. The actual emulation is started by a click on "Start". Then the emulation window appears in which the C64 +startup message is displayed.

+ +You can quit from the running emulation by selecting the "Quit" menu +item.

+ +Frodo should be run in a 256-color workspace for maximum speed.

+ +Sampled sounds are only played correctly if "Limit Speed" is on and "Draw every +n-th frame" is set to "1" and if the emulation can sustain 100% speed.

+ +

Unix

+ +Frodo accepts a single argument when started from the shell: The name of a +preferences file to load instead of the default "~/.frodorc".

+ +First the emulation window appears in which +the C64 startup message is displayed. If you have Tcl/Tk 4.1 installed +and the file "TkGui.tcl" is in the current directory, a second window opens +for the emulation settings.

+ +If you have no Tcl/Tk or are using the SVGAlib version under Linux, you +have to copy the included "Frodo Prefs" file to "~/.frodorc" and edit it manually +(read "Prefs.cpp" to find out about the syntax of the settings file). + +You can quit from the running emulation by pressing F10.

+ +Sound is currently only supported under Linux, HP-UX and Solaris 2.x. Sampled +sounds are only played correctly if "Limit Speed" is on and "Draw every n-th frame" +is set to "1" and if the emulation can sustain 100% speed.

+ +

MacOS

+ +Frodo is started by double-clicking its icon.

+ +You can quit from the running emulation by selecting the "Quit" menu +item.

+ +Frodo should be run with 8-bit color depth. The "DIR" 1541 emulation mode +and SAM are currently not implemented.

+ +See the file "MacFrodo Notes" for more information.

+ +

AmigaOS

+ +Frodo should be started from the shell. When started from the shell, Frodo +accepts a single argument: The name of a preferences file to load instead +of the default "Frodo Prefs".

+ +At first, the window for the emulation settings +appears. The actual emulation is started by a click on "OK". Then the emulation window appears in which the C64 +startup message is displayed.

+ +You can quit from the running emulation by selecting the "Quit" menu +item.

+ +Frodo opens its windows on the default public screen. This screen should +be a 256-color screen on a graphics card, otherwise the emulation will +be very slow.

+ +The AmigaOS version is somewhat experimental. You need AHI to get sound, +but the sound emulation is not very good. It might be better to leave the +SID emulation turned of for now. Only one joystick (on port 2) is supported.

+ +

RISC OS

+ +Frodo is started by double-clicking its icon. The shared resources stored in +!FrodoRsrc must have been seen by the filer first, otherwise the program will +abort with an error. Frodo and FrodoPC need 1344kB, FrodoSC 832kB to run.

+ +Frodo can be controlled with the menus and the emulator +pane. You can load snapshots and native C64-files (which usually have the +filetype &64) by dragging them to the emulator window.

+ +Frodo can be run in any colour-depth, although 16bpp and especially 32bpp are +noticably slower than the others. You can toggle between two display sizes +using the pane.

+ +For a lot more information see the file !Help supplied in the RISC OS +distribution. + + + diff --git a/Docs/technicalinfo.html b/Docs/technicalinfo.html new file mode 100644 index 0000000..9c7d985 --- /dev/null +++ b/Docs/technicalinfo.html @@ -0,0 +1,61 @@ + + +Technical info + + + +

Technical info

+ + +"Known?" said Gandalf.
+"I have known much that only the Wise know, Frodo." +
+ +
+ +Frodo tries to exactly imitate C64 hardware features. Now the 64's hardware +(esp. the graphics chip "VIC-II") has a rather simple design resulting in +many of the internal processes coming to the "outside". So there are lots +of "undocumented features" you can do effects with the designers never +dared to dream about.

+ +Frodo uses a line-by-line emulation, i.e. the function of the VIC and the +processor (6510) are emulated for one raster line of the C64 screen at +times. In practice, Frodo runs VIC and 6510 alternately for 63 simulated +cycles each (corresponding to one raster line). At first, it emulates the +processor for 63 cycles, then switches over to the VIC that paints one +pixel row to the screen, then again 63 cycles processor, and so on... If +the 1541 processor emulation is turned on, 6510 and 6502 (in the 1541) +instructions are executed by Frodo in an interleaved fashion.

+ +Even though this is a heavy simplification of the processes in a real C64, +it lets you perfectly emulate many graphical effects possible on the C64, +e.g. FLD, DYCP, hyperscreen and many more. But this method has one big +disadvantage: Changes made to VIC registers by the processor in the middle +of a raster line will only take effect at the start of the next line. E.g. +you can't change the border color in the middle of a line, the color change +takes place in the next line. Therefore, very sophisticated techniques +depending on the exact position of a register change can't be emulated. For +instance, it is no problem to open the top and bottom border, but opening +the left and right border is impossible (and therefore not implemented in +the emulation).

+ +Frodo SC goes one step further by switching between VIC and 6510 in every +cycle and precisely emulating the internal functions. Modifications to +VIC registers become visible immediately in the next clock phase and +therefore it can even emulate effects that depend on the exact position +of a register change within a raster line, e.g. special FLI routines, +opening the left/right border, linecrunch, DMA delay, multiple repeated +sprite lines and executing programs in open address spaces ($de00-$dfff) +and in the color RAM. The 6510 emulation is also more precise and does +the same memory accesses as the real 6510, even the "unnecessary" ones +that come from design weaknesses of the 6510 and are not needed for the +function of single opcodes (e.g. in an instruction sequence like +INX:INX:INX:INX, the 6510 reads every opcode twice).

+ +A detailed technical description of the VIC-II can be found in an +article +I wrote (32k gzipped). + + + diff --git a/Docs/thanks.html b/Docs/thanks.html new file mode 100644 index 0000000..1ad2d77 --- /dev/null +++ b/Docs/thanks.html @@ -0,0 +1,36 @@ + + +Credits + + + +

Credits

+ + +"The Silmaril as lantern light
+And banner bright with living flame
+To gleam thereon by Elbereth
+Herself was set, who thither came." +
+ +
+ +The following persons deserve special thanks from me as they made a +significant contribution to the development of Frodo:

+ +

+ + + diff --git a/Docs/whatsnew.html b/Docs/whatsnew.html new file mode 100644 index 0000000..b560f32 --- /dev/null +++ b/Docs/whatsnew.html @@ -0,0 +1,24 @@ + + +What's new? + + + +

What's new in V4.1?

+ +
+ +The most important changes from V4.0 are: + + + +For the tons of other small changes and bug fixes, please look here.

+ + + diff --git a/Frodo Logo b/Frodo Logo new file mode 100644 index 0000000..f17abd3 Binary files /dev/null and b/Frodo Logo differ diff --git a/Frodo.proj b/Frodo.proj new file mode 100644 index 0000000..bfec55e Binary files /dev/null and b/Frodo.proj differ diff --git a/FrodoPC.proj b/FrodoPC.proj new file mode 100644 index 0000000..9a099bb Binary files /dev/null and b/FrodoPC.proj differ diff --git a/FrodoSC.proj b/FrodoSC.proj new file mode 100644 index 0000000..465e301 Binary files /dev/null and b/FrodoSC.proj differ diff --git a/Kernal ROM b/Kernal ROM new file mode 100644 index 0000000..0fb99af Binary files /dev/null and b/Kernal ROM differ diff --git a/Src/1541d64.cpp b/Src/1541d64.cpp new file mode 100644 index 0000000..fc48e51 --- /dev/null +++ b/Src/1541d64.cpp @@ -0,0 +1,1025 @@ +/* + * 1541d64.cpp - 1541 emulation in .d64 file + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Incompatibilities: + * ------------------ + * + * - Only read accesses possible + * - Not all commands implemented + * - The .d64 error info is read, but unused + */ + +#include "sysdeps.h" + +#include "1541d64.h" +#include "IEC.h" +#include "Prefs.h" + + +// Channel modes (IRC users listen up :-) +enum { + CHMOD_FREE, // Channel free + CHMOD_COMMAND, // Command/error channel + CHMOD_DIRECTORY, // Reading directory + CHMOD_FILE, // Sequential file open + CHMOD_DIRECT // Direct buffer access ('#') +}; + +// Access modes +enum { + FMODE_READ, FMODE_WRITE, FMODE_APPEND +}; + +// File types +enum { + FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL +}; + +// Number of tracks/sectors +const int NUM_TRACKS = 35; +const int NUM_SECTORS = 683; + +// Prototypes +static bool match(uint8 *p, uint8 *n); + + +/* + * Constructor: Prepare emulation, open .d64 file + */ + +D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec) +{ + the_file = NULL; + ram = NULL; + + Ready = false; + strcpy(orig_d64_name, filepath); + for (int i=0; i<=14; i++) { + chan_mode[i] = CHMOD_FREE; + chan_buf[i] = NULL; + } + chan_mode[15] = CHMOD_COMMAND; + + // Open .d64 file + open_close_d64_file(filepath); + if (the_file != NULL) { + + // Allocate 1541 RAM + ram = new uint8[0x800]; + bam = (BAM *)(ram + 0x700); + + Reset(); + Ready = true; + } +} + + +/* + * Destructor + */ + +D64Drive::~D64Drive() +{ + // Close .d64 file + open_close_d64_file(""); + + delete[] ram; + Ready = false; +} + + +/* + * Open/close the .d64 file + */ + +void D64Drive::open_close_d64_file(char *d64name) +{ + long size; + uint8 magic[4]; + + // Close old .d64, if open + if (the_file != NULL) { + close_all_channels(); + fclose(the_file); + the_file = NULL; + } + + // Open new .d64 file + if (d64name[0]) { + if ((the_file = fopen(d64name, "rb")) != NULL) { + + // Check length + fseek(the_file, 0, SEEK_END); + if ((size = ftell(the_file)) < NUM_SECTORS * 256) { + fclose(the_file); + the_file = NULL; + return; + } + + // x64 image? + rewind(the_file); + fread(&magic, 4, 1, the_file); + if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64) + image_header = 64; + else + image_header = 0; + + // Preset error info (all sectors no error) + memset(error_info, 1, NUM_SECTORS); + + // Load sector error info from .d64 file, if present + if (!image_header && size == NUM_SECTORS * 257) { + fseek(the_file, NUM_SECTORS * 256, SEEK_SET); + fread(&error_info, NUM_SECTORS, 1, the_file); + } + } + } +} + + +/* + * Open channel + */ + +uint8 D64Drive::Open(int channel, char *filename) +{ + set_error(ERR_OK); + + // Channel 15: execute file name as command + if (channel == 15) { + execute_command(filename); + return ST_OK; + } + + if (chan_mode[channel] != CHMOD_FREE) { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + if (filename[0] == '$') + if (channel) + return open_file_ts(channel, 18, 0); + else + return open_directory(filename+1); + + if (filename[0] == '#') + return open_direct(channel, filename); + + return open_file(channel, filename); +} + + +/* + * Open file + */ + +uint8 D64Drive::open_file(int channel, char *filename) +{ + char plainname[256]; + int filemode = FMODE_READ; + int filetype = FTYPE_PRG; + int track, sector; + + convert_filename(filename, plainname, &filemode, &filetype); + + // Channel 0 is READ PRG, channel 1 is WRITE PRG + if (!channel) { + filemode = FMODE_READ; + filetype = FTYPE_PRG; + } + if (channel == 1) { + filemode = FMODE_WRITE; + filetype = FTYPE_PRG; + } + + // Allow only read accesses + if (filemode != FMODE_READ) { + set_error(ERR_WRITEPROTECT); + return ST_OK; + } + + // Find file in directory and open it + if (find_file(plainname, &track, §or)) + return open_file_ts(channel, track, sector); + else + set_error(ERR_FILENOTFOUND); + + return ST_OK; +} + + +/* + * Analyze file name, get access mode and type + */ + +void D64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype) +{ + char *p; + + // Search for ':', p points to first character after ':' + if ((p = strchr(srcname, ':')) != NULL) + p++; + else + p = srcname; + + // Remaining string -> destname + strncpy(destname, p, NAMEBUF_LENGTH); + + // Search for ',' + p = destname; + while (*p && (*p != ',')) p++; + + // Look for mode parameters seperated by ',' + p = destname; + while ((p = strchr(p, ',')) != NULL) { + + // Cut string after the first ',' + *p++ = 0; + + switch (*p) { + case 'P': + *filetype = FTYPE_PRG; + break; + case 'S': + *filetype = FTYPE_SEQ; + break; + case 'U': + *filetype = FTYPE_USR; + break; + case 'L': + *filetype = FTYPE_REL; + break; + case 'R': + *filemode = FMODE_READ; + break; + case 'W': + *filemode = FMODE_WRITE; + break; + case 'A': + *filemode = FMODE_APPEND; + break; + } + } +} + + +/* + * Search file in directory, find first track and sector + * false: not found, true: found + */ + +bool D64Drive::find_file(char *filename, int *track, int *sector) +{ + int i, j; + uint8 *p, *q; + DirEntry *de; + + // Scan all directory blocks + dir.next_track = bam->dir_track; + dir.next_sector = bam->dir_sector; + + while (dir.next_track) { + if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track)) + return false; + + // Scan all 8 entries of a block + for (j=0; j<8; j++) { + de = &dir.entry[j]; + *track = de->track; + *sector = de->sector; + + if (de->type) { + p = (uint8 *)filename; + q = de->name; + for (i=0; i<16 && *p; i++, p++, q++) { + if (*p == '*') // Wildcard '*' matches all following characters + return true; + if (*p != *q) { + if (*p != '?') goto next_entry; // Wildcard '?' matches single character + if (*q == 0xa0) goto next_entry; + } + } + + if (i == 16 || *q == 0xa0) + return true; + } +next_entry: ; + } + } + + return false; +} + + +/* + * Open file given track/sector of first block + */ + +uint8 D64Drive::open_file_ts(int channel, int track, int sector) +{ + chan_buf[channel] = new uint8[256]; + chan_mode[channel] = CHMOD_FILE; + + // On the next call to Read, the first block will be read + chan_buf[channel][0] = track; + chan_buf[channel][1] = sector; + buf_len[channel] = 0; + + return ST_OK; +} + + +/* + * Prepare directory as BASIC program (channel 0) + */ + +const char type_char_1[] = "DSPUREERSELQGRL?"; +const char type_char_2[] = "EERSELQGRL??????"; +const char type_char_3[] = "LQGRL???????????"; + +// Return true if name 'n' matches pattern 'p' +static bool match(uint8 *p, uint8 *n) +{ + if (!*p) // Null pattern matches everything + return true; + + do { + if (*p == '*') // Wildcard '*' matches all following characters + return true; + if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character + return false; + p++; n++; + } while (*p); + + return *n == 0xa0; +} + +uint8 D64Drive::open_directory(char *pattern) +{ + int i, j, n, m; + uint8 *p, *q; + DirEntry *de; + uint8 c; + char *tmppat; + + // Special treatment for "$0" + if (pattern[0] == '0' && pattern[1] == 0) + pattern += 1; + + // Skip everything before the ':' in the pattern + if ((tmppat = strchr(pattern, ':')) != NULL) + pattern = tmppat + 1; + + p = buf_ptr[0] = chan_buf[0] = new uint8[8192]; + chan_mode[0] = CHMOD_DIRECTORY; + + // Create directory title + *p++ = 0x01; // Load address $0401 (from PET days :-) + *p++ = 0x04; + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + *p++ = 0; // Drive number (0) as line number + *p++ = 0; + *p++ = 0x12; // RVS ON + *p++ = '\"'; + + q = bam->disk_name; + for (i=0; i<23; i++) { + if ((c = *q++) == 0xa0) + *p++ = ' '; // Replace 0xa0 by space + else + *p++ = c; + } + *(p-7) = '\"'; + *p++ = 0; + + // Scan all directory blocks + dir.next_track = bam->dir_track; + dir.next_sector = bam->dir_sector; + + while (dir.next_track) { + if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track)) + return ST_OK; + + // Scan all 8 entries of a block + for (j=0; j<8; j++) { + de = &dir.entry[j]; + + if (de->type && match((uint8 *)pattern, de->name)) { + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + + *p++ = de->num_blocks_l; // Line number + *p++ = de->num_blocks_h; + + *p++ = ' '; + n = (de->num_blocks_h << 8) + de->num_blocks_l; + if (n<10) *p++ = ' '; + if (n<100) *p++ = ' '; + + *p++ = '\"'; + q = de->name; + m = 0; + for (i=0; i<16; i++) { + if ((c = *q++) == 0xa0) { + if (m) + *p++ = ' '; // Replace all 0xa0 by spaces + else + m = *p++ = '\"'; // But the first by a '"' + } else + *p++ = c; + } + if (m) + *p++ = ' '; + else + *p++ = '\"'; // No 0xa0, then append a space + + if (de->type & 0x80) + *p++ = ' '; + else + *p++ = '*'; + + *p++ = type_char_1[de->type & 0x0f]; + *p++ = type_char_2[de->type & 0x0f]; + *p++ = type_char_3[de->type & 0x0f]; + + if (de->type & 0x40) + *p++ = '<'; + else + *p++ = ' '; + + *p++ = ' '; + if (n >= 10) *p++ = ' '; + if (n >= 100) *p++ = ' '; + *p++ = 0; + } + } + } + + // Final line + q = p; + for (i=0; i<29; i++) + *q++ = ' '; + + n = 0; + for (i=0; i<35; i++) + n += bam->bitmap[i*4]; + + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + *p++ = n & 0xff; // Number of free blocks as line number + *p++ = (n >> 8) & 0xff; + + *p++ = 'B'; + *p++ = 'L'; + *p++ = 'O'; + *p++ = 'C'; + *p++ = 'K'; + *p++ = 'S'; + *p++ = ' '; + *p++ = 'F'; + *p++ = 'R'; + *p++ = 'E'; + *p++ = 'E'; + *p++ = '.'; + + p = q; + *p++ = 0; + *p++ = 0; + *p++ = 0; + + buf_len[0] = p - chan_buf[0]; + + return ST_OK; +} + + +/* + * Open channel for direct buffer access + */ + +uint8 D64Drive::open_direct(int channel, char *filename) +{ + int buf = -1; + + if (filename[1] == 0) + buf = alloc_buffer(-1); + else + if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0)) + buf = alloc_buffer(filename[1] - '0'); + + if (buf == -1) { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + // The buffers are in the 1541 RAM at $300 and are 256 bytes each + chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8); + chan_mode[channel] = CHMOD_DIRECT; + chan_buf_num[channel] = buf; + + // Store actual buffer number in buffer + *chan_buf[channel] = buf + '0'; + buf_len[channel] = 1; + + return ST_OK; +} + + +/* + * Close channel + */ + +uint8 D64Drive::Close(int channel) +{ + if (channel == 15) { + close_all_channels(); + return ST_OK; + } + + switch (chan_mode[channel]) { + case CHMOD_FREE: + break; + + case CHMOD_DIRECT: + free_buffer(chan_buf_num[channel]); + chan_buf[channel] = NULL; + chan_mode[channel] = CHMOD_FREE; + break; + + default: + delete[] chan_buf[channel]; + chan_buf[channel] = NULL; + chan_mode[channel] = CHMOD_FREE; + break; + } + + return ST_OK; +} + + +/* + * Close all channels + */ + +void D64Drive::close_all_channels() +{ + for (int i=0; i<15; i++) + Close(i); + + cmd_len = 0; +} + + +/* + * Read from channel + */ + +uint8 D64Drive::Read(int channel, uint8 *byte) +{ + switch (chan_mode[channel]) { + case CHMOD_COMMAND: + *byte = *error_ptr++; + if (--error_len) + return ST_OK; + else { + set_error(ERR_OK); + return ST_EOF; + } + break; + + case CHMOD_FILE: + // Read next block if necessary + if (chan_buf[channel][0] && !buf_len[channel]) { + if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel])) + return ST_READ_TIMEOUT; + buf_ptr[channel] = chan_buf[channel] + 2; + + // Determine block length + buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1; + } + + if (buf_len[channel] > 0) { + *byte = *buf_ptr[channel]++; + if (!--buf_len[channel] && !chan_buf[channel][0]) + return ST_EOF; + else + return ST_OK; + } else + return ST_READ_TIMEOUT; + break; + + case CHMOD_DIRECTORY: + case CHMOD_DIRECT: + if (buf_len[channel] > 0) { + *byte = *buf_ptr[channel]++; + if (--buf_len[channel]) + return ST_OK; + else + return ST_EOF; + } else + return ST_READ_TIMEOUT; + break; + } + return ST_READ_TIMEOUT; +} + + +/* + * Write byte to channel + */ + +uint8 D64Drive::Write(int channel, uint8 byte, bool eoi) +{ + switch (chan_mode[channel]) { + case CHMOD_FREE: + set_error(ERR_FILENOTOPEN); + break; + + case CHMOD_COMMAND: + // Collect characters and execute command on EOI + if (cmd_len >= 40) + return ST_TIMEOUT; + + cmd_buffer[cmd_len++] = byte; + + if (eoi) { + cmd_buffer[cmd_len++] = 0; + cmd_len = 0; + execute_command(cmd_buffer); + } + return ST_OK; + + case CHMOD_DIRECTORY: + set_error(ERR_WRITEFILEOPEN); + break; + } + return ST_TIMEOUT; +} + + +/* + * Execute command string + */ + +void D64Drive::execute_command(char *command) +{ + uint16 adr; + int len; + + switch (command[0]) { + case 'B': + if (command[1] != '-') + set_error(ERR_SYNTAX30); + else + switch (command[2]) { + case 'R': + block_read_cmd(&command[3]); + break; + + case 'P': + buffer_ptr_cmd(&command[3]); + break; + + case 'A': + case 'F': + case 'W': + set_error(ERR_WRITEPROTECT); + break; + + default: + set_error(ERR_SYNTAX30); + break; + } + break; + + case 'M': + if (command[1] != '-') + set_error(ERR_SYNTAX30); + else + switch (command[2]) { + case 'R': + adr = ((uint8)command[4] << 8) | ((uint8)command[3]); + error_ptr = (char *)(ram + (adr & 0x07ff)); + if (!(error_len = (uint8)command[5])) + error_len = 1; + break; + + case 'W': + adr = ((uint8)command[4] << 8) | ((uint8)command[3]); + len = (uint8)command[5]; + for (int i=0; i= 0x30 && *cmd < 0x40) { + i *= 10; + i += *cmd++ & 0x0f; + } + *arg1 = i & 0xff; + + while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++; + if (!*cmd) return false; + + i = 0; + while (*cmd >= 0x30 && *cmd < 0x40) { + i *= 10; + i += *cmd++ & 0x0f; + } + *arg2 = i & 0xff; + + while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++; + if (!*cmd) return false; + + i = 0; + while (*cmd >= 0x30 && *cmd < 0x40) { + i *= 10; + i += *cmd++ & 0x0f; + } + *arg3 = i & 0xff; + + while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++; + if (!*cmd) return false; + + i = 0; + while (*cmd >= 0x30 && *cmd < 0x40) { + i *= 10; + i += *cmd++ & 0x0f; + } + *arg4 = i & 0xff; + + return true; +} + + +/* + * Execute 'G' command + */ + +void D64Drive::chd64_cmd(char *d64name) +{ + char str[NAMEBUF_LENGTH]; + char *p = str; + + // Convert .d64 file name + for (int i=0; i Desired buffer number or -1 + * <- Allocated buffer number or -1 + */ + +int D64Drive::alloc_buffer(int want) +{ + if (want == -1) { + for (want=3; want>=0; want--) + if (buf_free[want]) { + buf_free[want] = false; + return want; + } + return -1; + } + + if (want < 4) + if (buf_free[want]) { + buf_free[want] = false; + return want; + } else + return -1; + else + return -1; +} + + +/* + * Free floppy buffer + */ + +void D64Drive::free_buffer(int buf) +{ + buf_free[buf] = true; +} + + +/* + * Read sector (256 bytes) + * true: success, false: error + */ + +bool D64Drive::read_sector(int track, int sector, uint8 *buffer) +{ + int offset; + + // Convert track/sector to byte offset in file + if ((offset = offset_from_ts(track, sector)) < 0) { + set_error(ERR_ILLEGALTS); + return false; + } + + if (the_file == NULL) { + set_error(ERR_NOTREADY); + return false; + } + +#ifdef AMIGA + if (offset != ftell(the_file)) + fseek(the_file, offset + image_header, SEEK_SET); +#else + fseek(the_file, offset + image_header, SEEK_SET); +#endif + fread(buffer, 256, 1, the_file); + return true; +} + + +/* + * Convert track/sector to offset + */ + +const int num_sectors[41] = { + 0, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 19,19,19,19,19,19,19, + 18,18,18,18,18,18, + 17,17,17,17,17, + 17,17,17,17,17 // Tracks 36..40 +}; + +const int sector_offset[41] = { + 0, + 0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336, + 357,376,395,414,433,452,471, + 490,508,526,544,562,580, + 598,615,632,649,666, + 683,700,717,734,751 // Tracks 36..40 +}; + +int D64Drive::offset_from_ts(int track, int sector) +{ + if ((track < 1) || (track > NUM_TRACKS) || + (sector < 0) || (sector >= num_sectors[track])) + return -1; + + return (sector_offset[track] + sector) << 8; +} + + +/* + * Conversion PETSCII->ASCII + */ + +uint8 D64Drive::conv_from_64(uint8 c, bool map_slash) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + if ((c >= 0xc1) && (c <= 0xda)) + return c ^ 0x80; + if ((c == '/') && map_slash && ThePrefs.MapSlash) + return '\\'; + return c; +} diff --git a/Src/1541d64.h b/Src/1541d64.h new file mode 100644 index 0000000..745623f --- /dev/null +++ b/Src/1541d64.h @@ -0,0 +1,108 @@ +/* + * 1541d64.h - 1541 emulation in .d64 file + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _1541D64_H +#define _1541D64_H + +#include "IEC.h" + + +// BAM structure +typedef struct { + uint8 dir_track; // Track... + uint8 dir_sector; // ...and sector of first directory block + int8 fmt_type; // Format type + int8 pad0; + uint8 bitmap[4*35]; // Sector allocation + uint8 disk_name[18]; // Disk name + uint8 id[2]; // Disk ID + int8 pad1; + uint8 fmt_char[2]; // Format characters + int8 pad2[4]; + int8 pad3[85]; +} BAM; + +// Directory entry structure +typedef struct { + uint8 type; // File type + uint8 track; // Track... + uint8 sector; // ...and sector of first data block + uint8 name[16]; // File name + uint8 side_track; // Track... + uint8 side_sector; // ...and sector of first side sector + uint8 rec_len; // Record length + int8 pad0[4]; + uint8 ovr_track; // Track... + uint8 ovr_sector; // ...and sector on overwrite + uint8 num_blocks_l; // Number of blocks, LSB + uint8 num_blocks_h; // Number of blocks, MSB + int8 pad1[2]; +} DirEntry; + +// Directory block structure +typedef struct { + uint8 padding[2]; // Keep DirEntry word-aligned + uint8 next_track; + uint8 next_sector; + DirEntry entry[8]; +} Directory; + + +class D64Drive : public Drive { +public: + D64Drive(IEC *iec, char *filepath); + virtual ~D64Drive(); + virtual uint8 Open(int channel, char *filename); + virtual uint8 Close(int channel); + virtual uint8 Read(int channel, uint8 *byte); + virtual uint8 Write(int channel, uint8 byte, bool eoi); + virtual void Reset(void); + +private: + void open_close_d64_file(char *d64name); + uint8 open_file(int channel, char *filename); + void convert_filename(char *srcname, char *destname, int *filemode, int *filetype); + bool find_file(char *filename, int *track, int *sector); + uint8 open_file_ts(int channel, int track, int sector); + uint8 open_directory(char *pattern); + uint8 open_direct(int channel, char *filename); + void close_all_channels(); + void execute_command(char *command); + void block_read_cmd(char *command); + void buffer_ptr_cmd(char *command); + bool parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4); + void chd64_cmd(char *d64name); + int alloc_buffer(int want); + void free_buffer(int buf); + bool read_sector(int track, int sector, uint8 *buffer); + int offset_from_ts(int track, int sector); + uint8 conv_from_64(uint8 c, bool map_slash); + + char orig_d64_name[256]; // Original path of .d64 file + + FILE *the_file; // File pointer for .d64 file + + uint8 *ram; // 2KB 1541 RAM + BAM *bam; // Pointer to BAM + Directory dir; // Buffer for directory blocks + + int chan_mode[16]; // Channel mode + int chan_buf_num[16]; // Buffer number of channel (for direct access channels) + uint8 *chan_buf[16]; // Pointer to buffer + uint8 *buf_ptr[16]; // Pointer in buffer + int buf_len[16]; // Remaining bytes in buffer + + bool buf_free[4]; // Buffer 0..3 free? + + char cmd_buffer[44]; // Buffer for incoming command strings + int cmd_len; // Length of received command + + int image_header; // Length of .d64 file header + + uint8 error_info[683]; // Sector error information (1 byte/sector) +}; + +#endif diff --git a/Src/1541fs.cpp b/Src/1541fs.cpp new file mode 100644 index 0000000..c6e42a1 --- /dev/null +++ b/Src/1541fs.cpp @@ -0,0 +1,734 @@ +/* + * 1541fs.cpp - 1541 emulation in host file system + * + * Frodo (C) 1994-1997,2002 Christian Bauer + + * + * Notes: + * ------ + * + * - If the directory is opened (file name "$"), a temporary file + * with the structure of a 1541 directory file is created and + * opened. It can then be accessed in the same way as all other + * files. + * + * Incompatibilities: + * ------------------ + * + * - No "raw" directory reading + * - No relative/sequential/user files + * - Only "I" and "UJ" commands implemented + */ + +#include "sysdeps.h" + +#include "1541fs.h" +#include "IEC.h" +#include "main.h" +#include "Prefs.h" + +#ifdef __riscos__ +#include "ROlib.h" +#endif + + +// Access modes +enum { + FMODE_READ, FMODE_WRITE, FMODE_APPEND +}; + +// File types +enum { + FTYPE_PRG, FTYPE_SEQ +}; + +// Prototypes +static bool match(char *p, char *n); + + +/* + * Constructor: Prepare emulation + */ + +FSDrive::FSDrive(IEC *iec, char *path) : Drive(iec) +{ + strcpy(orig_dir_path, path); + dir_path[0] = 0; + + if (change_dir(orig_dir_path)) { + for (int i=0; i<16; i++) + file[i] = NULL; + + Reset(); + + Ready = true; + } +} + + +/* + * Destructor + */ + +FSDrive::~FSDrive() +{ + if (Ready) { + close_all_channels(); + Ready = false; + } +} + + +/* + * Change emulation directory + */ + +bool FSDrive::change_dir(char *dirpath) +{ +#ifndef __riscos__ + DIR *dir; + + if ((dir = opendir(dirpath)) != NULL) { + closedir(dir); + strcpy(dir_path, dirpath); + strncpy(dir_title, dir_path, 16); + return true; + } else + return false; +#else + int Info[4]; + + if ((ReadCatalogueInfo(dirpath,Info) & 2) != 0) // Directory or image file + { + strcpy(dir_path, dirpath); + strncpy(dir_title, dir_path, 16); + return true; + } + else + { + return false; + } +#endif +} + + +/* + * Open channel + */ + +uint8 FSDrive::Open(int channel, char *filename) +{ + set_error(ERR_OK); + + // Channel 15: Execute file name as command + if (channel == 15) { + execute_command(filename); + return ST_OK; + } + + // Close previous file if still open + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + if (filename[0] == '$') + return open_directory(channel, filename+1); + + if (filename[0] == '#') { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + return open_file(channel, filename); +} + + +/* + * Open file + */ + +uint8 FSDrive::open_file(int channel, char *filename) +{ + char plainname[NAMEBUF_LENGTH]; + int filemode = FMODE_READ; + int filetype = FTYPE_PRG; + bool wildflag = false; + char *mode = "rb"; + + convert_filename(filename, plainname, &filemode, &filetype, &wildflag); + + // Channel 0 is READ PRG, channel 1 is WRITE PRG + if (!channel) { + filemode = FMODE_READ; + filetype = FTYPE_PRG; + } + if (channel == 1) { + filemode = FMODE_WRITE; + filetype = FTYPE_PRG; + } + + // Wildcards are only allowed on reading + if (wildflag) { + if (filemode != FMODE_READ) { + set_error(ERR_SYNTAX33); + return ST_OK; + } + find_first_file(plainname); + } + + // Select fopen() mode according to file mode + switch (filemode) { + case FMODE_READ: + mode = "rb"; + break; + case FMODE_WRITE: + mode = "wb"; + break; + case FMODE_APPEND: + mode = "ab"; + break; + } + + // Open file +#ifndef __riscos__ + if (chdir(dir_path)) + set_error(ERR_NOTREADY); + else if ((file[channel] = fopen(plainname, mode)) != NULL) { + if (filemode == FMODE_READ) // Read and buffer first byte + read_char[channel] = fgetc(file[channel]); + } else + set_error(ERR_FILENOTFOUND); + chdir(AppDirPath); +#else + { + char fullname[NAMEBUF_LENGTH]; + + // On RISC OS make a full filename + sprintf(fullname,"%s.%s",dir_path,plainname); + if ((file[channel] = fopen(fullname, mode)) != NULL) + { + if (filemode == FMODE_READ) + { + read_char[channel] = fgetc(file[channel]); + } + } + else + { + set_error(ERR_FILENOTFOUND); + } + } +#endif + + return ST_OK; +} + + +/* + * Analyze file name, get access mode and type + */ + +void FSDrive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype, bool *wildflag) +{ + char *p, *q; + int i; + + // Search for ':', p points to first character after ':' + if ((p = strchr(srcname, ':')) != NULL) + p++; + else + p = srcname; + + // Convert char set of the remaining string -> destname + q = destname; + for (i=0; id_name) || 0 == strcmp("..", de->d_name))) + de = readdir(dir); + + while (de) { + + // Match found? Then copy real file name + if (match(name, de->d_name)) { + strncpy(name, de->d_name, NAMEBUF_LENGTH); + closedir(dir); + return; + } + + // Get next directory entry + de = readdir(dir); + } + + closedir(dir); +#else + dir_env de; + char Buffer[NAMEBUF_LENGTH]; + + de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = name; + do + { + de.readno = 1; + if (ReadDirName(dir_path,Buffer,&de) != NULL) {de.offset = -1;} + else if (de.offset != -1) + { + if (match(name,Buffer)) + { + strncpy(name, Buffer, NAMEBUF_LENGTH); + return; + } + } + } + while (de.offset != -1); +#endif +} + + +/* + * Open directory, create temporary file + */ + +uint8 FSDrive::open_directory(int channel, char *filename) +{ + char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; + char str[NAMEBUF_LENGTH]; + char pattern[NAMEBUF_LENGTH]; + char *p, *q; + int i; + int filemode; + int filetype; + bool wildflag; + +#ifndef __riscos__ + DIR *dir; + struct dirent *de; + struct stat statbuf; + + // Special treatment for "$0" + if (filename[0] == '0' && filename[1] == 0) + filename += 1; + + // Convert filename ('$' already stripped), filemode/type are ignored + convert_filename(filename, pattern, &filemode, &filetype, &wildflag); + + // Open directory for reading and skip '.' and '..' + if ((dir = opendir(dir_path)) == NULL) { + set_error(ERR_NOTREADY); + return ST_OK; + } + de = readdir(dir); + while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name))) + de = readdir(dir); + + // Create temporary file + if ((file[channel] = tmpfile()) == NULL) { + closedir(dir); + return ST_OK; + } + + // Create directory title + p = &buf[8]; + for (i=0; i<16 && dir_title[i]; i++) + *p++ = conv_to_64(dir_title[i], false); + fwrite(buf, 1, 32, file[channel]); + + // Create and write one line for every directory entry + while (de) { + + // Include only files matching the pattern + if (match(pattern, de->d_name)) { + + // Get file statistics + chdir(dir_path); + stat(de->d_name, &statbuf); + chdir(AppDirPath); + + // Clear line with spaces and terminate with null byte + memset(buf, ' ', 31); + buf[31] = 0; + + p = buf; + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + + // Calculate size in blocks (254 bytes each) + i = (statbuf.st_size + 254) / 254; + *p++ = i & 0xff; + *p++ = (i >> 8) & 0xff; + + p++; + if (i < 10) p++; // Less than 10: add one space + if (i < 100) p++; // Less than 100: add another space + + // Convert and insert file name + strcpy(str, de->d_name); + *p++ = '\"'; + q = p; + for (i=0; i<16 && str[i]; i++) + *q++ = conv_to_64(str[i], true); + *q++ = '\"'; + p += 18; + + // File type + if (S_ISDIR(statbuf.st_mode)) { + *p++ = 'D'; + *p++ = 'I'; + *p++ = 'R'; + } else { + *p++ = 'P'; + *p++ = 'R'; + *p++ = 'G'; + } + + // Write line + fwrite(buf, 1, 32, file[channel]); + } + + // Get next directory entry + de = readdir(dir); + } +#else + dir_full_info di; + dir_env de; + + // Much of this is very similar to the original + if ((filename[0] == '0') && (filename[1] == 0)) {filename++;} + // Concatenate dir_path and pattern in buffer pattern ==> read subdirs! + strcpy(pattern,dir_path); + convert_filename(filename, pattern + strlen(pattern), &filemode, &filetype, &wildflag); + + // We don't use tmpfile() -- problems involved! + DeleteFile(RO_TEMPFILE); // first delete it, if it exists + if ((file[channel] = fopen(RO_TEMPFILE,"wb+")) == NULL) + { + return(ST_OK); + } + de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = filename; + + // Create directory title - copied from above + p = &buf[8]; + for (i=0; i<16 && dir_title[i]; i++) + *p++ = conv_to_64(dir_title[i], false); + fwrite(buf, 1, 32, file[channel]); + + do + { + de.readno = 1; + if (ReadDirNameInfo(pattern,&di,&de) != NULL) {de.offset = -1;} + else if (de.offset != -1) // don't have to check for match here + { + memset(buf,' ',31); buf[31] = 0; // most of this: see above + p = buf; *p++ = 0x01; *p++ = 0x01; + i = (di.length + 254) / 254; *p++ = i & 0xff; *p++ = (i>>8) & 0xff; + p++; + if (i < 10) {*p++ = ' ';} + if (i < 100) {*p++ = ' ';} + strcpy(str, di.name); + *p++ = '\"'; q = p; + for (i=0; (i<16 && str[i]); i++) + { + *q++ = conv_to_64(str[i], true); + } + *q++ = '\"'; p += 18; + if ((di.otype & 2) == 0) + { + *p++ = 'P'; *p++ = 'R'; *p++ = 'G'; + } + else + { + *p++ = 'D'; *p++ = 'I'; *p++ = 'R'; + } + fwrite(buf, 1, 32, file[channel]); + } + } + while (de.offset != -1); +#endif + + // Final line + fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]); + + // Rewind file for reading and read first byte + rewind(file[channel]); + read_char[channel] = fgetc(file[channel]); + +#ifndef __riscos + // Close directory + closedir(dir); +#endif + + return ST_OK; +} + + +/* + * Close channel + */ + +uint8 FSDrive::Close(int channel) +{ + if (channel == 15) { + close_all_channels(); + return ST_OK; + } + + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + return ST_OK; +} + + +/* + * Close all channels + */ + +void FSDrive::close_all_channels(void) +{ + for (int i=0; i<15; i++) + Close(i); + + cmd_len = 0; +} + + +/* + * Read from channel + */ + +uint8 FSDrive::Read(int channel, uint8 *byte) +{ + int c; + + // Channel 15: Error channel + if (channel == 15) { + *byte = *error_ptr++; + + if (*byte != '\r') + return ST_OK; + else { // End of message + set_error(ERR_OK); + return ST_EOF; + } + } + + if (!file[channel]) return ST_READ_TIMEOUT; + + // Read one byte + *byte = read_char[channel]; + c = fgetc(file[channel]); + if (c == EOF) + return ST_EOF; + else { + read_char[channel] = c; + return ST_OK; + } +} + + +/* + * Write to channel + */ + +uint8 FSDrive::Write(int channel, uint8 byte, bool eoi) +{ + // Channel 15: Collect chars and execute command on EOI + if (channel == 15) { + if (cmd_len >= 40) + return ST_TIMEOUT; + + cmd_buffer[cmd_len++] = byte; + + if (eoi) { + cmd_buffer[cmd_len] = 0; + cmd_len = 0; + execute_command(cmd_buffer); + } + return ST_OK; + } + + if (!file[channel]) { + set_error(ERR_FILENOTOPEN); + return ST_TIMEOUT; + } + + if (fputc(byte, file[channel]) == EOF) { + set_error(ERR_WRITEERROR); + return ST_TIMEOUT; + } + + return ST_OK; +} + + +/* + * Execute command string + */ + +void FSDrive::execute_command(char *command) +{ + switch (command[0]) { + case 'I': + close_all_channels(); + set_error(ERR_OK); + break; + + case 'U': + if ((command[1] & 0x0f) == 0x0a) { + Reset(); + } else + set_error(ERR_SYNTAX30); + break; + + case 'G': + if (command[1] != ':') + set_error(ERR_SYNTAX30); + else + chdir_cmd(&command[2]); + break; + + default: + set_error(ERR_SYNTAX30); + } +} + + +/* + * Execute 'G' command + */ + +void FSDrive::chdir_cmd(char *dirpath) +{ + char str[NAMEBUF_LENGTH]; + char *p = str; + + close_all_channels(); + + // G:. resets the directory path to its original setting + if (dirpath[0] == '.' && dirpath[1] == 0) { + change_dir(orig_dir_path); + } else { + + // Convert directory name + for (int i=0; iASCII + */ + +uint8 FSDrive::conv_from_64(uint8 c, bool map_slash) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + if ((c >= 0xc1) && (c <= 0xda)) + return c ^ 0x80; + if ((c == '/') && map_slash && ThePrefs.MapSlash) +#ifdef __riscos__ + return '.'; // directory separator is '.' in RO + if (c == '.') {return('_');} // convert dot to underscore +#else + return '\\'; +#endif + return c; +} + + +/* + * Conversion ASCII->PETSCII + */ + +uint8 FSDrive::conv_to_64(uint8 c, bool map_slash) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; +#ifdef __riscos__ + if ((c == '.') && map_slash && ThePrefs.MapSlash) +#else + if ((c == '\\') && map_slash && ThePrefs.MapSlash) +#endif + return '/'; +#ifdef __riscos__ + if (c == '_') {return('.');} // convert underscore to dot +#endif + return c; +} diff --git a/Src/1541fs.h b/Src/1541fs.h new file mode 100644 index 0000000..1e975f0 --- /dev/null +++ b/Src/1541fs.h @@ -0,0 +1,46 @@ +/* + * 1541fs.h - 1541 emulation in host file system + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _1541FS_H +#define _1541FS_H + +#include "IEC.h" + + +class FSDrive : public Drive { +public: + FSDrive(IEC *iec, char *path); + virtual ~FSDrive(); + virtual uint8 Open(int channel, char *filename); + virtual uint8 Close(int channel); + virtual uint8 Read(int channel, uint8 *byte); + virtual uint8 Write(int channel, uint8 byte, bool eoi); + virtual void Reset(void); + +private: + bool change_dir(char *dirpath); + uint8 open_file(int channel, char *filename); + uint8 open_directory(int channel, char *filename); + void convert_filename(char *srcname, char *destname, int *filemode, int *filetype, bool *wildflag); + void find_first_file(char *name); + void close_all_channels(void); + void execute_command(char *command); + void chdir_cmd(char *dirpath); + uint8 conv_from_64(uint8 c, bool map_slash); + uint8 conv_to_64(uint8 c, bool map_slash); + + char dir_path[256]; // Path to directory + char orig_dir_path[256]; // Original directory path + char dir_title[16]; // Directory title + FILE *file[16]; // File pointers for each of the 16 channels + + char cmd_buffer[44]; // Buffer for incoming command strings + int cmd_len; // Length of received command + + uint8 read_char[16]; // Buffers for one-byte read-ahead +}; + +#endif diff --git a/Src/1541job.cpp b/Src/1541job.cpp new file mode 100644 index 0000000..d1b0f96 --- /dev/null +++ b/Src/1541job.cpp @@ -0,0 +1,462 @@ +/* + * 1541job.cpp - Emulation of 1541 GCR disk reading/writing + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - This is only used for processor-level 1541 emulation. + * It simulates the 1541 disk controller hardware (R/W head, + * GCR reading/writing). + * - The preferences settings for drive 8 are used to + * specify the .d64 file + * + * Incompatibilities: + * ------------------ + * + * - No GCR writing possible (WriteSector is a ROM patch) + * - Programs depending on the exact timing of head movement/disk + * rotation don't work + * - The .d64 error info is unused + */ + +#include "sysdeps.h" + +#include "1541job.h" +#include "CPU1541.h" +#include "Prefs.h" + + +// Number of tracks/sectors +const int NUM_TRACKS = 35; +const int NUM_SECTORS = 683; + +// Size of GCR encoded data +const int GCR_SECTOR_SIZE = 1+10+9+1+325+8; // SYNC Header Gap SYNC Data Gap (should be 5 SYNC bytes each) +const int GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has 21 sectors +const int GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS; + +// Job return codes +const int RET_OK = 1; // No error +const int RET_NOT_FOUND = 2; // Block not found +const int RET_NOT_READY = 15; // Drive not ready + + +// Number of sectors of each track +const int num_sectors[36] = { + 0, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 19,19,19,19,19,19,19, + 18,18,18,18,18,18, + 17,17,17,17,17 +}; + +// Sector offset of start of track in .d64 file +const int sector_offset[36] = { + 0, + 0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336, + 357,376,395,414,433,452,471, + 490,508,526,544,562,580, + 598,615,632,649,666 +}; + + +/* + * Constructor: Open .d64 file if processor-level 1541 + * emulation is enabled + */ + +Job1541::Job1541(uint8 *ram1541) : ram(ram1541) +{ + the_file = NULL; + + gcr_data = gcr_ptr = gcr_track_start = new uint8[GCR_DISK_SIZE]; + gcr_track_end = gcr_track_start + GCR_TRACK_SIZE; + current_halftrack = 2; + + disk_changed = true; + + if (ThePrefs.Emul1541Proc) + open_d64_file(ThePrefs.DrivePath[0]); +} + + +/* + * Destructor: Close .d64 file + */ + +Job1541::~Job1541() +{ + close_d64_file(); + delete[] gcr_data; +} + + +/* + * Preferences may have changed + */ + +void Job1541::NewPrefs(Prefs *prefs) +{ + // 1541 emulation turned off? + if (!prefs->Emul1541Proc) + close_d64_file(); + + // 1541 emulation turned on? + else if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc) + open_d64_file(prefs->DrivePath[0]); + + // .d64 file name changed? + else if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0])) { + close_d64_file(); + open_d64_file(prefs->DrivePath[0]); + disk_changed = true; + } +} + + +/* + * Open .d64 file + */ + +void Job1541::open_d64_file(char *filepath) +{ + long size; + uint8 magic[4]; + uint8 bam[256]; + + // Clear GCR buffer + memset(gcr_data, 0x55, GCR_DISK_SIZE); + + // Try opening the file for reading/writing first, then for reading only + write_protected = false; + the_file = fopen(filepath, "rb+"); + if (the_file == NULL) { + write_protected = true; + the_file = fopen(filepath, "rb"); + } + if (the_file != NULL) { + + // Check length + fseek(the_file, 0, SEEK_END); + if ((size = ftell(the_file)) < NUM_SECTORS * 256) { + fclose(the_file); + the_file = NULL; + return; + } + + // x64 image? + fseek(the_file, 0, SEEK_SET); + fread(&magic, 4, 1, the_file); + if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64) + image_header = 64; + else + image_header = 0; + + // Preset error info (all sectors no error) + memset(error_info, 1, NUM_SECTORS); + + // Load sector error info from .d64 file, if present + if (!image_header && size == NUM_SECTORS * 257) { + fseek(the_file, NUM_SECTORS * 256, SEEK_SET); + fread(&error_info, NUM_SECTORS, 1, the_file); + }; + + // Read BAM and get ID + read_sector(18, 0, bam); + id1 = bam[162]; + id2 = bam[163]; + + // Create GCR encoded disk data from image + disk2gcr(); + } +} + + +/* + * Close .d64 file + */ + +void Job1541::close_d64_file(void) +{ + if (the_file != NULL) { + fclose(the_file); + the_file = NULL; + } +} + + +/* + * Write sector to disk (1541 ROM patch) + */ + +void Job1541::WriteSector(void) +{ + int track = ram[0x18]; + int sector = ram[0x19]; + uint16 buf = ram[0x30] | (ram[0x31] << 8); + + if (buf <= 0x0700) + if (write_sector(track, sector, ram + buf)) + sector2gcr(track, sector); +} + + +/* + * Format one track (1541 ROM patch) + */ + +void Job1541::FormatTrack(void) +{ + int track = ram[0x51]; + + // Get new ID + uint8 bufnum = ram[0x3d]; + id1 = ram[0x12 + bufnum]; + id2 = ram[0x13 + bufnum]; + + // Create empty block + uint8 buf[256]; + memset(buf, 1, 256); + buf[0] = 0x4b; + + // Write block to all sectors on track + for(int sector=0; sector GCR + * true: success, false: error + */ + +bool Job1541::write_sector(int track, int sector, uint8 *buffer) +{ + int offset; + + // Convert track/sector to byte offset in file + if ((offset = offset_from_ts(track, sector)) < 0) + return false; + +#ifdef AMIGA + if (offset != ftell(the_file)) + fseek(the_file, offset + image_header, SEEK_SET); +#else + fseek(the_file, offset + image_header, SEEK_SET); +#endif + fwrite(buffer, 256, 1, the_file); + return true; +} + + +/* + * Convert track/sector to offset + */ + +int Job1541::secnum_from_ts(int track, int sector) +{ + return sector_offset[track] + sector; +} + +int Job1541::offset_from_ts(int track, int sector) +{ + if ((track < 1) || (track > NUM_TRACKS) || + (sector < 0) || (sector >= num_sectors[track])) + return -1; + + return (sector_offset[track] + sector) << 8; +} + + +/* + * Convert 4 bytes to 5 GCR encoded bytes + */ + +const uint16 gcr_table[16] = { + 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, + 0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15 +}; + +void Job1541::gcr_conv4(uint8 *from, uint8 *to) +{ + uint16 g; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ = g >> 2; + *to = (g << 6) & 0xc0; + from++; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ |= (g >> 4) & 0x3f; + *to = (g << 4) & 0xf0; + from++; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ |= (g >> 6) & 0x0f; + *to = (g << 2) & 0xfc; + from++; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ |= (g >> 8) & 0x03; + *to = g; +} + + +/* + * Create GCR encoded disk data from image + */ + +void Job1541::sector2gcr(int track, int sector) +{ + uint8 block[256]; + uint8 buf[4]; + uint8 *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE; + + read_sector(track, sector, block); + + // Create GCR header + *p++ = 0xff; // SYNC + buf[0] = 0x08; // Header mark + buf[1] = sector ^ track ^ id2 ^ id1; // Checksum + buf[2] = sector; + buf[3] = track; + gcr_conv4(buf, p); + buf[0] = id2; + buf[1] = id1; + buf[2] = 0x0f; + buf[3] = 0x0f; + gcr_conv4(buf, p+5); + p += 10; + memset(p, 0x55, 9); // Gap + p += 9; + + // Create GCR data + uint8 sum; + *p++ = 0xff; // SYNC + buf[0] = 0x07; // Data mark + sum = buf[1] = block[0]; + sum ^= buf[2] = block[1]; + sum ^= buf[3] = block[2]; + gcr_conv4(buf, p); + p += 5; + for (int i=3; i<255; i+=4) { + sum ^= buf[0] = block[i]; + sum ^= buf[1] = block[i+1]; + sum ^= buf[2] = block[i+2]; + sum ^= buf[3] = block[i+3]; + gcr_conv4(buf, p); + p += 5; + } + sum ^= buf[0] = block[255]; + buf[1] = sum; // Checksum + buf[2] = 0; + buf[3] = 0; + gcr_conv4(buf, p); + p += 5; + memset(p, 0x55, 8); // Gap +} + +void Job1541::disk2gcr(void) +{ + // Convert all tracks and sectors + for (int track=1; track<=NUM_TRACKS; track++) + for(int sector=0; sector> 1) - 1) * GCR_TRACK_SIZE; + gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; +} + + +/* + * Move R/W head in (higher track numbers) + */ + +void Job1541::MoveHeadIn(void) +{ + if (current_halftrack == NUM_TRACKS*2) + return; + current_halftrack++; +#ifndef __riscos__ + printf("Head move %d\n", current_halftrack); +#endif + gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; + gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; +} + + +/* + * Get state + */ + +void Job1541::GetState(Job1541State *state) +{ + state->current_halftrack = current_halftrack; + state->gcr_ptr = gcr_ptr - gcr_data; + state->write_protected = write_protected; + state->disk_changed = disk_changed; +} + + +/* + * Set state + */ + +void Job1541::SetState(Job1541State *state) +{ + current_halftrack = state->current_halftrack; + gcr_ptr = gcr_data + state->gcr_ptr; + gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; + gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; + write_protected = state->write_protected; + disk_changed = state->disk_changed; +} diff --git a/Src/1541job.h b/Src/1541job.h new file mode 100644 index 0000000..cff55f1 --- /dev/null +++ b/Src/1541job.h @@ -0,0 +1,112 @@ +/* + * 1541job.h - Emulation of 1541 GCR disk reading/writing + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _1541JOB_H +#define _1541JOB_H + + +class MOS6502_1541; +class Prefs; +struct Job1541State; + +class Job1541 { +public: + Job1541(uint8 *ram1541); + ~Job1541(); + + void GetState(Job1541State *state); + void SetState(Job1541State *state); + void NewPrefs(Prefs *prefs); + void MoveHeadOut(void); + void MoveHeadIn(void); + bool SyncFound(void); + uint8 ReadGCRByte(void); + uint8 WPState(void); + void WriteSector(void); + void FormatTrack(void); + +private: + void open_d64_file(char *filepath); + void close_d64_file(void); + bool read_sector(int track, int sector, uint8 *buffer); + bool write_sector(int track, int sector, uint8 *buffer); + void format_disk(void); + int secnum_from_ts(int track, int sector); + int offset_from_ts(int track, int sector); + void gcr_conv4(uint8 *from, uint8 *to); + void sector2gcr(int track, int sector); + void disk2gcr(void); + + uint8 *ram; // Pointer to 1541 RAM + FILE *the_file; // File pointer for .d64 file + int image_header; // Length of .d64/.x64 file header + + uint8 id1, id2; // ID of disk + uint8 error_info[683]; // Sector error information (1 byte/sector) + + uint8 *gcr_data; // Pointer to GCR encoded disk data + uint8 *gcr_ptr; // Pointer to GCR data under R/W head + uint8 *gcr_track_start; // Pointer to start of GCR data of current track + uint8 *gcr_track_end; // Pointer to end of GCR data of current track + int current_halftrack; // Current halftrack number (2..70) + + bool write_protected; // Flag: Disk write-protected + bool disk_changed; // Flag: Disk changed (WP sensor strobe control) +}; + +// 1541 GCR state +struct Job1541State { + int current_halftrack; + uint32 gcr_ptr; + bool write_protected; + bool disk_changed; +}; + + +/* + * Check if R/W head is over SYNC + */ + +inline bool Job1541::SyncFound(void) +{ + if (*gcr_ptr == 0xff) + return true; + else { + gcr_ptr++; // Rotate disk + if (gcr_ptr == gcr_track_end) + gcr_ptr = gcr_track_start; + return false; + } +} + + +/* + * Read one GCR byte from disk + */ + +inline uint8 Job1541::ReadGCRByte(void) +{ + uint8 byte = *gcr_ptr++; // Rotate disk + if (gcr_ptr == gcr_track_end) + gcr_ptr = gcr_track_start; + return byte; +} + + +/* + * Return state of write protect sensor + */ + +inline uint8 Job1541::WPState(void) +{ + if (disk_changed) { // Disk change -> WP sensor strobe + disk_changed = false; + return write_protected ? 0x10 : 0; + } else + return write_protected ? 0 : 0x10; +} + +#endif diff --git a/Src/1541t64.cpp b/Src/1541t64.cpp new file mode 100644 index 0000000..06785dc --- /dev/null +++ b/Src/1541t64.cpp @@ -0,0 +1,715 @@ +/* + * 1541t64.cpp - 1541 emulation in .t64/LYNX file + * + * Frodo (C) 1994-1997,2002 Christian Bauer + + * + * Notes: + * ------ + * + * - If any file is opened, the contents of the file in the + * .t64 file are copied into a temporary file which is used + * for reading. This is done to insert the load address. + * - C64 LYNX archives are also handled by these routines + * + * Incompatibilities: + * ------------------ + * + * - Only read accesses possible + * - No "raw" directory reading + * - No relative/sequential/user files + * - Only "I" and "UJ" commands implemented + */ + +#include "sysdeps.h" + +#include "1541t64.h" +#include "IEC.h" +#include "Prefs.h" + + +// Access modes +enum { + FMODE_READ, FMODE_WRITE, FMODE_APPEND +}; + +// File types +enum { + FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL +}; + +// Prototypes +static bool match(char *p, char *n); + + +/* + * Constructor: Prepare emulation + */ + +T64Drive::T64Drive(IEC *iec, char *filepath) : Drive(iec) +{ + the_file = NULL; + file_info = NULL; + + Ready = false; + strcpy(orig_t64_name, filepath); + for (int i=0; i<16; i++) + file[i] = NULL; + + // Open .t64 file + open_close_t64_file(filepath); + if (the_file != NULL) { + Reset(); + Ready = true; + } +} + + +/* + * Destructor + */ + +T64Drive::~T64Drive() +{ + // Close .t64 file + open_close_t64_file(""); + + Ready = false; +} + + +/* + * Open/close the .t64/LYNX file + */ + +void T64Drive::open_close_t64_file(char *t64name) +{ + uint8 buf[64]; + bool parsed_ok = false; + + // Close old .t64, if open + if (the_file != NULL) { + close_all_channels(); + fclose(the_file); + the_file = NULL; + delete[] file_info; + file_info = NULL; + } + + // Open new .t64 file + if (t64name[0]) { + if ((the_file = fopen(t64name, "rb")) != NULL) { + + // Check file ID + fread(&buf, 64, 1, the_file); + if (buf[0] == 0x43 && buf[1] == 0x36 && buf[2] == 0x34) { + is_lynx = false; + parsed_ok = parse_t64_file(); + } else if (buf[0x3c] == 0x4c && buf[0x3d] == 0x59 && buf[0x3e] == 0x4e && buf[0x3f] == 0x58) { + is_lynx = true; + parsed_ok = parse_lynx_file(); + } + + if (!parsed_ok) { + fclose(the_file); + the_file = NULL; + delete[] file_info; + file_info = NULL; + return; + } + } + } +} + + +/* + * Parse .t64 file and construct FileInfo array + */ + +bool T64Drive::parse_t64_file(void) +{ + uint8 buf[32]; + uint8 *buf2; + char *p; + int max, i, j; + + // Read header and get maximum number of files contained + fseek(the_file, 32, SEEK_SET); + fread(&buf, 32, 1, the_file); + max = (buf[3] << 8) | buf[2]; + + memcpy(dir_title, buf+8, 16); + + // Allocate buffer for file records and read them + buf2 = new uint8[max*32]; + fread(buf2, 32, max, the_file); + + // Determine number of files contained + for (i=0, num_files=0; i destname + strncpy(destname, p, NAMEBUF_LENGTH); + + // Search for ',' + p = destname; + while (*p && (*p != ',')) p++; + + // Look for mode parameters seperated by ',' + p = destname; + while ((p = strchr(p, ',')) != NULL) { + + // Cut string after the first ',' + *p++ = 0; + + switch (*p) { + case 'P': + *filetype = FTYPE_PRG; + break; + case 'S': + *filetype = FTYPE_SEQ; + break; + case 'U': + *filetype = FTYPE_USR; + break; + case 'L': + *filetype = FTYPE_REL; + break; + case 'R': + *filemode = FMODE_READ; + break; + case 'W': + *filemode = FMODE_WRITE; + break; + case 'A': + *filemode = FMODE_APPEND; + break; + } + } +} + + +/* + * Find first file matching wildcard pattern + */ + +// Return true if name 'n' matches pattern 'p' +static bool match(char *p, char *n) +{ + if (!*p) // Null pattern matches everything + return true; + + do { + if (*p == '*') // Wildcard '*' matches all following characters + return true; + if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character + return false; + p++; n++; + } while (*p); + + return !(*n); +} + +bool T64Drive::find_first_file(char *name, int type, int *num) +{ + for (int i=0; i> 8) & 0xff; + + p++; + if (i < 10) p++; // Less than 10: add one space + if (i < 100) p++; // Less than 100: add another space + + // Convert and insert file name + strcpy(str, file_info[num].name); + *p++ = '\"'; + q = p; + for (i=0; i<16 && str[i]; i++) + *q++ = str[i]; + *q++ = '\"'; + p += 18; + + // File type + switch (file_info[num].type) { + case FTYPE_PRG: + *p++ = 'P'; + *p++ = 'R'; + *p++ = 'G'; + break; + case FTYPE_SEQ: + *p++ = 'S'; + *p++ = 'E'; + *p++ = 'Q'; + break; + case FTYPE_USR: + *p++ = 'U'; + *p++ = 'S'; + *p++ = 'R'; + break; + case FTYPE_REL: + *p++ = 'R'; + *p++ = 'E'; + *p++ = 'L'; + break; + default: + *p++ = '?'; + *p++ = '?'; + *p++ = '?'; + break; + } + + // Write line + fwrite(buf, 1, 32, file[channel]); + } + } + + // Final line + fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]); + + // Rewind file for reading and read first byte + rewind(file[channel]); + read_char[channel] = fgetc(file[channel]); + + return ST_OK; +} + + +/* + * Close channel + */ + +uint8 T64Drive::Close(int channel) +{ + if (channel == 15) { + close_all_channels(); + return ST_OK; + } + + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + return ST_OK; +} + + +/* + * Close all channels + */ + +void T64Drive::close_all_channels(void) +{ + for (int i=0; i<15; i++) + Close(i); + + cmd_len = 0; +} + + +/* + * Read from channel + */ + +uint8 T64Drive::Read(int channel, uint8 *byte) +{ + int c; + + // Channel 15: Error channel + if (channel == 15) { + *byte = *error_ptr++; + + if (*byte != '\r') + return ST_OK; + else { // End of message + set_error(ERR_OK); + return ST_EOF; + } + } + + if (!file[channel]) return ST_READ_TIMEOUT; + + // Get char from buffer and read next + *byte = read_char[channel]; + c = fgetc(file[channel]); + if (c == EOF) + return ST_EOF; + else { + read_char[channel] = c; + return ST_OK; + } +} + + +/* + * Write to channel + */ + +uint8 T64Drive::Write(int channel, uint8 byte, bool eoi) +{ + // Channel 15: Collect chars and execute command on EOI + if (channel == 15) { + if (cmd_len >= 40) + return ST_TIMEOUT; + + cmd_buffer[cmd_len++] = byte; + + if (eoi) { + cmd_buffer[cmd_len] = 0; + cmd_len = 0; + execute_command(cmd_buffer); + } + return ST_OK; + } + + if (!file[channel]) + set_error(ERR_FILENOTOPEN); + else + set_error(ERR_WRITEPROTECT); + + return ST_TIMEOUT; +} + + +/* + * Execute command string + */ + +void T64Drive::execute_command(char *command) +{ + switch (command[0]) { + case 'I': + close_all_channels(); + set_error(ERR_OK); + break; + + case 'U': + if ((command[1] & 0x0f) == 0x0a) { + Reset(); + } else + set_error(ERR_SYNTAX30); + break; + + case 'G': + if (command[1] != ':') + set_error(ERR_SYNTAX30); + else + cht64_cmd(&command[2]); + break; + + default: + set_error(ERR_SYNTAX30); + } +} + + +/* + * Execute 'G' command + */ + +void T64Drive::cht64_cmd(char *t64name) +{ + char str[NAMEBUF_LENGTH]; + char *p = str; + + // Convert .t64 file name + for (int i=0; iASCII + */ + +uint8 T64Drive::conv_from_64(uint8 c, bool map_slash) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + if ((c >= 0xc1) && (c <= 0xda)) + return c ^ 0x80; + if ((c == '/') && map_slash && ThePrefs.MapSlash) + return '\\'; + return c; +} diff --git a/Src/1541t64.h b/Src/1541t64.h new file mode 100644 index 0000000..4c784a1 --- /dev/null +++ b/Src/1541t64.h @@ -0,0 +1,62 @@ +/* + * 1541t64.h - 1541 emulation in .t64/LYNX file + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _1541T64_H +#define _1541T64_H + +#include "IEC.h" + + +// Information for file inside a .t64 file +typedef struct { + char name[17]; // File name, PETSCII + uint8 type; // File type + uint8 sa_lo, sa_hi; // Start address + int offset; // Offset of first byte in .t64 file + int length; // Length of file +} FileInfo; + + +class T64Drive : public Drive { +public: + T64Drive(IEC *iec, char *filepath); + virtual ~T64Drive(); + virtual uint8 Open(int channel, char *filename); + virtual uint8 Close(int channel); + virtual uint8 Read(int channel, uint8 *byte); + virtual uint8 Write(int channel, uint8 byte, bool eoi); + virtual void Reset(void); + +private: + void open_close_t64_file(char *t64name); + bool parse_t64_file(void); + bool parse_lynx_file(void); + uint8 open_file(int channel, char *filename); + uint8 open_directory(int channel, char *filename); + void convert_filename(char *srcname, char *destname, int *filemode, int *filetype); + bool find_first_file(char *name, int type, int *num); + void close_all_channels(void); + void execute_command(char *command); + void cht64_cmd(char *t64path); + uint8 conv_from_64(uint8 c, bool map_slash); + + FILE *the_file; // File pointer for .t64 file + bool is_lynx; // Flag: .t64 file is really a LYNX archive + + char orig_t64_name[256]; // Original path of .t64 file + char dir_title[16]; // Directory title + FILE *file[16]; // File pointers for each of the 16 channels (all temporary files) + + int num_files; // Number of files in .t64 file and in file_info array + FileInfo *file_info; // Pointer to array of file information structs for each file + + char cmd_buffer[44]; // Buffer for incoming command strings + int cmd_len; // Length of received command + + uint8 read_char[16]; // Buffers for one-byte read-ahead +}; + +#endif diff --git a/Src/AcornGUI.cc b/Src/AcornGUI.cc new file mode 100644 index 0000000..38abdbc --- /dev/null +++ b/Src/AcornGUI.cc @@ -0,0 +1,1774 @@ +/* + * AcornGUI.cc + * + * Frodo's Graphical User Interface for Acorn RISC OS machines (WIMP) + * (C) 1997 Andreas Dehmel + * + */ + + + +#include "sysdeps.h" + +#include "C64.h" +#include "VIC.h" +#include "Display.h" +#include "Prefs.h" +#include "main.h" +#include "ROlib.h" +#include "AcornGUI.h" +#include "Version.h" + + + +#define CUSTOM_SPRITES "FrodoRsrc:Sprites" +#define TEMPLATES_FILE "FrodoRsrc:Templates" +#define WIMP_SCRAP_FILE "" + + +#define FileType_Data 0xffd +#define FileType_Text 0xfff +#define FileType_C64File 0x064 +#define FileType_D64File 0x164 + +#define IconSpriteSize 68 // OS units of an icon sprite (for dragging) +#define EstimatedPrefsSize 1024 // can't say how big it'll be before saving... +#define EstimatedConfSize 256 +#define EstimatedRAMSize 0x10400 +#define EstimatedSnapSize 0x11000 + + + + +// For the scanning of the joystick keys in the SysConfig window +#define IntKey_ScanFrom 3 +// This key gets translated to 0xff, i.e. none (joystick inactive) +#define IntKey_Void 44 + + +#define Key_Return 13 + + + + +// LED number to Icon number translation: +char LEDtoIcon[4] = {Icon_Pane_LED0, Icon_Pane_LED1, Icon_Pane_LED2, Icon_Pane_LED3}; +// Drive number to Icon number (Pane) translation: +char PDrvToIcon[4] = {Icon_Pane_Drive0, Icon_Pane_Drive1, Icon_Pane_Drive2, Icon_Pane_Drive3}; +// Drive number to Icon number (Prefs) translation: +#define IconsPerDrive 4 +char DriveToIcon[16] = { + Icon_Prefs_Dr8DIR, Icon_Prefs_Dr8D64, Icon_Prefs_Dr8T64, Icon_Prefs_Dr8Path, + Icon_Prefs_Dr9DIR, Icon_Prefs_Dr9D64, Icon_Prefs_Dr9T64, Icon_Prefs_Dr9Path, + Icon_Prefs_Dr10DIR, Icon_Prefs_Dr10D64, Icon_Prefs_Dr10T64, Icon_Prefs_Dr10Path, + Icon_Prefs_Dr11DIR, Icon_Prefs_Dr11D64, Icon_Prefs_Dr11T64, Icon_Prefs_Dr11Path +}; +// SID type to Icon number translation: +char SIDtoIcon[3] = { + Icon_Prefs_SIDNone, Icon_Prefs_SIDDigi, Icon_Prefs_SIDCard +}; +// REU type to Icon number translation: +char REUtoIcon[4] = { + Icon_Prefs_REUNone, Icon_Prefs_REU128, Icon_Prefs_REU256, Icon_Prefs_REU512 +}; +char KJoyToIcon[2][5] = { + {Icon_Conf_Joy1Up, Icon_Conf_Joy1Down, Icon_Conf_Joy1Left, Icon_Conf_Joy1Right, Icon_Conf_Joy1Fire}, + {Icon_Conf_Joy2Up, Icon_Conf_Joy2Down, Icon_Conf_Joy2Left, Icon_Conf_Joy2Right, Icon_Conf_Joy2Fire} +}; + + + + + +// Internal keynumbers lookup table +// Single characters are represented by single chars, others by pointers to strings +int IntKeyTable[128] = { + (int)"Shft", (int)"Ctrl", (int)"Alt", (int)"ShftL", + (int)"CtrlL", (int)"AltL", (int)"ShftR", (int)"CtrlR", + (int)"AltR", (int)"Slct", (int)"Menu", (int)"Adjst", + 0, 0, 0, 0, + 'q', '3', '4', '5', + (int)"F4", '8', (int)"F7", '-', + '6', (int)"Left", (int)"num6", (int)"num7", + (int)"F11", (int)"F12", (int)"F10", (int)"ScLck", + (int)"Prnt", 'w', 'e', 't', + '7', 'i', '9', '0', + '-', (int)"Down", (int)"num8", (int)"num9", + (int)"Brk", '`', '£', (int)"Del", + '1', '2', 'd', 'r', + '6', 'u', 'o', 'p', + '[', (int)"Up", (int)"num+", (int)"num-", + (int)"nmEnt", (int)"Isrt", (int)"Home", (int)"PgUp", + (int)"CpLck", 'a', 'x', 'f', + 'y', 'j', 'k', '2', + ';', (int)"Ret", (int)"num/", 0, + (int)"num.", (int)"nmLck", (int)"PgDwn", '\'', + 0, 's', 'c', 'g', + 'h', 'n', 'l', ';', + ']', (int)"Del", (int)"num#", (int)"num*", + 0, '=', (int)"Extra", 0, + (int)"Tab", 'z', (int)"Space", 'v', + 'b', 'm', ',', '.', + '/', (int)"Copy", (int)"num0", (int)"num1", + (int)"num3", 0, 0, 0, + (int)"Esc", (int)"F1", (int)"F2", (int)"F3", + (int)"F5", (int)"F6", (int)"F8", (int)"F9", + '\\', (int)"Right", (int)"num4", (int)"num5", + (int)"num2", 0, 0, 0 +}; + + + + + +// The icon bar icon +RO_IconDesc IBarIcon = { + -1, 0, 0, 68, 68, 0x301a, +#ifdef FRODO_SC + "!frodosc" +#else +# ifdef FRODO_PC + "!frodopc" +# else + "!frodo" +# endif +#endif +}; + + + + + +// The menus + +char *MIBTextPrefs = "Preferences..."; +char *MIBTextConf = "Configuration..."; + + +struct MenuIconBar { + RO_MenuHead head; + RO_MenuItem item[Menu_IBar_Items]; +} MenuIconBar = { + { +#ifdef FRODO_SC + "FrodoSC", +#else +# ifdef FRODO_PC + "FrodoPC", +# else + "Frodo", +# endif +#endif + 7, 2, 7, 0, Menu_IBar_Width, Menu_Height, 0}, + { + {0, (RO_MenuHead*)-1, Menu_Flags, "Info"}, + {0, (RO_MenuHead*)-1, Menu_Flags + IFlg_Indir, ""}, + {0, (RO_MenuHead*)-1, Menu_Flags + IFlg_Indir, ""}, + {0, (RO_MenuHead*)-1, Menu_Flags, "Sound"}, + {MFlg_LastItem, (RO_MenuHead*)-1, Menu_Flags, "Quit"} + } +}; + +struct MenuEmuWindow { + RO_MenuHead head; + RO_MenuItem item[Menu_EWind_Items]; +} MenuEmuWindow = { + { +#ifdef FRODO_SC + "FrodoSC Job", +#else +# ifdef FRODO_PC + "FrodoPC Job", +# else + "Frodo Job", +# endif +#endif + 7, 2, 7, 0, Menu_EWind_Width, Menu_Height, 0}, + { + {0, (RO_MenuHead*)-1, Menu_Flags, "Info"}, + {0, (RO_MenuHead*)-1, Menu_Flags, "Sound"}, + {MFlg_Warning, (RO_MenuHead*)-1, Menu_Flags, "Save RAM"}, + {MFlg_LastItem | MFlg_Warning, (RO_MenuHead*)-1, Menu_Flags, "Snapshot"} + } +}; + + + + +// For less writing in LoadATemplate... +#define wic(base,no,off) base[RO_WINDOW_WORDS + no*RO_ICON_WORDS + off] + +// For less writing in WindowToThePrefs... +#define pread_opt(var,icn) PrefsWindow->GetIconState(Icon_Prefs_##icn,AuxBlock);\ + prefs->##var = ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0) ? false : true; + + + +// +// WIMP member-functions +// + +WIMP::WIMP(C64 *my_c64) +{ + int x,y,i; + WIdata *wid; + + CMOS_DragType = ReadDragType(); + + // Determine the dimensions of the icon bar sprite first. + if (Wimp_SpriteInfo((char*)(&IBarIcon.dat),&x,&y,&i) == NULL) + { + IBarIcon.maxx = x << OS_ReadModeVariable(i,4); + IBarIcon.maxy = y << OS_ReadModeVariable(i,5); + } + + IBicon = new Icon(0,&IBarIcon); Mask = 0x00000830; + LastMenu = LastClick = LastDrag = MenuType = DragType = 0; + UseScrap = false; EmuPaused = false; UseNULL = 0; // one NULL client - the emulator + the_c64 = my_c64; + EmuZoom = 1; + + // Read the customized sprite area (LEDs etc) + if ((ReadCatalogueInfo(CUSTOM_SPRITES,AuxBlock) & 1) != 0) // (image) file + { + int i; + FILE *fp; + + i = AuxBlock[2]; SpriteArea = (int*)(new char[i+16]); // allocate space for sprite + fp = fopen(CUSTOM_SPRITES,"rb"); + i = fread(SpriteArea+1,1,i,fp); SpriteArea[0] = i+4; + fclose(fp); + } + else + { + WimpError.errnum = 0; sprintf(WimpError.errmess,"Can't read sprites!!!"); + Wimp_ReportError(&WimpError,1,TASKNAME); + } + + // Read Template and create windows + if (Wimp_OpenTemplate(TEMPLATES_FILE) == NULL) + { + // Load Pane first + LoadATemplate("EmuPane",&EmuPane); + LoadATemplate("EmuWindow",&EmuWindow); + LoadATemplate("ThePrefs",&PrefsWindow); + LoadATemplate("ConfigWind",&ConfigWindow); + LoadATemplate("InfoWindow",&InfoWindow); + LoadATemplate("SoundWindow",&SoundWindow); + LoadATemplate("SaveBox",&SaveBox); + } + else + { + Wimp_ReportError(&WimpError,1,TASKNAME); + WimpError.errnum = 0; sprintf(WimpError.errmess,"Error in Templates!!!"); + } + Wimp_CloseTemplate(); + + // Add info window to icon bar menu + MenuIconBar.item[Menu_IBar_Info].submenu = + MenuEmuWindow.item[Menu_EWind_Info].submenu = (RO_MenuHead*)InfoWindow->MyHandle(); + MenuIconBar.item[Menu_IBar_Sound].submenu = + MenuEmuWindow.item[Menu_EWind_Sound].submenu = (RO_MenuHead*)SoundWindow->MyHandle(); + MenuEmuWindow.item[Menu_EWind_SaveRAM].submenu = + MenuEmuWindow.item[Menu_EWind_Snapshot].submenu = (RO_MenuHead*)SaveBox->MyHandle(); + // I couldn't find ONE FUCKING WAY to do this in the initialisation directly and I'm + // REALLY PISSED OFF! + wid = &(MenuIconBar.item[Menu_IBar_Prefs].dat); + wid->ind.tit = (int*)MIBTextPrefs; wid->ind.val = NULL; wid->ind.len = sizeof(MIBTextPrefs); + wid = &(MenuIconBar.item[Menu_IBar_Config].dat); + wid->ind.tit = (int*)MIBTextConf; wid->ind.val = NULL; wid->ind.len = sizeof(MIBTextConf); + + // Write default path into config window + ConfigWindow->WriteIconText(Icon_Conf_ConfPath,DEFAULT_SYSCONF); + + // Set up the contents of the prefs window and init with the default prefs path + ThePrefsToWindow(); + PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,DEFAULT_PREFS); + // Grey out SID card icon -- not supported! + PrefsWindow->SetIconState(Icon_Prefs_SIDCard,IFlg_Grey,IFlg_Grey); + + // Open Emulator window + pane in the center of the screen and give it the input focus + Wimp_GetCaretPosition(&LastCaret); + OpenEmuWindow(); + Wimp_SetCaretPosition(EmuWindow->MyHandle(),-1,-100,100,-1,-1); // emu window gets input focus + + // Init export files + sprintf(RAMFile+44,"C64_RAM"); sprintf(SnapFile+44,"FrodoSnap"); +} + + +WIMP::~WIMP(void) +{ + delete IBicon; + delete EmuWindow; delete EmuPane; delete PrefsWindow; delete ConfigWindow; + delete InfoWindow; delete SoundWindow; delete SaveBox; +} + + +bool WIMP::LoadATemplate(char *Name, Window **Which) +{ + char *Buffer, *Indirect, *Desc; + int IndSize, Pos; + char TempName[12]; + + strncpy((char*)TempName,Name,12); + Buffer = NULL; Pos = 0; + if (Wimp_LoadTemplate(&Buffer,&Indirect,0,(char*)-1,TempName,&Pos) != NULL) {return(false);} + Buffer = new char[(int)Buffer + 4]; + IndSize = (int)Indirect; Indirect = new char[IndSize]; + Pos = 0; Desc = Buffer+4; + Wimp_LoadTemplate(&Desc,&Indirect,Indirect+IndSize,(char*)-1,TempName,&Pos); + if (Which == &EmuWindow) + { + int eigen, dx, dy; + RO_Window *row = (RO_Window*)Buffer; + RORes res; + + // Center emulator window on screen + eigen = (res.eigx < res.eigy) ? res.eigx : res.eigy; + dx = (DISPLAY_X << eigen); dy = (DISPLAY_Y << eigen); + row->vminx = res.resx/2 - dx/2; row->vmaxx = row->vminx + dx; + row->vminy = res.resy/2 - dy/2; row->vmaxy = row->vminy + dy; + row->wminy = -dy; row->wmaxx = dx; + Indirect = (char*)VERSION_STRING; + } + else + { + if (Which == &EmuPane) // EmuPane needs customized sprites + { + register RO_Window *row = (RO_Window*)Buffer; + register int *b = (int*)Buffer; + + // Insert sprite pointer in window and icon definitions + row->SpriteAreaPtr = (int)SpriteArea; + wic(b,Icon_Pane_LED0,RawIB_Data1) = wic(b,Icon_Pane_LED1,RawIB_Data1) = + wic(b,Icon_Pane_LED2,RawIB_Data1) = wic(b,Icon_Pane_LED3,RawIB_Data1) = (int)SpriteArea; + sprintf((char*)wic(b,Icon_Pane_Pause,RawIB_Data0),PANE_TEXT_PAUSE); + sprintf((char*)wic(b,Icon_Pane_Toggle,RawIB_Data0),PANE_TEXT_ZOOM2); + } + else if (Which == &SoundWindow) // ditto + { + register RO_Window *row = (RO_Window*)Buffer; + register int *b = (int*)Buffer; + register int orr; + + row->SpriteAreaPtr = (int)SpriteArea; + // if sound emulation off grey out notes + if (ThePrefs.SIDType == SIDTYPE_NONE) {orr = IFlg_Grey;} else {orr = 0;} + wic(b,Icon_Sound_Notes,RawIB_Flags)=(wic(b,Icon_Sound_Notes,RawIB_Flags)&~IFlg_Grey)|orr; + } + else if (Which == &InfoWindow) // ditto + { + register RO_Window *row = (RO_Window*)Buffer; + + row->SpriteAreaPtr = (int)SpriteArea; + } + Indirect = NULL; + } + *Which = new Window((int*)Buffer,Indirect); + return(true); +} + + +// All changes in position of the Emulator window have to be treated seperately +// to keep the pane attached to it at all times! +void WIMP::OpenEmuWindow(void) +{ + RO_Window *wind; + int work[WindowB_WFlags], i; + + wind = EmuWindow->Descriptor(); + EmuPane->GetWorkArea(&work[WindowB_VMinX]); i = work[WindowB_VMaxX] - work[WindowB_VMinX]; + if ((work[WindowB_VMinX] = wind->vminx - EmuPaneSpace - i) < 0) + { + // if main window still on screen then keep pane on screen as well + if (wind->vminx >= 0) {work[WindowB_VMinX] = 0;} + // otherwise align pane and main window on the left + else {work[WindowB_VMinX] = wind->vminx;} + } + work[WindowB_VMaxX] = work[WindowB_VMinX] + i; + work[WindowB_VMinY] = wind->vmaxy - (work[WindowB_VMaxY]-work[WindowB_VMinY]); + work[WindowB_VMaxY] = wind->vmaxy; + work[WindowB_Handle] = EmuPane->MyHandle(); + work[WindowB_ScrollX] = work[WindowB_ScrollY] = 0; + work[WindowB_Stackpos] = -1; // open on top of stack + // open the pane first + EmuPane->open(work); + wind->stackpos = EmuPane->MyHandle(); // open window behind pane. + EmuWindow->open(); +} + + +void WIMP::OpenEmuWindow(int *Where) +{ + int work[WindowB_WFlags], i; + + EmuPane->GetWorkArea(&work[WindowB_VMinX]); i = work[WindowB_VMaxX] - work[WindowB_VMinX]; + if ((work[WindowB_VMinX] = Where[WindowB_VMinX] - EmuPaneSpace - i) < 0) + { + if (Where[WindowB_VMinX] >= 0) {work[WindowB_VMinX] = 0;} + else {work[WindowB_VMinX] = Where[WindowB_VMinX];} + } + work[WindowB_VMaxX] = work[WindowB_VMinX] + i; + work[WindowB_VMinY] = Where[WindowB_VMaxY] - (work[WindowB_VMaxY]-work[WindowB_VMinY]); + work[WindowB_VMaxY] = Where[WindowB_VMaxY]; + work[WindowB_Handle] = EmuPane->MyHandle(); + work[WindowB_ScrollX] = work[WindowB_ScrollY] = 0; + work[WindowB_Stackpos] = Where[WindowB_Stackpos]; + // open the pane first + EmuPane->open(work); + Where[WindowB_Stackpos] = EmuPane->MyHandle(); // open window behind pane + EmuWindow->open(Where); +} + + +void WIMP::CloseEmuWindow(void) +{ + EmuWindow->close(); EmuPane->close(); +} + + +void WIMP::UpdateEmuWindow(void) +{ + C64Display *disp = the_c64->TheDisplay; + + EmuWindow->update(disp->BitmapBase(),disp); +} + + +// Write the values given in ThePrefs into the Prefs Window +void WIMP::ThePrefsToWindow(void) +{ + int i, j, k; + + // Update the data for each of the drives + for (i=0; i<4; i++) + { + switch(ThePrefs.DriveType[i]) + { + case DRVTYPE_D64: j=1; break; + case DRVTYPE_T64: j=2; break; + default: j=0; break; // otherwise type 0 = DIR + } + // unselect all but other icons + for (k=0; k<3; k++) + { + if (k != j) {PrefsWindow->SetIconState(DriveToIcon[i*IconsPerDrive + k],0,IFlg_Slct);} + } + // and select the correct one. + PrefsWindow->SetIconState(DriveToIcon[i*IconsPerDrive + j],IFlg_Slct,IFlg_Slct); + // Now update the path name (buffer won't change, i.e. use original data) + PrefsWindow->WriteIconText(DriveToIcon[i*IconsPerDrive + 3],ThePrefs.DrivePath[i]); + } + if (ThePrefs.Emul1541Proc) {i = IFlg_Slct;} else {i = 0;} + PrefsWindow->SetIconState(Icon_Prefs_Emul1541,i,IFlg_Slct); + SetLEDIcons(ThePrefs.Emul1541Proc); + if (ThePrefs.MapSlash) {i = IFlg_Slct;} else {i = 0;} + PrefsWindow->SetIconState(Icon_Prefs_MapSlash,i,IFlg_Slct); + + // SID prefs + switch (ThePrefs.SIDType) + { + case SIDTYPE_DIGITAL: i = 1; break; + case SIDTYPE_SIDCARD: i = 2; break; + default: i = 0; break; // includes NONE + } + for (j=0; j<3; j++) + { + if (j != i) {PrefsWindow->SetIconState(SIDtoIcon[j],0,IFlg_Slct);} + } + PrefsWindow->SetIconState(SIDtoIcon[i],IFlg_Slct,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_SIDFilter,(ThePrefs.SIDFilters)?IFlg_Slct:0,IFlg_Slct); + + // REU prefs + switch (ThePrefs.REUSize) + { + case REU_128K: i=1; break; + case REU_256K: i=2; break; + case REU_512K: i=3; break; + default: i=0; break; + } + for (j=0; j<4; j++) + { + if (j != i) {PrefsWindow->SetIconState(REUtoIcon[j],0,IFlg_Slct);} + } + PrefsWindow->SetIconState(REUtoIcon[i],IFlg_Slct,IFlg_Slct); + + // Skip Frames + PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,ThePrefs.SkipFrames); + + // Sprites + PrefsWindow->SetIconState(Icon_Prefs_SprOn,(ThePrefs.SpritesOn)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_SprColl,(ThePrefs.SpriteCollisions)?IFlg_Slct:0,IFlg_Slct); + // Joystick + PrefsWindow->SetIconState(Icon_Prefs_Joy1On,(ThePrefs.Joystick1On)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_Joy2On,(ThePrefs.Joystick2On)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_JoySwap,(ThePrefs.JoystickSwap)?IFlg_Slct:0,IFlg_Slct); + + // Misc + SetSpeedLimiter(ThePrefs.LimitSpeed); + PrefsWindow->SetIconState(Icon_Prefs_FastReset,(ThePrefs.FastReset)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_CIAHack,(ThePrefs.CIAIRQHack)?IFlg_Slct:0,IFlg_Slct); + + // Cycles + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleNorm,ThePrefs.NormalCycles); + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleBad,ThePrefs.BadLineCycles); + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleCIA,ThePrefs.CIACycles); + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleFloppy,ThePrefs.FloppyCycles); +} + + +// Update ThePrefs according to the values given in the Prefs Window +void WIMP::WindowToThePrefs(void) +{ + int i, j, k; + Prefs *prefs = new Prefs(ThePrefs); + + for (i=0; i<4; i++) + { + // find out which of the drive type icons is selected + j = -1; + do + { + ++j; PrefsWindow->GetIconState(DriveToIcon[i*IconsPerDrive + j],AuxBlock); + } + while ((j < 3) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0)); + switch (j) + { + case 1: prefs->DriveType[i] = DRVTYPE_D64; break; + case 2: prefs->DriveType[i] = DRVTYPE_T64; break; + default: prefs->DriveType[i] = DRVTYPE_DIR; break; + } + strcpy(prefs->DrivePath[i],PrefsWindow->ReadIconText(DriveToIcon[i*IconsPerDrive + 3])); + } + // Emulation of the 1541 processor is a special case as it also affects LED1-3 + pread_opt(Emul1541Proc,Emul1541); + SetLEDIcons(prefs->Emul1541Proc); + pread_opt(MapSlash,MapSlash); + + // SID + j = -1; + do + { + ++j; PrefsWindow->GetIconState(SIDtoIcon[j],AuxBlock); + } + while ((j < 3) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0)); + switch (j) + { + case 1: prefs->SIDType = SIDTYPE_DIGITAL; break; + case 2: prefs->SIDType = SIDTYPE_SIDCARD; break; + default: prefs->SIDType = SIDTYPE_NONE; break; + } + pread_opt(SIDFilters,SIDFilter); + SoundWindow->SetIconState(Icon_Sound_Notes,(prefs->SIDType==SIDTYPE_NONE)?IFlg_Grey:0,IFlg_Grey); + + // REU + j = -1; + do + { + ++j; PrefsWindow->GetIconState(REUtoIcon[j],AuxBlock); + } + while ((j < 4) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0)); + switch (j) + { + case 1: prefs->REUSize = REU_128K; break; + case 2: prefs->REUSize = REU_256K; break; + case 3: prefs->REUSize = REU_512K; break; + default: prefs->REUSize = REU_NONE; break; + } + + // Skip Frames + prefs->SkipFrames = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText); + + // Sprites + pread_opt(SpritesOn,SprOn); pread_opt(SpriteCollisions,SprColl); + + // Joystick + pread_opt(Joystick1On,Joy1On); pread_opt(Joystick2On,Joy2On); pread_opt(JoystickSwap,JoySwap); + + // Misc + pread_opt(LimitSpeed,LimSpeed); pread_opt(FastReset,FastReset); pread_opt(CIAIRQHack,CIAHack); + + // Cycles + prefs->NormalCycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleNorm); + prefs->BadLineCycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleBad); + prefs->CIACycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleCIA); + prefs->FloppyCycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleFloppy); + + // Finally make the changes known to the system: + the_c64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; +} + + +// Update the SysConfig window according to the values used +void WIMP::SysConfToWindow(void) +{ + int i, j, k; + Joy_Keys *jk; + char OneChar[2], *b; + + OneChar[1] = 0; + + // Write timings + the_c64->ReadTimings(&i,&j,&k); + ConfigWindow->WriteIconNumber(Icon_Conf_PollAfter, i); + ConfigWindow->WriteIconNumber(Icon_Conf_SpeedAfter, j); + ConfigWindow->WriteIconNumber(Icon_Conf_SoundAfter, k); + + // Write joystick keys + for (i=0; i<2; i++) + { + jk = &(the_c64->TheDisplay->JoystickKeys[i]); NewJoyKeys[i] = *jk; + if ((j = jk->up) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][0], b); + if ((j = jk->down) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][1], b); + if ((j = jk->left) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][2], b); + if ((j = jk->right) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][3], b); + if ((j = jk->fire) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][4], b); + } +} + + +// Update SysConfig according to the values in the window +void WIMP::WindowToSysConf(void) +{ + int i, j, k; + Joy_Keys *jk; + + // Read timings + i = ConfigWindow->ReadIconNumber(Icon_Conf_PollAfter); + j = ConfigWindow->ReadIconNumber(Icon_Conf_SpeedAfter); + k = ConfigWindow->ReadIconNumber(Icon_Conf_SoundAfter); + the_c64->WriteTimings(i,j,k); + + // Read joystick keys + for (i=0; i<2; i++) + { + jk = &(the_c64->TheDisplay->JoystickKeys[i]); *jk = NewJoyKeys[i]; + } +} + + +// Low-level keyboard scan in SysConfig Window +void WIMP::PollSysConfWindow(void) +{ + Wimp_GetPointerInfo(Block); + if (Block[MouseB_Window] == ConfigWindow->MyHandle()) + { + int i, j; + + for (i=0; i<2; i++) + { + for (j=0; j<5; j++) + { + if (Block[MouseB_Icon] == KJoyToIcon[i][j]) + { + int key; + + // Gain caret (to window, but none of its icons!) + Wimp_GetCaretPosition(&LastCaret); + Wimp_SetCaretPosition(ConfigWindow->MyHandle(),-1,0,0,-1,-1); + if ((key = ScanKeys(IntKey_ScanFrom)) != 0xff) + { + char OneChar[2], *b; + + if (key == IntKey_Void) {key = 0xff;} + switch (j) + { + case 0: NewJoyKeys[i].up = key; break; + case 1: NewJoyKeys[i].down = key; break; + case 2: NewJoyKeys[i].left = key; break; + case 3: NewJoyKeys[i].right = key; break; + case 4: NewJoyKeys[i].fire = key; break; + } + if (key >= 128) {key = 127;} + key = IntKeyTable[key]; + if (key < 256) {OneChar[0]=key; OneChar[1]=0; b=OneChar;} else {b=(char*)key;} + ConfigWindow->WriteIconText(KJoyToIcon[i][j], b); + } + } + } + } + } +} + + +// Start a drag operation on the icon in the window +void WIMP::DragIconSprite(Window *host, unsigned int number) +{ + host->GetIconState(number,AuxBlock); + if ((AuxBlock[IconB_Flags] & IFlg_Sprite) != 0) // it needs to have a sprite, of course + { + char spritename[16] = "\0"; + + if ((AuxBlock[IconB_Flags] & IFlg_Indir) == 0) // not indirected + { + strncpy(spritename,((char*)AuxBlock+IconB_Data0),15); + } + else + { + if ((AuxBlock[IconB_Flags] & IFlg_Text) == 0) + { + strncpy(spritename,(char*)AuxBlock[IconB_Data0],15); + } + else // this necessitates parsing the validation string + { + register char *b, *d, *s, c; + + s = spritename; + if ((b = (char*)AuxBlock[IconB_Data1]) != NULL) // pointer to val str + { + do + { + c = *b++; + if ((c == 's') || (c == 'S')) // sprite command + { + c = *b++; while ((c != ';') && (c >= 32)) {*s++ = c; c = *b++;} + c = 0; *s++ = c; // we can stop now + } + else if (c >= 32) // some other command ==> skip to next. + { + c = *b++; + while ((c != ';') && (c >= 32)) {if (c == '\\') {b++;} c = *b++;} + } + } + while (c >= 32); + } + } + } + // we should now have the spritename + if (spritename[0] != '\0') + { + LastDrag = host->MyHandle(); LastIcon = number; + if (CMOS_DragType == 0) // Drag outline + { + ROScreen *screen = the_c64->TheDisplay->screen; + + AuxBlock[DragB_Handle] = LastDrag; AuxBlock[DragB_Type] = 5; + Wimp_GetPointerInfo(AuxBlock + DragB_IMinX); + // Drag fixed sized box of this size: + AuxBlock[DragB_IMinX] -= IconSpriteSize/2; + AuxBlock[DragB_IMaxX] = AuxBlock[DragB_IMinX] + IconSpriteSize; + AuxBlock[DragB_IMinY] -= IconSpriteSize/2; + AuxBlock[DragB_IMaxY] = AuxBlock[DragB_IMinY] + IconSpriteSize; + // Parent box is whole screen + AuxBlock[DragB_BBMinX] = AuxBlock[DragB_BBMinY] = 0; + AuxBlock[DragB_BBMaxX] = screen->resx; AuxBlock[DragB_BBMaxY] = screen->resy; + Wimp_DragBox(AuxBlock); + } + else // DragASprite + { + Wimp_GetPointerInfo(AuxBlock); + AuxBlock[DASB_MinX] -= IconSpriteSize/2; + AuxBlock[DASB_MaxX] = AuxBlock[DASB_MinX] + IconSpriteSize; + AuxBlock[DASB_MinY] -= IconSpriteSize/2; + AuxBlock[DASB_MaxY] = AuxBlock[DASB_MinY] + IconSpriteSize; + DragASprite_Start(0xc5,1,spritename,AuxBlock,NULL); + } + } + } +} + + +// Blk is a block as in MouseClick or PointerInfo +int WIMP::CalculateVolume(int *Blk) +{ + int orgx, vol; + + SoundWindow->getstate(AuxBlock); + orgx = AuxBlock[WindowB_VMinX] - AuxBlock[WindowB_ScrollX]; + SoundWindow->GetIconState(Icon_Sound_Volume, AuxBlock); + vol = (MaximumVolume*((Blk[MouseB_PosX] - orgx) - AuxBlock[IconB_MinX] - WellBorder)) / (AuxBlock[IconB_MaxX] - AuxBlock[IconB_MinX] - 2*WellBorder); + if (vol < 0) {vol = 0;} if (vol > MaximumVolume) {vol = MaximumVolume;} + return(vol); +} + + +// Check whether a filename contains a full pathname and reports an error if not. +int WIMP::CheckFilename(char *name) +{ + bool OK = false; + register char *b, *d, c; + + b = d = name; c = *b++; + while (c > 32) + { + // valid path must contain '$' or ':' + if ((c == '$') || (c == ':')) {OK = true; d = b;} + if (c == '.') {d = b;} + c = *b++; + } + if ((b - d) == 1) {OK = false;} // filename mustn't end with '$', ':' or '.' + if (OK) {return(0);} + + WimpError.errnum = 0; sprintf(WimpError.errmess,"Bad filename %s",name); + Wimp_ReportError(&WimpError,1,TASKNAME); + return(-1); +} + + +void WIMP::SnapshotSaved(bool OK) +{ + if (OK) + { + int *b = (int*)SnapFile; + + if (b[MsgB_Sender] != 0) + { + b[MsgB_YourRef] = b[MsgB_MyRef]; b[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,b,b[MsgB_Sender],b[6]); + } + else {Wimp_CreateMenu((int*)-1,0,0);} + SaveType = 0; + } +} + + +void WIMP::IssueSnapshotRequest(void) +{ + if (EmuPaused) + { + EmuPane->WriteIconTextU(Icon_Pane_Pause, PANE_TEXT_PAUSE); + EmuPaused = false; + } + the_c64->RequestSnapshot(); +} + + +// Sets the Emu pane's LEDs according to the floppy emulation state +void WIMP::SetLEDIcons(bool FloppyEmulation) +{ + int i, eor; + + if (FloppyEmulation) {eor = IFlg_Grey;} else {eor = 0;} + for (i=1; i<4; i++) + { + EmuPane->SetIconState(LEDtoIcon[i],eor,IFlg_Grey); + } +} + + + +// Doesn't open window, just resizes... +void WIMP::SetEmuWindowSize(void) +{ + register int i; + register C64Display *disp = the_c64->TheDisplay; + + i = (disp->screen->eigx < disp->screen->eigy) ? disp->screen->eigx : disp->screen->eigy; + if (EmuZoom == 2) {i++;} + EmuWindow->extent(0,-(DISPLAY_Y << i),(DISPLAY_X << i),0); +} + + + +// switch between zoom 1 and zoom 2 +void WIMP::ToggleEmuWindowSize(void) +{ + int x,y; + + // Icon in pane shows _alternative_ zoom mode + if (EmuZoom == 1) + { + EmuZoom = 2; + EmuPane->WriteIconText(Icon_Pane_Toggle,"1 x"); + } + else + { + EmuZoom = 1; + EmuPane->WriteIconText(Icon_Pane_Toggle,"2 x"); + } + SetEmuWindowSize(); + EmuWindow->GetWorkArea(AuxBlock); + x = AuxBlock[2] - AuxBlock[0]; y = AuxBlock[3] - AuxBlock[1]; + EmuWindow->getstate(AuxBlock); + AuxBlock[WindowB_VMaxX] = AuxBlock[WindowB_VMinX] + x; + AuxBlock[WindowB_VMinY] = AuxBlock[WindowB_VMaxY] - y; + // Open emu window alone to get the dimensions set by the WIMP + Wimp_OpenWindow(AuxBlock); + // Then open with the pane at the correct position + EmuWindow->getstate(AuxBlock); OpenEmuWindow(AuxBlock); UpdateEmuWindow(); +} + + + +int WIMP::ReadEmuWindowSize(void) +{ + return EmuZoom; +} + + + +// Set a new drive path for drive DrNum. MsgBlock is the DataLoad MessageBlock. +void WIMP::NewDriveImage(int DrNum, int *MsgBlock, bool SetNow) +{ + int type, j = -1, map = -1; + + // determine currently selected type + do + { + ++j; PrefsWindow->GetIconState(DriveToIcon[DrNum*IconsPerDrive + j], AuxBlock); + } + while ((j < 3) && ((AuxBlock[6] & IFlg_Slct) == 0)); + if (j >= 3) {j = 0;} + // Check the type and set the drive type accordingly + type = ReadCatalogueInfo(((char*)Block)+44,AuxBlock); + // New path is directory but DRVTYPE != DIR ==> set DIR + if ((type == 2) && (j != 0)) {map = 0;} + // New path is not directory/image but DRVTYPE == DIR ==> remap to D64 + if (((type & 2) == 0) && (j == 0)) {map = 1;} + // Filetype indicated D64 image? + if (((type = AuxBlock[0]) & 0xfff00000) == 0xfff00000) // typed file + { + type = (type >> 8) & 0xfff; + // D64 image can also be used in DIR mode (-> D64ImageFS). Only remap from T64! + if ((type == FileType_D64File) && (j == 2)) {map = 1;} + } + // Do we need to remap? + if (map >= 0) + { + PrefsWindow->SetIconState(DriveToIcon[DrNum*IconsPerDrive+j],0,IFlg_Slct); + PrefsWindow->SetIconState(DriveToIcon[DrNum*IconsPerDrive+map],IFlg_Slct,IFlg_Slct); + j = map; + } + // j is the number of the emulation mode (DIR (0), D64 (1), T64 (2)) + PrefsWindow->WriteIconText(DriveToIcon[DrNum*IconsPerDrive+3],((char*)Block)+44); + // Send acknowledge message + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + + // Set this drive path immediately? + if (SetNow) + { + Prefs *prefs = new Prefs(ThePrefs); + + prefs->DriveType[DrNum] = j; strcpy(prefs->DrivePath[DrNum],((char*)Block)+44); + the_c64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } +} + + + +void WIMP::SetSpeedLimiter(bool LimitSpeed) +{ + RO_Icon *roi; + char *b, c; + + PrefsWindow->SetIconState(Icon_Prefs_LimSpeed,(LimitSpeed) ? IFlg_Slct : 0, IFlg_Slct); + roi = EmuPane->GetIcon(Icon_Pane_Speed); + if ((b = (char*)roi->dat.ind.val) != NULL) + { + do + { + c = *b++; + if ((c == 'r') || (c == 'R')) // boRder command? + { + if (LimitSpeed) {*b++ = '1';} else {*b++ = '2';} + c = 0; // stop now + } + else if (c >= 32) // skip to next command + { + c = *b++; + while ((c >= 32) && (c != ';')) {if (c == '\\') {b++;} c = *b++;} + } + } + while (c >= 32); + EmuPane->UpdateIcon(Icon_Pane_Speed); + } +} + + + + + +void WIMP::Poll(bool Paused) +{ + int event; + bool LastPause; + + LastPause = EmuPaused; EmuPaused = Paused; + + // If emulator is paused disable null events + if ((!EmuPaused) || (UseNULL > 0)) {Mask &= 0xfffffffe;} else {Mask |= 1;} + + do + { + // Loop until a null event is received, then continue the emulation. + // (this looks best) + do + { + event = Wimp_Poll(Mask,Block,NULL); + switch (event) + { + case 1: Redraw(); break; + case 2: OpenWindow(); break; + case 3: CloseWindow(); break; + case 6: MouseClick(); break; + case 7: UserDrag(); break; + case 8: if ((EmuPaused) && (Block[KeyPB_Window] == EmuWindow->MyHandle())) + { + if (the_c64->TheDisplay->CheckForUnpause(!LastPause)) {EmuPaused = false;} + } + KeyPressed(); break; + case 9: MenuSelection(); break; + case 17: + case 18: UserMessage(); break; + case 19: UserMessageAck(); break; + default: break; + } + // This is important + if ((!EmuPaused) || (UseNULL > 0)) {Mask &= 0xfffffffe;} else {Mask |= 1;} + LastPause = EmuPaused; + } + while (event != 0); + if (UseNULL > 0) {PollSysConfWindow();} + // This should probably become a new member-function one day... + if (DragType == DRAG_VolumeWell) + { + Wimp_GetPointerInfo(Block); + if (Block[MouseB_Icon] == Icon_Sound_Volume) // should always be the case (BBox)! + { + the_c64->HostVolume = CalculateVolume(Block); + SoundWindow->ForceIconRedraw(Icon_Sound_Volume); + } + } + } + while (EmuPaused); +} + + +void WIMP::Redraw(void) +{ + if (Block[RedrawB_Handle] == EmuWindow->MyHandle()) // emulator main window + { + C64Display *disp = the_c64->TheDisplay; + + EmuWindow->redraw(Block,disp->BitmapBase(),disp); + } + else if (Block[RedrawB_Handle] == SoundWindow->MyHandle()) // sound window + { + int more; + int minx, miny, maxx, maxy, thresh; + + more = Wimp_RedrawWindow(Block); + // compute screen coordinates of work (0,0) + minx = Block[RedrawB_VMinX] - Block[RedrawB_ScrollX]; + maxy = Block[RedrawB_VMaxY] - Block[RedrawB_ScrollY]; + // get volume well bounding box + SoundWindow->GetIconState(Icon_Sound_Volume, AuxBlock); + maxx = minx + AuxBlock[IconB_MaxX] - WellBorder; minx += AuxBlock[IconB_MinX] + WellBorder; + miny = maxy + AuxBlock[IconB_MinY] + WellBorder; maxy += AuxBlock[IconB_MaxY] - WellBorder; + thresh = minx + (the_c64->HostVolume * (maxx - minx))/ MaximumVolume; + while (more != 0) + { + // clip + if ((minx <= Block[RedrawB_CMaxX]) && (maxx >= Block[RedrawB_CMinX]) && + (miny <= Block[RedrawB_CMaxY]) && (maxy >= Block[RedrawB_CMinY])) + { + if (Block[RedrawB_CMinX] < thresh) + { + ColourTrans_SetGCOL(0x00ff0000,0,0); // green + OS_Plot(0x04,minx,miny); OS_Plot(0x65,thresh,maxy); + } + if (Block[RedrawB_CMaxX] > thresh) + { + ColourTrans_SetGCOL(0xffffff00,0,0); // white + OS_Plot(0x04,thresh,miny); OS_Plot(0x65,maxx,maxy); + } + } + more = Wimp_GetRectangle(Block); + } + } + else // Dummy redraw loop + { + int more; + + more = Wimp_RedrawWindow(Block); + while (more != 0) + { + more = Wimp_GetRectangle(Block); + } + } +} + + +void WIMP::OpenWindow(void) +{ + if (Block[WindowB_Handle] == EmuWindow->MyHandle()) {OpenEmuWindow(Block);} + else if (Block[WindowB_Handle] != EmuPane->MyHandle()) + { + Wimp_OpenWindow(Block); + } +} + + +void WIMP::CloseWindow(void) +{ + if (Block[WindowB_Handle] == EmuWindow->MyHandle()) {CloseEmuWindow();} + else if (Block[WindowB_Handle] != EmuPane->MyHandle()) + { + if (Block[WindowB_Handle] == ConfigWindow->MyHandle()) {UseNULL--;} + Wimp_CloseWindow(Block); + } +} + + +void WIMP::MouseClick(void) +{ + if ((Block[MouseB_Window] == -2) && (Block[MouseB_Icon] == IBicon->IHandle)) // Icon Bar icon + { + if (Block[MouseB_Buttons] == 2) // Menu + { + Wimp_CreateMenu((int*)&MenuIconBar,Block[MouseB_PosX]-MenuIconBar.head.width/2,96+Menu_Height*Menu_IBar_Items); + LastMenu = Menu_IBar; + } + else // Some other click + { + if (!EmuWindow->OpenStatus(Block)) {Block[WindowB_Stackpos] = -1; OpenEmuWindow(Block);} + } + } + else if (Block[MouseB_Window] == EmuWindow->MyHandle()) // Emulator window + { + if (Block[MouseB_Buttons] >= 256) // click + { + Wimp_GetCaretPosition(&LastCaret); + Wimp_SetCaretPosition(Block[MouseB_Window],-1,-100,100,-1,-1); // gain input focus + } + if (Block[MouseB_Buttons] == 2) // menu + { + Wimp_CreateMenu((int*)&MenuEmuWindow,Block[MouseB_PosX]-MenuEmuWindow.head.width/2,Block[MouseB_PosY]); + LastMenu = Menu_Emulator; + } + } + else if (Block[MouseB_Window] == EmuPane->MyHandle()) // Emulator pane + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) + { + switch (Block[MouseB_Icon]) + { + case Icon_Pane_Pause: // Pause icon + if (EmuPaused) + { + EmuPane->WriteIconText(Icon_Pane_Pause,PANE_TEXT_PAUSE); + the_c64->Resume(); EmuPaused = false; + } + else + { + EmuPane->WriteIconText(Icon_Pane_Pause,PANE_TEXT_RESUME); + the_c64->Pause(); EmuPaused = true; + // Update the window now! + UpdateEmuWindow(); + } + break; + case Icon_Pane_Reset: the_c64->Reset(); break; + case Icon_Pane_Toggle: ToggleEmuWindowSize(); break; + case Icon_Pane_Speed: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; SetSpeedLimiter(ThePrefs.LimitSpeed); + break; + default: break; + } + } + } + else if (Block[MouseB_Window] == PrefsWindow->MyHandle()) // Prefs window + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) // normal click + { + register int i; + + switch(Block[MouseB_Icon]) + { + case Icon_Prefs_SkipFLeft: + i = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText); + if (i > 0) + { + PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,--i); + ThePrefs.SkipFrames = i; // instant update + } + break; + case Icon_Prefs_SkipFRight: + i = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText); + PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,++i); + ThePrefs.SkipFrames = i; // instant update + break; + case Icon_Prefs_Cancel: ThePrefsToWindow(); break; // restore current settings + case Icon_Prefs_OK: WindowToThePrefs(); + if (Block[MouseB_Buttons] == 4) {PrefsWindow->close();} + break; // use new prefs + case Icon_Prefs_Save: + WindowToThePrefs(); + if (CheckFilename(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)) == 0) + { + ThePrefs.Save(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)); + } + break; + default: break; + } + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) + // drag (only the sprite) + { + if (Block[MouseB_Icon] == Icon_Prefs_PrefSprite) + { + DragIconSprite(PrefsWindow, Icon_Prefs_PrefSprite); + DragType = DRAG_PrefsSprite; + } + } + } + else if (Block[MouseB_Window] == ConfigWindow->MyHandle()) // sys config window + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) + { + if (Block[MouseB_Icon] == Icon_Conf_OK) + { + WindowToSysConf(); UseNULL--; + if (Block[MouseB_Buttons] == 4) {ConfigWindow->close();} + } + else if (Block[MouseB_Icon] == Icon_Conf_Save) + { + if (CheckFilename(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)) == 0) + { + WindowToSysConf(); UseNULL--; ConfigWindow->close(); + the_c64->SaveSystemConfig(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)); + } + } + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) + { + if (Block[MouseB_Icon] == Icon_Conf_ConfSprite) + { + DragIconSprite(ConfigWindow, Icon_Conf_ConfSprite); + DragType = DRAG_ConfSprite; + } + } + } + else if (Block[MouseB_Window] == SoundWindow->MyHandle()) // sound window + { + if (Block[MouseB_Icon] == Icon_Sound_Volume) + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) // click + { + the_c64->HostVolume = CalculateVolume(Block); Sound_Volume(the_c64->HostVolume); + SoundWindow->ForceIconRedraw(Icon_Sound_Volume); + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) // drag + { + int orgx, orgy; + + SoundWindow->getstate(AuxBlock); + orgx = AuxBlock[WindowB_VMinX] - AuxBlock[WindowB_ScrollX]; + orgy = AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_ScrollY]; + SoundWindow->GetIconState(Icon_Sound_Volume, &AuxBlock[DragB_BBMinX - IconB_MinX]); + AuxBlock[DragB_BBMinX] += orgx; AuxBlock[DragB_BBMinY] += orgy; + AuxBlock[DragB_BBMaxX] += orgx; AuxBlock[DragB_BBMaxY] += orgy; + AuxBlock[DragB_Type] = 7; + Wimp_DragBox(AuxBlock); + DragType = DRAG_VolumeWell; UseNULL++; + } + } + } + else if (Block[MouseB_Window] == SaveBox->MyHandle()) + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) + { + if (Block[MouseB_Icon] == Icon_Save_OK) + { + if (CheckFilename(SaveBox->ReadIconText(Icon_Save_Path)) == 0) + { + if (SaveType == SAVE_RAM) + { + strcpy(RAMFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + the_c64->SaveRAM(RAMFile+44); + Wimp_CreateMenu((int*)-1,0,0); + SaveType = 0; + } + else if (SaveType == SAVE_Snapshot) + { + *(((int*)SnapFile) + MsgB_Sender) = 0; + strcpy(SnapFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + IssueSnapshotRequest(); + } + } + } + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) + { + if (Block[MouseB_Icon] == Icon_Save_Sprite) + { + DragIconSprite(SaveBox, Icon_Save_Sprite); + DragType = DRAG_SaveSprite; + } + } + } +} + + +// A drag operation has terminated +void WIMP::UserDrag(void) +{ + char *b = NULL; + int filetype, size; + + if ((CMOS_DragType == 0) || (DragType == DRAG_VolumeWell)) + { + Wimp_DragBox(NULL); + } + else + { + DragASprite_Stop(); + } + + if (DragType == DRAG_VolumeWell) + { + UseNULL--; DragType = 0; Sound_Volume(the_c64->HostVolume); // just set the new volume. + } + + // Drag of the path sprite + if (DragType == DRAG_PrefsSprite) + { + b = PrefsWindow->ReadIconText(Icon_Prefs_PrefPath); filetype = FileType_Text; + size = EstimatedPrefsSize; // can't say how large it's gonna be + } + else if (DragType == DRAG_ConfSprite) + { + b = ConfigWindow->ReadIconText(Icon_Conf_ConfPath); filetype = FileType_Text; + size = EstimatedConfSize; + } + else if (DragType == DRAG_SaveSprite) + { + b = SaveBox->ReadIconText(Icon_Save_Path); filetype = FileType_Data; + if (SaveType == SAVE_RAM) {size = EstimatedRAMSize;} + else if (SaveType == SAVE_Snapshot) {size = EstimatedSnapSize;} + else {size = 0;} + } + + // now b should point to the path and filetype should contain the type + if (b != NULL) + { + Wimp_GetPointerInfo(Block); + // Not on background and not on my own icon bar icon + if ((Block[MouseB_Window] != -1) && ((Block[MouseB_Window] != -2) || (Block[MouseB_Icon] != IBicon->IHandle))) + { + int handle = Block[MouseB_Window]; + + // None of my windows + if ((handle != EmuWindow->MyHandle()) && (handle != EmuPane->MyHandle()) && + (handle != PrefsWindow->MyHandle()) && (handle != ConfigWindow->MyHandle()) && + (handle != InfoWindow->MyHandle()) && (handle != SaveBox->MyHandle())) + { + char *d, c; + + d = b; c = *b++; + // get pointer to leafname in d + while (c > 32) {if ((c == '.') || (c == ':')) {d = b;} c = *b++;} + // Build message block + Block[5] = Block[MouseB_Window]; Block[6] = Block[MouseB_Icon]; + Block[7] = Block[MouseB_PosX]; Block[8] = Block[MouseB_PosY]; + Block[9] = size; Block[10] = filetype; + Block[MsgB_YourRef] = 0; Block[MsgB_Action] = Message_DataSave; + b = ((char*)Block) + 44; c = *d++; + while (c > 32) {*b++ = c; c = *d++;} + *b++ = 0; Block[MsgB_Size] = (((int)(b - (char*)Block)) + 3) & 0xfffffffc; + Wimp_SendMessage(18,Block,Block[5],Block[6]); + } + } + } + else {DragType = 0;} + LastDrag = 0; +} + + +void WIMP::KeyPressed(void) +{ + register int key = Block[KeyPB_Key]; + + if (Block[KeyPB_Window] == EmuWindow->MyHandle()) + { + if ((key >= 0x180) && (key <= 0x1fd)) // special keys (incl. FKeys) + { + key -= 0x180; + if ((((key & 0x4f) >= 0x05) && ((key & 0x4f) <= 0x09)) || // F5 -F9 [shift|ctrl] + (((key & 0x4f) >= 0x4a) && ((key & 0x4f) <= 0x4c))) // F10-F12 [shift|ctrl] + { + if ((key != 0x06) && (key != 0x07) && (key != 0x08)) + { + Wimp_ProcessKey(Block[KeyPB_Key]); + } + } + } + return; + } + else if (Block[KeyPB_Window] == PrefsWindow->MyHandle()) + { + if (key == Key_Return) + { + WindowToThePrefs(); + if (Block[KeyPB_Icon] == Icon_Prefs_PrefPath) // return pressed in path string icon? + { + if (CheckFilename(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)) == 0) + { + ThePrefs.Save(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)); + } + } + return; + } + } + else if (Block[KeyPB_Window] == ConfigWindow->MyHandle()) + { + if ((key == Key_Return) && (Block[KeyPB_Icon] != -1)) + { + WindowToSysConf(); UseNULL--; ConfigWindow->close(); + if (Block[KeyPB_Icon] == Icon_Conf_ConfPath) // return pressed in path string icon? + { + if (CheckFilename(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)) == 0) + { + the_c64->SaveSystemConfig(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)); + } + } + return; + } + } + else if (Block[KeyPB_Window] == SaveBox->MyHandle()) + { + if (key == Key_Return) + { + if (Block[KeyPB_Icon] == Icon_Save_Path) + { + if (CheckFilename(SaveBox->ReadIconText(Icon_Save_Path)) == 0) + { + if (SaveType == SAVE_RAM) + { + strcpy(RAMFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + the_c64->SaveRAM(RAMFile+44); + Wimp_CreateMenu((int*)-1,0,0); + SaveType = 0; + } + else if (SaveType == SAVE_Snapshot) + { + *(((int*)SnapFile) + MsgB_Sender) = 0; + strcpy(SnapFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + IssueSnapshotRequest(); + } + } + } + return; + } + } + Wimp_ProcessKey(Block[KeyPB_Key]); +} + + +void WIMP::MenuSelection(void) +{ + int Buttons; + + Wimp_GetPointerInfo(AuxBlock); Buttons = AuxBlock[MouseB_Buttons]; + + switch (LastMenu) + { + case Menu_IBar: + if (Block[0] == Menu_IBar_Quit) {EmuPaused = false; the_c64->Quit();} + else if (Block[0] == Menu_IBar_Prefs) + { + // Is it already open? Then don't do anything + if (!PrefsWindow->OpenStatus()) + { + int y; + + // Open Prefs window with top left corner in top left corner of screen + PrefsWindow->getstate(AuxBlock); + y = the_c64->TheDisplay->screen->resy; + AuxBlock[WindowB_VMaxX] -= AuxBlock[WindowB_VMinX]; AuxBlock[WindowB_VMinX] = 0; + AuxBlock[WindowB_VMinY] = y - (AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]); + AuxBlock[WindowB_VMaxY] = y; + // Open Prefs window on top + AuxBlock[WindowB_Stackpos] = -1; + PrefsWindow->open(AuxBlock); + } + else + { + PrefsWindow->getstate(AuxBlock); + AuxBlock[WindowB_Stackpos] = -1; PrefsWindow->open(AuxBlock); + } + } + else if (Block[0] == Menu_IBar_Config) + { + if (!ConfigWindow->OpenStatus()) + { + int x, y; + + // Update window contents + SysConfToWindow(); + // Open config window in top right corner of screen + ConfigWindow->getstate(AuxBlock); + x = the_c64->TheDisplay->screen->resx; y = the_c64->TheDisplay->screen->resy; + AuxBlock[WindowB_VMinX] = x - (AuxBlock[WindowB_VMaxX] - AuxBlock[WindowB_VMinX]); + AuxBlock[WindowB_VMaxX] = x; + AuxBlock[WindowB_VMinY] = y - (AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]); + AuxBlock[WindowB_VMaxY] = y; + AuxBlock[WindowB_Stackpos] = -1; + ConfigWindow->open(AuxBlock); + // We need NULL-events for the low-level keyboard scan. + UseNULL++; + } + else + { + ConfigWindow->getstate(AuxBlock); + AuxBlock[WindowB_Stackpos] = -1; ConfigWindow->open(AuxBlock); + } + } + if (Buttons == 1) {Wimp_CreateMenu((int*)&MenuIconBar,0,0);} + break; + case Menu_Emulator: + if (Buttons == 1) {Wimp_CreateMenu((int*)&MenuEmuWindow,0,0);} + break; + default: break; + } +} + + +// Handle regular messages +void WIMP::UserMessage(void) +{ + C64Display *disp = the_c64->TheDisplay; + int i; + + switch (Block[MsgB_Action]) // Message Action + { + case Message_Quit: + EmuPaused = false; the_c64->Quit(); break; + case Message_ModeChange: + disp->ModeChange(); the_c64->TheVIC->ReInitColors(); SetEmuWindowSize(); + // The window could have changed position ==> reposition pane as well! + // we have to force the window to the screen manually + EmuWindow->getstate(AuxBlock); + if ((AuxBlock[WindowB_WFlags] & (1<<16)) != 0) // is it open anyway? + { + i = AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]; + if (AuxBlock[WindowB_VMaxY] > disp->screen->resy - TitleBarHeight) + { + AuxBlock[WindowB_VMaxY] = disp->screen->resy - TitleBarHeight; + if ((AuxBlock[WindowB_VMinY] = AuxBlock[WindowB_VMaxY] - i) < TitleBarHeight) + { + AuxBlock[WindowB_VMinY] = TitleBarHeight; + } + } + i = AuxBlock[WindowB_VMaxX] - AuxBlock[WindowB_VMinX]; + if (AuxBlock[WindowB_VMaxX] > disp->screen->resx - TitleBarHeight) + { + AuxBlock[WindowB_VMaxX] = disp->screen->resx - TitleBarHeight; + if ((AuxBlock[WindowB_VMinX] = AuxBlock[WindowB_VMaxX] - i) < 0) + { + AuxBlock[WindowB_VMinX] = 0; + } + } + // Don't you just love it -- you can't open the window directly, you need + // a delay... like for instance by sending yourself an OpenWindow message... + Wimp_SendMessage(2,AuxBlock,TaskHandle,0); + } + break; + case Message_PaletteChange: + // Updating EmuWindow is pointless since the bitmap still contains data for another mode + disp->ModeChange(); the_c64->TheVIC->ReInitColors(); + break; + case Message_DataSave: + i = -1; // indicator whether file is accepted + if ((Block[5] == EmuWindow->MyHandle()) && (Block[10] == FileType_C64File)) {i=0;} + else if ((Block[5] == EmuWindow->MyHandle()) && (Block[10] == FileType_Data)) {i=0;} + else if ((Block[5] == PrefsWindow->MyHandle()) && (Block[10] == FileType_Text)) {i=0;} + else if ((Block[5] == ConfigWindow->MyHandle()) && (Block[10] == FileType_Text)) {i=0;} + if (i >= 0) + { + Block[9] = -1; // file unsafe + strcpy(((char*)Block)+44,WIMP_SCRAP_FILE); + Block[MsgB_Size] = (48 + strlen(WIMP_SCRAP_FILE)) & 0xfffffffc; + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataSaveAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + UseScrap = true; + } + break; + case Message_DataLoad: + if (Block[5] == EmuWindow->MyHandle()) // Emulator window: load file? + { + if (Block[10] == FileType_C64File) // Load only files with type &64 this way + { + FILE *fp; + + if ((fp = fopen(((char*)Block)+44,"rb")) != NULL) + { + uint8 lo, hi, *mem = the_c64->RAM; + int length; + + lo = fgetc(fp); hi = fgetc(fp); length = lo + (hi<<8); + length += fread(mem+length,1,0x10000-length,fp); + // length is now end-address + fclose(fp); + mem[0xc3] = lo; mem[0xc4] = hi; // Load-address + lo = length & 0xff; hi = (length >> 8) & 0xff; + mem[0xae] = mem[0x2d] = mem[0x2f] = mem[0x31] = mem[0x33] = lo; + mem[0xaf] = mem[0x2e] = mem[0x30] = mem[0x32] = mem[0x34] = hi; + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + } + else if (Block[10] == FileType_Data) + { + if (the_c64->LoadSnapshot(((char*)Block)+44)) + { + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + } + } + else if (Block[5] == PrefsWindow->MyHandle()) // Prefs window? + { + if (Block[10] == FileType_Text) // load a prefs file? + { + Prefs *prefs = new Prefs(ThePrefs); + + prefs->Load(((char*)Block)+44); + the_c64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,((char*)Block)+44); + ThePrefsToWindow(); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + else // interpret as drive path (if dragged on one of the drive path icons) + { + switch (Block[6]) + { + case Icon_Prefs_Dr8Path: i = 0; break; + case Icon_Prefs_Dr9Path: i = 1; break; + case Icon_Prefs_Dr10Path: i = 2; break; + case Icon_Prefs_Dr11Path: i = 3; break; + default: -1; break; + } + if (i >= 0) {NewDriveImage(i,Block,false);} + } + } + else if (Block[5] == ConfigWindow->MyHandle()) // load sys config file + { + if (Block[10] == FileType_Text) + { + the_c64->LoadSystemConfig(((char*)Block)+44); + SysConfToWindow(); + ConfigWindow->WriteIconText(Icon_Conf_ConfPath,((char*)Block)+44); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + } + else if (Block[5] == EmuPane->MyHandle()) // emulator pane + { + switch (Block[6]) + { + case Icon_Pane_Drive0: + case Icon_Pane_LED0: i = 0; break; + case Icon_Pane_Drive1: + case Icon_Pane_LED1: i = 1; break; + case Icon_Pane_Drive2: + case Icon_Pane_LED2: i = 2; break; + case Icon_Pane_Drive3: + case Icon_Pane_LED3: i = 3; break; + default: i = -1; break; + } + if (i >= 0) {NewDriveImage(i,Block,true);} + } + // Clean up if necessary + if (UseScrap) {DeleteFile(WIMP_SCRAP_FILE); UseScrap = false;} + break; + case Message_DataSaveAck: + if (DragType == DRAG_PrefsSprite) + { + WindowToThePrefs(); // read window entries + ThePrefs.Save(((char*)Block)+44); + if (Block[9] != -1) // we're talking to the filer ==> set new pathname + { + PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,((char*)Block)+44); + } + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]); + } + else if (DragType == DRAG_ConfSprite) + { + WindowToSysConf(); // read window entries + the_c64->SaveSystemConfig(((char*)Block)+44); + if (Block[9] != -1) + { + ConfigWindow->WriteIconText(Icon_Conf_ConfPath,((char*)Block)+44); + } + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]); + } + else if (DragType == DRAG_SaveSprite) + { + if (SaveType == SAVE_RAM) + { + memcpy(RAMFile,(char*)Block,256); the_c64->SaveRAM(RAMFile+44); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]); + } + else if (SaveType == SAVE_Snapshot) + { + memcpy(SnapFile,(char*)Block,256); + IssueSnapshotRequest(); + } + } + break; + case Message_DataLoadAck: + if (DragType == DRAG_ConfSprite) + { + UseNULL--; ConfigWindow->close(); + } + if (DragType == DRAG_SaveSprite) + { + Wimp_CreateMenu((int*)-1,0,0); + } + DragType = SaveType = 0; break; + case Message_MenuWarning: + if (LastMenu == Menu_Emulator) + { + if (Block[8] == Menu_EWind_SaveRAM) + { + SaveType = SAVE_RAM; SaveBox->WriteIconText(Icon_Save_Path,RAMFile+44); + } + else if (Block[8] == Menu_EWind_Snapshot) + { + SaveType = SAVE_Snapshot; SaveBox->WriteIconText(Icon_Save_Path,SnapFile+44); + } + else {SaveType = 0;} + Wimp_CreateSubMenu((int*)Block[5],Block[6],Block[7]); + } + break; + default: break; + } +} + + +// If a recorded message was not answered, i.e. something went wrong. +void WIMP::UserMessageAck(void) +{ + switch(Block[MsgB_Action]) + { + case Message_DataSave: + sprintf(WimpError.errmess,"Can't save data."); break; + case Message_DataLoad: + sprintf(WimpError.errmess,"Receiver couldn't load data."); break; + default: + sprintf(WimpError.errmess,"Some error occurred..."); break; + } + WimpError.errnum = 0; Wimp_ReportError(&WimpError,1,TASKNAME); +} diff --git a/Src/AcornGUI.h b/Src/AcornGUI.h new file mode 100644 index 0000000..c4f0825 --- /dev/null +++ b/Src/AcornGUI.h @@ -0,0 +1,341 @@ +/* + * AcornGUI.h + * + * Defines variables for the WIMP interface + * (C) 1997 Andreas Dehmel + * + */ + + + +#ifndef _ACORN_GUI_H_ +#define _ACORN_GUI_H_ + + +// Determine which paths to load from +#ifdef FRODO_SC +# define DEFAULT_PREFS "FrodoSC:Prefs" +# define DEFAULT_SYSCONF "FrodoSC:SysConf" +#else +# ifdef FRODO_PC +# define DEFAULT_PREFS "FrodoPC:Prefs" +# define DEFAULT_SYSCONF "FrodoPC:SysConf" +# else +# define DEFAULT_PREFS "Frodo:Prefs" +# define DEFAULT_SYSCONF "Frodo:SysConf" +# endif +#endif + + +// Text written in pane icons: +#define PANE_TEXT_PAUSE "Pause" +#define PANE_TEXT_RESUME "Cont" +#define PANE_TEXT_ZOOM1 "1 x" +#define PANE_TEXT_ZOOM2 "2 x" + + +// OS units of extra space between EmuWindow and Pane +#define EmuPaneSpace 2 +// OS units of the (volume) well's border +#define WellBorder 12 +// Height of title bar in OS units +#define TitleBarHeight 44 +// Maximum volume of the Sound system +#define MaximumVolume 127 + + + + +// Message Block indices +#define MsgB_Size 0 +#define MsgB_Sender 1 +#define MsgB_MyRef 2 +#define MsgB_YourRef 3 +#define MsgB_Action 4 + + +// Messages +#define Message_Quit 0x00000 +#define Message_DataSave 0x00001 +#define Message_DataSaveAck 0x00002 +#define Message_DataLoad 0x00003 +#define Message_DataLoadAck 0x00004 +#define Message_DataOpen 0x00005 +#define Message_RAMFetch 0x00006 +#define Message_RAMTransmit 0x00007 +#define Message_PreQuit 0x00008 +#define Message_PaletteChange 0x00009 +#define Message_MenuWarning 0x400c0 +#define Message_ModeChange 0x400c1 + + +// Redraw Window Block +#define RedrawB_Handle 0 +#define RedrawB_VMinX 1 +#define RedrawB_VMinY 2 +#define RedrawB_VMaxX 3 +#define RedrawB_VMaxY 4 +#define RedrawB_ScrollX 5 +#define RedrawB_ScrollY 6 +#define RedrawB_CMinX 7 +#define RedrawB_CMinY 8 +#define RedrawB_CMaxX 9 +#define RedrawB_CMaxY 10 + + +// Window block (e.g. open, getstate.... For create: subtract -1 (no handle)) +#define WindowB_Handle 0 +#define WindowB_VMinX 1 +#define WindowB_VMinY 2 +#define WindowB_VMaxX 3 +#define WindowB_VMaxY 4 +#define WindowB_ScrollX 5 +#define WindowB_ScrollY 6 +#define WindowB_Stackpos 7 +#define WindowB_WFlags 8 +#define WindowB_Colours1 9 +#define WindowB_Colours2 10 +#define WindowB_WMinX 11 +#define WindowB_WMinY 12 +#define WindowB_WMaxX 13 +#define WindowB_WMaxY 14 +#define WindowB_TFlags 15 +#define WindowB_WAFlags 16 +#define WindowB_SpriteArea 17 +#define WindowB_MinDims 18 +#define WindowB_Data 19 +#define WindowB_Icons 22 + + +// Raw icon block +#define RawIB_MinX 0 +#define RawIB_MinY 1 +#define RawIB_MaxX 2 +#define RawIB_MaxY 3 +#define RawIB_Flags 4 +#define RawIB_Data0 5 +#define RawIB_Data1 6 +#define RawIB_Data2 7 + + +// Icon block (as in GetIconState) +#define IconB_Handle 0 +#define IconB_Number 1 +#define IconB_MinX 2 +#define IconB_MinY 3 +#define IconB_MaxX 4 +#define IconB_MaxY 5 +#define IconB_Flags 6 +#define IconB_Data0 7 +#define IconB_Data1 8 +#define IconB_Data2 9 + + +// Mouse click block (also: get pointer info): +#define MouseB_PosX 0 +#define MouseB_PosY 1 +#define MouseB_Buttons 2 +#define MouseB_Window 3 +#define MouseB_Icon 4 + + +// Key pressed block +#define KeyPB_Window 0 +#define KeyPB_Icon 1 +#define KeyPB_PosX 2 +#define KeyPB_PosY 3 +#define KeyPB_CHeight 4 +#define KeyPB_Index 5 +#define KeyPB_Key 6 + + +// Drag Block +#define DragB_Handle 0 +#define DragB_Type 1 +#define DragB_IMinX 2 +#define DragB_IMinY 3 +#define DragB_IMaxX 4 +#define DragB_IMaxY 5 +#define DragB_BBMinX 6 +#define DragB_BBMinY 7 +#define DragB_BBMaxX 8 +#define DragB_BBMaxY 9 +#define DragB_R12 10 +#define DragB_DrawCode 11 +#define DragB_RemoveCode 12 +#define DragB_MoveCode 13 + + +// Drag A Sprite Block +#define DASB_MinX 0 +#define DASB_MinY 1 +#define DASB_MaxX 2 +#define DASB_MaxY 3 + + + + + +// Menu definitions +#define Menu_IBar 1 +#define Menu_Emulator 2 + +#define Menu_Height 44 +#define Menu_Flags 0x07003011 + +#define Menu_IBar_Items 5 +#define Menu_IBar_Width 256 +#define Menu_IBar_Info 0 +#define Menu_IBar_Prefs 1 +#define Menu_IBar_Config 2 +#define Menu_IBar_Sound 3 +#define Menu_IBar_Quit 4 + +#define Menu_EWind_Items 4 +#define Menu_EWind_Width 200 +#define Menu_EWind_Info 0 +#define Menu_EWind_Sound 1 +#define Menu_EWind_SaveRAM 2 +#define Menu_EWind_Snapshot 3 + + + + + +// Icons used in window definitions: +#define Icon_Pane_LED0 1 +#define Icon_Pane_LED1 3 +#define Icon_Pane_LED2 5 +#define Icon_Pane_LED3 7 +#define Icon_Pane_Drive0 0 +#define Icon_Pane_Drive1 2 +#define Icon_Pane_Drive2 4 +#define Icon_Pane_Drive3 6 +#define Icon_Pane_Reset 8 +#define Icon_Pane_Pause 9 +#define Icon_Pane_Speed 10 +#define Icon_Pane_Toggle 11 + +#define Icon_Prefs_Dr8DIR 6 +#define Icon_Prefs_Dr8D64 7 +#define Icon_Prefs_Dr8T64 8 +#define Icon_Prefs_Dr8Path 9 +#define Icon_Prefs_Dr9DIR 11 +#define Icon_Prefs_Dr9D64 12 +#define Icon_Prefs_Dr9T64 13 +#define Icon_Prefs_Dr9Path 14 +#define Icon_Prefs_Dr10DIR 16 +#define Icon_Prefs_Dr10D64 17 +#define Icon_Prefs_Dr10T64 18 +#define Icon_Prefs_Dr10Path 19 +#define Icon_Prefs_Dr11DIR 21 +#define Icon_Prefs_Dr11D64 22 +#define Icon_Prefs_Dr11T64 23 +#define Icon_Prefs_Dr11Path 24 +#define Icon_Prefs_Emul1541 25 +#define Icon_Prefs_MapSlash 26 +#define Icon_Prefs_SIDNone 29 +#define Icon_Prefs_SIDDigi 30 +#define Icon_Prefs_SIDCard 31 +#define Icon_Prefs_SIDFilter 32 +#define Icon_Prefs_REUNone 35 +#define Icon_Prefs_REU128 36 +#define Icon_Prefs_REU256 37 +#define Icon_Prefs_REU512 38 +#define Icon_Prefs_SkipFLeft 41 +#define Icon_Prefs_SkipFRight 42 +#define Icon_Prefs_SkipFText 43 +#define Icon_Prefs_SprOn 47 +#define Icon_Prefs_SprColl 48 +#define Icon_Prefs_Joy1On 50 +#define Icon_Prefs_Joy2On 51 +#define Icon_Prefs_JoySwap 52 +#define Icon_Prefs_LimSpeed 55 +#define Icon_Prefs_FastReset 56 +#define Icon_Prefs_CIAHack 57 +#define Icon_Prefs_CycleNorm 64 +#define Icon_Prefs_CycleBad 65 +#define Icon_Prefs_CycleCIA 66 +#define Icon_Prefs_CycleFloppy 67 +#define Icon_Prefs_Cancel 68 +#define Icon_Prefs_OK 69 +#define Icon_Prefs_PrefPath 70 +#define Icon_Prefs_Save 71 +#define Icon_Prefs_PrefSprite 72 + +#define Icon_Conf_PollAfter 3 +#define Icon_Conf_SpeedAfter 5 +#define Icon_Conf_Joy1Up 15 +#define Icon_Conf_Joy1Down 16 +#define Icon_Conf_Joy1Left 17 +#define Icon_Conf_Joy1Right 18 +#define Icon_Conf_Joy1Fire 19 +#define Icon_Conf_Joy2Up 27 +#define Icon_Conf_Joy2Down 28 +#define Icon_Conf_Joy2Left 29 +#define Icon_Conf_Joy2Right 30 +#define Icon_Conf_Joy2Fire 31 +#define Icon_Conf_OK 32 +#define Icon_Conf_Save 33 +#define Icon_Conf_ConfPath 34 +#define Icon_Conf_ConfSprite 35 +#define Icon_Conf_SoundAfter 37 + +#define Icon_Info_Name 4 +#define Icon_Info_Purpose 5 +#define Icon_Info_Author 6 +#define Icon_Info_AuthorPort 7 +#define Icon_Info_Version 8 + +#define Icon_Sound_Volume 0 +#define Icon_Sound_Notes 1 + +#define Icon_Save_Sprite 0 +#define Icon_Save_Path 1 +#define Icon_Save_OK 2 + + + + +// Drag types +#define DRAG_PrefsSprite 1 +#define DRAG_ConfSprite 2 +#define DRAG_SaveSprite 3 +#define DRAG_VolumeWell 16 + + + +// Save types +#define SAVE_RAM 1 +#define SAVE_Snapshot 2 + + + + +// variables + +extern char LEDtoIcon[4]; +extern char DriveToIcon[16]; +extern char SIDtoIcon[3]; +extern char REUtoIcon[4]; + + + + + +// Plotter structs and variables +typedef struct { + int x, y, dimx, dimy; +} graph_env; + +#define PLOTTER_ARGS const graph_env *GraphEnv, const int *Clipwindow,\ + const uint8 *Bitmap, const unsigned int *TransTab + +// Plotters provided in Plotters.s -- declare as C-functions ! +extern "C" +{ +extern void PlotZoom1(PLOTTER_ARGS); +extern void PlotZoom2(PLOTTER_ARGS); +} + +#endif diff --git a/Src/AcornGUI_SC.cc b/Src/AcornGUI_SC.cc new file mode 100644 index 0000000..e5233c3 --- /dev/null +++ b/Src/AcornGUI_SC.cc @@ -0,0 +1,10 @@ +/* + * AcornGUI_SC.cc + * + * The RISC OS port needs to recompile AcornGUI_SC.cc with FRODO_SC defined + * or it won't work. Source code is identical with AcornGUI.cc + * + */ + + +#include "AcornGUI.cc" diff --git a/Src/AmigaGUI.c b/Src/AmigaGUI.c new file mode 100644 index 0000000..eef5cb7 --- /dev/null +++ b/Src/AmigaGUI.c @@ -0,0 +1,407 @@ +/* + * Source machine generated by GadToolsBox V2.0b + * which is (c) Copyright 1991-1993 Jaba Development + * + * GUI Designed by : Christian Bauer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "AmigaGUI.h" + +struct Screen *Scr = NULL; +UBYTE *PubScreenName = NULL; +APTR VisualInfo = NULL; +struct Window *PrefsWnd = NULL; +struct Gadget *PrefsGList = NULL; +struct Menu *PrefsMenus = NULL; +struct IntuiMessage PrefsMsg; +UWORD PrefsZoom[4]; +struct Gadget *PrefsGadgets[31]; +UWORD PrefsLeft = 0; +UWORD PrefsTop = 16; +UWORD PrefsWidth = 561; +UWORD PrefsHeight = 238; +UBYTE *PrefsWdt = (UBYTE *)"Frodo Preferences"; +struct TextAttr *Font, Attr; +UWORD FontX, FontY; +UWORD OffX, OffY; + +UBYTE *SIDType0Labels[] = { + (UBYTE *)"None", + (UBYTE *)"Digital (AHI)", + (UBYTE *)"SID Card", + NULL }; + +UBYTE *REUSize0Labels[] = { + (UBYTE *)"None", + (UBYTE *)"128K", + (UBYTE *)"256K", + (UBYTE *)"512K", + NULL }; + +UBYTE *DriveType80Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +UBYTE *DriveType90Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +UBYTE *DriveType100Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +UBYTE *DriveType110Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +struct IntuiText PrefsIText[] = { + 2, 0, JAM1,34, 115, NULL, (UBYTE *)"Drive", NULL }; + +#define Prefs_TNUM 1 + +struct NewMenu PrefsNewMenu[] = { + NM_TITLE, (STRPTR)"Preferences", NULL, 0, NULL, NULL, + NM_ITEM, (STRPTR)"Open...", (STRPTR)"O", 0, 0L, (APTR)PrefsOpen, + NM_ITEM, (STRPTR)"Save", (STRPTR)"S", 0, 0L, (APTR)PrefsSave, + NM_ITEM, (STRPTR)"Save As...", (STRPTR)"A", 0, 0L, (APTR)PrefsSaveAs, + NM_ITEM, (STRPTR)"Revert", (STRPTR)"R", 0, 0L, (APTR)PrefsRevert, + NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL, + NM_ITEM, (STRPTR)"OK", NULL, 0, 0L, (APTR)PrefsOK, + NM_ITEM, (STRPTR)"Cancel", NULL, 0, 0L, (APTR)PrefsCancel, + NM_END, NULL, NULL, 0, 0L, NULL }; + +UWORD PrefsGTypes[] = { + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + INTEGER_KIND, + INTEGER_KIND, + INTEGER_KIND, + INTEGER_KIND, + INTEGER_KIND, + CYCLE_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND +}; + +struct NewGadget PrefsNGad[] = { + 8, 4, 26, 11, (UBYTE *)"Sprite display", NULL, GD_SpritesOn, PLACETEXT_RIGHT, NULL, (APTR)SpritesOnClicked, + 8, 16, 26, 11, (UBYTE *)"Sprite collisions", NULL, GD_SpriteCollisions, PLACETEXT_RIGHT, NULL, (APTR)SpriteCollisionsClicked, + 8, 28, 26, 11, (UBYTE *)"Joystick connected", NULL, GD_Joystick2On, PLACETEXT_RIGHT, NULL, (APTR)Joystick2OnClicked, + 8, 40, 26, 11, (UBYTE *)"Map joystick to port 1", NULL, GD_JoystickSwap, PLACETEXT_RIGHT, NULL, (APTR)JoystickSwapClicked, + 8, 52, 26, 11, (UBYTE *)"Limit speed", NULL, GD_LimitSpeed, PLACETEXT_RIGHT, NULL, (APTR)LimitSpeedClicked, + 8, 64, 26, 11, (UBYTE *)"Fast reset", NULL, GD_FastReset, PLACETEXT_RIGHT, NULL, (APTR)FastResetClicked, + 8, 76, 26, 11, (UBYTE *)"Clear CIA ICR on write", NULL, GD_CIAIRQHack, PLACETEXT_RIGHT, NULL, (APTR)CIAIRQHackClicked, + 8, 88, 26, 11, (UBYTE *)"SID filters", NULL, GD_SIDFilters, PLACETEXT_RIGHT, NULL, (APTR)SIDFiltersClicked, + 490, 4, 65, 14, (UBYTE *)"Cycles per line (CPU)", NULL, GD_NormalCycles, PLACETEXT_LEFT, NULL, (APTR)NormalCyclesClicked, + 490, 19, 65, 14, (UBYTE *)"Cycles per Bad Line (CPU)", NULL, GD_BadLineCycles, PLACETEXT_LEFT, NULL, (APTR)BadLineCyclesClicked, + 490, 34, 65, 14, (UBYTE *)"Cycles per line (CIA)", NULL, GD_CIACycles, PLACETEXT_LEFT, NULL, (APTR)CIACyclesClicked, + 490, 49, 65, 14, (UBYTE *)"Cycles per line (1541)", NULL, GD_FloppyCycles, PLACETEXT_LEFT, NULL, (APTR)FloppyCyclesClicked, + 490, 64, 65, 14, (UBYTE *)"Draw every n-th frame", NULL, GD_SkipFrames, PLACETEXT_LEFT, NULL, (APTR)SkipFramesClicked, + 426, 79, 129, 14, (UBYTE *)"SID emulation type", NULL, GD_SIDType, PLACETEXT_LEFT, NULL, (APTR)SIDTypeClicked, + 426, 94, 129, 14, (UBYTE *)"REU size", NULL, GD_REUSize, PLACETEXT_LEFT, NULL, (APTR)REUSizeClicked, + 47, 123, 401, 14, (UBYTE *)"8", NULL, GD_DrivePath8, PLACETEXT_LEFT, NULL, (APTR)DrivePath8Clicked, + 470, 123, 65, 14, NULL, NULL, GD_DriveType8, 0, NULL, (APTR)DriveType8Clicked, + 47, 138, 401, 14, (UBYTE *)"9", NULL, GD_DrivePath9, PLACETEXT_LEFT, NULL, (APTR)DrivePath9Clicked, + 470, 138, 65, 14, NULL, NULL, GD_DriveType9, 0, NULL, (APTR)DriveType9Clicked, + 47, 153, 401, 14, (UBYTE *)"10", NULL, GD_DrivePath10, PLACETEXT_LEFT, NULL, (APTR)DrivePath10Clicked, + 470, 153, 65, 14, NULL, NULL, GD_DriveType10, 0, NULL, (APTR)DriveType10Clicked, + 47, 168, 401, 14, (UBYTE *)"11", NULL, GD_DrivePath11, PLACETEXT_LEFT, NULL, (APTR)DrivePath11Clicked, + 470, 168, 65, 14, NULL, NULL, GD_DriveType11, 0, NULL, (APTR)DriveType11Clicked, + 20, 186, 26, 11, (UBYTE *)"Map '/'<->'\' in filenames", NULL, GD_MapSlash, PLACETEXT_RIGHT, NULL, (APTR)MapSlashClicked, + 20, 198, 26, 11, (UBYTE *)"Enable 1541 processor emulation", NULL, GD_Emul1541Proc, PLACETEXT_RIGHT, NULL, (APTR)Emul1541ProcClicked, + 61, 218, 81, 16, (UBYTE *)"_OK", NULL, GD_OK, PLACETEXT_IN, NULL, (APTR)OKClicked, + 416, 218, 81, 16, (UBYTE *)"_Cancel", NULL, GD_Cancel, PLACETEXT_IN, NULL, (APTR)CancelClicked, + 448, 123, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive8, PLACETEXT_IN, NULL, (APTR)GetDrive8Clicked, + 448, 138, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive9, PLACETEXT_IN, NULL, (APTR)GetDrive9Clicked, + 448, 153, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive10, PLACETEXT_IN, NULL, (APTR)GetDrive10Clicked, + 448, 168, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive11, PLACETEXT_IN, NULL, (APTR)GetDrive11Clicked +}; + +ULONG PrefsGTags[] = { + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTCY_Labels), (ULONG)&SIDType0Labels[ 0 ], (TAG_DONE), + (GTCY_Labels), (ULONG)&REUSize0Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType80Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType90Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType100Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType110Labels[ 0 ], (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (GT_Underscore), '_', (TAG_DONE), + (GT_Underscore), '_', (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE) +}; + +static UWORD ComputeX( UWORD value ) +{ + return(( UWORD )((( FontX * value ) + 4 ) / 8 )); +} + +static UWORD ComputeY( UWORD value ) +{ + return(( UWORD )((( FontY * value ) + 4 ) / 8 )); +} + +static void ComputeFont( UWORD width, UWORD height ) +{ + Font = &Attr; + Font->ta_Name = (STRPTR)Scr->RastPort.Font->tf_Message.mn_Node.ln_Name; + Font->ta_YSize = FontY = Scr->RastPort.Font->tf_YSize; + FontX = Scr->RastPort.Font->tf_XSize; + + OffX = Scr->WBorLeft; + OffY = Scr->RastPort.TxHeight + Scr->WBorTop + 1; + + if ( width && height ) { + if (( ComputeX( width ) + OffX + Scr->WBorRight ) > Scr->Width ) + goto UseTopaz; + if (( ComputeY( height ) + OffY + Scr->WBorBottom ) > Scr->Height ) + goto UseTopaz; + } + return; + +UseTopaz: + Font->ta_Name = (STRPTR)"topaz.font"; + FontX = FontY = Font->ta_YSize = 8; +} + +int SetupScreen( void ) +{ + if ( ! ( Scr = LockPubScreen( PubScreenName ))) + return( 1L ); + + ComputeFont( 0, 0 ); + + if ( ! ( VisualInfo = GetVisualInfo( Scr, TAG_DONE ))) + return( 2L ); + + return( 0L ); +} + +void CloseDownScreen( void ) +{ + if ( VisualInfo ) { + FreeVisualInfo( VisualInfo ); + VisualInfo = NULL; + } + + if ( Scr ) { + UnlockPubScreen( NULL, Scr ); + Scr = NULL; + } +} + +void PrefsRender( void ) +{ + struct IntuiText it; + UWORD cnt; + + ComputeFont( PrefsWidth, PrefsHeight ); + + + for ( cnt = 0; cnt < Prefs_TNUM; cnt++ ) { + CopyMem(( char * )&PrefsIText[ cnt ], ( char * )&it, (long)sizeof( struct IntuiText )); + it.ITextFont = Font; + it.LeftEdge = OffX + ComputeX( it.LeftEdge ) - ( IntuiTextLength( &it ) >> 1 ); + it.TopEdge = OffY + ComputeY( it.TopEdge ) - ( Font->ta_YSize >> 1 ); + PrintIText( PrefsWnd->RPort, &it, 0, 0 ); + } +} + +int HandlePrefsIDCMP( void ) +{ + struct IntuiMessage *m; + struct MenuItem *n; + int (*func)(); + BOOL running = TRUE; + + while( m = GT_GetIMsg( PrefsWnd->UserPort )) { + + CopyMem(( char * )m, ( char * )&PrefsMsg, (long)sizeof( struct IntuiMessage )); + + GT_ReplyIMsg( m ); + + switch ( PrefsMsg.Class ) { + + case IDCMP_REFRESHWINDOW: + GT_BeginRefresh( PrefsWnd ); + PrefsRender(); + GT_EndRefresh( PrefsWnd, TRUE ); + break; + + case IDCMP_VANILLAKEY: + running = PrefsVanillaKey(); + break; + + case IDCMP_GADGETUP: + func = ( void * )(( struct Gadget * )PrefsMsg.IAddress )->UserData; + running = func(); + break; + + case IDCMP_MENUPICK: + while( PrefsMsg.Code != MENUNULL ) { + n = ItemAddress( PrefsMenus, PrefsMsg.Code ); + func = (void *)(GTMENUITEM_USERDATA( n )); + running = func(); + PrefsMsg.Code = n->NextSelect; + } + break; + } + } + return( running ); +} + +int OpenPrefsWindow( void ) +{ + struct NewGadget ng; + struct Gadget *g; + UWORD lc, tc; + UWORD wleft = PrefsLeft, wtop = PrefsTop, ww, wh; + + ComputeFont( PrefsWidth, PrefsHeight ); + + ww = ComputeX( PrefsWidth ); + wh = ComputeY( PrefsHeight ); + + if (( wleft + ww + OffX + Scr->WBorRight ) > Scr->Width ) wleft = Scr->Width - ww; + if (( wtop + wh + OffY + Scr->WBorBottom ) > Scr->Height ) wtop = Scr->Height - wh; + + if ( ! ( g = CreateContext( &PrefsGList ))) + return( 1L ); + + for( lc = 0, tc = 0; lc < Prefs_CNT; lc++ ) { + + CopyMem((char * )&PrefsNGad[ lc ], (char * )&ng, (long)sizeof( struct NewGadget )); + + ng.ng_VisualInfo = VisualInfo; + ng.ng_TextAttr = Font; + ng.ng_LeftEdge = OffX + ComputeX( ng.ng_LeftEdge ); + ng.ng_TopEdge = OffY + ComputeY( ng.ng_TopEdge ); + ng.ng_Width = ComputeX( ng.ng_Width ); + ng.ng_Height = ComputeY( ng.ng_Height); + + PrefsGadgets[ lc ] = g = CreateGadgetA((ULONG)PrefsGTypes[ lc ], g, &ng, ( struct TagItem * )&PrefsGTags[ tc ] ); + + while( PrefsGTags[ tc ] ) tc += 2; + tc++; + + if ( NOT g ) + return( 2L ); + } + + if ( ! ( PrefsMenus = CreateMenus( PrefsNewMenu, GTMN_FrontPen, 0L, TAG_DONE ))) + return( 3L ); + + LayoutMenus( PrefsMenus, VisualInfo, TAG_DONE ); + + PrefsZoom[0] = PrefsZoom[1] = 0; + if ( PrefsWdt ) + PrefsZoom[2] = TextLength( &Scr->RastPort, (UBYTE *)PrefsWdt, strlen((char *)PrefsWdt )) + 80; + else + PrefsZoom[2] = 80L; + PrefsZoom[3] = Scr->WBorTop + Scr->RastPort.TxHeight + 1; + + if ( ! ( PrefsWnd = OpenWindowTags( NULL, + WA_Left, wleft, + WA_Top, wtop, + WA_Width, ww + OffX + Scr->WBorRight, + WA_Height, wh + OffY + Scr->WBorBottom, + WA_IDCMP, CHECKBOXIDCMP|INTEGERIDCMP|CYCLEIDCMP|STRINGIDCMP|BUTTONIDCMP|IDCMP_MENUPICK|IDCMP_VANILLAKEY|IDCMP_REFRESHWINDOW, + WA_Flags, WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_SMART_REFRESH|WFLG_SIMPLE_REFRESH|WFLG_ACTIVATE, + WA_Gadgets, PrefsGList, + WA_Title, PrefsWdt, + WA_ScreenTitle, "Frodo C64 Emulator", + WA_PubScreen, Scr, + WA_Zoom, PrefsZoom, + TAG_DONE ))) + return( 4L ); + + SetMenuStrip( PrefsWnd, PrefsMenus ); + GT_RefreshWindow( PrefsWnd, NULL ); + + PrefsRender(); + + return( 0L ); +} + +void ClosePrefsWindow( void ) +{ + if ( PrefsMenus ) { + ClearMenuStrip( PrefsWnd ); + FreeMenus( PrefsMenus ); + PrefsMenus = NULL; } + + if ( PrefsWnd ) { + CloseWindow( PrefsWnd ); + PrefsWnd = NULL; + } + + if ( PrefsGList ) { + FreeGadgets( PrefsGList ); + PrefsGList = NULL; + } +} + diff --git a/Src/AmigaGUI.gui b/Src/AmigaGUI.gui new file mode 100644 index 0000000..162c72c Binary files /dev/null and b/Src/AmigaGUI.gui differ diff --git a/Src/AmigaGUI.h b/Src/AmigaGUI.h new file mode 100644 index 0000000..3009b45 --- /dev/null +++ b/Src/AmigaGUI.h @@ -0,0 +1,153 @@ +/* + * Source machine generated by GadToolsBox V2.0b + * which is (c) Copyright 1991-1993 Jaba Development + * + * GUI Designed by : Christian Bauer + */ + +#define GetString( g ) ((( struct StringInfo * )g->SpecialInfo )->Buffer ) +#define GetNumber( g ) ((( struct StringInfo * )g->SpecialInfo )->LongInt ) + +#define GD_SpritesOn 0 +#define GD_SpriteCollisions 1 +#define GD_Joystick2On 2 +#define GD_JoystickSwap 3 +#define GD_LimitSpeed 4 +#define GD_FastReset 5 +#define GD_CIAIRQHack 6 +#define GD_SIDFilters 7 +#define GD_NormalCycles 8 +#define GD_BadLineCycles 9 +#define GD_CIACycles 10 +#define GD_FloppyCycles 11 +#define GD_SkipFrames 12 +#define GD_SIDType 13 +#define GD_REUSize 14 +#define GD_DrivePath8 15 +#define GD_DriveType8 16 +#define GD_DrivePath9 17 +#define GD_DriveType9 18 +#define GD_DrivePath10 19 +#define GD_DriveType10 20 +#define GD_DrivePath11 21 +#define GD_DriveType11 22 +#define GD_MapSlash 23 +#define GD_Emul1541Proc 24 +#define GD_OK 25 +#define GD_Cancel 26 +#define GD_GetDrive8 27 +#define GD_GetDrive9 28 +#define GD_GetDrive10 29 +#define GD_GetDrive11 30 + +#define GDX_SpritesOn 0 +#define GDX_SpriteCollisions 1 +#define GDX_Joystick2On 2 +#define GDX_JoystickSwap 3 +#define GDX_LimitSpeed 4 +#define GDX_FastReset 5 +#define GDX_CIAIRQHack 6 +#define GDX_SIDFilters 7 +#define GDX_NormalCycles 8 +#define GDX_BadLineCycles 9 +#define GDX_CIACycles 10 +#define GDX_FloppyCycles 11 +#define GDX_SkipFrames 12 +#define GDX_SIDType 13 +#define GDX_REUSize 14 +#define GDX_DrivePath8 15 +#define GDX_DriveType8 16 +#define GDX_DrivePath9 17 +#define GDX_DriveType9 18 +#define GDX_DrivePath10 19 +#define GDX_DriveType10 20 +#define GDX_DrivePath11 21 +#define GDX_DriveType11 22 +#define GDX_MapSlash 23 +#define GDX_Emul1541Proc 24 +#define GDX_OK 25 +#define GDX_Cancel 26 +#define GDX_GetDrive8 27 +#define GDX_GetDrive9 28 +#define GDX_GetDrive10 29 +#define GDX_GetDrive11 30 + +#define Prefs_CNT 31 + +extern struct IntuitionBase *IntuitionBase; +extern struct Library *GadToolsBase; + +extern struct Screen *Scr; +extern UBYTE *PubScreenName; +extern APTR VisualInfo; +extern struct Window *PrefsWnd; +extern struct Gadget *PrefsGList; +extern struct Menu *PrefsMenus; +extern struct IntuiMessage PrefsMsg; +extern UWORD PrefsZoom[4]; +extern struct Gadget *PrefsGadgets[31]; +extern UWORD PrefsLeft; +extern UWORD PrefsTop; +extern UWORD PrefsWidth; +extern UWORD PrefsHeight; +extern UBYTE *PrefsWdt; +extern struct TextAttr *Font, Attr; +extern UWORD FontX, FontY; +extern UWORD OffX, OffY; +extern UBYTE *SIDType0Labels[]; +extern UBYTE *REUSize0Labels[]; +extern UBYTE *DriveType80Labels[]; +extern UBYTE *DriveType90Labels[]; +extern UBYTE *DriveType100Labels[]; +extern UBYTE *DriveType110Labels[]; +extern struct IntuiText PrefsIText[]; +extern struct NewMenu PrefsNewMenu[]; +extern UWORD PrefsGTypes[]; +extern struct NewGadget PrefsNGad[]; +extern ULONG PrefsGTags[]; + +extern int SpritesOnClicked( void ); +extern int SpriteCollisionsClicked( void ); +extern int Joystick2OnClicked( void ); +extern int JoystickSwapClicked( void ); +extern int LimitSpeedClicked( void ); +extern int FastResetClicked( void ); +extern int CIAIRQHackClicked( void ); +extern int SIDFiltersClicked( void ); +extern int NormalCyclesClicked( void ); +extern int BadLineCyclesClicked( void ); +extern int CIACyclesClicked( void ); +extern int FloppyCyclesClicked( void ); +extern int SkipFramesClicked( void ); +extern int SIDTypeClicked( void ); +extern int REUSizeClicked( void ); +extern int DrivePath8Clicked( void ); +extern int DriveType8Clicked( void ); +extern int DrivePath9Clicked( void ); +extern int DriveType9Clicked( void ); +extern int DrivePath10Clicked( void ); +extern int DriveType10Clicked( void ); +extern int DrivePath11Clicked( void ); +extern int DriveType11Clicked( void ); +extern int MapSlashClicked( void ); +extern int Emul1541ProcClicked( void ); +extern int OKClicked( void ); +extern int CancelClicked( void ); +extern int GetDrive8Clicked( void ); +extern int GetDrive9Clicked( void ); +extern int GetDrive10Clicked( void ); +extern int GetDrive11Clicked( void ); +extern int PrefsOpen( void ); +extern int PrefsSave( void ); +extern int PrefsSaveAs( void ); +extern int PrefsRevert( void ); +extern int PrefsOK( void ); +extern int PrefsCancel( void ); + +extern int SetupScreen( void ); +extern void CloseDownScreen( void ); +extern void PrefsRender( void ); +extern int HandlePrefsIDCMP( void ); +extern int PrefsVanillaKey(); +extern int OpenPrefsWindow( void ); +extern void ClosePrefsWindow( void ); diff --git a/Src/C64.cpp b/Src/C64.cpp new file mode 100644 index 0000000..b29dee4 --- /dev/null +++ b/Src/C64.cpp @@ -0,0 +1,714 @@ +/* + * C64.cpp - Put the pieces together + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysdeps.h" + +#include "C64.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" +#include "REU.h" +#include "IEC.h" +#include "1541job.h" +#include "Display.h" +#include "Prefs.h" + +#if defined(__unix) && !defined(__svgalib__) +#include "CmdPipe.h" +#endif + + +#ifdef FRODO_SC +bool IsFrodoSC = true; +#else +bool IsFrodoSC = false; +#endif + + +/* + * Constructor: Allocate objects and memory + */ + +C64::C64() +{ + int i,j; + uint8 *p; + + // The thread is not yet running + thread_running = false; + quit_thyself = false; + have_a_break = false; + + // System-dependent things + c64_ctor1(); + + // Open display + TheDisplay = new C64Display(this); + + // Allocate RAM/ROM memory + RAM = new uint8[0x10000]; + Basic = new uint8[0x2000]; + Kernal = new uint8[0x2000]; + Char = new uint8[0x1000]; + Color = new uint8[0x0400]; + RAM1541 = new uint8[0x0800]; + ROM1541 = new uint8[0x4000]; + + // Create the chips + TheCPU = new MOS6510(this, RAM, Basic, Kernal, Char, Color); + + TheJob1541 = new Job1541(RAM1541); + TheCPU1541 = new MOS6502_1541(this, TheJob1541, TheDisplay, RAM1541, ROM1541); + + TheVIC = TheCPU->TheVIC = new MOS6569(this, TheDisplay, TheCPU, RAM, Char, Color); + TheSID = TheCPU->TheSID = new MOS6581(this); + TheCIA1 = TheCPU->TheCIA1 = new MOS6526_1(TheCPU, TheVIC); + TheCIA2 = TheCPU->TheCIA2 = TheCPU1541->TheCIA2 = new MOS6526_2(TheCPU, TheVIC, TheCPU1541); + TheIEC = TheCPU->TheIEC = new IEC(TheDisplay); + TheREU = TheCPU->TheREU = new REU(TheCPU); + + // Initialize RAM with powerup pattern + for (i=0, p=RAM; i<512; i++) { + for (j=0; j<64; j++) + *p++ = 0; + for (j=0; j<64; j++) + *p++ = 0xff; + } + + // Initialize color RAM with random values + for (i=0, p=Color; i<1024; i++) + *p++ = rand() & 0x0f; + + // Clear 1541 RAM + memset(RAM1541, 0, 0x800); + + // Open joystick drivers if required + open_close_joysticks(false, false, ThePrefs.Joystick1On, ThePrefs.Joystick2On); + joykey = 0xff; + +#ifdef FRODO_SC + CycleCounter = 0; +#endif + + // System-dependent things + c64_ctor2(); +} + + +/* + * Destructor: Delete all objects + */ + +C64::~C64() +{ + open_close_joysticks(ThePrefs.Joystick1On, ThePrefs.Joystick2On, false, false); + + delete TheJob1541; + delete TheREU; + delete TheIEC; + delete TheCIA2; + delete TheCIA1; + delete TheSID; + delete TheVIC; + delete TheCPU1541; + delete TheCPU; + delete TheDisplay; + + delete[] RAM; + delete[] Basic; + delete[] Kernal; + delete[] Char; + delete[] Color; + delete[] RAM1541; + delete[] ROM1541; + + c64_dtor(); +} + + +/* + * Reset C64 + */ + +void C64::Reset(void) +{ + TheCPU->AsyncReset(); + TheCPU1541->AsyncReset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheIEC->Reset(); +} + + +/* + * NMI C64 + */ + +void C64::NMI(void) +{ + TheCPU->AsyncNMI(); +} + + +/* + * The preferences have changed. prefs is a pointer to the new + * preferences, ThePrefs still holds the previous ones. + * The emulation must be in the paused state! + */ + +void C64::NewPrefs(Prefs *prefs) +{ + open_close_joysticks(ThePrefs.Joystick1On, ThePrefs.Joystick2On, prefs->Joystick1On, prefs->Joystick2On); + PatchKernal(prefs->FastReset, prefs->Emul1541Proc); + + TheDisplay->NewPrefs(prefs); + +#ifdef __riscos__ + // Changed order of calls. If 1541 mode hasn't changed the order is insignificant. + if (prefs->Emul1541Proc) { + // New prefs have 1541 enabled ==> if old prefs had disabled free drives FIRST + TheIEC->NewPrefs(prefs); + TheJob1541->NewPrefs(prefs); + } else { + // New prefs has 1541 disabled ==> if old prefs had enabled free job FIRST + TheJob1541->NewPrefs(prefs); + TheIEC->NewPrefs(prefs); + } +#else + TheIEC->NewPrefs(prefs); + TheJob1541->NewPrefs(prefs); +#endif + + TheREU->NewPrefs(prefs); + TheSID->NewPrefs(prefs); + + // Reset 1541 processor if turned on + if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc) + TheCPU1541->AsyncReset(); +} + + +/* + * Patch kernal IEC routines + */ + +void C64::PatchKernal(bool fast_reset, bool emul_1541_proc) +{ + if (fast_reset) { + Kernal[0x1d84] = 0xa0; + Kernal[0x1d85] = 0x00; + } else { + Kernal[0x1d84] = orig_kernal_1d84; + Kernal[0x1d85] = orig_kernal_1d85; + } + + if (emul_1541_proc) { + Kernal[0x0d40] = 0x78; + Kernal[0x0d41] = 0x20; + Kernal[0x0d23] = 0x78; + Kernal[0x0d24] = 0x20; + Kernal[0x0d36] = 0x78; + Kernal[0x0d37] = 0x20; + Kernal[0x0e13] = 0x78; + Kernal[0x0e14] = 0xa9; + Kernal[0x0def] = 0x78; + Kernal[0x0df0] = 0x20; + Kernal[0x0dbe] = 0xad; + Kernal[0x0dbf] = 0x00; + Kernal[0x0dcc] = 0x78; + Kernal[0x0dcd] = 0x20; + Kernal[0x0e03] = 0x20; + Kernal[0x0e04] = 0xbe; + } else { + Kernal[0x0d40] = 0xf2; // IECOut + Kernal[0x0d41] = 0x00; + Kernal[0x0d23] = 0xf2; // IECOutATN + Kernal[0x0d24] = 0x01; + Kernal[0x0d36] = 0xf2; // IECOutSec + Kernal[0x0d37] = 0x02; + Kernal[0x0e13] = 0xf2; // IECIn + Kernal[0x0e14] = 0x03; + Kernal[0x0def] = 0xf2; // IECSetATN + Kernal[0x0df0] = 0x04; + Kernal[0x0dbe] = 0xf2; // IECRelATN + Kernal[0x0dbf] = 0x05; + Kernal[0x0dcc] = 0xf2; // IECTurnaround + Kernal[0x0dcd] = 0x06; + Kernal[0x0e03] = 0xf2; // IECRelease + Kernal[0x0e04] = 0x07; + } + + // 1541 + ROM1541[0x2ae4] = 0xea; // Don't check ROM checksum + ROM1541[0x2ae5] = 0xea; + ROM1541[0x2ae8] = 0xea; + ROM1541[0x2ae9] = 0xea; + ROM1541[0x2c9b] = 0xf2; // DOS idle loop + ROM1541[0x2c9c] = 0x00; + ROM1541[0x3594] = 0x20; // Write sector + ROM1541[0x3595] = 0xf2; + ROM1541[0x3596] = 0xf5; + ROM1541[0x3597] = 0xf2; + ROM1541[0x3598] = 0x01; + ROM1541[0x3b0c] = 0xf2; // Format track + ROM1541[0x3b0d] = 0x02; +} + + +/* + * Save RAM contents + */ + +void C64::SaveRAM(char *filename) +{ + FILE *f; + + if ((f = fopen(filename, "wb")) == NULL) + ShowRequester("RAM save failed.", "OK", NULL); + else { + fwrite((void*)RAM, 1, 0x10000, f); + fwrite((void*)Color, 1, 0x400, f); + if (ThePrefs.Emul1541Proc) + fwrite((void*)RAM1541, 1, 0x800, f); + fclose(f); + } +} + + +/* + * Save CPU state to snapshot + * + * 0: Error + * 1: OK + * -1: Instruction not completed + */ + +int C64::SaveCPUState(FILE *f) +{ + MOS6510State state; + TheCPU->GetState(&state); + + if (!state.instruction_complete) + return -1; + + int i = fwrite(RAM, 0x10000, 1, f); + i += fwrite(Color, 0x400, 1, f); + i += fwrite((void*)&state, sizeof(state), 1, f); + + return i == 3; +} + + +/* + * Load CPU state from snapshot + */ + +bool C64::LoadCPUState(FILE *f) +{ + MOS6510State state; + + int i = fread(RAM, 0x10000, 1, f); + i += fread(Color, 0x400, 1, f); + i += fread((void*)&state, sizeof(state), 1, f); + + if (i == 3) { + TheCPU->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save 1541 state to snapshot + * + * 0: Error + * 1: OK + * -1: Instruction not completed + */ + +int C64::Save1541State(FILE *f) +{ + MOS6502State state; + TheCPU1541->GetState(&state); + + if (!state.idle && !state.instruction_complete) + return -1; + + int i = fwrite(RAM1541, 0x800, 1, f); + i += fwrite((void*)&state, sizeof(state), 1, f); + + return i == 2; +} + + +/* + * Load 1541 state from snapshot + */ + +bool C64::Load1541State(FILE *f) +{ + MOS6502State state; + + int i = fread(RAM1541, 0x800, 1, f); + i += fread((void*)&state, sizeof(state), 1, f); + + if (i == 2) { + TheCPU1541->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save VIC state to snapshot + */ + +bool C64::SaveVICState(FILE *f) +{ + MOS6569State state; + TheVIC->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; +} + + +/* + * Load VIC state from snapshot + */ + +bool C64::LoadVICState(FILE *f) +{ + MOS6569State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheVIC->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save SID state to snapshot + */ + +bool C64::SaveSIDState(FILE *f) +{ + MOS6581State state; + TheSID->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; +} + + +/* + * Load SID state from snapshot + */ + +bool C64::LoadSIDState(FILE *f) +{ + MOS6581State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheSID->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save CIA states to snapshot + */ + +bool C64::SaveCIAState(FILE *f) +{ + MOS6526State state; + TheCIA1->GetState(&state); + + if (fwrite((void*)&state, sizeof(state), 1, f) == 1) { + TheCIA2->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; + } else + return false; +} + + +/* + * Load CIA states from snapshot + */ + +bool C64::LoadCIAState(FILE *f) +{ + MOS6526State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheCIA1->SetState(&state); + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheCIA2->SetState(&state); + return true; + } else + return false; + } else + return false; +} + + +/* + * Save 1541 GCR state to snapshot + */ + +bool C64::Save1541JobState(FILE *f) +{ + Job1541State state; + TheJob1541->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; +} + + +/* + * Load 1541 GCR state from snapshot + */ + +bool C64::Load1541JobState(FILE *f) +{ + Job1541State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheJob1541->SetState(&state); + return true; + } else + return false; +} + + +#define SNAPSHOT_HEADER "FrodoSnapshot" +#define SNAPSHOT_1541 1 + +#define ADVANCE_CYCLES \ + TheVIC->EmulateCycle(); \ + TheCIA1->EmulateCycle(); \ + TheCIA2->EmulateCycle(); \ + TheCPU->EmulateCycle(); \ + if (ThePrefs.Emul1541Proc) { \ + TheCPU1541->CountVIATimers(1); \ + if (!TheCPU1541->Idle) \ + TheCPU1541->EmulateCycle(); \ + } + + +/* + * Save snapshot (emulation must be paused and in VBlank) + * + * To be able to use SC snapshots with SL, SC snapshots are made thus that no + * partially dealt with instructions are saved. Instead all devices are advanced + * cycle by cycle until the current instruction has been finished. The number of + * cycles this takes is saved in the snapshot and will be reconstructed if the + * snapshot is loaded into FrodoSC again. + */ + +void C64::SaveSnapshot(char *filename) +{ + FILE *f; + uint8 flags; + uint8 delay; + int stat; + + if ((f = fopen(filename, "wb")) == NULL) { + ShowRequester("Unable to open snapshot file", "OK", NULL); + return; + } + + fprintf(f, "%s%c", SNAPSHOT_HEADER, 10); + fputc(0, f); // Version number 0 + flags = 0; + if (ThePrefs.Emul1541Proc) + flags |= SNAPSHOT_1541; + fputc(flags, f); + SaveVICState(f); + SaveSIDState(f); + SaveCIAState(f); + +#ifdef FRODO_SC + delay = 0; + do { + if ((stat = SaveCPUState(f)) == -1) { // -1 -> Instruction not finished yet + ADVANCE_CYCLES; // Advance everything by one cycle + delay++; + } + } while (stat == -1); + fputc(delay, f); // Number of cycles the saved CPUC64 lags behind the previous chips +#else + SaveCPUState(f); + fputc(0, f); // No delay +#endif + + if (ThePrefs.Emul1541Proc) { + fwrite(ThePrefs.DrivePath[0], 256, 1, f); +#ifdef FRODO_SC + delay = 0; + do { + if ((stat = Save1541State(f)) == -1) { + ADVANCE_CYCLES; + delay++; + } + } while (stat == -1); + fputc(delay, f); +#else + Save1541State(f); + fputc(0, f); // No delay +#endif + Save1541JobState(f); + } + fclose(f); + +#ifdef __riscos__ + TheWIMP->SnapshotSaved(true); +#endif +} + + +/* + * Load snapshot (emulation must be paused and in VBlank) + */ + +bool C64::LoadSnapshot(char *filename) +{ + FILE *f; + + if ((f = fopen(filename, "rb")) != NULL) { + char Header[] = SNAPSHOT_HEADER; + char *b = Header, c = 0; + uint8 delay, i; + + // For some reason memcmp()/strcmp() and so forth utterly fail here. + while (*b > 32) { + if ((c = fgetc(f)) != *b++) { + b = NULL; + break; + } + } + if (b != NULL) { + uint8 flags; + bool error = false; +#ifndef FRODO_SC + long vicptr; // File offset of VIC data +#endif + + while (c != 10) + c = fgetc(f); // Shouldn't be necessary + if (fgetc(f) != 0) { + ShowRequester("Unknown snapshot format", "OK", NULL); + fclose(f); + return false; + } + flags = fgetc(f); +#ifndef FRODO_SC + vicptr = ftell(f); +#endif + + error |= !LoadVICState(f); + error |= !LoadSIDState(f); + error |= !LoadCIAState(f); + error |= !LoadCPUState(f); + + delay = fgetc(f); // Number of cycles the 6510 is ahead of the previous chips +#ifdef FRODO_SC + // Make the other chips "catch up" with the 6510 + for (i=0; iEmulateCycle(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + } +#endif + if ((flags & SNAPSHOT_1541) != 0) { + Prefs *prefs = new Prefs(ThePrefs); + + // First switch on emulation + error |= (fread(prefs->DrivePath[0], 256, 1, f) != 1); + prefs->Emul1541Proc = true; + NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + + // Then read the context + error |= !Load1541State(f); + + delay = fgetc(f); // Number of cycles the 6502 is ahead of the previous chips +#ifdef FRODO_SC + // Make the other chips "catch up" with the 6502 + for (i=0; iEmulateCycle(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + } +#endif + Load1541JobState(f); +#ifdef __riscos__ + TheWIMP->ThePrefsToWindow(); +#endif + } else if (ThePrefs.Emul1541Proc) { // No emulation in snapshot, but currently active? + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = false; + NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; +#ifdef __riscos__ + TheWIMP->ThePrefsToWindow(); +#endif + } + +#ifndef FRODO_SC + fseek(f, vicptr, SEEK_SET); + LoadVICState(f); // Load VIC data twice in SL (is REALLY necessary sometimes!) +#endif + fclose(f); + + if (error) { + ShowRequester("Error reading snapshot file", "OK", NULL); + Reset(); + return false; + } else + return true; + } else { + fclose(f); + ShowRequester("Not a Frodo snapshot file", "OK", NULL); + return false; + } + } else { + ShowRequester("Can't open snapshot file", "OK", NULL); + return false; + } +} + + +#ifdef __BEOS__ +#include "C64_Be.i" +#endif + +#ifdef AMIGA +#include "C64_Amiga.i" +#endif + +#ifdef __unix +#include "C64_x.i" +#endif + +#ifdef __mac__ +#include "C64_mac.i" +#endif + +#ifdef WIN32 +#include "C64_WIN32.i" +#endif + +#ifdef __riscos__ +#include "C64_Acorn.i" +#endif diff --git a/Src/C64.h b/Src/C64.h new file mode 100644 index 0000000..83d8d16 --- /dev/null +++ b/Src/C64.h @@ -0,0 +1,198 @@ +/* + * C64.h - Put the pieces together + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _C64_H +#define _C64_H + +#ifdef __BEOS__ +#include +#include +#endif + +#ifdef AMIGA +#include +#include +#include +#endif + +#ifdef __riscos__ +#include "ROlib.h" +#endif + + +// false: Frodo, true: FrodoSC +extern bool IsFrodoSC; + + +class Prefs; +class C64Display; +class MOS6510; +class MOS6569; +class MOS6581; +class MOS6526_1; +class MOS6526_2; +class IEC; +class REU; +class MOS6502_1541; +class Job1541; +class CmdPipe; + +class C64 { +public: + C64(); + ~C64(); + + void Run(void); + void Quit(void); + void Pause(void); + void Resume(void); + void Reset(void); + void NMI(void); + void VBlank(bool draw_frame); + void NewPrefs(Prefs *prefs); + void PatchKernal(bool fast_reset, bool emul_1541_proc); + void SaveRAM(char *filename); + void SaveSnapshot(char *filename); + bool LoadSnapshot(char *filename); + int SaveCPUState(FILE *f); + int Save1541State(FILE *f); + bool Save1541JobState(FILE *f); + bool SaveVICState(FILE *f); + bool SaveSIDState(FILE *f); + bool SaveCIAState(FILE *f); + bool LoadCPUState(FILE *f); + bool Load1541State(FILE *f); + bool Load1541JobState(FILE *f); + bool LoadVICState(FILE *f); + bool LoadSIDState(FILE *f); + bool LoadCIAState(FILE *f); + + uint8 *RAM, *Basic, *Kernal, + *Char, *Color; // C64 + uint8 *RAM1541, *ROM1541; // 1541 + + C64Display *TheDisplay; + + MOS6510 *TheCPU; // C64 + MOS6569 *TheVIC; + MOS6581 *TheSID; + MOS6526_1 *TheCIA1; + MOS6526_2 *TheCIA2; + IEC *TheIEC; + REU *TheREU; + + MOS6502_1541 *TheCPU1541; // 1541 + Job1541 *TheJob1541; + +#ifdef FRODO_SC + uint32 CycleCounter; +#endif + +private: + void c64_ctor1(void); + void c64_ctor2(void); + void c64_dtor(void); + void open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2); + uint8 poll_joystick(int port); + void thread_func(void); + + bool thread_running; // Emulation thread is running + bool quit_thyself; // Emulation thread shall quit + bool have_a_break; // Emulation thread shall pause + + int joy_minx, joy_maxx, joy_miny, joy_maxy; // For dynamic joystick calibration + uint8 joykey; // Joystick keyboard emulation mask value + + uint8 orig_kernal_1d84, // Original contents of kernal locations $1d84 and $1d85 + orig_kernal_1d85; // (for undoing the Fast Reset patch) + +#ifdef __BEOS__ +public: + void SoundSync(void); + +private: + static long thread_invoc(void *obj); + + BJoystick *joy[2]; // Joystick objects + thread_id the_thread; + sem_id pause_sem; + sem_id sound_sync_sem; + double start_time; +#endif + +#ifdef AMIGA + struct MsgPort *timer_port; // For speed limiter + struct timerequest *timer_io; + struct timeval start_time; + struct MsgPort *game_port; // For joystick + struct IOStdReq *game_io; + struct GamePortTrigger game_trigger; + struct InputEvent game_event; + UBYTE joy_state; // Current state of joystick + bool game_open, port_allocated; // Flags: gameport.device opened, game port allocated +#endif + +#ifdef __unix + int joyfd[2]; // File descriptors for joysticks + double speed_index; +public: + CmdPipe *gui; +#endif + +#ifdef WIN32 +private: + void CheckTimerChange(); + void StartTimer(); + void StopTimer(); + static void CALLBACK StaticTimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); + void TimeProc(UINT id); +#ifdef FRODO_SC + void EmulateCyclesWith1541(); + void EmulateCyclesWithout1541(); +#endif + + DWORD ref_time; // when frame count was reset + int skipped_frames; // number of skipped frames + int timer_every; // frequency of timer in frames + HANDLE timer_semaphore; // Timer semaphore for synch + MMRESULT timer_id; // Timer identifier + int frame; // current frame number + uint8 joy_state; // Current state of joystick + bool state_change; +#endif + +#ifdef __riscos__ +public: + void RequestSnapshot(void); + bool LoadOldSnapshot(FILE *f); + void LoadSystemConfig(const char *filename); // loads timing vals and keyboard joys + void SaveSystemConfig(const char *filename); // saves timing vals and keyboard joys + void ReadTimings(int *poll_after, int *speed_after, int *sound_after); + void WriteTimings(int poll_after, int speed_after, int sound_after); + + WIMP *TheWIMP; + int PollAfter; // centiseconds before polling + int SpeedAfter; // centiseconds before updating speedometer + int PollSoundAfter; // *rasterlines* after which DigitalRenderer is polled + int HostVolume; // sound volume of host machine + +private: + bool make_a_snapshot; + + uint8 joykey2; // two keyboard joysticks possible here + + uint8 joystate[2]; // Joystick state + bool Poll; // TRUE if polling should take place + int LastPoll, LastFrame, LastSpeed; // time of last poll / last frame / speedom (cs) + int FramesSince; + int laststate; // last keyboard state (-> scroll lock) + int lastptr; // last mouse pointer shape + bool SingleTasking; +#endif +}; + + +#endif diff --git a/Src/C64_Acorn.i b/Src/C64_Acorn.i new file mode 100644 index 0000000..920e27e --- /dev/null +++ b/Src/C64_Acorn.i @@ -0,0 +1,411 @@ +/* + * C64_Acorn.i + * + * RISC OS specific stuff concerning the actual emulator + * Frodo (C) 1994-1997,2002 Christian Bauer + * Acorn port by Andreas Dehmel, 1997 + * + */ + + +#include "Prefs.h" +#include "ROlib.h" +#include "AcornGUI.h" + + +void C64::LoadSystemConfig(const char *filename) +{ + FILE *fp; + + if ((fp = fopen(filename, "r")) != NULL) + { + int i; + Joy_Keys *jk; + int args[10]; + char line[256]; + + while (fgets(line, 255, fp) != 0) + { + char *b = line; + register char c; + + do {c = *b++;} while (c > 32); + if (c == 32) // keyword mustn't contain spaces + { + *(b-1) = '\0'; + do {c = *b++;} while ((c >= 32) && (c != '=')); + if (c == '=') // read in keyword's arguments + { + int i=0; + + while ((*b != '\0') && (i < 10)) + { + args[i++] = strtol(b, &b, 10); + } + if (strcmp(line, "PollAfter") == 0) {PollAfter = args[0];} + else if (strcmp(line, "SpeedAfter") == 0) {SpeedAfter = args[0];} + else if (strcmp(line, "PollSoundAfter") == 0) {PollSoundAfter = args[0];} + else if (strcmp(line, "JoystickKeys1") == 0) + { + jk = &(TheDisplay->JoystickKeys[0]); + jk->up = args[0]; jk->down = args[1]; jk->left = args[2]; jk->right = args[3]; + jk->fire = args[4]; + } + else if (strcmp(line, "JoystickKeys2") == 0) + { + jk = &(TheDisplay->JoystickKeys[1]); + jk->up = args[0]; jk->down = args[1]; jk->left = args[2]; jk->right = args[3]; + jk->fire = args[4]; + } + else + { + _kernel_oserror err; + + err.errnum = 0; + sprintf(err.errmess,"Bad keyword <%s> in system configure file!",line); + Wimp_ReportError(&err,1,TASKNAME); + } + } + } + } + fclose(fp); + } +} + + +void C64::SaveSystemConfig(const char *filename) +{ + FILE *fp; + + if ((fp = fopen(filename, "w")) != NULL) + { + int i; + Joy_Keys *jk; + + fprintf(fp,"PollAfter = %d\n", PollAfter); + fprintf(fp,"SpeedAfter = %d\n", SpeedAfter); + fprintf(fp,"PollSoundAfter = %d\n", PollSoundAfter); + for (i=0; i<2; i++) + { + jk = &(TheDisplay->JoystickKeys[i]); + fprintf(fp,"JoystickKeys%d",i+1); + fprintf(fp," = %d %d %d %d %d\n", jk->up, jk->down, jk->left, jk->right, jk->fire); + } + fclose(fp); + } +} + + +void C64::ReadTimings(int *poll_after, int *speed_after, int *sound_after) +{ + *poll_after = PollAfter; *speed_after = SpeedAfter; *sound_after = PollSoundAfter; +} + + +void C64::WriteTimings(int poll_after, int speed_after, int sound_after) +{ + PollAfter = poll_after; SpeedAfter = speed_after; PollSoundAfter = sound_after; +} + + +void C64::RequestSnapshot(void) +{ + // Snapshots are only possible if the emulation progresses to the next vsync + if (have_a_break) Resume(); + make_a_snapshot = true; +} + + +void C64::c64_ctor1(void) +{ + TheWIMP = new WIMP(this); + PollAfter = 20; // poll every 20 centiseconds + SpeedAfter = 200; // update speedometer every 2 seconds + PollSoundAfter = 50; // poll DigitalRenderer every 50 lines + HostVolume = Sound_Volume(0); + // Just a precaution + if (HostVolume < 0) {HostVolume = 0;} + if (HostVolume > MaximumVolume) {HostVolume = MaximumVolume;} + Poll = false; + make_a_snapshot = false; +} + + +void C64::c64_ctor2(void) +{ + LoadSystemConfig(DEFAULT_SYSCONF); + // was started from multitasking so pretend ScrollLock OFF no matter what + laststate = (ReadKeyboardStatus() & ~2); SingleTasking = false; + lastptr = 1; +} + + +void C64::c64_dtor(void) +{ + delete TheWIMP; +} + + +void C64::open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2) +{ + // Check if the Joystick module is loaded. If not then write an illegal value to + // the joystick state. + if (Joystick_Read(0) == -2) {joystate[0] = 0;} else {joystate[0] = 0xff;} + if (Joystick_Read(1) == -2) {joystate[1] = 0;} else {joystate[1] = 0xff;} +} + + +uint8 C64::poll_joystick(int port) +{ + register int state; + uint8 joy; + + if ((state = Joystick_Read(port)) != -2) // module present + { + if (state == -1) {joy = joystate[port];} // use old value + else + { + joy = 0xff; + if ((state & (JoyButton1 + JoyButton2)) != 0) {joy &= 0xef;} // fire + if ((state & 0x80) == 0) // positive direction #1 + { + if ((state & 0xff) >= JoyDir_Thresh) {joy &= 0xfe;} // up + } + else + { + if ((256 - (state & 0xff)) >= JoyDir_Thresh) {joy &= 0xfd;} // down + } + if ((state & 0x8000) == 0) // positive direction #2 + { + if ((state & 0xff00) >= JoyDir_Thresh<<8) {joy &= 0xf7;} // right + } + else + { + if ((0x10000 - (state & 0xff00)) >= JoyDir_Thresh<<8) {joy &= 0xfb;} // left + } + } + joystate[port] = joy; return(joy); + } + else + { + joystate[port] = 0; return(0xff); + } +} + + +void C64::VBlank(bool draw_frame) +{ + int Now, KeyState; + bool InputFocus; + + // Poll keyboard if the window has the input focus. + InputFocus = TheWIMP->EmuWindow->HaveInput(); + if (InputFocus) + { + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey, &joykey2); + } + + // Poll Joysticks + TheCIA1->Joystick1 = (ThePrefs.Joystick1On) ? poll_joystick(0) : 0xff; + TheCIA1->Joystick2 = (ThePrefs.Joystick2On) ? poll_joystick(1) : 0xff; + + // Swap joysticks? + if (ThePrefs.JoystickSwap) + { + register uint8 h; + + h = TheCIA1->Joystick1; TheCIA1->Joystick1 = TheCIA1->Joystick2; TheCIA1->Joystick2 = h; + } + + // Read keyboard state directly since we'll also need ScrollLock later! + KeyState = ReadKeyboardStatus(); + if (InputFocus) + { + // Keyboard emulates which joystick? (NumLock ==> Port 2, else Port 1) + if ((KeyState & 4) == 0) + { + TheCIA1->Joystick2 &= joykey; + } + else // joykey2 only mapped if numLOCK is off. + { + TheCIA1->Joystick1 &= joykey; TheCIA1->Joystick2 &= joykey2; + } + } + + if (draw_frame) + { + TheDisplay->Update(); + } + + // Make snapshot? + if (make_a_snapshot) + { + SaveSnapshot((TheWIMP->SnapFile)+44); + make_a_snapshot = false; + } + + Now = OS_ReadMonotonicTime(); + + // Limit speed? (hahaha.... ah well...) + if (ThePrefs.LimitSpeed) + { + int Now; + + while ((Now - LastFrame) < 2) // 2cs per frame = 50fps (original speed) + { + Now = OS_ReadMonotonicTime(); + } + LastFrame = Now; + } + FramesSince++; + + // Update speedometer (update, not force redraw!)? + if ((Now - LastSpeed) >= SpeedAfter) + { + char b[16]; + + if ((Now - LastSpeed) <= 0) {Now = LastSpeed+1;} + // Speed: 100% equals 50fps (round result) + sprintf(b,"%d%%\0",((400*FramesSince)/(Now - LastSpeed) + 1) >> 1); + TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Speed,b); + LastSpeed = Now; FramesSince = 0; + } + + if (InputFocus) + { + // Scroll lock state changed? + if (((KeyState ^ laststate) & 2) != 0) + { + // change to single tasking: turn off mouse, else restore previous pointer + if ((KeyState & 2) != 0) {lastptr = SetMousePointer(0); SingleTasking = true;} + else {SetMousePointer(lastptr); OS_FlushBuffer(9); SingleTasking = false;} + } + if ((KeyState & 2) != 0) {lastptr = SetMousePointer(0);} + else {SetMousePointer(lastptr); OS_FlushBuffer(9);} + } + + // Poll? ScrollLock forces single tasking, i.e. overrides timings. + if (!SingleTasking) + { + if ((Now - LastPoll) >= PollAfter) + { + Poll = true; + } + } + laststate = KeyState; +} + + +void C64::Run(void) +{ + // Resetting chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernel IEC routines (copied from C64_Amiga.i + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start the emulation + thread_running = true; quit_thyself = false; have_a_break = false; + thread_func(); +} + + +void C64::Quit(void) +{ + if (thread_running) + { + quit_thyself = true; thread_running = false; + } +} + + +void C64::Pause(void) +{ + have_a_break = true; TheSID->PauseSound(); +} + + +void C64::Resume(void) +{ + have_a_break = false; TheSID->ResumeSound(); +} + + +void C64::thread_func(void) +{ + LastPoll = LastFrame = LastSpeed = OS_ReadMonotonicTime(); FramesSince = 0; + + while (!quit_thyself) + { +#ifdef FRODO_SC + if (TheVIC->EmulateCycle()) {TheSID->EmulateLine();} + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) + { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) {TheCPU1541->EmulateCycle();} + } + CycleCounter++; + +#else + // Emulate each device one rasterline. Order is important! + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) + { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + if (!TheCPU1541->Idle) + { + while ((cycles >= 0) || (cycles_1541 >= 0)) + { + if (cycles > cycles_1541) {cycles -= TheCPU->EmulateLine(1);} + else {cycles_1541 -= TheCPU1541->EmulateLine(1);} + } + } + else {TheCPU->EmulateLine(cycles);} + } + else + { + TheCPU->EmulateLine(cycles); + } +#endif + + // Single-tasking: busy-wait 'til unpause + while (SingleTasking && have_a_break) + { + int KeyState; + + TheDisplay->CheckForUnpause(true); // unpause? + KeyState = ReadKeyboardStatus(); + if ((KeyState & 2) == 0) // leave single tasking? + { + SetMousePointer(lastptr); OS_FlushBuffer(9); SingleTasking = false; + } + laststate = KeyState; + } + if (!SingleTasking) + { + // The system-specific part of this function + if (Poll || have_a_break) + { + TheWIMP->Poll(have_a_break); + LastPoll = LastFrame = OS_ReadMonotonicTime(); Poll = false; + } + } + } +} diff --git a/Src/C64_Amiga.i b/Src/C64_Amiga.i new file mode 100644 index 0000000..ad8e629 --- /dev/null +++ b/Src/C64_Amiga.i @@ -0,0 +1,393 @@ +/* + * C64_Amiga.i - Put the pieces together, Amiga specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include + + +// Library bases +struct Device *TimerBase; + + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + // Open game_io + game_port = CreateMsgPort(); + game_io = (struct IOStdReq *)CreateIORequest(game_port, sizeof(IOStdReq)); + game_io->io_Message.mn_Node.ln_Type = NT_UNKNOWN; + game_open = port_allocated = false; + if (!OpenDevice("gameport.device", 1, (struct IORequest *)game_io, 0)) + game_open = true; +} + +void C64::c64_ctor2(void) +{ + // Initialize joystick variables + joy_state = 0xff; + + // Open timer_io + timer_port = CreateMsgPort(); + timer_io = (struct timerequest *)CreateIORequest(timer_port, sizeof(struct timerequest)); + OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0); + + // Get timer base + TimerBase = timer_io->tr_node.io_Device; + + // Preset speedometer start time + GetSysTime(&start_time); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ + // Stop and delete timer_io + if (timer_io != NULL) { + if (!CheckIO((struct IORequest *)timer_io)) + WaitIO((struct IORequest *)timer_io); + CloseDevice((struct IORequest *)timer_io); + DeleteIORequest((struct IORequest *)timer_io); + } + + if (timer_port != NULL) + DeleteMsgPort(timer_port); + + if (game_open) { + if (!CheckIO((struct IORequest *)game_io)) { + AbortIO((struct IORequest *)game_io); + WaitIO((struct IORequest *)game_io); + } + CloseDevice((struct IORequest *)game_io); + } + + if (game_io != NULL) + DeleteIORequest((struct IORequest *)game_io); + + if (game_port != NULL) + DeleteMsgPort(game_port); +} + + +/* + * Start emulation + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start timer_io + timer_io->tr_node.io_Command = TR_ADDREQUEST; + timer_io->tr_time.tv_secs = 0; + timer_io->tr_time.tv_micro = ThePrefs.SkipFrames * 20000; // 20ms per frame + SendIO((struct IORequest *)timer_io); + + // Start the CPU thread + thread_running = true; + quit_thyself = false; + have_a_break = false; + thread_func(); +} + + +/* + * Stop emulation + */ + +void C64::Quit(void) +{ + // Ask the thread to quit itself if it is running + if (thread_running) { + quit_thyself = true; + thread_running = false; + } +} + + +/* + * Pause emulation + */ + +void C64::Pause(void) +{ + TheSID->PauseSound(); +} + + +/* + * Resume emulation + */ + +void C64::Resume(void) +{ + TheSID->ResumeSound(); +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + struct timeval end_time; + long speed_index; + + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + GetSysTime(&end_time); + SubTime(&end_time, &start_time); + speed_index = 20000 * 100 * ThePrefs.SkipFrames / (end_time.tv_micro + 1); + + // Abort timer_io if speed limiter is off + if (!ThePrefs.LimitSpeed) { + if (!CheckIO((struct IORequest *)timer_io)) + AbortIO((struct IORequest *)timer_io); + } else if (speed_index > 100) + speed_index = 100; + + // Wait for timer_io (limit speed) + WaitIO((struct IORequest *)timer_io); + + // Restart timer_io + timer_io->tr_node.io_Command = TR_ADDREQUEST; + timer_io->tr_time.tv_secs = 0; + timer_io->tr_time.tv_micro = ThePrefs.SkipFrames * 20000; // 20ms per frame + SendIO((struct IORequest *)timer_io); + + GetSysTime(&start_time); + + TheDisplay->Speedometer(speed_index); + } +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2) +{ + if (game_open && (oldjoy2 != newjoy2)) + + if (newjoy2) { // Open joystick + joy_state = 0xff; + port_allocated = false; + + // Allocate game port + BYTE ctype; + Forbid(); + game_io->io_Command = GPD_ASKCTYPE; + game_io->io_Data = &ctype; + game_io->io_Length = 1; + DoIO((struct IORequest *)game_io); + + if (ctype != GPCT_NOCONTROLLER) + Permit(); + else { + ctype = GPCT_ABSJOYSTICK; + game_io->io_Command = GPD_SETCTYPE; + game_io->io_Data = &ctype; + game_io->io_Length = 1; + DoIO((struct IORequest *)game_io); + Permit(); + + port_allocated = true; + + // Set trigger conditions + game_trigger.gpt_Keys = GPTF_UPKEYS | GPTF_DOWNKEYS; + game_trigger.gpt_Timeout = 65535; + game_trigger.gpt_XDelta = 1; + game_trigger.gpt_YDelta = 1; + game_io->io_Command = GPD_SETTRIGGER; + game_io->io_Data = &game_trigger; + game_io->io_Length = sizeof(struct GamePortTrigger); + DoIO((struct IORequest *)game_io); + + // Flush device buffer + game_io->io_Command = CMD_CLEAR; + DoIO((struct IORequest *)game_io); + + // Start reading joystick events + game_io->io_Command = GPD_READEVENT; + game_io->io_Data = &game_event; + game_io->io_Length = sizeof(struct InputEvent); + SendIO((struct IORequest *)game_io); + } + + } else { // Close joystick + + // Abort game_io + if (!CheckIO((struct IORequest *)game_io)) { + AbortIO((struct IORequest *)game_io); + WaitIO((struct IORequest *)game_io); + } + + // Free game port + if (port_allocated) { + BYTE ctype = GPCT_NOCONTROLLER; + game_io->io_Command = GPD_SETCTYPE; + game_io->io_Data = &ctype; + game_io->io_Length = 1; + DoIO((struct IORequest *)game_io); + + port_allocated = false; + } + } +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + if (port == 0) + return 0xff; + + if (game_open && port_allocated) { + + // Joystick event arrived? + while (GetMsg(game_port) != NULL) { + + // Yes, analyze event + switch (game_event.ie_Code) { + case IECODE_LBUTTON: // Button pressed + joy_state &= 0xef; + break; + + case IECODE_LBUTTON | IECODE_UP_PREFIX: // Button released + joy_state |= 0x10; + break; + + case IECODE_NOBUTTON: // Joystick moved + if (game_event.ie_X == 1) + joy_state &= 0xf7; // Right + if (game_event.ie_X == -1) + joy_state &= 0xfb; // Left + if (game_event.ie_X == 0) + joy_state |= 0x0c; + if (game_event.ie_Y == 1) + joy_state &= 0xfd; // Down + if (game_event.ie_Y == -1) + joy_state &= 0xfe; // Up + if (game_event.ie_Y == 0) + joy_state |= 0x03; + break; + } + + // Start reading the next event + game_io->io_Command = GPD_READEVENT; + game_io->io_Data = &game_event; + game_io->io_Length = sizeof(struct InputEvent); + SendIO((struct IORequest *)game_io); + } + return joy_state; + + } else + return 0xff; +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func(void) +{ + while (!quit_thyself) { + +#ifdef FRODO_SC + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + } +} diff --git a/Src/C64_Be.i b/Src/C64_Be.i new file mode 100644 index 0000000..acaae69 --- /dev/null +++ b/Src/C64_Be.i @@ -0,0 +1,366 @@ +/* + * C64_Be.i - Put the pieces together, Be specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include + +#undef PROFILING + + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + joy[0] = new BJoystick(); + joy[1] = new BJoystick(); +} + +void C64::c64_ctor2(void) +{ + // Initialize joystick variables + joy_minx = joy_miny = 32767; + joy_maxx = joy_maxy = 0; + + // Initialize semaphores (initially acquired) + pause_sem = create_sem(0, "Frodo Pause Semaphore"); + sound_sync_sem = create_sem(0, "Frodo Sound Sync Semaphore"); + + // Preset speedometer start time + start_time = system_time(); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ + delete_sem(pause_sem); + delete_sem(sound_sync_sem); + + delete joy[0]; + delete joy[1]; +} + + +/* + * Start main emulation thread + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start the CPU thread + the_thread = spawn_thread(thread_invoc, "Frodo 6510", B_URGENT_DISPLAY_PRIORITY, this); + thread_running = true; + quit_thyself = false; + have_a_break = false; + resume_thread(the_thread); +} + + +/* + * Stop main emulation thread + */ + +void C64::Quit(void) +{ + long ret; + + // Ask the thread to quit itself if it is running + if (thread_running) { + if (have_a_break) + Resume(); + quit_thyself = true; + wait_for_thread(the_thread, &ret); + thread_running = false; + } +} + + +/* + * Pause main emulation thread + */ + +void C64::Pause(void) +{ + // Ask the thread to pause and wait for acknowledge + if (thread_running && !have_a_break) { + have_a_break = true; + acquire_sem(pause_sem); + TheSID->PauseSound(); + } +} + + +/* + * Resume main emulation thread + */ + +void C64::Resume(void) +{ + if (thread_running && have_a_break) { + have_a_break = false; + release_sem(pause_sem); + TheSID->ResumeSound(); + } +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + bigtime_t elapsed_time; + long speed_index; + + // To avoid deadlocks on quitting + if (quit_thyself) return; + + // Pause requested? + if (have_a_break) { + release_sem(pause_sem); // Acknowledge pause + acquire_sem(pause_sem); // Wait for resume + } + + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + elapsed_time = system_time() - start_time; + speed_index = 20000 * 100 * ThePrefs.SkipFrames / (elapsed_time + 1); + + // Limit speed to 100% if desired (20ms/frame) + // If the SID emulation is on and no frames are skipped, synchronize to the SID + if (ThePrefs.LimitSpeed && speed_index > 100) { + if (ThePrefs.SIDType == SIDTYPE_DIGITAL && ThePrefs.SkipFrames == 1) { + long l; + get_sem_count(sound_sync_sem, &l); + if (l > 0) // Avoid C64 lagging behind + acquire_sem_etc(sound_sync_sem, l+1, 0, 0); + else + acquire_sem(sound_sync_sem); + } else + snooze(ThePrefs.SkipFrames * 20000 - elapsed_time); + speed_index = 100; + } + + start_time = system_time(); + + TheDisplay->Speedometer(speed_index); + } +} + + +/* + * Called by SID after playing 1/50 sec of sound + */ + +void C64::SoundSync(void) +{ + release_sem(sound_sync_sem); +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2) +{ + if (oldjoy1 != newjoy1) { + joy_minx = joy_miny = 32767; // Reset calibration + joy_maxx = joy_maxy = 0; + if (newjoy1) + joy[0]->Open("joystick2"); + else + joy[0]->Close(); + } + + if (oldjoy2 != newjoy2) { + joy_minx = joy_miny = 32767; // Reset calibration + joy_maxx = joy_maxy = 0; + if (newjoy2) + joy[1]->Open("joystick1"); + else + joy[1]->Close(); + } +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + uint8 j = 0xff; + + if (joy[port]->Update() != B_ERROR) { + if (joy[port]->horizontal > joy_maxx) + joy_maxx = joy[port]->horizontal; + if (joy[port]->horizontal < joy_minx) + joy_minx = joy[port]->horizontal; + if (joy[port]->vertical > joy_maxy) + joy_maxy = joy[port]->vertical; + if (joy[port]->vertical < joy_miny) + joy_miny = joy[port]->vertical; + + if (!joy[port]->button1) + j &= 0xef; // Button + + if (joy_maxx-joy_minx < 100 || joy_maxy-joy_miny < 100) + return j; + + if (joy[port]->horizontal < (joy_minx + (joy_maxx-joy_minx)/3)) + j &= 0xf7; // Right + else if (joy[port]->horizontal > (joy_minx + 2*(joy_maxx-joy_minx)/3)) + j &= 0xfb; // Left + + if (joy[port]->vertical < (joy_miny + (joy_maxy-joy_miny)/3)) + j &= 0xfd; // Down + else if (joy[port]->vertical > (joy_miny + 2*(joy_maxy-joy_miny)/3)) + j &= 0xfe; // Up + } + return j; +} + + +/* + * The emulation's main loop + */ + +long C64::thread_invoc(void *obj) +{ + ((C64 *)obj)->thread_func(); + return 0; +} + +void C64::thread_func(void) +{ +#ifdef PROFILING +static bigtime_t vic_time_acc = 0; +static bigtime_t sid_time_acc = 0; +static bigtime_t cia_time_acc = 0; +static bigtime_t cpu_time_acc = 0; +#endif +#ifdef FRODO_SC + while (!quit_thyself) { + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + while (!quit_thyself) { + // The order of calls is important here +#ifdef PROFILING +bigtime_t start_time = system_time(); +#endif + int cycles = TheVIC->EmulateLine(); +#ifdef PROFILING +bigtime_t vic_time = system_time(); +#endif + TheSID->EmulateLine(); +#ifdef PROFILING +bigtime_t sid_time = system_time(); +#endif +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif +#ifdef PROFILING +bigtime_t cia_time = system_time(); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#ifdef PROFILING +bigtime_t cpu_time = system_time(); +vic_time_acc += vic_time - start_time; +sid_time_acc += sid_time - vic_time; +cia_time_acc += cia_time - sid_time; +cpu_time_acc += cpu_time - cia_time; +#endif + +#endif + } + +#ifdef PROFILING +bigtime_t total_time = vic_time_acc + sid_time_acc + cia_time_acc + cpu_time_acc; +printf("VIC: %Ld\n", vic_time_acc * 100 / total_time); +printf("SID: %Ld\n", sid_time_acc * 100 / total_time); +printf("CIA: %Ld\n", cia_time_acc * 100 / total_time); +printf("CPU: %Ld\n", cpu_time_acc * 100 / total_time); +#endif +} diff --git a/Src/C64_PC.cpp b/Src/C64_PC.cpp new file mode 100644 index 0000000..096cbb3 --- /dev/null +++ b/Src/C64_PC.cpp @@ -0,0 +1,12 @@ +/* + * C64_PC.cpp - Put the pieces together (Frodo PC) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +// Same as C64.cpp (mainly to keep the BeIDE happy) +#ifdef __riscos__ +#include "C64.cc" +#else +#include "C64.cpp" +#endif diff --git a/Src/C64_SC.cpp b/Src/C64_SC.cpp new file mode 100644 index 0000000..32dcff5 --- /dev/null +++ b/Src/C64_SC.cpp @@ -0,0 +1,12 @@ +/* + * C64_SC.cpp - Put the pieces together (Frodo SC) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +// Same as C64.cpp (mainly to keep the BeIDE happy) +#ifdef __riscos__ +#include "C64.cc" +#else +#include "C64.cpp" +#endif diff --git a/Src/C64_WIN32.i b/Src/C64_WIN32.i new file mode 100644 index 0000000..b723caf --- /dev/null +++ b/Src/C64_WIN32.i @@ -0,0 +1,439 @@ +/* + * C64_WIN32.i - Put the pieces together, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * WIN32 code by J. Richard Sladkey + */ + +#include +#include "main.h" + +#define FRAME_INTERVAL (1000/SCREEN_FREQ) // in milliseconds +#ifdef FRODO_SC +#define SPEEDOMETER_INTERVAL 4000 // in milliseconds +#else +#define SPEEDOMETER_INTERVAL 1000 // in milliseconds +#endif +#define JOYSTICK_SENSITIVITY 40 // % of live range +#define JOYSTICK_MIN 0x0000 // min value of range +#define JOYSTICK_MAX 0xffff // max value of range +#define JOYSTICK_RANGE (JOYSTICK_MAX - JOYSTICK_MIN) + +static BOOL high_resolution_timer = FALSE; + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1() +{ + Debug("C64::c64_ctor1\n"); + + // Initialize joystick variables. + joy_state = 0xff; + + // No need to check for state change. + state_change = FALSE; + + // Start the synchronization timer. + timer_semaphore = NULL; + timer_id = NULL; + StartTimer(); +} + +void C64::c64_ctor2() +{ + Debug("C64::c64_ctor2\n"); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor() +{ + Debug("C64::c64_dtor\n"); + + StopTimer(); +} + + +/* + * Start emulation + */ + +void C64::Run() +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + patch_kernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start the CPU thread + thread_func(); +} + + +/* + * Stop emulation + */ + +void C64::Quit() +{ + // Ask the thread to quit itself if it is running + quit_thyself = TRUE; + state_change = TRUE; +} + + +/* + * Pause emulation + */ + +void C64::Pause() +{ + StopTimer(); + TheSID->PauseSound(); + have_a_break = TRUE; + state_change = TRUE; +} + + +/* + * Resume emulation + */ + +void C64::Resume() +{ + StartTimer(); + TheSID->ResumeSound(); + have_a_break = FALSE; +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + //Debug("C64::VBlank\n"); + + // Poll the keyboard. + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + // Poll the joysticks. + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation. + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks. + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + +#if 1 + // Output a frag. + TheSID->VBlank(); +#endif + + if (have_a_break) + return; + + // Update the window if needed. + frame++; + if (draw_frame) { + + // Synchronize to the timer if limiting the speed. + if (ThePrefs.LimitSpeed) { + if (skipped_frames == 0) { + // There is a tiny race condtion here that + // could cause a full extra delay cycle. + WaitForSingleObject(timer_semaphore, INFINITE); + } + else { + Debug("*** Skipped a frame! ***\n"); + skipped_frames = 0; + } + } + + // Perform the actual screen update exactly at the + // beginning of an interval for the smoothest video. + TheDisplay->Update(); + + // Compute the speed index and show it in the speedometer. + DWORD now = timeGetTime(); + int elapsed_time = now - ref_time; + if (now - ref_time >= SPEEDOMETER_INTERVAL) { + double speed_index = double(frame * FRAME_INTERVAL * 100 + elapsed_time/2) / elapsed_time; + TheDisplay->Speedometer((int)speed_index); + ref_time = now; + frame = 0; + } + + // Make sure our timer is set correctly. + CheckTimerChange(); + } +} + + +void C64::CheckTimerChange() +{ + // Make sure the timer interval matches the preferences. + if (!ThePrefs.LimitSpeed && timer_every == 0) + return; + if (ThePrefs.LimitSpeed && ThePrefs.SkipFrames == timer_every) + return; + StopTimer(); + StartTimer(); +} + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +BOOL joystick_open[2]; + +void C64::open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2) +{ + if (oldjoy1 != newjoy1) { + joystick_open[0] = FALSE; + if (newjoy1) { + JOYINFO joyinfo; + if (joyGetPos(0, &joyinfo) == JOYERR_NOERROR) + joystick_open[0] = TRUE; + } + } + + if (oldjoy2 != newjoy2) { + joystick_open[1] = FALSE; + if (newjoy1) { + JOYINFO joyinfo; + if (joyGetPos(1, &joyinfo) == JOYERR_NOERROR) + joystick_open[1] = TRUE; + } + } + + // XXX: Should have our own new prefs! + state_change = TRUE; +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + uint8 j = 0xff; + + if (joystick_open[port]) { + JOYINFO joyinfo; + if (joyGetPos(port, &joyinfo) == JOYERR_NOERROR) { + int x = joyinfo.wXpos; + int y = joyinfo.wYpos; + int buttons = joyinfo.wButtons; + int s1 = JOYSTICK_SENSITIVITY; + int s2 = 100 - JOYSTICK_SENSITIVITY; + if (x < JOYSTICK_MIN + s1*JOYSTICK_RANGE/100) + j &= 0xfb; // Left + else if (x > JOYSTICK_MIN + s2*JOYSTICK_RANGE/100) + j &= 0xf7; // Right + if (y < JOYSTICK_MIN + s1*JOYSTICK_RANGE/100) + j &= 0xfe; // Up + else if (y > JOYSTICK_MIN + s2*JOYSTICK_RANGE/100) + j &= 0xfd; // Down + if (buttons & 1) + j &= 0xef; // Button + if (buttons & 2) { + Pause(); + while (joyGetPos(port, &joyinfo) == JOYERR_NOERROR && (joyinfo.wButtons & 2)) + Sleep(100); + Resume(); + } + } + } + + return j; +} + +void C64::StartTimer() +{ + ref_time = timeGetTime(); + skipped_frames = 0; + frame = 0; + + if (!ThePrefs.LimitSpeed) { + timer_every = 0; + StopTimer(); + return; + } + timer_every = ThePrefs.SkipFrames; + + if (!timer_semaphore) { + timer_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (!timer_semaphore) + Debug("CreateSemaphore failed\n"); + } + + if (!timer_id) { + + // Turn on high-resolution times and delays. + int resolution = FRAME_INTERVAL; + if (high_resolution_timer) { + timeBeginPeriod(1); + resolution = 0; + } + + timer_id = timeSetEvent(timer_every*FRAME_INTERVAL, resolution, StaticTimeProc, (DWORD) this, TIME_PERIODIC); + if (!timer_id) + Debug("timeSetEvent failed\n"); + } +} + +void C64::StopTimer() +{ + if (timer_semaphore) { + CloseHandle(timer_semaphore); + timer_semaphore = NULL; + } + if (timer_id) { + timeKillEvent(timer_id); + timer_id = NULL; + + // Turn off high-resolution delays. + if (high_resolution_timer) + timeEndPeriod(1); + } + +} + +void CALLBACK C64::StaticTimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + C64* TheC64 = (C64 *) dwUser; + TheC64->TimeProc(uID); +} + +void C64::TimeProc(UINT id) +{ + if (id != timer_id) { + Debug("TimeProc called for wrong timer id!\n"); + timeKillEvent(id); + return; + } + + if (!ReleaseSemaphore(timer_semaphore, 1, NULL)) + skipped_frames++; +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func() +{ + Debug("C64::thread_func\n"); + + thread_running = TRUE; + + while (!quit_thyself) { + + if (have_a_break) + TheDisplay->WaitUntilActive(); + +#ifdef FRODO_SC + if (ThePrefs.Emul1541Proc) + EmulateCyclesWith1541(); + else + EmulateCyclesWithout1541(); + state_change = FALSE; +#else + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + } + + thread_running = FALSE; + +} + +#ifdef FRODO_SC + +void C64::EmulateCyclesWith1541() +{ + thread_running = TRUE; + while (!state_change) { + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); +#ifndef BATCH_CIA_CYCLES + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); +#endif + TheCPU->EmulateCycle(); + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + CycleCounter++; + } +} + +void C64::EmulateCyclesWithout1541() +{ + thread_running = TRUE; + while (!state_change) { + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); +#ifndef BATCH_CIA_CYCLES + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); +#endif + TheCPU->EmulateCycle(); + CycleCounter++; + } +} + +#endif diff --git a/Src/C64_x.i b/Src/C64_x.i new file mode 100644 index 0000000..aa964fa --- /dev/null +++ b/Src/C64_x.i @@ -0,0 +1,425 @@ +/* + * C64_x.i - Put the pieces together, X specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * Unix stuff by Bernd Schmidt/Lutz Vieweg + */ + +#include "main.h" + + +static struct timeval tv_start; + +#ifndef HAVE_USLEEP +/* + * NAME: + * usleep -- This is the precision timer for Test Set + * Automation. It uses the select(2) system + * call to delay for the desired number of + * micro-seconds. This call returns ZERO + * (which is usually ignored) on successful + * completion, -1 otherwise. + * + * ALGORITHM: + * 1) We range check the passed in microseconds and log a + * warning message if appropriate. We then return without + * delay, flagging an error. + * 2) Load the Seconds and micro-seconds portion of the + * interval timer structure. + * 3) Call select(2) with no file descriptors set, just the + * timer, this results in either delaying the proper + * ammount of time or being interupted early by a signal. + * + * HISTORY: + * Added when the need for a subsecond timer was evident. + * + * AUTHOR: + * Michael J. Dyer Telephone: AT&T 414.647.4044 + * General Electric Medical Systems GE DialComm 8 *767.4044 + * P.O. Box 414 Mail Stop 12-27 Sect'y AT&T 414.647.4584 + * Milwaukee, Wisconsin USA 53201 8 *767.4584 + * internet: mike@sherlock.med.ge.com GEMS WIZARD e-mail: DYER + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int usleep(unsigned long int microSeconds) +{ + unsigned int Seconds, uSec; + int nfds, readfds, writefds, exceptfds; + struct timeval Timer; + + nfds = readfds = writefds = exceptfds = 0; + + if( (microSeconds == (unsigned long) 0) + || microSeconds > (unsigned long) 4000000 ) + { + errno = ERANGE; /* value out of range */ + perror( "usleep time out of range ( 0 -> 4000000 ) " ); + return -1; + } + + Seconds = microSeconds / (unsigned long) 1000000; + uSec = microSeconds % (unsigned long) 1000000; + + Timer.tv_sec = Seconds; + Timer.tv_usec = uSec; + + if( select( nfds, &readfds, &writefds, &exceptfds, &Timer ) < 0 ) + { + perror( "usleep (select) failed" ); + return -1; + } + + return 0; +} +#endif + + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + // Initialize joystick variables + joyfd[0] = joyfd[1] = -1; + joy_minx = joy_miny = 32767; + joy_maxx = joy_maxy = -32768; + + // we need to create a potential GUI subprocess here, because we don't want + // it to inherit file-descriptors (such as for the audio-device and alike..) +#if defined(__svgalib__) + gui = 0; +#else + // try to start up Tk gui. + gui = new CmdPipe("wish", "TkGui.tcl"); + if (gui) { + if (gui->fail) { + delete gui; gui = 0; + } + } + // wait until the GUI process responds (if it does...) + if (gui) { + if (5 != gui->ewrite("ping\n",5)) { + delete gui; gui = 0; + } else { + char c; + fd_set set; + FD_ZERO(&set); + FD_SET(gui->get_read_fd(), &set); + struct timeval tv; + tv.tv_usec = 0; + tv.tv_sec = 5; +// Use the following commented line for HP-UX < 10.20 +// if (select(FD_SETSIZE, (int *)&set, (int *)NULL, (int *)NULL, &tv) <= 0) { + if (select(FD_SETSIZE, &set, NULL, NULL, &tv) <= 0) { + delete gui; gui = 0; + } else { + if (1 != gui->eread(&c, 1)) { + delete gui; gui = 0; + } else { + if (c != 'o') { + delete gui; gui = 0; + } + } + } + } + } +#endif // __svgalib__ +} + +void C64::c64_ctor2(void) +{ +#ifndef __svgalib__ + if (!gui) { + fprintf(stderr,"Alas, master, no preferences window will be available.\n" + "If you wish to see one, make sure the 'wish' interpreter\n" + "(Tk version >= 4.1) is installed in your path.\n" + "You can still use Frodo, though. Use F10 to quit, \n" + "F11 to cause an NMI and F12 to reset the C64.\n" + "You can change the preferences by editing ~/.frodorc\n"); + } +#endif // SVGAlib + + gettimeofday(&tv_start, NULL); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ +} + + +/* + * Start main emulation thread + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + quit_thyself = false; + thread_func(); +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + if (TheDisplay->quit_requested) + quit_thyself = true; + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + struct timeval tv; + gettimeofday(&tv, NULL); + if ((tv.tv_usec -= tv_start.tv_usec) < 0) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + tv.tv_sec -= tv_start.tv_sec; + double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec; + speed_index = 20000 / (elapsed_time + 1) * ThePrefs.SkipFrames * 100; + + // Limit speed to 100% if desired + if ((speed_index > 100) && ThePrefs.LimitSpeed) { + usleep((unsigned long)(ThePrefs.SkipFrames * 20000 - elapsed_time)); + speed_index = 100; + } + + gettimeofday(&tv_start, NULL); + + TheDisplay->Speedometer((int)speed_index); + } +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2) +{ +#ifdef HAVE_LINUX_JOYSTICK_H + if (oldjoy1 != newjoy1) { + joy_minx = joy_miny = 32767; // Reset calibration + joy_maxx = joy_maxy = -32768; + if (newjoy1) { + joyfd[0] = open("/dev/js0", O_RDONLY); + if (joyfd[0] < 0) + fprintf(stderr, "Couldn't open joystick 1\n"); + } else { + close(joyfd[0]); + joyfd[0] = -1; + } + } + + if (oldjoy2 != newjoy2) { + joy_minx = joy_miny = 32767; // Reset calibration + joy_maxx = joy_maxy = -32768; + if (newjoy2) { + joyfd[1] = open("/dev/js1", O_RDONLY); + if (joyfd[1] < 0) + fprintf(stderr, "Couldn't open joystick 2\n"); + } else { + close(joyfd[1]); + joyfd[1] = -1; + } + } +#endif +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ +#ifdef HAVE_LINUX_JOYSTICK_H + JS_DATA_TYPE js; + uint8 j = 0xff; + + if (joyfd[port] >= 0) { + if (read(joyfd[port], &js, JS_RETURN) == JS_RETURN) { + if (js.x > joy_maxx) + joy_maxx = js.x; + if (js.x < joy_minx) + joy_minx = js.x; + if (js.y > joy_maxy) + joy_maxy = js.y; + if (js.y < joy_miny) + joy_miny = js.y; + + if (joy_maxx-joy_minx < 100 || joy_maxy-joy_miny < 100) + return 0xff; + + if (js.x < (joy_minx + (joy_maxx-joy_minx)/3)) + j &= 0xfb; // Left + else if (js.x > (joy_minx + 2*(joy_maxx-joy_minx)/3)) + j &= 0xf7; // Right + + if (js.y < (joy_miny + (joy_maxy-joy_miny)/3)) + j &= 0xfe; // Up + else if (js.y > (joy_miny + 2*(joy_maxy-joy_miny)/3)) + j &= 0xfd; // Down + + if (js.buttons & 1) + j &= 0xef; // Button + } + } + return j; +#else + return 0xff; +#endif +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func(void) +{ + int linecnt = 0; + +#ifdef FRODO_SC + while (!quit_thyself) { + + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + while (!quit_thyself) { + + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + linecnt++; +#if !defined(__svgalib__) + if ((linecnt & 0xfff) == 0 && gui) { + + // check for command from GUI process + // fprintf(stderr,":"); + while (gui->probe()) { + char c; + if (gui->eread(&c, 1) != 1) { + delete gui; + gui = 0; + fprintf(stderr,"Oops, GUI process died...\n"); + } else { + // fprintf(stderr,"%c",c); + switch (c) { + case 'q': + quit_thyself = true; + break; + case 'r': + Reset(); + break; + case 'p':{ + Prefs *np = Frodo::reload_prefs(); + NewPrefs(np); + ThePrefs = *np; + break; + } + default: + break; + } + } + } + } +#endif + } +#if !defined(__svgalib__) + if (gui) { + gui->ewrite("quit\n",5); + } +#endif +} diff --git a/Src/CIA.cpp b/Src/CIA.cpp new file mode 100644 index 0000000..6c6fefd --- /dev/null +++ b/Src/CIA.cpp @@ -0,0 +1,556 @@ +/* + * CIA.cpp - 6526 emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated raster + * line. It counts down the timers and triggers interrupts if + * necessary. + * - The TOD clocks are counted by CountTOD() during the VBlank, so + * the input frequency is 50Hz + * - The fields KeyMatrix and RevMatrix contain one bit for each + * key on the C64 keyboard (0: key pressed, 1: key released). + * KeyMatrix is used for normal keyboard polling (PRA->PRB), + * RevMatrix for reversed polling (PRB->PRA). + * + * Incompatibilities: + * ------------------ + * + * - The TOD clock should not be stopped on a read access, but + * latched + * - The SDR interrupt is faked + */ + +#include "sysdeps.h" + +#include "CIA.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "Prefs.h" + + +/* + * Constructors + */ + +MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {} +MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC) {} +MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541) {} + + +/* + * Reset the CIA + */ + +void MOS6526::Reset(void) +{ + pra = prb = ddra = ddrb = 0; + + ta = tb = 0xffff; + latcha = latchb = 1; + + tod_10ths = tod_sec = tod_min = tod_hr = 0; + alm_10ths = alm_sec = alm_min = alm_hr = 0; + + sdr = icr = cra = crb = int_mask = 0; + + tod_halt = ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false; + tod_divider = 0; +} + +void MOS6526_1::Reset(void) +{ + MOS6526::Reset(); + + // Clear keyboard matrix and joystick states + for (int i=0; i<8; i++) + KeyMatrix[i] = RevMatrix[i] = 0xff; + + Joystick1 = Joystick2 = 0xff; + prev_lp = 0x10; +} + +void MOS6526_2::Reset(void) +{ + MOS6526::Reset(); + + // VA14/15 = 0 + the_vic->ChangedVA(0); + + // IEC + IECLines = 0xd0; +} + + +/* + * Get CIA state + */ + +void MOS6526::GetState(MOS6526State *cs) +{ + cs->pra = pra; + cs->prb = prb; + cs->ddra = ddra; + cs->ddrb = ddrb; + + cs->ta_lo = ta & 0xff; + cs->ta_hi = ta >> 8; + cs->tb_lo = tb & 0xff; + cs->tb_hi = tb >> 8; + cs->latcha = latcha; + cs->latchb = latchb; + cs->cra = cra; + cs->crb = crb; + + cs->tod_10ths = tod_10ths; + cs->tod_sec = tod_sec; + cs->tod_min = tod_min; + cs->tod_hr = tod_hr; + cs->alm_10ths = alm_10ths; + cs->alm_sec = alm_sec; + cs->alm_min = alm_min; + cs->alm_hr = alm_hr; + + cs->sdr = sdr; + + cs->int_data = icr; + cs->int_mask = int_mask; +} + + +/* + * Restore CIA state + */ + +void MOS6526::SetState(MOS6526State *cs) +{ + pra = cs->pra; + prb = cs->prb; + ddra = cs->ddra; + ddrb = cs->ddrb; + + ta = (cs->ta_hi << 8) | cs->ta_lo; + tb = (cs->tb_hi << 8) | cs->tb_lo; + latcha = cs->latcha; + latchb = cs->latchb; + cra = cs->cra; + crb = cs->crb; + + tod_10ths = cs->tod_10ths; + tod_sec = cs->tod_sec; + tod_min = cs->tod_min; + tod_hr = cs->tod_hr; + alm_10ths = cs->alm_10ths; + alm_sec = cs->alm_sec; + alm_min = cs->alm_min; + alm_hr = cs->alm_hr; + + sdr = cs->sdr; + + icr = cs->int_data; + int_mask = cs->int_mask; + + tod_halt = false; + ta_cnt_phi2 = ((cra & 0x21) == 0x01); + tb_cnt_phi2 = ((crb & 0x61) == 0x01); + tb_cnt_ta = ((crb & 0x61) == 0x41); +} + + +/* + * Read from register (CIA 1) + */ + +uint8 MOS6526_1::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: { + uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1; + if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns + if (!(tst & 0x02)) ret &= RevMatrix[1]; + if (!(tst & 0x04)) ret &= RevMatrix[2]; + if (!(tst & 0x08)) ret &= RevMatrix[3]; + if (!(tst & 0x10)) ret &= RevMatrix[4]; + if (!(tst & 0x20)) ret &= RevMatrix[5]; + if (!(tst & 0x40)) ret &= RevMatrix[6]; + if (!(tst & 0x80)) ret &= RevMatrix[7]; + return ret & Joystick2; + } + case 0x01: { + uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2; + if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows + if (!(tst & 0x02)) ret &= KeyMatrix[1]; + if (!(tst & 0x04)) ret &= KeyMatrix[2]; + if (!(tst & 0x08)) ret &= KeyMatrix[3]; + if (!(tst & 0x10)) ret &= KeyMatrix[4]; + if (!(tst & 0x20)) ret &= KeyMatrix[5]; + if (!(tst & 0x40)) ret &= KeyMatrix[6]; + if (!(tst & 0x80)) ret &= KeyMatrix[7]; + return (ret | (prb & ddrb)) & Joystick1; + } + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearCIAIRQ(); // Clear IRQ + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Read from register (CIA 2) + */ + +uint8 MOS6526_2::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: + return (pra | ~ddra) & 0x3f + | IECLines & the_cpu_1541->IECLines; + case 0x01: return prb | ~ddrb; + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearNMI(); // Clear NMI + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Write to register (CIA 1) + */ + +// Write to port B, check for lightpen interrupt +inline void MOS6526_1::check_lp(void) +{ + if ((prb | ~ddrb) & 0x10 != prev_lp) + the_vic->TriggerLightpen(); + prev_lp = (prb | ~ddrb) & 0x10; +} + +void MOS6526_1::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0: pra = byte; break; + case 0x1: + prb = byte; + check_lp(); + break; + case 0x2: ddra = byte; break; + case 0x3: + ddrb = byte; + check_lp(); + break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (ThePrefs.CIAIRQHack) // Hack for addressing modes that read from the address + icr = 0; + if (byte & 0x80) { + int_mask |= byte & 0x7f; + if (icr & int_mask & 0x1f) { // Trigger IRQ if pending + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } + } else + int_mask &= ~byte; + break; + + case 0xe: + cra = byte & 0xef; + if (byte & 0x10) // Force load + ta = latcha; + ta_cnt_phi2 = ((byte & 0x21) == 0x01); + break; + + case 0xf: + crb = byte & 0xef; + if (byte & 0x10) // Force load + tb = latchb; + tb_cnt_phi2 = ((byte & 0x61) == 0x01); + tb_cnt_ta = ((byte & 0x61) == 0x41); + break; + } +} + + +/* + * Write to register (CIA 2) + */ + +void MOS6526_2::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0:{ + pra = byte; + byte = ~pra & ddra; + the_vic->ChangedVA(byte & 3); + uint8 old_lines = IECLines; + IECLines = (byte << 2) & 0x80 // DATA + | (byte << 2) & 0x40 // CLK + | (byte << 1) & 0x10; // ATN + if ((IECLines ^ old_lines) & 0x10) { // ATN changed + the_cpu_1541->NewATNState(); + if (old_lines & 0x10) // ATN 1->0 + the_cpu_1541->IECInterrupt(); + } + break; + } + case 0x1: prb = byte; break; + + case 0x2: + ddra = byte; + the_vic->ChangedVA(~(pra | ~ddra) & 3); + break; + case 0x3: ddrb = byte; break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (ThePrefs.CIAIRQHack) + icr = 0; + if (byte & 0x80) { + int_mask |= byte & 0x7f; + if (icr & int_mask & 0x1f) { // Trigger NMI if pending + icr |= 0x80; + the_cpu->TriggerNMI(); + } + } else + int_mask &= ~byte; + break; + + case 0xe: + cra = byte & 0xef; + if (byte & 0x10) // Force load + ta = latcha; + ta_cnt_phi2 = ((byte & 0x21) == 0x01); + break; + + case 0xf: + crb = byte & 0xef; + if (byte & 0x10) // Force load + tb = latchb; + tb_cnt_phi2 = ((byte & 0x61) == 0x01); + tb_cnt_ta = ((byte & 0x61) == 0x41); + break; + } +} + + +/* + * Count CIA TOD clock (called during VBlank) + */ + +void MOS6526::CountTOD(void) +{ + uint8 lo, hi; + + // Decrement frequency divider + if (tod_divider) + tod_divider--; + else { + + // Reload divider according to 50/60 Hz flag + if (cra & 0x80) + tod_divider = 4; + else + tod_divider = 5; + + // 1/10 seconds + tod_10ths++; + if (tod_10ths > 9) { + tod_10ths = 0; + + // Seconds + lo = (tod_sec & 0x0f) + 1; + hi = tod_sec >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_sec = 0; + + // Minutes + lo = (tod_min & 0x0f) + 1; + hi = tod_min >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_min = 0; + + // Hours + lo = (tod_hr & 0x0f) + 1; + hi = (tod_hr >> 4) & 1; + tod_hr &= 0x80; // Keep AM/PM flag + if (lo > 9) { + lo = 0; + hi++; + } + tod_hr |= (hi << 4) | lo; + if ((tod_hr & 0x1f) > 0x11) + tod_hr = tod_hr & 0x80 ^ 0x80; + } else + tod_min = (hi << 4) | lo; + } else + tod_sec = (hi << 4) | lo; + } + + // Alarm time reached? Trigger interrupt if enabled + if (tod_10ths == alm_10ths && tod_sec == alm_sec && + tod_min == alm_min && tod_hr == alm_hr) + TriggerInterrupt(4); + } +} + + +/* + * Trigger IRQ (CIA 1) + */ + +void MOS6526_1::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } +} + + +/* + * Trigger NMI (CIA 2) + */ + +void MOS6526_2::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerNMI(); + } +} diff --git a/Src/CIA.h b/Src/CIA.h new file mode 100644 index 0000000..67b6d9c --- /dev/null +++ b/Src/CIA.h @@ -0,0 +1,196 @@ +/* + * CIA.h - 6526 emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _CIA_H +#define _CIA_H + +#include "Prefs.h" + + +class MOS6510; +class MOS6502_1541; +class MOS6569; +struct MOS6526State; + + +class MOS6526 { +public: + MOS6526(MOS6510 *CPU); + + void Reset(void); + void GetState(MOS6526State *cs); + void SetState(MOS6526State *cs); +#ifdef FRODO_SC + void CheckIRQs(void); + void EmulateCycle(void); +#else + void EmulateLine(int cycles); +#endif + void CountTOD(void); + virtual void TriggerInterrupt(int bit)=0; + +protected: + MOS6510 *the_cpu; // Pointer to 6510 + + uint8 pra, prb, ddra, ddrb; + + uint16 ta, tb, latcha, latchb; + + uint8 tod_10ths, tod_sec, tod_min, tod_hr; + uint8 alm_10ths, alm_sec, alm_min, alm_hr; + + uint8 sdr, icr, cra, crb; + uint8 int_mask; + + int tod_divider; // TOD frequency divider + + bool tod_halt, // Flag: TOD halted + ta_cnt_phi2, // Flag: Timer A is counting Phi 2 + tb_cnt_phi2, // Flag: Timer B is counting Phi 2 + tb_cnt_ta; // Flag: Timer B is counting underflows of Timer A + +#ifdef FRODO_SC + bool ta_irq_next_cycle, // Flag: Trigger TA IRQ in next cycle + tb_irq_next_cycle, // Flag: Trigger TB IRQ in next cycle + has_new_cra, // Flag: New value for CRA pending + has_new_crb; // Flag: New value for CRB pending + char ta_state, tb_state; // Timer A/B states + uint8 new_cra, new_crb; // New values for CRA/CRB +#endif +}; + + +class MOS6526_1 : public MOS6526 { +public: + MOS6526_1(MOS6510 *CPU, MOS6569 *VIC); + + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + virtual void TriggerInterrupt(int bit); + + uint8 KeyMatrix[8]; // C64 keyboard matrix, 1 bit/key (0: key down, 1: key up) + uint8 RevMatrix[8]; // Reversed keyboard matrix + + uint8 Joystick1; // Joystick 1 AND value + uint8 Joystick2; // Joystick 2 AND value + +private: + void check_lp(void); + + MOS6569 *the_vic; + + uint8 prev_lp; // Previous state of LP line (bit 4) +}; + + +class MOS6526_2 : public MOS6526{ +public: + MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541); + + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + virtual void TriggerInterrupt(int bit); + + uint8 IECLines; // State of IEC lines (bit 7 - DATA, bit 6 - CLK, bit 4 - ATN) + +private: + MOS6569 *the_vic; + MOS6502_1541 *the_cpu_1541; +}; + + +// CIA state +struct MOS6526State { + uint8 pra; + uint8 ddra; + uint8 prb; + uint8 ddrb; + uint8 ta_lo; + uint8 ta_hi; + uint8 tb_lo; + uint8 tb_hi; + uint8 tod_10ths; + uint8 tod_sec; + uint8 tod_min; + uint8 tod_hr; + uint8 sdr; + uint8 int_data; // Pending interrupts + uint8 cra; + uint8 crb; + // Additional registers + uint16 latcha; // Timer latches + uint16 latchb; + uint8 alm_10ths; // Alarm time + uint8 alm_sec; + uint8 alm_min; + uint8 alm_hr; + uint8 int_mask; // Enabled interrupts +}; + + +/* + * Emulate CIA for one cycle/raster line + */ + +#ifdef FRODO_SC +inline void MOS6526::CheckIRQs(void) +{ + // Trigger pending interrupts + if (ta_irq_next_cycle) { + ta_irq_next_cycle = false; + TriggerInterrupt(1); + } + if (tb_irq_next_cycle) { + tb_irq_next_cycle = false; + TriggerInterrupt(2); + } +} +#else +inline void MOS6526::EmulateLine(int cycles) +{ + unsigned long tmp; + + // Timer A + if (ta_cnt_phi2) { + ta = tmp = ta - cycles; // Decrement timer + + if (tmp > 0xffff) { // Underflow? + ta = latcha; // Reload timer + + if (cra & 8) { // One-shot? + cra &= 0xfe; + ta_cnt_phi2 = false; + } + TriggerInterrupt(1); + if (tb_cnt_ta) { // Timer B counting underflows of Timer A? + tb = tmp = tb - 1; // tmp = --tb doesn't work + if (tmp > 0xffff) goto tb_underflow; + } + } + } + + // Timer B + if (tb_cnt_phi2) { + tb = tmp = tb - cycles; // Decrement timer + + if (tmp > 0xffff) { // Underflow? +tb_underflow: + tb = latchb; + + if (crb & 8) { // One-shot? + crb &= 0xfe; + tb_cnt_phi2 = false; + tb_cnt_ta = false; + } + TriggerInterrupt(2); + } + } +} +#endif + +#endif diff --git a/Src/CIA_SC.cpp b/Src/CIA_SC.cpp new file mode 100644 index 0000000..bd15123 --- /dev/null +++ b/Src/CIA_SC.cpp @@ -0,0 +1,763 @@ +/* + * CIA_SC.cpp - Single-cycle 6526 emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - The Emulate() function is called for every emulated Phi2 + * clock cycle. It counts down the timers and triggers + * interrupts if necessary. + * - The TOD clocks are counted by CountTOD() during the VBlank, so + * the input frequency is 50Hz + * - The fields KeyMatrix and RevMatrix contain one bit for each + * key on the C64 keyboard (0: key pressed, 1: key released). + * KeyMatrix is used for normal keyboard polling (PRA->PRB), + * RevMatrix for reversed polling (PRB->PRA). + * + * Incompatibilities: + * ------------------ + * + * - The TOD clock should not be stopped on a read access, but be + * latched + * - The SDR interrupt is faked + * - Some small incompatibilities with the timers + */ + +#include "sysdeps.h" + +#include "CIA.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "Prefs.h" + + +// Timer states +enum { + T_STOP, + T_WAIT_THEN_COUNT, + T_LOAD_THEN_STOP, + T_LOAD_THEN_COUNT, + T_LOAD_THEN_WAIT_THEN_COUNT, + T_COUNT, + T_COUNT_THEN_STOP, +}; + + +/* + * Constructors + */ + +MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {} +MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC) {} +MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541) {} + + +/* + * Reset the CIA + */ + +void MOS6526::Reset(void) +{ + pra = prb = ddra = ddrb = 0; + + ta = tb = 0xffff; + latcha = latchb = 1; + + tod_10ths = tod_sec = tod_min = tod_hr = 0; + alm_10ths = alm_sec = alm_min = alm_hr = 0; + + sdr = icr = cra = crb = int_mask = 0; + + tod_halt = false; + tod_divider = 0; + + ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false; + + ta_irq_next_cycle = tb_irq_next_cycle = false; + ta_state = tb_state = T_STOP; +} + +void MOS6526_1::Reset(void) +{ + MOS6526::Reset(); + + // Clear keyboard matrix and joystick states + for (int i=0; i<8; i++) + KeyMatrix[i] = RevMatrix[i] = 0xff; + + Joystick1 = Joystick2 = 0xff; + prev_lp = 0x10; +} + +void MOS6526_2::Reset(void) +{ + MOS6526::Reset(); + + // VA14/15 = 0 + the_vic->ChangedVA(0); + + // IEC + IECLines = 0xd0; +} + + +/* + * Get CIA state + */ + +void MOS6526::GetState(MOS6526State *cs) +{ + cs->pra = pra; + cs->prb = prb; + cs->ddra = ddra; + cs->ddrb = ddrb; + + cs->ta_lo = ta & 0xff; + cs->ta_hi = ta >> 8; + cs->tb_lo = tb & 0xff; + cs->tb_hi = tb >> 8; + cs->latcha = latcha; + cs->latchb = latchb; + cs->cra = cra; + cs->crb = crb; + + cs->tod_10ths = tod_10ths; + cs->tod_sec = tod_sec; + cs->tod_min = tod_min; + cs->tod_hr = tod_hr; + cs->alm_10ths = alm_10ths; + cs->alm_sec = alm_sec; + cs->alm_min = alm_min; + cs->alm_hr = alm_hr; + + cs->sdr = sdr; + + cs->int_data = icr; + cs->int_mask = int_mask; +} + + +/* + * Restore CIA state + */ + +void MOS6526::SetState(MOS6526State *cs) +{ + pra = cs->pra; + prb = cs->prb; + ddra = cs->ddra; + ddrb = cs->ddrb; + + ta = (cs->ta_hi << 8) | cs->ta_lo; + tb = (cs->tb_hi << 8) | cs->tb_lo; + latcha = cs->latcha; + latchb = cs->latchb; + cra = cs->cra; + crb = cs->crb; + + tod_10ths = cs->tod_10ths; + tod_sec = cs->tod_sec; + tod_min = cs->tod_min; + tod_hr = cs->tod_hr; + alm_10ths = cs->alm_10ths; + alm_sec = cs->alm_sec; + alm_min = cs->alm_min; + alm_hr = cs->alm_hr; + + sdr = cs->sdr; + + icr = cs->int_data; + int_mask = cs->int_mask; + + tod_halt = false; + ta_cnt_phi2 = ((cra & 0x20) == 0x00); + tb_cnt_phi2 = ((crb & 0x60) == 0x00); + tb_cnt_ta = ((crb & 0x60) == 0x40); + + ta_state = (cra & 1) ? T_COUNT : T_STOP; + tb_state = (crb & 1) ? T_COUNT : T_STOP; +} + + +/* + * Read from register (CIA 1) + */ + +uint8 MOS6526_1::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: { + uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1; + if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns + if (!(tst & 0x02)) ret &= RevMatrix[1]; + if (!(tst & 0x04)) ret &= RevMatrix[2]; + if (!(tst & 0x08)) ret &= RevMatrix[3]; + if (!(tst & 0x10)) ret &= RevMatrix[4]; + if (!(tst & 0x20)) ret &= RevMatrix[5]; + if (!(tst & 0x40)) ret &= RevMatrix[6]; + if (!(tst & 0x80)) ret &= RevMatrix[7]; + return ret & Joystick2; + } + case 0x01: { + uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2; + if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows + if (!(tst & 0x02)) ret &= KeyMatrix[1]; + if (!(tst & 0x04)) ret &= KeyMatrix[2]; + if (!(tst & 0x08)) ret &= KeyMatrix[3]; + if (!(tst & 0x10)) ret &= KeyMatrix[4]; + if (!(tst & 0x20)) ret &= KeyMatrix[5]; + if (!(tst & 0x40)) ret &= KeyMatrix[6]; + if (!(tst & 0x80)) ret &= KeyMatrix[7]; + return (ret | (prb & ddrb)) & Joystick1; + } + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearCIAIRQ(); // Clear IRQ + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Read from register (CIA 2) + */ + +uint8 MOS6526_2::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: + return (pra | ~ddra) & 0x3f + | IECLines & the_cpu_1541->IECLines; + case 0x01: return prb | ~ddrb; + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearNMI(); + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Write to register (CIA 1) + */ + +// Write to port B, check for lightpen interrupt +inline void MOS6526_1::check_lp(void) +{ + if ((prb | ~ddrb) & 0x10 != prev_lp) + the_vic->TriggerLightpen(); + prev_lp = (prb | ~ddrb) & 0x10; +} + +void MOS6526_1::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0: pra = byte; break; + case 0x1: + prb = byte; + check_lp(); + break; + case 0x2: ddra = byte; break; + case 0x3: + ddrb = byte; + check_lp(); + break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (byte & 0x80) + int_mask |= byte & 0x7f; + else + int_mask &= ~byte; + if (icr & int_mask & 0x1f) { // Trigger IRQ if pending + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } + break; + + case 0xe: + has_new_cra = true; // Delay write by 1 cycle + new_cra = byte; + ta_cnt_phi2 = ((byte & 0x20) == 0x00); + break; + + case 0xf: + has_new_crb = true; // Delay write by 1 cycle + new_crb = byte; + tb_cnt_phi2 = ((byte & 0x60) == 0x00); + tb_cnt_ta = ((byte & 0x60) == 0x40); + break; + } +} + + +/* + * Write to register (CIA 2) + */ + +void MOS6526_2::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0:{ + pra = byte; + the_vic->ChangedVA(~(pra | ~ddra) & 3); + uint8 old_lines = IECLines; + IECLines = (~byte << 2) & 0x80 // DATA + | (~byte << 2) & 0x40 // CLK + | (~byte << 1) & 0x10; // ATN + if ((IECLines ^ old_lines) & 0x10) { // ATN changed + the_cpu_1541->NewATNState(); + if (old_lines & 0x10) // ATN 1->0 + the_cpu_1541->IECInterrupt(); + } + break; + } + case 0x1: prb = byte; break; + + case 0x2: + ddra = byte; + the_vic->ChangedVA(~(pra | ~ddra) & 3); + break; + case 0x3: ddrb = byte; break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (byte & 0x80) + int_mask |= byte & 0x7f; + else + int_mask &= ~byte; + if (icr & int_mask & 0x1f) { // Trigger NMI if pending + icr |= 0x80; + the_cpu->TriggerNMI(); + } + break; + + case 0xe: + has_new_cra = true; // Delay write by 1 cycle + new_cra = byte; + ta_cnt_phi2 = ((byte & 0x20) == 0x00); + break; + + case 0xf: + has_new_crb = true; // Delay write by 1 cycle + new_crb = byte; + tb_cnt_phi2 = ((byte & 0x60) == 0x00); + tb_cnt_ta = ((byte & 0x60) == 0x40); + break; + } +} + + +/* + * Emulate CIA for one cycle/raster line + */ + +void MOS6526::EmulateCycle(void) +{ + bool ta_underflow = false; + + // Timer A state machine + switch (ta_state) { + case T_WAIT_THEN_COUNT: + ta_state = T_COUNT; // fall through + case T_STOP: + goto ta_idle; + case T_LOAD_THEN_STOP: + ta_state = T_STOP; + ta = latcha; // Reload timer + goto ta_idle; + case T_LOAD_THEN_COUNT: + ta_state = T_COUNT; + ta = latcha; // Reload timer + goto ta_idle; + case T_LOAD_THEN_WAIT_THEN_COUNT: + ta_state = T_WAIT_THEN_COUNT; + if (ta == 1) + goto ta_interrupt; // Interrupt if timer == 1 + else { + ta = latcha; // Reload timer + goto ta_idle; + } + case T_COUNT: + goto ta_count; + case T_COUNT_THEN_STOP: + ta_state = T_STOP; + goto ta_count; + } + + // Count timer A +ta_count: + if (ta_cnt_phi2) + if (!ta || !--ta) { // Decrement timer, underflow? + if (ta_state != T_STOP) { +ta_interrupt: + ta = latcha; // Reload timer + ta_irq_next_cycle = true; // Trigger interrupt in next cycle + icr |= 1; // But set ICR bit now + + if (cra & 8) { // One-shot? + cra &= 0xfe; // Yes, stop timer + new_cra &= 0xfe; + ta_state = T_LOAD_THEN_STOP; // Reload in next cycle + } else + ta_state = T_LOAD_THEN_COUNT; // No, delay one cycle (and reload) + } + ta_underflow = true; + } + + // Delayed write to CRA? +ta_idle: + if (has_new_cra) { + switch (ta_state) { + case T_STOP: + case T_LOAD_THEN_STOP: + if (new_cra & 1) { // Timer started, wasn't running + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_WAIT_THEN_COUNT; + else // No force load + ta_state = T_WAIT_THEN_COUNT; + } else { // Timer stopped, was already stopped + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_STOP; + } + break; + case T_COUNT: + if (new_cra & 1) { // Timer started, was already running + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { // Timer stopped, was running + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_STOP; + else // No force load + ta_state = T_COUNT_THEN_STOP; + } + break; + case T_LOAD_THEN_COUNT: + case T_WAIT_THEN_COUNT: + if (new_cra & 1) { + if (new_cra & 8) { // One-shot? + new_cra &= 0xfe; // Yes, stop timer + ta_state = T_STOP; + } else if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { + ta_state = T_STOP; + } + break; + } + cra = new_cra & 0xef; + has_new_cra = false; + } + + // Timer B state machine + switch (tb_state) { + case T_WAIT_THEN_COUNT: + tb_state = T_COUNT; // fall through + case T_STOP: + goto tb_idle; + case T_LOAD_THEN_STOP: + tb_state = T_STOP; + tb = latchb; // Reload timer + goto tb_idle; + case T_LOAD_THEN_COUNT: + tb_state = T_COUNT; + tb = latchb; // Reload timer + goto ta_idle; + case T_LOAD_THEN_WAIT_THEN_COUNT: + tb_state = T_WAIT_THEN_COUNT; + if (tb == 1) + goto tb_interrupt; // Interrupt if timer == 1 + else { + tb = latchb; // Reload timer + goto tb_idle; + } + case T_COUNT: + goto tb_count; + case T_COUNT_THEN_STOP: + tb_state = T_STOP; + goto tb_count; + } + + // Count timer B +tb_count: + if (tb_cnt_phi2 || (tb_cnt_ta && ta_underflow)) + if (!tb || !--tb) { // Decrement timer, underflow? + if (tb_state != T_STOP) { +tb_interrupt: + tb = latchb; // Reload timer + tb_irq_next_cycle = true; // Trigger interrupt in next cycle + icr |= 2; // But set ICR bit now + + if (crb & 8) { // One-shot? + crb &= 0xfe; // Yes, stop timer + new_crb &= 0xfe; + tb_state = T_LOAD_THEN_STOP; // Reload in next cycle + } else + tb_state = T_LOAD_THEN_COUNT; // No, delay one cycle (and reload) + } + } + + // Delayed write to CRB? +tb_idle: + if (has_new_crb) { + switch (tb_state) { + case T_STOP: + case T_LOAD_THEN_STOP: + if (new_crb & 1) { // Timer started, wasn't running + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_WAIT_THEN_COUNT; + else // No force load + tb_state = T_WAIT_THEN_COUNT; + } else { // Timer stopped, was already stopped + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_STOP; + } + break; + case T_COUNT: + if (new_crb & 1) { // Timer started, was already running + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { // Timer stopped, was running + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_STOP; + else // No force load + tb_state = T_COUNT_THEN_STOP; + } + break; + case T_LOAD_THEN_COUNT: + case T_WAIT_THEN_COUNT: + if (new_crb & 1) { + if (new_crb & 8) { // One-shot? + new_crb &= 0xfe; // Yes, stop timer + tb_state = T_STOP; + } else if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { + tb_state = T_STOP; + } + break; + } + crb = new_crb & 0xef; + has_new_crb = false; + } +} + + +/* + * Count CIA TOD clock (called during VBlank) + */ + +void MOS6526::CountTOD(void) +{ + uint8 lo, hi; + + // Decrement frequency divider + if (tod_divider) + tod_divider--; + else { + + // Reload divider according to 50/60 Hz flag + if (cra & 0x80) + tod_divider = 4; + else + tod_divider = 5; + + // 1/10 seconds + tod_10ths++; + if (tod_10ths > 9) { + tod_10ths = 0; + + // Seconds + lo = (tod_sec & 0x0f) + 1; + hi = tod_sec >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_sec = 0; + + // Minutes + lo = (tod_min & 0x0f) + 1; + hi = tod_min >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_min = 0; + + // Hours + lo = (tod_hr & 0x0f) + 1; + hi = (tod_hr >> 4) & 1; + tod_hr &= 0x80; // Keep AM/PM flag + if (lo > 9) { + lo = 0; + hi++; + } + tod_hr |= (hi << 4) | lo; + if ((tod_hr & 0x1f) > 0x11) + tod_hr = tod_hr & 0x80 ^ 0x80; + } else + tod_min = (hi << 4) | lo; + } else + tod_sec = (hi << 4) | lo; + } + + // Alarm time reached? Trigger interrupt if enabled + if (tod_10ths == alm_10ths && tod_sec == alm_sec && + tod_min == alm_min && tod_hr == alm_hr) + TriggerInterrupt(4); + } +} + + +/* + * Trigger IRQ (CIA 1) + */ + +void MOS6526_1::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } +} + + +/* + * Trigger NMI (CIA 2) + */ + +void MOS6526_2::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerNMI(); + } +} diff --git a/Src/CPU1541.cpp b/Src/CPU1541.cpp new file mode 100644 index 0000000..09d407f --- /dev/null +++ b/Src/CPU1541.cpp @@ -0,0 +1,772 @@ +/* + * CPU1541.cpp - 6502 (1541) emulation (line based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated + * raster line. It has a cycle counter that is decremented + * by every executed opcode and if the counter goes below + * zero, the function returns. + * - Memory map (1541C, the 1541 and 1541-II are a bit different): + * $0000-$07ff RAM (2K) + * $0800-$0fff RAM mirror + * $1000-$17ff free + * $1800-$1bff VIA 1 + * $1c00-$1fff VIA 2 + * $2000-$bfff free + * $c000-$ffff ROM (16K) + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. The read_zp() and write_zp() functions allow + * faster access to the zero page, the pop_byte() and + * push_byte() macros for the stack. + * - The PC is either emulated with a 16 bit address or a + * direct memory pointer (for faster access), depending on + * the PC_IS_POINTER #define. In the latter case, a second + * pointer, pc_base, is kept to allow recalculating the + * 16 bit 6502 PC if it has to be pushed on the stack. + * - The possible interrupt sources are: + * INT_VIA1IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_VIA2IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_IECIRQ: I flag is checked, jump to ($fffe) (unused) + * INT_RESET: Jump to ($fffc) + * - Interrupts are not checked before every opcode but only + * at certain times: + * On entering EmulateLine() + * On CLI + * On PLP if the I flag was cleared + * On RTI if the I flag was cleared + * - The z_flag variable has the inverse meaning of the + * 6502 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6502 is + * used to implement emulator-specific functions + * - The 1541 6502 emulation also includes a very simple VIA + * emulation (enough to make the IEC bus and GCR loading work). + * It's too small to move it to a source file of its own. + * + * Incompatibilities: + * ------------------ + * + * - If PC_IS_POINTER is set, neither branches accross memory + * areas nor jumps to I/O space are possible + * - Extra cycles for crossing page boundaries are not + * accounted for + */ + +#include "sysdeps.h" + +#include "CPU1541.h" +#include "1541job.h" +#include "C64.h" +#include "CIA.h" +#include "Display.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6502 constructor: Initialize registers + */ + +MOS6502_1541::MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom) + : ram(Ram), rom(Rom), the_c64(c64), the_display(disp), the_job(job) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + + borrowed_cycles = 0; + + via1_t1c = via1_t1l = via1_t2c = via1_t2l = 0; + via1_sr = 0; + via2_t1c = via2_t1l = via2_t2c = via2_t2l = 0; + via2_sr = 0; + + Idle = false; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6502_1541::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; + Idle = false; +} + + +/* + * Read a byte from I/O space + */ + +inline uint8 MOS6502_1541::read_byte_io(uint16 adr) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + return (via1_prb & 0x1a + | ((IECLines & TheCIA2->IECLines) >> 7) // DATA + | ((IECLines & TheCIA2->IECLines) >> 4) & 0x04 // CLK + | (TheCIA2->IECLines << 3) & 0x80) ^ 0x85; // ATN + case 1: + case 15: + return 0xff; // Keep 1541C ROMs happy (track 0 sensor) + case 2: + return via1_ddrb; + case 3: + return via1_ddra; + case 4: + via1_ifr &= 0xbf; + return via1_t1c; + case 5: + return via1_t1c >> 8; + case 6: + return via1_t1l; + case 7: + return via1_t1l >> 8; + case 8: + via1_ifr &= 0xdf; + return via1_t2c; + case 9: + return via1_t2c >> 8; + case 10: + return via1_sr; + case 11: + return via1_acr; + case 12: + return via1_pcr; + case 13: + return via1_ifr | (via1_ifr & via1_ier ? 0x80 : 0); + case 14: + return via1_ier | 0x80; + default: // Can't happen + return 0; + } + + else if ((adr & 0xfc00) == 0x1c00) // VIA 2 + switch (adr & 0xf) { + case 0: + if (the_job->SyncFound()) + return via2_prb & 0x7f | the_job->WPState(); + else + return via2_prb | 0x80 | the_job->WPState(); + case 1: + case 15: + return the_job->ReadGCRByte(); + case 2: + return via2_ddrb; + case 3: + return via2_ddra; + case 4: + via2_ifr &= 0xbf; + interrupt.intr[INT_VIA2IRQ] = false; // Clear job IRQ + return via2_t1c; + case 5: + return via2_t1c >> 8; + case 6: + return via2_t1l; + case 7: + return via2_t1l >> 8; + case 8: + via2_ifr &= 0xdf; + return via2_t2c; + case 9: + return via2_t2c >> 8; + case 10: + return via2_sr; + case 11: + return via2_acr; + case 12: + return via2_pcr; + case 13: + return via2_ifr | (via2_ifr & via2_ier ? 0x80 : 0); + case 14: + return via2_ier | 0x80; + default: // Can't happen + return 0; + } + + else + return adr >> 8; +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6502_1541::read_byte(uint16 adr) +{ + if (adr >= 0xc000) + return rom[adr & 0x3fff]; + else if (adr < 0x1000) + return ram[adr & 0x07ff]; + else + return read_byte_io(adr); +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +inline uint16 MOS6502_1541::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + + +/* + * Write a byte to I/O space + */ + +void MOS6502_1541::write_byte_io(uint16 adr, uint8 byte) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + via1_prb = byte; + byte = ~via1_prb & via1_ddrb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 1: + case 15: + via1_pra = byte; + break; + case 2: + via1_ddrb = byte; + byte &= ~via1_prb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 3: + via1_ddra = byte; + break; + case 4: + case 6: + via1_t1l = via1_t1l & 0xff00 | byte; + break; + case 5: + via1_t1l = via1_t1l & 0xff | (byte << 8); + via1_ifr &= 0xbf; + via1_t1c = via1_t1l; + break; + case 7: + via1_t1l = via1_t1l & 0xff | (byte << 8); + break; + case 8: + via1_t2l = via1_t2l & 0xff00 | byte; + break; + case 9: + via1_t2l = via1_t2l & 0xff | (byte << 8); + via1_ifr &= 0xdf; + via1_t2c = via1_t2l; + break; + case 10: + via1_sr = byte; + break; + case 11: + via1_acr = byte; + break; + case 12: + via1_pcr = byte; + break; + case 13: + via1_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via1_ier |= byte & 0x7f; + else + via1_ier &= ~byte; + break; + } + + else if ((adr & 0xfc00) == 0x1c00) // VIA 2 + switch (adr & 0xf) { + case 0: + if ((via2_prb ^ byte) & 8) // Bit 3: Drive LED + the_display->UpdateLEDs(byte & 8 ? 1 : 0, 0, 0, 0); + if ((via2_prb ^ byte) & 3) // Bits 0/1: Stepper motor + if ((via2_prb & 3) == ((byte+1) & 3)) + the_job->MoveHeadOut(); + else if ((via2_prb & 3) == ((byte-1) & 3)) + the_job->MoveHeadIn(); + via2_prb = byte & 0xef; + break; + case 1: + case 15: + via2_pra = byte; + break; + case 2: + via2_ddrb = byte; + break; + case 3: + via2_ddra = byte; + break; + case 4: + case 6: + via2_t1l = via2_t1l & 0xff00 | byte; + break; + case 5: + via2_t1l = via2_t1l & 0xff | (byte << 8); + via2_ifr &= 0xbf; + via2_t1c = via2_t1l; + break; + case 7: + via2_t1l = via2_t1l & 0xff | (byte << 8); + break; + case 8: + via2_t2l = via2_t2l & 0xff00 | byte; + break; + case 9: + via2_t2l = via2_t2l & 0xff | (byte << 8); + via2_ifr &= 0xdf; + via2_t2c = via2_t2l; + break; + case 10: + via2_sr = byte; + break; + case 11: + via2_acr = byte; + break; + case 12: + via2_pcr = byte; + break; + case 13: + via2_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via2_ier |= byte & 0x7f; + else + via2_ier &= ~byte; + break; + } +} + + +/* + * Write a byte to the CPU's address space + */ + +inline void MOS6502_1541::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0x1000) + ram[adr & 0x7ff] = byte; + else + write_byte_io(adr, byte); +} + + +/* + * Read a byte from the zeropage + */ + +inline uint8 MOS6502_1541::read_zp(uint16 adr) +{ + return ram[adr]; +} + + +/* + * Read a word (little-endian) from the zeropage + */ + +inline uint16 MOS6502_1541::read_zp_word(uint16 adr) +{ + return ram[adr & 0xff] | (ram[(adr+1) & 0xff] << 8); +} + + +/* + * Write a byte to the zeropage + */ + +inline void MOS6502_1541::write_zp(uint16 adr, uint8 byte) +{ + ram[adr] = byte; +} + + +/* + * Read byte from 6502/1541 address space (used by SAM) + */ + +uint8 MOS6502_1541::ExtReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6502/1541 address space (used by SAM) + */ + +void MOS6502_1541::ExtWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Jump to address + */ + +#if PC_IS_POINTER +void MOS6502_1541::jump(uint16 adr) +{ + if (adr >= 0xc000) { + pc = rom + (adr & 0x3fff); + pc_base = rom - 0xc000; + } else if (adr < 0x800) { + pc = ram + adr; + pc_base = ram; + } else + illegal_jump(pc-pc_base, adr); +} +#else +inline void MOS6502_1541::jump(uint16 adr) +{ + pc = adr; +} +#endif + + +/* + * Adc instruction + */ + +void MOS6502_1541::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +void MOS6502_1541::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Get 6502 register state + */ + +void MOS6502_1541::GetState(MOS6502State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + +#if PC_IS_POINTER + s->pc = pc - pc_base; +#else + s->pc = pc; +#endif + s->sp = sp | 0x0100; + + s->intr[INT_VIA1IRQ] = interrupt.intr[INT_VIA1IRQ]; + s->intr[INT_VIA2IRQ] = interrupt.intr[INT_VIA2IRQ]; + s->intr[INT_IECIRQ] = interrupt.intr[INT_IECIRQ]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->instruction_complete = true; + s->idle = Idle; + + s->via1_pra = via1_pra; s->via1_ddra = via1_ddra; + s->via1_prb = via1_prb; s->via1_ddrb = via1_ddrb; + s->via1_t1c = via1_t1c; s->via1_t1l = via1_t1l; + s->via1_t2c = via1_t2c; s->via1_t2l = via1_t2l; + s->via1_sr = via1_sr; + s->via1_acr = via1_acr; s->via1_pcr = via1_pcr; + s->via1_ifr = via1_ifr; s->via1_ier = via1_ier; + + s->via2_pra = via2_pra; s->via2_ddra = via2_ddra; + s->via2_prb = via2_prb; s->via2_ddrb = via2_ddrb; + s->via2_t1c = via2_t1c; s->via2_t1l = via2_t1l; + s->via2_t2c = via2_t2c; s->via2_t2l = via2_t2l; + s->via2_sr = via2_sr; + s->via2_acr = via2_acr; s->via2_pcr = via2_pcr; + s->via2_ifr = via2_ifr; s->via2_ier = via2_ier; +} + + +/* + * Restore 6502 state + */ + +void MOS6502_1541::SetState(MOS6502State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + jump(s->pc); + sp = s->sp & 0xff; + + interrupt.intr[INT_VIA1IRQ] = s->intr[INT_VIA1IRQ]; + interrupt.intr[INT_VIA2IRQ] = s->intr[INT_VIA2IRQ]; + interrupt.intr[INT_IECIRQ] = s->intr[INT_IECIRQ]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + Idle = s->idle; + + via1_pra = s->via1_pra; via1_ddra = s->via1_ddra; + via1_prb = s->via1_prb; via1_ddrb = s->via1_ddrb; + via1_t1c = s->via1_t1c; via1_t1l = s->via1_t1l; + via1_t2c = s->via1_t2c; via1_t2l = s->via1_t2l; + via1_sr = s->via1_sr; + via1_acr = s->via1_acr; via1_pcr = s->via1_pcr; + via1_ifr = s->via1_ifr; via1_ier = s->via1_ier; + + via2_pra = s->via2_pra; via2_ddra = s->via2_ddra; + via2_prb = s->via2_prb; via2_ddrb = s->via2_ddrb; + via2_t1c = s->via2_t1c; via2_t1l = s->via2_t1l; + via2_t2c = s->via2_t2c; via2_t2l = s->via2_t2l; + via2_sr = s->via2_sr; + via2_acr = s->via2_acr; via2_pcr = s->via2_pcr; + via2_ifr = s->via2_ifr; via2_ier = s->via2_ier; +} + + +/* + * Reset CPU + */ + +void MOS6502_1541::Reset(void) +{ + // IEC lines and VIA registers + IECLines = 0xc0; + + via1_pra = via1_ddra = via1_prb = via1_ddrb = 0; + via1_acr = via1_pcr = 0; + via1_ifr = via1_ier = 0; + via2_pra = via2_ddra = via2_prb = via2_ddrb = 0; + via2_acr = via2_pcr = 0; + via2_ifr = via2_ier = 0; + + // Clear all interrupt lines + interrupt.intr_any = 0; + + // Read reset vector + jump(read_word(0xfffc)); + + // Wake up 1541 + Idle = false; +} + + +/* + * Illegal opcode encountered + */ + +void MOS6502_1541::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "1541: Illegal opcode %02x at %04x.", op, at); + if (ShowRequester(illop_msg, "Reset 1541", "Reset C64")) + the_c64->Reset(); + Reset(); +} + + +/* + * Jump to illegal address space (PC_IS_POINTER only) + */ + +void MOS6502_1541::illegal_jump(uint16 at, uint16 to) +{ + char illop_msg[80]; + + sprintf(illop_msg, "1541: Jump to I/O space at %04x to %04x.", at, to); + if (ShowRequester(illop_msg, "Reset 1541", "Reset C64")) + the_c64->Reset(); + Reset(); +} + + +/* + * Stack macros + */ + +// Pop a byte from the stack +#define pop_byte() ram[(++sp) | 0x0100] + +// Push a byte onto the stack +#define push_byte(byte) (ram[(sp--) & 0xff | 0x0100] = (byte)) + +// Pop processor flags from the stack +#define pop_flags() \ + n_flag = tmp = pop_byte(); \ + v_flag = tmp & 0x40; \ + d_flag = tmp & 0x08; \ + i_flag = tmp & 0x04; \ + z_flag = !(tmp & 0x02); \ + c_flag = tmp & 0x01; + +// Push processor flags onto the stack +#define push_flags(b_flag) \ + tmp = 0x20 | (n_flag & 0x80); \ + if (v_flag) tmp |= 0x40; \ + if (b_flag) tmp |= 0x10; \ + if (d_flag) tmp |= 0x08; \ + if (i_flag) tmp |= 0x04; \ + if (!z_flag) tmp |= 0x02; \ + if (c_flag) tmp |= 0x01; \ + push_byte(tmp); + + +/* + * Emulate cycles_left worth of 6502 instructions + * Returns number of cycles of last instruction + */ + +int MOS6502_1541::EmulateLine(int cycles_left) +{ + uint8 tmp, tmp2; + uint16 adr; + int last_cycles = 0; + + // Any pending interrupts? + if (interrupt.intr_any) { +handle_int: + if (interrupt.intr[INT_RESET]) + Reset(); + + else if ((interrupt.intr[INT_VIA1IRQ] || interrupt.intr[INT_VIA2IRQ] || interrupt.intr[INT_IECIRQ]) && !i_flag) { +#if PC_IS_POINTER + push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base); +#else + push_byte(pc >> 8); push_byte(pc); +#endif + push_flags(false); + i_flag = true; + jump(read_word(0xfffe)); + last_cycles = 7; + } + } + +#define IS_CPU_1541 +#include "CPU_emulline.i" + + // Extension opcode + case 0xf2: +#if PC_IS_POINTER + if ((pc-pc_base) < 0xc000) { + illegal_op(0xf2, pc-pc_base-1); +#else + if (pc < 0xc000) { + illegal_op(0xf2, pc-1); +#endif + break; + } + switch (read_byte_imm()) { + case 0x00: // Go to sleep in DOS idle loop if error flag is clear and no command received + Idle = !(ram[0x26c] | ram[0x7c]); + jump(0xebff); + break; + case 0x01: // Write sector + the_job->WriteSector(); + jump(0xf5dc); + break; + case 0x02: // Format track + the_job->FormatTrack(); + jump(0xfd8b); + break; + default: +#if PC_IS_POINTER + illegal_op(0xf2, pc-pc_base-1); +#else + illegal_op(0xf2, pc-1); +#endif + break; + } + break; + } + } + return last_cycles; +} diff --git a/Src/CPU1541.h b/Src/CPU1541.h new file mode 100644 index 0000000..6c56202 --- /dev/null +++ b/Src/CPU1541.h @@ -0,0 +1,281 @@ +/* + * CPU1541.h - 6502 (1541) emulation (line based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _CPU_1541_H +#define _CPU_1541_H + +#include "CIA.h" +#include "C64.h" + + +// Set this to 1 if the 6502 PC should be represented by a real pointer +#ifndef FRODO_SC +#ifndef PC_IS_POINTER +#define PC_IS_POINTER 1 +#endif +#endif + +// Set this to 1 for more precise CPU cycle calculation +#ifndef PRECISE_CPU_CYCLES +#define PRECISE_CPU_CYCLES 0 +#endif + + +// Interrupt types +enum { + INT_VIA1IRQ, + INT_VIA2IRQ, + INT_IECIRQ + // INT_RESET (private) +}; + + +class C64; +class Job1541; +class C64Display; +struct MOS6502State; + + +// 6502 emulation (1541) +class MOS6502_1541 { +public: + MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom); + +#ifdef FRODO_SC + void EmulateCycle(void); // Emulate one clock cycle +#else + int EmulateLine(int cycles_left); // Emulate until cycles_left underflows +#endif + void Reset(void); + void AsyncReset(void); // Reset the CPU asynchronously + void GetState(MOS6502State *s); + void SetState(MOS6502State *s); + uint8 ExtReadByte(uint16 adr); + void ExtWriteByte(uint16 adr, uint8 byte); + void CountVIATimers(int cycles); + void NewATNState(void); + void IECInterrupt(void); + void TriggerJobIRQ(void); + bool InterruptEnabled(void); + + MOS6526_2 *TheCIA2; // Pointer to C64 CIA 2 + + uint8 IECLines; // State of IEC lines (bit 7 - DATA, bit 6 - CLK) + bool Idle; // true: 1541 is idle + +private: + uint8 read_byte(uint16 adr); + uint8 read_byte_io(uint16 adr); + uint16 read_word(uint16 adr); + void write_byte(uint16 adr, uint8 byte); + void write_byte_io(uint16 adr, uint8 byte); + + uint8 read_zp(uint16 adr); + uint16 read_zp_word(uint16 adr); + void write_zp(uint16 adr, uint8 byte); + + void jump(uint16 adr); + void illegal_op(uint8 op, uint16 at); + void illegal_jump(uint16 at, uint16 to); + + void do_adc(uint8 byte); + void do_sbc(uint8 byte); + + uint8 *ram; // Pointer to main RAM + uint8 *rom; // Pointer to ROM + C64 *the_c64; // Pointer to C64 object + C64Display *the_display; // Pointer to C64 display object + Job1541 *the_job; // Pointer to 1541 job object + + union { // Pending interrupts + uint8 intr[4]; // Index: See definitions above + unsigned long intr_any; + } interrupt; + + uint8 n_flag, z_flag; + bool v_flag, d_flag, i_flag, c_flag; + uint8 a, x, y, sp; +#if PC_IS_POINTER + uint8 *pc, *pc_base; +#else + uint16 pc; +#endif + +#ifdef FRODO_SC + uint32 first_irq_cycle; + + uint8 state, op; // Current state and opcode + uint16 ar, ar2; // Address registers + uint8 rdbuf; // Data buffer for RMW instructions + uint8 ddr, pr; // Processor port +#else + int borrowed_cycles; // Borrowed cycles from next line +#endif + + uint8 via1_pra; // PRA of VIA 1 + uint8 via1_ddra; // DDRA of VIA 1 + uint8 via1_prb; // PRB of VIA 1 + uint8 via1_ddrb; // DDRB of VIA 1 + uint16 via1_t1c; // T1 Counter of VIA 1 + uint16 via1_t1l; // T1 Latch of VIA 1 + uint16 via1_t2c; // T2 Counter of VIA 1 + uint16 via1_t2l; // T2 Latch of VIA 1 + uint8 via1_sr; // SR of VIA 1 + uint8 via1_acr; // ACR of VIA 1 + uint8 via1_pcr; // PCR of VIA 1 + uint8 via1_ifr; // IFR of VIA 1 + uint8 via1_ier; // IER of VIA 1 + + uint8 via2_pra; // PRA of VIA 2 + uint8 via2_ddra; // DDRA of VIA 2 + uint8 via2_prb; // PRB of VIA 2 + uint8 via2_ddrb; // DDRB of VIA 2 + uint16 via2_t1c; // T1 Counter of VIA 2 + uint16 via2_t1l; // T1 Latch of VIA 2 + uint16 via2_t2c; // T2 Counter of VIA 2 + uint16 via2_t2l; // T2 Latch of VIA 2 + uint8 via2_sr; // SR of VIA 2 + uint8 via2_acr; // ACR of VIA 2 + uint8 via2_pcr; // PCR of VIA 2 + uint8 via2_ifr; // IFR of VIA 2 + uint8 via2_ier; // IER of VIA 2 +}; + +// 6502 state +struct MOS6502State { + uint8 a, x, y; + uint8 p; // Processor flags + uint16 pc, sp; + + uint8 intr[4]; // Interrupt state + bool instruction_complete; + bool idle; + + uint8 via1_pra; // VIA 1 + uint8 via1_ddra; + uint8 via1_prb; + uint8 via1_ddrb; + uint16 via1_t1c; + uint16 via1_t1l; + uint16 via1_t2c; + uint16 via1_t2l; + uint8 via1_sr; + uint8 via1_acr; + uint8 via1_pcr; + uint8 via1_ifr; + uint8 via1_ier; + + uint8 via2_pra; // VIA 2 + uint8 via2_ddra; + uint8 via2_prb; + uint8 via2_ddrb; + uint16 via2_t1c; + uint16 via2_t1l; + uint16 via2_t2c; + uint16 via2_t2l; + uint8 via2_sr; + uint8 via2_acr; + uint8 via2_pcr; + uint8 via2_ifr; + uint8 via2_ier; +}; + + + +/* + * Trigger job loop IRQ + */ + +#ifdef FRODO_SC +inline void MOS6502_1541::TriggerJobIRQ(void) +{ + if (!(interrupt.intr[INT_VIA2IRQ])) + first_irq_cycle = the_c64->CycleCounter; + interrupt.intr[INT_VIA2IRQ] = true; + Idle = false; +} +#else +inline void MOS6502_1541::TriggerJobIRQ(void) +{ + interrupt.intr[INT_VIA2IRQ] = true; + Idle = false; +} +#endif + + +/* + * Count VIA timers + */ + +inline void MOS6502_1541::CountVIATimers(int cycles) +{ + unsigned long tmp; + + via1_t1c = tmp = via1_t1c - cycles; + if (tmp > 0xffff) { + if (via1_acr & 0x40) // Reload from latch in free-run mode + via1_t1c = via1_t1l; + via1_ifr |= 0x40; + } + + if (!(via1_acr & 0x20)) { // Only count in one-shot mode + via1_t2c = tmp = via1_t2c - cycles; + if (tmp > 0xffff) + via1_ifr |= 0x20; + } + + via2_t1c = tmp = via2_t1c - cycles; + if (tmp > 0xffff) { + if (via2_acr & 0x40) // Reload from latch in free-run mode + via2_t1c = via2_t1l; + via2_ifr |= 0x40; + if (via2_ier & 0x40) + TriggerJobIRQ(); + } + + if (!(via2_acr & 0x20)) { // Only count in one-shot mode + via2_t2c = tmp = via2_t2c - cycles; + if (tmp > 0xffff) + via2_ifr |= 0x20; + } +} + + +/* + * ATN line probably changed state, recalc IECLines + */ + +inline void MOS6502_1541::NewATNState(void) +{ + uint8 byte = ~via1_prb & via1_ddrb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 // DATA (incl. ATN acknowledge) + | (byte << 3) & 0x40; // CLK +} + + +/* + * Interrupt by negative edge of ATN on IEC bus + */ + +inline void MOS6502_1541::IECInterrupt(void) +{ + ram[0x7c] = 1; + + // Wake up 1541 + Idle = false; +} + + +/* + * Test if interrupts are enabled (for job loop) + */ + +inline bool MOS6502_1541::InterruptEnabled(void) +{ + return !i_flag; +} + +#endif diff --git a/Src/CPU1541_PC.cpp b/Src/CPU1541_PC.cpp new file mode 100644 index 0000000..e284e28 --- /dev/null +++ b/Src/CPU1541_PC.cpp @@ -0,0 +1,12 @@ +/* + * CPU1541_PC.cpp - Put the pieces together (Frodo PC) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +// Same as CPU1541.cpp (mainly to keep the BeIDE happy) +#ifdef __riscos__ +#include "CPU1541.cc" +#else +#include "CPU1541.cpp" +#endif diff --git a/Src/CPU1541_SC.cpp b/Src/CPU1541_SC.cpp new file mode 100644 index 0000000..f7d0207 --- /dev/null +++ b/Src/CPU1541_SC.cpp @@ -0,0 +1,647 @@ +/* + * CPU1541_SC.cpp - Single-cycle 6502 (1541) emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * Opcode execution: + * - All opcodes are resolved into single clock cycles. There is one + * switch case for each cycle. + * - The "state" variable specifies the routine to be executed in the + * next cycle. Its upper 8 bits contain the current opcode, its lower + * 8 bits contain the cycle number (0..7) within the opcode. + * - Opcodes are fetched in cycle 0 (state = 0) + * - The states 0x0010..0x0027 are used for interrupts + * - There is exactly one memory access in each clock cycle + * + * Memory map (1541C, the 1541 and 1541-II are a bit different): + * + * $0000-$07ff RAM (2K) + * $0800-$0fff RAM mirror + * $1000-$17ff free + * $1800-$1bff VIA 1 + * $1c00-$1fff VIA 2 + * $2000-$bfff free + * $c000-$ffff ROM (16K) + * + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. + * - The possible interrupt sources are: + * INT_VIA1IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_VIA2IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_IECIRQ: I flag is checked, jump to ($fffe) (unused) + * INT_RESET: Jump to ($fffc) + * - The z_flag variable has the inverse meaning of the + * 6502 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6502 is + * used to implement emulator-specific functions + * - The 1541 6502 emulation also includes a very simple VIA + * emulation (enough to make the IEC bus and GCR loading work). + * It's too small to move it to a source file of its own. + * + * Incompatibilities: + * ------------------ + * + * - VIA emulation incomplete + */ + +#include "sysdeps.h" + +#include "CPU1541.h" +#include "CPU_common.h" +#include "1541job.h" +#include "C64.h" +#include "CIA.h" +#include "Display.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6502 constructor: Initialize registers + */ + +MOS6502_1541::MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom) + : ram(Ram), rom(Rom), the_c64(c64), the_display(disp), the_job(job) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + + via1_t1c = via1_t1l = via1_t2c = via1_t2l = 0; + via1_sr = 0; + via2_t1c = via2_t1l = via2_t2c = via2_t2l = 0; + via2_sr = 0; + + first_irq_cycle = 0; + Idle = false; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6502_1541::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; + Idle = false; +} + + +/* + * Get 6502 register state + */ + +void MOS6502_1541::GetState(MOS6502State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + + s->pc = pc; + s->sp = sp | 0x0100; + + s->intr[INT_VIA1IRQ] = interrupt.intr[INT_VIA1IRQ]; + s->intr[INT_VIA2IRQ] = interrupt.intr[INT_VIA2IRQ]; + s->intr[INT_IECIRQ] = interrupt.intr[INT_IECIRQ]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->idle = Idle; + + s->via1_pra = via1_pra; s->via1_ddra = via1_ddra; + s->via1_prb = via1_prb; s->via1_ddrb = via1_ddrb; + s->via1_t1c = via1_t1c; s->via1_t1l = via1_t1l; + s->via1_t2c = via1_t2c; s->via1_t2l = via1_t2l; + s->via1_sr = via1_sr; + s->via1_acr = via1_acr; s->via1_pcr = via1_pcr; + s->via1_ifr = via1_ifr; s->via1_ier = via1_ier; + + s->via2_pra = via2_pra; s->via2_ddra = via2_ddra; + s->via2_prb = via2_prb; s->via2_ddrb = via2_ddrb; + s->via2_t1c = via2_t1c; s->via2_t1l = via2_t1l; + s->via2_t2c = via2_t2c; s->via2_t2l = via2_t2l; + s->via2_sr = via2_sr; + s->via2_acr = via2_acr; s->via2_pcr = via2_pcr; + s->via2_ifr = via2_ifr; s->via2_ier = via2_ier; +} + + +/* + * Restore 6502 state + */ + +void MOS6502_1541::SetState(MOS6502State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + pc = s->pc; + sp = s->sp & 0xff; + + interrupt.intr[INT_VIA1IRQ] = s->intr[INT_VIA1IRQ]; + interrupt.intr[INT_VIA2IRQ] = s->intr[INT_VIA2IRQ]; + interrupt.intr[INT_IECIRQ] = s->intr[INT_IECIRQ]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + Idle = s->idle; + + via1_pra = s->via1_pra; via1_ddra = s->via1_ddra; + via1_prb = s->via1_prb; via1_ddrb = s->via1_ddrb; + via1_t1c = s->via1_t1c; via1_t1l = s->via1_t1l; + via1_t2c = s->via1_t2c; via1_t2l = s->via1_t2l; + via1_sr = s->via1_sr; + via1_acr = s->via1_acr; via1_pcr = s->via1_pcr; + via1_ifr = s->via1_ifr; via1_ier = s->via1_ier; + + via2_pra = s->via2_pra; via2_ddra = s->via2_ddra; + via2_prb = s->via2_prb; via2_ddrb = s->via2_ddrb; + via2_t1c = s->via2_t1c; via2_t1l = s->via2_t1l; + via2_t2c = s->via2_t2c; via2_t2l = s->via2_t2l; + via2_sr = s->via2_sr; + via2_acr = s->via2_acr; via2_pcr = s->via2_pcr; + via2_ifr = s->via2_ifr; via2_ier = s->via2_ier; +} + + +/* + * Read a byte from I/O space + */ + +inline uint8 MOS6502_1541::read_byte_io(uint16 adr) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + return (via1_prb & 0x1a + | ((IECLines & TheCIA2->IECLines) >> 7) // DATA + | ((IECLines & TheCIA2->IECLines) >> 4) & 0x04 // CLK + | (TheCIA2->IECLines << 3) & 0x80) ^ 0x85; // ATN + case 1: + case 15: + return 0xff; // Keep 1541C ROMs happy (track 0 sensor) + case 2: + return via1_ddrb; + case 3: + return via1_ddra; + case 4: + via1_ifr &= 0xbf; + return via1_t1c; + case 5: + return via1_t1c >> 8; + case 6: + return via1_t1l; + case 7: + return via1_t1l >> 8; + case 8: + via1_ifr &= 0xdf; + return via1_t2c; + case 9: + return via1_t2c >> 8; + case 10: + return via1_sr; + case 11: + return via1_acr; + case 12: + return via1_pcr; + case 13: + return via1_ifr | (via1_ifr & via1_ier ? 0x80 : 0); + case 14: + return via1_ier | 0x80; + default: // Can't happen + return 0; + } + + else if ((adr & 0xfc00) == 0x1c00) // VIA 2 + switch (adr & 0xf) { + case 0: + if (the_job->SyncFound()) + return via2_prb & 0x7f | the_job->WPState(); + else + return via2_prb | 0x80 | the_job->WPState(); + case 1: + case 15: + return the_job->ReadGCRByte(); + case 2: + return via2_ddrb; + case 3: + return via2_ddra; + case 4: + via2_ifr &= 0xbf; + interrupt.intr[INT_VIA2IRQ] = false; // Clear job IRQ + return via2_t1c; + case 5: + return via2_t1c >> 8; + case 6: + return via2_t1l; + case 7: + return via2_t1l >> 8; + case 8: + via2_ifr &= 0xdf; + return via2_t2c; + case 9: + return via2_t2c >> 8; + case 10: + return via2_sr; + case 11: + return via2_acr; + case 12: + return via2_pcr; + case 13: + return via2_ifr | (via2_ifr & via2_ier ? 0x80 : 0); + case 14: + return via2_ier | 0x80; + default: // Can't happen + return 0; + } + + else + return adr >> 8; +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6502_1541::read_byte(uint16 adr) +{ + if (adr >= 0xc000) + return rom[adr & 0x3fff]; + else if (adr < 0x1000) + return ram[adr & 0x07ff]; + else + return read_byte_io(adr); +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +inline uint16 MOS6502_1541::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + + +/* + * Write a byte to I/O space + */ + +void MOS6502_1541::write_byte_io(uint16 adr, uint8 byte) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + via1_prb = byte; + byte = ~via1_prb & via1_ddrb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 1: + case 15: + via1_pra = byte; + break; + case 2: + via1_ddrb = byte; + byte &= ~via1_prb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 3: + via1_ddra = byte; + break; + case 4: + case 6: + via1_t1l = via1_t1l & 0xff00 | byte; + break; + case 5: + via1_t1l = via1_t1l & 0xff | (byte << 8); + via1_ifr &= 0xbf; + via1_t1c = via1_t1l; + break; + case 7: + via1_t1l = via1_t1l & 0xff | (byte << 8); + break; + case 8: + via1_t2l = via1_t2l & 0xff00 | byte; + break; + case 9: + via1_t2l = via1_t2l & 0xff | (byte << 8); + via1_ifr &= 0xdf; + via1_t2c = via1_t2l; + break; + case 10: + via1_sr = byte; + break; + case 11: + via1_acr = byte; + break; + case 12: + via1_pcr = byte; + break; + case 13: + via1_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via1_ier |= byte & 0x7f; + else + via1_ier &= ~byte; + break; + } + + else if ((adr & 0xfc00) == 0x1c00) + switch (adr & 0xf) { + case 0: + if ((via2_prb ^ byte) & 8) // Bit 3: Drive LED + the_display->UpdateLEDs(byte & 8 ? 1 : 0, 0, 0, 0); + if ((via2_prb ^ byte) & 3) // Bits 0/1: Stepper motor + if ((via2_prb & 3) == ((byte+1) & 3)) + the_job->MoveHeadOut(); + else if ((via2_prb & 3) == ((byte-1) & 3)) + the_job->MoveHeadIn(); + via2_prb = byte & 0xef; + break; + case 1: + case 15: + via2_pra = byte; + break; + case 2: + via2_ddrb = byte; + break; + case 3: + via2_ddra = byte; + break; + case 4: + case 6: + via2_t1l = via2_t1l & 0xff00 | byte; + break; + case 5: + via2_t1l = via2_t1l & 0xff | (byte << 8); + via2_ifr &= 0xbf; + via2_t1c = via2_t1l; + break; + case 7: + via2_t1l = via2_t1l & 0xff | (byte << 8); + break; + case 8: + via2_t2l = via2_t2l & 0xff00 | byte; + break; + case 9: + via2_t2l = via2_t2l & 0xff | (byte << 8); + via2_ifr &= 0xdf; + via2_t2c = via2_t2l; + break; + case 10: + via2_sr = byte; + break; + case 11: + via2_acr = byte; + break; + case 12: + via2_pcr = byte; + break; + case 13: + via2_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via2_ier |= byte & 0x7f; + else + via2_ier &= ~byte; + break; + } +} + + +/* + * Write a byte to the CPU's address space + */ + +inline void MOS6502_1541::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0x1000) + ram[adr & 0x7ff] = byte; + else + write_byte_io(adr, byte); +} + + +/* + * Read byte from 6502/1541 address space (used by SAM) + */ + +uint8 MOS6502_1541::ExtReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6502/1541 address space (used by SAM) + */ + +void MOS6502_1541::ExtWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Adc instruction + */ + +inline void MOS6502_1541::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +inline void MOS6502_1541::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Reset CPU + */ + +void MOS6502_1541::Reset(void) +{ + // IEC lines and VIA registers + IECLines = 0xc0; + + via1_pra = via1_ddra = via1_prb = via1_ddrb = 0; + via1_acr = via1_pcr = 0; + via1_ifr = via1_ier = 0; + via2_pra = via2_ddra = via2_prb = via2_ddrb = 0; + via2_acr = via2_pcr = 0; + via2_ifr = via2_ier = 0; + + // Clear all interrupt lines + interrupt.intr_any = 0; + + // Read reset vector + pc = read_word(0xfffc); + state = 0; + + // Wake up 1541 + Idle = false; +} + + +/* + * Illegal opcode encountered + */ + +void MOS6502_1541::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "1541: Illegal opcode %02x at %04x.", op, at); + if (ShowRequester(illop_msg, "Reset 1541", "Reset C64")) + the_c64->Reset(); + Reset(); +} + + +/* + * Emulate one 6502 clock cycle + */ + +// Read byte from memory +#define read_to(adr, to) \ + to = read_byte(adr); + +// Read byte from memory, throw away result +#define read_idle(adr) \ + read_byte(adr); + +void MOS6502_1541::EmulateCycle(void) +{ + uint8 data, tmp; + + // Any pending interrupts in state 0 (opcode fetch)? + if (!state && interrupt.intr_any) { + if (interrupt.intr[INT_RESET]) + Reset(); + else if ((interrupt.intr[INT_VIA1IRQ] || interrupt.intr[INT_VIA2IRQ] || interrupt.intr[INT_IECIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag) + state = 0x0008; + } + +#define IS_CPU_1541 +#include "CPU_emulcycle.i" + + // Extension opcode + case O_EXT: + if (pc < 0xc000) { + illegal_op(0xf2, pc-1); + break; + } + switch (read_byte(pc++)) { + case 0x00: // Go to sleep in DOS idle loop if error flag is clear and no command received + Idle = !(ram[0x26c] | ram[0x7c]); + pc = 0xebff; + Last; + case 0x01: // Write sector + the_job->WriteSector(); + pc = 0xf5dc; + Last; + case 0x02: // Format track + the_job->FormatTrack(); + pc = 0xfd8b; + Last; + default: + illegal_op(0xf2, pc-1); + break; + } + break; + + default: + illegal_op(op, pc-1); + break; + } +} diff --git a/Src/CPUC64.cpp b/Src/CPUC64.cpp new file mode 100644 index 0000000..985184a --- /dev/null +++ b/Src/CPUC64.cpp @@ -0,0 +1,852 @@ +/* + * CPUC64.cpp - 6510 (C64) emulation (line based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated + * raster line. It has a cycle counter that is decremented + * by every executed opcode and if the counter goes below + * zero, the function returns. + * - Memory configurations: + * $01 $a000-$bfff $d000-$dfff $e000-$ffff + * ----------------------------------------------- + * 0 RAM RAM RAM + * 1 RAM Char ROM RAM + * 2 RAM Char ROM Kernal ROM + * 3 Basic ROM Char ROM Kernal ROM + * 4 RAM RAM RAM + * 5 RAM I/O RAM + * 6 RAM I/O Kernal ROM + * 7 Basic ROM I/O Kernal ROM + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. The read_zp() and write_zp() functions allow + * faster access to the zero page, the pop_byte() and + * push_byte() macros for the stack. + * - If a write occurs to addresses 0 or 1, new_config is + * called to check whether the memory configuration has + * changed + * - The PC is either emulated with a 16 bit address or a + * direct memory pointer (for faster access), depending on + * the PC_IS_POINTER #define. In the latter case, a second + * pointer, pc_base, is kept to allow recalculating the + * 16 bit 6510 PC if it has to be pushed on the stack. + * - The possible interrupt sources are: + * INT_VICIRQ: I flag is checked, jump to ($fffe) + * INT_CIAIRQ: I flag is checked, jump to ($fffe) + * INT_NMI: Jump to ($fffa) + * INT_RESET: Jump to ($fffc) + * - Interrupts are not checked before every opcode but only + * at certain times: + * On entering EmulateLine() + * On CLI + * On PLP if the I flag was cleared + * On RTI if the I flag was cleared + * - The z_flag variable has the inverse meaning of the + * 6510 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6510 is + * used to implement emulator-specific functions, mainly + * those for the IEC routines + * + * Incompatibilities: + * ------------------ + * + * - If PC_IS_POINTER is set, neither branches accross memory + * areas nor jumps to I/O space are possible + * - Extra cycles for crossing page boundaries are not + * accounted for + * - The cassette sense line is always closed + */ + +#include "sysdeps.h" + +#include "CPUC64.h" +#include "C64.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" +#include "REU.h" +#include "IEC.h" +#include "Display.h" +#include "Version.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6510 constructor: Initialize registers + */ + +MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color) + : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + dfff_byte = 0x55; + borrowed_cycles = 0; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6510::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; +} + + +/* + * Raise NMI asynchronously (Restore key) + */ + +void MOS6510::AsyncNMI(void) +{ + if (!nmi_state) + interrupt.intr[INT_NMI] = true; +} + + +/* + * Memory configuration has probably changed + */ + +void MOS6510::new_config(void) +{ + uint8 port = ~ram[0] | ram[1]; + + basic_in = (port & 3) == 3; + kernal_in = port & 2; + char_in = (port & 3) && !(port & 4); + io_in = (port & 3) && (port & 4); +} + + +/* + * Read a byte from I/O / ROM space + */ + +inline uint8 MOS6510::read_byte_io(uint16 adr) +{ + switch (adr >> 12) { + case 0xa: + case 0xb: + if (basic_in) + return basic_rom[adr & 0x1fff]; + else + return ram[adr]; + case 0xc: + return ram[adr]; + case 0xd: + if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + return TheVIC->ReadRegister(adr & 0x3f); + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + return TheSID->ReadRegister(adr & 0x1f); + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + return color_ram[adr & 0x03ff] | rand() & 0xf0; + case 0xc: // CIA 1 + return TheCIA1->ReadRegister(adr & 0x0f); + case 0xd: // CIA 2 + return TheCIA2->ReadRegister(adr & 0x0f); + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + return TheREU->ReadRegister(adr & 0x0f); + else if (adr < 0xdfa0) + return rand(); + else + return read_emulator_id(adr & 0x7f); + } + else if (char_in) + return char_rom[adr & 0x0fff]; + else + return ram[adr]; + case 0xe: + case 0xf: + if (kernal_in) + return kernal_rom[adr & 0x1fff]; + else + return ram[adr]; + default: // Can't happen + return 0; + } +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6510::read_byte(uint16 adr) +{ + if (adr < 0xa000) + return ram[adr]; + else + return read_byte_io(adr); +} + + +/* + * $dfa0-$dfff: Emulator identification + */ + +const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER"; + +uint8 MOS6510::read_emulator_id(uint16 adr) +{ + switch (adr) { + case 0x7c: // $dffc: revision + return FRODO_REVISION << 4; + case 0x7d: // $dffd: version + return FRODO_VERSION; + case 0x7e: // $dffe returns 'F' (Frodo ID) + return 'F'; + case 0x7f: // $dfff alternates between $55 and $aa + dfff_byte = ~dfff_byte; + return dfff_byte; + default: + return frodo_id[adr - 0x20]; + } +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +#if LITTLE_ENDIAN_UNALIGNED + +inline uint16 MOS6510::read_word(uint16 adr) +{ + switch (adr >> 12) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + return *(uint16*)&ram[adr]; + break; + case 0xa: + case 0xb: + if (basic_in) + return *(uint16*)&basic_rom[adr & 0x1fff]; + else + return *(uint16*)&ram[adr]; + case 0xc: + return *(uint16*)&ram[adr]; + case 0xd: + if (io_in) + return read_byte(adr) | (read_byte(adr+1) << 8); + else if (char_in) + return *(uint16*)&char_rom[adr & 0x0fff]; + else + return *(uint16*)&ram[adr]; + case 0xe: + case 0xf: + if (kernal_in) + return *(uint16*)&kernal_rom[adr & 0x1fff]; + else + return *(uint16*)&ram[adr]; + default: // Can't happen + return 0; + } +} + +#else + +inline uint16 MOS6510::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + +#endif + + +/* + * Write byte to I/O space + */ + +void MOS6510::write_byte_io(uint16 adr, uint8 byte) +{ + if (adr >= 0xe000) { + ram[adr] = byte; + if (adr == 0xff00) + TheREU->FF00Trigger(); + } else if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + TheVIC->WriteRegister(adr & 0x3f, byte); + return; + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + TheSID->WriteRegister(adr & 0x1f, byte); + return; + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + color_ram[adr & 0x03ff] = byte & 0x0f; + return; + case 0xc: // CIA 1 + TheCIA1->WriteRegister(adr & 0x0f, byte); + return; + case 0xd: // CIA 2 + TheCIA2->WriteRegister(adr & 0x0f, byte); + return; + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + TheREU->WriteRegister(adr & 0x0f, byte); + return; + } + else + ram[adr] = byte; +} + + +/* + * Write a byte to the CPU's address space + */ + +inline void MOS6510::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0xd000) { + ram[adr] = byte; + if (adr < 2) + new_config(); + } else + write_byte_io(adr, byte); +} + + +/* + * Read a byte from the zeropage + */ + +inline uint8 MOS6510::read_zp(uint16 adr) +{ + return ram[adr]; +} + + +/* + * Read a word (little-endian) from the zeropage + */ + +inline uint16 MOS6510::read_zp_word(uint16 adr) +{ +// !! zeropage word addressing wraps around !! +#if LITTLE_ENDIAN_UNALIGNED + return *(uint16 *)&ram[adr & 0xff]; +#else + return ram[adr & 0xff] | (ram[(adr+1) & 0xff] << 8); +#endif +} + + +/* + * Write a byte to the zeropage + */ + +inline void MOS6510::write_zp(uint16 adr, uint8 byte) +{ + ram[adr] = byte; + + // Check if memory configuration may have changed. + if (adr < 2) + new_config(); +} + + +/* + * Read byte from 6510 address space with special memory config (used by SAM) + */ + +uint8 MOS6510::ExtReadByte(uint16 adr) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Read byte + uint8 byte = read_byte(adr); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; + + return byte; +} + + +/* + * Write byte to 6510 address space with special memory config (used by SAM) + */ + +void MOS6510::ExtWriteByte(uint16 adr, uint8 byte) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Write byte + write_byte(adr, byte); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; +} + + +/* + * Read byte from 6510 address space with current memory config (used by REU) + */ + +uint8 MOS6510::REUReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6510 address space with current memory config (used by REU) + */ + +void MOS6510::REUWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Jump to address + */ + +#if PC_IS_POINTER +void MOS6510::jump(uint16 adr) +{ + if (adr < 0xa000) { + pc = ram + adr; + pc_base = ram; + } else + switch (adr >> 12) { + case 0xa: + case 0xb: + if (basic_in) { + pc = basic_rom + (adr & 0x1fff); + pc_base = basic_rom - 0xa000; + } else { + pc = ram + adr; + pc_base = ram; + } + break; + case 0xc: + pc = ram + adr; + pc_base = ram; + break; + case 0xd: + if (io_in) + illegal_jump(pc-pc_base, adr); + else if (char_in) { + pc = char_rom + (adr & 0x0fff); + pc_base = char_rom - 0xd000; + } else { + pc = ram + adr; + pc_base = ram; + } + break; + case 0xe: + case 0xf: + if (kernal_in) { + pc = kernal_rom + (adr & 0x1fff); + pc_base = kernal_rom - 0xe000; + } else { + pc = ram + adr; + pc_base = ram; + } + break; + } +} +#else +inline void MOS6510::jump(uint16 adr) +{ + pc = adr; +} +#endif + + +/* + * Adc instruction + */ + +void MOS6510::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +void MOS6510::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Get 6510 register state + */ + +void MOS6510::GetState(MOS6510State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + + s->ddr = ram[0]; + s->pr = ram[1] & 0x3f; + +#if PC_IS_POINTER + s->pc = pc - pc_base; +#else + s->pc = pc; +#endif + s->sp = sp | 0x0100; + + s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ]; + s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ]; + s->intr[INT_NMI] = interrupt.intr[INT_NMI]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->nmi_state = nmi_state; + s->dfff_byte = dfff_byte; + s->instruction_complete = true; +} + + +/* + * Restore 6510 state + */ + +void MOS6510::SetState(MOS6510State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + ram[0] = s->ddr; + ram[1] = s->pr; + new_config(); + + jump(s->pc); + sp = s->sp & 0xff; + + interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ]; + interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ]; + interrupt.intr[INT_NMI] = s->intr[INT_NMI]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + nmi_state = s->nmi_state; + dfff_byte = s->dfff_byte; +} + + +/* + * Reset CPU + */ + +void MOS6510::Reset(void) +{ + // Delete 'CBM80' if present + if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd + && ram[0x8007] == 0x38 && ram[0x8008] == 0x30) + ram[0x8004] = 0; + + // Initialize extra 6510 registers and memory configuration + ram[0] = ram[1] = 0; + new_config(); + + // Clear all interrupt lines + interrupt.intr_any = 0; + nmi_state = false; + + // Read reset vector + jump(read_word(0xfffc)); +} + + +/* + * Illegal opcode encountered + */ + +void MOS6510::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at); + ShowRequester(illop_msg, "Reset"); + the_c64->Reset(); + Reset(); +} + + +/* + * Jump to illegal address space (PC_IS_POINTER only) + */ + +void MOS6510::illegal_jump(uint16 at, uint16 to) +{ + char illop_msg[80]; + + sprintf(illop_msg, "Jump to I/O space at %04x to %04x.", at, to); + ShowRequester(illop_msg, "Reset"); + the_c64->Reset(); + Reset(); +} + + +/* + * Stack macros + */ + +// Pop a byte from the stack +#define pop_byte() ram[(++sp) | 0x0100] + +// Push a byte onto the stack +#define push_byte(byte) (ram[(sp--) & 0xff | 0x0100] = (byte)) + +// Pop processor flags from the stack +#define pop_flags() \ + n_flag = tmp = pop_byte(); \ + v_flag = tmp & 0x40; \ + d_flag = tmp & 0x08; \ + i_flag = tmp & 0x04; \ + z_flag = !(tmp & 0x02); \ + c_flag = tmp & 0x01; + +// Push processor flags onto the stack +#define push_flags(b_flag) \ + tmp = 0x20 | (n_flag & 0x80); \ + if (v_flag) tmp |= 0x40; \ + if (b_flag) tmp |= 0x10; \ + if (d_flag) tmp |= 0x08; \ + if (i_flag) tmp |= 0x04; \ + if (!z_flag) tmp |= 0x02; \ + if (c_flag) tmp |= 0x01; \ + push_byte(tmp); + + +/* + * Emulate cycles_left worth of 6510 instructions + * Returns number of cycles of last instruction + */ + +int MOS6510::EmulateLine(int cycles_left) +{ + uint8 tmp, tmp2; + uint16 adr; // Used by read_adr_abs()! + int last_cycles = 0; + + // Any pending interrupts? + if (interrupt.intr_any) { +handle_int: + if (interrupt.intr[INT_RESET]) + Reset(); + + else if (interrupt.intr[INT_NMI]) { + interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input +#if PC_IS_POINTER + push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base); +#else + push_byte(pc >> 8); push_byte(pc); +#endif + push_flags(false); + i_flag = true; + jump(read_word(0xfffa)); + last_cycles = 7; + + } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && !i_flag) { +#if PC_IS_POINTER + push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base); +#else + push_byte(pc >> 8); push_byte(pc); +#endif + push_flags(false); + i_flag = true; + jump(read_word(0xfffe)); + last_cycles = 7; + } + } + +#include "CPU_emulline.i" + + // Extension opcode + case 0xf2: +#if PC_IS_POINTER + if ((pc-pc_base) < 0xe000) { + illegal_op(0xf2, pc-pc_base-1); +#else + if (pc < 0xe000) { + illegal_op(0xf2, pc-1); +#endif + break; + } + switch (read_byte_imm()) { + case 0x00: + ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80); + c_flag = false; + jump(0xedac); + break; + case 0x01: + ram[0x90] |= TheIEC->OutATN(ram[0x95]); + c_flag = false; + jump(0xedac); + break; + case 0x02: + ram[0x90] |= TheIEC->OutSec(ram[0x95]); + c_flag = false; + jump(0xedac); + break; + case 0x03: + ram[0x90] |= TheIEC->In(&a); + set_nz(a); + c_flag = false; + jump(0xedac); + break; + case 0x04: + TheIEC->SetATN(); + jump(0xedfb); + break; + case 0x05: + TheIEC->RelATN(); + jump(0xedac); + break; + case 0x06: + TheIEC->Turnaround(); + jump(0xedac); + break; + case 0x07: + TheIEC->Release(); + jump(0xedac); + break; + default: +#if PC_IS_POINTER + illegal_op(0xf2, pc-pc_base-1); +#else + illegal_op(0xf2, pc-1); +#endif + break; + } + break; + } + } + return last_cycles; +} diff --git a/Src/CPUC64.h b/Src/CPUC64.h new file mode 100644 index 0000000..3e0641a --- /dev/null +++ b/Src/CPUC64.h @@ -0,0 +1,217 @@ +/* + * CPUC64.h - 6510 (C64) emulation (line based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _CPU_C64_H +#define _CPU_C64_H + +#include "C64.h" + + +// Set this to 1 if the 6502 PC should be represented by a real pointer +#ifndef FRODO_SC +#ifndef PC_IS_POINTER +#define PC_IS_POINTER 1 +#endif +#endif + +// Set this to 1 for more precise CPU cycle calculation +#ifndef PRECISE_CPU_CYCLES +#define PRECISE_CPU_CYCLES 0 +#endif + +// Set this to 1 for instruction-aligned CIA emulation +#ifndef PRECISE_CIA_CYCLES +#define PRECISE_CIA_CYCLES 0 +#endif + + +// Interrupt types +enum { + INT_VICIRQ, + INT_CIAIRQ, + INT_NMI + // INT_RESET (private) +}; + + +class MOS6569; +class MOS6581; +class MOS6526_1; +class MOS6526_2; +class REU; +class IEC; +struct MOS6510State; + + +// 6510 emulation (C64) +class MOS6510 { +public: + MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color); + +#ifdef FRODO_SC + void EmulateCycle(void); // Emulate one clock cycle +#else + int EmulateLine(int cycles_left); // Emulate until cycles_left underflows +#endif + void Reset(void); + void AsyncReset(void); // Reset the CPU asynchronously + void AsyncNMI(void); // Raise NMI asynchronously (NMI pulse) + void GetState(MOS6510State *s); + void SetState(MOS6510State *s); + uint8 ExtReadByte(uint16 adr); + void ExtWriteByte(uint16 adr, uint8 byte); + uint8 REUReadByte(uint16 adr); + void REUWriteByte(uint16 adr, uint8 byte); + + void TriggerVICIRQ(void); + void ClearVICIRQ(void); + void TriggerCIAIRQ(void); + void ClearCIAIRQ(void); + void TriggerNMI(void); + void ClearNMI(void); + + int ExtConfig; // Memory configuration for ExtRead/WriteByte (0..7) + + MOS6569 *TheVIC; // Pointer to VIC + MOS6581 *TheSID; // Pointer to SID + MOS6526_1 *TheCIA1; // Pointer to CIA 1 + MOS6526_2 *TheCIA2; // Pointer to CIA 2 + REU *TheREU; // Pointer to REU + IEC *TheIEC; // Pointer to drive array + +#ifdef FRODO_SC + bool BALow; // BA line for Frodo SC +#endif + +private: + uint8 read_byte(uint16 adr); + uint8 read_byte_io(uint16 adr); + uint16 read_word(uint16 adr); + void write_byte(uint16 adr, uint8 byte); + void write_byte_io(uint16 adr, uint8 byte); + + uint8 read_zp(uint16 adr); + uint16 read_zp_word(uint16 adr); + void write_zp(uint16 adr, uint8 byte); + + void new_config(void); + void jump(uint16 adr); + void illegal_op(uint8 op, uint16 at); + void illegal_jump(uint16 at, uint16 to); + + void do_adc(uint8 byte); + void do_sbc(uint8 byte); + + uint8 read_emulator_id(uint16 adr); + + C64 *the_c64; // Pointer to C64 object + + uint8 *ram; // Pointer to main RAM + uint8 *basic_rom, *kernal_rom, *char_rom, *color_ram; // Pointers to ROMs and color RAM + + union { // Pending interrupts + uint8 intr[4]; // Index: See definitions above + unsigned long intr_any; + } interrupt; + bool nmi_state; // State of NMI line + + uint8 n_flag, z_flag; + bool v_flag, d_flag, i_flag, c_flag; + uint8 a, x, y, sp; + +#if PC_IS_POINTER + uint8 *pc, *pc_base; +#else + uint16 pc; +#endif + +#ifdef FRODO_SC + uint32 first_irq_cycle, first_nmi_cycle; + + uint8 state, op; // Current state and opcode + uint16 ar, ar2; // Address registers + uint8 rdbuf; // Data buffer for RMW instructions + uint8 ddr, pr; // Processor port +#else + int borrowed_cycles; // Borrowed cycles from next line +#endif + + bool basic_in, kernal_in, char_in, io_in; + uint8 dfff_byte; +}; + +// 6510 state +struct MOS6510State { + uint8 a, x, y; + uint8 p; // Processor flags + uint8 ddr, pr; // Port + uint16 pc, sp; + uint8 intr[4]; // Interrupt state + bool nmi_state; + uint8 dfff_byte; + bool instruction_complete; +}; + + +// Interrupt functions +#ifdef FRODO_SC +inline void MOS6510::TriggerVICIRQ(void) +{ + if (!(interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ])) + first_irq_cycle = the_c64->CycleCounter; + interrupt.intr[INT_VICIRQ] = true; +} + +inline void MOS6510::TriggerCIAIRQ(void) +{ + if (!(interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ])) + first_irq_cycle = the_c64->CycleCounter; + interrupt.intr[INT_CIAIRQ] = true; +} + +inline void MOS6510::TriggerNMI(void) +{ + if (!nmi_state) { + nmi_state = true; + interrupt.intr[INT_NMI] = true; + first_nmi_cycle = the_c64->CycleCounter; + } +} +#else +inline void MOS6510::TriggerVICIRQ(void) +{ + interrupt.intr[INT_VICIRQ] = true; +} + +inline void MOS6510::TriggerCIAIRQ(void) +{ + interrupt.intr[INT_CIAIRQ] = true; +} + +inline void MOS6510::TriggerNMI(void) +{ + if (!nmi_state) { + nmi_state = true; + interrupt.intr[INT_NMI] = true; + } +} +#endif +inline void MOS6510::ClearVICIRQ(void) +{ + interrupt.intr[INT_VICIRQ] = false; +} + +inline void MOS6510::ClearCIAIRQ(void) +{ + interrupt.intr[INT_CIAIRQ] = false; +} + +inline void MOS6510::ClearNMI(void) +{ + nmi_state = false; +} + +#endif diff --git a/Src/CPUC64_PC.cpp b/Src/CPUC64_PC.cpp new file mode 100644 index 0000000..a479513 --- /dev/null +++ b/Src/CPUC64_PC.cpp @@ -0,0 +1,12 @@ +/* + * C64_PC.cpp - Put the pieces together (Frodo PC) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +// Same as CPUC64.cpp (mainly to keep the BeIDE happy) +#ifdef __riscos__ +#include "CPUC64.cc" +#else +#include "CPUC64.cpp" +#endif diff --git a/Src/CPUC64_SC.cpp b/Src/CPUC64_SC.cpp new file mode 100644 index 0000000..485d395 --- /dev/null +++ b/Src/CPUC64_SC.cpp @@ -0,0 +1,653 @@ +/* + * CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * Opcode execution: + * - All opcodes are resolved into single clock cycles. There is one + * switch case for each cycle. + * - The "state" variable specifies the routine to be executed in the + * next cycle. Its upper 8 bits contain the current opcode, its lower + * 8 bits contain the cycle number (0..7) within the opcode. + * - Opcodes are fetched in cycle 0 (state = 0) + * - The states 0x0010..0x0027 are used for interrupts + * - There is exactly one memory access in each clock cycle + * + * Memory configurations: + * + * $01 $a000-$bfff $d000-$dfff $e000-$ffff + * ----------------------------------------------- + * 0 RAM RAM RAM + * 1 RAM Char ROM RAM + * 2 RAM Char ROM Kernal ROM + * 3 Basic ROM Char ROM Kernal ROM + * 4 RAM RAM RAM + * 5 RAM I/O RAM + * 6 RAM I/O Kernal ROM + * 7 Basic ROM I/O Kernal ROM + * + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. + * - If a write occurs to addresses 0 or 1, new_config is + * called to check whether the memory configuration has + * changed + * - The possible interrupt sources are: + * INT_VICIRQ: I flag is checked, jump to ($fffe) + * INT_CIAIRQ: I flag is checked, jump to ($fffe) + * INT_NMI: Jump to ($fffa) + * INT_RESET: Jump to ($fffc) + * - The z_flag variable has the inverse meaning of the + * 6510 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6510 is + * used to implement emulator-specific functions, mainly + * those for the IEC routines + * + * Incompatibilities: + * ------------------ + * + * - If BA is low and AEC is high, read accesses should occur + */ + +#include "sysdeps.h" + +#include "CPUC64.h" +#include "CPU_common.h" +#include "C64.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" +#include "REU.h" +#include "IEC.h" +#include "Display.h" +#include "Version.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6510 constructor: Initialize registers + */ + +MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color) + : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + dfff_byte = 0x55; + BALow = false; + first_irq_cycle = first_nmi_cycle = 0; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6510::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; +} + + +/* + * Raise NMI asynchronously (Restore key) + */ + +void MOS6510::AsyncNMI(void) +{ + if (!nmi_state) + interrupt.intr[INT_NMI] = true; +} + + +/* + * Get 6510 register state + */ + +void MOS6510::GetState(MOS6510State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + + s->ddr = ddr; + s->pr = pr; + + s->pc = pc; + s->sp = sp | 0x0100; + + s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ]; + s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ]; + s->intr[INT_NMI] = interrupt.intr[INT_NMI]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->nmi_state = nmi_state; + s->dfff_byte = dfff_byte; + s->instruction_complete = (state == 0); +} + + +/* + * Restore 6510 state + */ + +void MOS6510::SetState(MOS6510State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + ddr = s->ddr; + pr = s->pr; + new_config(); + + pc = s->pc; + sp = s->sp & 0xff; + + interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ]; + interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ]; + interrupt.intr[INT_NMI] = s->intr[INT_NMI]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + nmi_state = s->nmi_state; + dfff_byte = s->dfff_byte; + if (s->instruction_complete) + state = 0; +} + + +/* + * Memory configuration has probably changed + */ + +void MOS6510::new_config(void) +{ + uint8 port = ~ddr | pr; + + basic_in = (port & 3) == 3; + kernal_in = port & 2; + char_in = (port & 3) && !(port & 4); + io_in = (port & 3) && (port & 4); +} + + +/* + * Read a byte from I/O / ROM space + */ + +inline uint8 MOS6510::read_byte_io(uint16 adr) +{ + switch (adr >> 12) { + case 0xa: + case 0xb: + if (basic_in) + return basic_rom[adr & 0x1fff]; + else + return ram[adr]; + case 0xc: + return ram[adr]; + case 0xd: + if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + return TheVIC->ReadRegister(adr & 0x3f); + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + return TheSID->ReadRegister(adr & 0x1f); + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + return color_ram[adr & 0x03ff] & 0x0f | TheVIC->LastVICByte & 0xf0; + case 0xc: // CIA 1 + return TheCIA1->ReadRegister(adr & 0x0f); + case 0xd: // CIA 2 + return TheCIA2->ReadRegister(adr & 0x0f); + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + return TheREU->ReadRegister(adr & 0x0f); + else if (adr < 0xdfa0) + return TheVIC->LastVICByte; + else + return read_emulator_id(adr & 0x7f); + } + else if (char_in) + return char_rom[adr & 0x0fff]; + else + return ram[adr]; + case 0xe: + case 0xf: + if (kernal_in) + return kernal_rom[adr & 0x1fff]; + else + return ram[adr]; + default: // Can't happen + return 0; + } +} + + +/* + * Read a byte from the CPU's address space + */ + +#ifdef __i386 +inline +#endif +uint8 MOS6510::read_byte(uint16 adr) +{ + if (adr < 0xa000) { + if (adr >= 2) + return ram[adr]; + else if (adr == 0) + return ddr; + else + return (ddr & pr) | (~ddr & 0x17); + } else + return read_byte_io(adr); +} + + +/* + * $dfa0-$dfff: Emulator identification + */ + +const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER"; + +uint8 MOS6510::read_emulator_id(uint16 adr) +{ + switch (adr) { + case 0x7c: // $dffc: revision + return FRODO_REVISION << 4; + case 0x7d: // $dffd: version + return FRODO_VERSION; + case 0x7e: // $dffe returns 'F' (Frodo ID) + return 'F'; + case 0x7f: // $dfff alternates between $55 and $aa + dfff_byte = ~dfff_byte; + return dfff_byte; + default: + return frodo_id[adr - 0x20]; + } +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +inline uint16 MOS6510::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + + +/* + * Write a byte to I/O space + */ + +inline void MOS6510::write_byte_io(uint16 adr, uint8 byte) +{ + if (adr >= 0xe000) { + ram[adr] = byte; + if (adr == 0xff00) + TheREU->FF00Trigger(); + } else if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + TheVIC->WriteRegister(adr & 0x3f, byte); + return; + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + TheSID->WriteRegister(adr & 0x1f, byte); + return; + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + color_ram[adr & 0x03ff] = byte & 0x0f; + return; + case 0xc: // CIA 1 + TheCIA1->WriteRegister(adr & 0x0f, byte); + return; + case 0xd: // CIA 2 + TheCIA2->WriteRegister(adr & 0x0f, byte); + return; + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + TheREU->WriteRegister(adr & 0x0f, byte); + return; + } + else + ram[adr] = byte; +} + + +/* + * Write a byte to the CPU's address space + */ + +void MOS6510::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0xd000) { + if (adr >= 2) + ram[adr] = byte; + else if (adr == 0) { + ddr = byte; + ram[0] = TheVIC->LastVICByte; + new_config(); + } else { + pr = byte; + ram[1] = TheVIC->LastVICByte; + new_config(); + } + } else + write_byte_io(adr, byte); +} + + +/* + * Read byte from 6510 address space with special memory config (used by SAM) + */ + +uint8 MOS6510::ExtReadByte(uint16 adr) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Read byte + uint8 byte = read_byte(adr); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; + + return byte; +} + + +/* + * Write byte to 6510 address space with special memory config (used by SAM) + */ + +void MOS6510::ExtWriteByte(uint16 adr, uint8 byte) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Write byte + write_byte(adr, byte); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; +} + + +/* + * Read byte from 6510 address space with current memory config (used by REU) + */ + +uint8 MOS6510::REUReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6510 address space with current memory config (used by REU) + */ + +void MOS6510::REUWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Adc instruction + */ + +inline void MOS6510::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +inline void MOS6510::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Reset CPU + */ + +void MOS6510::Reset(void) +{ + // Delete 'CBM80' if present + if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd + && ram[0x8007] == 0x38 && ram[0x8008] == 0x30) + ram[0x8004] = 0; + + // Initialize extra 6510 registers and memory configuration + ddr = pr = 0; + new_config(); + + // Clear all interrupt lines + interrupt.intr_any = 0; + nmi_state = false; + + // Read reset vector + pc = read_word(0xfffc); + state = 0; +} + + +/* + * Illegal opcode encountered + */ + +void MOS6510::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at); + ShowRequester(illop_msg, "Reset"); + the_c64->Reset(); + Reset(); +} + + +/* + * Emulate one 6510 clock cycle + */ + +// Read byte from memory +#define read_to(adr, to) \ + if (BALow) \ + return; \ + to = read_byte(adr); + +// Read byte from memory, throw away result +#define read_idle(adr) \ + if (BALow) \ + return; \ + read_byte(adr); + +void MOS6510::EmulateCycle(void) +{ + uint8 data, tmp; + + // Any pending interrupts in state 0 (opcode fetch)? + if (!state && interrupt.intr_any) { + if (interrupt.intr[INT_RESET]) + Reset(); + else if (interrupt.intr[INT_NMI] && (the_c64->CycleCounter-first_nmi_cycle >= 2)) { + interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input + state = 0x0010; + } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag) + state = 0x0008; + } + +#include "CPU_emulcycle.i" + + // Extension opcode + case O_EXT: + if (pc < 0xe000) { + illegal_op(0xf2, pc-1); + break; + } + switch (read_byte(pc++)) { + case 0x00: + ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80); + c_flag = false; + pc = 0xedac; + Last; + case 0x01: + ram[0x90] |= TheIEC->OutATN(ram[0x95]); + c_flag = false; + pc = 0xedac; + Last; + case 0x02: + ram[0x90] |= TheIEC->OutSec(ram[0x95]); + c_flag = false; + pc = 0xedac; + Last; + case 0x03: + ram[0x90] |= TheIEC->In(&a); + set_nz(a); + c_flag = false; + pc = 0xedac; + Last; + case 0x04: + TheIEC->SetATN(); + pc = 0xedfb; + Last; + case 0x05: + TheIEC->RelATN(); + pc = 0xedac; + Last; + case 0x06: + TheIEC->Turnaround(); + pc = 0xedac; + Last; + case 0x07: + TheIEC->Release(); + pc = 0xedac; + Last; + default: + illegal_op(0xf2, pc-1); + break; + } + break; + + default: + illegal_op(op, pc-1); + break; + } +} diff --git a/Src/CPU_common.cpp b/Src/CPU_common.cpp new file mode 100644 index 0000000..0f0ae2e --- /dev/null +++ b/Src/CPU_common.cpp @@ -0,0 +1,83 @@ +/* + * CPU_common.cpp - Definitions common to 6502/6510 SC emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysdeps.h" + +#include "CPU_common.h" + + +// Addressing mode for each opcode (first part of execution) (Frodo SC) +const uint8 ModeTab[256] = { + O_BRK, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 00 + O_PHP, O_ORA_I,O_ASL_A,O_ANC_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BPL, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 10 + O_CLC, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_JSR, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 20 + O_PLP, O_AND_I,O_ROL_A,O_ANC_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BMI, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 30 + O_SEC, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_RTI, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 40 + O_PHA, O_EOR_I,O_LSR_A,O_ASR_I,O_JMP, A_ABS, M_ABS, M_ABS, + O_BVC, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 50 + O_CLI, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_RTS, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 60 + O_PLA, O_ADC_I,O_ROR_A,O_ARR_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BVS, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 70 + O_SEI, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_NOP_I,A_INDX, O_NOP_I,A_INDX, A_ZERO, A_ZERO, A_ZERO, A_ZERO, // 80 + O_DEY, O_NOP_I,O_TXA, O_ANE_I,A_ABS, A_ABS, A_ABS, A_ABS, + O_BCC, A_INDY, 1, A_INDY, A_ZEROX,A_ZEROX,A_ZEROY,A_ZEROY,// 90 + O_TYA, A_ABSY, O_TXS, A_ABSY, A_ABSX, A_ABSX, A_ABSY, A_ABSY, + O_LDY_I,A_INDX, O_LDX_I,A_INDX, A_ZERO, A_ZERO, A_ZERO, A_ZERO, // a0 + O_TAY, O_LDA_I,O_TAX, O_LXA_I,A_ABS, A_ABS, A_ABS, A_ABS, + O_BCS, AE_INDY,1, AE_INDY,A_ZEROX,A_ZEROX,A_ZEROY,A_ZEROY,// b0 + O_CLV, AE_ABSY,O_TSX, AE_ABSY,AE_ABSX,AE_ABSX,AE_ABSY,AE_ABSY, + O_CPY_I,A_INDX, O_NOP_I,M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // c0 + O_INY, O_CMP_I,O_DEX, O_SBX_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BNE, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// d0 + O_CLD, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_CPX_I,A_INDX, O_NOP_I,M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // e0 + O_INX, O_SBC_I,O_NOP, O_SBC_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BEQ, AE_INDY,O_EXT, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// f0 + O_SED, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX +}; + + +// Operation for each opcode (second part of execution) (Frodo SC) +const uint8 OpTab[256] = { + 1, O_ORA, 1, O_SLO, O_NOP_A,O_ORA, O_ASL, O_SLO, // 00 + 1, 1, 1, 1, O_NOP_A,O_ORA, O_ASL, O_SLO, + 1, O_ORA, 1, O_SLO, O_NOP_A,O_ORA, O_ASL, O_SLO, // 10 + 1, O_ORA, 1, O_SLO, O_NOP_A,O_ORA, O_ASL, O_SLO, + 1, O_AND, 1, O_RLA, O_BIT, O_AND, O_ROL, O_RLA, // 20 + 1, 1, 1, 1, O_BIT, O_AND, O_ROL, O_RLA, + 1, O_AND, 1, O_RLA, O_NOP_A,O_AND, O_ROL, O_RLA, // 30 + 1, O_AND, 1, O_RLA, O_NOP_A,O_AND, O_ROL, O_RLA, + 1, O_EOR, 1, O_SRE, O_NOP_A,O_EOR, O_LSR, O_SRE, // 40 + 1, 1, 1, 1, 1, O_EOR, O_LSR, O_SRE, + 1, O_EOR, 1, O_SRE, O_NOP_A,O_EOR, O_LSR, O_SRE, // 50 + 1, O_EOR, 1, O_SRE, O_NOP_A,O_EOR, O_LSR, O_SRE, + 1, O_ADC, 1, O_RRA, O_NOP_A,O_ADC, O_ROR, O_RRA, // 60 + 1, 1, 1, 1, O_JMP_I,O_ADC, O_ROR, O_RRA, + 1, O_ADC, 1, O_RRA, O_NOP_A,O_ADC, O_ROR, O_RRA, // 70 + 1, O_ADC, 1, O_RRA, O_NOP_A,O_ADC, O_ROR, O_RRA, + 1, O_STA, 1, O_SAX, O_STY, O_STA, O_STX, O_SAX, // 80 + 1, 1, 1, 1, O_STY, O_STA, O_STX, O_SAX, + 1, O_STA, 1, O_SHA, O_STY, O_STA, O_STX, O_SAX, // 90 + 1, O_STA, 1, O_SHS, O_SHY, O_STA, O_SHX, O_SHA, + 1, O_LDA, 1, O_LAX, O_LDY, O_LDA, O_LDX, O_LAX, // a0 + 1, 1, 1, 1, O_LDY, O_LDA, O_LDX, O_LAX, + 1, O_LDA, 1, O_LAX, O_LDY, O_LDA, O_LDX, O_LAX, // b0 + 1, O_LDA, 1, O_LAS, O_LDY, O_LDA, O_LDX, O_LAX, + 1, O_CMP, 1, O_DCP, O_CPY, O_CMP, O_DEC, O_DCP, // c0 + 1, 1, 1, 1, O_CPY, O_CMP, O_DEC, O_DCP, + 1, O_CMP, 1, O_DCP, O_NOP_A,O_CMP, O_DEC, O_DCP, // d0 + 1, O_CMP, 1, O_DCP, O_NOP_A,O_CMP, O_DEC, O_DCP, + 1, O_SBC, 1, O_ISB, O_CPX, O_SBC, O_INC, O_ISB, // e0 + 1, 1, 1, 1, O_CPX, O_SBC, O_INC, O_ISB, + 1, O_SBC, 1, O_ISB, O_NOP_A,O_SBC, O_INC, O_ISB, // f0 + 1, O_SBC, 1, O_ISB, O_NOP_A,O_SBC, O_INC, O_ISB +}; diff --git a/Src/CPU_common.h b/Src/CPU_common.h new file mode 100644 index 0000000..7fb7c93 --- /dev/null +++ b/Src/CPU_common.h @@ -0,0 +1,76 @@ +/* + * CPU_common.h - Definitions common to 6502/6510 SC emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _CPU_COMMON_H_ +#define _CPU_COMMON_H_ + + +// States for addressing modes/operations (Frodo SC) +enum { + // Read effective address, no extra cycles + A_ZERO=0x18, + A_ZEROX, A_ZEROX1, + A_ZEROY, A_ZEROY1, + A_ABS, A_ABS1, + A_ABSX, A_ABSX1, A_ABSX2, A_ABSX3, + A_ABSY, A_ABSY1, A_ABSY2, A_ABSY3, + A_INDX, A_INDX1, A_INDX2, A_INDX3, + A_INDY, A_INDY1, A_INDY2, A_INDY3, A_INDY4, + + // Read effective address, extra cycle on page crossing + AE_ABSX, AE_ABSX1, AE_ABSX2, + AE_ABSY, AE_ABSY1, AE_ABSY2, + AE_INDY, AE_INDY1, AE_INDY2, AE_INDY3, + + // Read operand and write it back (for RMW instructions), no extra cycles + M_ZERO, + M_ZEROX, M_ZEROX1, + M_ZEROY, M_ZEROY1, + M_ABS, M_ABS1, + M_ABSX, M_ABSX1, M_ABSX2, M_ABSX3, + M_ABSY, M_ABSY1, M_ABSY2, M_ABSY3, + M_INDX, M_INDX1, M_INDX2, M_INDX3, + M_INDY, M_INDY1, M_INDY2, M_INDY3, M_INDY4, + RMW_DO_IT, RMW_DO_IT1, + + // Operations (_I = Immediate/Indirect, _A = Accumulator) + O_LDA, O_LDA_I, O_LDX, O_LDX_I, O_LDY, O_LDY_I, + O_STA, O_STX, O_STY, + O_TAX, O_TXA, O_TAY, O_TYA, O_TSX, O_TXS, + O_ADC, O_ADC_I, O_SBC, O_SBC_I, + O_INX, O_DEX, O_INY, O_DEY, O_INC, O_DEC, + O_AND, O_AND_I, O_ORA, O_ORA_I, O_EOR, O_EOR_I, + O_CMP, O_CMP_I, O_CPX, O_CPX_I, O_CPY, O_CPY_I, + O_BIT, + O_ASL, O_ASL_A, O_LSR, O_LSR_A, O_ROL, O_ROL_A, O_ROR, O_ROR_A, + O_PHA, O_PHA1, O_PLA, O_PLA1, O_PLA2, + O_PHP, O_PHP1, O_PLP, O_PLP1, O_PLP2, + O_JMP, O_JMP1, O_JMP_I, O_JMP_I1, + O_JSR, O_JSR1, O_JSR2, O_JSR3, O_JSR4, + O_RTS, O_RTS1, O_RTS2, O_RTS3, O_RTS4, + O_RTI, O_RTI1, O_RTI2, O_RTI3, O_RTI4, + O_BRK, O_BRK1, O_BRK2, O_BRK3, O_BRK4, O_BRK5, O_BRK5NMI, + O_BCS, O_BCC, O_BEQ, O_BNE, O_BVS, O_BVC, O_BMI, O_BPL, + O_BRANCH_NP, O_BRANCH_BP, O_BRANCH_BP1, O_BRANCH_FP, O_BRANCH_FP1, + O_SEC, O_CLC, O_SED, O_CLD, O_SEI, O_CLI, O_CLV, + O_NOP, + + O_NOP_I, O_NOP_A, + O_LAX, O_SAX, + O_SLO, O_RLA, O_SRE, O_RRA, O_DCP, O_ISB, + O_ANC_I, O_ASR_I, O_ARR_I, O_ANE_I, O_LXA_I, O_SBX_I, + O_LAS, O_SHS, O_SHY, O_SHX, O_SHA, + O_EXT +}; + + +// Addressing mode for each opcode (first part of execution) (Frodo SC) +extern const uint8 ModeTab[256]; + +// Operation for each opcode (second part of execution) (Frodo SC) +extern const uint8 OpTab[256]; + +#endif diff --git a/Src/CPU_emulcycle.i b/Src/CPU_emulcycle.i new file mode 100644 index 0000000..c757af4 --- /dev/null +++ b/Src/CPU_emulcycle.i @@ -0,0 +1,1100 @@ +/* + * CPU_emulcycle.i - SC 6510/6502 emulation core (body of + * EmulateCycle() function, the same for + * both 6510 and 6502) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + + +/* + * Stack macros + */ + +// Pop processor flags from the stack +#define pop_flags() \ + read_to(sp | 0x100, data); \ + n_flag = data; \ + v_flag = data & 0x40; \ + d_flag = data & 0x08; \ + i_flag = data & 0x04; \ + z_flag = !(data & 0x02); \ + c_flag = data & 0x01; + +// Push processor flags onto the stack +#define push_flags(b_flag) \ + data = 0x20 | (n_flag & 0x80); \ + if (v_flag) data |= 0x40; \ + if (b_flag) data |= 0x10; \ + if (d_flag) data |= 0x08; \ + if (i_flag) data |= 0x04; \ + if (!z_flag) data |= 0x02; \ + if (c_flag) data |= 0x01; \ + write_byte(sp-- | 0x100, data); + + +/* + * Other macros + */ + +// Branch (cycle 1) +#define Branch(flag) \ + read_to(pc++, data); \ + if (flag) { \ + ar = pc + (int8)data; \ + if ((ar >> 8) != (pc >> 8)) { \ + if (data & 0x80) \ + state = O_BRANCH_BP; \ + else \ + state = O_BRANCH_FP; \ + } else \ + state = O_BRANCH_NP; \ + } else \ + state = 0; \ + break; + +// Set N and Z flags according to byte +#define set_nz(x) (z_flag = n_flag = (x)) + +// Address fetch of RMW instruction done, now read and write operand +#define DoRMW state = RMW_DO_IT; break; + +// Operand fetch done, now execute opcode +#define Execute state = OpTab[op]; break; + +// Last cycle of opcode +#define Last state = 0; break; + + +/* + * EmulCycle() function + */ + + switch (state) { + + + // Opcode fetch (cycle 0) + case 0: + read_to(pc++, op); + state = ModeTab[op]; + break; + + + // IRQ + case 0x0008: + read_idle(pc); + state = 0x0009; + break; + case 0x0009: + read_idle(pc); + state = 0x000a; + break; + case 0x000a: + write_byte(sp-- | 0x100, pc >> 8); + state = 0x000b; + break; + case 0x000b: + write_byte(sp-- | 0x100, pc); + state = 0x000c; + break; + case 0x000c: + push_flags(false); + i_flag = true; + state = 0x000d; + break; + case 0x000d: + read_to(0xfffe, pc); + state = 0x000e; + break; + case 0x000e: + read_to(0xffff, data); + pc |= data << 8; + Last; + + + // NMI + case 0x0010: + read_idle(pc); + state = 0x0011; + break; + case 0x0011: + read_idle(pc); + state = 0x0012; + break; + case 0x0012: + write_byte(sp-- | 0x100, pc >> 8); + state = 0x0013; + break; + case 0x0013: + write_byte(sp-- | 0x100, pc); + state = 0x0014; + break; + case 0x0014: + push_flags(false); + i_flag = true; + state = 0x0015; + break; + case 0x0015: + read_to(0xfffa, pc); + state = 0x0016; + break; + case 0x0016: + read_to(0xfffb, data); + pc |= data << 8; + Last; + + + // Addressing modes: Fetch effective address, no extra cycles (-> ar) + case A_ZERO: + read_to(pc++, ar); + Execute; + + case A_ZEROX: + read_to(pc++, ar); + state = A_ZEROX1; + break; + case A_ZEROX1: + read_idle(ar); + ar = (ar + x) & 0xff; + Execute; + + case A_ZEROY: + read_to(pc++, ar); + state = A_ZEROY1; + break; + case A_ZEROY1: + read_idle(ar); + ar = (ar + y) & 0xff; + Execute; + + case A_ABS: + read_to(pc++, ar); + state = A_ABS1; + break; + case A_ABS1: + read_to(pc++, data); + ar = ar | (data << 8); + Execute; + + case A_ABSX: + read_to(pc++, ar); + state = A_ABSX1; + break; + case A_ABSX1: + read_to(pc++, ar2); // Note: Some undocumented opcodes rely on the value of ar2 + if (ar+x < 0x100) + state = A_ABSX2; + else + state = A_ABSX3; + ar = (ar + x) & 0xff | (ar2 << 8); + break; + case A_ABSX2: // No page crossed + read_idle(ar); + Execute; + case A_ABSX3: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case A_ABSY: + read_to(pc++, ar); + state = A_ABSY1; + break; + case A_ABSY1: + read_to(pc++, ar2); // Note: Some undocumented opcodes rely on the value of ar2 + if (ar+y < 0x100) + state = A_ABSY2; + else + state = A_ABSY3; + ar = (ar + y) & 0xff | (ar2 << 8); + break; + case A_ABSY2: // No page crossed + read_idle(ar); + Execute; + case A_ABSY3: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case A_INDX: + read_to(pc++, ar2); + state = A_INDX1; + break; + case A_INDX1: + read_idle(ar2); + ar2 = (ar2 + x) & 0xff; + state = A_INDX2; + break; + case A_INDX2: + read_to(ar2, ar); + state = A_INDX3; + break; + case A_INDX3: + read_to((ar2 + 1) & 0xff, data); + ar = ar | (data << 8); + Execute; + + case A_INDY: + read_to(pc++, ar2); + state = A_INDY1; + break; + case A_INDY1: + read_to(ar2, ar); + state = A_INDY2; + break; + case A_INDY2: + read_to((ar2 + 1) & 0xff, ar2); // Note: Some undocumented opcodes rely on the value of ar2 + if (ar+y < 0x100) + state = A_INDY3; + else + state = A_INDY4; + ar = (ar + y) & 0xff | (ar2 << 8); + break; + case A_INDY3: // No page crossed + read_idle(ar); + Execute; + case A_INDY4: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + + // Addressing modes: Fetch effective address, extra cycle on page crossing (-> ar) + case AE_ABSX: + read_to(pc++, ar); + state = AE_ABSX1; + break; + case AE_ABSX1: + read_to(pc++, data); + if (ar+x < 0x100) { + ar = (ar + x) & 0xff | (data << 8); + Execute; + } else { + ar = (ar + x) & 0xff | (data << 8); + state = AE_ABSX2; + } + break; + case AE_ABSX2: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case AE_ABSY: + read_to(pc++, ar); + state = AE_ABSY1; + break; + case AE_ABSY1: + read_to(pc++, data); + if (ar+y < 0x100) { + ar = (ar + y) & 0xff | (data << 8); + Execute; + } else { + ar = (ar + y) & 0xff | (data << 8); + state = AE_ABSY2; + } + break; + case AE_ABSY2: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case AE_INDY: + read_to(pc++, ar2); + state = AE_INDY1; + break; + case AE_INDY1: + read_to(ar2, ar); + state = AE_INDY2; + break; + case AE_INDY2: + read_to((ar2 + 1) & 0xff, data); + if (ar+y < 0x100) { + ar = (ar + y) & 0xff | (data << 8); + Execute; + } else { + ar = (ar + y) & 0xff | (data << 8); + state = AE_INDY3; + } + break; + case AE_INDY3: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + + // Addressing modes: Read operand, write it back, no extra cycles (-> ar, rdbuf) + case M_ZERO: + read_to(pc++, ar); + DoRMW; + + case M_ZEROX: + read_to(pc++, ar); + state = M_ZEROX1; + break; + case M_ZEROX1: + read_idle(ar); + ar = (ar + x) & 0xff; + DoRMW; + + case M_ZEROY: + read_to(pc++, ar); + state = M_ZEROY1; + break; + case M_ZEROY1: + read_idle(ar); + ar = (ar + y) & 0xff; + DoRMW; + + case M_ABS: + read_to(pc++, ar); + state = M_ABS1; + break; + case M_ABS1: + read_to(pc++, data); + ar = ar | (data << 8); + DoRMW; + + case M_ABSX: + read_to(pc++, ar); + state = M_ABSX1; + break; + case M_ABSX1: + read_to(pc++, data); + if (ar+x < 0x100) + state = M_ABSX2; + else + state = M_ABSX3; + ar = (ar + x) & 0xff | (data << 8); + break; + case M_ABSX2: // No page crossed + read_idle(ar); + DoRMW; + case M_ABSX3: // Page crossed + read_idle(ar); + ar += 0x100; + DoRMW; + + case M_ABSY: + read_to(pc++, ar); + state = M_ABSY1; + break; + case M_ABSY1: + read_to(pc++, data); + if (ar+y < 0x100) + state = M_ABSY2; + else + state = M_ABSY3; + ar = (ar + y) & 0xff | (data << 8); + break; + case M_ABSY2: // No page crossed + read_idle(ar); + DoRMW; + case M_ABSY3: // Page crossed + read_idle(ar); + ar += 0x100; + DoRMW; + + case M_INDX: + read_to(pc++, ar2); + state = M_INDX1; + break; + case M_INDX1: + read_idle(ar2); + ar2 = (ar2 + x) & 0xff; + state = M_INDX2; + break; + case M_INDX2: + read_to(ar2, ar); + state = M_INDX3; + break; + case M_INDX3: + read_to((ar2 + 1) & 0xff, data); + ar = ar | (data << 8); + DoRMW; + + case M_INDY: + read_to(pc++, ar2); + state = M_INDY1; + break; + case M_INDY1: + read_to(ar2, ar); + state = M_INDY2; + break; + case M_INDY2: + read_to((ar2 + 1) & 0xff, data); + if (ar+y < 0x100) + state = M_INDY3; + else + state = M_INDY4; + ar = (ar + y) & 0xff | (data << 8); + break; + case M_INDY3: // No page crossed + read_idle(ar); + DoRMW; + case M_INDY4: // Page crossed + read_idle(ar); + ar += 0x100; + DoRMW; + + case RMW_DO_IT: + read_to(ar, rdbuf); + state = RMW_DO_IT1; + break; + case RMW_DO_IT1: + write_byte(ar, rdbuf); + Execute; + + + // Load group + case O_LDA: + read_to(ar, data); + set_nz(a = data); + Last; + case O_LDA_I: + read_to(pc++, data); + set_nz(a = data); + Last; + + case O_LDX: + read_to(ar, data); + set_nz(x = data); + Last; + case O_LDX_I: + read_to(pc++, data); + set_nz(x = data); + Last; + + case O_LDY: + read_to(ar, data); + set_nz(y = data); + Last; + case O_LDY_I: + read_to(pc++, data); + set_nz(y = data); + Last; + + + // Store group + case O_STA: + write_byte(ar, a); + Last; + + case O_STX: + write_byte(ar, x); + Last; + + case O_STY: + write_byte(ar, y); + Last; + + + // Transfer group + case O_TAX: + read_idle(pc); + set_nz(x = a); + Last; + + case O_TXA: + read_idle(pc); + set_nz(a = x); + Last; + + case O_TAY: + read_idle(pc); + set_nz(y = a); + Last; + + case O_TYA: + read_idle(pc); + set_nz(a = y); + Last; + + case O_TSX: + read_idle(pc); + set_nz(x = sp); + Last; + + case O_TXS: + read_idle(pc); + sp = x; + Last; + + + // Arithmetic group + case O_ADC: + read_to(ar, data); + do_adc(data); + Last; + case O_ADC_I: + read_to(pc++, data); + do_adc(data); + Last; + + case O_SBC: + read_to(ar, data); + do_sbc(data); + Last; + case O_SBC_I: + read_to(pc++, data); + do_sbc(data); + Last; + + + // Increment/decrement group + case O_INX: + read_idle(pc); + set_nz(++x); + Last; + + case O_DEX: + read_idle(pc); + set_nz(--x); + Last; + + case O_INY: + read_idle(pc); + set_nz(++y); + Last; + + case O_DEY: + read_idle(pc); + set_nz(--y); + Last; + + case O_INC: + write_byte(ar, set_nz(rdbuf + 1)); + Last; + + case O_DEC: + write_byte(ar, set_nz(rdbuf - 1)); + Last; + + + // Logic group + case O_AND: + read_to(ar, data); + set_nz(a &= data); + Last; + case O_AND_I: + read_to(pc++, data); + set_nz(a &= data); + Last; + + case O_ORA: + read_to(ar, data); + set_nz(a |= data); + Last; + case O_ORA_I: + read_to(pc++, data); + set_nz(a |= data); + Last; + + case O_EOR: + read_to(ar, data); + set_nz(a ^= data); + Last; + case O_EOR_I: + read_to(pc++, data); + set_nz(a ^= data); + Last; + + // Compare group + case O_CMP: + read_to(ar, data); + set_nz(ar = a - data); + c_flag = ar < 0x100; + Last; + case O_CMP_I: + read_to(pc++, data); + set_nz(ar = a - data); + c_flag = ar < 0x100; + Last; + + case O_CPX: + read_to(ar, data); + set_nz(ar = x - data); + c_flag = ar < 0x100; + Last; + case O_CPX_I: + read_to(pc++, data); + set_nz(ar = x - data); + c_flag = ar < 0x100; + Last; + + case O_CPY: + read_to(ar, data); + set_nz(ar = y - data); + c_flag = ar < 0x100; + Last; + case O_CPY_I: + read_to(pc++, data); + set_nz(ar = y - data); + c_flag = ar < 0x100; + Last; + + + // Bit-test group + case O_BIT: + read_to(ar, data); + z_flag = a & data; + n_flag = data; + v_flag = data & 0x40; + Last; + + + // Shift/rotate group + case O_ASL: + c_flag = rdbuf & 0x80; + write_byte(ar, set_nz(rdbuf << 1)); + Last; + case O_ASL_A: + read_idle(pc); + c_flag = a & 0x80; + set_nz(a <<= 1); + Last; + + case O_LSR: + c_flag = rdbuf & 0x01; + write_byte(ar, set_nz(rdbuf >> 1)); + Last; + case O_LSR_A: + read_idle(pc); + c_flag = a & 0x01; + set_nz(a >>= 1); + Last; + + case O_ROL: + write_byte(ar, set_nz(c_flag ? (rdbuf << 1) | 0x01 : rdbuf << 1)); + c_flag = rdbuf & 0x80; + Last; + case O_ROL_A: + read_idle(pc); + data = a & 0x80; + set_nz(a = c_flag ? (a << 1) | 0x01 : a << 1); + c_flag = data; + Last; + + case O_ROR: + write_byte(ar, set_nz(c_flag ? (rdbuf >> 1) | 0x80 : rdbuf >> 1)); + c_flag = rdbuf & 0x01; + Last; + case O_ROR_A: + read_idle(pc); + data = a & 0x01; + set_nz(a = (c_flag ? (a >> 1) | 0x80 : a >> 1)); + c_flag = data; + Last; + + + // Stack group + case O_PHA: + read_idle(pc); + state = O_PHA1; + break; + case O_PHA1: + write_byte(sp-- | 0x100, a); + Last; + + case O_PLA: + read_idle(pc); + state = O_PLA1; + break; + case O_PLA1: + read_idle(sp++ | 0x100); + state = O_PLA2; + break; + case O_PLA2: + read_to(sp | 0x100, data); + set_nz(a = data); + Last; + + case O_PHP: + read_idle(pc); + state = O_PHP1; + break; + case O_PHP1: + push_flags(true); + Last; + + case O_PLP: + read_idle(pc); + state = O_PLP1; + break; + case O_PLP1: + read_idle(sp++ | 0x100); + state = O_PLP2; + break; + case O_PLP2: + pop_flags(); + Last; + + + // Jump/branch group + case O_JMP: + read_to(pc++, ar); + state = O_JMP1; + break; + case O_JMP1: + read_to(pc, data); + pc = (data << 8) | ar; + Last; + + case O_JMP_I: + read_to(ar, pc); + state = O_JMP_I1; + break; + case O_JMP_I1: + read_to((ar + 1) & 0xff | ar & 0xff00, data); + pc |= data << 8; + Last; + + case O_JSR: + read_to(pc++, ar); + state = O_JSR1; + break; + case O_JSR1: + read_idle(sp | 0x100); + state = O_JSR2; + break; + case O_JSR2: + write_byte(sp-- | 0x100, pc >> 8); + state = O_JSR3; + break; + case O_JSR3: + write_byte(sp-- | 0x100, pc); + state = O_JSR4; + break; + case O_JSR4: + read_to(pc++, data); + pc = ar | (data << 8); + Last; + + case O_RTS: + read_idle(pc); + state = O_RTS1; + break; + case O_RTS1: + read_idle(sp++ | 0x100); + state = O_RTS2; + break; + case O_RTS2: + read_to(sp++ | 0x100, pc); + state = O_RTS3; + break; + case O_RTS3: + read_to(sp | 0x100, data); + pc |= data << 8; + state = O_RTS4; + break; + case O_RTS4: + read_idle(pc++); + Last; + + case O_RTI: + read_idle(pc); + state = O_RTI1; + break; + case O_RTI1: + read_idle(sp++ | 0x100); + state = O_RTI2; + break; + case O_RTI2: + pop_flags(); + sp++; + state = O_RTI3; + break; + case O_RTI3: + read_to(sp++ | 0x100, pc); + state = O_RTI4; + break; + case O_RTI4: + read_to(sp | 0x100, data); + pc |= data << 8; + Last; + + case O_BRK: + read_idle(pc++); + state = O_BRK1; + break; + case O_BRK1: + write_byte(sp-- | 0x100, pc >> 8); + state = O_BRK2; + break; + case O_BRK2: + write_byte(sp-- | 0x100, pc); + state = O_BRK3; + break; + case O_BRK3: + push_flags(true); + i_flag = true; +#ifndef IS_CPU_1541 + if (interrupt.intr[INT_NMI]) { // BRK interrupted by NMI? + interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input + state = 0x0015; // Jump to NMI sequence + break; + } +#endif + state = O_BRK4; + break; + case O_BRK4: +#ifndef IS_CPU_1541 + first_nmi_cycle++; // Delay NMI +#endif + read_to(0xfffe, pc); + state = O_BRK5; + break; + case O_BRK5: + read_to(0xffff, data); + pc |= data << 8; + Last; + + case O_BCS: + Branch(c_flag); + + case O_BCC: + Branch(!c_flag); + + case O_BEQ: + Branch(!z_flag); + + case O_BNE: + Branch(z_flag); + + case O_BVS: +#ifndef IS_CPU_1541 + Branch(v_flag); +#else + Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag); // GCR byte ready flag +#endif + + case O_BVC: +#ifndef IS_CPU_1541 + Branch(!v_flag); +#else + Branch(!((via2_pcr & 0x0e) == 0x0e) ? 0 : v_flag); // GCR byte ready flag +#endif + + case O_BMI: + Branch(n_flag & 0x80); + + case O_BPL: + Branch(!(n_flag & 0x80)); + + case O_BRANCH_NP: // No page crossed + first_irq_cycle++; // Delay IRQ +#ifndef IS_CPU_1541 + first_nmi_cycle++; // Delay NMI +#endif + read_idle(pc); + pc = ar; + Last; + case O_BRANCH_BP: // Page crossed, branch backwards + read_idle(pc); + pc = ar; + state = O_BRANCH_BP1; + break; + case O_BRANCH_BP1: + read_idle(pc + 0x100); + Last; + case O_BRANCH_FP: // Page crossed, branch forwards + read_idle(pc); + pc = ar; + state = O_BRANCH_FP1; + break; + case O_BRANCH_FP1: + read_idle(pc - 0x100); + Last; + + + // Flag group + case O_SEC: + read_idle(pc); + c_flag = true; + Last; + + case O_CLC: + read_idle(pc); + c_flag = false; + Last; + + case O_SED: + read_idle(pc); + d_flag = true; + Last; + + case O_CLD: + read_idle(pc); + d_flag = false; + Last; + + case O_SEI: + read_idle(pc); + i_flag = true; + Last; + + case O_CLI: + read_idle(pc); + i_flag = false; + Last; + + case O_CLV: + read_idle(pc); + v_flag = false; + Last; + + + // NOP group + case O_NOP: + read_idle(pc); + Last; + + +/* + * Undocumented opcodes start here + */ + + // NOP group + case O_NOP_I: + read_idle(pc++); + Last; + + case O_NOP_A: + read_idle(ar); + Last; + + + // Load A/X group + case O_LAX: + read_to(ar, data); + set_nz(a = x = data); + Last; + + + // Store A/X group + case O_SAX: + write_byte(ar, a & x); + Last; + + + // ASL/ORA group + case O_SLO: + c_flag = rdbuf & 0x80; + rdbuf <<= 1; + write_byte(ar, rdbuf); + set_nz(a |= rdbuf); + Last; + + + // ROL/AND group + case O_RLA: + tmp = rdbuf & 0x80; + rdbuf = c_flag ? (rdbuf << 1) | 0x01 : rdbuf << 1; + c_flag = tmp; + write_byte(ar, rdbuf); + set_nz(a &= rdbuf); + Last; + + + // LSR/EOR group + case O_SRE: + c_flag = rdbuf & 0x01; + rdbuf >>= 1; + write_byte(ar, rdbuf); + set_nz(a ^= rdbuf); + Last; + + + // ROR/ADC group + case O_RRA: + tmp = rdbuf & 0x01; + rdbuf = c_flag ? (rdbuf >> 1) | 0x80 : rdbuf >> 1; + c_flag = tmp; + write_byte(ar, rdbuf); + do_adc(rdbuf); + Last; + + + // DEC/CMP group + case O_DCP: + write_byte(ar, --rdbuf); + set_nz(ar = a - rdbuf); + c_flag = ar < 0x100; + Last; + + + // INC/SBC group + case O_ISB: + write_byte(ar, ++rdbuf); + do_sbc(rdbuf); + Last; + + + // Complex functions + case O_ANC_I: + read_to(pc++, data); + set_nz(a &= data); + c_flag = n_flag & 0x80; + Last; + + case O_ASR_I: + read_to(pc++, data); + a &= data; + c_flag = a & 0x01; + set_nz(a >>= 1); + Last; + + case O_ARR_I: + read_to(pc++, data); + data &= a; + a = (c_flag ? (data >> 1) | 0x80 : data >> 1); + if (!d_flag) { + set_nz(a); + c_flag = a & 0x40; + v_flag = (a & 0x40) ^ ((a & 0x20) << 1); + } else { + n_flag = c_flag ? 0x80 : 0; + z_flag = a; + v_flag = (data ^ a) & 0x40; + if ((data & 0x0f) + (data & 0x01) > 5) + a = a & 0xf0 | (a + 6) & 0x0f; + if (c_flag = ((data + (data & 0x10)) & 0x1f0) > 0x50) + a += 0x60; + } + Last; + + case O_ANE_I: + read_to(pc++, data); + set_nz(a = (a | 0xee) & x & data); + Last; + + case O_LXA_I: + read_to(pc++, data); + set_nz(a = x = (a | 0xee) & data); + Last; + + case O_SBX_I: + read_to(pc++, data); + set_nz(x = ar = (x & a) - data); + c_flag = ar < 0x100; + Last; + + case O_LAS: + read_to(ar, data); + set_nz(a = x = sp = data & sp); + Last; + + case O_SHS: // ar2 contains the high byte of the operand address + write_byte(ar, (ar2+1) & (sp = a & x)); + Last; + + case O_SHY: // ar2 contains the high byte of the operand address + write_byte(ar, y & (ar2+1)); + Last; + + case O_SHX: // ar2 contains the high byte of the operand address + write_byte(ar, x & (ar2+1)); + Last; + + case O_SHA: // ar2 contains the high byte of the operand address + write_byte(ar, a & x & (ar2+1)); + Last; diff --git a/Src/CPU_emulline.i b/Src/CPU_emulline.i new file mode 100644 index 0000000..9b35b9c --- /dev/null +++ b/Src/CPU_emulline.i @@ -0,0 +1,1398 @@ +/* + * CPU_emulline.i - 6510/6502 emulation core (body of + * EmulateLine() function, the same for + * both 6510 and 6502) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + + +/* + * Addressing mode macros + */ + +// Read immediate operand +#if PC_IS_POINTER +#define read_byte_imm() (*pc++) +#else +#define read_byte_imm() read_byte(pc++) +#endif + +// Read zeropage operand address +#define read_adr_zero() ((uint16)read_byte_imm()) + +// Read zeropage x-indexed operand address +#define read_adr_zero_x() ((read_byte_imm() + x) & 0xff) + +// Read zeropage y-indexed operand address +#define read_adr_zero_y() ((read_byte_imm() + y) & 0xff) + +// Read absolute operand address (uses adr!) +#if PC_IS_POINTER +#if LITTLE_ENDIAN_UNALIGNED +#define read_adr_abs() (adr = *(UWORD *)pc, pc+=2, adr) +#else +#define read_adr_abs() (adr = ((*(pc+1)) << 8) | *pc, pc+=2, adr) +#endif +#else +#define read_adr_abs() (adr = read_word(pc), pc+=2, adr) +#endif + +// Read absolute x-indexed operand address +#define read_adr_abs_x() (read_adr_abs() + x) + +// Read absolute y-indexed operand address +#define read_adr_abs_y() (read_adr_abs() + y) + +// Read indexed indirect operand address +#define read_adr_ind_x() (read_zp_word(read_byte_imm() + x)) + +// Read indirect indexed operand address +#define read_adr_ind_y() (read_zp_word(read_byte_imm()) + y) + +// Read zeropage operand +#define read_byte_zero() read_zp(read_adr_zero()) + +// Read zeropage x-indexed operand +#define read_byte_zero_x() read_zp(read_adr_zero_x()) + +// Read zeropage y-indexed operand +#define read_byte_zero_y() read_zp(read_adr_zero_y()) + +// Read absolute operand +#define read_byte_abs() read_byte(read_adr_abs()) + +#if PRECISE_CPU_CYCLES +// Acount for cyles due to crossing page boundaries +#define page_plus(exp, reg) \ + (adr = exp, page_cycles = (adr & 0xff) + reg >= 0x100, adr + reg) + +// Read absolute x-indexed operand +#define read_byte_abs_x() read_byte(page_plus(read_adr_abs(), x)) + +// Read absolute x-indexed operand +#define read_byte_abs_y() read_byte(page_plus(read_adr_abs(), y)) + +// Read indirect y-indexed operand +#define read_byte_ind_y() read_byte(page_plus(read_zp_word(read_byte_imm()), y)) + +#else + +// Read absolute x-indexed operand +#define read_byte_abs_x() read_byte(read_adr_abs_x()) + +// Read absolute x-indexed operand +#define read_byte_abs_y() read_byte(read_adr_abs_y()) + +// Read indirect y-indexed operand +#define read_byte_ind_y() read_byte(read_adr_ind_y()) +#endif + +// Read indexed indirect operand +#define read_byte_ind_x() read_byte(read_adr_ind_x()) + + +/* + * Set N and Z flags according to byte + */ + +#define set_nz(x) (z_flag = n_flag = (x)) + + +/* + * End of opcode, decrement cycles left + */ + +#define ENDOP(cyc) last_cycles = cyc; break; + + + // Main opcode fetch/execute loop +#if PRECISE_CPU_CYCLES + if (cycles_left != 1) + cycles_left -= borrowed_cycles; + int page_cycles = 0; + for (;;) { + if (last_cycles) { + last_cycles += page_cycles; + page_cycles = 0; +#if PRECISE_CIA_CYCLES && !defined(IS_CPU_1541) + TheCIA1->EmulateLine(last_cycles); + TheCIA2->EmulateLine(last_cycles); +#endif + } + if ((cycles_left -= last_cycles) < 0) { + borrowed_cycles = -cycles_left; + break; + } +#else + while ((cycles_left -= last_cycles) >= 0) { +#endif + + switch (read_byte_imm()) { + + + // Load group + case 0xa9: // LDA #imm + set_nz(a = read_byte_imm()); + ENDOP(2); + + case 0xa5: // LDA zero + set_nz(a = read_byte_zero()); + ENDOP(3); + + case 0xb5: // LDA zero,X + set_nz(a = read_byte_zero_x()); + ENDOP(4); + + case 0xad: // LDA abs + set_nz(a = read_byte_abs()); + ENDOP(4); + + case 0xbd: // LDA abs,X + set_nz(a = read_byte_abs_x()); + ENDOP(4); + + case 0xb9: // LDA abs,Y + set_nz(a = read_byte_abs_y()); + ENDOP(4); + + case 0xa1: // LDA (ind,X) + set_nz(a = read_byte_ind_x()); + ENDOP(6); + + case 0xb1: // LDA (ind),Y + set_nz(a = read_byte_ind_y()); + ENDOP(5); + + case 0xa2: // LDX #imm + set_nz(x = read_byte_imm()); + ENDOP(2); + + case 0xa6: // LDX zero + set_nz(x = read_byte_zero()); + ENDOP(3); + + case 0xb6: // LDX zero,Y + set_nz(x = read_byte_zero_y()); + ENDOP(4); + + case 0xae: // LDX abs + set_nz(x = read_byte_abs()); + ENDOP(4); + + case 0xbe: // LDX abs,Y + set_nz(x = read_byte_abs_y()); + ENDOP(4); + + case 0xa0: // LDY #imm + set_nz(y = read_byte_imm()); + ENDOP(2); + + case 0xa4: // LDY zero + set_nz(y = read_byte_zero()); + ENDOP(3); + + case 0xb4: // LDY zero,X + set_nz(y = read_byte_zero_x()); + ENDOP(4); + + case 0xac: // LDY abs + set_nz(y = read_byte_abs()); + ENDOP(4); + + case 0xbc: // LDY abs,X + set_nz(y = read_byte_abs_x()); + ENDOP(4); + + + // Store group + case 0x85: // STA zero + write_byte(read_adr_zero(), a); + ENDOP(3); + + case 0x95: // STA zero,X + write_byte(read_adr_zero_x(), a); + ENDOP(4); + + case 0x8d: // STA abs + write_byte(read_adr_abs(), a); + ENDOP(4); + + case 0x9d: // STA abs,X + write_byte(read_adr_abs_x(), a); + ENDOP(5); + + case 0x99: // STA abs,Y + write_byte(read_adr_abs_y(), a); + ENDOP(5); + + case 0x81: // STA (ind,X) + write_byte(read_adr_ind_x(), a); + ENDOP(6); + + case 0x91: // STA (ind),Y + write_byte(read_adr_ind_y(), a); + ENDOP(6); + + case 0x86: // STX zero + write_byte(read_adr_zero(), x); + ENDOP(3); + + case 0x96: // STX zero,Y + write_byte(read_adr_zero_y(), x); + ENDOP(4); + + case 0x8e: // STX abs + write_byte(read_adr_abs(), x); + ENDOP(4); + + case 0x84: // STY zero + write_byte(read_adr_zero(), y); + ENDOP(3); + + case 0x94: // STY zero,X + write_byte(read_adr_zero_x(), y); + ENDOP(4); + + case 0x8c: // STY abs + write_byte(read_adr_abs(), y); + ENDOP(4); + + + // Transfer group + case 0xaa: // TAX + set_nz(x = a); + ENDOP(2); + + case 0x8a: // TXA + set_nz(a = x); + ENDOP(2); + + case 0xa8: // TAY + set_nz(y = a); + ENDOP(2); + + case 0x98: // TYA + set_nz(a = y); + ENDOP(2); + + case 0xba: // TSX + set_nz(x = sp); + ENDOP(2); + + case 0x9a: // TXS + sp = x; + ENDOP(2); + + + // Arithmetic group + case 0x69: // ADC #imm + do_adc(read_byte_imm()); + ENDOP(2); + + case 0x65: // ADC zero + do_adc(read_byte_zero()); + ENDOP(3); + + case 0x75: // ADC zero,X + do_adc(read_byte_zero_x()); + ENDOP(4); + + case 0x6d: // ADC abs + do_adc(read_byte_abs()); + ENDOP(4); + + case 0x7d: // ADC abs,X + do_adc(read_byte_abs_x()); + ENDOP(4); + + case 0x79: // ADC abs,Y + do_adc(read_byte_abs_y()); + ENDOP(4); + + case 0x61: // ADC (ind,X) + do_adc(read_byte_ind_x()); + ENDOP(6); + + case 0x71: // ADC (ind),Y + do_adc(read_byte_ind_y()); + ENDOP(5); + + case 0xe9: // SBC #imm + case 0xeb: // Undocumented opcode + do_sbc(read_byte_imm()); + ENDOP(2); + + case 0xe5: // SBC zero + do_sbc(read_byte_zero()); + ENDOP(3); + + case 0xf5: // SBC zero,X + do_sbc(read_byte_zero_x()); + ENDOP(4); + + case 0xed: // SBC abs + do_sbc(read_byte_abs()); + ENDOP(4); + + case 0xfd: // SBC abs,X + do_sbc(read_byte_abs_x()); + ENDOP(4); + + case 0xf9: // SBC abs,Y + do_sbc(read_byte_abs_y()); + ENDOP(4); + + case 0xe1: // SBC (ind,X) + do_sbc(read_byte_ind_x()); + ENDOP(6); + + case 0xf1: // SBC (ind),Y + do_sbc(read_byte_ind_y()); + ENDOP(5); + + + // Increment/decrement group + case 0xe8: // INX + set_nz(++x); + ENDOP(2); + + case 0xca: // DEX + set_nz(--x); + ENDOP(2); + + case 0xc8: // INY + set_nz(++y); + ENDOP(2); + + case 0x88: // DEY + set_nz(--y); + ENDOP(2); + + case 0xe6: // INC zero + adr = read_adr_zero(); + write_zp(adr, set_nz(read_zp(adr) + 1)); + ENDOP(5); + + case 0xf6: // INC zero,X + adr = read_adr_zero_x(); + write_zp(adr, set_nz(read_zp(adr) + 1)); + ENDOP(6); + + case 0xee: // INC abs + adr = read_adr_abs(); + write_byte(adr, set_nz(read_byte(adr) + 1)); + ENDOP(6); + + case 0xfe: // INC abs,X + adr = read_adr_abs_x(); + write_byte(adr, set_nz(read_byte(adr) + 1)); + ENDOP(7); + + case 0xc6: // DEC zero + adr = read_adr_zero(); + write_zp(adr, set_nz(read_zp(adr) - 1)); + ENDOP(5); + + case 0xd6: // DEC zero,X + adr = read_adr_zero_x(); + write_zp(adr, set_nz(read_zp(adr) - 1)); + ENDOP(6); + + case 0xce: // DEC abs + adr = read_adr_abs(); + write_byte(adr, set_nz(read_byte(adr) - 1)); + ENDOP(6); + + case 0xde: // DEC abs,X + adr = read_adr_abs_x(); + write_byte(adr, set_nz(read_byte(adr) - 1)); + ENDOP(7); + + + // Logic group + case 0x29: // AND #imm + set_nz(a &= read_byte_imm()); + ENDOP(2); + + case 0x25: // AND zero + set_nz(a &= read_byte_zero()); + ENDOP(3); + + case 0x35: // AND zero,X + set_nz(a &= read_byte_zero_x()); + ENDOP(4); + + case 0x2d: // AND abs + set_nz(a &= read_byte_abs()); + ENDOP(4); + + case 0x3d: // AND abs,X + set_nz(a &= read_byte_abs_x()); + ENDOP(4); + + case 0x39: // AND abs,Y + set_nz(a &= read_byte_abs_y()); + ENDOP(4); + + case 0x21: // AND (ind,X) + set_nz(a &= read_byte_ind_x()); + ENDOP(6); + + case 0x31: // AND (ind),Y + set_nz(a &= read_byte_ind_y()); + ENDOP(5); + + case 0x09: // ORA #imm + set_nz(a |= read_byte_imm()); + ENDOP(2); + + case 0x05: // ORA zero + set_nz(a |= read_byte_zero()); + ENDOP(3); + + case 0x15: // ORA zero,X + set_nz(a |= read_byte_zero_x()); + ENDOP(4); + + case 0x0d: // ORA abs + set_nz(a |= read_byte_abs()); + ENDOP(4); + + case 0x1d: // ORA abs,X + set_nz(a |= read_byte_abs_x()); + ENDOP(4); + + case 0x19: // ORA abs,Y + set_nz(a |= read_byte_abs_y()); + ENDOP(4); + + case 0x01: // ORA (ind,X) + set_nz(a |= read_byte_ind_x()); + ENDOP(6); + + case 0x11: // ORA (ind),Y + set_nz(a |= read_byte_ind_y()); + ENDOP(5); + + case 0x49: // EOR #imm + set_nz(a ^= read_byte_imm()); + ENDOP(2); + + case 0x45: // EOR zero + set_nz(a ^= read_byte_zero()); + ENDOP(3); + + case 0x55: // EOR zero,X + set_nz(a ^= read_byte_zero_x()); + ENDOP(4); + + case 0x4d: // EOR abs + set_nz(a ^= read_byte_abs()); + ENDOP(4); + + case 0x5d: // EOR abs,X + set_nz(a ^= read_byte_abs_x()); + ENDOP(4); + + case 0x59: // EOR abs,Y + set_nz(a ^= read_byte_abs_y()); + ENDOP(4); + + case 0x41: // EOR (ind,X) + set_nz(a ^= read_byte_ind_x()); + ENDOP(6); + + case 0x51: // EOR (ind),Y + set_nz(a ^= read_byte_ind_y()); + ENDOP(5); + + + // Compare group + case 0xc9: // CMP #imm + set_nz(adr = a - read_byte_imm()); + c_flag = adr < 0x100; + ENDOP(2); + + case 0xc5: // CMP zero + set_nz(adr = a - read_byte_zero()); + c_flag = adr < 0x100; + ENDOP(3); + + case 0xd5: // CMP zero,X + set_nz(adr = a - read_byte_zero_x()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xcd: // CMP abs + set_nz(adr = a - read_byte_abs()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xdd: // CMP abs,X + set_nz(adr = a - read_byte_abs_x()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xd9: // CMP abs,Y + set_nz(adr = a - read_byte_abs_y()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xc1: // CMP (ind,X) + set_nz(adr = a - read_byte_ind_x()); + c_flag = adr < 0x100; + ENDOP(6); + + case 0xd1: // CMP (ind),Y + set_nz(adr = a - read_byte_ind_y()); + c_flag = adr < 0x100; + ENDOP(5); + + case 0xe0: // CPX #imm + set_nz(adr = x - read_byte_imm()); + c_flag = adr < 0x100; + ENDOP(2); + + case 0xe4: // CPX zero + set_nz(adr = x - read_byte_zero()); + c_flag = adr < 0x100; + ENDOP(3); + + case 0xec: // CPX abs + set_nz(adr = x - read_byte_abs()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xc0: // CPY #imm + set_nz(adr = y - read_byte_imm()); + c_flag = adr < 0x100; + ENDOP(2); + + case 0xc4: // CPY zero + set_nz(adr = y - read_byte_zero()); + c_flag = adr < 0x100; + ENDOP(3); + + case 0xcc: // CPY abs + set_nz(adr = y - read_byte_abs()); + c_flag = adr < 0x100; + ENDOP(4); + + + // Bit-test group + case 0x24: // BIT zero + z_flag = a & (tmp = read_byte_zero()); + n_flag = tmp; + v_flag = tmp & 0x40; + ENDOP(3); + + case 0x2c: // BIT abs + z_flag = a & (tmp = read_byte_abs()); + n_flag = tmp; + v_flag = tmp & 0x40; + ENDOP(4); + + + // Shift/rotate group + case 0x0a: // ASL A + c_flag = a & 0x80; + set_nz(a <<= 1); + ENDOP(2); + + case 0x06: // ASL zero + tmp = read_zp(adr = read_adr_zero()); + c_flag = tmp & 0x80; + write_zp(adr, set_nz(tmp << 1)); + ENDOP(5); + + case 0x16: // ASL zero,X + tmp = read_zp(adr = read_adr_zero_x()); + c_flag = tmp & 0x80; + write_zp(adr, set_nz(tmp << 1)); + ENDOP(6); + + case 0x0e: // ASL abs + tmp = read_byte(adr = read_adr_abs()); + c_flag = tmp & 0x80; + write_byte(adr, set_nz(tmp << 1)); + ENDOP(6); + + case 0x1e: // ASL abs,X + tmp = read_byte(adr = read_adr_abs_x()); + c_flag = tmp & 0x80; + write_byte(adr, set_nz(tmp << 1)); + ENDOP(7); + + case 0x4a: // LSR A + c_flag = a & 0x01; + set_nz(a >>= 1); + ENDOP(2); + + case 0x46: // LSR zero + tmp = read_zp(adr = read_adr_zero()); + c_flag = tmp & 0x01; + write_zp(adr, set_nz(tmp >> 1)); + ENDOP(5); + + case 0x56: // LSR zero,X + tmp = read_zp(adr = read_adr_zero_x()); + c_flag = tmp & 0x01; + write_zp(adr, set_nz(tmp >> 1)); + ENDOP(6); + + case 0x4e: // LSR abs + tmp = read_byte(adr = read_adr_abs()); + c_flag = tmp & 0x01; + write_byte(adr, set_nz(tmp >> 1)); + ENDOP(6); + + case 0x5e: // LSR abs,X + tmp = read_byte(adr = read_adr_abs_x()); + c_flag = tmp & 0x01; + write_byte(adr, set_nz(tmp >> 1)); + ENDOP(7); + + case 0x2a: // ROL A + tmp2 = a & 0x80; + set_nz(a = c_flag ? (a << 1) | 0x01 : a << 1); + c_flag = tmp2; + ENDOP(2); + + case 0x26: // ROL zero + tmp = read_zp(adr = read_adr_zero()); + tmp2 = tmp & 0x80; + write_zp(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(5); + + case 0x36: // ROL zero,X + tmp = read_zp(adr = read_adr_zero_x()); + tmp2 = tmp & 0x80; + write_zp(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x2e: // ROL abs + tmp = read_byte(adr = read_adr_abs()); + tmp2 = tmp & 0x80; + write_byte(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x3e: // ROL abs,X + tmp = read_byte(adr = read_adr_abs_x()); + tmp2 = tmp & 0x80; + write_byte(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(7); + + case 0x6a: // ROR A + tmp2 = a & 0x01; + set_nz(a = (c_flag ? (a >> 1) | 0x80 : a >> 1)); + c_flag = tmp2; + ENDOP(2); + + case 0x66: // ROR zero + tmp = read_zp(adr = read_adr_zero()); + tmp2 = tmp & 0x01; + write_zp(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(5); + + case 0x76: // ROR zero,X + tmp = read_zp(adr = read_adr_zero_x()); + tmp2 = tmp & 0x01; + write_zp(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x6e: // ROR abs + tmp = read_byte(adr = read_adr_abs()); + tmp2 = tmp & 0x01; + write_byte(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x7e: // ROR abs,X + tmp = read_byte(adr = read_adr_abs_x()); + tmp2 = tmp & 0x01; + write_byte(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(7); + + + // Stack group + case 0x48: // PHA + push_byte(a); + ENDOP(3); + + case 0x68: // PLA + set_nz(a = pop_byte()); + ENDOP(4); + + case 0x08: // PHP + push_flags(true); + ENDOP(3); + + case 0x28: // PLP + pop_flags(); + if (interrupt.intr_any && !i_flag) + goto handle_int; + ENDOP(4); + + + // Jump/branch group + case 0x4c: // JMP abs + jump(read_adr_abs()); + ENDOP(3); + + case 0x6c: // JMP (ind) + adr = read_adr_abs(); + jump(read_byte(adr) | (read_byte((adr + 1) & 0xff | adr & 0xff00) << 8)); + ENDOP(5); + + case 0x20: // JSR abs +#if PC_IS_POINTER + push_byte((pc-pc_base+1) >> 8); push_byte(pc-pc_base+1); +#else + push_byte(pc+1 >> 8); push_byte(pc+1); +#endif + jump(read_adr_abs()); + ENDOP(6); + + case 0x60: // RTS + adr = pop_byte(); // Split because of pop_byte ++sp side-effect + jump((adr | pop_byte() << 8) + 1); + ENDOP(6); + + case 0x40: // RTI + pop_flags(); + adr = pop_byte(); // Split because of pop_byte ++sp side-effect + jump(adr | pop_byte() << 8); + if (interrupt.intr_any && !i_flag) + goto handle_int; + ENDOP(6); + + case 0x00: // BRK +#if PC_IS_POINTER + push_byte((pc+1-pc_base) >> 8); push_byte(pc+1-pc_base); +#else + push_byte((pc+1) >> 8); push_byte(pc+1); +#endif + push_flags(true); + i_flag = true; + jump(read_word(0xfffe)); + ENDOP(7); + +#if PC_IS_POINTER +#if PRECISE_CPU_CYCLES +#define Branch(flag) \ + if (flag) { \ + pc += (int8)*pc + 1; \ + if (((pc-pc_base) ^ (old_pc - pc_base)) & 0xff00) { \ + ENDOP(4); \ + } else { \ + ENDOP(3); \ + } \ + } else { \ + pc++; \ + ENDOP(2); \ + } +#else +#define Branch(flag) \ + if (flag) { \ + pc += (int8)*pc + 1; \ + ENDOP(3); \ + } else { \ + pc++; \ + ENDOP(2); \ + } +#endif +#else +#define Branch(flag) \ + if (flag) { \ + uint16 old_pc = pc; \ + pc += (int8)read_byte(pc) + 1; \ + if ((pc ^ old_pc) & 0xff00) { \ + ENDOP(4); \ + } else { \ + ENDOP(3); \ + } \ + } else { \ + pc++; \ + ENDOP(2); \ + } +#endif + + case 0xb0: // BCS rel + Branch(c_flag); + + case 0x90: // BCC rel + Branch(!c_flag); + + case 0xf0: // BEQ rel + Branch(!z_flag); + + case 0xd0: // BNE rel + Branch(z_flag); + + case 0x70: // BVS rel +#ifndef IS_CPU_1541 + Branch(v_flag); +#else + Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag); // GCR byte ready flag +#endif + + case 0x50: // BVC rel +#ifndef IS_CPU_1541 + Branch(!v_flag); +#else + Branch(!((via2_pcr & 0x0e) == 0x0e) ? 0 : v_flag); // GCR byte ready flag +#endif + + case 0x30: // BMI rel + Branch(n_flag & 0x80); + + case 0x10: // BPL rel + Branch(!(n_flag & 0x80)); + + + // Flags group + case 0x38: // SEC + c_flag = true; + ENDOP(2); + + case 0x18: // CLC + c_flag = false; + ENDOP(2); + + case 0xf8: // SED + d_flag = true; + ENDOP(2); + + case 0xd8: // CLD + d_flag = false; + ENDOP(2); + + case 0x78: // SEI + i_flag = true; + ENDOP(2); + + case 0x58: // CLI + i_flag = false; + if (interrupt.intr_any) + goto handle_int; + ENDOP(2); + + case 0xb8: // CLV + v_flag = false; + ENDOP(2); + + + // NOP group + case 0xea: // NOP + ENDOP(2); + + +/* + * Undocumented opcodes start here + */ + + // NOP group + case 0x1a: // NOP + case 0x3a: + case 0x5a: + case 0x7a: + case 0xda: + case 0xfa: + ENDOP(2); + + case 0x80: // NOP #imm + case 0x82: + case 0x89: + case 0xc2: + case 0xe2: + pc++; + ENDOP(2); + + case 0x04: // NOP zero + case 0x44: + case 0x64: + pc++; + ENDOP(3); + + case 0x14: // NOP zero,X + case 0x34: + case 0x54: + case 0x74: + case 0xd4: + case 0xf4: + pc++; + ENDOP(4); + + case 0x0c: // NOP abs + pc+=2; + ENDOP(4); + + case 0x1c: // NOP abs,X + case 0x3c: + case 0x5c: + case 0x7c: + case 0xdc: + case 0xfc: +#if PRECISE_CPU_CYCLES + read_byte_abs_x(); +#else + pc+=2; +#endif + ENDOP(4); + + + // Load A/X group + case 0xa7: // LAX zero + set_nz(a = x = read_byte_zero()); + ENDOP(3); + + case 0xb7: // LAX zero,Y + set_nz(a = x = read_byte_zero_y()); + ENDOP(4); + + case 0xaf: // LAX abs + set_nz(a = x = read_byte_abs()); + ENDOP(4); + + case 0xbf: // LAX abs,Y + set_nz(a = x = read_byte_abs_y()); + ENDOP(4); + + case 0xa3: // LAX (ind,X) + set_nz(a = x = read_byte_ind_x()); + ENDOP(6); + + case 0xb3: // LAX (ind),Y + set_nz(a = x = read_byte_ind_y()); + ENDOP(5); + + + // Store A/X group + case 0x87: // SAX zero + write_byte(read_adr_zero(), a & x); + ENDOP(3); + + case 0x97: // SAX zero,Y + write_byte(read_adr_zero_y(), a & x); + ENDOP(4); + + case 0x8f: // SAX abs + write_byte(read_adr_abs(), a & x); + ENDOP(4); + + case 0x83: // SAX (ind,X) + write_byte(read_adr_ind_x(), a & x); + ENDOP(6); + + + // ASL/ORA group +#define ShiftLeftOr \ + c_flag = tmp & 0x80; \ + tmp <<= 1; \ + set_nz(a |= tmp); + + case 0x07: // SLO zero + tmp = read_zp(adr = read_adr_zero()); + ShiftLeftOr; + write_zp(adr, tmp); + ENDOP(5); + + case 0x17: // SLO zero,X + tmp = read_zp(adr = read_adr_zero_x()); + ShiftLeftOr; + write_zp(adr, tmp); + ENDOP(6); + + case 0x0f: // SLO abs + tmp = read_byte(adr = read_adr_abs()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(6); + + case 0x1f: // SLO abs,X + tmp = read_byte(adr = read_adr_abs_x()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(7); + + case 0x1b: // SLO abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(7); + + case 0x03: // SLO (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(8); + + case 0x13: // SLO (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(8); + + + // ROL/AND group +#define RoLeftAnd \ + tmp2 = tmp & 0x80; \ + tmp = c_flag ? (tmp << 1) | 0x01 : tmp << 1; \ + set_nz(a &= tmp); \ + c_flag = tmp2; + + case 0x27: // RLA zero + tmp = read_zp(adr = read_adr_zero()); + RoLeftAnd; + write_zp(adr, tmp); + ENDOP(5); + + case 0x37: // RLA zero,X + tmp = read_zp(adr = read_adr_zero_x()); + RoLeftAnd; + write_zp(adr, tmp); + ENDOP(6); + + case 0x2f: // RLA abs + tmp = read_byte(adr = read_adr_abs()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(6); + + case 0x3f: // RLA abs,X + tmp = read_byte(adr = read_adr_abs_x()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(7); + + case 0x3b: // RLA abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(7); + + case 0x23: // RLA (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(8); + + case 0x33: // RLA (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(8); + + + // LSR/EOR group +#define ShiftRightEor \ + c_flag = tmp & 0x01; \ + tmp >>= 1; \ + set_nz(a ^= tmp); + + case 0x47: // SRE zero + tmp = read_zp(adr = read_adr_zero()); + ShiftRightEor; + write_zp(adr, tmp); + ENDOP(5); + + case 0x57: // SRE zero,X + tmp = read_zp(adr = read_adr_zero_x()); + ShiftRightEor; + write_zp(adr, tmp); + ENDOP(6); + + case 0x4f: // SRE abs + tmp = read_byte(adr = read_adr_abs()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(6); + + case 0x5f: // SRE abs,X + tmp = read_byte(adr = read_adr_abs_x()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(7); + + case 0x5b: // SRE abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(7); + + case 0x43: // SRE (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(8); + + case 0x53: // SRE (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(8); + + + // ROR/ADC group +#define RoRightAdc \ + tmp2 = tmp & 0x01; \ + tmp = c_flag ? (tmp >> 1) | 0x80 : tmp >> 1; \ + c_flag = tmp2; \ + do_adc(tmp); + + case 0x67: // RRA zero + tmp = read_zp(adr = read_adr_zero()); + RoRightAdc; + write_zp(adr, tmp); + ENDOP(5); + + case 0x77: // RRA zero,X + tmp = read_zp(adr = read_adr_zero_x()); + RoRightAdc; + write_zp(adr, tmp); + ENDOP(6); + + case 0x6f: // RRA abs + tmp = read_byte(adr = read_adr_abs()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(6); + + case 0x7f: // RRA abs,X + tmp = read_byte(adr = read_adr_abs_x()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(7); + + case 0x7b: // RRA abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(7); + + case 0x63: // RRA (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(8); + + case 0x73: // RRA (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(8); + + + // DEC/CMP group +#define DecCompare \ + set_nz(adr = a - tmp); \ + c_flag = adr < 0x100; + + case 0xc7: // DCP zero + tmp = read_zp(adr = read_adr_zero()) - 1; + write_zp(adr, tmp); + DecCompare; + ENDOP(5); + + case 0xd7: // DCP zero,X + tmp = read_zp(adr = read_adr_zero_x()) - 1; + write_zp(adr, tmp); + DecCompare; + ENDOP(6); + + case 0xcf: // DCP abs + tmp = read_byte(adr = read_adr_abs()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(6); + + case 0xdf: // DCP abs,X + tmp = read_byte(adr = read_adr_abs_x()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(7); + + case 0xdb: // DCP abs,Y + tmp = read_byte(adr = read_adr_abs_y()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(7); + + case 0xc3: // DCP (ind,X) + tmp = read_byte(adr = read_adr_ind_x()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(8); + + case 0xd3: // DCP (ind),Y + tmp = read_byte(adr = read_adr_ind_y()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(8); + + + // INC/SBC group + case 0xe7: // ISB zero + tmp = read_zp(adr = read_adr_zero()) + 1; + do_sbc(tmp); + write_zp(adr, tmp); + ENDOP(5); + + case 0xf7: // ISB zero,X + tmp = read_zp(adr = read_adr_zero_x()) + 1; + do_sbc(tmp); + write_zp(adr, tmp); + ENDOP(6); + + case 0xef: // ISB abs + tmp = read_byte(adr = read_adr_abs()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(6); + + case 0xff: // ISB abs,X + tmp = read_byte(adr = read_adr_abs_x()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(7); + + case 0xfb: // ISB abs,Y + tmp = read_byte(adr = read_adr_abs_y()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(7); + + case 0xe3: // ISB (ind,X) + tmp = read_byte(adr = read_adr_ind_x()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(8); + + case 0xf3: // ISB (ind),Y + tmp = read_byte(adr = read_adr_ind_y()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(8); + + + // Complex functions + case 0x0b: // ANC #imm + case 0x2b: + set_nz(a &= read_byte_imm()); + c_flag = n_flag & 0x80; + ENDOP(2); + + case 0x4b: // ASR #imm + a &= read_byte_imm(); + c_flag = a & 0x01; + set_nz(a >>= 1); + ENDOP(2); + + case 0x6b: // ARR #imm + tmp2 = read_byte_imm() & a; + a = (c_flag ? (tmp2 >> 1) | 0x80 : tmp2 >> 1); + if (!d_flag) { + set_nz(a); + c_flag = a & 0x40; + v_flag = (a & 0x40) ^ ((a & 0x20) << 1); + } else { + n_flag = c_flag ? 0x80 : 0; + z_flag = a; + v_flag = (tmp2 ^ a) & 0x40; + if ((tmp2 & 0x0f) + (tmp2 & 0x01) > 5) + a = a & 0xf0 | (a + 6) & 0x0f; + if (c_flag = ((tmp2 + (tmp2 & 0x10)) & 0x1f0) > 0x50) + a += 0x60; + } + ENDOP(2); + + case 0x8b: // ANE #imm + set_nz(a = read_byte_imm() & x & (a | 0xee)); + ENDOP(2); + + case 0x93: // SHA (ind),Y +#if PC_IS_POINTER + tmp2 = read_zp(pc[0] + 1); +#else + tmp2 = read_zp(read_byte(pc) + 1); +#endif + write_byte(read_adr_ind_y(), a & x & (tmp2+1)); + ENDOP(6); + + case 0x9b: // SHS abs,Y +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_y(), a & x & (tmp2+1)); + sp = a & x; + ENDOP(5); + + case 0x9c: // SHY abs,X +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_x(), y & (tmp2+1)); + ENDOP(5); + + case 0x9e: // SHX abs,Y +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_y(), x & (tmp2+1)); + ENDOP(5); + + case 0x9f: // SHA abs,Y +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_y(), a & x & (tmp2+1)); + ENDOP(5); + + case 0xab: // LXA #imm + set_nz(a = x = (a | 0xee) & read_byte_imm()); + ENDOP(2); + + case 0xbb: // LAS abs,Y + set_nz(a = x = sp = read_byte_abs_y() & sp); + ENDOP(4); + + case 0xcb: // SBX #imm + x &= a; + adr = x - read_byte_imm(); + c_flag = adr < 0x100; + set_nz(x = adr); + ENDOP(2); + + case 0x02: + case 0x12: + case 0x22: + case 0x32: + case 0x42: + case 0x52: + case 0x62: + case 0x72: + case 0x92: + case 0xb2: + case 0xd2: +#if PC_IS_POINTER + illegal_op(*(pc-1), pc-pc_base-1); +#else + illegal_op(read_byte(pc-1), pc-1); +#endif + break; diff --git a/Src/CmdPipe.cpp b/Src/CmdPipe.cpp new file mode 100644 index 0000000..c449c41 --- /dev/null +++ b/Src/CmdPipe.cpp @@ -0,0 +1,168 @@ +/* + * CmdPipe.cpp + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * Tcl/Tk stuff by Lutz Vieweg + */ + +#include "CmdPipe.h" + + +extern "C" { + #include + #include + #include + #include + #include + #include + #include + +#if defined(__alpha__) + #include +#endif + +#if defined(AIX) + #include +#else + #include +#endif + +#if defined(__linux__) + #include +#endif + + #include + #include +} + +static void kaputt(const char * c1, const char * c2) { + fprintf(stderr,"error: %s%s\n",c1,c2); + exit(20); +} + +Pipe::Pipe(void) : fail(true) { + + fds[0] = 0; + fds[1] = 1; + + if (-1 == pipe(fds)) { + kaputt("Pipe: ","unable to create pipe"); + return; + } + + fail = false; + return; +} + +Pipe::~Pipe(void) { + + if (! fail) { + close(fds[0]); + close(fds[1]); + } + return; +} + +unsigned long Pipe::ewrite(const void * buf, unsigned long len) { + + unsigned long wsum = 0; + while (len) { + long wlen; + + wlen = ::write(fds[1], buf, (long) len); + if (wlen <= 0) { + kaputt("Pipe::ewrite ","write-error"); + } + + len -= wlen; + buf = (void*) ((char*) buf + wlen); + wsum += wlen; + } + return wsum; +} + +unsigned long Pipe::eread(void * buf, unsigned long len) { + + unsigned long rsum = 0; + while (len) { + long rlen; + + rlen = ::read(fds[0], buf, (long) len); + + if (rlen <= 0) { + kaputt("Pipe::eread ","read-error"); + } + + len -= rlen; + buf = (void*) ((char*) buf + rlen); + rsum += rlen; + } + return rsum; +} + +int Pipe::probe(void) const { + + fd_set set; + FD_ZERO(&set); + FD_SET(fds[0], &set); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + int res; +// Use the following commented line for HP-UX < 10.20 +// res = select(FD_SETSIZE, (int *)&set, (int *)0, (int *)0, &tv); + res = select(FD_SETSIZE, &set, (fd_set *)0, (fd_set *)0, &tv); + + if (res > 0) return -1; + return 0; + +} + +CmdPipe::CmdPipe(const char * command, const char * arg, int nicediff) : childpid(0), fail(true) { + + if (tocmd.fail || fromcmd.fail) { + kaputt("CmdPipe: ","unable to initialize pipes"); + return; + } + + childpid = fork(); + + if (childpid == -1) { + childpid = 0; + kaputt("CmdPipe: ","unable to fork process"); + return; + } + + if (childpid == 0) { + + if (nicediff) { + if (-1 == nice(nicediff)) { + fprintf(stderr,"CmdPipe: unable to change nice-level (non-fatal)"); + } + } + + dup2(tocmd.get_read_fd(), STDIN_FILENO); + + dup2(fromcmd.get_write_fd(), STDOUT_FILENO); + execlp(command, "Frodo_GUI", arg, (char *)0); + kaputt("CmdPipe: unable to execute child process ",command); + _exit(0); // exit (and do NOT call destructors etc..) + } + + fail = false; + return; +} + +CmdPipe::~CmdPipe(void) { + + if (childpid) { + int status; + waitpid(childpid, &status, 0); + + if (status != 0) { + fprintf(stderr,"~CmdPipe child process returned error\n"); + } + } +} diff --git a/Src/CmdPipe.h b/Src/CmdPipe.h new file mode 100644 index 0000000..983483f --- /dev/null +++ b/Src/CmdPipe.h @@ -0,0 +1,85 @@ +/* + * CmdPipe.h + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * Tcl/Tk stuff by Lutz Vieweg + */ + +#ifndef CmdPipe_h +#define CmdPipe_h + +extern "C" { + #include + #include +} + +class Pipe { + +protected: + + int fds[2]; + +public: + + bool fail; + + Pipe(void); + Pipe(int fdin, int fdout) : fail(false) { + fds[0] = fdin; + fds[1] = fdout; + } + ~Pipe(void); + + unsigned long ewrite(const void * buf, unsigned long len); + unsigned long eread (void * buf, unsigned long len); + + int get_read_fd(void) const { + return fds[0]; + } + + int get_write_fd(void) const { + return fds[1]; + } + + int probe(void) const; +}; + +class CmdPipe { + +protected: + + Pipe tocmd; + Pipe fromcmd; + + int childpid; + +public: + + bool fail; + + CmdPipe(const char * command, const char * arg, int nicediff = 0); + ~CmdPipe(void); + + unsigned long ewrite(const void * buf, unsigned long len) { + return tocmd.ewrite(buf, len); + } + + unsigned long eread (void * buf, unsigned long len) { + return fromcmd.eread(buf, len); + } + + int get_read_fd(void) const { + return fromcmd.get_read_fd(); + } + + int get_write_fd(void) const { + return tocmd.get_write_fd(); + } + + int probe(void) const { + return fromcmd.probe(); + } + +}; + +#endif // CmdPipe_h diff --git a/Src/Display.cpp b/Src/Display.cpp new file mode 100644 index 0000000..d2474f3 --- /dev/null +++ b/Src/Display.cpp @@ -0,0 +1,89 @@ +/* + * Display.cpp - C64 graphics display, emulator window handling + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysdeps.h" + +#include "Display.h" +#include "main.h" +#include "Prefs.h" + + +// LED states +enum { + LED_OFF, // LED off + LED_ON, // LED on (green) + LED_ERROR_ON, // LED blinking (red), currently on + LED_ERROR_OFF // LED blinking, currently off +}; + + +#undef USE_THEORETICAL_COLORS + +#ifdef USE_THEORETICAL_COLORS + +// C64 color palette (theoretical values) +const uint8 palette_red[16] = { + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x80, 0xff, 0x40, 0x80, 0x80, 0x80, 0xc0 +}; + +const uint8 palette_green[16] = { + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x80, 0x40, 0x80, 0x40, 0x80, 0xff, 0x80, 0xc0 +}; + +const uint8 palette_blue[16] = { + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x80, 0x40, 0x80, 0x80, 0xff, 0xc0 +}; + +#else + +// C64 color palette (more realistic looking colors) +const uint8 palette_red[16] = { + 0x00, 0xff, 0x99, 0x00, 0xcc, 0x44, 0x11, 0xff, 0xaa, 0x66, 0xff, 0x40, 0x80, 0x66, 0x77, 0xc0 +}; + +const uint8 palette_green[16] = { + 0x00, 0xff, 0x00, 0xff, 0x00, 0xcc, 0x00, 0xff, 0x55, 0x33, 0x66, 0x40, 0x80, 0xff, 0x77, 0xc0 +}; + +const uint8 palette_blue[16] = { + 0x00, 0xff, 0x00, 0xcc, 0xcc, 0x44, 0x99, 0x00, 0x00, 0x00, 0x66, 0x40, 0x80, 0x66, 0xff, 0xc0 +}; + +#endif + + +/* + * Update drive LED display (deferred until Update()) + */ + +void C64Display::UpdateLEDs(int l0, int l1, int l2, int l3) +{ + led_state[0] = l0; + led_state[1] = l1; + led_state[2] = l2; + led_state[3] = l3; +} + + +#if defined(__BEOS__) +#include "Display_Be.i" +#elif defined(AMIGA) +#include "Display_Amiga.i" +#elif defined(HAVE_SDL) +#include "Display_SDL.i" +#elif defined(__unix) +# ifdef __svgalib__ +# include "Display_svga.i" +# else +# include "Display_x.i" +# endif +#elif defined(__mac__) +#include "Display_mac.i" +#elif defined(WIN32) +#include "Display_WIN32.i" +#elif defined(__riscos__) +#include "Display_Acorn.i" +#endif diff --git a/Src/Display.h b/Src/Display.h new file mode 100644 index 0000000..4a3c228 --- /dev/null +++ b/Src/Display.h @@ -0,0 +1,222 @@ +/* + * Display.h - C64 graphics display, emulator window handling + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _DISPLAY_H +#define _DISPLAY_H + +#ifdef __BEOS__ +#include +#endif + +#ifdef AMIGA +#include +#endif + +#ifdef HAVE_SDL +struct SDL_Surface; +#endif + +#ifdef WIN32 +#include +#endif + +#ifdef __riscos__ +#include "ROlib.h" +#endif + + +// Display dimensions +#if defined(SMALL_DISPLAY) +const int DISPLAY_X = 0x168; +const int DISPLAY_Y = 0x110; +#else +const int DISPLAY_X = 0x180; +const int DISPLAY_Y = 0x110; +#endif + + +class C64Window; +class C64Screen; +class C64; +class Prefs; + +// Class for C64 graphics display +class C64Display { +public: + C64Display(C64 *the_c64); + ~C64Display(); + + void Update(void); + void UpdateLEDs(int l0, int l1, int l2, int l3); + void Speedometer(int speed); + uint8 *BitmapBase(void); + int BitmapXMod(void); +#ifdef __riscos__ + void PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick, uint8 *joystick2); +#else + void PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick); +#endif + bool NumLock(void); + void InitColors(uint8 *colors); + void NewPrefs(Prefs *prefs); + + C64 *TheC64; + +#ifdef __BEOS__ + void Pause(void); + void Resume(void); +#endif + +#ifdef __riscos__ + void ModeChange(void); + unsigned int *GetColourTable(void); // returns pointer to mode_cols + bool CheckForUnpause(bool CheckLastState); + + ROScreen *screen; + Joy_Keys JoystickKeys[2]; // it's easier making the joystick keys public +#endif + +#ifdef __unix + bool quit_requested; +#endif + +private: + int led_state[4]; + int old_led_state[4]; + +#ifdef __BEOS__ + C64Window *the_window; // One of these is NULL + C64Screen *the_screen; + bool using_screen; // Flag: Using the_screen + key_info old_key_info; + int draw_bitmap; // Number of bitmap for the VIC to draw into +#endif + +#ifdef AMIGA + void draw_led_bar(void); // Draw LED bar at the bottom of the window + void draw_led(int num, int state); // Draw one LED + + struct Window *the_window; // Pointer to C64 display window + struct Screen *the_screen; // The window's screen + struct RastPort *the_rp; // The window's RastPort + struct VisualInfo *the_visual_info; + struct Menu *the_menus; + struct TextFont *led_font; + struct TextFont *speedo_font; + struct RastPort temp_rp; // For WritePixelArray8() + struct BitMap *temp_bm; + uint8 *chunky_buf; // Chunky buffer for drawing into + LONG pens[16]; // Pens for C64 colors + int xo, yo; // Window X/Y border size + struct FileRequester *open_req, *save_req; // File requesters for load/save snapshot +#endif + +#ifdef HAVE_SDL + char speedometer_string[16]; // Speedometer text + void draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color); +#endif + +#ifdef __unix + void draw_led(int num, int state); // Draw one LED + static void pulse_handler(...); // LED error blinking +#endif + +#ifdef WIN32 +public: + long ShowRequester(const char *str, const char *button1, const char *button2 = NULL); + void WaitUntilActive(); + void NewPrefs(); + void Pause(); + void Resume(); + void Quit(); + + struct DisplayMode { + int x; + int y; + int depth; + BOOL modex; + }; + int GetNumDisplayModes() const; + const DisplayMode *GetDisplayModes() const; + +private: + // Window members. + void ResetKeyboardState(); + BOOL MakeWindow(); + static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + long WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + static int VirtKey2C64(int virtkey, DWORD keydata); + BOOL CalcViewPort(); + BOOL SetupWindow(); + BOOL SetupWindowMode(BOOL full_screen); + BOOL RestoreWindow(); + BOOL ResizeWindow(int side, RECT *pRect); + void WindowTitle(); + void CreateObjects(); + void DeleteObjects(); + + // DirectDraw management members. + BOOL StartDirectDraw(); + BOOL ResumeDirectDraw(); + BOOL ResetDirectDraw(); + BOOL StopDirectDraw(); + static HRESULT CALLBACK EnumModesCallback(LPDDSURFACEDESC pDDSD, LPVOID lpContext); + HRESULT EnumModesCallback(LPDDSURFACEDESC pDDSD); + static int CompareModes(const void *e1, const void *e2); + BOOL Fail(const char *message); + + // DirectDraw worker members. + BOOL SetPalettes(); + BOOL BuildColorTable(); + BOOL CopySurface(RECT &rcWork); + BOOL FlipSurfaces(); + BOOL EraseSurfaces(); + BOOL RestoreSurfaces(); + + void draw_led_bar(void); // Draw LED bar on the window + void draw_leds(BOOL force = false); // Draw LEDs if force or changed + void led_rect(int n, RECT &rc, RECT &led); // Compute LED rectangle + void InsertNextDisk(); // should be a common func + BOOL FileNameDialog(char *prefs_path, BOOL save = false); + void OfferSave(); // Offer chance to save changes + + UBYTE *chunky_buf; // Chunky buffer for drawing + BOOL active; // is application active? + BOOL paused; // is application paused? + BOOL waiting; // is application waiting? + DWORD windowed_style; // style of windowed window + DWORD fullscreen_style; // style of fullscreen window + char failure_message[128]; // what when wrong + int speed_index; // look ma, no hands + BOOL show_leds; // cached prefs option + BOOL full_screen; // cached prefs option + BOOL in_constructor; // if we are being contructed + BOOL in_destructor; // if we are being destroyed + + LPDIRECTDRAW pDD; // DirectDraw object + LPDIRECTDRAWSURFACE pPrimary; // DirectDraw primary surface + LPDIRECTDRAWSURFACE pBack; // DirectDraw back surface + LPDIRECTDRAWSURFACE pWork; // DirectDraw working surface + LPDIRECTDRAWCLIPPER pClipper; // DirectDraw clipper + LPDIRECTDRAWPALETTE pPalette; // DirectDraw palette + + DWORD colors[256]; // our palette colors + int colors_depth; // depth of the colors table +#endif + +#ifdef __riscos__ + unsigned int mode_cols[256]; // Colours in the current mode corresponding to C64's + uint8 *bitmap; + uint32 lastkeys[8]; // bitfield describing keys pressed last time. +#endif +}; + + +// Exported functions +extern long ShowRequester(char *str, char *button1, char *button2 = NULL); + + +#endif diff --git a/Src/Display_Acorn.i b/Src/Display_Acorn.i new file mode 100644 index 0000000..14f558c --- /dev/null +++ b/Src/Display_Acorn.i @@ -0,0 +1,429 @@ +/* + * Display_Acorn.i + * + * Handles redraws and suchlike as well as the keyboard + * Frodo (C) 1994-1997 by Christian Bauer + * Acorn port by Andreas Dehmel, 1997 + * + */ + + +#include "C64.h" +#include "ROlib.h" +#include "AcornGUI.h" +#include "SAM.h" +#include "VIC.h" + + + +// (from Display_x.i) +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + + +#define IntKey_MinCode 3 // Scan from ShiftLeft (leave out Shift, Ctrl, Alt) +#define IntKey_MaxCode 124 +#define IntKey_Copy 105 + +// Maps internal keyboard numbers (Acorn) to C64 keyboard-matrix. +// Format: top nibble - row#, bottom nibble - column (bit#). +// Entry == 0xff <==> don't map +char KeysAcornToCBM[] = { + 0x17, 0x72, 0x75, 0x17, // 0 - 3: SHL, CTRL, ALT(C=), SHL + 0x72, 0x75, 0x64, 0x72, // 4 - 7: CTRL, ALT, SHR, CTRL + 0x75, 0xff, 0xff, 0xff, // 8 - 11: ALT, MouseSlct, MouseMen, MouseAdj + 0xff, 0xff, 0xff, 0xff, // 12 - 15: dummies + 0x76, 0x10, 0x13, 0x20, // 16 - 19: q, 3,4,5 + 0x03, 0x33, 0xff, 0x53, // 20 - 23: F4(F7), 8, F7, - + 0x23, 0x02, 0xff, 0xff, // 24 - 27: 6, crsrL, num6, num7 + 0xff, 0xff, 0xff, 0xff, // 28 - 31: F11, F12, F10, ScrLock + 0xff, 0x11, 0x16, 0x26, // 32 - 35: Print, w, e, t + 0x30, 0x41, 0x40, 0x43, // 36 - 39: 7, i, 9, 0 + 0x53, 0x07, 0xff, 0xff, // 40 - 43: -, crsrD, num8, num9 + 0x77, 0x71, 0x60, 0x00, // 44 - 47: break, `, £, DEL + 0x70, 0x73, 0x22, 0x21, // 48 - 51: 1, 2, d, r + 0x23, 0x36, 0x46, 0x51, // 52 - 55: 6, u, o, p + 0x56, 0x07, 0x50, 0x53, // 56 - 59: [(@), crsrU, num+(+), num-(-) + 0xff, 0x00, 0x63, 0xff, // 60 - 63: numENTER, insert, home, pgUp + 0x17, 0x12, 0x27, 0x25, // 64 - 67: capsLCK, a, x, f + 0x31, 0x42, 0x45, 0x73, // 68 - 71: y, j, k, 2 + 0x55, 0x01, 0xff, 0xff, // 72 - 75: ;(:), RET, num/, dummy + 0xff, 0xff, 0xff, 0x62, // 76 - 79: num., numLCK, pgDown, '(;) + 0xff, 0x15, 0x24, 0x32, // 80 - 83: dummy, s, c, g + 0x35, 0x47, 0x52, 0x55, // 84 - 87: h, n, l, ;(:) + 0x61, 0x00, 0xff, 0xff, // 88 - 91: ](*), Delete, num#, num* + 0xff, 0x65, 0xff, 0xff, // 92 - 95: dummy, =, dummies + 0x72, 0x14, 0x74, 0x37, // 96 - 99: TAB(CTRL), z, SPACE, v + 0x34, 0x44, 0x57, 0x54, // 100-103: b, m, ',', . + 0x67, 0xff, 0xff, 0xff, // 104-107: /, Copy, num0, num1 + 0xff, 0xff, 0xff, 0xff, // 108-111: num3, dummies + 0x77, 0x04, 0x05, 0x06, // 112-115: ESC, F1(F1), F2(F3), F3(F5) + 0xff, 0xff, 0xff, 0xff, // 116-119: F5, F6, F8, F9 + 0x66, 0x02, 0xff, 0xff, // 120-123: \(^), crsrR, num4, num5 + 0xff, 0xff, 0xff, 0xff // 124-127: num2, dummies +}; + + +// Special keycodes that have to be processed seperately: +#define IntKey_CrsrL 25 +#define IntKey_CrsrR 121 +#define IntKey_CrsrU 57 +#define IntKey_CrsrD 41 +#define IntKey_Insert 61 +#define IntKey_NumLock 77 +#define IntKey_F5 116 +#define IntKey_F6 117 +#define IntKey_F7 22 +#define IntKey_F8 118 +#define IntKey_PageUp 63 +#define IntKey_PageDown 78 +#define IntKey_NumSlash 74 +#define IntKey_NumStar 91 +#define IntKey_NumCross 90 + +#define KeyJoy1_Up 108 // num3 +#define KeyJoy1_Down 76 // num. +#define KeyJoy1_Left 107 // num1 +#define KeyJoy1_Right 124 // num2 +#define KeyJoy1_Fire 60 // numReturn +#define KeyJoy2_Up 67 // "f" +#define KeyJoy2_Down 82 // "c" +#define KeyJoy2_Left 97 // "z" +#define KeyJoy2_Right 66 // "x" +#define KeyJoy2_Fire 83 // "g" + + + + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + int i; + + bitmap = new uint8[DISPLAY_X * DISPLAY_Y]; + screen = new ROScreen(); + ModeChange(); + for (i=0; i<8; i++) {lastkeys[i] = 0;} + // First joystick: mapped to port 2 if numLOCK is on, else port 2 + JoystickKeys[0].up = KeyJoy1_Up; JoystickKeys[0].down = KeyJoy1_Down; + JoystickKeys[0].left = KeyJoy1_Left; JoystickKeys[0].right = KeyJoy1_Right; + JoystickKeys[0].fire = KeyJoy1_Fire; + // Second joystick: only active if numLOCK is off! Mapped to port 2 then. + JoystickKeys[1].up = KeyJoy2_Up; JoystickKeys[1].down = KeyJoy2_Down; + JoystickKeys[1].left = KeyJoy2_Left; JoystickKeys[1].right = KeyJoy2_Right; + JoystickKeys[1].fire = KeyJoy2_Fire; +} + + +C64Display::~C64Display(void) +{ + delete bitmap; delete screen; +} + + +void C64Display::ModeChange(void) +{ + register int i; + + screen->ReadMode(); + // find best matching colours in current mode. + switch (screen->ldbpp) + { + case 0: + case 1: + case 2: + case 3: for (i=0; i<16; i++) // for 1,2,4 and 8bpp + { + mode_cols[i] = ModeColourNumber((palette_blue[i] << 24) + (palette_green[i] << 16) + (palette_red[i] << 8)); + } + break; + case 4: for (i=0; i<16; i++) // for 16bpp + { + int r,g,b; + + r = (palette_red[i] + 4) & 0x1f8; if (r > 0xff) {r = 0xf8;} + g = (palette_green[i] + 4) & 0x1f8; if (g > 0xff) {g = 0xf8;} + b = (palette_blue[i] + 4) & 0x1f8; if (b > 0xff) {b = 0xf8;} + mode_cols[i] = (r >> 3) | (g << 2) | (b << 7); + } + break; + case 5: for (i=0; i<16; i++) // for 32bpp + { + mode_cols[i] = palette_red[i] | (palette_green[i] << 8) | (palette_blue[i] << 16); + } + break; + } +} + + +uint8 *C64Display::BitmapBase(void) +{ + return bitmap; +} + + +void C64Display::InitColors(uint8 *colors) +{ + register int i; + + // write index mapping C64colours -> ROcolours + if (screen->ldbpp <= 3) // at most 8bpp ==> use actual colour + { + for (i=0; i<256; i++) {colors[i] = mode_cols[i&15];} + } + else // else use index (takes time but can't be changed... + { + for (i=0; i<256; i++) {colors[i] = i&15;} + } +} + + +int C64Display::BitmapXMod(void) +{ + return DISPLAY_X; +} + + +// This routine reads the raw keyboard data from the host machine. Not entirely +// conformant with Acorn's rules but the only way to detect multiple simultaneous +// keypresses. +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick, uint8 *joystick2) +{ + register int scan_from=IntKey_MinCode, code, row, col; + int status; + uint8 kjoy, kjoy2; + uint32 newkeys[8]; + UBYTE kjoy, kjoy2; + + // Clear keyboard + for (code=0; code<8; code++) {key_matrix[code] = 0xff; rev_matrix[code] = 0xff; newkeys[code] = 0;} + kjoy = kjoy2 = 0xff; + status = ReadKeyboardStatus(); + if ((status & 16) == 0) {key_matrix[1] &= 0x7f; rev_matrix[7] &= 0xfd;} // Caps lock + + while (scan_from <= IntKey_MaxCode) + { + if ((code = ScanKeys(scan_from)) != 0xff) + { + newkeys[code >> 5] |= (1 << (code & 0x1f)); // update keys pressed + row = KeysAcornToCBM[code]; + if ((status & 4) != 0) // numLOCK off? ==> check for 2nd keyboard joystick too + { + if (code == JoystickKeys[1].up) {kjoy2 &= 0xfe; row = 0xff;} + else if (code == JoystickKeys[1].down) {kjoy2 &= 0xfd; row = 0xff;} + else if (code == JoystickKeys[1].left) {kjoy2 &= 0xfb; row = 0xff;} + else if (code == JoystickKeys[1].right) {kjoy2 &= 0xf7; row = 0xff;} + else if (code == JoystickKeys[1].fire) {kjoy2 &= 0xef; row = 0xff;} + } + // check 1st keyboard joystick + if (code == JoystickKeys[0].up) {kjoy &= 0xfe; row = 0xff;} + else if (code == JoystickKeys[0].down) {kjoy &= 0xfd; row = 0xff;} + else if (code == JoystickKeys[0].left) {kjoy &= 0xfb; row = 0xff;} + else if (code == JoystickKeys[0].right) {kjoy &= 0xf7; row = 0xff;} + else if (code == JoystickKeys[0].fire) {kjoy &= 0xef; row = 0xff;} + + // If key not mapped to joystick: try mapping to keyboard + if (row != 0xff) + { + col = row & 7; row >>= 4; + key_matrix[row] &= ~(1<Pause(); SAM(TheC64); TheC64->Resume(); + } + break; + case IntKey_F7: TheC64->NMI(); break; + case IntKey_F8: TheC64->Reset(); break; + default: break; + } + + // These shouldn't auto-repeat, therefore I check them seperately. + if ((lastkeys[code >> 5] & (1 << (code & 0x1f))) == 0) + { + // Icons should be updated, not force-redrawed (--> single tasking) + switch (code) + { + // decrease framerate + case IntKey_PageUp: + TheC64->TheWIMP->PrefsWindow-> + WriteIconNumberU(Icon_Prefs_SkipFText,++ThePrefs.SkipFrames); + break; + // increase framerate + case IntKey_PageDown: if (ThePrefs.SkipFrames > 0) + { + TheC64->TheWIMP->PrefsWindow-> + WriteIconNumberU(Icon_Prefs_SkipFText,--ThePrefs.SkipFrames); + } + break; + // toggle floppy emulation status + case IntKey_NumSlash: + { + register int eor, i; + Prefs *prefs = new Prefs(ThePrefs); + + // If Emulation active then ungrey icons now, else grey them + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->TheWIMP->SetLEDIcons(prefs->Emul1541Proc); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + // Show change in prefs window too + TheC64->TheWIMP->PrefsWindow-> + SetIconState(Icon_Prefs_Emul1541,(prefs->Emul1541Proc)?IFlg_Slct:0,IFlg_Slct); + delete prefs; + } + break; + // toggle speed limiter + case IntKey_NumStar: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + TheC64->TheWIMP->SetSpeedLimiter(ThePrefs.LimitSpeed); + break; + // toggle sound emulation + case IntKey_F5: + { + Window *pw = TheC64->TheWIMP->PrefsWindow; + int i, j; + Prefs *prefs = new Prefs(ThePrefs); + + if (prefs->SIDType == SIDTYPE_NONE) {prefs->SIDType = SIDTYPE_DIGITAL; i = 1;} + else {prefs->SIDType = SIDTYPE_NONE; i = 0;} + for (j=0; j<3; j++) + { + pw->SetIconState(SIDtoIcon[j], (j==i) ? IFlg_Slct : 0, IFlg_Slct); + } + TheC64->TheWIMP->SoundWindow-> + SetIconState(Icon_Sound_Notes, (i==0) ? IFlg_Grey : 0, IFlg_Grey); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } + break; + case IntKey_Copy: TheC64->Pause(); + TheC64->TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Pause,PANE_TEXT_RESUME); break; + default: break; + } + } + } + scan_from = code+1; + } + for (code=0; code<8; code++) {lastkeys[code] = newkeys[code];} + *joystick = kjoy; *joystick2 = kjoy2; +} + + +bool C64Display::NumLock(void) +{ + return(((ReadKeyboardStatus() & 4) == 0) ? true : false); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +void C64Display::Update(void) +{ + int i, state; + int *ic; + + // Do a redraw of the emulator window + TheC64->TheWIMP->UpdateEmuWindow(); + + // Update the LEDs if necessary + for (i=0; i<4; i++) + { + if ((state = led_state[i]) != old_led_state[i]) + { + ic = (int*)TheC64->TheWIMP->EmuPane->GetIcon(LEDtoIcon[i]); + switch(state) + { + case LED_OFF: + case LED_ERROR_OFF: + sprintf((char*)ic[5],"led_off"); break; + case LED_ON: + sprintf((char*)ic[5],"led_on"); break; + case LED_ERROR_ON: + sprintf((char*)ic[5],"led_error"); break; + } + TheC64->TheWIMP->EmuPane->UpdateIcon(LEDtoIcon[i]); // update, not force-redraw! + old_led_state[i] = state; + } + } +} + + +unsigned int *C64Display::GetColourTable(void) +{ + return (mode_cols); +} + + +// Check whether unpause-key (copy) is pressed +bool C64Display::CheckForUnpause(bool CheckLastState) +{ + int scan_from = IntKey_MinCode, code; + uint32 newkeys[8]; + uint32 lastpause; + + for (code=0; code<8; code++) {newkeys[code] = 0;} + + while (scan_from <= IntKey_MaxCode) + { + if ((code = ScanKeys(scan_from)) != 0xff) + { + newkeys[code >> 5] |= (1 << (code & 0x1f)); + } + scan_from = code+1; + } + lastpause = lastkeys[IntKey_Copy >> 5] & (1 << (IntKey_Copy & 0x1f)); + for (code=0; code<8; code++) {lastkeys[code] = newkeys[code];} + // unpause-key pressed? + if ((newkeys[IntKey_Copy >> 5] & (1 << (IntKey_Copy & 0x1f))) != 0) + { + if ((lastpause == 0) || !CheckLastState) + { + TheC64->Resume(); + TheC64->TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Pause,PANE_TEXT_PAUSE); + return(true); + } + } + return(false); +} + + +// Requester dialogue box +long ShowRequester(char *str, char *button1, char *button2) +{ + _kernel_oserror myerr; + + myerr.errnum = 0x0; strcpy(myerr.errmess,str); + Wimp_ReportError(&myerr,1,TASKNAME); // always provide an OK box + return(1); +} diff --git a/Src/Display_Amiga.i b/Src/Display_Amiga.i new file mode 100644 index 0000000..c846b68 --- /dev/null +++ b/Src/Display_Amiga.i @@ -0,0 +1,627 @@ +/* + * Display_Amiga.i - C64 graphics display, emulator window handling, + * Amiga specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "C64.h" +#include "SAM.h" +#include "Version.h" + + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + + +/* + Tables for key translation + Bit 0..2: row/column in C64 keyboard matrix + Bit 3 : implicit shift + Bit 5 : joystick emulation (bit 0..4: mask) +*/ + +const int key_byte[128] = { + 7, 7, 7, 1, 1, 2, 2, 3, + 3, 4, 4, 5, 5, 6, -1,0x30, + 7, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 6, -1,0x26,0x22,0x2a, + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, -1,0x24,0x30,0x28, + 6, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, -1, -1,0x25,0x21,0x29, + 7, 0, -1, 0, 0, 7, 6, -1, + -1, -1, -1, -1,8+0, 0, 0, 8+0, + 0,8+0, 0,8+0, 0, 8+0, 0, 8+0, + -1, -1, 6, 6, -1, -1, -1, -1, + 1, 6, 1, 7, 7, 7, 7, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +const int key_bit[128] = { + 1, 0, 3, 0, 3, 0, 3, 0, + 3, 0, 3, 0, 3, 0, -1, -1, + 6, 1, 6, 1, 6, 1, 6, 1, + 6, 1, 6, 1, -1, -1, -1, -1, + 2, 5, 2, 5, 2, 5, 2, 5, + 2, 5, 2, 5, -1, -1, -1, -1, + 6, 4, 7, 4, 7, 4, 7, 4, + 7, 4, 7, -1, -1, -1, -1, -1, + 4, 0, -1, 1, 1, 7, 3, -1, + -1, -1, -1, -1, 7, 7, 2, 2, + 4, 4, 5, 5, 6, 6, 3, 3, + -1, -1, 6, 5, -1, -1, -1, -1, + 7, 4, 7, 2, 5, 5, 5, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +/* + * Menu definitions + */ + +const struct NewMenu new_menus[] = { + NM_TITLE, "Frodo", NULL, 0, 0, NULL, + NM_ITEM, "About Frodo...", NULL, 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Preferences...", "P", 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Reset C64", NULL, 0, 0, NULL, + NM_ITEM, "Insert next disk", "D", 0, 0, NULL, + NM_ITEM, "SAM...", "M", 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Load snapshot...", "O", 0, 0, NULL, + NM_ITEM, "Save snapshot...", "S", 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Quit Frodo", "Q", 0, 0, NULL, + NM_END, NULL, NULL, 0, 0, NULL +}; + + +/* + * Font attributes + */ + +const struct TextAttr led_font_attr = { + "Helvetica.font", 11, FS_NORMAL, 0 +}; + +const struct TextAttr speedo_font_attr = { + "Courier.font", 11, FS_NORMAL, 0 +}; + + +/* + * Display constructor: Create window/screen + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + int i; + + // LEDs off + for (i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Allocate chunky buffer to draw into + chunky_buf = new UBYTE[DISPLAY_X * DISPLAY_Y]; + + // Open fonts + led_font = OpenDiskFont(&led_font_attr); + speedo_font = OpenDiskFont(&speedo_font_attr); + + // Open window on default pubscreen + the_window = OpenWindowTags(NULL, + WA_Left, 0, + WA_Top, 0, + WA_InnerWidth, DISPLAY_X, + WA_InnerHeight, DISPLAY_Y + 16, + WA_Title, (ULONG)"Frodo", + WA_ScreenTitle, (ULONG)"Frodo C64 Emulator", + WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_REFRESHWINDOW, + WA_DragBar, TRUE, + WA_DepthGadget, TRUE, + WA_CloseGadget, TRUE, + WA_SimpleRefresh, TRUE, + WA_Activate, TRUE, + WA_NewLookMenus, TRUE, + TAG_DONE); + the_screen = the_window->WScreen; + the_rp = the_window->RPort; + xo = the_window->BorderLeft; + yo = the_window->BorderTop; + + // Create menus + the_visual_info = GetVisualInfo(the_screen, NULL); + the_menus = CreateMenus(new_menus, GTMN_FullMenu, TRUE, TAG_DONE); + LayoutMenus(the_menus, the_visual_info, GTMN_NewLookMenus, TRUE, TAG_DONE); + SetMenuStrip(the_window, the_menus); + + // Obtain 16 pens for the C64 colors + for (i=0; i<16; i++) + pens[i] = ObtainBestPen(the_screen->ViewPort.ColorMap, + palette_red[i] * 0x01010101, palette_green[i] * 0x01010101, palette_blue[i] * 0x01010101); + + // Allocate temporary RastPort for WritePixelArra8() + temp_bm = AllocBitMap(DISPLAY_X, 1, 8, 0, NULL); + InitRastPort(&temp_rp); + temp_rp.BitMap = temp_bm; + + // Draw LED bar + draw_led_bar(); + + // Allocate file requesters + open_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)the_window, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Load snapshot...", + ASLFR_RejectIcons, TRUE, + TAG_DONE); + save_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)the_window, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Save snapshot...", + ASLFR_DoSaveMode, TRUE, + ASLFR_RejectIcons, TRUE, + TAG_DONE); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + // Free file requesters + if (open_req != NULL) + FreeAslRequest(open_req); + if (save_req != NULL) + FreeAslRequest(save_req); + + // Free temporary RastPort + if (temp_bm != NULL) + FreeBitMap(temp_bm); + + // Free pens + for (int i=0; i<16; i++) + ReleasePen(the_screen->ViewPort.ColorMap, pens[i]); + + // Delete menus + if (the_menus != NULL) { + if (the_window != NULL) + ClearMenuStrip(the_window); + FreeMenus(the_menus); + } + + // Delete VisualInfo + if (the_visual_info != NULL) + FreeVisualInfo(the_visual_info); + + // Close window + if (the_window != NULL) + CloseWindow(the_window); + + // Close fonts + CloseFont(speedo_font); + CloseFont(led_font); + + // Free chunky buffer + delete chunky_buf; +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + // Update C64 display + WritePixelArray8(the_rp, xo, yo, DISPLAY_X + xo - 1, DISPLAY_Y + yo - 1, + chunky_buf, &temp_rp); + + // Update drive LEDs + for (int i=0; i<4; i++) + if (led_state[i] != old_led_state[i]) { + draw_led(i, led_state[i]); + old_led_state[i] = led_state[i]; + } +} + + +/* + * Draw LED bar at the bottom of the window + */ + +void C64Display::draw_led_bar(void) +{ + int i; + char str[16]; + + SetAPen(the_rp, pens[15]); // Light gray + SetBPen(the_rp, pens[15]); // Light gray + RectFill(the_rp, xo, yo+DISPLAY_Y, xo+DISPLAY_X-1, yo+DISPLAY_Y+15); + + SetAPen(the_rp, pens[1]); // White + Move(the_rp, xo, yo+DISPLAY_Y); Draw(the_rp, xo+DISPLAY_X-1, yo+DISPLAY_Y); + for (i=0; i<5; i++) { + Move(the_rp, xo+DISPLAY_X*i/5, yo+DISPLAY_Y); Draw(the_rp, xo+DISPLAY_X*i/5, yo+DISPLAY_Y+14); + } + for (i=2; i<6; i++) { + Move(the_rp, xo+DISPLAY_X*i/5-23, yo+DISPLAY_Y+11); Draw(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+11); + Move(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+11); Draw(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+5); + } + + SetAPen(the_rp, pens[12]); // Medium gray + Move(the_rp, xo, yo+DISPLAY_Y+15); Draw(the_rp, xo+DISPLAY_X-1, yo+DISPLAY_Y+15); + for (i=1; i<6; i++) { + Move(the_rp, xo+DISPLAY_X*i/5-1, yo+DISPLAY_Y+1); Draw(the_rp, xo+DISPLAY_X*i/5-1, yo+DISPLAY_Y+15); + } + for (i=2; i<6; i++) { + Move(the_rp, xo+DISPLAY_X*i/5-24, yo+DISPLAY_Y+11); Draw(the_rp, xo+DISPLAY_X*i/5-24, yo+DISPLAY_Y+4); + Move(the_rp, xo+DISPLAY_X*i/5-24, yo+DISPLAY_Y+4); Draw(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+4); + } + + SetFont(the_rp, led_font); + for (i=0; i<4; i++) { + sprintf(str, "Drive %d", i+8); + SetAPen(the_rp, pens[0]); // Black + Move(the_rp, xo+DISPLAY_X*(i+1)/5+8, yo+DISPLAY_Y+11); + Text(the_rp, str, strlen(str)); + draw_led(i, LED_OFF); + } +} + + +/* + * Draw one LED + */ + +void C64Display::draw_led(int num, int state) +{ + switch (state) { + case LED_OFF: + case LED_ERROR_OFF: + SetAPen(the_rp, pens[0]); // Black; + break; + case LED_ON: + SetAPen(the_rp, pens[5]); // Green + break; + case LED_ERROR_ON: + SetAPen(the_rp, pens[2]); // Red + break; + } + RectFill(the_rp, xo+DISPLAY_X*(num+2)/5-23, yo+DISPLAY_Y+5, xo+DISPLAY_X*(num+2)/5-10, yo+DISPLAY_Y+10); +} + + +/* + * Update speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) { + char str[16]; + sprintf(str, "%d%%", speed); + SetAPen(the_rp, pens[15]); // Light gray + RectFill(the_rp, xo+1, yo+DISPLAY_Y+1, xo+DISPLAY_X/5-2, yo+DISPLAY_Y+14); + SetAPen(the_rp, pens[0]); // Black + SetFont(the_rp, speedo_font); + Move(the_rp, xo+24, yo+DISPLAY_Y+10); + Text(the_rp, str, strlen(str)); + delay = 0; + } else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +UBYTE *C64Display::BitmapBase(void) +{ + return chunky_buf; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + return DISPLAY_X; +} + + +/* + * Handle IDCMP messages + */ + +void C64Display::PollKeyboard(UBYTE *key_matrix, UBYTE *rev_matrix, UBYTE *joystick) +{ + struct IntuiMessage *msg; + + // Get and analyze all pending window messages + while ((msg = (struct IntuiMessage *)GetMsg(the_window->UserPort)) != NULL) { + + // Extract data and reply message + ULONG iclass = msg->Class; + USHORT code = msg->Code; + ReplyMsg((struct Message *)msg); + + // Action depends on message class + switch (iclass) { + + case IDCMP_CLOSEWINDOW: // Closing the window quits Frodo + TheC64->Quit(); + break; + + case IDCMP_RAWKEY: + switch (code) { + + case 0x58: // F9: NMI (Restore) + TheC64->NMI(); + break; + + case 0x59: // F10: Reset + TheC64->Reset(); + break; + + case 0x5e: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case 0x4a: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case 0x5d: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case 0x5c:{ // '/' on keypad: Toggle processor-level 1541 emulation + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + break; + } + + default:{ + // Convert Amiga keycode to C64 row/column + int c64_byte = key_byte[code & 0x7f]; + int c64_bit = key_bit[code & 0x7f]; + + if (c64_byte != -1) { + if (!(c64_byte & 0x20)) { + + // Normal keys + bool shifted = c64_byte & 8; + c64_byte &= 7; + if (!(code & 0x80)) { + + // Key pressed + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } else { + + // Key released + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } + } else { + + // Joystick emulation + c64_byte &= 0x1f; + if (code & 0x80) + *joystick |= c64_byte; + else + *joystick &= ~c64_byte; + } + } + } + } + break; + + case IDCMP_MENUPICK:{ + if (code == MENUNULL) + break; + + // Get item number + int item_number = ITEMNUM(code); + switch (item_number) { + + case 0: { // About Frodo + TheC64->Pause(); + char str[256]; + sprintf(str, "%s by Christian Bauer\n\n© Copyright 1994-1997\nFreely distributable", VERSION_STRING); + ShowRequester(str, "OK"); + TheC64->Resume(); + break; + } + + case 2: // Preferences + TheC64->Pause(); + be_app->RunPrefsEditor(); + TheC64->Resume(); + break; + + case 4: // Reset C64 + TheC64->Reset(); + break; + + case 5: // Insert next disk + if (strlen(ThePrefs.DrivePath[0]) > 4) { + char str[256]; + strcpy(str, ThePrefs.DrivePath[0]); + char *p = str + strlen(str) - 5; + + // If path matches "*.?64", increment character before the '.' + if (p[1] == '.' && p[3] == '6' && p[4] == '4') { + p[0]++; + + // If no such file exists, set character before the '.' to '1', 'a' or 'A' + FILE *file; + if ((file = fopen(str, "rb")) == NULL) { + if (isdigit(p[0])) + p[0] = '1'; + else if (isupper(p[0])) + p[0] = 'A'; + else + p[0] = 'a'; + } else + fclose(file); + + // Set new prefs + Prefs *prefs = new Prefs(ThePrefs); + strcpy(prefs->DrivePath[0], str); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } + } + break; + + case 6: // SAM + TheC64->Pause(); + SAM(TheC64); + TheC64->Resume(); + break; + + case 8: // Load snapshot + if (open_req != NULL && AslRequest(open_req, NULL)) { + char path[256]; + strncpy(path, open_req->fr_Drawer, 255); + AddPart(path, open_req->fr_File, 255); + TheC64->Pause(); + TheC64->LoadSnapshot(path); + TheC64->Resume(); + } + break; + + case 9: // Save snapshot + if (save_req != NULL && AslRequest(save_req, NULL)) { + char path[256]; + strncpy(path, save_req->fr_Drawer, 255); + AddPart(path, save_req->fr_File, 255); + TheC64->Pause(); + TheC64->SaveSnapshot(path); + TheC64->Resume(); + } + break; + + case 11: // Quit Frodo + TheC64->Quit(); + break; + } + break; + } + + case IDCMP_REFRESHWINDOW: + BeginRefresh(the_window); + draw_led_bar(); + EndRefresh(the_window, TRUE); + break; + } + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return FALSE; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(UBYTE *colors) +{ + // Spread pens into colors array + for (int i=0; i<256; i++) + colors[i] = pens[i & 0x0f]; +} + + +/* + * Show a requester + */ + +long ShowRequester(char *str, char *button1, char *button2) +{ + struct EasyStruct es; + char gads[256]; + + strcpy(gads, button1); + if (button2) { + strcat(gads, "|"); + strcat(gads, button2); + } + + es.es_StructSize = sizeof(struct EasyStruct); + es.es_Flags = 0; + es.es_Title = "Frodo"; + es.es_TextFormat = str; + es.es_GadgetFormat = gads; + + return EasyRequestArgs(NULL, &es, NULL, NULL) % 1; +} diff --git a/Src/Display_Be.i b/Src/Display_Be.i new file mode 100644 index 0000000..214b849 --- /dev/null +++ b/Src/Display_Be.i @@ -0,0 +1,959 @@ +/* + * Display_Be.i - C64 graphics display, emulator window handling, + * Be specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * GameKit stuff by Tinic Urou + */ + +#include +#include +#include +#include + +#include "C64.h" +#include "main.h" + + +// Window thread messages +const uint32 MSG_REDRAW = 1; + + +// C64 display and window frame +const BRect DisplayFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1); +const BRect WindowFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1 + 16); + + +// Background color +const rgb_color fill_gray = {208, 208, 208, 0}; +const rgb_color shine_gray = {232, 232, 232, 0}; +const rgb_color shadow_gray = {152, 152, 152, 0}; + + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + + +/* + Tables for key translation + Bit 0..2: row/column in C64 keyboard matrix + Bit 3 : implicit shift + Bit 5 : joystick emulation (bit 0..4: mask) +*/ + +const int key_byte[128] = { + -1, 7, 0,8+0, 0,8+0, 0, 8+0, + 0, 8+0, -1, -1, -1, -1, -1, -1, + + 7, 7, 7, 7, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 0, 8+0, + + 6, 6, -1, -1, -1, -1, -1, 7, + 1, 1, 2, 2, 3, 3, 4, 4, + + 5, 5, 6, 6, 0, 6, 6,0x25, + 0x21,0x29, -1, 1, 1, 1, 2, 2, + + 3, 3, 4, 4, 5, 5, 6, 0, + 0x24,0x30,0x28, 1, 1, 2, 2, 3, + + 3, 4, 4, 5, 5, 6, 6, 8+0, + 0x26,0x22,0x2a, 0, 7, -1, 7, -1, + + 7, 8+0, 0, 0,0x30, -1, 7, 7, + -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +const int key_bit[128] = { + -1, 7, 4, 4, 5, 5, 6, 6, + 3, 3, -1, -1, -1, -1, -1, -1, + + 7, 1, 0, 3, 0, 3, 0, 3, + 0, 3, 0, 3, 0, 3, 0, 0, + + 3, 0, -1, -1, -1, -1, -1, 6, + 1, 6, 1, 6, 1, 6, 1, 6, + + 1, 6, 1, 6, 0, 0, 5, -1, + -1, -1, -1, 7, 2, 5, 2, 5, + + 2, 5, 2, 5, 2, 5, 2, 1, + -1, -1, -1, 7, 4, 7, 4, 7, + + 4, 7, 4, 7, 4, 7, 4, 7, + -1, -1, -1, 1, 2, -1, 4, -1, + + 5, 2, 7, 2, -1, -1, 5, 5, + -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +/* + * A simple view class for blitting a bitmap on the screen + */ + +class BitmapView : public BView { +public: + BitmapView(BRect frame, BBitmap *bitmap); + virtual void Draw(BRect update); + virtual void KeyDown(const char *bytes, int32 numBytes); + void ChangeBitmap(BBitmap *bitmap); + +private: + BBitmap *the_bitmap; +}; + + +/* + * Class for the main C64 display window + */ + +class SpeedoView; +class LEDView; + +class C64Window : public BWindow { +public: + C64Window(); + + virtual bool QuitRequested(void); + virtual void MessageReceived(BMessage *msg); + + BBitmap *TheBitmap[2]; + SpeedoView *Speedometer; + LEDView *LED[4]; + +private: + BitmapView *main_view; +}; + + +/* + * Class for the main C64 display using the GameKit + */ + +class C64Screen : public BWindowScreen { +public: + C64Screen(C64Display *display) : BWindowScreen("Frodo", B_8_BIT_640x480, &error), the_display(display) + { + Lock(); + BitmapView *main_view = new BitmapView(Bounds(), NULL); + AddChild(main_view); + main_view->MakeFocus(); + Connected = false; + Unlock(); + } + + virtual void ScreenConnected(bool active); + virtual void DispatchMessage(BMessage *msg, BHandler *handler); + void DrawLED(int i, int state); + void DrawSpeedometer(void); + void FillRect(int x1, int y1, int x2, int y2, int color); + + bool Connected; // Flag: screen connected + int Speed; + char SpeedoStr[16]; // Speedometer value converted to a string + +private: + C64Display *the_display; + status_t error; +}; + + +/* + * Class for speedometer + */ + +class SpeedoView : public BView { +public: + SpeedoView(BRect frame); + virtual void Draw(BRect update); + virtual void Pulse(void); + void SetValue(int percent); + +private: + char speedostr[16]; // Speedometer value converted to a string + BRect bounds; +}; + + +/* + * Class for drive LED + */ + +class LEDView : public BView { +public: + LEDView(BRect frame, const char *label); + virtual void Draw(BRect update); + virtual void Pulse(void); + void DrawLED(void); + void SetState(int state); + +private: + int current_state; + const char *the_label; + BRect bounds; +}; + + +/* + * Display constructor: Create window/screen + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + // LEDs off + for (int i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Open window/screen + draw_bitmap = 1; + if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { + using_screen = true; + the_screen = new C64Screen(this); + the_screen->Show(); + while (!the_screen->Connected) + snooze(20000); + } else { + using_screen = false; + the_window = new C64Window(); + the_window->Show(); + } + + // Prepare key_info buffer + get_key_info(&old_key_info); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + if (using_screen) { + the_screen->Lock(); + the_screen->Quit(); + } else { + the_window->Lock(); + the_window->Quit(); + } +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ + if (prefs->DisplayType == DISPTYPE_SCREEN) { + if (!using_screen) { + // Switch to full screen display + using_screen = true; + the_window->Lock(); + the_window->Quit(); + the_screen = new C64Screen(this); + the_screen->Show(); + while (!the_screen->Connected) + snooze(20000); + } + } else { + if (using_screen) { + // Switch to window display + using_screen = false; + the_screen->Lock(); + the_screen->Quit(); + the_window = new C64Window(); + the_window->Show(); + } + } +} + + +/* + * Redraw bitmap (let the window thread do it) + */ + +void C64Display::Update(void) +{ + if (using_screen) { + + // Update LEDs/speedometer + for (int i=0; i<4; i++) + the_screen->DrawLED(i, led_state[i]); + the_screen->DrawSpeedometer(); + + } else { + + // Update C64 display + BMessage msg(MSG_REDRAW); + msg.AddInt32("bitmap", draw_bitmap); + the_window->PostMessage(&msg); + draw_bitmap ^= 1; + + // Update LEDs + for (int i=0; i<4; i++) + if (led_state[i] != old_led_state[i]) { + the_window->LED[i]->SetState(led_state[i]); + old_led_state[i] = led_state[i]; + } + } +} + + +/* + * Set value displayed by the speedometer + */ + +void C64Display::Speedometer(int speed) +{ + if (using_screen) { + the_screen->Speed = speed; + sprintf(the_screen->SpeedoStr, "%3d%%", speed); + } else + the_window->Speedometer->SetValue(speed); +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + if (using_screen) + return (uint8 *)the_screen->CardInfo()->frame_buffer; + else + return (uint8 *)the_window->TheBitmap[draw_bitmap]->Bits(); +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + if (using_screen) + return the_screen->CardInfo()->bytes_per_row; + else + return the_window->TheBitmap[draw_bitmap]->BytesPerRow(); +} + + +/* + * Poll the keyboard + */ + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + key_info the_key_info; + int be_code, be_byte, be_bit, c64_byte, c64_bit; + bool shifted; + + // Window must be active, command key must be up + if (using_screen) { + if (!the_screen->Connected) + return; + } else + if (!the_window->IsActive()) + return; + if (!(modifiers() & B_COMMAND_KEY)) { + + // Read the state of all keys + get_key_info(&the_key_info); + + // Did anything change at all? + if (!memcmp(&old_key_info, &the_key_info, sizeof(key_info))) + return; + + // Loop to convert BeOS keymap to C64 keymap + for (be_code=0; be_code<0x68; be_code++) { + be_byte = be_code >> 3; + be_bit = 1 << (~be_code & 7); + + // Key state changed? + if ((the_key_info.key_states[be_byte] & be_bit) + != (old_key_info.key_states[be_byte] & be_bit)) { + + c64_byte = key_byte[be_code]; + c64_bit = key_bit[be_code]; + if (c64_byte != -1) { + if (!(c64_byte & 0x20)) { + + // Normal keys + shifted = c64_byte & 8; + c64_byte &= 7; + if (the_key_info.key_states[be_byte] & be_bit) { + + // Key pressed + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } else { + + // Key released + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } + } else { + + // Joystick emulation + c64_byte &= 0x1f; + if (the_key_info.key_states[be_byte] & be_bit) + *joystick &= ~c64_byte; + else + *joystick |= c64_byte; + } + } + } + } + + old_key_info = the_key_info; + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return modifiers() & B_NUM_LOCK; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + BScreen scr(using_screen ? (BWindow *)the_screen : the_window); + for (int i=0; i<256; i++) + colors[i] = scr.IndexForColor(palette_red[i & 0x0f], palette_green[i & 0x0f], palette_blue[i & 0x0f]); +} + + +/* + * Pause display (GameKit only) + */ + +void C64Display::Pause(void) +{ + if (using_screen) + the_screen->Hide(); +} + + +/* + * Resume display (GameKit only) + */ + +void C64Display::Resume(void) +{ + if (using_screen) + the_screen->Show(); +} + + +/* + * Window constructor + */ + +C64Window::C64Window() : BWindow(WindowFrame, "Frodo", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE) +{ + // Move window to right position + Lock(); + MoveTo(80, 60); + + // Set up menus + BMenuBar *bar = new BMenuBar(Bounds(), ""); + BMenu *menu = new BMenu("Frodo"); + menu->AddItem(new BMenuItem("About Frodo" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MSG_PREFS), 'P')); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Reset C64", new BMessage(MSG_RESET))); + menu->AddItem(new BMenuItem("Insert next disk", new BMessage(MSG_NEXTDISK), 'D')); + menu->AddItem(new BMenuItem("SAM" B_UTF8_ELLIPSIS, new BMessage(MSG_SAM), 'M')); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Load snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_OPEN_SNAPSHOT), 'O')); + menu->AddItem(new BMenuItem("Save snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_SAVE_SNAPSHOT), 'S')); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Quit Frodo", new BMessage(B_QUIT_REQUESTED), 'Q')); + menu->SetTargetForItems(be_app); + bar->AddItem(menu); + AddChild(bar); + SetKeyMenuBar(bar); + int mbar_height = bar->Frame().bottom + 1; + + // Resize window to fit menu bar + ResizeBy(0, mbar_height); + + // Allocate bitmaps + TheBitmap[0] = new BBitmap(DisplayFrame, B_COLOR_8_BIT); + TheBitmap[1] = new BBitmap(DisplayFrame, B_COLOR_8_BIT); + + // Create top view + BRect b = Bounds(); + BView *top = new BView(BRect(0, mbar_height, b.right, b.bottom), "top", B_FOLLOW_NONE, 0); + AddChild(top); + + // Create bitmap view + main_view = new BitmapView(DisplayFrame, TheBitmap[0]); + top->AddChild(main_view); + main_view->MakeFocus(); + + // Create speedometer + Speedometer = new SpeedoView(BRect(0, DISPLAY_Y, DISPLAY_X/5-1, DISPLAY_Y+15)); + top->AddChild(Speedometer); + + // Create drive LEDs + LED[0] = new LEDView(BRect(DISPLAY_X/5, DISPLAY_Y, DISPLAY_X*2/5-1, DISPLAY_Y+15), "Drive 8"); + top->AddChild(LED[0]); + LED[1] = new LEDView(BRect(DISPLAY_X*2/5, DISPLAY_Y, DISPLAY_X*3/5-1, DISPLAY_Y+15), "Drive 9"); + top->AddChild(LED[1]); + LED[2] = new LEDView(BRect(DISPLAY_X*3/5, DISPLAY_Y, DISPLAY_X*4/5-1, DISPLAY_Y+15), "Drive 10"); + top->AddChild(LED[2]); + LED[3] = new LEDView(BRect(DISPLAY_X*4/5, DISPLAY_Y, DISPLAY_X-1, DISPLAY_Y+15), "Drive 11"); + top->AddChild(LED[3]); + + // Set pulse rate to 0.4 seconds for blinking drive LEDs + SetPulseRate(400000); + Unlock(); +} + + +/* + * Closing the window quits Frodo + */ + +bool C64Window::QuitRequested(void) +{ + be_app->PostMessage(B_QUIT_REQUESTED); + return false; +} + + +/* + * Handles redraw messages + */ + +void C64Window::MessageReceived(BMessage *msg) +{ + BMessage *msg2; + + switch (msg->what) { + case MSG_REDRAW: // Redraw bitmap + MessageQueue()->Lock(); + while ((msg2 = MessageQueue()->FindMessage(MSG_REDRAW, 0)) != NULL) + MessageQueue()->RemoveMessage(msg2); + MessageQueue()->Unlock(); + main_view->ChangeBitmap(TheBitmap[msg->FindInt32("bitmap")]); + Lock(); + main_view->Draw(DisplayFrame); + Unlock(); + break; + + default: + BWindow::MessageReceived(msg); + } +} + + +/* + * Workspace activated/deactivated + */ + +void C64Screen::ScreenConnected(bool active) +{ + if (active) { + FillRect(0, 0, 639, 479, 0); // Clear screen + the_display->TheC64->Resume(); + Connected = true; + } else { + the_display->TheC64->Pause(); + Connected = false; + } + BWindowScreen::ScreenConnected(active); +} + + +/* + * Simulate menu commands + */ + +void C64Screen::DispatchMessage(BMessage *msg, BHandler *handler) +{ + switch (msg->what) { + case B_KEY_DOWN: { + uint32 mods = msg->FindInt32("modifiers"); + if (mods & B_COMMAND_KEY) { + uint32 key = msg->FindInt32("raw_char"); + switch (key) { + case 'p': + be_app->PostMessage(MSG_PREFS); + break; + case 'd': + be_app->PostMessage(MSG_NEXTDISK); + break; + case 'm': + be_app->PostMessage(MSG_SAM); + break; + } + } + BWindowScreen::DispatchMessage(msg, handler); + break; + } + + default: + BWindowScreen::DispatchMessage(msg, handler); + } +} + + +/* + * Draw drive LEDs + */ + +void C64Screen::DrawLED(int i, int state) +{ + switch (state) { + case LED_ON: + FillRect(10+i*20, DISPLAY_Y-20, 20+i*20, DISPLAY_Y-12, 54); + break; + case LED_ERROR_ON: + FillRect(10+i*20, DISPLAY_Y-20, 20+i*20, DISPLAY_Y-12, 44); + break; + } +} + + +/* + * Draw speedometer + */ + +static const int8 Digits[11][8] = { // Digit images + {0x3c, 0x66, 0x6e, 0x76, 0x66, 0x66, 0x3c, 0x00}, + {0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00}, + {0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00}, + {0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00}, + {0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00}, + {0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00}, + {0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00}, + {0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00}, + {0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00}, + {0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00}, +}; + +void C64Screen::DrawSpeedometer() +{ + // Don't display speedometer if we're running at about 100% + if (Speed >= 99 && Speed <= 101) + return; + + char *s = SpeedoStr; + char c; + long xmod = CardInfo()->bytes_per_row; + uint8 *p = (uint8 *)CardInfo()->frame_buffer + DISPLAY_X - 8*8 + (DISPLAY_Y-20) * xmod; + while (c = *s++) { + if (c == ' ') + continue; + if (c == '%') + c = 10; + else + c -= '0'; + uint8 *q = p; + for (int y=0; y<8; y++) { + uint8 data = Digits[c][y]; + for (int x=0; x<8; x++) { + if (data & (1 << (7-x))) + q[x] = 255; + else + q[x] = 0; + } + q += xmod; + } + p += 8; + } +} + + +/* + * Fill rectangle + */ + +void C64Screen::FillRect(int x1, int y1, int x2, int y2, int color) +{ + long xmod = CardInfo()->bytes_per_row; + uint8 *p = (uint8 *)CardInfo()->frame_buffer + y1 * xmod + x1; + int n = x2 - x1 + 1; + for(int y=y1; y<=y2; y++) { + memset_nc(p, color, n); + p += xmod; + } +} + + +/* + * Bitmap view constructor + */ + +BitmapView::BitmapView(BRect frame, BBitmap *bitmap) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW) +{ + ChangeBitmap(bitmap); +} + + +/* + * Blit the bitmap + */ + +void BitmapView::Draw(BRect update) +{ + if (the_bitmap != NULL) + DrawBitmapAsync(the_bitmap, update, update); +} + + +/* + * Receive special key-down events (main C64 keyboard handling is done in PollKeyboard) + */ + +void BitmapView::KeyDown(const char *bytes, int32 numBytes) +{ + if (bytes[0] == B_FUNCTION_KEY || bytes[0] == '+' || bytes[0] == '-' || bytes[0] == '*' || bytes[0] == '/') { + BMessage *msg = Window()->CurrentMessage(); + long key; + if (msg->FindInt32("key", &key) == B_NO_ERROR) { + switch (key) { + + case B_F11_KEY: // F11: NMI (Restore) + be_app->PostMessage(MSG_NMI); + break; + + case B_F12_KEY: // F12: Reset + be_app->PostMessage(MSG_RESET); + break; + + case 0x3a: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case 0x25: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case 0x24: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case 0x23: // '/' on keypad: Toggle processor-level 1541 emulation + be_app->PostMessage(MSG_TOGGLE_1541); + break; + } + } + } +} + + +/* + * Change view bitmap + */ + +void BitmapView::ChangeBitmap(BBitmap *bitmap) +{ + the_bitmap = bitmap; +} + + +/* + * Speedometer constructor + */ + +SpeedoView::SpeedoView(BRect frame) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED) +{ + speedostr[0] = 0; + bounds = Bounds(); + SetViewColor(fill_gray); + SetFont(be_plain_font); +} + + +/* + * Draw speedometer + */ + +void SpeedoView::Draw(BRect update) +{ + // Draw bevelled border + SetHighColor(shine_gray); + StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0)); + StrokeLine(BPoint(bounds.right, 0)); + SetHighColor(shadow_gray); + StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1)); + + // Draw text + SetHighColor(0, 0, 0); + DrawString(speedostr, BPoint(24, 12)); +} + + +/* + * Update speedometer at regular intervals + */ + +void SpeedoView::Pulse(void) +{ + Invalidate(BRect(1, 1, bounds.right-1, 15)); +} + + +/* + * Set new speedometer value + */ + +void SpeedoView::SetValue(int speed) +{ + sprintf(speedostr, "%d%%", speed); +} + + +/* + * LED view constructor + */ + +LEDView::LEDView(BRect frame, const char *label) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED) +{ + current_state = 0; + the_label = label; + bounds = Bounds(); + SetViewColor(fill_gray); + SetFont(be_plain_font); +} + + +/* + * Draw drive LED + */ + +void LEDView::Draw(BRect update) +{ + // Draw bevelled border + SetHighColor(shine_gray); + StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0)); + StrokeLine(BPoint(bounds.right, 0)); + SetHighColor(shadow_gray); + StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1)); + + // Draw label + SetHighColor(0, 0, 0); + SetLowColor(fill_gray); + DrawString(the_label, BPoint(8, 12)); + + // Draw LED + SetHighColor(shadow_gray); + StrokeLine(BPoint(bounds.right-24, 12), BPoint(bounds.right-24, 4)); + StrokeLine(BPoint(bounds.right-8, 4)); + SetHighColor(shine_gray); + StrokeLine(BPoint(bounds.right-23, 12), BPoint(bounds.right-8, 12)); + StrokeLine(BPoint(bounds.right-8, 5)); + DrawLED(); +} + + +/* + * Redraw just the LED + */ + +void LEDView::DrawLED(void) +{ + Window()->Lock(); + switch (current_state) { + case LED_OFF: + case LED_ERROR_OFF: + SetHighColor(32, 32, 32); + break; + case LED_ON: + SetHighColor(0, 240, 0); + break; + case LED_ERROR_ON: + SetHighColor(240, 0, 0); + break; + } + FillRect(BRect(bounds.right-23, 5, bounds.right-9, 11)); + Window()->Unlock(); +} + + +/* + * Set LED state + */ + +void LEDView::SetState(int state) +{ + if (state != current_state) { + current_state = state; + DrawLED(); + } +} + + +/* + * Toggle red error LED + */ + +void LEDView::Pulse(void) +{ + switch (current_state) { + case LED_ERROR_ON: + current_state = LED_ERROR_OFF; + DrawLED(); + break; + case LED_ERROR_OFF: + current_state = LED_ERROR_ON; + DrawLED(); + break; + } +} + + +/* + * Show a requester + */ + +long ShowRequester(char *str, char *button1, char *button2) +{ + BAlert *the_alert; + + the_alert = new BAlert("", str, button1, button2, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); + return the_alert->Go(); +} diff --git a/Src/Display_SDL.i b/Src/Display_SDL.i new file mode 100644 index 0000000..217b880 --- /dev/null +++ b/Src/Display_SDL.i @@ -0,0 +1,513 @@ +/* + * Display_SDL.i - C64 graphics display, emulator window handling, + * SDL specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "C64.h" +#include "SAM.h" +#include "Version.h" + +#include + + +// Display surface +static SDL_Surface *screen = NULL; + +// Keyboard +static bool num_locked = false; + +// For LED error blinking +static C64Display *c64_disp; +static struct sigaction pulse_sa; +static itimerval pulse_tv; + +// Colors for speedometer/drive LEDs +enum { + black = 0, + white = 1, + fill_gray = 16, + shine_gray = 17, + shadow_gray = 18, + red = 19, + green = 20, + PALETTE_SIZE = 21 +}; + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + + +/* + * Open window + */ + +int init_graphics(void) +{ + // Init SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError()); + return 0; + } + + // Open window + SDL_WM_SetCaption(VERSION_STRING, "Frodo"); + screen = SDL_SetVideoMode(DISPLAY_X, DISPLAY_Y + 17, 8, SDL_DOUBLEBUF); + + return 1; +} + + +/* + * Display constructor + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + quit_requested = false; + speedometer_string[0] = 0; + + // LEDs off + for (int i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Start timer for LED error blinking + c64_disp = this; + pulse_sa.sa_handler = (void (*)(int))pulse_handler; + pulse_sa.sa_flags = 0; + sigemptyset(&pulse_sa.sa_mask); + sigaction(SIGALRM, &pulse_sa, NULL); + pulse_tv.it_interval.tv_sec = 0; + pulse_tv.it_interval.tv_usec = 400000; + pulse_tv.it_value.tv_sec = 0; + pulse_tv.it_value.tv_usec = 400000; + setitimer(ITIMER_REAL, &pulse_tv, NULL); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + SDL_Quit(); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + // Draw speedometer/LEDs + SDL_Rect r = {0, DISPLAY_Y, DISPLAY_X, 15}; + SDL_FillRect(screen, &r, fill_gray); + r.w = DISPLAY_X; r.h = 1; + SDL_FillRect(screen, &r, shine_gray); + r.y = DISPLAY_Y + 14; + SDL_FillRect(screen, &r, shadow_gray); + r.w = 16; + for (int i=2; i<6; i++) { + r.x = DISPLAY_X * i/5 - 24; r.y = DISPLAY_Y + 4; + SDL_FillRect(screen, &r, shadow_gray); + r.y = DISPLAY_Y + 10; + SDL_FillRect(screen, &r, shine_gray); + } + r.y = DISPLAY_Y; r.w = 1; r.h = 15; + for (int i=0; i<5; i++) { + r.x = DISPLAY_X * i / 5; + SDL_FillRect(screen, &r, shine_gray); + r.x = DISPLAY_X * (i+1) / 5 - 1; + SDL_FillRect(screen, &r, shadow_gray); + } + r.y = DISPLAY_Y + 4; r.h = 7; + for (int i=2; i<6; i++) { + r.x = DISPLAY_X * i/5 - 24; + SDL_FillRect(screen, &r, shadow_gray); + r.x = DISPLAY_X * i/5 - 9; + SDL_FillRect(screen, &r, shine_gray); + } + r.y = DISPLAY_Y + 5; r.w = 14; r.h = 5; + for (int i=0; i<4; i++) { + r.x = DISPLAY_X * (i+2) / 5 - 23; + int c; + switch (led_state[i]) { + case LED_ON: + c = green; + break; + case LED_ERROR_ON: + c = red; + break; + default: + c = black; + break; + } + SDL_FillRect(screen, &r, c); + } + + draw_string(screen, DISPLAY_X * 1/5 + 8, DISPLAY_Y + 4, "D\x12 8", black, fill_gray); + draw_string(screen, DISPLAY_X * 2/5 + 8, DISPLAY_Y + 4, "D\x12 9", black, fill_gray); + draw_string(screen, DISPLAY_X * 3/5 + 8, DISPLAY_Y + 4, "D\x12 10", black, fill_gray); + draw_string(screen, DISPLAY_X * 4/5 + 8, DISPLAY_Y + 4, "D\x12 11", black, fill_gray); + draw_string(screen, 24, DISPLAY_Y + 4, speedometer_string, black, fill_gray); + + // Update display + SDL_Flip(screen); +} + + +/* + * Draw string into surface using the C64 ROM font + */ + +void C64Display::draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color) +{ + uint8 *pb = (uint8 *)s->pixels + s->pitch*y + x; + char c; + while ((c = *str++) != 0) { + uint8 *q = TheC64->Char + c*8 + 0x800; + uint8 *p = pb; + for (int y=0; y<8; y++) { + uint8 v = *q++; + p[0] = (v & 0x80) ? front_color : back_color; + p[1] = (v & 0x40) ? front_color : back_color; + p[2] = (v & 0x20) ? front_color : back_color; + p[3] = (v & 0x10) ? front_color : back_color; + p[4] = (v & 0x08) ? front_color : back_color; + p[5] = (v & 0x04) ? front_color : back_color; + p[6] = (v & 0x02) ? front_color : back_color; + p[7] = (v & 0x01) ? front_color : back_color; + p += s->pitch; + } + pb += 8; + } +} + + +/* + * LED error blink + */ + +void C64Display::pulse_handler(...) +{ + for (int i=0; i<4; i++) + switch (c64_disp->led_state[i]) { + case LED_ERROR_ON: + c64_disp->led_state[i] = LED_ERROR_OFF; + break; + case LED_ERROR_OFF: + c64_disp->led_state[i] = LED_ERROR_ON; + break; + } +} + + +/* + * Draw speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) { + delay = 0; + sprintf(speedometer_string, "%d%%", speed); + } else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + return (uint8 *)screen->pixels; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + return screen->pitch; +} + + +/* + * Poll the keyboard + */ + +static void translate_key(SDLKey key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + int c64_key = -1; + switch (key) { + case SDLK_a: c64_key = MATRIX(1,2); break; + case SDLK_b: c64_key = MATRIX(3,4); break; + case SDLK_c: c64_key = MATRIX(2,4); break; + case SDLK_d: c64_key = MATRIX(2,2); break; + case SDLK_e: c64_key = MATRIX(1,6); break; + case SDLK_f: c64_key = MATRIX(2,5); break; + case SDLK_g: c64_key = MATRIX(3,2); break; + case SDLK_h: c64_key = MATRIX(3,5); break; + case SDLK_i: c64_key = MATRIX(4,1); break; + case SDLK_j: c64_key = MATRIX(4,2); break; + case SDLK_k: c64_key = MATRIX(4,5); break; + case SDLK_l: c64_key = MATRIX(5,2); break; + case SDLK_m: c64_key = MATRIX(4,4); break; + case SDLK_n: c64_key = MATRIX(4,7); break; + case SDLK_o: c64_key = MATRIX(4,6); break; + case SDLK_p: c64_key = MATRIX(5,1); break; + case SDLK_q: c64_key = MATRIX(7,6); break; + case SDLK_r: c64_key = MATRIX(2,1); break; + case SDLK_s: c64_key = MATRIX(1,5); break; + case SDLK_t: c64_key = MATRIX(2,6); break; + case SDLK_u: c64_key = MATRIX(3,6); break; + case SDLK_v: c64_key = MATRIX(3,7); break; + case SDLK_w: c64_key = MATRIX(1,1); break; + case SDLK_x: c64_key = MATRIX(2,7); break; + case SDLK_y: c64_key = MATRIX(3,1); break; + case SDLK_z: c64_key = MATRIX(1,4); break; + + case SDLK_0: c64_key = MATRIX(4,3); break; + case SDLK_1: c64_key = MATRIX(7,0); break; + case SDLK_2: c64_key = MATRIX(7,3); break; + case SDLK_3: c64_key = MATRIX(1,0); break; + case SDLK_4: c64_key = MATRIX(1,3); break; + case SDLK_5: c64_key = MATRIX(2,0); break; + case SDLK_6: c64_key = MATRIX(2,3); break; + case SDLK_7: c64_key = MATRIX(3,0); break; + case SDLK_8: c64_key = MATRIX(3,3); break; + case SDLK_9: c64_key = MATRIX(4,0); break; + + case SDLK_SPACE: c64_key = MATRIX(7,4); break; + case SDLK_BACKQUOTE: c64_key = MATRIX(7,1); break; + case SDLK_BACKSLASH: c64_key = MATRIX(6,6); break; + case SDLK_COMMA: c64_key = MATRIX(5,7); break; + case SDLK_PERIOD: c64_key = MATRIX(5,4); break; + case SDLK_MINUS: c64_key = MATRIX(5,0); break; + case SDLK_EQUALS: c64_key = MATRIX(5,3); break; + case SDLK_LEFTBRACKET: c64_key = MATRIX(5,6); break; + case SDLK_RIGHTBRACKET: c64_key = MATRIX(6,1); break; + case SDLK_SEMICOLON: c64_key = MATRIX(5,5); break; + case SDLK_QUOTE: c64_key = MATRIX(6,2); break; + case SDLK_SLASH: c64_key = MATRIX(6,7); break; + + case SDLK_ESCAPE: c64_key = MATRIX(7,7); break; + case SDLK_RETURN: c64_key = MATRIX(0,1); break; + case SDLK_BACKSPACE: case SDLK_DELETE: c64_key = MATRIX(0,0); break; + case SDLK_INSERT: c64_key = MATRIX(6,3); break; + case SDLK_HOME: c64_key = MATRIX(6,3); break; + case SDLK_END: c64_key = MATRIX(6,0); break; + case SDLK_PAGEUP: c64_key = MATRIX(6,0); break; + case SDLK_PAGEDOWN: c64_key = MATRIX(6,5); break; + + case SDLK_LCTRL: case SDLK_TAB: c64_key = MATRIX(7,2); break; + case SDLK_RCTRL: c64_key = MATRIX(7,5); break; + case SDLK_LSHIFT: c64_key = MATRIX(1,7); break; + case SDLK_RSHIFT: c64_key = MATRIX(6,4); break; + case SDLK_LALT: case SDLK_LMETA: c64_key = MATRIX(7,5); break; + case SDLK_RALT: case SDLK_RMETA: c64_key = MATRIX(7,5); break; + + case SDLK_UP: c64_key = MATRIX(0,7)| 0x80; break; + case SDLK_DOWN: c64_key = MATRIX(0,7); break; + case SDLK_LEFT: c64_key = MATRIX(0,2) | 0x80; break; + case SDLK_RIGHT: c64_key = MATRIX(0,2); break; + + case SDLK_F1: c64_key = MATRIX(0,4); break; + case SDLK_F2: c64_key = MATRIX(0,4) | 0x80; break; + case SDLK_F3: c64_key = MATRIX(0,5); break; + case SDLK_F4: c64_key = MATRIX(0,5) | 0x80; break; + case SDLK_F5: c64_key = MATRIX(0,6); break; + case SDLK_F6: c64_key = MATRIX(0,6) | 0x80; break; + case SDLK_F7: c64_key = MATRIX(0,3); break; + case SDLK_F8: c64_key = MATRIX(0,3) | 0x80; break; + + case SDLK_KP0: case SDLK_KP5: c64_key = 0x10 | 0x40; break; + case SDLK_KP1: c64_key = 0x06 | 0x40; break; + case SDLK_KP2: c64_key = 0x02 | 0x40; break; + case SDLK_KP3: c64_key = 0x0a | 0x40; break; + case SDLK_KP4: c64_key = 0x04 | 0x40; break; + case SDLK_KP6: c64_key = 0x08 | 0x40; break; + case SDLK_KP7: c64_key = 0x05 | 0x40; break; + case SDLK_KP8: c64_key = 0x01 | 0x40; break; + case SDLK_KP9: c64_key = 0x09 | 0x40; break; + + case SDLK_KP_DIVIDE: c64_key = MATRIX(6,7); break; + case SDLK_KP_ENTER: c64_key = MATRIX(0,1); break; + } + + if (c64_key < 0) + return; + + // Handle joystick emulation + if (c64_key & 0x40) { + c64_key &= 0x1f; + if (key_up) + *joystick |= c64_key; + else + *joystick &= ~c64_key; + return; + } + + // Handle other keys + bool shifted = c64_key & 0x80; + int c64_byte = (c64_key >> 3) & 7; + int c64_bit = c64_key & 7; + if (key_up) { + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } else { + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } +} + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + + // Key pressed + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + + case SDLK_F9: // F9: Invoke SAM + SAM(TheC64); + break; + + case SDLK_F10: // F10: Quit + quit_requested = true; + break; + + case SDLK_F11: // F11: NMI (Restore) + TheC64->NMI(); + break; + + case SDLK_F12: // F12: Reset + TheC64->Reset(); + break; + + case SDLK_NUMLOCK: + num_locked = true; + break; + + case SDLK_KP_PLUS: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case SDLK_KP_MINUS: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case SDLK_KP_MULTIPLY: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + default: + translate_key(event.key.keysym.sym, false, key_matrix, rev_matrix, joystick); + break; + } + break; + + // Key released + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_NUMLOCK) + num_locked = false; + else + translate_key(event.key.keysym.sym, true, key_matrix, rev_matrix, joystick); + break; + + // Quit Frodo + case SDL_QUIT: + quit_requested = true; + break; + } + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return num_locked; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + SDL_Color palette[PALETTE_SIZE]; + for (int i=0; i<16; i++) { + palette[i].r = palette_red[i]; + palette[i].g = palette_green[i]; + palette[i].b = palette_blue[i]; + } + palette[fill_gray].r = palette[fill_gray].g = palette[fill_gray].b = 0xd0; + palette[shine_gray].r = palette[shine_gray].g = palette[shine_gray].b = 0xf0; + palette[shadow_gray].r = palette[shadow_gray].g = palette[shadow_gray].b = 0x80; + palette[red].r = 0xf0; + palette[red].g = palette[red].b = 0; + palette[green].g = 0xf0; + palette[green].r = palette[green].b = 0; + SDL_SetColors(screen, palette, 0, PALETTE_SIZE); + + for (int i=0; i<256; i++) + colors[i] = i & 0x0f; +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(char *a,char *b,char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/Display_WIN32.i b/Src/Display_WIN32.i new file mode 100644 index 0000000..c6cccf4 --- /dev/null +++ b/Src/Display_WIN32.i @@ -0,0 +1,2367 @@ +/* + * Display_WIN32.i - C64 graphics display, emulator window handling, + * WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * WIN32 code by J. Richard Sladkey + */ + +#include +#include + +#include "C64.h" +#include "SAM.h" +#include "Version.h" +#include "VIC.h" +#include "resource.h" + +#define NAME "Frodo" +#define TITLE (IsFrodoSC ? "FrodoSC" : "Frodo") + +#ifdef DEBUG + +class TimeScope +{ + +public: + TimeScope(const char *s) + { + tag = s; + QueryPerformanceCounter(&liStart); + } + ~TimeScope() + { + QueryPerformanceCounter(&liFinish); + OutputTime(); + } + +private: + void + OutputTime() + { + LARGE_INTEGER liFreq; + + QueryPerformanceFrequency(&liFreq); + Debug("%s: %.0f usec\n", tag, + double(liFinish.LowPart - liStart.LowPart) + /liFreq.LowPart*1000000); + } + const char *tag; + LARGE_INTEGER liStart, liFinish; +}; + +#define TIMESCOPE(var, tag) TimeScope var(tag) + +#else + +#define TIMESCOPE(var, tag) + +#endif + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + +#define KEY_F9 256 +#define KEY_F10 257 +#define KEY_F11 258 +#define KEY_F12 259 + +#define KEY_FIRE 260 +#define KEY_JUP 261 +#define KEY_JDN 262 +#define KEY_JLF 263 +#define KEY_JRT 264 +#define KEY_JUPLF 265 +#define KEY_JUPRT 266 +#define KEY_JDNLF 267 +#define KEY_JDNRT 268 +#define KEY_CENTER 269 + +#define KEY_NUMLOCK 270 + +#define KEY_KPPLUS 271 +#define KEY_KPMINUS 272 +#define KEY_KPMULT 273 +#define KEY_KPDIV 274 +#define KEY_KPENTER 275 +#define KEY_KPPERIOD 276 + +#define KEY_PAUSE 277 +#define KEY_ALTENTER 278 +#define KEY_CTRLENTER 279 + +#define VK_bracketleft 0xdb +#define VK_bracketright 0xdd +#define VK_comma 0xbc +#define VK_period 0xbe +#define VK_slash 0xbf +#define VK_semicolon 0xba +#define VK_grave 0xc0 +#define VK_minus 0xbd +#define VK_equal 0xbb +#define VK_quote 0xde +#define VK_backslash 0xdc + +static C64Display *TheDisplay; +static int keystate[256]; +static UBYTE rev_matrix[8], key_matrix[8]; +static int quit = 0; +static int numlock = 0; +static int joystate = 0xff; + +static RECT rcScreen; +static RECT rcLast; +static RECT rcWindow; +static RECT rcWork; +static BOOL need_new_color_table = FALSE; +static int view_x, view_y; + +static int led_rows = 16; + +static HCURSOR invisible_cursor; +static HCURSOR arrow_cursor; + +static HFONT led_font; + +static HPEN led_highlight; +static HPEN led_shadow; + +static HBRUSH led_brush; +static HBRUSH off_brush; +static HBRUSH error_off_brush; +static HBRUSH on_brush; +static HBRUSH error_on_brush; + +// Not fully working yet. +#ifdef WORKBUFFER_BITMAP +static BOOL workbuffer_bitmap = FALSE; +static BOOL workbuffer_locked = FALSE; +static DDSURFACEDESC bitmap_ddsd; +#endif + +C64Display::DisplayMode default_modes[] = { + { 320, 200, 8 }, + { 320, 240, 8 }, + { 512, 384, 8 }, + { 640, 400, 8 }, + { 640, 480, 8 }, + { 320, 200, 16 }, + { 320, 240, 16 }, + { 512, 384, 16 }, + { 640, 400, 16 }, + { 640, 480, 16 }, +}; +static int num_default_modes = + sizeof(default_modes)/sizeof(C64Display::DisplayMode); + +static C64Display::DisplayMode *display_modes = NULL; +static int num_display_modes = 0; +static int max_display_modes = 16; + +int C64Display::GetNumDisplayModes() const +{ + if (num_display_modes == 0) + return num_default_modes; + return num_display_modes; +} + +const C64Display::DisplayMode *C64Display::GetDisplayModes() const +{ + if (num_display_modes == 0) + return default_modes; + return display_modes; +} + +long ShowRequester(char *str, char *button1, char *button2) +{ + if (!TheDisplay) { + MessageBox(hwnd, str, "Frodo", MB_OK | MB_ICONSTOP); + return FALSE; + } + return TheDisplay->ShowRequester(str, button1, button2); +} + +/* + * Display constructor: Create window/screen + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + in_constructor = TRUE; + in_destructor = FALSE; + + TheDisplay = this; + speed_index = 0; + + pDD = NULL; + pPrimary = NULL; + pBack = NULL; + pWork = NULL; + pClipper = NULL; + pPalette = NULL; + active = FALSE; + paused = FALSE; + waiting = FALSE; + show_leds = ThePrefs.ShowLEDs; + full_screen = ThePrefs.DisplayType == DISPTYPE_SCREEN; + + // Turn LEDs off. + for (int i = 0; i < 4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Allocate chunky buffer to draw into. + chunky_buf = new UBYTE[DISPLAY_X * DISPLAY_Y]; + + CalcViewPort(); + + ResetKeyboardState(); + + if (!MakeWindow()) { + ShowRequester("Failed to create window.", "Quit"); + Quit(); + } + else { + WindowTitle(); + + if (!StartDirectDraw()) { + ShowRequester(failure_message, "Quit"); + Quit(); + } + else + draw_led_bar(); + } + + in_constructor = FALSE; +} + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + in_destructor = TRUE; + + Debug("~C64Display\n"); + + StopDirectDraw(); + + // Offer to save now that we are not in full screen mode. + OfferSave(); + + // Free the display modes table. + delete[] display_modes; + + // Free chunky buffer + delete chunky_buf; + + // Destroy the main window. + DestroyWindow(hwnd); + + // Drain the window message queue. + for (;;) + { + MSG msg; + if (!GetMessage(&msg, NULL, 0, 0)) + break; + if (ThePrefs.SystemKeys) + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + DeleteObjects(); + + in_destructor = FALSE; +} + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + +void C64Display::DeleteObjects() +{ + // Delete objects we created. + DeleteObject(led_highlight); + DeleteObject(led_shadow); + DeleteObject(led_font); + DeleteObject(led_brush); + DeleteObject(off_brush); + DeleteObject(error_off_brush); + DeleteObject(on_brush); + DeleteObject(error_on_brush); +} + +BOOL C64Display::CalcViewPort() +{ + int old_view_x = view_x, old_view_y = view_y; + const char *view_port = ThePrefs.ViewPort; + if (view_port[0] == '\0' || + stricmp(view_port, "Default") == 0) + view_port = NULL; + if (!view_port || sscanf(view_port, "%dx%d", &view_x, &view_y) != 2) { + view_x = DISPLAY_X; + view_y = DISPLAY_Y; + } + SetRect(&rcWork, 0, 0, view_x, view_y); + if (view_x != old_view_x || view_y != old_view_y) + return TRUE; + return FALSE; +} + + +BOOL C64Display::ResizeWindow(int side, RECT *pRect) +{ + // Compute size of non-client borders. + DWORD style = GetWindowLong(hwnd, GWL_STYLE); + RECT rc; + SetRect(&rc, 0, 0, view_x, view_y); + BOOL has_menu = GetMenu(hwnd) != NULL; + AdjustWindowRect(&rc, style, has_menu); + if (ThePrefs.ShowLEDs) + rc.bottom += led_rows; + int nc_x = rc.right - rc.left - view_x; + int nc_y = rc.bottom - rc.top - view_y; + + // Compute client area corresponding to resizing. + int old_x = pRect->right - pRect->left - nc_x; + int old_y = pRect->bottom - pRect->top - nc_y; + + // Compute nearest integral scaling numerators. + int d = ThePrefs.ScalingDenominator; + int x = (old_x + view_x/d/2)/(view_x/d); + if (x == 0) + x = 1; + int y = (old_y + view_y/4)/(view_y/d); + if (y == 0) + y = 1; + + // When resizing corners make the scale factors agree. + switch (side) { + case WMSZ_BOTTOMRIGHT: + case WMSZ_BOTTOMLEFT: + case WMSZ_TOPRIGHT: + case WMSZ_TOPLEFT: + if (x < y) + y = x; + else + x = y; + } + + // Compute the quantized size of the window area. + int new_x = x*(view_x/d) + nc_x; + int new_y = y*(view_y/d) + nc_y; + + // Adjust the resizing rectangle. + switch (side) { + + case WMSZ_BOTTOMRIGHT: + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOM: + pRect->bottom = pRect->top + new_y; + break; + + case WMSZ_TOPRIGHT: + case WMSZ_TOPLEFT: + case WMSZ_TOP: + pRect->top = pRect->bottom - new_y; + break; + } + switch (side) { + + case WMSZ_TOPRIGHT: + case WMSZ_BOTTOMRIGHT: + case WMSZ_RIGHT: + pRect->right = pRect->left + new_x; + break; + + case WMSZ_TOPLEFT: + case WMSZ_BOTTOMLEFT: + case WMSZ_LEFT: + pRect->left = pRect->right - new_x; + break; + } + + return TRUE; +} + +/* + * Update speedometer + */ + +void C64Display::Speedometer(int speed) +{ + Debug("speed = %d %%\n", speed); + speed_index = speed; + + if (full_screen) + return; + + if (!ThePrefs.ShowLEDs) { + WindowTitle(); + return; + } + + if (speed_index == 0) + return; + + HDC hdc = GetDC(hwnd); + RECT rc; + GetClientRect(hwnd, &rc); + rc.top = rc.bottom - led_rows; + rc.right = rc.left + (rc.right - rc.left)/5; + FillRect(hdc, &rc, led_brush); + SelectObject(hdc, led_font); + SetTextAlign(hdc, TA_TOP | TA_LEFT); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT)); + char str[128]; + if (IsFrodoSC) + sprintf(str, "%d%%", speed_index); + else + sprintf(str, "%d%%", speed_index); + int x = rc.left + 4; + int y = rc.top + 2; + TextOut(hdc, x, y, str, strlen(str)); + ReleaseDC(hwnd, hdc); +} + + +/* + * Return pointer to bitmap data + */ + +UBYTE *C64Display::BitmapBase() +{ +#ifdef WORKBUFFER_BITMAP + if (colors_depth == 8 && pWork) { + if (workbuffer_locked) { + pWork->Unlock(NULL); + workbuffer_locked = FALSE; + } + HRESULT ddrval; + for (;;) { + bitmap_ddsd.dwSize = sizeof(bitmap_ddsd); + ddrval = pWork->Lock(NULL, &bitmap_ddsd, 0, NULL); + if (ddrval != DDERR_WASSTILLDRAWING) + break; + } + if (ddrval == DD_OK) { + workbuffer_locked = TRUE; + workbuffer_bitmap = TRUE; + return (UBYTE *) bitmap_ddsd.lpSurface; + } + } + workbuffer_bitmap = FALSE; +#endif + return chunky_buf; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod() +{ +#ifdef WORKBUFFER_BITMAP + if (workbuffer_locked) + return bitmap_ddsd.lPitch; +#endif + return DISPLAY_X; +} + + +/* + * Freshen keyboard state + */ + +void C64Display::PollKeyboard(UBYTE *CIA_key_matrix, UBYTE *CIA_rev_matrix, UBYTE *joystick) +{ + //Debug("Display::PollKeyboard\n"); + +#ifdef WORKBUFFER_BITMAP + if (workbuffer_locked) { + pWork->Unlock(NULL); + workbuffer_locked = FALSE; + } +#endif + + for (;;) + { + MSG msg; + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + break; + if (ThePrefs.SystemKeys) + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + *joystick = joystate; + memcpy(CIA_key_matrix, key_matrix, sizeof(key_matrix)); + memcpy(CIA_rev_matrix, rev_matrix, sizeof(rev_matrix)); +} + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock() +{ + return numlock; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(UBYTE *array) +{ + if (colors_depth == 8) { + for (int i = 0; i < 256; i++) + array[i] = colors[i & 0x0f]; + } + else { + for (int i = 0; i < 256; i++) + array[i] = i & 0x0f; + } +} + + +long C64Display::ShowRequester(const char *str, const char *button1, const char *button2) +{ + // This could be a lot nicer but quick and dirty is fine with me. + char message[1024]; + strcpy(message, str); + strcat(message, "\nPress OK to "); + strcat(message, button1); + if (button2) { + strcat(message, ", Cancel to "); + strcat(message, button2); + } + strcat(message, "."); + UINT type; + if (button2) + type = MB_OKCANCEL | MB_ICONQUESTION; + else + type = MB_OK | MB_ICONSTOP; + Pause(); + if (full_screen) + StopDirectDraw(); + int result = MessageBox(hwnd, message, NAME " Error", type); + if (full_screen) + StartDirectDraw(); + Resume(); + if (result == IDCANCEL) + return TRUE; + return FALSE; +} + +void C64Display::WaitUntilActive() +{ + Debug("waiting until not paused...\n"); + waiting = TRUE; + WindowTitle(); + for (;;) { + + // Check for termination condition. + if (!paused || quit) + break; + + // Process message queue. + MSG msg; + if (GetMessage(&msg, NULL, 0, 0) != TRUE) + break; + + // Always translate system keys while paused. + TranslateMessage(&msg); + DispatchMessage(&msg); + } + waiting = FALSE; + Debug("...done waiting\n"); + WindowTitle(); + ResetKeyboardState(); +} + +void C64Display::ResetKeyboardState() +{ + memset(keystate, 0, sizeof(keystate)); + memset(key_matrix, 0xff, sizeof(key_matrix)); + memset(rev_matrix, 0xff, sizeof(rev_matrix)); + joystate = 0xff; +} + +BOOL C64Display::MakeWindow() +{ + // Set up and register window class. + WNDCLASS wc; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = StaticWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(FRODO_ICON)); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN_MENU); + wc.lpszClassName = NAME; + RegisterClass(&wc); + + // Set up our preferred styles for our window depending on the mode. + windowed_style = WS_VISIBLE | WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME; + fullscreen_style = WS_POPUP | WS_VISIBLE; + + // Compute the initial window size. + DWORD style = windowed_style; + RECT rc; + int n = ThePrefs.ScalingNumerator; + int d = ThePrefs.ScalingDenominator; + SetRect(&rc, 0, 0, n*view_x/d, n*view_y/d); + BOOL has_menu = wc.lpszMenuName != NULL; + AdjustWindowRect(&rc, style, has_menu); + if (ThePrefs.ShowLEDs) + rc.bottom += led_rows; + int x_size = rc.right - rc.left; + int y_size = rc.bottom - rc.top; + + // Create the window and save the initial position. + hwnd = CreateWindowEx(0, NAME, TITLE, style, CW_USEDEFAULT, 0, x_size, y_size, NULL, NULL, hInstance, NULL); + GetWindowRect(hwnd, &rcLast); + SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + + // Load cursors. + invisible_cursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_INVISIBLE)); + arrow_cursor = LoadCursor(0, MAKEINTRESOURCE(IDC_ARROW)); + + // Create fonts, pens, brushes, etc. + CreateObjects(); + + if (!hwnd) + return FALSE; + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + return TRUE; +} + +void C64Display::CreateObjects() +{ + // Create fonts. + led_font = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + VARIABLE_PITCH | FF_SWISS, ""); + + // Create pens. + led_highlight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DHIGHLIGHT)); + led_shadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DSHADOW)); + + // Create brushes. + LOGBRUSH logbrush; + logbrush.lbStyle = BS_SOLID; + logbrush.lbHatch = 0; + logbrush.lbColor = GetSysColor(COLOR_MENU); + led_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black + off_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black + error_off_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0x00, 0xff, 0x00); // green + on_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0xff, 0x00, 0x00); // red + error_on_brush = CreateBrushIndirect(&logbrush); +} + +HRESULT CALLBACK C64Display::StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return TheDisplay->WindowProc(hWnd, message, wParam, lParam); +} + +long C64Display::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + Debug("window message: 0x%x\n", message); + + switch (message) { + + case WM_SYSCOLORCHANGE: + DeleteObjects(); + CreateObjects(); + InvalidateRect(hwnd, NULL, FALSE); + break; + + case WM_MOUSEMOVE: + SetCursor(ThePrefs.HideCursor ? invisible_cursor : arrow_cursor); + break; + + case WM_MENUCHAR: + // Eat Alt-foo characters so that it doesn't beep. + if (HIWORD(wParam) == 0) + return MAKELONG(0, 1); + break; + + case WM_ENTERSIZEMOVE: + Pause(); + break; + + case WM_EXITSIZEMOVE: + Resume(); + break; + + case WM_SIZING: + ResizeWindow(wParam, (RECT *) lParam); + return TRUE; + + case WM_SIZE: + case WM_MOVE: + if (full_screen) + SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + else { + GetClientRect(hWnd, &rcWindow); + if (ThePrefs.ShowLEDs) + rcWindow.bottom -= led_rows; + ClientToScreen(hWnd, (LPPOINT) &rcWindow); + ClientToScreen(hWnd, (LPPOINT) &rcWindow + 1); + + // Align the client rect to a four-byte + // boundary because this can triple the + // speed of a memcpy to the display. + int align_to = 4; + int misalignment = rcWindow.left % align_to; + if (misalignment == 0) + Update(); + else { + if (misalignment > align_to/2) + misalignment -= align_to; + RECT rc; + GetWindowRect(hwnd, &rc); + MoveWindow(hwnd, rc.left - misalignment, + rc.top, rc.right - rc.left, + rc.bottom - rc.top, TRUE); + } + } + break; + + case WM_DISPLAYCHANGE: + if (!full_screen) + ResumeDirectDraw(); + SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + break; + + case WM_ACTIVATE: + Debug("WM_ACTIVATE\n"); + { + int old_active = active; + active = LOWORD(wParam) != WA_INACTIVE; + if (ThePrefs.AutoPause && active != old_active) { + if (!active) + Pause(); + else + Resume(); + } + } + if (active) { + ResumeDirectDraw(); + ResetKeyboardState(); + } + + // Kick the message loop since this was sent to us, not posted. + PostMessage(hWnd, WM_USER, 0, 0); + break; + + case WM_COMMAND: + { + int id = LOWORD(wParam); + switch (id) { + + case ID_FILE_NEW: + { + OfferSave(); + Prefs *prefs = new Prefs; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + ThePrefsOnDisk = ThePrefs; + delete prefs; + strcpy(TheApp->prefs_path, "Untitled.fpr"); + NewPrefs(); + } + break; + + case ID_FILE_OPEN: + Pause(); + OfferSave(); + if (FileNameDialog(TheApp->prefs_path)) { + Prefs *prefs = new Prefs; + prefs->Load(TheApp->prefs_path); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + NewPrefs(); + } + Resume(); + break; + + case ID_FILE_SAVE: + ThePrefs.Save(TheApp->prefs_path); + break; + + case ID_FILE_SAVEAS: + Pause(); + if (FileNameDialog(TheApp->prefs_path, TRUE)) { + ThePrefs.Save(TheApp->prefs_path); + WindowTitle(); + } + Resume(); + break; + + case ID_FILE_EX: + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + + case ID_TOOLS_PREFERENCES: + Pause(); + TheApp->RunPrefsEditor(); + NewPrefs(); + Resume(); + break; + + case ID_TOOLS_FULLSCREEN: + Pause(); + StopDirectDraw(); + full_screen = !full_screen; + if (!StartDirectDraw()) { + StopDirectDraw(); + full_screen = !full_screen; + StartDirectDraw(); + if (!full_screen) + ShowRequester(failure_message, "Continue"); + } + Resume(); + CheckMenuItem(GetMenu(hWnd), ID_TOOLS_FULLSCREEN, full_screen ? MF_CHECKED : MF_UNCHECKED); + if (paused) + Update(); + break; + + case ID_TOOLS_RESETDIRECTDRAW: + ResetDirectDraw(); + break; + + case ID_TOOLS_PAUSE: + if (!paused) + Pause(); + else { + // XXX: Shouldn't happen but be safe. + while (paused) + Resume(); + ResetKeyboardState(); + } + CheckMenuItem(GetMenu(hWnd), ID_TOOLS_PAUSE, paused ? MF_CHECKED : MF_UNCHECKED); + break; + + case ID_TOOLS_RESETC64: + TheC64->Reset(); + break; + + case ID_TOOLS_INSERTNEXTDISK: + InsertNextDisk(); + break; + + case ID_TOOLS_SAM: + Pause(); + MessageBox(hWnd, "SAM not yet implemented.", NAME, MB_OK); + Resume(); + break; + + case ID_HELP_CONTENTS: + case ID_HELP_KEYBOARD: + case ID_HELP_SETTINGS: + { + const char *html; + switch (id) { + case ID_HELP_CONTENTS: html = "Main"; break; + case ID_HELP_KEYBOARD: html = "keyboard"; break; + case ID_HELP_SETTINGS: html = "settings"; break; + } + char helpfile[256]; + sprintf(helpfile, "%s\\Docs\\%s.html", AppDirPath, html); + ShellExecute(0, 0, helpfile, 0, 0, SW_NORMAL); + } + break; + + case ID_HELP_ABOUT: + { + Pause(); + char message[256]; + sprintf(message, "%s by %s\n%s by %s", + VERSION_STRING, + "Christian Bauer", + "WIN32 port", + "J. Richard Sladkey"); + MessageBox(hWnd, message, NAME, MB_OK); + Resume(); + } + break; + } + ResetKeyboardState(); + } + break; + + case WM_CLOSE: + Quit(); + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_QUERYNEWPALETTE: + if (!full_screen && pPalette && pPrimary) { + SetPalettes(); + BuildColorTable(); + if (!active) + Update(); + } + break; + + case WM_PALETTECHANGED: + if (!full_screen) { + if ((HWND) wParam != hWnd) { + need_new_color_table = TRUE; + InvalidateRect(hwnd, NULL, FALSE); + } + } + break; + + case WM_PAINT: + if (!full_screen) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + if (need_new_color_table) { + BuildColorTable(); + need_new_color_table = FALSE; + } + if (paused) + Update(); + draw_led_bar(); + Speedometer(speed_index); + return 0; + } + break; + + case WM_ENTERMENULOOP: + Pause(); + break; + + case WM_EXITMENULOOP: + Resume(); + ResetKeyboardState(); + break; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + Debug("Display::WindowProc: KEYDOWN: 0x%x\n", wParam); + { + int kc = VirtKey2C64(wParam, lParam); + switch (kc) { + + case KEY_PAUSE: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_PAUSE, 0); + break; + + case KEY_KPPLUS: + if (ThePrefs.SkipFrames < 10) + ThePrefs.SkipFrames++; + break; + + case KEY_KPMINUS: + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case KEY_KPMULT: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case KEY_KPDIV: + { + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } + break; + + case KEY_KPPERIOD: + ThePrefs.JoystickSwap = !ThePrefs.JoystickSwap; + break; + + case KEY_F9: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_INSERTNEXTDISK, 0); + break; + + case KEY_ALTENTER: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_FULLSCREEN, 0); + break; + + case KEY_CTRLENTER: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_RESETDIRECTDRAW, 0); + break; + + case KEY_F10: + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + + case KEY_F11: + if (!paused) + TheC64->NMI(); + break; + + case KEY_F12: + if (!paused) + TheC64->Reset(); + break; + + case KEY_FIRE: + joystate &= ~0x10; + break; + + case KEY_JUP: + joystate |= 0x02; + joystate &= ~0x01; + break; + + case KEY_JDN: + joystate |= 0x01; + joystate &= ~0x02; + break; + + case KEY_JLF: + joystate |= 0x08; + joystate &= ~0x04; + break; + + case KEY_JRT: + joystate |= 0x04; + joystate &= ~0x08; + break; + + case KEY_JUPLF: + joystate |= 0x0a; + joystate &= ~0x05; + break; + + case KEY_JUPRT: + joystate |= 0x06; + joystate &= ~0x09; + break; + + case KEY_JDNLF: + joystate |= 0x09; + joystate &= ~0x06; + break; + + case KEY_JDNRT: + joystate |= 0x05; + joystate &= ~0x0a; + break; + + case KEY_CENTER: + joystate |= 0x0f; + break; + + default: + if (kc < 0 || kc >= 256) + break; + if (keystate[kc]) + break; + keystate[kc] = 1; + int c64_byte = kc >> 3; + int c64_bit = kc & 7; + int shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + break; + } + return 0; + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Debug("Display::WindowProc: KEYUP: 0x%x\n", wParam); + { + int kc = VirtKey2C64(wParam, lParam); + switch (kc) { + + case KEY_FIRE: + joystate |= 0x10; + break; + + case KEY_JUP: + joystate |= 0x01; + break; + + case KEY_JDN: + joystate |= 0x02; + break; + + case KEY_JLF: + joystate |= 0x04; + break; + + case KEY_JRT: + joystate |= 0x08; + break; + + case KEY_JUPLF: + joystate |= 0x05; + break; + + case KEY_JUPRT: + joystate |= 0x09; + break; + + case KEY_JDNLF: + joystate |= 0x06; + break; + + case KEY_JDNRT: + joystate |= 0x0a; + break; + + default: + if (kc < 0 || kc >= 256) + break; + if (!keystate[kc]) + break; + keystate[kc] = 0; + int c64_byte = kc >> 3; + int c64_bit = kc & 7; + int shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + break; + } + return 0; + } + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +int C64Display::VirtKey2C64(int virtkey, DWORD keydata) +{ + int ext = keydata & 0x01000000; + int sc = (keydata & 0x00ff0000) >> 16; + int result = -1; + + switch (virtkey) { + + case VK_NUMPAD0: numlock = 1; return KEY_FIRE; + case VK_NUMPAD1: numlock = 1; return KEY_JDNLF; + case VK_NUMPAD2: numlock = 1; return KEY_JDN; + case VK_NUMPAD3: numlock = 1; return KEY_JDNRT; + case VK_NUMPAD4: numlock = 1; return KEY_JLF; + case VK_NUMPAD5: numlock = 1; return KEY_CENTER; + case VK_NUMPAD6: numlock = 1; return KEY_JRT; + case VK_NUMPAD7: numlock = 1; return KEY_JUPLF; + case VK_NUMPAD8: numlock = 1; return KEY_JUP; + case VK_NUMPAD9: numlock = 1; return KEY_JUPRT; + + case VK_NUMLOCK: return KEY_NUMLOCK; + case VK_MULTIPLY: return KEY_KPMULT; + case VK_DIVIDE: return KEY_KPDIV; + case VK_SUBTRACT: return KEY_KPMINUS; + case VK_ADD: return KEY_KPPLUS; + case VK_DECIMAL: return KEY_KPPERIOD; + + case VK_F9: return KEY_F9; + case VK_F10: return KEY_F10; + case VK_F11: return KEY_F11; + case VK_F12: return KEY_F12; + case VK_PAUSE: return KEY_PAUSE; + + case VK_BACK: return MATRIX(0,0); + case VK_DELETE: return ext ? MATRIX(0,0) : /*KP*/ KEY_KPPERIOD; + case VK_TAB: return -1; + case VK_RETURN: + if ((GetKeyState(VK_MENU) & 0x8000)) + return KEY_ALTENTER; + if ((GetKeyState(VK_CONTROL) & 0x8000)) + return KEY_CTRLENTER; + return ext ? /*KP*/ MATRIX(0,1) : MATRIX(0,1); + case VK_SPACE: return MATRIX(7,4); + case VK_ESCAPE: return MATRIX(7,7); + case VK_INSERT: if (!ext) numlock = 0; return ext ? MATRIX(0,0) | 0x80 : /*KP*/ KEY_FIRE; + case VK_HOME: if (!ext) numlock = 0; return ext ? MATRIX(6,3) : /*KP*/ KEY_JUPLF; + case VK_END: if (!ext) numlock = 0; return ext ? MATRIX(6,0) : /*KP*/ KEY_JDNLF; + case VK_PRIOR: if (!ext) numlock = 0; return ext ? MATRIX(6,6) : /*KP*/ KEY_JUPRT; + case VK_NEXT: if (!ext) numlock = 0; return ext ? MATRIX(6,5) : /*KP*/ KEY_JDNRT; + case VK_CLEAR: return KEY_CENTER; + + case VK_SHIFT: return sc == 0x36 ? /*R*/ MATRIX(6,4) : MATRIX(1,7); + case VK_CONTROL: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,2); + case VK_MENU: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,5); + + case VK_UP: if (!ext) numlock = 0; return ext ? MATRIX(0,7) | 0x80 : /*KP*/ KEY_JUP; + case VK_DOWN: if (!ext) numlock = 0; return ext ? MATRIX(0,7) : /*KP*/ KEY_JDN; + case VK_LEFT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) | 0x80 : /*KP*/ KEY_JLF; + case VK_RIGHT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) : /*KP*/ KEY_JRT; + + case VK_F1: return MATRIX(0,4); + case VK_F2: return MATRIX(0,4) | 0x80; + case VK_F3: return MATRIX(0,5); + case VK_F4: return MATRIX(0,5) | 0x80; + case VK_F5: return MATRIX(0,6); + case VK_F6: return MATRIX(0,6) | 0x80; + case VK_F7: return MATRIX(0,3); + case VK_F8: return MATRIX(0,3) | 0x80; + + case '0': return MATRIX(4,3); + case '1': return MATRIX(7,0); + case '2': return MATRIX(7,3); + case '3': return MATRIX(1,0); + case '4': return MATRIX(1,3); + case '5': return MATRIX(2,0); + case '6': return MATRIX(2,3); + case '7': return MATRIX(3,0); + case '8': return MATRIX(3,3); + case '9': return MATRIX(4,0); + + case VK_bracketleft: return MATRIX(5,6); + case VK_bracketright: return MATRIX(6,1); + case VK_slash: return MATRIX(6,7); + case VK_semicolon: return MATRIX(5,5); + case VK_grave: return MATRIX(7,1); + case VK_minus: return MATRIX(5,0); + case VK_equal: return MATRIX(5,3); + case VK_comma: return MATRIX(5,7); + case VK_period: return MATRIX(5,4); + case VK_quote: return MATRIX(6,2); + case VK_backslash: return MATRIX(6,6); + + case 'A': result = MATRIX(1,2); break; + case 'B': result = MATRIX(3,4); break; + case 'C': result = MATRIX(2,4); break; + case 'D': result = MATRIX(2,2); break; + case 'E': result = MATRIX(1,6); break; + case 'F': result = MATRIX(2,5); break; + case 'G': result = MATRIX(3,2); break; + case 'H': result = MATRIX(3,5); break; + case 'I': result = MATRIX(4,1); break; + case 'J': result = MATRIX(4,2); break; + case 'K': result = MATRIX(4,5); break; + case 'L': result = MATRIX(5,2); break; + case 'M': result = MATRIX(4,4); break; + case 'N': result = MATRIX(4,7); break; + case 'O': result = MATRIX(4,6); break; + case 'P': result = MATRIX(5,1); break; + case 'Q': result = MATRIX(7,6); break; + case 'R': result = MATRIX(2,1); break; + case 'S': result = MATRIX(1,5); break; + case 'T': result = MATRIX(2,6); break; + case 'U': result = MATRIX(3,6); break; + case 'V': result = MATRIX(3,7); break; + case 'W': result = MATRIX(1,1); break; + case 'X': result = MATRIX(2,7); break; + case 'Y': result = MATRIX(3,1); break; + case 'Z': result = MATRIX(1,4); break; + + } + + if (result != -1 && GetKeyState(VK_CAPITAL)) + result |= 0x80; + + return result; +} + +BOOL C64Display::SetupWindow() +{ + // Setup the window. + SetupWindowMode(full_screen); + + UpdateWindow(hwnd); + + if (full_screen) + ShowCursor(FALSE); + + return TRUE; +} + +BOOL C64Display::SetupWindowMode(BOOL full_screen_mode) +{ + DWORD style; + int x0, y0, x, y; + if (full_screen_mode) { + style = fullscreen_style; + x0 = 0; + y0 = 0; + x = GetSystemMetrics(SM_CXSCREEN); + y = GetSystemMetrics(SM_CYSCREEN); + } + else { + style = windowed_style; + x0 = rcLast.left; + y0 = rcLast.top; + x = rcLast.right - rcLast.left; + y = rcLast.bottom - rcLast.top; + } + SetWindowLong(hwnd, GWL_STYLE, style); + SetWindowPos(hwnd, NULL, x0, y0, x, y, SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + GetClientRect(hwnd, &rcWindow); + if (!full_screen_mode && ThePrefs.ShowLEDs) + rcWindow.bottom -= led_rows; + ClientToScreen(hwnd, (LPPOINT) &rcWindow); + ClientToScreen(hwnd, (LPPOINT) &rcWindow + 1); + + // Windowed mode has a menu, full screen mode doesn't. + HMENU old_menu = GetMenu(hwnd); + if (old_menu) { + SetMenu(hwnd, NULL); + DestroyMenu(old_menu); + } + if (!full_screen_mode) { + HMENU new_menu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU)); + SetMenu(hwnd, new_menu); + } + + return TRUE; +} + +BOOL C64Display::RestoreWindow() +{ + if (full_screen) + ShowCursor(TRUE); + + if (!full_screen) + GetWindowRect(hwnd, &rcLast); + + SetupWindowMode(FALSE); + + return TRUE; +} + +HRESULT CALLBACK C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD, LPVOID lpContext) +{ + C64Display *pDisplay = (C64Display *) lpContext; + return pDisplay->EnumModesCallback(pDDSD); +} + +HRESULT C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD) +{ + DisplayMode mode; + mode.x = pDDSD->dwWidth; + mode.y = pDDSD->dwHeight; + mode.depth = pDDSD->ddpfPixelFormat.dwRGBBitCount; + mode.modex = (pDDSD->ddsCaps.dwCaps & DDSCAPS_MODEX) != 0; + Debug("EnumModesCallback: %dx%dx%d (modex: %d)\n", + mode.x, mode.y, mode.depth, mode.modex); + if (display_modes == NULL) + display_modes = new DisplayMode[max_display_modes]; + if (num_display_modes == max_display_modes) { + int old_max = max_display_modes; + max_display_modes *= 2; + DisplayMode *new_modes = new DisplayMode[max_display_modes]; + memcpy(new_modes, display_modes, sizeof(DisplayMode)*old_max); + delete[] display_modes; + display_modes = new_modes; + } + display_modes[num_display_modes++] = mode; + return DDENUMRET_OK; +} + +int C64Display::CompareModes(const void *e1, const void *e2) +{ + DisplayMode *m1 = (DisplayMode *) e1; + DisplayMode *m2 = (DisplayMode *) e2; + if (m1->depth != m2->depth) + return m1->depth - m2->depth; + if (m1->x != m2->x) + return m1->x - m2->x; + if (m1->y != m2->y) + return m1->y - m2->y; + if (m1->modex != m2->modex) + return int(m1->modex) - int(m2->modex); + return 0; +} + +BOOL C64Display::StartDirectDraw() +{ + // Setup our window size, position, style, etc. + SetupWindow(); + + // Create the main DirectDraw object. + HRESULT ddrval = DirectDrawCreate(NULL, &pDD, NULL); + if (ddrval != DD_OK) { + DebugResult("DirectDrawCreate failed", ddrval); + return Fail("Failed to initialize direct draw."); + } + + if (full_screen) { + + // Set exclusive mode. + ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX); + if (ddrval != DD_OK) { + DebugResult("SetCooperativeLevel failed", ddrval); + return Fail("Failed to set exclusive cooperative level."); + } + + if (!display_modes) { + + // Get all available video modes and sort them. + num_display_modes = 0; + pDD->EnumDisplayModes(0, NULL, this, EnumModesCallback); + qsort(display_modes, num_display_modes, sizeof(DisplayMode), CompareModes); + } + + // Set the video mode. + const char *display_mode = ThePrefs.DisplayMode; + if (display_mode[0] == '\0' || + stricmp(display_mode, "Default") == 0) + display_mode = NULL; + if (display_mode) { + int x, y, depth = 8; + if (sscanf(display_mode, "%dx%dx%d", &x, &y, &depth) < 2) + return Fail("Invalid command line mode format."); + ddrval = pDD->SetDisplayMode(x, y, depth); + if (ddrval != DD_OK) { + DebugResult("SetDisplayMode failed", ddrval); + return Fail("Failed to set the video mode."); + } + } + else { + for (int i = 0; i < num_display_modes; i++) { + DisplayMode *mode = &display_modes[i]; + if (mode->x < view_x || mode->y < view_y) + continue; + ddrval = pDD->SetDisplayMode(mode->x, mode->y, mode->depth); + if (ddrval == DD_OK) + break; + } + if (i == num_display_modes) + return Fail("Failed to find a suitable video mode."); + } + } + else { + + // Set normal mode. + ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL); + if (ddrval != DD_OK) + return Fail("Failed to set normal cooperative level."); + } + + // Create the primary surface with one back buffer. + DDSURFACEDESC ddsd; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 1; + ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL); + if (ddrval != DD_OK) { + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL); + if (ddrval != DD_OK) + return Fail("Failed to create primary surface."); + } + + if (ddsd.dwBackBufferCount == 1) { + DDSCAPS ddscaps; + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + ddrval = pPrimary->GetAttachedSurface(&ddscaps, &pBack); + if (ddrval != DD_OK) + return Fail("Failed to get attached surface."); + } + + // Create work surface. It displays correctly without + // this but doesn't handle clipping. We would have to + // do that ourselves. + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + if (ThePrefs.SystemMemory) + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + ddsd.dwHeight = DISPLAY_Y; + ddsd.dwWidth = DISPLAY_X; + ddrval = pDD->CreateSurface(&ddsd, &pWork, NULL); + if (ddrval != DD_OK) { + //return Fail("Failed to create work surface."); + Debug("cannot create work surface: %d\n", ddrval); + } + if (pWork) { + pWork->GetCaps(&ddsd.ddsCaps); + if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) + Debug("Work surface is in video memory.\n"); + else if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) + Debug("Work surface is in system memory.\n"); + else + Debug("Work surface is in unknown memory.\n"); + } + + if (!full_screen) { + + // Create clipper object. + ddrval = pDD->CreateClipper(0, &pClipper, NULL); + if (ddrval != DD_OK) + return Fail("Failed to create direct draw clipper."); + ddrval = pClipper->SetHWnd(0, hwnd); + if (ddrval != DD_OK) + return Fail("Failed setting clipper window handle."); + ddrval = pPrimary->SetClipper(pClipper); + if (ddrval != DD_OK) + return Fail("Failed setting primary surface clipper."); + } + + // We need to use a 256 color palette otherwise we get an + // invalid pixel format error when trying to set the palette + // on a windowed surface. + PALETTEENTRY ape[256]; + HDC hdc = GetDC(NULL); + int entries = GetSystemPaletteEntries(hdc, 0, 256, ape); + ReleaseDC(NULL, hdc); + if (entries != 256) { + Debug("failed to get 256 system palette entries: %d (%d)\n", + entries, GetLastError()); + + // Build a 332 palette as the default. This makes it easy for + // other apps to find colors when they aren't the foreground. + for (int i = 0; i < 256; i++) { + ape[i].peRed = (BYTE)(((i >> 5) & 0x07) * 255 / 7); + ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7); + ape[i].peBlue = (BYTE)(((i >> 0) & 0x03) * 255 / 3); + ape[i].peFlags = 0; + } + } + + // Now override the first 16 entries with the C64 colors. + // If we were really obsessive we could try to find the + // nearest matches and replace them instead. + for (int i = 0; i < 16; i++) { + ape[i].peRed = palette_red[i]; + ape[i].peGreen = palette_green[i]; + ape[i].peBlue = palette_blue[i]; + ape[i].peFlags = 0; + } + + // Create the palette and set it on all surfaces. + ddrval = pDD->CreatePalette(DDPCAPS_8BIT, ape, &pPalette, NULL); + if (ddrval != DD_OK) + return Fail("Failed to create palette."); + if (!SetPalettes()) + return Fail("Failed to set palettes."); + if (!BuildColorTable()) + return Fail("Failed to build color table."); + + // Start with a clean slate. + if (!EraseSurfaces()) { + // Some display drivers have bugs, I guess. + // What's a little problem erasing gonna hurt. +#if 0 + return Fail("Failed to erase surfaces."); +#endif + } + + + return TRUE; +} + +BOOL C64Display::ResumeDirectDraw() +{ + if (!RestoreSurfaces()) + ResetDirectDraw(); + + return TRUE; +} + +BOOL C64Display::ResetDirectDraw() +{ + Pause(); + StopDirectDraw(); + StartDirectDraw(); + Resume(); + if (paused) + Update(); + + return TRUE; +} + +BOOL C64Display::StopDirectDraw() +{ + if (pDD != NULL) { + if (pClipper != NULL) { + pClipper->Release(); + pClipper = NULL; + } + if (pWork != NULL) { + pWork->Release(); + pWork = NULL; + } + if (pBack != NULL) { + pBack->Release(); + pBack = NULL; + } + if (pPrimary != NULL) { + pPrimary->Release(); + pPrimary = NULL; + } + if (pPalette != NULL) { + pPalette->Release(); + pPalette = NULL; + } + pDD->RestoreDisplayMode(); + pDD->Release(); + pDD = NULL; + } + + // Restore windowing state, window position, etc. + RestoreWindow(); + + return TRUE; +} + + +/* + * This function is called if the initialization function fails + */ +BOOL C64Display::Fail(const char *error) +{ + Debug(error); + Debug("\n"); + strcpy(failure_message, error); + return FALSE; +} + + +BOOL C64Display::SetPalettes() +{ + // Only try to set palettes when in 256 color mode. + HDC hdc = GetDC(NULL); + int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL); + ReleaseDC(NULL, hdc); + if (depth != 8) + return TRUE; + + // Set palette on primary surface. + HRESULT ddrval = pPrimary->SetPalette(pPalette); + if (ddrval == DDERR_SURFACELOST) { + pPrimary->Restore(); + ddrval = pPrimary->SetPalette(pPalette); + } + if (ddrval == DDERR_NOT8BITCOLOR) + return TRUE; + if (ddrval != DD_OK) { + DebugResult("failed to set palette on primary", ddrval); + return FALSE; + } + + // Set palette on back surface. + if (pBack) { + FlipSurfaces(); + pPrimary->SetPalette(pPalette); + if (ddrval == DDERR_SURFACELOST) { + pPrimary->Restore(); + ddrval = pPrimary->SetPalette(pPalette); + } + if (ddrval != DD_OK) { + DebugResult("failed to set palette on back", ddrval); + return FALSE; + } + } + + // Set palette on work surface. + if (pWork) { + ddrval = pWork->SetPalette(pPalette); + if (ddrval == DDERR_SURFACELOST) { + pWork->Restore(); + ddrval = pWork->SetPalette(pPalette); + } + if (ddrval != DD_OK) { + DebugResult("failed to set palette on work", ddrval); + return FALSE; + } + } + + return TRUE; +} + +BOOL C64Display::BuildColorTable() +{ + if (!pPrimary) + return FALSE; + + // Determine the physical colors corresponding to the 16 C64 colors. + for (int j = 0; j < 16; j++) { + + // Compute the true color in RGB format. + int red = palette_red[j]; + int green = palette_green[j]; + int blue = palette_blue[j]; + COLORREF rgb = RGB(red, green, blue); + + // Set pixel(0, 0) to that value. + LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary; + HDC hdc; + if (pSurface->GetDC(&hdc) != DD_OK) + return Fail("Failed getting direct draw device context."); + COLORREF new_rgb = SetPixel(hdc, 0, 0, PALETTERGB(red, green, blue)); + Debug("new: %.8x, old %.8x\n", new_rgb, rgb); + pSurface->ReleaseDC(hdc); + + // Read the physical color from linear memory. + DDSURFACEDESC ddsd; + ddsd.dwSize = sizeof(ddsd); + HRESULT ddrval; + for (;;) { + ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL); + if (ddrval != DDERR_WASSTILLDRAWING) + break; + } + if (ddrval != DD_OK) + return Fail("Failed to lock surface."); + colors_depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + DWORD dw = *(DWORD *) ddsd.lpSurface; + Debug("DWORD = %.8x, depth = %d\n", dw, colors_depth); + if (colors_depth != 32) + dw &= (1 << colors_depth) - 1; + pSurface->Unlock(NULL); + + // Store the physical color in the colors array. + colors[j] = dw; + Debug("colors[%d] = %d\n", j, dw); + } + + // Replicate the physical colors into the rest of the color array. + for (int k = 16; k < 256; k++) + colors[k] = colors[k & 0x0f]; + + // Tell the VIC all about it; + if (!in_constructor) + TheC64->TheVIC->ReInitColors(); + + return TRUE; +} + +/* + * Redraw bitmap using double buffering when possible. + */ + +void C64Display::Update() +{ + TIMESCOPE(ts0, "Update"); + + //Debug("Display::Update\n"); + + if (full_screen && !active) + return; + + if (!pPrimary) + return; + +#ifdef WORKBUFFER_BITMAP + // Special case for using the workbuffer as a bitmap. + if (workbuffer_bitmap) { + if (workbuffer_locked) { + pWork->Unlock(NULL); + workbuffer_locked = FALSE; + } + RECT rc; + rc.left = (DISPLAY_X - view_x)/2; + rc.top = (DISPLAY_Y - view_y)/2 - 1; + if (rc.top < 0) + rc.top = 0; + rc.right = rc.left + view_x; + rc.bottom = rc.top + view_y; + CopySurface(rc); + draw_leds(); + return; + + } +#endif + + // Work on the backing surface unless there isn't one. + // We'll flip to it when we're done. + LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary; + + // Use a work surface when we have to: + // * when always copy is on + // * when possibly clipped + // * when streching + // * when partially offscreen + + if (!full_screen && pWork) { + if (ThePrefs.AlwaysCopy || !active || paused || +#if 0 + GetForegroundWindow() != hwnd || +#endif + rcWindow.right - rcWindow.left != view_x || + rcWindow.bottom - rcWindow.top != view_y || + rcWindow.left < rcScreen.left || + rcWindow.top < rcScreen.top || + rcWindow.right > rcScreen.right || + rcWindow.bottom > rcScreen.bottom) { + pSurface = pWork; + //Debug("using work surface\n"); + } + } + + // Lock the surface. + DDSURFACEDESC ddsd; + ddsd.dwSize = sizeof(ddsd); + + for (;;) { + HRESULT ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL); + if (ddrval == DD_OK) + break; + if (ddrval == DDERR_SURFACELOST) { + Debug("surface lost\n"); + if (pSurface == pWork) + ddrval = pWork->Restore(); + else + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("surface Restore failed", ddrval); + return; + } + EraseSurfaces(); + BuildColorTable(); + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + if (pWork && pSurface != pWork) + pSurface = pWork; + else { + DebugResult("surface Lock failed", ddrval); + return; + } + } + Debug("was still drawing\n"); + } + + // Compute the optimal placement of our window depending on + // the screen dimensions. + int x_off, y_off; + int x_beg, y_beg; + int x_siz, y_siz; + + // XXX: Do these calculations only when the parameters change. + if (full_screen) { + if (rcWindow.right >= view_x) { + x_off = (rcWindow.right - view_x)/2; + x_beg = (DISPLAY_X - view_x)/2; + x_siz = view_x; + } + else { + x_off = 0; + x_beg = (DISPLAY_X - rcWindow.right)/2; + x_siz = rcWindow.right; + } + if (rcWindow.bottom >= view_y) { + y_off = (rcWindow.bottom - view_y)/2; + y_beg = (DISPLAY_Y - view_y)/2 - 1; + y_siz = view_y; + } + else { + y_off = 0; + y_beg = (DISPLAY_Y - rcWindow.bottom)/2 - 1; + y_siz = rcWindow.bottom; + } + } + else { + if (pSurface == pWork) { + x_off = 0; + y_off = 0; + } + else { + x_off = rcWindow.left; + y_off = rcWindow.top; + } + x_beg = (DISPLAY_X - view_x)/2; + y_beg = (DISPLAY_Y - view_y)/2 - 1; + x_siz = view_x; + y_siz = view_y; + } + if (y_beg < 0) + y_beg = 0; + + // Translate chunky colors into the surface's linear memory. + int pitch = ddsd.lPitch; + int depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + BYTE *surface = (BYTE *) ddsd.lpSurface + pitch*y_off + x_off*(depth/8); + BYTE *chunky = chunky_buf + DISPLAY_X*y_beg + x_beg; + + // These tight loops are where the display speed action is at. + // Note that MSVC optimizes out the mulitiplications and + // reverses the direction of the loop counters automatically. + if (depth == 8) { + + // Since the VIC is using our palette entries we just copy. + //TIMESCOPE(ts1, "hand blt 8"); + BYTE *scanline = surface; + BYTE *scanbuf = chunky; + //Debug("scanline = %8p, scanbuf = %8p\n", scanline, scanbuf); + for (int j = 0; j < y_siz; j++) { + memcpy(scanline, scanbuf, x_siz); + scanline += pitch; + scanbuf += DISPLAY_X; + } + } + else if (depth == 16) { + //TIMESCOPE(ts1, "hand blt 16"); + for (int j = 0; j < y_siz; j++) { + WORD *scanline = (WORD *) (surface + pitch*j); + BYTE *scanbuf = chunky + +DISPLAY_X*j; + for (int i = 0; i < x_siz; i++) + *scanline++ = (WORD) colors[*scanbuf++]; + } + } + else if (depth == 24) { + + // XXX: Works for little-endian only. + //TIMESCOPE(ts1, "hand blt 24"); + for (int j = 0; j < y_siz; j++) { + BYTE *scanline = surface + pitch*j; + BYTE *scanbuf = chunky + +DISPLAY_X*j; + for (int i = 0; i < x_siz; i++) { + *((DWORD *) scanline) = colors[*scanbuf++]; + scanline += 3; + } + } + } + else if (depth == 32) { + //TIMESCOPE(ts1, "hand blt 32"); + for (int j = 0; j < y_siz; j++) { + DWORD *scanline = (DWORD *) (surface + pitch*j); + BYTE *scanbuf = chunky + +DISPLAY_X*j; + for (int i = 0; i < x_siz; i++) + *scanline++ = colors[*scanbuf++]; + } + } + else + Debug("PixelCount not 8, 16, 24, or 32\n"); + + // Unlock the surface. + HRESULT ddrval = pSurface->Unlock(NULL); + if (ddrval != DD_OK) + Debug("DirectDrawSurface::Unlock failed\n"); + + // Now flip from the primary surface to the backing surface. + if (pSurface == pWork) + CopySurface(rcWork); + else if (full_screen && pBack) + FlipSurfaces(); + + // Update drive LEDs + draw_leds(); +} + + +BOOL C64Display::CopySurface(RECT &rcWork) +{ + // Copy work surface to primary. + for (;;) { + HRESULT ddrval = pPrimary->Blt(&rcWindow, pWork, &rcWork, DDBLT_WAIT, NULL); + if (ddrval == DD_OK) + break; + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("CopySurface Restore failed", ddrval); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + DebugResult("CopySurface Blt failed", ddrval); + return FALSE; + } + } + return TRUE; +} + +BOOL C64Display::FlipSurfaces() +{ + // Flip buffers. + for (;;) { + HRESULT ddrval = pPrimary->Flip(NULL, 0); + if (ddrval == DD_OK) + break; + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + Debug("Restore failed\n"); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) + return FALSE; + } + return TRUE; +} + +BOOL C64Display::EraseSurfaces() +{ + DDBLTFX ddbltfx; + ddbltfx.dwSize = sizeof(ddbltfx); + ddbltfx.dwFillColor = 0; + + // Erase the backing surface. + for (;;) { + if (!pBack) + break; + HRESULT ddrval = pBack->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx); + + if (ddrval == DD_OK) + break; + + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("Restore primary failed", ddrval); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + DebugResult("Blt erase back failed", ddrval); + return FALSE; + } + } + + // Erase the primary surface. + for (;;) { + HRESULT ddrval = pPrimary->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx); + + if (ddrval == DD_OK) + break; + + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("Restore primary failed", ddrval); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + DebugResult("Blt erase primary failed", ddrval); + return FALSE; + } + } + + return TRUE; +} + +BOOL C64Display::RestoreSurfaces() +{ + if (pPrimary) { + HRESULT ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) + return FALSE; + } + + if (pWork) { + HRESULT ddrval = pWork->Restore(); + if (ddrval != DD_OK) + return FALSE; + } + + return TRUE; +} + +/* + * Draw LED bar at the bottom of the window + */ + +void C64Display::draw_led_bar() +{ + if (full_screen || !ThePrefs.ShowLEDs) + return; + + HDC hdc = GetDC(hwnd); + RECT rc; + GetClientRect(hwnd, &rc); + rc.top = rc.bottom - led_rows; + FillRect(hdc, &rc, led_brush); + if (rc.right - rc.left > view_x) + rc.left = rc.right - view_x; + SelectObject(hdc, led_font); + SetTextAlign(hdc, TA_TOP | TA_RIGHT); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT)); + for (int i = 0; i < 4; i++) { + char str[128]; + if (rc.right - rc.left < view_x) + sprintf(str, "%d", i + 8); + else + sprintf(str, "Drive %d", i + 8); + RECT led; + led_rect(i, rc, led); + SelectObject(hdc, led_shadow); + MoveToEx(hdc, led.left - 1, led.bottom - 1, NULL); + LineTo(hdc, led.left - 1, led.top - 1); + LineTo(hdc, led.right, led.top - 1); + SelectObject(hdc, led_highlight); + LineTo(hdc, led.right, led.bottom); + LineTo(hdc, led.left - 2, led.bottom); + TextOut(hdc, led.left - 4, rc.top + 2, str, strlen(str)); + } + ReleaseDC(hwnd, hdc); + draw_leds(TRUE); +} + +/* + * Draw one LED + */ + +void C64Display::draw_leds(BOOL force) +{ + if (full_screen || !ThePrefs.ShowLEDs) + return; + + if (!force) { + int i; + for (i = 0; i < 4; i++) { + if (led_state[i] != old_led_state[i]) + break; + } + if (i == 4) + return; + } + + HDC hdc = GetDC(hwnd); + RECT rc; + GetClientRect(hwnd, &rc); + rc.top = rc.bottom - led_rows; + if (rc.right - rc.left > view_x) + rc.left = rc.right - view_x; + for (int i = 0; i < 4; i++) { + old_led_state[i] = led_state[i]; + HBRUSH brush; + switch (led_state[i]) { + case LED_OFF: brush = off_brush; break; + case LED_ERROR_OFF: brush = error_off_brush; break; + case LED_ON: brush = on_brush; break; + case LED_ERROR_ON: brush = error_on_brush; break; + } + RECT led; + led_rect(i, rc, led); + FillRect(hdc, &led, brush); + } + ReleaseDC(hwnd, hdc); +} + +void C64Display::led_rect(int n, RECT &rc, RECT &led) +{ + int x = rc.left + (rc.right - rc.left)*(n + 2)/5 - 20; + int y = rc.top + 2 + led_rows/3; + SetRect(&led, x, y, x + 13, y + led_rows/3); +} + +void C64Display::InsertNextDisk() +{ + if (strlen(ThePrefs.DrivePath[0]) > 4) { + char str[256]; + strcpy(str, ThePrefs.DrivePath[0]); + char *p = str + strlen(str) - 5; + + // If path matches "*.?64", increment character before the '.' + if (p[1] == '.' && p[3] == '6' && p[4] == '4') { + p[0]++; + + // If no such file exists, set character before the '.' to '1', 'a' or 'A' + FILE *file; + if ((file = fopen(str, "rb")) == NULL) { + if (isdigit(p[0])) + p[0] = '1'; + else if (isupper(p[0])) + p[0] = 'A'; + else + p[0] = 'a'; + } else + fclose(file); + + // Set new prefs + Pause(); + Prefs *prefs = new Prefs(ThePrefs); + strcpy(prefs->DrivePath[0], str); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + Resume(); + } + } +} + +BOOL C64Display::FileNameDialog(char *prefs_path, BOOL save) +{ + char filename[256]; + strcpy(filename, prefs_path); + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwnd; + ofn.hInstance = hInstance; + ofn.lpstrFilter = + "Preferences Files (*.fpr)\0*.fpr\0" + "All Files (*.*)\0*.*\0" + ; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = NULL; + ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE | + OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_SHAREAWARE; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = "fpr"; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + BOOL result = save ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn); + if (result) { + char cwd[256]; + GetCurrentDirectory(sizeof(cwd), cwd); + int cwd_len = strlen(cwd); + if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') { + strcat(cwd, "\\"); + cwd_len++; + } + if (strnicmp(filename, cwd, cwd_len) == 0) + strcpy(prefs_path, filename + cwd_len); + else + strcpy(prefs_path, filename); + } + return result; +} + +void C64Display::WindowTitle() +{ + // Show the program name, the current preferences file, + // and the paused state or the speedometer. + const char *prefs_path = TheApp->prefs_path; + int prefs_path_length = strlen(prefs_path); + if (prefs_path_length > 4 && + stricmp(prefs_path + prefs_path_length - 4, ".fpr") == 0) + prefs_path_length -= 4; + const char *info = NULL; + char tmp[128]; + if (waiting) + info = "PAUSED"; + else if (!ThePrefs.ShowLEDs && speed_index != 0) { + if (IsFrodoSC) + sprintf(tmp, "%.1f%%", speed_index); + else + sprintf(tmp, "%.0f%%", speed_index); + info = tmp; + } + const char *sep1 = info ? " (" : ""; + const char *sep2 = info ? ")" : ""; + char title[256]; + sprintf(title, "%s - %.*s%s%s%s", TITLE, + prefs_path_length, prefs_path, sep1, info ? info : "", sep2); + SetWindowText(hwnd, title); +} + +void C64Display::NewPrefs() +{ + // Resize the window to the new viewport while preserving + // as closely as possible the previous scaling factors. + RECT rc; + GetWindowRect(hwnd, &rc); + int x_nc = rc.right - rc.left - (rcWindow.right - rcWindow.left); + int y_nc = rc.bottom - rc.top - (rcWindow.bottom - rcWindow.top); + if (show_leds) + y_nc -= led_rows; + double x_scale = double(rcWindow.right - rcWindow.left)/view_x; + double y_scale = double(rcWindow.bottom - rcWindow.top)/view_y; + if (CalcViewPort() || show_leds != ThePrefs.ShowLEDs) { + show_leds = ThePrefs.ShowLEDs; + rc.right = int(rc.left + x_scale*view_x + x_nc); + rc.bottom = int(rc.top + y_scale*view_y + y_nc); + if (show_leds) + rc.bottom += led_rows; + ResizeWindow(WMSZ_BOTTOMRIGHT, &rc); + MoveWindow(hwnd, rc.left, rc.top, + rc.right - rc.left, + rc.bottom - rc.top, TRUE); + } + + // The prefs filename might have changed. + WindowTitle(); +} + +void C64Display::OfferSave() +{ + if (ThePrefs == ThePrefsOnDisk) + return; + const char *str = "Preferences have changed.\nSave preferences now?"; + int result = MessageBox(hwnd, str, "Frodo", MB_YESNO | MB_ICONQUESTION); + if (result == IDYES) + ThePrefs.Save(TheApp->prefs_path); +} + +void C64Display::Pause() +{ + // It's not safe to call this from the contructor or destructor. + if (in_constructor || in_destructor) + return; + + if (paused == 0) + TheC64->Pause(); + paused++; +} + +void C64Display::Resume() +{ + // It's not safe to call this from the contructor or destructor. + if (in_constructor || in_destructor) + return; + + if (paused > 0) { + paused--; + if (!paused) + TheC64->Resume(); + } + else + _ASSERTE(paused > 0); +} + +void C64Display::Quit() +{ + quit = 1; + TheC64->Quit(); +} diff --git a/Src/Display_svga.i b/Src/Display_svga.i new file mode 100644 index 0000000..29f0874 --- /dev/null +++ b/Src/Display_svga.i @@ -0,0 +1,556 @@ +/* + * Display_svga.i - C64 graphics display, emulator window handling, + * SVGAlib specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * SVGAlib stuff by Bernd Schmidt + */ + +#include +#include +#include + +#include "C64.h" + + +#define SCODE_CURSORBLOCKUP 103 /* Cursor key block. */ +#define SCODE_CURSORBLOCKLEFT 105 +#define SCODE_CURSORBLOCKRIGHT 106 +#define SCODE_CURSORBLOCKDOWN 108 + +#define SCODE_INSERT 110 +#define SCODE_HOME 102 +#define SCODE_PGUP 104 +#define SCODE_DELETE 111 +#define SCODE_END 107 +#define SCODE_PGDN 109 + +#define SCODE_NUMLOCK 69 + +#define SCODE_KEYPAD0 82 +#define SCODE_KEYPAD1 79 +#define SCODE_KEYPAD2 80 +#define SCODE_KEYPAD3 81 +#define SCODE_KEYPAD4 75 +#define SCODE_KEYPAD5 76 +#define SCODE_KEYPAD6 77 +#define SCODE_KEYPAD7 71 +#define SCODE_KEYPAD8 72 +#define SCODE_KEYPAD9 73 +#define SCODE_KEYPADENTER 96 +#define SCODE_KEYPADPLUS 78 +#define SCODE_KEYPADMINUS 74 +#define SCODE_KEYPADMULTIPLY 55 +#define SCODE_KEYPADDIVIDE 98 + +#define SCODE_Q 16 +#define SCODE_W 17 +#define SCODE_E 18 +#define SCODE_R 19 +#define SCODE_T 20 +#define SCODE_Y 21 +#define SCODE_U 22 +#define SCODE_I 23 +#define SCODE_O 24 +#define SCODE_P 25 + +#define SCODE_A 30 +#define SCODE_S 31 +#define SCODE_D 32 +#define SCODE_F 33 +#define SCODE_G 34 +#define SCODE_H 35 +#define SCODE_J 36 +#define SCODE_K 37 +#define SCODE_L 38 + +#define SCODE_Z 44 +#define SCODE_X 45 +#define SCODE_C 46 +#define SCODE_V 47 +#define SCODE_B 48 +#define SCODE_N 49 +#define SCODE_M 50 + +#define SCODE_ESCAPE 1 +#define SCODE_ENTER 28 +#define SCODE_RIGHTCONTROL 97 +#define SCODE_CONTROL 97 +#define SCODE_RIGHTALT 100 +#define SCODE_LEFTCONTROL 29 +#define SCODE_LEFTALT 56 +#define SCODE_SPACE 57 + +#define SCODE_F1 59 +#define SCODE_F2 60 +#define SCODE_F3 61 +#define SCODE_F4 62 +#define SCODE_F5 63 +#define SCODE_F6 64 +#define SCODE_F7 65 +#define SCODE_F8 66 +#define SCODE_F9 67 +#define SCODE_F10 68 + +#define SCODE_0 11 +#define SCODE_1 2 +#define SCODE_2 3 +#define SCODE_3 4 +#define SCODE_4 5 +#define SCODE_5 6 +#define SCODE_6 7 +#define SCODE_7 8 +#define SCODE_8 9 +#define SCODE_9 10 + +#define SCODE_LEFTSHIFT 42 +#define SCODE_RIGHTSHIFT 54 +#define SCODE_TAB 15 + +#define SCODE_F11 87 +#define SCODE_F12 88 +#define SCODE_NEXT 81 +#define SCODE_PRIOR 73 +#define SCODE_BS 14 + +#define SCODE_asciicircum 41 +#define SCODE_bracketleft 26 +#define SCODE_bracketright 27 +#define SCODE_comma 51 +#define SCODE_period 52 +#define SCODE_slash 53 +#define SCODE_semicolon 39 +#define SCODE_grave 40 +#define SCODE_minus 12 +#define SCODE_equal 13 +#define SCODE_numbersign 43 +#define SCODE_ltgt 86 +#define SCODE_scrolllock 70 + +static int bitdepth; +static char *bufmem; +static int hsize; +static vga_modeinfo modeinfo; +static char *linear_mem; + +static int keystate[256]; +static int f11pressed = 0, f12pressed = 0, quit = 0; +static int joystate = 0xFF; +static int numlock = 0; +static UBYTE rev_matrix[8], key_matrix[8]; + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ +#define MATRIX(a,b) (((a) << 3) | (b)) +#define KEY_F10 512 +#define KEY_F11 513 +#define KEY_F12 514 + +#define KEY_FIRE 515 +#define KEY_JUP 516 +#define KEY_JDN 517 +#define KEY_JLF 518 +#define KEY_JRT 519 + +#define KEY_NUMLOCK 520 + +#define KEY_KPPLUS 521 +#define KEY_KPMINUS 522 +#define KEY_KPMULT 523 +#define KEY_KPDIV 524 + +static int scode2c64(int scancode) +{ + switch (scancode) { + case SCODE_asciicircum: return MATRIX(7,1); + case SCODE_KEYPAD0: return KEY_FIRE; + case SCODE_KEYPAD1: return -1; + case SCODE_KEYPAD2: return KEY_JDN; + case SCODE_KEYPAD3: return -1; + case SCODE_KEYPAD4: return KEY_JLF; + case SCODE_KEYPAD5: return -1; + case SCODE_KEYPAD6: return KEY_JRT; + case SCODE_KEYPAD7: return -1; + case SCODE_KEYPAD8: return KEY_JUP; + case SCODE_KEYPAD9: return -1; + + case SCODE_NUMLOCK: return KEY_NUMLOCK; + case SCODE_KEYPADMULTIPLY: return KEY_KPMULT; + case SCODE_KEYPADDIVIDE: return KEY_KPDIV; + case SCODE_KEYPADMINUS: return KEY_KPMINUS; + case SCODE_KEYPADPLUS: return KEY_KPPLUS; + case SCODE_KEYPADENTER: return MATRIX(0,1); + + case SCODE_F10: return KEY_F10; + case SCODE_F11: return KEY_F11; + case SCODE_F12: return KEY_F12; + + case SCODE_comma: return MATRIX(5,7); + case SCODE_period: return MATRIX(5,4); + + case SCODE_A: return MATRIX(1,2); + case SCODE_B: return MATRIX(3,4); + case SCODE_C: return MATRIX(2,4); + case SCODE_D: return MATRIX(2,2); + case SCODE_E: return MATRIX(1,6); + case SCODE_F: return MATRIX(2,5); + case SCODE_G: return MATRIX(3,2); + case SCODE_H: return MATRIX(3,5); + case SCODE_I: return MATRIX(4,1); + case SCODE_J: return MATRIX(4,2); + case SCODE_K: return MATRIX(4,5); + case SCODE_L: return MATRIX(5,2); + case SCODE_M: return MATRIX(4,4); + case SCODE_N: return MATRIX(4,7); + case SCODE_O: return MATRIX(4,6); + case SCODE_P: return MATRIX(5,1); + case SCODE_Q: return MATRIX(7,6); + case SCODE_R: return MATRIX(2,1); + case SCODE_S: return MATRIX(1,5); + case SCODE_T: return MATRIX(2,6); + case SCODE_U: return MATRIX(3,6); + case SCODE_V: return MATRIX(3,7); + case SCODE_W: return MATRIX(1,1); + case SCODE_X: return MATRIX(2,7); + case SCODE_Y: return MATRIX(3,1); + case SCODE_Z: return MATRIX(1,4); + + case SCODE_BS: return MATRIX(0,0); + case SCODE_DELETE: return MATRIX(0,0); + case SCODE_LEFTCONTROL: return MATRIX(7,2); + case SCODE_TAB: return MATRIX(7,1); + case SCODE_ENTER: return MATRIX(0,1); + case SCODE_SPACE: return MATRIX(7,4); + case SCODE_LEFTSHIFT: return MATRIX(1,7); + case SCODE_RIGHTSHIFT: return MATRIX(6,4); + case SCODE_ESCAPE: return MATRIX(7,7); + case SCODE_RIGHTCONTROL: + case SCODE_LEFTALT: + case SCODE_RIGHTALT: return MATRIX(7,5); + + case SCODE_INSERT: return MATRIX(0,0) | 0x80; + case SCODE_HOME: return MATRIX(6,3); + case SCODE_END: return MATRIX(6,0); + case SCODE_PGUP: return MATRIX(6,6); + case SCODE_PGDN: return MATRIX(6,5); + + case SCODE_CURSORBLOCKUP: return MATRIX(0,7)| 0x80; + case SCODE_CURSORBLOCKDOWN: return MATRIX(0,7); + case SCODE_CURSORBLOCKLEFT: return MATRIX(0,2) | 0x80; + case SCODE_CURSORBLOCKRIGHT: return MATRIX(0,2); + + case SCODE_F1: return MATRIX(0,4); + case SCODE_F2: return MATRIX(0,4) | 0x80; + case SCODE_F3: return MATRIX(0,5); + case SCODE_F4: return MATRIX(0,5) | 0x80; + case SCODE_F5: return MATRIX(0,6); + case SCODE_F6: return MATRIX(0,6) | 0x80; + case SCODE_F7: return MATRIX(0,3); + case SCODE_F8: return MATRIX(0,3) | 0x80; + + case SCODE_0: return MATRIX(4,3); + case SCODE_1: return MATRIX(7,0); + case SCODE_2: return MATRIX(7,3); + case SCODE_3: return MATRIX(1,0); + case SCODE_4: return MATRIX(1,3); + case SCODE_5: return MATRIX(2,0); + case SCODE_6: return MATRIX(2,3); + case SCODE_7: return MATRIX(3,0); + case SCODE_8: return MATRIX(3,3); + case SCODE_9: return MATRIX(4,0); + + case SCODE_bracketleft: return MATRIX(5,6); + case SCODE_bracketright: return MATRIX(6,1); + case SCODE_slash: return MATRIX(6,7); + case SCODE_semicolon: return MATRIX(5,5); + case SCODE_grave: return MATRIX(6,2); + case SCODE_numbersign: return MATRIX(6,5); + case SCODE_ltgt: return MATRIX(6,6); + case SCODE_minus: return MATRIX(5,0); + case SCODE_equal: return MATRIX(5,3); + } +} + +static void my_kbd_handler(int scancode, int newstate) +{ + int kc = scode2c64(scancode); +#if 0 + if (kc == -1) { + printf("%d\n",kc); + return; + } +#endif + if (newstate == KEY_EVENTPRESS) { + switch (kc) { + case KEY_KPPLUS: + if (ThePrefs.SkipFrames < 10) + ThePrefs.SkipFrames++; + break; + + case KEY_KPMINUS: + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case KEY_KPMULT: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case KEY_KPDIV: + ThePrefs.JoystickSwap = !ThePrefs.JoystickSwap; + break; + + case KEY_NUMLOCK: + numlock = !numlock; + break; + + case KEY_F10: + quit = 1; + break; + + case KEY_F11: + f11pressed = 1; + break; + + case KEY_F12: + f12pressed = 1; + break; + + case KEY_FIRE: + joystate &= ~0x10; + break; + + case KEY_JDN: + joystate &= ~0x2; + break; + + case KEY_JUP: + joystate &= ~0x1; + break; + + case KEY_JLF: + joystate &= ~0x4; + break; + + case KEY_JRT: + joystate &= ~0x8; + break; + + default: + if (keystate[kc]) + break; + keystate[kc] = 1; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + break; + } + } else { + switch (kc) { + case KEY_FIRE: + joystate |= 0x10; + break; + + case KEY_JDN: + joystate |= 0x2; + break; + + case KEY_JUP: + joystate |= 0x1; + break; + + case KEY_JLF: + joystate |= 0x4; + break; + + case KEY_JRT: + joystate |= 0x8; + break; + + default: + if (!keystate[kc]) + break; + keystate[kc] = 0; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + break; + } + + } +} + + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + quit_requested = false; +} + + +C64Display::~C64Display() +{ + sleep(1); + vga_setmode(TEXT); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +void C64Display::Speedometer(int speed) +{ +} + + +int init_graphics(void) +{ + int vgamode = G640x480x256; + modeinfo = *vga_getmodeinfo (vgamode); + + if (vga_setmode(vgamode) < 0) { + sleep(1); + vga_setmode(TEXT); + fprintf(stderr, "SVGAlib doesn't like my video mode. Giving up.\n"); + return 0; + } + + hsize = modeinfo.linewidth; + if (hsize < DISPLAY_X) + hsize = DISPLAY_X; + + bufmem = NULL; + if ((modeinfo.flags & CAPABLE_LINEAR) && modeinfo.linewidth >= DISPLAY_X) { + if (vga_setlinearaddressing() != -1) { + linear_mem = (char *)vga_getgraphmem(); + printf("Using linear addressing: %p.\n", linear_mem); + bufmem = linear_mem; + } + } + if (bufmem == NULL) + bufmem = (char *)malloc(hsize * DISPLAY_Y); + + if (keyboard_init() != 0) + abort(); + keyboard_seteventhandler(my_kbd_handler); + /* keyboard_translatekeys(DONT_CATCH_CTRLC);*/ + + memset(keystate, 0, sizeof(keystate)); + memset(key_matrix, 0xFF, 8); + memset(rev_matrix, 0xFF, 8); + return 1; +} + + +void C64Display::Update(void) +{ + int y; + + if (linear_mem) + return; + + for (y = 0; y < DISPLAY_Y; y++) { + vga_drawscanline(y, bufmem + hsize * y); + } +} + + +UBYTE *C64Display::BitmapBase(void) +{ + return (UBYTE *)bufmem; +} + + +int C64Display::BitmapXMod(void) +{ + return hsize; +} + + +void C64Display::PollKeyboard(UBYTE *CIA_key_matrix, UBYTE *CIA_rev_matrix, UBYTE *joystick) +{ + keyboard_update(); + *joystick = joystate; + memcpy(CIA_key_matrix, key_matrix, 8); + memcpy(CIA_rev_matrix, rev_matrix, 8); + if (f11pressed) + TheC64->NMI(); + if (f12pressed) + TheC64->Reset(); + if (quit) + quit_requested = true; + f11pressed = f12pressed = 0; +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return numlock; +} + + +/* + * Allocate C64 colors + */ + +static int colorval(int v) +{ + return ((v & 255)*0x01010101) >> 26; +} + +void C64Display::InitColors(UBYTE *colors) +{ + int i; + + for (i=0; i< 256; i++) { + vga_setpalette(i, colorval(palette_red[i & 0x0f]), colorval(palette_green[i & 0x0f]), colorval(palette_blue[i & 0x0f])); + colors[i] = i; + } +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(char *a,char *b,char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/Display_x.i b/Src/Display_x.i new file mode 100644 index 0000000..0c30afe --- /dev/null +++ b/Src/Display_x.i @@ -0,0 +1,793 @@ +/* + * Display_x.i - C64 graphics display, emulator window handling, + * X specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * X11 stuff by Bernd Schmidt/Lutz Vieweg + */ + +#include "SAM.h" +#include "C64.h" + +#include +#include +#include +#include + +#if defined(X_USE_SHM) +#include +#include +#include +static XShmSegmentInfo shminfo; +#endif + +static Display *display; +static int screen; +static Window rootwin, mywin; + +static GC black_gc, led_gc; +static XColor black, fill_gray, shine_gray, shadow_gray, red, green; +static Colormap cmap; +static Font led_font; + +static XImage *img; +static Visual *vis; +static XVisualInfo visualInfo; +static int bitdepth; +static char *bufmem; +static int hsize; + +// For LED error blinking +static C64Display *c64_disp; +static struct sigaction pulse_sa; +static itimerval pulse_tv; + +// Keyboard and joystick +static int keystate[256]; +static int joystate = 0xFF; +static bool num_locked = false; + +static const long int eventmask = (KeyPressMask|KeyReleaseMask|FocusChangeMask|ExposureMask); + + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + +#define KEY_F9 512 +#define KEY_F10 513 +#define KEY_F11 514 +#define KEY_F12 515 + +#ifdef SUN +#define KEY_FIRE 58 +#define KEY_JU 135 +#define KEY_JD 7 +#define KEY_JL 130 +#define KEY_JR 2 +#else +#define KEY_FIRE 516 +#define KEY_JU 517 +#define KEY_JD 518 +#define KEY_JL 519 +#define KEY_JR 520 +#endif + +#define KEY_JUL 521 +#define KEY_JUR 522 +#define KEY_JDL 523 +#define KEY_JDR 524 + +#define KEY_KP_PLUS 525 +#define KEY_KP_MINUS 526 +#define KEY_KP_MULT 527 +#define KEY_NUM_LOCK 528 + + +/* + * Decode KeySyms. This function knows about all keys that + * are common between different keyboard languages. + */ + +static int kc_decode(KeySym ks) +{ + switch (ks) { + case XK_A: case XK_a: return MATRIX(1,2); + case XK_B: case XK_b: return MATRIX(3,4); + case XK_C: case XK_c: return MATRIX(2,4); + case XK_D: case XK_d: return MATRIX(2,2); + case XK_E: case XK_e: return MATRIX(1,6); + case XK_F: case XK_f: return MATRIX(2,5); + case XK_G: case XK_g: return MATRIX(3,2); + case XK_H: case XK_h: return MATRIX(3,5); + case XK_I: case XK_i: return MATRIX(4,1); + case XK_J: case XK_j: return MATRIX(4,2); + case XK_K: case XK_k: return MATRIX(4,5); + case XK_L: case XK_l: return MATRIX(5,2); + case XK_M: case XK_m: return MATRIX(4,4); + case XK_N: case XK_n: return MATRIX(4,7); + case XK_O: case XK_o: return MATRIX(4,6); + case XK_P: case XK_p: return MATRIX(5,1); + case XK_Q: case XK_q: return MATRIX(7,6); + case XK_R: case XK_r: return MATRIX(2,1); + case XK_S: case XK_s: return MATRIX(1,5); + case XK_T: case XK_t: return MATRIX(2,6); + case XK_U: case XK_u: return MATRIX(3,6); + case XK_V: case XK_v: return MATRIX(3,7); + case XK_W: case XK_w: return MATRIX(1,1); + case XK_X: case XK_x: return MATRIX(2,7); + case XK_Y: case XK_y: return MATRIX(3,1); + case XK_Z: case XK_z: return MATRIX(1,4); + + case XK_0: return MATRIX(4,3); + case XK_1: return MATRIX(7,0); + case XK_2: return MATRIX(7,3); + case XK_3: return MATRIX(1,0); + case XK_4: return MATRIX(1,3); + case XK_5: return MATRIX(2,0); + case XK_6: return MATRIX(2,3); + case XK_7: return MATRIX(3,0); + case XK_8: return MATRIX(3,3); + case XK_9: return MATRIX(4,0); + + case XK_space: return MATRIX(7,4); + case XK_grave: return MATRIX(7,1); + case XK_backslash: return MATRIX(6,6); + case XK_comma: return MATRIX(5,7); + case XK_period: return MATRIX(5,4); + + case XK_Escape: return MATRIX(7,7); + case XK_Return: return MATRIX(0,1); + case XK_BackSpace: case XK_Delete: return MATRIX(0,0); + case XK_Insert: return MATRIX(6,3); + case XK_Home: case XK_Help: return MATRIX(6,3); + case XK_End: return MATRIX(6,0); +#ifdef __hpux + case XK_Prior: return MATRIX(6,0); + case XK_Next: return MATRIX(6,5); +#else + case XK_Page_Up: return MATRIX(6,0); + case XK_Page_Down: return MATRIX(6,5); +#endif + case XK_Control_L: return MATRIX(7,2); + case XK_Control_R: return MATRIX(7,5); + case XK_Shift_L: return MATRIX(1,7); + case XK_Shift_R: return MATRIX(6,4); + case XK_Alt_L: return MATRIX(7,5); + case XK_Alt_R: return MATRIX(7,5); + + case XK_Up: return MATRIX(0,7)| 0x80; + case XK_Down: return MATRIX(0,7); + case XK_Left: return MATRIX(0,2) | 0x80; + case XK_Right: return MATRIX(0,2); + + case XK_F1: return MATRIX(0,4); + case XK_F2: return MATRIX(0,4) | 0x80; + case XK_F3: return MATRIX(0,5); + case XK_F4: return MATRIX(0,5) | 0x80; + case XK_F5: return MATRIX(0,6); + case XK_F6: return MATRIX(0,6) | 0x80; + case XK_F7: return MATRIX(0,3); + case XK_F8: return MATRIX(0,3) | 0x80; + + case XK_F9: return KEY_F9; + case XK_F10: return KEY_F10; + case XK_F11: return KEY_F11; + case XK_F12: return KEY_F12; + + /* You never know which Keysyms might be missing on some workstation + * This #ifdef should be enough. */ +#if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End) + case XK_KP_0: case XK_KP_Insert: return KEY_FIRE; + case XK_KP_1: case XK_KP_End: return KEY_JDL; + case XK_KP_2: case XK_KP_Down: return KEY_JD; + case XK_KP_3: case XK_KP_Next: return KEY_JDR; + case XK_KP_4: case XK_KP_Left: return KEY_JL; + case XK_KP_5: case XK_KP_Begin: return KEY_FIRE; + case XK_KP_6: case XK_KP_Right: return KEY_JR; + case XK_KP_7: case XK_KP_Home: return KEY_JUL; + case XK_KP_8: case XK_KP_Up: return KEY_JU; + case XK_KP_9: case XK_KP_Prior: return KEY_JUR; +#else + case XK_KP_0: return KEY_FIRE; + case XK_KP_1: return KEY_JDL; + case XK_KP_2: return KEY_JD; + case XK_KP_3: return KEY_JDR; + case XK_KP_4: return KEY_JL; + case XK_KP_5: return KEY_FIRE; + case XK_KP_6: return KEY_JR; + case XK_KP_7: return KEY_JUL; + case XK_KP_8: return KEY_JU; + case XK_KP_9: return KEY_JUR; +#endif + + case XK_KP_Add: return KEY_KP_PLUS; + case XK_KP_Subtract: return KEY_KP_MINUS; + case XK_KP_Multiply: return KEY_KP_MULT; + case XK_KP_Divide: return MATRIX(6,7); + case XK_KP_Enter: return MATRIX(0,1); + +#ifdef SUN + case XK_Num_Lock: return KEY_NUM_LOCK; +#endif + } + return -1; +} + +static int decode_us(KeySym ks) +{ + switch(ks) { /* US specific */ + case XK_minus: return MATRIX(5,0); + case XK_equal: return MATRIX(5,3); + case XK_bracketleft: return MATRIX(5,6); + case XK_bracketright: return MATRIX(6,1); + case XK_semicolon: return MATRIX(5,5); + case XK_apostrophe: return MATRIX(6,2); + case XK_slash: return MATRIX(6,7); + } + + return -1; +} + +static int decode_de(KeySym ks) +{ + switch(ks) { /* DE specific */ + case XK_ssharp: return MATRIX(5,0); + case XK_apostrophe: return MATRIX(5,3); + case XK_Udiaeresis: case XK_udiaeresis: return MATRIX(5,6); + case XK_plus: return MATRIX(6,1); + case XK_Odiaeresis: case XK_odiaeresis: return MATRIX(5,5); + case XK_Adiaeresis: case XK_adiaeresis: return MATRIX(6,2); + case XK_numbersign: return MATRIX(6,5); + case XK_less: case XK_greater: return MATRIX(6,0); + case XK_minus: return MATRIX(6,7); + } + + return -1; +} + +static int keycode2c64(XKeyEvent *event) +{ + KeySym ks; + int as; + int index = 0; + + do { + ks = XLookupKeysym(event, index); + as = kc_decode(ks); + + if (as == -1) + as = KBD_LANG == 0 ? decode_us(ks) : decode_de(ks); + if (as != -1) + return as; + index++; + } while (ks != NoSymbol); + + return -1; +} + + +/* + * Display constructor: Draw Speedometer/LEDs in window + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + int i; + char str[16]; + + quit_requested = false; + + // LEDs off + for (i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Draw speedometer/LEDs + led_gc = XCreateGC(display, mywin, 0, 0); + XSetFont(display, led_gc, led_font); + + XSetForeground(display, led_gc, fill_gray.pixel); + XFillRectangle(display, mywin, led_gc, 0, DISPLAY_Y, DISPLAY_X-1, 16); + + XSetForeground(display, led_gc, shine_gray.pixel); + XDrawLine(display, mywin, led_gc, 0, DISPLAY_Y, DISPLAY_X-1, DISPLAY_Y); + for (i=0; i<5; i++) + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5, DISPLAY_Y, DISPLAY_X*i/5, DISPLAY_Y+14); + for (i=2; i<6; i++) { + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-23, DISPLAY_Y+11, DISPLAY_X*i/5-9, DISPLAY_Y+11); + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-9, DISPLAY_Y+11, DISPLAY_X*i/5-9, DISPLAY_Y+5); + } + + XSetForeground(display, led_gc, shadow_gray.pixel); + XDrawLine(display, mywin, led_gc, 0, DISPLAY_Y+15, DISPLAY_X-1, DISPLAY_Y+15); + for (i=1; i<6; i++) + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-1, DISPLAY_Y+1, DISPLAY_X*i/5-1, DISPLAY_Y+15); + for (i=2; i<6; i++) { + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-24, DISPLAY_Y+11, DISPLAY_X*i/5-24, DISPLAY_Y+4); + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-24, DISPLAY_Y+4, DISPLAY_X*i/5-9, DISPLAY_Y+4); + } + + for (i=0; i<4; i++) { + sprintf(str, "Drive %d", i+8); + XSetForeground(display, led_gc, black.pixel); + XDrawString(display, mywin, led_gc, DISPLAY_X*(i+1)/5+8, DISPLAY_Y+12, str, strlen(str)); + draw_led(i, LED_OFF); + } + + // Start timer for LED error blinking + c64_disp = this; + pulse_sa.sa_handler = (void (*)(int))pulse_handler; + pulse_sa.sa_flags = 0; + sigemptyset(&pulse_sa.sa_mask); + sigaction(SIGALRM, &pulse_sa, NULL); + pulse_tv.it_interval.tv_sec = 0; + pulse_tv.it_interval.tv_usec = 400000; + pulse_tv.it_value.tv_sec = 0; + pulse_tv.it_value.tv_usec = 400000; + setitimer(ITIMER_REAL, &pulse_tv, NULL); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + XAutoRepeatOn(display); + XSync(display, 0); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Connect to X server and open window + */ + +int init_graphics(void) +{ + int i; + char *display_name = 0; + XSetWindowAttributes wattr; + XSizeHints *hints; + XColor exact_color; + int pixbytes; + + display = XOpenDisplay(display_name); + if (display == 0) { + fprintf(stderr, "Can't connect to X server %s\n", XDisplayName(display_name)); + return 0; + } + + screen = XDefaultScreen(display); + rootwin = XRootWindow(display, screen); + if (XMatchVisualInfo(display, screen, 8, PseudoColor, &visualInfo)) { + /* for our HP boxes */ + } else if (XMatchVisualInfo(display, screen, 8, GrayScale, &visualInfo)) { + } else { + fprintf(stderr, "Can't obtain appropriate X visual\n"); + return 0; + } + + vis = visualInfo.visual; + bitdepth = visualInfo.depth; + pixbytes = (bitdepth == 24 || bitdepth == 32 ? 4 : bitdepth == 12 || bitdepth == 16 ? 2 : 1); + fprintf(stderr, "Using %d bit visual\n", bitdepth); + + hsize = (DISPLAY_X + 3) & ~3; + +#if defined(X_USE_SHM) + img = XShmCreateImage(display, vis, bitdepth, ZPixmap, 0, &shminfo, + hsize, DISPLAY_Y); + + shminfo.shmid = shmget(IPC_PRIVATE, DISPLAY_Y * img->bytes_per_line, + IPC_CREAT | 0777); + shminfo.shmaddr = img->data = bufmem = (char *)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = False; + XShmAttach(display, &shminfo); + XSync(display,0); + /* now deleting means making it temporary */ + shmctl(shminfo.shmid, IPC_RMID, 0); +#else + bufmem = (char *)malloc(pixbytes * hsize * DISPLAY_Y); + img = XCreateImage(display, vis, bitdepth, ZPixmap, 0, bufmem, hsize, DISPLAY_Y, 32, 0); +#endif + + cmap = XCreateColormap(display, rootwin, vis, AllocNone); + + XParseColor(display, cmap, "#000000", &black); + if (!XAllocColor(display, cmap, &black)) + fprintf(stderr, "Whoops??\n"); + + wattr.event_mask = eventmask; + wattr.background_pixel = black.pixel; + wattr.backing_store = Always; + wattr.backing_planes = bitdepth; + wattr.border_pixmap = None; + wattr.border_pixel = black.pixel; + wattr.colormap = cmap; + + mywin = XCreateWindow(display, rootwin, 0, 0, DISPLAY_X, DISPLAY_Y + 16, 0, + bitdepth, InputOutput, vis, + CWEventMask|CWBackPixel|CWBorderPixel|CWBackingStore + |CWBackingPlanes|CWColormap, + &wattr); + XMapWindow(display, mywin); + XStoreName(display, mywin, "Frodo"); + + if ((hints = XAllocSizeHints()) != NULL) { + hints->min_width = DISPLAY_X; + hints->max_width = DISPLAY_X; + hints->min_height = DISPLAY_Y + 16; + hints->max_height = DISPLAY_Y + 16; + hints->flags = PMinSize | PMaxSize; + XSetWMNormalHints(display, mywin, hints); + XFree((char *)hints); + } + + black_gc = XCreateGC(display,mywin, 0, 0); + XSetForeground(display, black_gc, black.pixel); + + // Allocate colors for speedometer/LEDs + if (!XAllocNamedColor(display, cmap, "rgb:d0/d0/d0", &fill_gray, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:e8/e8/e8", &shine_gray, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:98/98/98", &shadow_gray, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:f0/00/00", &red, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:00/f0/00", &green, &exact_color)) + return 0; + + // Load font for speedometer/LED labels + led_font = XLoadFont(display, "-*-helvetica-medium-r-*-*-10-*"); + + for(i=0; i<256; i++) + keystate[i] = 0; + + return 1; +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + // Update C64 display + XSync(display, 0); +#if defined(X_USE_SHM) + XShmPutImage(display, mywin, black_gc, img, 0, 0, 0, 0, DISPLAY_X, DISPLAY_Y, 0); +#else + XPutImage(display, mywin, black_gc, img, 0, 0, 0, 0, DISPLAY_X, DISPLAY_Y); +#endif + + // Update drive LEDs + for (int i=0; i<4; i++) + if (led_state[i] != old_led_state[i]) { + draw_led(i, led_state[i]); + old_led_state[i] = led_state[i]; + } +} + + +/* + * Draw one drive LED + */ + +void C64Display::draw_led(int num, int state) +{ + switch (state) { + case LED_OFF: + case LED_ERROR_OFF: + XSetForeground(display, led_gc, black.pixel); + break; + case LED_ON: + XSetForeground(display, led_gc, green.pixel); + break; + case LED_ERROR_ON: + XSetForeground(display, led_gc, red.pixel); + break; + } + XFillRectangle(display, mywin, led_gc, DISPLAY_X*(num+2)/5-23, DISPLAY_Y+5, 14, 6); +} + + +/* + * LED error blink + */ + +void C64Display::pulse_handler(...) +{ + for (int i=0; i<4; i++) + switch (c64_disp->led_state[i]) { + case LED_ERROR_ON: + c64_disp->led_state[i] = LED_ERROR_OFF; + break; + case LED_ERROR_OFF: + c64_disp->led_state[i] = LED_ERROR_ON; + break; + } +} + + +/* + * Draw speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) { + char str[16]; + sprintf(str, "%d%%", speed); + XSetForeground(display, led_gc, fill_gray.pixel); + XFillRectangle(display,mywin, led_gc, 1, DISPLAY_Y+1, DISPLAY_X/5-2, 14); + XSetForeground(display, led_gc, black.pixel); + XDrawString(display, mywin, led_gc, 24, DISPLAY_Y+12, str, strlen(str)); + delay = 0; + } else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + return (uint8 *)bufmem; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + return hsize; +} + + +/* + * Poll the keyboard + */ + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + static bool auto_rep = true; + for(;;) { + XEvent event; + if (!XCheckMaskEvent(display, eventmask, &event)) + break; + + switch(event.type) { + + case KeyPress: { + int kc = keycode2c64((XKeyEvent *)&event); + if (kc == -1) + break; + switch (kc) { + + case KEY_F9: // F9: Invoke SAM + SAM(TheC64); + break; + + case KEY_F10: // F10: Quit + quit_requested = true; + break; + + case KEY_F11: // F11: NMI (Restore) + TheC64->NMI(); + break; + + case KEY_F12: // F12: Reset + TheC64->Reset(); + break; + + case KEY_NUM_LOCK: // NumLock: Toggle joyport + num_locked = true; + break; + + case KEY_FIRE: + joystate &= ~0x10; + break; + case KEY_JD: + joystate &= ~0x02; + break; + case KEY_JU: + joystate &= ~0x01; + break; + case KEY_JL: + joystate &= ~0x04; + break; + case KEY_JR: + joystate &= ~0x08; + break; + case KEY_JUL: + joystate &= ~0x05; + break; + case KEY_JUR: + joystate &= ~0x09; + break; + case KEY_JDL: + joystate &= ~0x06; + break; + case KEY_JDR: + joystate &= ~0x0a; + break; + + case KEY_KP_PLUS: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case KEY_KP_MINUS: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case KEY_KP_MULT: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + default: + if (keystate[kc]) + break; + keystate[kc] = 1; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + break; + } + break; + } + + case KeyRelease: { + int kc = keycode2c64((XKeyEvent *)&event); + if (kc == -1) + break; + switch (kc) { + + case KEY_NUM_LOCK: + num_locked = false; + break; + + case KEY_FIRE: + joystate |= 0x10; + break; + case KEY_JD: + joystate |= 0x02; + break; + case KEY_JU: + joystate |= 0x01; + break; + case KEY_JL: + joystate |= 0x04; + break; + case KEY_JR: + joystate |= 0x08; + break; + case KEY_JUL: + joystate |= 0x05; + break; + case KEY_JUR: + joystate |= 0x09; + break; + case KEY_JDL: + joystate |= 0x06; + break; + case KEY_JDR: + joystate |= 0x0a; + break; + + default: + if (!keystate[kc]) + break; + keystate[kc] = 0; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + break; + } + } + + case FocusIn: + if (auto_rep) { + XAutoRepeatOff(display); + auto_rep = false; + } + break; + + case FocusOut: + if (!auto_rep) { + XAutoRepeatOn(display); + auto_rep = true; + } + break; + } + } + *joystick = joystate; +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return num_locked; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + int i; + XColor col; + char str[20]; + + for (i=0; i< 256; i++) { + sprintf(str, "rgb:%x/%x/%x", palette_red[i & 0x0f], palette_green[i & 0x0f], palette_blue[i & 0x0f]); + XParseColor(display, cmap, str, &col); + if (XAllocColor(display, cmap, &col)) + colors[i] = col.pixel; + else + fprintf(stderr, "Couldn't get all colors\n"); + } +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(char *a,char *b,char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/FixPoint.i b/Src/FixPoint.i new file mode 100644 index 0000000..4b04290 --- /dev/null +++ b/Src/FixPoint.i @@ -0,0 +1,415 @@ +/* + * FixPoint.i + * + * Provides fixpoint arithmetic (for use in SID.cpp) + * You need to define FIXPOINT_PREC (number of fractional bits) and + * ldSINTAB (ld of the size of the sinus table) as well M_PI + * _before_ including this file. + * Requires at least 32bit ints! + * (C) 1997 Andreas Dehmel + */ + + +#define FIXPOINT_BITS 32 +// Sign-bit +#define FIXPOINT_SIGN (1<<(FIXPOINT_BITS-1)) + + +/* + * Elementary functions for the FixPoint class + */ + +// Multiplies two fixpoint numbers, result is a fixpoint number. +static inline int fixmult(int x, int y) +{ + register unsigned int a,b; + register bool sign; + + sign = (x ^ y) < 0; + if (x < 0) {x = -x;} + if (y < 0) {y = -y;} + // a, b : integer part; x, y : fractional part. All unsigned now (for shift right)!!! + a = (((unsigned int)x) >> FIXPOINT_PREC); x &= ~(a << FIXPOINT_PREC); + b = (((unsigned int)y) >> FIXPOINT_PREC); y &= ~(b << FIXPOINT_PREC); + x = ((a*b) << FIXPOINT_PREC) + (a*y + b*x) + + ((unsigned int)((x*y) + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC); +#ifdef FIXPOINT_SIGN + if (x < 0) {x ^= FIXPOINT_SIGN;} +#endif + if (sign) {x = -x;} + return(x); +} + + +// Multiplies a fixpoint number with an integer, result is a 32 bit (!) integer in +// contrast to using the standard member-functions which can provide only (32-FIXPOINT_PREC) +// valid bits. +static inline int intmult(int x, int y) // x is fixpoint, y integer +{ + register unsigned int i,j; + register bool sign; + + sign = (x ^ y) < 0; + if (x < 0) {x = -x;} + if (y < 0) {y = -y;} + i = (((unsigned int)x) >> 16); x &= ~(i << 16); // split both into 16.16 parts + j = (((unsigned int)y) >> 16); y &= ~(j << 16); +#if FIXPOINT_PREC <= 16 + // This '32' is independent of the number of bits used, it's due to the 16 bit shift + i = ((i*j) << (32 - FIXPOINT_PREC)) + ((i*y + j*x) << (16 - FIXPOINT_PREC)) + + ((unsigned int)(x*y + (1 << (FIXPOINT_PREC - 1))) >> FIXPOINT_PREC); +#else + { + register unsigned int h; + + h = (i*y + j*x); + i = ((i*j) << (32 - FIXPOINT_PREC)) + (h >> (FIXPOINT_PREC - 16)); + h &= ((1 << (FIXPOINT_PREC - 16)) - 1); x *= y; + i += (x >> FIXPOINT_PREC); x &= ((1 << FIXPOINT_PREC) - 1); + i += (((h + (x >> 16)) + (1 << (FIXPOINT_PREC - 17))) >> (FIXPOINT_PREC - 16)); + } +#endif +#ifdef FIXPOINT_SIGN + if (i < 0) {i ^= FIXPOINT_SIGN;} +#endif + if (sign) {i = -i;} + return(i); +} + + +// Computes the product of a fixpoint number with itself. +static inline int fixsquare(int x) +{ + register unsigned int a; + + if (x < 0) {x = -x;} + a = (((unsigned int)x) >> FIXPOINT_PREC); x &= ~(a << FIXPOINT_PREC); + x = ((a*a) << FIXPOINT_PREC) + ((a*x) << 1) + + ((unsigned int)((x*x) + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC); +#ifdef FIXPOINT_SIGN + if (x < 0) {x ^= FIXPOINT_SIGN;} +#endif + return(x); +} + + +// Computes the square root of a fixpoint number. +static inline int fixsqrt(int x) +{ + register int test, step; + + if (x < 0) return(-1); if (x == 0) return(0); + step = (x <= (1<>1)); + test = 0; + while (step != 0) + { + register int h; + + h = fixsquare(test + step); + if (h <= x) {test += step;} + if (h == x) break; + step >>= 1; + } + return(test); +} + + +// Divides a fixpoint number by another fixpoint number, yielding a fixpoint result. +static inline int fixdiv(int x, int y) +{ + register int res, mask; + register bool sign; + + sign = (x ^ y) < 0; + if (x < 0) {x = -x;} + if (y < 0) {y = -y;} + mask = (1< y) {y <<= 1; mask <<= 1;} + while (mask != 0) + { + if (x >= y) {res |= mask; x -= y;} + mask >>= 1; y >>= 1; + } +#ifdef FIXPOINT_SIGN + if (res < 0) {res ^= FIXPOINT_SIGN;} +#endif + if (sign) {res = -res;} + return(res); +} + + + + + +/* + * The C++ Fixpoint class. By no means exhaustive... + * Since it contains only one int data, variables of type FixPoint can be + * passed directly rather than as a reference. + */ + +class FixPoint +{ +private: + int x; + +public: + FixPoint(void); + FixPoint(int y); + ~FixPoint(void); + + // conversions + int Value(void); + int round(void); + operator int(void); + + // unary operators + FixPoint sqrt(void); + FixPoint sqr(void); + FixPoint abs(void); + FixPoint operator+(void); + FixPoint operator-(void); + FixPoint operator++(void); + FixPoint operator--(void); + + // binary operators + int imul(int y); + FixPoint operator=(FixPoint y); + FixPoint operator=(int y); + FixPoint operator+(FixPoint y); + FixPoint operator+(int y); + FixPoint operator-(FixPoint y); + FixPoint operator-(int y); + FixPoint operator/(FixPoint y); + FixPoint operator/(int y); + FixPoint operator*(FixPoint y); + FixPoint operator*(int y); + FixPoint operator+=(FixPoint y); + FixPoint operator+=(int y); + FixPoint operator-=(FixPoint y); + FixPoint operator-=(int y); + FixPoint operator*=(FixPoint y); + FixPoint operator*=(int y); + FixPoint operator/=(FixPoint y); + FixPoint operator/=(int y); + FixPoint operator<<(int y); + FixPoint operator>>(int y); + FixPoint operator<<=(int y); + FixPoint operator>>=(int y); + + // conditional operators + bool operator<(FixPoint y); + bool operator<(int y); + bool operator<=(FixPoint y); + bool operator<=(int y); + bool operator>(FixPoint y); + bool operator>(int y); + bool operator>=(FixPoint y); + bool operator>=(int y); + bool operator==(FixPoint y); + bool operator==(int y); + bool operator!=(FixPoint y); + bool operator!=(int y); +}; + + +/* + * int gets treated differently according to the case: + * + * a) Equations (=) or condition checks (==, <, <= ...): raw int (i.e. no conversion) + * b) As an argument for an arithmetic operation: conversion to fixpoint by shifting + * + * Otherwise loading meaningful values into FixPoint variables would be very awkward. + */ + +FixPoint::FixPoint(void) {x = 0;} + +FixPoint::FixPoint(int y) {x = y;} + +FixPoint::~FixPoint(void) {;} + +inline int FixPoint::Value(void) {return(x);} + +inline int FixPoint::round(void) {return((x + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC);} + +inline FixPoint::operator int(void) {return(x);} + + +// unary operators +inline FixPoint FixPoint::sqrt(void) {return(fixsqrt(x));} + +inline FixPoint FixPoint::sqr(void) {return(fixsquare(x));} + +inline FixPoint FixPoint::abs(void) {return((x < 0) ? -x : x);} + +inline FixPoint FixPoint::operator+(void) {return(x);} + +inline FixPoint FixPoint::operator-(void) {return(-x);} + +inline FixPoint FixPoint::operator++(void) {x += (1 << FIXPOINT_PREC); return x;} + +inline FixPoint FixPoint::operator--(void) {x -= (1 << FIXPOINT_PREC); return x;} + + +// binary operators +inline int FixPoint::imul(int y) {return(intmult(x,y));} + +inline FixPoint FixPoint::operator=(FixPoint y) {x = y.Value(); return x;} + +inline FixPoint FixPoint::operator=(int y) {x = y; return x;} + +inline FixPoint FixPoint::operator+(FixPoint y) {return(x + y.Value());} + +inline FixPoint FixPoint::operator+(int y) {return(x + (y << FIXPOINT_PREC));} + +inline FixPoint FixPoint::operator-(FixPoint y) {return(x - y.Value());} + +inline FixPoint FixPoint::operator-(int y) {return(x - (y << FIXPOINT_PREC));} + +inline FixPoint FixPoint::operator/(FixPoint y) {return(fixdiv(x,y.Value()));} + +inline FixPoint FixPoint::operator/(int y) {return(x/y);} + +inline FixPoint FixPoint::operator*(FixPoint y) {return(fixmult(x,y.Value()));} + +inline FixPoint FixPoint::operator*(int y) {return(x*y);} + +inline FixPoint FixPoint::operator+=(FixPoint y) {x += y.Value(); return x;} + +inline FixPoint FixPoint::operator+=(int y) {x += (y << FIXPOINT_PREC); return x;} + +inline FixPoint FixPoint::operator-=(FixPoint y) {x -= y.Value(); return x;} + +inline FixPoint FixPoint::operator-=(int y) {x -= (y << FIXPOINT_PREC); return x;} + +inline FixPoint FixPoint::operator*=(FixPoint y) {x = fixmult(x,y.Value()); return x;} + +inline FixPoint FixPoint::operator*=(int y) {x *= y; return x;} + +inline FixPoint FixPoint::operator/=(FixPoint y) {x = fixdiv(x,y.Value()); return x;} + +inline FixPoint FixPoint::operator/=(int y) {x /= y; return x;} + +inline FixPoint FixPoint::operator<<(int y) {return(x << y);} + +inline FixPoint FixPoint::operator>>(int y) {return(x >> y);} + +inline FixPoint FixPoint::operator<<=(int y) {x <<= y; return x;} + +inline FixPoint FixPoint::operator>>=(int y) {x >>= y; return x;} + + +// conditional operators +inline bool FixPoint::operator<(FixPoint y) {return(x < y.Value());} + +inline bool FixPoint::operator<(int y) {return(x < y);} + +inline bool FixPoint::operator<=(FixPoint y) {return(x <= y.Value());} + +inline bool FixPoint::operator<=(int y) {return(x <= y);} + +inline bool FixPoint::operator>(FixPoint y) {return(x > y.Value());} + +inline bool FixPoint::operator>(int y) {return(x > y);} + +inline bool FixPoint::operator>=(FixPoint y) {return(x >= y.Value());} + +inline bool FixPoint::operator>=(int y) {return(x >= y);} + +inline bool FixPoint::operator==(FixPoint y) {return(x == y.Value());} + +inline bool FixPoint::operator==(int y) {return(x == y);} + +inline bool FixPoint::operator!=(FixPoint y) {return(x != y.Value());} + +inline bool FixPoint::operator!=(int y) {return(x != y);} + + + +/* + * In case the first argument is an int (i.e. member-operators not applicable): + * Not supported: things like int/FixPoint. The same difference in conversions + * applies as mentioned above. + */ + + +// binary operators +inline FixPoint operator+(int x, FixPoint y) {return((x << FIXPOINT_PREC) + y.Value());} + +inline FixPoint operator-(int x, FixPoint y) {return((x << FIXPOINT_PREC) - y.Value());} + +inline FixPoint operator*(int x, FixPoint y) {return(x*y.Value());} + + +// conditional operators +inline bool operator==(int x, FixPoint y) {return(x == y.Value());} + +inline bool operator!=(int x, FixPoint y) {return(x != y.Value());} + +inline bool operator<(int x, FixPoint y) {return(x < y.Value());} + +inline bool operator<=(int x, FixPoint y) {return(x <= y.Value());} + +inline bool operator>(int x, FixPoint y) {return(x > y.Value());} + +inline bool operator>=(int x, FixPoint y) {return(x >= y.Value());} + + + +/* + * For more convenient creation of constant fixpoint numbers from constant floats. + */ + +#define FixNo(n) (FixPoint)((int)(n*(1<= 3*(1<= 2*(1<= (1<> (FIXPOINT_PREC - ldSINTAB - 1)) & ((1<<(ldSINTAB+2))-1); + FIXPOINT_SIN_COS_GENERIC +} + + +static inline FixPoint fixcos(FixPoint x) +{ + int angle = x; + + // cos(x) = sin(x+PI/2) + angle = (angle + (1<<(FIXPOINT_PREC-1)) >> (FIXPOINT_PREC - ldSINTAB - 1)) & ((1<<(ldSINTAB+2))-1); + FIXPOINT_SIN_COS_GENERIC +} + + + +static inline void InitFixSinTab(void) +{ + int i; + float step; + + for (i=0, step=0; i<(1< +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +FRODO_ICON ICON DISCARDABLE "Frodo.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAIN_MENU MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New", ID_FILE_NEW + MENUITEM "&Open...", ID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "&Save", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVEAS + MENUITEM SEPARATOR + MENUITEM "E&xit\tF10", ID_FILE_EX + END + POPUP "&Tools" + BEGIN + MENUITEM "&Preferences...", ID_TOOLS_PREFERENCES + MENUITEM SEPARATOR + MENUITEM "P&ause\tPause", ID_TOOLS_PAUSE + MENUITEM "&Fullscreen\tAlt-Enter", ID_TOOLS_FULLSCREEN + MENUITEM "Reset &DirectDraw\tCtrl-Enter", ID_TOOLS_RESETDIRECTDRAW + MENUITEM SEPARATOR + MENUITEM "Reset &C64\tF12", ID_TOOLS_RESETC64 + MENUITEM "&Insert Next Disk", ID_TOOLS_INSERTNEXTDISK + MENUITEM SEPARATOR + MENUITEM "&SAM...", ID_TOOLS_SAM, GRAYED + END + POPUP "&Help" + BEGIN + MENUITEM "&Contents...", ID_HELP_CONTENTS + MENUITEM "&Keyboard...", ID_HELP_KEYBOARD + MENUITEM "&Settings...", ID_HELP_SETTINGS + MENUITEM SEPARATOR + MENUITEM "&About...", ID_HELP_ABOUT + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include \r\n" + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PREFERENCES_STANDARD DIALOG DISCARDABLE 0, 0, 272, 221 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Standard" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "&Devices",IDC_STATIC,5,0,260,90 + RTEXT "8",IDC_STATIC,10,10,15,10 + EDITTEXT IDC_DEVICE8,35,10,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE8,205,10,50,14 + RTEXT "9",IDC_STATIC,10,30,15,10 + EDITTEXT IDC_DEVICE9,35,30,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE9,205,30,50,14 + RTEXT "10",IDC_STATIC,10,50,15,10 + EDITTEXT IDC_DEVICE10,35,50,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE10,205,50,50,14 + RTEXT "11",IDC_STATIC,10,70,15,10 + EDITTEXT IDC_DEVICE11,35,70,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE11,205,70,50,14 + GROUPBOX "&Parameters",IDC_STATIC,5,95,260,45 + LTEXT "Normal",IDC_STATIC,15,105,40,8 + EDITTEXT IDC_NORMAL,15,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_NORMAL_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,40,120,11,14 + LTEXT "Bad Lines",IDC_STATIC,55,105,40,8 + EDITTEXT IDC_BADLINES,55,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_BADLINES_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,80,120,11,14 + LTEXT "CIA",IDC_STATIC,95,105,35,8 + EDITTEXT IDC_CIA,95,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_CIA_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,120,120, + 11,14 + LTEXT "Floppy",IDC_STATIC,135,105,40,8 + EDITTEXT IDC_FLOPPY,135,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_FLOPPY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,161,120,11,14 + LTEXT "Draw Every",IDC_STATIC,175,105,45,8 + EDITTEXT IDC_DRAWEVERY,175,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_DRAWEVERY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,201,120,11,14 + LTEXT "REU Size",IDC_STATIC,220,105,40,8 + COMBOBOX IDC_REUSIZE,220,120,35,95,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + GROUPBOX "&Options",IDC_STATIC,5,145,260,70 + CONTROL "Limit Speed",IDC_LIMITSPEED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,155,80,10 + CONTROL "Sprites",IDC_SPRITES,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,155,80,10 + CONTROL "Sprite Collisions",IDC_SPRITECOLLISIONS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,185,155,75,10 + CONTROL "Joystick 1",IDC_JOYSTICK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,170,80,10 + CONTROL "Joystick 2",IDC_JOYSTICK2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,170,80,10 + CONTROL "Swap Joysticks",IDC_SWAPJOYSTICKS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,185,170,75,10 + CONTROL "Fast Reset",IDC_FASTRESET,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,185,80,10 + CONTROL "CIA IRQ Hack",IDC_CIAIRQHACK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,185,80,10 + CONTROL "Map /",IDC_MAPSLASH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,185,185,75,10 + CONTROL "SID Emulation",IDC_SIDEMULATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,200,80,10 + CONTROL "SID Filters",IDC_SIDFILTERS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,100,200,80,10 + CONTROL "1541 Emulation",IDC_1541EMULATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,185,200,75,10 +END + +IDD_PREFERENCES_WIN32 DIALOG DISCARDABLE 0, 0, 272, 221 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "WIN32" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "&Video",IDC_STATIC,5,0,260,60 + CONTROL "Fullscreen at startup",IDC_FULLSCREEN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,15,85,10 + CONTROL "System Memory",IDC_SYSTEMMEMORY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,30,85,10 + CONTROL "Always Copy",IDC_ALWAYSCOPY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,45,85,10 + LTEXT "View&port",IDC_STATIC,110,20,70,8 + COMBOBOX IDC_VIEWPORT,110,31,70,184,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Display &Mode",IDC_STATIC,185,20,70,8 + COMBOBOX IDC_DISPLAYMODE,185,31,70,184,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + GROUPBOX "&Window",IDC_STATIC,5,65,260,50 + CONTROL "Hide Cursor",IDC_HIDECURSOR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,80,85,10 + CONTROL "System Keys",IDC_SYSTEMKEYS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,95,85,10 + RTEXT "Scaling &Numerator",IDC_STATIC,110,75,95,10 + EDITTEXT IDC_SCALINGNUMERATOR,215,75,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SCALINGNUMERATOR_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,245,75,11,14 + RTEXT "Scaling &Denominator",IDC_STATIC,110,95,95,10 + EDITTEXT IDC_SCALINGDENOMINATOR,215,95,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SCALINGDENOMINATOR_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,245,95,11,14 + GROUPBOX "&Sound",IDC_STATIC,5,120,260,45 + CONTROL "DirectSound",IDC_DIRECTSOUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,135,85,10 + CONTROL "Exclusive",IDC_EXCLUSIVESOUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,150,85,10 + LTEXT "Latency Min",IDC_STATIC,105,130,50,8 + EDITTEXT IDC_LATENCYMIN,105,140,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_LATENCYMIN_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,135,140,11,14 + LTEXT "Latency Max",IDC_STATIC,160,130,50,8 + EDITTEXT IDC_LATENCYMAX,160,140,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_LATENCYMAX_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,190,140,11,14 + LTEXT "Latency Avg",IDC_STATIC,215,130,45,8 + EDITTEXT IDC_LATENCYAVG,215,140,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_LATENCYAVG_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,245,140,11,14 + GROUPBOX "&General",IDC_STATIC,5,170,260,45 + CONTROL "Auto Pause",IDC_AUTOPAUSE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,185,85,10 + CONTROL "Show LEDs",IDC_SHOWLEDS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,105,185,85,10 + CONTROL "Preferences at startup",IDC_PREFSATSTARTUP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,200,85,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_PREFERENCES_STANDARD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 265 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END + + IDD_PREFERENCES_WIN32, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 265 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_INVISIBLE CURSOR DISCARDABLE "Invisible.cur" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Frodo.rsrc b/Src/Frodo.rsrc new file mode 100644 index 0000000..873bb38 Binary files /dev/null and b/Src/Frodo.rsrc differ diff --git a/Src/FrodoHeaders.pch++ b/Src/FrodoHeaders.pch++ new file mode 100644 index 0000000..5c0e65b --- /dev/null +++ b/Src/FrodoHeaders.pch++ @@ -0,0 +1,12 @@ +/* + * FrodoHeaders.pch++ (for Metrowerks BeIDE) + */ + +#define PC_IS_POINTER 1 +#define PRECISE_CPU_CYCLES 0 +#define PRECISE_CIA_CYCLES 0 + +#include +#include +#include +#include diff --git a/Src/FrodoPCHeaders.pch++ b/Src/FrodoPCHeaders.pch++ new file mode 100644 index 0000000..82aeb70 --- /dev/null +++ b/Src/FrodoPCHeaders.pch++ @@ -0,0 +1,12 @@ +/* + * FrodoPCHeaders.pch++ (for Metrowerks BeIDE) + */ + +#define PC_IS_POINTER 0 +#define PRECISE_CPU_CYCLES 1 +#define PRECISE_CIA_CYCLES 1 + +#include +#include +#include +#include diff --git a/Src/FrodoSCHeaders.pch++ b/Src/FrodoSCHeaders.pch++ new file mode 100644 index 0000000..b076ed8 --- /dev/null +++ b/Src/FrodoSCHeaders.pch++ @@ -0,0 +1,10 @@ +/* + * FrodoSCHeaders.pch++ (for Metrowerks BeIDE) + */ + +#define FRODO_SC + +#include +#include +#include +#include diff --git a/Src/IEC.cpp b/Src/IEC.cpp new file mode 100644 index 0000000..607c5bb --- /dev/null +++ b/Src/IEC.cpp @@ -0,0 +1,430 @@ +/* + * IEC.cpp - IEC bus routines, 1541 emulation (DOS level) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - There are three kinds of devices on the IEC bus: controllers, + * listeners and talkers. We are always the controller and we + * can additionally be either listener or talker. There can be + * only one listener and one talker active at the same time (the + * real IEC bus allows multiple listeners, but we don't). + * - There is one Drive object for every emulated drive (8..11). + * A pointer to one of them is stored in "listener"/"talker" + * when talk()/listen() is called and is used by the functions + * called afterwards. + * - The Drive objects have four virtual functions so that the + * interface to them is independent of their implementation: + * Open() opens a channel + * Close() closes a channel + * Read() reads from a channel + * Write() writes to a channel + * - The EOI/EOF signal is special on the IEC bus in that it is + * Sent before the last byte, not after it. + */ + +#include "sysdeps.h" + +#include "IEC.h" +#include "1541fs.h" +#include "1541d64.h" +#include "1541t64.h" +#include "Prefs.h" +#include "Display.h" + + +/* + * Constructor: Initialize variables + */ + +IEC::IEC(C64Display *display) : the_display(display) +{ + int i; + + // Create drives 8..11 + for (i=0; i<4; i++) + drive[i] = NULL; // Important because UpdateLEDs is called from the drive constructors (via set_error) + + if (!ThePrefs.Emul1541Proc) + for (i=0; i<4; i++) { + if (ThePrefs.DriveType[i] == DRVTYPE_DIR) + drive[i] = new FSDrive(this, ThePrefs.DrivePath[i]); + else if (ThePrefs.DriveType[i] == DRVTYPE_D64) + drive[i] = new D64Drive(this, ThePrefs.DrivePath[i]); + else + drive[i] = new T64Drive(this, ThePrefs.DrivePath[i]); + } + + listener_active = talker_active = false; + listening = false; +} + + +/* + * Destructor: Delete drives + */ + +IEC::~IEC() +{ + for (int i=0; i<4; i++) + delete drive[i]; +} + + +/* + * Reset all drives + */ + +void IEC::Reset(void) +{ + for (int i=0; i<4; i++) + if (drive[i] != NULL && drive[i]->Ready) + drive[i]->Reset(); + + UpdateLEDs(); +} + + +/* + * Preferences have changed, prefs points to new preferences, + * ThePrefs still holds the previous ones. Check if drive settings + * have changed. + */ + +void IEC::NewPrefs(Prefs *prefs) +{ + // Delete and recreate all changed drives + for (int i=0; i<4; i++) + if ((ThePrefs.DriveType[i] != prefs->DriveType[i]) || strcmp(ThePrefs.DrivePath[i], prefs->DrivePath[i]) || ThePrefs.Emul1541Proc != prefs->Emul1541Proc) { + delete drive[i]; + drive[i] = NULL; // Important because UpdateLEDs is called from drive constructors (via set_error()) + if (!prefs->Emul1541Proc) { + if (prefs->DriveType[i] == DRVTYPE_DIR) + drive[i] = new FSDrive(this, prefs->DrivePath[i]); + else if (prefs->DriveType[i] == DRVTYPE_D64) + drive[i] = new D64Drive(this, prefs->DrivePath[i]); + else + drive[i] = new T64Drive(this, prefs->DrivePath[i]); + } + } + + UpdateLEDs(); +} + + +/* + * Update drive LED display + */ + +void IEC::UpdateLEDs(void) +{ + if (drive[0] != NULL && drive[1] != NULL && drive[2] != NULL && drive[3] != NULL) + the_display->UpdateLEDs(drive[0]->LED, drive[1]->LED, drive[2]->LED, drive[3]->LED); +} + + +/* + * Output one byte + */ + +uint8 IEC::Out(uint8 byte, bool eoi) +{ + if (listener_active) { + if (received_cmd == CMD_OPEN) + return open_out(byte, eoi); + if (received_cmd == CMD_DATA) + return data_out(byte, eoi); + return ST_TIMEOUT; + } else + return ST_TIMEOUT; +} + + +/* + * Output one byte with ATN (Talk/Listen/Untalk/Unlisten) + */ + +uint8 IEC::OutATN(uint8 byte) +{ + received_cmd = sec_addr = 0; // Command is sent with secondary address + switch (byte & 0xf0) { + case ATN_LISTEN: + listening = true; + return listen(byte & 0x0f); + case ATN_UNLISTEN: + listening = false; + return unlisten(); + case ATN_TALK: + listening = false; + return talk(byte & 0x0f); + case ATN_UNTALK: + listening = false; + return untalk(); + } + return ST_TIMEOUT; +} + + +/* + * Output secondary address + */ + +uint8 IEC::OutSec(uint8 byte) +{ + if (listening) { + if (listener_active) { + sec_addr = byte & 0x0f; + received_cmd = byte & 0xf0; + return sec_listen(); + } + } else { + if (talker_active) { + sec_addr = byte & 0x0f; + received_cmd = byte & 0xf0; + return sec_talk(); + } + } + return ST_TIMEOUT; +} + + +/* + * Read one byte + */ + +uint8 IEC::In(uint8 *byte) +{ + if (talker_active && (received_cmd == CMD_DATA)) + return data_in(byte); + + *byte = 0; + return ST_TIMEOUT; +} + + +/* + * Assert ATN (for Untalk) + */ + +void IEC::SetATN(void) +{ + // Only needed for real IEC +} + + +/* + * Release ATN + */ + +void IEC::RelATN(void) +{ + // Only needed for real IEC +} + + +/* + * Talk-attention turn-around + */ + +void IEC::Turnaround(void) +{ + // Only needed for real IEC +} + + +/* + * System line release + */ + +void IEC::Release(void) +{ + // Only needed for real IEC +} + + +/* + * Listen + */ + +uint8 IEC::listen(int device) +{ + if ((device >= 8) && (device <= 11)) { + if ((listener = drive[device-8]) != NULL && listener->Ready) { + listener_active = true; + return ST_OK; + } + } + + listener_active = false; + return ST_NOTPRESENT; +} + + +/* + * Talk + */ + +uint8 IEC::talk(int device) +{ + if ((device >= 8) && (device <= 11)) { + if ((talker = drive[device-8]) != NULL && talker->Ready) { + talker_active = true; + return ST_OK; + } + } + + talker_active = false; + return ST_NOTPRESENT; +} + + +/* + * Unlisten + */ + +uint8 IEC::unlisten(void) +{ + listener_active = false; + return ST_OK; +} + + +/* + * Untalk + */ + +uint8 IEC::untalk(void) +{ + talker_active = false; + return ST_OK; +} + + +/* + * Secondary address after Listen + */ + +uint8 IEC::sec_listen(void) +{ + switch (received_cmd) { + + case CMD_OPEN: // Prepare for receiving the file name + name_ptr = name_buf; + name_len = 0; + return ST_OK; + + case CMD_CLOSE: // Close channel + if (listener->LED != DRVLED_ERROR) { + listener->LED = DRVLED_OFF; // Turn off drive LED + UpdateLEDs(); + } + return listener->Close(sec_addr); + } + return ST_OK; +} + + +/* + * Secondary address after Talk + */ + +uint8 IEC::sec_talk(void) +{ + return ST_OK; +} + + +/* + * Byte after Open command: Store character in file name, open file on EOI + */ + +uint8 IEC::open_out(uint8 byte, bool eoi) +{ + if (name_len < NAMEBUF_LENGTH) { + *name_ptr++ = byte; + name_len++; + } + + if (eoi) { + *name_ptr = 0; // End string + listener->LED = DRVLED_ON; // Turn on drive LED + UpdateLEDs(); + return listener->Open(sec_addr, name_buf); + } + + return ST_OK; +} + + +/* + * Write byte to channel + */ + +uint8 IEC::data_out(uint8 byte, bool eoi) +{ + return listener->Write(sec_addr, byte, eoi); +} + + +/* + * Read byte from channel + */ + +uint8 IEC::data_in(uint8 *byte) +{ + return talker->Read(sec_addr, byte); +} + + +/* + * Drive constructor + */ + +Drive::Drive(IEC *iec) +{ + the_iec = iec; + LED = DRVLED_OFF; + Ready = false; + set_error(ERR_STARTUP); +} + + +/* + * Set error message on drive + */ + +// 1541 error messages +char *Errors_1541[] = { + "00, OK,00,00\r", + "25,WRITE ERROR,00,00\r", + "26,WRITE PROTECT ON,00,00\r", + "30,SYNTAX ERROR,00,00\r", + "33,SYNTAX ERROR,00,00\r", + "60,WRITE FILE OPEN,00,00\r", + "61,FILE NOT OPEN,00,00\r", + "62,FILE NOT FOUND,00,00\r", + "67,ILLEGAL TRACK OR SECTOR,00,00\r", + "70,NO CHANNEL,00,00\r", + "73,CBM DOS V2.6 1541,00,00\r", + "74,DRIVE NOT READY,00,00\r" +}; + +void Drive::set_error(int error) +{ + error_ptr = Errors_1541[error]; + error_len = strlen(error_ptr); + + // Set drive condition + if (error != ERR_OK) + if (error == ERR_STARTUP) + LED = DRVLED_OFF; + else + LED = DRVLED_ERROR; + else if (LED == DRVLED_ERROR) + LED = DRVLED_OFF; + the_iec->UpdateLEDs(); +} diff --git a/Src/IEC.h b/Src/IEC.h new file mode 100644 index 0000000..b0a76e4 --- /dev/null +++ b/Src/IEC.h @@ -0,0 +1,146 @@ +/* + * IEC.h - IEC bus routines, 1541 emulation (DOS level) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _IEC_H +#define _IEC_H + + +// Maximum length of file names +const int NAMEBUF_LENGTH = 256; + + +// C64 status codes +enum { + ST_OK = 0, // No error + ST_READ_TIMEOUT = 0x02, // Timeout on reading + ST_TIMEOUT = 0x03, // Timeout + ST_EOF = 0x40, // End of file + ST_NOTPRESENT = 0x80 // Device not present +}; + + +// 1541 error codes +enum { + ERR_OK, // 00 OK + ERR_WRITEERROR, // 25 WRITE ERROR + ERR_WRITEPROTECT, // 26 WRITE PROTECT ON + ERR_SYNTAX30, // 30 SYNTAX ERROR (unknown command) + ERR_SYNTAX33, // 33 SYNTAX ERROR (wildcards on writing) + ERR_WRITEFILEOPEN, // 60 WRITE FILE OPEN + ERR_FILENOTOPEN, // 61 FILE NOT OPEN + ERR_FILENOTFOUND, // 62 FILE NOT FOUND + ERR_ILLEGALTS, // 67 ILLEGAL TRACK OR SECTOR + ERR_NOCHANNEL, // 70 NO CHANNEL + ERR_STARTUP, // 73 Power-up message + ERR_NOTREADY // 74 DRIVE NOT READY +}; + + +// IEC command codes +enum { + CMD_DATA = 0x60, // Data transfer + CMD_CLOSE = 0xe0, // Close channel + CMD_OPEN = 0xf0 // Open channel +}; + + +// IEC ATN codes +enum { + ATN_LISTEN = 0x20, + ATN_UNLISTEN = 0x30, + ATN_TALK = 0x40, + ATN_UNTALK = 0x50 +}; + + +// Drive LED states +enum { + DRVLED_OFF, // Inactive, LED off + DRVLED_ON, // Active, LED on + DRVLED_ERROR // Error, blink LED +}; + + +class Drive; +class C64Display; +class Prefs; + +// Class for complete IEC bus system with drives 8..11 +class IEC { +public: + IEC(C64Display *display); + ~IEC(); + + void Reset(void); + void NewPrefs(Prefs *prefs); + void UpdateLEDs(void); + + uint8 Out(uint8 byte, bool eoi); + uint8 OutATN(uint8 byte); + uint8 OutSec(uint8 byte); + uint8 In(uint8 *byte); + void SetATN(void); + void RelATN(void); + void Turnaround(void); + void Release(void); + +private: + uint8 listen(int device); + uint8 talk(int device); + uint8 unlisten(void); + uint8 untalk(void); + uint8 sec_listen(void); + uint8 sec_talk(void); + uint8 open_out(uint8 byte, bool eoi); + uint8 data_out(uint8 byte, bool eoi); + uint8 data_in(uint8 *byte); + + C64Display *the_display; // Pointer to display object (for drive LEDs) + + char name_buf[NAMEBUF_LENGTH]; // Buffer for file names and command strings + char *name_ptr; // Pointer for reception of file name + int name_len; // Received length of file name + + Drive *drive[4]; // 4 drives (8..11) + + Drive *listener; // Pointer to active listener + Drive *talker; // Pointer to active talker + + bool listener_active; // Listener selected, listener_data is valid + bool talker_active; // Talker selected, talker_data is valid + bool listening; // Last ATN was listen (to decide between sec_listen/sec_talk) + + uint8 received_cmd; // Received command code ($x0) + uint8 sec_addr; // Received secondary address ($0x) +}; + + +// Abstract superclass for individual drives +class Drive { +public: + Drive(IEC *iec); + virtual ~Drive() {} + + virtual uint8 Open(int channel, char *filename)=0; + virtual uint8 Close(int channel)=0; + virtual uint8 Read(int channel, uint8 *byte)=0; + virtual uint8 Write(int channel, uint8 byte, bool eoi)=0; + virtual void Reset(void)=0; + + int LED; // Drive LED state + bool Ready; // Drive is ready for operation + +protected: + void set_error(int error); + + char *error_ptr; // Pointer within error message + int error_len; // Remaining length of error message + +private: + IEC *the_iec; // Pointer to IEC object +}; + +#endif diff --git a/Src/Invisible.cur b/Src/Invisible.cur new file mode 100644 index 0000000..6bb01a5 Binary files /dev/null and b/Src/Invisible.cur differ diff --git a/Src/Makefile.Amiga b/Src/Makefile.Amiga new file mode 100644 index 0000000..265fc42 --- /dev/null +++ b/Src/Makefile.Amiga @@ -0,0 +1,95 @@ +# Makefile.in for Frodo (AmigaOS with GCC) +# Copyright (C) 1995-1997 Christian Bauer + +## Version information +VERSION = 4 +REVISION = 1 + +CXX = gcc +CFLAGS = -O2 -I./ -fomit-frame-pointer -m68040 -m68881 +LIBRARIES = + +.SUFFIXES: .o .cpp .c .h + +## Files +OBJS = main.o Display.o Prefs.o AmigaGUI.o SID.o REU.o IEC.o 1541fs.o \ + 1541d64.o 1541t64.o 1541job.o SAM.o +SLOBJS = $(OBJS) C64.o CPUC64.o VIC.o CIA.o CPU1541.o +PCOBJS = $(OBJS) C64_PC.o CPUC64_PC.o VIC.o CIA.o CPU1541_PC.o +PCFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 +SCOBJS = $(OBJS) C64_SC.o CPUC64_SC.o VIC_SC.o CIA_SC.o CPU1541_SC.o CPU_common.o +SCFLAGS = -DFRODO_SC + +Frodo: $(SLOBJS) + $(CXX) -o Frodo $(SLOBJS) $(LDFLAGS) $(LIBRARIES) + copy Frodo / + +FrodoPC: $(PCOBJS) + $(CXX) -o FrodoPC $(PCOBJS) $(LDFLAGS) $(LIBRARIES) + copy FrodoPC / + +FrodoSC: $(SCOBJS) + $(CXX) -o FrodoSC $(SCOBJS) $(LDFLAGS) $(LIBRARIES) + copy FrodoSC / + +all: Frodo FrodoPC FrodoSC + +clean: + @-delete $(SLOBJS) $(PCOBJS) $(SCOBJS) + @-delete -f Frodo FrodoPC FrodoSC + +.cpp.o: + $(CXX) $(INCLUDES) $(CFLAGS) -o $@ -c $*.cpp + +.c.o: + $(CXX) $(INCLUDES) $(CFLAGS) -o $@ -c $*.c + +.cpp.s: + $(CXX) $(INCLUDES) $(CFLAGS) -o $@ -S $*.cpp -g0 + +C64_SC.o: C64.cpp C64.h C64_Amiga.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPUC64_SC.o: CPUC64_SC.cpp CPUC64.h CPU_emulcycle.i CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPU1541_SC.o: CPU1541_SC.cpp CPU1541.h CPU_emulcycle.i CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +VIC_SC.o: VIC_SC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CIA_SC.o: CIA_SC.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +C64_PC.o: C64.cpp C64.h C64_Amiga.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CXX) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +CPUC64_PC.o: CPUC64.cpp CPUC64.h CPU_emulline.i C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CXX) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +CPU1541_PC.o: CPU1541.cpp CPU1541.h CPU_emulline.i 1541job.h C64.h CIA.h Display.h + $(CXX) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +## Dependencies +main.o: main.cpp main.h main_Amiga.i C64.h Display.h Prefs.h SAM.h +C64.o: C64.cpp C64.h C64_Amiga.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h +CmdPipe.o: CmdPipe.h +Display.o: Display.cpp Display.h Display_Amiga.i main.h Prefs.h Version.h +Prefs.o: Prefs.cpp Prefs.h Prefs_Amiga.i Display.h main.h +CPUC64.o: CPUC64.cpp CPUC64.h CPU_emulline.i C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h +CPU1541.o: CPU1541.cpp CPU1541.h CPU_emulline.i 1541job.h C64.h CIA.h Display.h +CPU_common.o: CPU_common.cpp CPU_common.h +VIC.o: VIC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h +SID.o: SID.cpp SID.h SID_Amiga.i Prefs.h +CIA.o: CIA.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h +IEC.o: IEC.cpp IEC.h 1541fs.h 1541d64.h 1541t64.h Prefs.h Display.h +1541fs.o: 1541fs.cpp 1541fs.h IEC.h main.h Prefs.h +1541fs.h: IEC.h +1541d64.o: 1541d64.cpp 1541d64.h IEC.h Prefs.h +1541d64.h: IEC.h +1541t64.o: 1541t64.cpp 1541t64.h IEC.h Prefs.h +1541t64.h: IEC.h +1541job.o: 1541job.cpp 1541job.h CPU1541.h Prefs.h +REU.o: REU.cpp REU.h CPUC64.h Prefs.h +SAM.o: SAM.cpp SAM.h C64.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h diff --git a/Src/Makefile.WIN32 b/Src/Makefile.WIN32 new file mode 100644 index 0000000..a48a563 --- /dev/null +++ b/Src/Makefile.WIN32 @@ -0,0 +1,113 @@ +# Makefile.in for Frodo (WIN32 DirectX with MSVC++) +# Copyright (C) 1994-1997 Christian Bauer +# WIN32 code by J. Richard Sladkey + +## Version information +VERSION = 4 +REVISION = 1 + +# Choose static (L) or dynamic (D) version of C library +LIBC = L +#LIBC = D + +# Choose debug or optimized build +#CFLAGS = -W2 -Zi -DDEBUG -D_DEBUG -M$(LIBC)d +CFLAGS = -Ox2 -G5 -Gi- -FAsc -M$(LIBC) + +CXX = cl +RC = rc +ALLCFLAGS = -I. -DWIN32 -DSTRICT -D__i386 $(CFLAGS) +ALLSCCFLAGS = -DFRODO_SC -DBATCH_CIA_CYCLES $(ALLCFLAGS) +#ALLSCCFLAGS = -DFRODO_SC $(ALLCFLAGS) +ALLPCCFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 $(ALLCFLAGS) +LDFLAGS = $(ALLCFLAGS) +LIBRARIES = user32.lib kernel32.lib gdi32.lib ddraw.lib dsound.lib winmm.lib comctl32.lib comdlg32.lib shell32.lib +#PROFILE = /link /profile + +## Files +OBJS = main.obj Display.obj Prefs.obj SID.obj REU.obj IEC.obj 1541fs.obj \ + 1541d64.obj 1541t64.obj 1541job.obj SAM.obj ndir.obj Frodo.res +SLOBJS = $(OBJS) C64.obj CPUC64.obj VIC.obj CIA.obj CPU1541.obj +SCOBJS = $(OBJS) C64_SC.obj CPUC64_SC.obj VIC_SC.obj CIA_SC.obj CPU1541_SC.obj CPU_common.obj +PCOBJS = $(OBJS) C64_PC.obj CPUC64_PC.obj VIC.obj CIA.obj CPU1541_PC.obj + +Frodo: Frodo.exe + +Frodo.exe: $(SLOBJS) + $(CXX) -FeFrodo.exe $(SLOBJS) $(LDFLAGS) $(LIBRARIES) + copy Frodo.exe .. + +FrodoSC: FrodoSC.exe + +FrodoSC.exe: $(SCOBJS) + $(CXX) -FeFrodoSC.exe $(SCOBJS) $(LDFLAGS) $(LIBRARIES) $(PROFILE) + copy FrodoSC.exe .. + +FrodoPC: FrodoPC.exe + +FrodoPC.exe: $(PCOBJS) + $(CXX) -FeFrodoPC.exe $(PCOBJS) $(LDFLAGS) $(LIBRARIES) + copy FrodoPC.exe .. + +Frodo.res: Frodo.rc Frodo.ico + $(RC) Frodo.rc + +all: Frodo FrodoSC FrodoPC + +clean: + rm -f $(SLOBJS) $(SCOBJS) $(PCOBJS) + rm -f Frodo.exe FrodoSC.exe FrodoPC.exe + rm -f *.ilk *.pdb *.cod *.nms *.res *.aps + +.cpp.obj: + $(CXX) $(ALLCFLAGS) -o $@ -c $*.cpp + +.c.obj: + $(CXX) $(ALLCFLAGS) -o $@ -c $*.c + +C64_SC.obj: C64.cpp C64.h C64_WIN32.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +CPUC64_SC.obj: CPUC64_SC.cpp CPUC64.h CPU_emulcycle.i CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +CPU1541_SC.obj: CPU1541_SC.cpp CPU1541.h CPU_emulcycle.i CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +VIC_SC.obj: VIC_SC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +CIA_SC.obj: CIA_SC.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +C64_PC.obj: C64.cpp C64.h C64_WIN32.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CXX) $(ALLPCCFLAGS) -Fo$@ -c $*.cpp + +CPUC64_PC.obj: CPUC64.cpp CPUC64.h CPU_emulline.i CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CXX) $(ALLPCCFLAGS) -DPC_IS_POINTER=0 -Fo$@ -c $*.cpp + +CPU1541_PC.obj: CPU1541_PC.cpp CPU1541.h CPU_emulline.i CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CXX) $(ALLPCCFLAGS) -Fo$@ -c $*.cpp + +## Dependencies +main.obj: main.cpp main.h main_WIN32.i C64.h Display.h Prefs.h SAM.h +C64.obj: C64.cpp C64.h C64_WIN32.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h +CmdPipe.obj: CmdPipe.h +Display.obj: Display.cpp Display.h Display_WIN32.i main.h Prefs.h Version.h resource.h +Prefs.obj: Prefs.cpp Prefs.h Prefs_WIN32.i Display.h main.h resource.h +CPUC64.obj: CPUC64.cpp CPUC64.h CPU_emulline.i C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h +CPU1541.obj: CPU1541.cpp CPU1541.h CPU_emulline.i 1541job.h C64.h CIA.h Display.h +CPU_common.obj: CPU_common.cpp CPU_common.h +VIC.obj: VIC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h +SID.obj: SID.cpp SID.h SID_WIN32.i Prefs.h main.h +CIA.obj: CIA.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h +IEC.obj: IEC.cpp IEC.h 1541fs.h 1541d64.h 1541t64.h Prefs.h Display.h +1541fs.obj: 1541fs.cpp 1541fs.h IEC.h main.h Prefs.h +1541fs.h: IEC.h +1541d64.obj: 1541d64.cpp 1541d64.h IEC.h Prefs.h +1541d64.h: IEC.h +1541t64.obj: 1541t64.cpp 1541t64.h IEC.h Prefs.h +1541t64.h: IEC.h +1541job.obj: 1541job.cpp 1541job.h CPU1541.h Prefs.h +REU.obj: REU.cpp REU.h CPUC64.h Prefs.h +SAM.obj: SAM.cpp SAM.h C64.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h diff --git a/Src/Makefile.bak b/Src/Makefile.bak new file mode 100644 index 0000000..7992fdd --- /dev/null +++ b/Src/Makefile.bak @@ -0,0 +1,133 @@ +# Makefile.in for Frodo (generic Unix/X11) +# Copyright (C) 1995-1997 Christian Bauer + +## Version information +VERSION = 4 +REVISION = 1 + + +CXX = g++ +LIBS = -L/usr/lib -Wl,-rpath,/usr/lib -lSDL -lpthread +CFLAGS = -O2 -g -fomit-frame-pointer -Wall -Wno-unused -Wno-format -I/usr/include/SDL -D_REENTRANT -DHAVE_SDL -fno-strength-reduce -DREGPARAM="__attribute__((regparm(3)))" -I./ -DFRODO_HPUX_REV=0 -DKBD_LANG=0 + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +prefix = /usr/local +exec_prefix = ${prefix} +bindir = ${exec_prefix}/bin +libdir = ${exec_prefix}/lib +sysconfdir = ${prefix}/etc + +.SUFFIXES: .o .cpp .h + +## Files +OBJS = main.o Display.o Prefs.o SID.o REU.o IEC.o 1541fs.o \ + 1541d64.o 1541t64.o 1541job.o SAM.o CmdPipe.o +SLOBJS = $(OBJS) C64.o CPUC64.o VIC.o CIA.o CPU1541.o +PCOBJS = $(OBJS) C64_PC.o CPUC64_PC.o VIC.o CIA.o CPU1541_PC.o +PCFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 +SCOBJS = $(OBJS) C64_SC.o CPUC64_SC.o VIC_SC.o CIA_SC.o CPU1541_SC.o CPU_common.o +SCFLAGS = -DFRODO_SC +SRCS = main.cpp Display.cpp Prefs.cpp SID.cpp REU.cpp IEC.cpp 1541fs.cpp \ + 1541d64.cpp 1541t64.cpp 1541job.cpp SAM.cpp CmdPipe.cpp C64.cpp \ + C64_PC.cpp C64_SC.cpp CPUC64.cpp CPUC64_PC.cpp CPUC64_SC.cpp \ + VIC.cpp VIC_SC.cpp CIA.cpp CIA_SC.cpp CPU1541.cpp CPU1541_PC.cpp \ + CPU1541_SC.cpp CPU_common.cpp + +all: Frodo FrodoPC FrodoSC + +Frodo: $(SLOBJS) + $(CXX) -o Frodo $(SLOBJS) $(LDFLAGS) $(LIBS) + cp Frodo .. + +FrodoPC: $(PCOBJS) + $(CXX) -o FrodoPC $(PCOBJS) $(LDFLAGS) $(LIBS) + cp FrodoPC .. + +FrodoSC: $(SCOBJS) + $(CXX) -o FrodoSC $(SCOBJS) $(LDFLAGS) $(LIBS) + cp FrodoSC .. + +clean: + rm -f $(SLOBJS) $(PCOBJS) $(SCOBJS) + rm -f Frodo FrodoPC FrodoSC + +distclean: clean + rm -rf autom4te.cache + rm -f Makefile sysconfig.h + rm -f config.cache config.log config.status config.h + +depend dep: + makedepend $(CPPFLAGS) -Y. $(SRCS) 2>/dev/null + +.cpp.o: + $(CC) $(INCLUDES) $(CFLAGS) -o $@ -c $*.cpp + +.cpp.s: + $(CC) $(INCLUDES) $(CFLAGS) $(EXTRAFLAGS) -o $@ -S $*.cpp -g0 + +C64_PC.o: C64.cpp C64.h C64_x.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +CPUC64_PC.o: CPUC64.cpp CPUC64.h CPU_emulline.i C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CC) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +CPU1541_PC.o: CPU1541.cpp CPU1541.h CPU_emulline.i 1541job.h C64.h CIA.h Display.h + $(CC) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +C64_SC.o: C64_SC.cpp C64.h C64_x.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPUC64_SC.o: CPUC64_SC.cpp CPUC64.h CPU_emulcycle.i CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPU1541_SC.o: CPU1541_SC.cpp CPU1541.h CPU_emulcycle.i CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +VIC_SC.o: VIC_SC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CIA_SC.o: CIA_SC.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +#------------------------------------------------------------------------- +# DO NOT DELETE THIS LINE -- make depend depends on it. + +main.o: sysdeps.h sysconfig.h main.h C64.h Display.h Prefs.h SAM.h +Display.o: sysdeps.h sysconfig.h Display.h main.h Prefs.h +Prefs.o: sysdeps.h sysconfig.h Prefs.h Display.h C64.h main.h +SID.o: sysdeps.h sysconfig.h SID.h Prefs.h +REU.o: sysdeps.h sysconfig.h REU.h CPUC64.h C64.h Prefs.h +IEC.o: sysdeps.h sysconfig.h IEC.h 1541fs.h 1541d64.h 1541t64.h Prefs.h +IEC.o: Display.h +1541fs.o: sysdeps.h sysconfig.h 1541fs.h IEC.h main.h Prefs.h +1541d64.o: sysdeps.h sysconfig.h 1541d64.h IEC.h Prefs.h +1541t64.o: sysdeps.h sysconfig.h 1541t64.h IEC.h Prefs.h +1541job.o: sysdeps.h sysconfig.h 1541job.h CPU1541.h CIA.h Prefs.h C64.h +SAM.o: sysdeps.h sysconfig.h SAM.h C64.h CPUC64.h CPU1541.h CIA.h Prefs.h +SAM.o: VIC.h SID.h +CmdPipe.o: CmdPipe.h +C64.o: sysdeps.h sysconfig.h C64.h CPUC64.h CPU1541.h CIA.h Prefs.h VIC.h +C64.o: SID.h REU.h IEC.h 1541job.h Display.h +C64_PC.o: C64.cpp sysdeps.h sysconfig.h C64.h CPUC64.h CPU1541.h CIA.h +C64_PC.o: Prefs.h VIC.h SID.h REU.h IEC.h 1541job.h Display.h +C64_SC.o: C64.cpp sysdeps.h sysconfig.h C64.h CPUC64.h CPU1541.h CIA.h +C64_SC.o: Prefs.h VIC.h SID.h REU.h IEC.h 1541job.h Display.h +CPUC64.o: sysdeps.h sysconfig.h CPUC64.h C64.h VIC.h SID.h CIA.h Prefs.h +CPUC64.o: REU.h IEC.h Display.h Version.h CPU_emulline.i +CPUC64_PC.o: CPUC64.cpp sysdeps.h sysconfig.h CPUC64.h C64.h VIC.h SID.h +CPUC64_PC.o: CIA.h Prefs.h REU.h IEC.h Display.h Version.h CPU_emulline.i +CPUC64_SC.o: sysdeps.h sysconfig.h CPUC64.h C64.h CPU_common.h VIC.h SID.h +CPUC64_SC.o: CIA.h Prefs.h REU.h IEC.h Display.h Version.h CPU_emulcycle.i +VIC.o: sysdeps.h sysconfig.h VIC.h C64.h CPUC64.h Display.h Prefs.h +VIC_SC.o: sysdeps.h sysconfig.h VIC.h C64.h CPUC64.h Display.h Prefs.h +CIA.o: sysdeps.h sysconfig.h CIA.h Prefs.h CPUC64.h C64.h CPU1541.h VIC.h +CIA_SC.o: sysdeps.h sysconfig.h CIA.h Prefs.h CPUC64.h C64.h CPU1541.h VIC.h +CPU1541.o: sysdeps.h sysconfig.h CPU1541.h CIA.h Prefs.h C64.h 1541job.h +CPU1541.o: Display.h CPU_emulline.i +CPU1541_PC.o: CPU1541.cpp sysdeps.h sysconfig.h CPU1541.h CIA.h Prefs.h C64.h +CPU1541_PC.o: 1541job.h Display.h CPU_emulline.i +CPU1541_SC.o: sysdeps.h sysconfig.h CPU1541.h CIA.h Prefs.h C64.h +CPU1541_SC.o: CPU_common.h 1541job.h Display.h CPU_emulcycle.i +CPU_common.o: sysdeps.h sysconfig.h CPU_common.h diff --git a/Src/Makefile.in b/Src/Makefile.in new file mode 100644 index 0000000..e848b47 --- /dev/null +++ b/Src/Makefile.in @@ -0,0 +1,95 @@ +# Makefile.in for Frodo (generic Unix/X11) +# Copyright (C) 1995-1997 Christian Bauer + +## Version information +VERSION = 4 +REVISION = 1 + +@SET_MAKE@ +CXX = @CXX@ +LIBS = @LIBS@ +CFLAGS = @CFLAGS@ -I./ -DFRODO_HPUX_REV=@HPUX_REV@ -DKBD_LANG=@KBD_LANG@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +sysconfdir = @sysconfdir@ + +.SUFFIXES: .o .cpp .h + +## Files +OBJS = main.o Display.o Prefs.o SID.o REU.o IEC.o 1541fs.o \ + 1541d64.o 1541t64.o 1541job.o SAM.o CmdPipe.o +SLOBJS = $(OBJS) C64.o CPUC64.o VIC.o CIA.o CPU1541.o +PCOBJS = $(OBJS) C64_PC.o CPUC64_PC.o VIC.o CIA.o CPU1541_PC.o +PCFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 +SCOBJS = $(OBJS) C64_SC.o CPUC64_SC.o VIC_SC.o CIA_SC.o CPU1541_SC.o CPU_common.o +SCFLAGS = -DFRODO_SC +SRCS = main.cpp Display.cpp Prefs.cpp SID.cpp REU.cpp IEC.cpp 1541fs.cpp \ + 1541d64.cpp 1541t64.cpp 1541job.cpp SAM.cpp CmdPipe.cpp C64.cpp \ + C64_PC.cpp C64_SC.cpp CPUC64.cpp CPUC64_PC.cpp CPUC64_SC.cpp \ + VIC.cpp VIC_SC.cpp CIA.cpp CIA_SC.cpp CPU1541.cpp CPU1541_PC.cpp \ + CPU1541_SC.cpp CPU_common.cpp + +all: Frodo FrodoPC FrodoSC + +Frodo: $(SLOBJS) + $(CXX) -o Frodo $(SLOBJS) $(LDFLAGS) $(LIBS) + cp Frodo .. + +FrodoPC: $(PCOBJS) + $(CXX) -o FrodoPC $(PCOBJS) $(LDFLAGS) $(LIBS) + cp FrodoPC .. + +FrodoSC: $(SCOBJS) + $(CXX) -o FrodoSC $(SCOBJS) $(LDFLAGS) $(LIBS) + cp FrodoSC .. + +clean: + rm -f $(SLOBJS) $(PCOBJS) $(SCOBJS) + rm -f Frodo FrodoPC FrodoSC + +distclean: clean + rm -rf autom4te.cache + rm -f Makefile sysconfig.h + rm -f config.cache config.log config.status config.h + +depend dep: + makedepend $(CPPFLAGS) -Y. $(SRCS) 2>/dev/null + +.cpp.o: + $(CC) $(INCLUDES) $(CFLAGS) -o $@ -c $*.cpp + +.cpp.s: + $(CC) $(INCLUDES) $(CFLAGS) $(EXTRAFLAGS) -o $@ -S $*.cpp -g0 + +C64_PC.o: C64.cpp C64.h C64_x.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +CPUC64_PC.o: CPUC64.cpp CPUC64.h CPU_emulline.i C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CC) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +CPU1541_PC.o: CPU1541.cpp CPU1541.h CPU_emulline.i 1541job.h C64.h CIA.h Display.h + $(CC) $(INCLUDES) $(CFLAGS) $(PCFLAGS) -o $@ -c $*.cpp + +C64_SC.o: C64_SC.cpp C64.h C64_x.i CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPUC64_SC.o: CPUC64_SC.cpp CPUC64.h CPU_emulcycle.i CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPU1541_SC.o: CPU1541_SC.cpp CPU1541.h CPU_emulcycle.i CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +VIC_SC.o: VIC_SC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CIA_SC.o: CIA_SC.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h + $(CC) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +#------------------------------------------------------------------------- +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/Src/MakefileRO b/Src/MakefileRO new file mode 100644 index 0000000..c7d9aef --- /dev/null +++ b/Src/MakefileRO @@ -0,0 +1,166 @@ +# Makefile.in for Frodo (RISC OS with GCC) +# Copyright (C) 1995-1997 Christian Bauer +# Acorn port 1997 by Andreas Dehmel + +## Version information +VERSION = 4 +REVISION = 0 + +CXX = gcc +CFLAGS = -O2 +CFLAGSO = -O3 +SCFLAGS = -O2 -DFRODO_SC +SCFLAGSO = -O3 -DFRODO_SC +PCFLAGS = -O2 -DFRODO_PC +PCFLAGSO = -O3 -DFRODO_PC + +LIBRARIES = GCC:o.libgcc C:o.stubs +LINK = drlink +ASS = as + +## Files +OBJS = o.Prefs o.REU o.IEC o.1541fs o.1541d64 o.1541t64 o.1541job o.SAM o.ROlib +SLOBJS = $(OBJS) o.C64 o.CPUC64 o.VIC o.CIA o.CPU1541 o.Display o.SID o.main o.AcornGUI +SCOBJS = $(OBJS) o.C64_SC o.CPUC64_SC o.VIC_SC o.CIA_SC o.CPU1541_SC o.CPU_common o.Display_SC\ + o.main_SC o.SID_SC o.AcornGUI_SC +PCOBJS = $(OBJS) o.C64_PC o.CPUC64_PC o.VIC o.CIA o.CPU1541_PC o.Display o.main_PC o.SID\ + o.AcornGUI_PC + + +all: Frodo FrodoSC FrodoPC + +Frodo: $(SLOBJS) + $(LINK) -o Frodo $(LIBRARIES) $(SLOBJS) $(LDFLAGS) + +FrodoSC: $(SCOBJS) + $(LINK) -o FrodoSC $(LIBRARIES) $(SCOBJS) $(LDFLAGS) + +FrodoPC: $(PCOBJS) + $(LINK) -o FrodoPC $(LIBRARIES) $(PCOBJS) $(LDFLAGS) + + +# SC objects +o.C64_SC: cc.C64 h.C64 i.C64_Acorn h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA h.REU \ + h.IEC h.1541job h.Display h.Prefs h.ROlib h.AcornGUI i.OldSnap + $(CXX) $(INCLUDES) $(SCFLAGS) -c C64_SC.cc + +o.CPUC64_SC: cc.CPUC64_SC h.CPUC64 i.CPU_emulcycle h.CPU_common h.C64 h.VIC h.SID h.CIA \ + h.REU h.IEC h.Display h.Version h.ROlib + $(CXX) $(INCLUDES) $(SCFLAGSO) -c CPUC64_SC.cc + +o.CPU1541_SC: cc.CPU1541_SC h.CPU1541 i.CPU_emulcycle h.CPU_common h.1541job h.C64 h.CIA \ + h.Display h.ROlib + $(CXX) $(INCLUDES) $(SCFLAGSO) -c CPU1541_SC.cc + +o.VIC_SC: cc.VIC_SC h.VIC h.C64 h.CPUC64 h.Display h.Prefs h.ROlib + $(CXX) $(INCLUDES) $(SCFLAGSO) -c VIC_SC.cc + +o.CIA_SC: cc.CIA_SC h.CIA h.CPUC64 h.CPU1541 h.VIC h.Prefs + $(CXX) $(INCLUDES) $(SCFLAGSO) -c CIA_SC.cc + +## These were added for RISC OS -- same source code, but different object files needed! +o.main_SC: cc.main_SC cc.main h.main i.main_Acorn h.C64 h.Display h.Prefs h.SAM h.ROlib\ + h.AcornGUI + $(CXX) $(INCLUDES) $(SCFLAGS) -c main_SC.cc + +o.Display_SC: cc.Display_SC cc.Display h.Display i.Display_Acorn h.main h.Prefs h.Version\ + h.ROlib h.C64 h.AcornGUI h.VIC + $(CXX) $(INCLUDES) $(SCFLAGS) -c Display_SC.cc + +o.SID_SC: cc.SID_SC cc.SID h.SID i.SID_Acorn h.Prefs h.ROlib h.C64 i.FixPoint + $(CXX) $(INCLUDES) $(SCFLAGSO) -c SID_SC.cc + +o.AcornGUI_SC: cc.AcornGUI_SC cc.AcornGUI h.AcornGUI h.ROlib h.main h.Prefs h.C64 h.VIC\ + h.Version + $(CXX) $(INCLUDES) $(SCFLAGS) -c AcornGUI_SC.cc + + +# PC objects +o.C64_PC: cc.C64 h.C64 i.C64_Acorn h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA h.REU \ + h.IEC h.1541job h.Display h.Prefs h.ROlib h.AcornGUI i.OldSnap + $(CXX) $(INCLUDES) $(PCFLAGS) -c C64_PC.cc + +o.CPUC64_PC: cc.CPUC64_PC cc.CPUC64 h.CPUC64 i.CPU_emulline h.C64 h.VIC h.SID h.CIA \ + h.REU h.IEC h.Display h.Version h.ROlib + $(CXX) $(INCLUDES) $(PCFLAGSO) -c CPUC64_PC.cc + +o.CPU1541_PC: cc.CPU1541_PC cc.CPU1541 h.CPU1541 i.CPU_emulline h.1541job h.C64 h.CIA \ + h.Display h.ROlib + $(CXX) $(INCLUDES) $(PCFLAGSO) -c CPU1541_PC.cc + +## These were added for RISC OS -- same source code, but different object files needed! +o.main_PC: cc.main_PC cc.main h.main i.main_Acorn h.C64 h.Display h.Prefs h.SAM h.ROlib\ + h.AcornGUI + $(CXX) $(INCLUDES) $(PCFLAGS) -c main_PC.cc + +o.AcornGUI_PC: cc.AcornGUI_PC cc.AcornGUI h.AcornGUI h.ROlib h.main h.Prefs h.C64 h.VIC\ + h.Version + $(CXX) $(INCLUDES) $(PCFLAGS) -c AcornGUI_PC.cc + + +## Dependencies +o.main: cc.main h.main i.main_Acorn h.C64 h.Display h.Prefs h.SAM h.ROlib h.AcornGUI + $(CXX) $(INCLUDES) $(CFLAGS) -c main.cc + +o.C64: cc.C64 h.C64 i.C64_Acorn h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA \ + h.REU h.IEC h.1541job h.Display h.Prefs h.ROlib h.AcornGUI i.OldSnap + $(CXX) $(INCLUDES) $(CFLAGS) -c C64.cc + +o.Display: cc.Display h.Display i.Display_Acorn h.main h.Prefs h.Version h.ROlib h.C64\ + h.AcornGUI h.VIC + $(CXX) $(INCLUDES) $(CFLAGS) -c Display.cc + +o.Prefs: cc.Prefs h.Prefs h.Display h.main h.ROlib + $(CXX) $(INCLUDES) $(CFLAGS) -c Prefs.cc + +o.CPUC64: cc.CPUC64 h.CPUC64 i.CPU_emulline h.C64 h.VIC h.SID h.CIA h.REU h.IEC \ + h.Display h.Version h.ROlib + $(CXX) $(INCLUDES) $(CFLAGSO) -c CPUC64.cc + +o.CPU1541: cc.CPU1541 h.CPU1541 i.CPU_emulline h.1541job h.C64 h.CIA h.Display h.ROlib + $(CXX) $(INCLUDES) $(CFLAGSO) -c CPU1541.cc + +o.CPU_common: cc.CPU_common h.CPU_common + $(CXX) $(INCLUDES) $(CFLAGS) -c CPU_common.cc + +o.VIC: cc.VIC h.VIC h.C64 h.CPUC64 h.Display h.Prefs h.ROlib i.el_Acorn + $(CXX) $(INCLUDES) $(CFLAGSO) -c VIC.cc + +o.SID: cc.SID h.SID i.SID_Acorn h.Prefs h.ROlib h.C64 i.FixPoint + $(CXX) $(INCLUDES) $(CFLAGSO) -c SID.cc + +o.CIA: cc.CIA h.CIA h.CPUC64 h.CPU1541 h.VIC h.Prefs + $(CXX) $(INCLUDES) $(CFLAGSO) -c CIA.cc + +o.IEC: cc.IEC h.IEC h.1541fs h.1541d64 h.1541t64 h.Prefs h.Display h.ROlib + $(CXX) $(INCLUDES) $(CFLAGS) -c IEC.cc + +o.1541fs: cc.1541fs h.1541fs h.IEC h.main h.Prefs h.ROlib + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541fs.cc + +h.1541fs: h.IEC + +o.1541d64: cc.1541d64 h.1541d64 h.IEC h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541d64.cc + +h.1541d64: h.IEC + +o.1541t64: cc.1541t64 h.1541t64 h.IEC h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541t64.cc + +h.1541t64: h.IEC + +o.1541job: cc.1541job h.1541job h.CPU1541 h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541job.cc + +o.REU: cc.REU h.REU h.CPUC64 h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c REU.cc + +o.SAM: cc.SAM h.SAM h.C64 h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA + $(CXX) $(INCLUDES) $(CFLAGS) -c SAM.cc + +o.ROlib: s.ROlib + $(ASS) -o o.ROlib s.ROlib + +o.AcornGUI: cc.AcornGUI h.AcornGUI h.ROlib h.main h.Display h.Prefs h.Version h.C64 h.VIC + $(CXX) $(INCLUDES) $(CFLAGS) -c AcornGUI.cc diff --git a/Src/Prefs.cpp b/Src/Prefs.cpp new file mode 100644 index 0000000..229d8ba --- /dev/null +++ b/Src/Prefs.cpp @@ -0,0 +1,407 @@ +/* + * Prefs.cpp - Global preferences + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysdeps.h" + +#include "Prefs.h" +#include "Display.h" +#include "C64.h" +#include "main.h" + + +// These are the active preferences +Prefs ThePrefs; + +// These are the preferences on disk +Prefs ThePrefsOnDisk; + + +/* + * Constructor: Set up preferences with defaults + */ + +Prefs::Prefs() +{ + NormalCycles = 63; + BadLineCycles = 23; + CIACycles = 63; + FloppyCycles = 64; + SkipFrames = 2; + LatencyMin = 80; + LatencyMax = 120; + LatencyAvg = 280; + ScalingNumerator = 2; + ScalingDenominator = 2; + + for (int i=0; i<4; i++) + DriveType[i] = DRVTYPE_DIR; + + strcpy(DrivePath[0], "64prgs"); + strcpy(DrivePath[1], ""); + strcpy(DrivePath[2], ""); + strcpy(DrivePath[3], ""); + + strcpy(ViewPort, "Default"); + strcpy(DisplayMode, "Default"); + + SIDType = SIDTYPE_NONE; + REUSize = REU_NONE; + DisplayType = DISPTYPE_WINDOW; + + SpritesOn = true; + SpriteCollisions = true; + Joystick1On = false; + Joystick2On = false; + JoystickSwap = false; + LimitSpeed = false; + FastReset = false; + CIAIRQHack = false; + MapSlash = true; + Emul1541Proc = false; + SIDFilters = true; + DoubleScan = true; + HideCursor = false; + DirectSound = true; + ExclusiveSound = false; + AutoPause = false; + PrefsAtStartup = false; + SystemMemory = false; + AlwaysCopy = false; + SystemKeys = true; + ShowLEDs = true; +} + + +/* + * Check if two Prefs structures are equal + */ + +bool Prefs::operator==(const Prefs &rhs) const +{ + return (1 + && NormalCycles == rhs.NormalCycles + && BadLineCycles == rhs.BadLineCycles + && CIACycles == rhs.CIACycles + && FloppyCycles == rhs.FloppyCycles + && SkipFrames == rhs.SkipFrames + && LatencyMin == rhs.LatencyMin + && LatencyMax == rhs.LatencyMax + && LatencyAvg == rhs.LatencyAvg + && ScalingNumerator == rhs.ScalingNumerator + && ScalingDenominator == rhs.ScalingNumerator + && DriveType[0] == rhs.DriveType[0] + && DriveType[1] == rhs.DriveType[1] + && DriveType[2] == rhs.DriveType[2] + && DriveType[3] == rhs.DriveType[3] + && strcmp(DrivePath[0], rhs.DrivePath[0]) == 0 + && strcmp(DrivePath[1], rhs.DrivePath[1]) == 0 + && strcmp(DrivePath[2], rhs.DrivePath[2]) == 0 + && strcmp(DrivePath[3], rhs.DrivePath[3]) == 0 + && strcmp(ViewPort, rhs.ViewPort) == 0 + && strcmp(DisplayMode, rhs.DisplayMode) == 0 + && SIDType == rhs.SIDType + && REUSize == rhs.REUSize + && DisplayType == rhs.DisplayType + && SpritesOn == rhs.SpritesOn + && SpriteCollisions == rhs.SpriteCollisions + && Joystick1On == rhs.Joystick1On + && Joystick2On == rhs.Joystick2On + && JoystickSwap == rhs.JoystickSwap + && LimitSpeed == rhs.LimitSpeed + && FastReset == rhs.FastReset + && CIAIRQHack == rhs.CIAIRQHack + && MapSlash == rhs.MapSlash + && Emul1541Proc == rhs.Emul1541Proc + && SIDFilters == rhs.SIDFilters + && DoubleScan == rhs.DoubleScan + && HideCursor == rhs.HideCursor + && DirectSound == rhs.DirectSound + && ExclusiveSound == rhs.ExclusiveSound + && AutoPause == rhs.AutoPause + && PrefsAtStartup == rhs.PrefsAtStartup + && SystemMemory == rhs.SystemMemory + && AlwaysCopy == rhs.AlwaysCopy + && SystemKeys == rhs.SystemKeys + && ShowLEDs == rhs.ShowLEDs + ); +} + +bool Prefs::operator!=(const Prefs &rhs) const +{ + return !operator==(rhs); +} + + +/* + * Check preferences for validity and correct if necessary + */ + +void Prefs::Check(void) +{ + if (SkipFrames <= 0) SkipFrames = 1; + + if (SIDType < SIDTYPE_NONE || SIDType > SIDTYPE_SIDCARD) + SIDType = SIDTYPE_NONE; + + if (REUSize < REU_NONE || REUSize > REU_512K) + REUSize = REU_NONE; + + if (DisplayType < DISPTYPE_WINDOW || DisplayType > DISPTYPE_SCREEN) + DisplayType = DISPTYPE_WINDOW; + + for (int i=0; i<4; i++) + if (DriveType[i] < DRVTYPE_DIR || DriveType[i] > DRVTYPE_T64) + DriveType[i] = DRVTYPE_DIR; +} + + +/* + * Load preferences from file + */ + +void Prefs::Load(char *filename) +{ + FILE *file; + char line[256], keyword[256], value[256]; + + if ((file = fopen(filename, "r")) != NULL) { + while(fgets(line, 255, file)) { + if (sscanf(line, "%s = %s\n", keyword, value) == 2) { + if (!strcmp(keyword, "NormalCycles")) + NormalCycles = atoi(value); + else if (!strcmp(keyword, "BadLineCycles")) + BadLineCycles = atoi(value); + else if (!strcmp(keyword, "CIACycles")) + CIACycles = atoi(value); + else if (!strcmp(keyword, "FloppyCycles")) + FloppyCycles = atoi(value); + else if (!strcmp(keyword, "SkipFrames")) + SkipFrames = atoi(value); + else if (!strcmp(keyword, "LatencyMin")) + LatencyMin = atoi(value); + else if (!strcmp(keyword, "LatencyMax")) + LatencyMax = atoi(value); + else if (!strcmp(keyword, "LatencyAvg")) + LatencyAvg = atoi(value); + else if (!strcmp(keyword, "ScalingNumerator")) + ScalingNumerator = atoi(value); + else if (!strcmp(keyword, "ScalingDenominator")) + ScalingDenominator = atoi(value); + else if (!strcmp(keyword, "DriveType8")) + if (!strcmp(value, "DIR")) + DriveType[0] = DRVTYPE_DIR; + else if (!strcmp(value, "D64")) + DriveType[0] = DRVTYPE_D64; + else + DriveType[0] = DRVTYPE_T64; + else if (!strcmp(keyword, "DriveType9")) + if (!strcmp(value, "DIR")) + DriveType[1] = DRVTYPE_DIR; + else if (!strcmp(value, "D64")) + DriveType[1] = DRVTYPE_D64; + else + DriveType[1] = DRVTYPE_T64; + else if (!strcmp(keyword, "DriveType10")) + if (!strcmp(value, "DIR")) + DriveType[2] = DRVTYPE_DIR; + else if (!strcmp(value, "D64")) + DriveType[2] = DRVTYPE_D64; + else + DriveType[2] = DRVTYPE_T64; + else if (!strcmp(keyword, "DriveType11")) + if (!strcmp(value, "DIR")) + DriveType[3] = DRVTYPE_DIR; + else if (!strcmp(value, "D64")) + DriveType[3] = DRVTYPE_D64; + else + DriveType[3] = DRVTYPE_T64; + else if (!strcmp(keyword, "DrivePath8")) + strcpy(DrivePath[0], value); + else if (!strcmp(keyword, "DrivePath9")) + strcpy(DrivePath[1], value); + else if (!strcmp(keyword, "DrivePath10")) + strcpy(DrivePath[2], value); + else if (!strcmp(keyword, "DrivePath11")) + strcpy(DrivePath[3], value); + else if (!strcmp(keyword, "ViewPort")) + strcpy(ViewPort, value); + else if (!strcmp(keyword, "DisplayMode")) + strcpy(DisplayMode, value); + else if (!strcmp(keyword, "SIDType")) + if (!strcmp(value, "DIGITAL")) + SIDType = SIDTYPE_DIGITAL; + else if (!strcmp(value, "SIDCARD")) + SIDType = SIDTYPE_SIDCARD; + else + SIDType = SIDTYPE_NONE; + else if (!strcmp(keyword, "REUSize")) { + if (!strcmp(value, "128K")) + REUSize = REU_128K; + else if (!strcmp(value, "256K")) + REUSize = REU_256K; + else if (!strcmp(value, "512K")) + REUSize = REU_512K; + else + REUSize = REU_NONE; + } else if (!strcmp(keyword, "DisplayType")) + DisplayType = strcmp(value, "SCREEN") ? DISPTYPE_WINDOW : DISPTYPE_SCREEN; + else if (!strcmp(keyword, "SpritesOn")) + SpritesOn = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SpriteCollisions")) + SpriteCollisions = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "Joystick1On")) + Joystick1On = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "Joystick2On")) + Joystick2On = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "JoystickSwap")) + JoystickSwap = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "LimitSpeed")) + LimitSpeed = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "FastReset")) + FastReset = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "CIAIRQHack")) + CIAIRQHack = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "MapSlash")) + MapSlash = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "Emul1541Proc")) + Emul1541Proc = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SIDFilters")) + SIDFilters = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "DoubleScan")) + DoubleScan = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "HideCursor")) + HideCursor = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "DirectSound")) + DirectSound = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "ExclusiveSound")) + ExclusiveSound = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "AutoPause")) + AutoPause = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "PrefsAtStartup")) + PrefsAtStartup = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SystemMemory")) + SystemMemory = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "AlwaysCopy")) + AlwaysCopy = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SystemKeys")) + SystemKeys = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "ShowLEDs")) + ShowLEDs = !strcmp(value, "TRUE"); + } + } + fclose(file); + } + Check(); + ThePrefsOnDisk = *this; +} + + +/* + * Save preferences to file + * true: success, false: error + */ + +bool Prefs::Save(char *filename) +{ + FILE *file; + + Check(); + if ((file = fopen(filename, "w")) != NULL) { + fprintf(file, "NormalCycles = %d\n", NormalCycles); + fprintf(file, "BadLineCycles = %d\n", BadLineCycles); + fprintf(file, "CIACycles = %d\n", CIACycles); + fprintf(file, "FloppyCycles = %d\n", FloppyCycles); + fprintf(file, "SkipFrames = %d\n", SkipFrames); + fprintf(file, "LatencyMin = %d\n", LatencyMin); + fprintf(file, "LatencyMax = %d\n", LatencyMax); + fprintf(file, "LatencyAvg = %d\n", LatencyAvg); + fprintf(file, "ScalingNumerator = %d\n", ScalingNumerator); + fprintf(file, "ScalingDenominator = %d\n", ScalingDenominator); + for (int i=0; i<4; i++) { + fprintf(file, "DriveType%d = ", i+8); + switch (DriveType[i]) { + case DRVTYPE_DIR: + fprintf(file, "DIR\n"); + break; + case DRVTYPE_D64: + fprintf(file, "D64\n"); + break; + case DRVTYPE_T64: + fprintf(file, "T64\n"); + break; + } + fprintf(file, "DrivePath%d = %s\n", i+8, DrivePath[i]); + } + fprintf(file, "ViewPort = %s\n", ViewPort); + fprintf(file, "DisplayMode = %s\n", DisplayMode); + fprintf(file, "SIDType = "); + switch (SIDType) { + case SIDTYPE_NONE: + fprintf(file, "NONE\n"); + break; + case SIDTYPE_DIGITAL: + fprintf(file, "DIGITAL\n"); + break; + case SIDTYPE_SIDCARD: + fprintf(file, "SIDCARD\n"); + break; + } + fprintf(file, "REUSize = "); + switch (REUSize) { + case REU_NONE: + fprintf(file, "NONE\n"); + break; + case REU_128K: + fprintf(file, "128K\n"); + break; + case REU_256K: + fprintf(file, "256K\n"); + break; + case REU_512K: + fprintf(file, "512K\n"); + break; + }; + fprintf(file, "DisplayType = %s\n", DisplayType == DISPTYPE_WINDOW ? "WINDOW" : "SCREEN"); + fprintf(file, "SpritesOn = %s\n", SpritesOn ? "TRUE" : "FALSE"); + fprintf(file, "SpriteCollisions = %s\n", SpriteCollisions ? "TRUE" : "FALSE"); + fprintf(file, "Joystick1On = %s\n", Joystick1On ? "TRUE" : "FALSE"); + fprintf(file, "Joystick2On = %s\n", Joystick2On ? "TRUE" : "FALSE"); + fprintf(file, "JoystickSwap = %s\n", JoystickSwap ? "TRUE" : "FALSE"); + fprintf(file, "LimitSpeed = %s\n", LimitSpeed ? "TRUE" : "FALSE"); + fprintf(file, "FastReset = %s\n", FastReset ? "TRUE" : "FALSE"); + fprintf(file, "CIAIRQHack = %s\n", CIAIRQHack ? "TRUE" : "FALSE"); + fprintf(file, "MapSlash = %s\n", MapSlash ? "TRUE" : "FALSE"); + fprintf(file, "Emul1541Proc = %s\n", Emul1541Proc ? "TRUE" : "FALSE"); + fprintf(file, "SIDFilters = %s\n", SIDFilters ? "TRUE" : "FALSE"); + fprintf(file, "DoubleScan = %s\n", DoubleScan ? "TRUE" : "FALSE"); + fprintf(file, "HideCursor = %s\n", HideCursor ? "TRUE" : "FALSE"); + fprintf(file, "DirectSound = %s\n", DirectSound ? "TRUE" : "FALSE"); + fprintf(file, "ExclusiveSound = %s\n", ExclusiveSound ? "TRUE" : "FALSE"); + fprintf(file, "AutoPause = %s\n", AutoPause ? "TRUE" : "FALSE"); + fprintf(file, "PrefsAtStartup = %s\n", PrefsAtStartup ? "TRUE" : "FALSE"); + fprintf(file, "SystemMemory = %s\n", SystemMemory ? "TRUE" : "FALSE"); + fprintf(file, "AlwaysCopy = %s\n", AlwaysCopy ? "TRUE" : "FALSE"); + fprintf(file, "SystemKeys = %s\n", SystemKeys ? "TRUE" : "FALSE"); + fprintf(file, "ShowLEDs = %s\n", ShowLEDs ? "TRUE" : "FALSE"); + fclose(file); + ThePrefsOnDisk = *this; + return true; + } + return false; +} + + +#ifdef __BEOS__ +#include "Prefs_Be.i" +#endif + +#ifdef AMIGA +#include "Prefs_Amiga.i" +#endif + +#ifdef WIN32 +#include "Prefs_WIN32.i" +#endif diff --git a/Src/Prefs.h b/Src/Prefs.h new file mode 100644 index 0000000..296a838 --- /dev/null +++ b/Src/Prefs.h @@ -0,0 +1,126 @@ +/* + * Prefs.h - Global preferences + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _PREFS_H +#define _PREFS_H + + +// Drive types +enum { + DRVTYPE_DIR, // 1541 emulation in host file system + DRVTYPE_D64, // 1541 emulation in .d64 file + DRVTYPE_T64 // 1541 emulation in .t64 file +}; + + +// SID types +enum { + SIDTYPE_NONE, // SID emulation off + SIDTYPE_DIGITAL, // Digital SID emulation + SIDTYPE_SIDCARD // SID card +}; + + +// REU sizes +enum { + REU_NONE, // No REU + REU_128K, // 128K + REU_256K, // 256K + REU_512K // 512K +}; + + +// Display types (BeOS) +enum { + DISPTYPE_WINDOW, // BWindow + DISPTYPE_SCREEN // BWindowScreen +}; + + +// Preferences data +class Prefs { +public: + Prefs(); + bool ShowEditor(bool startup, char *prefs_name); + void Check(void); + void Load(char *filename); + bool Save(char *filename); + + bool operator==(const Prefs &rhs) const; + bool operator!=(const Prefs &rhs) const; + + int NormalCycles; // Available CPU cycles in normal raster lines + int BadLineCycles; // Available CPU cycles in Bad Lines + int CIACycles; // CIA timer ticks per raster line + int FloppyCycles; // Available 1541 CPU cycles per line + int SkipFrames; // Draw every n-th frame + + int DriveType[4]; // Type of drive 8..11 + + char DrivePath[4][256]; // Path for drive 8..11 + + char ViewPort[256]; // Size of the C64 screen to display (Win32) + char DisplayMode[256]; // Video mode to use for full screen (Win32) + + int SIDType; // SID emulation type + int REUSize; // Size of REU + int DisplayType; // Display type (BeOS) + int LatencyMin; // Min msecs ahead of sound buffer (Win32) + int LatencyMax; // Max msecs ahead of sound buffer (Win32) + int LatencyAvg; // Averaging interval in msecs (Win32) + int ScalingNumerator; // Window scaling numerator (Win32) + int ScalingDenominator; // Window scaling denominator (Win32) + + bool SpritesOn; // Sprite display is on + bool SpriteCollisions; // Sprite collision detection is on + bool Joystick1On; // Joystick connected to port 1 of host + bool Joystick2On; // Joystick connected to port 2 of host + bool JoystickSwap; // Swap joysticks 1<->2 + bool LimitSpeed; // Limit speed to 100% + bool FastReset; // Skip RAM test on reset + bool CIAIRQHack; // Write to CIA ICR clears IRQ + bool MapSlash; // Map '/' in C64 filenames + bool Emul1541Proc; // Enable processor-level 1541 emulation + bool SIDFilters; // Emulate SID filters + bool DoubleScan; // Double scan lines (BeOS, if DisplayType == DISPTYPE_SCREEN) + bool HideCursor; // Hide mouse cursor when visible (Win32) + bool DirectSound; // Use direct sound (instead of wav) (Win32) + bool ExclusiveSound; // Use exclusive mode with direct sound (Win32) + bool AutoPause; // Auto pause when not foreground app (Win32) + bool PrefsAtStartup; // Show prefs dialog at startup (Win32) + bool SystemMemory; // Put view work surface in system mem (Win32) + bool AlwaysCopy; // Always use a work surface (Win32) + bool SystemKeys; // Enable system keys and menu keys (Win32) + bool ShowLEDs; // Show LEDs (Win32) + +#ifdef __mac__ + void ChangeDisks(void); +#endif + +#ifdef WIN32 +private: + static BOOL CALLBACK StandardDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK WIN32DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + BOOL DialogProc(int page, HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + void SetupControls(int page); + void SetValues(int page); + void GetValues(int page); + void BrowseForDevice(int id); + + static Prefs *edit_prefs; + static char *edit_prefs_name; + static HWND hDlg; +#endif +}; + + +// These are the active preferences +extern Prefs ThePrefs; + +// Theses are the preferences on disk +extern Prefs ThePrefsOnDisk; + +#endif diff --git a/Src/Prefs_Amiga.i b/Src/Prefs_Amiga.i new file mode 100644 index 0000000..e560d83 --- /dev/null +++ b/Src/Prefs_Amiga.i @@ -0,0 +1,419 @@ +/* + * Prefs_Amiga.i - Global preferences, Amiga specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "AmigaGUI.h" +} + + +// Flag: All done, close prefs window +static bool done, result; + +// Pointer to preferences being edited +static Prefs *prefs; + +// Pointer to prefs file name +static char *path; + +// File requesters +struct FileRequester *open_req, *save_req, *drive_req; + +// Prototypes +static void set_values(void); +static void get_values(void); +static void ghost_gadgets(void); +static void get_drive(int i); + + +/* + * Show preferences editor (synchronously) + * prefs_name points to the file name of the preferences (which may be changed) + */ + +bool Prefs::ShowEditor(bool startup, char *prefs_name) +{ + done = result = FALSE; + prefs = this; + path = prefs_name; + open_req = save_req = NULL; + + // Open prefs window + if (!SetupScreen()) { + if (!OpenPrefsWindow()) { + + // Allocate file requesters + open_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)PrefsWnd, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Open preferences...", + ASLFR_RejectIcons, TRUE, + TAG_DONE); + save_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)PrefsWnd, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Save preferences as...", + ASLFR_DoSaveMode, TRUE, + ASLFR_RejectIcons, TRUE, + TAG_DONE); + drive_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)PrefsWnd, + ASLFR_SleepWindow, TRUE, + ASLFR_RejectIcons, TRUE, + TAG_DONE); + + // Handle prefs window + set_values(); + while (!done) { + WaitPort(PrefsWnd->UserPort); + HandlePrefsIDCMP(); + } + + // Free file requesters + FreeAslRequest(open_req); + FreeAslRequest(save_req); + FreeAslRequest(drive_req); + } + ClosePrefsWindow(); + } + CloseDownScreen(); + + return result; +} + + +/* + * Set the values of the gadgets + */ + +static void set_values(void) +{ + prefs->Check(); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_NormalCycles], PrefsWnd, NULL, GTIN_Number, prefs->NormalCycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_BadLineCycles], PrefsWnd, NULL, GTIN_Number, prefs->BadLineCycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIACycles], PrefsWnd, NULL, GTIN_Number, prefs->CIACycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_FloppyCycles], PrefsWnd, NULL, GTIN_Number, prefs->FloppyCycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_SkipFrames], PrefsWnd, NULL, GTIN_Number, prefs->SkipFrames, TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_SIDType], PrefsWnd, NULL, GTCY_Active, prefs->SIDType, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_REUSize], PrefsWnd, NULL, GTCY_Active, prefs->REUSize, TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_SpritesOn], PrefsWnd, NULL, GTCB_Checked, prefs->SpritesOn, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_SpriteCollisions], PrefsWnd, NULL, GTCB_Checked, prefs->SpriteCollisions, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_Joystick2On], PrefsWnd, NULL, GTCB_Checked, prefs->Joystick2On, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_JoystickSwap], PrefsWnd, NULL, GTCB_Checked, prefs->JoystickSwap, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_LimitSpeed], PrefsWnd, NULL, GTCB_Checked, prefs->LimitSpeed, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_FastReset], PrefsWnd, NULL, GTCB_Checked, prefs->FastReset, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIAIRQHack], PrefsWnd, NULL, GTCB_Checked, prefs->CIAIRQHack, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_SIDFilters], PrefsWnd, NULL, GTCB_Checked, prefs->SIDFilters, TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath8], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[0], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath9], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[1], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath10], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[2], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath11], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[3], TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_DriveType8], PrefsWnd, NULL, GTCY_Active, prefs->DriveType[0], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DriveType9], PrefsWnd, NULL, GTCY_Active, prefs->DriveType[1], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DriveType10], PrefsWnd, NULL, GTCY_Active, prefs->DriveType[2], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DriveType11], PrefsWnd, NULL, GTCY_Active, prefs->DriveType[3], TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_MapSlash], PrefsWnd, NULL, GTCB_Checked, prefs->MapSlash, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_Emul1541Proc], PrefsWnd, NULL, GTCB_Checked, prefs->Emul1541Proc, TAG_DONE); + + ghost_gadgets(); +} + + +/* + * Get the values of the gadgets + */ + +static void get_values(void) +{ + prefs->NormalCycles = GetNumber(PrefsGadgets[GDX_NormalCycles]); + prefs->BadLineCycles = GetNumber(PrefsGadgets[GDX_BadLineCycles]); + prefs->CIACycles = GetNumber(PrefsGadgets[GDX_CIACycles]); + prefs->FloppyCycles = GetNumber(PrefsGadgets[GDX_FloppyCycles]); + prefs->SkipFrames = GetNumber(PrefsGadgets[GDX_SkipFrames]); + + strcpy(prefs->DrivePath[0], GetString(PrefsGadgets[GDX_DrivePath8])); + strcpy(prefs->DrivePath[1], GetString(PrefsGadgets[GDX_DrivePath9])); + strcpy(prefs->DrivePath[2], GetString(PrefsGadgets[GDX_DrivePath10])); + strcpy(prefs->DrivePath[3], GetString(PrefsGadgets[GDX_DrivePath11])); + + prefs->Check(); +} + + +/* + * Enable/disable certain gadgets + */ + +static void ghost_gadgets(void) +{ + GT_SetGadgetAttrs(PrefsGadgets[GDX_NormalCycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_BadLineCycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIACycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_FloppyCycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIAIRQHack], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); +} + + +/* + * Handle gadgets + */ + +int SpritesOnClicked(void) +{ + prefs->SpritesOn = !prefs->SpritesOn; +} + +int SpriteCollisionsClicked(void) +{ + prefs->SpriteCollisions = !prefs->SpriteCollisions; +} + +int Joystick2OnClicked(void) +{ + prefs->Joystick2On = !prefs->Joystick2On; +} + +int JoystickSwapClicked(void) +{ + prefs->JoystickSwap = !prefs->JoystickSwap; +} + +int LimitSpeedClicked(void) +{ + prefs->LimitSpeed = !prefs->LimitSpeed; +} + +int FastResetClicked(void) +{ + prefs->FastReset = !prefs->FastReset; +} + +int CIAIRQHackClicked(void) +{ + prefs->CIAIRQHack = !prefs->CIAIRQHack; +} + +int SIDFiltersClicked(void) +{ + prefs->SIDFilters = !prefs->SIDFilters; +} + +int NormalCyclesClicked(void) {} +int BadLineCyclesClicked(void) {} +int CIACyclesClicked(void) {} +int FloppyCyclesClicked(void) {} +int SkipFramesClicked(void) {} +int DrivePath8Clicked(void) {} +int DrivePath9Clicked(void) {} +int DrivePath10Clicked(void) {} +int DrivePath11Clicked(void) {} + +int SIDTypeClicked(void) +{ + prefs->SIDType = PrefsMsg.Code; +} + +int REUSizeClicked(void) +{ + prefs->REUSize = PrefsMsg.Code; +} + +int DriveType8Clicked(void) +{ + prefs->DriveType[0] = PrefsMsg.Code; +} + +int DriveType9Clicked(void) +{ + prefs->DriveType[1] = PrefsMsg.Code; +} + +int DriveType10Clicked(void) +{ + prefs->DriveType[2] = PrefsMsg.Code; +} + +int DriveType11Clicked(void) +{ + prefs->DriveType[3] = PrefsMsg.Code; +} + +void get_drive(int i) +{ + bool result = FALSE; + + if (drive_req == NULL) + return; + + get_values(); + + if (prefs->DriveType[i] == DRVTYPE_DIR) + result = AslRequestTags(drive_req, + ASLFR_TitleText, (ULONG)"Frodo: Select directory", + ASLFR_DrawersOnly, TRUE, + ASLFR_DoPatterns, FALSE, + ASLFR_InitialPattern, (ULONG)"#?", + ASLFR_InitialDrawer, (ULONG)prefs->DrivePath[i], + ASLFR_InitialFile, (ULONG)"", + TAG_DONE); + else { + + // Separate path and file + char dir[256], file[256]; + strncpy(dir, prefs->DrivePath[i], 255); + char *s = FilePart(dir); + strncpy(file, s, 255); + *s = 0; + + if (prefs->DriveType[i] == DRVTYPE_D64) + result = AslRequestTags(drive_req, + ASLFR_TitleText, (ULONG)"Frodo: Select disk image file", + ASLFR_DrawersOnly, FALSE, + ASLFR_DoPatterns, TRUE, + ASLFR_InitialPattern, (ULONG)"#?.(d64|x64)", + ASLFR_InitialDrawer, (ULONG)dir, + ASLFR_InitialFile, (ULONG)file, + TAG_DONE); + else + result = AslRequestTags(drive_req, + ASLFR_TitleText, (ULONG)"Frodo: Select archive file", + ASLFR_DrawersOnly, FALSE, + ASLFR_DoPatterns, TRUE, + ASLFR_InitialPattern, (ULONG)"#?.(t64|LNX)", + ASLFR_InitialDrawer, (ULONG)dir, + ASLFR_InitialFile, (ULONG)file, + TAG_DONE); + } + + if (result) { + strncpy(prefs->DrivePath[i], drive_req->fr_Drawer, 255); + if (prefs->DriveType[i] != DRVTYPE_DIR) + AddPart(prefs->DrivePath[i], drive_req->fr_File, 255); + set_values(); + } +} + +int GetDrive8Clicked(void) +{ + get_drive(0); +} + +int GetDrive9Clicked(void) +{ + get_drive(1); +} + +int GetDrive10Clicked(void) +{ + get_drive(2); +} + +int GetDrive11Clicked(void) +{ + get_drive(3); +} + +int MapSlashClicked(void) +{ + prefs->MapSlash = !prefs->MapSlash; +} + +int Emul1541ProcClicked(void) +{ + prefs->Emul1541Proc = !prefs->Emul1541Proc; +} + +int OKClicked(void) +{ + get_values(); + done = result = TRUE; +} + +int CancelClicked(void) +{ + done = TRUE; + result = FALSE; +} + + +/* + * Handle menus + */ + +int PrefsOpen(void) +{ + if (open_req != NULL && AslRequest(open_req, NULL)) { + strncpy(path, open_req->fr_Drawer, 255); + AddPart(path, open_req->fr_File, 255); + + get_values(); // Useful if Load() is unsuccessful + prefs->Load(path); + set_values(); + } +} + +int PrefsRevert(void) +{ + get_values(); // Useful if Load() is unsuccessful + prefs->Load(path); + set_values(); +} + +int PrefsSaveAs(void) +{ + if (save_req != NULL && AslRequest(save_req, NULL)) { + strncpy(path, save_req->fr_Drawer, 255); + AddPart(path, save_req->fr_File, 255); + + get_values(); + prefs->Save(path); + } +} + +int PrefsSave(void) +{ + get_values(); + prefs->Save(path); + +} + +int PrefsOK(void) +{ + return OKClicked(); +} + +int PrefsCancel(void) +{ + return CancelClicked(); +} + + +/* + * Handle keys + */ + +int PrefsVanillaKey(void) +{ + switch (PrefsMsg.Code) { + case 'o': case 'O': + return OKClicked(); + case 'c': case 'C': + return CancelClicked(); + } +} diff --git a/Src/Prefs_Be.i b/Src/Prefs_Be.i new file mode 100644 index 0000000..387c8c4 --- /dev/null +++ b/Src/Prefs_Be.i @@ -0,0 +1,782 @@ +/* + * Prefs_Be.i - Global preferences, Be specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include +#include + + +// Special colors +const rgb_color light_color = {255, 255, 255, 0}; +const rgb_color fill_color = {216, 216, 216, 0}; +const rgb_color dark_color = {184, 184, 184, 0}; + + +// Window thread messages +const uint32 MSG_OK = 'okok'; +const uint32 MSG_CANCEL = 'cncl'; +const uint32 MSG_SPRITES_ON = 'spon'; +const uint32 MSG_SPRITE_COLLISIONS = 'scol'; +const uint32 MSG_JOYSTICK_1_ON = 'joy1'; +const uint32 MSG_JOYSTICK_2_ON = 'joy2'; +const uint32 MSG_JOYSTICK_SWAP = 'jswp'; +const uint32 MSG_LIMIT_SPEED = 'lmit'; +const uint32 MSG_FAST_RESET = 'frst'; +const uint32 MSG_CIA_IRQ_HACK = 'cirq'; +const uint32 MSG_SID_FILTERS = 'filt'; +const uint32 MSG_DOUBLE_SCAN = 'dbls'; +const uint32 MSG_MAP_SLASH = 'mpsl'; +const uint32 MSG_EMUL_1541_PROC = '15pr'; +const uint32 MSG_DRVTYPE_8 = 'drt8'; +const uint32 MSG_DRVTYPE_9 = 'drt9'; +const uint32 MSG_DRVTYPE_10 = 'drt:'; +const uint32 MSG_DRVTYPE_11 = 'drt;'; +const uint32 MSG_GETDRIVE_8 = 'gtd8'; +const uint32 MSG_GETDRIVE_9 = 'gtd9'; +const uint32 MSG_GETDRIVE_10 = 'gtd:'; +const uint32 MSG_GETDRIVE_11 = 'gtd;'; +const uint32 MSG_DRIVE_PANEL_RETURNED = 'gdr8'; +const uint32 MSG_SID_TYPE = 'sidt'; +const uint32 MSG_REU_SIZE = 'reus'; +const uint32 MSG_DISPLAY_TYPE = 'dspt'; + +const uint32 MSG_OPEN = 'open'; +const uint32 MSG_SAVE = 'save'; +const uint32 MSG_SAVE_AS = 'svas'; +const uint32 MSG_REVERT = 'rvrt'; +const uint32 MSG_OPEN_PANEL_RETURNED = 'oprt'; +const uint32 MSG_SAVE_PANEL_RETURNED = 'svrt'; + + +/* + * Preferences window class + */ + +class NumberControl; +class PathControl; + +class PrefsWindow : public BWindow { +public: + PrefsWindow(Prefs *p, bool start, char *path); + virtual void MessageReceived(BMessage *msg); + virtual bool QuitRequested(void); + virtual bool FilterKeyDown(uint32 *aChar, BView **target); + +private: + BCheckBox *make_checkbox(BRect frame, char *label, uint32 what, BView *parent); + NumberControl *make_number_entry(BRect frame, char *label_text, BView *parent); + BPopUpMenu *make_drvtype_popup(BRect frame, uint32 what, BView *parent); + PathControl *make_path_entry(BRect frame, char *label, BView *parent); + BPopUpMenu *make_sidtype_popup(BRect frame, char *label_text, uint32 what, BView *parent); + BPopUpMenu *make_reusize_popup(BRect frame, char *label_text, uint32 what, BView *parent); + BPopUpMenu *make_disptype_popup(BRect frame, char *label_text, uint32 what, BView *parent); + void set_values(void); + void get_values(void); + void ghost_controls(void); + + Prefs *prefs; + + BMessenger this_messenger; + BFilePanel *open_panel; // For opening prefs + BFilePanel *save_panel; // For saving prefs + BFilePanel *file_panel; // For D64/T64 drives + BFilePanel *dir_panel; // For directory drives + int panel_drive_num; // Drive number (0..3) of the file panel + + BButton *g_ok; + BButton *g_cancel; + NumberControl *g_normal_cycles; + NumberControl *g_bad_line_cycles; + NumberControl *g_cia_cycles; + NumberControl *g_floppy_cycles; + NumberControl *g_skip_frames; + BPopUpMenu *g_drive_type[4]; + PathControl *g_drive_path[4]; + BPopUpMenu *g_sid_type; + BPopUpMenu *g_reu_size; + BPopUpMenu *g_display_type; + BCheckBox *g_sprites_on; + BCheckBox *g_sprite_collisions; + BCheckBox *g_joystick_1_on; + BCheckBox *g_joystick_2_on; + BCheckBox *g_joystick_swap; + BCheckBox *g_limit_speed; + BCheckBox *g_fast_reset; + BCheckBox *g_cia_irq_hack; + BCheckBox *g_sid_filters; + BCheckBox *g_double_scan; + BCheckBox *g_map_slash; + BCheckBox *g_emul_1541_proc; + + char *prefs_path; + bool startup; +}; + + +/* + * Start preferences editor (asynchronously) + * startup = false: Send MSG_PREFS_DONE to application on close + * startup = true : Send MSG_STARTUP to application on close if not canceled, + * B_QUIT_REQUESTED otherwise + * prefs_name points to the file name of the preferences (which may be changed) + */ + +bool Prefs::ShowEditor(bool startup, char *prefs_name) +{ + PrefsWindow *win = new PrefsWindow(this, startup, prefs_name); + win->Show(); + return true; +} + + +/* + * Number-only BTextControl + */ + +// Class definition +class NumberControl : public BTextControl { +public: + NumberControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message); + void SetValue(long value); + long Value(void); +}; + +// Constructor: Allow only digits +NumberControl::NumberControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message) + : BTextControl(frame, name, label, text, message, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE) +{ + SetDivider(divider); + for (int c=0; c<256; c++) + if (!isdigit(c) && c != B_BACKSPACE && c != B_LEFT_ARROW && c != B_RIGHT_ARROW) + ((BTextView *)ChildAt(0))->DisallowChar(c); +} + +// Set integer value +void NumberControl::SetValue(long value) +{ + char str[32]; + sprintf(str, "%ld", value); + SetText(str); +} + +// Get integer value +long NumberControl::Value(void) +{ + return atol(Text()); +} + + +/* + * Path-entry BTextControl + */ + +// Class definition +class PathControl : public BTextControl { +public: + PathControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message); + virtual void MessageReceived(BMessage *msg); +}; + +// Constructor: Disable some keys +PathControl::PathControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message) + : BTextControl(frame, name, label, text, message, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE) +{ + SetDivider(divider); + for (int c=0; c<' '; c++) + if (c != B_BACKSPACE && c != B_LEFT_ARROW && c != B_RIGHT_ARROW) + ((BTextView *)ChildAt(0))->DisallowChar(c); +} + +// Message received: Look out for dropped refs +void PathControl::MessageReceived(BMessage *msg) +{ + if (msg->what == B_SIMPLE_DATA) { + entry_ref the_ref; + BEntry the_entry; + + // First look for refs + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) { + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + BPath the_path; + the_entry.GetPath(&the_path); + SetText(the_path.Path()); + } + } else + BTextControl::MessageReceived(msg); + + MakeFocus(); + } else + BTextControl::MessageReceived(msg); +} + + +/* + * Open preferences window + */ + +PrefsWindow::PrefsWindow(Prefs *p, bool start, char *path) : BWindow(BRect(0, 0, 400, 349), "Frodo Preferences", B_TITLED_WINDOW, B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_NOT_ZOOMABLE), this_messenger(this) +{ + int i; + prefs = p; + startup = start; + prefs_path = path; + + // Move window to right position + Lock(); + MoveTo(80, 80); + + // Set up menus + BMenuBar *bar = new BMenuBar(Bounds(), ""); + BMenu *menu = new BMenu("Preferences"); + BMenuItem *item; + menu->AddItem(item = new BMenuItem("About Frodo" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); + item->SetTarget(be_app); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS, new BMessage(MSG_OPEN), 'O')); + menu->AddItem(new BMenuItem("Save", new BMessage(MSG_SAVE), 'S')); + menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, new BMessage(MSG_SAVE_AS), 'A')); + menu->AddItem(new BMenuItem("Revert", new BMessage(MSG_REVERT))); + menu->AddItem(new BSeparatorItem); + menu->AddItem(item = new BMenuItem("Quit Frodo", new BMessage(B_QUIT_REQUESTED), 'Q')); + item->SetTarget(be_app); + bar->AddItem(menu); + AddChild(bar); + SetKeyMenuBar(bar); + int mbar_height = bar->Frame().bottom + 1; + + // Resize window to fit menu bar + ResizeBy(0, mbar_height); + + // Light gray background + BRect b = Bounds(); + BView *top = new BView(BRect(0, mbar_height, b.right, b.bottom), "top", B_FOLLOW_NONE, B_WILL_DRAW); + AddChild(top); + top->SetViewColor(fill_color); + + // Checkboxes + g_sprites_on = make_checkbox(BRect(10, 10, 160, 21), "Sprite display", MSG_SPRITES_ON, top); + g_sprite_collisions = make_checkbox(BRect(10, 25, 160, 36), "Sprite collisions", MSG_SPRITE_COLLISIONS, top); + g_joystick_1_on = make_checkbox(BRect(10, 40, 160, 51), "Joystick on port 1", MSG_JOYSTICK_1_ON, top); + g_joystick_2_on = make_checkbox(BRect(10, 55, 160, 66), "Joystick on port 2", MSG_JOYSTICK_2_ON, top); + g_joystick_swap = make_checkbox(BRect(10, 70, 160, 81), "Swap joysticks", MSG_JOYSTICK_SWAP, top); + g_limit_speed = make_checkbox(BRect(10, 85, 160, 96), "Limit speed", MSG_LIMIT_SPEED, top); + g_fast_reset = make_checkbox(BRect(10, 100, 160, 111), "Fast reset", MSG_FAST_RESET, top); + g_cia_irq_hack = make_checkbox(BRect(10, 115, 160, 126), "Clear CIA ICR on write", MSG_CIA_IRQ_HACK, top); + g_sid_filters = make_checkbox(BRect(10, 130, 160, 141), "SID filters", MSG_SID_FILTERS, top); + g_double_scan = make_checkbox(BRect(10, 145, 160, 156), "Doublescan lines", MSG_DOUBLE_SCAN, top); + + // Number entry fields + g_normal_cycles = make_number_entry(BRect(160, 10, 390, 26), "Cycles per line (CPU)", top); + g_bad_line_cycles = make_number_entry(BRect(160, 30, 390, 46), "Cycles per Bad Line (CPU)", top); + g_cia_cycles = make_number_entry(BRect(160, 50, 390, 66), "Cycles per line (CIA)", top); + g_floppy_cycles = make_number_entry(BRect(160, 70, 390, 86), "Cycles per line (1541)", top); + g_skip_frames = make_number_entry(BRect(160, 90, 390, 106), "Draw every n-th frame", top); + + // Popup fields + g_display_type = make_disptype_popup(BRect(160, 110, 390, 126), "Display type", MSG_DISPLAY_TYPE, top); + g_sid_type = make_sidtype_popup(BRect(160, 130, 390, 146), "SID emulation type", MSG_SID_TYPE, top); + g_reu_size = make_reusize_popup(BRect(160, 150, 390, 166), "REU size", MSG_REU_SIZE, top); + + // Prepare on/off pictures for file panel buttons + BView *view = new BView(BRect(0, 0, 19, 15), "", B_FOLLOW_NONE, 0); + AddChild(view); + view->SetViewColor(fill_color); + + view->BeginPicture(new BPicture); + view->SetHighColor(fill_color); + view->FillRect(BRect(0, 0, 19, 15)); + view->SetHighColor(light_color); + view->StrokeRect(BRect(0, 0, 18, 0)); + view->StrokeRect(BRect(0, 0, 0, 14)); + view->SetHighColor(dark_color); + view->StrokeRect(BRect(0, 15, 19, 15)); + view->StrokeRect(BRect(19, 0, 19, 15)); + view->SetFont(be_plain_font); + view->SetHighColor(0, 0, 0); + view->SetLowColor(fill_color); + view->MovePenTo(7, 11); + view->DrawString("B"); + BPicture *on = view->EndPicture(); + + view->BeginPicture(new BPicture); + view->SetHighColor(dark_color); + view->FillRect(BRect(0, 0, 19, 15)); + view->SetHighColor(128, 128, 128); + view->StrokeRect(BRect(0, 0, 18, 0)); + view->StrokeRect(BRect(0, 0, 0, 14)); + view->SetHighColor(light_color); + view->StrokeRect(BRect(0, 15, 19, 15)); + view->StrokeRect(BRect(19, 0, 19, 15)); + view->SetFont(be_plain_font); + view->SetHighColor(0, 0, 0); + view->SetLowColor(dark_color); + view->MovePenTo(7, 11); + view->DrawString("B"); + BPicture *off = view->EndPicture(); + + RemoveChild(view); + delete view; + + // Drive settings + BBox *drvbox = new BBox(BRect(10, 173, 390, 304)); + top->AddChild(drvbox); + drvbox->SetViewColor(fill_color); + drvbox->SetLowColor(fill_color); + drvbox->SetLabel("Drives"); + + for (i=0; i<4; i++) { + char str[4]; + sprintf(str, "%d", i+8); + g_drive_path[i] = make_path_entry(BRect(10, 14+i*20, 299, 30+i*20), str, drvbox); + drvbox->AddChild(new BPictureButton(BRect(304, 16+i*20, 323, 31+i*20), "", new BPicture(*on), new BPicture(*off), new BMessage(MSG_GETDRIVE_8 + i))); + g_drive_type[i] = make_drvtype_popup(BRect(329, 14+i*20, 373, 30+i*20), MSG_DRVTYPE_8 + i, drvbox); + } + + g_map_slash = make_checkbox(BRect(10, 94, 300, 110), "Map '/'<->'\\' in filenames", MSG_MAP_SLASH, drvbox); + g_emul_1541_proc = make_checkbox(BRect(10, 109, 300, 125), "Enable 1541 processor emulation", MSG_EMUL_1541_PROC, drvbox); + + // "OK" button + top->AddChild(g_ok = new BButton(BRect(20, 315, 90, 340), "", startup ? "Start" : "OK", new BMessage(MSG_OK))); + SetDefaultButton(g_ok); + + // "Cancel" button + top->AddChild(g_cancel = new BButton(BRect(b.right-90, 315, b.right-20, 340), "", startup ? "Quit" : "Cancel", new BMessage(MSG_CANCEL))); + + // Set the values of all controls to reflect the preferences + set_values(); + g_normal_cycles->MakeFocus(); + + // Create file panels + open_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_OPEN_PANEL_RETURNED)); + open_panel->Window()->SetTitle("Frodo: Open preferences"); + save_panel = new BFilePanel(B_SAVE_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_SAVE_PANEL_RETURNED)); + save_panel->Window()->SetTitle("Frodo: Save preferences"); + file_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_DRIVE_PANEL_RETURNED)); + file_panel->SetPanelDirectory(&AppDirectory); + dir_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, B_DIRECTORY_NODE, false, new BMessage(MSG_DRIVE_PANEL_RETURNED)); + dir_panel->SetPanelDirectory(&AppDirectory); + dir_panel->Window()->SetTitle("Frodo: Select directory"); + dir_panel->SetButtonLabel(B_DEFAULT_BUTTON, "Select"); + + Unlock(); +} + + +/* + * Create checkbox + */ + +BCheckBox *PrefsWindow::make_checkbox(BRect frame, char *label, uint32 what, BView *parent) +{ + BCheckBox *checkbox = new BCheckBox(frame, "", label, new BMessage(what)); + parent->AddChild(checkbox); + return checkbox; +} + + +/* + * Create number entry field + */ + +NumberControl *PrefsWindow::make_number_entry(BRect frame, char *label_text, BView *parent) +{ + NumberControl *num = new NumberControl(frame, frame.right-frame.left-55, "", label_text, NULL, NULL); + parent->AddChild(num); + + num->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_RIGHT); + num->SetFont(be_plain_font); + num->ChildAt(0)->SetFont(be_plain_font); + + return num; +} + + +/* + * Create drive type popup menu + */ + +BPopUpMenu *PrefsWindow::make_drvtype_popup(BRect frame, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("drive_type popup", true, true); + popup->AddItem(new BMenuItem("Dir", new BMessage(what))); + popup->AddItem(new BMenuItem("D64", new BMessage(what))); + popup->AddItem(new BMenuItem("T64", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "drive_type", NULL, popup); + menu_field->SetDivider(0); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Create path entry field + */ + +PathControl *PrefsWindow::make_path_entry(BRect frame, char *label, BView *parent) +{ + PathControl *path = new PathControl(frame, 16, "", label, NULL, NULL); + parent->AddChild(path); + + path->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); + path->SetFont(be_plain_font); + path->ChildAt(0)->SetFont(be_plain_font); + ((BTextView *)(path->ChildAt(0)))->SetMaxBytes(255); + + return path; +} + + +/* + * Create display type popup + */ + +BPopUpMenu *PrefsWindow::make_disptype_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("display_type popup", true, true); + popup->AddItem(new BMenuItem("Window", new BMessage(what))); + popup->AddItem(new BMenuItem("Screen", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "display_type", label_text, popup); + menu_field->SetDivider(frame.Width()-55); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Create SID type popup + */ + +BPopUpMenu *PrefsWindow::make_sidtype_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("sid_type popup", true, true); + popup->AddItem(new BMenuItem("None", new BMessage(what))); + popup->AddItem(new BMenuItem("Digital", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "sid_type", label_text, popup); + menu_field->SetDivider(frame.Width()-55); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Create REU size popup + */ + +BPopUpMenu *PrefsWindow::make_reusize_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("reu_size popup", true, true); + popup->AddItem(new BMenuItem("None", new BMessage(what))); + popup->AddItem(new BMenuItem("128K", new BMessage(what))); + popup->AddItem(new BMenuItem("256K", new BMessage(what))); + popup->AddItem(new BMenuItem("512K", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "reu_size", label_text, popup); + menu_field->SetDivider(frame.Width()-55); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Set the values of the controls + */ + +void PrefsWindow::set_values(void) +{ + prefs->Check(); + + g_normal_cycles->SetValue(prefs->NormalCycles); + g_bad_line_cycles->SetValue(prefs->BadLineCycles); + g_cia_cycles->SetValue(prefs->CIACycles); + g_floppy_cycles->SetValue(prefs->FloppyCycles); + g_skip_frames->SetValue(prefs->SkipFrames); + + for (int i=0; i<4; i++) { + g_drive_type[i]->ItemAt(prefs->DriveType[i])->SetMarked(true); + g_drive_path[i]->SetText(prefs->DrivePath[i]); + } + + g_sid_type->ItemAt(prefs->SIDType)->SetMarked(true); + g_reu_size->ItemAt(prefs->REUSize)->SetMarked(true); + g_display_type->ItemAt(prefs->DisplayType)->SetMarked(true); + + g_sprites_on->SetValue(prefs->SpritesOn ? B_CONTROL_ON : B_CONTROL_OFF); + g_sprite_collisions->SetValue(prefs->SpriteCollisions ? B_CONTROL_ON : B_CONTROL_OFF); + g_joystick_1_on->SetValue(prefs->Joystick1On ? B_CONTROL_ON : B_CONTROL_OFF); + g_joystick_2_on->SetValue(prefs->Joystick2On ? B_CONTROL_ON : B_CONTROL_OFF); + g_joystick_swap->SetValue(prefs->JoystickSwap ? B_CONTROL_ON : B_CONTROL_OFF); + g_limit_speed->SetValue(prefs->LimitSpeed ? B_CONTROL_ON : B_CONTROL_OFF); + g_fast_reset->SetValue(prefs->FastReset ? B_CONTROL_ON : B_CONTROL_OFF); + g_cia_irq_hack->SetValue(prefs->CIAIRQHack ? B_CONTROL_ON : B_CONTROL_OFF); + g_sid_filters->SetValue(prefs->SIDFilters ? B_CONTROL_ON : B_CONTROL_OFF); + g_double_scan->SetValue(prefs->DoubleScan ? B_CONTROL_ON : B_CONTROL_OFF); + + g_map_slash->SetValue(prefs->MapSlash ? B_CONTROL_ON : B_CONTROL_OFF); + g_emul_1541_proc->SetValue(prefs->Emul1541Proc ? B_CONTROL_ON : B_CONTROL_OFF); + + ghost_controls(); +} + + +/* + * Get the values of the controls + */ + +void PrefsWindow::get_values(void) +{ + prefs->NormalCycles = g_normal_cycles->Value(); + prefs->BadLineCycles = g_bad_line_cycles->Value(); + prefs->CIACycles = g_cia_cycles->Value(); + prefs->FloppyCycles = g_floppy_cycles->Value(); + prefs->SkipFrames = g_skip_frames->Value(); + + for (int i=0; i<4; i++) + strcpy(prefs->DrivePath[i], g_drive_path[i]->Text()); + + prefs->Check(); +} + + +/* + * Enable/disable certain controls + */ + +void PrefsWindow::ghost_controls(void) +{ + g_normal_cycles->SetEnabled(!IsFrodoSC); + g_bad_line_cycles->SetEnabled(!IsFrodoSC); + g_cia_cycles->SetEnabled(!IsFrodoSC); + g_floppy_cycles->SetEnabled(!IsFrodoSC); + g_cia_irq_hack->SetEnabled(!IsFrodoSC); + g_double_scan->SetEnabled(prefs->DisplayType == DISPTYPE_SCREEN); +} + + +/* + * Message from controls/menus received + */ + +void PrefsWindow::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case MSG_OK: + get_values(); + if (startup) + be_app->PostMessage(MSG_STARTUP); + else { + BMessage msg(MSG_PREFS_DONE); + msg.AddBool("canceled", false); + msg.AddPointer("prefs", prefs); + be_app->PostMessage(&msg); + } + PostMessage(B_QUIT_REQUESTED); + break; + + case MSG_CANCEL: + if (startup) + be_app->PostMessage(B_QUIT_REQUESTED); + else { + BMessage msg(MSG_PREFS_DONE); + msg.AddBool("canceled", true); + msg.AddPointer("prefs", prefs); + be_app->PostMessage(&msg); + } + PostMessage(B_QUIT_REQUESTED); + break; + + case MSG_SPRITES_ON: + prefs->SpritesOn = !prefs->SpritesOn; + break; + + case MSG_SPRITE_COLLISIONS: + prefs->SpriteCollisions = !prefs->SpriteCollisions; + break; + + case MSG_JOYSTICK_1_ON: + prefs->Joystick1On = !prefs->Joystick1On; + break; + + case MSG_JOYSTICK_2_ON: + prefs->Joystick2On = !prefs->Joystick2On; + break; + + case MSG_JOYSTICK_SWAP: + prefs->JoystickSwap = !prefs->JoystickSwap; + break; + + case MSG_LIMIT_SPEED: + prefs->LimitSpeed = !prefs->LimitSpeed; + break; + + case MSG_FAST_RESET: + prefs->FastReset = !prefs->FastReset; + break; + + case MSG_CIA_IRQ_HACK: + prefs->CIAIRQHack = !prefs->CIAIRQHack; + break; + + case MSG_SID_FILTERS: + prefs->SIDFilters = !prefs->SIDFilters; + break; + + case MSG_DOUBLE_SCAN: + prefs->DoubleScan = !prefs->DoubleScan; + break; + + case MSG_SID_TYPE: + prefs->SIDType = msg->FindInt32("index"); + break; + + case MSG_REU_SIZE: + prefs->REUSize = msg->FindInt32("index"); + break; + + case MSG_DISPLAY_TYPE: + prefs->DisplayType = msg->FindInt32("index"); + g_double_scan->SetEnabled(prefs->DisplayType == DISPTYPE_SCREEN); + break; + + case MSG_MAP_SLASH: + prefs->MapSlash = !prefs->MapSlash; + break; + + case MSG_EMUL_1541_PROC: + prefs->Emul1541Proc = !prefs->Emul1541Proc; + break; + + case MSG_DRVTYPE_8: + case MSG_DRVTYPE_9: + case MSG_DRVTYPE_10: + case MSG_DRVTYPE_11: + prefs->DriveType[msg->what & 3] = msg->FindInt32("index"); + break; + + case MSG_GETDRIVE_8: + case MSG_GETDRIVE_9: + case MSG_GETDRIVE_10: + case MSG_GETDRIVE_11: + panel_drive_num = msg->what & 3; + file_panel->Hide(); + dir_panel->Hide(); + if (prefs->DriveType[panel_drive_num] == DRVTYPE_D64) { + file_panel->Window()->SetTitle("Frodo: Select disk image file"); + file_panel->Show(); + } else if (prefs->DriveType[panel_drive_num] == DRVTYPE_T64) { + file_panel->Window()->SetTitle("Frodo: Select archive file"); + file_panel->Show(); + } else + dir_panel->Show(); + break; + + case MSG_DRIVE_PANEL_RETURNED: { // Drive path file panel returned + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs->DrivePath[panel_drive_num], the_path.Path(), 255); + prefs->DrivePath[panel_drive_num][255] = 0; + set_values(); + } + break; + } + + case MSG_OPEN: + open_panel->Show(); + break; + + case MSG_OPEN_PANEL_RETURNED: { // Open file panel returned + get_values(); // Useful if Load() is unsuccessful + + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) + if (the_entry.IsFile()) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs_path, the_path.Path(), 1023); + prefs_path[1023] = 0; + prefs->Load(prefs_path); + set_values(); + } + } + + case MSG_SAVE: + get_values(); + prefs->Save(prefs_path); + break; + + case MSG_SAVE_AS: + save_panel->Show(); + break; + + case MSG_SAVE_PANEL_RETURNED: { // Save file panel returned + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("directory", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs_path, the_path.Path(), 1023); + strncat(prefs_path, "/", 1023); + strncat(prefs_path, msg->FindString("name"), 1023); + prefs_path[1023] = 0; + get_values(); + if (!prefs->Save(prefs_path)) + ShowRequester("Couldn't save preferences.", "Too bad"); + } + break; + } + + case MSG_REVERT: + get_values(); // Useful if Load() is unsuccessful + prefs->Load(prefs_path); + set_values(); + break; + + default: + BWindow::MessageReceived(msg); + } +} + + +/* + * Intercept ESC key (works as clicking the Cancel button) + */ + +bool PrefsWindow::FilterKeyDown(uint32 *aChar, BView **target) +{ + if (*aChar == B_ESCAPE) { + // Flash Cancel button + g_cancel->SetValue(B_CONTROL_ON); + snooze(100000.0); + PostMessage(MSG_CANCEL); + } + return true; +} + + +/* + * Quit requested + */ + +bool PrefsWindow::QuitRequested(void) +{ + delete open_panel; + delete save_panel; + delete file_panel; + delete dir_panel; + return true; +} diff --git a/Src/Prefs_WIN32.i b/Src/Prefs_WIN32.i new file mode 100644 index 0000000..c2b5552 --- /dev/null +++ b/Src/Prefs_WIN32.i @@ -0,0 +1,390 @@ +/* + * Prefs_WIN32.i - Global preferences, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * WIN32 code by J. Richard Sladkey + */ + +#include +#include "resource.h" + +Prefs *Prefs::edit_prefs; +char *Prefs::edit_prefs_name; +HWND Prefs::hDlg; + +#define STANDARD_PAGE 0 +#define WIN32_PAGE 1 + +bool Prefs::ShowEditor(bool /* startup */, char *prefs_name) +{ + edit_prefs = this; + edit_prefs_name = prefs_name; + + PROPSHEETPAGE psp[2]; + + // Set up standard preferences property page. + psp[0].dwSize = sizeof(PROPSHEETPAGE); + psp[0].dwFlags = PSP_HASHELP; + psp[0].hInstance = hInstance; + psp[0].pszTemplate = MAKEINTRESOURCE(IDD_PREFERENCES_STANDARD); + psp[0].pszIcon = NULL; + psp[0].pfnDlgProc = StandardDialogProc; + psp[0].pszTitle = NULL; + psp[0].lParam = 0; + psp[0].pfnCallback = NULL; + + // Set up WIN32 preferences property page. + psp[1].dwSize = sizeof(PROPSHEETPAGE); + psp[1].dwFlags = PSP_HASHELP; + psp[1].hInstance = hInstance; + psp[1].pszTemplate = MAKEINTRESOURCE(IDD_PREFERENCES_WIN32); + psp[1].pszIcon = NULL; + psp[1].pfnDlgProc = WIN32DialogProc; + psp[1].pszTitle = NULL; + psp[1].lParam = 0; + psp[1].pfnCallback = NULL; + + // Setup property sheet. + PROPSHEETHEADER psh; + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW; + psh.hwndParent = hwnd; + psh.hInstance = hInstance; + psh.pszIcon = NULL; + psh.pszCaption = "Preferences"; + psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); + psh.nStartPage = 0; + psh.ppsp = psp; + psh.pfnCallback = NULL; + + int result = PropertySheet(&psh); + + if (result == -1) + return FALSE; + return result; +} + +BOOL CALLBACK Prefs::StandardDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + return edit_prefs->DialogProc(STANDARD_PAGE, hDlg, message, wParam, lParam); +} + +BOOL CALLBACK Prefs::WIN32DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + return edit_prefs->DialogProc(WIN32_PAGE, hDlg, message, wParam, lParam); +} + +BOOL Prefs::DialogProc(int page, HWND hDlg_arg, UINT message, WPARAM wParam, LPARAM lParam) +{ + hDlg = hDlg_arg; + + switch (message) { + + case WM_INITDIALOG: + SetupControls(page); + SetValues(page); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + + case IDC_BROWSE8: + BrowseForDevice(IDC_DEVICE8); + break; + + case IDC_BROWSE9: + BrowseForDevice(IDC_DEVICE9); + break; + + case IDC_BROWSE10: + BrowseForDevice(IDC_DEVICE10); + break; + + case IDC_BROWSE11: + BrowseForDevice(IDC_DEVICE11); + break; + + } + return TRUE; + + case WM_NOTIFY: + { + NMHDR *pnmhdr = (NMHDR *) lParam; + switch (pnmhdr->code) { + + case PSN_KILLACTIVE: + SetWindowLong(hDlg, DWL_MSGRESULT, FALSE); + break; + + case PSN_APPLY: + GetValues(page); + break; + + case PSN_HELP: + PostMessage(hwnd, WM_COMMAND, ID_HELP_SETTINGS, 0); + break; + } + } + return TRUE; + } + + return FALSE; +} + +#define SetupSpin(id, upper, lower) SendMessage(GetDlgItem(hDlg, id), UDM_SETRANGE, 0, MAKELONG(upper, lower)) +#define SetupSpinIncrement(id, increment) (udaccel.nSec = 0, udaccel.nInc = increment, SendMessage(GetDlgItem(hDlg, id), UDM_SETACCEL, 1, (LPARAM) &udaccel)) +#define SetupComboClear(id) SendMessage(GetDlgItem(hDlg, id), CB_RESETCONTENT, 0, 0) +#define SetupComboAdd(id, string) SendMessage(GetDlgItem(hDlg, id), CB_ADDSTRING, 0, (LPARAM) string) + +void Prefs::SetupControls(int page) +{ + UDACCEL udaccel; + switch (page) { + + case STANDARD_PAGE: + SetupSpin(IDC_NORMAL_SPIN, 200, 1); + SetupSpin(IDC_BADLINES_SPIN, 200, 1); + SetupSpin(IDC_CIA_SPIN, 200, 1); + SetupSpin(IDC_FLOPPY_SPIN, 200, 1); + SetupSpin(IDC_DRAWEVERY_SPIN, 10, 1); + SetupComboClear(IDC_REUSIZE); + SetupComboAdd(IDC_REUSIZE, "None"); + SetupComboAdd(IDC_REUSIZE, "128k"); + SetupComboAdd(IDC_REUSIZE, "256k"); + SetupComboAdd(IDC_REUSIZE, "512k"); + break; + + case WIN32_PAGE: + SetupComboClear(IDC_VIEWPORT); + SetupComboAdd(IDC_VIEWPORT, "Default"); + SetupComboAdd(IDC_VIEWPORT, "320x200"); + SetupComboAdd(IDC_VIEWPORT, "336x216"); + SetupComboAdd(IDC_VIEWPORT, "384x272"); + SetupComboClear(IDC_DISPLAYMODE); + SetupComboAdd(IDC_DISPLAYMODE, "Default"); + { + C64Display *TheDisplay = TheApp->TheC64->TheDisplay; + int n = TheDisplay->GetNumDisplayModes(); + const C64Display::DisplayMode *modes = + TheDisplay->GetDisplayModes(); + for (int i = 0; i < n; i++) { + char mode[64]; + sprintf(mode, "%dx%dx%d%s", + modes[i].x, modes[i].y, modes[i].depth, + modes[i].modex ? " (ModeX)" : ""); + SetupComboAdd(IDC_DISPLAYMODE, mode); + } + } + SetupSpin(IDC_SCALINGNUMERATOR_SPIN, 16, 1); + SetupSpin(IDC_SCALINGDENOMINATOR_SPIN, 4, 1); + SetupSpin(IDC_LATENCYMIN_SPIN, 1000, 20); + SetupSpinIncrement(IDC_LATENCYMIN_SPIN, 20); + SetupSpin(IDC_LATENCYMAX_SPIN, 1000, 20); + SetupSpinIncrement(IDC_LATENCYMAX_SPIN, 20); + SetupSpin(IDC_LATENCYAVG_SPIN, 1000, 20); + SetupSpinIncrement(IDC_LATENCYAVG_SPIN, 20); + break; + } +} + +#define SetText(id, val) SetDlgItemText(hDlg, id, val) +#define SetInteger(id, val) SetDlgItemInt(hDlg, id, val, FALSE) +#define SetCheckBox(id, val) CheckDlgButton(hDlg, id, (val) ? BST_CHECKED : BST_UNCHECKED) +#define SetCombo(id, val) SendMessage(GetDlgItem(hDlg, id), CB_SELECTSTRING, 0, (LPARAM) val) + +void Prefs::SetValues(int page) +{ + const char *str; + switch (page) { + + case STANDARD_PAGE: + SetText(IDC_DEVICE8, DrivePath[0]); + SetText(IDC_DEVICE9, DrivePath[1]); + SetText(IDC_DEVICE10, DrivePath[2]); + SetText(IDC_DEVICE11, DrivePath[3]); + + SetInteger(IDC_NORMAL, NormalCycles); + SetInteger(IDC_BADLINES, BadLineCycles); + SetInteger(IDC_CIA, CIACycles); + SetInteger(IDC_FLOPPY, FloppyCycles); + SetInteger(IDC_DRAWEVERY, SkipFrames); + switch (REUSize) { + case REU_NONE: str = "None"; break; + case REU_128K: str = "128k"; break; + case REU_256K: str = "256k"; break; + case REU_512K: str = "512k"; break; + } + SetCombo(IDC_REUSIZE, str); + + SetCheckBox(IDC_LIMITSPEED, LimitSpeed); + SetCheckBox(IDC_SPRITES, SpritesOn); + SetCheckBox(IDC_SPRITECOLLISIONS, SpriteCollisions); + SetCheckBox(IDC_JOYSTICK1, Joystick1On); + SetCheckBox(IDC_JOYSTICK2, Joystick2On); + SetCheckBox(IDC_SWAPJOYSTICKS, JoystickSwap); + SetCheckBox(IDC_FASTRESET, FastReset); + SetCheckBox(IDC_CIAIRQHACK, CIAIRQHack); + SetCheckBox(IDC_MAPSLASH, MapSlash); + SetCheckBox(IDC_SIDEMULATION, SIDType == SIDTYPE_DIGITAL); + SetCheckBox(IDC_SIDFILTERS, SIDFilters); + SetCheckBox(IDC_1541EMULATION, Emul1541Proc); + break; + + case WIN32_PAGE: + SetCheckBox(IDC_FULLSCREEN, DisplayType == DISPTYPE_SCREEN); + SetCheckBox(IDC_SYSTEMMEMORY, SystemMemory); + SetCheckBox(IDC_ALWAYSCOPY, AlwaysCopy); + SetText(IDC_VIEWPORT, ViewPort); + SetText(IDC_DISPLAYMODE, DisplayMode); + + SetCheckBox(IDC_HIDECURSOR, HideCursor); + SetCheckBox(IDC_SYSTEMKEYS, SystemKeys); + SetInteger(IDC_SCALINGNUMERATOR, ScalingNumerator); + SetInteger(IDC_SCALINGDENOMINATOR, ScalingDenominator); + + SetCheckBox(IDC_DIRECTSOUND, DirectSound); + SetCheckBox(IDC_EXCLUSIVESOUND, ExclusiveSound); + SetInteger(IDC_LATENCYMIN, LatencyMin); + SetInteger(IDC_LATENCYMAX, LatencyMax); + SetInteger(IDC_LATENCYAVG, LatencyAvg); + + SetCheckBox(IDC_AUTOPAUSE, AutoPause); + SetCheckBox(IDC_PREFSATSTARTUP, PrefsAtStartup); + SetCheckBox(IDC_SHOWLEDS, ShowLEDs); + break; + } +} + +#define GetCheckBox(id, val) (val = IsDlgButtonChecked(hDlg, id) == BST_CHECKED) +#define GetInteger(id, val) (val = GetDlgItemInt(hDlg, id, NULL, FALSE)) +#define GetText(id, val) GetDlgItemText(hDlg, id, val, sizeof(val)) + +void Prefs::GetValues(int page) +{ + BOOL temp; + char str[256]; + switch (page) { + + case STANDARD_PAGE: + GetText(IDC_DEVICE8, DrivePath[0]); + GetText(IDC_DEVICE9, DrivePath[1]); + GetText(IDC_DEVICE10, DrivePath[2]); + GetText(IDC_DEVICE11, DrivePath[3]); + + GetInteger(IDC_NORMAL, NormalCycles); + GetInteger(IDC_BADLINES, BadLineCycles); + GetInteger(IDC_CIA, CIACycles); + GetInteger(IDC_FLOPPY, FloppyCycles); + GetInteger(IDC_DRAWEVERY, SkipFrames); + GetText(IDC_REUSIZE, str); + if (strcmp(str, "None") == 0) + REUSize = REU_NONE; + else if (strcmp(str, "128k") == 0) + REUSize = REU_128K; + else if (strcmp(str, "256k") == 0) + REUSize = REU_256K; + else if (strcmp(str, "512k") == 0) + REUSize = REU_512K; + + GetCheckBox(IDC_LIMITSPEED, LimitSpeed); + GetCheckBox(IDC_SPRITES, SpritesOn); + GetCheckBox(IDC_SPRITECOLLISIONS, SpriteCollisions); + GetCheckBox(IDC_JOYSTICK1, Joystick1On); + GetCheckBox(IDC_JOYSTICK2, Joystick2On); + GetCheckBox(IDC_SWAPJOYSTICKS, JoystickSwap); + GetCheckBox(IDC_FASTRESET, FastReset); + GetCheckBox(IDC_CIAIRQHACK, CIAIRQHack); + GetCheckBox(IDC_MAPSLASH, MapSlash); + GetCheckBox(IDC_SIDEMULATION, temp); + SIDType = temp ? SIDTYPE_DIGITAL : SIDTYPE_NONE; + GetCheckBox(IDC_SIDFILTERS, SIDFilters); + GetCheckBox(IDC_1541EMULATION, Emul1541Proc); + break; + + case WIN32_PAGE: + GetCheckBox(IDC_FULLSCREEN, temp); + DisplayType = temp ? DISPTYPE_SCREEN : DISPTYPE_WINDOW; + GetCheckBox(IDC_SYSTEMMEMORY, SystemMemory); + GetCheckBox(IDC_ALWAYSCOPY, AlwaysCopy); + GetText(IDC_VIEWPORT, ViewPort); + GetText(IDC_DISPLAYMODE, DisplayMode); + + GetCheckBox(IDC_HIDECURSOR, HideCursor); + GetCheckBox(IDC_SYSTEMKEYS, SystemKeys); + GetInteger(IDC_SCALINGNUMERATOR, ScalingNumerator); + GetInteger(IDC_SCALINGDENOMINATOR, ScalingDenominator); + + GetCheckBox(IDC_DIRECTSOUND, DirectSound); + GetCheckBox(IDC_EXCLUSIVESOUND, ExclusiveSound); + GetInteger(IDC_LATENCYMIN, LatencyMin); + GetInteger(IDC_LATENCYMAX, LatencyMax); + GetInteger(IDC_LATENCYAVG, LatencyAvg); + + GetCheckBox(IDC_AUTOPAUSE, AutoPause); + GetCheckBox(IDC_PREFSATSTARTUP, PrefsAtStartup); + GetCheckBox(IDC_SHOWLEDS, ShowLEDs); + break; + } + + for (int i = 0; i < 4; i++) { + DriveType[i] = DRVTYPE_DIR; + int length = strlen(DrivePath[i]); + if (length >= 4) { + char *suffix = DrivePath[i] + length - 4; + if (stricmp(suffix, ".t64") == 0) + DriveType[i] = DRVTYPE_T64; + else if (stricmp(suffix, ".d64") == 0) + DriveType[i] = DRVTYPE_D64; + } + } +} + +void Prefs::BrowseForDevice(int id) +{ + char filename[256]; + GetDlgItemText(hDlg, id, filename, sizeof(filename)); + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hDlg; + ofn.hInstance = hInstance; + ofn.lpstrFilter = + "All Files (*.*)\0*.*\0" + "All C64 Files (*.d64;*.t64)\0*.d64;*.t64\0" + "D64 Files (*.d64)\0*.d64\0" + "T64 Files (*.t64)\0*.t64\0" + ; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "Set Device"; + ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE | + OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_SHAREAWARE; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = NULL; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + BOOL result = GetOpenFileName(&ofn); + if (result) { + const char *ext = filename + ofn.nFileExtension; + if (stricmp(ext, "d64") != 0 && stricmp(ext, "t64") != 0) + filename[ofn.nFileOffset - 1] = '\0'; + char cwd[256]; + GetCurrentDirectory(sizeof(cwd), cwd); + int cwd_len = strlen(cwd); + if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') { + strcat(cwd, "\\"); + cwd_len++; + } + if (strnicmp(filename, cwd, cwd_len) == 0) + SetDlgItemText(hDlg, id, filename + cwd_len); + else + SetDlgItemText(hDlg, id, filename); + } +} diff --git a/Src/REU.cpp b/Src/REU.cpp new file mode 100644 index 0000000..16c1d6a --- /dev/null +++ b/Src/REU.cpp @@ -0,0 +1,262 @@ +/* + * REU.cpp - 17xx REU emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Incompatibilities: + * ------------------ + * + * - REU interrupts are not emulated + * - Transfer time is not accounted for, all transfers + * are done in 0 cycles + */ + +#include "sysdeps.h" + +#include "REU.h" +#include "CPUC64.h" +#include "Prefs.h" + + +/* + * Constructor + */ + +REU::REU(MOS6510 *CPU) : the_cpu(CPU) +{ + int i; + + // Init registers + regs[0] = 0x40; + for (i=1; i<11; i++) + regs[i] = 0; + for (i=11; i<16; i++) + regs[i] = 0xff; + + ex_ram = NULL; + ram_size = ram_mask = 0; + + // Allocate RAM + open_close_reu(REU_NONE, ThePrefs.REUSize); +} + + +/* + * Destructor + */ + +REU::~REU() +{ + // Free RAM + open_close_reu(ThePrefs.REUSize, REU_NONE); +} + + +/* + * Prefs may have changed, reallocate expansion RAM + */ + +void REU::NewPrefs(Prefs *prefs) +{ + open_close_reu(ThePrefs.REUSize, prefs->REUSize); +} + + +/* + * Allocate/free expansion RAM + */ + +void REU::open_close_reu(int old_size, int new_size) +{ + if (old_size == new_size) + return; + + // Free old RAM + if (old_size != REU_NONE) { + delete[] ex_ram; + ex_ram = NULL; + } + + // Allocate new RAM + if (new_size != REU_NONE) { + switch (new_size) { + case REU_128K: + ram_size = 0x20000; + break; + case REU_256K: + ram_size = 0x40000; + break; + case REU_512K: + ram_size = 0x80000; + break; + } + ram_mask = ram_size - 1; + ex_ram = new uint8[ram_size]; + + // Set size bit in status register + if (ram_size > 0x20000) + regs[0] |= 0x10; + else + regs[0] &= 0xef; + } +} + + +/* + * Reset the REU + */ + +void REU::Reset(void) +{ + int i; + + for (i=1; i<11; i++) + regs[i] = 0; + for (i=11; i<16; i++) + regs[i] = 0xff; + + if (ram_size > 0x20000) + regs[0] = 0x50; + else + regs[0] = 0x40; +} + + +/* + * Read from REU register + */ + +uint8 REU::ReadRegister(uint16 adr) +{ + if (ex_ram == NULL) + return rand(); + + switch (adr) { + case 0:{ + uint8 ret = regs[0]; + regs[0] &= 0x1f; + return ret; + } + case 6: + return regs[6] | 0xf8; + case 9: + return regs[9] | 0x1f; + case 10: + return regs[10] | 0x3f; + default: + return regs[adr]; + } +} + + +/* + * Write to REU register + */ + +void REU::WriteRegister(uint16 adr, uint8 byte) +{ + if (ex_ram == NULL) + return; + + switch (adr) { + case 0: // Status register is read-only + case 11: // Unconnected registers + case 12: + case 13: + case 14: + case 15: + break; + case 1: // Command register + regs[1] = byte; + if ((byte & 0x90) == 0x90) + execute_dma(); + break; + default: + regs[adr] = byte; + break; + } +} + + +/* + * CPU triggered REU by writing to $ff00 + */ + +void REU::FF00Trigger(void) +{ + if (ex_ram == NULL) + return; + + if ((regs[1] & 0x90) == 0x80) + execute_dma(); +} + + +/* + * Execute REU DMA transfer + */ + +void REU::execute_dma(void) +{ + // Get C64 and REU transfer base addresses + uint16 c64_adr = regs[2] | (regs[3] << 8); + uint32 reu_adr = regs[4] | (regs[5] << 8) | (regs[6] << 16); + + // Calculate transfer length + int length = regs[7] | (regs[8] << 8); + if (!length) + length = 0x10000; + + // Calculate address increments + uint32 c64_inc = (regs[10] & 0x80) ? 0 : 1; + uint32 reu_inc = (regs[10] & 0x40) ? 0 : 1; + + // Do transfer + switch (regs[1] & 3) { + + case 0: // C64 -> REU + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) + ex_ram[reu_adr & ram_mask] = the_cpu->REUReadByte(c64_adr); + break; + + case 1: // C64 <- REU + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) + the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]); + break; + + case 2: // C64 <-> REU + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) { + uint8 tmp = the_cpu->REUReadByte(c64_adr); + the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]); + ex_ram[reu_adr & ram_mask] = tmp; + } + break; + + case 3: // Compare + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) + if (ex_ram[reu_adr & ram_mask] != the_cpu->REUReadByte(c64_adr)) { + regs[0] |= 0x20; + break; + } + break; + } + + // Update address and length registers if autoload is off + if (!(regs[1] & 0x20)) { + regs[2] = c64_adr; + regs[3] = c64_adr >> 8; + regs[4] = reu_adr; + regs[5] = reu_adr >> 8; + regs[6] = reu_adr >> 16; + regs[7] = length + 1; + regs[8] = (length + 1) >> 8; + } + + // Set complete bit in status register + regs[0] |= 0x40; + + // Clear execute bit in command register + regs[1] &= 0x7f; +} diff --git a/Src/REU.h b/Src/REU.h new file mode 100644 index 0000000..941734d --- /dev/null +++ b/Src/REU.h @@ -0,0 +1,39 @@ +/* + * REU.h - 17xx REU emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _REU_H +#define _REU_H + + +class MOS6510; +class Prefs; + +class REU { +public: + REU(MOS6510 *CPU); + ~REU(); + + void NewPrefs(Prefs *prefs); + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + void FF00Trigger(void); + +private: + void open_close_reu(int old_size, int new_size); + void execute_dma(void); + + MOS6510 *the_cpu; // Pointer to 6510 + + uint8 *ex_ram; // REU expansion RAM + + uint32 ram_size; // Size of expansion RAM + uint32 ram_mask; // Expansion RAM address bit mask + + uint8 regs[16]; // REU registers +}; + +#endif diff --git a/Src/ROlib.h b/Src/ROlib.h new file mode 100644 index 0000000..8224865 --- /dev/null +++ b/Src/ROlib.h @@ -0,0 +1,520 @@ +/* + * ROlib.h + * + * Defines Classes, variables and OS interface calls for Acorn RISC OS computers. + * (C) 1997 Andreas Dehmel + * + */ + + + +#ifndef RO_CUSTOM_LIB +#define RO_CUSTOM_LIB + +#include + + +#define TASKNAME "Frodo" +#define TASK_WORD 0x4b534154 + +/* Scrap-file for 1541fs directory function */ +#define RO_TEMPFILE ".FrodoDIR" + + + +/* Icon Flags */ +#define IFlg_Text 1 +#define IFlg_Sprite 2 +#define IFlg_Border 4 +#define IFlg_HCenter 8 +#define IFlg_VCenter 16 +#define IFlg_Filled 32 +#define IFlg_AntiA 64 +#define IFlg_AutoRdrw 128 +#define IFlg_Indir 256 +#define IFlg_RAdjust 512 +#define IFlg_Slct (1<<21) +#define IFlg_Grey (1<<22) + +/* Menu Flags */ +#define MFlg_Tick 1 +#define MFlg_Dotted 2 +#define MFlg_Writable 4 +#define MFlg_Warning 8 +#define MFlg_LastItem 128 + +/* Joystick stuff */ +#define JoyDir_Thresh 32 +#define JoyButton1 (1<<16) +#define JoyButton2 (1<<17) + + + +/* Size of WIMP data types */ +#define RO_WINDOW_WORDS 23 +#define RO_WINDOW_BYTES 92 +#define RO_ICON_WORDS 8 +#define RO_ICON_BYTES 32 +#define RO_MHEAD_WORDS 7 +#define RO_MHEAD_BYTES 28 +#define RO_MITEM_WORDS 6 +#define RO_MITEM_BYTES 24 + + + + +/* Structures for directory scanning (mainly 1541fs) */ +/* May hold entire pathname, so DON'T REDUCE!!! */ +#define FILENAME_MAX_CHARS 256 /* must be >= NAMEBUF_LENGTH (256) */ + +typedef struct { + unsigned int load, exec, length, attrib, otype; + char name[FILENAME_MAX_CHARS]; +} dir_full_info; + +typedef struct { + int readno, offset, buffsize; + char *match; +} dir_env; + + + +/* WIMP structures: */ +typedef struct { + int *tit, *val; + int len; +} WIdatI; + +/* data type for window / icon data */ +typedef union { + char strg[12]; + WIdatI ind; +} WIdata; + + +/* Window descriptor - 23 words = 92 bytes */ +typedef struct { + int Handle; + int vminx,vminy,vmaxx,vmaxy; + int scrollx,scrolly; + int stackpos; + unsigned int wflags; + char col_tfg, col_tbg, col_wfg, col_wbg; /* title/work_area fore/background colour */ + char col_sbo, col_sbi, col_tsl, reserved;/* scroll bar inner/outer, title if input focus */ + int wminx, wminy, wmaxx, wmaxy; + unsigned int tflags, waflags; + int SpriteAreaPtr; + short min_width, min_height; + WIdata dat; + int icon_no; +} RO_Window; + + +/* Icon descriptor */ +typedef struct { + int minx, miny, maxx, maxy; + int iflags; + WIdata dat; +} RO_Icon; + + +typedef struct { + int WindowHandle; + int minx, miny, maxx, maxy; + int iflags; + WIdata dat; +} RO_IconDesc; + + +typedef struct { + char title[12]; + char col_tfg, col_tbg, col_wfg, col_wbg; + int width, height, vgap; +} RO_MenuHead; + + +typedef struct { + unsigned int mflags; + RO_MenuHead *submenu; + unsigned int iflags; + WIdata dat; +} RO_MenuItem; + + +/* Caret descriptor */ +typedef struct { + int WHandle; + int IHandle; + int offx, offy; + int height, index; +} RO_Caret; + + + +/* Joystick key descriptor */ +typedef struct { + unsigned char up, down, left, right, fire; +} Joy_Keys; + + + + +/* Declare classes that are needed in the following new classes */ +class C64Display; +class C64; + + + +/* Very simple class to read the resolution and eigen values */ +class RORes +{ + public: + RORes(void); + ~RORes(void); + + int resx,resy,eigx,eigy; +}; + + +/* Handle current screenmode */ +class ROScreen +{ + private: + _kernel_oserror ModeError; + + public: + ROScreen(void); + ~ROScreen(void); + int ReadMode(void); + + int resx, resy, ldbpp, eigx, eigy, ladd; + char *scrbase; +}; + + +class Icon +{ + private: + RO_IconDesc icon; + + public: + Icon(int IconHandle, const RO_IconDesc *IDesc); + ~Icon(void); + void setstate(unsigned int eor, unsigned int clear); + void getstate(void); + + int IHandle; +}; + + +class Window +{ + private: + RO_Window *wind; + bool isopen; + + public: + Window(const int *WDesc, const char *Title); + ~Window(void); + int MyHandle(void); + void GetWorkArea(int *Dest); + void RedrawAWindow(int *Block, uint8 *Bitmap, C64Display *Disp); + RO_Window *Descriptor(void); + RO_Icon *GetIcon(unsigned int number); + void SetIconState(unsigned int number, unsigned int eor, unsigned int clear); + void GetIconState(unsigned int number, int *Block); + void WriteIconText(unsigned int number, const char *text); + void WriteIconTextU(unsigned int number, const char *text); // update instead of force redrw + void WriteIconNumber(unsigned int number, int value); + void WriteIconNumberU(unsigned int number, int value); + char *ReadIconText(unsigned int number); + int ReadIconNumber(unsigned int number); + void ForceIconRedraw(unsigned int number); + void UpdateIcon(unsigned int number); + void WriteTitle(const char *title); + char *ReadTitle(void); + void UpdateTitle(void); + bool HaveInput(void); + bool OpenStatus(void); + bool OpenStatus(int *Block); + void open(void); + void open(int *Block); + void close(void); + void forceredraw(int minx, int miny, int maxx, int maxy); + void update(uint8 *Bitmap, C64Display *Disp); + void update(int *Block, uint8 *Bitmap, C64Display *Disp); + void redraw(int *Block, uint8 *Bitmap, C64Display *Disp); + void extent(int minx, int miny, int maxx, int maxy); + void getstate(void); + void getstate(int *dest); // read window definition to external block +}; + + +class WIMP +{ + private: + _kernel_oserror WimpError; + int Block[64], AuxBlock[64]; // two WIMP blocks for convenience + int Mask; + int DragType, CMOS_DragType; + bool UseScrap; // Scrap file used (data transfer protocol!) + bool EmuPaused; + int UseNULL; // Number of clients that need NULL events + int RAMsize, RAMtransfered, RAMpartner; // for RAM transfer + int LastMenu, LastClick, LastDrag, MenuType, LastIcon, SaveType; + int *SpriteArea; + RO_Caret LastCaret; + int EmuZoom; + Joy_Keys NewJoyKeys[2]; + C64 *the_c64; + + public: + WIMP(C64 *my_c64); + ~WIMP(void); + + // On startup + bool LoadATemplate(char *Name, Window **Which); + + // To make window and pane work as a team + void OpenEmuWindow(void); + void OpenEmuWindow(int *Block); // open at pos + void CloseEmuWindow(void); + void UpdateEmuWindow(void); // update window and pane + void ThePrefsToWindow(void); // write ThePrefs into the window + void WindowToThePrefs(void); // update ThePrefs from window + void SysConfToWindow(void); + void WindowToSysConf(void); + void PollSysConfWindow(void); // low-level koyboard scan + void DragIconSprite(Window *host, unsigned int number); + int CalculateVolume(int *Block); + int CheckFilename(char *name); + void SnapshotSaved(bool OK); + void IssueSnapshotRequest(void); + void SetLEDIcons(bool FloppyEmulation); + void SetEmuWindowSize(void); + void ToggleEmuWindowSize(void); + int ReadEmuWindowSize(void); + void NewDriveImage(int DrNum, int *MsgBlock, bool SetNow); + void SetSpeedLimiter(bool LimitSpeed); + + // Standard WIMP functions + void Poll(bool Paused); + void Redraw(void); + void OpenWindow(void); + void CloseWindow(void); + void MouseClick(void); + void UserDrag(void); + void KeyPressed(void); + void MenuSelection(void); + void UserMessage(void); + void UserMessageAck(void); + + // WIMP's Windows and Icons + Icon *IBicon; + Window *EmuWindow, *EmuPane, *PrefsWindow, *ConfigWindow, *InfoWindow, *SoundWindow, *SaveBox; + char SnapFile[256], RAMFile[256]; +}; + + + +/* system-specific Variables */ + +extern RO_IconDesc IBarIcon; +extern unsigned int TaskHandle; +extern int WimpMessages[]; + + +/* Functions available via ROlib (not RISC OS Lib, mind you) + All SWIs are called in the X-Form! */ + +extern "C" +{ + +/* WIMP FUNCTIONS */ + +/* returns handle or 0 if error */ +extern int Wimp_Initialise(int Version, int tw, const char *TaskName, const int *Messages); + +extern _kernel_oserror *Wimp_CloseDown(int Handle, int tw); + +/* returns handle or 0 if error */ +extern int Wimp_CreateWindow(const int *Window); + +extern int Wimp_CreateIcon(int Priority, const RO_IconDesc *Icon); + +extern _kernel_oserror *Wimp_DeleteWindow(const int *Window); + +extern _kernel_oserror *Wimp_DeleteIcon(int *Block); + +extern _kernel_oserror *Wimp_OpenWindow(const int *Window); + +extern _kernel_oserror *Wimp_CloseWindow(const int *Window); + +extern int Wimp_Poll(unsigned int Mask, int *Block, int *PollWord); + +/* returns 0 if no more to do or error */ +extern int Wimp_RedrawWindow(int *Block); + +extern int Wimp_UpdateWindow(int *Block); + +extern int Wimp_GetRectangle(int *Block); + +extern _kernel_oserror *Wimp_GetWindowState(int *Block); + +extern _kernel_oserror *Wimp_GetWindowInfo(int *Block); + +extern _kernel_oserror *Wimp_SetIconState(int *Block); + +extern _kernel_oserror *Wimp_GetIconState(int *Block); + +extern _kernel_oserror *Wimp_GetPointerInfo(int *Block); + +extern _kernel_oserror *Wimp_DragBox(int *Block); + +extern _kernel_oserror *Wimp_ForceRedraw(int Handle, int minx, int miny, int maxx, int maxy); + +extern _kernel_oserror *Wimp_SetCaretPosition(int WHandle, int IHandle, int xoff, int yoff, int height, int index); + +extern _kernel_oserror *Wimp_GetCaretPosition(RO_Caret *Caret); + +extern _kernel_oserror *Wimp_CreateMenu(const int *Menu, int cx, int cy); + +extern _kernel_oserror *Wimp_SetExtent(int Handle, int *Block); + +extern _kernel_oserror *Wimp_OpenTemplate(char *Name); + +extern _kernel_oserror *Wimp_CloseTemplate(void); + +extern _kernel_oserror *Wimp_LoadTemplate(char **Template, char **Indirect, char *IndirLimit, char *Fonts, char *Name, int *Position); + +extern _kernel_oserror *Wimp_ProcessKey(int Key); + +extern int Wimp_StartTask(char *command); + +extern _kernel_oserror *Wimp_ReportError(const _kernel_oserror *Error, unsigned int Flags, const char *AppName); + +extern _kernel_oserror *Wimp_GetWindowOutline(int *Block); + +extern int Wimp_PollIdle(unsigned int Mask, int *Block, int MinTime, int *PollWord); + +extern _kernel_oserror *Wimp_PlotIcon(int *Block); + +extern _kernel_oserror *Wimp_SendMessage(int Event, int *Block, int THandle, int IHandle); + +extern _kernel_oserror *Wimp_CreateSubMenu(int *MenuBlock, int cx, int xy); + +extern _kernel_oserror *Wimp_SpriteOp(int, int, int, int, int, int, int, int); + +extern _kernel_oserror *Wimp_BaseOfSprites(int *ROM, int *RAM); + +extern _kernel_oserror *Wimp_CommandWindow(int Action); + +extern _kernel_oserror *Wimp_TransferBlock(int SHandle, char *SBuff, int DHandle, char *DBuff, int BuffSize); + +extern _kernel_oserror *Wimp_SpriteInfo(char *name, int *width, int *height, int *mode); + +extern _kernel_oserror *DragASprite_Start(unsigned int Flags, int SpriteArea, char *SpriteName, int *Box, int *BBox); + +extern _kernel_oserror *DragASprite_Stop(void); + +extern _kernel_oserror *ColourTrans_SelectTable(int SMode, int SPal, int DMode, int DPal, char **Buffer, unsigned int Flags, int *TransWork, int *TransFunc); + +extern _kernel_oserror *ColourTrans_SetFontColours(int Handle, int BPal, int FPal, int Offset); + +extern _kernel_oserror *ColourTrans_SetColour(int GCOL, unsigned int Flags, int Action); + +extern _kernel_oserror *ColourTrans_SetGCOL(int Palette, unsigned int Flags, int Action); + + + +/* OS FUNCTIONS */ + +extern int OS_ReadModeVariable(int mode, int var); + +extern int OS_ReadDynamicArea(int area); + +extern int ScanKeys(int keys); + +extern int ReadKeyboardStatus(void); + +extern int ReadDragType(void); + +extern int SetMousePointer(int NewShape); + + +/* Generic sprite op call */ +extern _kernel_oserror *OS_SpriteOp(int, int, int, int, int, int, int, int); + +extern _kernel_oserror *OS_Plot(int Command, int x, int y); + +extern _kernel_oserror *MouseBoundingBox(char Box[8]); + +extern int OS_ReadMonotonicTime(void); + +extern _kernel_oserror *OS_ReadC(char *Code); + +/* returns length of characters read; if length negative ==> terminated by escape */ +extern int OS_ReadLine(char *Buffer, int BuffSize, int minasc, int maxasc, int Echo); + +/* TRUE ==> escape */ +extern bool OS_ReadEscapeState(void); + +/* File related calls */ + +/* Returns object type */ +extern int ReadCatalogueInfo(char *Name, int Result[4]); + +/* Read the next name in the directory */ +extern _kernel_oserror *ReadDirName(const char *dirname, char *buffer, dir_env *env); + +/* Read the next entry (name, length, type,...) in the directory */ +extern _kernel_oserror *ReadDirNameInfo(const char *dirname,dir_full_info *buffer,dir_env *env); + +extern _kernel_oserror *DeleteFile(char *name); + +extern _kernel_oserror *OS_FlushBuffer(int BuffNum); + +/* These functions are more secure than using sprintf because they allow buffersize */ +/* Return value is a pointer to the terminating null */ +extern char *ConvertInteger1(int value, char *buffer, int buffsize); +extern char *ConvertInteger2(int value, char *buffer, int buffsize); +extern char *ConvertInteger3(int value, char *buffer, int buffsize); +extern char *ConvertInteger4(int value, char *buffer, int buffsize); + + +/* Misc */ + +extern unsigned int ModeColourNumber(unsigned int pal_entry); + +/* Returns -1 if error in joystick module, -2 if SWI unknown, joystate otherwise */ +extern int Joystick_Read(int joyno); + + +/* Sound stuff */ + +#define DRState_Active 1 +#define DRState_NeedData 2 +#define DRState_Overflow 4 + +/* Sound calls */ + +extern int Sound_Volume(int volume); + +/* Digital Renderer SWI calls */ +extern _kernel_oserror *DigitalRenderer_Activate(int Channels, int Length, int SamPeriod); + +extern _kernel_oserror *DigitalRenderer_Deactivate(void); + +extern _kernel_oserror *DigitalRenderer_Pause(void); + +extern _kernel_oserror *DigitalRenderer_Resume(void); + +extern _kernel_oserror *DigitalRenderer_GetTables(uint8 **LinToLog, uint8 **LogScale); + +extern int DigitalRenderer_ReadState(void); + +extern _kernel_oserror *DigitalRenderer_NewSample(uint8 *Sample); + +} + +#endif diff --git a/Src/ROlib.s b/Src/ROlib.s new file mode 100644 index 0000000..88b2029 --- /dev/null +++ b/Src/ROlib.s @@ -0,0 +1,1152 @@ +;* +;* ROlib.h +;* +;* Assembler interface to RISC OS +;* (C) 1997 Andreas Dehmel +;* + + +a1 rn 0 +a2 rn 1 +a3 rn 2 +a4 rn 3 +v1 rn 4 +v2 rn 5 +v3 rn 6 +v4 rn 7 +v5 rn 8 +v6 rn 9 +sl rn 10 +fp rn 11 +ip rn 12 +sp rn 13 +lr rn 14 +pc rn 15 + + + idfn (C) 1997 by Andreas Dehmel + +; ************ WIMP STUFF **************** + + AREA CODE, READONLY + align 4 + export |Wimp_Initialise| + = "Wimp_Initialise" + align 4 + +|Wimp_Initialise|: + swi 0x600c0 ;XWimp_Initialise + movvs a1,#0 ;return 0 if error + movvc a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CloseDown| + = "Wimp_CloseDown" + align 4 + +|Wimp_CloseDown| + swi 0x600dd ;XWimp_CloseDown + movvc a1,#0 ;return pointer to error block + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateWindow| + = "Wimp_CreateWindow" + align 4 + +|Wimp_CreateWindow|: + mov a2,a1 + swi 0x600c1 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateIcon| + = "Wimp_CreateIcon" + align 4 + +|Wimp_CreateIcon|: + swi 0x600c2 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_DeleteWindow| + = "Wimp_DeleteWindow" + align 4 + +|Wimp_DeleteWindow|: + mov a2,a1 + swi 0x600c3 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_DeleteIcon| + = "Wimp_DeleteIcon" + align 4 + +|Wimp_DeleteIcon|: + mov a2,a1 + swi 0x600c4 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_OpenWindow| + = "Wimp_OpenWindow" + align 4 + +|Wimp_OpenWindow|: + mov a2,a1 + swi 0x600c5 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CloseWindow| + = "Wimp_CloseWindow" + align 4 + +|Wimp_CloseWindow|: + mov a2,a1 + swi 0x600c6 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_Poll| + = "Wimp_Poll" + align 4 + +|Wimp_Poll|: + mov a4,a3 + swi 0x600c7 + mvnvs a1,#0 ;return -1 if error + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_RedrawWindow| + = "Wimp_RedrawWindow" + align 4 + +|Wimp_RedrawWindow|: + mov a2,a1 + swi 0x600c8 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_UpdateWindow| + = "Wimp_UpdateWindow" + align 4 + +|Wimp_UpdateWindow| + mov a2,a1 + swi 0x600c9 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetRectangle| + = "Wimp_GetRectangle" + align 4 + +|Wimp_GetRectangle|: + mov a2,a1 + swi 0x600ca + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetWindowState| + = "Wimp_GetWindowState" + align 4 + +|Wimp_GetWindowState|: + mov a2,a1 + swi 0x600cb + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetWindowInfo| + = "Wimp_GetWindowInfo" + align 4 + +|Wimp_GetWindowInfo|: + mov a2,a1 + swi 0x600cc + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SetIconState| + = "Wimp_SetIconState" + align 4 + +|Wimp_SetIconState|: + mov a2,a1 + swi 0x600cd + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetIconState| + = "Wimp_GetIconState" + align 4 + +|Wimp_GetIconState|: + mov a2,a1 + swi 0x600ce + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetPointerInfo| + = "Wimp_GetPointerInfo" + align 4 + +|Wimp_GetPointerInfo|: + mov a2,a1 + swi 0x600cf + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_DragBox| + = "Wimp_DragBox" + align 4 + +|Wimp_DragBox|: + mov a2,a1 + swi 0x600d0 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_ForceRedraw| + = "Wimp_ForceRedraw" + align 4 + +|Wimp_ForceRedraw|: + stmdb sp!,{v1} + ldr v1,[sp,#4] + swi 0x600d1 + movvc a1,#0 + ldmia sp!,{v1} + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SetCaretPosition| + = "Wimp_SetCaretPosition" + align 4 + +|Wimp_SetCaretPosition|: + stmdb sp!,{v1,v2} + add v1,sp,#8 + ldmia v1,{v1,v2} + swi 0x600d2 + movvc a1,#0 + ldmia sp!,{v1,v2} + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetCaretPosition| + = "Wimp_GetCaretPosition" + align 4 + +|Wimp_GetCaretPosition|: + mov a2,a1 + swi 0x600d3 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateMenu| + = "Wimp_CreateMenu" + align 4 + +|Wimp_CreateMenu|: + mov a4,a3 + mov a3,a2 + mov a2,a1 + swi 0x600d4 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SetExtent| + = "Wimp_SetExtent" + align 4 + +|Wimp_SetExtent|: + swi 0x600d7 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_OpenTemplate| + = "Wimp_OpenTemplate" + align 4 + +|Wimp_OpenTemplate|: + mov a2,a1 + swi 0x600d9 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CloseTemplate| + = "Wimp_CloseTemplate" + align 4 + +|Wimp_CloseTemplate|: + swi 0x600da + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_LoadTemplate| + = "Wimp_LoadTemplate" + align 4 + +|Wimp_LoadTemplate|: + stmdb sp!,{a1,a2,v1-v4,lr} ;7 registers + mov v1,a4 ;Fonts + mov a4,a3 ;IndirectLimit + ldr a3,[a2] ;*Indirect + ldr a2,[a1] ;*Template + add v2,sp,#28 + ldmia v2,{v2,v4} + ldr v3,[v4] ;Position + swi 0x600db + addvs sp,sp,#8 + bvs |WLTexit| + str v3,[v4] ;store Position + ldmia sp!,{v1,v2} + str a2,[v1] + str a3,[v2] + mov a1,#0 +|WLTexit|: + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |Wimp_ProcessKey| + = "Wimp_ProcessKey" + align 4 + +|Wimp_ProcessKey|: + swi 0x600dc + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_StartTask| + = "Wimp_StartTask" + align 4 + +|Wimp_StartTask|: + swi 0x600de + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_ReportError| + = "Wimp_ReportError" + align 4 + +|Wimp_ReportError|: + swi 0x600df + movvs a1,#0 + movvc a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetWindowOutline| + = "Wimp_GetWindowOutline" + align 4 + +|Wimp_GetWindowOutline|: + mov a2,a1 + swi 0x600e0 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_PollIdle| + = "Wimp_PollIdle" + align 4 + +|Wimp_PollIdle|: + swi 0x600e1 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_PlotIcon| + = "Wimp_PlotIcon" + align 4 + +|Wimp_PlotIcon|: + mov a2,a1 + swi 0x600e2 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SendMessage| + = "Wimp_SendMessage" + align 4 + +|Wimp_SendMessage|: + swi 0x600e7 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateSubMenu| + = "Wimp_CreateSubMenu" + align 4 + +|Wimp_CreateSubMenu|: + mov a4,a3 + mov a3,a2 + mov a2,a1 + swi 0x600e8 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SpriteOp| + = "Wimp_SpriteOp" + align 4 + +|Wimp_SpriteOp|: + stmdb sp!,{v1-v4,lr} + add v1,sp,#20 + ldmia v1,{v1-v4} + swi 0x600e9 + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |Wimp_BaseOfSprites| + = "Wimp_BaseOfSprites" + align 4 + +|Wimp_BaseOfSprites|: + mov a3,a1 + mov a4,a2 + swi 0x600ea + strvc a1,[a3] + strvc a2,[a4] + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CommandWindow| + = "Wimp_CommandWindow" + align 4 + +|Wimp_CommandWindow|: + swi 0x600ef + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_TransferBlock| + = "Wimp_TransferBlock" + align 4 + +|Wimp_TransferBlock|: + str v1,[sp,#-4]! + ldr v1,[sp,#4] + swi 0x600f1 + ldr v1,[sp],#4 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SpriteInfo| + = "Wimp_SpriteInfo" + align 4 + +|Wimp_SpriteInfo|: + stmdb sp!,{v1-v6,lr} + mov v4,a2 + mov v5,a3 + mov v6,a4 + mov a3,a1 + mov a1,#40 + movvc a1,#0 + swi 0x600e9 ;Wimp_SpriteOp + str a4,[v4] + str v1,[v5] + str v3,[v6] + ldmia sp!,{v1-v6,pc}^ + + + AREA CODE, READONLY + align 4 + export |DragASprite_Start| + = "DragASprite_Start" + align 4 + +|DragASprite_Start|: + swi 0x62400 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DragASprite_Stop| + = "DragASprite_Stop" + align 4 + +|DragASprite_Stop|: + swi 0x62401 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SelectTable| + = "ColourTrans_SelectTable" + align 4 + +|ColourTrans_SelectTable|: + stmdb sp!,{v1-v4,lr} + add v1,sp,#20 + ldmia v1,{v1-v4} + ldr v1,[v1] + swi 0x60740 + addvs sp,sp,#4 + bvs |CTSTexit| + mov a1,v1 + ldr v1,[sp],#4 + str a1,[v1] + mov a1,#0 +|CTSTexit|: + ldmia sp!,{v2-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SetFontColours| + = "ColourTrans_SetFontColours" + align 4 + +|ColourTrans_SetFontColours|: + swi 0x6074f + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SetColour| + = "ColourTrans_SetColour" + align 4 + +|ColourTrans_SetColour|: + str v1,[sp,#-4]! + mov v1,a3 + mov a4,a2 + swi 0x6075e + movvc a1,#0 + ldr v1,[sp],#4 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SetGCOL| + = "ColourTrans_SetGCOL" + align 4 + +|ColourTrans_SetGCOL|: + str v1,[sp,#-4]! + mov v1,a3 + mov a4,a2 + swi 0x60743 + movvc a1,#0 + ldr v1,[sp],#4 + movs pc,lr + + + + + +; ************* OS STUFF *************** + + AREA CODE, READONLY + align 4 + export |OS_ReadModeVariable| + = "OS_ReadModeVariable" + align 4 + +|OS_ReadModeVariable|: + swi 0x20035 + mvncs a1,#0 + movcc a1,a3 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadDynamicArea| + = "OS_ReadDynamicArea" + align 4 + +|OS_ReadDynamicArea|: + swi 0x2005c + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ModeColourNumber| + = "ModeColourNumber" + align 4 + +|ModeColourNumber|: + swi 0x60744 ;XColourTrans_ReturnColourNumber + mvnvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ScanKeys| + = "ScanKeys" + align 4 + +|ScanKeys|: + mov a2,a1 + mov a1,#121 + swi 0x20006 ;XOS_Byte 121 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ReadKeyboardStatus| + = "ReadKeyboardStatus" + align 4 + +|ReadKeyboardStatus|: + mov a1,#202 + mov a2,#0 + mov a3,#255 + swi 0x20006 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ReadDragType| + = "ReadDragType" + align 4 + +|ReadDragType|: + mov a1,#161 + mov a2,#28 + swi 0x20006 + and a1,a3,#2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |SetMousePointer| + = "SetMousePointer" + align 4 + +|SetMousePointer|: + mov a2,a1 + mov a1,#106 + swi 0x20006 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY ;sprite ops (I have 52, 53 and 56 in mind) + align 4 + export |OS_SpriteOp| + = "OS_SpriteOp" + align 4 + +|OS_SpriteOp|: + stmdb sp!,{v1-v4,lr} + add v1,sp,#20 + ldmia v1,{v1-v4} + swi 0x2002e + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |OS_Plot| + = "OS_Plot" + align 4 + +|OS_Plot|: + swi 0x20045 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |MouseBoundingBox| + = "MouseBoundingBox" + align 4 + +|MouseBoundingBox|: + mov a2,a1 + mov a1,#21 + swi 0x20007 ;OS_Word21,1 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadMonotonicTime| + = "OS_ReadMonotonicTime" + align 4 + +|OS_ReadMonotonicTime|: + swi 0x20042 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadC| + = "OS_ReadC" + align 4 + +|OS_ReadC|: + mov a2,a1 + swi 0x20004 + strccb a1,[a2] + movcc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadLine| + = "OS_ReadLine" + align 4 + +|OS_ReadLine|: + stmdb sp!,{a1,v1,v2} + stmdb sp!,{a1-a3} + mov a1,#200 + mov a2,#1 + mvn a3,#1 + swi 0x20006 + mov v2,a2 + ldmia sp!,{a1-a3} + ldr v1,[sp,#8] + swi 0x2000e + mov v1,a2 + mov a1,#200 + mov a2,v2 + mvn a3,#0 + swi 0x20006 + mov a2,v1 + ldmia sp!,{a4,v1,v2} + bic a4,a4,#0xc0000000 + mov a1,#10 + strb a1,[a4,a2] ;make compatible with fgets (terminated by 10, not 13) + mov a1,a2 + rsbcs a1,a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadEscapeState| + = "OS_ReadEscapeState" + align 4 + +|OS_ReadEscapeState|: + swi 0x2002c + movcc a1,#0 + movcs a1,#1 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ReadCatalogueInfo| + = "ReadCatalogueInfo" + align 4 + +|ReadCatalogueInfo|: + stmdb sp!,{v1-v3,lr} + mov v3,a2 + mov a2,a1 + mov a1,#17 + swi 0x20008 ;OS_File 17 + stmia v3,{a3-v2} + ldmia sp!,{v1-v3,pc}^ + + + AREA CODE, READONLY + align 4 + export |ReadDirName| + = "ReadDirName" + align 4 + +|ReadDirName|: + stmdb sp!,{v1-v4,lr} + mov v4,a3 ;preserve dir_env * + ldmia a3,{a4-v3} + mov a3,a2 + mov a2,a1 + mov a1,#9 + swi 0x2000c ;OS_GBPB 9 + stmia v4,{a4,v1} + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |ReadDirNameInfo| + = "ReadDirNameInfo" + align 4 + +|ReadDirNameInfo|: + stmdb sp!,{v1-v4,lr} + mov v4,a3 + ldmia a3,{a4-v3} + mov a3,a2 + mov a2,a1 + mov a1,#10 + swi 0x2000c ;OS_GBPB 10 + stmia v4,{a4,v1} + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |DeleteFile| + = "DeleteFile" + align 4 + +|DeleteFile|: + stmdb sp!,{v1,v2} + mov a2,a1 + mov a1,#6 + swi 0x20008 ;OS_File 6 + movvc a1,#0 + ldmia sp!,{v1,v2} + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger1| + = "ConvertInteger1" + align 4 + +|ConvertInteger1|: + swi 0x200d9 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger2| + = "ConvertInteger2" + align 4 + +|ConvertInteger2|: + swi 0x200da + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger3| + = "ConvertInteger3" + align 4 + +|ConvertInteger3|: + swi 0x200db + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger4| + = "ConvertInteger4" + align 4 + +|ConvertInteger4|: + swi 0x200dc + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_FlushBuffer| + = "OS_FlushBuffer" + align 4 + +|OS_FlushBuffer|: + mov a2,a1 + mov a1,#21 + swi 0x20006 + movvc a1,#0 + movs pc,lr + + + + + + +; ************ MISC STUFF *************** + + AREA CODE, READONLY + align 4 + export |Joystick_Read| + = "Joystick_Read" + align 4 + +|Joystick_Read|: + swi 0x63f40 ;XJoystick_Read (Acorn) + bvc |JRexit| + ldr a2,[a1] ;get error-number + eor a2,a2,#0xe6 + eors a2,a2,#0x100 + mvneq a1,#1 ;-2 ==> unknown SWI + mvnne a1,#0 ;-1 ==> error generated from joystick module +|JRexit|: + movs pc,lr + + + + + +; ************** SOUND SWIS ************* + + AREA CODE, READONLY + align 4 + export |Sound_Volume| + = "Sound_Volume" + align 4 + +|Sound_Volume|: + swi 0x60180 + mvnvs a1,#0 + movs pc,lr + + +; *********** DIGITAL RENDERER ********** + +DigiRendChunk equ 0x6F700 ;X flag set + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Activate| + = "DigitalRenderer_Activate" + align 4 + +|DigitalRenderer_Activate|: + swi DigiRendChunk + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Deactivate| + = "DigitalRenderer_Deactivate" + align 4 + +|DigitalRenderer_Deactivate|: + swi DigiRendChunk+1 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Pause| + = "DigitalRenderer_Pause" + align 4 + +|DigitalRenderer_Pause|: + swi DigiRendChunk+2 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Resume| + = "DigitalRenderer_Resume" + align 4 + +|DigitalRenderer_Resume|: + swi DigiRendChunk+3 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_GetTables| + = "DigitalRenderer_GetTables" + align 4 + +|DigitalRenderer_GetTables|: + mov a3,a1 + mov a4,a2 + swi DigiRendChunk+4 + strvc a1,[a3] + strvc a2,[a4] + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_ReadState| + = "DigitalRenderer_ReadState" + align 4 + +|DigitalRenderer_ReadState|: + swi DigiRendChunk+5 + mvnvs a1,#0 ;returns -1 on error + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_NewSample| + = "DigitalRenderer_NewSample" + align 4 + +|DigitalRenderer_NewSample|: + swi DigiRendChunk+6 + movvc a1,#0 + movs pc,lr + + +; *********** PLOTTER ************** + +BPlotChunk equ 0x70100 ;X bit set + + AREA CODE, READONLY + align 4 + export |PlotZoom1| + = "PlotZoom1" + align 4 + +|PlotZoom1|: + swi BPlotChunk + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |PlotZoom2| + = "PlotZoom2" + align 4 + +|PlotZoom2|: + swi BPlotChunk+1 diff --git a/Src/SAM.cpp b/Src/SAM.cpp new file mode 100644 index 0000000..92c8700 --- /dev/null +++ b/Src/SAM.cpp @@ -0,0 +1,2175 @@ +/* + * SAM.h - Simple Assembler and Monitor With Integrated System Explorer + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysdeps.h" + +#include "SAM.h" +#include "C64.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" + + +// Pointers to chips +static MOS6510 *TheCPU; +static MOS6502_1541 *TheCPU1541; +static MOS6569 *TheVIC; +static MOS6581 *TheSID; +static MOS6526_1 *TheCIA1; +static MOS6526_2 *TheCIA2; + +// 6510/6502 registers +static MOS6510State R64; +static MOS6502State R1541; + +static bool access_1541; // false: accessing C64, true: accessing 1541 + +// Access to 6510/6502 address space +static inline uint8 SAMReadByte(uint16 adr) +{ + if (access_1541) + return TheCPU1541->ExtReadByte(adr); + else + return TheCPU->ExtReadByte(adr); +} + +static inline void SAMWriteByte(uint16 adr, uint8 byte) +{ + if (access_1541) + TheCPU1541->ExtWriteByte(adr, byte); + else + TheCPU->ExtWriteByte(adr, byte); +} + + +// Streams for input, output and error messages +static FILE *fin, *fout, *ferr; + +// Input line +#define INPUT_LENGTH 80 +static char input[INPUT_LENGTH]; +static char *in_ptr; + +static uint16 address, end_address; + + +// Input tokens +enum Token { + T_NULL, // Invalid token + T_END, // End of line + T_NUMBER, // Hexadecimal number + T_STRING, // String enclosed in "" + T_LPAREN, // '(' + T_RPAREN, // ')' + T_ADD, // '+' + T_SUB, // '-' + T_MUL, // '*' + T_DIV, // '/' + T_COMMA, // ',' + T_IMMED, // '#' + T_X, // 'x' + T_Y, // 'y' + T_PC, // 'pc' + T_SP, // 'sp' + + T_A, // 'a' (get_reg_token() only) + T_DR, // 'dr' (get_reg_token() only) + T_PR // 'pr' (get_reg_token() only) +}; + +static enum Token the_token; // Last token read +static uint16 the_number; // Contains the number if the_token==T_NUMBER +static char the_string[INPUT_LENGTH]; // Contains the string if the_token==T_STRING + + +// Addressing modes +enum { + A_IMPL, + A_ACCU, // A + A_IMM, // #zz + A_REL, // Branches + A_ZERO, // zz + A_ZEROX, // zz,x + A_ZEROY, // zz,y + A_ABS, // zzzz + A_ABSX, // zzzz,x + A_ABSY, // zzzz,y + A_IND, // (zzzz) + A_INDX, // (zz,x) + A_INDY // (zz),y +}; + +// Mnemonics +enum { + M_ADC, M_AND, M_ASL, M_BCC, M_BCS, M_BEQ, M_BIT, M_BMI, M_BNE, M_BPL, + M_BRK, M_BVC, M_BVS, M_CLC, M_CLD, M_CLI, M_CLV, M_CMP, M_CPX, M_CPY, + M_DEC, M_DEX, M_DEY, M_EOR, M_INC, M_INX, M_INY, M_JMP, M_JSR, M_LDA, + M_LDX, M_LDY, M_LSR, M_NOP, M_ORA, M_PHA, M_PHP, M_PLA, M_PLP, M_ROL, + M_ROR, M_RTI, M_RTS, M_SBC, M_SEC, M_SED, M_SEI, M_STA, M_STX, M_STY, + M_TAX, M_TAY, M_TSX, M_TXA, M_TXS, M_TYA, + + M_ILLEGAL, // Undocumented opcodes start here + + M_IANC, M_IANE, M_IARR, M_IASR, M_IDCP, M_IISB, M_IJAM, M_INOP, M_ILAS, + M_ILAX, M_ILXA, M_IRLA, M_IRRA, M_ISAX, M_ISBC, M_ISBX, M_ISHA, M_ISHS, + M_ISHX, M_ISHY, M_ISLO, M_ISRE, + + M_MAXIMUM // Highest element +}; + +// Mnemonic for each opcode +static const char mnemonic[256] = { + M_BRK , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 00 + M_PHP , M_ORA , M_ASL , M_IANC, M_INOP, M_ORA, M_ASL , M_ISLO, + M_BPL , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 10 + M_CLC , M_ORA , M_INOP, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, + M_JSR , M_AND , M_IJAM, M_IRLA, M_BIT , M_AND, M_ROL , M_IRLA, // 20 + M_PLP , M_AND , M_ROL , M_IANC, M_BIT , M_AND, M_ROL , M_IRLA, + M_BMI , M_AND , M_IJAM, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, // 30 + M_SEC , M_AND , M_INOP, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, + M_RTI , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 40 + M_PHA , M_EOR , M_LSR , M_IASR, M_JMP , M_EOR, M_LSR , M_ISRE, + M_BVC , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 50 + M_CLI , M_EOR , M_INOP, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, + M_RTS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 60 + M_PLA , M_ADC , M_ROR , M_IARR, M_JMP , M_ADC, M_ROR , M_IRRA, + M_BVS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 70 + M_SEI , M_ADC , M_INOP, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, + M_INOP, M_STA , M_INOP, M_ISAX, M_STY , M_STA, M_STX , M_ISAX, // 80 + M_DEY , M_INOP, M_TXA , M_IANE, M_STY , M_STA, M_STX , M_ISAX, + M_BCC , M_STA , M_IJAM, M_ISHA, M_STY , M_STA, M_STX , M_ISAX, // 90 + M_TYA , M_STA , M_TXS , M_ISHS, M_ISHY, M_STA, M_ISHX, M_ISHA, + M_LDY , M_LDA , M_LDX , M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // a0 + M_TAY , M_LDA , M_TAX , M_ILXA, M_LDY , M_LDA, M_LDX , M_ILAX, + M_BCS , M_LDA , M_IJAM, M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // b0 + M_CLV , M_LDA , M_TSX , M_ILAS, M_LDY , M_LDA, M_LDX , M_ILAX, + M_CPY , M_CMP , M_INOP, M_IDCP, M_CPY , M_CMP, M_DEC , M_IDCP, // c0 + M_INY , M_CMP , M_DEX , M_ISBX, M_CPY , M_CMP, M_DEC , M_IDCP, + M_BNE , M_CMP , M_IJAM, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, // d0 + M_CLD , M_CMP , M_INOP, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, + M_CPX , M_SBC , M_INOP, M_IISB, M_CPX , M_SBC, M_INC , M_IISB, // e0 + M_INX , M_SBC , M_NOP , M_ISBC, M_CPX , M_SBC, M_INC , M_IISB, + M_BEQ , M_SBC , M_IJAM, M_IISB, M_INOP, M_SBC, M_INC , M_IISB, // f0 + M_SED , M_SBC , M_INOP, M_IISB, M_INOP, M_SBC, M_INC , M_IISB +}; + +// Addressing mode for each opcode +static const char adr_mode[256] = { + A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 00 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 10 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_ABS , A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 20 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 30 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 40 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 50 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 60 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_IND , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 70 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 80 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // 90 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // a0 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // b0 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // c0 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // d0 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // e0 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // f0 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX +}; + +// Chars for each mnemonic +static const char mnem_1[] = "aaabbbbbbbbbbcccccccdddeiiijjllllnopppprrrrssssssstttttt?aaaadijnlllrrsssssssss"; +static const char mnem_2[] = "dnscceimnprvvllllmppeeeonnnmsdddsorhhlloottbeeetttaasxxy?nnrscsaoaaxlrabbhhhhlr"; +static const char mnem_3[] = "cdlcsqtielkcscdivpxycxyrcxypraxyrpaapaplrisccdiaxyxyxasa?cerrpbmpsxaaaxcxasxyoe"; + +// Instruction length for each addressing mode +static const char adr_length[] = {1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2}; + + +// Prototypes +static void error(char *s); +static void handle_abort(...); +static void init_abort(void); +static void exit_abort(void); +static bool aborted(void); + +static void read_line(void); // Scanner +static char get_char(void); +static void put_back(char c); +static enum Token get_token(void); +static enum Token get_reg_token(void); +static uint16 get_number(void); +static enum Token get_string(char *str); + +static bool expression(uint16 *number); // Parser +static bool term(uint16 *number); +static bool factor(uint16 *number); +static bool address_args(void); +static bool range_args(int def_range); +static bool instr_args(uint16 *number, char *mode); + +static void help(void); // Routines for commands +static void registers(void); +static void display_registers(void); +static void memory_dump(void); +static void ascii_dump(void); +static char conv_from_64(char c); +static void screen_dump(void); +static char conv_from_scode(char c); +static void binary_dump(void); +static void sprite_dump(void); +static void byte_to_bin(uint8 byte, char *str); +static void disassemble(void); +static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi); +static void assemble(void); +static char find_mnemonic(char op1, char op2, char op3); +static bool find_opcode(char mnem, char mode, uint8 *opcode); +static void mem_config(void); +static void fill(void); +static void compare(void); +static void transfer(void); +static void modify(void); +static void print_expr(void); +static void redir_output(void); +static void int_vectors(void); +static void view_state(void); +static void view_cia_state(void); +static void dump_cia_ints(uint8 i); +static void view_sid_state(void); +static void dump_sid_waveform(uint8 wave); +static void view_vic_state(void); +static void dump_spr_flags(uint8 f); +static void dump_vic_ints(uint8 i); +static void view_1541_state(void); +static void dump_via_ints(uint8 i); +static void load_data(void); +static void save_data(void); + + +/* + * Open and handle SAM + */ + +void SAM(C64 *the_c64) +{ + bool done = false; + char c; + + TheCPU = the_c64->TheCPU; + TheCPU1541 = the_c64->TheCPU1541; + TheVIC = the_c64->TheVIC; + TheSID = the_c64->TheSID; + TheCIA1 = the_c64->TheCIA1; + TheCIA2 = the_c64->TheCIA2; + + // Get CPU registers and current memory configuration + TheCPU->GetState(&R64); + TheCPU->ExtConfig = (~R64.ddr | R64.pr) & 7; + TheCPU1541->GetState(&R1541); + +#ifdef __riscos__ + Wimp_CommandWindow((int)"SAM"); +#endif + +#ifdef AMIGA + if (!(fin = fout = ferr = fopen("CON:0/0/640/480/SAM", "w+"))) + return; +#else + fin = stdin; + fout = stdout; + ferr = stdout; +#endif + + access_1541 = false; + address = R64.pc; + + fprintf(ferr, "\n *** SAM - Simple Assembler and Monitor ***\n *** Press 'h' for help ***\n\n"); + init_abort(); + display_registers(); + + while (!done) { + if (access_1541) + fprintf(ferr, "1541> "); + else + fprintf(ferr, "C64> "); + fflush(ferr); + read_line(); + while ((c = get_char()) == ' ') ; + + switch (c) { + case 'a': // Assemble + get_token(); + assemble(); + break; + + case 'b': // Binary dump + get_token(); + binary_dump(); + break; + + case 'c': // Compare + get_token(); + compare(); + break; + + case 'd': // Disassemble + get_token(); + disassemble(); + break; + + case 'e': // Interrupt vectors + int_vectors(); + break; + + case 'f': // Fill + get_token(); + fill(); + break; + + case 'h': // Help + help(); + break; + + case 'i': // ASCII dump + get_token(); + ascii_dump(); + break; + + case 'k': // Memory configuration + get_token(); + mem_config(); + break; + + case 'l': // Load data + get_token(); + load_data(); + break; + + case 'm': // Memory dump + get_token(); + memory_dump(); + break; + + case 'n': // Screen code dump + get_token(); + screen_dump(); + break; + + case 'o': // Redirect output + get_token(); + redir_output(); + break; + + case 'p': // Sprite dump + get_token(); + sprite_dump(); + break; + + case 'r': // Registers + get_reg_token(); + registers(); + break; + + case 's': // Save data + get_token(); + save_data(); + break; + + case 't': // Transfer + get_token(); + transfer(); + break; + + case 'v': // View machine state + view_state(); + break; + + case 'x': // Exit + done = true; + break; + + case ':': // Change memory + get_token(); + modify(); + break; + + case '1': // Switch to 1541 mode + access_1541 = true; + break; + + case '6': // Switch to C64 mode + access_1541 = false; + break; + + case '?': // Compute expression + get_token(); + print_expr(); + break; + + case '\n': // Blank line + break; + + default: // Unknown command + error("Unknown command"); + break; + } + } + + exit_abort(); + +#ifdef AMIGA + fclose(fin); +#endif + if (fout != ferr) + fclose(fout); + +#ifdef __riscos__ + Wimp_CommandWindow(-1); +#endif + + // Set CPU registers + TheCPU->SetState(&R64); + TheCPU1541->SetState(&R1541); +} + + +/* + * Print error message + */ + +static void error(char *s) +{ + fprintf(ferr, "*** %s\n", s); +} + + +/* + * CTRL-C pressed? + */ + +static bool WasAborted; + +#ifdef HAVE_SIGACTION +struct sigaction my_sa; +#endif + +static void handle_abort(...) +{ + WasAborted = true; +#if !defined(HAVE_SIGACTION) && defined(HAVE_SIGNAL) +#ifdef __riscos__ + signal(SIGINT, (Handler*) handle_abort); +#else + signal(SIGINT, (sighandler_t) handle_abort); +#endif +#endif +} + +static void init_abort(void) +{ + WasAborted = false; +#if defined(HAVE_SIGACTION) + my_sa.sa_handler = (void (*)(int))handle_abort; + my_sa.sa_flags = 0; + sigemptyset(&my_sa.sa_mask); + sigaction(SIGINT, &my_sa, NULL); +#elif defined(HAVE_SIGNAL) +#ifdef __riscos__ + signal(SIGINT, (Handler*) handle_abort); +#else + signal(SIGINT, (sighandler_t) handle_abort); +#endif +#endif +} + +static void exit_abort(void) +{ +#if defined(HAVE_SIGACTION) + my_sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &my_sa, NULL); +#elif defined(HAVE_SIGNAL) + signal(SIGINT, SIG_DFL); +#endif +} + +static bool aborted(void) +{ + bool ret = WasAborted; + + WasAborted = false; + return ret; +} + + +/* + * Read a line from the keyboard + */ + +static void read_line(void) +{ +#ifdef __riscos__ + OS_ReadLine(in_ptr = input, INPUT_LENGTH, 0, 255, 0); +#else + fgets(in_ptr = input, INPUT_LENGTH, fin); +#endif +} + + +/* + * Read a character from the input line + */ + +static char get_char(void) +{ + return *in_ptr++; +} + + +/* + * Stuff back a character into the input line + */ + +static void put_back(char c) +{ + *(--in_ptr) = c; +} + + +/* + * Scanner: Get a token from the input line + */ + +static enum Token get_token(void) +{ + char c; + + // Skip spaces + while ((c = get_char()) == ' ') ; + + switch (c) { + case '\n': + return the_token = T_END; + case '(': + return the_token = T_LPAREN; + case ')': + return the_token = T_RPAREN; + case '+': + return the_token = T_ADD; + case '-': + return the_token = T_SUB; + case '*': + return the_token = T_MUL; + case '/': + return the_token = T_DIV; + case ',': + return the_token = T_COMMA; + case '#': + return the_token = T_IMMED; + case 'x': + return the_token = T_X; + case 'y': + return the_token = T_Y; + case 'p': + if (get_char() == 'c') + return the_token = T_PC; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 's': + if (get_char() == 'p') + return the_token = T_SP; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + put_back(c); + the_number = get_number(); + return the_token = T_NUMBER; + case '"': + return the_token = get_string(the_string); + default: + error("Unrecognized token"); + return the_token = T_NULL; + } +} + +static enum Token get_reg_token(void) +{ + char c; + + // Skip spaces + while ((c = get_char()) == ' ') ; + + switch (c) { + case '\n': + return the_token = T_END; + case 'a': + return the_token = T_A; + case 'd': + if (get_char() == 'r') + return the_token = T_DR; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 'p': + if ((c = get_char()) == 'c') + return the_token = T_PC; + else if (c == 'r') + return the_token = T_PR; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 's': + if (get_char() == 'p') + return the_token = T_SP; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 'x': + return the_token = T_X; + case 'y': + return the_token = T_Y; + default: + error("Unrecognized token"); + return the_token = T_NULL; + } +} + +static uint16 get_number(void) +{ + char c; + uint16 i = 0; + + while (((c = get_char()) >= '0') && (c <= '9') || (c >= 'a') && (c <= 'f')) + if (c < 'a') + i = (i << 4) + (c - '0'); + else + i = (i << 4) + (c - 'a' + 10); + + put_back(c); + return i; +} + +static enum Token get_string(char *str) +{ + char c; + + while ((c = get_char()) != '\n') { + if (c == '"') { + *str = 0; + return T_STRING; + } + *str++ = c; + } + + error("Unterminated string"); + return T_NULL; +} + + +/* + * expression = term {(ADD | SUB) term} + * true: OK, false: Error + */ + +static bool expression(uint16 *number) +{ + uint16 accu, trm; + + if (!term(&accu)) + return false; + + for (;;) + switch (the_token) { + case T_ADD: + get_token(); + if (!term(&trm)) + return false; + accu += trm; + break; + + case T_SUB: + get_token(); + if (!term(&trm)) + return false; + accu -= trm; + break; + + default: + *number = accu; + return true; + } +} + + +/* + * term = factor {(MUL | DIV) factor} + * true: OK, false: Error + */ + +static bool term(uint16 *number) +{ + uint16 accu, fact; + + if (!factor(&accu)) + return false; + + for (;;) + switch (the_token) { + case T_MUL: + get_token(); + if (!factor(&fact)) + return false; + accu *= fact; + break; + + case T_DIV: + get_token(); + if (!factor(&fact)) + return false; + if (fact == 0) { + error("Division by 0"); + return false; + } + accu /= fact; + break; + + default: + *number = accu; + return true; + } +} + + +/* + * factor = NUMBER | PC | SP | LPAREN expression RPAREN + * true: OK, false: Error + */ + +static bool factor(uint16 *number) +{ + switch (the_token) { + case T_NUMBER: + *number = the_number; + get_token(); + return true; + + case T_PC: + get_token(); + *number = access_1541 ? R1541.pc : R64.pc; + return true; + + case T_SP: + get_token(); + *number = access_1541 ? R1541.sp : R64.sp; + return true; + + case T_LPAREN: + get_token(); + if (expression(number)) + if (the_token == T_RPAREN) { + get_token(); + return true; + } else { + error("Missing ')'"); + return false; + } + else { + error("Error in expression"); + return false; + } + + case T_END: + error("Required argument missing"); + return false; + + default: + error("'pc', 'sp', '(' or number expected"); + return false; + } +} + + +/* + * address_args = [expression] END + * + * Read start to "address" + * + * true: OK, false: Error + */ + +static bool address_args(void) +{ + if (the_token == T_END) + return true; + else { + if (!expression(&address)) + return false; + return the_token == T_END; + } +} + + +/* + * range_args = [expression] [[COMMA] expression] END + * + * Read start address to "address", end address to "end_address" + * + * true: OK, false: Error + */ + +static bool range_args(int def_range) +{ + end_address = address + def_range; + + if (the_token == T_END) + return true; + else { + if (!expression(&address)) + return false; + end_address = address + def_range; + if (the_token == T_END) + return true; + else { + if (the_token == T_COMMA) get_token(); + if (!expression(&end_address)) + return false; + return the_token == T_END; + } + } +} + + +/* + * instr_args = END + * | IMMED NUMBER END + * | NUMBER [COMMA (X | Y)] END + * | LPAREN NUMBER (RPAREN [COMMA Y] | COMMA X RPAREN) END + * + * Read arguments of a 6510 instruction, determine address and addressing mode + * + * true: OK, false: Error + */ + +static bool instr_args(uint16 *number, char *mode) +{ + switch (the_token) { + + case T_END: + *mode = A_IMPL; + return true; + + case T_IMMED: + get_token(); + if (the_token == T_NUMBER) { + *number = the_number; + *mode = A_IMM; + get_token(); + return the_token == T_END; + } else { + error("Number expected"); + return false; + } + + case T_NUMBER: + *number = the_number; + get_token(); + switch (the_token) { + + case T_END: + if (*number < 0x100) + *mode = A_ZERO; + else + *mode = A_ABS; + return true; + + case T_COMMA: + get_token(); + switch (the_token) { + + case T_X: + get_token(); + if (*number < 0x100) + *mode = A_ZEROX; + else + *mode = A_ABSX; + return the_token == T_END; + + case T_Y: + get_token(); + if (*number < 0x100) + *mode = A_ZEROY; + else + *mode = A_ABSY; + return the_token == T_END; + + default: + error("Illegal index register"); + return false; + } + + default: + return false; + } + + case T_LPAREN: + get_token(); + if (the_token == T_NUMBER) { + *number = the_number; + get_token(); + switch (the_token) { + + case T_RPAREN: + get_token(); + switch (the_token) { + + case T_END: + *mode = A_IND; + return true; + + case T_COMMA: + get_token(); + if (the_token == T_Y) { + *mode = A_INDY; + get_token(); + return the_token == T_END; + } else { + error("Only 'y' index register allowed"); + return false; + } + + default: + error("Illegal characters after ')'"); + return false; + } + + case T_COMMA: + get_token(); + if (the_token == T_X) { + get_token(); + if (the_token == T_RPAREN) { + *mode = A_INDX; + get_token(); + return the_token == T_END; + } else { + error("')' expected"); + return false; + } + } else { + error("Only 'x' index register allowed"); + return false; + } + + default: + error("')' or ',' expected"); + return false; + } + } else { + error("Number expected"); + return false; + } + + default: + error("'(', '#' or number expected"); + return false; + } +} + + +/* + * Display help + * h + */ + +static void help(void) +{ + fprintf(fout, "a [start] Assemble\n" + "b [start] [end] Binary dump\n" + "c start end dest Compare memory\n" + "d [start] [end] Disassemble\n" + "e Show interrupt vectors\n" + "f start end byte Fill memory\n" + "i [start] [end] ASCII/PETSCII dump\n" + "k [config] Show/set C64 memory configuration\n" + "l start \"file\" Load data\n" + "m [start] [end] Memory dump\n" + "n [start] [end] Screen code dump\n" + "o [\"file\"] Redirect output\n" + "p [start] [end] Sprite dump\n" + "r [reg value] Show/set CPU registers\n" + "s start end \"file\" Save data\n" + "t start end dest Transfer memory\n" + "vc1 View CIA 1 state\n" + "vc2 View CIA 2 state\n" + "vf View 1541 state\n" + "vs View SID state\n" + "vv View VIC state\n" + "x Return to Frodo\n" + ": addr {byte} Modify memory\n" + "1541 Switch to 1541\n" + "64 Switch to C64\n" + "? expression Calculate expression\n"); +} + + +/* + * Display/change 6510 registers + * r [reg value] + */ + +static void registers(void) +{ + enum Token the_reg; + uint16 value; + + if (the_token != T_END) + switch (the_reg = the_token) { + case T_A: + case T_X: + case T_Y: + case T_PC: + case T_SP: + case T_DR: + case T_PR: + get_token(); + if (!expression(&value)) + return; + + switch (the_reg) { + case T_A: + if (access_1541) + R1541.a = value; + else + R64.a = value; + break; + case T_X: + if (access_1541) + R1541.x = value; + else + R64.x = value; + break; + case T_Y: + if (access_1541) + R1541.y = value; + else + R64.y = value; + break; + case T_PC: + if (access_1541) + R1541.pc = value; + else + R64.pc = value; + break; + case T_SP: + if (access_1541) + R1541.sp = (value & 0xff) | 0x0100; + else + R64.sp = (value & 0xff) | 0x0100; + break; + case T_DR: + if (!access_1541) + R64.ddr = value; + break; + case T_PR: + if (!access_1541) + R64.pr = value; + break; + default: + break; + } + break; + + default: + return; + } + + display_registers(); +} + +static void display_registers(void) +{ + if (access_1541) { + fprintf(fout, " PC A X Y SP NVDIZC Instruction\n"); + fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %c%c%c%c%c%c ", + R1541.pc, R1541.a, R1541.x, R1541.y, R1541.sp, + R1541.p & 0x80 ? '1' : '0', R1541.p & 0x40 ? '1' : '0', R1541.p & 0x08 ? '1' : '0', + R1541.p & 0x04 ? '1' : '0', R1541.p & 0x02 ? '1' : '0', R1541.p & 0x01 ? '1' : '0'); + disass_line(R1541.pc, SAMReadByte(R1541.pc), SAMReadByte(R1541.pc+1), SAMReadByte(R1541.pc+2)); + } else { + fprintf(fout, " PC A X Y SP DR PR NVDIZC Instruction\n"); + fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %02lx %02lx %c%c%c%c%c%c ", + R64.pc, R64.a, R64.x, R64.y, R64.sp, R64.ddr, R64.pr, + R64.p & 0x80 ? '1' : '0', R64.p & 0x40 ? '1' : '0', R64.p & 0x08 ? '1' : '0', + R64.p & 0x04 ? '1' : '0', R64.p & 0x02 ? '1' : '0', R64.p & 0x01 ? '1' : '0'); + disass_line(R64.pc, SAMReadByte(R64.pc), SAMReadByte(R64.pc+1), SAMReadByte(R64.pc+2)); + } +} + + +/* + * Memory dump + * m [start] [end] + */ + +#define MEMDUMP_BPL 16 // Bytes per line + +static void memory_dump(void) +{ + bool done = false; + short i; + uint8 mem[MEMDUMP_BPL + 2]; + uint8 byte; + + mem[MEMDUMP_BPL] = 0; + + if (!range_args(16 * MEMDUMP_BPL - 1)) // 16 lines unless end address specified + return; + + do { + fprintf(fout, "%04lx:", address); + for (i=0; i= ' ') && (byte <= '~')) + mem[i] = conv_from_64(byte); + else + mem[i] = '.'; + } + fprintf(fout, " '%s'\n", mem); + } while (!done && !aborted()); +} + + +/* + * ASCII dump + * i [start] [end] + */ + +#define ASCIIDUMP_BPL 64 // Bytes per line + +static void ascii_dump(void) +{ + bool done = false; + short i; + uint8 mem[ASCIIDUMP_BPL + 2]; + uint8 byte; + + mem[ASCIIDUMP_BPL] = 0; + + if (!range_args(16 * ASCIIDUMP_BPL - 1)) // 16 lines unless end address specified + return; + + do { + fprintf(fout, "%04lx:", address); + for (i=0; i= ' ') && (byte <= '~')) + mem[i] = conv_from_64(byte); + else + mem[i] = '.'; + } + fprintf(fout, " '%s'\n", mem); + } while (!done && !aborted()); +} + + +/* + * Convert PETSCII->ASCII + */ + +static char conv_from_64(char c) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + else + return c; +} + + +/* + * Screen code dump + * n [start] [end] + */ + +#define SCRDUMP_BPL 64 // Bytes per line + +static void screen_dump(void) +{ + bool done = false; + short i; + uint8 mem[SCRDUMP_BPL + 2]; + uint8 byte; + + mem[SCRDUMP_BPL] = 0; + + if (!range_args(16 * SCRDUMP_BPL - 1)) // 16 Zeilen unless end address specified + return; + + do { + fprintf(fout, "%04lx:", address); + for (i=0; iASCII + */ + +static char conv_from_scode(char c) +{ + c &= 0x7f; + + if (c <= 31) + return c + 64; + else + if (c >= 64) + return c + 32; + else + return c; +} + + +/* + * Binary dump + * b [start] [end] + */ + +static void binary_dump(void) +{ + bool done = false; + char bin[10]; + + bin[8] = 0; + + if (!range_args(7)) // 8 lines unless end address specified + return; + + do { + if (address == end_address) done = true; + + byte_to_bin(SAMReadByte(address), bin); + fprintf(fout, "%04lx: %s\n", address++, bin); + } while (!done && !aborted()); +} + + +/* + * Sprite data dump + * p [start] [end] + */ + +static void sprite_dump(void) +{ + bool done = false; + short i; + char bin[10]; + + bin[8] = 0; + + if (!range_args(21 * 3 - 1)) // 21 lines unless end address specified + return; + + do { + fprintf(fout, "%04lx: ", address); + for (i=0; i<3; i++, address++) { + if (address == end_address) done = true; + + byte_to_bin(SAMReadByte(address), bin); + fprintf(fout, "%s", bin); + } + fputc('\n', fout); + } while (!done && !aborted()); +} + + +/* + * Convert byte to binary representation + */ + +static void byte_to_bin(uint8 byte, char *str) +{ + short i; + + for (i=0; i<8; i++, byte<<=1) + if (byte & 0x80) + str[i] = '#'; + else + str[i] = '.'; +} + + +/* + * Disassemble + * d [start] [end] + */ + +static void disassemble(void) +{ + bool done = false; + short i; + uint8 op[3]; + uint16 adr; + + if (!range_args(31)) // 32 bytes unless end address specified + return; + + do { + fprintf(fout, "%04lx:", adr = address); + for (i=0; i<3; i++, adr++) { + if (adr == end_address) done = true; + op[i] = SAMReadByte(adr); + } + address += disass_line(address, op[0], op[1], op[2]); + } while (!done && !aborted()); +} + + +/* + * Disassemble one instruction, return length + */ + +static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi) +{ + char mode = adr_mode[op], mnem = mnemonic[op]; + + // Display instruction bytes in hex + switch (adr_length[mode]) { + case 1: + fprintf(fout, " %02lx ", op); + break; + + case 2: + fprintf(fout, " %02lx %02lx ", op, lo); + break; + + case 3: + fprintf(fout, " %02lx %02lx %02lx ", op, lo, hi); + break; + } + + // Tag undocumented opcodes with an asterisk + if (mnem > M_ILLEGAL) + fputc('*', fout); + else + fputc(' ', fout); + + // Print mnemonic + fprintf(fout, "%c%c%c ", mnem_1[mnem], mnem_2[mnem], mnem_3[mnem]); + + // Pring argument + switch (mode) { + case A_IMPL: + break; + + case A_ACCU: + fprintf(fout, "a"); + break; + + case A_IMM: + fprintf(fout, "#%02lx", lo); + break; + + case A_REL: + fprintf(fout, "%04lx", ((adr + 2) + (int8)lo) & 0xffff); + break; + + case A_ZERO: + fprintf(fout, "%02lx", lo); + break; + + case A_ZEROX: + fprintf(fout, "%02lx,x", lo); + break; + + case A_ZEROY: + fprintf(fout, "%02lx,y", lo); + break; + + case A_ABS: + fprintf(fout, "%04lx", (hi << 8) | lo); + break; + + case A_ABSX: + fprintf(fout, "%04lx,x", (hi << 8) | lo); + break; + + case A_ABSY: + fprintf(fout, "%04lx,y", (hi << 8) | lo); + break; + + case A_IND: + fprintf(fout, "(%04lx)", (hi << 8) | lo); + break; + + case A_INDX: + fprintf(fout, "(%02lx,x)", lo); + break; + + case A_INDY: + fprintf(fout, "(%02lx),y", lo); + break; + } + + fputc('\n', fout); + return adr_length[mode]; +} + + +/* + * Assemble + * a [start] + */ + +static void assemble(void) +{ + bool done = false; + char c1, c2, c3; + char mnem, mode; + uint8 opcode; + uint16 arg; + int16 rel; + + // Read parameters + if (!address_args()) + return; + + do { + fprintf(fout, "%04lx> ", address); + fflush(ferr); + read_line(); + + c1 = get_char(); + c2 = get_char(); + c3 = get_char(); + + if (c1 != '\n') { + + if ((mnem = find_mnemonic(c1, c2, c3)) != M_ILLEGAL) { + + get_token(); + if (instr_args(&arg, &mode)) { + + // Convert A_IMPL -> A_ACCU if necessary + if ((mode == A_IMPL) && find_opcode(mnem, A_ACCU, &opcode)) + mode = A_ACCU; + + // Handle relative addressing seperately + if (((mode == A_ABS) || (mode == A_ZERO)) && find_opcode(mnem, A_REL, &opcode)) { + mode = A_REL; + rel = arg - (address + 2) & 0xffff; + if ((rel < -128) || (rel > 127)) { + error("Branch too long"); + continue; + } else + arg = rel & 0xff; + } + + if (find_opcode(mnem, mode, &opcode)) { + + // Print disassembled line + fprintf(fout, "\v%04lx:", address); + disass_line(address, opcode, arg & 0xff, arg >> 8); + + switch (adr_length[mode]) { + case 1: + SAMWriteByte(address++, opcode); + break; + + case 2: + SAMWriteByte(address++, opcode); + SAMWriteByte(address++, arg); + break; + + case 3: + SAMWriteByte(address++, opcode); + SAMWriteByte(address++, arg & 0xff); + SAMWriteByte(address++, arg >> 8); + break; + + default: + error("Internal error"); + break; + } + + } else + error("Addressing mode not supported by instruction"); + + } else + error("Unrecognized addressing mode"); + + } else + error("Unknown instruction"); + + } else // Input is terminated with a blank line + done = true; + } while (!done); +} + + +/* + * Find mnemonic code to three letters + * M_ILLEGAL: No matching mnemonic found + */ + +static char find_mnemonic(char op1, char op2, char op3) +{ + int i; + + for (i=0; iExtConfig = con; + else + con = TheCPU->ExtConfig; + + fprintf(fout, "Configuration: %ld\n", con & 7); + fprintf(fout, "A000-BFFF: %s\n", (con & 3) == 3 ? "Basic" : "RAM"); + fprintf(fout, "D000-DFFF: %s\n", (con & 3) ? ((con & 4) ? "I/O" : "Char") : "RAM"); + fprintf(fout, "E000-FFFF: %s\n", (con & 2) ? "Kernal" : "RAM"); +} + + +/* + * Fill + * f start end byte + */ + +static void fill(void) +{ + bool done = false; + uint16 adr, end_adr, value; + + if (!expression(&adr)) + return; + if (!expression(&end_adr)) + return; + if (!expression(&value)) + return; + + do { + if (adr == end_adr) done = true; + + SAMWriteByte(adr++, value); + } while (!done); +} + + +/* + * Compare + * c start end dest + */ + +static void compare(void) +{ + bool done = false; + uint16 adr, end_adr, dest; + int num = 0; + + if (!expression(&adr)) + return; + if (!expression(&end_adr)) + return; + if (!expression(&dest)) + return; + + do { + if (adr == end_adr) done = true; + + if (SAMReadByte(adr) != SAMReadByte(dest)) { + fprintf(fout, "%04lx ", adr); + num++; + if (!(num & 7)) + fputc('\n', fout); + } + adr++; dest++; + } while (!done && !aborted()); + + if (num & 7) + fputc('\n', fout); + fprintf(fout, "%ld byte(s) different\n", num); +} + + +/* + * Transfer memory + * t start end dest + */ + +static void transfer(void) +{ + bool done = false; + uint16 adr, end_adr, dest; + + if (!expression(&adr)) + return; + if (!expression(&end_adr)) + return; + if (!expression(&dest)) + return; + + if (dest < adr) + do { + if (adr == end_adr) done = true; + SAMWriteByte(dest++, SAMReadByte(adr++)); + } while (!done); + else { + dest += end_adr - adr; + do { + if (adr == end_adr) done = true; + SAMWriteByte(dest--, SAMReadByte(end_adr--)); + } while (!done); + } +} + + +/* + * Change memory + * : addr {byte} + */ + +static void modify(void) +{ + uint16 adr, val; + + if (!expression(&adr)) + return; + + while (the_token != T_END) + if (expression(&val)) + SAMWriteByte(adr++, val); + else + return; +} + + +/* + * Compute and display expression + * ? expression + */ + +static void print_expr(void) +{ + uint16 val; + + if (!expression(&val)) + return; + + fprintf(fout, "Hex: %04lx\nDec: %lu\n", val, val); +} + + +/* + * Redirect output + * o [file] + */ + +static void redir_output(void) +{ + // Close old file + if (fout != ferr) { + fclose(fout); + fout = ferr; + return; + } + + // No argument given? + if (the_token == T_END) + return; + + // Otherwise open file + if (the_token == T_STRING) { + if (!(fout = fopen(the_string, "w"))) + error("Unable to open file"); + } else + error("'\"' around file name expected"); +} + + +/* + * Display interrupt vectors + */ + +static void int_vectors(void) +{ + fprintf(fout, " IRQ BRK NMI\n"); + fprintf(fout, "%d : %04lx %04lx %04lx\n", + access_1541 ? 6502 : 6510, + SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe), + SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe), + SAMReadByte(0xfffb) << 8 | SAMReadByte(0xfffa)); + + if (!access_1541 && TheCPU->ExtConfig & 2) + fprintf(fout, "Kernal: %04lx %04lx %04lx\n", + SAMReadByte(0x0315) << 8 | SAMReadByte(0x0314), + SAMReadByte(0x0317) << 8 | SAMReadByte(0x0316), + SAMReadByte(0x0319) << 8 | SAMReadByte(0x0318)); +} + + +/* + * Display state of custom chips + */ + +static void view_state(void) +{ + switch (get_char()) { + case 'c': // CIA + view_cia_state(); + break; + + case 's': // SID + view_sid_state(); + break; + + case 'v': // VIC + view_vic_state(); + break; + + case 'f': // Floppy + view_1541_state(); + break; + + default: + error("Unknown command"); + break; + } +} + +static void view_cia_state(void) +{ + MOS6526State cs; + + switch (get_char()) { + case '1': + TheCIA1->GetState(&cs); + break; + case '2': + TheCIA2->GetState(&cs); + break; + default: + error("Unknown command"); + return; + } + + fprintf(fout, "Timer A : %s\n", cs.cra & 1 ? "On" : "Off"); + fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.ta_hi << 8) | cs.ta_lo, cs.latcha); + fprintf(fout, " Run mode: %s\n", cs.cra & 8 ? "One-shot" : "Continuous"); + fprintf(fout, " Input : %s\n", cs.cra & 0x20 ? "CNT" : "Phi2"); + fprintf(fout, " Output : "); + if (cs.cra & 2) + if (cs.cra & 4) + fprintf(fout, "PB6 Toggle\n\n"); + else + fprintf(fout, "PB6 Pulse\n\n"); + else + fprintf(fout, "None\n\n"); + + fprintf(fout, "Timer B : %s\n", cs.crb & 1 ? "On" : "Off"); + fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.tb_hi << 8) | cs.tb_lo, cs.latchb); + fprintf(fout, " Run mode: %s\n", cs.crb & 8 ? "One-shot" : "Continuous"); + fprintf(fout, " Input : "); + if (cs.crb & 0x40) + if (cs.crb & 0x20) + fprintf(fout, "Timer A underflow (CNT high)\n"); + else + fprintf(fout, "Timer A underflow\n"); + else + if (cs.crb & 0x20) + fprintf(fout, "CNT\n"); + else + fprintf(fout, "Phi2\n"); + fprintf(fout, " Output : "); + if (cs.crb & 2) + if (cs.crb & 4) + fprintf(fout, "PB7 Toggle\n\n"); + else + fprintf(fout, "PB7 Pulse\n\n"); + else + fprintf(fout, "None\n\n"); + + fprintf(fout, "TOD : %lx%lx:%lx%lx:%lx%lx.%lx %s\n", + (cs.tod_hr >> 4) & 1, cs.tod_hr & 0x0f, + (cs.tod_min >> 4) & 7, cs.tod_min & 0x0f, + (cs.tod_sec >> 4) & 7, cs.tod_sec & 0x0f, + cs.tod_10ths & 0x0f, cs.tod_hr & 0x80 ? "PM" : "AM"); + fprintf(fout, "Alarm : %lx%lx:%lx%lx:%lx%lx.%lx %s\n", + (cs.alm_hr >> 4) & 1, cs.alm_hr & 0x0f, + (cs.alm_min >> 4) & 7, cs.alm_min & 0x0f, + (cs.alm_sec >> 4) & 7, cs.alm_sec & 0x0f, + cs.alm_10ths & 0x0f, cs.alm_hr & 0x80 ? "PM" : "AM"); + fprintf(fout, "TOD input : %s\n", cs.cra & 0x80 ? "50Hz" : "60Hz"); + fprintf(fout, "Write to : %s registers\n\n", cs.crb & 0x80 ? "Alarm" : "TOD"); + + fprintf(fout, "Serial data : %02lx\n", cs.sdr); + fprintf(fout, "Serial mode : %s\n\n", cs.cra & 0x40 ? "Output" : "Input"); + + fprintf(fout, "Pending int.: "); + dump_cia_ints(cs.int_data); + fprintf(fout, "Enabled int.: "); + dump_cia_ints(cs.int_mask); +} + +static void dump_cia_ints(uint8 i) +{ + if (i & 0x1f) { + if (i & 1) fprintf(fout, "TA "); + if (i & 2) fprintf(fout, "TB "); + if (i & 4) fprintf(fout, "Alarm "); + if (i & 8) fprintf(fout, "Serial "); + if (i & 0x10) fprintf(fout, "Flag"); + } else + fprintf(fout, "None"); + fputc('\n', fout); +} + +static void view_sid_state(void) +{ + MOS6581State ss; + + TheSID->GetState(&ss); + + fprintf(fout, "Voice 1\n"); + fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_1 << 8) | ss.freq_lo_1); + fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_1 & 0x0f) << 8) | ss.pw_lo_1); + fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_1 >> 4, ss.AD_1 & 0x0f, ss.SR_1 >> 4, ss.SR_1 & 0x0f); + fprintf(fout, " Waveform : "); + dump_sid_waveform(ss.ctrl_1); + fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_1 & 0x01 ? "On " : "Off", ss.ctrl_1 & 0x04 ? "On" : "Off"); + fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_1 & 0x08 ? "On " : "Off", ss.ctrl_1 & 0x02 ? "On" : "Off"); + fprintf(fout, " Filter : %s\n", ss.res_filt & 0x01 ? "On" : "Off"); + + fprintf(fout, "\nVoice 2\n"); + fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_2 << 8) | ss.freq_lo_2); + fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_2 & 0x0f) << 8) | ss.pw_lo_2); + fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_2 >> 4, ss.AD_2 & 0x0f, ss.SR_2 >> 4, ss.SR_2 & 0x0f); + fprintf(fout, " Waveform : "); + dump_sid_waveform(ss.ctrl_2); + fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_2 & 0x01 ? "On " : "Off", ss.ctrl_2 & 0x04 ? "On" : "Off"); + fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_2 & 0x08 ? "On " : "Off", ss.ctrl_2 & 0x02 ? "On" : "Off"); + fprintf(fout, " Filter : %s\n", ss.res_filt & 0x02 ? "On" : "Off"); + + fprintf(fout, "\nVoice 3\n"); + fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_3 << 8) | ss.freq_lo_3); + fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_3 & 0x0f) << 8) | ss.pw_lo_3); + fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_3 >> 4, ss.AD_3 & 0x0f, ss.SR_3 >> 4, ss.SR_3 & 0x0f); + fprintf(fout, " Waveform : "); + dump_sid_waveform(ss.ctrl_3); + fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_3 & 0x01 ? "On " : "Off", ss.ctrl_3 & 0x04 ? "On" : "Off"); + fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_3 & 0x08 ? "On " : "Off", ss.ctrl_3 & 0x02 ? "On" : "Off"); + fprintf(fout, " Filter : %s Mute : %s\n", ss.res_filt & 0x04 ? "On" : "Off", ss.mode_vol & 0x80 ? "Yes" : "No"); + + fprintf(fout, "\nFilters/Volume\n"); + fprintf(fout, " Frequency: %04lx\n", (ss.fc_hi << 3) | (ss.fc_lo & 0x07)); + fprintf(fout, " Resonance: %lx\n", ss.res_filt >> 4); + fprintf(fout, " Mode : "); + if (ss.mode_vol & 0x70) { + if (ss.mode_vol & 0x10) fprintf(fout, "Low-pass "); + if (ss.mode_vol & 0x20) fprintf(fout, "Band-pass "); + if (ss.mode_vol & 0x40) fprintf(fout, "High-pass"); + } else + fprintf(fout, "None"); + fprintf(fout, "\n Volume : %lx\n", ss.mode_vol & 0x0f); +} + +static void dump_sid_waveform(uint8 wave) +{ + if (wave & 0xf0) { + if (wave & 0x10) fprintf(fout, "Triangle "); + if (wave & 0x20) fprintf(fout, "Sawtooth "); + if (wave & 0x40) fprintf(fout, "Rectangle "); + if (wave & 0x80) fprintf(fout, "Noise"); + } else + fprintf(fout, "None"); + fputc('\n', fout); +} + +static void view_vic_state(void) +{ + MOS6569State vs; + short i; + + TheVIC->GetState(&vs); + + fprintf(fout, "Raster line : %04lx\n", vs.raster | ((vs.ctrl1 & 0x80) << 1)); + fprintf(fout, "IRQ raster line : %04lx\n\n", vs.irq_raster); + + fprintf(fout, "X scroll : %ld\n", vs.ctrl2 & 7); + fprintf(fout, "Y scroll : %ld\n", vs.ctrl1 & 7); + fprintf(fout, "Horizontal border : %ld columns\n", vs.ctrl2 & 8 ? 40 : 38); + fprintf(fout, "Vertical border : %ld rows\n\n", vs.ctrl1 & 8 ? 25 : 24); + + fprintf(fout, "Display mode : "); + switch (((vs.ctrl1 >> 4) & 6) | ((vs.ctrl2 >> 4) & 1)) { + case 0: + fprintf(fout, "Standard text\n"); + break; + case 1: + fprintf(fout, "Multicolor text\n"); + break; + case 2: + fprintf(fout, "Standard bitmap\n"); + break; + case 3: + fprintf(fout, "Multicolor bitmap\n"); + break; + case 4: + fprintf(fout, "ECM text\n"); + break; + case 5: + fprintf(fout, "Invalid text (ECM+MCM)\n"); + break; + case 6: + fprintf(fout, "Invalid bitmap (ECM+BMM)\n"); + break; + case 7: + fprintf(fout, "Invalid bitmap (ECM+BMM+ECM)\n"); + break; + } + fprintf(fout, "Sequencer state : %s\n", vs.display_state ? "Display" : "Idle"); + fprintf(fout, "Bad line state : %s\n", vs.bad_line ? "Yes" : "No"); + fprintf(fout, "Bad lines enabled : %s\n", vs.bad_line_enable ? "Yes" : "No"); + fprintf(fout, "Video counter : %04lx\n", vs.vc); + fprintf(fout, "Video counter base: %04lx\n", vs.vc_base); + fprintf(fout, "Row counter : %ld\n\n", vs.rc); + + fprintf(fout, "VIC bank : %04lx-%04lx\n", vs.bank_base, vs.bank_base + 0x3fff); + fprintf(fout, "Video matrix base : %04lx\n", vs.matrix_base); + fprintf(fout, "Character base : %04lx\n", vs.char_base); + fprintf(fout, "Bitmap base : %04lx\n\n", vs.bitmap_base); + + fprintf(fout, " Spr.0 Spr.1 Spr.2 Spr.3 Spr.4 Spr.5 Spr.6 Spr.7\n"); + fprintf(fout, "Enabled: "); dump_spr_flags(vs.me); + fprintf(fout, "Data : %04lx %04lx %04lx %04lx %04lx %04lx %04lx %04lx\n", + vs.sprite_base[0], vs.sprite_base[1], vs.sprite_base[2], vs.sprite_base[3], + vs.sprite_base[4], vs.sprite_base[5], vs.sprite_base[6], vs.sprite_base[7]); + fprintf(fout, "MC : %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx\n", + vs.mc[0], vs.mc[1], vs.mc[2], vs.mc[3], vs.mc[4], vs.mc[5], vs.mc[6], vs.mc[7]); + + fprintf(fout, "Mode : "); + for (i=0; i<8; i++) + if (vs.mmc & (1< + +#include "SID.h" +#include "Prefs.h" + +#ifdef __BEOS__ +#include +#endif + +#ifdef AMIGA +#include +#include +#include +#define USE_FIXPOINT_MATHS +#define FIXPOINT_PREC 16 // number of fractional bits used in fixpoint representation +#define PRECOMPUTE_RESONANCE +#define ldSINTAB 9 // size of sinus table (0 to 90 degrees) +#endif + +#ifdef SUN +extern "C" { + #include +} +#endif + +#ifdef __hpux +extern "C" { + #include +} +#endif + +#ifdef __mac__ +#include +#define M_PI 3.14159265358979323846 +#endif + +#ifdef WIN32 +class DigitalPlayer; +#endif + +#ifdef __riscos__ +#include "ROLib.h" +# ifndef M_PI +# define M_PI 3.14159265358979323846 +# endif +#define USE_FIXPOINT_MATHS +#define FIXPOINT_PREC 16 // number of fractional bits used in fixpoint representation +#define PRECOMPUTE_RESONANCE +#define ldSINTAB 9 // size of sinus table (0 to 90 degrees) +#endif + + +#ifdef USE_FIXPOINT_MATHS +#include "FixPoint.i" +#endif + + +/* + * Resonance frequency polynomials + */ + +#define CALC_RESONANCE_LP(f) (227.755\ + - 1.7635 * f\ + - 0.0176385 * f * f\ + + 0.00333484 * f * f * f\ + - 9.05683E-6 * f * f * f * f) + +#define CALC_RESONANCE_HP(f) (366.374\ + - 14.0052 * f\ + + 0.603212 * f * f\ + - 0.000880196 * f * f * f) + + +/* + * Random number generator for noise waveform + */ + +static uint8 sid_random(void); +static uint8 sid_random(void) +{ + static uint32 seed = 1; + seed = seed * 1103515245 + 12345; + return seed >> 16; +} + + +/* + * Constructor + */ + +MOS6581::MOS6581(C64 *c64) : the_c64(c64) +{ + the_renderer = NULL; + for (int i=0; i<32; i++) + regs[i] = 0; + + // Open the renderer + open_close_renderer(SIDTYPE_NONE, ThePrefs.SIDType); +} + + +/* + * Destructor + */ + +MOS6581::~MOS6581() +{ + // Close the renderer + open_close_renderer(ThePrefs.SIDType, SIDTYPE_NONE); +} + + +/* + * Reset the SID + */ + +void MOS6581::Reset(void) +{ + for (int i=0; i<32; i++) + regs[i] = 0; + last_sid_byte = 0; + + // Reset the renderer + if (the_renderer != NULL) + the_renderer->Reset(); +} + + +/* + * Preferences may have changed + */ + +void MOS6581::NewPrefs(Prefs *prefs) +{ + open_close_renderer(ThePrefs.SIDType, prefs->SIDType); + if (the_renderer != NULL) + the_renderer->NewPrefs(prefs); +} + + +/* + * Pause sound output + */ + +void MOS6581::PauseSound(void) +{ + if (the_renderer != NULL) + the_renderer->Pause(); +} + + +/* + * Resume sound output + */ + +void MOS6581::ResumeSound(void) +{ + if (the_renderer != NULL) + the_renderer->Resume(); +} + + +/* + * Get SID state + */ + +void MOS6581::GetState(MOS6581State *ss) +{ + ss->freq_lo_1 = regs[0]; + ss->freq_hi_1 = regs[1]; + ss->pw_lo_1 = regs[2]; + ss->pw_hi_1 = regs[3]; + ss->ctrl_1 = regs[4]; + ss->AD_1 = regs[5]; + ss->SR_1 = regs[6]; + + ss->freq_lo_2 = regs[7]; + ss->freq_hi_2 = regs[8]; + ss->pw_lo_2 = regs[9]; + ss->pw_hi_2 = regs[10]; + ss->ctrl_2 = regs[11]; + ss->AD_2 = regs[12]; + ss->SR_2 = regs[13]; + + ss->freq_lo_3 = regs[14]; + ss->freq_hi_3 = regs[15]; + ss->pw_lo_3 = regs[16]; + ss->pw_hi_3 = regs[17]; + ss->ctrl_3 = regs[18]; + ss->AD_3 = regs[19]; + ss->SR_3 = regs[20]; + + ss->fc_lo = regs[21]; + ss->fc_hi = regs[22]; + ss->res_filt = regs[23]; + ss->mode_vol = regs[24]; + + ss->pot_x = 0xff; + ss->pot_y = 0xff; + ss->osc_3 = 0; + ss->env_3 = 0; +} + + +/* + * Restore SID state + */ + +void MOS6581::SetState(MOS6581State *ss) +{ + regs[0] = ss->freq_lo_1; + regs[1] = ss->freq_hi_1; + regs[2] = ss->pw_lo_1; + regs[3] = ss->pw_hi_1; + regs[4] = ss->ctrl_1; + regs[5] = ss->AD_1; + regs[6] = ss->SR_1; + + regs[7] = ss->freq_lo_2; + regs[8] = ss->freq_hi_2; + regs[9] = ss->pw_lo_2; + regs[10] = ss->pw_hi_2; + regs[11] = ss->ctrl_2; + regs[12] = ss->AD_2; + regs[13] = ss->SR_2; + + regs[14] = ss->freq_lo_3; + regs[15] = ss->freq_hi_3; + regs[16] = ss->pw_lo_3; + regs[17] = ss->pw_hi_3; + regs[18] = ss->ctrl_3; + regs[19] = ss->AD_3; + regs[20] = ss->SR_3; + + regs[21] = ss->fc_lo; + regs[22] = ss->fc_hi; + regs[23] = ss->res_filt; + regs[24] = ss->mode_vol; + + // Stuff the new register values into the renderer + if (the_renderer != NULL) + for (int i=0; i<25; i++) + the_renderer->WriteRegister(i, regs[i]); +} + + +/** + ** Renderer for digital SID emulation (SIDTYPE_DIGITAL) + **/ + +#if defined(AMIGA) || defined(__riscos__) +const uint32 SAMPLE_FREQ = 22050; // Sample output frequency in Hz +#else +const uint32 SAMPLE_FREQ = 44100; // Sample output frequency in Hz +#endif +const uint32 SID_FREQ = 985248; // SID frequency in Hz +const uint32 CALC_FREQ = 50; // Frequency at which calc_buffer is called in Hz (should be 50Hz) +const uint32 SID_CYCLES = SID_FREQ/SAMPLE_FREQ; // # of SID clocks per sample frame +const int SAMPLE_BUF_SIZE = 0x138*2;// Size of buffer for sampled voice (double buffered) + +// SID waveforms (some of them :-) +enum { + WAVE_NONE, + WAVE_TRI, + WAVE_SAW, + WAVE_TRISAW, + WAVE_RECT, + WAVE_TRIRECT, + WAVE_SAWRECT, + WAVE_TRISAWRECT, + WAVE_NOISE +}; + +// EG states +enum { + EG_IDLE, + EG_ATTACK, + EG_DECAY, + EG_RELEASE +}; + +// Filter types +enum { + FILT_NONE, + FILT_LP, + FILT_BP, + FILT_LPBP, + FILT_HP, + FILT_NOTCH, + FILT_HPBP, + FILT_ALL +}; + +// Structure for one voice +struct DRVoice { + int wave; // Selected waveform + int eg_state; // Current state of EG + DRVoice *mod_by; // Voice that modulates this one + DRVoice *mod_to; // Voice that is modulated by this one + + uint32 count; // Counter for waveform generator, 8.16 fixed + uint32 add; // Added to counter in every frame + + uint16 freq; // SID frequency value + uint16 pw; // SID pulse-width value + + uint32 a_add; // EG parameters + uint32 d_sub; + uint32 s_level; + uint32 r_sub; + uint32 eg_level; // Current EG level, 8.16 fixed + + uint32 noise; // Last noise generator output value + + bool gate; // EG gate bit + bool ring; // Ring modulation bit + bool test; // Test bit + bool filter; // Flag: Voice filtered + + // The following bit is set for the modulating + // voice, not for the modulated one (as the SID bits) + bool sync; // Sync modulation bit +}; + +// Renderer class +class DigitalRenderer : public SIDRenderer { +public: +#if defined(__BEOS__) || defined(__riscos__) + DigitalRenderer(C64 *c64); +#else + DigitalRenderer(); +#endif + virtual ~DigitalRenderer(); + + virtual void Reset(void); + virtual void EmulateLine(void); + virtual void WriteRegister(uint16 adr, uint8 byte); + virtual void NewPrefs(Prefs *prefs); + virtual void Pause(void); + virtual void Resume(void); + +private: + void init_sound(void); + void calc_filter(void); +#ifdef __riscos__ + void calc_buffer(uint8 *buf, long count); +#else + void calc_buffer(int16 *buf, long count); +#endif + + bool ready; // Flag: Renderer has initialized and is ready + uint8 volume; // Master volume + bool v3_mute; // Voice 3 muted + + static uint16 TriTable[0x1000*2]; // Tables for certain waveforms + static const uint16 TriSawTable[0x100]; + static const uint16 TriRectTable[0x100]; + static const uint16 SawRectTable[0x100]; + static const uint16 TriSawRectTable[0x100]; + static const uint32 EGTable[16]; // Increment/decrement values for all A/D/R settings + static const uint8 EGDRShift[256]; // For exponential approximation of D/R + static const int16 SampleTab[16]; // Table for sampled voice + + DRVoice voice[3]; // Data for 3 voices + + uint8 f_type; // Filter type + uint8 f_freq; // SID filter frequency (upper 8 bits) + uint8 f_res; // Filter resonance (0..15) +#ifdef USE_FIXPOINT_MATHS + FixPoint f_ampl; + FixPoint d1, d2, g1, g2; + int32 xn1, xn2, yn1, yn2; // can become very large + FixPoint sidquot; +#ifdef PRECOMPUTE_RESONANCE + FixPoint resonanceLP[256]; + FixPoint resonanceHP[256]; +#endif +#else + float f_ampl; // IIR filter input attenuation + float d1, d2, g1, g2; // IIR filter coefficients + float xn1, xn2, yn1, yn2; // IIR filter previous input/output signal +#ifdef PRECOMPUTE_RESONANCE + float resonanceLP[256]; // shortcut for calc_filter + float resonanceHP[256]; +#endif +#endif + + uint8 sample_buf[SAMPLE_BUF_SIZE]; // Buffer for sampled voice + int sample_in_ptr; // Index in sample_buf for writing + +#ifdef __BEOS__ + static bool stream_func(void *arg, char *buf, size_t count, void *header); + C64 *the_c64; // Pointer to C64 object + BDACStream *the_stream; // Pointer to stream + BSubscriber *the_sub; // Pointer to subscriber + bool in_stream; // Flag: Subscriber has entered stream +#endif + +#ifdef AMIGA + static void sub_invoc(void); // Sound sub-process + void sub_func(void); + struct Process *sound_process; + int quit_sig, pause_sig, + resume_sig, ahi_sig; // Sub-process signals + struct Task *main_task; // Main task + int main_sig; // Main task signals + static ULONG sound_func(void); // AHI callback + struct MsgPort *ahi_port; // Port and IORequest for AHI + struct AHIRequest *ahi_io; + struct AHIAudioCtrl *ahi_ctrl; // AHI control structure + struct AHISampleInfo sample[2]; // SampleInfos for double buffering + struct Hook sf_hook; // Hook for callback function + int play_buf; // Number of buffer currently playing +#endif + +#ifdef __linux__ + int devfd, sndbufsize, buffer_rate; + int16 *sound_buffer; +#endif + +#ifdef SUN + int fd; + audio_info status; + uint_t sent_samples,delta_samples; + WORD *sound_calc_buf; +#endif + +#ifdef __hpux + int fd; + audio_status status; + int16 * sound_calc_buf; + int linecnt; +#endif + +#ifdef __mac__ + SndChannelPtr chan1; + SndDoubleBufferHeader myDblHeader; + SndDoubleBufferPtr sampleBuffer1, sampleBuffer2; + SCStatus myStatus; + short sndbufsize; + OSErr err; + + static void doubleBackProc(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer); +#endif + +#ifdef WIN32 +public: + void VBlank(void); + +private: + void StartPlayer(void); + void StopPlayer(void); + + BOOL direct_sound; + DigitalPlayer *ThePlayer; + SWORD *sound_buffer; + int to_output; + int sb_pos; + int divisor; + int *lead; + int lead_pos; +#endif + +#ifdef __riscos__ + int linecnt, sndbufsize; + uint8 *sound_buffer; + C64 *the_c64; +#endif +}; + +// Static data members +uint16 DigitalRenderer::TriTable[0x1000*2]; + +#ifndef EMUL_MOS8580 +// Sampled from a 6581R4 +const uint16 DigitalRenderer::TriSawTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C +}; + +const uint16 DigitalRenderer::TriRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0xC0C0, + 0x0000, 0x8080, 0x8080, 0xE0E0, 0x8080, 0xE0E0, 0xF0F0, 0xFCFC, + 0xFFFF, 0xFCFC, 0xFAFA, 0xF0F0, 0xF6F6, 0xE0E0, 0xE0E0, 0x8080, + 0xEEEE, 0xE0E0, 0xE0E0, 0x8080, 0xC0C0, 0x0000, 0x0000, 0x0000, + 0xDEDE, 0xC0C0, 0xC0C0, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000, + 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xBEBE, 0x8080, 0x8080, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000, + 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7E7E, 0x4040, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +const uint16 DigitalRenderer::SawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878 +}; + +const uint16 DigitalRenderer::TriSawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; +#else +// Sampled from an 8580R5 +const uint16 DigitalRenderer::TriSawTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1818, 0x3C3C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1C1C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x0000, 0x8080, 0x8080, + 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, + 0xF0F0, 0xF0F0, 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE +}; + +const uint16 DigitalRenderer::TriRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFFFF, 0xFCFC, 0xF8F8, 0xF0F0, 0xF4F4, 0xF0F0, 0xF0F0, 0xE0E0, + 0xECEC, 0xE0E0, 0xE0E0, 0xC0C0, 0xE0E0, 0xC0C0, 0xC0C0, 0xC0C0, + 0xDCDC, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0x8080, 0x8080, + 0xC0C0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000, + 0xBEBE, 0xA0A0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, + 0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7E7E, 0x7070, 0x6060, 0x0000, 0x4040, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +const uint16 DigitalRenderer::SawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, + 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xB0B0, 0xBEBE, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, + 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, + 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xDCDC, + 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, + 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xECEC, + 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF0F0, 0xF4F4, + 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE, 0xFFFF +}; + +const uint16 DigitalRenderer::TriSawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, + 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, + 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF8F8, 0xFCFC +}; +#endif + +const uint32 DigitalRenderer::EGTable[16] = { + (SID_CYCLES << 16) / 9, (SID_CYCLES << 16) / 32, + (SID_CYCLES << 16) / 63, (SID_CYCLES << 16) / 95, + (SID_CYCLES << 16) / 149, (SID_CYCLES << 16) / 220, + (SID_CYCLES << 16) / 267, (SID_CYCLES << 16) / 313, + (SID_CYCLES << 16) / 392, (SID_CYCLES << 16) / 977, + (SID_CYCLES << 16) / 1954, (SID_CYCLES << 16) / 3126, + (SID_CYCLES << 16) / 3906, (SID_CYCLES << 16) / 11720, + (SID_CYCLES << 16) / 19531, (SID_CYCLES << 16) / 31251 +}; + +const uint8 DigitalRenderer::EGDRShift[256] = { + 5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4, + 3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +const int16 DigitalRenderer::SampleTab[16] = { + 0x8000, 0x9111, 0xa222, 0xb333, 0xc444, 0xd555, 0xe666, 0xf777, + 0x0888, 0x1999, 0x2aaa, 0x3bbb, 0x4ccc, 0x5ddd, 0x6eee, 0x7fff, +}; + + +/* + * Constructor + */ + +#if defined(__BEOS__) || defined(__riscos__) +DigitalRenderer::DigitalRenderer(C64 *c64) : the_c64(c64) +#else +DigitalRenderer::DigitalRenderer() +#endif +{ + // Link voices together + voice[0].mod_by = &voice[2]; + voice[1].mod_by = &voice[0]; + voice[2].mod_by = &voice[1]; + voice[0].mod_to = &voice[1]; + voice[1].mod_to = &voice[2]; + voice[2].mod_to = &voice[0]; + + // Calculate triangle table + for (int i=0; i<0x1000; i++) { + TriTable[i] = (i << 4) | (i >> 8); + TriTable[0x1fff-i] = (i << 4) | (i >> 8); + } + +#ifdef PRECOMPUTE_RESONANCE +#ifdef USE_FIXPOINT_MATHS + // slow floating point doesn't matter much on startup! + for (int i=0; i<256; i++) { + resonanceLP[i] = FixNo(CALC_RESONANCE_LP(i)); + resonanceHP[i] = FixNo(CALC_RESONANCE_HP(i)); + } + // Pre-compute the quotient. No problem since int-part is small enough + sidquot = (int32)((((double)SID_FREQ)*65536) / SAMPLE_FREQ); + // compute lookup table for sin and cos + InitFixSinTab(); +#else + for (int i=0; i<256; i++) { + resonanceLP[i] = CALC_RESONANCE_LP(i); + resonanceHP[i] = CALC_RESONANCE_HP(i); + } +#endif +#endif + + Reset(); + + // System specific initialization + init_sound(); +} + + +/* + * Reset emulation + */ + +void DigitalRenderer::Reset(void) +{ + volume = 0; + v3_mute = false; + + for (int v=0; v<3; v++) { + voice[v].wave = WAVE_NONE; + voice[v].eg_state = EG_IDLE; + voice[v].count = voice[v].add = 0; + voice[v].freq = voice[v].pw = 0; + voice[v].eg_level = voice[v].s_level = 0; + voice[v].a_add = voice[v].d_sub = voice[v].r_sub = EGTable[0]; + voice[v].gate = voice[v].ring = voice[v].test = false; + voice[v].filter = voice[v].sync = false; + } + + f_type = FILT_NONE; + f_freq = f_res = 0; +#ifdef USE_FIXPOINT_MATHS + f_ampl = FixNo(1); + d1 = d2 = g1 = g2 = 0; + xn1 = xn2 = yn1 = yn2 = 0; +#else + f_ampl = 1.0; + d1 = d2 = g1 = g2 = 0.0; + xn1 = xn2 = yn1 = yn2 = 0.0; +#endif + + sample_in_ptr = 0; + memset(sample_buf, 0, SAMPLE_BUF_SIZE); +} + + +/* + * Write to register + */ + +void DigitalRenderer::WriteRegister(uint16 adr, uint8 byte) +{ + if (!ready) + return; + + int v = adr/7; // Voice number + + switch (adr) { + case 0: + case 7: + case 14: + voice[v].freq = (voice[v].freq & 0xff00) | byte; +#ifdef USE_FIXPOINT_MATHS + voice[v].add = sidquot.imul((int)voice[v].freq); +#else + voice[v].add = (uint32)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ); +#endif + break; + + case 1: + case 8: + case 15: + voice[v].freq = (voice[v].freq & 0xff) | (byte << 8); +#ifdef USE_FIXPOINT_MATHS + voice[v].add = sidquot.imul((int)voice[v].freq); +#else + voice[v].add = (uint32)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ); +#endif + break; + + case 2: + case 9: + case 16: + voice[v].pw = (voice[v].pw & 0x0f00) | byte; + break; + + case 3: + case 10: + case 17: + voice[v].pw = (voice[v].pw & 0xff) | ((byte & 0xf) << 8); + break; + + case 4: + case 11: + case 18: + voice[v].wave = (byte >> 4) & 0xf; + if ((byte & 1) != voice[v].gate) + if (byte & 1) // Gate turned on + voice[v].eg_state = EG_ATTACK; + else // Gate turned off + if (voice[v].eg_state != EG_IDLE) + voice[v].eg_state = EG_RELEASE; + voice[v].gate = byte & 1; + voice[v].mod_by->sync = byte & 2; + voice[v].ring = byte & 4; + if ((voice[v].test = byte & 8)) + voice[v].count = 0; + break; + + case 5: + case 12: + case 19: + voice[v].a_add = EGTable[byte >> 4]; + voice[v].d_sub = EGTable[byte & 0xf]; + break; + + case 6: + case 13: + case 20: + voice[v].s_level = (byte >> 4) * 0x111111; + voice[v].r_sub = EGTable[byte & 0xf]; + break; + + case 22: + if (byte != f_freq) { + f_freq = byte; + if (ThePrefs.SIDFilters) + calc_filter(); + } + break; + + case 23: + voice[0].filter = byte & 1; + voice[1].filter = byte & 2; + voice[2].filter = byte & 4; + if ((byte >> 4) != f_res) { + f_res = byte >> 4; + if (ThePrefs.SIDFilters) + calc_filter(); + } + break; + + case 24: + volume = byte & 0xf; + v3_mute = byte & 0x80; + if (((byte >> 4) & 7) != f_type) { + f_type = (byte >> 4) & 7; +#ifdef USE_FIXPOINT_MATHS + xn1 = xn2 = yn1 = yn2 = 0; +#else + xn1 = xn2 = yn1 = yn2 = 0.0; +#endif + if (ThePrefs.SIDFilters) + calc_filter(); + } + break; + } +} + + +/* + * Preferences may have changed + */ + +void DigitalRenderer::NewPrefs(Prefs *prefs) +{ + calc_filter(); +} + + +/* + * Calculate IIR filter coefficients + */ + +void DigitalRenderer::calc_filter(void) +{ +#ifdef USE_FIXPOINT_MATHS + FixPoint fr, arg; + + if (f_type == FILT_ALL) + { + d1 = 0; d2 = 0; g1 = 0; g2 = 0; f_ampl = FixNo(1); return; + } + else if (f_type == FILT_NONE) + { + d1 = 0; d2 = 0; g1 = 0; g2 = 0; f_ampl = 0; return; + } +#else + float fr, arg; + + // Check for some trivial cases + if (f_type == FILT_ALL) { + d1 = 0.0; d2 = 0.0; + g1 = 0.0; g2 = 0.0; + f_ampl = 1.0; + return; + } else if (f_type == FILT_NONE) { + d1 = 0.0; d2 = 0.0; + g1 = 0.0; g2 = 0.0; + f_ampl = 0.0; + return; + } +#endif + + // Calculate resonance frequency + if (f_type == FILT_LP || f_type == FILT_LPBP) +#ifdef PRECOMPUTE_RESONANCE + fr = resonanceLP[f_freq]; +#else + fr = CALC_RESONANCE_LP(f_freq); +#endif + else +#ifdef PRECOMPUTE_RESONANCE + fr = resonanceHP[f_freq]; +#else + fr = CALC_RESONANCE_HP(f_freq); +#endif + +#ifdef USE_FIXPOINT_MATHS + // explanations see below. + arg = fr / (SAMPLE_FREQ >> 1); + if (arg > FixNo(0.99)) {arg = FixNo(0.99);} + if (arg < FixNo(0.01)) {arg = FixNo(0.01);} + + g2 = FixNo(0.55) + FixNo(1.2) * arg * (arg - 1) + FixNo(0.0133333333) * f_res; + g1 = FixNo(-2) * g2.sqrt() * fixcos(arg); + + if (f_type == FILT_LPBP || f_type == FILT_HPBP) {g2 += FixNo(0.1);} + + if (g1.abs() >= g2 + 1) + { + if (g1 > 0) {g1 = g2 + FixNo(0.99);} + else {g1 = -(g2 + FixNo(0.99));} + } + + switch (f_type) + { + case FILT_LPBP: + case FILT_LP: + d1 = FixNo(2); d2 = FixNo(1); f_ampl = FixNo(0.25) * (1 + g1 + g2); break; + case FILT_HPBP: + case FILT_HP: + d1 = FixNo(-2); d2 = FixNo(1); f_ampl = FixNo(0.25) * (1 - g1 + g2); break; + case FILT_BP: + d1 = 0; d2 = FixNo(-1); + f_ampl = FixNo(0.25) * (1 + g1 + g2) * (1 + fixcos(arg)) / fixsin(arg); + break; + case FILT_NOTCH: + d1 = FixNo(-2) * fixcos(arg); d2 = FixNo(1); + f_ampl = FixNo(0.25) * (1 + g1 + g2) * (1 + fixcos(arg)) / fixsin(arg); + break; + default: break; + } + +#else + + // Limit to <1/2 sample frequency, avoid div by 0 in case FILT_BP below + arg = fr / (float)(SAMPLE_FREQ >> 1); + if (arg > 0.99) + arg = 0.99; + if (arg < 0.01) + arg = 0.01; + + // Calculate poles (resonance frequency and resonance) + g2 = 0.55 + 1.2 * arg * arg - 1.2 * arg + (float)f_res * 0.0133333333; + g1 = -2.0 * sqrt(g2) * cos(M_PI * arg); + + // Increase resonance if LP/HP combined with BP + if (f_type == FILT_LPBP || f_type == FILT_HPBP) + g2 += 0.1; + + // Stabilize filter + if (fabs(g1) >= g2 + 1.0) + if (g1 > 0.0) + g1 = g2 + 0.99; + else + g1 = -(g2 + 0.99); + + // Calculate roots (filter characteristic) and input attenuation + switch (f_type) { + + case FILT_LPBP: + case FILT_LP: + d1 = 2.0; d2 = 1.0; + f_ampl = 0.25 * (1.0 + g1 + g2); + break; + + case FILT_HPBP: + case FILT_HP: + d1 = -2.0; d2 = 1.0; + f_ampl = 0.25 * (1.0 - g1 + g2); + break; + + case FILT_BP: + d1 = 0.0; d2 = -1.0; + f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / sin(M_PI * arg); + break; + + case FILT_NOTCH: + d1 = -2.0 * cos(M_PI * arg); d2 = 1.0; + f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / (sin(M_PI * arg)); + break; + + default: + break; + } +#endif +} + + +/* + * Fill one audio buffer with calculated SID sound + */ + +#ifdef __riscos__ +void DigitalRenderer::calc_buffer(uint8 *buf, long count) +#else +void DigitalRenderer::calc_buffer(int16 *buf, long count) +#endif +{ + // Get filter coefficients, so the emulator won't change + // them in the middle of our calculations +#ifdef USE_FIXPOINT_MATHS + FixPoint cf_ampl = f_ampl; + FixPoint cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2; +#else + float cf_ampl = f_ampl; + float cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2; +#endif + +#ifdef __riscos__ + uint8 *LinToLog, *LogScale; +#endif + + // Index in sample_buf for reading, 16.16 fixed + uint32 sample_count = (sample_in_ptr + SAMPLE_BUF_SIZE/2) << 16; + +#ifdef __riscos__ // on RISC OS we have 8 bit logarithmic sound + DigitalRenderer_GetTables(&LinToLog, &LogScale); // get translation tables +#else +#ifdef __BEOS__ + count >>= 2; // 16 bit stereo output, count is in bytes +#else + count >>= 1; // 16 bit mono output, count is in bytes +#endif +#endif + while (count--) { + int32 sum_output; + int32 sum_output_filter = 0; + + // Get current master volume from sample buffer, + // calculate sampled voice + uint8 master_volume = sample_buf[(sample_count >> 16) % SAMPLE_BUF_SIZE]; + sample_count += ((0x138 * 50) << 16) / SAMPLE_FREQ; + sum_output = SampleTab[master_volume] << 8; + + // Loop for all three voices + for (int j=0; j<3; j++) { + DRVoice *v = &voice[j]; + + // Envelope generators + uint16 envelope; + + switch (v->eg_state) { + case EG_ATTACK: + v->eg_level += v->a_add; + if (v->eg_level > 0xffffff) { + v->eg_level = 0xffffff; + v->eg_state = EG_DECAY; + } + break; + case EG_DECAY: + if (v->eg_level <= v->s_level || v->eg_level > 0xffffff) + v->eg_level = v->s_level; + else { + v->eg_level -= v->d_sub >> EGDRShift[v->eg_level >> 16]; + if (v->eg_level <= v->s_level || v->eg_level > 0xffffff) + v->eg_level = v->s_level; + } + break; + case EG_RELEASE: + v->eg_level -= v->r_sub >> EGDRShift[v->eg_level >> 16]; + if (v->eg_level > 0xffffff) { + v->eg_level = 0; + v->eg_state = EG_IDLE; + } + break; + case EG_IDLE: + v->eg_level = 0; + break; + } + envelope = (v->eg_level * master_volume) >> 20; + + // Waveform generator + uint16 output; + + if (!v->test) + v->count += v->add; + + if (v->sync && (v->count > 0x1000000)) + v->mod_to->count = 0; + + v->count &= 0xffffff; + + switch (v->wave) { + case WAVE_TRI: + if (v->ring) + output = TriTable[(v->count ^ (v->mod_by->count & 0x800000)) >> 11]; + else + output = TriTable[v->count >> 11]; + break; + case WAVE_SAW: + output = v->count >> 8; + break; + case WAVE_RECT: + if (v->count > (uint32)(v->pw << 12)) + output = 0xffff; + else + output = 0; + break; + case WAVE_TRISAW: + output = TriSawTable[v->count >> 16]; + break; + case WAVE_TRIRECT: + if (v->count > (uint32)(v->pw << 12)) + output = TriRectTable[v->count >> 16]; + else + output = 0; + break; + case WAVE_SAWRECT: + if (v->count > (uint32)(v->pw << 12)) + output = SawRectTable[v->count >> 16]; + else + output = 0; + break; + case WAVE_TRISAWRECT: + if (v->count > (uint32)(v->pw << 12)) + output = TriSawRectTable[v->count >> 16]; + else + output = 0; + break; + case WAVE_NOISE: + if (v->count > 0x100000) { + output = v->noise = sid_random() << 8; + v->count &= 0xfffff; + } else + output = v->noise; + break; + default: + output = 0x8000; + break; + } + if (v->filter) + sum_output_filter += (int16)(output ^ 0x8000) * envelope; + else + sum_output += (int16)(output ^ 0x8000) * envelope; + } + + // Filter + if (ThePrefs.SIDFilters) { +#ifdef USE_FIXPOINT_MATHS + int32 xn = cf_ampl.imul(sum_output_filter); + int32 yn = xn+cd1.imul(xn1)+cd2.imul(xn2)-cg1.imul(yn1)-cg2.imul(yn2); + yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn; + sum_output_filter = yn; +#else + float xn = (float)sum_output_filter * cf_ampl; + float yn = xn + cd1 * xn1 + cd2 * xn2 - cg1 * yn1 - cg2 * yn2; + yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn; + sum_output_filter = (int32)yn; +#endif + } + + // Write to buffer +#ifdef __BEOS__ + int16 audio_data = (sum_output + sum_output_filter) >> 10; + int val = *buf + audio_data; + if (val > 32767) + val = 32767; + if (val < -32768) + val = -32768; + *buf++ = val; + val = *buf + audio_data; + if (val > 32767) + val = 32767; + if (val < -32768) + val = -32768; + *buf++ = val; +#elif defined(__riscos__) // lookup in 8k (13bit) translation table + *buf++ = LinToLog[((sum_output + sum_output_filter) >> 13) & 0x1fff]; +#else + *buf++ = (sum_output + sum_output_filter) >> 10; +#endif + } +} + + +// Manufacturer independent sound is still just a dream... +#if defined(__BEOS__) +#include "SID_Be.i" + +#elif defined(AMIGA) +#include "SID_Amiga.i" + +#elif defined(__linux__) +#include "SID_linux.i" + +#elif defined(SUN) +#include "SID_sun.i" + +#elif defined(__hpux) +#include "SID_hp.i" + +#elif defined(__mac__) +#include "SID_mac.i" + +#elif defined(WIN32) +#include "SID_WIN32.i" + +#elif defined(__riscos__) +#include "SID_Acorn.i" + +#else // No sound +void DigitalRenderer::init_sound(void) {ready = false;} +DigitalRenderer::~DigitalRenderer() {} +void DigitalRenderer::EmulateLine(void) {} +void DigitalRenderer::Pause(void) {} +void DigitalRenderer::Resume(void) {} +#endif + + +/* + * Open/close the renderer, according to old and new prefs + */ + +void MOS6581::open_close_renderer(int old_type, int new_type) +{ + if (old_type == new_type) + return; + + // Delete the old renderer + delete the_renderer; + + // Create new renderer + if (new_type == SIDTYPE_DIGITAL) +#if defined(__BEOS__) || defined(__riscos__) + the_renderer = new DigitalRenderer(the_c64); +#else + the_renderer = new DigitalRenderer(); +#endif +#ifdef AMIGA + else if (new_type == SIDTYPE_SIDCARD) + the_renderer = new SIDCardRenderer(); +#endif + else + the_renderer = NULL; + + // Stuff the current register values into the new renderer + if (the_renderer != NULL) + for (int i=0; i<25; i++) + the_renderer->WriteRegister(i, regs[i]); +} diff --git a/Src/SID.h b/Src/SID.h new file mode 100644 index 0000000..cd77bf6 --- /dev/null +++ b/Src/SID.h @@ -0,0 +1,147 @@ +/* + * SID.h - 6581 emulation + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _SID_H +#define _SID_H + +#include + + +// Define this if you want an emulation of an 8580 +// (affects combined waveforms) +#undef EMUL_MOS8580 + + +class Prefs; +class C64; +class SIDRenderer; +struct MOS6581State; + +// Class for administrative functions +class MOS6581 { +public: + MOS6581(C64 *c64); + ~MOS6581(); + + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + void NewPrefs(Prefs *prefs); + void PauseSound(void); + void ResumeSound(void); + void GetState(MOS6581State *ss); + void SetState(MOS6581State *ss); + void EmulateLine(void); + +private: + void open_close_renderer(int old_type, int new_type); + + C64 *the_c64; // Pointer to C64 object + SIDRenderer *the_renderer; // Pointer to current renderer + uint8 regs[32]; // Copies of the 25 write-only SID registers + uint8 last_sid_byte; // Last value written to SID +}; + + +// Renderers do the actual audio data processing +class SIDRenderer { +public: + virtual ~SIDRenderer() {} + virtual void Reset(void)=0; + virtual void EmulateLine(void)=0; + virtual void WriteRegister(uint16 adr, uint8 byte)=0; + virtual void NewPrefs(Prefs *prefs)=0; + virtual void Pause(void)=0; + virtual void Resume(void)=0; +}; + + +// SID state +struct MOS6581State { + uint8 freq_lo_1; + uint8 freq_hi_1; + uint8 pw_lo_1; + uint8 pw_hi_1; + uint8 ctrl_1; + uint8 AD_1; + uint8 SR_1; + + uint8 freq_lo_2; + uint8 freq_hi_2; + uint8 pw_lo_2; + uint8 pw_hi_2; + uint8 ctrl_2; + uint8 AD_2; + uint8 SR_2; + + uint8 freq_lo_3; + uint8 freq_hi_3; + uint8 pw_lo_3; + uint8 pw_hi_3; + uint8 ctrl_3; + uint8 AD_3; + uint8 SR_3; + + uint8 fc_lo; + uint8 fc_hi; + uint8 res_filt; + uint8 mode_vol; + + uint8 pot_x; + uint8 pot_y; + uint8 osc_3; + uint8 env_3; +}; + + +/* + * Fill buffer (for Unix sound routines), sample volume (for sampled voice) + */ + +inline void MOS6581::EmulateLine(void) +{ + if (the_renderer != NULL) + the_renderer->EmulateLine(); +} + + +/* + * Read from register + */ + +inline uint8 MOS6581::ReadRegister(uint16 adr) +{ + // A/D converters + if (adr == 0x19 || adr == 0x1a) { + last_sid_byte = 0; + return 0xff; + } + + // Voice 3 oscillator/EG readout + if (adr == 0x1b || adr == 0x1c) { + last_sid_byte = 0; + return rand(); + } + + // Write-only register: Return last value written to SID + return last_sid_byte; +} + + +/* + * Write to register + */ + +inline void MOS6581::WriteRegister(uint16 adr, uint8 byte) +{ + // Keep a local copy of the register values + last_sid_byte = regs[adr] = byte; + + if (the_renderer != NULL) + the_renderer->WriteRegister(adr, byte); +} + +#endif diff --git a/Src/SID_Acorn.i b/Src/SID_Acorn.i new file mode 100644 index 0000000..02dff4a --- /dev/null +++ b/Src/SID_Acorn.i @@ -0,0 +1,97 @@ +/* + * SID_Acorn.i + * + * RISC OS specific parts of the sound emulation + * Frodo (C) 1994-1997,2002 Christian Bauer + * Acorn port by Andreas Dehmel, 1997 + * + */ + +#include "C64.h" + + + +void DigitalRenderer::init_sound(void) +{ + _kernel_oserror *err; + + ready = false; sound_buffer = NULL; + if ((DigitalRenderer_ReadState() & DRState_Active) != 0) + { + _kernel_oserror dra; + + dra.errnum = 0; sprintf(dra.errmess,"Can't claim sound system -- already active!"); + Wimp_ReportError(&dra,1,TASKNAME); return; + } + // Try starting up the renderer + sndbufsize = 2*224; linecnt = 0; + if ((err = DigitalRenderer_Activate(1,sndbufsize,1000000/SAMPLE_FREQ)) != NULL) + { + Wimp_ReportError(err,1,TASKNAME); return; + } + sound_buffer = new uint8[sndbufsize]; + ready = true; +} + + + + +DigitalRenderer::~DigitalRenderer() +{ + if (ready) + { + _kernel_oserror *err; + + delete sound_buffer; + if ((err = DigitalRenderer_Deactivate()) != NULL) + { + Wimp_ReportError(err,1,TASKNAME); + } + } +} + + + + +void DigitalRenderer::EmulateLine(void) +{ + if (ready) + { + sample_buf[sample_in_ptr++] = volume; + // faster than modulo; usually there shouldn't be a loop (while)... + while (sample_in_ptr >= SAMPLE_BUF_SIZE) {sample_in_ptr -= SAMPLE_BUF_SIZE;} + + // A similar approach to the HP variant: check every of lines if + // new sample needed. + if (--linecnt < 0) + { + int status; + + linecnt = the_c64->PollSoundAfter; + if ((status = DigitalRenderer_ReadState()) > 0) + { + if ((status & DRState_NeedData) != 0) + { + calc_buffer(sound_buffer, sndbufsize); + DigitalRenderer_NewSample(sound_buffer); + } + } + } + } +} + + + + +void DigitalRenderer::Pause(void) +{ + if (ready) {DigitalRenderer_Pause();} +} + + + + +void DigitalRenderer::Resume(void) +{ + if (ready) {DigitalRenderer_Resume();} +} diff --git a/Src/SID_Amiga.i b/Src/SID_Amiga.i new file mode 100644 index 0000000..7ef993d --- /dev/null +++ b/Src/SID_Amiga.i @@ -0,0 +1,284 @@ +/* + * SID_Amiga.i - 6581 emulation, Amiga specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include +#include +#include +#include +#include + + +// Library bases +struct Library *AHIBase; + +// CIA-A base +extern struct CIA ciaa; + + +/* + * Initialization, create sub-process + */ + +void DigitalRenderer::init_sound(void) +{ + // Find our (main) task + main_task = FindTask(NULL); + + // Create signal for communication + main_sig = AllocSignal(-1); + + // Create sub-process and wait until it is ready + if ((sound_process = CreateNewProcTags( + NP_Entry, (ULONG)&sub_invoc, + NP_Name, (ULONG)"Frodo Sound Process", + NP_Priority, 1, + NP_ExitData, (ULONG)this, // Easiest way to supply sub_invoc with this pointer + TAG_DONE)) != NULL) + Wait(1 << main_sig); +} + + +/* + * Destructor, delete sub-process + */ + +DigitalRenderer::~DigitalRenderer() +{ + // Tell sub-process to quit and wait for completion + if (sound_process != NULL) { + Signal(&(sound_process->pr_Task), 1 << quit_sig); + Wait(1 << main_sig); + } + + // Free signal + FreeSignal(main_sig); +} + + +/* + * Sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ + if (sound_process != NULL) + Signal(&(sound_process->pr_Task), 1 << pause_sig); +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ + if (sound_process != NULL) + Signal(&(sound_process->pr_Task), 1 << resume_sig); +} + + +/* + * Sound sub-process + */ + +void DigitalRenderer::sub_invoc(void) +{ + // Get pointer to the DigitalRenderer object and call sub_func() + DigitalRenderer *r = (DigitalRenderer *)((struct Process *)FindTask(NULL))->pr_ExitData; + r->sub_func(); +} + +void DigitalRenderer::sub_func(void) +{ + ahi_port = NULL; + ahi_io = NULL; + ahi_ctrl = NULL; + sample[0].ahisi_Address = sample[1].ahisi_Address = NULL; + ready = FALSE; + + // Create signals for communication + quit_sig = AllocSignal(-1); + pause_sig = AllocSignal(-1); + resume_sig = AllocSignal(-1); + ahi_sig = AllocSignal(-1); + + // Open AHI + if ((ahi_port = CreateMsgPort()) == NULL) + goto wait_for_quit; + if ((ahi_io = (struct AHIRequest *)CreateIORequest(ahi_port, sizeof(struct AHIRequest))) == NULL) + goto wait_for_quit; + ahi_io->ahir_Version = 2; + if (OpenDevice(AHINAME, AHI_NO_UNIT, (struct IORequest *)ahi_io, NULL)) + goto wait_for_quit; + AHIBase = (struct Library *)ahi_io->ahir_Std.io_Device; + + // Initialize callback hook + sf_hook.h_Entry = sound_func; + + // Open audio control structure + if ((ahi_ctrl = AHI_AllocAudio( + AHIA_AudioID, 0x0002000b, + AHIA_MixFreq, SAMPLE_FREQ, + AHIA_Channels, 1, + AHIA_Sounds, 2, + AHIA_SoundFunc, (ULONG)&sf_hook, + AHIA_UserData, (ULONG)this, + TAG_DONE)) == NULL) + goto wait_for_quit; + + // Prepare SampleInfos and load sounds (two sounds for double buffering) + sample[0].ahisi_Type = AHIST_M16S; + sample[0].ahisi_Length = SAMPLE_FREQ / CALC_FREQ; + sample[0].ahisi_Address = AllocVec(SAMPLE_FREQ / CALC_FREQ * 2, MEMF_PUBLIC | MEMF_CLEAR); + sample[1].ahisi_Type = AHIST_M16S; + sample[1].ahisi_Length = SAMPLE_FREQ / CALC_FREQ; + sample[1].ahisi_Address = AllocVec(SAMPLE_FREQ / CALC_FREQ * 2, MEMF_PUBLIC | MEMF_CLEAR); + if (sample[0].ahisi_Address == NULL || sample[1].ahisi_Address == NULL) + goto wait_for_quit; + AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample[0], ahi_ctrl); + AHI_LoadSound(1, AHIST_DYNAMICSAMPLE, &sample[1], ahi_ctrl); + + // Set parameters + play_buf = 0; + AHI_SetVol(0, 0x10000, 0x8000, ahi_ctrl, AHISF_IMM); + AHI_SetFreq(0, SAMPLE_FREQ, ahi_ctrl, AHISF_IMM); + AHI_SetSound(0, play_buf, 0, 0, ahi_ctrl, AHISF_IMM); + + // Start audio output + AHI_ControlAudio(ahi_ctrl, AHIC_Play, TRUE, TAG_DONE); + + // We are now ready for commands + ready = TRUE; + Signal(main_task, 1 << main_sig); + + // Accept and execute commands + for (;;) { + ULONG sigs = Wait((1 << quit_sig) | (1 << pause_sig) | (1 << resume_sig) | (1 << ahi_sig)); + + // Quit sub-process + if (sigs & (1 << quit_sig)) + goto quit; + + // Pause sound output + if (sigs & (1 << pause_sig)) + AHI_ControlAudio(ahi_ctrl, AHIC_Play, FALSE, TAG_DONE); + + // Resume sound output + if (sigs & (1 << resume_sig)) + AHI_ControlAudio(ahi_ctrl, AHIC_Play, TRUE, TAG_DONE); + + // Calculate next buffer + if (sigs & (1 << ahi_sig)) + calc_buffer((int16 *)(sample[play_buf].ahisi_Address), sample[play_buf].ahisi_Length * 2); + } + +wait_for_quit: + // Initialization failed, wait for quit signal + Wait(1 << quit_sig); + +quit: + // Free everything + if (ahi_ctrl != NULL) { + AHI_ControlAudio(ahi_ctrl, AHIC_Play, FALSE, TAG_DONE); + AHI_FreeAudio(ahi_ctrl); + CloseDevice((struct IORequest *)ahi_io); + } + + FreeVec(sample[0].ahisi_Address); + FreeVec(sample[1].ahisi_Address); + + if (ahi_io != NULL) + DeleteIORequest((struct IORequest *)ahi_io); + + if (ahi_port != NULL) + DeleteMsgPort(ahi_port); + + FreeSignal(quit_sig); + FreeSignal(pause_sig); + FreeSignal(resume_sig); + FreeSignal(ahi_sig); + + // Quit (synchronized with main task) + Forbid(); + Signal(main_task, 1 << main_sig); +} + + +/* + * AHI sound callback, play next buffer and signal sub-process + */ + +ULONG DigitalRenderer::sound_func(void) +{ + register struct AHIAudioCtrl *ahi_ctrl asm ("a2"); + DigitalRenderer *r = (DigitalRenderer *)ahi_ctrl->ahiac_UserData; + r->play_buf ^= 1; + AHI_SetSound(0, r->play_buf, 0, 0, ahi_ctrl, 0); + Signal(&(r->sound_process->pr_Task), 1 << (r->ahi_sig)); + return 0; +} + + +/* + * Renderer for SID card + */ + +// Renderer class +class SIDCardRenderer : public SIDRenderer { +public: + SIDCardRenderer(); + virtual ~SIDCardRenderer(); + + virtual void Reset(void); + virtual void EmulateLine(void) {} + virtual void WriteRegister(uint16 adr, uint8 byte); + virtual void NewPrefs(Prefs *prefs) {} + virtual void Pause(void) {} + virtual void Resume(void) {} + +private: + UBYTE *sid_base; // SID card base pointer +}; + +// Constructor: Reset SID +SIDCardRenderer::SIDCardRenderer() +{ + sid_base = (UBYTE *)0xa00001; + Reset(); +} + +// Destructor: Reset SID +SIDCardRenderer::~SIDCardRenderer() +{ + Reset(); +} + +// Reset SID +void SIDCardRenderer::Reset(void) +{ + WaitTOF(); + ciaa.ciapra |= CIAF_LED; + WaitTOF(); + ciaa.ciapra &= ~CIAF_LED; +} + +// Write to register +void SIDCardRenderer::WriteRegister(uint16 adr, uint8 byte) +{ + sid_base[adr << 1] = byte; +} diff --git a/Src/SID_Be.i b/Src/SID_Be.i new file mode 100644 index 0000000..e0eb852 --- /dev/null +++ b/Src/SID_Be.i @@ -0,0 +1,98 @@ +/* + * SID_Be.i - 6581 emulation, Be specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include + +#include "C64.h" + + +/* + * Initialization, open subscriber + */ + +void DigitalRenderer::init_sound(void) +{ + in_stream = false; + + the_stream = new BDACStream(); + the_sub = new BSubscriber("Frodo SID emulation"); + ready = the_sub->Subscribe(the_stream) == B_NO_ERROR; + if (ready) { + the_stream->SetSamplingRate(SAMPLE_FREQ); + the_stream->SetStreamBuffers(SAMPLE_FREQ / CALC_FREQ * 4, 4); // Must be called after EnterStream() + the_sub->EnterStream(NULL, true, this, stream_func, NULL, true); + in_stream = true; + } +} + + +/* + * Destructor, close subscriber + */ + +DigitalRenderer::~DigitalRenderer() +{ + if (ready) { + if (in_stream) { + the_sub->ExitStream(true); + in_stream = false; + } + the_stream->SetStreamBuffers(4096, 8); + the_sub->Unsubscribe(); + ready = false; + } + delete the_sub; + delete the_stream; +} + + +/* + * Sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ + if (in_stream) { + the_sub->ExitStream(true); + in_stream = false; + } +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ + if (!in_stream) { + the_sub->EnterStream(NULL, true, this, stream_func, NULL, true); + in_stream = true; + } +} + + +/* + * Stream function + */ + +bool DigitalRenderer::stream_func(void *arg, char *buf, size_t count, void *header) +{ + ((DigitalRenderer *)arg)->calc_buffer((int16 *)buf, count); + ((DigitalRenderer *)arg)->the_c64->SoundSync(); + return true; +} diff --git a/Src/SID_WIN32.i b/Src/SID_WIN32.i new file mode 100644 index 0000000..f765f01 --- /dev/null +++ b/Src/SID_WIN32.i @@ -0,0 +1,556 @@ +/* + * SID_WIN32.i - 6581 emulation, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * WIN32 code by J. Richard Sladkey + */ + +#include + +#include "VIC.h" +#include "main.h" + +#define FRAG_FREQ SCREEN_FREQ // one frag per frame + +#define FRAGMENT_SIZE (SAMPLE_FREQ/FRAG_FREQ) // samples, not bytes +#define FRAG_INTERVAL (1000/FRAG_FREQ) // in milliseconds +#define BUFFER_FRAGS FRAG_FREQ // frags the in buffer +#define BUFFER_SIZE (2*FRAGMENT_SIZE*BUFFER_FRAGS) // bytes, not samples +#define MAX_LEAD_AVG BUFFER_FRAGS // lead average count + +// This won't hurt DirectX 2 but it will help when using the DirectX 3 runtime. +#if !defined(DSBCAPS_GETCURRENTPOSITION2) +#define DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#endif + +class DigitalPlayer { + +public: + virtual ~DigitalPlayer() = 0; + virtual BOOL Ready() = 0; + virtual int GetCurrentPosition() = 0; + virtual void Write(void *buffer, int position, int length) = 0; + virtual void Pause() = 0; + virtual void Resume() = 0; +}; + +DigitalPlayer::~DigitalPlayer() +{ +} + +class DirectSound: public DigitalPlayer { + +public: + DirectSound(); + ~DirectSound(); + BOOL Ready(); + int GetCurrentPosition(); + void Write(void *buffer, int position, int length); + void Pause(); + void Resume(); + +private: + BOOL ready; + LPDIRECTSOUND pDS; + LPDIRECTSOUNDBUFFER pPrimaryBuffer; + LPDIRECTSOUNDBUFFER pSoundBuffer; +}; + +class WaveOut: public DigitalPlayer { + +public: + WaveOut(); + ~WaveOut(); + BOOL Ready(); + int GetCurrentPosition(); + void Write(void *buffer, int position, int length); + void Pause(); + void Resume(); + +private: + void UnprepareHeader(int index); + void UnprepareHeaders(); + +private: + BOOL ready; + HWAVEOUT hWaveOut; + char wave_buffer[BUFFER_SIZE]; + WAVEHDR wave_header[SCREEN_FREQ]; + int last_unprepared; +}; + +void DigitalRenderer::init_sound() +{ + ready = FALSE; + sound_buffer = new SWORD[2*FRAGMENT_SIZE]; + ThePlayer = 0; + to_output = 0; + divisor = 0; + lead = new int[MAX_LEAD_AVG]; + + StartPlayer(); +} + +DigitalRenderer::~DigitalRenderer() +{ + StopPlayer(); + + delete[] sound_buffer; + delete[] lead; +} + +void DigitalRenderer::StartPlayer() +{ + direct_sound = ThePrefs.DirectSound; + if (ThePrefs.DirectSound) + ThePlayer = new DirectSound; + else + ThePlayer = new WaveOut; + ready = ThePlayer->Ready(); + sb_pos = 0; + memset(lead, 0, sizeof(lead)); + lead_pos = 0; +} + +void DigitalRenderer::StopPlayer() +{ + delete ThePlayer; + ready = FALSE; +} + +void DigitalRenderer::EmulateLine() +{ + if (!ready) + return; + + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + +#if 0 + // Now see how many samples have to be added for this line. + // XXX: This is too much computation here, precompute it. + divisor += SAMPLE_FREQ; + while (divisor >= 0) + divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++; + + // Calculate the sound data only when we have enough to fill + // the buffer entirely. + if (to_output < FRAGMENT_SIZE) + return; + to_output -= FRAGMENT_SIZE; + + VBlank(); +#endif +} + +void DigitalRenderer::VBlank() +{ + if (!ready) + return; + + // Delete and recreate the player if preferences have changed. + if (direct_sound != ThePrefs.DirectSound) { + StopPlayer(); + StartPlayer(); + } + + // Convert latency preferences from milliseconds to frags. + int lead_smooth = ThePrefs.LatencyAvg/FRAG_INTERVAL; + int lead_hiwater = ThePrefs.LatencyMax/FRAG_INTERVAL; + int lead_lowater = ThePrefs.LatencyMin/FRAG_INTERVAL; + + // Compute the current lead in frags. + int current_position = ThePlayer->GetCurrentPosition(); + if (current_position == -1) + return; + int lead_in_bytes = (sb_pos - current_position + BUFFER_SIZE) % BUFFER_SIZE; + if (lead_in_bytes >= BUFFER_SIZE/2) + lead_in_bytes -= BUFFER_SIZE; + int lead_in_frags = lead_in_bytes / int(2*FRAGMENT_SIZE); + lead[lead_pos++] = lead_in_frags; + if (lead_pos == lead_smooth) + lead_pos = 0; + + // Compute the average lead in frags. + int avg_lead = 0; + for (int i = 0; i < lead_smooth; i++) + avg_lead += lead[i]; + avg_lead /= lead_smooth; + //Debug("lead = %d, avg = %d\n", lead_in_frags, avg_lead); + + // If we're getting too far ahead of the audio skip a frag. + if (avg_lead > lead_hiwater) { + for (int i = 0; i < lead_smooth; i++) + lead[i]--; + Debug("Skipping a frag...\n"); + return; + } + + // Calculate one frag. + int nsamples = FRAGMENT_SIZE; + calc_buffer(sound_buffer, 2*FRAGMENT_SIZE); + + // If we're getting too far behind the audio add an extra frag. + if (avg_lead < lead_lowater) { + for (int i = 0; i < lead_smooth; i++) + lead[i]++; + Debug("Adding an extra frag...\n"); + calc_buffer(sound_buffer + FRAGMENT_SIZE, 2*FRAGMENT_SIZE); + nsamples += FRAGMENT_SIZE; + } + + // Write the frags to the player and update out write position. + ThePlayer->Write(sound_buffer, sb_pos, 2*nsamples); + sb_pos = (sb_pos + 2*nsamples) % BUFFER_SIZE; +} + +void DigitalRenderer::Pause() +{ + if (!ready) + return; + ThePlayer->Pause(); +} + +void DigitalRenderer::Resume() +{ + if (!ready) + return; + ThePlayer->Resume(); +} + +// Direct sound implemenation. + +DirectSound::DirectSound() +{ + ready = FALSE; + pDS = NULL; + pPrimaryBuffer = NULL; + pSoundBuffer = NULL; + + HRESULT dsrval = DirectSoundCreate(NULL, &pDS, NULL); + if (dsrval != DS_OK) { + DebugResult("DirectSoundCreate failed", dsrval); + return; + } + + // Set cooperative level trying to get exclusive or normal mode. + DWORD level = ThePrefs.ExclusiveSound ? DSSCL_EXCLUSIVE : DSSCL_NORMAL; + dsrval = pDS->SetCooperativeLevel(hwnd, level); + if (dsrval != DS_OK) { + DebugResult("SetCooperativeLevel failed", dsrval); + return; + } + + WAVEFORMATEX wfx; + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8; + wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec; + wfx.cbSize = 0; + + DSBUFFERDESC dsbd; + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + dsrval = pDS->CreateSoundBuffer(&dsbd, &pPrimaryBuffer, NULL); + if (dsrval != DS_OK) { + DebugResult("CreateSoundBuffer for primary failed", dsrval); + return; + } + + dsrval = pPrimaryBuffer->SetFormat(&wfx); + if (dsrval != DS_OK) { + DebugResult("SetFormat on primary failed", dsrval); + //return; + } + + dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) { + DebugResult("Play primary failed", dsrval); + return; + } + + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2; + dsbd.dwBufferBytes = BUFFER_SIZE; + dsbd.lpwfxFormat = &wfx; + + dsrval = pDS->CreateSoundBuffer(&dsbd, &pSoundBuffer, NULL); + if (dsrval != DS_OK) { + DebugResult("CreateSoundBuffer failed", dsrval); + return; + } + + ready = TRUE; +} + +DirectSound::~DirectSound() +{ + if (pDS != NULL) { + if (pSoundBuffer != NULL) { + pSoundBuffer->Release(); + pSoundBuffer = NULL; + } + if (pPrimaryBuffer != NULL) { + pPrimaryBuffer->Release(); + pPrimaryBuffer = NULL; + } + pDS->Release(); + pDS = NULL; + } +} + +BOOL DirectSound::Ready() +{ + return ready; +} + +int DirectSound::GetCurrentPosition() +{ + DWORD dwPlayPos, dwWritePos; + HRESULT dsrval = pSoundBuffer->GetCurrentPosition(&dwPlayPos, &dwWritePos); + if (dsrval != DS_OK) { + DebugResult("GetCurrentPostion failed", dsrval); + return -1; + } + return dwWritePos; +} + +void DirectSound::Write(void *buffer, int position, int length) +{ + // Lock sound buffer. + LPVOID pMem1, pMem2; + DWORD dwSize1, dwSize2; + HRESULT dsrval = pSoundBuffer->Lock(position, length, + &pMem1, &dwSize1, &pMem2, &dwSize2, 0); + if (dsrval != DS_OK) { + DebugResult("Sound Lock failed", dsrval); + return; + } + + // Copy the sample buffer into the sound buffer. + BYTE *pSample = (BYTE *) buffer; + memcpy(pMem1, pSample, dwSize1); + if (dwSize2 != 0) + memcpy(pMem2, pSample + dwSize1, dwSize2); + + // Unlock the sound buffer. + dsrval = pSoundBuffer->Unlock(pMem1, dwSize1, pMem2, dwSize2); + if (dsrval != DS_OK) { + DebugResult("Unlock failed\n", dsrval); + return; + } + + // Play the sound buffer. + dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) { + DebugResult("Play failed", dsrval); + return; + } +} + +void DirectSound::Pause() +{ + HRESULT dsrval = pSoundBuffer->Stop(); + if (dsrval != DS_OK) + DebugResult("Stop failed", dsrval); + dsrval = pPrimaryBuffer->Stop(); + if (dsrval != DS_OK) + DebugResult("Stop primary failed", dsrval); +} + +void DirectSound::Resume() +{ + HRESULT dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) + DebugResult("Play primary failed", dsrval); + dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) + DebugResult("Play failed", dsrval); +} + +// Wave output implemenation. + +WaveOut::WaveOut() +{ + ready = FALSE; + + WAVEFORMATEX wfx; + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8; + wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec; + wfx.cbSize = 0; + MMRESULT worval = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, 0); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutOpen returned %d\n", worval); + return; + } + memset(wave_header, 0, sizeof(wave_header)); + + last_unprepared = 0; + + ready = TRUE; +} + +WaveOut::~WaveOut() +{ + waveOutReset(hWaveOut); + UnprepareHeaders(); + waveOutClose(hWaveOut); +} + +BOOL WaveOut::Ready() +{ + return ready; +} + +int WaveOut::GetCurrentPosition() +{ + MMTIME mmtime; + memset(&mmtime, 0, sizeof(mmtime)); + mmtime.wType = TIME_BYTES; + MMRESULT worval = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutGetPosition(%d) returned %d\n", worval); + return -1; + } + int current_position = mmtime.u.cb % BUFFER_SIZE; + return current_position; +} + +void WaveOut::Write(void *buffer, int position, int length) +{ + // If we are called for a double length buffer split it in half. + if (length == 4*FRAGMENT_SIZE) { + int half = length/2; + Write(buffer, position, half); + Write(((char *) buffer) + half, position + half, half); + return; + } + + // Free up as many previous frags as possible. + for (;;) { + WAVEHDR &wh = wave_header[last_unprepared]; + if (!(wh.dwFlags & WHDR_DONE)) + break; + UnprepareHeader(last_unprepared); + last_unprepared++; + if (last_unprepared == BUFFER_FRAGS) + last_unprepared = 0; + } + + // Make sure the current header isn't in use. + int index = position/(2*FRAGMENT_SIZE); + WAVEHDR &wh = wave_header[index]; + if (wh.dwFlags & WHDR_DONE) + UnprepareHeader(index); + if (wh.dwFlags != 0) { + Debug("wave header %d is in use!\n", index); + return; + } + + // Prepare the header and write the sound data. + wh.lpData = wave_buffer + position; + wh.dwBufferLength = length; + wh.dwFlags = 0; + memcpy(wh.lpData, buffer, length); + MMRESULT worval = waveOutPrepareHeader(hWaveOut, &wh, sizeof(wh)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutPrepareHeader(%d) returned %d\n", index, worval); + return; + } + worval = waveOutWrite(hWaveOut, &wh, sizeof(wh)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutWrite(%d) returned %d\n", index, worval); + return; + } +} + +void WaveOut::Pause() +{ + waveOutPause(hWaveOut); +} + +void WaveOut::Resume() +{ + waveOutRestart(hWaveOut); +} + +void WaveOut::UnprepareHeader(int index) +{ + WAVEHDR &wh = wave_header[index]; + MMRESULT worval = waveOutUnprepareHeader(hWaveOut, &wh, sizeof(wh)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutUnprepareHeader(%d) returned %d\n", index, worval); + return; + } + memset(&wh, 0, sizeof(wh)); +} + +void WaveOut::UnprepareHeaders() +{ + for (int i = 0; i < BUFFER_FRAGS; i++) { + WAVEHDR &wh = wave_header[i]; + if (wh.dwFlags & WHDR_DONE) + UnprepareHeader(i); + } +} + +#if 0 + +// Log player implemenation. + +void Log::Write() +{ + // Dump the sound output to the log for debugging. + { + int last = sound_buffer[0]; + int count = 1; + for (int i = 1; i < nsamples; i++) { + if (sound_buffer[i] == last) { + count++; + continue; + } + Debug("[%dx%d] ", count, last); + count = 1; + last = sound_buffer[i]; + } + Debug("[%dx%d] ", count, last); + } +} + +#endif + +#if 0 + +// File player implemenation. + +void Log::Write() +{ + // Log the sound output to a file for debugging. + { + static FILE *ofp = NULL; + if (ofp == NULL) { + ofp = fopen("debug.sid", "wb"); + if (ofp == NULL) + Debug("fopen failed\n"); + } + if (ofp != NULL) { + Debug("Write sound data to file\n"); + if (fwrite(sound_buffer, 1, 2*nsamples, ofp) != 2*nsamples) + Debug("fwrite failed\n"); + } + } +} + +#endif diff --git a/Src/SID_hp.i b/Src/SID_hp.i new file mode 100644 index 0000000..d08e0e6 --- /dev/null +++ b/Src/SID_hp.i @@ -0,0 +1,177 @@ +/* + * SID_hp.i - 6581 emulation, HP-UX specific stuff + * + * Lutz Vieweg + */ + +extern "C" { + #include + #include + #include + #include +} + +#define TXBUFSIZE 16384 // bytes, not samples +#define TXFRAGSIZE 1024 // samples, not bytes +#define TXLOWWATER (TXBUFSIZE-(TXFRAGSIZE*2)) // bytes, not samples + +/* + * Initialization + */ + +void DigitalRenderer::init_sound(void) +{ + ready = false; + + if ((fd = open("/dev/audio", O_WRONLY | O_NDELAY, 0)) < 0) { + fprintf(stderr, "unable to open /dev/audio -> no sound\n"); + return; + } + + int flags; + if ((flags = fcntl (fd, F_GETFL, 0)) < 0) { + fprintf(stderr, "unable to set non-blocking mode for /dev/audio -> no sound\n"); + return; + } + flags |= O_NDELAY; + if (fcntl (fd, F_SETFL, flags) < 0) { + fprintf(stderr, "unable to set non-blocking mode for /dev/audio -> no sound\n"); + return; + } + + if (ioctl(fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT)) { + fprintf(stderr, "unable to select 16bit-linear sample format -> no sound\n"); + return; + } + + if (ioctl(fd, AUDIO_SET_SAMPLE_RATE, 44100)) { + fprintf(stderr, "unable to select 44.1kHz sample-rate -> no sound\n"); + return; + } + + if (ioctl(fd, AUDIO_SET_CHANNELS, 1)) { + fprintf(stderr, "unable to select 1-channel playback -> no sound\n"); + return; + } + + // choose between: + // AUDIO_OUT_SPEAKER + // AUDIO_OUT_HEADPHONE + // AUDIO_OUT_LINE + if (ioctl(fd, AUDIO_SET_OUTPUT, AUDIO_OUT_SPEAKER)) { + fprintf(stderr, "unable to select audio output -> no sound\n"); + return; + } + + { + // set volume: + audio_describe description; + audio_gains gains; + if (ioctl(fd, AUDIO_DESCRIBE, &description)) { + fprintf(stderr, "unable to get audio description -> no sound\n"); + return; + } + if (ioctl (fd, AUDIO_GET_GAINS, &gains)) { + fprintf(stderr, "unable to get gain values -> no sound\n"); + return; + } + + float volume = 1.0; + gains.transmit_gain = (int)((float)description.min_transmit_gain + + (float)(description.max_transmit_gain + - description.min_transmit_gain) + * volume); + if (ioctl (fd, AUDIO_SET_GAINS, &gains)) { + fprintf(stderr, "unable to set gain values -> no sound\n"); + return; + } + } + + if (ioctl(fd, AUDIO_SET_TXBUFSIZE, TXBUFSIZE)) { + fprintf(stderr, "unable to set transmission buffer size -> no sound\n"); + return; + } + + sound_calc_buf = new int16[TXFRAGSIZE]; + + linecnt = 0; + + ready = true; + return; +} + + +/* + * Destructor + */ + +DigitalRenderer::~DigitalRenderer() +{ + if (fd > 0) { + if (ready) { + ioctl(fd, AUDIO_DRAIN); + } + ioctl(fd, AUDIO_RESET); + close(fd); + } +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ +} + + +/* + * Fill buffer, sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + + // testing the audio status at each raster-line is + // really not necessary. Let's do it every 50th one.. + // ought to be enough by far. + + linecnt--; + if (linecnt < 0 && ready) { + linecnt = 50; + + // check whether we should add some more data to the + // transmission buffer. + + if (ioctl(fd, AUDIO_GET_STATUS, &status)) { + fprintf(stderr,"fatal: unable to get audio status\n"); + exit(20); + } + + if (status.transmit_buffer_count < TXLOWWATER) { + // add one sound fragment.. + calc_buffer(sound_calc_buf, TXFRAGSIZE*2); + + // since we've checked for enough space in the transmission buffer, + // it is an error if the non-blocking write returns anything but + // TXFRAGSIZE*2 + if (TXFRAGSIZE*2 != write (fd, sound_calc_buf, TXFRAGSIZE*2)) { + fprintf(stderr,"fatal: write to audio-device failed\n"); + exit(20); + } + } + } +} + diff --git a/Src/SID_linux.i b/Src/SID_linux.i new file mode 100644 index 0000000..e330fde --- /dev/null +++ b/Src/SID_linux.i @@ -0,0 +1,125 @@ +/* + * SID_linux.i - 6581 emulation, Linux specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * Linux sound stuff by Bernd Schmidt + */ + +#include +#include +#include +#include + +#include "VIC.h" + + +/* + * Initialization + */ + +void DigitalRenderer::init_sound(void) +{ + int tmp; + unsigned long format; + + ready = false; + devfd = open("/dev/dsp", O_WRONLY); + if (devfd < 0) + return; + + ioctl(devfd, SNDCTL_DSP_GETFMTS, &format); + if (!(format & AFMT_S16_LE)) + return; + format = AFMT_S16_LE; + ioctl(devfd, SNDCTL_DSP_SETFMT, &format); + + /* + * Buffer size: 2^9 == 512 bytes. Note that too large buffers will not work + * very well: The speed of the C64 is slowed down to an average speed of + * 100% by the blocking write() call in EmulateLine(). If you use a buffer + * of, say 4096 bytes, that will happen only about every 4 frames, which + * means that the emulation runs much faster in some frames, and much + * slower in others. + * On really fast machines, it might make sense to use an even smaller + * buffer size. + */ + tmp = 0x00100009; + ioctl(devfd, SNDCTL_DSP_SETFRAGMENT, &tmp); + tmp = 0; + ioctl(devfd, SNDCTL_DSP_STEREO, &tmp); + tmp = 44100; + ioctl(devfd, SNDCTL_DSP_SPEED, &tmp); + ioctl(devfd, SOUND_PCM_READ_RATE, &tmp); + if (tmp < 43000 || tmp > 45000) + return; + + ioctl(devfd, SNDCTL_DSP_GETBLKSIZE, &sndbufsize); + sound_buffer = new int16[sndbufsize]; + ready = true; +} + + +/* + * Destructor + */ + +DigitalRenderer::~DigitalRenderer() +{ + if (devfd >= 0) + close(devfd); +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ +} + + +/* + * Fill buffer, sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + static int divisor = 0; + static int to_output = 0; + static int buffer_pos = 0; + + if (!ready) + return; + + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + + /* + * Now see how many samples have to be added for this line + */ + divisor += SAMPLE_FREQ; + while (divisor >= 0) + divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++; + + /* + * Calculate the sound data only when we have enough to fill + * the buffer entirely. + */ + if ((buffer_pos + to_output) >= sndbufsize) { + int datalen = sndbufsize - buffer_pos; + to_output -= datalen; + calc_buffer(sound_buffer + buffer_pos, datalen*2); + write(devfd, sound_buffer, sndbufsize*2); + buffer_pos = 0; + } +} diff --git a/Src/SID_sun.i b/Src/SID_sun.i new file mode 100644 index 0000000..dd7ee90 --- /dev/null +++ b/Src/SID_sun.i @@ -0,0 +1,153 @@ +/* + * SID_sun.i - 6581 emulation, SUN specific stuff + * + * Marc Chabanas + * + * The SID emulation will slow down the entire emulator to 100% after + * it is selected. It is not compatible with the SID filters so these + * have to be disabled if you don't want to see the performance becoming + * as low as 20%. + */ + +extern "C" { + #include + #include + #include "VIC.h" +} + +#define FRAGSIZE (SAMPLE_FREQ/CALC_FREQ) // samples in a fragment +#define MAXBUFFERED (3*FRAGSIZE) // allow ourself a little buffering + +/* + * Initialization + */ + +void DigitalRenderer::init_sound(void) +{ + struct audio_info info; + + ready = false; + + if ((fd = open("/dev/audio", O_WRONLY|O_NDELAY, 0)) < 0) + { + fprintf(stderr, "SID_sun : unable to open /dev/audio.\n"); + return; + } + + AUDIO_INITINFO(&info); + info.play.sample_rate = SAMPLE_FREQ; + info.play.channels = 1; + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.port = AUDIO_SPEAKER; + if (ioctl(fd, AUDIO_SETINFO, &info)) + { + fprintf(stderr, + "SID_sun : unable to select 16 bits/%d khz linear encoding.\n", + SAMPLE_FREQ); + return; + } + else + { + fprintf(stderr,"SID_sun : selecting 16 bits/%d khz linear encoding.\n", + SAMPLE_FREQ); + } + + sound_calc_buf = new int16[FRAGSIZE]; + sent_samples = 0; + delta_samples = 0; + + ready = true; + return; +} + + +/* + * Destructor + */ + +DigitalRenderer::~DigitalRenderer() +{ + fprintf(stderr,"SID_sun : leaving audio mode.\n"); + if (fd > 0) + { + if (ready) + ioctl(fd, AUDIO_DRAIN); + close(fd); + } +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ +} + +/* + * Fill buffer, sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + static int divisor = 0; + static int to_output = 0; + + static unsigned int sleeping; + + int ret; + + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + + /* + * Now see how many samples have to be added for this line + */ + + divisor += SAMPLE_FREQ; + while (divisor >= 0) + divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++; + + if (to_output >= FRAGSIZE) /* got enough to send */ + { + calc_buffer(sound_calc_buf, FRAGSIZE*2); + + ret = write (fd,sound_calc_buf, FRAGSIZE*2); + if (ret != FRAGSIZE*2) /* should not happen */ + { + fprintf(stderr,"SID_sun : write to audio device failed. Exit.\n"); + exit(-1); + } + + sent_samples+=FRAGSIZE; + to_output=0; + + if (ioctl(fd, AUDIO_GETINFO, &status)) + { + fprintf(stderr,"SID_sun : get audio status failed. Exit.\n"); + exit(-1); + } + while ((delta_samples = (sent_samples - status.play.samples)) > MAXBUFFERED) + { + sleeping = ((1000000*(delta_samples-MAXBUFFERED))/SAMPLE_FREQ); + usleep(sleeping); + + if (ioctl(fd, AUDIO_GETINFO, &status)) + { + fprintf(stderr,"SID_sun : get audio status failed. Exit.\n"); + exit(-1); + } + } + } +} diff --git a/Src/VIC.cpp b/Src/VIC.cpp new file mode 100644 index 0000000..a3b54ac --- /dev/null +++ b/Src/VIC.cpp @@ -0,0 +1,1861 @@ +/* + * VIC.cpp - 6569R5 emulation (line based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated + * raster line. It computes one pixel row of the graphics + * according to the current VIC register settings and returns + * the number of cycles available for the CPU in that line. + * - The graphics are output into an 8 bit chunky bitmap + * - The sprite-graphics priority handling and collision + * detection is done in a bit-oriented way with masks. + * The foreground/background pixel mask for the graphics + * is stored in the fore_mask_buf[] array. Multicolor + * sprites are converted from their original chunky format + * to a bitplane representation (two bit masks) for easier + * handling of priorities and collisions. + * - The sprite-sprite priority handling and collision + * detection is done in with the byte array spr_coll_buf[], + * that is used to keep track of which sprites are already + * visible at certain X positions. + * + * Incompatibilities: + * ------------------ + * + * - Raster effects that are achieved by modifying VIC registers + * in the middle of a raster line cannot be emulated + * - Sprite collisions are only detected within the visible + * screen area (excluding borders) + * - Sprites are only drawn if they completely fit within the + * left/right limits of the chunky bitmap + * - The Char ROM is not visible in the bitmap displays at + * addresses $0000 and $8000 + * - The IRQ is cleared on every write access to the flag + * register. This is a hack for the RMW instructions of the + * 6510 that first write back the original value. + */ + +#include "sysdeps.h" + +#include "VIC.h" +#include "C64.h" +#include "CPUC64.h" +#include "Display.h" +#include "Prefs.h" + + +// Test alignment on run-time for processors that can't access unaligned: +#ifdef __riscos__ +#define ALIGNMENT_CHECK +#endif + +// First and last displayed line +const unsigned FIRST_DISP_LINE = 0x10; +const unsigned LAST_DISP_LINE = 0x11f; + +// First and last possible line for Bad Lines +const unsigned FIRST_DMA_LINE = 0x30; +const unsigned LAST_DMA_LINE = 0xf7; + +// Display window coordinates +const int ROW25_YSTART = 0x33; +const int ROW25_YSTOP = 0xfb; +const int ROW24_YSTART = 0x37; +const int ROW24_YSTOP = 0xf7; + +#if defined(SMALL_DISPLAY) +/* This does not work yet, the sprite code doesn't know about it. */ +const int COL40_XSTART = 0x14; +const int COL40_XSTOP = 0x154; +const int COL38_XSTART = 0x1B; +const int COL38_XSTOP = 0x14B; +#else +const int COL40_XSTART = 0x20; +const int COL40_XSTOP = 0x160; +const int COL38_XSTART = 0x27; +const int COL38_XSTOP = 0x157; +#endif + + +// Tables for sprite X expansion +uint16 ExpTable[256] = { + 0x0000, 0x0003, 0x000C, 0x000F, 0x0030, 0x0033, 0x003C, 0x003F, + 0x00C0, 0x00C3, 0x00CC, 0x00CF, 0x00F0, 0x00F3, 0x00FC, 0x00FF, + 0x0300, 0x0303, 0x030C, 0x030F, 0x0330, 0x0333, 0x033C, 0x033F, + 0x03C0, 0x03C3, 0x03CC, 0x03CF, 0x03F0, 0x03F3, 0x03FC, 0x03FF, + 0x0C00, 0x0C03, 0x0C0C, 0x0C0F, 0x0C30, 0x0C33, 0x0C3C, 0x0C3F, + 0x0CC0, 0x0CC3, 0x0CCC, 0x0CCF, 0x0CF0, 0x0CF3, 0x0CFC, 0x0CFF, + 0x0F00, 0x0F03, 0x0F0C, 0x0F0F, 0x0F30, 0x0F33, 0x0F3C, 0x0F3F, + 0x0FC0, 0x0FC3, 0x0FCC, 0x0FCF, 0x0FF0, 0x0FF3, 0x0FFC, 0x0FFF, + 0x3000, 0x3003, 0x300C, 0x300F, 0x3030, 0x3033, 0x303C, 0x303F, + 0x30C0, 0x30C3, 0x30CC, 0x30CF, 0x30F0, 0x30F3, 0x30FC, 0x30FF, + 0x3300, 0x3303, 0x330C, 0x330F, 0x3330, 0x3333, 0x333C, 0x333F, + 0x33C0, 0x33C3, 0x33CC, 0x33CF, 0x33F0, 0x33F3, 0x33FC, 0x33FF, + 0x3C00, 0x3C03, 0x3C0C, 0x3C0F, 0x3C30, 0x3C33, 0x3C3C, 0x3C3F, + 0x3CC0, 0x3CC3, 0x3CCC, 0x3CCF, 0x3CF0, 0x3CF3, 0x3CFC, 0x3CFF, + 0x3F00, 0x3F03, 0x3F0C, 0x3F0F, 0x3F30, 0x3F33, 0x3F3C, 0x3F3F, + 0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF, + 0xC000, 0xC003, 0xC00C, 0xC00F, 0xC030, 0xC033, 0xC03C, 0xC03F, + 0xC0C0, 0xC0C3, 0xC0CC, 0xC0CF, 0xC0F0, 0xC0F3, 0xC0FC, 0xC0FF, + 0xC300, 0xC303, 0xC30C, 0xC30F, 0xC330, 0xC333, 0xC33C, 0xC33F, + 0xC3C0, 0xC3C3, 0xC3CC, 0xC3CF, 0xC3F0, 0xC3F3, 0xC3FC, 0xC3FF, + 0xCC00, 0xCC03, 0xCC0C, 0xCC0F, 0xCC30, 0xCC33, 0xCC3C, 0xCC3F, + 0xCCC0, 0xCCC3, 0xCCCC, 0xCCCF, 0xCCF0, 0xCCF3, 0xCCFC, 0xCCFF, + 0xCF00, 0xCF03, 0xCF0C, 0xCF0F, 0xCF30, 0xCF33, 0xCF3C, 0xCF3F, + 0xCFC0, 0xCFC3, 0xCFCC, 0xCFCF, 0xCFF0, 0xCFF3, 0xCFFC, 0xCFFF, + 0xF000, 0xF003, 0xF00C, 0xF00F, 0xF030, 0xF033, 0xF03C, 0xF03F, + 0xF0C0, 0xF0C3, 0xF0CC, 0xF0CF, 0xF0F0, 0xF0F3, 0xF0FC, 0xF0FF, + 0xF300, 0xF303, 0xF30C, 0xF30F, 0xF330, 0xF333, 0xF33C, 0xF33F, + 0xF3C0, 0xF3C3, 0xF3CC, 0xF3CF, 0xF3F0, 0xF3F3, 0xF3FC, 0xF3FF, + 0xFC00, 0xFC03, 0xFC0C, 0xFC0F, 0xFC30, 0xFC33, 0xFC3C, 0xFC3F, + 0xFCC0, 0xFCC3, 0xFCCC, 0xFCCF, 0xFCF0, 0xFCF3, 0xFCFC, 0xFCFF, + 0xFF00, 0xFF03, 0xFF0C, 0xFF0F, 0xFF30, 0xFF33, 0xFF3C, 0xFF3F, + 0xFFC0, 0xFFC3, 0xFFCC, 0xFFCF, 0xFFF0, 0xFFF3, 0xFFFC, 0xFFFF +}; + +uint16 MultiExpTable[256] = { + 0x0000, 0x0005, 0x000A, 0x000F, 0x0050, 0x0055, 0x005A, 0x005F, + 0x00A0, 0x00A5, 0x00AA, 0x00AF, 0x00F0, 0x00F5, 0x00FA, 0x00FF, + 0x0500, 0x0505, 0x050A, 0x050F, 0x0550, 0x0555, 0x055A, 0x055F, + 0x05A0, 0x05A5, 0x05AA, 0x05AF, 0x05F0, 0x05F5, 0x05FA, 0x05FF, + 0x0A00, 0x0A05, 0x0A0A, 0x0A0F, 0x0A50, 0x0A55, 0x0A5A, 0x0A5F, + 0x0AA0, 0x0AA5, 0x0AAA, 0x0AAF, 0x0AF0, 0x0AF5, 0x0AFA, 0x0AFF, + 0x0F00, 0x0F05, 0x0F0A, 0x0F0F, 0x0F50, 0x0F55, 0x0F5A, 0x0F5F, + 0x0FA0, 0x0FA5, 0x0FAA, 0x0FAF, 0x0FF0, 0x0FF5, 0x0FFA, 0x0FFF, + 0x5000, 0x5005, 0x500A, 0x500F, 0x5050, 0x5055, 0x505A, 0x505F, + 0x50A0, 0x50A5, 0x50AA, 0x50AF, 0x50F0, 0x50F5, 0x50FA, 0x50FF, + 0x5500, 0x5505, 0x550A, 0x550F, 0x5550, 0x5555, 0x555A, 0x555F, + 0x55A0, 0x55A5, 0x55AA, 0x55AF, 0x55F0, 0x55F5, 0x55FA, 0x55FF, + 0x5A00, 0x5A05, 0x5A0A, 0x5A0F, 0x5A50, 0x5A55, 0x5A5A, 0x5A5F, + 0x5AA0, 0x5AA5, 0x5AAA, 0x5AAF, 0x5AF0, 0x5AF5, 0x5AFA, 0x5AFF, + 0x5F00, 0x5F05, 0x5F0A, 0x5F0F, 0x5F50, 0x5F55, 0x5F5A, 0x5F5F, + 0x5FA0, 0x5FA5, 0x5FAA, 0x5FAF, 0x5FF0, 0x5FF5, 0x5FFA, 0x5FFF, + 0xA000, 0xA005, 0xA00A, 0xA00F, 0xA050, 0xA055, 0xA05A, 0xA05F, + 0xA0A0, 0xA0A5, 0xA0AA, 0xA0AF, 0xA0F0, 0xA0F5, 0xA0FA, 0xA0FF, + 0xA500, 0xA505, 0xA50A, 0xA50F, 0xA550, 0xA555, 0xA55A, 0xA55F, + 0xA5A0, 0xA5A5, 0xA5AA, 0xA5AF, 0xA5F0, 0xA5F5, 0xA5FA, 0xA5FF, + 0xAA00, 0xAA05, 0xAA0A, 0xAA0F, 0xAA50, 0xAA55, 0xAA5A, 0xAA5F, + 0xAAA0, 0xAAA5, 0xAAAA, 0xAAAF, 0xAAF0, 0xAAF5, 0xAAFA, 0xAAFF, + 0xAF00, 0xAF05, 0xAF0A, 0xAF0F, 0xAF50, 0xAF55, 0xAF5A, 0xAF5F, + 0xAFA0, 0xAFA5, 0xAFAA, 0xAFAF, 0xAFF0, 0xAFF5, 0xAFFA, 0xAFFF, + 0xF000, 0xF005, 0xF00A, 0xF00F, 0xF050, 0xF055, 0xF05A, 0xF05F, + 0xF0A0, 0xF0A5, 0xF0AA, 0xF0AF, 0xF0F0, 0xF0F5, 0xF0FA, 0xF0FF, + 0xF500, 0xF505, 0xF50A, 0xF50F, 0xF550, 0xF555, 0xF55A, 0xF55F, + 0xF5A0, 0xF5A5, 0xF5AA, 0xF5AF, 0xF5F0, 0xF5F5, 0xF5FA, 0xF5FF, + 0xFA00, 0xFA05, 0xFA0A, 0xFA0F, 0xFA50, 0xFA55, 0xFA5A, 0xFA5F, + 0xFAA0, 0xFAA5, 0xFAAA, 0xFAAF, 0xFAF0, 0xFAF5, 0xFAFA, 0xFAFF, + 0xFF00, 0xFF05, 0xFF0A, 0xFF0F, 0xFF50, 0xFF55, 0xFF5A, 0xFF5F, + 0xFFA0, 0xFFA5, 0xFFAA, 0xFFAF, 0xFFF0, 0xFFF5, 0xFFFA, 0xFFFF +}; + +#ifdef __POWERPC__ +static union { + struct { + uint8 a,b,c,d,e,f,g,h; + } a; + double b; +} TextColorTable[16][16][256]; +#else +static union { + struct { + uint8 a,b,c,d; + } a; + uint32 b; +} TextColorTable[16][16][256][2]; +#endif + +#ifdef GLOBAL_VARS +static uint16 mc_color_lookup[4]; +#ifndef CAN_ACCESS_UNALIGNED +static uint8 text_chunky_buf[40*8]; +#endif +static uint16 mx[8]; // VIC registers +static uint8 mx8; +static uint8 my[8]; +static uint8 ctrl1, ctrl2; +static uint8 lpx, lpy; +static uint8 me, mxe, mye, mdp, mmc; +static uint8 vbase; +static uint8 irq_flag, irq_mask; +static uint8 clx_spr, clx_bgr; +static uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1; +static uint8 sc[8]; + +static uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM +static C64 *the_c64; // Pointer to C64 +static C64Display *the_display; // Pointer to C64Display +static MOS6510 *the_cpu; // Pointer to 6510 + +static uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f") + +static uint8 ec_color, b0c_color, b1c_color, b2c_color, b3c_color; // Indices for exterior/background colors +static uint8 mm0_color, mm1_color; // Indices for MOB multicolors +static uint8 spr_color[8]; // Indices for MOB colors + +static uint32 ec_color_long; // ec_color expanded to 32 bits + +static uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines +static uint8 color_line[40]; // Buffer for color line, read in Bad Lines + +#ifdef __POWERPC__ +static double chunky_tmp[DISPLAY_X/8]; // Temporary line buffer for GameKit speedup +#endif +static uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer +static int xmod; // Number of bytes per row + +static uint8 *matrix_base; // Video matrix base +static uint8 *char_base; // Character generator base +static uint8 *bitmap_base; // Bitmap base + +static uint16 raster_y; // Current raster line +static uint16 irq_raster; // Interrupt raster line +static uint16 dy_start; // Comparison values for border logic +static uint16 dy_stop; +static uint16 rc; // Row counter +static uint16 vc; // Video counter +static uint16 vc_base; // Video counter base +static uint16 x_scroll; // X scroll value +static uint16 y_scroll; // Y scroll value +static uint16 cia_vabase; // CIA VA14/15 video base + +static int display_idx; // Index of current display mode +static int skip_counter; // Counter for frame-skipping + +static uint16 mc[8]; // Sprite data counters +static uint8 sprite_on; // 8 Flags: Sprite display/DMA active + +static uint8 spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities +static uint8 fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities + +static bool display_state; // true: Display state, false: Idle state +static bool border_on; // Flag: Upper/lower border on +static bool border_40_col; // Flag: 40 column border +static bool frame_skipped; // Flag: Frame is being skipped +static uint8 bad_lines_enabled; // Flag: Bad Lines enabled for this frame +static bool lp_triggered; // Flag: Lightpen was triggered in this frame +#endif + + +/* + * Constructor: Initialize variables + */ + +static void init_text_color_table(uint8 *colors) +{ + for (int i = 0; i < 16; i++) + for (int j = 0; j < 16; j++) + for (int k = 0; k < 256; k++) { +#ifdef __POWERPC__ + TextColorTable[i][j][k].a.a = colors[k & 128 ? i : j]; + TextColorTable[i][j][k].a.b = colors[k & 64 ? i : j]; + TextColorTable[i][j][k].a.c = colors[k & 32 ? i : j]; + TextColorTable[i][j][k].a.d = colors[k & 16 ? i : j]; + TextColorTable[i][j][k].a.e = colors[k & 8 ? i : j]; + TextColorTable[i][j][k].a.f = colors[k & 4 ? i : j]; + TextColorTable[i][j][k].a.g = colors[k & 2 ? i : j]; + TextColorTable[i][j][k].a.h = colors[k & 1 ? i : j]; +#else + TextColorTable[i][j][k][0].a.a = colors[k & 128 ? i : j]; + TextColorTable[i][j][k][0].a.b = colors[k & 64 ? i : j]; + TextColorTable[i][j][k][0].a.c = colors[k & 32 ? i : j]; + TextColorTable[i][j][k][0].a.d = colors[k & 16 ? i : j]; + TextColorTable[i][j][k][1].a.a = colors[k & 8 ? i : j]; + TextColorTable[i][j][k][1].a.b = colors[k & 4 ? i : j]; + TextColorTable[i][j][k][1].a.c = colors[k & 2 ? i : j]; + TextColorTable[i][j][k][1].a.d = colors[k & 1 ? i : j]; +#endif + } +} + +MOS6569::MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color) +#ifndef GLOBAL_VARS + : ram(RAM), char_rom(Char), color_ram(Color), the_c64(c64), the_display(disp), the_cpu(CPU) +#endif +{ + int i; + + // Set pointers +#ifdef GLOBAL_VARS + the_c64 = c64; + the_display = disp; + the_cpu = CPU; + ram = RAM; + char_rom = Char; + color_ram = Color; +#endif + matrix_base = RAM; + char_base = RAM; + bitmap_base = RAM; + + // Get bitmap info + chunky_line_start = disp->BitmapBase(); + xmod = disp->BitmapXMod(); + + // Initialize VIC registers + mx8 = 0; + ctrl1 = ctrl2 = 0; + lpx = lpy = 0; + me = mxe = mye = mdp = mmc = 0; + vbase = irq_flag = irq_mask = 0; + clx_spr = clx_bgr = 0; + cia_vabase = 0; + ec = b0c = b1c = b2c = b3c = mm0 = mm1 = 0; + for (i=0; i<8; i++) mx[i] = my[i] = sc[i] = 0; + + // Initialize other variables + raster_y = 0xffff; + rc = 7; + irq_raster = vc = vc_base = x_scroll = y_scroll = 0; + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + + display_idx = 0; + display_state = false; + border_on = false; + lp_triggered = false; + + sprite_on = 0; + for (i=0; i<8; i++) + mc[i] = 63; + + frame_skipped = false; + skip_counter = 1; + + // Clear foreground mask + memset(fore_mask_buf, 0, DISPLAY_X/8); + + // Preset colors to black + disp->InitColors(colors); + init_text_color_table(colors); + ec_color = b0c_color = b1c_color = b2c_color = b3c_color = mm0_color = mm1_color = colors[0]; + ec_color_long = (ec_color << 24) | (ec_color << 16) | (ec_color << 8) | ec_color; + for (i=0; i<8; i++) spr_color[i] = colors[0]; +} + + +/* + * Reinitialize the colors table for when the palette has changed + */ + +void MOS6569::ReInitColors(void) +{ + int i; + + // Build inverse color table. + uint8 xlate_colors[256]; + memset(xlate_colors, 0, sizeof(xlate_colors)); + for (i = 0; i < 16; i++) + xlate_colors[colors[i]] = i; + + // Get the new colors. + the_display->InitColors(colors); + init_text_color_table(colors); + + // Build color translation table. + for (i = 0; i < 256; i++) + xlate_colors[i] = colors[xlate_colors[i]]; + + // Translate all the old colors variables. + ec_color = colors[ec]; + ec_color_long = ec_color | (ec_color << 8) | (ec_color << 16) | (ec_color << 24); + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + for (i = 0; i < 8; i++) + spr_color[i] = colors[sc[i]]; + mc_color_lookup[0] = b0c_color | (b0c_color << 8); + mc_color_lookup[1] = b1c_color | (b1c_color << 8); + mc_color_lookup[2] = b2c_color | (b2c_color << 8); + + // Translate the chunky buffer. + uint8 *scanline = the_display->BitmapBase(); + for (int y = 0; y < DISPLAY_Y; y++) { + for (int x = 0; x < DISPLAY_X; x++) + scanline[x] = xlate_colors[scanline[x]]; + scanline += xmod; + } +} + +#ifdef GLOBAL_VARS +static void make_mc_table(void) +#else +void MOS6569::make_mc_table(void) +#endif +{ + mc_color_lookup[0] = b0c_color | (b0c_color << 8); + mc_color_lookup[1] = b1c_color | (b1c_color << 8); + mc_color_lookup[2] = b2c_color | (b2c_color << 8); +} + + +/* + * Convert video address to pointer + */ + +#ifdef GLOBAL_VARS +static inline uint8 *get_physical(uint16 adr) +#else +inline uint8 *MOS6569::get_physical(uint16 adr) +#endif +{ + int va = adr | cia_vabase; + if ((va & 0x7000) == 0x1000) + return char_rom + (va & 0x0fff); + else + return ram + va; +} + + +/* + * Get VIC state + */ + +void MOS6569::GetState(MOS6569State *vd) +{ + int i; + + vd->m0x = mx[0] & 0xff; vd->m0y = my[0]; + vd->m1x = mx[1] & 0xff; vd->m1y = my[1]; + vd->m2x = mx[2] & 0xff; vd->m2y = my[2]; + vd->m3x = mx[3] & 0xff; vd->m3y = my[3]; + vd->m4x = mx[4] & 0xff; vd->m4y = my[4]; + vd->m5x = mx[5] & 0xff; vd->m5y = my[5]; + vd->m6x = mx[6] & 0xff; vd->m6y = my[6]; + vd->m7x = mx[7] & 0xff; vd->m7y = my[7]; + vd->mx8 = mx8; + + vd->ctrl1 = (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + vd->raster = raster_y & 0xff; + vd->lpx = lpx; vd->lpy = lpy; + vd->ctrl2 = ctrl2; + vd->vbase = vbase; + vd->irq_flag = irq_flag; + vd->irq_mask = irq_mask; + + vd->me = me; vd->mxe = mxe; vd->mye = mye; vd->mdp = mdp; vd->mmc = mmc; + vd->mm = clx_spr; vd->md = clx_bgr; + + vd->ec = ec; + vd->b0c = b0c; vd->b1c = b1c; vd->b2c = b2c; vd->b3c = b3c; + vd->mm0 = mm0; vd->mm1 = mm1; + vd->m0c = sc[0]; vd->m1c = sc[1]; + vd->m2c = sc[2]; vd->m3c = sc[3]; + vd->m4c = sc[4]; vd->m5c = sc[5]; + vd->m6c = sc[6]; vd->m7c = sc[7]; + + vd->pad0 = 0; + vd->irq_raster = irq_raster; + vd->vc = vc; + vd->vc_base = vc_base; + vd->rc = rc; + vd->spr_dma = vd->spr_disp = sprite_on; + for (i=0; i<8; i++) + vd->mc[i] = vd->mc_base[i] = mc[i]; + vd->display_state = display_state; + vd->bad_line = raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled; + vd->bad_line_enable = bad_lines_enabled; + vd->lp_triggered = lp_triggered; + vd->border_on = border_on; + + vd->bank_base = cia_vabase; + vd->matrix_base = ((vbase & 0xf0) << 6) | cia_vabase; + vd->char_base = ((vbase & 0x0e) << 10) | cia_vabase; + vd->bitmap_base = ((vbase & 0x08) << 10) | cia_vabase; + for (i=0; i<8; i++) + vd->sprite_base[i] = (matrix_base[0x3f8 + i] << 6) | cia_vabase; + + vd->cycle = 1; + vd->raster_x = 0; + vd->ml_index = 0; + vd->ref_cnt = 0xff; + vd->last_vic_byte = 0; + vd->ud_border_on = border_on; +} + + +/* + * Set VIC state (only works if in VBlank) + */ + +void MOS6569::SetState(MOS6569State *vd) +{ + int i, j; + + mx[0] = vd->m0x; my[0] = vd->m0y; + mx[1] = vd->m1x; my[1] = vd->m1y; + mx[2] = vd->m2x; my[2] = vd->m2y; + mx[3] = vd->m3x; my[3] = vd->m3y; + mx[4] = vd->m4x; my[4] = vd->m4y; + mx[5] = vd->m5x; my[5] = vd->m5y; + mx[6] = vd->m6x; my[6] = vd->m6y; + mx[7] = vd->m7x; my[7] = vd->m7y; + mx8 = vd->mx8; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + + ctrl1 = vd->ctrl1; + ctrl2 = vd->ctrl2; + x_scroll = ctrl2 & 7; + y_scroll = ctrl1 & 7; + if (ctrl1 & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + border_40_col = ctrl2 & 8; + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + + raster_y = 0; + lpx = vd->lpx; lpy = vd->lpy; + + vbase = vd->vbase; + cia_vabase = vd->bank_base; + matrix_base = get_physical((vbase & 0xf0) << 6); + char_base = get_physical((vbase & 0x0e) << 10); + bitmap_base = get_physical((vbase & 0x08) << 10); + + irq_flag = vd->irq_flag; + irq_mask = vd->irq_mask; + + me = vd->me; mxe = vd->mxe; mye = vd->mye; mdp = vd->mdp; mmc = vd->mmc; + clx_spr = vd->mm; clx_bgr = vd->md; + + ec = vd->ec; + ec_color = colors[ec]; + ec_color_long = (ec_color << 24) | (ec_color << 16) | (ec_color << 8) | ec_color; + + b0c = vd->b0c; b1c = vd->b1c; b2c = vd->b2c; b3c = vd->b3c; + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + make_mc_table(); + + mm0 = vd->mm0; mm1 = vd->mm1; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + + sc[0] = vd->m0c; sc[1] = vd->m1c; + sc[2] = vd->m2c; sc[3] = vd->m3c; + sc[4] = vd->m4c; sc[5] = vd->m5c; + sc[6] = vd->m6c; sc[7] = vd->m7c; + for (i=0; i<8; i++) + spr_color[i] = colors[sc[i]]; + + irq_raster = vd->irq_raster; + vc = vd->vc; + vc_base = vd->vc_base; + rc = vd->rc; + sprite_on = vd->spr_dma; + for (i=0; i<8; i++) + mc[i] = vd->mc[i]; + display_state = vd->display_state; + bad_lines_enabled = vd->bad_line_enable; + lp_triggered = vd->lp_triggered; + border_on = vd->border_on; +} + + +/* + * Trigger raster IRQ + */ + +#ifdef GLOBAL_VARS +static inline void raster_irq(void) +#else +inline void MOS6569::raster_irq(void) +#endif +{ + irq_flag |= 0x01; + if (irq_mask & 0x01) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } +} + + +/* + * Read from VIC register + */ + +uint8 MOS6569::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + return mx[adr >> 1]; + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + return my[adr >> 1]; + + case 0x10: // Sprite X position MSB + return mx8; + + case 0x11: // Control register 1 + return (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + + case 0x12: // Raster counter + return raster_y; + + case 0x13: // Light pen X + return lpx; + + case 0x14: // Light pen Y + return lpy; + + case 0x15: // Sprite enable + return me; + + case 0x16: // Control register 2 + return ctrl2 | 0xc0; + + case 0x17: // Sprite Y expansion + return mye; + + case 0x18: // Memory pointers + return vbase | 0x01; + + case 0x19: // IRQ flags + return irq_flag | 0x70; + + case 0x1a: // IRQ mask + return irq_mask | 0xf0; + + case 0x1b: // Sprite data priority + return mdp; + + case 0x1c: // Sprite multicolor + return mmc; + + case 0x1d: // Sprite X expansion + return mxe; + + case 0x1e:{ // Sprite-sprite collision + uint8 ret = clx_spr; + clx_spr = 0; // Read and clear + return ret; + } + + case 0x1f:{ // Sprite-background collision + uint8 ret = clx_bgr; + clx_bgr = 0; // Read and clear + return ret; + } + + case 0x20: return ec | 0xf0; + case 0x21: return b0c | 0xf0; + case 0x22: return b1c | 0xf0; + case 0x23: return b2c | 0xf0; + case 0x24: return b3c | 0xf0; + case 0x25: return mm0 | 0xf0; + case 0x26: return mm1 | 0xf0; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + return sc[adr - 0x27] | 0xf0; + + default: + return 0xff; + } +} + + +/* + * Write to VIC register + */ + +void MOS6569::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + mx[adr >> 1] = (mx[adr >> 1] & 0xff00) | byte; + break; + + case 0x10:{ + int i, j; + mx8 = byte; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + break; + } + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + my[adr >> 1] = byte; + break; + + case 0x11:{ // Control register 1 + ctrl1 = byte; + y_scroll = byte & 7; + + uint16 new_irq_raster = (irq_raster & 0xff) | ((byte & 0x80) << 1); + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + + if (byte & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + } + + case 0x12:{ // Raster counter + uint16 new_irq_raster = (irq_raster & 0xff00) | byte; + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + break; + } + + case 0x15: // Sprite enable + me = byte; + break; + + case 0x16: // Control register 2 + ctrl2 = byte; + x_scroll = byte & 7; + border_40_col = byte & 8; + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + + case 0x17: // Sprite Y expansion + mye = byte; + break; + + case 0x18: // Memory pointers + vbase = byte; + matrix_base = get_physical((byte & 0xf0) << 6); + char_base = get_physical((byte & 0x0e) << 10); + bitmap_base = get_physical((byte & 0x08) << 10); + break; + + case 0x19: // IRQ flags + irq_flag = irq_flag & (~byte & 0x0f); + the_cpu->ClearVICIRQ(); // Clear interrupt (hack!) + if (irq_flag & irq_mask) // Set master bit if allowed interrupt still pending + irq_flag |= 0x80; + break; + + case 0x1a: // IRQ mask + irq_mask = byte & 0x0f; + if (irq_flag & irq_mask) { // Trigger interrupt if pending and now allowed + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } else { + irq_flag &= 0x7f; + the_cpu->ClearVICIRQ(); + } + break; + + case 0x1b: // Sprite data priority + mdp = byte; + break; + + case 0x1c: // Sprite multicolor + mmc = byte; + break; + + case 0x1d: // Sprite X expansion + mxe = byte; + break; + + case 0x20: + ec_color = colors[ec = byte]; + ec_color_long = (ec_color << 24) | (ec_color << 16) | (ec_color << 8) | ec_color; + break; + + case 0x21: + if (b0c != byte) { + b0c_color = colors[b0c = byte & 0xF]; + make_mc_table(); + } + break; + + case 0x22: + if (b1c != byte) { + b1c_color = colors[b1c = byte & 0xF]; + make_mc_table(); + } + break; + + case 0x23: + if (b2c != byte) { + b2c_color = colors[b2c = byte & 0xF]; + make_mc_table(); + } + break; + + case 0x24: b3c_color = colors[b3c = byte & 0xF]; break; + case 0x25: mm0_color = colors[mm0 = byte]; break; + case 0x26: mm1_color = colors[mm1 = byte]; break; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + spr_color[adr - 0x27] = colors[sc[adr - 0x27] = byte]; + break; + } +} + + +/* + * CIA VA14/15 has changed + */ + +void MOS6569::ChangedVA(uint16 new_va) +{ + cia_vabase = new_va << 14; + WriteRegister(0x18, vbase); // Force update of memory pointers +} + + +/* + * Trigger lightpen interrupt, latch lightpen coordinates + */ + +void MOS6569::TriggerLightpen(void) +{ + if (!lp_triggered) { // Lightpen triggers only once per frame + lp_triggered = true; + + lpx = 0; // Latch current coordinates + lpy = raster_y; + + irq_flag |= 0x08; // Trigger IRQ + if (irq_mask & 0x08) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } +} + + +/* + * VIC vertical blank: Reset counters and redraw screen + */ + +#ifdef GLOBAL_VARS +static inline void vblank(void) +#else +inline void MOS6569::vblank(void) +#endif +{ + raster_y = vc_base = 0; + lp_triggered = false; + + if (!(frame_skipped = --skip_counter)) + skip_counter = ThePrefs.SkipFrames; + + the_c64->VBlank(!frame_skipped); + + // Get bitmap pointer for next frame. This must be done + // after calling the_c64->VBlank() because the preferences + // and screen configuration may have been changed there + chunky_line_start = the_display->BitmapBase(); + xmod = the_display->BitmapXMod(); +} + + +#ifdef __riscos__ +#include "el_Acorn.i" +#else + +#ifdef GLOBAL_VARS +static inline void el_std_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + unsigned int b0cc = b0c; +#ifdef __POWERPC__ + double *dp = (double *)p - 1; +#else + uint32 *lp = (uint32 *)p; +#endif + uint8 *cp = color_line; + uint8 *mp = matrix_line; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 color = cp[i]; + uint8 data = r[i] = q[mp[i] << 3]; + +#ifdef __POWERPC__ + *++dp = TextColorTable[color][b0cc][data].b; +#else + *lp++ = TextColorTable[color][b0cc][data][0].b; + *lp++ = TextColorTable[color][b0cc][data][1].b; +#endif + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + uint16 *wp = (uint16 *)p; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint16 *mclp = mc_color_lookup; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = q[mp[i] << 3]; + + if (cp[i] & 8) { + uint8 color = colors[cp[i] & 7]; + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + mclp[3] = color | (color << 8); + *wp++ = mclp[(data >> 6) & 3]; + *wp++ = mclp[(data >> 4) & 3]; + *wp++ = mclp[(data >> 2) & 3]; + *wp++ = mclp[(data >> 0) & 3]; + + } else { // Standard mode in multicolor mode + uint8 color = cp[i]; + r[i] = data; +#ifdef __POWERPC__ + *(double *)wp = TextColorTable[color][b0c][data].b; + wp += 4; +#else + *(uint32 *)wp = TextColorTable[color][b0c][data][0].b; + wp += 2; + *(uint32 *)wp = TextColorTable[color][b0c][data][1].b; + wp += 2; +#endif + } + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ +#ifdef __POWERPC__ + double *dp = (double *)p-1; +#else + uint32 *lp = (uint32 *)p; +#endif + uint8 *mp = matrix_line; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 data = r[i] = *q; + uint8 color = mp[i] >> 4; + uint8 bcolor = mp[i] & 15; + +#ifdef __POWERPC__ + *++dp = TextColorTable[color][bcolor][data].b; +#else + *lp++ = TextColorTable[color][bcolor][data][0].b; + *lp++ = TextColorTable[color][bcolor][data][1].b; +#endif + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + uint16 lookup[4]; + uint16 *wp = (uint16 *)p - 1; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + +#ifdef __GNU_C__ + &lookup; /* Statement with no effect other than preventing GCC from + * putting the array in a register, which generates + * spectacularly bad code. */ +#endif + + lookup[0] = (b0c_color << 8) | b0c_color; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 color, acolor, bcolor; + + color = colors[mp[i] >> 4]; + lookup[1] = (color << 8) | color; + bcolor = colors[mp[i]]; + lookup[2] = (bcolor << 8) | bcolor; + acolor = colors[cp[i]]; + lookup[3] = (acolor << 8) | acolor; + + uint8 data = *q; + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + + *++wp = lookup[(data >> 6) & 3]; + *++wp = lookup[(data >> 4) & 3]; + *++wp = lookup[(data >> 2) & 3]; + *++wp = lookup[(data >> 0) & 3]; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ +#ifdef __POWERPC__ + double *dp = (double *)p - 1; +#else + uint32 *lp = (uint32 *)p; +#endif + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint8 *bcp = &b0c; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = r[i] = mp[i]; + uint8 color = cp[i]; + uint8 bcolor = bcp[(data >> 6) & 3]; + + data = q[(data & 0x3f) << 3]; +#ifdef __POWERPC__ + *++dp = TextColorTable[color][bcolor][data].b; +#else + *lp++ = TextColorTable[color][bcolor][data][0].b; + *lp++ = TextColorTable[color][bcolor][data][1].b; +#endif + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_std_idle(uint8 *p, uint8 *r) +#endif +{ +#ifdef __POWERPC__ + uint8 data = *get_physical(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + double *dp = (double *)p - 1; + double conv = TextColorTable[0][b0c][data].b; + r--; + + for (int i=0; i<40; i++) { + *++dp = conv; + *++r = data; + } +#else + uint8 data = *get_physical(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + uint32 *lp = (uint32 *)p; + uint32 conv0 = TextColorTable[0][b0c][data][0].b; + uint32 conv1 = TextColorTable[0][b0c][data][1].b; + + for (int i=0; i<40; i++) { + *lp++ = conv0; + *lp++ = conv1; + *r++ = data; + } +#endif +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_mc_idle(uint8 *p, uint8 *r) +#endif +{ + uint8 data = *get_physical(0x3fff); + uint32 *lp = (uint32 *)p - 1; + r--; + + uint16 lookup[4]; + lookup[0] = (b0c_color << 8) | b0c_color; + lookup[1] = lookup[2] = lookup[3] = colors[0]; + + uint16 conv0 = (lookup[(data >> 6) & 3] << 16) | lookup[(data >> 4) & 3]; + uint16 conv1 = (lookup[(data >> 2) & 3] << 16) | lookup[(data >> 0) & 3]; + + for (int i=0; i<40; i++) { + *++lp = conv0; + *++lp = conv1; + *++r = data; + } +} + +#endif //__riscos__ + + +#ifdef GLOBAL_VARS +static inline void el_sprites(uint8 *chunky_ptr) +#else +inline void MOS6569::el_sprites(uint8 *chunky_ptr) +#endif +{ + int i; + int snum, sbit; // Sprite number/bit mask + int spr_coll=0, gfx_coll=0; + + // Draw each active sprite + for (snum=0, sbit=1; snum<8; snum++, sbit<<=1) + if ((sprite_on & sbit) && mx[snum] < DISPLAY_X-32) { + int spr_mask_pos; // Sprite bit position in fore_mask_buf + uint32 sdata, fore_mask; + + uint8 *p = chunky_ptr + mx[snum] + 8; + uint8 *q = spr_coll_buf + mx[snum] + 8; + + uint8 *sdatap = get_physical(matrix_base[0x3f8 + snum] << 6 | mc[snum]); + sdata = (*sdatap << 24) | (*(sdatap+1) << 16) | (*(sdatap+2) << 8); + + uint8 color = spr_color[snum]; + + spr_mask_pos = mx[snum] + 8 - x_scroll; + + uint8 *fmbp = fore_mask_buf + (spr_mask_pos / 8); + int sshift = spr_mask_pos & 7; + fore_mask = (((*(fmbp+0) << 24) | (*(fmbp+1) << 16) | (*(fmbp+2) << 8) + | (*(fmbp+3))) << sshift) | (*(fmbp+4) >> (8-sshift)); + + if (mxe & sbit) { // X-expanded + if (mx[snum] >= DISPLAY_X-56) + continue; + + uint32 sdata_l = 0, sdata_r = 0, fore_mask_r; + fore_mask_r = (((*(fmbp+4) << 24) | (*(fmbp+5) << 16) | (*(fmbp+6) << 8) + | (*(fmbp+7))) << sshift) | (*(fmbp+8) >> (8-sshift)); + + if (mmc & sbit) { // Multicolor mode + uint32 plane0_l, plane0_r, plane1_l, plane1_r; + + // Expand sprite data + sdata_l = MultiExpTable[sdata >> 24 & 0xff] << 16 | MultiExpTable[sdata >> 16 & 0xff]; + sdata_r = MultiExpTable[sdata >> 8 & 0xff] << 16; + + // Convert sprite chunky pixels to bitplanes + plane0_l = (sdata_l & 0x55555555) | (sdata_l & 0x55555555) << 1; + plane1_l = (sdata_l & 0xaaaaaaaa) | (sdata_l & 0xaaaaaaaa) >> 1; + plane0_r = (sdata_r & 0x55555555) | (sdata_r & 0x55555555) << 1; + plane1_r = (sdata_r & 0xaaaaaaaa) | (sdata_r & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if ((fore_mask & (plane0_l | plane1_l)) || (fore_mask_r & (plane0_r | plane1_r))) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0_l &= ~fore_mask; // Mask sprite if in background + plane1_l &= ~fore_mask; + plane0_r &= ~fore_mask_r; + plane1_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, plane0_l<<=1, plane1_l<<=1) { + uint8 col; + if (plane1_l & 0x80000000) { + if (plane0_l & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_l & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + for (; i<48; i++, plane0_r<<=1, plane1_r<<=1) { + uint8 col; + if (plane1_r & 0x80000000) { + if (plane0_r & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_r & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Expand sprite data + sdata_l = ExpTable[sdata >> 24 & 0xff] << 16 | ExpTable[sdata >> 16 & 0xff]; + sdata_r = ExpTable[sdata >> 8 & 0xff] << 16; + + // Collision with graphics? + if ((fore_mask & sdata_l) || (fore_mask_r & sdata_r)) { + gfx_coll |= sbit; + if (mdp & sbit) { + sdata_l &= ~fore_mask; // Mask sprite if in background + sdata_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, sdata_l<<=1) + if (sdata_l & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + for (; i<48; i++, sdata_r<<=1) + if (sdata_r & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + } + + } else // Unexpanded + + if (mmc & sbit) { // Multicolor mode + uint32 plane0, plane1; + + // Convert sprite chunky pixels to bitplanes + plane0 = (sdata & 0x55555555) | (sdata & 0x55555555) << 1; + plane1 = (sdata & 0xaaaaaaaa) | (sdata & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if (fore_mask & (plane0 | plane1)) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0 &= ~fore_mask; // Mask sprite if in background + plane1 &= ~fore_mask; + } + } + + // Paint sprite + for (i=0; i<24; i++, plane0<<=1, plane1<<=1) { + uint8 col; + if (plane1 & 0x80000000) { + if (plane0 & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0 & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Collision with graphics? + if (fore_mask & sdata) { + gfx_coll |= sbit; + if (mdp & sbit) + sdata &= ~fore_mask; // Mask sprite if in background + } + + // Paint sprite + for (i=0; i<24; i++, sdata<<=1) + if (sdata & 0x80000000) { + if (q[i]) { // Collision with sprite? + spr_coll |= q[i] | sbit; + } else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + + } + } + + if (ThePrefs.SpriteCollisions) { + + // Check sprite-sprite collisions + if (clx_spr) + clx_spr |= spr_coll; + else { + clx_spr |= spr_coll; + irq_flag |= 0x04; + if (irq_mask & 0x04) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + + // Check sprite-background collisions + if (clx_bgr) + clx_bgr |= gfx_coll; + else { + clx_bgr |= gfx_coll; + irq_flag |= 0x02; + if (irq_mask & 0x02) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + } +} + + +#ifdef GLOBAL_VARS +static inline int el_update_mc(int raster) +#else +inline int MOS6569::el_update_mc(int raster) +#endif +{ + int i, j; + int cycles_used = 0; + uint8 spron = sprite_on; + uint8 spren = me; + uint8 sprye = mye; + uint8 raster8bit = raster; + uint16 *mcp = mc; + uint8 *myp = my; + + // Increment sprite data counters + for (i=0, j=1; i<8; i++, j<<=1, mcp++, myp++) { + + // Sprite enabled? + if (spren & j) + + // Yes, activate if Y position matches raster counter + if (*myp == (raster8bit & 0xff)) { + *mcp = 0; + spron |= j; + } else + goto spr_off; + else +spr_off: + // No, turn sprite off when data counter exceeds 60 + // and increment counter + if (*mcp != 63) { + if (sprye & j) { // Y expansion + if (!((*myp ^ raster8bit) & 1)) { + *mcp += 3; + cycles_used += 2; + if (*mcp == 63) + spron &= ~j; + } + } else { + *mcp += 3; + cycles_used += 2; + if (*mcp == 63) + spron &= ~j; + } + } + } + + sprite_on = spron; + return cycles_used; +} + + +#ifdef __POWERPC__ +static asm void fastcopy(register uchar *dst, register uchar *src); +static asm void fastcopy(register uchar *dst, register uchar *src) +{ + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + blr +} +#endif + + +/* + * Emulate one raster line + */ + +int MOS6569::EmulateLine(void) +{ + int cycles_left = ThePrefs.NormalCycles; // Cycles left for CPU + bool is_bad_line = false; + + // Get raster counter into local variable for faster access and increment + unsigned int raster = raster_y+1; + + // End of screen reached? + if (raster != TOTAL_RASTERS) + raster_y = raster; + else { + vblank(); + raster = 0; + } + + // Trigger raster IRQ if IRQ line reached + if (raster == irq_raster) + raster_irq(); + + // In line $30, the DEN bit controls if Bad Lines can occur + if (raster == 0x30) + bad_lines_enabled = ctrl1 & 0x10; + + // Skip frame? Only calculate Bad Lines then + if (frame_skipped) { + if (raster >= FIRST_DMA_LINE && raster <= LAST_DMA_LINE && ((raster & 7) == y_scroll) && bad_lines_enabled) { + is_bad_line = true; + cycles_left = ThePrefs.BadLineCycles; + } + goto VIC_nop; + } + + // Within the visible range? + if (raster >= FIRST_DISP_LINE && raster <= LAST_DISP_LINE) { + + // Our output goes here +#ifdef __POWERPC__ + uint8 *chunky_ptr = (uint8 *)chunky_tmp; +#else + uint8 *chunky_ptr = chunky_line_start; +#endif + + // Set video counter + vc = vc_base; + + // Bad Line condition? + if (raster >= FIRST_DMA_LINE && raster <= LAST_DMA_LINE && ((raster & 7) == y_scroll) && bad_lines_enabled) { + + // Turn on display + display_state = is_bad_line = true; + cycles_left = ThePrefs.BadLineCycles; + rc = 0; + + // Read and latch 40 bytes from video matrix and color RAM + uint8 *mp = matrix_line - 1; + uint8 *cp = color_line - 1; + int vc1 = vc - 1; + uint8 *mbp = matrix_base + vc1; + uint8 *crp = color_ram + vc1; + for (int i=0; i<40; i++) { + *++mp = *++mbp; + *++cp = *++crp; + } + } + + // Handler upper/lower border + if (raster == dy_stop) + border_on = true; + if (raster == dy_start && (ctrl1 & 0x10)) // Don't turn off border if DEN bit cleared + border_on = false; + + if (!border_on) { + + // Display window contents + uint8 *p = chunky_ptr + COL40_XSTART; // Pointer in chunky display buffer + uint8 *r = fore_mask_buf + COL40_XSTART/8; // Pointer in foreground mask buffer +#ifdef ALIGNMENT_CHECK + uint8 *use_p = ((((int)p) & 3) == 0) ? p : text_chunky_buf; +#endif + + { + p--; + uint8 b0cc = b0c_color; + int limit = x_scroll; + for (int i=0; i0 + *++p = b0cc; + p++; + } + + if (display_state) { + switch (display_idx) { + + case 0: // Standard text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_std_text(use_p, char_base + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_std_text(text_chunky_buf, char_base + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_std_text(p, char_base + rc, r); +#endif +#else + el_std_text(p, char_base + rc, r); +#endif + break; + + case 1: // Multicolor text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_mc_text(use_p, char_base + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_mc_text(text_chunky_buf, char_base + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_mc_text(p, char_base + rc, r); +#endif +#else + el_mc_text(p, char_base + rc, r); +#endif + break; + + case 2: // Standard bitmap +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_std_bitmap(use_p, bitmap_base + (vc << 3) + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_std_bitmap(text_chunky_buf, bitmap_base + (vc << 3) + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_std_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif +#else + el_std_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif + break; + + case 3: // Multicolor bitmap +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_mc_bitmap(use_p, bitmap_base + (vc << 3) + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_mc_bitmap(text_chunky_buf, bitmap_base + (vc << 3) + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_mc_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif +#else + el_mc_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif + break; + + case 4: // ECM text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_ecm_text(use_p, char_base + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_ecm_text(text_chunky_buf, char_base + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_ecm_text(p, char_base + rc, r); +#endif +#else + el_ecm_text(p, char_base + rc, r); +#endif + break; + + default: // Invalid mode (all black) + memset(p, colors[0], 320); + memset(r, 0, 40); + break; + } + vc += 40; + + } else { // Idle state graphics + switch (display_idx) { + + case 0: // Standard text + case 1: // Multicolor text + case 4: // ECM text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_std_idle(use_p, r); + if (use_p != p) {memcpy(p, use_p, 8*40);} +#else + if (x_scroll) { + el_std_idle(text_chunky_buf, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_std_idle(p, r); +#endif +#else + el_std_idle(p, r); +#endif + break; + + case 3: // Multicolor bitmap +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_mc_idle(use_p, r); + if (use_p != p) {memcpy(p, use_p, 8*40);} +#else + if (x_scroll) { + el_mc_idle(text_chunky_buf, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_mc_idle(p, r); +#endif +#else + el_mc_idle(p, r); +#endif + break; + + default: // Invalid mode (all black) + memset(p, colors[0], 320); + memset(r, 0, 40); + break; + } + } + + // Draw sprites + if (sprite_on && ThePrefs.SpritesOn) { + + // Clear sprite collision buffer + uint32 *lp = (uint32 *)spr_coll_buf - 1; + for (int i=0; i= FIRST_DMA_LINE-1 && raster <= LAST_DMA_LINE-1 && (((raster+1) & 7) == y_scroll) && bad_lines_enabled) + rc = 0; + } + +VIC_nop: + // Skip this if all sprites are off + if (me | sprite_on) + cycles_left -= el_update_mc(raster); + + return cycles_left; +} diff --git a/Src/VIC.h b/Src/VIC.h new file mode 100644 index 0000000..1d47d98 --- /dev/null +++ b/Src/VIC.h @@ -0,0 +1,274 @@ +/* + * VIC.h - 6569R5 emulation (line based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _VIC_H +#define _VIC_H + + +// Define this if you want global variables instead of member variables +#if defined(__i386) || defined(mc68000) || defined(__MC68K__) +#define GLOBAL_VARS +#endif + +// Define this if you have a processor that can do unaligned accesses quickly +#if defined(__i386) || defined(mc68000) || defined(__MC68K__) +#define CAN_ACCESS_UNALIGNED +#endif + + +// Total number of raster lines (PAL) +const unsigned TOTAL_RASTERS = 0x138; + +// Screen refresh frequency (PAL) +const unsigned SCREEN_FREQ = 50; + + +class MOS6510; +class C64Display; +class C64; +struct MOS6569State; + + +class MOS6569 { +public: + MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color); + + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); +#ifdef FRODO_SC + bool EmulateCycle(void); +#else + int EmulateLine(void); +#endif + void ChangedVA(uint16 new_va); // CIA VA14/15 has changed + void TriggerLightpen(void); // Trigger lightpen interrupt + void ReInitColors(void); + void GetState(MOS6569State *vd); + void SetState(MOS6569State *vd); + +#ifdef FRODO_SC + uint8 LastVICByte; +#endif + +private: +#ifndef GLOBAL_VARS + void vblank(void); + void raster_irq(void); + + uint16 mx[8]; // VIC registers + uint8 my[8]; + uint8 mx8; + uint8 ctrl1, ctrl2; + uint8 lpx, lpy; + uint8 me, mxe, mye, mdp, mmc; + uint8 vbase; + uint8 irq_flag, irq_mask; + uint8 clx_spr, clx_bgr; + uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1; + uint8 sc[8]; + + uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM + C64 *the_c64; // Pointer to C64 + C64Display *the_display; // Pointer to C64Display + MOS6510 *the_cpu; // Pointer to 6510 + + uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f") + + uint8 ec_color, b0c_color, b1c_color, + b2c_color, b3c_color; // Indices for exterior/background colors + uint8 mm0_color, mm1_color; // Indices for MOB multicolors + uint8 spr_color[8]; // Indices for MOB colors + + uint32 ec_color_long; // ec_color expanded to 32 bits + + uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines + uint8 color_line[40]; // Buffer for color line, read in Bad Lines + +#ifdef __POWERPC__ + double chunky_tmp[0x180/8]; // Temporary line buffer for speedup +#endif + uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer + int xmod; // Number of bytes per row + + uint16 raster_y; // Current raster line + uint16 irq_raster; // Interrupt raster line + uint16 dy_start; // Comparison values for border logic + uint16 dy_stop; + uint16 rc; // Row counter + uint16 vc; // Video counter + uint16 vc_base; // Video counter base + uint16 x_scroll; // X scroll value + uint16 y_scroll; // Y scroll value + uint16 cia_vabase; // CIA VA14/15 video base + + uint16 mc[8]; // Sprite data counters + + int display_idx; // Index of current display mode + int skip_counter; // Counter for frame-skipping + + long pad0; // Keep buffers long-aligned + uint8 spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities + uint8 fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities +#ifndef CAN_ACCESS_UNALIGNED + uint8 text_chunky_buf[40*8]; // Line graphics buffer +#endif + + bool display_state; // true: Display state, false: Idle state + bool border_on; // Flag: Upper/lower border on (Frodo SC: Main border flipflop) + bool frame_skipped; // Flag: Frame is being skipped + uint8 bad_lines_enabled; // Flag: Bad Lines enabled for this frame + bool lp_triggered; // Flag: Lightpen was triggered in this frame + +#ifdef FRODO_SC + uint8 read_byte(uint16 adr); + void matrix_access(void); + void graphics_access(void); + void draw_graphics(void); + void draw_sprites(void); + void draw_background(void); + + int cycle; // Current cycle in line (1..63) + + uint8 *chunky_ptr; // Pointer in chunky bitmap buffer (this is where out output goes) + uint8 *fore_mask_ptr; // Pointer in fore_mask_buf + + uint16 matrix_base; // Video matrix base + uint16 char_base; // Character generator base + uint16 bitmap_base; // Bitmap base + + bool is_bad_line; // Flag: Current line is bad line + bool draw_this_line; // Flag: This line is drawn on the screen + bool ud_border_on; // Flag: Upper/lower border on + bool vblanking; // Flag: VBlank in next cycle + + bool border_on_sample[5]; // Samples of border state at different cycles (1, 17, 18, 56, 57) + uint8 border_color_sample[0x180/8]; // Samples of border color at each "displayed" cycle + + uint8 ref_cnt; // Refresh counter + uint8 spr_exp_y; // 8 sprite y expansion flipflops + uint8 spr_dma_on; // 8 flags: Sprite DMA active + uint8 spr_disp_on; // 8 flags: Sprite display active + uint8 spr_draw; // 8 flags: Draw sprite in this line + uint16 spr_ptr[8]; // Sprite data pointers + uint16 mc_base[8]; // Sprite data counter bases + + uint16 raster_x; // Current raster x position + + int ml_index; // Index in matrix/color_line[] + uint8 gfx_data, char_data, color_data, last_char_data; + uint8 spr_data[8][4]; // Sprite data read + uint8 spr_draw_data[8][4]; // Sprite data for drawing + + uint32 first_ba_cycle; // Cycle when BA first went low +#else + uint8 *get_physical(uint16 adr); + void make_mc_table(void); + void el_std_text(uint8 *p, uint8 *q, uint8 *r); + void el_mc_text(uint8 *p, uint8 *q, uint8 *r); + void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r); + void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r); + void el_ecm_text(uint8 *p, uint8 *q, uint8 *r); + void el_std_idle(uint8 *p, uint8 *r); + void el_mc_idle(uint8 *p, uint8 *r); + void el_sprites(uint8 *chunky_ptr); + int el_update_mc(int raster); + + uint16 mc_color_lookup[4]; + + bool border_40_col; // Flag: 40 column border + uint8 sprite_on; // 8 flags: Sprite display/DMA active + + uint8 *matrix_base; // Video matrix base + uint8 *char_base; // Character generator base + uint8 *bitmap_base; // Bitmap base +#endif +#endif +}; + + +// VIC state +struct MOS6569State { + uint8 m0x; // Sprite coordinates + uint8 m0y; + uint8 m1x; + uint8 m1y; + uint8 m2x; + uint8 m2y; + uint8 m3x; + uint8 m3y; + uint8 m4x; + uint8 m4y; + uint8 m5x; + uint8 m5y; + uint8 m6x; + uint8 m6y; + uint8 m7x; + uint8 m7y; + uint8 mx8; + + uint8 ctrl1; // Control registers + uint8 raster; + uint8 lpx; + uint8 lpy; + uint8 me; + uint8 ctrl2; + uint8 mye; + uint8 vbase; + uint8 irq_flag; + uint8 irq_mask; + uint8 mdp; + uint8 mmc; + uint8 mxe; + uint8 mm; + uint8 md; + + uint8 ec; // Color registers + uint8 b0c; + uint8 b1c; + uint8 b2c; + uint8 b3c; + uint8 mm0; + uint8 mm1; + uint8 m0c; + uint8 m1c; + uint8 m2c; + uint8 m3c; + uint8 m4c; + uint8 m5c; + uint8 m6c; + uint8 m7c; + // Additional registers + uint8 pad0; + uint16 irq_raster; // IRQ raster line + uint16 vc; // Video counter + uint16 vc_base; // Video counter base + uint8 rc; // Row counter + uint8 spr_dma; // 8 Flags: Sprite DMA active + uint8 spr_disp; // 8 Flags: Sprite display active + uint8 mc[8]; // Sprite data counters + uint8 mc_base[8]; // Sprite data counter bases + bool display_state; // true: Display state, false: Idle state + bool bad_line; // Flag: Bad Line state + bool bad_line_enable; // Flag: Bad Lines enabled for this frame + bool lp_triggered; // Flag: Lightpen was triggered in this frame + bool border_on; // Flag: Upper/lower border on (Frodo SC: Main border flipflop) + + uint16 bank_base; // VIC bank base address + uint16 matrix_base; // Video matrix base + uint16 char_base; // Character generator base + uint16 bitmap_base; // Bitmap base + uint16 sprite_base[8]; // Sprite bases + + // Frodo SC: + int cycle; // Current cycle in line (1..63) + uint16 raster_x; // Current raster x position + int ml_index; // Index in matrix/color_line[] + uint8 ref_cnt; // Refresh counter + uint8 last_vic_byte; // Last byte read by VIC + bool ud_border_on; // Flag: Upper/lower border on +}; + +#endif diff --git a/Src/VIC_SC.cpp b/Src/VIC_SC.cpp new file mode 100644 index 0000000..e39c582 --- /dev/null +++ b/Src/VIC_SC.cpp @@ -0,0 +1,1985 @@ +/* + * VIC_SC.cpp - 6569R5 emulation (cycle based) + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * + + * + * Incompatibilities: + * ------------------ + * + * - Color of $ff bytes read when BA is low and AEC is high + * is not correct + * - Changes to border/background color are visible 7 pixels + * too late + * - Sprite data access doesn't respect BA + * - Sprite collisions are only detected within the visible + * screen area (excluding borders) + * - Sprites are only drawn if they completely fit within the + * left/right limits of the chunky bitmap + */ + +#include "sysdeps.h" + +#include "VIC.h" +#include "C64.h" +#include "CPUC64.h" +#include "Display.h" +#include "Prefs.h" + + +// First and last displayed line +const int FIRST_DISP_LINE = 0x10; +const int LAST_DISP_LINE = 0x11f; + +// First and last possible line for Bad Lines +const int FIRST_DMA_LINE = 0x30; +const int LAST_DMA_LINE = 0xf7; + +// Display window coordinates +const int ROW25_YSTART = 0x33; +const int ROW25_YSTOP = 0xfb; +const int ROW24_YSTART = 0x37; +const int ROW24_YSTOP = 0xf7; + +#if defined(SMALL_DISPLAY) +/* This does not work yet, the sprite code doesn't know about it. */ +const int COL40_XSTART = 0x14; +const int COL40_XSTOP = 0x154; +const int COL38_XSTART = 0x1B; +const int COL38_XSTOP = 0x14B; +#else +const int COL40_XSTART = 0x20; +const int COL40_XSTOP = 0x160; +const int COL38_XSTART = 0x27; +const int COL38_XSTOP = 0x157; +#endif + + +// Tables for sprite X expansion +uint16 ExpTable[256] = { + 0x0000, 0x0003, 0x000C, 0x000F, 0x0030, 0x0033, 0x003C, 0x003F, + 0x00C0, 0x00C3, 0x00CC, 0x00CF, 0x00F0, 0x00F3, 0x00FC, 0x00FF, + 0x0300, 0x0303, 0x030C, 0x030F, 0x0330, 0x0333, 0x033C, 0x033F, + 0x03C0, 0x03C3, 0x03CC, 0x03CF, 0x03F0, 0x03F3, 0x03FC, 0x03FF, + 0x0C00, 0x0C03, 0x0C0C, 0x0C0F, 0x0C30, 0x0C33, 0x0C3C, 0x0C3F, + 0x0CC0, 0x0CC3, 0x0CCC, 0x0CCF, 0x0CF0, 0x0CF3, 0x0CFC, 0x0CFF, + 0x0F00, 0x0F03, 0x0F0C, 0x0F0F, 0x0F30, 0x0F33, 0x0F3C, 0x0F3F, + 0x0FC0, 0x0FC3, 0x0FCC, 0x0FCF, 0x0FF0, 0x0FF3, 0x0FFC, 0x0FFF, + 0x3000, 0x3003, 0x300C, 0x300F, 0x3030, 0x3033, 0x303C, 0x303F, + 0x30C0, 0x30C3, 0x30CC, 0x30CF, 0x30F0, 0x30F3, 0x30FC, 0x30FF, + 0x3300, 0x3303, 0x330C, 0x330F, 0x3330, 0x3333, 0x333C, 0x333F, + 0x33C0, 0x33C3, 0x33CC, 0x33CF, 0x33F0, 0x33F3, 0x33FC, 0x33FF, + 0x3C00, 0x3C03, 0x3C0C, 0x3C0F, 0x3C30, 0x3C33, 0x3C3C, 0x3C3F, + 0x3CC0, 0x3CC3, 0x3CCC, 0x3CCF, 0x3CF0, 0x3CF3, 0x3CFC, 0x3CFF, + 0x3F00, 0x3F03, 0x3F0C, 0x3F0F, 0x3F30, 0x3F33, 0x3F3C, 0x3F3F, + 0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF, + 0xC000, 0xC003, 0xC00C, 0xC00F, 0xC030, 0xC033, 0xC03C, 0xC03F, + 0xC0C0, 0xC0C3, 0xC0CC, 0xC0CF, 0xC0F0, 0xC0F3, 0xC0FC, 0xC0FF, + 0xC300, 0xC303, 0xC30C, 0xC30F, 0xC330, 0xC333, 0xC33C, 0xC33F, + 0xC3C0, 0xC3C3, 0xC3CC, 0xC3CF, 0xC3F0, 0xC3F3, 0xC3FC, 0xC3FF, + 0xCC00, 0xCC03, 0xCC0C, 0xCC0F, 0xCC30, 0xCC33, 0xCC3C, 0xCC3F, + 0xCCC0, 0xCCC3, 0xCCCC, 0xCCCF, 0xCCF0, 0xCCF3, 0xCCFC, 0xCCFF, + 0xCF00, 0xCF03, 0xCF0C, 0xCF0F, 0xCF30, 0xCF33, 0xCF3C, 0xCF3F, + 0xCFC0, 0xCFC3, 0xCFCC, 0xCFCF, 0xCFF0, 0xCFF3, 0xCFFC, 0xCFFF, + 0xF000, 0xF003, 0xF00C, 0xF00F, 0xF030, 0xF033, 0xF03C, 0xF03F, + 0xF0C0, 0xF0C3, 0xF0CC, 0xF0CF, 0xF0F0, 0xF0F3, 0xF0FC, 0xF0FF, + 0xF300, 0xF303, 0xF30C, 0xF30F, 0xF330, 0xF333, 0xF33C, 0xF33F, + 0xF3C0, 0xF3C3, 0xF3CC, 0xF3CF, 0xF3F0, 0xF3F3, 0xF3FC, 0xF3FF, + 0xFC00, 0xFC03, 0xFC0C, 0xFC0F, 0xFC30, 0xFC33, 0xFC3C, 0xFC3F, + 0xFCC0, 0xFCC3, 0xFCCC, 0xFCCF, 0xFCF0, 0xFCF3, 0xFCFC, 0xFCFF, + 0xFF00, 0xFF03, 0xFF0C, 0xFF0F, 0xFF30, 0xFF33, 0xFF3C, 0xFF3F, + 0xFFC0, 0xFFC3, 0xFFCC, 0xFFCF, 0xFFF0, 0xFFF3, 0xFFFC, 0xFFFF +}; + +uint16 MultiExpTable[256] = { + 0x0000, 0x0005, 0x000A, 0x000F, 0x0050, 0x0055, 0x005A, 0x005F, + 0x00A0, 0x00A5, 0x00AA, 0x00AF, 0x00F0, 0x00F5, 0x00FA, 0x00FF, + 0x0500, 0x0505, 0x050A, 0x050F, 0x0550, 0x0555, 0x055A, 0x055F, + 0x05A0, 0x05A5, 0x05AA, 0x05AF, 0x05F0, 0x05F5, 0x05FA, 0x05FF, + 0x0A00, 0x0A05, 0x0A0A, 0x0A0F, 0x0A50, 0x0A55, 0x0A5A, 0x0A5F, + 0x0AA0, 0x0AA5, 0x0AAA, 0x0AAF, 0x0AF0, 0x0AF5, 0x0AFA, 0x0AFF, + 0x0F00, 0x0F05, 0x0F0A, 0x0F0F, 0x0F50, 0x0F55, 0x0F5A, 0x0F5F, + 0x0FA0, 0x0FA5, 0x0FAA, 0x0FAF, 0x0FF0, 0x0FF5, 0x0FFA, 0x0FFF, + 0x5000, 0x5005, 0x500A, 0x500F, 0x5050, 0x5055, 0x505A, 0x505F, + 0x50A0, 0x50A5, 0x50AA, 0x50AF, 0x50F0, 0x50F5, 0x50FA, 0x50FF, + 0x5500, 0x5505, 0x550A, 0x550F, 0x5550, 0x5555, 0x555A, 0x555F, + 0x55A0, 0x55A5, 0x55AA, 0x55AF, 0x55F0, 0x55F5, 0x55FA, 0x55FF, + 0x5A00, 0x5A05, 0x5A0A, 0x5A0F, 0x5A50, 0x5A55, 0x5A5A, 0x5A5F, + 0x5AA0, 0x5AA5, 0x5AAA, 0x5AAF, 0x5AF0, 0x5AF5, 0x5AFA, 0x5AFF, + 0x5F00, 0x5F05, 0x5F0A, 0x5F0F, 0x5F50, 0x5F55, 0x5F5A, 0x5F5F, + 0x5FA0, 0x5FA5, 0x5FAA, 0x5FAF, 0x5FF0, 0x5FF5, 0x5FFA, 0x5FFF, + 0xA000, 0xA005, 0xA00A, 0xA00F, 0xA050, 0xA055, 0xA05A, 0xA05F, + 0xA0A0, 0xA0A5, 0xA0AA, 0xA0AF, 0xA0F0, 0xA0F5, 0xA0FA, 0xA0FF, + 0xA500, 0xA505, 0xA50A, 0xA50F, 0xA550, 0xA555, 0xA55A, 0xA55F, + 0xA5A0, 0xA5A5, 0xA5AA, 0xA5AF, 0xA5F0, 0xA5F5, 0xA5FA, 0xA5FF, + 0xAA00, 0xAA05, 0xAA0A, 0xAA0F, 0xAA50, 0xAA55, 0xAA5A, 0xAA5F, + 0xAAA0, 0xAAA5, 0xAAAA, 0xAAAF, 0xAAF0, 0xAAF5, 0xAAFA, 0xAAFF, + 0xAF00, 0xAF05, 0xAF0A, 0xAF0F, 0xAF50, 0xAF55, 0xAF5A, 0xAF5F, + 0xAFA0, 0xAFA5, 0xAFAA, 0xAFAF, 0xAFF0, 0xAFF5, 0xAFFA, 0xAFFF, + 0xF000, 0xF005, 0xF00A, 0xF00F, 0xF050, 0xF055, 0xF05A, 0xF05F, + 0xF0A0, 0xF0A5, 0xF0AA, 0xF0AF, 0xF0F0, 0xF0F5, 0xF0FA, 0xF0FF, + 0xF500, 0xF505, 0xF50A, 0xF50F, 0xF550, 0xF555, 0xF55A, 0xF55F, + 0xF5A0, 0xF5A5, 0xF5AA, 0xF5AF, 0xF5F0, 0xF5F5, 0xF5FA, 0xF5FF, + 0xFA00, 0xFA05, 0xFA0A, 0xFA0F, 0xFA50, 0xFA55, 0xFA5A, 0xFA5F, + 0xFAA0, 0xFAA5, 0xFAAA, 0xFAAF, 0xFAF0, 0xFAF5, 0xFAFA, 0xFAFF, + 0xFF00, 0xFF05, 0xFF0A, 0xFF0F, 0xFF50, 0xFF55, 0xFF5A, 0xFF5F, + 0xFFA0, 0xFFA5, 0xFFAA, 0xFFAF, 0xFFF0, 0xFFF5, 0xFFFA, 0xFFFF +}; + +#ifdef GLOBAL_VARS +static uint16 mx[8]; // VIC registers +static uint8 my[8]; +static uint8 mx8; +static uint8 ctrl1, ctrl2; +static uint8 lpx, lpy; +static uint8 me, mxe, mye, mdp, mmc; +static uint8 vbase; +static uint8 irq_flag, irq_mask; +static uint8 clx_spr, clx_bgr; +static uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1; +static uint8 sc[8]; + +static uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM +static C64 *the_c64; // Pointer to C64 +static C64Display *the_display; // Pointer to C64Display +static MOS6510 *the_cpu; // Pointer to 6510 +static MOS6569 *the_vic; // Pointer to self + +static uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f") + +static uint8 ec_color, b0c_color, b1c_color, b2c_color, b3c_color; // Indices for exterior/background colors +static uint8 mm0_color, mm1_color; // Indices for MOB multicolors +static uint8 spr_color[8]; // Indices for MOB colors + +static uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines +static uint8 color_line[40]; // Buffer for color line, read in Bad Lines + +#ifdef __POWERPC__ +static double chunky_tmp[DISPLAY_X/8]; // Temporary line buffer for GameKit speedup +#endif +static uint8 *chunky_ptr; // Pointer in chunky bitmap buffer +static uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer +static uint8 *fore_mask_ptr; // Pointer in fore_mask_buf +static int xmod; // Number of bytes per row + +static uint16 raster_x; // Current raster x position +static uint16 raster_y; // Current raster line +static uint16 irq_raster; // Interrupt raster line +static uint16 dy_start; // Comparison values for border logic +static uint16 dy_stop; +static uint16 rc; // Row counter +static uint16 vc; // Video counter +static uint16 vc_base; // Video counter base +static uint16 x_scroll; // X scroll value +static uint16 y_scroll; // Y scroll value +static uint16 cia_vabase; // CIA VA14/15 video base + +static int cycle; // Current cycle in line (1..63) + +static int display_idx; // Index of current display mode +static int ml_index; // Index in matrix/color_line[] +static int skip_counter; // Counter for frame-skipping + +static uint16 mc[8]; // Sprite data counters +static uint16 mc_base[8]; // Sprite data counter bases + +static uint8 spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities +static uint8 fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities + +static bool display_state; // true: Display state, false: Idle state +static bool border_on; // Flag: Upper/lower border on +static bool frame_skipped; // Flag: Frame is being skipped +static bool bad_lines_enabled; // Flag: Bad Lines enabled for this frame +static bool lp_triggered; // Flag: Lightpen was triggered in this frame +static bool is_bad_line; // Flag: Current line is Bad Line +static bool draw_this_line; // Flag: This line is drawn on the screen +static bool ud_border_on; // Flag: Upper/lower border on +static bool vblanking; // Flag: VBlank in next cycle + +static bool border_on_sample[5]; // Samples of border state at different cycles (1, 17, 18, 56, 57) +static uint8 border_color_sample[DISPLAY_X/8]; // Samples of border color at each "displayed" cycle + +static uint16 matrix_base; // Video matrix base +static uint16 char_base; // Character generator base +static uint16 bitmap_base; // Bitmap base + +static uint8 ref_cnt; // Refresh counter +static uint8 spr_exp_y; // 8 sprite y expansion flipflops +static uint8 spr_dma_on; // 8 flags: Sprite DMA active +static uint8 spr_disp_on; // 8 flags: Sprite display active +static uint8 spr_draw; // 8 flags: Draw sprite in this line +static uint16 spr_ptr[8]; // Sprite data pointers + +static uint8 gfx_data, char_data, color_data, last_char_data; +static uint8 spr_data[8][4]; // Sprite data read +static uint8 spr_draw_data[8][4]; // Sprite data for drawing + +static uint32 first_ba_cycle; // Cycle when BA first went low +#endif + + +/* + * Constructor: Initialize variables + */ + +MOS6569::MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color) +#ifndef GLOBAL_VARS + : ram(RAM), char_rom(Char), color_ram(Color), the_c64(c64), the_display(disp), the_cpu(CPU) +#endif +{ + int i; + + // Set pointers +#ifdef GLOBAL_VARS + the_vic = this; + the_c64 = c64; + the_display = disp; + the_cpu = CPU; + ram = RAM; + char_rom = Char; + color_ram = Color; +#endif + matrix_base = 0; + char_base = 0; + bitmap_base = 0; + + // Get bitmap info + chunky_ptr = chunky_line_start = disp->BitmapBase(); + xmod = disp->BitmapXMod(); + + // Initialize VIC registers + mx8 = 0; + ctrl1 = ctrl2 = 0; + lpx = lpy = 0; + me = mxe = mye = mdp = mmc = 0; + vbase = irq_flag = irq_mask = 0; + clx_spr = clx_bgr = 0; + cia_vabase = 0; + ec = b0c = b1c = b2c = b3c = mm0 = mm1 = 0; + for (i=0; i<8; i++) mx[i] = my[i] = sc[i] = 0; + + // Initialize other variables + raster_y = TOTAL_RASTERS - 1; + rc = 7; + irq_raster = vc = vc_base = x_scroll = y_scroll = 0; + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + ml_index = 0; + + cycle = 1; + display_idx = 0; + display_state = false; + border_on = ud_border_on = vblanking = false; + lp_triggered = draw_this_line = false; + + spr_dma_on = spr_disp_on = 0; + for (i=0; i<8; i++) { + mc[i] = 63; + spr_ptr[i] = 0; + } + + frame_skipped = false; + skip_counter = 1; + + memset(spr_coll_buf, 0, 0x180); + memset(fore_mask_buf, 0, 0x180/8); + + // Preset colors to black + disp->InitColors(colors); + ec_color = b0c_color = b1c_color = b2c_color = b3c_color = mm0_color = mm1_color = colors[0]; + for (i=0; i<8; i++) spr_color[i] = colors[0]; +} + + +/* + * Reinitialize the colors table for when the palette has changed + */ + +void MOS6569::ReInitColors(void) +{ + int i; + + // Build inverse color table. + uint8 xlate_colors[256]; + memset(xlate_colors, 0, sizeof(xlate_colors)); + for (i = 0; i < 16; i++) + xlate_colors[colors[i]] = i; + + // Get the new colors. + the_display->InitColors(colors); + + // Build color translation table. + for (i = 0; i < 256; i++) + xlate_colors[i] = colors[xlate_colors[i]]; + + // Translate all the old colors variables. + ec_color = colors[ec]; + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + for (i = 0; i < 8; i++) + spr_color[i] = colors[sc[i]]; + + // Translate the border color sample buffer. + for (int x = 0; x < sizeof(border_color_sample); x++) + border_color_sample[x] = xlate_colors[border_color_sample[x]]; + + // Translate the chunky buffer. + uint8 *scanline = the_display->BitmapBase(); + for (int y = 0; y < DISPLAY_Y; y++) { + for (int x = 0; x < DISPLAY_X; x++) + scanline[x] = xlate_colors[scanline[x]]; + scanline += xmod; + } +} + + +/* + * Get VIC state + */ + +void MOS6569::GetState(MOS6569State *vd) +{ + int i; + + vd->m0x = mx[0] & 0xff; vd->m0y = my[0]; + vd->m1x = mx[1] & 0xff; vd->m1y = my[1]; + vd->m2x = mx[2] & 0xff; vd->m2y = my[2]; + vd->m3x = mx[3] & 0xff; vd->m3y = my[3]; + vd->m4x = mx[4] & 0xff; vd->m4y = my[4]; + vd->m5x = mx[5] & 0xff; vd->m5y = my[5]; + vd->m6x = mx[6] & 0xff; vd->m6y = my[6]; + vd->m7x = mx[7] & 0xff; vd->m7y = my[7]; + vd->mx8 = mx8; + + vd->ctrl1 = (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + vd->raster = raster_y & 0xff; + vd->lpx = lpx; vd->lpy = lpy; + vd->ctrl2 = ctrl2; + vd->vbase = vbase; + vd->irq_flag = irq_flag; + vd->irq_mask = irq_mask; + + vd->me = me; vd->mxe = mxe; vd->mye = mye; vd->mdp = mdp; vd->mmc = mmc; + vd->mm = clx_spr; vd->md = clx_bgr; + + vd->ec = ec; + vd->b0c = b0c; vd->b1c = b1c; vd->b2c = b2c; vd->b3c = b3c; + vd->mm0 = mm0; vd->mm1 = mm1; + vd->m0c = sc[0]; + vd->m1c = sc[1]; + vd->m2c = sc[2]; + vd->m3c = sc[3]; + vd->m4c = sc[4]; + vd->m5c = sc[5]; + vd->m6c = sc[6]; + vd->m7c = sc[7]; + + vd->pad0 = 0; + vd->irq_raster = irq_raster; + vd->vc = vc; + vd->vc_base = vc_base; + vd->rc = rc; + vd->spr_dma = spr_dma_on; + vd->spr_disp = spr_disp_on; + for (i=0; i<8; i++) { + vd->mc[i] = mc[i]; + vd->mc_base[i] = mc_base[i]; + } + vd->display_state = display_state; + vd->bad_line = raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled; + vd->bad_line_enable = bad_lines_enabled; + vd->lp_triggered = lp_triggered; + vd->border_on = border_on; + + vd->bank_base = cia_vabase; + vd->matrix_base = ((vbase & 0xf0) << 6) | cia_vabase; + vd->char_base = ((vbase & 0x0e) << 10) | cia_vabase; + vd->bitmap_base = ((vbase & 0x08) << 10) | cia_vabase; + for (i=0; i<8; i++) + vd->sprite_base[i] = spr_ptr[i] | cia_vabase; + + vd->cycle = cycle; + vd->raster_x = raster_x; + vd->ml_index = ml_index; + vd->ref_cnt = ref_cnt; + vd->last_vic_byte = LastVICByte; + vd->ud_border_on = ud_border_on; +} + + +/* + * Set VIC state (only works if in VBlank) + */ + +void MOS6569::SetState(MOS6569State *vd) +{ + int i, j; + + mx[0] = vd->m0x; my[0] = vd->m0y; + mx[1] = vd->m1x; my[1] = vd->m1y; + mx[2] = vd->m2x; my[2] = vd->m2y; + mx[3] = vd->m3x; my[3] = vd->m3y; + mx[4] = vd->m4x; my[4] = vd->m4y; + mx[5] = vd->m5x; my[5] = vd->m5y; + mx[6] = vd->m6x; my[6] = vd->m6y; + mx[7] = vd->m7x; my[7] = vd->m7y; + mx8 = vd->mx8; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + + ctrl1 = vd->ctrl1; + ctrl2 = vd->ctrl2; + x_scroll = ctrl2 & 7; + y_scroll = ctrl1 & 7; + if (ctrl1 & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + + raster_y = 0; + lpx = vd->lpx; lpy = vd->lpy; + + vbase = vd->vbase; + cia_vabase = vd->bank_base; + matrix_base = (vbase & 0xf0) << 6; + char_base = (vbase & 0x0e) << 10; + bitmap_base = (vbase & 0x08) << 10; + + irq_flag = vd->irq_flag; + irq_mask = vd->irq_mask; + + me = vd->me; mxe = vd->mxe; mye = vd->mye; mdp = vd->mdp; mmc = vd->mmc; + clx_spr = vd->mm; clx_bgr = vd->md; + + ec = vd->ec; + ec_color = colors[ec]; + + b0c = vd->b0c; b1c = vd->b1c; b2c = vd->b2c; b3c = vd->b3c; + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + + mm0 = vd->mm0; mm1 = vd->mm1; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + + sc[0] = vd->m0c; sc[1] = vd->m1c; + sc[2] = vd->m2c; sc[3] = vd->m3c; + sc[4] = vd->m4c; sc[5] = vd->m5c; + sc[6] = vd->m6c; sc[7] = vd->m7c; + for (i=0; i<8; i++) + spr_color[i] = colors[sc[i]]; + + irq_raster = vd->irq_raster; + vc = vd->vc; + vc_base = vd->vc_base; + rc = vd->rc; + spr_dma_on = vd->spr_dma; + spr_disp_on = vd->spr_disp; + for (i=0; i<8; i++) { + mc[i] = vd->mc[i]; + mc_base[i] = vd->mc_base[i]; + spr_ptr[i] = vd->sprite_base[i] & 0x3fff; + } + display_state = vd->display_state; + bad_lines_enabled = vd->bad_line_enable; + lp_triggered = vd->lp_triggered; + border_on = vd->border_on; + + cycle = vd->cycle; + raster_x = vd->raster_x; + ml_index = vd->ml_index; + ref_cnt = vd->ref_cnt; + LastVICByte = vd->last_vic_byte; + ud_border_on = vd->ud_border_on; +} + + +/* + * Trigger raster IRQ + */ + +#ifdef GLOBAL_VARS +static inline void raster_irq(void) +#else +inline void MOS6569::raster_irq(void) +#endif +{ + irq_flag |= 0x01; + if (irq_mask & 0x01) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } +} + + +/* + * Read from VIC register + */ + +uint8 MOS6569::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + return mx[adr >> 1]; + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + return my[adr >> 1]; + + case 0x10: // Sprite X position MSB + return mx8; + + case 0x11: // Control register 1 + return (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + + case 0x12: // Raster counter + return raster_y; + + case 0x13: // Light pen X + return lpx; + + case 0x14: // Light pen Y + return lpy; + + case 0x15: // Sprite enable + return me; + + case 0x16: // Control register 2 + return ctrl2 | 0xc0; + + case 0x17: // Sprite Y expansion + return mye; + + case 0x18: // Memory pointers + return vbase | 0x01; + + case 0x19: // IRQ flags + return irq_flag | 0x70; + + case 0x1a: // IRQ mask + return irq_mask | 0xf0; + + case 0x1b: // Sprite data priority + return mdp; + + case 0x1c: // Sprite multicolor + return mmc; + + case 0x1d: // Sprite X expansion + return mxe; + + case 0x1e:{ // Sprite-sprite collision + uint8 ret = clx_spr; + clx_spr = 0; // Read and clear + return ret; + } + + case 0x1f:{ // Sprite-background collision + uint8 ret = clx_bgr; + clx_bgr = 0; // Read and clear + return ret; + } + + case 0x20: return ec | 0xf0; + case 0x21: return b0c | 0xf0; + case 0x22: return b1c | 0xf0; + case 0x23: return b2c | 0xf0; + case 0x24: return b3c | 0xf0; + case 0x25: return mm0 | 0xf0; + case 0x26: return mm1 | 0xf0; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + return sc[adr - 0x27] | 0xf0; + + default: + return 0xff; + } +} + + +/* + * Write to VIC register + */ + +void MOS6569::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + mx[adr >> 1] = (mx[adr >> 1] & 0xff00) | byte; + break; + + case 0x10:{ + int i, j; + mx8 = byte; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + break; + } + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + my[adr >> 1] = byte; + break; + + case 0x11:{ // Control register 1 + ctrl1 = byte; + y_scroll = byte & 7; + + uint16 new_irq_raster = (irq_raster & 0xff) | ((byte & 0x80) << 1); + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + + if (byte & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + + // In line $30, the DEN bit controls if Bad Lines can occur + if (raster_y == 0x30 && byte & 0x10) + bad_lines_enabled = true; + + // Bad Line condition? + is_bad_line = (raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled); + + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + } + + case 0x12:{ // Raster counter + uint16 new_irq_raster = (irq_raster & 0xff00) | byte; + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + break; + } + + case 0x15: // Sprite enable + me = byte; + break; + + case 0x16: // Control register 2 + ctrl2 = byte; + x_scroll = byte & 7; + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + + case 0x17: // Sprite Y expansion + mye = byte; + spr_exp_y |= ~byte; + break; + + case 0x18: // Memory pointers + vbase = byte; + matrix_base = (byte & 0xf0) << 6; + char_base = (byte & 0x0e) << 10; + bitmap_base = (byte & 0x08) << 10; + break; + + case 0x19: // IRQ flags + irq_flag = irq_flag & (~byte & 0x0f); + if (irq_flag & irq_mask) // Set master bit if allowed interrupt still pending + irq_flag |= 0x80; + else + the_cpu->ClearVICIRQ(); // Else clear interrupt + break; + + case 0x1a: // IRQ mask + irq_mask = byte & 0x0f; + if (irq_flag & irq_mask) { // Trigger interrupt if pending and now allowed + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } else { + irq_flag &= 0x7f; + the_cpu->ClearVICIRQ(); + } + break; + + case 0x1b: // Sprite data priority + mdp = byte; + break; + + case 0x1c: // Sprite multicolor + mmc = byte; + break; + + case 0x1d: // Sprite X expansion + mxe = byte; + break; + + case 0x20: ec_color = colors[ec = byte]; break; + case 0x21: b0c_color = colors[b0c = byte]; break; + case 0x22: b1c_color = colors[b1c = byte]; break; + case 0x23: b2c_color = colors[b2c = byte]; break; + case 0x24: b3c_color = colors[b3c = byte]; break; + case 0x25: mm0_color = colors[mm0 = byte]; break; + case 0x26: mm1_color = colors[mm1 = byte]; break; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + spr_color[adr - 0x27] = colors[sc[adr - 0x27] = byte]; + break; + } +} + + +/* + * CIA VA14/15 has changed + */ + +void MOS6569::ChangedVA(uint16 new_va) +{ + cia_vabase = new_va << 14; + WriteRegister(0x18, vbase); // Force update of memory pointers +} + + +/* + * Trigger lightpen interrupt, latch lightpen coordinates + */ + +void MOS6569::TriggerLightpen(void) +{ + if (!lp_triggered) { // Lightpen triggers only once per frame + lp_triggered = true; + + lpx = raster_x >> 1; // Latch current coordinates + lpy = raster_y; + + irq_flag |= 0x08; // Trigger IRQ + if (irq_mask & 0x08) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } +} + + +/* + * Read a byte from the VIC's address space + */ + +#ifdef GLOBAL_VARS +static inline uint8 read_byte(uint16 adr) +#else +inline uint8 MOS6569::read_byte(uint16 adr) +#endif +{ + uint16 va = adr | cia_vabase; + if ((va & 0x7000) == 0x1000) +#ifdef GLOBAL_VARS + return the_vic->LastVICByte = char_rom[va & 0x0fff]; +#else + return LastVICByte = char_rom[va & 0x0fff]; +#endif + else +#ifdef GLOBAL_VARS + return the_vic->LastVICByte = ram[va]; +#else + return LastVICByte = ram[va]; +#endif +} + + +/* + * Quick memset of 8 bytes + */ + +inline void memset8(uint8 *p, uint8 c) +{ + p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = p[6] = p[7] = c; +} + + +/* + * Video matrix access + */ + +#ifdef __i386 +inline +#endif +#ifdef GLOBAL_VARS +static void matrix_access(void) +#else +void MOS6569::matrix_access(void) +#endif +{ + if (the_cpu->BALow) { + if (the_c64->CycleCounter-first_ba_cycle < 3) + matrix_line[ml_index] = color_line[ml_index] = 0xff; + else { + uint16 adr = (vc & 0x03ff) | matrix_base; + matrix_line[ml_index] = read_byte(adr); + color_line[ml_index] = color_ram[adr & 0x03ff]; + } + } +} + + +/* + * Graphics data access + */ + +#ifdef __i386 +inline +#endif +#ifdef GLOBAL_VARS +static void graphics_access(void) +#else +void MOS6569::graphics_access(void) +#endif +{ + if (display_state) { + + uint16 adr; + if (ctrl1 & 0x20) // Bitmap + adr = ((vc & 0x03ff) << 3) | bitmap_base | rc; + else // Text + adr = (matrix_line[ml_index] << 3) | char_base | rc; + if (ctrl1 & 0x40) // ECM + adr &= 0xf9ff; + gfx_data = read_byte(adr); + char_data = matrix_line[ml_index]; + color_data = color_line[ml_index]; + ml_index++; + vc++; + + } else { + + // Display is off + gfx_data = read_byte(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + char_data = color_data = 0; + } +} + + +/* + * Background display (8 pixels) + */ + +#ifdef GLOBAL_VARS +static void draw_background(void) +#else +void MOS6569::draw_background(void) +#endif +{ + uint8 *p = chunky_ptr; + uint8 c; + + if (!draw_this_line) + return; + + switch (display_idx) { + case 0: // Standard text + case 1: // Multicolor text + case 3: // Multicolor bitmap + c = b0c_color; + break; + case 2: // Standard bitmap + c = colors[last_char_data]; + break; + case 4: // ECM text + if (last_char_data & 0x80) + if (last_char_data & 0x40) + c = b3c_color; + else + c = b2c_color; + else + if (last_char_data & 0x40) + c = b1c_color; + else + c = b0c_color; + break; + default: + c = colors[0]; + break; + } + memset8(p, c); +} + + +/* + * Graphics display (8 pixels) + */ + +#ifdef __i386 +inline +#endif +#ifdef GLOBAL_VARS +static void draw_graphics(void) +#else +void MOS6569::draw_graphics(void) +#endif +{ + uint8 *p = chunky_ptr + x_scroll; + uint8 c[4], data; + + if (!draw_this_line) + return; + if (ud_border_on) { + draw_background(); + return; + } + + switch (display_idx) { + + case 0: // Standard text + c[0] = b0c_color; + c[1] = colors[color_data]; + goto draw_std; + + case 1: // Multicolor text + if (color_data & 8) { + c[0] = b0c_color; + c[1] = b1c_color; + c[2] = b2c_color; + c[3] = colors[color_data & 7]; + goto draw_multi; + } else { + c[0] = b0c_color; + c[1] = colors[color_data]; + goto draw_std; + } + + case 2: // Standard bitmap + c[0] = colors[char_data]; + c[1] = colors[char_data >> 4]; + goto draw_std; + + case 3: // Multicolor bitmap + c[0]= b0c_color; + c[1] = colors[char_data >> 4]; + c[2] = colors[char_data]; + c[3] = colors[color_data]; + goto draw_multi; + + case 4: // ECM text + if (char_data & 0x80) + if (char_data & 0x40) + c[0] = b3c_color; + else + c[0] = b2c_color; + else + if (char_data & 0x40) + c[0] = b1c_color; + else + c[0] = b0c_color; + c[1] = colors[color_data]; + goto draw_std; + + case 5: // Invalid multicolor text + memset8(p, colors[0]); + if (color_data & 8) { + fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; + fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); + } else { + fore_mask_ptr[0] |= gfx_data >> x_scroll; + fore_mask_ptr[1] |= gfx_data << (7-x_scroll); + } + return; + + case 6: // Invalid standard bitmap + memset8(p, colors[0]); + fore_mask_ptr[0] |= gfx_data >> x_scroll; + fore_mask_ptr[1] |= gfx_data << (7-x_scroll); + return; + + case 7: // Invalid multicolor bitmap + memset8(p, colors[0]); + fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; + fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); + return; + + default: // Can't happen + return; + } + +draw_std: + + fore_mask_ptr[0] |= gfx_data >> x_scroll; + fore_mask_ptr[1] |= gfx_data << (7-x_scroll); + + data = gfx_data; + p[7] = c[data & 1]; data >>= 1; + p[6] = c[data & 1]; data >>= 1; + p[5] = c[data & 1]; data >>= 1; + p[4] = c[data & 1]; data >>= 1; + p[3] = c[data & 1]; data >>= 1; + p[2] = c[data & 1]; data >>= 1; + p[1] = c[data & 1]; data >>= 1; + p[0] = c[data]; + return; + +draw_multi: + + fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; + fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); + + data = gfx_data; + p[7] = p[6] = c[data & 3]; data >>= 2; + p[5] = p[4] = c[data & 3]; data >>= 2; + p[3] = p[2] = c[data & 3]; data >>= 2; + p[1] = p[0] = c[data]; + return; +} + + +/* + * Sprite display + */ + +#ifdef GLOBAL_VARS +inline static void draw_sprites(void) +#else +inline void MOS6569::draw_sprites(void) +#endif +{ + int i; + int snum, sbit; // Sprite number/bit mask + int spr_coll=0, gfx_coll=0; + + // Clear sprite collision buffer + { + uint32 *lp = (uint32 *)spr_coll_buf - 1; + for (i=0; i> (8-sshift)); + + if (mxe & sbit) { // X-expanded + if (mx[snum] > DISPLAY_X-56) + continue; + + uint32 sdata_l = 0, sdata_r = 0, fore_mask_r; + fore_mask_r = (((*(fmbp+4) << 24) | (*(fmbp+5) << 16) | (*(fmbp+6) << 8) + | (*(fmbp+7))) << sshift) | (*(fmbp+8) >> (8-sshift)); + + if (mmc & sbit) { // Multicolor mode + uint32 plane0_l, plane0_r, plane1_l, plane1_r; + + // Expand sprite data + sdata_l = MultiExpTable[sdata >> 24 & 0xff] << 16 | MultiExpTable[sdata >> 16 & 0xff]; + sdata_r = MultiExpTable[sdata >> 8 & 0xff] << 16; + + // Convert sprite chunky pixels to bitplanes + plane0_l = (sdata_l & 0x55555555) | (sdata_l & 0x55555555) << 1; + plane1_l = (sdata_l & 0xaaaaaaaa) | (sdata_l & 0xaaaaaaaa) >> 1; + plane0_r = (sdata_r & 0x55555555) | (sdata_r & 0x55555555) << 1; + plane1_r = (sdata_r & 0xaaaaaaaa) | (sdata_r & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if ((fore_mask & (plane0_l | plane1_l)) || (fore_mask_r & (plane0_r | plane1_r))) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0_l &= ~fore_mask; // Mask sprite if in background + plane1_l &= ~fore_mask; + plane0_r &= ~fore_mask_r; + plane1_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, plane0_l<<=1, plane1_l<<=1) { + uint8 col; + if (plane1_l & 0x80000000) { + if (plane0_l & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_l & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + for (; i<48; i++, plane0_r<<=1, plane1_r<<=1) { + uint8 col; + if (plane1_r & 0x80000000) { + if (plane0_r & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_r & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Expand sprite data + sdata_l = ExpTable[sdata >> 24 & 0xff] << 16 | ExpTable[sdata >> 16 & 0xff]; + sdata_r = ExpTable[sdata >> 8 & 0xff] << 16; + + // Collision with graphics? + if ((fore_mask & sdata_l) || (fore_mask_r & sdata_r)) { + gfx_coll |= sbit; + if (mdp & sbit) { + sdata_l &= ~fore_mask; // Mask sprite if in background + sdata_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, sdata_l<<=1) + if (sdata_l & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + for (; i<48; i++, sdata_r<<=1) + if (sdata_r & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + } + + } else { // Unexpanded + + if (mmc & sbit) { // Multicolor mode + uint32 plane0, plane1; + + // Convert sprite chunky pixels to bitplanes + plane0 = (sdata & 0x55555555) | (sdata & 0x55555555) << 1; + plane1 = (sdata & 0xaaaaaaaa) | (sdata & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if (fore_mask & (plane0 | plane1)) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0 &= ~fore_mask; // Mask sprite if in background + plane1 &= ~fore_mask; + } + } + + // Paint sprite + for (i=0; i<24; i++, plane0<<=1, plane1<<=1) { + uint8 col; + if (plane1 & 0x80000000) { + if (plane0 & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0 & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Collision with graphics? + if (fore_mask & sdata) { + gfx_coll |= sbit; + if (mdp & sbit) + sdata &= ~fore_mask; // Mask sprite if in background + } + + // Paint sprite + for (i=0; i<24; i++, sdata<<=1) + if (sdata & 0x80000000) { + if (q[i]) { // Collision with sprite? + spr_coll |= q[i] | sbit; + } else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + } + } + } + } + + if (ThePrefs.SpriteCollisions) { + + // Check sprite-sprite collisions + if (clx_spr) + clx_spr |= spr_coll; + else { + clx_spr |= spr_coll; + irq_flag |= 0x04; + if (irq_mask & 0x04) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + + // Check sprite-background collisions + if (clx_bgr) + clx_bgr |= gfx_coll; + else { + clx_bgr |= gfx_coll; + irq_flag |= 0x02; + if (irq_mask & 0x02) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + } +} + + +#ifdef __POWERPC__ +static asm void fastcopy(register uchar *dst, register uchar *src); +static asm void fastcopy(register uchar *dst, register uchar *src) +{ + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + blr +} +#endif + + +/* + * Emulate one clock cycle, returns true if new raster line has started + */ + +// Set BA low +#define SetBALow \ + if (!the_cpu->BALow) { \ + first_ba_cycle = the_c64->CycleCounter; \ + the_cpu->BALow = true; \ + } + +// Turn on display if Bad Line +#define DisplayIfBadLine \ + if (is_bad_line) \ + display_state = true; + +// Turn on display and matrix access if Bad Line +#define FetchIfBadLine \ + if (is_bad_line) { \ + display_state = true; \ + SetBALow; \ + } + +// Turn on display and matrix access and reset RC if Bad Line +#define RCIfBadLine \ + if (is_bad_line) { \ + display_state = true; \ + rc = 0; \ + SetBALow; \ + } + +// Idle access +#define IdleAccess \ + read_byte(0x3fff) + +// Refresh access +#define RefreshAccess \ + read_byte(0x3f00 | ref_cnt--) + +// Turn on sprite DMA if necessary +#define CheckSpriteDMA \ + mask = 1; \ + for (i=0; i<8; i++, mask<<=1) \ + if ((me & mask) && (raster_y & 0xff) == my[i]) { \ + spr_dma_on |= mask; \ + mc_base[i] = 0; \ + if (mye & mask) \ + spr_exp_y &= ~mask; \ + } + +// Fetch sprite data pointer +#define SprPtrAccess(num) \ + spr_ptr[num] = read_byte(matrix_base | 0x03f8 | num) << 6; + +// Fetch sprite data, increment data counter +#define SprDataAccess(num, bytenum) \ + if (spr_dma_on & (1 << num)) { \ + spr_data[num][bytenum] = read_byte(mc[num] & 0x3f | spr_ptr[num]); \ + mc[num]++; \ + } else if (bytenum == 1) \ + IdleAccess; + +// Sample border color and increment chunky_ptr and fore_mask_ptr +#define SampleBorder \ + if (draw_this_line) { \ + if (border_on) \ + border_color_sample[cycle-13] = ec_color; \ + chunky_ptr += 8; \ + fore_mask_ptr++; \ + } + + +bool MOS6569::EmulateCycle(void) +{ + uint8 mask; + int i; + + switch (cycle) { + + // Fetch sprite pointer 3, increment raster counter, trigger raster IRQ, + // test for Bad Line, reset BA if sprites 3 and 4 off, read data of sprite 3 + case 1: + if (raster_y == TOTAL_RASTERS-1) + + // Trigger VBlank in cycle 2 + vblanking = true; + + else { + + // Increment raster counter + raster_y++; + + // Trigger raster IRQ if IRQ line reached + if (raster_y == irq_raster) + raster_irq(); + + // In line $30, the DEN bit controls if Bad Lines can occur + if (raster_y == 0x30) + bad_lines_enabled = ctrl1 & 0x10; + + // Bad Line condition? + is_bad_line = (raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled); + + // Don't draw all lines, hide some at the top and bottom + draw_this_line = (raster_y >= FIRST_DISP_LINE && raster_y <= LAST_DISP_LINE && !frame_skipped); + } + + // First sample of border state + border_on_sample[0] = border_on; + + SprPtrAccess(3); + SprDataAccess(3, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x18)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 5, read data of sprite 3 + case 2: + if (vblanking) { + + // Vertical blank, reset counters + raster_y = vc_base = 0; + ref_cnt = 0xff; + lp_triggered = vblanking = false; + + if (!(frame_skipped = --skip_counter)) + skip_counter = ThePrefs.SkipFrames; + + the_c64->VBlank(!frame_skipped); + + // Get bitmap pointer for next frame. This must be done + // after calling the_c64->VBlank() because the preferences + // and screen configuration may have been changed there + chunky_line_start = the_display->BitmapBase(); + xmod = the_display->BitmapXMod(); + + // Trigger raster IRQ if IRQ in line 0 + if (irq_raster == 0) + raster_irq(); + + } + + // Our output goes here +#ifdef __POWERPC__ + chunky_ptr = (uint8 *)chunky_tmp; +#else + chunky_ptr = chunky_line_start; +#endif + + // Clear foreground mask + memset(fore_mask_buf, 0, DISPLAY_X/8); + fore_mask_ptr = fore_mask_buf; + + SprDataAccess(3,1); + SprDataAccess(3,2); + DisplayIfBadLine; + if (spr_dma_on & 0x20) + SetBALow; + break; + + // Fetch sprite pointer 4, reset BA is sprite 4 and 5 off + case 3: + SprPtrAccess(4); + SprDataAccess(4, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x30)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 6, read data of sprite 4 + case 4: + SprDataAccess(4, 1); + SprDataAccess(4, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x40) + SetBALow; + break; + + // Fetch sprite pointer 5, reset BA if sprite 5 and 6 off + case 5: + SprPtrAccess(5); + SprDataAccess(5, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x60)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 7, read data of sprite 5 + case 6: + SprDataAccess(5, 1); + SprDataAccess(5, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x80) + SetBALow; + break; + + // Fetch sprite pointer 6, reset BA if sprite 6 and 7 off + case 7: + SprPtrAccess(6); + SprDataAccess(6, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0xc0)) + the_cpu->BALow = false; + break; + + // Read data of sprite 6 + case 8: + SprDataAccess(6, 1); + SprDataAccess(6, 2); + DisplayIfBadLine; + break; + + // Fetch sprite pointer 7, reset BA if sprite 7 off + case 9: + SprPtrAccess(7); + SprDataAccess(7, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x80)) + the_cpu->BALow = false; + break; + + // Read data of sprite 7 + case 10: + SprDataAccess(7, 1); + SprDataAccess(7, 2); + DisplayIfBadLine; + break; + + // Refresh, reset BA + case 11: + RefreshAccess; + DisplayIfBadLine; + the_cpu->BALow = false; + break; + + // Refresh, turn on matrix access if Bad Line + case 12: + RefreshAccess; + FetchIfBadLine; + break; + + // Refresh, turn on matrix access if Bad Line, reset raster_x, graphics display starts here + case 13: + draw_background(); + SampleBorder; + RefreshAccess; + FetchIfBadLine; + raster_x = 0xfffc; + break; + + // Refresh, VCBASE->VCCOUNT, turn on matrix access and reset RC if Bad Line + case 14: + draw_background(); + SampleBorder; + RefreshAccess; + RCIfBadLine; + vc = vc_base; + break; + + // Refresh and matrix access, increment mc_base by 2 if y expansion flipflop is set + case 15: + draw_background(); + SampleBorder; + RefreshAccess; + FetchIfBadLine; + + for (i=0; i<8; i++) + if (spr_exp_y & (1 << i)) + mc_base[i] += 2; + + ml_index = 0; + matrix_access(); + break; + + // Graphics and matrix access, increment mc_base by 1 if y expansion flipflop is set + // and check if sprite DMA can be turned off + case 16: + draw_background(); + SampleBorder; + graphics_access(); + FetchIfBadLine; + + mask = 1; + for (i=0; i<8; i++, mask<<=1) { + if (spr_exp_y & mask) + mc_base[i]++; + if ((mc_base[i] & 0x3f) == 0x3f) + spr_dma_on &= ~mask; + } + + matrix_access(); + break; + + // Graphics and matrix access, turn off border in 40 column mode, display window starts here + case 17: + if (ctrl2 & 8) { + if (raster_y == dy_stop) + ud_border_on = true; + else { + if (ctrl1 & 0x10) { + if (raster_y == dy_start) + border_on = ud_border_on = false; + else + if (!ud_border_on) + border_on = false; + } else + if (!ud_border_on) + border_on = false; + } + } + + // Second sample of border state + border_on_sample[1] = border_on; + + draw_background(); + draw_graphics(); + SampleBorder; + graphics_access(); + FetchIfBadLine; + matrix_access(); + break; + + // Turn off border in 38 column mode + case 18: + if (!(ctrl2 & 8)) { + if (raster_y == dy_stop) + ud_border_on = true; + else { + if (ctrl1 & 0x10) { + if (raster_y == dy_start) + border_on = ud_border_on = false; + else + if (!ud_border_on) + border_on = false; + } else + if (!ud_border_on) + border_on = false; + } + } + + // Third sample of border state + border_on_sample[2] = border_on; + + // Falls through + + // Graphics and matrix access + case 19: case 20: case 21: case 22: case 23: case 24: + case 25: case 26: case 27: case 28: case 29: case 30: + case 31: case 32: case 33: case 34: case 35: case 36: + case 37: case 38: case 39: case 40: case 41: case 42: + case 43: case 44: case 45: case 46: case 47: case 48: + case 49: case 50: case 51: case 52: case 53: case 54: // Gnagna... + draw_graphics(); + SampleBorder; + graphics_access(); + FetchIfBadLine; + matrix_access(); + last_char_data = char_data; + break; + + // Last graphics access, turn off matrix access, turn on sprite DMA if Y coordinate is + // right and sprite is enabled, handle sprite y expansion, set BA for sprite 0 + case 55: + draw_graphics(); + SampleBorder; + graphics_access(); + DisplayIfBadLine; + + // Invert y expansion flipflop if bit in MYE is set + mask = 1; + for (i=0; i<8; i++, mask<<=1) + if (mye & mask) + spr_exp_y ^= mask; + CheckSpriteDMA; + + if (spr_dma_on & 0x01) { // Don't remove these braces! + SetBALow; + } else + the_cpu->BALow = false; + break; + + // Turn on border in 38 column mode, turn on sprite DMA if Y coordinate is right and + // sprite is enabled, set BA for sprite 0, display window ends here + case 56: + if (!(ctrl2 & 8)) + border_on = true; + + // Fourth sample of border state + border_on_sample[3] = border_on; + + draw_graphics(); + SampleBorder; + IdleAccess; + DisplayIfBadLine; + CheckSpriteDMA; + + if (spr_dma_on & 0x01) + SetBALow; + break; + + // Turn on border in 40 column mode, set BA for sprite 1, paint sprites + case 57: + if (ctrl2 & 8) + border_on = true; + + // Fifth sample of border state + border_on_sample[4] = border_on; + + // Sample spr_disp_on and spr_data for sprite drawing + if ((spr_draw = spr_disp_on)) + memcpy(spr_draw_data, spr_data, 8*4); + + // Turn off sprite display if DMA is off + mask = 1; + for (i=0; i<8; i++, mask<<=1) + if ((spr_disp_on & mask) && !(spr_dma_on & mask)) + spr_disp_on &= ~mask; + + draw_background(); + SampleBorder; + IdleAccess; + DisplayIfBadLine; + if (spr_dma_on & 0x02) + SetBALow; + break; + + // Fetch sprite pointer 0, mc_base->mc, turn on sprite display if necessary, + // turn off display if RC=7, read data of sprite 0 + case 58: + draw_background(); + SampleBorder; + + mask = 1; + for (i=0; i<8; i++, mask<<=1) { + mc[i] = mc_base[i]; + if ((spr_dma_on & mask) && (raster_y & 0xff) == my[i]) + spr_disp_on |= mask; + } + SprPtrAccess(0); + SprDataAccess(0, 0); + + if (rc == 7) { + vc_base = vc; + display_state = false; + } + if (is_bad_line || display_state) { + display_state = true; + rc = (rc + 1) & 7; + } + break; + + // Set BA for sprite 2, read data of sprite 0 + case 59: + draw_background(); + SampleBorder; + SprDataAccess(0, 1); + SprDataAccess(0, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x04) + SetBALow; + break; + + // Fetch sprite pointer 1, reset BA if sprite 1 and 2 off, graphics display ends here + case 60: + draw_background(); + SampleBorder; + + if (draw_this_line) { + + // Draw sprites + if (spr_draw && ThePrefs.SpritesOn) + draw_sprites(); + + // Draw border +#ifdef __POWERPC__ + if (border_on_sample[0]) + for (i=0; i<4; i++) + memset8((uint8 *)chunky_tmp+i*8, border_color_sample[i]); + if (border_on_sample[1]) + memset8((uint8 *)chunky_tmp+4*8, border_color_sample[4]); + if (border_on_sample[2]) + for (i=5; i<43; i++) + memset8((uint8 *)chunky_tmp+i*8, border_color_sample[i]); + if (border_on_sample[3]) + memset8((uint8 *)chunky_tmp+43*8, border_color_sample[43]); + if (border_on_sample[4]) + for (i=44; iBALow = false; + break; + + // Set BA for sprite 3, read data of sprite 1 + case 61: + SprDataAccess(1, 1); + SprDataAccess(1, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x08) + SetBALow; + break; + + // Read sprite pointer 2, reset BA if sprite 2 and 3 off, read data of sprite 2 + case 62: + SprPtrAccess(2); + SprDataAccess(2, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x0c)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 4, read data of sprite 2 + case 63: + SprDataAccess(2, 1); + SprDataAccess(2, 2); + DisplayIfBadLine; + + if (raster_y == dy_stop) + ud_border_on = true; + else + if (ctrl1 & 0x10 && raster_y == dy_start) + ud_border_on = false; + + if (spr_dma_on & 0x10) + SetBALow; + + // Last cycle + raster_x += 8; + cycle = 1; + return true; + } + + // Next cycle + raster_x += 8; + cycle++; + return false; +} diff --git a/Src/Version.h b/Src/Version.h new file mode 100644 index 0000000..e102013 --- /dev/null +++ b/Src/Version.h @@ -0,0 +1,15 @@ +/* + * Version.h - Version information + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _VERSION_H +#define _VERSION_H + +// Version/revision +const int FRODO_VERSION = 4; +const int FRODO_REVISION = 1; +const char VERSION_STRING[] = "Frodo V4.1b"; + +#endif diff --git a/Src/aclocal.m4 b/Src/aclocal.m4 new file mode 100644 index 0000000..38d8b0f --- /dev/null +++ b/Src/aclocal.m4 @@ -0,0 +1,168 @@ +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN(AM_PATH_SDL, +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_args="$sdl_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + AC_PATH_PROG(SDL_CONFIG, sdl-config, no) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_TRY_RUN([ +#include +#include +#include +#include "SDL.h" + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_TRY_LINK([ +#include +#include "SDL.h" +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) diff --git a/Src/configure b/Src/configure new file mode 100755 index 0000000..fb04376 --- /dev/null +++ b/Src/configure @@ -0,0 +1,7733 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by Autoconf 2.52d. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +cross_compiling=no +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +ac_unique_file="VIC.cpp" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: should be removed in autoconf 3.0. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo "$ac_prog" | sed 's%[\\/][^\\/][^\\/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +X features: + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-sdltest Do not try to compile and run a test SDL program + --enable-kbd-lang-de Use german keyboard layout + --enable-kbd-lang-us Use american keyboard layout + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-sdl-prefix=PFX Prefix where SDL is installed (optional) + --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional) + --with-x use the X Window System + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + cd $ac_dir + if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\./,,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.52d. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +PATH = $PATH + +_ASUNAME +} >&5 + +cat >&5 <<_ACEOF +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell meta-characters. +ac_configure_args= +ac_sep= +for ac_arg +do + case $ac_arg in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + ac_sep=" " ;; + *) ac_configure_args="$ac_configure_args$ac_sep$ac_arg" + ac_sep=" " ;; + esac + # Get rid of the leading space. +done + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -rf conftest* confdefs* core core.* *.core conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:905: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:916: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:924: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:940: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:944: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:950: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:952: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:954: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. It doesn't matter if + # we pass some twice (in addition to the command line arguments). + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + *) ac_configure_args="$ac_configure_args $ac_var=$ac_new_val" + ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:973: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:975: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac +echo "#! $SHELL" >conftest.sh +echo "exit 0" >>conftest.sh +chmod +x conftest.sh +if { (echo "$as_me:995: PATH=\".;.\"; conftest.sh") >&5 + (PATH=".;."; conftest.sh) 2>&5 + ac_status=$? + echo "$as_me:998: \$? = $ac_status" >&5 + (exit $ac_status); }; then + ac_path_separator=';' +else + ac_path_separator=: +fi +PATH_SEPARATOR="$ac_path_separator" +rm -f conftest.sh + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:1015: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="${ac_tool_prefix}gcc" +echo "$as_me:1030: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1038: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1041: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:1050: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="gcc" +echo "$as_me:1065: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1073: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1076: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:1089: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="${ac_tool_prefix}cc" +echo "$as_me:1104: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1112: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1115: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:1124: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="cc" +echo "$as_me:1139: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1147: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1150: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:1163: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue +fi +ac_cv_prog_CC="cc" +echo "$as_me:1183: found $ac_dir/$ac_word" >&5 +break +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" ${1+"$@"} + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1205: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1208: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:1219: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="$ac_tool_prefix$ac_prog" +echo "$as_me:1234: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1242: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1245: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1258: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="$ac_prog" +echo "$as_me:1273: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1281: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1284: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + +test -z "$CC" && { { echo "$as_me:1296: error: no acceptable cc found in \$PATH" >&5 +echo "$as_me: error: no acceptable cc found in \$PATH" >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:1301:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:1304: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:1307: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1309: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:1312: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1314: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:1317: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line 1321 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:1343: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:1346: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:1349: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. +for ac_file in `ls a.exe conftest.exe 2>/dev/null; + ls a.out conftest 2>/dev/null; + ls a.* conftest.* 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb | *.xSYM ) ;; + a.out ) # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool --akim. + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1372: error: C compiler cannot create executables" >&5 +echo "$as_me: error: C compiler cannot create executables" >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:1378: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1383: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:1389: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1392: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:1399: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:1407: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1414: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:1416: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:1419: checking for executable suffix" >&5 +echo $ECHO_N "checking for executable suffix... $ECHO_C" >&6 +if { (eval echo "$as_me:1421: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1424: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:1440: error: cannot compute EXEEXT: cannot compile and link" >&5 +echo "$as_me: error: cannot compute EXEEXT: cannot compile and link" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:1446: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:1452: checking for object suffix" >&5 +echo $ECHO_N "checking for object suffix... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1458 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:1476: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1479: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1491: error: cannot compute OBJEXT: cannot compile" >&5 +echo "$as_me: error: cannot compute OBJEXT: cannot compile" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:1498: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:1502: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1508 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1529: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1532: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1535: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1538: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:1550: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:1556: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1562 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1580: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1583: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1586: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1589: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:1599: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1626: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1629: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1632: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1635: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line 1647 "configure" +#include "confdefs.h" +#include +$ac_declaration +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1666: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1669: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1672: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1675: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line 1685 "configure" +#include "confdefs.h" +$ac_declaration +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1703: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1706: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1709: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1712: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:1749: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" +echo "$as_me:1764: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:1772: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:1775: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1788: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CXX="$ac_prog" +echo "$as_me:1803: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:1811: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:1814: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + +# Provide some information about the compiler. +echo "$as_me:1826:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:1829: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:1832: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1834: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:1837: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1839: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:1842: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:1845: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1851 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1872: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1875: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1878: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1881: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:1893: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:1899: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1905 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1923: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1926: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1929: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1932: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cxx_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:1942: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line 1969 "configure" +#include "confdefs.h" +#include +$ac_declaration +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1988: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1991: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1994: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1997: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line 2007 "configure" +#include "confdefs.h" +$ac_declaration +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:2025: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:2028: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:2031: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2034: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:2061: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 2082 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:2087: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2093: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 2116 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2120: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2126: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:2163: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 2173 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:2178: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2184: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 2207 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2211: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2217: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:2245: error: C preprocessor \"$CPP\" fails sanity check" >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +echo "$as_me:2256: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \${MAKE}... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="${MAKE}"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:2276: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:2280: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +if [ x"$GXX" = "xyes" ]; then + CFLAGS="-O2 -g -fomit-frame-pointer -Wall -Wno-unused -Wno-format" +fi + +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown + +HPUX_REV=0 +if [ x$UNAME_SYSTEM = "xHP-UX" ]; then + HPUX_REV=`echo $UNAME_RELEASE | sed -e 's/^.*.0B*//' -e 's/\..*$//'` +fi + +echo "$as_me:2297: checking for AIX" >&5 +echo $ECHO_N "checking for AIX... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line 2300 "configure" +#include "confdefs.h" +#ifdef _AIX + yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + echo "$as_me:2309: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + +else + echo "$as_me:2316: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + +echo "$as_me:2321: checking for POSIXized ISC" >&5 +echo $ECHO_N "checking for POSIXized ISC... $ECHO_C" >&6 +if test -d /etc/conf/kconfig.d && + grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 +then + echo "$as_me:2326: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ISC=yes # If later tests want to check for ISC. + +cat >>confdefs.h <<\_ACEOF +#define _POSIX_SOURCE 1 +_ACEOF + + if test "$GCC" = yes; then + CC="$CC -posix" + else + CC="$CC -Xp" + fi +else + echo "$as_me:2340: result: no" >&5 +echo "${ECHO_T}no" >&6 + ISC= +fi + +HAVE_BEBOX=no +echo "$as_me:2346: checking for OpenLibrary in -lamiga" >&5 +echo $ECHO_N "checking for OpenLibrary in -lamiga... $ECHO_C" >&6 +if test "${ac_cv_lib_amiga_OpenLibrary+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lamiga $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 2354 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char OpenLibrary (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +OpenLibrary (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2379: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2382: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2385: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2388: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_amiga_OpenLibrary=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_amiga_OpenLibrary=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:2399: result: $ac_cv_lib_amiga_OpenLibrary" >&5 +echo "${ECHO_T}$ac_cv_lib_amiga_OpenLibrary" >&6 +if test $ac_cv_lib_amiga_OpenLibrary = yes; then + HAVE_AMIGA_LIB=yes +else + HAVE_AMIGA_LIB=no +fi + +echo "$as_me:2407: checking for vga_setmode in -lvga" >&5 +echo $ECHO_N "checking for vga_setmode in -lvga... $ECHO_C" >&6 +if test "${ac_cv_lib_vga_vga_setmode+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lvga $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 2415 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vga_setmode (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +vga_setmode (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2440: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2443: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2446: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2449: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_vga_vga_setmode=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_vga_vga_setmode=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:2460: result: $ac_cv_lib_vga_vga_setmode" >&5 +echo "${ECHO_T}$ac_cv_lib_vga_vga_setmode" >&6 +if test $ac_cv_lib_vga_vga_setmode = yes; then + HAVE_SVGA_LIB=yes +else + HAVE_SVGA_LIB=no +fi + +# Check whether --with-sdl-prefix or --without-sdl-prefix was given. +if test "${with_sdl_prefix+set}" = set; then + withval="$with_sdl_prefix" + sdl_prefix="$withval" +else + sdl_prefix="" +fi; + +# Check whether --with-sdl-exec-prefix or --without-sdl-exec-prefix was given. +if test "${with_sdl_exec_prefix+set}" = set; then + withval="$with_sdl_exec_prefix" + sdl_exec_prefix="$withval" +else + sdl_exec_prefix="" +fi; +# Check whether --enable-sdltest or --disable-sdltest was given. +if test "${enable_sdltest+set}" = set; then + enableval="$enable_sdltest" + +else + enable_sdltest=yes +fi; + + if test x$sdl_exec_prefix != x ; then + sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_args="$sdl_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + # Extract the first word of "sdl-config", so it can be a program name with args. +set dummy sdl-config; ac_word=$2 +echo "$as_me:2506: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_SDL_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $SDL_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_SDL_CONFIG="$SDL_CONFIG" # Let the user override the test with a path. + ;; + *) + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + if $as_executable_p "$ac_dir/$ac_word"; then + ac_cv_path_SDL_CONFIG="$ac_dir/$ac_word" + echo "$as_me:2523: found $ac_dir/$ac_word" >&5 + break +fi +done + + test -z "$ac_cv_path_SDL_CONFIG" && ac_cv_path_SDL_CONFIG="no" + ;; +esac +fi +SDL_CONFIG=$ac_cv_path_SDL_CONFIG + +if test -n "$SDL_CONFIG"; then + echo "$as_me:2535: result: $SDL_CONFIG" >&5 +echo "${ECHO_T}$SDL_CONFIG" >&6 +else + echo "$as_me:2538: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + min_sdl_version=1.2.0 + echo "$as_me:2543: checking for SDL - version >= $min_sdl_version" >&5 +echo $ECHO_N "checking for SDL - version >= $min_sdl_version... $ECHO_C" >&6 + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + rm -f conf.sdltest + if test "$cross_compiling" = yes; then + echo $ac_n "cross compiling; assumed OK... $ac_c" +else + cat >conftest.$ac_ext <<_ACEOF +#line 2568 "configure" +#include "confdefs.h" + +#include +#include +#include +#include "SDL.h" + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:2629: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2632: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:2634: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2637: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +no_sdl=yes +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + echo "$as_me:2653: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + HAVE_SDL=yes + else + echo "$as_me:2657: result: no" >&5 +echo "${ECHO_T}no" >&6 + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2672 "configure" +#include "confdefs.h" + +#include +#include "SDL.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2693: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2696: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2699: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2702: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 + echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + HAVE_SDL=no + fi + + rm -f conf.sdltest + +echo "$as_me:2733: checking for X" >&5 +echo $ECHO_N "checking for X... $ECHO_C" >&6 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + +fi; +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else + if test "${ac_cv_have_x+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=no ac_x_libraries=no +rm -fr conftest.dir +if mkdir conftest.dir; then + cd conftest.dir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat >Imakefile <<'_ACEOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +_ACEOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case $ac_im_incroot in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; + esac + case $ac_im_usrlibdir in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; + esac + fi + cd .. + rm -fr conftest.dir +fi + +# Standard set of common directories for X headers. +# Check X11 before X11Rn because it is often a symlink to the current release. +ac_x_header_dirs=' +/usr/X11/include +/usr/X11R6/include +/usr/X11R5/include +/usr/X11R4/include + +/usr/include/X11 +/usr/include/X11R6 +/usr/include/X11R5 +/usr/include/X11R4 + +/usr/local/X11/include +/usr/local/X11R6/include +/usr/local/X11R5/include +/usr/local/X11R4/include + +/usr/local/include/X11 +/usr/local/include/X11R6 +/usr/local/include/X11R5 +/usr/local/include/X11R4 + +/usr/X386/include +/usr/x386/include +/usr/XFree86/include/X11 + +/usr/include +/usr/local/include +/usr/unsupported/include +/usr/athena/include +/usr/local/x11r5/include +/usr/lpp/Xamples/include + +/usr/openwin/include +/usr/openwin/share/include' + +if test "$ac_x_includes" = no; then + # Guess where to find include files, by looking for Intrinsic.h. + # First, try using that file with no special directory specified. + cat >conftest.$ac_ext <<_ACEOF +#line 2830 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2834: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2840: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + for ac_dir in $ac_x_header_dirs; do + if test -r "$ac_dir/X11/Intrinsic.h"; then + ac_x_includes=$ac_dir + break + fi +done +fi +rm -f conftest.err conftest.$ac_ext +fi # $ac_x_includes = no + +if test "$ac_x_libraries" = no; then + # Check for the libraries. + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS=$LIBS + LIBS="-lXt $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2873 "configure" +#include "confdefs.h" +#include +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +XtMalloc (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2891: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2894: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2897: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2900: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + LIBS=$ac_save_LIBS +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +LIBS=$ac_save_LIBS +for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +do + # Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl; do + if test -r $ac_dir/libXt.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi # $ac_x_libraries = no + +if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$as_me:2938: result: $have_x" >&5 +echo "${ECHO_T}$have_x" >&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$as_me:2948: result: libraries $x_libraries, headers $x_includes" >&5 +echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + +cat >>confdefs.h <<\_ACEOF +#define X_DISPLAY_MISSING 1 +_ACEOF + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + case `(uname -sr) 2>/dev/null` in + "SunOS 5"*) + echo "$as_me:2972: checking whether -R must be followed by a space" >&5 +echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6 + ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries" + cat >conftest.$ac_ext <<_ACEOF +#line 2976 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2994: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2997: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3000: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3003: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_nospace=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_R_nospace=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test $ac_R_nospace = yes; then + echo "$as_me:3013: result: no" >&5 +echo "${ECHO_T}no" >&6 + X_LIBS="$X_LIBS -R$x_libraries" + else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat >conftest.$ac_ext <<_ACEOF +#line 3019 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3037: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3040: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3043: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3046: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_space=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_R_space=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test $ac_R_space = yes; then + echo "$as_me:3056: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + X_LIBS="$X_LIBS -R $x_libraries" + else + echo "$as_me:3060: result: neither works" >&5 +echo "${ECHO_T}neither works" >&6 + fi + fi + LIBS=$ac_xsave_LIBS + esac + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn Johnson says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And Karl Berry says + # the Alpha needs dnet_stub (dnet does not exist). + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11" + cat >conftest.$ac_ext <<_ACEOF +#line 3080 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XOpenDisplay (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +XOpenDisplay (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3105: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3108: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3111: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3114: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +echo "$as_me:3120: checking for dnet_ntoa in -ldnet" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3128 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3153: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3156: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3159: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3162: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dnet_dnet_ntoa=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3173: result: $ac_cv_lib_dnet_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + echo "$as_me:3180: checking for dnet_ntoa in -ldnet_stub" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet_stub $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3188 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3213: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3216: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3219: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3222: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_stub_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dnet_stub_dnet_ntoa=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3233: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +fi + + fi +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_xsave_LIBS" + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to T.E. Dickey. + # The functions gethostbyname, getservbyname, and inet_addr are + # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking. + echo "$as_me:3252: checking for gethostbyname" >&5 +echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_func_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3258 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostbyname (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +char (*f) (); + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +f = gethostbyname; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3295: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3298: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3301: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3304: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3314: result: $ac_cv_func_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6 + + if test $ac_cv_func_gethostbyname = no; then + echo "$as_me:3318: checking for gethostbyname in -lnsl" >&5 +echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3326 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3351: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3354: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3357: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3360: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_nsl_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3371: result: $ac_cv_lib_nsl_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6 +if test $ac_cv_lib_nsl_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +fi + + if test $ac_cv_lib_nsl_gethostbyname = no; then + echo "$as_me:3378: checking for gethostbyname in -lbsd" >&5 +echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6 +if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3386 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3411: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3414: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3417: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3420: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_bsd_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_bsd_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3431: result: $ac_cv_lib_bsd_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6 +if test $ac_cv_lib_bsd_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd" +fi + + fi + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says Simon Leinen: it contains gethostby* + # variants that don't use the nameserver (or something). -lsocket + # must be given before -lnsl if both are needed. We assume that + # if connect needs -lnsl, so does gethostbyname. + echo "$as_me:3447: checking for connect" >&5 +echo $ECHO_N "checking for connect... $ECHO_C" >&6 +if test "${ac_cv_func_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3453 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char connect (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +char (*f) (); + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +f = connect; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3490: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3493: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3496: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3499: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_connect=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_connect=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3509: result: $ac_cv_func_connect" >&5 +echo "${ECHO_T}$ac_cv_func_connect" >&6 + + if test $ac_cv_func_connect = no; then + echo "$as_me:3513: checking for connect in -lsocket" >&5 +echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6 +if test "${ac_cv_lib_socket_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3521 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3546: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3549: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3552: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3555: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_socket_connect=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_socket_connect=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3566: result: $ac_cv_lib_socket_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6 +if test $ac_cv_lib_socket_connect = yes; then + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +fi + + fi + + # Guillermo Gomez says -lposix is necessary on A/UX. + echo "$as_me:3575: checking for remove" >&5 +echo $ECHO_N "checking for remove... $ECHO_C" >&6 +if test "${ac_cv_func_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3581 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char remove (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +char (*f) (); + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_remove) || defined (__stub___remove) +choke me +#else +f = remove; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3618: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3621: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3624: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3627: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_remove=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_remove=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3637: result: $ac_cv_func_remove" >&5 +echo "${ECHO_T}$ac_cv_func_remove" >&6 + + if test $ac_cv_func_remove = no; then + echo "$as_me:3641: checking for remove in -lposix" >&5 +echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6 +if test "${ac_cv_lib_posix_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lposix $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3649 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +remove (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3674: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3677: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3680: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3683: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_posix_remove=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_posix_remove=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3694: result: $ac_cv_lib_posix_remove" >&5 +echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6 +if test $ac_cv_lib_posix_remove = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + echo "$as_me:3703: checking for shmat" >&5 +echo $ECHO_N "checking for shmat... $ECHO_C" >&6 +if test "${ac_cv_func_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3709 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shmat (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +char (*f) (); + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shmat) || defined (__stub___shmat) +choke me +#else +f = shmat; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3746: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3749: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3752: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3755: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shmat=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_shmat=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3765: result: $ac_cv_func_shmat" >&5 +echo "${ECHO_T}$ac_cv_func_shmat" >&6 + + if test $ac_cv_func_shmat = no; then + echo "$as_me:3769: checking for shmat in -lipc" >&5 +echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6 +if test "${ac_cv_lib_ipc_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lipc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3777 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +shmat (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3802: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3805: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3808: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3811: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ipc_shmat=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_ipc_shmat=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3822: result: $ac_cv_lib_ipc_shmat" >&5 +echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6 +if test $ac_cv_lib_ipc_shmat = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS=$LDFLAGS + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # John Interrante, Karl Berry + echo "$as_me:3840: checking for IceConnectionNumber in -lICE" >&5 +echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6 +if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3848 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char IceConnectionNumber (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +IceConnectionNumber (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3873: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3876: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3879: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3882: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ICE_IceConnectionNumber=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_ICE_IceConnectionNumber=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3893: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5 +echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6 +if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +fi + + LDFLAGS=$ac_save_LDFLAGS + +fi + +ac_config_headers="$ac_config_headers sysconfig.h" + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +echo "$as_me:3908: checking for $ac_hdr that defines DIR" >&5 +echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3914 "configure" +#include "confdefs.h" +#include +#include <$ac_hdr> + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:3935: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:3938: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:3941: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3944: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:3954: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + echo "$as_me:3967: checking for opendir in -ldir" >&5 +echo $ECHO_N "checking for opendir in -ldir... $ECHO_C" >&6 +if test "${ac_cv_lib_dir_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldir $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3975 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4000: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4003: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4006: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4009: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dir_opendir=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dir_opendir=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4020: result: $ac_cv_lib_dir_opendir" >&5 +echo "${ECHO_T}$ac_cv_lib_dir_opendir" >&6 +if test $ac_cv_lib_dir_opendir = yes; then + LIBS="$LIBS -ldir" +fi + +else + echo "$as_me:4027: checking for opendir in -lx" >&5 +echo $ECHO_N "checking for opendir in -lx... $ECHO_C" >&6 +if test "${ac_cv_lib_x_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lx $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4035 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4060: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4063: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4066: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4069: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_x_opendir=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_x_opendir=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4080: result: $ac_cv_lib_x_opendir" >&5 +echo "${ECHO_T}$ac_cv_lib_x_opendir" >&6 +if test $ac_cv_lib_x_opendir = yes; then + LIBS="$LIBS -lx" +fi + +fi + +echo "$as_me:4088: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4094 "configure" +#include "confdefs.h" +#include +#include +#include +#include + +_ACEOF +if { (eval echo "$as_me:4102: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4108: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line 4130 "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line 4148 "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +#line 4169 "configure" +#include "confdefs.h" +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:4195: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4198: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:4200: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4203: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_header_stdc=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:4216: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:4232: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4238 "configure" +#include "confdefs.h" +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4245: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4248: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4251: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4254: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4264: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in unistd.h fcntl.h sys/time.h sys/types.h utime.h string.h strings.h values.h ncurses.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:4279: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:4284: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:4288: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line 4291 "configure" +#include "confdefs.h" +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4297: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4300: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4303: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4306: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:4315: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:4319: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line 4322 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4326: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4332: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:4350: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:4356: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:4358: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};; + no:yes ) + { echo "$as_me:4361: WARNING: $ac_header: present but cannot be compiled." >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled." >&2;} + { echo "$as_me:4363: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:4365: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};; +esac +echo "$as_me:4368: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:4375: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in sys/vfs.h sys/mount.h sys/select.h sys/param.h sys/statfs.h sys/statvfs.h sys/stat.h linux/joystick.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:4392: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:4397: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:4401: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line 4404 "configure" +#include "confdefs.h" +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4410: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4413: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4416: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4419: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:4428: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:4432: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line 4435 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4439: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4445: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:4463: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:4469: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:4471: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};; + no:yes ) + { echo "$as_me:4474: WARNING: $ac_header: present but cannot be compiled." >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled." >&2;} + { echo "$as_me:4476: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:4478: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};; +esac +echo "$as_me:4481: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:4488: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:4501: checking for char" >&5 +echo $ECHO_N "checking for char... $ECHO_C" >&6 +if test "${ac_cv_type_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4507 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((char *) 0) + return 0; +if (sizeof (char)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4528: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4531: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4534: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4537: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_char=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_char=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4547: result: $ac_cv_type_char" >&5 +echo "${ECHO_T}$ac_cv_type_char" >&6 + +echo "$as_me:4550: checking size of char" >&5 +echo $ECHO_N "checking size of char... $ECHO_C" >&6 +if test "${ac_cv_sizeof_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_char" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 4559 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4577: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4580: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4583: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4586: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4591 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4609: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4612: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4615: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4618: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4634 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4652: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4655: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4658: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4661: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 4677 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4695: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4698: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4701: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4704: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_char=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:4717: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 4722 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (char))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:4744: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4747: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:4749: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4752: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_char=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_char=0 +fi +fi +echo "$as_me:4768: result: $ac_cv_sizeof_char" >&5 +echo "${ECHO_T}$ac_cv_sizeof_char" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_CHAR $ac_cv_sizeof_char +_ACEOF + +echo "$as_me:4774: checking for short" >&5 +echo $ECHO_N "checking for short... $ECHO_C" >&6 +if test "${ac_cv_type_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4780 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((short *) 0) + return 0; +if (sizeof (short)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4801: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4804: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4807: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4810: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_short=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_short=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4820: result: $ac_cv_type_short" >&5 +echo "${ECHO_T}$ac_cv_type_short" >&6 + +echo "$as_me:4823: checking size of short" >&5 +echo $ECHO_N "checking size of short... $ECHO_C" >&6 +if test "${ac_cv_sizeof_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_short" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 4832 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4850: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4853: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4856: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4859: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4864 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4882: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4885: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4888: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4891: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4907 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4925: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4928: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4931: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4934: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 4950 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4968: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4971: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4974: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4977: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_short=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:4990: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 4995 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (short))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5017: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5020: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5022: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5025: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_short=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_short=0 +fi +fi +echo "$as_me:5041: result: $ac_cv_sizeof_short" >&5 +echo "${ECHO_T}$ac_cv_sizeof_short" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SHORT $ac_cv_sizeof_short +_ACEOF + +echo "$as_me:5047: checking for int" >&5 +echo $ECHO_N "checking for int... $ECHO_C" >&6 +if test "${ac_cv_type_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5053 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((int *) 0) + return 0; +if (sizeof (int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5074: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5077: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5080: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5083: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_int=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_int=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5093: result: $ac_cv_type_int" >&5 +echo "${ECHO_T}$ac_cv_type_int" >&6 + +echo "$as_me:5096: checking size of int" >&5 +echo $ECHO_N "checking size of int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_int" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5105 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5123: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5126: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5129: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5132: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5137 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5155: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5158: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5161: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5164: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5180 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5198: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5201: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5204: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5207: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5223 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5241: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5244: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5247: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5250: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_int=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5263: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5268 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (int))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5290: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5293: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5295: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5298: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_int=0 +fi +fi +echo "$as_me:5314: result: $ac_cv_sizeof_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_int" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT $ac_cv_sizeof_int +_ACEOF + +echo "$as_me:5320: checking for long" >&5 +echo $ECHO_N "checking for long... $ECHO_C" >&6 +if test "${ac_cv_type_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5326 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((long *) 0) + return 0; +if (sizeof (long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5347: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5350: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5353: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5356: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_long=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5366: result: $ac_cv_type_long" >&5 +echo "${ECHO_T}$ac_cv_type_long" >&6 + +echo "$as_me:5369: checking size of long" >&5 +echo $ECHO_N "checking size of long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5378 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5396: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5399: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5402: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5405: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5410 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5428: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5431: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5434: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5437: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5453 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5471: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5474: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5477: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5480: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5496 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5514: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5517: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5520: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5523: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_long=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5536: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5541 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (long))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5563: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5566: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5568: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5571: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long=0 +fi +fi +echo "$as_me:5587: result: $ac_cv_sizeof_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG $ac_cv_sizeof_long +_ACEOF + +echo "$as_me:5593: checking for long long" >&5 +echo $ECHO_N "checking for long long... $ECHO_C" >&6 +if test "${ac_cv_type_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5599 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((long long *) 0) + return 0; +if (sizeof (long long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5620: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5623: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5626: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5629: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_long=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_long_long=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5639: result: $ac_cv_type_long_long" >&5 +echo "${ECHO_T}$ac_cv_type_long_long" >&6 + +echo "$as_me:5642: checking size of long long" >&5 +echo $ECHO_N "checking size of long long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long_long" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5651 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long long)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5669: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5672: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5675: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5678: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5683 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5701: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5704: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5707: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5710: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5726 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long long)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5744: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5747: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5750: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5753: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5769 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5787: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5790: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5793: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5796: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_long_long=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5809: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5814 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (long long))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5836: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5839: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5841: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5844: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long_long=0 +fi +fi +echo "$as_me:5860: result: $ac_cv_sizeof_long_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long_long" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long +_ACEOF + +echo "$as_me:5866: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +#line 5874 "configure" +#include "confdefs.h" +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:5929: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5932: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5935: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5938: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:5955: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:5958: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +echo "$as_me:5963: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5969 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6033: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6036: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6039: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6042: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_c_const=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6052: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:6062: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6 +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +#line 6070 "configure" +#include "confdefs.h" +#ifndef __cplusplus +static $ac_kw int static_foo () {return 0; } +$ac_kw int foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6079: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6082: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6085: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6088: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_inline=$ac_kw; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done + +fi +echo "$as_me:6099: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6 +case $ac_cv_c_inline in + inline | yes) ;; + no) +cat >>confdefs.h <<\_ACEOF +#define inline +_ACEOF + ;; + *) cat >>confdefs.h <<_ACEOF +#define inline $ac_cv_c_inline +_ACEOF + ;; +esac + +echo "$as_me:6114: checking for mode_t" >&5 +echo $ECHO_N "checking for mode_t... $ECHO_C" >&6 +if test "${ac_cv_type_mode_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6120 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((mode_t *) 0) + return 0; +if (sizeof (mode_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6141: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6144: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6147: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6150: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_mode_t=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_mode_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6160: result: $ac_cv_type_mode_t" >&5 +echo "${ECHO_T}$ac_cv_type_mode_t" >&6 +if test $ac_cv_type_mode_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + +echo "$as_me:6172: checking for off_t" >&5 +echo $ECHO_N "checking for off_t... $ECHO_C" >&6 +if test "${ac_cv_type_off_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6178 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((off_t *) 0) + return 0; +if (sizeof (off_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6199: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6202: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6205: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6208: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_off_t=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_off_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6218: result: $ac_cv_type_off_t" >&5 +echo "${ECHO_T}$ac_cv_type_off_t" >&6 +if test $ac_cv_type_off_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define off_t long +_ACEOF + +fi + +echo "$as_me:6230: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6236 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6257: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6260: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6263: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6266: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_pid_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6276: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:6288: checking for struct stat.st_blocks" >&5 +echo $ECHO_N "checking for struct stat.st_blocks... $ECHO_C" >&6 +if test "${ac_cv_member_struct_stat_st_blocks+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6294 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +static struct stat ac_aggr; +if (ac_aggr.st_blocks) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6314: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6317: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6320: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6323: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_blocks=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_member_struct_stat_st_blocks=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6333: result: $ac_cv_member_struct_stat_st_blocks" >&5 +echo "${ECHO_T}$ac_cv_member_struct_stat_st_blocks" >&6 +if test $ac_cv_member_struct_stat_st_blocks = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +_ACEOF + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ST_BLOCKS 1 +_ACEOF + +else + LIBOBJS="$LIBOBJS fileblocks.$ac_objext" +fi + +echo "$as_me:6349: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6355 "configure" +#include "confdefs.h" +#include +#include +#include + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6377: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6380: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6383: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6386: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_header_time=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6396: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:6406: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6412 "configure" +#include "confdefs.h" +#include +#include + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6432: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6435: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6438: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6441: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6451: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:6462: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +#line 6469 "configure" +#include "confdefs.h" +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +#line 6484 "configure" +#include "confdefs.h" +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:6497: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo "$as_me:6504: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +#line 6513 "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:6555: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6558: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:6560: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6563: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_memcmp_working=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:6575: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && LIBOBJS="$LIBOBJS memcmp.$ac_objext" + +echo "$as_me:6579: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6585 "configure" +#include "confdefs.h" +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6613: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6616: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6619: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6622: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_signal=int +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:6632: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + +echo "$as_me:6639: checking whether utime accepts a null argument" >&5 +echo $ECHO_N "checking whether utime accepts a null argument... $ECHO_C" >&6 +if test "${ac_cv_func_utime_null+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.data; >conftest.data +# Sequent interprets utime(file, 0) to mean use start of epoch. Wrong. +if test "$cross_compiling" = yes; then + ac_cv_func_utime_null=no +else + cat >conftest.$ac_ext <<_ACEOF +#line 6650 "configure" +#include "confdefs.h" +$ac_includes_default +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +struct stat s, t; + exit (!(stat ("conftest.data", &s) == 0 + && utime ("conftest.data", (long *)0) == 0 + && stat ("conftest.data", &t) == 0 + && t.st_mtime >= s.st_mtime + && t.st_mtime - s.st_mtime < 120)); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:6673: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6676: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:6678: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6681: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_utime_null=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_utime_null=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +rm -f core core.* *.core +fi +echo "$as_me:6694: result: $ac_cv_func_utime_null" >&5 +echo "${ECHO_T}$ac_cv_func_utime_null" >&6 +if test $ac_cv_func_utime_null = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_UTIME_NULL 1 +_ACEOF + +fi +rm -f conftest.data + +for ac_func in gettimeofday sigaction mkdir rmdir select strerror strstr statfs usleep +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:6708: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6714 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6751: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6754: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6757: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6760: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6770: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +KBD_LANG=0 +# Check whether --enable-kbd-lang-de or --disable-kbd-lang-de was given. +if test "${enable_kbd_lang_de+set}" = set; then + enableval="$enable_kbd_lang_de" + KBD_LANG=1 +fi; +# Check whether --enable-kbd-lang-us or --disable-kbd-lang-us was given. +if test "${enable_kbd_lang_us+set}" = set; then + enableval="$enable_kbd_lang_us" + KBD_LANG=0 +fi; + +echo "$as_me:6792: checking which target to use" >&5 +echo $ECHO_N "checking which target to use... $ECHO_C" >&6 +if [ x"$HAVE_BEBOX" = "xyes" ]; then + echo "$as_me:6795: result: BeBox" >&5 +echo "${ECHO_T}BeBox" >&6 + TARGET=be +elif [ x"$HAVE_AMIGA_LIB" = "xyes" ]; then + echo "$as_me:6799: result: AmigaOS" >&5 +echo "${ECHO_T}AmigaOS" >&6 + TARGET=amigaos +elif [ x"$no_x" = "xyes" ]; then + if [ x"$HAVE_SVGA_LIB" = "xyes" ]; then + echo "$as_me:6804: result: SVGAlib" >&5 +echo "${ECHO_T}SVGAlib" >&6 + TARGET=svgalib + CFLAGS="$CFLAGS -D__svgalib__" + LIBS="$LIBS -lvga" + else + echo "$as_me:6810: result: Ummm..." >&5 +echo "${ECHO_T}Ummm..." >&6 + { { echo "$as_me:6812: error: Neither X nor SVGAlib found, don't know what target to use." >&5 +echo "$as_me: error: Neither X nor SVGAlib found, don't know what target to use." >&2;} + { (exit 1); exit 1; }; } + fi +elif [ x"$HAVE_SDL" = "xyes" ]; then + echo "$as_me:6817: result: SDL" >&5 +echo "${ECHO_T}SDL" >&6 + TARGET=sdl + CFLAGS="$CFLAGS $SDL_CFLAGS -DHAVE_SDL" + LIBS="$LIBS $SDL_LIBS" +else + echo "$as_me:6823: result: X Window System" >&5 +echo "${ECHO_T}X Window System" >&6 + TARGET=x11 + CFLAGS="$CFLAGS $X_CFLAGS -I$ac_cv_x_include" + LIBS="$LIBS $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS" +fi + +HAVEGCC27=n +HAVEI386=n + +cat >conftest.cpp << EOF +#include +int main() +{ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#if __GNUC__ > 2 || __GNUC_MINOR__ > 6 +printf("GNU\n"); +#endif +#endif +return 0; +} +EOF + +$CXX conftest.cpp -c -o conftest.o +$CXX conftest.o -o conftest +./conftest >conftest.file +if grep GNU conftest.file >/dev/null; then + HAVEGCC27=y + echo "Good news. Found GCC 2.7 or better." +elif [ "$CC" = "gcc" ]; then + echo "I suggest you upgrade to at least version 2.7 of GCC" +else + echo "Couldn't find GCC. Frodo may or may not compile and run correctly." +fi + +cat >conftest.c << EOF +#include +int main() +{ +#ifdef __i386__ +printf("386\n"); +#endif +return 0; +} +EOF + +$CC conftest.c -c -o conftest.o +$CC conftest.o -o conftest +./conftest >conftest.file +if grep 386 conftest.file >/dev/null; then + HAVEI386=y + echo "You seem to be using a x86 CPU" +else + echo "No special hacks for your CPU, sorry." +fi + +rm -f conftest* + +if [ "$HAVEGCC27" = "y" -a "$HAVEI386" = "y" ]; then +# Don't want strength-reduce on the i386, makes the code slower usually. + CFLAGS="$CFLAGS -fno-strength-reduce -DREGPARAM=\"__attribute__((regparm(3)))\"" +elif [ "$TARGET" = "amigaos" ]; then + CFLAGS="$CFLAGS -DREGPARAM=\"__attribute__((regargs(4)))\" " +else + CFLAGS="$CFLAGS -DREGPARAM=" +fi + +CC=$CXX + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overriden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if cmp -s $cache_file confcache; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:6973: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated automatically by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running \$as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.52d. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.52d, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + shift + set dummy "$ac_option" "$ac_optarg" ${1+"$@"} + shift + ;; + -*);; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_need_defaults=false;; + esac + + case $1 in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running $SHELL $0 " $ac_configure_args " --no-create --no-recursion" + exec $SHELL $0 $ac_configure_args --no-create --no-recursion ;; +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:7172: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + shift + CONFIG_FILES="$CONFIG_FILES $1" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + shift + CONFIG_HEADERS="$CONFIG_HEADERS $1" + ac_need_defaults=false;; + + # This is an error. + -*) { { echo "$as_me:7191: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "sysconfig.h" ) CONFIG_HEADERS="$CONFIG_HEADERS sysconfig.h" ;; + *) { { echo "$as_me:7212: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. +: ${TMPDIR=/tmp} +{ + tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=$TMPDIR/cs$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in $TMPDIR" >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@DEFS@,$DEFS,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@CPP@,$CPP,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@SDL_CONFIG@,$SDL_CONFIG,;t t +s,@SDL_CFLAGS@,$SDL_CFLAGS,;t t +s,@SDL_LIBS@,$SDL_LIBS,;t t +s,@X_CFLAGS@,$X_CFLAGS,;t t +s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t +s,@X_LIBS@,$X_LIBS,;t t +s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@TARGET@,$TARGET,;t t +s,@top_srcdir@,$top_srcdir,;t t +s,@HPUX_REV@,$HPUX_REV,;t t +s,@KBD_LANG@,$KBD_LANG,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || + mkdir "$as_incr_dir" || + { { echo "$as_me:7399: error: cannot create \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; } + ;; + esac +done; } + + if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\./,,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + + if test x"$ac_file" != x-; then + { echo "$as_me:7431: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + configure_input="Generated automatically from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:7449: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:7462: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:7522: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:7533: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:7546: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\(\([^ (][^ (]*\)([^)]*)\)[ ]*\(.*\)$,${ac_dA}\2${ac_dB}\1${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if egrep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # egrep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated automatically by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated automatically by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if cmp -s $ac_file $tmp/config.h 2>/dev/null; then + { echo "$as_me:7663: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || + mkdir "$as_incr_dir" || + { { echo "$as_me:7690: error: cannot create \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; } + ;; + esac +done; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + exec 5>/dev/null + $SHELL $CONFIG_STATUS || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + +echo +echo "Configuration done. Now type \"make\"." diff --git a/Src/configure.in b/Src/configure.in new file mode 100644 index 0000000..cee913c --- /dev/null +++ b/Src/configure.in @@ -0,0 +1,177 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT +AC_CONFIG_SRCDIR([VIC.cpp]) +AC_PREREQ(2.52d) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_PROG_CPP +AC_PROG_MAKE_SET + +dnl Don't want the default "-O2 -g" that autoconf uses for gcc. +if [[ x"$GXX" = "xyes" ]]; then + CFLAGS="-O2 -g -fomit-frame-pointer -Wall -Wno-unused -Wno-format" +fi + +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown + +HPUX_REV=0 +if [[ x$UNAME_SYSTEM = "xHP-UX" ]]; then + HPUX_REV=`echo $UNAME_RELEASE | sed -e 's/[^.]*.[0B]*//' -e 's/\..*$//'` +fi + +AC_AIX +AC_ISC_POSIX + +dnl Checks for libraries. +HAVE_BEBOX=no +AC_CHECK_LIB(amiga, OpenLibrary, HAVE_AMIGA_LIB=yes, HAVE_AMIGA_LIB=no) +AC_CHECK_LIB(vga, vga_setmode, HAVE_SVGA_LIB=yes, HAVE_SVGA_LIB=no) +AM_PATH_SDL(1.2.0, HAVE_SDL=yes, HAVE_SDL=no) + +AC_PATH_XTRA +AC_CONFIG_HEADER(sysconfig.h) + +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h fcntl.h sys/time.h sys/types.h utime.h string.h strings.h values.h ncurses.h) +AC_CHECK_HEADERS(sys/vfs.h sys/mount.h sys/select.h sys/param.h sys/statfs.h sys/statvfs.h sys/stat.h linux/joystick.h) + +AC_CHECK_SIZEOF(char) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(long long) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_STRUCT_ST_BLOCKS +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for library functions. +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL +AC_FUNC_UTIME_NULL +AC_CHECK_FUNCS(gettimeofday sigaction mkdir rmdir select strerror strstr statfs usleep) + +KBD_LANG=0 +AC_ARG_ENABLE(kbd-lang-de,[ --enable-kbd-lang-de Use german keyboard layout],[KBD_LANG=1],[]) +AC_ARG_ENABLE(kbd-lang-us,[ --enable-kbd-lang-us Use american keyboard layout],[KBD_LANG=0],[]) + +AC_MSG_CHECKING(which target to use) +if [[ x"$HAVE_BEBOX" = "xyes" ]]; then + AC_MSG_RESULT([BeBox]) + TARGET=be +elif [[ x"$HAVE_AMIGA_LIB" = "xyes" ]]; then + AC_MSG_RESULT([AmigaOS]) + TARGET=amigaos +elif [[ x"$no_x" = "xyes" ]]; then + if [[ x"$HAVE_SVGA_LIB" = "xyes" ]]; then + AC_MSG_RESULT([SVGAlib]) + TARGET=svgalib + CFLAGS="$CFLAGS -D__svgalib__" + LIBS="$LIBS -lvga" + else + AC_MSG_RESULT([Ummm...]) + AC_MSG_ERROR([Neither X nor SVGAlib found, don't know what target to use.]) + fi +elif [[ x"$HAVE_SDL" = "xyes" ]]; then + AC_MSG_RESULT([SDL]) + TARGET=sdl + CFLAGS="$CFLAGS $SDL_CFLAGS -DHAVE_SDL" + LIBS="$LIBS $SDL_LIBS" +else + AC_MSG_RESULT([X Window System]) + TARGET=x11 + CFLAGS="$CFLAGS $X_CFLAGS -I$ac_cv_x_include" + LIBS="$LIBS $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS" +fi + +dnl Find out some things about the system +dnl - whether we have GCC 2.7 or better. +dnl - what CPU we have (to use some assembly hacks on the x86) + +HAVEGCC27=n +HAVEI386=n + +cat >conftest.cpp << EOF +#include +int main() +{ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#if __GNUC__ > 2 || __GNUC_MINOR__ > 6 +printf("GNU\n"); +#endif +#endif +return 0; +} +EOF + +$CXX conftest.cpp -c -o conftest.o +$CXX conftest.o -o conftest +./conftest >conftest.file +if grep GNU conftest.file >/dev/null; then + HAVEGCC27=y + echo "Good news. Found GCC 2.7 or better." +elif [[ "$CC" = "gcc" ]]; then + echo "I suggest you upgrade to at least version 2.7 of GCC" +else + echo "Couldn't find GCC. Frodo may or may not compile and run correctly." +fi + +cat >conftest.c << EOF +#include +int main() +{ +#ifdef __i386__ +printf("386\n"); +#endif +return 0; +} +EOF + +$CC conftest.c -c -o conftest.o +$CC conftest.o -o conftest +./conftest >conftest.file +if grep 386 conftest.file >/dev/null; then + HAVEI386=y + echo "You seem to be using a x86 CPU" +else + echo "No special hacks for your CPU, sorry." +fi + +rm -f conftest* + +if [[ "$HAVEGCC27" = "y" -a "$HAVEI386" = "y" ]]; then +# Don't want strength-reduce on the i386, makes the code slower usually. + CFLAGS="$CFLAGS -fno-strength-reduce -DREGPARAM=\"__attribute__((regparm(3)))\"" +elif [[ "$TARGET" = "amigaos" ]]; then + CFLAGS="$CFLAGS -DREGPARAM=\"__attribute__((regargs(4)))\" " +else + CFLAGS="$CFLAGS -DREGPARAM=" +fi + +CC=$CXX + +AC_SUBST(TARGET) +AC_SUBST(SET_MAKE) +AC_SUBST(top_srcdir) +AC_SUBST(HPUX_REV) +AC_SUBST(KBD_LANG) + +dnl Generate Makefile. +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT + +dnl Print summary. +echo +echo "Configuration done. Now type \"make\"." diff --git a/Src/el_Acorn.i b/Src/el_Acorn.i new file mode 100644 index 0000000..e7a9653 --- /dev/null +++ b/Src/el_Acorn.i @@ -0,0 +1,172 @@ +/* + * el_Acorn.i + * somewhat faster (on Acorn) versions of the el_-functions of VIC.cc + */ + + +#ifdef GLOBAL_VARS +static inline void el_std_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *lp = (uint32 *)p, *t; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint32 *tab = (uint32*)&TextColorTable[0][b0c][0][0]; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = r[i] = q[mp[i] << 3]; + + t = tab + ((cp[i] & 15) << 13) + (data << 1); + *lp++ = *t++; *lp++ = *t++; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *wp = (uint32 *)p, *t; + uint32 *tab = (uint32*)&TextColorTable[0][b0c][0][0]; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint8 *lookup = (uint8*)mc_color_lookup; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + register uint32 color = cp[i]; + uint8 data = q[mp[i] << 3]; + + if (color & 8) { + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + lookup[6] = colors[color & 7]; + color = lookup[(data & 0xc0) >> 5] | (lookup[(data & 0x30) >> 3] << 16); + *wp++ = color | (color << 8); + color = lookup[(data & 0x0c) >> 1] | (lookup[(data & 0x03) << 1] << 16); + *wp++ = color | (color << 8); + + } else { // Standard mode in multicolor mode + r[i] = data; + color = cp[i]; t = tab + (color << 13) + (data << 1); + *wp++ = *t++; *wp++ = *t++; + } + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *lp = (uint32 *)p, *t; + uint8 *mp = matrix_line; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 data = r[i] = *q; + uint8 h = mp[i]; + + t = (uint32*)&TextColorTable[h >> 4][h & 15][data][0]; + *lp++ = *t++; *lp++ = *t++; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + uint8 lookup[4]; + register uint32 *wp = (uint32 *)p; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + + lookup[0] = b0c_color; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 data = *q; + register uint32 h = mp[i]; + + lookup[1] = colors[h >> 4]; + lookup[2] = colors[h]; + lookup[3] = colors[cp[i]]; + + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + + h = lookup[data >> 6] | (lookup[(data >> 4) & 3] << 16); *wp++ = h | (h << 8); + h = lookup[(data >> 2) & 3] | (lookup[data & 3] << 16); *wp++ = h | (h << 8); + } +} + + +#ifdef GLOBAL_VARS +static inline void el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *lp = (uint32 *)p, *t; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint8 *bcp = &b0c; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = r[i] = mp[i]; + + t = (uint32*)&TextColorTable[cp[i]][bcp[(data >> 6) & 3]][q[(data & 0x3f) << 3]][0]; + *lp++ = *t++; *lp++ = *t++; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_std_idle(uint8 *p, uint8 *r) +#endif +{ + uint8 data = *get_physical(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + uint32 *lp = (uint32 *)p; + uint32 conv0 = TextColorTable[0][b0c][data][0].b; + uint32 conv1 = TextColorTable[0][b0c][data][1].b; + + for (int i=0; i<40; i++) { + *lp++ = conv0; + *lp++ = conv1; + *r++ = data; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_mc_idle(uint8 *p, uint8 *r) +#endif +{ + uint8 data = *get_physical(0x3fff); + register uint8 c0 = b0c_color, c1 = colors[0]; + register uint32 *lp = (uint32 *)p; + register uint32 conv0, conv1; + + conv0 = (((data & 0xc0) == 0) ? c0 : c1) | (((data & 0x30) == 0) ? c0<<16 : c1<<16); + conv1 = (((data & 0x0c) == 0) ? c0 : c1) | (((data & 0x03) == 0) ? c0<<16 : c1<<16); + conv0 |= (conv0 << 8); conv1 |= (conv1 << 8); + + for (int i=0; i<40; i++) { + *lp++ = conv0; + *lp++ = conv1; + *r++ = data; + } +} diff --git a/Src/main.cpp b/Src/main.cpp new file mode 100644 index 0000000..8ea529e --- /dev/null +++ b/Src/main.cpp @@ -0,0 +1,122 @@ +/* + * main.cpp - Main program + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysdeps.h" + +#include "main.h" +#include "C64.h" +#include "Display.h" +#include "Prefs.h" +#include "SAM.h" + + +// Global variables +char AppDirPath[1024]; // Path of application directory + + +// ROM file names +#ifdef __riscos__ +#define BASIC_ROM_FILE "FrodoRsrc:Basic_ROM" +#define KERNAL_ROM_FILE "FrodoRsrc:Kernal_ROM" +#define CHAR_ROM_FILE "FrodoRsrc:Char_ROM" +#define FLOPPY_ROM_FILE "FrodoRsrc:1541_ROM" +#else +#define BASIC_ROM_FILE "Basic ROM" +#define KERNAL_ROM_FILE "Kernal ROM" +#define CHAR_ROM_FILE "Char ROM" +#define FLOPPY_ROM_FILE "1541 ROM" +#endif + + +/* + * Load C64 ROM files + */ + +#ifndef __PSXOS__ +bool Frodo::load_rom_files(void) +{ + FILE *file; + + // Load Basic ROM + if ((file = fopen(BASIC_ROM_FILE, "rb")) != NULL) { + if (fread(TheC64->Basic, 1, 0x2000, file) != 0x2000) { + ShowRequester("Can't read 'Basic ROM'.", "Quit"); + return false; + } + fclose(file); + } else { + ShowRequester("Can't find 'Basic ROM'.", "Quit"); + return false; + } + + // Load Kernal ROM + if ((file = fopen(KERNAL_ROM_FILE, "rb")) != NULL) { + if (fread(TheC64->Kernal, 1, 0x2000, file) != 0x2000) { + ShowRequester("Can't read 'Kernal ROM'.", "Quit"); + return false; + } + fclose(file); + } else { + ShowRequester("Can't find 'Kernal ROM'.", "Quit"); + return false; + } + + // Load Char ROM + if ((file = fopen(CHAR_ROM_FILE, "rb")) != NULL) { + if (fread(TheC64->Char, 1, 0x1000, file) != 0x1000) { + ShowRequester("Can't read 'Char ROM'.", "Quit"); + return false; + } + fclose(file); + } else { + ShowRequester("Can't find 'Char ROM'.", "Quit"); + return false; + } + + // Load 1541 ROM + if ((file = fopen(FLOPPY_ROM_FILE, "rb")) != NULL) { + if (fread(TheC64->ROM1541, 1, 0x4000, file) != 0x4000) { + ShowRequester("Can't read '1541 ROM'.", "Quit"); + return false; + } + fclose(file); + } else { + ShowRequester("Can't find '1541 ROM'.", "Quit"); + return false; + } + + return true; +} +#endif + + +#ifdef __BEOS__ +#include "main_Be.i" +#endif + +#ifdef AMIGA +#include "main_Amiga.i" +#endif + +#ifdef __unix +#include "main_x.i" +#endif + +#ifdef __mac__ +#include "main_mac.i" +#endif + +#ifdef WIN32 +#include "main_WIN32.i" +#endif + +#ifdef __riscos__ +#include "main_Acorn.i" +#endif + +#ifdef __PSXOS__ +#include "main_PSX.i" +#endif diff --git a/Src/main.h b/Src/main.h new file mode 100644 index 0000000..43940dd --- /dev/null +++ b/Src/main.h @@ -0,0 +1,251 @@ +/* + * main.h - Main program + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#ifndef _MAIN_H +#define _MAIN_H + + +class C64; + +// Global variables +extern char AppDirPath[1024]; // Path of application directory + + +/* + * BeOS specific stuff + */ + +#ifdef __BEOS__ +#include +#include + +// Global variables +extern bool FromShell; // true: Started from shell, SAM can be used +extern BEntry AppDirectory; // Application directory + + +// Message codes +const uint32 MSG_STARTUP = 'strt'; // Start emulation +const uint32 MSG_PREFS = 'pref'; // Show preferences editor +const uint32 MSG_PREFS_DONE = 'pdon'; // Preferences editor closed +const uint32 MSG_RESET = 'rset'; // Reset C64 +const uint32 MSG_NMI = 'nmi '; // Raise NMI +const uint32 MSG_SAM = 'sam '; // Invoke SAM +const uint32 MSG_NEXTDISK = 'ndsk'; // Insert next disk in drive 8 +const uint32 MSG_TOGGLE_1541 = '1541'; // Toggle processor-level 1541 emulation +const uint32 MSG_OPEN_SNAPSHOT = 'opss'; // Open snapshot file +const uint32 MSG_SAVE_SNAPSHOT = 'svss'; // Save snapshot file +const uint32 MSG_OPEN_SNAPSHOT_RETURNED = 'opsr'; // Open snapshot file panel returned +const uint32 MSG_SAVE_SNAPSHOT_RETURNED = 'svsr'; // Save snapshot file panel returned + + +// Application signature +const char APP_SIGNATURE[] = "application/x-vnd.cebix-Frodo"; + + +// Application class +class Frodo : public BApplication { +public: + Frodo(); + virtual void ArgvReceived(int32 argc, char **argv); + virtual void RefsReceived(BMessage *message); + virtual void ReadyToRun(void); + virtual void MessageReceived(BMessage *msg); + virtual bool QuitRequested(void); + virtual void AboutRequested(void); + + C64 *TheC64; + +private: + bool load_rom_files(void); + + char prefs_path[1024]; // Pathname of current preferences file + bool prefs_showing; // true: Preferences editor is on screen + + BMessenger this_messenger; + BFilePanel *open_panel; + BFilePanel *save_panel; +}; + +#endif + + +/* + * AmigaOS specific stuff + */ + +#ifdef AMIGA + +class Frodo { +public: + Frodo(); + void ArgvReceived(int argc, char **argv); + void ReadyToRun(void); + void RunPrefsEditor(void); + + C64 *TheC64; + +private: + bool load_rom_files(void); + + char prefs_path[256]; // Pathname of current preferences file +}; + +// Global variables +extern Frodo *be_app; // Pointer to Frodo object + +#endif + + +/* + * X specific stuff + */ + +#ifdef __unix + +class Prefs; + +class Frodo { +public: + Frodo(); + void ArgvReceived(int argc, char **argv); + void ReadyToRun(void); + static Prefs *reload_prefs(void); + + C64 *TheC64; + +private: + bool load_rom_files(void); + + static char prefs_path[256]; // Pathname of current preferences file +}; + +#endif + + +/* + * Mac specific stuff + */ + +#ifdef __mac__ + +class Frodo { +public: + Frodo(); + + void Run(void); + C64 *TheC64; + +private: + bool load_rom_files(void); +}; + +#endif + + +/* + * WIN32 specific stuff + */ + +#ifdef WIN32 + +class Frodo { +public: + Frodo(); + ~Frodo(); + void ArgvReceived(int argc, char **argv); + void ReadyToRun(); + void RunPrefsEditor(); + + C64 *TheC64; + char prefs_path[256]; // Pathname of current preferences file + +private: + bool load_rom_files(); +}; + +// Global variables +extern Frodo *TheApp; // Pointer to Frodo object +extern HINSTANCE hInstance; +extern int nCmdShow; +extern HWND hwnd; + +// Command line options. +extern BOOL full_screen; + +#if defined(DEBUG) + +inline void Debug(const char *format, ...) +{ + va_list args; + va_start(args, format); + char tmp[256]; + vsprintf(tmp, format, args); + va_end(args); + OutputDebugString(tmp); +} + +#else + +inline void Debug(const char *format, ...) +{ +} + +#endif + +#define DebugResult(message, val) \ + Debug("%s: 0x%x (%d)\n", message, val, HRESULT_CODE(val)) + +#endif + + +/* + * RiscOS specific stuff + */ + +#ifdef __riscos__ + +class Frodo +{ + public: + Frodo(void); + ~Frodo(void); + void ReadyToRun(void); + + C64 *TheC64; + + private: + bool load_rom_files(void); +}; + +#endif + + +/* + * PSX specific stuff + */ + +#ifdef __PSXOS__ + +class Frodo { +public: + Frodo(); + void ReadyToRun(void); + + C64 *TheC64; + +private: + bool load_rom_files(void); + + char prefs_path[256]; // Pathname of current preferences file +}; + +// Global variables +extern Frodo *be_app; // Pointer to Frodo object + +#endif + +#endif diff --git a/Src/main_Acorn.i b/Src/main_Acorn.i new file mode 100644 index 0000000..baaf0a1 --- /dev/null +++ b/Src/main_Acorn.i @@ -0,0 +1,571 @@ +/* + * main_Acorn.i + * + * RISC OS specific stuff + * Frodo (C) 1994-1997,2002 Christian Bauer + * Acorn port by Andreas Dehmel, 1997 + * + */ + + +#include "ROlib.h" +#include "AcornGUI.h" + + + + + +// Shared, system-specific data +// The application +Frodo *the_app; + +// My task handle +unsigned int TaskHandle; + +int WimpMessages[] = { + Message_DataSave, Message_DataSaveAck, Message_DataLoad, Message_DataLoadAck, + Message_DataOpen, Message_PaletteChange, Message_ModeChange, Message_MenuWarning, 0 +}; + + + + +// RORes member-functions - very simple reading of screen res and eigen +RORes::RORes(void) +{ + resx = OS_ReadModeVariable(-1,11); resy = OS_ReadModeVariable(-1,12); + eigx = OS_ReadModeVariable(-1,4) ; eigy = OS_ReadModeVariable(-1,5); + resx = (resx+1)< 0 if error +int ROScreen::ReadMode(void) +{ + register int h=0; + + if ((resx = OS_ReadModeVariable(-1,11)) < 0) h++; + if ((resy = OS_ReadModeVariable(-1,12)) < 0) h++; + if ((eigx = OS_ReadModeVariable(-1,4)) < 0) h++; + if ((eigy = OS_ReadModeVariable(-1,5)) < 0) h++; + resx = (resx+1)< eigy) + { + ModeError.errnum = 0x0; + sprintf((char*)ModeError.errmess,"Can't handle screen modes with eigen_x > eigen_y!"); + Wimp_ReportError(&ModeError,1,TASKNAME); + } + return(h); +} + + + + +// Window member-functions - handle all things concerned with windows +// WDesc is a pointer to a complete window descriptor (incl. Handle at pos 0!) +Window::Window(const int *WDesc, const char *Title) +{ + register int h; + + wind = (RO_Window *)WDesc; + if (Title != NULL) + { + if ((wind->tflags & IFlg_Indir) != 0) + { + strcpy((char*)(wind->dat.ind.tit),Title); // indirected + } + else {strncpy((char *)&wind->dat,Title,12);} + } + if ((wind->Handle = Wimp_CreateWindow((int*)&wind->vminx)) == 0) + { + _kernel_oserror WindError; + + WindError.errnum = 0x0; strcpy((char*)WindError.errmess,"Can't create window!"); + Wimp_ReportError(&WindError,1,TASKNAME); + } + // Isopen is unreliable. Works only if the window is never opened/closed by other + // means than calling the member-functions. For 100% accuracy use OpenStatus(). + isopen = false; +} + + +Window::~Window(void) +{ + Wimp_DeleteWindow((int*)wind); +} + + +int Window::MyHandle(void) +{ + return(wind->Handle); +} + + +void Window::GetWorkArea(int *Dest) +{ + Dest[0] = wind->wminx; Dest[1] = wind->wminy; + Dest[2] = wind->wmaxx; Dest[3] = wind->wmaxy; +} + + +void Window::open(void) {isopen = true; Wimp_OpenWindow((int*)wind);} + + +void Window::open(int *Block) {isopen = true; Wimp_OpenWindow(Block);} + + +void Window::close(void) {isopen = false; Wimp_CloseWindow((int*)wind);} + + +void Window::forceredraw(int minx, int miny, int maxx, int maxy) +{ + Wimp_ForceRedraw(wind->Handle,minx,miny,maxx,maxy); +} + + +void Window::redraw(int *Block, uint8 *Bitmap, C64Display *Disp) +{ + int more; + + more = Wimp_RedrawWindow(Block); + while (more != 0) + { + RedrawAWindow(Block,Bitmap,Disp); + more = Wimp_GetRectangle(Block); + } +} + + +// Block contains the coordinates to update, the handle is entered by this +// memberfunction +void Window::update(int *Block, uint8 *Bitmap, C64Display *Disp) +{ + int more; + + Block[0] = wind->Handle; + more = Wimp_UpdateWindow(Block); + while (more != 0) + { + RedrawAWindow(Block,Bitmap,Disp); + more = Wimp_GetRectangle(Block); + } +} + + +// This updated the entire window +void Window::update(uint8 *Bitmap, C64Display *Disp) +{ + int more; + int Block[11]; + + Block[0] = wind->Handle; + GetWorkArea(Block+1); + more = Wimp_UpdateWindow(Block); + while (more != 0) + { + RedrawAWindow(Block,Bitmap,Disp); + more = Wimp_GetRectangle(Block); + } +} + + +void Window::extent(int minx, int miny, int maxx, int maxy) +{ + int extent[4]; + + extent[0] = minx; extent[1] = miny; extent[2] = maxx; extent[3] = maxy; + Wimp_SetExtent(wind->Handle,(int*)extent); + // update internal window info as well + wind->wminx = minx; wind->wminy = miny; + wind->wmaxx = maxx; wind->wmaxy = maxy; +} + + +void Window::getstate(void) {Wimp_GetWindowState((int*)wind);} + + +void Window::getstate(int *dest) +{ + dest[0] = wind->Handle; + Wimp_GetWindowState(dest); +} + + +// The actual redrawing: if the bitmap pointer is not NULL the bitmap is +// painted into the window. +void Window::RedrawAWindow(int *Block, uint8 *Bitmap, C64Display *Disp) +{ + if (Bitmap != NULL) + { + // Plot the bitmap into the window + graph_env ge; + unsigned int *ct; + + // Coordinates are TOP left of rectangle (not bottom, like in RO) + ge.x = Block[RedrawB_VMinX] - Block[RedrawB_ScrollX]; + ge.y = Block[RedrawB_VMaxY] - Block[RedrawB_ScrollY]; + ge.dimx = DISPLAY_X; ge.dimy = DISPLAY_Y; + ct = Disp->GetColourTable(); + + if (Disp->TheC64->TheWIMP->ReadEmuWindowSize() == 1) + { + PlotZoom1(&ge,&Block[RedrawB_CMinX],Bitmap,ct); + } + else + { + PlotZoom2(&ge,&Block[RedrawB_CMinX],Bitmap,ct); + } + } +} + + +// Returns a pointer to a window's icon (or NULL of invalid number) +RO_Icon *Window::GetIcon(unsigned int number) +{ + if (number > wind->icon_no) {return(NULL);} + return((RO_Icon*)(((int*)wind) + RO_WINDOW_WORDS + RO_ICON_WORDS*number)); +} + + +void Window::SetIconState(unsigned int number, unsigned int eor, unsigned int clear) +{ + int Block[4]; + + Block[0] = wind->Handle; Block[1] = number; Block[2] = eor; Block[3] = clear; + Wimp_SetIconState((int*)Block); +} + + +void Window::GetIconState(unsigned int number, int *Block) +{ + Block[0] = wind->Handle; Block[1] = number; + Wimp_GetIconState(Block); +} + + +// Returns true if this window has the input focus +bool Window::HaveInput(void) +{ + RO_Caret Store; + + Wimp_GetCaretPosition(&Store); + return(Store.WHandle == wind->Handle); +} + + +// Writes text into an indirected icon +void Window::WriteIconText(unsigned int number, const char *text) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + // This only makes sense for indirected icons! + if ((ic->iflags & IFlg_Indir) != 0) + { + strncpy((char*)ic->dat.ind.tit,text,ic->dat.ind.len); + ForceIconRedraw(number); + } + } +} + + +// The same but update the window (i.e. immediate result) +void Window::WriteIconTextU(unsigned int number, const char *text) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + strncpy((char*)ic->dat.ind.tit,text,ic->dat.ind.len); + UpdateIcon(number); + } + } +} + + +// Writes the value as a decimal string into the indirected icon +void Window::WriteIconNumber(unsigned int number, int value) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + ConvertInteger4(value,(char*)ic->dat.ind.tit,ic->dat.ind.len); + ForceIconRedraw(number); + } + } +} + + +// The same but update the window +void Window::WriteIconNumberU(unsigned int number, int value) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + ConvertInteger4(value,(char*)ic->dat.ind.tit,ic->dat.ind.len); + UpdateIcon(number); + } + } +} + + +// Returns a pointer to the text in the icon +char *Window::ReadIconText(unsigned int number) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + // only indirected icons! + if ((ic->iflags & IFlg_Indir) != 0) {return((char*)ic->dat.ind.tit);} + } + return(NULL); +} + + +// Reads the number in an indirected icon. +int Window::ReadIconNumber(unsigned int number) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + return(atoi((char*)ic->dat.ind.tit)); + } + } + return(-1); // rather arbitrary, but we only have positive numbers here... +} + + +void Window::WriteTitle(const char *title) +{ + // only indirected window titles, must contain text + if ((wind->tflags & (IFlg_Indir | IFlg_Text)) == (IFlg_Indir | IFlg_Text)) + { + strcpy((char*)wind->dat.ind.tit,title); + UpdateTitle(); + } +} + + +char *Window::ReadTitle(void) +{ + if ((wind->tflags & (IFlg_Indir | IFlg_Text)) == (IFlg_Indir | IFlg_Text)) + { + return((char*)wind->dat.ind.tit); + } + else {return(NULL);} +} + + +// Forces a redraw of the window title +void Window::UpdateTitle(void) +{ + getstate(); + // Force global redraw (in screen-coordinates) + Wimp_ForceRedraw(-1, wind->vminx, wind->vmaxy, wind->vmaxx, wind->vmaxy + TitleBarHeight); +} + + +RO_Window *Window::Descriptor(void) {return(wind);} + + +// Force a redraw on an icon (visible after the next Poll) +void Window::ForceIconRedraw(unsigned int number) +{ + if (number <= wind->icon_no) + { + register RO_Icon *ic; + + ic = GetIcon(number); + forceredraw(ic->minx, ic->miny, ic->maxx, ic->maxy); + } +} + + +// Update an icon (visible immediately -- works only on purely WIMP-drawn icons!) +void Window::UpdateIcon(unsigned int number) +{ + if (number <= wind->icon_no) + { + register RO_Icon *ic; + int Block[11]; // redraw block + int more; + + ic = GetIcon(number); + Block[RedrawB_Handle] = wind->Handle; + Block[RedrawB_VMinX] = ic->minx; Block[RedrawB_VMinY] = ic->miny; + Block[RedrawB_VMaxX] = ic->maxx; Block[RedrawB_VMaxY] = ic->maxy; + more = Wimp_UpdateWindow(Block); // standard redraw loop + while (more != 0) + { + more = Wimp_GetRectangle(Block); + } + } +} + + +// returns the current open-state of the window (true if open) +bool Window::OpenStatus(void) +{ + getstate(); + return(((wind->wflags & (1<<16)) == 0) ? false : true); +} + + +// Same as above, but reads the current state to Block +bool Window::OpenStatus(int *Block) +{ + getstate(Block); + return(((Block[8] & (1<<16)) == 0) ? false : true); +} + + + + + + + +// Icon member-functions - handle all things concerned with icons +Icon::Icon(int Priority, const RO_IconDesc *IDesc) +{ + memcpy((char*)&icon,(char*)IDesc,sizeof(RO_IconDesc)); + IHandle = Wimp_CreateIcon(Priority,IDesc); +} + + +Icon::~Icon(void) +{ + int blk[2]; + + blk[0] = icon.WindowHandle; blk[1] = IHandle; + Wimp_DeleteIcon((int*)blk); +} + + +void Icon::setstate(unsigned int eor, unsigned int clear) +{ + int blk[4]; + + blk[0] = icon.WindowHandle; blk[1] = IHandle; blk[2] = eor; blk[3] = clear; + Wimp_SetIconState((int*)blk); +} + + +void Icon::getstate(void) +{ + int blk[10]; + + blk[0] = icon.WindowHandle; blk[1] = IHandle; + Wimp_GetIconState((int*)blk); + memcpy((char*)&icon,(int*)&blk[2],sizeof(RO_Icon)); +} + + + + +// Frodo member functions +Frodo::Frodo(void) {TheC64 = NULL;} + + +Frodo::~Frodo(void) +{ + if (TheC64 != NULL) {delete TheC64;} +} + + +void Frodo::ReadyToRun(void) +{ + ThePrefs.Load(DEFAULT_PREFS); + TheC64 = new C64; + if (load_rom_files()) + { + TheC64->Run(); + } + delete TheC64; TheC64 = NULL; +} + + + + + +extern void (*__new_handler)(void); + +// Out of memory +void OutOfMemory(void) +{ + _kernel_oserror err; + + + err.errnum = 0; sprintf(err.errmess,"Out of memory error! Aborting."); + Wimp_ReportError(&err,1,TASKNAME); + delete the_app; + Wimp_CloseDown(TaskHandle,TASK_WORD); + exit(1); +} + + + + +// Frodo main +int main(int argc, char *argv[]) +{ +#ifdef __GNUG__ + // Switch off filename conversions in UnixLib: + //__uname_control = __UNAME_NO_PROCESS; +#endif + +#ifdef FRODO_SC + TaskHandle = Wimp_Initialise(310,TASK_WORD,"FrodoSC",(int*)WimpMessages); +#else +# ifdef FRODO_PC + TaskHandle = Wimp_Initialise(310,TASK_WORD,"FrodoPC",(int*)WimpMessages); +# else + TaskHandle = Wimp_Initialise(310,TASK_WORD,"Frodo",(int*)WimpMessages); +# endif +#endif + + // Install handler for failed new + __new_handler = OutOfMemory; + + the_app = new Frodo(); + the_app->ReadyToRun(); + // Clean up directory scrap file + DeleteFile(RO_TEMPFILE); + delete the_app; + Wimp_CloseDown(TaskHandle,TASK_WORD); + return(0); +} diff --git a/Src/main_Amiga.i b/Src/main_Amiga.i new file mode 100644 index 0000000..41321c2 --- /dev/null +++ b/Src/main_Amiga.i @@ -0,0 +1,163 @@ +/* + * main_Amiga.i - Main program, AmigaOS specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include +#include +#include + + +// Global variables +Frodo *be_app; // Pointer to Frodo object + +// Library bases +extern ExecBase *SysBase; +struct GfxBase *GfxBase = NULL; +struct IntuitionBase *IntuitionBase = NULL; +struct Library *GadToolsBase = NULL; +struct Library *DiskfontBase = NULL; +struct Library *AslBase = NULL; + +// Prototypes +void error_exit(char *str); +void open_libs(void); +void close_libs(void); + + +/* + * Create application object and start it + */ + +int main(int argc, char **argv) +{ + if ((SysBase->AttnFlags & (AFF_68040 | AFF_68881)) != (AFF_68040 | AFF_68881)) + error_exit("68040/68881 or higher required.\n"); + open_libs(); + + ULONG secs, micros; + CurrentTime(&secs, µs); + srand(micros); + + be_app = new Frodo(); + be_app->ArgvReceived(argc, argv); + be_app->ReadyToRun(); + delete be_app; + + close_libs(); + return 0; +} + + +/* + * Low-level failure + */ + +void error_exit(char *str) +{ + printf(str); + close_libs(); + exit(20); +} + + +/* + * Open libraries + */ + +void open_libs(void) +{ + if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39))) + error_exit("Couldn't open Gfx V39.\n"); + if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39))) + error_exit("Couldn't open Intuition V39.\n"); + if (!(GadToolsBase = OpenLibrary("gadtools.library", 39))) + error_exit("Couldn't open GadTools V39.\n"); + if (!(DiskfontBase = OpenLibrary("diskfont.library", 39))) + error_exit("Couldn't open Diskfont V39.\n"); + if (!(AslBase = OpenLibrary("asl.library", 39))) + error_exit("Couldn't open ASL V39.\n"); +} + + +/* + * Close libraries + */ + +void close_libs(void) +{ + if (AslBase) + CloseLibrary(AslBase); + if (DiskfontBase) + CloseLibrary(DiskfontBase); + if (GadToolsBase) + CloseLibrary(GadToolsBase); + if (IntuitionBase) + CloseLibrary((struct Library *)IntuitionBase); + if (GfxBase) + CloseLibrary((struct Library *)GfxBase); +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; + prefs_path[0] = 0; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ + if (argc == 2) + strncpy(prefs_path, argv[1], 255); +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + // Load preferences + if (!prefs_path[0]) + strcpy(prefs_path, "Frodo Prefs"); + ThePrefs.Load(prefs_path); + + // Show preferences editor + if (ThePrefs.ShowEditor(TRUE, prefs_path)) { + + // Create and start C64 + TheC64 = new C64; + if (load_rom_files()) + TheC64->Run(); + delete TheC64; + } +} + + +/* + * Run preferences editor + */ + +void Frodo::RunPrefsEditor(void) +{ + Prefs *prefs = new Prefs(ThePrefs); + if (prefs->ShowEditor(FALSE, prefs_path)) { + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + } + delete prefs; +} diff --git a/Src/main_Be.i b/Src/main_Be.i new file mode 100644 index 0000000..435534f --- /dev/null +++ b/Src/main_Be.i @@ -0,0 +1,378 @@ +/* + * main_Be.i - Main program, BeOS specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include +#include +#include +#include + +#include "Version.h" + + +// Global variables +bool FromShell; +BEntry AppDirectory; +BBitmap *AboutBitmap; +const BRect AboutFrame = BRect(0, 0, 383, 99); + + +/* + * Create application object and start it + */ + +int main(int argc, char **argv) +{ + Frodo *the_app; + + srand(real_time_clock()); + FromShell = (argc != 0); // !! This doesn't work... + + the_app = new Frodo(); + if (the_app != NULL) { + the_app->Run(); + delete the_app; + } + return 0; +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() : BApplication(APP_SIGNATURE), this_messenger(this) +{ + TheC64 = NULL; + AboutBitmap = NULL; + strcpy(prefs_path, "/boot/home/config/settings/Frodo_settings"); + prefs_showing = false; + + // Create file panels + open_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_OPEN_SNAPSHOT_RETURNED)); + open_panel->Window()->SetTitle("Frodo: Load snapshot"); + save_panel = new BFilePanel(B_SAVE_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_SAVE_SNAPSHOT_RETURNED)); + save_panel->Window()->SetTitle("Frodo: Save snapshot"); +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int32 argc, char **argv) +{ + if (argc == 2) { + strncpy(prefs_path, argv[1], 1023); + prefs_path[1023] = 0; + } +} + + +/* + * Process Browser arguments + */ + +void Frodo::RefsReceived(BMessage *message) +{ + // Set preferences path unless prefs editor is open or C64 is running + if (!prefs_showing && !TheC64) { + entry_ref the_ref; + BEntry the_entry; + + if (message->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) + if (the_entry.IsFile()) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs_path, the_path.Path(), 1023); + prefs_path[1023] = 0; + } + } +} + + +/* + * Arguments processed, prepare emulation and show preferences editor window + */ + +void Frodo::ReadyToRun(void) +{ + // Find application directory and cwd to it + app_info the_info; + GetAppInfo(&the_info); + BEntry the_file(&the_info.ref); + the_file.GetParent(&AppDirectory); + BPath the_path; + AppDirectory.GetPath(&the_path); + strncpy(AppDirPath, the_path.Path(), 1023); + AppDirPath[1023] = 0; + chdir(AppDirPath); + + // Set up "about" window bitmap + AboutBitmap = new BBitmap(AboutFrame, B_COLOR_8_BIT); + FILE *logofile = fopen("Frodo Logo", "rb"); + if (logofile != NULL) { + fread(AboutBitmap->Bits(), 384*100, 1, logofile); + fclose(logofile); + } + + // Load preferences + ThePrefs.Load(prefs_path); + + // Show preferences editor (sends MSG_STARTUP on close) + prefs_showing = true; + ThePrefs.ShowEditor(true, prefs_path); +} + + +/* + * Handle incoming messages + */ + +void Frodo::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case MSG_STARTUP: // Start the emulation + + // Preferences editor is not longer on screen + prefs_showing = false; + + // Create everything + TheC64 = new C64; + + // Load ROMs + if (!load_rom_files()) { + PostMessage(B_QUIT_REQUESTED); + return; + } + + // Run the 6510 + TheC64->Run(); + break; + + case MSG_PREFS: // Show preferences editor + if (TheC64 != NULL && !prefs_showing) { + TheC64->Pause(); + TheC64->TheDisplay->Pause(); + + Prefs *prefs = new Prefs(ThePrefs); + prefs_showing = true; + prefs->ShowEditor(false, prefs_path); // Sends MSG_PREFS_DONE on close + } + break; + + case MSG_PREFS_DONE: // Preferences editor closed + Prefs *prefs; + msg->FindPointer("prefs", &prefs); + if (!msg->FindBool("canceled")) { + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + } + delete prefs; + prefs_showing = false; + + TheC64->TheDisplay->Resume(); + TheC64->Resume(); + break; + + case MSG_RESET: // Reset C64 + if (TheC64 != NULL) + TheC64->Reset(); + break; + + case MSG_NMI: // NMI + if (TheC64 != NULL) + TheC64->NMI(); + break; + + case MSG_SAM: // Invoke SAM + if (TheC64 != NULL && !prefs_showing && FromShell) { + TheC64->Pause(); + TheC64->TheDisplay->Pause(); + SAM(TheC64); + TheC64->TheDisplay->Resume(); + TheC64->Resume(); + } + break; + + case MSG_NEXTDISK: // Insert next disk in drive 8 + if (TheC64 != NULL && !prefs_showing && strlen(ThePrefs.DrivePath[0]) > 4) { + char str[256]; + strcpy(str, ThePrefs.DrivePath[0]); + char *p = str + strlen(str) - 5; + + // If path matches "*.?64", increment character before the '.' + if (p[1] == '.' && p[3] == '6' && p[4] == '4') { + p[0]++; + + // If no such file exists, set character before the '.' to '1', 'a' or 'A' + FILE *file; + if ((file = fopen(str, "rb")) == NULL) { + if (isdigit(p[0])) + p[0] = '1'; + else if (isupper(p[0])) + p[0] = 'A'; + else + p[0] = 'a'; + } else + fclose(file); + + // Set new prefs + TheC64->Pause(); + Prefs *prefs = new Prefs(ThePrefs); + strcpy(prefs->DrivePath[0], str); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + TheC64->Resume(); + } + } + break; + + case MSG_TOGGLE_1541: // Toggle processor-level 1541 emulation + if (TheC64 != NULL && !prefs_showing) { + TheC64->Pause(); + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + TheC64->Resume(); + } + break; + + case MSG_OPEN_SNAPSHOT: + if (TheC64 != NULL && !prefs_showing) + open_panel->Show(); + break; + + case MSG_SAVE_SNAPSHOT: + if (TheC64 != NULL && !prefs_showing) + save_panel->Show(); + break; + + case MSG_OPEN_SNAPSHOT_RETURNED: + if (TheC64 != NULL && !prefs_showing) { + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) + if (the_entry.IsFile()) { + char path[1024]; + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(path, the_path.Path(), 1023); + path[1023] = 0; + TheC64->Pause(); + TheC64->LoadSnapshot(path); + TheC64->Resume(); + } + } + break; + + case MSG_SAVE_SNAPSHOT_RETURNED: + if (TheC64 != NULL && !prefs_showing) { + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("directory", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + char path[1024]; + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(path, the_path.Path(), 1023); + strncat(path, "/", 1023); + strncat(path, msg->FindString("name"), 1023); + path[1023] = 0; + TheC64->Pause(); + TheC64->SaveSnapshot(path); + TheC64->Resume(); + } + } + break; + + default: + BApplication::MessageReceived(msg); + } +} + + +/* + * Quit requested (either by menu or by closing the C64 display window) + */ + +bool Frodo::QuitRequested(void) +{ + // Stop emulation + if (TheC64) { + TheC64->Quit(); + delete TheC64; + } + + delete AboutBitmap; + delete open_panel; + delete save_panel; + + return BApplication::QuitRequested(); +} + + +/* + * Display "about" window + */ + +class AboutView : public BView { +public: + AboutView() : BView(AboutFrame, "", B_FOLLOW_NONE, B_WILL_DRAW) {} + + virtual void AttachedToWindow(void) + { + SetHighColor(0, 0, 0); + } + + virtual void Draw(BRect update) + { + DrawBitmap(AboutBitmap, update, update); + + SetFont(be_bold_font); + SetDrawingMode(B_OP_OVER); + MovePenTo(204, 20); + DrawString(VERSION_STRING); + + SetFont(be_plain_font); + MovePenTo(204, 40); + DrawString("by Christian Bauer"); + MovePenTo(204, 52); + DrawString(""); + MovePenTo(204, 75); + DrawString(B_UTF8_COPYRIGHT " Copyright 1994-1997"); + MovePenTo(204, 87); + DrawString("Freely distributable."); + } + + virtual void MouseDown(BPoint point) + { + Window()->PostMessage(B_QUIT_REQUESTED); + } +}; + +class AboutWindow : public BWindow { +public: + AboutWindow() : BWindow(AboutFrame, NULL, B_BORDERED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK) + { + Lock(); + MoveTo(100, 100); + AboutView *view = new AboutView; + AddChild(view); + view->MakeFocus(); + Unlock(); + Show(); + } +}; + +void Frodo::AboutRequested(void) +{ + new AboutWindow(); +} diff --git a/Src/main_WIN32.i b/Src/main_WIN32.i new file mode 100644 index 0000000..d4a1ee9 --- /dev/null +++ b/Src/main_WIN32.i @@ -0,0 +1,113 @@ +/* + * main_WIN32.i - Main program, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * WIN32 code by J. Richard Sladkey + */ + +#include + +// The application. +Frodo *TheApp; + +// WinMain args. +HINSTANCE hInstance; +int nCmdShow; +HWND hwnd; + +int PASCAL WinMain(HINSTANCE hInstance_arg, HINSTANCE /* hPrevInstance */, LPSTR lpCmdLine, int nCmdShow_arg) +{ + hInstance = hInstance_arg; + nCmdShow = nCmdShow_arg; + TheApp = new Frodo(); + TheApp->ArgvReceived(__argc, __argv); + TheApp->ReadyToRun(); + delete TheApp; + DestroyWindow(hwnd); + return 0; +} + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; + prefs_path[0] = 0; +} + + +Frodo::~Frodo() +{ + delete TheC64; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ + char *progname = argv[0]; + argc--, argv++; + if (argc >= 1 && argv[0][0] != '\0') { + + // XXX: This should be a function. + char cwd[256]; + GetCurrentDirectory(sizeof(cwd), cwd); + int cwd_len = strlen(cwd); + if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') { + strcat(cwd, "\\"); + cwd_len++; + } + if (strnicmp(argv[0], cwd, cwd_len) == 0) + strncpy(prefs_path, argv[0] + cwd_len, 255); + else + strncpy(prefs_path, argv[0], 255); + int length = strlen(prefs_path); + if (length > 4 && strchr(prefs_path, '.') == NULL) + strcat(prefs_path, ".fpr"); + } +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + // Load preferences + if (!prefs_path[0]) + strcpy(prefs_path, "Frodo.fpr"); + ThePrefs.Load(prefs_path); + + if (ThePrefs.PrefsAtStartup) { + if (!ThePrefs.ShowEditor(TRUE, prefs_path)) + return; + } + + // Create and start C64 + TheC64 = new C64; + if (load_rom_files()) + TheC64->Run(); +} + + +/* + * Run preferences editor + */ + +void Frodo::RunPrefsEditor(void) +{ + Prefs *prefs = new Prefs(ThePrefs); + if (prefs->ShowEditor(FALSE, prefs_path)) { + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + } + delete prefs; +} diff --git a/Src/main_x.i b/Src/main_x.i new file mode 100644 index 0000000..f26a50a --- /dev/null +++ b/Src/main_x.i @@ -0,0 +1,96 @@ +/* + * main_x.i - Main program, X specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "Version.h" + + +extern int init_graphics(void); + + +// Global variables +char Frodo::prefs_path[256] = ""; + + +/* + * Create application object and start it + */ + +int main(int argc, char **argv) +{ + Frodo *the_app; + + timeval tv; + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + + printf("%s by Christian Bauer\n", VERSION_STRING); + if (!init_graphics()) + return 0; + fflush(stdout); + + the_app = new Frodo(); + the_app->ArgvReceived(argc, argv); + the_app->ReadyToRun(); + delete the_app; + + return 0; +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ + if (argc == 2) + strncpy(prefs_path, argv[1], 255); +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + // Load preferences + if (!prefs_path[0]) { + char *home = getenv("HOME"); + if (home != NULL && strlen(home) < 240) { + strncpy(prefs_path, home, 200); + strcat(prefs_path, "/"); + } + strcat(prefs_path, ".frodorc"); + } + ThePrefs.Load(prefs_path); + + // Create and start C64 + TheC64 = new C64; + if (load_rom_files()) + TheC64->Run(); + delete TheC64; +} + + +Prefs *Frodo::reload_prefs(void) +{ + static Prefs newprefs; + newprefs.Load(prefs_path); + return &newprefs; +} diff --git a/Src/ndir.c b/Src/ndir.c new file mode 100644 index 0000000..4f555e5 --- /dev/null +++ b/Src/ndir.c @@ -0,0 +1,222 @@ +/* msd_dir.c - portable directory routines + Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Header: /u/src/master/ccvs/windows-NT/ndir.c,v 1.3 1995/09/08 00:34:09 jimb Exp $ + */ + +/* Everything non trivial in this code is from: @(#)msd_dir.c 1.4 + 87/11/06. A public domain implementation of BSD directory routines + for MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), + August 1897 */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static void free_dircontents (struct _dircontents *); + +/* find ALL files! */ +#define ATTRIBUTES (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR) + + + +DIR * +opendir (const char *name) +{ + struct _finddata_t find_buf; + DIR *dirp; + struct _dircontents *dp; + char name_buf[_MAX_PATH + 1]; + char *slash = ""; + long hFile; + + if (!name) + name = ""; + else if (*name) + { + const char *s; + int l = strlen (name); + + s = name + l - 1; + if ( !(l == 2 && *s == ':') && *s != '\\' && *s != '/') + slash = "/"; /* save to insert slash between path and "*.*" */ + } + + strcat (strcat (strcpy (name_buf, name), slash), "*.*"); + + dirp = (DIR *) malloc (sizeof (DIR)); + if (dirp == (DIR *)0) + return (DIR *)0; + + dirp->dd_loc = 0; + dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) 0; + + if ((hFile = _findfirst (name_buf, &find_buf)) < 0) + { + free (dirp); + return (DIR *)0; + } + + do + { + dp = (struct _dircontents *) malloc (sizeof (struct _dircontents)); + if (dp == (struct _dircontents *)0) + { + free_dircontents (dirp->dd_contents); + return (DIR *)0; + } + + dp->_d_entry = malloc (strlen (find_buf.name) + 1); + if (dp->_d_entry == (char *)0) + { + free (dp); + free_dircontents (dirp->dd_contents); + return (DIR *)0; + } + + if (dirp->dd_contents) + dirp->dd_cp = dirp->dd_cp->_d_next = dp; + else + dirp->dd_contents = dirp->dd_cp = dp; + + strcpy (dp->_d_entry, find_buf.name); + + dp->_d_next = (struct _dircontents *)0; + + } while (!_findnext (hFile, &find_buf)); + + dirp->dd_cp = dirp->dd_contents; + + _findclose(hFile); + + return dirp; +} + + +void +closedir (DIR *dirp) +{ + free_dircontents (dirp->dd_contents); + free ((char *) dirp); +} + + +struct direct * +readdir (DIR *dirp) +{ + static struct direct dp; + + if (dirp->dd_cp == (struct _dircontents *)0) + return (struct direct *)0; + dp.d_namlen = dp.d_reclen = + strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry)); +#if 0 /* JB */ + strlwr (dp.d_name); /* JF */ +#endif + dp.d_ino = 0; + dirp->dd_cp = dirp->dd_cp->_d_next; + dirp->dd_loc++; + + return &dp; +} + + +void +seekdir (DIR *dirp, long off) +{ + long i = off; + struct _dircontents *dp; + + if (off < 0) + return; + for (dp = dirp->dd_contents; --i >= 0 && dp; dp = dp->_d_next) + ; + dirp->dd_loc = off - (i + 1); + dirp->dd_cp = dp; +} + + +long +telldir (DIR *dirp) +{ + return dirp->dd_loc; +} + + +/* Garbage collection */ + +static void +free_dircontents (struct _dircontents *dp) +{ + struct _dircontents *odp; + + while (dp) + { + if (dp->_d_entry) + free (dp->_d_entry); + dp = (odp = dp)->_d_next; + free (odp); + } +} + + +#ifdef TEST + +void main (int argc, char *argv[]); + +void +main (int argc, char *argv[]) +{ + static DIR *directory; + struct direct *entry = (struct direct *)0; + + char *name = ""; + + if (argc > 1) + name = argv[1]; + + directory = opendir (name); + + if (!directory) + { + fprintf (stderr, "can't open directory `%s'.\n", name); + exit (2); + } + + while (entry = readdir (directory)) + printf ("> %s\n", entry->d_name); + + printf ("done.\n"); +} + +#endif /* TEST */ + +/* + * Local Variables: + * mode:C + * ChangeLog:ChangeLog + * compile-command:make + * End: + */ diff --git a/Src/ndir.h b/Src/ndir.h new file mode 100644 index 0000000..a81ae41 --- /dev/null +++ b/Src/ndir.h @@ -0,0 +1,69 @@ +/* ndir.c - portable directory routines + Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Header: /u/src/master/ccvs/windows-NT/ndir.h,v 1.4 1996/01/03 21:15:00 kingdon Exp $ + */ + +/* Everything non trivial in this code is taken from: @(#)msd_dir.c 1.4 + 87/11/06. A public domain implementation of BSD directory routines + for MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), + August 1897 */ + +#include /* ino_t definition */ + +#define rewinddir(dirp) seekdir(dirp, 0L) + +/* 255 is said to be big enough for Windows NT. The more elegant + solution would be declaring d_name as one byte long and allocating + it to the actual size needed. */ +#define MAXNAMLEN 255 + +struct direct +{ + ino_t d_ino; /* a bit of a farce */ + int d_reclen; /* more farce */ + int d_namlen; /* length of d_name */ + char d_name[MAXNAMLEN + 1]; /* garentee null termination */ +}; + +struct _dircontents +{ + char *_d_entry; + struct _dircontents *_d_next; +}; + +typedef struct _dirdesc +{ + int dd_id; /* uniquely identify each open directory */ + long dd_loc; /* where we are in directory entry is this */ + struct _dircontents *dd_contents; /* pointer to contents of dir */ + struct _dircontents *dd_cp; /* pointer to current position */ +} DIR; + +extern void seekdir (DIR *, long); +extern long telldir (DIR *); +extern DIR *opendir (const char *); +extern void closedir (DIR *); +extern struct direct *readdir (DIR *); + +/* + * Local Variables: + * mode:C + * ChangeLog:ChangeLog + * compile-command:make + * End: + */ diff --git a/Src/resource.h b/Src/resource.h new file mode 100644 index 0000000..cc66374 --- /dev/null +++ b/Src/resource.h @@ -0,0 +1,92 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Frodo.rc +// +#define FRODO_ICON 101 +#define IDR_MAIN_MENU 101 +#define IDD_PREFERENCES_STANDARD 102 +#define IDD_PREFERENCES_WIN32 103 +#define IDC_INVISIBLE 106 +#define IDC_NORMAL 1000 +#define IDC_SPRITES 1001 +#define IDC_SPRITECOLLISIONS 1002 +#define IDC_JOYSTICK1 1003 +#define IDC_JOYSTICK2 1004 +#define IDC_LIMITSPEED 1005 +#define IDC_SWAPJOYSTICKS 1006 +#define IDC_FASTRESET 1007 +#define IDC_CIAIRQHACK 1008 +#define IDC_1541EMULATION 1009 +#define IDC_MAPSLASH 1010 +#define IDC_SIDEMULATION 1011 +#define IDC_SIDFILTERS 1012 +#define IDC_BADLINES 1013 +#define IDC_NORMAL_SPIN 1014 +#define IDC_BADLINES_SPIN 1015 +#define IDC_CIA 1016 +#define IDC_CIA_SPIN 1017 +#define IDC_FLOPPY 1018 +#define IDC_FLOPPY_SPIN 1019 +#define IDC_DRAWEVERY 1020 +#define IDC_DRAWEVERY_SPIN 1021 +#define IDC_DEVICE8 1022 +#define IDC_DEVICE9 1023 +#define IDC_BROWSE8 1024 +#define IDC_BROWSE9 1025 +#define IDC_DEVICE10 1026 +#define IDC_BROWSE10 1027 +#define IDC_DEVICE11 1028 +#define IDC_BROWSE11 1029 +#define IDC_FULLSCREEN 1035 +#define IDC_AUTOPAUSE 1036 +#define IDC_SYSTEMMEMORY 1037 +#define IDC_EXCLUSIVESOUND 1038 +#define IDC_PREFSATSTARTUP 1039 +#define IDC_VIEWPORT 1040 +#define IDC_DISPLAYMODE 1041 +#define IDC_ALWAYSCOPY 1042 +#define IDC_HIDECURSOR 1043 +#define IDC_SYSTEMKEYS 1044 +#define IDC_LATENCYMIN 1045 +#define IDC_LATENCYMAX 1046 +#define IDC_LATENCYAVG 1047 +#define IDC_SCALINGNUMERATOR 1048 +#define IDC_LATENCYMIN_SPIN 1049 +#define IDC_LATENCYAVG_SPIN 1050 +#define IDC_REUSIZE 1050 +#define IDC_LATENCYMAX_SPIN 1051 +#define IDC_SCALINGDENOMINATOR 1052 +#define IDC_SCALINGNUMERATOR_SPIN 1053 +#define IDC_SCALINGDENOMINATOR_SPIN 1054 +#define IDC_DIRECTSOUND 1055 +#define IDC_SHOWLEDS 1056 +#define ID_FILE_EX 40001 +#define ID_TOOLS_RESETC64 40002 +#define ID_TOOLS_SAM 40003 +#define ID_TOOLS_INSERTNEXTDISK 40004 +#define ID_TOOLS_PREFERENCES 40005 +#define ID_HELP_ABOUT 40006 +#define ID_FILE_NEW 40007 +#define ID_FILE_OPEN 40009 +#define ID_FILE_SAVE 40011 +#define ID_FILE_SAVEAS 40012 +#define ID_HELP_CONTENTS 40013 +#define ID_HELP_KEYBOARD 40014 +#define ID_TOOLS_PAUSE 40015 +#define ID_HELP_SETTINGS 40016 +#define ID_TOOLS_FULLSCREEN 40017 +#define ID_TOOLS_RESETDIRECTDRAW 40018 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40019 +#define _APS_NEXT_CONTROL_VALUE 1051 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/sysconfig.h.Acorn b/Src/sysconfig.h.Acorn new file mode 100644 index 0000000..dc1c88c --- /dev/null +++ b/Src/sysconfig.h.Acorn @@ -0,0 +1,150 @@ +/* Src/sysconfig.h for Acorn RISC OS */ + + +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if your struct stat has st_blocks. */ +/* #undef HAVE_ST_BLOCKS */ + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +/*#define HAVE_UTIME_NULL 1 */ + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +/*#define TIME_WITH_SYS_TIME 1*/ + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +/* #undef SIZEOF_LONG_LONG */ + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +/*#define HAVE_GETTIMEOFDAY 1*/ + +/* Define if you have the mkdir function. */ +/*#define HAVE_MKDIR 1*/ + +/* Define if you have the rmdir function. */ +/*#define HAVE_RMDIR 1*/ + +/* Define if you have the select function. */ +/*#define HAVE_SELECT 1*/ + +/* Define if you have the sigaction function. */ +/*#define HAVE_SIGACTION 1*/ + +/* Define if you have the signal function */ +#define HAVE_SIGNAL + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +/*#define HAVE_DIRENT_H 1*/ + +/* Define if you have the header file. */ +/*#define HAVE_FCNTL_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_DIR_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_PARAM_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_STAT_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_TIME_H 1*/ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_TYPES_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +/*#define HAVE_UNISTD_H 1*/ + +/* Define if you have the header file. */ +/*#define HAVE_UTIME_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.Amiga b/Src/sysconfig.h.Amiga new file mode 100644 index 0000000..ab43ade --- /dev/null +++ b/Src/sysconfig.h.Amiga @@ -0,0 +1,149 @@ +/* Src/sysconfig.h for AmigaOS */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if your struct stat has st_blocks. */ +#define HAVE_ST_BLOCKS 1 + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +#define _POSIX_SOURCE 1 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +#define HAVE_NDIR_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_DIR_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.Be b/Src/sysconfig.h.Be new file mode 100644 index 0000000..c6f4a2e --- /dev/null +++ b/Src/sysconfig.h.Be @@ -0,0 +1,149 @@ +/* Src/sysconfig.h for BeOS */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if your struct stat has st_blocks. */ +/* #undef HAVE_ST_BLOCKS */ + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_DIR_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.WIN32 b/Src/sysconfig.h.WIN32 new file mode 100644 index 0000000..cb2f003 --- /dev/null +++ b/Src/sysconfig.h.WIN32 @@ -0,0 +1,149 @@ +/* Src/sysconfig.h for AmigaOS */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if your struct stat has st_blocks. */ +/* #define HAVE_ST_BLOCKS 1 */ + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `int' if doesn't define. */ +#define mode_t int + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +#define pid_t int + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +/* #undef TIME_WITH_SYS_TIME */ + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +/* #undef HAVE_SIGACTION */ + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +/* #undef HAVE_DIRENT_H */ + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +#define HAVE_NDIR_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_DIR_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_PARAM_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_TIME_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_UNISTD_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_UTIME_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.in b/Src/sysconfig.h.in new file mode 100644 index 0000000..d3b9f63 --- /dev/null +++ b/Src/sysconfig.h.in @@ -0,0 +1,169 @@ +/* sysconfig.h.in. Generated from configure.in by autoheader. */ + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_DIRENT_H + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if you have the header file. */ +#undef HAVE_LINUX_JOYSTICK_H + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the `mkdir' function. */ +#undef HAVE_MKDIR + +/* Define if you have the header file. */ +#undef HAVE_NCURSES_H + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define if you have the `rmdir' function. */ +#undef HAVE_RMDIR + +/* Define if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define if you have the `statfs' function. */ +#undef HAVE_STATFS + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define if `st_blocks' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLOCKS + +/* Define if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#undef HAVE_ST_BLOCKS + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STATFS_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STATVFS_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_VFS_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the `usleep' function. */ +#undef HAVE_USLEEP + +/* Define if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define if `utime(file, NULL)' sets file's timestamp to the present. */ +#undef HAVE_UTIME_NULL + +/* Define if you have the header file. */ +#undef HAVE_VALUES_H + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* The size of a `char', as computed by sizeof. */ +#undef SIZEOF_CHAR + +/* The size of a `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of a `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of a `long long', as computed by sizeof. */ +#undef SIZEOF_LONG_LONG + +/* The size of a `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +#undef inline + +/* Define to `int' if does not define. */ +#undef mode_t + +/* Define to `long' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t diff --git a/Src/sysdeps.h b/Src/sysdeps.h new file mode 100644 index 0000000..d517162 --- /dev/null +++ b/Src/sysdeps.h @@ -0,0 +1,186 @@ +/* + * sysdeps.h - Try to include the right system headers and get other + * system-specific stuff right + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "sysconfig.h" + +extern "C" +{ + +#include +#include +#include +#include + +#ifndef __PSXOS__ +#include +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_VALUES_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UTIME_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_VFS_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_MOUNT_H +#include +#endif + +#ifdef HAVE_SYS_STATFS_H +#include +#endif + +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +#ifndef __PSXOS__ +# include +#endif +# endif +#endif + +#if HAVE_DIRENT_H +# include +#else +# define dirent direct +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#ifndef __PSXOS__ +#include +#endif +#include + +#if EEXIST == ENOTEMPTY +#define BROKEN_OS_PROBABLY_AIX +#endif + +#ifdef HAVE_LINUX_JOYSTICK_H +#include +#endif + +#ifdef __NeXT__ +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC +#define S_ISDIR(val) (S_IFDIR & val) +struct utimbuf +{ + time_t actime; + time_t modtime; +}; +#endif + +#ifdef __DOS__ +#include +#include +#else +#undef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef __mac__ +#define bool Boolean +#endif + +#ifdef __riscos +#define bool int +#endif + +#ifdef WIN32 +#include +#include +#if !defined(M_PI) +#define M_PI 3.14159265358979323846 +#endif +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#if _MSC_VER < 1100 +#define bool char +#endif +#define LITTLE_ENDIAN_UNALIGNED 1 +#endif + +/* If char has more then 8 bits, good night. */ +#ifndef __BEOS__ +typedef unsigned char uint8; +typedef signed char int8; + +#if SIZEOF_SHORT == 2 +typedef unsigned short uint16; +typedef short int16; +#elif SIZEOF_INT == 2 +typedef unsigned int uint16; +typedef int int16; +#else +#error No 2 byte type, you lose. +#endif + +#if SIZEOF_INT == 4 +typedef unsigned int uint32; +typedef int int32; +#elif SIZEOF_LONG == 4 +typedef unsigned long uint32; +typedef long int32; +#else +#error No 4 byte type, you lose. +#endif +#endif // __BEOS__ + +#define UNUSED(x) (x = x) +} diff --git a/TkGui.tcl b/TkGui.tcl new file mode 100755 index 0000000..2ebed21 --- /dev/null +++ b/TkGui.tcl @@ -0,0 +1,1018 @@ +#!/usr/bin/wish + +# Frodo Tk GUI by Lutz Vieweg +# requires Tk >= 4.1 + +#package require Tk 4.1 + +set prefname "$env(HOME)/.frodorc" + +proc defaults {} { + global pref + set pref(NormalCycles) "63" + set pref(BadLineCycles) "23" + set pref(CIACycles) "63" + set pref(FloppyCycles) "64" + set pref(SkipFrames) "4" + set pref(DriveType8) "DIR" + set pref(DrivePath8) "./64prgs" + set pref(DriveType9) "D64" + set pref(DrivePath9) "./disk1.d64" + set pref(DriveType10) "DIR" + set pref(DrivePath10) "" + set pref(DriveType11) "DIR" + set pref(DrivePath11) "" + set pref(SIDType) "NONE" + set pref(SpritesOn) "TRUE" + set pref(SpriteCollisions) "TRUE" + set pref(Joystick1On) "FALSE" + set pref(Joystick2On) "TRUE" + set pref(JoystickSwap) "FALSE" + set pref(LimitSpeed) "FALSE" + set pref(FastReset) "FALSE" + set pref(CIAIRQHack) "FALSE" + set pref(MapSlash) "TRUE" + set pref(Emul1541Proc) "FALSE" + set pref(ShowOptions) "FALSE" + set pref(SIDFilters) "TRUE" +} + +proc s2bool { s } { + if {$s == "TRUE"} {return 1} + return 0 +} + +defaults + +if {![catch { set in [open $prefname] }]} { + while {![eof $in]} { + set line [gets $in] + if [regexp {^([^ ]*)[ ]*=[ ]*([^ ]*)$} $line range name val] { + switch -exact $name { + "NormalCycles" { + set pref(NormalCycles) $val + } + "BadLineCycles" { + set pref(BadLineCycles) $val + } + "CIACycles" { + set pref(CIACycles) $val + } + "FloppyCycles" { + set pref(FloppyCycles) $val + } + "SkipFrames" { + set pref(SkipFrames) $val + } + "DriveType8" { + set pref(DriveType8) $val + } + "DrivePath8" { + set pref(DrivePath8) $val + } + "DriveType9" { + set pref(DriveType9) $val + } + "DrivePath9" { + set pref(DrivePath9) $val + } + "DriveType10" { + set pref(DriveType10) $val + } + "DrivePath10" { + set pref(DrivePath10) $val + } + "DriveType11" { + set pref(DriveType11) $val + } + "DrivePath11" { + set pref(DrivePath11) $val + } + "SIDType" { + set pref(SIDType) $val + } + "SpritesOn" { + set pref(SpritesOn) [s2bool $val] + } + "SpriteCollisions" { + set pref(SpriteCollisions) [s2bool $val] + } + "Joystick1On" { + set pref(Joystick1On) [s2bool $val] + } + "Joystick2On" { + set pref(Joystick2On) [s2bool $val] + } + "JoystickSwap" { + set pref(JoystickSwap) [s2bool $val] + } + "LimitSpeed" { + set pref(LimitSpeed) [s2bool $val] + } + "FastReset" { + set pref(FastReset) [s2bool $val] + } + "CIAIRQHack" { + set pref(CIAIRQHack) [s2bool $val] + } + "MapSlash" { + set pref(MapSlash) [s2bool $val] + } + "Emul1541Proc" { + set pref(Emul1541Proc) [s2bool $val] + } + "ShowOptions" { + set pref(ShowOptions) [s2bool $val] + } + "SIDFilters" { + set pref(SIDFilters) [s2bool $val] + } + } + } + } +} + +proc bool2s { b } { + if {$b} { return "TRUE" } + return "FALSE" +} + +proc WritePrefs { } { + global pref prefname + + if [catch { set out [open "$prefname" "w"] }] { + puts stderr "unable to write preferences file '$prefname'" + } else { + puts $out "NormalCycles = $pref(NormalCycles)" + puts $out "BadLineCycles = $pref(BadLineCycles)" + puts $out "CIACycles = $pref(CIACycles)" + puts $out "FloppyCycles = $pref(FloppyCycles)" + puts $out "SkipFrames = $pref(SkipFrames)" + puts $out "DriveType8 = $pref(DriveType8)" + puts $out "DrivePath8 = $pref(DrivePath8)" + puts $out "DriveType9 = $pref(DriveType9)" + puts $out "DrivePath9 = $pref(DrivePath9)" + puts $out "DriveType10 = $pref(DriveType10)" + puts $out "DrivePath10 = $pref(DrivePath10)" + puts $out "DriveType11 = $pref(DriveType11)" + puts $out "DrivePath11 = $pref(DrivePath11)" + puts $out "SIDType = $pref(SIDType)" + puts $out "SpritesOn = [bool2s $pref(SpritesOn)]" + puts $out "SpriteCollisions = [bool2s $pref(SpriteCollisions)]" + puts $out "Joystick1On = [bool2s $pref(Joystick1On)]" + puts $out "Joystick2On = [bool2s $pref(Joystick2On)]" + puts $out "JoystickSwap = [bool2s $pref(JoystickSwap)]" + puts $out "LimitSpeed = [bool2s $pref(LimitSpeed)]" + puts $out "FastReset = [bool2s $pref(FastReset)]" + puts $out "CIAIRQHack = [bool2s $pref(CIAIRQHack)]" + puts $out "MapSlash = [bool2s $pref(MapSlash)]" + puts $out "Emul1541Proc = [bool2s $pref(Emul1541Proc)]" + puts $out "ShowOptions = [bool2s $pref(ShowOptions)]" + puts $out "SIDFilters = [bool2s $pref(SIDFilters)]" + + close $out + + puts -nonewline "p" + flush stdout + } +} + +proc Quit {} { + puts -nonewline "q" + flush stdout + exit 0 +} + +# ============================================================= + +frame .cmds +pack .cmds -expand false -fill both + +button .cmds.quit -text "Quit" -command "Quit" +pack .cmds.quit -side left -expand true -fill both + +button .cmds.reset -text "Reset" -command {puts -nonewline "r" ; flush stdout} +pack .cmds.reset -side left -expand true -fill both + +# ============================================================= + +proc Change { {dummy1 ""} {dummy2 ""}} { + WritePrefs +} + +#====================== begin of fs-box ======================== + +proc check_file_type {filename var} { + global pref + switch [file extension $filename] { + .d64 - .x64 { set $var D64; Change } + .lnx - .t64 { set $var T64; Change } + } +} + +proc fs_FileSelect {w {title {FileSelector}} {filter {*}} {name {}} typevar} { + global fs_priv + + if {[info commands tk_getOpenFile] != ""} { + switch $filter { + "*.{t64,lnx}" { + set types { + {{t64/lnx archive files} {.t64 .lnx}} + {{disk files} {.d64 .x64}} + {{all files} *} + } + } + "*.{d64,x64}" { + set types { + {{disk files} {.d64 .x64}} + {{t64/lnx archive files} {.t64 .lnx}} + {{all files} *} + } + } + default { + set types { + {{all files} *} + {{disk files} {.d64 .x64}} + {{t64/lnx archive files} {.t64 .lnx}} + } + } + } + if {[file isdir $name]} { + set name $name + } else { + set name "[file dirname $name]" + } + set fs_priv(result) [tk_getOpenFile -filetypes $types -initialdir $name] + check_file_type $fs_priv(result) $typevar + return $fs_priv(result) + } + + # remainder of the code is for pre-tk8.0 + + if {$name != ""} { + if {[file isdir $name]} { + set filter "$name/$filter" + } else { + set filter "[file dirname $name]/$filter" + } + } + + set fs_priv(window) $w + set fs_priv(filter) $filter + set fs_priv(name) "" + set fs_priv(result) "" + + # if this window already exists, destroy it + catch {destroy $w} + + # create new toplevel + toplevel $w + wm title $w $title + + # create frames + + # create filter-entry + frame $w.filter + pack $w.filter -side top -fill x + label $w.filter.lbl -text "Filter" + pack $w.filter.lbl -side top -anchor w + entry $w.filter.et -textvar fs_priv(filter) + pack $w.filter.et -side top -fill x -expand true + bind $w.filter.et { fs_newpath } + button $w.filter.up -text "Up" -command { + set f [file dirname $fs_priv(filter)] + set t [file tail $fs_priv(filter)] + if {$f == "."} {set f [pwd]} + set f [file dirname $f] + if {$f == "/"} {set f ""} + set fs_priv(filter) "$f/$t" + fs_newpath + } + pack $w.filter.up -side top -anchor w + + #create list-frames + frame $w.l + pack $w.l -side top -fill both -expand true + frame $w.l.d + pack $w.l.d -side left -fill both -expand true + frame $w.l.f + pack $w.l.f -side left -fill both -expand true + + fs_slist $w.l.d Directories single + + fs_slist $w.l.f Files single + bind $w.l.f.top.lst { + focus %W + global fs_priv + set sel [%W curselection] + if {$sel != ""} { + set fs_priv(name) [%W get [%W curselection]] + } + } + + bind $w.l.f.top.lst "$w.bts.ok flash; $w.bts.ok invoke" + bind $w.l.d.top.lst { + global fs_priv + set f [file dirname $fs_priv(filter)] + set t [file tail $fs_priv(filter)] + set d [%W get active] + switch $d { + "." { } + ".." { + if {$f == "."} {set f [pwd]} + set fs_priv(filter) "[file dirname $f]/$t" + } + default { + if {$f == "/"} {set f ""} + set fs_priv(filter) "$f/$d/$t" + } + } + fs_newpath + } + + #create name-entry + + frame $w.name + pack $w.name -side top -fill x + label $w.name.lbl -text "Filename" + pack $w.name.lbl -side top -anchor w + entry $w.name.et -textvar fs_priv(name) + pack $w.name.et -side top -fill x + bind $w.name.et { + global fs_priv + set fs_priv(filter) \ + "[file dirname $fs_priv(name)]/*[file extension $fs_priv(filter)]" + fs_newpath + } + bind $w.name.et { + global fs_priv + set n $fs_priv(name) + + if {[string index $n 0] != "/" && [string index $n 0] != "~"} { + set n "[file dirname $fs_priv(filter)]/$n" + } + + set n "[file dirname $n]/[file tail $n]" + + set fs_priv(result) $n + } + + # create buttons + frame $w.bts + pack $w.bts -side top -fill x + button $w.bts.ok -text "OK" -command { + global fs_priv + set w $fs_priv(window) + set sel [$w.l.f.top.lst curselection] + if {$sel != ""} { + set val [$w.l.f.top.lst get $sel] + set fs_priv(result) "[file dirname $fs_priv(filter)]/$val" + } + } + pack $w.bts.ok -side left -expand true + + button $w.bts.cancel -text "Cancel" -command { + global fs_priv + set fs_priv(result) "" + } + pack $w.bts.cancel -side left -expand true + + fs_newpath + + set oldfocus [focus] + grab $w + focus $w + + tkwait variable fs_priv(result) + + if { "$oldfocus" != "" } { focus $oldfocus } + + destroy $w + + check_file_type $fs_priv(result) $typevar + + return $fs_priv(result) +} + +proc fs_DirSelect {w {title {FileSelector}} {filter {*}} {name {}} } { + global fs_priv + + if {$name != ""} { + if {[file isdir $name]} { + set filter $name + } else { + set filter [file dirname $name] + } + } + + if {[info commands tk_chooseDirectory] != ""} { + return [tk_chooseDirectory -initialdir $filter] + } + + # remainder of the code is for pre-tk8.3 + + set fs_priv(window) $w + set fs_priv(filter) $filter + set fs_priv(name) $name + set fs_priv(result) "" + + # if this window already exists, destroy it + catch {destroy $w} + + # create new toplevel + toplevel $w + wm title $w $title + + # create frames + + # create filter-entry + frame $w.filter + pack $w.filter -side top -fill x + label $w.filter.lbl -text "Directory" + pack $w.filter.lbl -side top -anchor w + entry $w.filter.et -textvar fs_priv(filter) + pack $w.filter.et -side top -fill x -expand true + bind $w.filter.et { fs_dir_newpath } + button $w.filter.up -text "Up" -command { + set f $fs_priv(filter) + if {$f == "."} {set f [pwd]} + set fs_priv(filter) [file dirname $f] + fs_dir_newpath + } + pack $w.filter.up -side top -anchor w + + #create list-frames + frame $w.l + pack $w.l -side top -fill both -expand true + frame $w.l.d + pack $w.l.d -side left -fill both -expand true + + fs_slist $w.l.d "Sub Directories" single + + bind $w.l.d.top.lst { + global fs_priv + set f [string trimright $fs_priv(filter) /] + set d [%W get active] + switch $d { + "." { } + ".." { + if {$f == "."} {set f [pwd]} + set fs_priv(filter) [file dirname $f] + } + default { + if {$f == "/"} {set f ""} + set fs_priv(filter) "$f/$d" + } + } + fs_dir_newpath + } + + # create buttons + frame $w.bts + pack $w.bts -side top -fill x + button $w.bts.ok -text "OK" -command { + global fs_priv + set w $fs_priv(window) + set sel [$w.l.d.top.lst curselection] + if {$sel != ""} { + set val [$w.l.d.top.lst get $sel] + set fs_priv(result) "$fs_priv(filter)/$val" + } else { + set fs_priv(result) $fs_priv(filter) + } + } + pack $w.bts.ok -side left -expand true + + button $w.bts.cancel -text "Cancel" -command { + global fs_priv + set fs_priv(result) "" + } + pack $w.bts.cancel -side left -expand true + + fs_dir_newpath + + set oldfocus [focus] + grab $w + focus $w + + tkwait variable fs_priv(result) + + if { "$oldfocus" != "" } { focus $oldfocus } + + destroy $w + + return $fs_priv(result) +} + +proc fs_slist {w title mode} { + + if {$title != ""} { + label $w.lbl -text $title + pack $w.lbl -side top -anchor w + } + + frame $w.top + pack $w.top -side top -fill both -expand true + frame $w.bot + pack $w.bot -side top -fill x + + listbox $w.top.lst -relief sunken -bd 2 -yscrollcommand "$w.top.vs set" \ + -xscrollcommand "$w.bot.hs set" -selectmode $mode \ + -font -*-courier-medium-r-normal--14-*-*-*-*-*-* + pack $w.top.lst -side left -fill both -expand true + + scrollbar $w.top.vs -relief sunken -bd 2 -command "$w.top.lst yview" \ + -orient vertical + pack $w.top.vs -side left -fill y + + scrollbar $w.bot.hs -relief sunken -bd 2 -command "$w.top.lst xview" \ + -orient horizontal + pack $w.bot.hs -side left -fill x -expand true + + frame $w.bot.pad -width [expr [lindex [$w.top.vs config -width] 4] + \ + [lindex [$w.top.vs config -bd] 4] *2] + + pack $w.bot.pad -side left + +} + +proc fs_newpath {} { + + global fs_priv + + if {$fs_priv(filter) == ""} { + set fs_priv(filter) "./*" + } + + if {[file isdir $fs_priv(filter)]} { + set fs_priv(filter) "$fs_priv(filter)/*" + } + + set w $fs_priv(window) + set filter $fs_priv(filter) + + $w.l.d.top.lst delete 0 end + + $w.l.f.top.lst delete 0 end + + # update dirs + set dwidth 5 + set files [lsort "[glob -nocomplain "[file dirname $filter]/*" ] \ + [glob -nocomplain "[file dirname $filter]/.*"]" ] + foreach j $files { + if [file isdir $j] { + set name [file tail $j] + $w.l.d.top.lst insert end $name + if {[string length $name] > $dwidth} { set dwidth [string length $name] } + } + } + + #update files + set pos 0 + set fwidth 5 + set files [lsort [glob -nocomplain "$filter"]] + foreach j $files { + if [file isfile $j] { + set name [file tail $j] + $w.l.f.top.lst insert end $name + if {[string length $name] > $fwidth} { + set pos [string length [file dirname $j]] + set fwidth [string length $name] + } + } + } + + if {$fwidth < 20} { set fwidth 20 } + $w.l.f.top.lst configure -width [expr $fwidth+1] + + if {$dwidth < 20} { set dwidth 20 } + $w.l.d.top.lst configure -width [expr $dwidth+1] + + + if {$pos == 1} { set pos 0 } + + update idletasks + + $w.l.f.top.lst xview $pos + +} + +proc fs_dir_newpath {} { + + global fs_priv + + if {$fs_priv(filter) == ""} { + set fs_priv(filter) "." + } + + set w $fs_priv(window) + set filter $fs_priv(filter) + + $w.l.d.top.lst delete 0 end + + # update dirs + set dwidth 5 + set files [lsort "[glob -nocomplain "$filter/*" ] \ + [glob -nocomplain "$filter/.*"]" ] + foreach j $files { + if [file isdir $j] { + set name [file tail $j] + $w.l.d.top.lst insert end $name + if {[string length $name] > $dwidth} { set dwidth [string length $name] } + } + } + + + if {$dwidth < 20} { set dwidth 20 } + $w.l.d.top.lst configure -width [expr $dwidth+1] + + update idletasks + +} + +#====================== end of fs-box ======================== + +set num(1) "ABCDEFGHIJKLMNOPQRSTUVWXYZA" +set num(2) "abcdefghijklmnopqrstuvwxyza" +set num(3) "12345678901" + +proc NDname { name } { + global num + if [string match *.?64 $name] { + set len [string length $name] + set z [string index $name [expr $len-5]] + + foreach i "1 2 3" { + set c [string first $z $num($i)] + if {$c >= 0} { break } + } + incr c + set nname "[string range $name 0 [expr $len-6]][string index $num($i) $c][string range $name [expr $len-4] end]" + if [file exists $nname] { return $nname } + set nname "[string range $name 0 [expr $len-6]][string index $num($i) 0][string range $name [expr $len-4] end]" + if [file exists $nname] { return $nname } + } + return $name +} + +# =========================================================== + +frame .drives -borderwidth 0 +pack .drives -side top -expand false -fill x + +label .drives.l -text "Disk Drive Controls" -height 2 +pack .drives.l -side top -expand true -fill both + +checkbutton .drives.ef -text "Emulate 1541 CPU (Drive 8 only)" -variable pref(Emul1541Proc) -command "Change" -bg "dark grey" -anchor w +pack .drives.ef -side top -expand true -fill both + +frame .drives.d8 -borderwidth 0 +pack .drives.d8 -side top -expand true -fill x + +label .drives.d8.l -text "8" -width 2 +pack .drives.d8.l -side left -expand false +radiobutton .drives.d8.d64 -text "D64" -variable pref(DriveType8) -value "D64" \ + -bg "dark grey" -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath8) pref(DriveType8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } + } +pack .drives.d8.d64 -side left -expand false -fill y + +radiobutton .drives.d8.dir -text "DIR" -variable pref(DriveType8) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } + } +pack .drives.d8.dir -side left -expand false + +radiobutton .drives.d8.t64 -text "T64" -variable pref(DriveType8) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath8) pref(DriveType8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } + } +pack .drives.d8.t64 -side left -expand false + +entry .drives.d8.name -textvar pref(DrivePath8) +bind .drives.d8.name "Change" +bind .drives.d8.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath8) pref(DriveType8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } +} +pack .drives.d8.name -side left -expand true -fill x + +button .drives.d8.n -text "N" -command { set pref(DrivePath8) [NDname $pref(DrivePath8)]; Change } +pack .drives.d8.n -side left -expand false + +frame .drives.d9 +pack .drives.d9 -side top -expand true -fill x + +label .drives.d9.l -text "9" -width 2 +pack .drives.d9.l -side left -expand false +radiobutton .drives.d9.d64 -text "D64" -variable pref(DriveType9) -value "D64" \ + -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath9) pref(DriveType9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } + } +pack .drives.d9.d64 -side left -expand false + +radiobutton .drives.d9.dir -text "DIR" -variable pref(DriveType9) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } + } +pack .drives.d9.dir -side left -expand false + +radiobutton .drives.d9.t64 -text "T64" -variable pref(DriveType9) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath9) pref(DriveType9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } + } +pack .drives.d9.t64 -side left -expand false + +entry .drives.d9.name -textvar pref(DrivePath9) +bind .drives.d9.name "Change" +bind .drives.d9.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath9) pref(DriveType9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } +} +pack .drives.d9.name -side left -expand true -fill x + +button .drives.d9.n -text "N" -command { set pref(DrivePath9) [NDname $pref(DrivePath9)]; Change } +pack .drives.d9.n -side left -expand false + + +frame .drives.d10 +pack .drives.d10 -side top -expand true -fill x + +label .drives.d10.l -text "10" -width 2 +pack .drives.d10.l -side left -expand false +radiobutton .drives.d10.d64 -text "D64" -variable pref(DriveType10) -value "D64" \ + -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath10) pref(DriveType10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } + } +pack .drives.d10.d64 -side left -expand false + +radiobutton .drives.d10.dir -text "DIR" -variable pref(DriveType10) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } + } +pack .drives.d10.dir -side left -expand false + +radiobutton .drives.d10.t64 -text "T64" -variable pref(DriveType10) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath10) pref(DriveType10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } + } +pack .drives.d10.t64 -side left -expand false + +entry .drives.d10.name -textvar pref(DrivePath10) +bind .drives.d10.name "Change" +bind .drives.d10.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath10) pref(DriveType10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } +} +pack .drives.d10.name -side left -expand true -fill x + +button .drives.d10.n -text "N" -command { set pref(DrivePath10) [NDname $pref(DrivePath10)]; Change } +pack .drives.d10.n -side left -expand false + + +frame .drives.d11 +pack .drives.d11 -side top -expand true -fill x + +label .drives.d11.l -text "11" -width 2 +pack .drives.d11.l -side left -expand false +radiobutton .drives.d11.d64 -text "D64" -variable pref(DriveType11) -value "D64" \ + -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath11) pref(DriveType11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } + } +pack .drives.d11.d64 -side left -expand false + +radiobutton .drives.d11.dir -text "DIR" -variable pref(DriveType11) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } + } +pack .drives.d11.dir -side left -expand false + +radiobutton .drives.d11.t64 -text "T64" -variable pref(DriveType11) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath11) pref(DriveType11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } + } +pack .drives.d11.t64 -side left -expand false + +entry .drives.d11.name -textvar pref(DrivePath11) +bind .drives.d11.name "Change" +bind .drives.d11.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath11) pref(DriveType11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } +} +pack .drives.d11.name -side left -expand true -fill x + +button .drives.d11.n -text "N" -command { set pref(DrivePath11) [NDname $pref(DrivePath11)]; Change } +pack .drives.d11.n -side left -expand false + + +# ============================================================= + +global show_options_string + +checkbutton .more_options -borderwidth 3 -relief raised -textvariable show_options_string -variable pref(ShowOptions) -command "Change" +pack .more_options -side top -expand false -fill x + +frame .nums -borderwidth 3 -relief raised + +scale .nums.nc -from 1 -to 200 -orient horizontal -variable pref(NormalCycles) \ + -label "Normal Cycles" +pack .nums.nc -side top -expand false -fill x + +scale .nums.bc -from 1 -to 200 -orient horizontal -variable pref(BadLineCycles) \ + -label "Bad Line Cycles" +pack .nums.bc -side top -expand false -fill x + +scale .nums.cc -from 1 -to 200 -orient horizontal -variable pref(CIACycles) \ + -label "CIA Cycles" +pack .nums.cc -side top -expand false -fill x + +scale .nums.fc -from 1 -to 200 -orient horizontal -variable pref(FloppyCycles) \ + -label "Floppy Cycles" +pack .nums.fc -side top -expand false -fill x + +scale .nums.sf -from 1 -to 10 -orient horizontal -variable pref(SkipFrames) \ + -label "Skip Frames" +pack .nums.sf -side top -expand false -fill x + +# ============================================================= + +frame .bools1 -borderwidth 3 -relief raised + +frame .bools1.sprites +pack .bools1.sprites -side left -expand true -fill both + +checkbutton .bools1.sprites.o -text "Sprites" -variable pref(SpritesOn) -command "Change" +pack .bools1.sprites.o -anchor nw -expand false -fill y + +checkbutton .bools1.sprites.c -text "Sprite Collisions" \ + -variable pref(SpriteCollisions) -command "Change" +pack .bools1.sprites.c -anchor nw -expand false -fill y + + +frame .bools1.joy +pack .bools1.joy -side left -expand true -fill both + +checkbutton .bools1.joy.j1 -text "Joy 1" -variable pref(Joystick1On) -command "Change" +pack .bools1.joy.j1 -anchor nw -expand false -fill y + +checkbutton .bools1.joy.j2 -text "Joy 2" -variable pref(Joystick2On) -command "Change" +pack .bools1.joy.j2 -anchor nw -expand false -fill y + +checkbutton .bools1.joy.swap -text "Swap 1<->2" -variable pref(JoystickSwap) -command "Change" +pack .bools1.joy.swap -anchor nw -expand false -fill y + + +frame .bools2 -borderwidth 3 -relief raised + +frame .bools2.m1 +pack .bools2.m1 -side left -expand true -fill both + +checkbutton .bools2.m1.ls -text "Limit Speed" -variable pref(LimitSpeed) -command "Change" +pack .bools2.m1.ls -anchor nw -expand false -fill y + +checkbutton .bools2.m1.fr -text "Fast Reset" -variable pref(FastReset) -command "Change" +pack .bools2.m1.fr -anchor nw -expand false -fill y + + +frame .bools2.m2 +pack .bools2.m2 -side left -expand true -fill both + +checkbutton .bools2.m2.ch -text "CIA IRQ Hack" -variable pref(CIAIRQHack) -command "Change" +pack .bools2.m2.ch -anchor nw -expand false -fill y + +checkbutton .bools2.m2.ms -text "Map '/'" -variable pref(MapSlash) -command "Change" +pack .bools2.m2.ms -anchor nw -expand false -fill y + + +frame .bools4 -relief raised -borderwidth 3 + +frame .bools4.st +pack .bools4.st -side left -expand true -fill both + +label .bools4.st.l -text "SID Emulation" +pack .bools4.st.l -anchor nw +radiobutton .bools4.st.none -text "None" -variable pref(SIDType) -value "NONE" \ + -command {Change} +pack .bools4.st.none -anchor nw + +radiobutton .bools4.st.digi -text "Digital" -variable pref(SIDType) -value "DIGITAL" \ + -command {Change} +pack .bools4.st.digi -anchor nw + +frame .bools4.sf +pack .bools4.sf -side left -expand true -fill both + +checkbutton .bools4.sf.sf -text "SID Filters" -variable pref(SIDFilters) -command "Change" +pack .bools4.sf.sf -side top -expand false -fill y + + +# ============================================================= + +frame .pcmd +pack .pcmd -side top -expand false -fill both + +button .pcmd.apply -text "Apply" -command "Change" +pack .pcmd.apply -side left -expand true -fill both + +button .pcmd.default -text "Defaults" -command "defaults ; Change" +pack .pcmd.default -side left -expand false -fill both + +# ============================================================= + +set ledcolors(0) "#d9d9d9" +set ledcolors(1) "red" +set ledcolors(2) "brown" + +proc ListenToFrodo {} { + set line [gets stdin] + set cmd [lindex $line 0] + switch -exact $cmd { + "speed" { + .speed.v configure -text "[lindex $line 1]%" + } + "ping" { + puts -nonewline "o" + flush stdout + } + "quit" { + exit 0 + } + "leds" { + global ledcolors + .drives.d8.l configure -background $ledcolors([lindex $line 1]) + .drives.d9.l configure -background $ledcolors([lindex $line 2]) + .drives.d10.l configure -background $ledcolors([lindex $line 3]) + .drives.d11.l configure -background $ledcolors([lindex $line 4]) + } + default { + puts stderr "line = $line" + } + } +} + + +proc set_Emul1541Proc args { + global pref + + if {$pref(Emul1541Proc)} { + set state disabled + set pref(DriveType8) "D64" + } else { + set state normal + } + .drives.d8.dir configure -state $state + .drives.d8.t64 configure -state $state + foreach i {9 10 11} { + .drives.d${i}.d64 configure -state $state + .drives.d${i}.dir configure -state $state + .drives.d${i}.t64 configure -state $state + .drives.d${i}.name configure -state $state + .drives.d${i}.n configure -state $state + } +} + +proc set_ShowOptions args { + global pref show_options_string + + if {$pref(ShowOptions)} { + pack .nums -side top -expand false -fill x -after .more_options + pack .bools1 -side top -expand true -fill both -after .nums + pack .bools2 -side top -expand true -fill both -after .bools1 + pack .bools4 -side top -expand true -fill both -after .bools2 + set show_options_string "Hide Advanced Options" + } else { + pack forget .nums .bools1 .bools2 .bools4 + set show_options_string "Show Advanced Options" + } +} + +fileevent stdin readable { ListenToFrodo } + +# ============================================================= + +wm title . "Frodo Preferences Menu" + +# set trace and trigger it now +trace variable pref(Emul1541Proc) w set_Emul1541Proc +set pref(Emul1541Proc) $pref(Emul1541Proc) + +# set trace and trigger it now +trace variable pref(ShowOptions) w set_ShowOptions +set pref(ShowOptions) $pref(ShowOptions) + +tkwait window . + + +