mirror of
https://github.com/Oibaf66/frodo-wii.git
synced 2024-11-25 12:56:59 +01:00
Imported orig code
This commit is contained in:
commit
2a6bece89d
BIN
64prgs/3fff
Normal file
BIN
64prgs/3fff
Normal file
Binary file not shown.
BIN
64prgs/colorbars
Normal file
BIN
64prgs/colorbars
Normal file
Binary file not shown.
BIN
64prgs/d011h3
Normal file
BIN
64prgs/d011h3
Normal file
Binary file not shown.
BIN
64prgs/dadb
Normal file
BIN
64prgs/dadb
Normal file
Binary file not shown.
BIN
64prgs/de00all
Normal file
BIN
64prgs/de00all
Normal file
Binary file not shown.
BIN
64prgs/dycp
Normal file
BIN
64prgs/dycp
Normal file
Binary file not shown.
BIN
64prgs/fld
Normal file
BIN
64prgs/fld
Normal file
Binary file not shown.
BIN
64prgs/lrborder
Normal file
BIN
64prgs/lrborder
Normal file
Binary file not shown.
BIN
64prgs/sprsync
Normal file
BIN
64prgs/sprsync
Normal file
Binary file not shown.
BIN
64prgs/stretch
Normal file
BIN
64prgs/stretch
Normal file
Binary file not shown.
BIN
64prgs/tech-tech
Normal file
BIN
64prgs/tech-tech
Normal file
Binary file not shown.
BIN
64prgs/text26
Normal file
BIN
64prgs/text26
Normal file
Binary file not shown.
203
CHANGES
Normal file
203
CHANGES
Normal file
@ -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
|
48
Docs/Main.html
Normal file
48
Docs/Main.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Frodo Manual</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Frodo V4.1</H1>
|
||||||
|
<A HREF="whatsnew.html">What's new in V4.1?</A>
|
||||||
|
<H2>The free, portable Commodore 64 emulator for BeOS/Unix/MacOS/AmigaOS/RiscOS/Win32</H2>
|
||||||
|
© Copyright 1994-1997,2002 Christian Bauer et al. Freely distributable.
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A HREF="overview.html">Overview</A>: Why another C64 emulator?
|
||||||
|
<LI><A HREF="installation.html">Installation</A>: How to install Frodo
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A HREF="systemspecific.html">System specific notes</A>: Frodo is Frodo is Frodo is Frodo...
|
||||||
|
<LI><A HREF="settings.html">Settings</A>: You only have to configure...
|
||||||
|
<LI><A HREF="emulwindow.html">Emulation window</A>: See what's in here
|
||||||
|
<LI><A HREF="keyboard.html">Keyboard layout</A>: Where the hell is the "any" key?
|
||||||
|
<LI><A HREF="files.html">File access</A>: A 64 with hard disk
|
||||||
|
<LI><A HREF="sam.html">SAM</A>: The built-in assembler/monitor
|
||||||
|
<LI><A HREF="kernal.html">Kernal</A>: Extensions of the included Kernal ROM
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A HREF="flavours.html">Frodo flavours</A>: Frodo, Frodo PC and Frodo SC
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A HREF="demoprograms.html">Sample programs</A>: The included demo programs
|
||||||
|
<LI><A HREF="technicalinfo.html">Technical info</A>: Revealing the secrets
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A HREF="legalmush.html">Copyright</A>: Legal mush
|
||||||
|
<LI><A HREF="bugreports.html">Bug reports</A>: Got some problems?
|
||||||
|
<LI><A HREF="thanks.html">Credits</A>: The author wishes to thank...
|
||||||
|
<LI><A HREF="author.html">The author</A>: Programmer's address
|
||||||
|
<LI><A HREF="history.html">History</A>: Revision history of Frodo
|
||||||
|
<LI><A HREF="future.html">The future</A>: What's on my to-do list
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
37
Docs/author.html
Normal file
37
Docs/author.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>The author</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>How to reach me:</H1>
|
||||||
|
<H3>Snail mail:</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
Christian Bauer<BR>
|
||||||
|
Max-Planck-Str.60<BR>
|
||||||
|
55124 Mainz<BR>
|
||||||
|
Germany<P></BLOCKQUOTE>
|
||||||
|
<H3>EMail:</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<A HREF="mailto:cbauer@iphcip1.physik.uni-mainz.de">cbauer@iphcip1.physik.uni-mainz.de</A><BR>
|
||||||
|
</BLOCKQUOTE><P>
|
||||||
|
|
||||||
|
Questions, criticism, suggestions and <A HREF="bugreports.html">bug
|
||||||
|
reports</A> are always welcome. EMail is preferred. In fact, the probability
|
||||||
|
that physical mail to me will be answered is virtually zero (call me lazy
|
||||||
|
:-).<P>
|
||||||
|
|
||||||
|
Questions about the Unix version should go to <A HREF="mailto:crux@pool.informatik.rwth-aachen.de">Bernd Schmidt</A>
|
||||||
|
(esp. Linux) and <A HREF="mailto:lkv@mania.robin.de">Lutz Vieweg</A> (esp. HP-UX).<P>
|
||||||
|
|
||||||
|
Questions about the Mac version should go to <A HREF="mailto:titan@indigo.ie">Richard Bannister</A>.<P>
|
||||||
|
|
||||||
|
Questions about the Win32 version should go to <A HREF="mailto:jrs@world.std.com">J. Richard Sladkey</A>.<P>
|
||||||
|
|
||||||
|
Questions about the Acorn version should go to <A HREF="mailto:dehmel@informatik.tu-muenchen.de">Andreas Dehmel</A>.<P>
|
||||||
|
|
||||||
|
Frodo is <EM>not</EM> a shareware program, but I won't reject any gifts.
|
||||||
|
<TT>:-)</TT><P>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
52
Docs/bugreports.html
Normal file
52
Docs/bugreports.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Bug reports</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Bug reports</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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 <A HREF="author.html">here</A>.
|
||||||
|
Questions about the Unix version should go to
|
||||||
|
<A HREF="mailto:crux@pool.informatik.rwth-aachen.de">Bernd Schmidt</A> (esp. Linux) and
|
||||||
|
<A HREF="mailto:lkv@mania.robin.de">Lutz Vieweg</A> (esp. HP-UX).
|
||||||
|
Questions about the Mac version should go to
|
||||||
|
<A HREF="mailto:titan@indigo.ie">Richard Bannister</A>.
|
||||||
|
Questions about the Win32 version should go to
|
||||||
|
<A HREF="mailto:jrs@world.std.com">J. Richard Sladkey</A>.
|
||||||
|
Questions about the Acorn version should go to
|
||||||
|
<A HREF="mailto:dehmel@informatik.tu-muenchen.de">Andreas Dehmel</A>.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
Known bugs of the BeOS version:<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Ctrl-C in SAM doesn't work (probably a bug in the signal handling of BeOS)
|
||||||
|
<LI>The "SAM" menu option should be disabled if Frodo was started from the Tracker, but there is no way I know of to determine whether the program was launched from the Shell or the Tracker
|
||||||
|
<LI>Frodo SC doesn't work very well in full-screen mode
|
||||||
|
<LI>Double scan in full-screen mode doesn't work
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
Known bugs of the Unix version:<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Only supports german and US keyboards
|
||||||
|
<LI>Can only use 256 color visuals
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
Known bugs of the AmigaOS version:<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Many <TT>:-)</TT>
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
64
Docs/demoprograms.html
Normal file
64
Docs/demoprograms.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Sample programs</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Sample programs</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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
|
||||||
|
<CODE>LOAD"<name>",8,1</CODE> and started with <CODE>SYS49152</CODE>.
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
A short description of each program:<P>
|
||||||
|
|
||||||
|
<H4>3fff</H4>
|
||||||
|
Opens the top and bottom border and displays swinging letters ("Cycles per
|
||||||
|
line (CPU)" should be set to 60 for this one)
|
||||||
|
|
||||||
|
<H4>colorbars</H4>
|
||||||
|
Flickering colors
|
||||||
|
|
||||||
|
<H4>d011h3</H4>
|
||||||
|
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
|
||||||
|
|
||||||
|
<H4>dadb</H4>
|
||||||
|
A program running in the color RAM (press space), requires Frodo SC
|
||||||
|
|
||||||
|
<H4>de00all</H4>
|
||||||
|
A program running in the address space $de00-$dfff (press space), requires
|
||||||
|
Frodo SC
|
||||||
|
|
||||||
|
<H4>dycp</H4>
|
||||||
|
Scrolling with different Y character position
|
||||||
|
|
||||||
|
<H4>fld</H4>
|
||||||
|
Demonstrates the FLD effect (flexible line distance)
|
||||||
|
|
||||||
|
<H4>lrborder</H4>
|
||||||
|
Opens the left/right border, requires Frodo SC
|
||||||
|
|
||||||
|
<H4>sprsync</H4>
|
||||||
|
Stable raster routine by synchronizing with a sprite, requires Frodo SC
|
||||||
|
|
||||||
|
<H4>stretch</H4>
|
||||||
|
Variably expanded sprites, requires Frodo SC
|
||||||
|
|
||||||
|
<H4>tech-tech</H4>
|
||||||
|
Horizontal scrolling with large amplitude (use joystick in port 2 to
|
||||||
|
control)
|
||||||
|
|
||||||
|
<H4>text26</H4>
|
||||||
|
Displays (nearly) 26 lines of text
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
41
Docs/emulwindow.html
Normal file
41
Docs/emulwindow.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Emulation window</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Emulation window</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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):<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><B>"Reset C64"</B> resets the C64 and the 1541 emulation (same as pressing F12)
|
||||||
|
<LI><B>"Preferences..."</B> re-opens the <A HREF="settings.html">settings window</A>
|
||||||
|
<LI><B>"SAM..."</B> halts the emulation and activates <A HREF="sam.html">SAM</A> (you can exit from SAM with the "x" command)
|
||||||
|
<LI><B>"Insert next disk"</B> changes the disk image of drive 8 if in "D64" mode and inserts the "next" disk. Frodo tries to be smart about what the "next" disk is. Your disk images should be named "Foo1.d64", "Foo2.d64"... or "BarA.d64", "BarB.d64"... for this feature to work.
|
||||||
|
<LI><B>"Load snapshot..."</B> restores the emulator state from a snapshot file saved with "Save snapshot...".
|
||||||
|
<LI><B>"Save snapshot..."</B> saves the current emulator state to disk.
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
In the bottom left corner of the window, Frodo displays how much percent of
|
||||||
|
the speed of a real C64 the emulation achieves.<P>
|
||||||
|
|
||||||
|
The four items labeled "Drive 8" to "Drive 11" are the disk activity
|
||||||
|
indicators of the 1541 emulation.<P>
|
||||||
|
|
||||||
|
Under <b>RISC OS</b> Frodo can be controlled via Menus and the emulator pane. Some notes on
|
||||||
|
the pane entries:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><B>Drive LEDs 8-11</B> display the drives' state. You can also set the drive paths by dragging files/directories to the corresponding LEDs.
|
||||||
|
<LI>The <B>Speedometer</B> is clickable, its border type displays the state of the speed limiter. Slab in means off, slab out means on.
|
||||||
|
<LI>The <B>Pause</B> and <B>Reset</B> icons should be self-explanatory.
|
||||||
|
<LI>The <B>Size Toggler</B> lets you choose between two display sizes. It shows the one you'll get if you click it, rather than the current one.
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
85
Docs/files.html
Normal file
85
Docs/files.html
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>File access</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>File access</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
Frodo offers four possibilities for the 1541 emulation:
|
||||||
|
|
||||||
|
<H3>1. Host system directory, setting "Dir"</H3>
|
||||||
|
|
||||||
|
In this mode, the C64 programs and files are stored in a directory on your
|
||||||
|
hard disk and can be <KBD>LOAD</KBD>ed and <KBD>SAVE</KBD>d 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.<P>
|
||||||
|
|
||||||
|
You can also load the directory with <KBD>LOAD"$",8</KBD>. 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. <KBD>LOAD"GAMES/ELITE",8</KBD>),
|
||||||
|
unless the '/' translation setting is turned on.<P>
|
||||||
|
|
||||||
|
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 <KBD>LOAD"*",8</KBD> 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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<H3>2. .d64/x64 disk image file, setting "D64"</H3>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
Apart from .d64 files, Frodo can also use image files of the "x64"
|
||||||
|
emulator, automatically detecting the file type.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<H3>3. .t64/LYNX archive file, setting "T64"</H3>
|
||||||
|
|
||||||
|
.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.<P>
|
||||||
|
|
||||||
|
When loading the directory with <KBD>LOAD"$",8</KBD>, Frodo creates a
|
||||||
|
listing of all files within the archive. You cannot write to .t64 or LYNX
|
||||||
|
files, they are read-only under Frodo.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<H3>4. Processor-level 1541 emulation</H3>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
76
Docs/flavours.html
Normal file
76
Docs/flavours.html
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Frodo, Frodo PC and Frodo SC</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Frodo, Frodo PC and Frodo SC</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
Frodo comes in three 'flavours' that allow you to decide between speed
|
||||||
|
and accuracy of the emulation.
|
||||||
|
|
||||||
|
<H2>The line-based emulation 'Frodo'</H2>
|
||||||
|
|
||||||
|
<B>Frodo</B> 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.
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<H2>The improved line-based emulation 'Frodo PC'</H2>
|
||||||
|
|
||||||
|
<B>Frodo PC</B> is also a line-based emulation but it has some improvements
|
||||||
|
over the standard Frodo:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Code in chip registers can be executed
|
||||||
|
<LI>Correct calculation of 6510 instruction cycles
|
||||||
|
<LI>More precise CIA emulation
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<H2>The single-cycle emulation 'Frodo SC'</H2>
|
||||||
|
|
||||||
|
<B>Frodo SC</B> 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:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>The "Cycles per line" settings are not available as the timing of Frodo SC is hardcoded
|
||||||
|
<LI>The "Clear CIA IRC on write" hack is not necessary
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>On the left and right side of the screen, sprites are not clipped but blanked out
|
||||||
|
<LI>Sprite collisions are only detected within the visible screen area (excluding borders)
|
||||||
|
<LI>The sprite data fetch ignores the state of BA
|
||||||
|
<LI>On BA low and AEC high, the VIC always reads $f in D8-D11
|
||||||
|
<LI>Color register modifications are visible 7 pixels too late
|
||||||
|
<LI>The TOD clock should not be stopped on a read access, but be latched
|
||||||
|
<LI>The SDR interrupt is faked
|
||||||
|
<LI>Some small incompatibilities with the CIA timers
|
||||||
|
<LI>The readable SID registers are not emulated correctly
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
26
Docs/future.html
Normal file
26
Docs/future.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>The future</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>The future</H1>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
"Ai! Palan ú ná metta eldaloaron!"
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
These are some of the things planned for future versions:<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Usage of printers and modems (rather unlikely, as Frodo has not been written to run PrintShop on it <TT>:-)</TT>
|
||||||
|
<LI>A C128 and a GEOS mode
|
||||||
|
<LI>A fast, frame-based emulation mode
|
||||||
|
<LI>Port to more systems (NeXTStep/Rhapsody, Atari ST/TT/Falcon, Amiga PPC, PSX, N64)
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</PRE>
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
224
Docs/history.html
Normal file
224
Docs/history.html
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>History</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Revision history</H1>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
"Those days, the Third Age of Middle-earth,<BR>
|
||||||
|
are now long past, and the shape of all lands<BR>
|
||||||
|
has been changed."
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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++.<P>
|
||||||
|
|
||||||
|
<H3>V1.x/V2.x</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Amiga 68K versions
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>First release of BeOS/Unix version, features processor-level 1541
|
||||||
|
emulation and built-in SID emulation
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0a</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Implemented SID test bits
|
||||||
|
<LI>Combined SID waveforms respect pulse width
|
||||||
|
<LI>Corrected idle state graphics display
|
||||||
|
<LI>Processor-level 1541 emulation respects .d64 error info
|
||||||
|
<LI>CPU emulation optimized (6510 and 6502 split)
|
||||||
|
<LI>VIC emulation optimized (raster counter in local variable in EmulateLine())
|
||||||
|
<LI>BeOS: Now exiting the audio subscriber with ExitStream(TRUE)
|
||||||
|
<LI>Unix: Fixed missing thread_func() declaration
|
||||||
|
<LI>Unix: getcwd(AppDirPath) was missing in main_x.i
|
||||||
|
<LI>Unix: Speed limiter works
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0b</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Implemented REU emulation
|
||||||
|
<LI>Formatting disks with ID possible under processor-level 1541 emulation
|
||||||
|
<LI>Corrected and optimized SID waveform/envelope calculation (signed arithmetic)
|
||||||
|
<LI>Corrected idle state display again (ECM text)
|
||||||
|
<LI>1541 D64 mode ignores drive numbers when opening the directory
|
||||||
|
<LI>Processor-level 1541 emulation deactivates when idle
|
||||||
|
<LI>BeOS: Sound output quality is now 16 bits
|
||||||
|
<LI>Unix: Quits more cleanly, reactivates key repeat
|
||||||
|
<LI>Unix: Fixed alignment problem with text_chunky_buf in VIC.h
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0c</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>1541 DIR mode can load directory with "$0"
|
||||||
|
<LI>Rearranged the CPU code (more macros, less inline functions)
|
||||||
|
<LI>SID envelope generators rewritten, envelopes are now recalculated for every sample
|
||||||
|
<LI>SID calc_buffer function now takes pointer to WORD buffer
|
||||||
|
<LI>Unix: Sound for Linux
|
||||||
|
<LI>Unix: Prefs window implemented (needs Tcl/Tk)
|
||||||
|
<LI>Unix: Corrected x64 disk image detection on little-endian systems
|
||||||
|
<LI>Unix: SVGAlib support works again, accesses frame buffer directly if possible
|
||||||
|
<LI>Unix: Added support for SHM under X11
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0d</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>SID filter emulation implemented
|
||||||
|
<LI>SID master volume setting works again
|
||||||
|
<LI>Flags are recalculated in MOS6526::SetState()
|
||||||
|
<LI>Changed CBOOL->bool in some places
|
||||||
|
<LI>Fixed bug with char_in in MOS6510::new_config()
|
||||||
|
<LI>BeOS: Emulation thread priority lowered
|
||||||
|
<LI>Unix: Some changes for DEC Alpha
|
||||||
|
<LI>Unix: Joystick support for Linux
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0e</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Removed the CBOOL data type
|
||||||
|
<LI>Unix: Sound for HP-UX
|
||||||
|
<LI>Unix: Keyboard layout matches the picture in the docs more closely
|
||||||
|
<LI>Unix: Diagonal directions of keypad joystick emulation work
|
||||||
|
<LI>Unix: +/- on numerical keypad modifies SkipFrames
|
||||||
|
<LI>Unix: F9 invokes SAM
|
||||||
|
<LI>Unix: Drive LEDs and speedometer implemented
|
||||||
|
<LI>Unix: Some changes to the GUI
|
||||||
|
<LI>Unix: Random number generator is initialized
|
||||||
|
<LI>Unix: Name of prefs file can be given as an argument
|
||||||
|
<LI>Unix: Calls XFlush() and XSync() in C64Display::Update()
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0f</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Improved DIR/D64 drive reset, resetting the C64 resets the drives
|
||||||
|
<LI>Implemented 'G' command for DIR/D64 drives
|
||||||
|
<LI>Corrected translation of 0xc1..0xda characters in conv_from_64()
|
||||||
|
<LI>BeOS: Implemented smart "Insert next disk" menu item
|
||||||
|
<LI>Unix: Improved the speedometer/LED bar
|
||||||
|
<LI>Unix: Self-calibrating joystick routines
|
||||||
|
<LI>Unix: No need to enter path of 'wish' in TkGui.tcl
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0g</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>New T64/LYNX mode for 1541 emulation
|
||||||
|
<LI>1541fs.cpp/match() used to treat the pattern "foo" as "foo*"
|
||||||
|
<LI>1541 DIR mode uses tmpfile() for opening temporary directory files
|
||||||
|
<LI>1541 D64 mode allows wildcards for selective directory reading
|
||||||
|
<LI>Increased compatibility of processor-level 1541 emulation in various places (C64<->1541 communication, VIA registers, memory map, disk change flag)
|
||||||
|
<LI>Inlined MOS6526::EmulateLine() and some small public functions of MOS6502_1541
|
||||||
|
<LI>New prefs option to enable/disable SID filter emulation
|
||||||
|
<LI>Joystick calibration is reset when joystick options change
|
||||||
|
<LI>BeOS: Self-calibrating joystick routines
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.0h</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Implemented SID notch filter, better resonance frequency calculation
|
||||||
|
<LI>Fixed bug with SID filter option
|
||||||
|
<LI>CIA timer B one-shot mode stops timer when counting undeflows of timer A
|
||||||
|
<LI>Implemented lightpen trigger
|
||||||
|
<LI>BeOS: Fixed for BeOS DR8, improved the GUI a bit
|
||||||
|
<LI>Unix: Some fixes to the TkGui (T64, SIDFilters, removed speed display)
|
||||||
|
<LI>Unix: Main window no longer resizable
|
||||||
|
<LI>Unix: SVGALib support works again
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.1</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>BeOS: Joysticks work again
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.1a</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Frodo SC ported
|
||||||
|
<LI>Processor-level 1541 emulation supports reading GCR data, removed the faked job loop
|
||||||
|
<LI>Corrected ISB, RRA, SBX and SHA instructions
|
||||||
|
<LI>The last line of Y expanded sprites wasn't drawn
|
||||||
|
<LI>Light pen registers work
|
||||||
|
<LI>Small fixes to 1541d64/1541t64
|
||||||
|
<LI>CIA 2 PRA write: IEC lines respect DDRA
|
||||||
|
<LI>Better triangle waveform (12 bits)
|
||||||
|
<LI>SID emulation can play sampled sounds
|
||||||
|
<LI>New "Ignore SID Volume" prefs item for better sample playing
|
||||||
|
<LI>'*' on numerical keypad toggles speed limiter
|
||||||
|
<LI>BeOS: '/' on numerical keypad toggles processor-level 1541 emulation
|
||||||
|
<LI>BeOS: Safer quitting
|
||||||
|
<LI>BeOS: Option to use GameKit (screen)
|
||||||
|
<LI>BeOS: Replaced srand(system_time()) with srand(real_time_clock())
|
||||||
|
<LI>Unix: Sun makefile
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.1b</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Corrected SID sustain behaviour
|
||||||
|
<LI>Reading from write-only SID registers returns the last byte written to the SID
|
||||||
|
<LI>No more distortions when playing samples
|
||||||
|
<LI>Removed the "Ignore SID Volume" prefs item again
|
||||||
|
<LI>Combined SID waveforms now sampled from a 6581R4
|
||||||
|
<LI>Improved 1541 VIA timer operation
|
||||||
|
<LI>Fixed bug in 1541 head movement
|
||||||
|
<LI>Raster IRQs can be triggered by writing to $d011/$d012
|
||||||
|
<LI>Some changes for the MacOS port
|
||||||
|
<LI>Included autoconf stuff from Bernd
|
||||||
|
<LI>Frodo SC: Fixed some CIA timer bugs
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V3.1c</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Ported to AmigaOS
|
||||||
|
<LI>Fixed bug in IEC::Reset()
|
||||||
|
<LI>Fixed bug when writing to SID registers >24
|
||||||
|
<LI>The SID noise waveform should now sound the same on all platforms
|
||||||
|
<LI>Removed all calls to tolower() in SAM.cpp because of possible side-effects if tolower() is a macro
|
||||||
|
<LI>Drive LEDs are only updated once per frame
|
||||||
|
<LI>.d64/.t64 files are opened with read permissions only
|
||||||
|
<LI>Fixed bug with read_char buffering in 1541fs.cpp/1541t64.cpp
|
||||||
|
<LI>Frodo SC: Fixed memory trashing bug in MOS6569::draw_background()
|
||||||
|
<LI>Unix: Drive LEDs blink on error
|
||||||
|
<LI>Unix: Added more patches from Bernd
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V4.0</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>The C64 ROM files are now included
|
||||||
|
<LI>Unix: Added SVGAlib keyboard patches from Bernd
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V4.0a</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Corrected BRK, ANE, ARR, SBX and DCP instructions
|
||||||
|
<LI>Frodo SC: Improved the CIA timers
|
||||||
|
<LI>Frodo SC: MOS6526::EmulateCycle() split into MOS6526::EmulateCycle() and MOS6526::CheckIRQs()
|
||||||
|
<LI>Frodo SC: Corrected interrupt behaviour of branch instructions
|
||||||
|
<LI>BeOS: Sound calculation is now done in stereo
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H3>V4.1</H3>
|
||||||
|
<UL>
|
||||||
|
<LI>Integrated Win32 and RiscOS ports
|
||||||
|
<LI>Snapshot support
|
||||||
|
<LI>Added page-crossing and "borrowed" cycles in line-based CPU emulation (Frodo PC)
|
||||||
|
<LI>Added precise CIA cycles for line-based emulation (Frodo PC)
|
||||||
|
<LI>Optional fixed-point arithmetic and precomputed filters in SID.cpp
|
||||||
|
<LI>Optional dynamic alignment checks in VIC.cpp
|
||||||
|
<LI>Changed typedefs and constants (BYTE, WORD etc.)
|
||||||
|
<LI>Unix: Better configure script
|
||||||
|
<LI>Unix: Sound support for Solaris 2.x
|
||||||
|
<LI>Unix: Joystick can be toggled between port 1/2 with the NumLock key
|
||||||
|
<LI>Unix: US keyboard layouts supported
|
||||||
|
<LI>BeOS: Fixed for BeOS AA:DR9
|
||||||
|
<LI>BeOS: Can now switch between window/screen mode while the emulation is running, speeded up full screen mode
|
||||||
|
<LI>BeOS: Prefs saved in /system/settings/Frodo_settings by default
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
64
Docs/installation.html
Normal file
64
Docs/installation.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Installation</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Installation</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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?_?.<system name>.*") contain an executable that can be started directly.
|
||||||
|
|
||||||
|
<H2>Compiling under BeOS</H2>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<H2>Compiling under Unix</H2>
|
||||||
|
|
||||||
|
First you have to run the program "configure". This can be done simply with
|
||||||
|
the following command:<P>
|
||||||
|
|
||||||
|
<KBD>cd Src<BR>
|
||||||
|
./configure</KBD><P>
|
||||||
|
|
||||||
|
You might want to give configure optional arguments. To use SVGAlib on Linux
|
||||||
|
systems, you have to do<P>
|
||||||
|
|
||||||
|
<KBD>cd Src<BR>
|
||||||
|
./configure --without-x</KBD><P>
|
||||||
|
|
||||||
|
To select a german keyboard layout in the X11 version instead of the usual
|
||||||
|
US keyboard, do<P>
|
||||||
|
|
||||||
|
<KBD>cd Src<BR>
|
||||||
|
./configure --enable-kbd-lang-de</KBD><P>
|
||||||
|
|
||||||
|
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<P>
|
||||||
|
|
||||||
|
<KBD>make all</KBD><P>
|
||||||
|
|
||||||
|
<H2>Compiling under AmigaOS</H2>
|
||||||
|
|
||||||
|
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<P>
|
||||||
|
|
||||||
|
<KBD>cd Src<BR>
|
||||||
|
make -fMakefile.Amiga all</KBD><P>
|
||||||
|
|
||||||
|
<H2>The C64 ROM files (all systems)</H2>
|
||||||
|
|
||||||
|
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
|
||||||
|
<A HREF="kernal.html">extended</A> version of the C64 Kernal.
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
55
Docs/kernal.html
Normal file
55
Docs/kernal.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Kernal extensions</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Extensions of the included Kernal ROM</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
The included "Kernal ROM" has some improvements/changes as compared to
|
||||||
|
an original C64 kernal:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>RAM at $fd30-$fd4f is not overwritten during reset
|
||||||
|
<LI>Default device address is 8
|
||||||
|
<LI>Default secondary address is 1
|
||||||
|
<LI>Start and end address are displayed when loading
|
||||||
|
<LI>C= key stops scrolling
|
||||||
|
<LI>Faster key repeat
|
||||||
|
<LI>Tape and RS232 routines removed
|
||||||
|
<LI>Key commands:
|
||||||
|
<UL>
|
||||||
|
<LI>F1 : <KBD><CLS> LIST <CR></KBD>
|
||||||
|
<LI>F2 : <KBD>SYS32768 <CR></KBD>
|
||||||
|
<LI>F3 : <KBD>RUN <CR></KBD>
|
||||||
|
<LI>F4 : <KBD>SYS4096*12</KBD>
|
||||||
|
<LI>F5 : <KBD>LOAD"</KBD>
|
||||||
|
<LI>F6 : <KBD>SAVE"</KBD>
|
||||||
|
<LI>F7 : <KBD>LOAD"$",8 <CR></KBD>
|
||||||
|
<LI>F8 : <KBD>CLOSE7:OPEN7,8,15,"</KBD>
|
||||||
|
<LI>SHIFT-Run: <KBD>LOAD":*",8,1:RUN <CR></KBD>
|
||||||
|
<LI>CTRL-D : Display directory of drive 8
|
||||||
|
<LI>CTRL-K : Read error channel of drive 8
|
||||||
|
<LI>CTRL-L : Load Basic program from RAM disk
|
||||||
|
<LI>CTRL-O : UNNEW
|
||||||
|
<LI>CTRL-U : Modifies the SAVE routine so that the RAM at $a000-$bfff can be <KBD>SAVE</KBD>d
|
||||||
|
<LI>CTRL-V : Swap Basic program with RAM disk
|
||||||
|
<LI>CTRL-W : Save Basic program to RAM disk
|
||||||
|
<LI>CTRL-X : Continue LIST command
|
||||||
|
<LI>CTRL-Z : Continue LIST command 50 lines earlier
|
||||||
|
<LI>CTRL-F1 : Swap screen with buffer 1
|
||||||
|
<LI>CTRL-F3 : Swap screen with buffer 2
|
||||||
|
<LI>CTRL-F5 : Swap screen with buffer 3
|
||||||
|
<LI>CTRL-F7 : Swap screen with buffer 4
|
||||||
|
<LI>CBM-F1 : Write screen to buffer 3
|
||||||
|
<LI>CBM-F3 : Write screen to buffer 4
|
||||||
|
<LI>CBM-F5 : Get screen from buffer 3
|
||||||
|
<LI>CBM-F7 : Get screen from buffer 4
|
||||||
|
</UL>
|
||||||
|
<LI>Startup message shows "BASIC X2"
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
140
Docs/keyboard.html
Normal file
140
Docs/keyboard.html
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Keyboard layout</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Keyboard layout</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
The keyboard layout closely resembles that of a real C64. The individual
|
||||||
|
rows of the keyboard are mapped as follows (american keyboard):
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
<- 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 , . /
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
In addition, the following keys are used:
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
Esc - RUN/STOP
|
||||||
|
Backspace - INS/DEL
|
||||||
|
Return - RETURN
|
||||||
|
Enter - RETURN
|
||||||
|
Shift keys - SHIFT
|
||||||
|
Caps lock - SHIFT LOCK
|
||||||
|
F1-F8 - F1-F8
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
Special keys under BeOS:
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
\ - ^
|
||||||
|
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
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
Special keys under Unix:
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
\ - ^
|
||||||
|
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
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
Special keys under AmigaOS:
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
\ - £
|
||||||
|
Delete - CLR/HOME
|
||||||
|
( (keypad) - ^
|
||||||
|
) (keypad) - =
|
||||||
|
Alt Keys - C=
|
||||||
|
Ctrl - CTRL
|
||||||
|
F9 - RESTORE
|
||||||
|
F10 - C64 Reset
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
Special keys under RISC OS:
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
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
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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. <TT>:-)</TT><P>
|
||||||
|
|
||||||
|
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):<P>
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
7 8 9
|
||||||
|
^
|
||||||
|
|
|
||||||
|
4 5 6
|
||||||
|
<-- Fire -->
|
||||||
|
|
|
||||||
|
v
|
||||||
|
1 2 3
|
||||||
|
|
||||||
|
0
|
||||||
|
Fire
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
Keyboard joysticks are handled differently under RISC OS:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>NumLock on: only joystick 1 active, mapped to port 2. NumLock off: both joysticks active, 1 mapped to port 1, 2 mapped to port 2.
|
||||||
|
<LI>Joystick keys can be defined freely. Defaults are: joystick 1: (1 2 3 . enter) on the numerical keypad, joystick 2: (z x f c g).
|
||||||
|
</UL><P>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
37
Docs/legalmush.html
Normal file
37
Docs/legalmush.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Copyright and distribution</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Copyright and distribution</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
50
Docs/overview.html
Normal file
50
Docs/overview.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Overview</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Overview</H1>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
"Ú-queta i yéni avánier alye,<BR>
|
||||||
|
ú-queta i cirya ná vanwa!"
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<STRONG>Frodo</STRONG> 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. <TT>:-)</TT> (No, it has absolutely nothing to do with
|
||||||
|
frodo.hiof.no, that's a pure coincidence.)<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
Frodo comes in three <A HREF="flavours.html">flavours</A>: 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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
Frodo runs on these systems:<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>BeBox or PowerMac with BeOS Preview Release
|
||||||
|
<LI>Unix systems with X11R6 or Linux/SVGAlib (sound only under Linux, HP-UX and Solaris 2.x)
|
||||||
|
<LI>68k or PPC Macintosh with System 7.5
|
||||||
|
<LI>Amiga/DraCo with 68040/68060, AmigaOS 3.0 and a graphics card (AHI V3 required for sound)
|
||||||
|
<LI>Intel x86 system running Windows NT/95
|
||||||
|
<LI>Acorn computers with RiscOS 3
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
282
Docs/sam.html
Normal file
282
Docs/sam.html
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>SAM</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>SAM</H1>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
"Frodo! Mr.Frodo, my dear!" cried Sam,<BR>
|
||||||
|
tears almost blinding him. "It's Sam, I've come!"
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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).<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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).<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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):<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
a [start] Assemble
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
b [start] [end] Binary dump
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
displays the memory from "start" to "end" byte-wise binary. With this
|
||||||
|
command, you can view character sets.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
c start end dest Compare memory
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
d [start] [end] Disassemble
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
disassembles the memory from "start" to "end". Undocumented opcodes are
|
||||||
|
markes with a star '*'.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
e Show interrupt vectors
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
f start end byte Fill memory
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
fills the memory in the range from "start" to (and including) "end" with
|
||||||
|
the value "byte".<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
i [start] [end] ASCII/PETSCII dump
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
shows the memory from "start" to "end" as ASCII/PETSCII characters.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
k [config] Show/set C64 memory configuration
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
"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:
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
# $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
|
||||||
|
|
||||||
|
</PRE>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
l start "file" Load data
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
m [start] [end] Memory dump
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
displays the memory from "start" to "end" as hexadecimal numbers and ASCII
|
||||||
|
characters.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
n [start] [end] Screen code dump
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
displays the memory from "start" to "end" as ASCII characters, interpreting
|
||||||
|
each byte as a screen code of the standard character set.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
o ["file"] Redirect output
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
p [start] [end] Sprite dump
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
displays the memory from "start" to "end" binary with three bytes per line.
|
||||||
|
With this command, you can display sprite data.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
r [reg value] Show/set CPU registers
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
"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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
s start end "file" Save data
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
t start end dest Transfer memory
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
transfers the memory from "start" to (and including) "end" to "dest".
|
||||||
|
Source and destination may overlap.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
vc1 View CIA 1 state
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
shows the state of CIA 1 ($dc00).<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
vc2 View CIA 2 state
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
shows the state of CIA 2 ($dd00).<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
vf View floppy state
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
shows the state of the processor-level 1541 emulation.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
vs View SID state
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
shows the state of the SID.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
vv View VIC state
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
shows the state of the VIC.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
x Return to Frodo
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
quits SAM and returns to Frodo.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
: addr {byte} Modify memory
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
writes the space-separated values "byte" into memory starting at "addr".<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
1541 Switch to 1541 mode
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
switches to 1541 mode. All commands that access memory or the CPU will then
|
||||||
|
operate on the emulated 1541 (processor-level).<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
64 Switch to C64 mode
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
switches to C64 mode. All commands that access memory or the CPU will then
|
||||||
|
operate on the emulated C64.<P>
|
||||||
|
|
||||||
|
|
||||||
|
<PRE>
|
||||||
|
? expression Calculate expression
|
||||||
|
</PRE><P>
|
||||||
|
|
||||||
|
calculates the value of the given expression and displays it in decimal
|
||||||
|
and hexadecimal.<P>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
167
Docs/settings.html
Normal file
167
Docs/settings.html
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Settings</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Settings</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
With <B>"Sprite display"</B>, 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.<P>
|
||||||
|
|
||||||
|
<B>"Sprite collisions"</B> 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 <TT>:-/</TT>.<P>
|
||||||
|
|
||||||
|
<B>"Joystick on Port 1/2"</B> specifies on which ports you have real
|
||||||
|
joysticks connected (as opposed to the <A HREF="keyboard.html">joystick
|
||||||
|
emulation</A> 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.<P>
|
||||||
|
|
||||||
|
With <B>"Swap joysticks"</B> 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.<P>
|
||||||
|
|
||||||
|
When the field <B>"Limit speed"</B> 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.<P>
|
||||||
|
|
||||||
|
With the setting <B>"Fast Reset"</B> 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.<P>
|
||||||
|
|
||||||
|
The setting <B>"Clear CIA ICR on write"</B> 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.<P>
|
||||||
|
|
||||||
|
The <B>"SID Filters"</B> field enables the emulation of the SID filters.
|
||||||
|
The sound emulation is slightly faster, but worse, when the filters are
|
||||||
|
disabled.<P>
|
||||||
|
|
||||||
|
<B>"Doublescan lines"</B> is only available under BeOS for the "Screen"
|
||||||
|
display type. It removes the black lines between scanlines, but makes
|
||||||
|
the emulation a bit slower.<P>
|
||||||
|
|
||||||
|
<B>"Cycles per line (CPU)"</B> and <B>"Cycles per Bad Line (CPU)"</B> 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)".<P>
|
||||||
|
|
||||||
|
With <B>"Cycles per line (CIA)"</B> 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).<P>
|
||||||
|
|
||||||
|
<B>"Cycles per line (1541)"</B> 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.<P>
|
||||||
|
|
||||||
|
The settings for the four "cycles" coming closest to an original PAL C64
|
||||||
|
are (63, 23, 63, 64).<P>
|
||||||
|
|
||||||
|
With <B>"Draw every n-th frame"</B> 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.<P>
|
||||||
|
|
||||||
|
<B>"Display type"</B> is only available under BeOS. You can choose between
|
||||||
|
running the emulation in a window or in full-screen mode (using the
|
||||||
|
Game Kit).<P>
|
||||||
|
|
||||||
|
The <B>"SID emulation type"</B> controls the sound emulation and has two
|
||||||
|
settings: <EM>"None"</EM> and <EM>"Digital"</EM>. <EM>"None"</EM> means no
|
||||||
|
sound (faster), <EM>"Digital"</EM> 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.<P>
|
||||||
|
|
||||||
|
<B>"REU size"</B> 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).<P>
|
||||||
|
|
||||||
|
In the box <B>"Drives"</B>, 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 <EM>popup control</EM>, a <EM>path entry field</EM>
|
||||||
|
and a <EM>button</EM>:<P>
|
||||||
|
|
||||||
|
With the <B>popup control</B>, you select the emulation mode of the
|
||||||
|
respective disk drive (for more detailed information, see <A
|
||||||
|
HREF="files.html">here</A>). There are three choices: <EM>"Dir"</EM>,
|
||||||
|
<EM>"D64"</EM> and <EM>"T64"</EM>. <B>"Dir"</B> emulates the drive in a
|
||||||
|
directory of the BeOS/Unix file system. <B>"D64"</B> accesses a .d64 or x64
|
||||||
|
disk image file. <B>"T64"</B> is the setting for accessing a .t64 or C64
|
||||||
|
LYNX archive file.<P>
|
||||||
|
|
||||||
|
The <B>path entry field</B> 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.<P>
|
||||||
|
|
||||||
|
The <B>button labeled "B"</B> opens a file panel/requester for a more
|
||||||
|
comfortable selection of directories and .d64/x64/.t64/LYNX files.<P>
|
||||||
|
|
||||||
|
With <B>"Map '/' <-> '\' in file names"</B> 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.<P>
|
||||||
|
|
||||||
|
If <B>"Enable 1541 processor emulation"</B> 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.<P>
|
||||||
|
|
||||||
|
<H2>BeOS/AmigaOS</H2>
|
||||||
|
|
||||||
|
Clicking <B>"Start"/"OK"</B> will start the actual emulation (resp. return
|
||||||
|
to it) and <B>"Quit"/"Cancel"</b> will discard your changes to the settings
|
||||||
|
and quit Frodo (resp. discard the changes and return to the emulation).<P>
|
||||||
|
|
||||||
|
With the menu items <B>"Open..."</B>, <B>"Save"</B>, <B>"Save As..."</B>
|
||||||
|
and <B>"Revert"</B> you can load and save the settings from and to
|
||||||
|
arbitrary files.<P>
|
||||||
|
|
||||||
|
<H2>Unix</H2>
|
||||||
|
|
||||||
|
Clicking <B>"Apply"</B> applies the settings of the "Cycles" controls to
|
||||||
|
the running emulation (all other settings are applied automatically).
|
||||||
|
<B>"Defaults"</B> reverts to the default settings, <B>"Quit"</B> quits
|
||||||
|
Frodo and <B>"Reset"</B> resets the emulation.<P>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
102
Docs/systemspecific.html
Normal file
102
Docs/systemspecific.html
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>System specific notes</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>System specific notes</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<H2>BeOS</H2>
|
||||||
|
|
||||||
|
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".<P>
|
||||||
|
|
||||||
|
At first, the window for the emulation <A HREF="settings.html">settings</A>
|
||||||
|
appears. The actual emulation is started by a click on "Start". Then the <A
|
||||||
|
HREF="emulwindow.html">emulation</A> window appears in which the C64
|
||||||
|
startup message is displayed.<P>
|
||||||
|
|
||||||
|
You can quit from the running emulation by selecting the "Quit" menu
|
||||||
|
item.<P>
|
||||||
|
|
||||||
|
Frodo should be run in a 256-color workspace for maximum speed.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
<H2>Unix</H2>
|
||||||
|
|
||||||
|
Frodo accepts a single argument when started from the shell: The name of a
|
||||||
|
preferences file to load instead of the default "~/.frodorc".<P>
|
||||||
|
|
||||||
|
First the <A HREF="emulwindow.html">emulation</A> 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 <A HREF="settings.html">settings</A>.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
<H2>MacOS</H2>
|
||||||
|
|
||||||
|
Frodo is started by double-clicking its icon.<P>
|
||||||
|
|
||||||
|
You can quit from the running emulation by selecting the "Quit" menu
|
||||||
|
item.<P>
|
||||||
|
|
||||||
|
Frodo should be run with 8-bit color depth. The "DIR" 1541 emulation mode
|
||||||
|
and SAM are currently not implemented.<P>
|
||||||
|
|
||||||
|
See the file "MacFrodo Notes" for more information.<P>
|
||||||
|
|
||||||
|
<H2>AmigaOS</H2>
|
||||||
|
|
||||||
|
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".<P>
|
||||||
|
|
||||||
|
At first, the window for the emulation <A HREF="settings.html">settings</A>
|
||||||
|
appears. The actual emulation is started by a click on "OK". Then the <A
|
||||||
|
HREF="emulwindow.html">emulation</A> window appears in which the C64
|
||||||
|
startup message is displayed.<P>
|
||||||
|
|
||||||
|
You can quit from the running emulation by selecting the "Quit" menu
|
||||||
|
item.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
<H2>RISC OS</H2>
|
||||||
|
|
||||||
|
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.<p>
|
||||||
|
|
||||||
|
Frodo can be controlled with the menus and the <a href="emulwindow.html">emulator
|
||||||
|
pane</a>. You can load snapshots and native C64-files (which usually have the
|
||||||
|
filetype &64) by dragging them to the emulator window.<p>
|
||||||
|
|
||||||
|
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.<p>
|
||||||
|
|
||||||
|
For a lot more information see the file !Help supplied in the RISC OS
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
61
Docs/technicalinfo.html
Normal file
61
Docs/technicalinfo.html
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Technical info</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Technical info</H1>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
"Known?" said Gandalf.<BR>
|
||||||
|
"I have known much that only the Wise know, Frodo."
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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.<P>
|
||||||
|
|
||||||
|
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).<P>
|
||||||
|
|
||||||
|
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).<P>
|
||||||
|
|
||||||
|
A detailed technical description of the VIC-II can be found in an
|
||||||
|
<A HREF="http://www.uni-mainz.de/~bauec002/VIC-Article.gz">article</A>
|
||||||
|
I wrote (32k gzipped).
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
36
Docs/thanks.html
Normal file
36
Docs/thanks.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Credits</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>Credits</H1>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
"The Silmaril as lantern light<BR>
|
||||||
|
And banner bright with living flame<BR>
|
||||||
|
To gleam thereon by Elbereth<BR>
|
||||||
|
Herself was set, who thither came."
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
The following persons deserve special thanks from me as they made a
|
||||||
|
significant contribution to the development of Frodo:<P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A HREF="mailto:a.boose@ldb.han.de">Andreas Boose</A> and <A HREF="mailto:marko.makela@hut.fi">Marko Mäkelä</A> who provided me with precious information on the VIC and on the C64 in general
|
||||||
|
<LI><A HREF="mailto:crux@pool.informatik.rwth-aachen.de">Bernd Schmidt</A> and <A HREF="mailto:lkv@mania.robin.de">Lutz Vieweg</A> who ported Frodo to Unix systems
|
||||||
|
<LI><A HREF="mailto:titan@indigo.ie">Richard Bannister</A>, <A HREF="mailto:macsupport@overnet.com.ar">Ernesto Corvi</A> and <A HREF="mailto:e9426444@student.tuwien.ac.at">Andreas Varga</A> who ported Frodo to MacOS
|
||||||
|
<LI><A HREF="mailto:jrs@world.std.com">J. Richard Sladkey</A> who ported Frodo to Windows NT/95
|
||||||
|
<LI><A HREF="mailto:dehmel@informatik.tu-muenchen.de">Andreas Dehmel</A> who ported Frodo to RISC OS
|
||||||
|
<LI><A HREF="mailto:Marc.Chabanas@france.sun.com">Marc Chabanas</A> for the Solaris sound routines
|
||||||
|
<LI><A HREF="mailto:pst@cocoon.infra.de">Peter Stegemann</A> and <A HREF="mailto:stegeman@ai-lab.fh-furtwangen.de">Jörg Stegemann</A> who did extensive beta-testing
|
||||||
|
<LI><A HREF="mailto:5uro@informatik.uni-hamburg.de">Tinic Urou</A> for designing the Frodo logo and for the GameKit code
|
||||||
|
<LI><A HREF="mailto:jehamby@lightside.com">Jake Hamby</A>, Wolfgang Lorenz and <A HREF="mailto:d96shade@dtek.chalmers.se">Erik Lindberg</A> for bugfixes and improvements
|
||||||
|
<LI>J.R.R.Tolkien for the suggestion for the name of the emulator
|
||||||
|
<LI>All the people who have sent me suggestions and comments
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
24
Docs/whatsnew.html
Normal file
24
Docs/whatsnew.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>What's new?</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
|
||||||
|
<H1>What's new in V4.1?</H1>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
The most important changes from V4.0 are:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Ability to save/load the emulator state to/from snapshot files
|
||||||
|
<LI>Ported to Win32 and Acorn RiscOS
|
||||||
|
<LI>Added <A HREF="flavours.html">Frodo PC</A>, an improved line-based emulation
|
||||||
|
<LI>Sound support for Solaris 2.x
|
||||||
|
<LI>Fixed several bugs in the 6510/6526 emulation
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
For the tons of other small changes and bug fixes, please look <A HREF="history.html">here</A>.<P>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
BIN
Frodo Logo
Normal file
BIN
Frodo Logo
Normal file
Binary file not shown.
BIN
Frodo.proj
Normal file
BIN
Frodo.proj
Normal file
Binary file not shown.
BIN
FrodoPC.proj
Normal file
BIN
FrodoPC.proj
Normal file
Binary file not shown.
BIN
FrodoSC.proj
Normal file
BIN
FrodoSC.proj
Normal file
Binary file not shown.
BIN
Kernal ROM
Normal file
BIN
Kernal ROM
Normal file
Binary file not shown.
1025
Src/1541d64.cpp
Normal file
1025
Src/1541d64.cpp
Normal file
File diff suppressed because it is too large
Load Diff
108
Src/1541d64.h
Normal file
108
Src/1541d64.h
Normal file
@ -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
|
734
Src/1541fs.cpp
Normal file
734
Src/1541fs.cpp
Normal file
@ -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; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++, true)); i++) ;
|
||||||
|
|
||||||
|
// 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 'r':
|
||||||
|
*filemode = FMODE_READ;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
*filemode = FMODE_WRITE;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
*filemode = FMODE_APPEND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for wildcards
|
||||||
|
*wildflag = (strchr(destname, '?') != NULL) || (strchr(destname, '*') != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find first file matching wildcard pattern and get its real name
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSDrive::find_first_file(char *name)
|
||||||
|
{
|
||||||
|
#ifndef __riscos__
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
// Open directory for reading and skip '.' and '..'
|
||||||
|
if ((dir = opendir(dir_path)) == NULL)
|
||||||
|
return;
|
||||||
|
de = readdir(dir);
|
||||||
|
while (de && (0 == strcmp(".", de->d_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; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*dirpath++, false)); i++) ;
|
||||||
|
|
||||||
|
if (!change_dir(str))
|
||||||
|
set_error(ERR_NOTREADY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset drive
|
||||||
|
*/
|
||||||
|
|
||||||
|
void FSDrive::Reset(void)
|
||||||
|
{
|
||||||
|
close_all_channels();
|
||||||
|
cmd_len = 0;
|
||||||
|
set_error(ERR_STARTUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conversion PETSCII->ASCII
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
46
Src/1541fs.h
Normal file
46
Src/1541fs.h
Normal file
@ -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
|
462
Src/1541job.cpp
Normal file
462
Src/1541job.cpp
Normal file
@ -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<num_sectors[track]; sector++) {
|
||||||
|
write_sector(track, sector, buf);
|
||||||
|
sector2gcr(track, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear error info (all sectors no error)
|
||||||
|
if (track == 35)
|
||||||
|
memset(error_info, 1, NUM_SECTORS);
|
||||||
|
// Write error_info to disk?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read sector (256 bytes)
|
||||||
|
* true: success, false: error
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Job1541::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)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write sector (256 bytes) !! -> 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<num_sectors[track]; sector++)
|
||||||
|
sector2gcr(track, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move R/W head out (lower track numbers)
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Job1541::MoveHeadOut(void)
|
||||||
|
{
|
||||||
|
if (current_halftrack == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
112
Src/1541job.h
Normal file
112
Src/1541job.h
Normal file
@ -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
|
715
Src/1541t64.cpp
Normal file
715
Src/1541t64.cpp
Normal file
@ -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<max; i++)
|
||||||
|
if (buf2[i*32] == 1)
|
||||||
|
num_files++;
|
||||||
|
|
||||||
|
if (!num_files)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Construct file information array
|
||||||
|
file_info = new FileInfo[num_files];
|
||||||
|
for (i=0, j=0; i<max; i++)
|
||||||
|
if (buf2[i*32] == 1) {
|
||||||
|
memcpy(file_info[j].name, buf2+i*32+16, 16);
|
||||||
|
|
||||||
|
// Strip trailing spaces
|
||||||
|
file_info[j].name[16] = 0x20;
|
||||||
|
p = file_info[j].name + 16;
|
||||||
|
while (*p-- == 0x20) ;
|
||||||
|
p[2] = 0;
|
||||||
|
|
||||||
|
file_info[j].type = FTYPE_PRG;
|
||||||
|
file_info[j].sa_lo = buf2[i*32+2];
|
||||||
|
file_info[j].sa_hi = buf2[i*32+3];
|
||||||
|
file_info[j].offset = (buf2[i*32+11] << 24) | (buf2[i*32+10] << 16) | (buf2[i*32+9] << 8) | buf2[i*32+8];
|
||||||
|
file_info[j].length = ((buf2[i*32+5] << 8) | buf2[i*32+4]) - ((buf2[i*32+3] << 8) | buf2[i*32+2]);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buf2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse LYNX file and construct FileInfo array
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool T64Drive::parse_lynx_file(void)
|
||||||
|
{
|
||||||
|
uint8 *p;
|
||||||
|
int dir_blocks, cur_offset, num_blocks, last_block, i;
|
||||||
|
char type_char;
|
||||||
|
|
||||||
|
// Dummy directory title
|
||||||
|
strcpy(dir_title, "LYNX ARCHIVE ");
|
||||||
|
|
||||||
|
// Read header and get number of directory blocks and files contained
|
||||||
|
fseek(the_file, 0x60, SEEK_SET);
|
||||||
|
fscanf(the_file, "%d", &dir_blocks);
|
||||||
|
while (fgetc(the_file) != 0x0d)
|
||||||
|
if (feof(the_file))
|
||||||
|
return false;
|
||||||
|
fscanf(the_file, "%d\015", &num_files);
|
||||||
|
|
||||||
|
// Construct file information array
|
||||||
|
file_info = new FileInfo[num_files];
|
||||||
|
cur_offset = dir_blocks * 254;
|
||||||
|
for (i=0; i<num_files; i++) {
|
||||||
|
|
||||||
|
// Read file name
|
||||||
|
fread(file_info[i].name, 16, 1, the_file);
|
||||||
|
|
||||||
|
// Strip trailing shift-spaces
|
||||||
|
file_info[i].name[16] = 0xa0;
|
||||||
|
p = (uint8 *)file_info[i].name + 16;
|
||||||
|
while (*p-- == 0xa0) ;
|
||||||
|
p[2] = 0;
|
||||||
|
|
||||||
|
// Read file length and type
|
||||||
|
fscanf(the_file, "\015%d\015%c\015%d\015", &num_blocks, &type_char, &last_block);
|
||||||
|
|
||||||
|
switch (type_char) {
|
||||||
|
case 'S':
|
||||||
|
file_info[i].type = FTYPE_SEQ;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
file_info[i].type = FTYPE_USR;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
file_info[i].type = FTYPE_REL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
file_info[i].type = FTYPE_PRG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file_info[i].sa_lo = 0; // Only used for .t64 files
|
||||||
|
file_info[i].sa_hi = 0;
|
||||||
|
file_info[i].offset = cur_offset;
|
||||||
|
file_info[i].length = (num_blocks-1) * 254 + last_block;
|
||||||
|
|
||||||
|
cur_offset += num_blocks * 254;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open channel
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8 T64Drive::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] == '#') {
|
||||||
|
set_error(ERR_NOCHANNEL);
|
||||||
|
return ST_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (the_file == NULL) {
|
||||||
|
set_error(ERR_NOTREADY);
|
||||||
|
return ST_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename[0] == '$')
|
||||||
|
return open_directory(channel, filename+1);
|
||||||
|
|
||||||
|
return open_file(channel, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open file
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8 T64Drive::open_file(int channel, char *filename)
|
||||||
|
{
|
||||||
|
char plainname[NAMEBUF_LENGTH];
|
||||||
|
int filemode = FMODE_READ;
|
||||||
|
int filetype = FTYPE_PRG;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
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
|
||||||
|
if (find_first_file(plainname, filetype, &num)) {
|
||||||
|
|
||||||
|
// Open temporary file
|
||||||
|
if ((file[channel] = tmpfile()) != NULL) {
|
||||||
|
|
||||||
|
// Write load address (.t64 only)
|
||||||
|
if (!is_lynx) {
|
||||||
|
fwrite(&file_info[num].sa_lo, 1, 1, file[channel]);
|
||||||
|
fwrite(&file_info[num].sa_hi, 1, 1, file[channel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy file contents from .t64 file to temp file
|
||||||
|
uint8 *buf = new uint8[file_info[num].length];
|
||||||
|
fseek(the_file, file_info[num].offset, SEEK_SET);
|
||||||
|
fread(buf, file_info[num].length, 1, the_file);
|
||||||
|
fwrite(buf, file_info[num].length, 1, file[channel]);
|
||||||
|
rewind(file[channel]);
|
||||||
|
delete[] buf;
|
||||||
|
|
||||||
|
if (filemode == FMODE_READ) // Read and buffer first byte
|
||||||
|
read_char[channel] = fgetc(file[channel]);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
set_error(ERR_FILENOTFOUND);
|
||||||
|
|
||||||
|
return ST_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Analyze file name, get access mode and type
|
||||||
|
*/
|
||||||
|
|
||||||
|
void T64Drive::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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<num_files; i++)
|
||||||
|
if (match(name, file_info[i].name) && type == file_info[i].type) {
|
||||||
|
*num = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open directory, create temporary file
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8 T64Drive::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, num;
|
||||||
|
int filemode;
|
||||||
|
int filetype;
|
||||||
|
|
||||||
|
// Special treatment for "$0"
|
||||||
|
if (strlen(filename) == 1 && filename[0] == '0')
|
||||||
|
filename += 1;
|
||||||
|
|
||||||
|
// Convert filename ('$' already stripped), filemode/type are ignored
|
||||||
|
convert_filename(filename, pattern, &filemode, &filetype);
|
||||||
|
|
||||||
|
// Create temporary file
|
||||||
|
if ((file[channel] = tmpfile()) == NULL)
|
||||||
|
return ST_OK;
|
||||||
|
|
||||||
|
// Create directory title
|
||||||
|
p = &buf[8];
|
||||||
|
for (i=0; i<16 && dir_title[i]; i++)
|
||||||
|
*p++ = dir_title[i];
|
||||||
|
fwrite(buf, 1, 32, file[channel]);
|
||||||
|
|
||||||
|
// Create and write one line for every directory entry
|
||||||
|
for (num=0; num<num_files; num++) {
|
||||||
|
|
||||||
|
// Include only files matching the pattern
|
||||||
|
if (match(pattern, file_info[num].name)) {
|
||||||
|
|
||||||
|
// 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 = (file_info[num].length + 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, 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; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*t64name++, false)); i++) ;
|
||||||
|
|
||||||
|
close_all_channels();
|
||||||
|
|
||||||
|
// G:. resets the .t64 file name to its original setting
|
||||||
|
if (str[0] == '.' && str[1] == 0)
|
||||||
|
open_close_t64_file(orig_t64_name);
|
||||||
|
else
|
||||||
|
open_close_t64_file(str);
|
||||||
|
|
||||||
|
if (the_file == NULL)
|
||||||
|
set_error(ERR_NOTREADY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset drive
|
||||||
|
*/
|
||||||
|
|
||||||
|
void T64Drive::Reset(void)
|
||||||
|
{
|
||||||
|
close_all_channels();
|
||||||
|
cmd_len = 0;
|
||||||
|
set_error(ERR_STARTUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conversion PETSCII->ASCII
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
62
Src/1541t64.h
Normal file
62
Src/1541t64.h
Normal file
@ -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
|
1774
Src/AcornGUI.cc
Normal file
1774
Src/AcornGUI.cc
Normal file
File diff suppressed because it is too large
Load Diff
341
Src/AcornGUI.h
Normal file
341
Src/AcornGUI.h
Normal file
@ -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
|
10
Src/AcornGUI_SC.cc
Normal file
10
Src/AcornGUI_SC.cc
Normal file
@ -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"
|
407
Src/AmigaGUI.c
Normal file
407
Src/AmigaGUI.c
Normal file
@ -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 <exec/types.h>
|
||||||
|
#include <intuition/intuition.h>
|
||||||
|
#include <intuition/classes.h>
|
||||||
|
#include <intuition/classusr.h>
|
||||||
|
#include <intuition/imageclass.h>
|
||||||
|
#include <intuition/gadgetclass.h>
|
||||||
|
#include <libraries/gadtools.h>
|
||||||
|
#include <graphics/displayinfo.h>
|
||||||
|
#include <graphics/gfxbase.h>
|
||||||
|
#include <clib/exec_protos.h>
|
||||||
|
#include <clib/intuition_protos.h>
|
||||||
|
#include <clib/gadtools_protos.h>
|
||||||
|
#include <clib/graphics_protos.h>
|
||||||
|
#include <clib/utility_protos.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
BIN
Src/AmigaGUI.gui
Normal file
BIN
Src/AmigaGUI.gui
Normal file
Binary file not shown.
153
Src/AmigaGUI.h
Normal file
153
Src/AmigaGUI.h
Normal file
@ -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 );
|
714
Src/C64.cpp
Normal file
714
Src/C64.cpp
Normal file
@ -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; i<delay; i++) {
|
||||||
|
TheVIC->EmulateCycle();
|
||||||
|
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; i<delay; i++) {
|
||||||
|
TheVIC->EmulateCycle();
|
||||||
|
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
|
198
Src/C64.h
Normal file
198
Src/C64.h
Normal file
@ -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 <KernelKit.h>
|
||||||
|
#include <device/Joystick.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef AMIGA
|
||||||
|
#include <devices/timer.h>
|
||||||
|
#include <devices/gameport.h>
|
||||||
|
#include <devices/inputevent.h>
|
||||||
|
#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
|
411
Src/C64_Acorn.i
Normal file
411
Src/C64_Acorn.i
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
393
Src/C64_Amiga.i
Normal file
393
Src/C64_Amiga.i
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
/*
|
||||||
|
* C64_Amiga.i - Put the pieces together, Amiga specific stuff
|
||||||
|
*
|
||||||
|
* Frodo (C) 1994-1997,2002 Christian Bauer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <proto/exec.h>
|
||||||
|
#include <proto/timer.h>
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
366
Src/C64_Be.i
Normal file
366
Src/C64_Be.i
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
* C64_Be.i - Put the pieces together, Be specific stuff
|
||||||
|
*
|
||||||
|
* Frodo (C) 1994-1997,2002 Christian Bauer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <KernelKit.h>
|
||||||
|
#include <device/Joystick.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
12
Src/C64_PC.cpp
Normal file
12
Src/C64_PC.cpp
Normal file
@ -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
|
12
Src/C64_SC.cpp
Normal file
12
Src/C64_SC.cpp
Normal file
@ -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
|
439
Src/C64_WIN32.i
Normal file
439
Src/C64_WIN32.i
Normal file
@ -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 <jrs@world.std.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#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
|
425
Src/C64_x.i
Normal file
425
Src/C64_x.i
Normal file
@ -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 <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
556
Src/CIA.cpp
Normal file
556
Src/CIA.cpp
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
196
Src/CIA.h
Normal file
196
Src/CIA.h
Normal file
@ -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
|
763
Src/CIA_SC.cpp
Normal file
763
Src/CIA_SC.cpp
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
772
Src/CPU1541.cpp
Normal file
772
Src/CPU1541.cpp
Normal file
@ -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;
|
||||||
|
}
|
281
Src/CPU1541.h
Normal file
281
Src/CPU1541.h
Normal file
@ -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
|
12
Src/CPU1541_PC.cpp
Normal file
12
Src/CPU1541_PC.cpp
Normal file
@ -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
|
647
Src/CPU1541_SC.cpp
Normal file
647
Src/CPU1541_SC.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
852
Src/CPUC64.cpp
Normal file
852
Src/CPUC64.cpp
Normal file
@ -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;
|
||||||
|
}
|
217
Src/CPUC64.h
Normal file
217
Src/CPUC64.h
Normal file
@ -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
|
12
Src/CPUC64_PC.cpp
Normal file
12
Src/CPUC64_PC.cpp
Normal file
@ -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
|
653
Src/CPUC64_SC.cpp
Normal file
653
Src/CPUC64_SC.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
83
Src/CPU_common.cpp
Normal file
83
Src/CPU_common.cpp
Normal file
@ -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
|
||||||
|
};
|
76
Src/CPU_common.h
Normal file
76
Src/CPU_common.h
Normal file
@ -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
|
1100
Src/CPU_emulcycle.i
Normal file
1100
Src/CPU_emulcycle.i
Normal file
File diff suppressed because it is too large
Load Diff
1398
Src/CPU_emulline.i
Normal file
1398
Src/CPU_emulline.i
Normal file
File diff suppressed because it is too large
Load Diff
168
Src/CmdPipe.cpp
Normal file
168
Src/CmdPipe.cpp
Normal file
@ -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 <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#if defined(__alpha__)
|
||||||
|
#include <cma.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(AIX)
|
||||||
|
#include <sys/select.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
Src/CmdPipe.h
Normal file
85
Src/CmdPipe.h
Normal file
@ -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 <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
89
Src/Display.cpp
Normal file
89
Src/Display.cpp
Normal file
@ -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
|
222
Src/Display.h
Normal file
222
Src/Display.h
Normal file
@ -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 <InterfaceKit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef AMIGA
|
||||||
|
#include <graphics/rastport.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SDL
|
||||||
|
struct SDL_Surface;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <ddraw.h>
|
||||||
|
#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
|
429
Src/Display_Acorn.i
Normal file
429
Src/Display_Acorn.i
Normal file
@ -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<<col); rev_matrix[col] &= ~(1<<row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of the keys listed below should be used for
|
||||||
|
// joystick definitions since they're always used here.
|
||||||
|
switch(code)
|
||||||
|
{
|
||||||
|
// For either of these: additionally set SHIFT key.
|
||||||
|
case IntKey_CrsrL: // already mapped to CrsrL
|
||||||
|
case IntKey_CrsrU: // already mapped to CrsrD
|
||||||
|
case IntKey_Insert: // already mapped to DEL
|
||||||
|
key_matrix[6] &= (0xff - (1<<4)); rev_matrix[4] &= (0xff - (1<<6));
|
||||||
|
break;
|
||||||
|
case IntKey_F6:
|
||||||
|
if ((status & 2) == 0) // call SAM only in multitasking mode!
|
||||||
|
{
|
||||||
|
TheC64->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);
|
||||||
|
}
|
627
Src/Display_Amiga.i
Normal file
627
Src/Display_Amiga.i
Normal file
@ -0,0 +1,627 @@
|
|||||||
|
/*
|
||||||
|
* Display_Amiga.i - C64 graphics display, emulator window handling,
|
||||||
|
* Amiga specific stuff
|
||||||
|
*
|
||||||
|
* Frodo (C) 1994-1997,2002 Christian Bauer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <exec/types.h>
|
||||||
|
#include <exec/memory.h>
|
||||||
|
#include <intuition/intuition.h>
|
||||||
|
#include <libraries/gadtools.h>
|
||||||
|
#include <libraries/asl.h>
|
||||||
|
#include <proto/exec.h>
|
||||||
|
#include <proto/graphics.h>
|
||||||
|
#include <proto/intuition.h>
|
||||||
|
#include <proto/dos.h>
|
||||||
|
#include <proto/gadtools.h>
|
||||||
|
#include <proto/diskfont.h>
|
||||||
|
#include <proto/asl.h>
|
||||||
|
|
||||||
|
#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<cbauer@iphcip1.physik.uni-mainz.de>\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;
|
||||||
|
}
|
959
Src/Display_Be.i
Normal file
959
Src/Display_Be.i
Normal file
@ -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 <AppKit.h>
|
||||||
|
#include <InterfaceKit.h>
|
||||||
|
#include <GameKit.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
513
Src/Display_SDL.i
Normal file
513
Src/Display_SDL.i
Normal file
@ -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 <SDL.h>
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
2367
Src/Display_WIN32.i
Normal file
2367
Src/Display_WIN32.i
Normal file
File diff suppressed because it is too large
Load Diff
556
Src/Display_svga.i
Normal file
556
Src/Display_svga.i
Normal file
@ -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 <vga.h>
|
||||||
|
#include <vgamouse.h>
|
||||||
|
#include <vgakeyboard.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
793
Src/Display_x.i
Normal file
793
Src/Display_x.i
Normal file
@ -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 <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/keysym.h>
|
||||||
|
#include <X11/cursorfont.h>
|
||||||
|
|
||||||
|
#if defined(X_USE_SHM)
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#include <X11/extensions/XShm.h>
|
||||||
|
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;
|
||||||
|
}
|
415
Src/FixPoint.i
Normal file
415
Src/FixPoint.i
Normal file
@ -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<<FIXPOINT_PREC)) ? (1<<FIXPOINT_PREC) : (1<<((FIXPOINT_BITS - 2 + FIXPOINT_PREC)>>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<<FIXPOINT_PREC); res = 0;
|
||||||
|
while (x > 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<<FIXPOINT_PREC)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stuff re. the sinus table used with fixpoint arithmetic
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// define as global variable
|
||||||
|
FixPoint SinTable[(1<<ldSINTAB)];
|
||||||
|
|
||||||
|
|
||||||
|
#define FIXPOINT_SIN_COS_GENERIC \
|
||||||
|
if (angle >= 3*(1<<ldSINTAB)) {return(-SinTable[(1<<(ldSINTAB+2)) - angle]);}\
|
||||||
|
if (angle >= 2*(1<<ldSINTAB)) {return(-SinTable[angle - 2*(1<<ldSINTAB)]);}\
|
||||||
|
if (angle >= (1<<ldSINTAB)) {return(SinTable[2*(1<<ldSINTAB) - angle]);}\
|
||||||
|
return(SinTable[angle]);
|
||||||
|
|
||||||
|
|
||||||
|
// sin and cos: angle is fixpoint number 0 <= angle <= 2 (*PI)
|
||||||
|
static inline FixPoint fixsin(FixPoint x)
|
||||||
|
{
|
||||||
|
int angle = x;
|
||||||
|
|
||||||
|
angle = (angle >> (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<<ldSINTAB); i++, step+=0.5/(1<<ldSINTAB))
|
||||||
|
{
|
||||||
|
SinTable[i] = FixNo(sin(M_PI * step));
|
||||||
|
}
|
||||||
|
}
|
BIN
Src/Frodo.ico
Normal file
BIN
Src/Frodo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
291
Src/Frodo.rc
Normal file
291
Src/Frodo.rc
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
//Microsoft Developer Studio generated resource script.
|
||||||
|
//
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
|
//
|
||||||
|
#include <windows.h>
|
||||||
|
#include <commctrl.h>
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#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 <windows.h>\r\n"
|
||||||
|
"#include <commctrl.h>\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
|
||||||
|
|
BIN
Src/Frodo.rsrc
Normal file
BIN
Src/Frodo.rsrc
Normal file
Binary file not shown.
12
Src/FrodoHeaders.pch++
Normal file
12
Src/FrodoHeaders.pch++
Normal file
@ -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 <Be.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
12
Src/FrodoPCHeaders.pch++
Normal file
12
Src/FrodoPCHeaders.pch++
Normal file
@ -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 <Be.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
10
Src/FrodoSCHeaders.pch++
Normal file
10
Src/FrodoSCHeaders.pch++
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* FrodoSCHeaders.pch++ (for Metrowerks BeIDE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FRODO_SC
|
||||||
|
|
||||||
|
#include <Be.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
430
Src/IEC.cpp
Normal file
430
Src/IEC.cpp
Normal file
@ -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();
|
||||||
|
}
|
146
Src/IEC.h
Normal file
146
Src/IEC.h
Normal file
@ -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
|
BIN
Src/Invisible.cur
Normal file
BIN
Src/Invisible.cur
Normal file
Binary file not shown.
After Width: | Height: | Size: 326 B |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user