mirror of
https://github.com/digarok/gsplus.git
synced 2026-03-11 00:23:05 +00:00
Merge pull request #2 from digarok/0.0-project-rigging
0.0 project rigging - this is far from an initial version but starts to remove mac nib dependencies.
This commit is contained in:
7
TODO.md
Normal file
7
TODO.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# TODO
|
||||
|
||||
## Backlog
|
||||
|
||||
- [ ] Remove `#ifdef INCLUDE_RCSID_C` / `#endif` guards from header files and `#define`/`#undef INCLUDE_RCSID_C` from `sim65816.c` and `sound.c` in `gsplus/src/`. The `const char rcsid_` lines were already removed; these are the leftover scaffolding.
|
||||
|
||||
- [ ] Update Menubar titles for X/Win to match.. Mac build already updated to "GS+"
|
||||
BIN
gsplus/lib/2mg.icns
Normal file
BIN
gsplus/lib/2mg.icns
Normal file
Binary file not shown.
BIN
gsplus/lib/525.icns
Normal file
BIN
gsplus/lib/525.icns
Normal file
Binary file not shown.
BIN
gsplus/lib/MainMenu.nib
generated
Normal file
BIN
gsplus/lib/MainMenu.nib
generated
Normal file
Binary file not shown.
BIN
gsplus/lib/kegsicon.icns
Normal file
BIN
gsplus/lib/kegsicon.icns
Normal file
Binary file not shown.
BIN
gsplus/lib/kegsicon.png
Normal file
BIN
gsplus/lib/kegsicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
57
gsplus/lib/make_mac_icon
Executable file
57
gsplus/lib/make_mac_icon
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# Based on https://gist.github.com/ansarizafar/6fa64f44aa933794c4d6638eec32b9aa
|
||||
# and https://github.com/retifrav/generate-iconset
|
||||
# We need to create a directory of the icon in several scaled sizes,
|
||||
# (the Mac command "sips" can do this), then run iconutil to form the
|
||||
# .icns file.
|
||||
# kegsicon.png created by Alex Lee
|
||||
|
||||
my $icondir;
|
||||
my $img_file = "";
|
||||
my $ext = ".png";
|
||||
my $scale;
|
||||
my $sz;
|
||||
my $pixels;
|
||||
my $scale_str;
|
||||
|
||||
if($#ARGV == 0) {
|
||||
$img_file = shift;
|
||||
if($img_file =~ /^.*\.(^\.*)$/) {
|
||||
$ext = $1;
|
||||
print "Set ext to $ext\n";
|
||||
}
|
||||
} else {
|
||||
die "Usage: $0 image_file.jpg/.png"
|
||||
}
|
||||
|
||||
$icondir = "./icon.iconset"; # Must have .iconset extension
|
||||
if(-d $icondir) {
|
||||
`rm -rf $icondir`;
|
||||
}
|
||||
|
||||
`mkdir $icondir`;
|
||||
for($scale = 1; $scale <= 2; $scale++) {
|
||||
for($sz = 16; $sz <= 512; $sz = $sz * 2) {
|
||||
if($sz == 64) {
|
||||
next;
|
||||
}
|
||||
$pixels = $sz * $scale;
|
||||
$scale_str = "";
|
||||
if($scale == 2) {
|
||||
$scale_str = '@2x';
|
||||
}
|
||||
@cmd = ("sips", "-z", $pixels, $pixels, $img_file,
|
||||
"--matchTo",
|
||||
"/System/Library/ColorSync/Profiles/sRGB\\ Profile.icc",
|
||||
"--out", $icondir . "/" . "icon_" . $sz . "x" . $sz .
|
||||
$scale_str . $ext);
|
||||
print "cmd: @cmd\n";
|
||||
`@cmd`;
|
||||
}
|
||||
}
|
||||
|
||||
print "Calling: iconutil -o kegs.icns -c icns $icondir";
|
||||
`iconutil -o kegs.icns -c icns $icondir`;
|
||||
`rm -rf $icondir`;
|
||||
|
||||
365
gsplus/src/AppDelegate.swift
Normal file
365
gsplus/src/AppDelegate.swift
Normal file
@@ -0,0 +1,365 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2024 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
let Context_draw = false
|
||||
// Default: use safe draw function.
|
||||
// If true, use NSGraphicsContext.current.data to try to write
|
||||
// directly to screen memory (in a different ARGB format)
|
||||
|
||||
class Window_info {
|
||||
var x_win : NSWindow? = nil
|
||||
var mac_view : MainView? = nil
|
||||
var kimage_ptr : UnsafeMutablePointer<Kimage>! = nil
|
||||
var title : String = ""
|
||||
var app_delegate : AppDelegate! = nil
|
||||
var mac_a2_height : Int = 0
|
||||
|
||||
// init(_ new_is_main: Bool) {
|
||||
// is_main = new_is_main
|
||||
// }
|
||||
|
||||
func set_kimage(_ kimage_ptr : UnsafeMutablePointer<Kimage>!,
|
||||
title: String, delegate: AppDelegate!) {
|
||||
self.kimage_ptr = kimage_ptr
|
||||
self.title = title
|
||||
self.app_delegate = delegate
|
||||
}
|
||||
|
||||
func create_window() {
|
||||
let x_xpos = Int(video_get_x_xpos(kimage_ptr))
|
||||
let x_ypos = Int(video_get_x_ypos(kimage_ptr))
|
||||
let width = Int(video_get_x_width(kimage_ptr))
|
||||
let height = Int(video_get_x_height(kimage_ptr))
|
||||
let windowRect = NSRect(x: x_xpos, y: x_ypos, width: width,
|
||||
height: height)
|
||||
var window : NSWindow!
|
||||
var view : MainView!
|
||||
let style : NSWindow.StyleMask = [.titled, .closable,
|
||||
.resizable]
|
||||
|
||||
window = NSWindow(contentRect: windowRect,
|
||||
styleMask: style,
|
||||
backing: .buffered, defer: false)
|
||||
|
||||
let viewRect = NSRect(x: 0, y: 0, width: windowRect.size.width,
|
||||
height: windowRect.size.height)
|
||||
print("About to init MainView");
|
||||
view = MainView(frame: viewRect)
|
||||
print("About to set kimage_ptr");
|
||||
view.kimage_ptr = kimage_ptr;
|
||||
print("About to call initialize");
|
||||
view.initialize()
|
||||
view.closed = false
|
||||
|
||||
window.delegate = app_delegate
|
||||
window.contentView = view
|
||||
window.makeKeyAndOrderFront(app_delegate)
|
||||
window.acceptsMouseMovedEvents = true
|
||||
window.title = title
|
||||
window.showsToolbarButton = true
|
||||
window.contentAspectRatio = NSSize(width: width, height: height)
|
||||
|
||||
video_set_active(kimage_ptr, Int32(1))
|
||||
video_update_scale(kimage_ptr, Int32(width), Int32(height),
|
||||
Int32(1))
|
||||
|
||||
x_win = window
|
||||
mac_view = view
|
||||
mac_a2_height = height;
|
||||
window.makeKey()
|
||||
}
|
||||
|
||||
func update() {
|
||||
// Decide if window should be opened/closed (if it's the
|
||||
// debugger window), and call mac_update_display() to update
|
||||
let new_height = Int(video_get_a2_height(kimage_ptr))
|
||||
let a2_active = video_get_active(kimage_ptr)
|
||||
if let view = mac_view {
|
||||
if(new_height != mac_a2_height) {
|
||||
mac_resize_window()
|
||||
}
|
||||
if(a2_active == 0 && !view.closed) {
|
||||
print("a2_active 0 on \(title), calling close")
|
||||
x_win!.orderOut(x_win)
|
||||
view.closed = true
|
||||
} else if(a2_active != 0 && view.closed) {
|
||||
print("Opening closed window \(title)")
|
||||
view.closed = false
|
||||
x_win!.orderFront(x_win)
|
||||
x_win!.makeKey() // Move to front
|
||||
} else if(a2_active != 0) {
|
||||
view.mac_update_display()
|
||||
}
|
||||
if((a2_active != 0) && !view.closed) {
|
||||
if(adb_get_copy_requested() != 0) {
|
||||
view.do_copy_text(view);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(a2_active != 0) {
|
||||
print("Opening window \(title)")
|
||||
create_window()
|
||||
}
|
||||
}
|
||||
}
|
||||
func mac_resize_window() {
|
||||
let a2_height = Int(video_get_a2_height(kimage_ptr))
|
||||
let a2_width = Int(video_get_a2_width(kimage_ptr))
|
||||
let ratio = CGFloat(a2_height) / CGFloat(a2_width)
|
||||
|
||||
let cur_width = x_win!.frame.size.width
|
||||
let new_height = cur_width * ratio // CGFloat
|
||||
var newframe = x_win!.frame // NSRect
|
||||
mac_a2_height = a2_height
|
||||
newframe.size.height = new_height
|
||||
x_win!.contentAspectRatio = NSSize(width: a2_width,
|
||||
height: a2_height)
|
||||
x_win!.setFrame(newframe, display: true, animate: true)
|
||||
mac_view!.initialize()
|
||||
// Must call initialize for the case where the
|
||||
// status lines were enabled, we need more lines
|
||||
// print("Call video_update_scale from mac_resize_window\n");
|
||||
video_update_scale(kimage_ptr, Int32(x_win!.frame.width),
|
||||
Int32(x_win!.frame.height), 0)
|
||||
video_update_xpos_ypos(kimage_ptr, Int32(x_win!.frame.origin.x),
|
||||
Int32(x_win!.frame.origin.y))
|
||||
//print("Did mac_resize window to \(a2_width), \(a2_height)" +
|
||||
// " frame:\(x_win!.frame.width), " +
|
||||
// "\(x_win!.frame.height)" +
|
||||
// " ratio:\(ratio)\n")
|
||||
}
|
||||
|
||||
func update_window_size(width: Int, height: Int) {
|
||||
// print("Call video_update_scale from update_window_size\n");
|
||||
video_update_scale(kimage_ptr, Int32(width), Int32(height), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
|
||||
static func main() {
|
||||
let delegate = AppDelegate()
|
||||
NSApplication.shared.delegate = delegate
|
||||
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
||||
}
|
||||
|
||||
var mainwin_info = Window_info();
|
||||
var debugwin_info = Window_info();
|
||||
|
||||
func find_win_info(_ window: NSWindow) -> Window_info {
|
||||
if(mainwin_info.x_win == window) {
|
||||
return mainwin_info
|
||||
}
|
||||
return debugwin_info
|
||||
}
|
||||
@objc func do_about(_:AnyObject) {
|
||||
print("About")
|
||||
if let ver_str = Bundle.main.infoDictionary?[
|
||||
"CFBundleShortVersionString"] as? String {
|
||||
NSApplication.shared.orderFrontStandardAboutPanel(
|
||||
options: [.applicationVersion: ver_str,
|
||||
.version: "", .applicationName: "GS+"])
|
||||
}
|
||||
}
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
// This is your first real entry point into the app
|
||||
print("start!")
|
||||
set_menu_for_kegs()
|
||||
NSApp.activate(ignoringOtherApps: true) // Bring window to front
|
||||
main_init()
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(
|
||||
_ theApplication: NSApplication) -> Bool {
|
||||
// Application will close if main window is closed
|
||||
return true
|
||||
}
|
||||
func windowDidBecomeKey(_ notification: Notification) {
|
||||
if let w = notification.object as? NSWindow {
|
||||
if(w == mainwin_info.x_win) {
|
||||
adb_mainwin_focus(Int32(1));
|
||||
//print("Main window became KEY")
|
||||
}
|
||||
}
|
||||
//print("DidbecomeKey")
|
||||
// If window focus is changing, turn off key repeat
|
||||
adb_kbd_repeat_off()
|
||||
}
|
||||
func windowDidResignKey(_ notification: Notification) {
|
||||
//print("DidResignKey")
|
||||
adb_kbd_repeat_off()
|
||||
adb_mainwin_focus(Int32(0))
|
||||
CGDisplayShowCursor(CGMainDisplayID())
|
||||
}
|
||||
func windowDidMove(_ notification: Notification) {
|
||||
//print("DidMove")
|
||||
if let w = notification.object as? NSWindow {
|
||||
let win_info = find_win_info(w)
|
||||
video_update_xpos_ypos(win_info.kimage_ptr,
|
||||
Int32(w.frame.origin.x),
|
||||
Int32(w.frame.origin.y))
|
||||
}
|
||||
}
|
||||
|
||||
func windowWillResize(_ window: NSWindow, to frameSize: NSSize)
|
||||
-> NSSize {
|
||||
// print("WILL RESIZE app \(frameSize)")
|
||||
let width = Int(frameSize.width)
|
||||
let height = Int(frameSize.height)
|
||||
let win_info = find_win_info(window)
|
||||
win_info.update_window_size(width: width, height: height)
|
||||
return frameSize
|
||||
}
|
||||
func windowShouldClose(_ window: NSWindow) -> Bool {
|
||||
print("windowShouldClose")
|
||||
let win_info = find_win_info(window)
|
||||
if(mainwin_info.x_win == window) {
|
||||
// User has clicked the close box on the main emulator
|
||||
// window. Just exit the app
|
||||
NSApp.terminate(window)
|
||||
return true // Let main window close
|
||||
} else {
|
||||
video_set_active(win_info.kimage_ptr, Int32(0))
|
||||
win_info.update()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func set_menu_for_kegs() {
|
||||
let appname = "Kegs"
|
||||
let menu = NSMenu(title: "MainMenu")
|
||||
NSApp.mainMenu = menu
|
||||
|
||||
print("Installing my menu now")
|
||||
|
||||
// Add "Kegs" menu
|
||||
let kegs = NSMenu(title: appname)
|
||||
kegs.addItem(withTitle: "About \(appname)",
|
||||
action: #selector(AppDelegate.do_about(_:)),
|
||||
keyEquivalent: "")
|
||||
kegs.addItem(NSMenuItem.separator())
|
||||
let quit_item = NSMenuItem(title: "Quit \(appname)",
|
||||
action: #selector(NSApplication.terminate(_:)),
|
||||
keyEquivalent: "q")
|
||||
quit_item.keyEquivalentModifierMask = [.option,
|
||||
.command ]
|
||||
kegs.addItem(quit_item)
|
||||
let kegs_item = NSMenuItem()
|
||||
kegs_item.title = appname
|
||||
kegs_item.submenu = kegs
|
||||
menu.addItem(kegs_item)
|
||||
|
||||
// Add "Edit" menu
|
||||
let edit = NSMenu(title: "Edit")
|
||||
edit.addItem(withTitle: "Copy Text Screen",
|
||||
action: #selector(MainView.do_copy_text(_:)),
|
||||
keyEquivalent: "")
|
||||
edit.addItem(NSMenuItem.separator())
|
||||
edit.addItem(withTitle: "Paste",
|
||||
action: #selector(MainView.do_paste(_:)),
|
||||
keyEquivalent: "")
|
||||
let edit_item = NSMenuItem()
|
||||
edit_item.title = "Edit"
|
||||
edit_item.submenu = edit
|
||||
menu.addItem(edit_item)
|
||||
|
||||
// Add "Config" menu
|
||||
let config = NSMenu(title: "Config")
|
||||
config.addItem(withTitle: "Configuration F4",
|
||||
action: #selector(MainView.do_config(_:)),
|
||||
keyEquivalent: "")
|
||||
let config_item = NSMenuItem()
|
||||
config_item.title = "Config"
|
||||
config_item.submenu = config
|
||||
menu.addItem(config_item)
|
||||
|
||||
show_menu(menu, depth: 0)
|
||||
}
|
||||
|
||||
func show_menu(_ menu: NSMenu, depth: Int) {
|
||||
if(depth >= -10) {
|
||||
return // HACK: remove to see debug output!
|
||||
}
|
||||
print("menu at depth \(depth): \(menu.title)")
|
||||
if(depth > 5) { // Prevent infinite recursion
|
||||
return
|
||||
}
|
||||
for menuit in menu.items {
|
||||
print("menuit: depth:\(depth) \(menuit.title)")
|
||||
print(" keyeq:\(menuit.keyEquivalent)")
|
||||
print(" modifiers:\(menuit.keyEquivalentModifierMask)")
|
||||
print(" isSeparator:\(menuit.isSeparatorItem)")
|
||||
if let sub = menuit.submenu {
|
||||
show_menu(sub, depth: depth+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mainWindow : NSWindow!
|
||||
var mainwin_view : MainView!
|
||||
|
||||
func main_init() {
|
||||
var kimage_ptr : UnsafeMutablePointer<Kimage>!
|
||||
var rect = NSRect.zero
|
||||
|
||||
let argc = CommandLine.argc
|
||||
let argv = CommandLine.unsafeArgv
|
||||
parse_argv(argc, argv, 3);
|
||||
|
||||
rect.size.width = 2560
|
||||
rect.size.height = 1440
|
||||
if let screen = NSScreen.main {
|
||||
rect = screen.frame
|
||||
}
|
||||
kegs_init(24, Int32(rect.size.width), Int32(rect.size.height),
|
||||
Int32(1))
|
||||
if(Context_draw) {
|
||||
video_set_blue_mask(UInt32(0x0000ff))
|
||||
video_set_green_mask(UInt32(0x00ff00))
|
||||
video_set_red_mask(UInt32(0xff0000))
|
||||
} else {
|
||||
video_set_red_mask(UInt32(0x0000ff))
|
||||
video_set_green_mask(UInt32(0x00ff00))
|
||||
video_set_blue_mask(UInt32(0xff0000))
|
||||
}
|
||||
video_set_palette()
|
||||
kimage_ptr = video_get_kimage(Int32(0))
|
||||
mainwin_info.set_kimage(kimage_ptr, title: "GS+",
|
||||
delegate: self)
|
||||
kimage_ptr = video_get_kimage(Int32(1))
|
||||
debugwin_info.set_kimage(kimage_ptr, title: "Debugger",
|
||||
delegate: self)
|
||||
|
||||
mainwin_info.create_window()
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
main_run_loop()
|
||||
}
|
||||
|
||||
func main_run_loop() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() +
|
||||
.milliseconds(1)) {
|
||||
self.main_run_loop()
|
||||
}
|
||||
let ret = run_16ms()
|
||||
if(ret != 0) {
|
||||
exit(ret);
|
||||
}
|
||||
mainwin_info.update()
|
||||
debugwin_info.update()
|
||||
}
|
||||
}
|
||||
|
||||
52
gsplus/src/Info.plist
Normal file
52
gsplus/src/Info.plist
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>18G103</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>KEGSMAC</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>kegs.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.provalid.Kegs</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Kegs</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.38</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>11A1027</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>19A547</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.15</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1110</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>11A1027</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2025 Kent Dickey. All rights reserved.</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
5
gsplus/src/Kegs-Bridging-Header.h
Normal file
5
gsplus/src/Kegs-Bridging-Header.h
Normal file
@@ -0,0 +1,5 @@
|
||||
// $KmKId: Kegs-Bridging-Header.h,v 1.1 2019-10-14 22:33:09+00 kentd Exp $
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import "defc.h"
|
||||
412
gsplus/src/MainView.swift
Normal file
412
gsplus/src/MainView.swift
Normal file
@@ -0,0 +1,412 @@
|
||||
// $KmKId: MainView.swift,v 1.42 2024-09-15 13:55:35+00 kentd Exp $
|
||||
|
||||
// Copyright 2019-2024 by Kent Dickey
|
||||
// This code is covered by the GNU GPL v3
|
||||
// See the file COPYING.txt or https://www.gnu.org/licenses/
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import CoreGraphics
|
||||
import AudioToolbox
|
||||
|
||||
class MainView: NSView {
|
||||
|
||||
var bitmapContext : CGContext!
|
||||
var bitmapData : UnsafeMutableRawPointer!
|
||||
var rawData : UnsafeMutablePointer<UInt32>!
|
||||
var current_flags : UInt = 0
|
||||
var mouse_moved : Bool = false
|
||||
var mac_warp_pointer : Int32 = 0
|
||||
var mac_hide_pointer : Int32 = 0
|
||||
var kimage_ptr : UnsafeMutablePointer<Kimage>!
|
||||
var closed : Bool = false
|
||||
var pixels_per_line = 640
|
||||
var max_height = 600
|
||||
|
||||
let is_cmd = UInt(NSEvent.ModifierFlags.command.rawValue)
|
||||
let is_control = UInt(NSEvent.ModifierFlags.control.rawValue)
|
||||
let is_shift = NSEvent.ModifierFlags.shift.rawValue
|
||||
let is_capslock = NSEvent.ModifierFlags.capsLock.rawValue
|
||||
let is_option = NSEvent.ModifierFlags.option.rawValue
|
||||
|
||||
|
||||
override init(frame frameRect: NSRect) {
|
||||
super.init(frame: frameRect)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
func windowDidResize(_ notification: Notification) {
|
||||
print("DID RESIZE")
|
||||
}
|
||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||
let keycode = event.keyCode
|
||||
let is_repeat = event.isARepeat
|
||||
let unicode_key = get_unicode_key_from_event(event)
|
||||
// print(".performKeyEquiv keycode: \(keycode), is_repeat: " +
|
||||
// "\(is_repeat)")
|
||||
if(((current_flags & is_cmd) == 0) || is_repeat) {
|
||||
// If CMD isn't being held down, just ignore this
|
||||
return false
|
||||
}
|
||||
// Otherwise, manually do down, then up, for this key
|
||||
adb_physical_key_update(kimage_ptr, Int32(keycode),
|
||||
UInt32(unicode_key), 0);
|
||||
adb_physical_key_update(kimage_ptr, Int32(keycode),
|
||||
UInt32(unicode_key), 1);
|
||||
return true
|
||||
}
|
||||
|
||||
override var acceptsFirstResponder: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func get_unicode_key_from_event(_ event: NSEvent) -> UInt32 {
|
||||
var unicode_key = UInt32(0);
|
||||
if let str = event.charactersIgnoringModifiers {
|
||||
//print(" keydown unmod str: \(str), " +
|
||||
// "code:\(event.keyCode)")
|
||||
let arr_chars = Array(str.unicodeScalars)
|
||||
//print(" arr_chars: \(arr_chars)")
|
||||
if(arr_chars.count == 1) {
|
||||
unicode_key = UInt32(arr_chars[0]);
|
||||
//print("key1:\(unicode_key)")
|
||||
}
|
||||
}
|
||||
return unicode_key
|
||||
}
|
||||
override func keyDown(with event: NSEvent) {
|
||||
let keycode = event.keyCode
|
||||
let is_repeat = event.isARepeat;
|
||||
// print(".keyDown code: \(keycode), repeat: \(is_repeat)")
|
||||
//if let str = event.characters {
|
||||
// print(" keydown str: \(str), code:\(keycode)")
|
||||
//}
|
||||
let unicode_key = get_unicode_key_from_event(event)
|
||||
if(is_repeat) {
|
||||
// If we do CMD-E, then we never get a down for the E,
|
||||
// but we will get repeat events for that E. Let's
|
||||
// ignore those
|
||||
return
|
||||
}
|
||||
adb_physical_key_update(kimage_ptr, Int32(keycode),
|
||||
UInt32(unicode_key), 0);
|
||||
}
|
||||
|
||||
override func keyUp(with event: NSEvent) {
|
||||
let keycode = event.keyCode
|
||||
// let is_repeat = event.isARepeat;
|
||||
// print(".keyUp code: \(keycode), repeat: \(is_repeat)")
|
||||
let unicode_key = get_unicode_key_from_event(event)
|
||||
adb_physical_key_update(kimage_ptr, Int32(keycode),
|
||||
UInt32(unicode_key), 1);
|
||||
}
|
||||
|
||||
override func flagsChanged(with event: NSEvent) {
|
||||
let flags = event.modifierFlags.rawValue &
|
||||
(is_cmd | is_control | is_shift | is_capslock |
|
||||
is_option)
|
||||
var c025_val = UInt32(0);
|
||||
if((flags & is_shift) != 0) {
|
||||
c025_val |= 1;
|
||||
}
|
||||
if((flags & is_control) != 0) {
|
||||
c025_val |= 2;
|
||||
}
|
||||
if((flags & is_capslock) != 0) {
|
||||
c025_val |= 4;
|
||||
}
|
||||
if((flags & is_option) != 0) {
|
||||
c025_val |= 0x40;
|
||||
}
|
||||
if((flags & is_cmd) != 0) {
|
||||
c025_val |= 0x80;
|
||||
}
|
||||
adb_update_c025_mask(kimage_ptr, c025_val, UInt32(0xc7));
|
||||
current_flags = flags
|
||||
//print("flagsChanged: \(flags) and keycode: \(event.keyCode)")
|
||||
}
|
||||
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
|
||||
// This is to let the first click when changing to this window
|
||||
// through to the app, I probably don't want this.
|
||||
return false
|
||||
}
|
||||
override func mouseMoved(with event: NSEvent) {
|
||||
//let type = event.eventNumber
|
||||
//print(" event type: \(type)")
|
||||
mac_update_mouse(event: event, buttons_state:0, buttons_valid:0)
|
||||
|
||||
}
|
||||
override func mouseDragged(with event: NSEvent) {
|
||||
mac_update_mouse(event: event, buttons_state: 0,
|
||||
buttons_valid: 0)
|
||||
}
|
||||
override func otherMouseDown(with event: NSEvent) {
|
||||
mac_update_mouse(event: event, buttons_state:2, buttons_valid:2)
|
||||
}
|
||||
override func otherMouseUp(with event: NSEvent) {
|
||||
mac_update_mouse(event: event, buttons_state:0, buttons_valid:2)
|
||||
}
|
||||
|
||||
override func mouseEntered(with event: NSEvent) {
|
||||
print("mouse entered")
|
||||
}
|
||||
override func rightMouseUp(with event: NSEvent) {
|
||||
mac_update_mouse(event: event, buttons_state:0, buttons_valid:4)
|
||||
}
|
||||
override func rightMouseDown(with event: NSEvent) {
|
||||
print("Right mouse down")
|
||||
mac_update_mouse(event: event, buttons_state:4, buttons_valid:4)
|
||||
}
|
||||
override func mouseUp(with event: NSEvent) {
|
||||
// print("Mouse up \(event.locationInWindow.x)," +
|
||||
// "\(event.locationInWindow.y)")
|
||||
mac_update_mouse(event: event, buttons_state:0, buttons_valid:1)
|
||||
}
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
//print("Mouse down \(event.locationInWindow.x)," +
|
||||
// "\(event.locationInWindow.y)")
|
||||
mac_update_mouse(event: event, buttons_state:1, buttons_valid:1)
|
||||
}
|
||||
|
||||
func mac_update_mouse(event: NSEvent, buttons_state: Int,
|
||||
buttons_valid: Int) {
|
||||
var warp = Int32(0)
|
||||
var x_width = 0
|
||||
var y_height = 0
|
||||
var x = Int32(0)
|
||||
var y = Int32(0)
|
||||
var do_delta = Int(0)
|
||||
let hide = adb_get_hide_warp_info(kimage_ptr, &warp)
|
||||
if(warp != mac_warp_pointer) {
|
||||
mouse_moved = true
|
||||
}
|
||||
mac_warp_pointer = warp
|
||||
if(mac_hide_pointer != hide) {
|
||||
mac_hide_pointer(hide: hide)
|
||||
}
|
||||
mac_hide_pointer = hide
|
||||
let location = event.locationInWindow
|
||||
if(!Context_draw) {
|
||||
// We're letting the Mac scale the window for us,
|
||||
// so video_scale_mouse*() doesn't know the scale
|
||||
// factor, so pass it in
|
||||
x_width = Int(bounds.size.width);
|
||||
y_height = Int(bounds.size.height);
|
||||
}
|
||||
let raw_x = location.x
|
||||
let raw_y = bounds.size.height - 1 - location.y
|
||||
// raw_y is 0 at the top of the window now
|
||||
x = video_scale_mouse_x(kimage_ptr, Int32(raw_x),
|
||||
Int32(x_width))
|
||||
y = video_scale_mouse_y(kimage_ptr, Int32(raw_y),
|
||||
Int32(y_height))
|
||||
do_delta = 0;
|
||||
if(mac_warp_pointer != 0) {
|
||||
do_delta |= 0x1000; // x,y are deltas
|
||||
x = Int32(event.deltaX)
|
||||
y = Int32(event.deltaY)
|
||||
}
|
||||
let ret = adb_update_mouse(kimage_ptr, x, y,
|
||||
Int32(buttons_state),
|
||||
Int32(buttons_valid | do_delta))
|
||||
if(ret != 0) {
|
||||
mouse_moved = true
|
||||
}
|
||||
guard let win = window else {
|
||||
return // No valid window
|
||||
}
|
||||
if(mouse_moved) {
|
||||
var rect1 = NSRect.zero
|
||||
|
||||
// If warp, warp cursor to middle of window. Moving
|
||||
// the cursor requires absolute screen coordinates,
|
||||
// where y=0 is the top of the screen. We must convert
|
||||
// window coords (where y=0 is the bottom of the win).
|
||||
mouse_moved = false
|
||||
let warp_x = CGFloat(video_unscale_mouse_x(kimage_ptr,
|
||||
Int32(A2_WINDOW_WIDTH/2), Int32(x_width)))
|
||||
let warp_y = CGFloat(video_unscale_mouse_y(kimage_ptr,
|
||||
Int32(A2_WINDOW_HEIGHT/2), Int32(y_height)))
|
||||
let scr_height = CGDisplayPixelsHigh(CGMainDisplayID());
|
||||
rect1.origin.x = CGFloat(warp_x)
|
||||
rect1.origin.y = bounds.size.height - 1 -
|
||||
CGFloat(warp_y)
|
||||
rect1.size.width = 1;
|
||||
rect1.size.height = 0;
|
||||
let screen_rect = win.convertToScreen(rect1)
|
||||
let screen_rect_y = CGFloat(scr_height) -
|
||||
screen_rect.origin.y
|
||||
let cg_loc = CGPoint(x: screen_rect.origin.x,
|
||||
y: CGFloat(screen_rect_y))
|
||||
//print("scr_rect:\(screen_rect)")
|
||||
if(warp != 0) {
|
||||
// Warp to middle of the window
|
||||
CGDisplayMoveCursorToPoint(CGMainDisplayID(),
|
||||
cg_loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mac_hide_pointer(hide: Int32) {
|
||||
// print("Hide called: \(hide)")
|
||||
if(hide != 0) {
|
||||
CGDisplayHideCursor(CGMainDisplayID())
|
||||
} else {
|
||||
CGDisplayShowCursor(CGMainDisplayID())
|
||||
}
|
||||
}
|
||||
func initialize() {
|
||||
//let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)
|
||||
print("Initialize view called")
|
||||
// Get width,height from video.c to handle toggling status lines
|
||||
let width = Int(video_get_a2_width(kimage_ptr))
|
||||
let height = Int(video_get_a2_height(kimage_ptr))
|
||||
//if let screen = NSScreen.main {
|
||||
// let rect = screen.frame
|
||||
// width = Int(rect.size.width)
|
||||
// height = Int(rect.size.height)
|
||||
//}
|
||||
pixels_per_line = width
|
||||
max_height = height
|
||||
//print("pixels_per_line: \(pixels_per_line), " +
|
||||
// "max_height: \(max_height)")
|
||||
|
||||
let color_space = CGDisplayCopyColorSpace(CGMainDisplayID())
|
||||
//let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
bitmapContext = CGContext(data: nil, width: width,
|
||||
height: height, bitsPerComponent: 8,
|
||||
bytesPerRow: width * 4,
|
||||
space: color_space,
|
||||
bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)
|
||||
|
||||
//CGImageAlphaInfo.noneSkipLast.rawValue
|
||||
bitmapData = bitmapContext.data!
|
||||
// Set the intial value of the data to black (0)
|
||||
bitmapData.initializeMemory(as: UInt32.self, repeating: 0,
|
||||
count: height * width)
|
||||
rawData = bitmapData.bindMemory(to: UInt32.self,
|
||||
capacity: height * width)
|
||||
//print("Calling video_update_scale from MainViewinitialize()")
|
||||
let x_width = Int(video_get_x_width(kimage_ptr))
|
||||
let x_height = Int(video_get_x_height(kimage_ptr))
|
||||
video_update_scale(kimage_ptr, Int32(x_width), Int32(x_height),
|
||||
Int32(1))
|
||||
if(Context_draw) {
|
||||
video_set_alpha_mask(UInt32(0xff000000))
|
||||
// Set video.c alpha mask, since 0 means transparent
|
||||
}
|
||||
}
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
var rect : Change_rect
|
||||
//super.draw(dirtyRect)
|
||||
// Documentation says super.draw not needed...
|
||||
// Draw the current image buffer to the screen
|
||||
let context = NSGraphicsContext.current!.cgContext
|
||||
if(!Context_draw) {
|
||||
// Safe, simple drawing
|
||||
let image = bitmapContext.makeImage()!
|
||||
context.draw(image, in: bounds)
|
||||
// The above call does the actual copy of
|
||||
// data to the screen, and can take a while
|
||||
//print("Draw, bounds:\(bounds), dirtyr:\(dirtyRect)")
|
||||
} else {
|
||||
// Unsafe, more direct drawing by peeking into
|
||||
// NSGraphicsContext.current.data
|
||||
if let data = context.data {
|
||||
rect = Change_rect(x:0, y:0,
|
||||
width:Int32(context.width),
|
||||
height:Int32(context.height));
|
||||
video_update_scale(kimage_ptr,
|
||||
Int32(context.width),
|
||||
Int32(context.height), Int32(0))
|
||||
video_out_data_scaled(data, kimage_ptr,
|
||||
Int32(context.bytesPerRow/4), &rect);
|
||||
video_out_done(kimage_ptr);
|
||||
print("Did out_data_scaled, rect:\(rect)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func do_config(_ : AnyObject) {
|
||||
// Create a "virtual" F4 press
|
||||
//print("do_config")
|
||||
// Create a keydown for the F4 key (keycode:0x76)
|
||||
adb_physical_key_update(kimage_ptr, Int32(0x76), 0, 0);
|
||||
|
||||
// and create a keyup for the F4 key (keycode:0x76)
|
||||
adb_physical_key_update(kimage_ptr, Int32(0x76), 0, 1);
|
||||
}
|
||||
|
||||
@objc func do_copy_text(_ : AnyObject) {
|
||||
// print("do_copy");
|
||||
//let text_buf_cstr = UnsafeMutablePointer<Int8>.allocate(
|
||||
// capacity: 2100);
|
||||
if let cstr = cfg_text_screen_str() {
|
||||
let str = String(cString: cstr);
|
||||
NSPasteboard.general.clearContents();
|
||||
NSPasteboard.general.setString(str,
|
||||
forType: NSPasteboard.PasteboardType.string);
|
||||
}
|
||||
}
|
||||
@objc func do_paste(_ : AnyObject) {
|
||||
// print("do_paste")
|
||||
let general = NSPasteboard.general;
|
||||
guard let str = general.string(forType: .string) else {
|
||||
print("Cannot paste, nothing in clipboard");
|
||||
return
|
||||
}
|
||||
//print("str: \(str)")
|
||||
for raw_c in str.utf8 {
|
||||
let c = UInt32(raw_c)
|
||||
let ret = adb_paste_add_buf(c)
|
||||
if(ret != 0) {
|
||||
print("Paste too large!")
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mac_update_display() {
|
||||
var valid : Int32
|
||||
var rect : Change_rect
|
||||
var dirty_rect = NSRect.zero
|
||||
|
||||
if(Context_draw) {
|
||||
// We just need to know if there are any changes,
|
||||
// don't actually do the copies now
|
||||
valid = video_out_query(kimage_ptr);
|
||||
if(valid != 0) {
|
||||
self.setNeedsDisplay(bounds)
|
||||
print("Needs display");
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Otherwise, update rawData in our Bitmap now
|
||||
rect = Change_rect(x:0, y:0, width:0, height:0)
|
||||
for i in 0..<MAX_CHANGE_RECTS { // MAX_CHANGE_RECTS
|
||||
valid = video_out_data(rawData, kimage_ptr,
|
||||
Int32(pixels_per_line), &rect, Int32(i))
|
||||
if(valid == 0) {
|
||||
break
|
||||
}
|
||||
dirty_rect.origin.x = CGFloat(rect.x)
|
||||
dirty_rect.origin.y = bounds.size.height -
|
||||
CGFloat(rect.y) - CGFloat(rect.height)
|
||||
dirty_rect.size.width = CGFloat(rect.width)
|
||||
dirty_rect.size.height = CGFloat(rect.height)
|
||||
|
||||
self.setNeedsDisplay(bounds)
|
||||
// It's necessary to redraw the whole screen,
|
||||
// there's no mechanism to just redraw part
|
||||
// The coordinates would need transformation
|
||||
// (since Mac 0,0 is the lower left corner)
|
||||
//print("bounds: w:\(bounds.size.width) " +
|
||||
// "h:\(bounds.size.height)\n")
|
||||
//self.setNeedsDisplay(dirty_rect)
|
||||
}
|
||||
}
|
||||
}
|
||||
112
gsplus/src/Makefile
Normal file
112
gsplus/src/Makefile
Normal file
@@ -0,0 +1,112 @@
|
||||
# $KmKId: makefile,v 1.48 2025-04-28 15:12:19+00 kentd Exp $
|
||||
|
||||
XOPTS_WIN = -Wall -fomit-frame-pointer -march=pentium
|
||||
|
||||
SWIFTOBJS = AppDelegate.o MainView.o
|
||||
|
||||
include vars
|
||||
include ldvars
|
||||
|
||||
.SUFFIXES: .dep .proto
|
||||
|
||||
AS = $(CC)
|
||||
|
||||
XLIBS = -L/usr/X11/lib
|
||||
PERL = perl
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
specials:
|
||||
|
||||
specials_clean:
|
||||
|
||||
clean:
|
||||
rm -f *.o gsplus
|
||||
|
||||
|
||||
# Mac builds:
|
||||
gsplus: $(OBJECTS) $(OBJECTS1) compile_time.o $(SWIFTOBJS)
|
||||
clang $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) $(SWIFTOBJS) \
|
||||
compile_time.o $(LDFLAGS) -o gsplus $(EXTRA_LIBS) \
|
||||
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
|
||||
-Xlinker -rpath -Xlinker @executable_path/../Frameworks \
|
||||
-Xlinker -rpath -Xlinker /usr/lib/swift \
|
||||
-Xlinker -no_deduplicate -fobjc-link-runtime \
|
||||
-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
|
||||
-L/usr/lib/swift
|
||||
mkdir -p ../GSplus.app/Contents/MacOS
|
||||
mkdir -p ../GSplus.app/Contents/Frameworks
|
||||
$(PERL) cp_gsplus_libs gsplus /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx ../GSplus.app/Contents/Frameworks
|
||||
mv gsplus ../GSplus.app/Contents/MacOS/GSplus
|
||||
echo "APPL????" > ../GSplus.app/Contents/PkgInfo
|
||||
cp -f Info.plist ../GSplus.app/Contents/
|
||||
$(PROJROOT)/lib/make_mac_icon $(PROJROOT)/lib/kegsicon.png
|
||||
cp -f kegs.icns ../GSplus.app/Contents/Resources/
|
||||
touch '../GSplus.app/Icon?'
|
||||
#cp -f $(PROJROOT)/lib/2mg.icns ../GSplus.app/Contents/Resources/
|
||||
#cp -f $(PROJROOT)/lib/525.icns ../GSplus.app/Contents/Resources/
|
||||
|
||||
|
||||
# Linux for X builds:
|
||||
gsplus-x: $(OBJECTS) $(OBJECTS1) compile_time.o
|
||||
$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \
|
||||
$(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \
|
||||
-lX11 -lXext
|
||||
mv gsplus ..
|
||||
|
||||
# Cygwin for X builds:
|
||||
gsplus.exe: $(OBJECTS) $(OBJECTS1) compile_time.o
|
||||
$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \
|
||||
$(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \
|
||||
-lXext -lX11 -lm
|
||||
mv gsplus.exe ..
|
||||
|
||||
# Mingw32 (native windows) builds: (broken, doesn't work currently)
|
||||
gspluswin.exe: $(OBJECTS) $(OBJECTS1) compile_time.o
|
||||
$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \
|
||||
$(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) \
|
||||
-lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32
|
||||
mv $(NAME)$(SUFFIX) ..
|
||||
|
||||
|
||||
.s.o:
|
||||
$(AS) -c $(OPTS) -I. $*.s
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c
|
||||
|
||||
.m.o:
|
||||
$(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.m
|
||||
|
||||
AppDelegate.o: AppDelegate.swift
|
||||
sh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \
|
||||
MainView.swift -o $*.o
|
||||
|
||||
MainView.o: MainView.swift
|
||||
sh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \
|
||||
AppDelegate.swift -o $*.o
|
||||
|
||||
win32.o: win32.rc
|
||||
windres -o win32.o win32.rc
|
||||
|
||||
.c.proto:
|
||||
$(KMKROOT)/bin/kmkproto $(XOPTS) $*.c > $*.proto
|
||||
echo >> $*.proto
|
||||
|
||||
.m.proto:
|
||||
$(KMKROOT)/bin/kmkproto $(XOPTS) $*.m > $*.proto
|
||||
echo >> $*.proto
|
||||
|
||||
$(PROTO_OUT): $(PROTO_FILE_LIST) ldvars proto_vars
|
||||
$(KMKROOT)/bin/kmkproto_head $(PROTO_OUT) tmp_protos.h
|
||||
cat /dev/null $(PROTO_FILE_LIST) >> tmp_protos.h
|
||||
rm -f $(PROTO_OUT)
|
||||
mv tmp_protos.h $(PROTO_OUT)
|
||||
kmk_cp_if_diff $(PROTO_OUT) ..
|
||||
rm -f *.proto
|
||||
|
||||
|
||||
compile_time.o: $(OBJECTS) $(OBJECTS1)
|
||||
|
||||
include dependency
|
||||
|
||||
2264
gsplus/src/adb.c
Normal file
2264
gsplus/src/adb.c
Normal file
File diff suppressed because it is too large
Load Diff
362
gsplus/src/applesingle.c
Normal file
362
gsplus/src/applesingle.c
Normal file
@@ -0,0 +1,362 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2021 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// From Wikipedia AppleSingle_and_AppleDouble_formats):
|
||||
// https://web.archive.org/web/20180311140826/http://kaiser-edv.de/
|
||||
// documents/AppleSingle_AppleDouble.pdf
|
||||
// All fields in an Applesingle file are in big-endian format
|
||||
// ProDOS forked files are described in Technote tn-pdos-025.
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
word32
|
||||
applesingle_get_be32(const byte *bptr)
|
||||
{
|
||||
return (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3];
|
||||
}
|
||||
|
||||
word32
|
||||
applesingle_get_be16(const byte *bptr)
|
||||
{
|
||||
return (bptr[0] << 8) | bptr[1];
|
||||
}
|
||||
|
||||
void
|
||||
applesingle_set_be32(byte *bptr, word32 val)
|
||||
{
|
||||
bptr[3] = val;
|
||||
bptr[2] = val >> 8;
|
||||
bptr[1] = val >> 16;
|
||||
bptr[0] = val >> 24;
|
||||
}
|
||||
|
||||
void
|
||||
applesingle_set_be16(byte *bptr, word32 val)
|
||||
{
|
||||
bptr[1] = val;
|
||||
bptr[0] = val >> 8;
|
||||
}
|
||||
|
||||
word32
|
||||
applesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data)
|
||||
{
|
||||
byte *fptr, *bptr;
|
||||
word32 data_size, resource_size, rounded_data_size, num_entries;
|
||||
word32 rounded_resource_size, hdr_size, max_size, hdr_pos, data_pos;
|
||||
word32 block, key_block, ret, good, has_finder_info, offset;
|
||||
int level;
|
||||
int i, j;
|
||||
|
||||
#if 0
|
||||
printf("applesingle_map_from_prodos: %s do_file_data:%d\n",
|
||||
fileptr->unix_path, do_file_data);
|
||||
#endif
|
||||
|
||||
// First, handle mini directory describing the forks
|
||||
key_block = fileptr->key_block;
|
||||
ret = dynapro_map_one_file_block(dsk, fileptr, key_block, 1U << 30, 0);
|
||||
if(ret == 0) {
|
||||
printf(" dynapro_map_one_file_block ret 0, applesingle done\n");
|
||||
return 0;
|
||||
}
|
||||
bptr = &(dsk->raw_data[key_block << 9]);
|
||||
data_size = dynapro_get_word24(&bptr[5]);
|
||||
resource_size = dynapro_get_word24(&bptr[0x100 + 5]);
|
||||
has_finder_info = bptr[9] | bptr[27];
|
||||
|
||||
num_entries = 1; // ProDOS info always
|
||||
if(has_finder_info) {
|
||||
num_entries++;
|
||||
}
|
||||
rounded_data_size = data_size;
|
||||
if(data_size) {
|
||||
rounded_data_size = (data_size + 0x200) & -0x200;
|
||||
num_entries++;
|
||||
}
|
||||
rounded_resource_size = resource_size;
|
||||
if(resource_size) {
|
||||
rounded_resource_size = (resource_size + 0x200) & -0x200;
|
||||
num_entries++;
|
||||
}
|
||||
hdr_size = 256;
|
||||
max_size = hdr_size + rounded_resource_size + rounded_data_size;
|
||||
fileptr->buffer_ptr = 0;
|
||||
fptr = 0;
|
||||
if(do_file_data) {
|
||||
fptr = calloc(1, max_size + 0x200);
|
||||
#if 0
|
||||
printf(" fptr:%p, max_size:%08x, res:%08x, data:%08x\n",
|
||||
fptr, max_size, rounded_resource_size,
|
||||
rounded_data_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
// From now on, errors cannot return without free'ing fptr
|
||||
good = 1;
|
||||
if(resource_size) {
|
||||
block = dynapro_get_word16(&bptr[0x100 + 1]);
|
||||
level = bptr[0x100];
|
||||
if(fptr) {
|
||||
fileptr->buffer_ptr = fptr + 256;
|
||||
}
|
||||
ret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0,
|
||||
resource_size);
|
||||
if(ret == 0) {
|
||||
good = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(data_size) {
|
||||
block = dynapro_get_word16(&bptr[1]);
|
||||
level = bptr[0];
|
||||
if(fptr) {
|
||||
fileptr->buffer_ptr = fptr + 256 +
|
||||
rounded_resource_size;
|
||||
}
|
||||
ret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0,
|
||||
data_size);
|
||||
if(ret == 0) {
|
||||
good = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fileptr->buffer_ptr = 0;
|
||||
|
||||
// Now prepare the header
|
||||
if(fptr) {
|
||||
applesingle_set_be32(&fptr[0], 0x00051600); // Magic
|
||||
applesingle_set_be32(&fptr[4], 0x00020000); // Version
|
||||
applesingle_set_be16(&fptr[24], num_entries); // Version
|
||||
hdr_pos = 26;
|
||||
data_pos = 192;
|
||||
|
||||
// First do ProDOS entry
|
||||
applesingle_set_be32(&fptr[hdr_pos + 0], 11); // ProDOS Info
|
||||
applesingle_set_be32(&fptr[hdr_pos + 4], data_pos);
|
||||
applesingle_set_be32(&fptr[hdr_pos + 8], 8);
|
||||
|
||||
applesingle_set_be16(&fptr[data_pos + 0], 0xc3);
|
||||
applesingle_set_be16(&fptr[data_pos + 2], fileptr->file_type);
|
||||
applesingle_set_be32(&fptr[data_pos + 4], fileptr->aux_type);
|
||||
hdr_pos += 12;
|
||||
data_pos += 8;
|
||||
|
||||
// Then do FinderInfo
|
||||
if(has_finder_info) {
|
||||
applesingle_set_be32(&fptr[hdr_pos + 0], 9); //Finder
|
||||
applesingle_set_be32(&fptr[hdr_pos + 4], data_pos);
|
||||
applesingle_set_be32(&fptr[hdr_pos + 8], 32);
|
||||
|
||||
for(i = 0; i < 2; i++) {
|
||||
offset = bptr[9 + 18*i];
|
||||
if(!offset) {
|
||||
continue; // skip it
|
||||
}
|
||||
offset = ((offset - 1) & 1) * 8;
|
||||
for(j = 0; j < 9; j++) {
|
||||
fptr[data_pos + offset + j] =
|
||||
bptr[10 + 18*i + j];
|
||||
}
|
||||
}
|
||||
hdr_pos += 12;
|
||||
data_pos += 32;
|
||||
}
|
||||
|
||||
if(data_pos >= 256) {
|
||||
printf("data_pos:%08x is too big\n", data_pos);
|
||||
good = 0;
|
||||
}
|
||||
|
||||
// First, do data fork
|
||||
if(data_size) {
|
||||
applesingle_set_be32(&fptr[hdr_pos + 0], 1); // Data
|
||||
applesingle_set_be32(&fptr[hdr_pos + 4],
|
||||
256 + rounded_resource_size);
|
||||
applesingle_set_be32(&fptr[hdr_pos + 8], data_size);
|
||||
hdr_pos += 12;
|
||||
}
|
||||
// Then do resource fork
|
||||
if(resource_size) {
|
||||
applesingle_set_be32(&fptr[hdr_pos + 0], 2); // Rsrc
|
||||
applesingle_set_be32(&fptr[hdr_pos + 4], 256);
|
||||
applesingle_set_be32(&fptr[hdr_pos + 8], resource_size);
|
||||
hdr_pos += 12;
|
||||
}
|
||||
if(hdr_pos > 192) {
|
||||
printf("hdr:%08x stomped on data\n", hdr_pos);
|
||||
good = 0;
|
||||
}
|
||||
if(good) {
|
||||
ret = dynapro_write_to_unix_file(fileptr->unix_path,
|
||||
fptr, 256 + rounded_resource_size + data_size);
|
||||
if(ret == 0) {
|
||||
good = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(fptr);
|
||||
}
|
||||
|
||||
// printf("applesingle_map_from_prodos done, good:%d\n", good);
|
||||
return good;
|
||||
}
|
||||
|
||||
word32
|
||||
applesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr,
|
||||
dword64 dsize)
|
||||
{
|
||||
byte *bptr, *tptr;
|
||||
word32 key_block, blocks_used, entry_id, blocks_out, offset, length;
|
||||
word32 magic, version, hdr_pos, did_fork;
|
||||
int num_entries;
|
||||
int i;
|
||||
|
||||
// Return 0 if anything is wrong with the .applesingle file
|
||||
// Otherwise, return (blocks_used << 16) | (key_block & 0xffff)
|
||||
|
||||
#if 0
|
||||
printf("applesingle_from_unix %s, size:%08llx\n", fileptr->unix_path,
|
||||
dsize);
|
||||
#endif
|
||||
|
||||
key_block = fileptr->key_block;
|
||||
bptr = &(dsk->raw_data[key_block << 9]);
|
||||
|
||||
if(dsize < 50) {
|
||||
printf("Applesingle is too small\n");
|
||||
return 0;
|
||||
}
|
||||
magic = applesingle_get_be32(&fptr[0]);
|
||||
version = applesingle_get_be32(&fptr[4]);
|
||||
num_entries = applesingle_get_be16(&fptr[24]);
|
||||
if((magic != 0x00051600) || (version < 0x00020000)) {
|
||||
printf("Bad Applesingle magic number is: %08x, vers:%08x\n",
|
||||
magic, version);
|
||||
return 0;
|
||||
}
|
||||
hdr_pos = 26;
|
||||
blocks_used = 1;
|
||||
did_fork = 0;
|
||||
// printf(" num_entries:%d\n", num_entries);
|
||||
for(i = 0; i < num_entries; i++) {
|
||||
if((hdr_pos + 24) > dsize) {
|
||||
printf("Applesingle header exceeds file size i:%d of "
|
||||
"%d, hdr_pos:%04x dsize:%08llx\n", i,
|
||||
num_entries, hdr_pos, dsize);
|
||||
return 0;
|
||||
}
|
||||
entry_id = applesingle_get_be32(&fptr[hdr_pos + 0]);
|
||||
offset = applesingle_get_be32(&fptr[hdr_pos + 4]);
|
||||
length = applesingle_get_be32(&fptr[hdr_pos + 8]);
|
||||
#if 0
|
||||
printf(" header[%d] at +%04x: id:%d, offset:%08x, len:%08x\n",
|
||||
i, hdr_pos, entry_id, offset, length);
|
||||
#endif
|
||||
if((offset + length) > dsize) {
|
||||
printf("Applesingle entry_id:%d exceeds file size\n",
|
||||
entry_id);
|
||||
return 0;
|
||||
}
|
||||
switch(entry_id) {
|
||||
case 1: // Data fork
|
||||
case 2: // Resource fork
|
||||
tptr = bptr;
|
||||
if(entry_id == 2) { // Resource fork
|
||||
tptr += 0x100;
|
||||
}
|
||||
#if 0
|
||||
printf(" for entry_id %d, offset:%08x, length:%08x, "
|
||||
"fptr:%p\n", entry_id, offset, length, fptr);
|
||||
#endif
|
||||
if(did_fork & (1 << entry_id)) {
|
||||
printf("fork %d repeated!\n", entry_id);
|
||||
return 0;
|
||||
}
|
||||
did_fork |= (1 << entry_id);
|
||||
blocks_out = applesingle_make_prodos_fork(dsk,
|
||||
fptr + offset, tptr, length);
|
||||
if(blocks_out == 0) {
|
||||
return 0;
|
||||
}
|
||||
blocks_used += (blocks_out >> 16);
|
||||
break;
|
||||
case 9: // Finder Info
|
||||
if(length < 32) {
|
||||
printf("Invalid Finder info, len:%d\n", length);
|
||||
}
|
||||
bptr[8] = 0x12;
|
||||
bptr[8 + 18] = 0x12;
|
||||
bptr[9] = 1;
|
||||
bptr[9 + 18] = 2;
|
||||
for(i = 0; i < 16; i++) {
|
||||
bptr[10 + i] = fptr[offset + i];
|
||||
bptr[10 + 18 + i] = fptr[offset + 16 + i];
|
||||
}
|
||||
break;
|
||||
case 11: // ProDOS File Info
|
||||
fileptr->file_type = fptr[offset + 3];
|
||||
fileptr->aux_type = (fptr[offset + 6] << 8) |
|
||||
fptr[offset + 7];
|
||||
break;
|
||||
default:
|
||||
break; // Ignore it
|
||||
}
|
||||
hdr_pos += 12;
|
||||
}
|
||||
|
||||
for(i = 1; i < 3; i++) {
|
||||
if((did_fork >> i) & 1) {
|
||||
continue;
|
||||
}
|
||||
// Create one block for this fork even though it's length==0
|
||||
// i==1: no data fork; i==2: no resource fork
|
||||
printf(" Doing dummy fork, i:%d, fptr:%p\n", i, fptr);
|
||||
blocks_out = applesingle_make_prodos_fork(dsk, fptr,
|
||||
bptr + ((i & 2) * 0x80), 0);
|
||||
if(blocks_out == 0) {
|
||||
return blocks_out;
|
||||
}
|
||||
blocks_used += (blocks_out >> 16);
|
||||
}
|
||||
|
||||
fileptr->eof = 0x200;
|
||||
return (blocks_used << 16) | key_block;
|
||||
}
|
||||
|
||||
word32
|
||||
applesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length)
|
||||
{
|
||||
word32 block, blocks_out, storage_type;
|
||||
|
||||
#if 0
|
||||
printf("applesingle_make_prodos_fork: fptr:%p, tptr:%p, length:%08x\n",
|
||||
fptr, tptr, length);
|
||||
#endif
|
||||
|
||||
// Handle creating either a resource or data fork
|
||||
block = dynapro_find_free_block(dsk);
|
||||
if(block == 0) {
|
||||
return 0;
|
||||
}
|
||||
blocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type, block,
|
||||
length);
|
||||
|
||||
// printf(" dynapro_fork_from_unix ret: %08x, storage:%02x\n",
|
||||
// blocks_out, storage_type);
|
||||
tptr[0] = storage_type >> 4;
|
||||
tptr[1] = blocks_out & 0xff; // key_block lo
|
||||
tptr[2] = (blocks_out >> 8) & 0xff; // key_block hi
|
||||
tptr[3] = (blocks_out >> 16) & 0xff; // blocks_used lo
|
||||
tptr[4] = (blocks_out >> 24) & 0xff; // blocks_used hi
|
||||
tptr[5] = length & 0xff; // eof lo
|
||||
tptr[6] = (length >> 8) & 0xff; // eof mid
|
||||
tptr[7] = (length >> 16) & 0xff; // eof hi
|
||||
return blocks_out;
|
||||
}
|
||||
396
gsplus/src/clock.c
Normal file
396
gsplus/src/clock.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2022 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <mmsystem.h>
|
||||
#else
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
extern int Verbose;
|
||||
extern word32 g_vbl_count;
|
||||
extern int g_rom_version;
|
||||
extern int g_config_kegs_update_needed;
|
||||
|
||||
#define CLK_IDLE 1
|
||||
#define CLK_TIME 2
|
||||
#define CLK_INTERNAL 3
|
||||
#define CLK_BRAM1 4
|
||||
#define CLK_BRAM2 5
|
||||
|
||||
int g_clk_mode = CLK_IDLE;
|
||||
int g_clk_read = 0;
|
||||
int g_clk_reg1 = 0;
|
||||
|
||||
extern word32 g_c033_data;
|
||||
extern word32 g_c034_val;
|
||||
|
||||
byte g_bram[2][256];
|
||||
byte *g_bram_ptr = &(g_bram[0][0]);
|
||||
|
||||
word32 g_clk_cur_time = 0xa0000000;
|
||||
int g_clk_next_vbl_update = 0;
|
||||
|
||||
double
|
||||
get_dtime()
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
FILETIME filetime;
|
||||
dword64 dlow, dhigh;
|
||||
#else
|
||||
struct timeval tp1;
|
||||
double dsec;
|
||||
double dusec;
|
||||
#endif
|
||||
double dtime;
|
||||
|
||||
/* Routine used to return actual system time as a double */
|
||||
/* No routine cares about the absolute value, only deltas--maybe */
|
||||
/* take advantage of that in future to increase usec accuracy */
|
||||
|
||||
#ifdef _WIN32
|
||||
//dtime = timeGetTime() / 1000.0;
|
||||
GetSystemTimePreciseAsFileTime(&filetime);
|
||||
dlow = filetime.dwLowDateTime;
|
||||
dhigh = filetime.dwHighDateTime;
|
||||
dlow = (dhigh << 32) | dlow;
|
||||
dtime = (double)dlow;
|
||||
dtime = dtime / (1000*1000*10.0); // FILETIME is in 100ns incs
|
||||
#else
|
||||
|
||||
# ifdef SOLARIS
|
||||
gettimeofday(&tp1, (void *)0);
|
||||
# else
|
||||
gettimeofday(&tp1, (struct timezone *)0);
|
||||
# endif
|
||||
|
||||
dsec = (double)tp1.tv_sec;
|
||||
dusec = (double)tp1.tv_usec;
|
||||
|
||||
dtime = dsec + (dusec / (1000.0 * 1000.0));
|
||||
#endif
|
||||
|
||||
return dtime;
|
||||
}
|
||||
|
||||
int
|
||||
micro_sleep(double dtime)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct timeval Timer;
|
||||
int ret;
|
||||
#endif
|
||||
|
||||
if(dtime <= 0.0) {
|
||||
return 0;
|
||||
}
|
||||
if(dtime >= 1.0) {
|
||||
halt_printf("micro_sleep called with %f!!\n", dtime);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("usleep: %f\n", dtime);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
Sleep((word32)(dtime * 1000));
|
||||
#else
|
||||
Timer.tv_sec = 0;
|
||||
Timer.tv_usec = (dtime * 1000000.0);
|
||||
if( (ret = select(0, 0, 0, 0, &Timer)) < 0) {
|
||||
fprintf(stderr, "micro_sleep (select) ret: %d, errno: %d\n",
|
||||
ret, errno);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
clk_bram_zero()
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* zero out all bram */
|
||||
for(i = 0; i < 2; i++) {
|
||||
for(j = 0; j < 256; j++) {
|
||||
g_bram[i][j] = 0;
|
||||
}
|
||||
}
|
||||
g_bram_ptr = &(g_bram[0][0]);
|
||||
}
|
||||
|
||||
void
|
||||
clk_bram_set(int bram_num, int offset, int val)
|
||||
{
|
||||
if((bram_num < 0) || (bram_num > 2)) {
|
||||
printf("bram_num %d out of range\n", bram_num);
|
||||
return;
|
||||
}
|
||||
if((offset < 0) || (offset > 0x100)) {
|
||||
printf("bram offset %05x out of range\n", offset);
|
||||
return;
|
||||
}
|
||||
g_bram[bram_num][offset] = val;
|
||||
}
|
||||
|
||||
void
|
||||
clk_setup_bram_version()
|
||||
{
|
||||
if(g_rom_version < 3) {
|
||||
g_bram_ptr = (&g_bram[0][0]); // ROM 01
|
||||
} else {
|
||||
g_bram_ptr = (&g_bram[1][0]); // ROM 03
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
clk_write_bram(FILE *fconf)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
for(i = 0; i < 2; i++) {
|
||||
fprintf(fconf, "\n");
|
||||
for(j = 0; j < 256; j += 16) {
|
||||
fprintf(fconf, "bram%d[%02x] =", 2*i + 1, j);
|
||||
for(k = 0; k < 16; k++) {
|
||||
fprintf(fconf, " %02x", g_bram[i][j+k]);
|
||||
}
|
||||
fprintf(fconf, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
update_cur_time()
|
||||
{
|
||||
struct tm *tm_ptr;
|
||||
time_t cur_time, secs, secs2;
|
||||
|
||||
cur_time = time(0);
|
||||
|
||||
/* Figure out the timezone (effectively) by diffing two times. */
|
||||
/* this is probably not right for a few hours around daylight savings*/
|
||||
/* time transition */
|
||||
secs2 = mktime(gmtime(&cur_time));
|
||||
tm_ptr = localtime(&cur_time);
|
||||
secs = mktime(tm_ptr);
|
||||
|
||||
secs2 = secs2 - secs; // this is the timezone offset
|
||||
#ifdef MAC
|
||||
/* Mac OS X's mktime function modifies the tm_ptr passed in for */
|
||||
/* the CDT timezone and breaks this algorithm. So on a Mac, we */
|
||||
/* will use the tm_ptr->gmtoff member to correct the time */
|
||||
secs = secs + tm_ptr->tm_gmtoff;
|
||||
#else
|
||||
secs = cur_time - secs2;
|
||||
|
||||
if(tm_ptr->tm_isdst) {
|
||||
/* adjust for daylight savings time */
|
||||
secs += 3600;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* add in secs to make date based on Apple Jan 1, 1904 instead of */
|
||||
/* Unix's Jan 1, 1970 */
|
||||
/* So add in 66 years and 17 leap year days (1904 is a leap year) */
|
||||
secs += ((66*365) + 17) * (24*3600);
|
||||
|
||||
g_clk_cur_time = (word32)secs;
|
||||
|
||||
clk_printf("Update g_clk_cur_time to %08x\n", g_clk_cur_time);
|
||||
g_clk_next_vbl_update = g_vbl_count + 5;
|
||||
}
|
||||
|
||||
/* clock_update called by sim65816 every VBL */
|
||||
void
|
||||
clock_update()
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
void
|
||||
clock_update_if_needed()
|
||||
{
|
||||
int diff;
|
||||
|
||||
diff = g_clk_next_vbl_update - g_vbl_count;
|
||||
if(diff < 0 || diff > 60) {
|
||||
/* Been a while, re-read the clock */
|
||||
update_cur_time();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
clock_write_c034(word32 val)
|
||||
{
|
||||
g_c034_val = val & 0x7f;
|
||||
if((val & 0x80) != 0) {
|
||||
if((val & 0x20) == 0) {
|
||||
printf("c034 write not last = 1\n");
|
||||
/* set_halt(1); */
|
||||
}
|
||||
do_clock_data();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
do_clock_data()
|
||||
{
|
||||
word32 mask, read, op;
|
||||
|
||||
clk_printf("In do_clock_data, g_clk_mode: %02x\n", g_clk_mode);
|
||||
|
||||
read = g_c034_val & 0x40;
|
||||
switch(g_clk_mode) {
|
||||
case CLK_IDLE:
|
||||
g_clk_read = (g_c033_data >> 7) & 1;
|
||||
g_clk_reg1 = (g_c033_data >> 2) & 3;
|
||||
op = (g_c033_data >> 4) & 7;
|
||||
if(!read) {
|
||||
/* write */
|
||||
switch(op) {
|
||||
case 0x0: /* Read/write seconds register */
|
||||
g_clk_mode = CLK_TIME;
|
||||
clock_update_if_needed();
|
||||
break;
|
||||
case 0x3: /* internal registers */
|
||||
g_clk_mode = CLK_INTERNAL;
|
||||
if(g_clk_reg1 & 0x2) {
|
||||
/* extend BRAM read */
|
||||
g_clk_mode = CLK_BRAM2;
|
||||
g_clk_reg1 = (g_c033_data & 7) << 5;
|
||||
}
|
||||
break;
|
||||
case 0x2: /* read/write ram 0x10-0x13 */
|
||||
g_clk_mode = CLK_BRAM1;
|
||||
g_clk_reg1 += 0x10;
|
||||
break;
|
||||
case 0x4: /* read/write ram 0x00-0x0f */
|
||||
case 0x5: case 0x6: case 0x7:
|
||||
g_clk_mode = CLK_BRAM1;
|
||||
g_clk_reg1 = (g_c033_data >> 2) & 0xf;
|
||||
break;
|
||||
default:
|
||||
halt_printf("Bad c033_data in CLK_IDLE: %02x\n",
|
||||
g_c033_data);
|
||||
}
|
||||
} else {
|
||||
printf("clk read from IDLE mode!\n");
|
||||
/* set_halt(1); */
|
||||
g_clk_mode = CLK_IDLE;
|
||||
}
|
||||
break;
|
||||
case CLK_BRAM2:
|
||||
if(!read) {
|
||||
/* get more bits of bram addr */
|
||||
if((g_c033_data & 0x83) == 0x00) {
|
||||
/* more address bits */
|
||||
g_clk_reg1 |= ((g_c033_data >> 2) & 0x1f);
|
||||
g_clk_mode = CLK_BRAM1;
|
||||
} else {
|
||||
halt_printf("CLK_BRAM2: c033_data: %02x!\n",
|
||||
g_c033_data);
|
||||
g_clk_mode = CLK_IDLE;
|
||||
}
|
||||
} else {
|
||||
halt_printf("CLK_BRAM2: clock read!\n");
|
||||
g_clk_mode = CLK_IDLE;
|
||||
}
|
||||
break;
|
||||
case CLK_BRAM1:
|
||||
/* access battery ram addr g_clk_reg1 */
|
||||
if(read) {
|
||||
if(g_clk_read) {
|
||||
/* Yup, read */
|
||||
g_c033_data = g_bram_ptr[g_clk_reg1];
|
||||
clk_printf("Reading BRAM loc %02x: %02x\n",
|
||||
g_clk_reg1, g_c033_data);
|
||||
} else {
|
||||
halt_printf("CLK_BRAM1: said wr, now read\n");
|
||||
}
|
||||
} else {
|
||||
if(g_clk_read) {
|
||||
halt_printf("CLK_BRAM1: said rd, now write\n");
|
||||
} else {
|
||||
/* Yup, write */
|
||||
clk_printf("Writing BRAM loc %02x with %02x\n",
|
||||
g_clk_reg1, g_c033_data);
|
||||
g_bram_ptr[g_clk_reg1] = g_c033_data;
|
||||
g_config_kegs_update_needed = 1;
|
||||
}
|
||||
}
|
||||
g_clk_mode = CLK_IDLE;
|
||||
break;
|
||||
case CLK_TIME:
|
||||
if(read) {
|
||||
if(g_clk_read == 0) {
|
||||
halt_printf("Reading time, but in set mode!\n");
|
||||
}
|
||||
g_c033_data = (g_clk_cur_time >> (g_clk_reg1 * 8)) &
|
||||
0xff;
|
||||
clk_printf("Returning time byte %d: %02x\n",
|
||||
g_clk_reg1, g_c033_data);
|
||||
} else {
|
||||
/* Write */
|
||||
if(g_clk_read) {
|
||||
halt_printf("Write time, but in read mode!\n");
|
||||
}
|
||||
clk_printf("Writing TIME loc %d with %02x\n",
|
||||
g_clk_reg1, g_c033_data);
|
||||
mask = 0xff << (8 * g_clk_reg1);
|
||||
|
||||
g_clk_cur_time = (g_clk_cur_time & (~mask)) |
|
||||
((g_c033_data & 0xff) << (8 * g_clk_reg1));
|
||||
}
|
||||
g_clk_mode = CLK_IDLE;
|
||||
break;
|
||||
case CLK_INTERNAL:
|
||||
if(read) {
|
||||
printf("Attempting to read internal reg %02x!\n",
|
||||
g_clk_reg1);
|
||||
} else {
|
||||
switch(g_clk_reg1) {
|
||||
case 0x0: /* test register */
|
||||
if(g_c033_data & 0xc0) {
|
||||
printf("Writing test reg: %02x!\n",
|
||||
g_c033_data);
|
||||
/* set_halt(1); */
|
||||
}
|
||||
break;
|
||||
case 0x1: /* write protect reg */
|
||||
clk_printf("Writing clk wr_protect: %02x\n",
|
||||
g_c033_data);
|
||||
if(g_c033_data & 0x80) {
|
||||
printf("Stop, wr clk wr_prot: %02x\n",
|
||||
g_c033_data);
|
||||
/* set_halt(1); */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
halt_printf("Writing int reg: %02x with %02x\n",
|
||||
g_clk_reg1, g_c033_data);
|
||||
}
|
||||
}
|
||||
g_clk_mode = CLK_IDLE;
|
||||
break;
|
||||
default:
|
||||
halt_printf("clk mode: %d unknown!\n", g_clk_mode);
|
||||
g_clk_mode = CLK_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
13
gsplus/src/comp_swift
Normal file
13
gsplus/src/comp_swift
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# $KmKId: comp_swift,v 1.2 2020-12-11 22:58:32+00 kentd Exp $
|
||||
|
||||
echo "args are: " "$@"
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c \
|
||||
-enable-objc-interop \
|
||||
-sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
|
||||
-swift-version 4 -Onone \
|
||||
-serialize-debugging-options \
|
||||
-import-objc-header Kegs-Bridging-Header.h \
|
||||
-module-name Kegs \
|
||||
"$@"
|
||||
|
||||
2
gsplus/src/compile_time.c
Normal file
2
gsplus/src/compile_time.c
Normal file
@@ -0,0 +1,2 @@
|
||||
char g_compile_time[] = "Compiled: " __DATE__ " " __TIME__ ;
|
||||
|
||||
4613
gsplus/src/config.c
Normal file
4613
gsplus/src/config.c
Normal file
File diff suppressed because it is too large
Load Diff
36
gsplus/src/config.h
Normal file
36
gsplus/src/config.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2019 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#define CONF_BUF_LEN 1024
|
||||
#define COPY_BUF_SIZE 4096
|
||||
#define CFG_PRINTF_BUFSIZE 2048
|
||||
|
||||
#define CFG_PATH_MAX 1024
|
||||
|
||||
#define CFG_NUM_SHOWENTS 16
|
||||
|
||||
#define CFGTYPE_MENU 1
|
||||
#define CFGTYPE_INT 2
|
||||
#define CFGTYPE_DISK 3
|
||||
#define CFGTYPE_FUNC 4
|
||||
#define CFGTYPE_FILE 5
|
||||
#define CFGTYPE_STR 6
|
||||
/* CFGTYPE limited to just 4 bits: 0-15 */
|
||||
|
||||
/* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */
|
||||
|
||||
STRUCT(Cfg_defval) {
|
||||
Cfg_menu *menuptr;
|
||||
int intval;
|
||||
char *strval;
|
||||
};
|
||||
39
gsplus/src/cp_gsplus_libs
Normal file
39
gsplus/src/cp_gsplus_libs
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/perl -w
|
||||
# $KmKId: cp_kegs_libs,v 1.2 2021-02-09 00:35:48+00 kentd Exp $
|
||||
|
||||
use strict;
|
||||
use English;
|
||||
|
||||
if($#ARGV < 2) {
|
||||
die "Usage: executable srclib_dir destlib_dir";
|
||||
}
|
||||
|
||||
# Runs objdump on the executable, finds all unresolved dependencies, and
|
||||
# then copies each of those libraries from srclib_dir to destlib_dir
|
||||
# destlib_dir should be APPLICATION/Contents/Frameworks/
|
||||
|
||||
my $exe = shift;
|
||||
my $srcdir = shift;
|
||||
my $destdir = shift;
|
||||
if(! -f $exe || ! -d $srcdir || ! -d $destdir) {
|
||||
die "$exe is not a file, or $srcdir or $destdir are not a dir";
|
||||
}
|
||||
|
||||
my $do_swiftos = 0;
|
||||
open(RPATHS, "objdump -macho -dylibs-used $exe|") or die "Open failed: $!";
|
||||
my $line;
|
||||
my $lib;
|
||||
foreach $line (<RPATHS>) {
|
||||
chomp($line);
|
||||
if($line =~ m:\@rpath/([^ ]*) :) {
|
||||
$lib = $1;
|
||||
print "lib: $lib\n";
|
||||
`cp $srcdir/$lib $destdir/`;
|
||||
}
|
||||
$do_swiftos = 1;
|
||||
}
|
||||
|
||||
# And copy libswiftos.dylib if we copied any files
|
||||
if($do_swiftos) {
|
||||
`cp $srcdir/libswiftos.dylib $destdir/`;
|
||||
}
|
||||
2172
gsplus/src/debugger.c
Normal file
2172
gsplus/src/debugger.c
Normal file
File diff suppressed because it is too large
Load Diff
364
gsplus/src/defc.h
Normal file
364
gsplus/src/defc.h
Normal file
@@ -0,0 +1,364 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2024 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defcomm.h"
|
||||
|
||||
#define STRUCT(a) typedef struct a ## _st a; struct a ## _st
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short word16;
|
||||
typedef unsigned int word32;
|
||||
#if _MSC_VER
|
||||
typedef unsigned __int64 dword64;
|
||||
#else
|
||||
typedef unsigned long long dword64;
|
||||
#endif
|
||||
|
||||
/* 28MHz crystal, plus every 65th 1MHz cycle is stretched 140ns */
|
||||
#define CYCS_28_MHZ (28636360)
|
||||
#define DCYCS_28_MHZ (1.0*CYCS_28_MHZ)
|
||||
#define CYCS_3_5_MHZ (CYCS_28_MHZ/8)
|
||||
#define DCYCS_1_MHZ ((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0)))
|
||||
|
||||
// DCYCS_1_MHZ is 1020484.32016
|
||||
|
||||
#define CYCLES_IN_16MS_RAW (262 * 65)
|
||||
/* Use precisely 17030 instead of forcing 60 Hz since this is the number of */
|
||||
/* 1MHz cycles per screen */
|
||||
|
||||
#define DCYCS_IN_16MS ((double)(CYCLES_IN_16MS_RAW))
|
||||
#define DRECIP_DCYCS_IN_16MS (1.0 / (DCYCS_IN_16MS))
|
||||
#define VBL_RATE (DCYCS_1_MHZ / DCYCS_IN_16MS)
|
||||
// VBL rate is about 59.9227 frames/sec
|
||||
|
||||
#define MAXNUM_HEX_PER_LINE 32
|
||||
#define MAX_SCALE_SIZE 5100
|
||||
|
||||
#ifdef __NeXT__
|
||||
# include <libc.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#ifdef HPUX
|
||||
# include <machine/inline.h> /* for GET_ITIMER */
|
||||
#endif
|
||||
|
||||
#ifdef SOLARIS
|
||||
# include <sys/filio.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
# pragma warning(disable : 4996) /* open() is deprecated...sigh */
|
||||
int ftruncate(int fd, word32 length);
|
||||
int lstat(const char *path, struct stat *bufptr);
|
||||
#endif
|
||||
|
||||
#ifndef O_BINARY
|
||||
/* work around some Windows junk */
|
||||
# define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#define MAX_CHANGE_RECTS 20
|
||||
|
||||
#ifdef __GNUC__
|
||||
int dbg_printf(const char *fmt, ...) __attribute__ ((
|
||||
__format__(printf, 1, 2)));
|
||||
#endif
|
||||
|
||||
STRUCT(Pc_log) {
|
||||
dword64 dfcyc;
|
||||
word32 dbank_kpc;
|
||||
word32 instr;
|
||||
word32 psr_acc;
|
||||
word32 xreg_yreg;
|
||||
word32 stack_direct;
|
||||
word32 pad;
|
||||
};
|
||||
|
||||
STRUCT(Data_log) {
|
||||
dword64 dfcyc;
|
||||
byte *stat;
|
||||
word32 addr;
|
||||
word32 val;
|
||||
word32 size;
|
||||
};
|
||||
|
||||
STRUCT(Event) {
|
||||
dword64 dfcyc;
|
||||
int type;
|
||||
Event *next;
|
||||
};
|
||||
|
||||
STRUCT(Fplus) {
|
||||
dword64 dplus_1;
|
||||
dword64 dplus_x_minus_1;
|
||||
};
|
||||
|
||||
STRUCT(Engine_reg) {
|
||||
dword64 dfcyc;
|
||||
word32 kpc;
|
||||
word32 acc;
|
||||
|
||||
word32 xreg;
|
||||
word32 yreg;
|
||||
|
||||
word32 stack;
|
||||
word32 dbank;
|
||||
|
||||
word32 direct;
|
||||
word32 psr;
|
||||
Fplus *fplus_ptr;
|
||||
};
|
||||
|
||||
STRUCT(Break_point) {
|
||||
word32 start_addr;
|
||||
word32 end_addr;
|
||||
word32 acc_type;
|
||||
};
|
||||
|
||||
STRUCT(Change_rect) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
|
||||
STRUCT(Kimage) {
|
||||
word32 *wptr;
|
||||
int a2_width_full;
|
||||
int a2_height_full;
|
||||
int a2_width;
|
||||
int a2_height;
|
||||
int x_width;
|
||||
int x_height;
|
||||
int x_refresh_needed;
|
||||
int x_max_width;
|
||||
int x_max_height;
|
||||
int x_xpos;
|
||||
int x_ypos;
|
||||
int active;
|
||||
word32 vbl_of_last_resize;
|
||||
word32 c025_val;
|
||||
word32 scale_width_to_a2;
|
||||
word32 scale_width_a2_to_x;
|
||||
word32 scale_height_to_a2;
|
||||
word32 scale_height_a2_to_x;
|
||||
int num_change_rects;
|
||||
Change_rect change_rect[MAX_CHANGE_RECTS];
|
||||
word32 scale_width[MAX_SCALE_SIZE + 1];
|
||||
word32 scale_height[MAX_SCALE_SIZE + 1];
|
||||
};
|
||||
|
||||
typedef byte *Pg_info;
|
||||
STRUCT(Page_info) {
|
||||
Pg_info rd_wr;
|
||||
};
|
||||
|
||||
STRUCT(Cfg_menu) {
|
||||
const char *str;
|
||||
void *ptr;
|
||||
const char *name_str;
|
||||
void *defptr;
|
||||
int cfgtype;
|
||||
};
|
||||
|
||||
STRUCT(Cfg_dirent) {
|
||||
char *name;
|
||||
int is_dir;
|
||||
int part_num;
|
||||
dword64 dsize;
|
||||
dword64 dimage_start;
|
||||
dword64 compr_dsize;
|
||||
};
|
||||
|
||||
STRUCT(Cfg_listhdr) {
|
||||
Cfg_dirent *direntptr;
|
||||
int max;
|
||||
int last;
|
||||
int invalid;
|
||||
|
||||
int curent;
|
||||
int topent;
|
||||
|
||||
int num_to_show;
|
||||
};
|
||||
|
||||
typedef void (Dbg_fn)(const char *str);
|
||||
|
||||
STRUCT(Dbg_longcmd) {
|
||||
const char *str;
|
||||
Dbg_fn *fnptr;
|
||||
Dbg_longcmd *subptr;
|
||||
const char *help_str;
|
||||
};
|
||||
|
||||
STRUCT(Emustate_intlist) {
|
||||
const char *str;
|
||||
word32 *iptr;
|
||||
};
|
||||
|
||||
STRUCT(Emustate_dword64list) {
|
||||
const char *str;
|
||||
dword64 *dptr;
|
||||
};
|
||||
|
||||
STRUCT(Emustate_word32list) {
|
||||
const char *str;
|
||||
word32 *wptr;
|
||||
};
|
||||
|
||||
STRUCT(Lzw_state) {
|
||||
word32 table[4096 + 2];
|
||||
word32 entry;
|
||||
int bits;
|
||||
};
|
||||
|
||||
#ifdef __LP64__
|
||||
# define PTR2WORD(a) ((word32)(unsigned long long)(a))
|
||||
#else
|
||||
# define PTR2WORD(a) ((word32)(unsigned long long)(a))
|
||||
#endif
|
||||
|
||||
|
||||
#define ALTZP (g_c068_statereg & 0x80)
|
||||
/* #define PAGE2 (g_c068_statereg & 0x40) */
|
||||
#define RAMRD (g_c068_statereg & 0x20)
|
||||
#define RAMWRT (g_c068_statereg & 0x10)
|
||||
#define RDROM (g_c068_statereg & 0x08)
|
||||
#define LCBANK2 (g_c068_statereg & 0x04)
|
||||
#define ROMB (g_c068_statereg & 0x02)
|
||||
// #define INTCX (g_c068_statereg & 0x01)
|
||||
|
||||
#define C041_EN_25SEC_INTS 0x10
|
||||
#define C041_EN_VBL_INTS 0x08
|
||||
#define C041_EN_SWITCH_INTS 0x04
|
||||
#define C041_EN_MOVE_INTS 0x02
|
||||
#define C041_EN_MOUSE 0x01
|
||||
|
||||
/* WARNING: SCC1 and SCC0 interrupts must be in this order for scc.c */
|
||||
/* This order matches the SCC hardware, and SCC1_ZEROCNT must be 0x0001 */
|
||||
#define IRQ_PENDING_SCC1_ZEROCNT 0x00001
|
||||
#define IRQ_PENDING_SCC1_TX 0x00002
|
||||
#define IRQ_PENDING_SCC1_RX 0x00004
|
||||
#define IRQ_PENDING_SCC0_ZEROCNT 0x00008
|
||||
#define IRQ_PENDING_SCC0_TX 0x00010
|
||||
#define IRQ_PENDING_SCC0_RX 0x00020
|
||||
#define IRQ_PENDING_C023_SCAN 0x00100
|
||||
#define IRQ_PENDING_C023_1SEC 0x00200
|
||||
#define IRQ_PENDING_C046_25SEC 0x00400
|
||||
#define IRQ_PENDING_C046_VBL 0x00800
|
||||
#define IRQ_PENDING_ADB_KBD_SRQ 0x01000
|
||||
#define IRQ_PENDING_ADB_DATA 0x02000
|
||||
#define IRQ_PENDING_ADB_MOUSE 0x04000
|
||||
#define IRQ_PENDING_DOC 0x08000
|
||||
#define IRQ_PENDING_MOCKINGBOARDA 0x10000
|
||||
#define IRQ_PENDING_MOCKINGBOARDB 0x20000 /* must be BOARDA*2 */
|
||||
|
||||
|
||||
#define EXTRU(val, pos, len) \
|
||||
( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) : \
|
||||
(((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) )
|
||||
|
||||
#define DEP1(val, pos, old_val) \
|
||||
(((old_val) & ~(1 << (31 - (pos))) ) | \
|
||||
( ((val) & 1) << (31 - (pos))) )
|
||||
|
||||
#define set_halt(val) \
|
||||
if(val) { set_halt_act(val); }
|
||||
|
||||
#define clear_halt() \
|
||||
clr_halt_act()
|
||||
|
||||
#define GET_PAGE_INFO_RD(page) \
|
||||
(page_info_rd_wr[page].rd_wr)
|
||||
|
||||
#define GET_PAGE_INFO_WR(page) \
|
||||
(page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr)
|
||||
|
||||
#define SET_PAGE_INFO_RD(page,val) \
|
||||
;page_info_rd_wr[page].rd_wr = (Pg_info)val;
|
||||
|
||||
#define SET_PAGE_INFO_WR(page,val) \
|
||||
;page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \
|
||||
(Pg_info)val;
|
||||
|
||||
#define VERBOSE_DISK 0x001
|
||||
#define VERBOSE_IRQ 0x002
|
||||
#define VERBOSE_CLK 0x004
|
||||
#define VERBOSE_SHADOW 0x008
|
||||
#define VERBOSE_IWM 0x010
|
||||
#define VERBOSE_DOC 0x020
|
||||
#define VERBOSE_ADB 0x040
|
||||
#define VERBOSE_SCC 0x080
|
||||
#define VERBOSE_TEST 0x100
|
||||
#define VERBOSE_VIDEO 0x200
|
||||
#define VERBOSE_MAC 0x400
|
||||
#define VERBOSE_DYNA 0x800
|
||||
|
||||
#ifdef NO_VERB
|
||||
# define DO_VERBOSE 0
|
||||
#else
|
||||
# define DO_VERBOSE 1
|
||||
#endif
|
||||
|
||||
#define disk_printf if(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf
|
||||
#define irq_printf if(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf
|
||||
#define clk_printf if(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf
|
||||
#define shadow_printf if(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf
|
||||
#define iwm_printf if(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf
|
||||
#define doc_printf if(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf
|
||||
#define adb_printf if(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf
|
||||
#define scc_printf if(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf
|
||||
#define test_printf if(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf
|
||||
#define vid_printf if(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf
|
||||
#define mac_printf if(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf
|
||||
#define dyna_printf if(DO_VERBOSE && (Verbose & VERBOSE_DYNA)) printf
|
||||
|
||||
|
||||
#define HALT_ON_SCAN_INT 0x001
|
||||
#define HALT_ON_IRQ 0x002
|
||||
#define HALT_ON_SHADOW_REG 0x004
|
||||
#define HALT_ON_C70D_WRITES 0x008
|
||||
|
||||
#define HALT_ON(a, msg) \
|
||||
if(Halt_on & a) { \
|
||||
halt_printf(msg); \
|
||||
}
|
||||
|
||||
|
||||
#define MY_MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#define MY_MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#define GET_ITIMER(dest) dest = get_itimer();
|
||||
|
||||
#define FINISH(arg1, arg2) g_ret1 = arg1 | ((arg2) << 8); g_dcycles_end=0;
|
||||
|
||||
#include "iwm.h"
|
||||
#include "protos.h"
|
||||
129
gsplus/src/defcomm.h
Normal file
129
gsplus/src/defcomm.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
const char rcsdif_defcomm_h[] = "@(#)$KmKId: defcomm.h,v 1.109 2023-11-12 15:29:41+00 kentd Exp $";
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#define SHIFT_PER_CHANGE 3
|
||||
#define CHANGE_SHIFT (5 + SHIFT_PER_CHANGE)
|
||||
|
||||
#define SLOW_MEM_CH_SIZE (0x20000 >> CHANGE_SHIFT)
|
||||
|
||||
#define MAXNUM_HEX_PER_LINE 32
|
||||
|
||||
/* Different Joystick defines */
|
||||
#define JOYSTICK_MOUSE 1
|
||||
#define JOYSTICK_LINUX 2
|
||||
#define JOYSTICK_KEYPAD 3
|
||||
#define JOYSTICK_WIN32_1 4
|
||||
#define JOYSTICK_WIN32_2 5
|
||||
|
||||
#define MAX_BREAK_POINTS 0x20
|
||||
|
||||
#define MAX_BP_INDEX 0x100
|
||||
#define MAX_BP_PER_INDEX 3 /* 4 word32s total = 16 bytes */
|
||||
#define SIZE_BREAKPT_ENTRY_BITS 4 /* 16 bytes = 4 bits */
|
||||
|
||||
/* Warning--next defines used by asm! */
|
||||
#define PAGE_INFO_PAD_SIZE 0x800
|
||||
#define PAGE_INFO_WR_OFFSET 0x10000+PAGE_INFO_PAD_SIZE
|
||||
|
||||
#define BANK_IO_BIT 31
|
||||
#define BANK_SHADOW_BIT 30
|
||||
#define BANK_SHADOW2_BIT 29
|
||||
#define BANK_IO2_BIT 28
|
||||
#define BANK_BREAK_BIT 27
|
||||
#define BANK_BREAK (1 << (31 - BANK_BREAK_BIT))
|
||||
#define BANK_IO2_TMP (1 << (31 - BANK_IO2_BIT))
|
||||
#define BANK_IO_TMP (1 << (31 - BANK_IO_BIT))
|
||||
#define BANK_SHADOW (1 << (31 - BANK_SHADOW_BIT))
|
||||
#define BANK_SHADOW2 (1 << (31 - BANK_SHADOW2_BIT))
|
||||
#define SET_BANK_IO \
|
||||
(&g_dummy_memory1_ptr[BANK_IO_TMP | BANK_IO2_TMP])
|
||||
|
||||
#define BANK_BAD_MEM (&g_dummy_memory1_ptr[0xff])
|
||||
|
||||
|
||||
#define RET_BREAK 0x1
|
||||
#define RET_COP 0x2
|
||||
#define RET_WDM 0x3
|
||||
#define RET_WAI 0x4
|
||||
#define RET_STP 0x5
|
||||
#define RET_PSR 0x6
|
||||
#define RET_IRQ 0x7
|
||||
#define RET_TOOLTRACE 0x8
|
||||
|
||||
|
||||
#define BIT_ALL_STAT_TEXT 0
|
||||
#define BIT_ALL_STAT_VID80 1
|
||||
#define BIT_ALL_STAT_ST80 2
|
||||
#define BIT_ALL_STAT_COLOR_C021 3
|
||||
#define BIT_ALL_STAT_MIX_T_GR 4
|
||||
#define BIT_ALL_STAT_DIS_COLOR_DHIRES 5 /* special val, c029 */
|
||||
#define BIT_ALL_STAT_PAGE2 6 /* special val, statereg */
|
||||
#define BIT_ALL_STAT_SUPER_HIRES 7 /* special, c029 */
|
||||
#define BIT_ALL_STAT_HIRES 8
|
||||
#define BIT_ALL_STAT_ANNUNC3 9
|
||||
#define BIT_ALL_STAT_ALTCHARSET 10
|
||||
#define BIT_ALL_STAT_FLASH_STATE 11
|
||||
#define BIT_ALL_STAT_BG_COLOR 12 /* 4 bits */
|
||||
#define BIT_ALL_STAT_TEXT_COLOR 16 /* 4 bits */
|
||||
/* Text must be just above */
|
||||
/* bg to match c022 reg */
|
||||
#define BIT_ALL_STAT_VOC_INTERLACE 20
|
||||
#define BIT_ALL_STAT_VOC_MAIN 21
|
||||
#define BIT_ALL_STAT_BORDER 22
|
||||
|
||||
#define ALL_STAT_SUPER_HIRES (1 << (BIT_ALL_STAT_SUPER_HIRES))
|
||||
#define ALL_STAT_TEXT (1 << (BIT_ALL_STAT_TEXT))
|
||||
#define ALL_STAT_VID80 (1 << (BIT_ALL_STAT_VID80))
|
||||
#define ALL_STAT_PAGE2 (1 << (BIT_ALL_STAT_PAGE2))
|
||||
#define ALL_STAT_ST80 (1 << (BIT_ALL_STAT_ST80))
|
||||
#define ALL_STAT_COLOR_C021 (1 << (BIT_ALL_STAT_COLOR_C021))
|
||||
#define ALL_STAT_DIS_COLOR_DHIRES (1 << (BIT_ALL_STAT_DIS_COLOR_DHIRES))
|
||||
#define ALL_STAT_MIX_T_GR (1 << (BIT_ALL_STAT_MIX_T_GR))
|
||||
#define ALL_STAT_HIRES (1 << (BIT_ALL_STAT_HIRES))
|
||||
#define ALL_STAT_ANNUNC3 (1 << (BIT_ALL_STAT_ANNUNC3))
|
||||
#define ALL_STAT_TEXT_COLOR (0xf << (BIT_ALL_STAT_TEXT_COLOR))
|
||||
#define ALL_STAT_BG_COLOR (0xf << (BIT_ALL_STAT_BG_COLOR))
|
||||
#define ALL_STAT_ALTCHARSET (1 << (BIT_ALL_STAT_ALTCHARSET))
|
||||
#define ALL_STAT_FLASH_STATE (1 << (BIT_ALL_STAT_FLASH_STATE))
|
||||
#define ALL_STAT_VOC_INTERLACE (1 << (BIT_ALL_STAT_VOC_INTERLACE))
|
||||
#define ALL_STAT_VOC_MAIN (1 << (BIT_ALL_STAT_VOC_MAIN))
|
||||
#define ALL_STAT_BORDER (1 << (BIT_ALL_STAT_BORDER))
|
||||
|
||||
#define BORDER_WIDTH 32
|
||||
|
||||
#define EFF_BORDER_WIDTH (BORDER_WIDTH + (640-560))
|
||||
|
||||
/* BASE_MARGIN_BOTTOM+MARGIN_TOP must equal 62. There are 262 scan lines */
|
||||
/* at 60Hz (15.7KHz line rate) and so we just make 62 border lines */
|
||||
#define BASE_MARGIN_TOP 32
|
||||
#define BASE_MARGIN_BOTTOM 30
|
||||
#define BASE_MARGIN_LEFT BORDER_WIDTH
|
||||
#define BASE_MARGIN_RIGHT BORDER_WIDTH
|
||||
|
||||
#define A2_WINDOW_WIDTH 640
|
||||
#define A2_WINDOW_HEIGHT 400
|
||||
|
||||
#define X_A2_WINDOW_WIDTH (A2_WINDOW_WIDTH + BASE_MARGIN_LEFT + \
|
||||
BASE_MARGIN_RIGHT)
|
||||
#define X_A2_WINDOW_HEIGHT (A2_WINDOW_HEIGHT + BASE_MARGIN_TOP + \
|
||||
BASE_MARGIN_BOTTOM)
|
||||
|
||||
#define MAX_STATUS_LINES 4
|
||||
#define STATUS_LINE_LENGTH 88
|
||||
|
||||
#define BASE_WINDOW_WIDTH (X_A2_WINDOW_WIDTH)
|
||||
|
||||
|
||||
#define A2_BORDER_COLOR_NUM 0xfe
|
||||
|
||||
759
gsplus/src/defs_instr.h
Normal file
759
gsplus/src/defs_instr.h
Normal file
@@ -0,0 +1,759 @@
|
||||
// $KmKId: defs_instr.h,v 1.70 2023-11-05 16:22:26+00 kentd Exp $
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2021 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#undef GET_DLOC_X_IND_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_X_IND_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_X_IND_WR(); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DLOC_X_IND_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_X_IND_WR(); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_DISP8_S_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DISP8_S_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DISP8_S_WR(); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DISP8_S_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DISP8_S_WR(); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_DLOC_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2; \
|
||||
GET_MEMORY8((direct + arg) & 0xffff, arg);
|
||||
#else
|
||||
# define GET_DLOC_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2; \
|
||||
GET_MEMORY16((direct + arg) & 0xffff, arg, 1);
|
||||
#endif
|
||||
|
||||
#undef GET_DLOC_RD_RMW
|
||||
#undef GET_MEM_RMW
|
||||
|
||||
#define GET_MEM_RMW() \
|
||||
if(!IS_ACC16) { \
|
||||
if(psr & 0x100) { \
|
||||
/* emulation re-writes the address */ \
|
||||
SET_MEMORY8(addr_latch, arg); \
|
||||
} else { \
|
||||
/* otherwise, just read addr again */ \
|
||||
GET_MEMORY8(addr_latch, dummy1); \
|
||||
} \
|
||||
} else { \
|
||||
/* 16-bit re-reads addr+1 again */ \
|
||||
dummy1 = addr_latch + 1; \
|
||||
GET_MEMORY8(dummy1, dummy1); \
|
||||
addr_latch--; \
|
||||
}
|
||||
|
||||
#define GET_DLOC_RD_RMW() \
|
||||
GET_DLOC_RD(); \
|
||||
GET_MEM_RMW();
|
||||
|
||||
|
||||
#undef GET_DLOC_L_IND_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_L_IND_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_L_IND_WR(); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DLOC_L_IND_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_L_IND_WR(); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_IMM_MEM
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_IMM_MEM() \
|
||||
GET_1BYTE_ARG; \
|
||||
INC_KPC_2;
|
||||
#else
|
||||
# define GET_IMM_MEM() \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
INC_KPC_3;
|
||||
#endif
|
||||
|
||||
#undef GET_ABS_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_ABS_RD() \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
GET_MEMORY8((dbank << 16) + arg, arg); \
|
||||
INC_KPC_3;
|
||||
#else
|
||||
# define GET_ABS_RD() \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
GET_MEMORY16((dbank << 16) + arg, arg, 0); \
|
||||
INC_KPC_3;
|
||||
#endif
|
||||
|
||||
#undef GET_ABS_RD_RMW
|
||||
|
||||
#define GET_ABS_RD_RMW() \
|
||||
GET_ABS_RD(); \
|
||||
GET_MEM_RMW();
|
||||
|
||||
#undef GET_LONG_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_LONG_RD() \
|
||||
GET_3BYTE_ARG; \
|
||||
CYCLES_PLUS_2; \
|
||||
GET_MEMORY8(arg, arg); \
|
||||
INC_KPC_4;
|
||||
#else
|
||||
# define GET_LONG_RD() \
|
||||
GET_3BYTE_ARG; \
|
||||
CYCLES_PLUS_2; \
|
||||
GET_MEMORY16(arg, arg, 0); \
|
||||
INC_KPC_4;
|
||||
#endif
|
||||
|
||||
#undef GET_DLOC_IND_Y_RD
|
||||
#undef GET_DLOC_IND_Y_ADDR
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_IND_Y_RD() \
|
||||
GET_DLOC_IND_Y_ADDR(0) \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DLOC_IND_Y_RD() \
|
||||
GET_DLOC_IND_Y_ADDR(0) \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#define GET_DLOC_IND_Y_ADDR(is_write) \
|
||||
GET_1BYTE_ARG; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1, 0); \
|
||||
tmp1 += (dbank << 16); \
|
||||
arg = (tmp1 + yreg) & 0xffffff; \
|
||||
tmp2 = (tmp1 & 0xffff00) | (arg & 0xff); \
|
||||
if((psr & 0x10) && ((arg != tmp2) | is_write)) { \
|
||||
GET_MEMORY8(tmp2, tmp1); \
|
||||
} else if(((psr & 0x10) == 0) | (arg != tmp2) | is_write) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2;
|
||||
|
||||
#undef GET_DLOC_IND_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_IND_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
INC_KPC_2; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0); \
|
||||
GET_MEMORY8((dbank << 16) + arg, arg);
|
||||
#else
|
||||
# define GET_DLOC_IND_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
INC_KPC_2; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0); \
|
||||
GET_MEMORY16((dbank << 16) + arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_DLOC_X_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_X_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2; \
|
||||
arg = (arg + xreg + direct) & 0xffff; \
|
||||
if(psr & 0x100) { \
|
||||
if((direct & 0xff) == 0) { \
|
||||
arg = (direct & 0xff00) | (arg & 0xff); \
|
||||
} \
|
||||
} \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DLOC_X_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2; \
|
||||
arg = (arg + xreg + direct) & 0xffff; \
|
||||
if(!IS_ACC16 && (psr & 0x100)) { \
|
||||
if((direct & 0xff) == 0) { \
|
||||
arg = (direct & 0xff00) | (arg & 0xff); \
|
||||
} \
|
||||
} \
|
||||
GET_MEMORY16(arg, arg, 1);
|
||||
#endif
|
||||
|
||||
#undef GET_DLOC_X_RD_RMW
|
||||
|
||||
#define GET_DLOC_X_RD_RMW() \
|
||||
GET_DLOC_X_RD(); \
|
||||
GET_MEM_RMW();
|
||||
|
||||
|
||||
#undef GET_DISP8_S_IND_Y_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DISP8_S_IND_Y_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DISP8_S_IND_Y_WR(); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DISP8_S_IND_Y_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DISP8_S_IND_Y_WR(); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_DLOC_L_IND_Y_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_DLOC_L_IND_Y_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_L_IND_Y_WR(); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_DLOC_L_IND_Y_RD() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_L_IND_Y_WR(); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_ABS_INDEX_ADDR
|
||||
|
||||
#define GET_ABS_INDEX_ADDR(index_reg, is_write) \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
INC_KPC_3; \
|
||||
tmp1 = (dbank << 16) + arg; \
|
||||
arg = tmp1 + index_reg; \
|
||||
tmp1 = (tmp1 & 0xffff00) + (arg & 0xff); \
|
||||
if((psr & 0x10) && ((tmp1 != arg) | is_write)) { \
|
||||
GET_MEMORY8(tmp1, tmp2); \
|
||||
} else if(((psr & 0x10) == 0) | (tmp1 != arg) | is_write) { \
|
||||
CYCLES_PLUS_1; \
|
||||
}
|
||||
|
||||
#undef GET_ABS_Y_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_ABS_Y_RD() \
|
||||
GET_ABS_INDEX_ADDR(yreg, 0); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_ABS_Y_RD() \
|
||||
GET_ABS_INDEX_ADDR(yreg, 0); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#undef GET_ABS_X_RD
|
||||
#undef GET_ABS_X_RD_RMW
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_ABS_X_RD() \
|
||||
GET_ABS_INDEX_ADDR(xreg, 0); \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#define GET_ABS_X_RD_RMW() \
|
||||
GET_ABS_INDEX_ADDR(xreg, 1); \
|
||||
GET_MEMORY8(arg, arg); \
|
||||
GET_MEM_RMW();
|
||||
#else
|
||||
# define GET_ABS_X_RD() \
|
||||
GET_ABS_INDEX_ADDR(xreg, 0); \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#define GET_ABS_X_RD_RMW() \
|
||||
GET_ABS_INDEX_ADDR(xreg, 1); \
|
||||
GET_MEMORY16(arg, arg, 0); \
|
||||
GET_MEM_RMW();
|
||||
#endif
|
||||
|
||||
#undef GET_LONG_X_RD
|
||||
|
||||
#ifdef ACC8
|
||||
# define GET_LONG_X_RD() \
|
||||
GET_3BYTE_ARG; \
|
||||
arg = (arg + xreg) & 0xffffff; \
|
||||
INC_KPC_4; \
|
||||
CYCLES_PLUS_2; \
|
||||
GET_MEMORY8(arg, arg);
|
||||
#else
|
||||
# define GET_LONG_X_RD() \
|
||||
GET_3BYTE_ARG; \
|
||||
arg = (arg + xreg) & 0xffffff; \
|
||||
INC_KPC_4; \
|
||||
CYCLES_PLUS_2; \
|
||||
GET_MEMORY16(arg, arg, 0);
|
||||
#endif
|
||||
|
||||
#define SET_NEG_ZERO16(val) \
|
||||
zero = val; \
|
||||
neg7 = (val) >> 8;
|
||||
|
||||
#define SET_NEG_ZERO8(val) \
|
||||
zero = val; \
|
||||
neg7 = val;
|
||||
|
||||
#define SET_CARRY8(val) \
|
||||
psr = (psr & ~1) + (((val) >> 8) & 1);
|
||||
|
||||
#define SET_CARRY16(val) \
|
||||
psr = (psr & ~1) + (((val) >> 16) & 1);
|
||||
|
||||
#define SET_INDEX_REG(src, dest) \
|
||||
if(psr & 0x10) { \
|
||||
dest = (src) & 0xff; \
|
||||
SET_NEG_ZERO8(dest); \
|
||||
} else { \
|
||||
dest = (src) & 0xffff; \
|
||||
SET_NEG_ZERO16(dest); \
|
||||
}
|
||||
|
||||
#define LD_INDEX_INST(index_reg, in_bank) \
|
||||
if(psr & 0x10) { \
|
||||
GET_MEMORY8(arg, arg); \
|
||||
} else { \
|
||||
GET_MEMORY16(arg, arg, in_bank);\
|
||||
} \
|
||||
SET_INDEX_REG(arg, index_reg);
|
||||
|
||||
#define LDX_INST(in_bank) LD_INDEX_INST(xreg, in_bank)
|
||||
#define LDY_INST(in_bank) LD_INDEX_INST(yreg, in_bank)
|
||||
|
||||
#undef ORA_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define ORA_INST() \
|
||||
tmp1 = (acc | arg) & 0xff; \
|
||||
acc = (acc & 0xff00) + tmp1; \
|
||||
SET_NEG_ZERO8(tmp1);
|
||||
#else
|
||||
# define ORA_INST() \
|
||||
acc = (acc | arg); \
|
||||
SET_NEG_ZERO16(acc);
|
||||
#endif
|
||||
|
||||
#undef AND_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define AND_INST() \
|
||||
tmp1 = (acc & arg) & 0xff; \
|
||||
acc = (acc & 0xff00) + tmp1; \
|
||||
SET_NEG_ZERO8(tmp1);
|
||||
#else
|
||||
# define AND_INST() \
|
||||
acc = (acc & arg); \
|
||||
SET_NEG_ZERO16(acc);
|
||||
#endif
|
||||
|
||||
#undef EOR_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define EOR_INST() \
|
||||
tmp1 = (acc ^ arg) & 0xff; \
|
||||
acc = (acc & 0xff00) + tmp1; \
|
||||
SET_NEG_ZERO8(tmp1);
|
||||
#else
|
||||
# define EOR_INST() \
|
||||
acc = (acc ^ arg); \
|
||||
SET_NEG_ZERO16(acc);
|
||||
#endif
|
||||
|
||||
#undef LDA_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define LDA_INST() \
|
||||
acc = (acc & 0xff00) + (arg & 0xff); \
|
||||
SET_NEG_ZERO8(arg & 0xff);
|
||||
#else
|
||||
# define LDA_INST() \
|
||||
acc = (arg & 0xffff); \
|
||||
SET_NEG_ZERO16(acc);
|
||||
#endif
|
||||
|
||||
#undef ADC_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define ADC_INST() \
|
||||
tmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 0); \
|
||||
acc = (acc & 0xff00) + (tmp1 & 0xff); \
|
||||
psr = (tmp1 >> 16); \
|
||||
zero = !(psr & 0x2); \
|
||||
neg7 = psr;
|
||||
#else
|
||||
# define ADC_INST() \
|
||||
tmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 0); \
|
||||
acc = (tmp1 & 0xffff); \
|
||||
psr = (tmp1 >> 16); \
|
||||
zero = !(psr & 0x2); \
|
||||
neg7 = psr;
|
||||
#endif
|
||||
|
||||
#undef SBC_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define SBC_INST() \
|
||||
tmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 1); \
|
||||
acc = (acc & 0xff00) + (tmp1 & 0xff); \
|
||||
psr = (tmp1 >> 16); \
|
||||
zero = !(psr & 0x2); \
|
||||
neg7 = psr;
|
||||
#else
|
||||
# define SBC_INST() \
|
||||
tmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 1); \
|
||||
acc = (tmp1 & 0xffff); \
|
||||
psr = (tmp1 >> 16); \
|
||||
zero = !(psr & 0x2); \
|
||||
neg7 = psr;
|
||||
#endif
|
||||
|
||||
|
||||
#undef CMP_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define CMP_INST() \
|
||||
arg = (acc & 0xff) + (0x100 - arg); \
|
||||
SET_CARRY8(arg); \
|
||||
arg = arg & 0xff; \
|
||||
SET_NEG_ZERO8(arg & 0xff);
|
||||
#else
|
||||
# define CMP_INST() \
|
||||
arg = (acc & 0xffff) + (0x10000 - arg); \
|
||||
SET_CARRY16(arg); \
|
||||
arg = arg & 0xffff; \
|
||||
SET_NEG_ZERO16(arg & 0xffff);
|
||||
#endif
|
||||
|
||||
#undef BIT_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define BIT_INST() \
|
||||
neg7 = arg; \
|
||||
zero = (acc & arg & 0xff); \
|
||||
psr = (psr & (~0x40)) | (arg & 0x40);
|
||||
#else
|
||||
# define BIT_INST() \
|
||||
neg7 = arg >> 8; \
|
||||
zero = (acc & arg & 0xffff); \
|
||||
psr = (psr & (~0x40)) | ((arg >> 8) & 0x40);
|
||||
#endif
|
||||
|
||||
#undef STA_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define STA_INST(in_bank) \
|
||||
SET_MEMORY8(arg, acc);
|
||||
#else
|
||||
# define STA_INST(in_bank) \
|
||||
SET_MEMORY16(arg, acc, in_bank);
|
||||
#endif
|
||||
|
||||
#undef TSB_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define TSB_INST(in_bank) \
|
||||
arg = arg & 0xff; \
|
||||
tmp1 = arg | acc; \
|
||||
zero = arg & acc; \
|
||||
SET_MEMORY8(addr_latch, tmp1);
|
||||
#else
|
||||
# define TSB_INST(in_bank) \
|
||||
tmp1 = arg | acc; \
|
||||
zero = arg & acc; \
|
||||
SET_MEMORY16(addr_latch, tmp1, in_bank);
|
||||
#endif
|
||||
|
||||
#undef ASL_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define ASL_INST(in_bank) \
|
||||
psr = (psr & 0x1fe) + ((arg >> 7) & 1); \
|
||||
tmp1 = (arg << 1) & 0xff; \
|
||||
SET_NEG_ZERO8(tmp1); \
|
||||
SET_MEMORY8(addr_latch, tmp1);
|
||||
#else
|
||||
# define ASL_INST(in_bank) \
|
||||
psr = (psr & 0x1fe) + ((arg >> 15) & 1);\
|
||||
tmp1 = (arg << 1) & 0xffff; \
|
||||
SET_NEG_ZERO16(tmp1); \
|
||||
SET_MEMORY16(addr_latch, tmp1, in_bank);
|
||||
#endif
|
||||
|
||||
#undef ROL_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define ROL_INST(in_bank) \
|
||||
arg = arg & 0xff; \
|
||||
arg = (arg << 1) | (psr & 1); \
|
||||
SET_MEMORY8(addr_latch, arg); \
|
||||
SET_NEG_ZERO8(arg & 0xff); \
|
||||
SET_CARRY8(arg);
|
||||
#else
|
||||
# define ROL_INST(in_bank) \
|
||||
arg = (arg << 1) | (psr & 1); \
|
||||
SET_MEMORY16(addr_latch, arg, in_bank); \
|
||||
SET_NEG_ZERO16(arg & 0xffff); \
|
||||
SET_CARRY16(arg);
|
||||
#endif
|
||||
|
||||
#undef LSR_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define LSR_INST(in_bank) \
|
||||
SET_CARRY8(arg << 8); \
|
||||
arg = (arg >> 1) & 0x7f; \
|
||||
SET_NEG_ZERO8(arg); \
|
||||
SET_MEMORY8(addr_latch, arg);
|
||||
#else
|
||||
# define LSR_INST(in_bank) \
|
||||
SET_CARRY16(arg << 16); \
|
||||
arg = (arg >> 1) & 0x7fff; \
|
||||
SET_NEG_ZERO16(arg); \
|
||||
SET_MEMORY16(addr_latch, arg, in_bank);
|
||||
#endif
|
||||
|
||||
#undef ROR_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define ROR_INST(in_bank) \
|
||||
tmp1 = psr & 1; \
|
||||
SET_CARRY8(arg << 8); \
|
||||
arg = ((arg >> 1) & 0x7f) | (tmp1 << 7); \
|
||||
SET_MEMORY8(addr_latch, arg); \
|
||||
SET_NEG_ZERO8(arg);
|
||||
#else
|
||||
# define ROR_INST(in_bank) \
|
||||
tmp1 = psr & 1; \
|
||||
SET_CARRY16(arg << 16); \
|
||||
arg = ((arg >> 1) & 0x7fff) | (tmp1 << 15); \
|
||||
SET_MEMORY16(addr_latch, arg, in_bank); \
|
||||
SET_NEG_ZERO16(arg);
|
||||
#endif
|
||||
|
||||
#undef TRB_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define TRB_INST(in_bank) \
|
||||
arg = arg & 0xff; \
|
||||
tmp1 = arg & ~acc; \
|
||||
zero = arg & acc; \
|
||||
SET_MEMORY8(addr_latch, tmp1);
|
||||
#else
|
||||
# define TRB_INST(in_bank) \
|
||||
tmp1 = arg & ~acc; \
|
||||
zero = arg & acc; \
|
||||
SET_MEMORY16(addr_latch, tmp1, in_bank);
|
||||
#endif
|
||||
|
||||
#undef DEC_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define DEC_INST(in_bank) \
|
||||
arg = (arg - 1) & 0xff; \
|
||||
SET_MEMORY8(addr_latch, arg); \
|
||||
SET_NEG_ZERO8(arg);
|
||||
#else
|
||||
# define DEC_INST(in_bank) \
|
||||
arg = (arg - 1) & 0xffff; \
|
||||
SET_MEMORY16(addr_latch, arg, in_bank); \
|
||||
SET_NEG_ZERO16(arg);
|
||||
#endif
|
||||
|
||||
#undef INC_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define INC_INST(in_bank) \
|
||||
arg = (arg + 1) & 0xff; \
|
||||
SET_MEMORY8(addr_latch, arg); \
|
||||
SET_NEG_ZERO8(arg);
|
||||
#else
|
||||
# define INC_INST(in_bank) \
|
||||
arg = (arg + 1) & 0xffff; \
|
||||
SET_MEMORY16(addr_latch, arg, in_bank); \
|
||||
SET_NEG_ZERO16(arg);
|
||||
#endif
|
||||
|
||||
#undef STZ_INST
|
||||
|
||||
#ifdef ACC8
|
||||
# define STZ_INST(in_bank) \
|
||||
SET_MEMORY8(arg, 0);
|
||||
#else
|
||||
# define STZ_INST(in_bank) \
|
||||
SET_MEMORY16(arg, 0, in_bank);
|
||||
#endif
|
||||
|
||||
#undef BRANCH_DISP8
|
||||
|
||||
#define BRANCH_DISP8(cond) \
|
||||
GET_1BYTE_ARG; \
|
||||
tmp2 = kpc & 0xff0000; \
|
||||
kpc += 2; \
|
||||
tmp1 = kpc; \
|
||||
if(cond) { \
|
||||
kpc = kpc + arg - ((arg & 0x80) << 1); \
|
||||
CYCLES_PLUS_1; \
|
||||
if((tmp1 ^ kpc) & psr & 0x100) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
} \
|
||||
kpc = tmp2 + (kpc & 0xffff);
|
||||
|
||||
#undef STY_INST
|
||||
#undef STX_INST
|
||||
|
||||
#define STY_INST(in_bank) \
|
||||
if(psr & 0x10) { \
|
||||
SET_MEMORY8(arg, yreg); \
|
||||
} else { \
|
||||
SET_MEMORY16(arg, yreg, in_bank);\
|
||||
}
|
||||
#define STX_INST(in_bank) \
|
||||
if(psr & 0x10) { \
|
||||
SET_MEMORY8(arg, xreg); \
|
||||
} else { \
|
||||
SET_MEMORY16(arg, xreg, in_bank);\
|
||||
}
|
||||
|
||||
#define C_LDX_ABS_Y() \
|
||||
GET_ABS_INDEX_ADDR(yreg, 0); \
|
||||
LDX_INST(0);
|
||||
|
||||
#define C_LDY_ABS_X() \
|
||||
GET_ABS_INDEX_ADDR(xreg, 0); \
|
||||
LDY_INST(0);
|
||||
|
||||
#define C_LDX_ABS() \
|
||||
GET_ABS_ADDR(); \
|
||||
LDX_INST(0);
|
||||
|
||||
#define C_LDY_ABS() \
|
||||
GET_ABS_ADDR(); \
|
||||
LDY_INST(0);
|
||||
|
||||
#define C_LDX_DLOC() \
|
||||
GET_DLOC_ADDR(); \
|
||||
LDX_INST(1);
|
||||
|
||||
#define C_LDY_DLOC() \
|
||||
GET_DLOC_ADDR(); \
|
||||
LDY_INST(1);
|
||||
|
||||
#define C_LDY_DLOC_X() \
|
||||
GET_DLOC_X_ADDR(); \
|
||||
LDY_INST(1);
|
||||
|
||||
#define C_LDX_DLOC_Y() \
|
||||
GET_DLOC_Y_ADDR(); \
|
||||
LDX_INST(1);
|
||||
|
||||
#define CP_INDEX_VAL(index_reg) \
|
||||
arg = 0x100 - arg + index_reg; \
|
||||
if((psr & 0x10) == 0) { \
|
||||
arg += 0xff00; \
|
||||
SET_NEG_ZERO16(arg & 0xffff); \
|
||||
SET_CARRY16(arg); \
|
||||
} else { \
|
||||
SET_NEG_ZERO8(arg & 0xff);\
|
||||
SET_CARRY8(arg); \
|
||||
}
|
||||
|
||||
#define CP_INDEX_LOAD(index_reg, in_bank) \
|
||||
if((psr & 0x10) != 0) { \
|
||||
GET_MEMORY8(arg, arg); \
|
||||
} else { \
|
||||
GET_MEMORY16(arg, arg, in_bank);\
|
||||
} \
|
||||
CP_INDEX_VAL(index_reg)
|
||||
|
||||
#define CPX_INST(in_bank) \
|
||||
CP_INDEX_LOAD(xreg, in_bank);
|
||||
|
||||
#define CPY_INST(in_bank) \
|
||||
CP_INDEX_LOAD(yreg, in_bank);
|
||||
|
||||
#define C_CPX_IMM() \
|
||||
INC_KPC_2; \
|
||||
if((psr & 0x10) == 0) { \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
INC_KPC_1; \
|
||||
} else { \
|
||||
GET_1BYTE_ARG; \
|
||||
} \
|
||||
CP_INDEX_VAL(xreg);
|
||||
|
||||
#define C_CPY_IMM() \
|
||||
INC_KPC_2; \
|
||||
if((psr & 0x10) == 0) { \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
INC_KPC_1; \
|
||||
} else { \
|
||||
GET_1BYTE_ARG; \
|
||||
} \
|
||||
CP_INDEX_VAL(yreg);
|
||||
|
||||
#define C_CPX_DLOC() \
|
||||
GET_DLOC_ADDR(); \
|
||||
CPX_INST(1);
|
||||
|
||||
#define C_CPY_DLOC() \
|
||||
GET_DLOC_ADDR(); \
|
||||
CPY_INST(1);
|
||||
|
||||
#define C_CPX_ABS() \
|
||||
GET_ABS_ADDR(); \
|
||||
CPX_INST(0);
|
||||
|
||||
#define C_CPY_ABS() \
|
||||
GET_ABS_ADDR(); \
|
||||
CPY_INST(0);
|
||||
|
||||
31
gsplus/src/dependency
Normal file
31
gsplus/src/dependency
Normal file
@@ -0,0 +1,31 @@
|
||||
adb.o: adb.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_base.h size_c.h op_routs.h engine.h defs_instr.h instable.h
|
||||
clock.o: clock.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
compile_time.o: compile_time.c
|
||||
config.o: config.c defc.h defcomm.h iwm.h protos.h protos_base.h config.h win_dirent.h
|
||||
debugger.o: debugger.c defc.h defcomm.h iwm.h protos.h protos_base.h disas.h
|
||||
scc.o: scc.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h
|
||||
scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h
|
||||
scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h
|
||||
scc_unixdriver.o: scc_unixdriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h
|
||||
iwm.o: iwm.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
joystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
moremem.o: moremem.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
paddles.o: paddles.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
mockingboard.o: mockingboard.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h
|
||||
sim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
smartport.o: smartport.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
doc.o: doc.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h
|
||||
sound.o: sound.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h
|
||||
sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h
|
||||
woz.o: woz.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
unshk.o: unshk.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
undeflate.o: undeflate.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
dynapro.o: dynapro.c defc.h defcomm.h iwm.h protos.h protos_base.h win_dirent.h
|
||||
dyna_type.o: dyna_type.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
dyna_filt.o: dyna_filt.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
dyna_validate.o: dyna_validate.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
applesingle.o: applesingle.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
video.o: video.c defc.h defcomm.h iwm.h protos.h protos_base.h kegsfont.h
|
||||
voc.o: voc.c defc.h defcomm.h iwm.h protos.h protos_base.h
|
||||
macsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h
|
||||
208
gsplus/src/disas.h
Normal file
208
gsplus/src/disas.h
Normal file
@@ -0,0 +1,208 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2020 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
enum {
|
||||
ABS = 1,
|
||||
ABSX,
|
||||
ABSY,
|
||||
ABSLONG,
|
||||
ABSIND,
|
||||
ABSXIND,
|
||||
IMPLY,
|
||||
ACCUM,
|
||||
IMMED,
|
||||
JUST8,
|
||||
DLOC,
|
||||
DLOCX,
|
||||
DLOCY,
|
||||
LONG,
|
||||
LONGX,
|
||||
DLOCIND,
|
||||
DLOCINDY,
|
||||
DLOCXIND,
|
||||
DLOCBRAK,
|
||||
DLOCBRAKY,
|
||||
DISP8,
|
||||
DISP8S,
|
||||
DISP8SINDY,
|
||||
DISP16,
|
||||
MVPMVN,
|
||||
REPVAL,
|
||||
SEPVAL
|
||||
};
|
||||
|
||||
|
||||
const char * const disas_opcodes[256] = {
|
||||
"BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", /* 00-07 */
|
||||
"PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", /* 08-0f */
|
||||
"BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", /* 10-17 */
|
||||
"CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", /* 18-1f */
|
||||
"JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", /* 20-27 */
|
||||
"PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", /* 28-2f */
|
||||
"BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", /* 30-37 */
|
||||
"SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", /* 38-3f */
|
||||
"RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", /* 40-47 */
|
||||
"PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", /* 48-4f */
|
||||
"BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", /* 50-57 */
|
||||
"CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR", /* 58-5f */
|
||||
"RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", /* 60-67 */
|
||||
"PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", /* 68-6f */
|
||||
"BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", /* 70-77 */
|
||||
"SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", /* 78-7f */
|
||||
"BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", /* 80-87 */
|
||||
"DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", /* 88-8f */
|
||||
"BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", /* 90-97 */
|
||||
"TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", /* 98-9f */
|
||||
"LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", /* a0-a7 */
|
||||
"TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", /* a8-af */
|
||||
"BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", /* b0-b7 */
|
||||
"CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", /* b8-bf */
|
||||
"CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", /* c0-c7 */
|
||||
"INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", /* c8-cf */
|
||||
"BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", /* d0-d7 */
|
||||
"CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", /* d8-df */
|
||||
"CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", /* e0-e7 */
|
||||
"INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", /* e8-ef */
|
||||
"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", /* f0-f7 */
|
||||
"SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC", /* f8-ff */
|
||||
};
|
||||
|
||||
|
||||
const word32 disas_types[256] = {
|
||||
JUST8+0x100, DLOCXIND+0x100, /* 00-01 */
|
||||
JUST8+0x100, DISP8S+0x100, /* 02-03 */
|
||||
DLOC+0x100, DLOC+0x100, /* 04-05 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* 06-07 */
|
||||
IMPLY+0x000, IMMED+0x400, /* 08-9 */
|
||||
ACCUM+0x000, IMPLY+0x000, /* 0a-b */
|
||||
ABS+0x200, ABS+0x200, /* c-d */
|
||||
ABS+0x200, LONG+0x300, /* e-f */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* 10-11 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* 12-13 */
|
||||
DLOC+0x100, DLOCX+0x100, /* 14-15 */
|
||||
DLOCX+0x100, DLOCBRAKY+0x100, /* 16-17 */
|
||||
IMPLY+0x000, ABSY+0x200, /* 18-19 */
|
||||
ACCUM+0x000, IMPLY+0x000, /* 1a-1b */
|
||||
ABS+0x200, ABSX+0x200, /* 1c-1d */
|
||||
ABSX+0x200, LONGX+0x300, /* 1e-1f */
|
||||
ABS+0x200, DLOCXIND+0x100, /* 20-21 */
|
||||
ABSLONG+0x300, DISP8S+0x100, /* 22-23 */
|
||||
DLOC+0x100, DLOC+0x100, /* 24-25 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* 26-27 */
|
||||
IMPLY+0x000, IMMED+0x400, /* 28-29 */
|
||||
ACCUM+0x000, IMPLY+0x000, /* 2a-2b */
|
||||
ABS+0x200, ABS+0x200, /* 2c-2d */
|
||||
ABS+0x200, LONG+0x300, /* 2e-2f */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* 30-31 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* 32-33 */
|
||||
DLOCX+0x100, DLOCX+0x100, /* 34-35 */
|
||||
DLOCX+0x100, DLOCBRAKY+0x100, /* 36-37 */
|
||||
IMPLY+0x000, ABSY+0x200, /* 38-39 */
|
||||
ACCUM+0x000, IMPLY+0x000, /* 3a-3b */
|
||||
ABSX+0x200, ABSX+0x200, /* 3c-3d */
|
||||
ABSX+0x200, LONGX+0x300, /* 3e-3f */
|
||||
IMPLY+0x000, DLOCXIND+0x100, /* 40-41 */
|
||||
JUST8+0x100, DISP8S+0x100, /* 42-43 */
|
||||
MVPMVN+0x200, DLOC+0x100, /* 44-45 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* 46-47 */
|
||||
IMPLY+0x000, IMMED+0x400, /* 48-49 */
|
||||
ACCUM+0x000, IMPLY+0x000, /* 4a-4b */
|
||||
ABS+0x200, ABS+0x200, /* 4c-4d */
|
||||
ABS+0x200, LONG+0x300, /* 4e-4f */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* 50-51 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* 52-53 */
|
||||
MVPMVN+0x200, DLOCX+0x100, /* 54-55 */
|
||||
DLOCX+0x100, DLOCBRAKY+0x100, /* 56-57 */
|
||||
IMPLY+0x000, ABSY+0x200, /* 58-59 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* 5a-5b */
|
||||
LONG+0x300, ABSX+0x200, /* 5c-5d */
|
||||
ABSX+0x200, LONGX+0x300, /* 5e-5f */
|
||||
IMPLY+0x000, DLOCXIND+0x100, /* 60-61 */
|
||||
DISP16+0x200, DISP8S+0x100, /* 62-63 */
|
||||
DLOC+0x100, DLOC+0x100, /* 64-65 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* 66-67 */
|
||||
IMPLY+0x000, IMMED+0x400, /* 68-69 */
|
||||
ACCUM+0x000, IMPLY+0x000, /* 6a-6b */
|
||||
ABSIND+0x200, ABS+0x200, /* 6c-6d */
|
||||
ABS+0x200, LONG+0x300, /* 6e-6f */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* 70-71 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* 72-73 */
|
||||
DLOCX+0x100, DLOCX+0x100, /* 74-75 */
|
||||
DLOCX+0x100, DLOCBRAKY+0x100, /* 76-77 */
|
||||
IMPLY+0x000, ABSY+0x200, /* 78-79 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* 7a-7b */
|
||||
ABSXIND+0x200, ABSX+0x200, /* 7c-7d */
|
||||
ABSX+0x200, LONGX+0x300, /* 7e-7f */
|
||||
DISP8+0x100, DLOCXIND+0x100, /* 80-81 */
|
||||
DISP16+0x200, DISP8S+0x100, /* 82-83 */
|
||||
DLOC+0x100, DLOC+0x100, /* 84-85 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* 86-87 */
|
||||
IMPLY+0x000, IMMED+0x400, /* 88-89 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* 8a-8b */
|
||||
ABS+0x200, ABS+0x200, /* 8c-8d */
|
||||
ABS+0x200, LONG+0x300, /* 8e-8f */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* 90-91 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* 92-93 */
|
||||
DLOCX+0x100, DLOCX+0x100, /* 94-95 */
|
||||
DLOCY+0x100, DLOCBRAKY+0x100, /* 96-97 */
|
||||
IMPLY+0x000, ABSY+0x200, /* 98-99 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* 9a-9b */
|
||||
ABS+0x200, ABSX+0x200, /* 9c-9d */
|
||||
ABSX+0x200, LONGX+0x300, /* 9e-9f */
|
||||
IMMED+0x500, DLOCXIND+0x100, /* a0-a1 */
|
||||
IMMED+0x500, DISP8S+0x100, /* a2-a3 */
|
||||
DLOC+0x100, DLOC+0x100, /* a4-a5 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* a6-a7 */
|
||||
IMPLY+0x000, IMMED+0x400, /* a8-a9 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* aa-ab */
|
||||
ABS+0x200, ABS+0x200, /* ac-ad */
|
||||
ABS+0x200, LONG+0x300, /* ae-af */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* b0-b1 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* b2-b3 */
|
||||
DLOCX+0x100, DLOCX+0x100, /* b4-b5 */
|
||||
DLOCY+0x100, DLOCBRAKY+0x100, /* b6-b7 */
|
||||
IMPLY+0x000, ABSY+0x200, /* b8-b9 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* ba-bb */
|
||||
ABSX+0x200, ABSX+0x200, /* bc-bd */
|
||||
ABSY+0x200, LONGX+0x300, /* be-bf */
|
||||
IMMED+0x500, DLOCXIND+0x100, /* c0-c1 */
|
||||
REPVAL+0x100, DISP8S+0x100, /* c2-c3 */
|
||||
DLOC+0x100, DLOC+0x100, /* c4-c5 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* c6-c7 */
|
||||
IMPLY+0x000, IMMED+0x400, /* c8-c9 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* ca-cb */
|
||||
ABS+0x200, ABS+0x200, /* cc-cd */
|
||||
ABS+0x200, LONG+0x300, /* ce-cf */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* d0-d1 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* d2-d3 */
|
||||
DLOC+0x100, DLOCX+0x100, /* d4-d5 */
|
||||
DLOCX+0x100, DLOCBRAKY+0x100, /* d6-d7 */
|
||||
IMPLY+0x000, ABSY+0x200, /* d8-d9 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* da-db */
|
||||
ABSIND+0x200, ABSX+0x200, /* dc-dd */
|
||||
ABSX+0x200, LONGX+0x300, /* de-df */
|
||||
IMMED+0x500, DLOCXIND+0x100, /* e0-e1 */
|
||||
SEPVAL+0x100, DISP8S+0x100, /* e2-e3 */
|
||||
DLOC+0x100, DLOC+0x100, /* e4-e5 */
|
||||
DLOC+0x100, DLOCBRAK+0x100, /* e6-e7 */
|
||||
IMPLY+0x000, IMMED+0x400, /* e8-e9 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* ea-eb */
|
||||
ABS+0x200, ABS+0x200, /* ec-ed */
|
||||
ABS+0x200, LONG+0x300, /* ee-ef */
|
||||
DISP8+0x100, DLOCINDY+0x100, /* f0-f1 */
|
||||
DLOCIND+0x100, DISP8SINDY+0x100, /* f2-f3 */
|
||||
IMMED+0x200, DLOCX+0x100, /* f4-f5 */
|
||||
DLOCX+0x100, DLOCBRAKY+0x100, /* f6-f7 */
|
||||
IMPLY+0x000, ABSY+0x200, /* f8-f9 */
|
||||
IMPLY+0x000, IMPLY+0x000, /* fa-fb */
|
||||
ABSXIND+0x200, ABSX+0x200, /* fc-fd */
|
||||
ABSX+0x200, LONGX+0x300, /* fe-ff */
|
||||
};
|
||||
|
||||
1090
gsplus/src/doc.c
Normal file
1090
gsplus/src/doc.c
Normal file
File diff suppressed because it is too large
Load Diff
16
gsplus/src/dyna_filt.c
Normal file
16
gsplus/src/dyna_filt.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2021 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
// Provide filters for Dynapro to use for copying files from the host to
|
||||
// a ProDOS volume, and for writing changes to the ProDOS volume back to
|
||||
// host files.
|
||||
|
||||
329
gsplus/src/dyna_type.c
Normal file
329
gsplus/src/dyna_type.c
Normal file
@@ -0,0 +1,329 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2021-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
// Provide routines for Dynapro to use for detecting the file type on the
|
||||
// host system. Host files can be "basic1.bas", "basic2,tbas,a$801"
|
||||
|
||||
STRUCT(Dynatype_extensions) {
|
||||
char str[16];
|
||||
word16 file_type;
|
||||
word16 aux_type;
|
||||
};
|
||||
|
||||
Dynatype_extensions g_dynatype_extensions[] = {
|
||||
{ "applesingle", 0xfff, 0xffff },
|
||||
{ "txt", 0x04, 0 },
|
||||
{ "c", 0x04, 0 }, // ,ttxt
|
||||
{ "s", 0x04, 0 }, // ,ttxt
|
||||
{ "h", 0x04, 0 }, // ,ttxt
|
||||
{ "bin", 0x06, 0x2000 }, // ,tbin
|
||||
{ "bas", 0xfc, 0x0801 }, // ,tbas
|
||||
{ "system", 0xff, 0x2000 }, // ,tsys
|
||||
//{ "shr", 0xc0, 0x0002 }, // ,t$c0
|
||||
{ "shk", 0xe0, 0x8002 }, // ,t$e0
|
||||
{ "sdk", 0xe0, 0x8002 }, // ,t$e0
|
||||
{ "", 0, 0 }
|
||||
};
|
||||
|
||||
STRUCT(Dynatype_types) {
|
||||
char str[16];
|
||||
word16 file_type;
|
||||
word16 aux_type;
|
||||
};
|
||||
|
||||
Dynatype_types g_dynatype_types[] = {
|
||||
{ "non", 0x00, 0 },
|
||||
{ "bad", 0x01, 0 },
|
||||
{ "txt", 0x04, 0 },
|
||||
{ "bin", 0x06, 0x2000 },
|
||||
{ "pnt", 0xc0, 0x0002 },
|
||||
{ "fnd", 0xc9, 0 },
|
||||
{ "icn", 0xca, 0 },
|
||||
{ "cmd", 0xf0, 0 },
|
||||
{ "bas", 0xfc, 0x0801 },
|
||||
{ "sys", 0xff, 0x2000 },
|
||||
{ "", 0, 0 }
|
||||
};
|
||||
|
||||
word32
|
||||
dynatype_scan_extensions(const char *str)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
|
||||
len = (int)(sizeof(g_dynatype_extensions) /
|
||||
sizeof(g_dynatype_extensions[0]));
|
||||
for(i = 0; i < len; i++) {
|
||||
if(cfgcasecmp(str, g_dynatype_extensions[i].str) == 0) {
|
||||
return (g_dynatype_extensions[i].file_type << 16) |
|
||||
g_dynatype_extensions[i].aux_type |
|
||||
0x1000000;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
word32
|
||||
dynatype_find_prodos_type(const char *str)
|
||||
{
|
||||
word32 file_type;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
len = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0]));
|
||||
for(i = 0; i < len; i++) {
|
||||
if(cfgcasecmp(str, g_dynatype_types[i].str) == 0) {
|
||||
file_type = g_dynatype_types[i].file_type;
|
||||
return (file_type << 16) | g_dynatype_types[i].aux_type;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *
|
||||
dynatype_find_file_type(word32 file_type)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
|
||||
len = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0]));
|
||||
for(i = 0; i < len; i++) {
|
||||
if(g_dynatype_types[i].file_type == file_type) {
|
||||
return g_dynatype_types[i].str;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
word32
|
||||
dynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr,
|
||||
word32 storage_type)
|
||||
{
|
||||
char ext_buf[32];
|
||||
const char *str;
|
||||
char *endstr;
|
||||
word32 file_type, aux_type, type_or_aux;
|
||||
int len, this_len, c, pos;
|
||||
|
||||
// Look for ,tbas and ,a$2000 to get filetype and aux_type info
|
||||
|
||||
str = cfg_str_basename(path_ptr);
|
||||
len = (int)strlen(str);
|
||||
|
||||
// Look for .ext and ,tbas, etc.
|
||||
pos = 0;
|
||||
ext_buf[0] = 0;
|
||||
file_type = 0x06; // Default to BIN
|
||||
aux_type = 0;
|
||||
while(pos < len) {
|
||||
c = str[pos++];
|
||||
if(c == '.') {
|
||||
this_len = dynatype_get_extension(&str[pos],
|
||||
&ext_buf[0], 30);
|
||||
pos += this_len;
|
||||
continue;
|
||||
} else if(c == ',') {
|
||||
this_len = dynatype_comma_arg(&str[pos], &type_or_aux);
|
||||
if(type_or_aux & 0x1000000) {
|
||||
file_type = type_or_aux;
|
||||
} else if(type_or_aux & 0x2000000) {
|
||||
aux_type = type_or_aux;
|
||||
} else {
|
||||
printf("Unknown , extension, %s ignored\n",
|
||||
&str[pos]);
|
||||
}
|
||||
pos += this_len;
|
||||
continue;
|
||||
} else if(c == '#') {
|
||||
// Cadius style encoding: #ff2000 is type=$ff, aux=$2000
|
||||
type_or_aux = strtol(&str[pos], &endstr, 16);
|
||||
file_type = (type_or_aux & 0xffffff) | 0x1000000;
|
||||
aux_type = 0;
|
||||
pos += (int)(endstr - str);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle extensions and type. First do extension mapping
|
||||
if(ext_buf[0]) {
|
||||
type_or_aux = dynatype_scan_extensions(&ext_buf[0]);
|
||||
if((type_or_aux) >= 0x0f000000UL) {
|
||||
// AppleSingle
|
||||
storage_type = 0x50; // Forked file
|
||||
}
|
||||
if(file_type < 0x1000000) {
|
||||
file_type = type_or_aux;
|
||||
}
|
||||
if(aux_type < 0x1000000) {
|
||||
aux_type = type_or_aux;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
printf("After parsing ext, file_type:%08x, aux_type:%08x\n",
|
||||
file_type, aux_type);
|
||||
#endif
|
||||
|
||||
fileptr->file_type = (file_type >> 16) & 0xff;
|
||||
if(aux_type == 0) {
|
||||
aux_type = file_type & 0xffff;
|
||||
}
|
||||
fileptr->aux_type = aux_type & 0xffff;
|
||||
|
||||
return storage_type;
|
||||
}
|
||||
|
||||
int
|
||||
dynatype_get_extension(const char *str, char *out_ptr, int buf_len)
|
||||
{
|
||||
int c, len;
|
||||
|
||||
// Will write up to buf_len chars to out_ptr
|
||||
if(buf_len < 1) {
|
||||
return 0;
|
||||
}
|
||||
buf_len--;
|
||||
len = 0;
|
||||
while(1) {
|
||||
c = *str++;
|
||||
*out_ptr = c;
|
||||
if((c == 0) || (c == '.') || (c == ',') || (c == '#') ||
|
||||
(len >= buf_len)) {
|
||||
*out_ptr = 0;
|
||||
return len;
|
||||
}
|
||||
out_ptr++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
dynatype_comma_arg(const char *str, word32 *type_or_aux_ptr)
|
||||
{
|
||||
char type_buf[8];
|
||||
char *endstr;
|
||||
word32 val, type_or_aux;
|
||||
int c, len, base, this_len;
|
||||
int i;
|
||||
|
||||
// Read next char
|
||||
*type_or_aux_ptr = 0;
|
||||
|
||||
c = *str++;
|
||||
if(c == 0) {
|
||||
return 0;
|
||||
}
|
||||
len = 1;
|
||||
c = tolower(c);
|
||||
type_or_aux = c;
|
||||
// See if next char is $ for hex
|
||||
c = *str;
|
||||
base = 0;
|
||||
if(c == '$') {
|
||||
base = 16;
|
||||
str++;
|
||||
len++;
|
||||
}
|
||||
val = strtol(str, &endstr, base);
|
||||
this_len = (int)(endstr - str);
|
||||
if((val == 0) && (this_len < 2) && (base == 0) &&
|
||||
(type_or_aux == 't')) {
|
||||
// Not a valid number
|
||||
for(i = 0; i < 3; i++) {
|
||||
c = *str++;
|
||||
if(c == 0) {
|
||||
return len;
|
||||
}
|
||||
type_buf[i] = c;
|
||||
len++;
|
||||
}
|
||||
type_buf[3] = 0;
|
||||
val = dynatype_find_prodos_type(&type_buf[0]);
|
||||
*type_or_aux_ptr = 0x1000000 | val;
|
||||
} else {
|
||||
len += this_len;
|
||||
}
|
||||
if(type_or_aux == 't') {
|
||||
if(val < 0x100) {
|
||||
*type_or_aux_ptr = 0x1000000 | ((val << 16) & 0xffffff);
|
||||
}
|
||||
} else if(type_or_aux == 'a') {
|
||||
*type_or_aux_ptr = 0x2000000 | (val & 0xffff);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
dynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max)
|
||||
{
|
||||
char buf[16];
|
||||
Dynapro_file tmpfile;
|
||||
const char *str;
|
||||
word32 aux_type;
|
||||
|
||||
#if 0
|
||||
printf("Looking at %s ftype:%02x aux:%04x\n", outbuf_ptr,
|
||||
fileptr->file_type, fileptr->aux_type);
|
||||
#endif
|
||||
|
||||
if(fileptr->prodos_name[0] >= 0xd0) {
|
||||
return; // Directory, or Dir/Volume Header
|
||||
}
|
||||
if((fileptr->prodos_name[0] & 0xf0) == 0x50) {
|
||||
// Forked file, add .applesingle
|
||||
cfg_strlcat(outbuf_ptr, ".applesingle", path_max);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&tmpfile, 0, sizeof(Dynapro_file));
|
||||
|
||||
// See what this file defaults to as to type/aux
|
||||
(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);
|
||||
|
||||
// Otherwise, add ,ttype and ,a$aux as needed
|
||||
if(tmpfile.file_type != fileptr->file_type) {
|
||||
str = dynatype_find_file_type(fileptr->file_type);
|
||||
if(str) {
|
||||
aux_type = dynatype_find_prodos_type(str);
|
||||
} else {
|
||||
str = &buf[0];
|
||||
buf[15] = 0;
|
||||
snprintf(&buf[0], 15, "$%02x", fileptr->file_type);
|
||||
}
|
||||
cfg_strlcat(outbuf_ptr, ",t", path_max);
|
||||
cfg_strlcat(outbuf_ptr, str, path_max);
|
||||
}
|
||||
|
||||
(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);
|
||||
aux_type = fileptr->aux_type;
|
||||
|
||||
if(aux_type != tmpfile.aux_type) {
|
||||
buf[15] = 0;
|
||||
snprintf(&buf[0], 15, ",a$%04x", aux_type & 0xffff);
|
||||
cfg_strlcat(outbuf_ptr, &buf[0], path_max);
|
||||
}
|
||||
|
||||
// printf("dynatype_new_unix_name: %s\n", outbuf_ptr);
|
||||
|
||||
// Check that it succeeded
|
||||
(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);
|
||||
if((tmpfile.file_type != fileptr->file_type) ||
|
||||
(tmpfile.aux_type != fileptr->aux_type)) {
|
||||
halt_printf("File %s want ftype:%02x aux:%04x, got:%02x %04x\n",
|
||||
outbuf_ptr, fileptr->file_type, fileptr->aux_type,
|
||||
tmpfile.file_type, tmpfile.aux_type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
459
gsplus/src/dyna_validate.c
Normal file
459
gsplus/src/dyna_validate.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2021-2022 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// Main information is from Beneath Apple ProDOS which has disk layout
|
||||
// descriptions. Forked files are described in Technote tn-pdos-025.
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
word32
|
||||
dynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte,
|
||||
word32 parent_dir_byte)
|
||||
{
|
||||
word32 storage_type, exp_type, val, parent_block, exp_val;
|
||||
|
||||
storage_type = fileptr->prodos_name[0] & 0xf0;
|
||||
exp_type = 0xe0;
|
||||
if(dir_byte == 0x0404) {
|
||||
exp_type = 0xf0; // Volume header
|
||||
}
|
||||
if(storage_type != exp_type) {
|
||||
printf("Volume/Dir header is %02x at %07x\n",
|
||||
storage_type, dir_byte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(fileptr->aux_type != 0x0d27) {
|
||||
printf("entry_length, entries_per_block:%04x at %07x\n",
|
||||
fileptr->aux_type, dir_byte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(exp_type == 0xf0) { // Volume header
|
||||
val = fileptr->lastmod_time >> 16;
|
||||
if(val != 6) {
|
||||
printf("bit_map_ptr:%04x, should be 6\n", val);
|
||||
return 0;
|
||||
}
|
||||
val = fileptr->header_pointer;
|
||||
if(val != (dsk->dimage_size >> 9)) {
|
||||
printf("Num blocks at %07x is wrong: %04x\n", dir_byte,
|
||||
val);
|
||||
return 0;
|
||||
}
|
||||
} else { // Directory header
|
||||
val = fileptr->lastmod_time >> 16; // parent_pointer
|
||||
parent_block = parent_dir_byte >> 9;
|
||||
if(val != parent_block) {
|
||||
printf("Dir at %07x parent:%04x should be %04x\n",
|
||||
dir_byte, val, parent_block);
|
||||
return 0;
|
||||
}
|
||||
val = fileptr->header_pointer;
|
||||
exp_val = ((parent_dir_byte & 0x1ff) - 4) / 0x27;
|
||||
exp_val = (exp_val + 1) | 0x2700;
|
||||
if(val != exp_val) {
|
||||
printf("Parent entry at %07x is:%04x, should be:%04x\n",
|
||||
dir_byte, val, exp_val);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
dynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks)
|
||||
{
|
||||
word32 num_map_blocks, mask;
|
||||
int pos;
|
||||
word32 ui;
|
||||
|
||||
for(ui = 0; ui < (num_blocks + 7)/8; ui++) {
|
||||
freeblks_ptr[ui] = 0xff;
|
||||
}
|
||||
freeblks_ptr[0] &= 0x3f;
|
||||
if(num_blocks & 7) {
|
||||
freeblks_ptr[num_blocks / 8] = 0xff00 >> (num_blocks & 7);
|
||||
}
|
||||
|
||||
num_map_blocks = (num_blocks + 4095) >> 12; // 4096 bits per block
|
||||
for(ui = 0; ui < num_map_blocks; ui++) {
|
||||
// Mark blocks used in the bitmap as in use
|
||||
pos = (ui + 6) >> 3;
|
||||
mask = 0x80 >> ((ui + 6) & 7);
|
||||
freeblks_ptr[pos] &= (~mask);
|
||||
}
|
||||
}
|
||||
|
||||
word32
|
||||
dynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block)
|
||||
{
|
||||
word32 mask, ret;
|
||||
int pos;
|
||||
|
||||
// Return != 0 if block is free (which is success), returns == 0
|
||||
// if it is in use (which is an error). Marks block as in use
|
||||
pos = block >> 3;
|
||||
if(block >= (dsk->dimage_size >> 9)) {
|
||||
return 0x100; // Out of range
|
||||
}
|
||||
mask = 0x80 >> (block & 7);
|
||||
ret = freeblks_ptr[pos] & mask;
|
||||
freeblks_ptr[pos] &= (~mask);
|
||||
|
||||
if(!ret) {
|
||||
printf("Block %04x was already in use\n", block);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
word32
|
||||
dynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num,
|
||||
word32 eof, int level_first)
|
||||
{
|
||||
byte *bptr;
|
||||
word32 num_blocks, tmp, ret, exp_blocks, extra_blocks;
|
||||
int level, first;
|
||||
int i;
|
||||
|
||||
level = level_first & 0xf;
|
||||
first = level_first & 0x10;
|
||||
|
||||
if(!dynapro_validate_freeblk(dsk, freeblks_ptr, block_num)) {
|
||||
return 0;
|
||||
}
|
||||
if(level_first == 0x15) {
|
||||
return dynapro_validate_forked_file(dsk, freeblks_ptr,
|
||||
block_num, eof);
|
||||
}
|
||||
if((level < 1) || (level >= 4)) {
|
||||
printf("level %d out of range, %08x\n", level, level_first);
|
||||
return 0;
|
||||
}
|
||||
if(level == 1) {
|
||||
return 1;
|
||||
}
|
||||
num_blocks = 1;
|
||||
bptr = &(dsk->raw_data[block_num * 0x200]);
|
||||
for(i = 0; i < 256; i++) {
|
||||
tmp = bptr[i] + (bptr[256 + i] << 8);
|
||||
if(tmp == 0) {
|
||||
if(first) {
|
||||
printf("First block is spare, illegal!\n");
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof,
|
||||
first | (level - 1));
|
||||
if(ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
num_blocks += ret;
|
||||
first = 0;
|
||||
}
|
||||
|
||||
if(level_first & 0x10) {
|
||||
// Try to estimate exp_blocks based on eof
|
||||
exp_blocks = (eof + 0x1ff) >> 9;
|
||||
if(exp_blocks == 0) {
|
||||
exp_blocks = 1;
|
||||
} else if(exp_blocks > 1) {
|
||||
// Add in sapling blocks
|
||||
extra_blocks = ((exp_blocks + 255) >> 8);
|
||||
if(exp_blocks > 256) {
|
||||
extra_blocks++;
|
||||
}
|
||||
exp_blocks += extra_blocks;
|
||||
}
|
||||
if(num_blocks > exp_blocks) {
|
||||
printf("blocks_used:%04x, eof:%07x, exp:%04x\n",
|
||||
num_blocks, eof, exp_blocks);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
word32
|
||||
dynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num,
|
||||
word32 eof)
|
||||
{
|
||||
byte *bptr;
|
||||
word32 num_blocks, tmp, ret, size, type, exp_blocks;
|
||||
int level;
|
||||
int i;
|
||||
|
||||
bptr = &(dsk->raw_data[block_num * 0x200]);
|
||||
|
||||
if(eof != 0x200) {
|
||||
printf("In forked file block %04x, eof in dir:%08x, exp 0200\n",
|
||||
block_num, eof);
|
||||
return 0;
|
||||
}
|
||||
// Check that most of the block is 0
|
||||
for(i = 44; i < 512; i++) {
|
||||
if((i >= 0x100) && (i < 0x108)) {
|
||||
continue;
|
||||
}
|
||||
if(bptr[i] != 0) {
|
||||
printf("In forked file block:%04x, byte %03x is %02x\n",
|
||||
block_num, i, bptr[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for basic Finder Info format
|
||||
for(i = 0; i < 2; i++) {
|
||||
size = bptr[8 + 18*i];
|
||||
type = bptr[9 + 18*i];
|
||||
if(((size != 0) && (size != 18)) || (type > 2)) {
|
||||
printf("Finder Info size %04x+%03x=%02x, type:%02x\n",
|
||||
block_num, 8 + 18*i, size, type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
num_blocks = 1;
|
||||
for(i = 0; i < 2; i++) {
|
||||
tmp = bptr[1 + 0x100*i] + (bptr[2 + 0x100*i] << 8);
|
||||
if(tmp == 0) {
|
||||
printf("First fork %d block is spare, illegal!\n", i);
|
||||
return 0;
|
||||
}
|
||||
eof = bptr[5 + 0x100*i] + (bptr[6 + 0x100*i] << 8) +
|
||||
(bptr[7 + 0x100*i] << 16);
|
||||
level = bptr[0 + 0x100*i];
|
||||
ret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof,
|
||||
0x10 | level);
|
||||
if(ret == 0) {
|
||||
printf("Fork %d failed, eof:%08x, block:%04x "
|
||||
"fork:%04x, level:%d\n", i, eof, block_num,
|
||||
tmp, level);
|
||||
return 0;
|
||||
}
|
||||
exp_blocks = bptr[3 + 0x100*i] + (bptr[4 + 0x100*i] << 8);
|
||||
if(ret != exp_blocks) {
|
||||
printf("Fork %d at %04x, blocks:%04x, exp:%04x\n",
|
||||
i, block_num, ret, exp_blocks);
|
||||
}
|
||||
num_blocks += ret;
|
||||
}
|
||||
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
word32
|
||||
dynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte,
|
||||
word32 parent_dir_byte, word32 exp_blocks_used)
|
||||
{
|
||||
char buf32[32];
|
||||
Dynapro_file localfile;
|
||||
byte *bptr;
|
||||
word32 start_dir_block, last_block, max_block, tmp_byte, sub_blocks;
|
||||
word32 ret, act_entries, exp_entries, blocks_used, prev, next;
|
||||
int cnt, is_header;
|
||||
|
||||
// Read directory, make sure each entry is consistent
|
||||
// Return 0 if there is damage, != 0 if OK.
|
||||
bptr = dsk->raw_data;
|
||||
start_dir_block = dir_byte >> 9;
|
||||
last_block = 0;
|
||||
max_block = (word32)(dsk->dimage_size >> 9);
|
||||
cnt = 0;
|
||||
is_header = 1;
|
||||
exp_entries = 0xdeadbeef;
|
||||
act_entries = 0;
|
||||
blocks_used = 0;
|
||||
while(dir_byte) {
|
||||
if((dir_byte & 0x1ff) == 4) {
|
||||
// First entry in this block, check prev/next
|
||||
tmp_byte = dir_byte & -0x200; // Block align
|
||||
prev = dynapro_get_word16(&bptr[tmp_byte + 0]);
|
||||
next = dynapro_get_word16(&bptr[tmp_byte + 2]);
|
||||
if((prev != last_block) || (next >= max_block)) {
|
||||
printf("dir at %07x is damaged in links\n",
|
||||
dir_byte);
|
||||
return 0;
|
||||
}
|
||||
last_block = dir_byte >> 9;
|
||||
ret = dynapro_validate_freeblk(dsk, freeblks_ptr,
|
||||
dir_byte >> 9);
|
||||
if(!ret) {
|
||||
return 0;
|
||||
}
|
||||
blocks_used++;
|
||||
}
|
||||
if(cnt++ >= 65536) {
|
||||
printf("Loop detected, dir_byte:%07x\n", dir_byte);
|
||||
return 0;
|
||||
}
|
||||
ret = dynapro_fill_fileptr_from_prodos(dsk, &localfile,
|
||||
&buf32[0], dir_byte);
|
||||
if(ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
if(ret != 1) {
|
||||
act_entries = act_entries + 1 - is_header;
|
||||
}
|
||||
if(is_header) {
|
||||
if(ret == 1) {
|
||||
printf("Volume/Dir header is erased\n");
|
||||
return 0;
|
||||
}
|
||||
ret = dynapro_validate_header(dsk, &localfile, dir_byte,
|
||||
parent_dir_byte);
|
||||
if(ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
exp_entries = localfile.lastmod_time & 0xffff;
|
||||
} else if(ret != 1) {
|
||||
if(localfile.header_pointer != start_dir_block) {
|
||||
printf("At %07x, header_ptr:%04x != %04x\n",
|
||||
dir_byte, localfile.header_pointer,
|
||||
start_dir_block);
|
||||
return 0;
|
||||
}
|
||||
if(localfile.prodos_name[0] >= 0xd0) {
|
||||
sub_blocks = localfile.blocks_used;
|
||||
if(localfile.eof != (sub_blocks * 0x200UL)) {
|
||||
printf("At %07x, eof:%08x != %08x\n",
|
||||
dir_byte, localfile.eof,
|
||||
sub_blocks * 0x200U);
|
||||
return 0;
|
||||
}
|
||||
ret = dynapro_validate_dir(dsk, freeblks_ptr,
|
||||
(localfile.key_block * 0x200) + 4,
|
||||
dir_byte, sub_blocks);
|
||||
if(ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
ret = dynapro_validate_file(dsk, freeblks_ptr,
|
||||
localfile.key_block, localfile.eof,
|
||||
0x10 | (localfile.prodos_name[0] >> 4));
|
||||
if(ret == 0) {
|
||||
printf("At %07x, bad file\n", dir_byte);
|
||||
return 0;
|
||||
}
|
||||
if(localfile.blocks_used != ret) {
|
||||
printf("At %07x, blocks_used prodos "
|
||||
"%04x != %04x calc\n", dir_byte,
|
||||
localfile.blocks_used, ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_header = 0;
|
||||
dir_byte = dir_byte + 0x27;
|
||||
tmp_byte = (dir_byte & 0x1ff) + 0x27;
|
||||
if(tmp_byte < 0x200) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp_byte = (dir_byte - 0x27) & (0 - 0x200UL);
|
||||
dir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL;
|
||||
if(dir_byte == 0) {
|
||||
if(act_entries != exp_entries) {
|
||||
printf("act_entries:%04x != exp:%04x, "
|
||||
"dir_block:%04x\n", act_entries,
|
||||
exp_entries, start_dir_block);
|
||||
return 0;
|
||||
}
|
||||
if(blocks_used != exp_blocks_used) {
|
||||
printf("At dir %07x, blocks_used:%04x!=%04x "
|
||||
"exp\n", tmp_byte, blocks_used,
|
||||
exp_blocks_used);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
dir_byte += 4;
|
||||
if(dir_byte >= (max_block * 0x200L)) {
|
||||
printf(" invalid link pointer %07x\n", dir_byte);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dynapro_validate_disk(Disk *dsk)
|
||||
{
|
||||
byte freeblks[65536/8]; // 8KB
|
||||
byte *bptr;
|
||||
word32 num_blocks, ret;
|
||||
word32 ui;
|
||||
|
||||
num_blocks = (word32)(dsk->dimage_size >> 9);
|
||||
printf("******************************\n");
|
||||
printf("Validate disk: %s, blocks:%05x\n", dsk->name_ptr, num_blocks);
|
||||
dynapro_validate_init_freeblks(&freeblks[0], num_blocks);
|
||||
|
||||
// Validate starting at directory in block 2
|
||||
ret = dynapro_validate_dir(dsk, &freeblks[0], 0x0404, 0, 4);
|
||||
if(!ret) {
|
||||
printf("Disk does not validate!\n");
|
||||
exit(1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Check freeblks
|
||||
bptr = &(dsk->raw_data[6*0x200]);
|
||||
for(ui = 0; ui < (num_blocks + 7)/8; ui++) {
|
||||
if(freeblks[ui] != bptr[ui]) {
|
||||
printf("Expected free mask for blocks %04x-%04x:%02x, "
|
||||
"but it is %02x\n", ui*8, ui*8 + 7,
|
||||
freeblks[ui], bptr[ui]);
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
dynapro_validate_any_image(Disk *dsk)
|
||||
{
|
||||
byte *bufptr;
|
||||
dword64 dsize;
|
||||
int ret;
|
||||
|
||||
// If dsk->raw_data already set, just use it. Otherwise, we need to
|
||||
// temporarily read in entire image, set it, do validate, and then
|
||||
// free it
|
||||
|
||||
if(dsk->fd < 0) {
|
||||
return; // No disk
|
||||
}
|
||||
if(dsk->wozinfo_ptr) {
|
||||
return;
|
||||
}
|
||||
dsize = dsk->dimage_size;
|
||||
bufptr = 0;
|
||||
if((dsize >> 31) != 0) {
|
||||
printf("Disk is too large, not valid\n");
|
||||
ret = 0;
|
||||
} else if(dsk->raw_data == 0) {
|
||||
bufptr = malloc((size_t)dsize);
|
||||
dsk->raw_data = bufptr;
|
||||
cfg_read_from_fd(dsk->fd, bufptr, 0, dsize);
|
||||
ret = dynapro_validate_disk(dsk);
|
||||
dsk->raw_data = 0;
|
||||
free(bufptr);
|
||||
} else {
|
||||
ret = dynapro_validate_disk(dsk);
|
||||
}
|
||||
printf("validate_disk returned is_good: %d (0 is bad)\n", ret);
|
||||
}
|
||||
|
||||
2302
gsplus/src/dynapro.c
Normal file
2302
gsplus/src/dynapro.c
Normal file
File diff suppressed because it is too large
Load Diff
84
gsplus/src/engine.h
Normal file
84
gsplus/src/engine.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// "@(#)$KmKId: engine.h,v 1.9 2023-09-11 12:55:16+00 kentd Exp $"
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
int
|
||||
ENGINE_TYPE (Engine_reg *engine_ptr)
|
||||
{
|
||||
register byte *ptr;
|
||||
byte *arg_ptr;
|
||||
Pc_log *tmp_pc_ptr;
|
||||
Fplus *fplus_ptr;
|
||||
byte *stat;
|
||||
dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;
|
||||
register word32 kpc, acc, xreg, yreg, direct, psr, zero, neg7, addr;
|
||||
word32 wstat, arg, stack, dbank, opcode, addr_latch, tmp1, tmp2;
|
||||
word32 getmem_tmp, save_addr, pull_tmp, tmp_bytes, dummy1;
|
||||
|
||||
tmp_pc_ptr = 0;
|
||||
dummy1 = 0;
|
||||
if(tmp_pc_ptr || dummy1) { // "use" tmp_pc_ptr to avoid warning
|
||||
}
|
||||
|
||||
kpc = engine_ptr->kpc;
|
||||
acc = engine_ptr->acc;
|
||||
xreg = engine_ptr->xreg;
|
||||
yreg = engine_ptr->yreg;
|
||||
stack = engine_ptr->stack;
|
||||
dbank = engine_ptr->dbank;
|
||||
direct = engine_ptr->direct;
|
||||
psr = engine_ptr->psr;
|
||||
fplus_ptr = engine_ptr->fplus_ptr;
|
||||
zero = !(psr & 2);
|
||||
neg7 = psr;
|
||||
|
||||
dplus_1 = fplus_ptr->dplus_1;
|
||||
dplus_x_m1 = fplus_ptr->dplus_x_minus_1;
|
||||
dfcyc = engine_ptr->dfcyc;
|
||||
|
||||
g_ret1 = 0;
|
||||
|
||||
while(dfcyc <= g_dcycles_end) {
|
||||
|
||||
FETCH_OPCODE;
|
||||
|
||||
LOG_PC_MACRO();
|
||||
|
||||
switch(opcode) {
|
||||
default:
|
||||
halt_printf("acc8 unk op: %02x\n", opcode);
|
||||
arg = 9
|
||||
#include "defs_instr.h"
|
||||
* 2;
|
||||
break;
|
||||
#include "instable.h"
|
||||
break;
|
||||
}
|
||||
LOG_PC_MACRO2();
|
||||
}
|
||||
|
||||
engine_ptr->kpc = kpc;
|
||||
engine_ptr->acc = acc;
|
||||
engine_ptr->xreg = xreg;
|
||||
engine_ptr->yreg = yreg;
|
||||
engine_ptr->stack = stack;
|
||||
engine_ptr->dbank = dbank;
|
||||
engine_ptr->direct = direct;
|
||||
engine_ptr->dfcyc = dfcyc;
|
||||
|
||||
psr = psr & (~0x82);
|
||||
psr |= (neg7 & 0x80);
|
||||
psr |= ((!zero) << 1);
|
||||
|
||||
engine_ptr->psr = psr;
|
||||
|
||||
return g_ret1;
|
||||
}
|
||||
958
gsplus/src/engine_c.c
Normal file
958
gsplus/src/engine_c.c
Normal file
@@ -0,0 +1,958 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2025 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
// PSR[8:0] is E_NVMX_DIZC
|
||||
|
||||
extern int g_limit_speed;
|
||||
extern int g_halt_sim;
|
||||
extern int g_engine_recalc_event;
|
||||
extern int g_code_red;
|
||||
extern int g_ignore_halts;
|
||||
extern int g_user_halt_bad;
|
||||
extern dword64 g_dcycles_end;
|
||||
extern dword64 g_last_vbl_dfcyc;
|
||||
extern dword64 g_cur_dfcyc;
|
||||
extern int g_wait_pending;
|
||||
extern int g_irq_pending;
|
||||
extern int g_num_brk;
|
||||
extern int g_num_cop;
|
||||
extern int g_emul_6502_ind_page_cross_bug;
|
||||
extern byte *g_slow_memory_ptr;
|
||||
extern byte *g_memory_ptr;
|
||||
extern byte *g_rom_fc_ff_ptr;
|
||||
extern byte *g_rom_cards_ptr;
|
||||
extern byte *g_dummy_memory1_ptr;
|
||||
|
||||
extern int g_num_breakpoints;
|
||||
extern Break_point g_break_pts[];
|
||||
|
||||
extern Kimage g_debugwin_kimage;
|
||||
|
||||
extern word32 g_log_pc_enable;
|
||||
extern Pc_log *g_log_pc_ptr;
|
||||
extern Pc_log *g_log_pc_start_ptr;
|
||||
extern Pc_log *g_log_pc_end_ptr;
|
||||
|
||||
extern Data_log *g_log_data_ptr;
|
||||
extern Data_log *g_log_data_start_ptr;
|
||||
extern Data_log *g_log_data_end_ptr;
|
||||
|
||||
int g_ret1 = 0;
|
||||
|
||||
int size_tab[] = {
|
||||
#include "size_c.h"
|
||||
};
|
||||
|
||||
int bogus[] = {
|
||||
0,
|
||||
#include "op_routs.h"
|
||||
};
|
||||
|
||||
#define INC_KPC_1 kpc = (kpc & 0xff0000) + ((kpc + 1) & 0xffff);
|
||||
#define INC_KPC_2 kpc = (kpc & 0xff0000) + ((kpc + 2) & 0xffff);
|
||||
#define INC_KPC_3 kpc = (kpc & 0xff0000) + ((kpc + 3) & 0xffff);
|
||||
#define INC_KPC_4 kpc = (kpc & 0xff0000) + ((kpc + 4) & 0xffff);
|
||||
|
||||
#define CYCLES_PLUS_1 dfcyc += dplus_1;
|
||||
#define CYCLES_PLUS_2 dfcyc += dplus_1 * 2;
|
||||
#define CYCLES_PLUS_3 dfcyc += dplus_1 * 3;
|
||||
#define CYCLES_PLUS_4 dfcyc += dplus_1 * 4;
|
||||
#define CYCLES_PLUS_5 dfcyc += dplus_1 * 5;
|
||||
#define CYCLES_MINUS_1 dfcyc -= dplus_1;
|
||||
#define CYCLES_MINUS_2 dfcyc -= dplus_1 * 2;
|
||||
|
||||
#define FCYCLES_ROUND dfcyc = dfcyc + dplus_x_m1; \
|
||||
dfcyc = (dfcyc >> 16) << 16;
|
||||
|
||||
|
||||
#define GET_1BYTE_ARG arg = arg_ptr[1];
|
||||
#define GET_2BYTE_ARG arg = arg_ptr[1] + (arg_ptr[2] << 8);
|
||||
#define GET_3BYTE_ARG arg = arg_ptr[1] + (arg_ptr[2] << 8) + (arg_ptr[3]<<16);
|
||||
|
||||
#define LOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat) \
|
||||
g_log_data_ptr->dfcyc = dfcyc; \
|
||||
g_log_data_ptr->stat = in_stat; \
|
||||
g_log_data_ptr->addr = in_addr; \
|
||||
g_log_data_ptr->val = in_val; \
|
||||
g_log_data_ptr->size = in_size; \
|
||||
g_log_data_ptr++; \
|
||||
if(g_log_data_ptr >= g_log_data_end_ptr) { \
|
||||
g_log_data_ptr = g_log_data_start_ptr; \
|
||||
}
|
||||
|
||||
/* HACK HACK HACK */
|
||||
#define UPDATE_PSR(dummy, old_psr) \
|
||||
if(psr & 0x100) { \
|
||||
psr |= 0x30; \
|
||||
stack = 0x100 + (stack & 0xff); \
|
||||
} \
|
||||
if((~old_psr & psr) & 0x10) { \
|
||||
xreg = xreg & 0xff; \
|
||||
yreg = yreg & 0xff; \
|
||||
} \
|
||||
if(((psr & 4) == 0) && g_irq_pending) { \
|
||||
FINISH(RET_IRQ, 0); \
|
||||
} \
|
||||
if((old_psr ^ psr) & 0x20) { \
|
||||
FINISH(RET_PSR, 0); \
|
||||
}
|
||||
|
||||
extern Page_info page_info_rd_wr[];
|
||||
extern word32 g_slow_mem_changed[];
|
||||
|
||||
#define GET_MEMORY8(addr,dest) \
|
||||
addr_latch = (addr); \
|
||||
CYCLES_PLUS_1; \
|
||||
stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
if(wstat & (1 << (31 - BANK_IO_BIT))) { \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
dest = get_memory8_io_stub((addr), stat, \
|
||||
&dcycles_tmp1, dplus_x_m1); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
dest = *ptr; \
|
||||
}
|
||||
|
||||
#define GET_MEMORY(addr,dest) GET_MEMORY8(addr, dest)
|
||||
|
||||
#define GET_MEMORY16(addr, dest, in_bank) \
|
||||
save_addr = addr; \
|
||||
stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
dest = get_memory16_pieces_stub((addr), stat, \
|
||||
&dcycles_tmp1, fplus_ptr, in_bank); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
CYCLES_PLUS_2; \
|
||||
dest = ptr[0] + (ptr[1] << 8); \
|
||||
} \
|
||||
addr_latch = save_addr;
|
||||
|
||||
#define GET_MEMORY24(addr, dest, in_bank) \
|
||||
save_addr = addr; \
|
||||
stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
dest = get_memory24_pieces_stub((addr), stat, \
|
||||
&dcycles_tmp1, fplus_ptr, in_bank); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
CYCLES_PLUS_3; \
|
||||
dest = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16); \
|
||||
} \
|
||||
addr_latch = save_addr;
|
||||
|
||||
#define GET_MEMORY_DIRECT_PAGE16(addr, dest, dloc_x_wrap) \
|
||||
save_addr = addr; \
|
||||
if(psr & 0x100) { \
|
||||
if((direct & 0xff) == 0) { \
|
||||
save_addr = (save_addr & 0xff) + direct; \
|
||||
} \
|
||||
} \
|
||||
if((psr & 0x100) && (((addr) & 0xff) == 0xff)) { \
|
||||
GET_MEMORY8(save_addr, getmem_tmp); \
|
||||
if(dloc_x_wrap) { \
|
||||
save_addr = (save_addr & 0xff00) | \
|
||||
((save_addr + 1) & 0xff); \
|
||||
} else { \
|
||||
save_addr = (save_addr + 1) & 0xffff; \
|
||||
} \
|
||||
if((direct & 0xff) == 0) { \
|
||||
save_addr = (save_addr & 0xff) + direct; \
|
||||
} \
|
||||
GET_MEMORY8(save_addr, dest); \
|
||||
dest = (dest << 8) + getmem_tmp; \
|
||||
} else { \
|
||||
GET_MEMORY16(save_addr, dest, 1); \
|
||||
}
|
||||
|
||||
|
||||
#define PUSH8(arg) \
|
||||
SET_MEMORY8(stack, arg); \
|
||||
stack = (stack - 1) & 0xffff; \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
}
|
||||
|
||||
#define PUSH16(arg) \
|
||||
if((stack & 0xfe) == 0) { \
|
||||
/* stack will cross page! */ \
|
||||
PUSH8((arg) >> 8); \
|
||||
PUSH8(arg); \
|
||||
} else { \
|
||||
stack = (stack - 2) & 0xffff; \
|
||||
SET_MEMORY16(stack + 1, arg, 1); \
|
||||
}
|
||||
|
||||
#define PUSH16_UNSAFE(arg) \
|
||||
save_addr = (stack - 1) & 0xffff; \
|
||||
stack = (stack - 2) & 0xffff; \
|
||||
SET_MEMORY16(save_addr, arg, 1); \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
}
|
||||
|
||||
#define PUSH24_UNSAFE(arg) \
|
||||
save_addr = (stack - 2) & 0xffff; \
|
||||
stack = (stack - 3) & 0xffff; \
|
||||
SET_MEMORY24(save_addr, arg, 1); \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
}
|
||||
|
||||
#define PULL8(dest) \
|
||||
stack++; \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
} \
|
||||
stack = stack & 0xffff; \
|
||||
GET_MEMORY8(stack, dest);
|
||||
|
||||
#define PULL8_UNSAFE(dest) \
|
||||
stack = (stack + 1) & 0xffff; \
|
||||
GET_MEMORY8(stack, dest); \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
}
|
||||
|
||||
#define PULL16(dest) \
|
||||
if((stack & 0xfe) == 0xfe) { /* page cross */ \
|
||||
PULL8(dest); \
|
||||
PULL8(pull_tmp); \
|
||||
dest = (pull_tmp << 8) + dest; \
|
||||
} else { \
|
||||
GET_MEMORY16(stack + 1, dest, 1); \
|
||||
stack = (stack + 2) & 0xffff; \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PULL16_UNSAFE(dest) \
|
||||
stack = (stack + 1) & 0xffff; \
|
||||
GET_MEMORY16(stack, dest, 1); \
|
||||
stack = (stack + 1) & 0xffff; \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
}
|
||||
|
||||
#define PULL24(dest) \
|
||||
if((stack & 0xfc) == 0xfc) { /* page cross */ \
|
||||
PULL8(dest); \
|
||||
PULL8(pull_tmp); \
|
||||
pull_tmp = (pull_tmp << 8) + dest; \
|
||||
PULL8(dest); \
|
||||
dest = (dest << 16) + pull_tmp; \
|
||||
} else { \
|
||||
GET_MEMORY24(stack + 1, dest, 1); \
|
||||
stack = (stack + 3) & 0xffff; \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PULL24_UNSAFE(dest) \
|
||||
stack = (stack + 1) & 0xffff; \
|
||||
GET_MEMORY24(stack, dest, 1); \
|
||||
stack = (stack + 2) & 0xffff; \
|
||||
if(psr & 0x100) { \
|
||||
stack = 0x100 | (stack & 0xff); \
|
||||
}
|
||||
|
||||
#define SET_MEMORY8(addr, val) \
|
||||
stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \
|
||||
LOG_DATA_MACRO(addr, val, 8, stat); \
|
||||
CYCLES_PLUS_1; \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
if(wstat) { \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
set_memory8_io_stub((addr), val, stat, &dcycles_tmp1, \
|
||||
dplus_x_m1); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
*ptr = val; \
|
||||
}
|
||||
|
||||
|
||||
#define SET_MEMORY16(addr, val, in_bank) \
|
||||
stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \
|
||||
LOG_DATA_MACRO(addr, val, 16, stat); \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
if((wstat) || (((addr) & 0xff) == 0xff)) { \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
set_memory16_pieces_stub((addr), (val), \
|
||||
&dcycles_tmp1, dplus_1, dplus_x_m1, in_bank); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
CYCLES_PLUS_2; \
|
||||
ptr[0] = (val); \
|
||||
ptr[1] = (val) >> 8; \
|
||||
}
|
||||
|
||||
#define SET_MEMORY24(addr, val, in_bank) \
|
||||
stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \
|
||||
LOG_DATA_MACRO(addr, val, 24, stat); \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
if((wstat) || (((addr) & 0xfe) == 0xfe)) { \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
set_memory24_pieces_stub((addr), (val), \
|
||||
&dcycles_tmp1, fplus_ptr, in_bank); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
CYCLES_PLUS_3; \
|
||||
ptr[0] = (val); \
|
||||
ptr[1] = (val) >> 8; \
|
||||
ptr[2] = (val) >> 16; \
|
||||
}
|
||||
|
||||
|
||||
word32
|
||||
get_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,
|
||||
dword64 dplus_x_m1)
|
||||
{
|
||||
dword64 dfcyc;
|
||||
word32 wstat;
|
||||
byte *ptr;
|
||||
|
||||
wstat = PTR2WORD(stat) & 0xff;
|
||||
dfcyc = *dcycs_ptr;
|
||||
if(wstat & BANK_BREAK) {
|
||||
check_breakpoints(addr, dfcyc, 0, 1);
|
||||
}
|
||||
if(wstat & BANK_IO2_TMP) {
|
||||
FCYCLES_ROUND;
|
||||
*dcycs_ptr = dfcyc;
|
||||
return get_memory_io((addr), dcycs_ptr);
|
||||
} else {
|
||||
ptr = stat - wstat + (addr & 0xff);
|
||||
return *ptr;
|
||||
}
|
||||
}
|
||||
|
||||
word32
|
||||
get_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,
|
||||
Fplus *fplus_ptr, int in_bank)
|
||||
{
|
||||
byte *ptr;
|
||||
dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;
|
||||
word32 addrp1, wstat, ret, tmp1, addr_latch;
|
||||
|
||||
addr_latch = 0;
|
||||
if(addr_latch != 0) { // "Use" addr_latch to avoid warning
|
||||
}
|
||||
dfcyc = *dcycs_ptr;
|
||||
dplus_1 = fplus_ptr->dplus_1;
|
||||
dplus_x_m1 = fplus_ptr->dplus_x_minus_1;
|
||||
GET_MEMORY8(addr, tmp1);
|
||||
addrp1 = addr + 1;
|
||||
if(in_bank) {
|
||||
addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
|
||||
}
|
||||
GET_MEMORY8(addrp1, ret);
|
||||
*dcycs_ptr = dfcyc;
|
||||
return (ret << 8) + (tmp1);
|
||||
}
|
||||
|
||||
word32
|
||||
get_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,
|
||||
Fplus *fplus_ptr, int in_bank)
|
||||
{
|
||||
byte *ptr;
|
||||
dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;
|
||||
word32 addrp1, addrp2, wstat, addr_latch, ret, tmp1, tmp2;
|
||||
|
||||
addr_latch = 0;
|
||||
if(addr_latch != 0) { // "Use" addr_latch to avoid warning
|
||||
}
|
||||
dfcyc = *dcycs_ptr;
|
||||
dplus_1 = fplus_ptr->dplus_1;
|
||||
dplus_x_m1 = fplus_ptr->dplus_x_minus_1;
|
||||
GET_MEMORY8(addr, tmp1);
|
||||
addrp1 = addr + 1;
|
||||
if(in_bank) {
|
||||
addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
|
||||
}
|
||||
GET_MEMORY8(addrp1, tmp2);
|
||||
addrp2 = addr + 2;
|
||||
if(in_bank) {
|
||||
addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);
|
||||
}
|
||||
GET_MEMORY8(addrp2, ret);
|
||||
*dcycs_ptr = dfcyc;
|
||||
return (ret << 16) + (tmp2 << 8) + tmp1;
|
||||
}
|
||||
|
||||
void
|
||||
set_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr,
|
||||
dword64 dplus_x_m1)
|
||||
{
|
||||
byte *ptr;
|
||||
dword64 dfcyc;
|
||||
word32 setmem_tmp1, tmp1, tmp2, wstat;
|
||||
|
||||
wstat = PTR2WORD(stat) & 0xff;
|
||||
dfcyc = *dcycs_ptr;
|
||||
if(wstat & (1 << (31 - BANK_BREAK_BIT))) {
|
||||
check_breakpoints(addr, dfcyc, 0, 2);
|
||||
}
|
||||
ptr = stat - wstat + ((addr) & 0xff);
|
||||
if(wstat & (1 << (31 - BANK_IO2_BIT))) {
|
||||
FCYCLES_ROUND;
|
||||
*dcycs_ptr = dfcyc;
|
||||
set_memory_io((addr), val, dcycs_ptr);
|
||||
} else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) {
|
||||
if(g_limit_speed) {
|
||||
FCYCLES_ROUND;
|
||||
*dcycs_ptr = dfcyc;
|
||||
}
|
||||
tmp1 = (addr & 0xffff);
|
||||
setmem_tmp1 = g_slow_memory_ptr[tmp1];
|
||||
*ptr = val;
|
||||
g_slow_memory_ptr[tmp1] = val;
|
||||
if(setmem_tmp1 != ((val) & 0xff)) {
|
||||
g_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=
|
||||
(1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31));
|
||||
}
|
||||
} else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) {
|
||||
if(g_limit_speed) {
|
||||
FCYCLES_ROUND;
|
||||
*dcycs_ptr = dfcyc;
|
||||
}
|
||||
tmp2 = (addr & 0xffff);
|
||||
tmp1 = 0x10000 + tmp2;
|
||||
setmem_tmp1 = g_slow_memory_ptr[tmp1];
|
||||
*ptr = val;
|
||||
g_slow_memory_ptr[tmp1] = val;
|
||||
if(setmem_tmp1 != ((val) & 0xff)) {
|
||||
g_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=
|
||||
(1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31));
|
||||
if((tmp1 & 0xff00) == 0x9d00) {
|
||||
scb_changed(dfcyc, tmp1, val, setmem_tmp1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* breakpoint only */
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
|
||||
#define LOG_PC_MACRO()
|
||||
#define LOG_PC_MACRO2()
|
||||
#define LOG_DATA_MACRO(addr, val, size, in_stat)
|
||||
|
||||
void
|
||||
set_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr,
|
||||
dword64 dplus_1, dword64 dplus_x_m1, int in_bank)
|
||||
{
|
||||
byte *ptr;
|
||||
byte *stat;
|
||||
dword64 dfcyc, dcycles_tmp1;
|
||||
word32 addrp1, wstat;
|
||||
|
||||
dfcyc = *dcycs_ptr;
|
||||
SET_MEMORY8(addr, val);
|
||||
addrp1 = addr + 1;
|
||||
if(in_bank) {
|
||||
addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
|
||||
}
|
||||
SET_MEMORY8(addrp1, val >> 8);
|
||||
|
||||
*dcycs_ptr = dfcyc;
|
||||
}
|
||||
|
||||
void
|
||||
set_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr,
|
||||
Fplus *fplus_ptr, int in_bank)
|
||||
{
|
||||
byte *ptr;
|
||||
byte *stat;
|
||||
dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;
|
||||
word32 addrp1, addrp2;
|
||||
word32 wstat;
|
||||
|
||||
dfcyc = *dcycs_ptr;
|
||||
dplus_1 = fplus_ptr->dplus_1;
|
||||
dplus_x_m1 = fplus_ptr->dplus_x_minus_1;
|
||||
SET_MEMORY8(addr, val);
|
||||
addrp1 = addr + 1;
|
||||
if(in_bank) {
|
||||
addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
|
||||
}
|
||||
SET_MEMORY8(addrp1, val >> 8);
|
||||
addrp2 = addr + 2;
|
||||
if(in_bank) {
|
||||
addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);
|
||||
}
|
||||
SET_MEMORY8(addrp2, val >> 16);
|
||||
|
||||
*dcycs_ptr = dfcyc;
|
||||
}
|
||||
|
||||
word32
|
||||
get_memory_c(word32 addr)
|
||||
{
|
||||
byte *stat, *ptr;
|
||||
dword64 dfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;
|
||||
word32 addr_latch, wstat, ret;
|
||||
|
||||
dfcyc = 0;
|
||||
dplus_1 = 0;
|
||||
dplus_x_m1 = 0;
|
||||
addr_latch = 0;
|
||||
if(addr_latch != 0) { // "Use" addr_latch to avoid warning
|
||||
}
|
||||
GET_MEMORY8(addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
word32
|
||||
get_memory16_c(word32 addr)
|
||||
{
|
||||
return get_memory_c(addr) +
|
||||
(get_memory_c(addr+1) << 8);
|
||||
}
|
||||
|
||||
word32
|
||||
get_memory24_c(word32 addr)
|
||||
{
|
||||
return get_memory_c(addr) +
|
||||
(get_memory_c(addr+1) << 8) +
|
||||
(get_memory_c(addr+2) << 16);
|
||||
}
|
||||
|
||||
void
|
||||
set_memory_c(word32 addr, word32 val, int do_log)
|
||||
{
|
||||
byte *stat, *ptr;
|
||||
dword64 dfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;
|
||||
word32 wstat;
|
||||
|
||||
dfcyc = g_cur_dfcyc;
|
||||
dplus_1 = 0;
|
||||
dplus_x_m1 = 0;
|
||||
SET_MEMORY8(addr, val);
|
||||
if(g_log_pc_enable && do_log) {
|
||||
LOG_DATA_MACRO_ACT(addr, val, 8, stat)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_memory16_c(word32 addr, word32 val, int do_log)
|
||||
{
|
||||
byte *stat, *ptr;
|
||||
dword64 dfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;
|
||||
word32 wstat;
|
||||
|
||||
dfcyc = g_cur_dfcyc;
|
||||
dplus_1 = 0;
|
||||
dplus_x_m1 = 0;
|
||||
SET_MEMORY16(addr, val, 0);
|
||||
if(g_log_pc_enable && do_log) {
|
||||
LOG_DATA_MACRO_ACT(addr, val, 16, stat)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_memory24_c(word32 addr, word32 val)
|
||||
{
|
||||
set_memory_c(addr, val, 1);
|
||||
set_memory_c(addr + 1, val >> 8, 1);
|
||||
set_memory_c(addr + 2, val >> 16, 1);
|
||||
}
|
||||
|
||||
word32
|
||||
do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub)
|
||||
{
|
||||
word32 sum, carry, overflow;
|
||||
word32 zero;
|
||||
int decimal;
|
||||
|
||||
overflow = 0;
|
||||
decimal = psr & 8;
|
||||
if(sub) {
|
||||
in2 = (in2 ^ 0xff);
|
||||
}
|
||||
if(!decimal) {
|
||||
sum = (in1 & 0xff) + in2 + (psr & 1);
|
||||
overflow = ((sum ^ in2) >> 1) & 0x40;
|
||||
} else {
|
||||
/* decimal */
|
||||
sum = (in1 & 0xf) + (in2 & 0xf) + (psr & 1);
|
||||
if(sub) {
|
||||
if(sum < 0x10) {
|
||||
sum = (sum - 0x6) & 0xf;
|
||||
}
|
||||
} else {
|
||||
if(sum >= 0xa) {
|
||||
sum = (sum - 0xa) | 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
sum = (in1 & 0xf0) + (in2 & 0xf0) + sum;
|
||||
overflow = ((sum >> 2) ^ (sum >> 1)) & 0x40;
|
||||
if(sub) {
|
||||
if(sum < 0x100) {
|
||||
sum = (sum + 0xa0) & 0xff;
|
||||
}
|
||||
} else {
|
||||
if(sum >= 0xa0) {
|
||||
sum += 0x60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zero = ((sum & 0xff) == 0);
|
||||
carry = (sum >= 0x100);
|
||||
if((in1 ^ in2) & 0x80) {
|
||||
overflow = 0;
|
||||
}
|
||||
|
||||
psr = psr & (~0xc3);
|
||||
psr = psr + (sum & 0x80) + overflow + (zero << 1) + carry;
|
||||
|
||||
return (psr << 16) + (sum & 0xff);
|
||||
}
|
||||
|
||||
word32
|
||||
do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub)
|
||||
{
|
||||
word32 sum, carry, overflow;
|
||||
word32 tmp1, tmp2;
|
||||
word32 zero;
|
||||
int decimal;
|
||||
|
||||
overflow = 0;
|
||||
decimal = psr & 8;
|
||||
if(!decimal) {
|
||||
if(sub) {
|
||||
in2 = (in2 ^ 0xffff);
|
||||
}
|
||||
sum = in1 + in2 + (psr & 1);
|
||||
overflow = ((sum ^ in2) >> 9) & 0x40;
|
||||
} else {
|
||||
/* decimal */
|
||||
if(sub) {
|
||||
tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);
|
||||
psr = (tmp1 >> 16);
|
||||
tmp2 = do_adc_sbc8((in1 >> 8) & 0xff,
|
||||
(in2 >> 8) & 0xff, psr, sub);
|
||||
in2 = (in2 ^ 0xfffff);
|
||||
} else {
|
||||
tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);
|
||||
psr = (tmp1 >> 16);
|
||||
tmp2 = do_adc_sbc8((in1 >> 8) & 0xff,
|
||||
(in2 >> 8) &0xff, psr, sub);
|
||||
}
|
||||
sum = ((tmp2 & 0xff) << 8) + (tmp1 & 0xff) +
|
||||
(((tmp2 >> 16) & 1) << 16);
|
||||
overflow = (tmp2 >> 16) & 0x40;
|
||||
}
|
||||
|
||||
zero = ((sum & 0xffff) == 0);
|
||||
carry = (sum >= 0x10000);
|
||||
if((in1 ^ in2) & 0x8000) {
|
||||
overflow = 0;
|
||||
}
|
||||
|
||||
psr = psr & (~0xc3);
|
||||
psr = psr + ((sum & 0x8000) >> 8) + overflow + (zero << 1) + carry;
|
||||
|
||||
return (psr << 16) + (sum & 0xffff);
|
||||
}
|
||||
|
||||
void
|
||||
fixed_memory_ptrs_init()
|
||||
{
|
||||
/* set g_slow_memory_ptr, g_rom_fc_ff_ptr, g_dummy_memory1_ptr, */
|
||||
/* and rom_cards_ptr */
|
||||
|
||||
g_slow_memory_ptr = memalloc_align(128*1024, 0, 0);
|
||||
g_dummy_memory1_ptr = memalloc_align(256, 1024, 0);
|
||||
g_rom_fc_ff_ptr = memalloc_align(256*1024, 512, 0);
|
||||
g_rom_cards_ptr = memalloc_align(16*256, 256, 0);
|
||||
|
||||
#if 0
|
||||
printf("g_memory_ptr: %08x, dummy_mem: %08x, slow_mem_ptr: %08x\n",
|
||||
(word32)g_memory_ptr, (word32)g_dummy_memory1_ptr,
|
||||
(word32)g_slow_memory_ptr);
|
||||
printf("g_rom_fc_ff_ptr: %08x, g_rom_cards_ptr: %08x\n",
|
||||
(word32)g_rom_fc_ff_ptr, (word32)g_rom_cards_ptr);
|
||||
printf("page_info_rd = %08x, page_info_wr end = %08x\n",
|
||||
(word32)&(page_info_rd_wr[0]),
|
||||
(word32)&(page_info_rd_wr[PAGE_INFO_PAD_SIZE+0x1ffff].rd_wr));
|
||||
#endif
|
||||
}
|
||||
|
||||
word32
|
||||
get_itimer()
|
||||
{
|
||||
#if defined(__i386) && defined(__GNUC__)
|
||||
/* Here's my bad ia32 asm code to do rdtsc */
|
||||
/* Linux source uses: */
|
||||
/* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */
|
||||
/* asm volatile("rdtsc" : "=%eax"(ret) : : "%edx"); */
|
||||
|
||||
/* GCC bug report 2001-03/msg00786.html used: */
|
||||
/*register dword64 dtmp; */
|
||||
/*asm volatile ("rdtsc" : "=A" (dtmp)); */
|
||||
/*return (word32)dtmp; */
|
||||
|
||||
register word32 ret;
|
||||
|
||||
asm volatile ("rdtsc;movl %%eax,%0" : "=r"(ret) : : "%eax","%edx");
|
||||
|
||||
return ret;
|
||||
#else
|
||||
# if defined(__POWERPC__) && defined(__GNUC__)
|
||||
register word32 ret;
|
||||
|
||||
asm volatile ("mftb %0" : "=r"(ret));
|
||||
return ret;
|
||||
# else
|
||||
# if defined(__x86_64__)
|
||||
register word32 ret, hi;
|
||||
//ret = __rdtsc();
|
||||
asm volatile ("rdtsc" : "=a" (ret), "=d" (hi));
|
||||
return ret;
|
||||
# else
|
||||
# if defined(__aarch64__) /* 64-bit ARM architecture */
|
||||
register dword64 ret;
|
||||
asm volatile("mrs %0,CNTVCT_EL0" : "=r"(ret));
|
||||
return ret;
|
||||
# else
|
||||
return 0;
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
engine_recalc_events()
|
||||
{
|
||||
g_dcycles_end = 0; // End inner loop
|
||||
g_engine_recalc_event++;
|
||||
}
|
||||
|
||||
void
|
||||
set_halt_act(int val)
|
||||
{
|
||||
if((val == 1) && g_ignore_halts && !g_user_halt_bad) {
|
||||
g_code_red++;
|
||||
} else {
|
||||
if(g_halt_sim == 0) {
|
||||
debugger_update_list_kpc();
|
||||
}
|
||||
g_halt_sim |= val;
|
||||
if(g_halt_sim) {
|
||||
video_set_active(&g_debugwin_kimage, 1);
|
||||
}
|
||||
g_dcycles_end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
clr_halt_act()
|
||||
{
|
||||
g_halt_sim = 0;
|
||||
}
|
||||
|
||||
word32
|
||||
get_remaining_operands(word32 addr, word32 opcode, word32 psr,
|
||||
dword64 *dcyc_ptr, Fplus *fplus_ptr)
|
||||
{
|
||||
byte *stat, *ptr;
|
||||
dword64 dfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;
|
||||
word32 addr_latch, wstat, save_addr, arg, addrp1;
|
||||
int size;
|
||||
|
||||
dfcyc = *dcyc_ptr;
|
||||
dplus_1 = fplus_ptr->dplus_1;
|
||||
dplus_x_m1 = fplus_ptr->dplus_x_minus_1;
|
||||
addr_latch = 0;
|
||||
if(addr_latch != 0) { // "Use" addr_latch to avoid warning
|
||||
}
|
||||
|
||||
size = size_tab[opcode];
|
||||
|
||||
addrp1 = (addr & 0xff0000) + ((addr + 1) & 0xffff);
|
||||
switch(size) {
|
||||
case 0:
|
||||
// Always read pc+1 for single-byte opcodes
|
||||
GET_MEMORY8(addrp1, arg);
|
||||
arg = 0; /* no args */
|
||||
break;
|
||||
case 1:
|
||||
GET_MEMORY8(addrp1, arg);
|
||||
break; /* 1 arg, already done */
|
||||
case 2:
|
||||
GET_MEMORY16(addrp1, arg, 1);
|
||||
dfcyc -= dplus_1;
|
||||
break;
|
||||
case 3:
|
||||
GET_MEMORY24(addrp1, arg, 1);
|
||||
dfcyc = dfcyc - dplus_1 - dplus_1;
|
||||
break;
|
||||
case 4:
|
||||
if(psr & 0x20) {
|
||||
GET_MEMORY8(addrp1, arg);
|
||||
} else {
|
||||
GET_MEMORY16(addrp1, arg, 1);
|
||||
dfcyc -= dplus_1;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if(psr & 0x10) {
|
||||
GET_MEMORY8(addrp1, arg);
|
||||
} else {
|
||||
GET_MEMORY16(addrp1, arg, 1);
|
||||
dfcyc -= dplus_1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Unknown size: %d\n", size);
|
||||
arg = 0;
|
||||
exit(-2);
|
||||
}
|
||||
*dcyc_ptr = dfcyc;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
#define FETCH_OPCODE \
|
||||
addr = kpc; \
|
||||
CYCLES_PLUS_2; \
|
||||
stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \
|
||||
wstat = PTR2WORD(stat) & 0xff; \
|
||||
ptr = stat - wstat + ((addr) & 0xff); \
|
||||
arg_ptr = ptr; \
|
||||
opcode = *ptr; \
|
||||
if((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) {\
|
||||
CYCLES_MINUS_1; \
|
||||
if(wstat & BANK_BREAK) { \
|
||||
check_breakpoints(addr, dfcyc, stack, 4); \
|
||||
} \
|
||||
if(wstat & (1 << (31 - BANK_IO2_BIT))) { \
|
||||
FCYCLES_ROUND; \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
opcode = get_memory_io((addr), &dcycles_tmp1); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
} else { \
|
||||
opcode = *ptr; \
|
||||
} \
|
||||
dcycles_tmp1 = dfcyc; \
|
||||
arg = get_remaining_operands(addr, opcode, psr, \
|
||||
&dcycles_tmp1, fplus_ptr); \
|
||||
dfcyc = dcycles_tmp1; \
|
||||
arg_ptr = (byte *)&tmp_bytes; \
|
||||
arg_ptr[1] = arg; \
|
||||
arg_ptr[2] = arg >> 8; \
|
||||
arg_ptr[3] = arg >> 16; \
|
||||
}
|
||||
|
||||
|
||||
#define ACC8
|
||||
#define IS_ACC16 0
|
||||
#define ENGINE_TYPE enter_engine_acc8
|
||||
#include "engine.h"
|
||||
// The above creates enter_engine_acc8
|
||||
|
||||
#undef ACC8
|
||||
#undef IS_ACC16
|
||||
#undef ENGINE_TYPE
|
||||
#define IS_ACC16 1
|
||||
#define ENGINE_TYPE enter_engine_acc16
|
||||
#include "engine.h"
|
||||
// The above creates enter_engine_acc16
|
||||
|
||||
#undef LOG_PC_MACRO
|
||||
#undef LOG_PC_MACRO2
|
||||
#undef LOG_DATA_MACRO
|
||||
|
||||
#define LOG_PC_MACRO() \
|
||||
tmp_pc_ptr = g_log_pc_ptr; \
|
||||
tmp_pc_ptr->dbank_kpc = (dbank << 24) + kpc; \
|
||||
tmp_pc_ptr->instr = (opcode << 24) + arg_ptr[1] + \
|
||||
(arg_ptr[2] << 8) + (arg_ptr[3] << 16); \
|
||||
tmp_pc_ptr->dfcyc = dfcyc - dplus_1 * 2;
|
||||
|
||||
#define LOG_PC_MACRO2() \
|
||||
tmp_pc_ptr->psr_acc = ((psr & ~(0x82)) << 16) | acc | \
|
||||
((neg7 & 0x80) << 16) | ((!zero) << 17); \
|
||||
tmp_pc_ptr->xreg_yreg = (xreg << 16) + yreg; \
|
||||
tmp_pc_ptr->stack_direct = (stack << 16) + direct; \
|
||||
tmp_pc_ptr++; \
|
||||
if(tmp_pc_ptr >= g_log_pc_end_ptr) { \
|
||||
tmp_pc_ptr = g_log_pc_start_ptr; \
|
||||
} \
|
||||
g_log_pc_ptr = tmp_pc_ptr;
|
||||
|
||||
#define LOG_DATA_MACRO(in_addr, in_val, in_size, in_stat) \
|
||||
LOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat)
|
||||
|
||||
#undef ACC8
|
||||
#undef IS_ACC16
|
||||
#undef ENGINE_TYPE
|
||||
#define ACC8
|
||||
#define IS_ACC16 0
|
||||
#define ENGINE_TYPE enter_engine_acc8_log
|
||||
#include "engine.h"
|
||||
// The above creates enter_engine_acc8_log
|
||||
|
||||
#undef ACC8
|
||||
#undef IS_ACC16
|
||||
#undef ENGINE_TYPE
|
||||
#define IS_ACC16 1
|
||||
#define ENGINE_TYPE enter_engine_acc16_log
|
||||
#include "engine.h"
|
||||
// The above creates enter_engine_acc16_log
|
||||
|
||||
|
||||
int
|
||||
enter_engine(Engine_reg *engine_ptr)
|
||||
{
|
||||
dword64 dcycles_end_save;
|
||||
int ret;
|
||||
|
||||
dcycles_end_save = g_dcycles_end;
|
||||
while(1) {
|
||||
if(g_log_pc_enable) {
|
||||
if(engine_ptr->psr & 0x20) { // 8-bit accumulator
|
||||
ret = enter_engine_acc8_log(engine_ptr);
|
||||
} else {
|
||||
ret = enter_engine_acc16_log(engine_ptr);
|
||||
}
|
||||
} else {
|
||||
if(engine_ptr->psr & 0x20) { // 8-bit accumulator
|
||||
ret = enter_engine_acc8(engine_ptr);
|
||||
} else {
|
||||
ret = enter_engine_acc16(engine_ptr);
|
||||
}
|
||||
}
|
||||
if((ret == RET_PSR) && !g_halt_sim) {
|
||||
g_dcycles_end = dcycles_end_save;
|
||||
continue;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
1555
gsplus/src/instable.h
Normal file
1555
gsplus/src/instable.h
Normal file
File diff suppressed because it is too large
Load Diff
3752
gsplus/src/iwm.c
Normal file
3752
gsplus/src/iwm.c
Normal file
File diff suppressed because it is too large
Load Diff
206
gsplus/src/iwm.h
Normal file
206
gsplus/src/iwm.h
Normal file
@@ -0,0 +1,206 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#define MAX_TRACKS (2*80)
|
||||
#define MAX_C7_DISKS 16
|
||||
|
||||
#define NIB_LEN_525 0x18f2 /* 51088 bits per track */
|
||||
// Expected bits per track: (1020484/5)/4 = 51024. A little extra seems good
|
||||
|
||||
#define NIBS_FROM_ADDR_TO_DATA 28
|
||||
// Copy II+ Manual Sector Copy fails if this is 20, so make it 28
|
||||
|
||||
// image_type settings. 0 means unknown type
|
||||
#define DSK_TYPE_PRODOS 1
|
||||
#define DSK_TYPE_DOS33 2
|
||||
#define DSK_TYPE_DYNAPRO 3
|
||||
#define DSK_TYPE_NIB 4
|
||||
#define DSK_TYPE_WOZ 5
|
||||
|
||||
// Note: C031_APPLE35SEL must be 6, C031_CTRL must be 7, MOTOR_ON must be 5!
|
||||
// Q7 needs to be adjacent and higher than Q6
|
||||
// Bits 4:0 are IWM mode register: 0: latch mode; 1: async handshake;
|
||||
// 2: immediate motor off (no 1 sec delay); 3: 2us bit timing;
|
||||
// 4: Divide input clock by 8 (instead of 7)
|
||||
#define IWM_BIT_MOTOR_ON 5
|
||||
#define IWM_BIT_C031_APPLE35SEL 6
|
||||
#define IWM_BIT_C031_CTRL 7
|
||||
#define IWM_BIT_STEP_DIRECTION35 8
|
||||
#define IWM_BIT_MOTOR_ON35 9
|
||||
#define IWM_BIT_MOTOR_OFF 10
|
||||
#define IWM_BIT_DRIVE_SEL 11
|
||||
#define IWM_BIT_Q6 12
|
||||
#define IWM_BIT_Q7 13
|
||||
#define IWM_BIT_ENABLE2 14
|
||||
#define IWM_BIT_LAST_SEL35 15
|
||||
#define IWM_BIT_PHASES 16
|
||||
#define IWM_BIT_RESET 20
|
||||
|
||||
#define IWM_STATE_MOTOR_ON (1 << IWM_BIT_MOTOR_ON)
|
||||
#define IWM_STATE_C031_APPLE35SEL (1 << IWM_BIT_C031_APPLE35SEL)
|
||||
#define IWM_STATE_C031_CTRL (1 << IWM_BIT_C031_CTRL)
|
||||
#define IWM_STATE_STEP_DIRECTION35 (1 << IWM_BIT_STEP_DIRECTION35)
|
||||
#define IWM_STATE_MOTOR_ON35 (1 << IWM_BIT_MOTOR_ON35)
|
||||
#define IWM_STATE_MOTOR_OFF (1 << IWM_BIT_MOTOR_OFF)
|
||||
#define IWM_STATE_DRIVE_SEL (1 << IWM_BIT_DRIVE_SEL)
|
||||
#define IWM_STATE_Q6 (1 << IWM_BIT_Q6)
|
||||
#define IWM_STATE_Q7 (1 << IWM_BIT_Q7)
|
||||
#define IWM_STATE_ENABLE2 (1 << IWM_BIT_ENABLE2)
|
||||
#define IWM_STATE_LAST_SEL35 (1 << IWM_BIT_LAST_SEL35)
|
||||
#define IWM_STATE_PHASES (1 << IWM_BIT_PHASES)
|
||||
#define IWM_STATE_RESET (1 << IWM_BIT_RESET)
|
||||
|
||||
STRUCT(Trk) {
|
||||
byte *raw_bptr;
|
||||
byte *sync_ptr;
|
||||
dword64 dunix_pos;
|
||||
word16 unix_len;
|
||||
word16 dirty;
|
||||
word32 track_bits;
|
||||
};
|
||||
|
||||
STRUCT(Woz_info) {
|
||||
byte *wozptr;
|
||||
word32 woz_size;
|
||||
int version;
|
||||
int reparse_needed;
|
||||
word32 max_trk_blocks;
|
||||
int meta_size;
|
||||
int trks_size;
|
||||
int tmap_offset;
|
||||
int trks_offset;
|
||||
int info_offset;
|
||||
int meta_offset;
|
||||
};
|
||||
|
||||
typedef struct Dynapro_map_st Dynapro_map;
|
||||
|
||||
STRUCT(Dynapro_file) {
|
||||
Dynapro_file *next_ptr;
|
||||
Dynapro_file *parent_ptr;
|
||||
Dynapro_file *subdir_ptr;
|
||||
char *unix_path;
|
||||
byte *buffer_ptr;
|
||||
byte prodos_name[17]; // +0x00-0x0f: [0] is len, nul at end
|
||||
word32 dir_byte; // Byte address of this file's dir ent
|
||||
word32 eof; // +0x15-0x17
|
||||
word32 blocks_used; // +0x13-0x14
|
||||
word32 creation_time; // +0x18-0x1b
|
||||
word32 lastmod_time; // +0x21-0x24
|
||||
word16 upper_lower; // +0x1c-0x1d: Versions: lowercase flags
|
||||
word16 key_block; // +0x11-0x12
|
||||
word16 aux_type; // +0x1f-0x20
|
||||
word16 header_pointer; // +0x25-0x26
|
||||
word16 map_first_block;
|
||||
byte file_type; // +0x10
|
||||
byte modified_flag;
|
||||
byte damaged;
|
||||
};
|
||||
|
||||
struct Dynapro_map_st {
|
||||
Dynapro_file *file_ptr;
|
||||
word16 next_map_block;
|
||||
word16 modified;
|
||||
};
|
||||
|
||||
STRUCT(Dynapro_info) {
|
||||
char *root_path;
|
||||
Dynapro_file *volume_ptr;
|
||||
Dynapro_map *block_map_ptr;
|
||||
int damaged;
|
||||
};
|
||||
|
||||
STRUCT(Disk) {
|
||||
dword64 dfcyc_last_read;
|
||||
byte *raw_data;
|
||||
Woz_info *wozinfo_ptr;
|
||||
Dynapro_info *dynapro_info_ptr;
|
||||
char *name_ptr;
|
||||
char *partition_name;
|
||||
int partition_num;
|
||||
int fd;
|
||||
word32 dynapro_blocks;
|
||||
dword64 raw_dsize;
|
||||
dword64 dimage_start;
|
||||
dword64 dimage_size;
|
||||
int smartport;
|
||||
int disk_525;
|
||||
int drive;
|
||||
word32 cur_frac_track;
|
||||
int image_type;
|
||||
int vol_num;
|
||||
int write_prot;
|
||||
int write_through_to_unix;
|
||||
int disk_dirty;
|
||||
int just_ejected;
|
||||
int last_phases;
|
||||
dword64 dfcyc_last_phases;
|
||||
word32 cur_fbit_pos;
|
||||
word32 fbit_mult;
|
||||
word32 cur_track_bits;
|
||||
int raw_bptr_malloc;
|
||||
Trk *cur_trk_ptr;
|
||||
int num_tracks;
|
||||
Trk *trks;
|
||||
};
|
||||
|
||||
STRUCT(Iwm) {
|
||||
Disk drive525[2];
|
||||
Disk drive35[2];
|
||||
Disk smartport[MAX_C7_DISKS];
|
||||
dword64 dfcyc_last_fastemul_read;
|
||||
word32 state;
|
||||
word32 motor_off_vbl_count;
|
||||
word32 forced_sync_bit;
|
||||
word32 last_rd_bit;
|
||||
word32 write_val;
|
||||
word32 wr_last_bit[5];
|
||||
word32 wr_qtr_track[5];
|
||||
word32 wr_num_bits[5];
|
||||
word32 wr_prior_num_bits[5];
|
||||
word32 wr_delta[5];
|
||||
int num_active_writes;
|
||||
};
|
||||
|
||||
STRUCT(Driver_desc) {
|
||||
word16 sig;
|
||||
word16 blk_size;
|
||||
word32 blk_count;
|
||||
word16 dev_type;
|
||||
word16 dev_id;
|
||||
word32 data;
|
||||
word16 drvr_count;
|
||||
};
|
||||
|
||||
STRUCT(Part_map) {
|
||||
word16 sig;
|
||||
word16 sigpad;
|
||||
word32 map_blk_cnt;
|
||||
word32 phys_part_start;
|
||||
word32 part_blk_cnt;
|
||||
char part_name[32];
|
||||
char part_type[32];
|
||||
word32 data_start;
|
||||
word32 data_cnt;
|
||||
word32 part_status;
|
||||
word32 log_boot_start;
|
||||
word32 boot_size;
|
||||
word32 boot_load;
|
||||
word32 boot_load2;
|
||||
word32 boot_entry;
|
||||
word32 boot_entry2;
|
||||
word32 boot_cksum;
|
||||
char processor[16];
|
||||
char junk[128];
|
||||
};
|
||||
|
||||
468
gsplus/src/joystick_driver.c
Normal file
468
gsplus/src/joystick_driver.c
Normal file
@@ -0,0 +1,468 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
#ifdef __linux__
|
||||
# include <linux/joystick.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
extern int g_joystick_native_type1; /* in paddles.c */
|
||||
extern int g_joystick_native_type2; /* in paddles.c */
|
||||
extern int g_joystick_native_type; /* in paddles.c */
|
||||
extern int g_paddle_buttons;
|
||||
extern int g_paddle_val[];
|
||||
|
||||
|
||||
const char *g_joystick_dev = "/dev/js0"; /* default joystick dev file */
|
||||
#define MAX_JOY_NAME 128
|
||||
|
||||
int g_joystick_native_fd = -1;
|
||||
int g_joystick_num_axes = 0;
|
||||
int g_joystick_num_buttons = 0;
|
||||
|
||||
int g_joystick_callback_buttons = 0;
|
||||
int g_joystick_callback_x = 32767;
|
||||
int g_joystick_callback_y = 32767;
|
||||
|
||||
#ifdef __linux__
|
||||
# define JOYSTICK_DEFINED
|
||||
void
|
||||
joystick_init()
|
||||
{
|
||||
char joy_name[MAX_JOY_NAME];
|
||||
int version, fd;
|
||||
int i;
|
||||
|
||||
fd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK);
|
||||
if(fd < 0) {
|
||||
printf("Unable to open joystick dev file: %s, errno: %d\n",
|
||||
g_joystick_dev, errno);
|
||||
printf("Defaulting to mouse joystick\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(&joy_name[0], "Unknown Joystick");
|
||||
version = 0x800;
|
||||
|
||||
ioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]);
|
||||
ioctl(fd, JSIOCGAXES, &g_joystick_num_axes);
|
||||
ioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons);
|
||||
ioctl(fd, JSIOCGVERSION, &version);
|
||||
|
||||
printf("Detected joystick: %s [%d axes, %d buttons vers: %08x]\n",
|
||||
joy_name, g_joystick_num_axes, g_joystick_num_buttons,
|
||||
version);
|
||||
|
||||
g_joystick_native_type1 = 1;
|
||||
g_joystick_native_type2 = -1;
|
||||
g_joystick_native_fd = fd;
|
||||
for(i = 0; i < 4; i++) {
|
||||
g_paddle_val[i] = 32767;
|
||||
}
|
||||
g_paddle_buttons = 0xc;
|
||||
}
|
||||
|
||||
/* joystick_update_linux() called from paddles.c. Update g_paddle_val[] */
|
||||
/* and g_paddle_buttons with current information */
|
||||
void
|
||||
joystick_update(dword64 dfcyc)
|
||||
{
|
||||
struct js_event js; /* the linux joystick event record */
|
||||
int mask, val, num, type, ret, len;
|
||||
int i;
|
||||
|
||||
/* suck up to 20 events, then give up */
|
||||
len = sizeof(struct js_event);
|
||||
for(i = 0; i < 20; i++) {
|
||||
ret = read(g_joystick_native_fd, &js, len);
|
||||
if(ret != len) {
|
||||
/* just get out */
|
||||
break;
|
||||
}
|
||||
type = js.type & ~JS_EVENT_INIT;
|
||||
val = js.value;
|
||||
num = js.number & 3; /* clamp to 0-3 */
|
||||
switch(type) {
|
||||
case JS_EVENT_BUTTON:
|
||||
mask = 1 << num;
|
||||
if(val) {
|
||||
val = mask;
|
||||
}
|
||||
g_paddle_buttons = (g_paddle_buttons & ~mask) | val;
|
||||
break;
|
||||
case JS_EVENT_AXIS:
|
||||
/* val is -32767 to +32767 */
|
||||
g_paddle_val[num] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i > 0) {
|
||||
paddle_update_trigger_dcycs(dfcyc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update_buttons()
|
||||
{
|
||||
}
|
||||
#endif /* LINUX */
|
||||
|
||||
#ifdef _WIN32
|
||||
# define JOYSTICK_DEFINED
|
||||
#undef JOYSTICK_DEFINED
|
||||
// HACK: remove
|
||||
#if 0
|
||||
void
|
||||
joystick_init()
|
||||
{
|
||||
JOYINFO info;
|
||||
JOYCAPS joycap;
|
||||
MMRESULT ret1, ret2;
|
||||
int i;
|
||||
|
||||
// Check that there is a joystick device
|
||||
if(joyGetNumDevs() <= 0) {
|
||||
printf("No joystick hardware detected\n");
|
||||
g_joystick_native_type1 = -1;
|
||||
g_joystick_native_type2 = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
g_joystick_native_type1 = -1;
|
||||
g_joystick_native_type2 = -1;
|
||||
|
||||
// Check that at least joystick 1 or joystick 2 is available
|
||||
ret1 = joyGetPos(JOYSTICKID1, &info);
|
||||
ret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap));
|
||||
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
|
||||
g_joystick_native_type1 = JOYSTICKID1;
|
||||
printf("Joystick #1 = %s\n", joycap.szPname);
|
||||
g_joystick_native_type = JOYSTICKID1;
|
||||
}
|
||||
ret1 = joyGetPos(JOYSTICKID2, &info);
|
||||
ret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap));
|
||||
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
|
||||
g_joystick_native_type2 = JOYSTICKID2;
|
||||
printf("Joystick #2 = %s\n", joycap.szPname);
|
||||
if(g_joystick_native_type < 0) {
|
||||
g_joystick_native_type = JOYSTICKID2;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < 4; i++) {
|
||||
g_paddle_val[i] = 32767;
|
||||
}
|
||||
g_paddle_buttons = 0xc;
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update(dword64 dfcyc)
|
||||
{
|
||||
JOYCAPS joycap;
|
||||
JOYINFO info;
|
||||
UINT id;
|
||||
MMRESULT ret1, ret2;
|
||||
|
||||
id = g_joystick_native_type;
|
||||
|
||||
ret1 = joyGetDevCaps(id, &joycap, sizeof(joycap));
|
||||
ret2 = joyGetPos(id, &info);
|
||||
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
|
||||
g_paddle_val[0] = (info.wXpos - joycap.wXmin) * 32768 /
|
||||
(joycap.wXmax - joycap.wXmin);
|
||||
g_paddle_val[1] = (info.wYpos - joycap.wYmin) * 32768 /
|
||||
(joycap.wYmax - joycap.wYmin);
|
||||
if(info.wButtons & JOY_BUTTON1) {
|
||||
g_paddle_buttons = g_paddle_buttons | 1;
|
||||
} else {
|
||||
g_paddle_buttons = g_paddle_buttons & (~1);
|
||||
}
|
||||
if(info.wButtons & JOY_BUTTON2) {
|
||||
g_paddle_buttons = g_paddle_buttons | 2;
|
||||
} else {
|
||||
g_paddle_buttons = g_paddle_buttons & (~2);
|
||||
}
|
||||
paddle_update_trigger_dcycs(dfcyc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update_buttons()
|
||||
{
|
||||
JOYINFOEX info;
|
||||
UINT id;
|
||||
|
||||
id = g_joystick_native_type;
|
||||
|
||||
info.dwSize = sizeof(JOYINFOEX);
|
||||
info.dwFlags = JOY_RETURNBUTTONS;
|
||||
if(joyGetPosEx(id, &info) == JOYERR_NOERROR) {
|
||||
if(info.dwButtons & JOY_BUTTON1) {
|
||||
g_paddle_buttons = g_paddle_buttons | 1;
|
||||
} else {
|
||||
g_paddle_buttons = g_paddle_buttons & (~1);
|
||||
}
|
||||
if(info.dwButtons & JOY_BUTTON2) {
|
||||
g_paddle_buttons = g_paddle_buttons | 2;
|
||||
} else {
|
||||
g_paddle_buttons = g_paddle_buttons & (~2);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MAC
|
||||
# define JOYSTICK_DEFINED
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
#include <IOKit/usb/USBSpec.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
// Headers are at: /Applications/Xcode.app/Contents/Developer/Platforms/
|
||||
// MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/
|
||||
// Frameworks/IOKit.framework/Headers
|
||||
// Thanks to VirtualC64 and hidapi library for coding example
|
||||
|
||||
CFIndex g_joystick_min = 0;
|
||||
CFIndex g_joystick_range = 256;
|
||||
int g_joystick_minmax_valid = 0;
|
||||
int g_joystick_dummy = 0;
|
||||
|
||||
void
|
||||
hid_device_callback(void *ptr, IOReturn result, void *sender,
|
||||
IOHIDValueRef value)
|
||||
{
|
||||
IOHIDElementRef element;
|
||||
word32 mask;
|
||||
int usage_page, usage, ival, button;
|
||||
|
||||
// This is a callback routine, and it's unclear to me what the state is
|
||||
// For safety, do no printfs() (other than for debug).
|
||||
if((ptr || result || sender || 1) == 0) {
|
||||
printf("Bad\n"); // Avoid unused var warning
|
||||
}
|
||||
|
||||
element = IOHIDValueGetElement(value);
|
||||
usage_page = IOHIDElementGetUsagePage(element);
|
||||
usage = IOHIDElementGetUsage(element);
|
||||
ival = IOHIDValueGetIntegerValue(value);
|
||||
#if 0
|
||||
printf(" usage_page:%d, usage:%d, value:%d\n", usage_page, usage, ival);
|
||||
#endif
|
||||
if((usage_page == kHIDPage_GenericDesktop) &&
|
||||
((usage >= kHIDUsage_GD_X) &&
|
||||
(usage <= kHIDUsage_GD_Y)) &&
|
||||
!g_joystick_minmax_valid) {
|
||||
g_joystick_min = IOHIDElementGetLogicalMin(element);
|
||||
g_joystick_range = IOHIDElementGetLogicalMax(element) + 1 -
|
||||
g_joystick_min;
|
||||
// printf("min:%lld range:%lld\n", (dword64)g_joystick_min,
|
||||
// (dword64)g_joystick_range);
|
||||
if(g_joystick_range == 0) {
|
||||
g_joystick_range = 1;
|
||||
}
|
||||
g_joystick_minmax_valid = 1;
|
||||
}
|
||||
if((usage_page == kHIDPage_GenericDesktop) &&
|
||||
(usage == kHIDUsage_GD_X)) {
|
||||
g_joystick_callback_x = ((ival * 65536) / g_joystick_range) -
|
||||
32768;
|
||||
//printf("g_joystick_callback_x = %d\n", g_joystick_callback_x);
|
||||
}
|
||||
if((usage_page == kHIDPage_GenericDesktop) &&
|
||||
(usage == kHIDUsage_GD_Y)) {
|
||||
g_joystick_callback_y = ((ival * 65536) / g_joystick_range) -
|
||||
32768;
|
||||
//printf("g_joystick_callback_y = %d\n", g_joystick_callback_y);
|
||||
}
|
||||
if((usage_page == kHIDPage_Button) && (usage >= 1) && (usage <= 10)) {
|
||||
// Buttons: usage=1 is button 0, usage=2 is button 1, etc.
|
||||
button = (~usage) & 1;
|
||||
mask = 1 << button;
|
||||
//printf("Button %d (%d) pressed:%d\n", button, usage, ival);
|
||||
if(ival == 0) { // Button released
|
||||
g_joystick_callback_buttons &= (~mask);
|
||||
} else { // Button pressed
|
||||
g_joystick_callback_buttons |= mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
hid_get_int_property(IOHIDDeviceRef device, CFStringRef key_cfstr)
|
||||
{
|
||||
CFTypeRef ref;
|
||||
Boolean bret;
|
||||
int int_val;
|
||||
|
||||
ref = IOHIDDeviceGetProperty(device, key_cfstr);
|
||||
if(ref) {
|
||||
bret = CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType,
|
||||
&int_val);
|
||||
if(bret) {
|
||||
return int_val;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
joystick_init()
|
||||
{
|
||||
IOHIDManagerRef hid_mgr;
|
||||
CFSetRef devices_set;
|
||||
CFIndex num_devices;
|
||||
IOHIDDeviceRef *devices_array, device;
|
||||
int vendor, usage_page, usage;
|
||||
int i;
|
||||
|
||||
g_joystick_native_type1 = -1;
|
||||
g_joystick_native_type2 = -1;
|
||||
|
||||
hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault,
|
||||
kIOHIDOptionsTypeNone);
|
||||
IOHIDManagerSetDeviceMatching(hid_mgr, 0);
|
||||
IOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone);
|
||||
|
||||
devices_set = IOHIDManagerCopyDevices(hid_mgr);
|
||||
num_devices = CFSetGetCount(devices_set);
|
||||
|
||||
// Sets are hashtables, so we cannot directly access it. The only way
|
||||
// to iterate over all values is to use CFSetGetValues to get a simple
|
||||
// array of the values, and iterate over that
|
||||
devices_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
|
||||
CFSetGetValues(devices_set, (const void **)devices_array);
|
||||
|
||||
for(i = 0; i < num_devices; i++) {
|
||||
device = devices_array[i];
|
||||
vendor = hid_get_int_property(device, CFSTR(kIOHIDVendorIDKey));
|
||||
// printf(" vendor: %d\n", vendor);
|
||||
usage_page = hid_get_int_property(device,
|
||||
CFSTR(kIOHIDDeviceUsagePageKey));
|
||||
usage = hid_get_int_property(device,
|
||||
CFSTR(kIOHIDDeviceUsageKey));
|
||||
// printf(" usage_page:%d, usage:%d\n", usage_page, usage);
|
||||
usage_page = hid_get_int_property(device,
|
||||
CFSTR(kIOHIDPrimaryUsagePageKey));
|
||||
usage = hid_get_int_property(device,
|
||||
CFSTR(kIOHIDPrimaryUsageKey));
|
||||
// printf(" primary_usage_page:%d, usage:%d\n", usage_page,
|
||||
// usage);
|
||||
if(usage_page != kHIDPage_GenericDesktop) {
|
||||
continue;
|
||||
}
|
||||
if((usage != kHIDUsage_GD_Joystick) &&
|
||||
(usage != kHIDUsage_GD_GamePad) &&
|
||||
(usage != kHIDUsage_GD_MultiAxisController)) {
|
||||
continue;
|
||||
}
|
||||
printf(" JOYSTICK FOUND, vendor:%08x!\n", vendor);
|
||||
IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);
|
||||
IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(),
|
||||
kCFRunLoopCommonModes);
|
||||
IOHIDDeviceRegisterInputValueCallback(device,
|
||||
hid_device_callback, 0);
|
||||
g_joystick_native_type1 = 1;
|
||||
return;
|
||||
// Now, hid_device_callback will be called whenever a joystick
|
||||
// value changes. Only set global variables for joystick.
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update(dword64 dfcyc)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(dfcyc) {
|
||||
// Avoid unused parameter warnings
|
||||
}
|
||||
for(i = 0; i < 4; i++) {
|
||||
g_paddle_val[i] = 32767;
|
||||
}
|
||||
g_paddle_buttons = 0xc;
|
||||
if(g_joystick_native_type1 >= 0) {
|
||||
g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);
|
||||
g_paddle_val[0] = g_joystick_callback_x;
|
||||
g_paddle_val[1] = g_joystick_callback_y;
|
||||
paddle_update_trigger_dcycs(dfcyc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update_buttons()
|
||||
{
|
||||
if(g_joystick_native_type1 >= 0) {
|
||||
g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef JOYSTICK_DEFINED
|
||||
/* stubs for the routines */
|
||||
void
|
||||
joystick_init()
|
||||
{
|
||||
g_joystick_native_type1 = -1;
|
||||
g_joystick_native_type2 = -1;
|
||||
g_joystick_native_type = -1;
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update(dword64 dfcyc)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(dfcyc) {
|
||||
// Avoid unused parameter warnings
|
||||
}
|
||||
for(i = 0; i < 4; i++) {
|
||||
g_paddle_val[i] = 32767;
|
||||
}
|
||||
g_paddle_buttons = 0xc;
|
||||
if(g_joystick_native_type1 >= 0) {
|
||||
g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);
|
||||
g_paddle_val[0] = g_joystick_callback_x;
|
||||
g_paddle_val[1] = g_joystick_callback_y;
|
||||
paddle_update_trigger_dcycs(dfcyc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
joystick_update_buttons()
|
||||
{
|
||||
if(g_joystick_native_type1 >= 0) {
|
||||
g_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
joystick_callback_init(int native_type)
|
||||
{
|
||||
g_joystick_native_type1 = native_type;
|
||||
}
|
||||
|
||||
void
|
||||
joystick_callback_update(word32 buttons, int paddle_x, int paddle_y)
|
||||
{
|
||||
g_joystick_callback_buttons = (g_paddle_buttons & (~3)) | (buttons & 3);
|
||||
g_joystick_callback_x = paddle_x;
|
||||
g_joystick_callback_y = paddle_y;
|
||||
}
|
||||
BIN
gsplus/src/kegs.icns
Normal file
BIN
gsplus/src/kegs.icns
Normal file
Binary file not shown.
514
gsplus/src/kegsfont.h
Normal file
514
gsplus/src/kegsfont.h
Normal file
@@ -0,0 +1,514 @@
|
||||
/* $KmKId: kegsfont.h,v 1.1 2002-11-10 03:31:51-05 kadickey Exp $ */
|
||||
|
||||
/* char 0x00 (raw 0x40) */
|
||||
{ 0xc7, 0xbb, 0xab, 0xa3, 0xa7, 0xbf, 0xc3, 0xff },
|
||||
/* char 0x01 (raw 0x41) */
|
||||
{ 0xef, 0xd7, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x02 (raw 0x42) */
|
||||
{ 0x87, 0xbb, 0xbb, 0x87, 0xbb, 0xbb, 0x87, 0xff },
|
||||
/* char 0x03 (raw 0x43) */
|
||||
{ 0xc7, 0xbb, 0xbf, 0xbf, 0xbf, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x04 (raw 0x44) */
|
||||
{ 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x87, 0xff },
|
||||
/* char 0x05 (raw 0x45) */
|
||||
{ 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0x83, 0xff },
|
||||
/* char 0x06 (raw 0x46) */
|
||||
{ 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0xbf, 0xff },
|
||||
/* char 0x07 (raw 0x47) */
|
||||
{ 0xc3, 0xbf, 0xbf, 0xbf, 0xb3, 0xbb, 0xc3, 0xff },
|
||||
/* char 0x08 (raw 0x48) */
|
||||
{ 0xbb, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x09 (raw 0x49) */
|
||||
{ 0xc7, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },
|
||||
/* char 0x0a (raw 0x4a) */
|
||||
{ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x0b (raw 0x4b) */
|
||||
{ 0xbb, 0xb7, 0xaf, 0x9f, 0xaf, 0xb7, 0xbb, 0xff },
|
||||
/* char 0x0c (raw 0x4c) */
|
||||
{ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x83, 0xff },
|
||||
/* char 0x0d (raw 0x4d) */
|
||||
{ 0xbb, 0x93, 0xab, 0xab, 0xbb, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x0e (raw 0x4e) */
|
||||
{ 0xbb, 0xbb, 0x9b, 0xab, 0xb3, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x0f (raw 0x4f) */
|
||||
{ 0xc7, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x10 (raw 0x50) */
|
||||
{ 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf, 0xbf, 0xff },
|
||||
/* char 0x11 (raw 0x51) */
|
||||
{ 0xc7, 0xbb, 0xbb, 0xbb, 0xab, 0xb7, 0xcb, 0xff },
|
||||
/* char 0x12 (raw 0x52) */
|
||||
{ 0x87, 0xbb, 0xbb, 0x87, 0xaf, 0xb7, 0xbb, 0xff },
|
||||
/* char 0x13 (raw 0x53) */
|
||||
{ 0xc7, 0xbb, 0xbf, 0xc7, 0xfb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x14 (raw 0x54) */
|
||||
{ 0x83, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xff },
|
||||
/* char 0x15 (raw 0x55) */
|
||||
{ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x16 (raw 0x56) */
|
||||
{ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff },
|
||||
/* char 0x17 (raw 0x57) */
|
||||
{ 0xbb, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xbb, 0xff },
|
||||
/* char 0x18 (raw 0x58) */
|
||||
{ 0xbb, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x19 (raw 0x59) */
|
||||
{ 0xbb, 0xbb, 0xd7, 0xef, 0xef, 0xef, 0xef, 0xff },
|
||||
/* char 0x1a (raw 0x5a) */
|
||||
{ 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x83, 0xff },
|
||||
/* char 0x1b (raw 0x5b) */
|
||||
{ 0x83, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x83, 0xff },
|
||||
/* char 0x1c (raw 0x5c) */
|
||||
{ 0xff, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xff, 0xff },
|
||||
/* char 0x1d (raw 0x5d) */
|
||||
{ 0x83, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0x83, 0xff },
|
||||
/* char 0x1e (raw 0x5e) */
|
||||
{ 0xff, 0xff, 0xef, 0xd7, 0xbb, 0xff, 0xff, 0xff },
|
||||
/* char 0x1f (raw 0x5f) */
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 },
|
||||
/* char 0x20 (raw 0x20) */
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
/* char 0x21 (raw 0x21) */
|
||||
{ 0xef, 0xef, 0xef, 0xef, 0xef, 0xff, 0xef, 0xff },
|
||||
/* char 0x22 (raw 0x22) */
|
||||
{ 0xd7, 0xd7, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
/* char 0x23 (raw 0x23) */
|
||||
{ 0xd7, 0xd7, 0x83, 0xd7, 0x83, 0xd7, 0xd7, 0xff },
|
||||
/* char 0x24 (raw 0x24) */
|
||||
{ 0xef, 0xc3, 0xaf, 0xc7, 0xeb, 0x87, 0xef, 0xff },
|
||||
/* char 0x25 (raw 0x25) */
|
||||
{ 0x9f, 0x9b, 0xf7, 0xef, 0xdf, 0xb3, 0xf3, 0xff },
|
||||
/* char 0x26 (raw 0x26) */
|
||||
{ 0xdf, 0xaf, 0xaf, 0xdf, 0xab, 0xb7, 0xcb, 0xff },
|
||||
/* char 0x27 (raw 0x27) */
|
||||
{ 0xef, 0xef, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
/* char 0x28 (raw 0x28) */
|
||||
{ 0xef, 0xdf, 0xbf, 0xbf, 0xbf, 0xdf, 0xef, 0xff },
|
||||
/* char 0x29 (raw 0x29) */
|
||||
{ 0xef, 0xf7, 0xfb, 0xfb, 0xfb, 0xf7, 0xef, 0xff },
|
||||
/* char 0x2a (raw 0x2a) */
|
||||
{ 0xef, 0xab, 0xc7, 0xef, 0xc7, 0xab, 0xef, 0xff },
|
||||
/* char 0x2b (raw 0x2b) */
|
||||
{ 0xff, 0xef, 0xef, 0x83, 0xef, 0xef, 0xff, 0xff },
|
||||
/* char 0x2c (raw 0x2c) */
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xdf, 0xff },
|
||||
/* char 0x2d (raw 0x2d) */
|
||||
{ 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff },
|
||||
/* char 0x2e (raw 0x2e) */
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff },
|
||||
/* char 0x2f (raw 0x2f) */
|
||||
{ 0xff, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0xff, 0xff },
|
||||
/* char 0x30 (raw 0x30) */
|
||||
{ 0xc7, 0xbb, 0xb3, 0xab, 0x9b, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x31 (raw 0x31) */
|
||||
{ 0xef, 0xcf, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },
|
||||
/* char 0x32 (raw 0x32) */
|
||||
{ 0xc7, 0xbb, 0xfb, 0xe7, 0xdf, 0xbf, 0x83, 0xff },
|
||||
/* char 0x33 (raw 0x33) */
|
||||
{ 0x83, 0xfb, 0xf7, 0xe7, 0xfb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x34 (raw 0x34) */
|
||||
{ 0xf7, 0xe7, 0xd7, 0xb7, 0x83, 0xf7, 0xf7, 0xff },
|
||||
/* char 0x35 (raw 0x35) */
|
||||
{ 0x83, 0xbf, 0x87, 0xfb, 0xfb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x36 (raw 0x36) */
|
||||
{ 0xe3, 0xdf, 0xbf, 0x87, 0xbb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x37 (raw 0x37) */
|
||||
{ 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xdf, 0xdf, 0xff },
|
||||
/* char 0x38 (raw 0x38) */
|
||||
{ 0xc7, 0xbb, 0xbb, 0xc7, 0xbb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x39 (raw 0x39) */
|
||||
{ 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xf7, 0x8f, 0xff },
|
||||
/* char 0x3a (raw 0x3a) */
|
||||
{ 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xff, 0xff },
|
||||
/* char 0x3b (raw 0x3b) */
|
||||
{ 0xff, 0xff, 0xef, 0xff, 0xef, 0xef, 0xdf, 0xff },
|
||||
/* char 0x3c (raw 0x3c) */
|
||||
{ 0xf7, 0xef, 0xdf, 0xbf, 0xdf, 0xef, 0xf7, 0xff },
|
||||
/* char 0x3d (raw 0x3d) */
|
||||
{ 0xff, 0xff, 0x83, 0xff, 0x83, 0xff, 0xff, 0xff },
|
||||
/* char 0x3e (raw 0x3e) */
|
||||
{ 0xdf, 0xef, 0xf7, 0xfb, 0xf7, 0xef, 0xdf, 0xff },
|
||||
/* char 0x3f (raw 0x3f) */
|
||||
{ 0xc7, 0xbb, 0xf7, 0xef, 0xef, 0xff, 0xef, 0xff },
|
||||
/* char 0x40 (raw 0x14) */
|
||||
{ 0x08, 0x10, 0x6c, 0xfe, 0xfc, 0xfc, 0x7e, 0x6c },
|
||||
/* char 0x41 (raw 0x11) */
|
||||
{ 0x08, 0x10, 0x6c, 0x82, 0x84, 0x84, 0x52, 0x6c },
|
||||
/* char 0x42 (raw 0xf5) */
|
||||
{ 0x00, 0x00, 0x40, 0x60, 0x70, 0x78, 0x6c, 0x42 },
|
||||
/* char 0x43 (raw 0x82) */
|
||||
{ 0xfe, 0x44, 0x28, 0x10, 0x10, 0x28, 0x54, 0xfe },
|
||||
/* char 0x44 (raw 0xeb) */
|
||||
{ 0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x20, 0x00 },
|
||||
/* char 0x45 (raw 0xe4) */
|
||||
{ 0xfe, 0xfc, 0xfa, 0x36, 0xae, 0xde, 0xde, 0xfe },
|
||||
/* char 0x46 (raw 0xec) */
|
||||
{ 0xfc, 0xfc, 0xfc, 0xdc, 0x9c, 0x00, 0x9e, 0xde },
|
||||
/* char 0x47 (raw 0xed) */
|
||||
{ 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe },
|
||||
/* char 0x48 (raw 0xee) */
|
||||
{ 0x10, 0x20, 0x40, 0xfe, 0x40, 0x20, 0x10, 0x00 },
|
||||
/* char 0x49 (raw 0xe9) */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54 },
|
||||
/* char 0x4a (raw 0xef) */
|
||||
{ 0x10, 0x10, 0x10, 0x10, 0x92, 0x54, 0x38, 0x10 },
|
||||
/* char 0x4b (raw 0xf0) */
|
||||
{ 0x10, 0x38, 0x54, 0x92, 0x10, 0x10, 0x10, 0x10 },
|
||||
/* char 0x4c (raw 0xf1) */
|
||||
{ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0x4d (raw 0xf7) */
|
||||
{ 0x02, 0x02, 0x02, 0x22, 0x62, 0xfe, 0x60, 0x20 },
|
||||
/* char 0x4e (raw 0xf6) */
|
||||
{ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc },
|
||||
/* char 0x4f (raw 0xaf) */
|
||||
{ 0xc8, 0x18, 0x38, 0x7e, 0x38, 0x18, 0x08, 0xf6 },
|
||||
/* char 0x50 (raw 0xb8) */
|
||||
{ 0x26, 0x30, 0x38, 0xfc, 0x38, 0x30, 0x20, 0xde },
|
||||
/* char 0x51 (raw 0xce) */
|
||||
{ 0x02, 0x12, 0x10, 0xfe, 0x7c, 0x38, 0x12, 0x02 },
|
||||
/* char 0x52 (raw 0xe5) */
|
||||
{ 0x02, 0x12, 0x38, 0x7c, 0xfe, 0x10, 0x12, 0x02 },
|
||||
/* char 0x53 (raw 0xea) */
|
||||
{ 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0x54 (raw 0xe6) */
|
||||
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe },
|
||||
/* char 0x55 (raw 0xe8) */
|
||||
{ 0x10, 0x08, 0x04, 0xfe, 0x04, 0x08, 0x10, 0x00 },
|
||||
/* char 0x56 (raw 0xd7) */
|
||||
{ 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa },
|
||||
/* char 0x57 (raw 0xe3) */
|
||||
{ 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54 },
|
||||
/* char 0x58 (raw 0xf4) */
|
||||
{ 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0xfe, 0x00 },
|
||||
/* char 0x59 (raw 0xe7) */
|
||||
{ 0x00, 0x00, 0xfc, 0x02, 0x02, 0x02, 0xfe, 0x00 },
|
||||
/* char 0x5a (raw 0xf3) */
|
||||
{ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 },
|
||||
/* char 0x5b (raw 0xd2) */
|
||||
{ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00 },
|
||||
/* char 0x5c (raw 0xc7) */
|
||||
{ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },
|
||||
/* char 0x5d (raw 0xd4) */
|
||||
{ 0x28, 0x28, 0xee, 0x00, 0xee, 0x28, 0x28, 0x00 },
|
||||
/* char 0x5e (raw 0xdf) */
|
||||
{ 0xfe, 0x02, 0x02, 0x32, 0x32, 0x02, 0x02, 0xfe },
|
||||
/* char 0x5f (raw 0xd1) */
|
||||
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },
|
||||
/* char 0x60 (raw 0x60) */
|
||||
{ 0xdf, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
/* char 0x61 (raw 0x61) */
|
||||
{ 0xff, 0xff, 0xc7, 0xfb, 0xc3, 0xbb, 0xc3, 0xff },
|
||||
/* char 0x62 (raw 0x62) */
|
||||
{ 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0x87, 0xff },
|
||||
/* char 0x63 (raw 0x63) */
|
||||
{ 0xff, 0xff, 0xc3, 0xbf, 0xbf, 0xbf, 0xc3, 0xff },
|
||||
/* char 0x64 (raw 0x64) */
|
||||
{ 0xfb, 0xfb, 0xc3, 0xbb, 0xbb, 0xbb, 0xc3, 0xff },
|
||||
/* char 0x65 (raw 0x65) */
|
||||
{ 0xff, 0xff, 0xc7, 0xbb, 0x83, 0xbf, 0xc3, 0xff },
|
||||
/* char 0x66 (raw 0x66) */
|
||||
{ 0xe7, 0xdb, 0xdf, 0x87, 0xdf, 0xdf, 0xdf, 0xff },
|
||||
/* char 0x67 (raw 0x67) */
|
||||
{ 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 },
|
||||
/* char 0x68 (raw 0x68) */
|
||||
{ 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x69 (raw 0x69) */
|
||||
{ 0xef, 0xff, 0xcf, 0xef, 0xef, 0xef, 0xc7, 0xff },
|
||||
/* char 0x6a (raw 0x6a) */
|
||||
{ 0xf7, 0xff, 0xe7, 0xf7, 0xf7, 0xf7, 0xb7, 0xcf },
|
||||
/* char 0x6b (raw 0x6b) */
|
||||
{ 0xbf, 0xbf, 0xbb, 0xb7, 0x8f, 0xb7, 0xbb, 0xff },
|
||||
/* char 0x6c (raw 0x6c) */
|
||||
{ 0xcf, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },
|
||||
/* char 0x6d (raw 0x6d) */
|
||||
{ 0xff, 0xff, 0x93, 0xab, 0xab, 0xab, 0xbb, 0xff },
|
||||
/* char 0x6e (raw 0x6e) */
|
||||
{ 0xff, 0xff, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff },
|
||||
/* char 0x6f (raw 0x6f) */
|
||||
{ 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },
|
||||
/* char 0x70 (raw 0x70) */
|
||||
{ 0xff, 0xff, 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf },
|
||||
/* char 0x71 (raw 0x71) */
|
||||
{ 0xff, 0xff, 0xc3, 0xbb, 0xbb, 0xc3, 0xfb, 0xfb },
|
||||
/* char 0x72 (raw 0x72) */
|
||||
{ 0xff, 0xff, 0xa3, 0x9f, 0xbf, 0xbf, 0xbf, 0xff },
|
||||
/* char 0x73 (raw 0x73) */
|
||||
{ 0xff, 0xff, 0xc3, 0xbf, 0xc7, 0xfb, 0x87, 0xff },
|
||||
/* char 0x74 (raw 0x74) */
|
||||
{ 0xdf, 0xdf, 0x87, 0xdf, 0xdf, 0xdb, 0xe7, 0xff },
|
||||
/* char 0x75 (raw 0x75) */
|
||||
{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xb3, 0xcb, 0xff },
|
||||
/* char 0x76 (raw 0x76) */
|
||||
{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff },
|
||||
/* char 0x77 (raw 0x77) */
|
||||
{ 0xff, 0xff, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xff },
|
||||
/* char 0x78 (raw 0x78) */
|
||||
{ 0xff, 0xff, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xff },
|
||||
/* char 0x79 (raw 0x79) */
|
||||
{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 },
|
||||
/* char 0x7a (raw 0x7a) */
|
||||
{ 0xff, 0xff, 0x83, 0xf7, 0xef, 0xdf, 0x83, 0xff },
|
||||
/* char 0x7b (raw 0x7b) */
|
||||
{ 0xe3, 0xcf, 0xcf, 0x9f, 0xcf, 0xcf, 0xe3, 0xff },
|
||||
/* char 0x7c (raw 0x7c) */
|
||||
{ 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef },
|
||||
/* char 0x7d (raw 0x7d) */
|
||||
{ 0x8f, 0xe7, 0xe7, 0xf3, 0xe7, 0xe7, 0x8f, 0xff },
|
||||
/* char 0x7e (raw 0x7e) */
|
||||
{ 0xcb, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
/* char 0x7f (raw 0x7f) */
|
||||
{ 0xff, 0xab, 0xd7, 0xab, 0xd7, 0xab, 0xff, 0xff },
|
||||
/* char 0x80 (raw 0x40) */
|
||||
{ 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 },
|
||||
/* char 0x81 (raw 0x41) */
|
||||
{ 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 },
|
||||
/* char 0x82 (raw 0x42) */
|
||||
{ 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 },
|
||||
/* char 0x83 (raw 0x43) */
|
||||
{ 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 },
|
||||
/* char 0x84 (raw 0x44) */
|
||||
{ 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 },
|
||||
/* char 0x85 (raw 0x45) */
|
||||
{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 },
|
||||
/* char 0x86 (raw 0x46) */
|
||||
{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 },
|
||||
/* char 0x87 (raw 0x47) */
|
||||
{ 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 },
|
||||
/* char 0x88 (raw 0x48) */
|
||||
{ 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 },
|
||||
/* char 0x89 (raw 0x49) */
|
||||
{ 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },
|
||||
/* char 0x8a (raw 0x4a) */
|
||||
{ 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 },
|
||||
/* char 0x8b (raw 0x4b) */
|
||||
{ 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 },
|
||||
/* char 0x8c (raw 0x4c) */
|
||||
{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 },
|
||||
/* char 0x8d (raw 0x4d) */
|
||||
{ 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 },
|
||||
/* char 0x8e (raw 0x4e) */
|
||||
{ 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 },
|
||||
/* char 0x8f (raw 0x4f) */
|
||||
{ 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0x90 (raw 0x50) */
|
||||
{ 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 },
|
||||
/* char 0x91 (raw 0x51) */
|
||||
{ 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 },
|
||||
/* char 0x92 (raw 0x52) */
|
||||
{ 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 },
|
||||
/* char 0x93 (raw 0x53) */
|
||||
{ 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 },
|
||||
/* char 0x94 (raw 0x54) */
|
||||
{ 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 },
|
||||
/* char 0x95 (raw 0x55) */
|
||||
{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0x96 (raw 0x56) */
|
||||
{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },
|
||||
/* char 0x97 (raw 0x57) */
|
||||
{ 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 },
|
||||
/* char 0x98 (raw 0x58) */
|
||||
{ 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 },
|
||||
/* char 0x99 (raw 0x59) */
|
||||
{ 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 },
|
||||
/* char 0x9a (raw 0x5a) */
|
||||
{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 },
|
||||
/* char 0x9b (raw 0x5b) */
|
||||
{ 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 },
|
||||
/* char 0x9c (raw 0x5c) */
|
||||
{ 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 },
|
||||
/* char 0x9d (raw 0x5d) */
|
||||
{ 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 },
|
||||
/* char 0x9e (raw 0x5e) */
|
||||
{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 },
|
||||
/* char 0x9f (raw 0x5f) */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },
|
||||
/* char 0xa0 (raw 0x20) */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0xa1 (raw 0x21) */
|
||||
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00 },
|
||||
/* char 0xa2 (raw 0x22) */
|
||||
{ 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0xa3 (raw 0x23) */
|
||||
{ 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00 },
|
||||
/* char 0xa4 (raw 0x24) */
|
||||
{ 0x10, 0x3c, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00 },
|
||||
/* char 0xa5 (raw 0x25) */
|
||||
{ 0x60, 0x64, 0x08, 0x10, 0x20, 0x4c, 0x0c, 0x00 },
|
||||
/* char 0xa6 (raw 0x26) */
|
||||
{ 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00 },
|
||||
/* char 0xa7 (raw 0x27) */
|
||||
{ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0xa8 (raw 0x28) */
|
||||
{ 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00 },
|
||||
/* char 0xa9 (raw 0x29) */
|
||||
{ 0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00 },
|
||||
/* char 0xaa (raw 0x2a) */
|
||||
{ 0x10, 0x54, 0x38, 0x10, 0x38, 0x54, 0x10, 0x00 },
|
||||
/* char 0xab (raw 0x2b) */
|
||||
{ 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00 },
|
||||
/* char 0xac (raw 0x2c) */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00 },
|
||||
/* char 0xad (raw 0x2d) */
|
||||
{ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0xae (raw 0x2e) */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 },
|
||||
/* char 0xaf (raw 0x2f) */
|
||||
{ 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 },
|
||||
/* char 0xb0 (raw 0x30) */
|
||||
{ 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00 },
|
||||
/* char 0xb1 (raw 0x31) */
|
||||
{ 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },
|
||||
/* char 0xb2 (raw 0x32) */
|
||||
{ 0x38, 0x44, 0x04, 0x18, 0x20, 0x40, 0x7c, 0x00 },
|
||||
/* char 0xb3 (raw 0x33) */
|
||||
{ 0x7c, 0x04, 0x08, 0x18, 0x04, 0x44, 0x38, 0x00 },
|
||||
/* char 0xb4 (raw 0x34) */
|
||||
{ 0x08, 0x18, 0x28, 0x48, 0x7c, 0x08, 0x08, 0x00 },
|
||||
/* char 0xb5 (raw 0x35) */
|
||||
{ 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00 },
|
||||
/* char 0xb6 (raw 0x36) */
|
||||
{ 0x1c, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0xb7 (raw 0x37) */
|
||||
{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00 },
|
||||
/* char 0xb8 (raw 0x38) */
|
||||
{ 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0xb9 (raw 0x39) */
|
||||
{ 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x70, 0x00 },
|
||||
/* char 0xba (raw 0x3a) */
|
||||
{ 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00 },
|
||||
/* char 0xbb (raw 0x3b) */
|
||||
{ 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00 },
|
||||
/* char 0xbc (raw 0x3c) */
|
||||
{ 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00 },
|
||||
/* char 0xbd (raw 0x3d) */
|
||||
{ 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00 },
|
||||
/* char 0xbe (raw 0x3e) */
|
||||
{ 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00 },
|
||||
/* char 0xbf (raw 0x3f) */
|
||||
{ 0x38, 0x44, 0x08, 0x10, 0x10, 0x00, 0x10, 0x00 },
|
||||
/* char 0xc0 (raw 0x40) */
|
||||
{ 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 },
|
||||
/* char 0xc1 (raw 0x41) */
|
||||
{ 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 },
|
||||
/* char 0xc2 (raw 0x42) */
|
||||
{ 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 },
|
||||
/* char 0xc3 (raw 0x43) */
|
||||
{ 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 },
|
||||
/* char 0xc4 (raw 0x44) */
|
||||
{ 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 },
|
||||
/* char 0xc5 (raw 0x45) */
|
||||
{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 },
|
||||
/* char 0xc6 (raw 0x46) */
|
||||
{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 },
|
||||
/* char 0xc7 (raw 0x47) */
|
||||
{ 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 },
|
||||
/* char 0xc8 (raw 0x48) */
|
||||
{ 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 },
|
||||
/* char 0xc9 (raw 0x49) */
|
||||
{ 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },
|
||||
/* char 0xca (raw 0x4a) */
|
||||
{ 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 },
|
||||
/* char 0xcb (raw 0x4b) */
|
||||
{ 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 },
|
||||
/* char 0xcc (raw 0x4c) */
|
||||
{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 },
|
||||
/* char 0xcd (raw 0x4d) */
|
||||
{ 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 },
|
||||
/* char 0xce (raw 0x4e) */
|
||||
{ 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 },
|
||||
/* char 0xcf (raw 0x4f) */
|
||||
{ 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0xd0 (raw 0x50) */
|
||||
{ 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 },
|
||||
/* char 0xd1 (raw 0x51) */
|
||||
{ 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 },
|
||||
/* char 0xd2 (raw 0x52) */
|
||||
{ 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 },
|
||||
/* char 0xd3 (raw 0x53) */
|
||||
{ 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 },
|
||||
/* char 0xd4 (raw 0x54) */
|
||||
{ 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 },
|
||||
/* char 0xd5 (raw 0x55) */
|
||||
{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0xd6 (raw 0x56) */
|
||||
{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },
|
||||
/* char 0xd7 (raw 0x57) */
|
||||
{ 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 },
|
||||
/* char 0xd8 (raw 0x58) */
|
||||
{ 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 },
|
||||
/* char 0xd9 (raw 0x59) */
|
||||
{ 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 },
|
||||
/* char 0xda (raw 0x5a) */
|
||||
{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 },
|
||||
/* char 0xdb (raw 0x5b) */
|
||||
{ 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 },
|
||||
/* char 0xdc (raw 0x5c) */
|
||||
{ 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 },
|
||||
/* char 0xdd (raw 0x5d) */
|
||||
{ 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 },
|
||||
/* char 0xde (raw 0x5e) */
|
||||
{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 },
|
||||
/* char 0xdf (raw 0x5f) */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },
|
||||
/* char 0xe0 (raw 0x60) */
|
||||
{ 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0xe1 (raw 0x61) */
|
||||
{ 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00 },
|
||||
/* char 0xe2 (raw 0x62) */
|
||||
{ 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00 },
|
||||
/* char 0xe3 (raw 0x63) */
|
||||
{ 0x00, 0x00, 0x3c, 0x40, 0x40, 0x40, 0x3c, 0x00 },
|
||||
/* char 0xe4 (raw 0x64) */
|
||||
{ 0x04, 0x04, 0x3c, 0x44, 0x44, 0x44, 0x3c, 0x00 },
|
||||
/* char 0xe5 (raw 0x65) */
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00 },
|
||||
/* char 0xe6 (raw 0x66) */
|
||||
{ 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x20, 0x00 },
|
||||
/* char 0xe7 (raw 0x67) */
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x38 },
|
||||
/* char 0xe8 (raw 0x68) */
|
||||
{ 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 },
|
||||
/* char 0xe9 (raw 0x69) */
|
||||
{ 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00 },
|
||||
/* char 0xea (raw 0x6a) */
|
||||
{ 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x48, 0x30 },
|
||||
/* char 0xeb (raw 0x6b) */
|
||||
{ 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x00 },
|
||||
/* char 0xec (raw 0x6c) */
|
||||
{ 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },
|
||||
/* char 0xed (raw 0x6d) */
|
||||
{ 0x00, 0x00, 0x6c, 0x54, 0x54, 0x54, 0x44, 0x00 },
|
||||
/* char 0xee (raw 0x6e) */
|
||||
{ 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 },
|
||||
/* char 0xef (raw 0x6f) */
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 },
|
||||
/* char 0xf0 (raw 0x70) */
|
||||
{ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40 },
|
||||
/* char 0xf1 (raw 0x71) */
|
||||
{ 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x04 },
|
||||
/* char 0xf2 (raw 0x72) */
|
||||
{ 0x00, 0x00, 0x5c, 0x60, 0x40, 0x40, 0x40, 0x00 },
|
||||
/* char 0xf3 (raw 0x73) */
|
||||
{ 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00 },
|
||||
/* char 0xf4 (raw 0x74) */
|
||||
{ 0x20, 0x20, 0x78, 0x20, 0x20, 0x24, 0x18, 0x00 },
|
||||
/* char 0xf5 (raw 0x75) */
|
||||
{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x4c, 0x34, 0x00 },
|
||||
/* char 0xf6 (raw 0x76) */
|
||||
{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },
|
||||
/* char 0xf7 (raw 0x77) */
|
||||
{ 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x00 },
|
||||
/* char 0xf8 (raw 0x78) */
|
||||
{ 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 },
|
||||
/* char 0xf9 (raw 0x79) */
|
||||
{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38 },
|
||||
/* char 0xfa (raw 0x7a) */
|
||||
{ 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00 },
|
||||
/* char 0xfb (raw 0x7b) */
|
||||
{ 0x1c, 0x30, 0x30, 0x60, 0x30, 0x30, 0x1c, 0x00 },
|
||||
/* char 0xfc (raw 0x7c) */
|
||||
{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
|
||||
/* char 0xfd (raw 0x7d) */
|
||||
{ 0x70, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x70, 0x00 },
|
||||
/* char 0xfe (raw 0x7e) */
|
||||
{ 0x34, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* char 0xff (raw 0x7f) */
|
||||
{ 0x00, 0x54, 0x28, 0x54, 0x28, 0x54, 0x00, 0x00 },
|
||||
31
gsplus/src/kegswin.sln
Normal file
31
gsplus/src/kegswin.sln
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kegswin", "kegswin.vcxproj", "{C24D318A-501E-4B8C-901A-15977CB9C00C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.ActiveCfg = Release|x64
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.Build.0 = Release|x64
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.ActiveCfg = Release|x64
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.Build.0 = Release|x64
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {32BE13D0-68A7-486D-874C-EA158125775B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
169
gsplus/src/kegswin.vcxproj
Normal file
169
gsplus/src/kegswin.vcxproj
Normal file
@@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{C24D318A-501E-4B8C-901A-15977CB9C00C}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>wsock32.lib;dsound.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;dsound.lib;winmm.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="adb.c" />
|
||||
<ClCompile Include="applesingle.c" />
|
||||
<ClCompile Include="clock.c" />
|
||||
<ClCompile Include="compile_time.c" />
|
||||
<ClCompile Include="config.c" />
|
||||
<ClCompile Include="debugger.c" />
|
||||
<ClCompile Include="dynapro.c" />
|
||||
<ClCompile Include="dyna_filt.c" />
|
||||
<ClCompile Include="dyna_type.c" />
|
||||
<ClCompile Include="dyna_validate.c" />
|
||||
<ClCompile Include="engine_c.c" />
|
||||
<ClCompile Include="iwm.c" />
|
||||
<ClCompile Include="joystick_driver.c" />
|
||||
<ClCompile Include="mockingboard.c" />
|
||||
<ClCompile Include="moremem.c" />
|
||||
<ClCompile Include="paddles.c" />
|
||||
<ClCompile Include="scc.c" />
|
||||
<ClCompile Include="scc_unixdriver.c" />
|
||||
<ClCompile Include="scc_socket_driver.c" />
|
||||
<ClCompile Include="scc_windriver.c" />
|
||||
<ClCompile Include="sim65816.c" />
|
||||
<ClCompile Include="smartport.c" />
|
||||
<ClCompile Include="doc.c" />
|
||||
<ClCompile Include="sound.c" />
|
||||
<ClCompile Include="sound_driver.c" />
|
||||
<ClCompile Include="undeflate.c" />
|
||||
<ClCompile Include="unshk.c" />
|
||||
<ClCompile Include="video.c" />
|
||||
<ClCompile Include="voc.c" />
|
||||
<ClCompile Include="win32snd_driver.c" />
|
||||
<ClCompile Include="windriver.c" />
|
||||
<ClCompile Include="woz.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="config.h" />
|
||||
<ClInclude Include="defc.h" />
|
||||
<ClInclude Include="defcomm.h" />
|
||||
<ClInclude Include="defs_instr.h" />
|
||||
<ClInclude Include="disas.h" />
|
||||
<ClInclude Include="engine.h" />
|
||||
<ClInclude Include="instable.h" />
|
||||
<ClInclude Include="iwm.h" />
|
||||
<ClInclude Include="Kegs-Bridging-Header.h" />
|
||||
<ClInclude Include="kegsfont.h" />
|
||||
<ClInclude Include="op_routs.h" />
|
||||
<ClInclude Include="protos.h" />
|
||||
<ClInclude Include="protos_base.h" />
|
||||
<ClInclude Include="protos_windriver.h" />
|
||||
<ClInclude Include="protos_xdriver.h" />
|
||||
<ClInclude Include="scc.h" />
|
||||
<ClInclude Include="size_c.h" />
|
||||
<ClInclude Include="sound.h" />
|
||||
<ClInclude Include="winresource.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
2
gsplus/src/ldvars
Normal file
2
gsplus/src/ldvars
Normal file
@@ -0,0 +1,2 @@
|
||||
OBJECTS = adb.o engine_c.o clock.o config.o debugger.o scc.o scc_socket_driver.o scc_windriver.o scc_unixdriver.o iwm.o joystick_driver.o moremem.o paddles.o mockingboard.o sim65816.o smartport.o doc.o sound.o sound_driver.o woz.o unshk.o undeflate.o dynapro.o dyna_type.o dyna_filt.o dyna_validate.o applesingle.o video.o voc.o
|
||||
PROJROOT = ..
|
||||
272
gsplus/src/macsnd_driver.c
Normal file
272
gsplus/src/macsnd_driver.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2022 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// Headers at: /Applications/Xcode.app/Contents/Developer/Platforms/
|
||||
// MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/
|
||||
// Frameworks/AudioToolbox.framework/Headers
|
||||
|
||||
// Some ideas from SDL code:
|
||||
// http://www-personal.umich.edu/~bazald/l/api/_s_d_l__coreaudio_8c_source.html
|
||||
|
||||
#include "defc.h"
|
||||
#include "sound.h"
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Mac OS X 12.0 deprecates kAudioObjectPropertyElementMaster. So now we
|
||||
// need to use kAudioObjectPropertyElementMain, and set it to ...Master if it
|
||||
// isn't already set. This is beyond dumb
|
||||
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
|
||||
# if __MAC_OS_X_VERSION_MAX_ALLOWED < 120000
|
||||
# define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define MACSND_REBUF_SIZE (64*1024)
|
||||
|
||||
word32 g_macsnd_rebuf[MACSND_REBUF_SIZE];
|
||||
volatile int g_macsnd_rd_pos;
|
||||
volatile int g_macsnd_wr_pos;
|
||||
volatile int g_macsnd_playing = 0;
|
||||
int g_macsnd_channel_warn = 0;
|
||||
extern int g_sound_min_samples; // About 33ms
|
||||
extern int g_sound_max_multiplier; // About 6, so 6*33 ~= 200ms.
|
||||
|
||||
extern int Verbose;
|
||||
|
||||
extern int g_preferred_rate;
|
||||
extern word32 *g_sound_shm_addr;
|
||||
extern int g_sound_size;
|
||||
|
||||
int g_call_num = 0;
|
||||
|
||||
AURenderCallbackStruct g_callback_struct = { 0 };
|
||||
|
||||
static OSStatus
|
||||
audio_callback(void *in_ref_ptr, AudioUnitRenderActionFlags *aura_flags_ptr,
|
||||
const AudioTimeStamp *a_timestamp_ptr, UInt32 bus_number,
|
||||
UInt32 in_num_frame, AudioBufferList *abuflist_ptr)
|
||||
{
|
||||
word32 *wptr;
|
||||
int num_buffers, num_channels, num_samps, sample_num, samps_avail;
|
||||
int max_samples, min_samples;
|
||||
int i, j;
|
||||
|
||||
if(in_ref_ptr || aura_flags_ptr || a_timestamp_ptr || bus_number) {
|
||||
// Avoid unused parameter warning
|
||||
}
|
||||
#if 0
|
||||
printf("CB: audio_callback called, in_ref:%p, bus:%d, in_frame:%d\n",
|
||||
in_ref_ptr, bus_number, in_num_frame);
|
||||
#endif
|
||||
num_buffers = abuflist_ptr->mNumberBuffers;
|
||||
min_samples = g_sound_min_samples;
|
||||
max_samples = min_samples * g_sound_max_multiplier;
|
||||
#if 0
|
||||
printf("CB: num_buffers: %d. sample_time:%lf, host_time:%016llx "
|
||||
"rate_scalar:%lf, word_clock_time:%016llx, flags:%04x\n",
|
||||
num_buffers, a_timestamp_ptr->mSampleTime,
|
||||
a_timestamp_ptr->mHostTime, a_timestamp_ptr->mRateScalar,
|
||||
a_timestamp_ptr->mWordClockTime, a_timestamp_ptr->mFlags);
|
||||
#endif
|
||||
|
||||
sample_num = g_macsnd_rd_pos;
|
||||
samps_avail = (g_macsnd_wr_pos - sample_num) & (MACSND_REBUF_SIZE - 1);
|
||||
if((int)in_num_frame > samps_avail) {
|
||||
// We don't have enough samples, must pause
|
||||
g_macsnd_playing = 0;
|
||||
sample_num = g_macsnd_wr_pos; // Eat remaining samps
|
||||
}
|
||||
if((g_macsnd_playing == 0) &&
|
||||
(samps_avail > (min_samples + (int)in_num_frame))) {
|
||||
// We can unpause
|
||||
g_macsnd_playing = 1;
|
||||
}
|
||||
if(g_macsnd_playing && (samps_avail > max_samples)) {
|
||||
printf("JUMP SAMPLE_NUM by %d samples!\n", max_samples / 2);
|
||||
sample_num += (max_samples / 2);
|
||||
sample_num = sample_num & (MACSND_REBUF_SIZE - 1);
|
||||
}
|
||||
#if 0
|
||||
printf("CB: in_frame:%d, samps_avail:%d, playing:%d\n", in_num_frame,
|
||||
samps_avail, g_macsnd_playing);
|
||||
#endif
|
||||
|
||||
for(i = 0; i < num_buffers; i++) {
|
||||
num_channels = abuflist_ptr->mBuffers[i].mNumberChannels;
|
||||
if((num_channels != 2) && !g_macsnd_channel_warn) {
|
||||
printf("mNumberChannels:%d\n", num_channels);
|
||||
g_macsnd_channel_warn = 1;
|
||||
}
|
||||
num_samps = abuflist_ptr->mBuffers[i].mDataByteSize / 4;
|
||||
wptr = (word32 *)abuflist_ptr->mBuffers[i].mData;
|
||||
#if 0
|
||||
printf("CB buf[%d]: num_ch:%d, num_samps:%d, wptr:%p\n", i,
|
||||
num_channels, num_samps, wptr);
|
||||
#endif
|
||||
#if 0
|
||||
if((g_call_num & 31) == 0) {
|
||||
printf("%d play %d samples, samps_avail:%d\n",
|
||||
g_call_num, num_samps, samps_avail);
|
||||
}
|
||||
#endif
|
||||
if(g_macsnd_playing) {
|
||||
for(j = 0; j < num_samps; j++) {
|
||||
wptr[j] = g_macsnd_rebuf[sample_num];
|
||||
sample_num++;
|
||||
if(sample_num >= MACSND_REBUF_SIZE) {
|
||||
sample_num = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(j = 0; j < num_samps; j++) {
|
||||
wptr[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_call_num++;
|
||||
g_macsnd_rd_pos = sample_num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mac_send_audio(byte *ptr, int in_size)
|
||||
{
|
||||
word32 *wptr, *macptr;
|
||||
int samps, sample_num;
|
||||
int i;
|
||||
|
||||
samps = in_size / 4;
|
||||
wptr = (word32 *)ptr;
|
||||
sample_num = g_macsnd_wr_pos;
|
||||
macptr = &(g_macsnd_rebuf[0]);
|
||||
for(i = 0; i < samps; i++) {
|
||||
macptr[sample_num] = *wptr++;
|
||||
sample_num++;
|
||||
if(sample_num >= MACSND_REBUF_SIZE) {
|
||||
sample_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_macsnd_wr_pos = sample_num;
|
||||
|
||||
return in_size;
|
||||
}
|
||||
|
||||
AudioObjectPropertyAddress g_aopa = { 0 };
|
||||
|
||||
void
|
||||
macsnd_init()
|
||||
{
|
||||
AudioComponentInstance ac_inst;
|
||||
AudioStreamBasicDescription *str_desc_ptr;
|
||||
AudioComponentDescription *ac_descr_ptr;
|
||||
AudioComponent ac;
|
||||
AudioDeviceID device;
|
||||
OSStatus result;
|
||||
UInt32 size;
|
||||
|
||||
g_macsnd_rd_pos = 0;
|
||||
g_macsnd_wr_pos = 0;
|
||||
mac_printf("macsnd_init called\n");
|
||||
|
||||
g_aopa.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
g_aopa.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
g_aopa.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
size = 4;
|
||||
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &g_aopa,
|
||||
0, 0, &size, &device);
|
||||
if(result != 0) {
|
||||
printf("AudioObjectGetPropertData on DefaultOutputDevice:%d\n",
|
||||
result);
|
||||
return;
|
||||
}
|
||||
mac_printf("Audio Device number: %d\n", device);
|
||||
|
||||
str_desc_ptr = calloc(1, sizeof(AudioStreamBasicDescription));
|
||||
str_desc_ptr->mFormatID = kAudioFormatLinearPCM;
|
||||
str_desc_ptr->mFormatFlags = kLinearPCMFormatFlagIsPacked |
|
||||
kLinearPCMFormatFlagIsSignedInteger;
|
||||
str_desc_ptr->mChannelsPerFrame = 2;
|
||||
str_desc_ptr->mSampleRate = g_preferred_rate;
|
||||
str_desc_ptr->mFramesPerPacket = 1;
|
||||
str_desc_ptr->mBitsPerChannel = 16; // 16-bit samples
|
||||
str_desc_ptr->mBytesPerFrame = (16 * 2) / 8;
|
||||
str_desc_ptr->mBytesPerPacket = str_desc_ptr->mBytesPerFrame;
|
||||
|
||||
ac_descr_ptr = calloc(1, sizeof(AudioComponentDescription));
|
||||
ac_descr_ptr->componentType = kAudioUnitType_Output;
|
||||
ac_descr_ptr->componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
ac_descr_ptr->componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
ac = AudioComponentFindNext(0, ac_descr_ptr);
|
||||
mac_printf("AudioComponentFindNext ret: %p\n", ac);
|
||||
if(ac == 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result = AudioComponentInstanceNew(ac, &ac_inst);
|
||||
mac_printf("AudioComponentInstanceNew ret:%d\n", result);
|
||||
if(result != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result = AudioUnitSetProperty(ac_inst,
|
||||
kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &device,
|
||||
sizeof(device));
|
||||
mac_printf("AudioUnitSetProperty CurrentDevice ret: %d\n", result);
|
||||
if(result != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result = AudioUnitSetProperty(ac_inst,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, str_desc_ptr,
|
||||
sizeof(*str_desc_ptr));
|
||||
mac_printf("AudioUnitSetProperty StreamFormat ret: %d\n", result);
|
||||
if(result != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_callback_struct.inputProc = audio_callback;
|
||||
g_callback_struct.inputProcRefCon = (void *)1;
|
||||
result = AudioUnitSetProperty(ac_inst,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &g_callback_struct,
|
||||
sizeof(g_callback_struct));
|
||||
|
||||
mac_printf("AudioUnitSetProperty SetRenderCallback ret: %d\n", result);
|
||||
if(result != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result = AudioUnitInitialize(ac_inst);
|
||||
mac_printf("AudioUnitInitialize ret: %d\n", result);
|
||||
if(result != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result = AudioOutputUnitStart(ac_inst);
|
||||
mac_printf("AudioOutputUnitStart ret: %d\n", result);
|
||||
if(result != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sound_set_audio_rate(g_preferred_rate);
|
||||
|
||||
mac_printf("End of child_sound_init_mac\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
665
gsplus/src/mockingboard.c
Normal file
665
gsplus/src/mockingboard.c
Normal file
@@ -0,0 +1,665 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
#include "sound.h"
|
||||
|
||||
// Mockingboard contains two pairs of a 6522/AY-8913, where the 6522 interfaces
|
||||
// the AY-8913 (which makes the sounds) to the Apple II. Each AY-8913
|
||||
// contains 3 channels of sound: A,B,C. Model each pair separately.
|
||||
// The AY-8913 has 16 registers. The documentation numbers them using octal!
|
||||
// The AY8913 is accessed using ORB of the 6522 as control: 0 = Reset, 4=Idle
|
||||
// 5=Reg read; 6=Write reg; 7=Latch reg address
|
||||
|
||||
extern Mockingboard g_mockingboard;
|
||||
dword64 g_mockingboard_last_int_dusec = 0ULL;
|
||||
dword64 g_mockingboard_event_int_dusec = 0ULL; // 0 -> no event pending
|
||||
|
||||
extern int g_irq_pending;
|
||||
extern double g_dsamps_per_dfcyc;
|
||||
|
||||
void
|
||||
mock_ay8913_reset(int pair_num, dword64 dfcyc)
|
||||
{
|
||||
Ay8913 *ay8913ptr;
|
||||
int i;
|
||||
|
||||
if(dfcyc) {
|
||||
// Avoid unused parameter warning
|
||||
}
|
||||
ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);
|
||||
ay8913ptr->reg_addr_latch = 16; // out-of-range, mb-audit1.3
|
||||
for(i = 0; i < 16; i++) {
|
||||
ay8913ptr->regs[i] = 0;
|
||||
}
|
||||
for(i = 0; i < 3; i++) {
|
||||
ay8913ptr->toggle_tone[i] = 0;
|
||||
ay8913ptr->tone_samp[i] = 0;
|
||||
}
|
||||
ay8913ptr->noise_val = 0x12345678;
|
||||
ay8913ptr->noise_samp = 0;
|
||||
ay8913ptr->env_dsamp = 0;
|
||||
}
|
||||
|
||||
void
|
||||
mockingboard_reset(dword64 dfcyc)
|
||||
{
|
||||
word32 timer1_latch;
|
||||
|
||||
timer1_latch = g_mockingboard.pair[0].mos6522.timer1_latch;
|
||||
memset(&g_mockingboard, 0, sizeof(g_mockingboard));
|
||||
|
||||
g_mockingboard_last_int_dusec = (dfcyc >> 16) << 16;
|
||||
if(g_mockingboard_event_int_dusec != 0) {
|
||||
(void)remove_event_mockingboard();
|
||||
}
|
||||
g_mockingboard_event_int_dusec = 0;
|
||||
printf("At reset, timer1_latch: %08x\n", timer1_latch);
|
||||
if((timer1_latch & 0xffff) == 0x234) { // MB-audit
|
||||
g_mockingboard.pair[0].mos6522.timer1_latch = timer1_latch;
|
||||
} else {
|
||||
g_mockingboard.pair[0].mos6522.timer1_latch = 0xff00;
|
||||
}
|
||||
g_mockingboard.pair[0].mos6522.timer1_counter = 0x2ff00;
|
||||
g_mockingboard.pair[0].mos6522.timer2_counter = 0x2ff00;
|
||||
g_mockingboard.pair[1].mos6522.timer1_latch = 0xff00;
|
||||
g_mockingboard.pair[1].mos6522.timer1_counter = 0x2ff00;
|
||||
g_mockingboard.pair[1].mos6522.timer2_counter = 0x2ff00;
|
||||
|
||||
mock_ay8913_reset(0, dfcyc);
|
||||
mock_ay8913_reset(1, dfcyc);
|
||||
}
|
||||
|
||||
void
|
||||
mock_show_pair(int pair_num, dword64 dfcyc, const char *str)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
|
||||
mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);
|
||||
printf("Mock %d %s, t1_lat:%05x, t1_c:%05x, t2_l:%05x t2_c:%05x, ifr:"
|
||||
"%02x, acr:%02x, ier:%02x\n", pair_num, str,
|
||||
mos6522ptr->timer1_latch, mos6522ptr->timer1_counter,
|
||||
mos6522ptr->timer2_latch, mos6522ptr->timer2_counter,
|
||||
mos6522ptr->ifr, mos6522ptr->acr, mos6522ptr->ier);
|
||||
printf(" dfcyc:%016llx, event_int:%lld\n", dfcyc,
|
||||
g_mockingboard_event_int_dusec);
|
||||
|
||||
}
|
||||
|
||||
// Timers work as follows: if written with '10' in cycle 0, on cycle 1 the
|
||||
// counters loads with '10', and counts down to 9 on cycle 2...down to 1
|
||||
// on cycle 10, then 0 on cycle 11, and then signals an interrupt on cycle 12
|
||||
// To handle this, timers are "read value + 1" so that timer==0 means the
|
||||
// timer should interrupt right now. So to write '10' to a timer, the code
|
||||
// needs to write 10+2=12 to the variable, so that on the next cycle, it is
|
||||
// 11, which will be returned as 10 to software reading the reg.
|
||||
|
||||
void
|
||||
mock_update_timers(int doit, dword64 dfcyc)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
dword64 dusec, ddiff, dleft, timer1_int_dusec, timer2_int_dusec;
|
||||
dword64 closest_int_dusec, event_int_dusec;
|
||||
word32 timer_val, ier, timer_eff, timer_latch, log_ifr;
|
||||
int i;
|
||||
|
||||
dbg_log_info(dfcyc, doit, g_mockingboard.pair[0].mos6522.ifr |
|
||||
(g_mockingboard.pair[0].mos6522.ier << 8), 0xc0);
|
||||
|
||||
dusec = dfcyc >> 16;
|
||||
ddiff = dusec - g_mockingboard_last_int_dusec;
|
||||
if(!doit && (dusec <= g_mockingboard_last_int_dusec)) {
|
||||
return; // Nothing more to do
|
||||
}
|
||||
|
||||
// printf("mock_update_timers at %lf %016llx, ddiff:%llx\n", dfcyc,
|
||||
// dusec, ddiff);
|
||||
// Update timers by ddiff integer cycles, calculate next event time
|
||||
g_mockingboard_last_int_dusec = dusec;
|
||||
closest_int_dusec = 0;
|
||||
for(i = 0; i < 2; i++) { // pair_num
|
||||
mos6522ptr = &(g_mockingboard.pair[i].mos6522);
|
||||
timer1_int_dusec = 0;
|
||||
timer_val = mos6522ptr->timer1_counter;
|
||||
ier = mos6522ptr->ier;
|
||||
timer_eff = (timer_val & 0x1ffff);
|
||||
dleft = ddiff;
|
||||
timer_latch = mos6522ptr->timer1_latch + 2;
|
||||
dbg_log_info(dfcyc, (word32)dleft, timer_val, 0xcb);
|
||||
dbg_log_info(dfcyc, ier, timer_latch, 0xcb);
|
||||
if(dleft < timer_eff) {
|
||||
// Move ahead only a little, no triggering
|
||||
timer_val = timer_val - (word32)dleft;
|
||||
if(ddiff) {
|
||||
// printf("New timer1_val:%05x, dleft:%08llx\n",
|
||||
// timer_val, dleft);
|
||||
}
|
||||
if(((mos6522ptr->ifr & 0x40) == 0) && (ier & 0x40)) {
|
||||
// IFR not set yet, prepare an event
|
||||
timer1_int_dusec = dusec +
|
||||
(timer_val & 0x1ffff);
|
||||
dbg_log_info(dfcyc, (word32)timer1_int_dusec,
|
||||
timer_val, 0xcd);
|
||||
// printf("t1_int_dusec: %016llx\n",
|
||||
// timer1_int_dusec);
|
||||
}
|
||||
} else {
|
||||
// Timer1 has triggered now (maybe rolled over more
|
||||
// than once).
|
||||
log_ifr = 0;
|
||||
if((timer_val & 0x20000) == 0) {
|
||||
// Either free-running, or not one-shot already
|
||||
// triggered
|
||||
// Set interrupt: Ensure IFR | 0x40 is set
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i,
|
||||
mos6522ptr->ifr | 0x40, ier);
|
||||
log_ifr = 1;
|
||||
}
|
||||
dleft -= timer_eff;
|
||||
if(dleft >= timer_latch) {
|
||||
// It's rolled over several times, remove those
|
||||
dleft = dleft % timer_latch;
|
||||
dbg_log_info(dfcyc, (word32)dleft, timer_latch,
|
||||
0xcc);
|
||||
}
|
||||
if(dleft == 0) {
|
||||
dleft = timer_latch;
|
||||
}
|
||||
timer_val = (timer_latch - dleft) & 0x1ffff;
|
||||
if((mos6522ptr->acr & 0x40) == 0) {
|
||||
// ACR6=0: One shot mode, mark it as triggered
|
||||
timer_val |= 0x20000;
|
||||
}
|
||||
if(log_ifr) {
|
||||
dbg_log_info(dfcyc,
|
||||
mos6522ptr->ifr | (ier << 8),
|
||||
timer_val, 0xc3);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("%016llx ch%d timer1 was %05x, now %05x\n", dfcyc, i,
|
||||
mos6522ptr->timer1_counter, timer_val);
|
||||
#endif
|
||||
|
||||
mos6522ptr->timer1_counter = timer_val;
|
||||
dbg_log_info(dfcyc, timer_val, timer_latch, 0xce);
|
||||
|
||||
// Handle timer2
|
||||
timer2_int_dusec = 0;
|
||||
timer_val = mos6522ptr->timer2_counter;
|
||||
timer_eff = timer_val & 0x1ffff;
|
||||
dleft = ddiff;
|
||||
if(mos6522ptr->acr & 0x20) {
|
||||
// Count pulses mode. Just don't count
|
||||
dleft = 0;
|
||||
}
|
||||
if(dleft < timer_eff) {
|
||||
// Move ahead only a little, no triggering
|
||||
timer_val = timer_val - (word32)dleft;
|
||||
if(((mos6522ptr->ifr & 0x20) == 0) && (ier & 0x20)) {
|
||||
// IFR not set yet, prepare an event
|
||||
timer2_int_dusec = dusec +
|
||||
(timer_val & 0x1ffff);
|
||||
//printf("t2_int_dusec: %016llx\n",
|
||||
// timer1_int_dusec);
|
||||
}
|
||||
} else if(timer_val & 0x20000) {
|
||||
// And already triggered once, just update count
|
||||
timer_val = ((timer_eff - dleft) & 0xffff) | 0x20000;
|
||||
} else {
|
||||
// Has not triggered once yet, but it will now
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i,
|
||||
mos6522ptr->ifr | 0x20, ier);
|
||||
timer_val = ((timer_val - dleft) & 0xffff) | 0x20000;
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr | (ier << 8),
|
||||
timer_val, 0xc4);
|
||||
}
|
||||
|
||||
// printf("ch%d timer2 was %05x, now %05x\n", i,
|
||||
// mos6522ptr->timer2_counter, timer_val);
|
||||
|
||||
mos6522ptr->timer2_counter = timer_val;
|
||||
|
||||
if(timer1_int_dusec && timer2_int_dusec) {
|
||||
timer1_int_dusec = MY_MIN(timer1_int_dusec,
|
||||
timer2_int_dusec);
|
||||
}
|
||||
if(timer1_int_dusec) {
|
||||
if(closest_int_dusec) {
|
||||
closest_int_dusec = MY_MIN(closest_int_dusec,
|
||||
timer1_int_dusec);
|
||||
} else {
|
||||
closest_int_dusec = timer1_int_dusec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event_int_dusec = g_mockingboard_event_int_dusec;
|
||||
if(closest_int_dusec) {
|
||||
// See if this is sooner than the current pending event
|
||||
// printf("closest_int_dusec: %016llx, event_int:%016llx\n",
|
||||
// closest_int_dusec, event_int_dusec);
|
||||
doit = 0;
|
||||
if(event_int_dusec && (closest_int_dusec < event_int_dusec)) {
|
||||
// There was a pending event. Discard it
|
||||
// printf("Call remove_event_mockingboard\n");
|
||||
remove_event_mockingboard();
|
||||
doit = 1;
|
||||
}
|
||||
if(!event_int_dusec || doit) {
|
||||
//printf("Call add_event_mockingboard: %016llx %lld\n",
|
||||
// closest_int_dusec, closest_int_dusec);
|
||||
add_event_mockingboard(closest_int_dusec << 16);
|
||||
g_mockingboard_event_int_dusec = closest_int_dusec;
|
||||
dbg_log_info(dfcyc,
|
||||
(word32)(closest_int_dusec - (dfcyc >> 16)),
|
||||
0, 0xc1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mockingboard_event(dword64 dfcyc)
|
||||
{
|
||||
// Received an event--we believe we may need to set an IRQ now.
|
||||
// Event was already removed from the event queue
|
||||
// printf("Mockingboard_event received at %016llx\n", dfcyc);
|
||||
dbg_log_info(dfcyc, 0, 0, 0xc2);
|
||||
g_mockingboard_event_int_dusec = 0;
|
||||
mock_update_timers(1, dfcyc);
|
||||
}
|
||||
|
||||
word32
|
||||
mockingboard_read(word32 loc, dword64 dfcyc)
|
||||
{
|
||||
int pair_num;
|
||||
|
||||
// printf("mockingboard read: %04x\n", loc);
|
||||
pair_num = (loc >> 7) & 1; // 0 or 1
|
||||
return mock_6522_read(pair_num, loc & 0xf, dfcyc);
|
||||
}
|
||||
|
||||
void
|
||||
mockingboard_write(word32 loc, word32 val, dword64 dfcyc)
|
||||
{
|
||||
int pair_num;
|
||||
|
||||
// printf("mockingboard write: %04x=%02x\n", loc, val);
|
||||
pair_num = (loc >> 7) & 1; // 0 or 1
|
||||
mock_6522_write(pair_num, loc & 0xf, val, dfcyc);
|
||||
}
|
||||
|
||||
word32
|
||||
mock_6522_read(int pair_num, word32 loc, dword64 dfcyc)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
word32 val;
|
||||
|
||||
// Read from 6522 #pair_num loc (0-15)
|
||||
mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);
|
||||
val = 0;
|
||||
switch(loc) {
|
||||
case 0x0: // ORB/IRB
|
||||
// Connected to AY8913 { RESET, BDIR, BC1 }
|
||||
val = mos6522ptr->orb;
|
||||
// There are no outputs from AY8913 to the 6522 B Port
|
||||
break;
|
||||
case 0x1: // ORA
|
||||
case 0xf: // ORA, no handshake
|
||||
val = mos6522ptr->ora;
|
||||
break;
|
||||
case 0x2: // DDRB
|
||||
val = mos6522ptr->ddrb;
|
||||
break;
|
||||
case 0x3: // DDRA
|
||||
val = mos6522ptr->ddra;
|
||||
break;
|
||||
case 0x4: // T1C-L (timer[0])
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = (mos6522ptr->timer1_counter - 1) & 0xff;
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr & (~0x40), mos6522ptr->ier);
|
||||
// Clear Bit 6
|
||||
mock_update_timers(1, dfcyc); // Prepare another int (maybe)
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc5);
|
||||
break;
|
||||
case 0x5: // T1C-H
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = ((mos6522ptr->timer1_counter - 1) >> 8) & 0xff;
|
||||
break;
|
||||
case 0x6: // T1L-L
|
||||
val = mos6522ptr->timer1_latch & 0xff;
|
||||
break;
|
||||
case 0x7: // T1L-H
|
||||
val = (mos6522ptr->timer1_latch >> 8) & 0xff;
|
||||
break;
|
||||
case 0x8: // T2C-L
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = (mos6522ptr->timer2_counter - 1) & 0xff;
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr & (~0x20), mos6522ptr->ier);
|
||||
// Clear Bit 5
|
||||
mock_update_timers(1, dfcyc); // Prepare another int (maybe)
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc6);
|
||||
break;
|
||||
case 0x9: // T2C-H
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = ((mos6522ptr->timer2_counter - 1) >> 8) & 0xff;
|
||||
break;
|
||||
case 0xa: // SR
|
||||
val = mos6522ptr->sr;
|
||||
//halt_printf("Reading SR %d %02x\n", pair_num, val);
|
||||
break;
|
||||
case 0xb: // ACR
|
||||
val = mos6522ptr->acr;
|
||||
break;
|
||||
case 0xc: // PCR
|
||||
val = mos6522ptr->pcr;
|
||||
break;
|
||||
case 0xd: // IFR
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = mos6522ptr->ifr;
|
||||
break;
|
||||
case 0xe: // IER
|
||||
val = mos6522ptr->ier | 0x80; // Despite MOS6522
|
||||
break; // datasheet, bit 7 = 1
|
||||
}
|
||||
// printf("6522 %d loc:%x ret:%02x\n", pair_num, loc, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
mock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
word32 ora, orb, new_val, mask;
|
||||
|
||||
// Write to 6522 #num6522 loc (0-15)
|
||||
|
||||
// printf("6522 %d loc:%x write:%02x\n", pair_num, loc, val);
|
||||
|
||||
mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);
|
||||
switch(loc) {
|
||||
case 0x0: // ORB
|
||||
mask = mos6522ptr->ddrb;
|
||||
orb = mos6522ptr->orb;
|
||||
new_val = (val & mask) | (orb & (~mask));
|
||||
if(orb != new_val) {
|
||||
mock_ay8913_control_update(pair_num, new_val, orb,
|
||||
dfcyc);
|
||||
}
|
||||
mos6522ptr->orb = new_val;
|
||||
break;
|
||||
case 0x1: // ORA
|
||||
case 0xf: // ORA, no handshake
|
||||
mask = mos6522ptr->ddra;
|
||||
ora = mos6522ptr->ora;
|
||||
new_val = (val & mask) | (ora & (~mask));
|
||||
mos6522ptr->ora = new_val;
|
||||
break;
|
||||
case 0x2: // DDRB
|
||||
orb = mos6522ptr->orb;
|
||||
new_val = (orb & val) | (orb & (~val));
|
||||
if(orb != new_val) {
|
||||
mock_ay8913_control_update(pair_num, new_val, orb,
|
||||
dfcyc);
|
||||
}
|
||||
mos6522ptr->orb = new_val;
|
||||
mos6522ptr->ddrb = val;
|
||||
return;
|
||||
case 0x3: // DDRA
|
||||
ora = mos6522ptr->ora;
|
||||
mos6522ptr->ora = (ora & val) | (ora & (~val));
|
||||
mos6522ptr->ddra = val;
|
||||
return;
|
||||
case 0x4: // T1C-L
|
||||
mock_update_timers(0, dfcyc);
|
||||
mos6522ptr->timer1_latch =
|
||||
(mos6522ptr->timer1_latch & 0x1ff00) | val;
|
||||
// printf("Set T1C-L, timer1_latch=%05x\n",
|
||||
// mos6522ptr->timer1_latch);
|
||||
break;
|
||||
case 0x5: // T1C-H
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = (mos6522ptr->timer1_latch & 0xff) | (val << 8);
|
||||
mos6522ptr->timer1_latch = val;
|
||||
mos6522ptr->timer1_counter = val + 2;
|
||||
// The actual timer1_counter update happens next cycle,
|
||||
// so we want val+1, plus another 1
|
||||
// printf("Set T1C-H, timer1_latch=%05x\n",
|
||||
// mos6522ptr->timer1_latch);
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr & (~0x40), mos6522ptr->ier);
|
||||
// Clear Bit 6
|
||||
mock_update_timers(1, dfcyc);
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc7);
|
||||
break;
|
||||
case 0x6: // T1L-L
|
||||
mock_update_timers(0, dfcyc);
|
||||
mos6522ptr->timer1_latch =
|
||||
(mos6522ptr->timer1_latch & 0x1ff00) | val;
|
||||
break;
|
||||
case 0x7: // T1L-H
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = (mos6522ptr->timer1_latch & 0xff) | (val << 8);
|
||||
mos6522ptr->timer1_latch = val;
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr & (~0x40), mos6522ptr->ier);
|
||||
// Clear Bit 6
|
||||
mock_update_timers(1, dfcyc);
|
||||
// mock_show_pair(pair_num, dfcyc, "Wrote T1L-H");
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc8);
|
||||
break;
|
||||
case 0x8: // T2C-L
|
||||
mos6522ptr->timer2_latch = (mos6522ptr->timer2_latch & 0xff00) |
|
||||
val;
|
||||
break;
|
||||
case 0x9: // T2C-H
|
||||
mock_update_timers(1, dfcyc);
|
||||
val = (mos6522ptr->timer2_latch & 0xff) | (val << 8);
|
||||
mos6522ptr->timer2_latch = val;
|
||||
mos6522ptr->timer2_counter = val + 2;
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr & (~0x20), mos6522ptr->ier);
|
||||
// Clear bit 5
|
||||
mock_update_timers(1, dfcyc);
|
||||
break;
|
||||
case 0xa: // SR
|
||||
mos6522ptr->sr = val;
|
||||
halt_printf("Wrote SR reg: %d %02x\n", pair_num, val);
|
||||
break;
|
||||
case 0xb: // ACR
|
||||
mock_update_timers(0, dfcyc);
|
||||
mos6522ptr->acr = val;
|
||||
mock_update_timers(1, dfcyc);
|
||||
break;
|
||||
case 0xc: // PCR
|
||||
mos6522ptr->pcr = val;
|
||||
break;
|
||||
case 0xd: // IFR
|
||||
mock_update_timers(1, dfcyc);
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr & (~val), mos6522ptr->ier);
|
||||
mock_update_timers(1, dfcyc);
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc9);
|
||||
break;
|
||||
case 0xe: // IER
|
||||
// Recalculate effective IFR with new IER
|
||||
mock_update_timers(1, dfcyc);
|
||||
if(val & 0x80) { // Set EIR bits
|
||||
val = mos6522ptr->ier | val;
|
||||
} else { // Clear EIR bits
|
||||
val = mos6522ptr->ier & (~val);
|
||||
}
|
||||
val = val & 0x7f;
|
||||
mos6522ptr->ier = val;
|
||||
mos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,
|
||||
mos6522ptr->ifr, val);
|
||||
mock_update_timers(1, dfcyc);
|
||||
// mock_show_pair(pair_num, dfcyc, "Wrote IER");
|
||||
dbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xca);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
word32
|
||||
mock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier)
|
||||
{
|
||||
word32 irq_mask;
|
||||
|
||||
// Determine if there are any interrupts pending now
|
||||
irq_mask = IRQ_PENDING_MOCKINGBOARDA << pair_num;
|
||||
if((ifr & ier & 0x7f) == 0) {
|
||||
// No IRQ pending anymore
|
||||
ifr = ifr & 0x7f; // Clear bit 7
|
||||
if(g_irq_pending & irq_mask) {
|
||||
// printf("MOCK INT OFF\n");
|
||||
}
|
||||
remove_irq(irq_mask);
|
||||
dbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf);
|
||||
} else {
|
||||
// IRQ is pending
|
||||
ifr = ifr | 0x80; // Set bit 7
|
||||
if(!(g_irq_pending & irq_mask)) {
|
||||
// printf("MOCK INT ON\n");
|
||||
}
|
||||
add_irq(irq_mask);
|
||||
dbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf);
|
||||
}
|
||||
return ifr;
|
||||
}
|
||||
|
||||
void
|
||||
mock_ay8913_reg_read(int pair_num)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
Ay8913 *ay8913ptr;
|
||||
word32 reg_addr_latch, mask, val, ora;
|
||||
|
||||
mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);
|
||||
ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);
|
||||
reg_addr_latch = ay8913ptr->reg_addr_latch;
|
||||
val = 0;
|
||||
if(reg_addr_latch < 16) {
|
||||
val = ay8913ptr->regs[reg_addr_latch];
|
||||
}
|
||||
// ORA at 6522 is merge of ORA using DDRA
|
||||
mask = mos6522ptr->ddra;
|
||||
ora = mos6522ptr->ora;
|
||||
mos6522ptr->ora = (ora & mask) | (val & (~mask));
|
||||
}
|
||||
|
||||
word32 g_mock_channel_regs[3] = {
|
||||
0x39c3, // channel A: regs 0,1,6,7,8,11,12,13
|
||||
0x3acc, // channel B: regs 2,3,6,7,9,11,12,13
|
||||
0x3cf0 // channel C: regs 4,5,6,7,10,11,12,13
|
||||
};
|
||||
|
||||
void
|
||||
mock_ay8913_reg_write(int pair_num, dword64 dfcyc)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
Ay8913 *ay8913ptr;
|
||||
double dsamps;
|
||||
word32 reg_addr_latch, ora, mask, rmask, do_print;
|
||||
int i;
|
||||
|
||||
mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);
|
||||
ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);
|
||||
reg_addr_latch = ay8913ptr->reg_addr_latch;
|
||||
ora = mos6522ptr->ora;
|
||||
dsamps = dfcyc * g_dsamps_per_dfcyc;
|
||||
if(reg_addr_latch < 16) {
|
||||
mask = (g_mockingboard.disable_mask >> (3*pair_num)) & 7;
|
||||
rmask = 0;
|
||||
do_print = 0;
|
||||
for(i = 0; i < 3; i++) {
|
||||
if(((mask >> i) & 1) == 0) {
|
||||
rmask |= g_mock_channel_regs[i];
|
||||
}
|
||||
}
|
||||
do_print = (rmask >> reg_addr_latch) & 1;
|
||||
if((ora != ay8913ptr->regs[reg_addr_latch]) ||
|
||||
(reg_addr_latch == 13)) {
|
||||
// New value, or writing to Envelope control
|
||||
do_print = 0;
|
||||
if(do_print) {
|
||||
printf("%.2lf %016llx mock pair%d reg[%d]="
|
||||
"%02x. [2,3]=%02x_%02x [67]=%02x,%02x, "
|
||||
"[9]=%02x, [12,11]=%02x_%02x [13]="
|
||||
"%02x\n", dsamps, dfcyc, pair_num,
|
||||
reg_addr_latch, ora,
|
||||
ay8913ptr->regs[3], ay8913ptr->regs[2],
|
||||
ay8913ptr->regs[6], ay8913ptr->regs[7],
|
||||
ay8913ptr->regs[9], ay8913ptr->regs[12],
|
||||
ay8913ptr->regs[11],
|
||||
ay8913ptr->regs[13]);
|
||||
}
|
||||
sound_play(dfcyc);
|
||||
}
|
||||
ay8913ptr->regs[reg_addr_latch] = ora;
|
||||
if(reg_addr_latch == 13) { // Envelope control
|
||||
ay8913ptr->env_dsamp &= 0x1fffffffffffULL;
|
||||
// Clear "hold" in (env_val & (0x80 << 40))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val,
|
||||
dword64 dfcyc)
|
||||
{
|
||||
Mos6522 *mos6522ptr;
|
||||
Ay8913 *ay8913ptr;
|
||||
|
||||
mos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);
|
||||
ay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);
|
||||
// printf("ay8913 %d control now:%02x\n", pair_num, new_val);
|
||||
|
||||
// new_val and prev_val are { reset_l, BDIR, BC1 }
|
||||
// 4=Idle; 5=Read; 6=Write; 7=Latch_addr
|
||||
// Latch new address and write data at the time the ctl changes to Idle
|
||||
// Do read as soon as the ctl indicates to do a read.
|
||||
|
||||
if((new_val & 4) == 0) {
|
||||
mock_ay8913_reset(pair_num, dfcyc);
|
||||
return;
|
||||
}
|
||||
new_val = new_val & 7;
|
||||
prev_val = prev_val & 7;
|
||||
if(prev_val == 7) { // Latch new address, latch it now
|
||||
ay8913ptr->reg_addr_latch = mos6522ptr->ora;
|
||||
} else if(prev_val == 6) { // Write data, do it now
|
||||
mock_ay8913_reg_write(pair_num, dfcyc);
|
||||
}
|
||||
if(new_val == 5) {
|
||||
mock_ay8913_reg_read(pair_num);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mockingboard_show(int got_num, word32 disable_mask)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if(got_num) {
|
||||
g_mockingboard.disable_mask = disable_mask;
|
||||
}
|
||||
printf("g_mockingboard.disable_mask:%02x\n",
|
||||
g_mockingboard.disable_mask);
|
||||
for(i = 0; i < 2; i++) {
|
||||
for(j = 0; j < 14; j++) {
|
||||
printf("Mockingboard pair[%d].reg[%d]=%02x\n", i, j,
|
||||
g_mockingboard.pair[i].ay8913.regs[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2274
gsplus/src/moremem.c
Normal file
2274
gsplus/src/moremem.c
Normal file
File diff suppressed because it is too large
Load Diff
143
gsplus/src/op_routs.h
Normal file
143
gsplus/src/op_routs.h
Normal file
@@ -0,0 +1,143 @@
|
||||
// $KmKId: op_routs.h,v 1.47 2023-11-05 16:21:51+00 kentd Exp $
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2021 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
#define GET_DLOC_X_IND_WR() \
|
||||
CYCLES_PLUS_1; \
|
||||
INC_KPC_2; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
arg = arg + xreg + direct; \
|
||||
GET_MEMORY_DIRECT_PAGE16(arg & 0xffff, arg, 1); \
|
||||
arg = (dbank << 16) + arg;
|
||||
|
||||
|
||||
#define GET_DLOC_X_IND_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_X_IND_WR()
|
||||
|
||||
#define GET_DISP8_S_WR() \
|
||||
CYCLES_PLUS_1; \
|
||||
arg = (arg + stack) & 0xffff; \
|
||||
INC_KPC_2;
|
||||
|
||||
|
||||
#define GET_DISP8_S_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DISP8_S_WR()
|
||||
|
||||
#define GET_DLOC_WR() \
|
||||
arg = (arg + direct) & 0xffff; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2;
|
||||
|
||||
#define GET_DLOC_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_WR()
|
||||
|
||||
#define GET_DLOC_L_IND_WR() \
|
||||
arg = (arg + direct) & 0xffff; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
INC_KPC_2; \
|
||||
GET_MEMORY24(arg, arg, 1);
|
||||
|
||||
#define GET_DLOC_L_IND_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_L_IND_WR()
|
||||
|
||||
|
||||
#define GET_DLOC_IND_WR() \
|
||||
INC_KPC_2; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0); \
|
||||
arg = (dbank << 16) + arg;
|
||||
|
||||
|
||||
#define GET_DLOC_IND_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_IND_WR();
|
||||
|
||||
#define GET_DLOC_INDEX_WR(index_reg) \
|
||||
CYCLES_PLUS_1; \
|
||||
arg = (arg & 0xff) + index_reg; \
|
||||
INC_KPC_2; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
if((psr & 0x100) && ((direct & 0xff) == 0)) { \
|
||||
arg = (arg & 0xff); \
|
||||
} \
|
||||
arg = (arg + direct) & 0xffff;
|
||||
|
||||
#define GET_DLOC_X_WR() \
|
||||
GET_DLOC_INDEX_WR(xreg)
|
||||
#define GET_DLOC_Y_WR() \
|
||||
GET_DLOC_INDEX_WR(yreg)
|
||||
|
||||
#define GET_DLOC_X_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_INDEX_WR(xreg)
|
||||
|
||||
#define GET_DLOC_Y_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_INDEX_WR(yreg)
|
||||
|
||||
#define GET_DISP8_S_IND_Y_WR() \
|
||||
arg = (stack + arg) & 0xffff; \
|
||||
GET_MEMORY16(arg,arg,1); \
|
||||
CYCLES_PLUS_2; \
|
||||
arg += (dbank << 16); \
|
||||
INC_KPC_2; \
|
||||
arg = (arg + yreg) & 0xffffff;
|
||||
|
||||
#define GET_DISP8_S_IND_Y_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DISP8_S_IND_Y_WR()
|
||||
|
||||
#define GET_DLOC_L_IND_Y_WR() \
|
||||
arg = (direct + arg) & 0xffff; \
|
||||
if(direct & 0xff) { \
|
||||
CYCLES_PLUS_1; \
|
||||
} \
|
||||
GET_MEMORY24(arg,arg,1); \
|
||||
INC_KPC_2; \
|
||||
arg = (arg + yreg) & 0xffffff;
|
||||
|
||||
#define GET_DLOC_L_IND_Y_ADDR() \
|
||||
GET_1BYTE_ARG; \
|
||||
GET_DLOC_L_IND_Y_WR()
|
||||
|
||||
|
||||
#define GET_ABS_ADDR() \
|
||||
GET_2BYTE_ARG; \
|
||||
CYCLES_PLUS_1; \
|
||||
arg = arg + (dbank << 16); \
|
||||
INC_KPC_3;
|
||||
|
||||
#define GET_LONG_ADDR() \
|
||||
GET_3BYTE_ARG; \
|
||||
CYCLES_PLUS_2; \
|
||||
INC_KPC_4;
|
||||
|
||||
#define GET_LONG_X_ADDR_FOR_WR() \
|
||||
GET_3BYTE_ARG; \
|
||||
INC_KPC_4; \
|
||||
arg = (arg + xreg) & 0xffffff; \
|
||||
CYCLES_PLUS_2;
|
||||
|
||||
187
gsplus/src/paddles.c
Normal file
187
gsplus/src/paddles.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
extern int g_mouse_raw_x; /* from adb.c */
|
||||
extern int g_mouse_raw_y;
|
||||
|
||||
dword64 g_paddle_trig_dfcyc = 0;
|
||||
int g_swap_paddles = 0;
|
||||
int g_invert_paddles = 0;
|
||||
int g_joystick_scale_factor_x = 0x100;
|
||||
int g_joystick_scale_factor_y = 0x100;
|
||||
int g_joystick_trim_amount_x = 0;
|
||||
int g_joystick_trim_amount_y = 0;
|
||||
|
||||
int g_joystick_type = 0; /* 0 = Keypad Joystick */
|
||||
int g_joystick_native_type1 = -1;
|
||||
int g_joystick_native_type2 = -1;
|
||||
int g_joystick_native_type = -1;
|
||||
|
||||
extern int g_paddle_buttons;
|
||||
|
||||
int g_paddle_val[4] = { 0, 0, 0, 0 };
|
||||
/* g_paddle_val[0]: Joystick X coord, [1]:Y coord */
|
||||
|
||||
dword64 g_paddle_dfcyc[4] = { 0, 0, 0, 0 };
|
||||
/* g_paddle_dfcyc are the dfcyc the paddle goes to 0 */
|
||||
|
||||
|
||||
void
|
||||
paddle_fixup_joystick_type()
|
||||
{
|
||||
/* If g_joystick_type points to an illegal value, change it */
|
||||
if(g_joystick_type == 2) {
|
||||
g_joystick_native_type = g_joystick_native_type1;
|
||||
if(g_joystick_native_type1 < 0) {
|
||||
g_joystick_type = 0;
|
||||
}
|
||||
}
|
||||
if(g_joystick_type == 3) {
|
||||
g_joystick_native_type = g_joystick_native_type2;
|
||||
if(g_joystick_native_type2 < 0) {
|
||||
g_joystick_type = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
paddle_trigger(dword64 dfcyc)
|
||||
{
|
||||
/* Called by read/write to $c070 */
|
||||
g_paddle_trig_dfcyc = dfcyc;
|
||||
|
||||
/* Determine what all the paddle values are right now */
|
||||
paddle_fixup_joystick_type();
|
||||
|
||||
switch(g_joystick_type) {
|
||||
case 0: /* Keypad Joystick */
|
||||
paddle_trigger_keypad(dfcyc);
|
||||
break;
|
||||
case 1: /* Mouse Joystick */
|
||||
paddle_trigger_mouse(dfcyc);
|
||||
break;
|
||||
default:
|
||||
joystick_update(dfcyc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
paddle_trigger_mouse(dword64 dfcyc)
|
||||
{
|
||||
int val_x, val_y;
|
||||
int mouse_x, mouse_y;
|
||||
|
||||
val_x = 0;
|
||||
|
||||
mouse_x = g_mouse_raw_x;
|
||||
mouse_y = g_mouse_raw_y;
|
||||
/* mous_phys_x is 0->560, convert that to -32768 to + 32767 cyc */
|
||||
/* So subtract 280 then mult by 117 */
|
||||
val_x = (mouse_x - 280) * 117;
|
||||
|
||||
/* mous_phys_y is 0->384, convert that to -32768 to + 32767 cyc */
|
||||
/* so subtract 192 then mult by 180 to overscale it a bit */
|
||||
val_y = (mouse_y - 192) * 180;
|
||||
|
||||
g_paddle_val[0] = val_x;
|
||||
g_paddle_val[1] = val_y;
|
||||
g_paddle_val[2] = 32767;
|
||||
g_paddle_val[3] = 32767;
|
||||
g_paddle_buttons |= 0xc;
|
||||
paddle_update_trigger_dcycs(dfcyc);
|
||||
}
|
||||
|
||||
void
|
||||
paddle_trigger_keypad(dword64 dfcyc)
|
||||
{
|
||||
int get_y, val_x, val_y;
|
||||
|
||||
val_x = adb_get_keypad_xy(get_y=0);
|
||||
val_y = adb_get_keypad_xy(get_y=1);
|
||||
/* val_x and val_y are already scale -32768 to +32768 */
|
||||
|
||||
g_paddle_val[0] = val_x;
|
||||
g_paddle_val[1] = val_y;
|
||||
g_paddle_val[2] = 32767;
|
||||
g_paddle_val[3] = 32767;
|
||||
g_paddle_buttons |= 0xc;
|
||||
paddle_update_trigger_dcycs(dfcyc);
|
||||
}
|
||||
|
||||
void
|
||||
paddle_update_trigger_dcycs(dword64 dfcyc)
|
||||
{
|
||||
dword64 trig_dfcyc;
|
||||
int val, paddle_num, scale, trim;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 4; i++) {
|
||||
paddle_num = i;
|
||||
if(g_swap_paddles) {
|
||||
paddle_num = i ^ 1;
|
||||
}
|
||||
val = g_paddle_val[paddle_num];
|
||||
if(g_invert_paddles) {
|
||||
val = -val;
|
||||
}
|
||||
/* convert -32768 to +32768 into 0->2816.0 cycles (the */
|
||||
/* paddle delay const) */
|
||||
/* First multiply by the scale factor to adjust range */
|
||||
if(paddle_num & 1) {
|
||||
scale = g_joystick_scale_factor_y;
|
||||
trim = g_joystick_trim_amount_y;
|
||||
} else {
|
||||
scale = g_joystick_scale_factor_x;
|
||||
trim = g_joystick_trim_amount_x;
|
||||
}
|
||||
#if 0
|
||||
if(i == 0) {
|
||||
printf("val was %04x(%d) * scale %03x = %d\n",
|
||||
val, val, scale, (val*scale)>>16);
|
||||
}
|
||||
#endif
|
||||
val = (val * scale) >> 16;
|
||||
/* Val is now from -128 to + 128 since scale is */
|
||||
/* 256=1.0, 128 = 0.5 */
|
||||
val = val + 128 + trim;
|
||||
if(val >= 255) {
|
||||
val = 280; /* increase range */
|
||||
}
|
||||
trig_dfcyc = dfcyc + (dword64)((val * (2816/255.0)) * 65536);
|
||||
g_paddle_dfcyc[i] = trig_dfcyc;
|
||||
if(i < 2) {
|
||||
dbg_log_info(dfcyc, (scale << 16) | (val & 0xffff),
|
||||
(trim << 16) | i, 0x70);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
read_paddles(dword64 dfcyc, int paddle)
|
||||
{
|
||||
dword64 trig_dfcyc;
|
||||
|
||||
trig_dfcyc = g_paddle_dfcyc[paddle & 3];
|
||||
|
||||
if(dfcyc < trig_dfcyc) {
|
||||
return 0x80;
|
||||
} else {
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
paddle_update_buttons()
|
||||
{
|
||||
paddle_fixup_joystick_type();
|
||||
joystick_update_buttons();
|
||||
}
|
||||
14
gsplus/src/protos.h
Normal file
14
gsplus/src/protos.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2019 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
#include "protos_base.h"
|
||||
895
gsplus/src/protos_base.h
Normal file
895
gsplus/src/protos_base.h
Normal file
@@ -0,0 +1,895 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2024 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
void halt_printf(const char *fmt, ...) __attribute__ ((
|
||||
__format__(printf, 1, 2)));
|
||||
void cfg_err_printf(const char *pre_str, const char *fmt, ...) __attribute__ ((
|
||||
__format__(printf, 2, 3)));
|
||||
void dynapro_error(Disk *dsk, const char *fmt, ...) __attribute__ ((
|
||||
__format__(printf, 2, 3)));
|
||||
#endif
|
||||
|
||||
/* xdriver.c and macdriver.c and windriver.c */
|
||||
int win_nonblock_read_stdin(int fd, char *bufptr, int len);
|
||||
|
||||
/* special scc_unixdriver.c prototypes */
|
||||
void scc_serial_unix_open(int port);
|
||||
void scc_serial_unix_close(int port);
|
||||
void scc_serial_unix_change_params(int port);
|
||||
void scc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left);
|
||||
void scc_serial_unix_empty_writebuf(int port);
|
||||
|
||||
/* special scc_windriver.c prototypes */
|
||||
void scc_serial_win_open(int port);
|
||||
void scc_serial_win_close(int port);
|
||||
void scc_serial_win_change_params(int port);
|
||||
void scc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left);
|
||||
void scc_serial_win_empty_writebuf(int port);
|
||||
|
||||
/* special joystick_driver.c prototypes */
|
||||
void joystick_init(void);
|
||||
void joystick_update(dword64 dfcyc);
|
||||
void joystick_update_buttons(void);
|
||||
|
||||
/* END_HDR */
|
||||
|
||||
/* adb.c */
|
||||
int adb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr);
|
||||
int adb_get_copy_requested(void);
|
||||
void adb_nonmain_check(void);
|
||||
void adb_init(void);
|
||||
void adb_reset(void);
|
||||
void adb_log(word32 addr, int val);
|
||||
void show_adb_log(void);
|
||||
void adb_error(void);
|
||||
void adb_add_kbd_srq(void);
|
||||
void adb_clear_kbd_srq(void);
|
||||
void adb_add_data_int(void);
|
||||
void adb_add_mouse_int(void);
|
||||
void adb_clear_data_int(void);
|
||||
void adb_clear_mouse_int(void);
|
||||
void adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2);
|
||||
void adb_send_1byte(word32 val);
|
||||
void adb_response_packet(int num_bytes, word32 val);
|
||||
void adb_kbd_reg0_data(int a2code, int is_up);
|
||||
void adb_kbd_talk_reg0(void);
|
||||
void adb_set_config(word32 val0, word32 val1, word32 val2);
|
||||
void adb_set_new_mode(word32 val);
|
||||
int adb_read_c026(void);
|
||||
void adb_write_c026(int val);
|
||||
void do_adb_cmd(void);
|
||||
int adb_read_c027(void);
|
||||
void adb_write_c027(int val);
|
||||
int read_adb_ram(word32 addr);
|
||||
void write_adb_ram(word32 addr, int val);
|
||||
int adb_get_keypad_xy(int get_y);
|
||||
int adb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states, int buttons_valid);
|
||||
int mouse_read_c024(dword64 dfcyc);
|
||||
void mouse_compress_fifo(dword64 dfcyc);
|
||||
void adb_paste_update_state(void);
|
||||
int adb_paste_add_buf(word32 key);
|
||||
void adb_key_event(int a2code, int is_up);
|
||||
word32 adb_read_c000(void);
|
||||
word32 adb_access_c010(void);
|
||||
word32 adb_read_c025(void);
|
||||
int adb_is_cmd_key_down(void);
|
||||
int adb_is_option_key_down(void);
|
||||
void adb_increment_speed(void);
|
||||
void adb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask);
|
||||
int adb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr);
|
||||
void adb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c, int is_up);
|
||||
void adb_maybe_virtual_key_update(int a2code, int is_up);
|
||||
void adb_virtual_key_update(int a2code, int is_up);
|
||||
void adb_kbd_repeat_off(void);
|
||||
void adb_mainwin_focus(int has_focus);
|
||||
|
||||
|
||||
|
||||
/* engine_c.c */
|
||||
word32 get_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1);
|
||||
word32 get_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);
|
||||
word32 get_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);
|
||||
void set_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1);
|
||||
void set_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, dword64 dplus_1, dword64 dplus_x_m1, int in_bank);
|
||||
void set_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);
|
||||
word32 get_memory_c(word32 addr);
|
||||
word32 get_memory16_c(word32 addr);
|
||||
word32 get_memory24_c(word32 addr);
|
||||
void set_memory_c(word32 addr, word32 val, int do_log);
|
||||
void set_memory16_c(word32 addr, word32 val, int do_log);
|
||||
void set_memory24_c(word32 addr, word32 val);
|
||||
word32 do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub);
|
||||
word32 do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub);
|
||||
void fixed_memory_ptrs_init(void);
|
||||
word32 get_itimer(void);
|
||||
void engine_recalc_events(void);
|
||||
void set_halt_act(int val);
|
||||
void clr_halt_act(void);
|
||||
word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, dword64 *dcyc_ptr, Fplus *fplus_ptr);
|
||||
int enter_engine(Engine_reg *engine_ptr);
|
||||
|
||||
|
||||
|
||||
/* clock.c */
|
||||
double get_dtime(void);
|
||||
int micro_sleep(double dtime);
|
||||
void clk_bram_zero(void);
|
||||
void clk_bram_set(int bram_num, int offset, int val);
|
||||
void clk_setup_bram_version(void);
|
||||
void clk_write_bram(FILE *fconf);
|
||||
void update_cur_time(void);
|
||||
void clock_update(void);
|
||||
void clock_update_if_needed(void);
|
||||
void clock_write_c034(word32 val);
|
||||
void do_clock_data(void);
|
||||
|
||||
|
||||
|
||||
/* compile_time.c */
|
||||
|
||||
|
||||
|
||||
/* config.c */
|
||||
int config_add_argv_override(const char *str1, const char *str2);
|
||||
void config_set_config_kegs_name(const char *str1);
|
||||
void config_init_menus(Cfg_menu *menuptr);
|
||||
void config_init(void);
|
||||
void cfg_find_config_kegs_file(void);
|
||||
int config_setup_kegs_file(char *outname, int maxlen, const char **name_ptr);
|
||||
int config_expand_path(char *out_ptr, const char *in_ptr, int maxlen);
|
||||
char *cfg_exit(int get_status);
|
||||
void cfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap);
|
||||
void cfg_err_printf(const char *pre_str, const char *fmt, ...);
|
||||
void cfg_toggle_config_panel(void);
|
||||
void cfg_set_config_panel(int panel);
|
||||
char *cfg_text_screen_dump(int get_status);
|
||||
char *cfg_text_screen_str(void);
|
||||
char *cfg_get_serial0_status(int get_status);
|
||||
char *cfg_get_serial1_status(int get_status);
|
||||
char *cfg_get_current_copy_selection(void);
|
||||
void config_vbl_update(int doit_3_persec);
|
||||
void cfg_file_update_rom(const char *str);
|
||||
void cfg_file_update_ptr(char **strptr, const char *str, int need_update);
|
||||
void cfg_int_update(int *iptr, int new_val);
|
||||
void cfg_load_charrom(void);
|
||||
void config_load_roms(void);
|
||||
void config_parse_config_kegs_file(void);
|
||||
void cfg_parse_one_line(char *buf, int line);
|
||||
void cfg_parse_bram(char *buf, int pos, int len);
|
||||
void cfg_parse_sxdx(char *buf, int pos, int len);
|
||||
void config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, int with_extras);
|
||||
char *config_write_config_kegs_file(int get_status);
|
||||
void insert_disk(int slot, int drive, const char *name, int ejected, const char *partition_name, int part_num, word32 dynamic_blocks);
|
||||
dword64 cfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot);
|
||||
dword64 cfg_get_fd_size(int fd);
|
||||
dword64 cfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize);
|
||||
dword64 cfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize);
|
||||
int cfg_partition_maybe_add_dotdot(void);
|
||||
int cfg_partition_name_check(const byte *name_ptr, int name_len);
|
||||
int cfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size);
|
||||
int cfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr, int part_num);
|
||||
int cfg_partition_make_list_from_name(const char *namestr);
|
||||
int cfg_partition_make_list(int fd);
|
||||
int cfg_maybe_insert_disk(int slot, int drive, const char *namestr);
|
||||
void cfg_insert_disk_dynapro(int slot, int drive, const char *name);
|
||||
int cfg_stat(char *path, struct stat *sb, int do_lstat);
|
||||
word32 cfg_get_le16(byte *bptr);
|
||||
word32 cfg_get_le32(byte *bptr);
|
||||
dword64 cfg_get_le64(byte *bptr);
|
||||
word32 cfg_get_be_word16(word16 *ptr);
|
||||
word32 cfg_get_be_word32(word32 *ptr);
|
||||
void cfg_set_le32(byte *bptr, word32 val);
|
||||
void config_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr);
|
||||
void cfg_htab_vtab(int x, int y);
|
||||
void cfg_home(void);
|
||||
void cfg_cleol(void);
|
||||
void cfg_putchar(int c);
|
||||
void cfg_printf(const char *fmt, ...);
|
||||
void cfg_print_dnum(dword64 dnum, int max_len);
|
||||
int cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras);
|
||||
int cfg_get_disk_locked(int type_ext);
|
||||
void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change);
|
||||
void cfg_get_base_path(char *pathptr, const char *inptr, int go_up);
|
||||
char *cfg_name_new_image(int get_status);
|
||||
void cfg_dup_existing_image(word32 slotdrive);
|
||||
void cfg_dup_image_selected(void);
|
||||
void cfg_validate_image(word32 slotdrive);
|
||||
void cfg_toggle_lock_disk(word32 slotdrive);
|
||||
int cfg_create_new_image_act(const char *str, int type, int size_blocks);
|
||||
void cfg_create_new_image(void);
|
||||
void cfg_file_init(void);
|
||||
void cfg_free_alldirents(Cfg_listhdr *listhdrptr);
|
||||
void cfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num);
|
||||
void cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num);
|
||||
int cfg_dirent_sortfn(const void *obj1, const void *obj2);
|
||||
int cfg_str_match(const char *str1, const char *str2, int len);
|
||||
int cfg_str_match_maybecase(const char *str1, const char *str2, int len, int ignorecase);
|
||||
int cfgcasecmp(const char *str1, const char *str2);
|
||||
int cfg_strlcat(char *dstptr, const char *srcptr, int dstsize);
|
||||
char *cfg_strncpy(char *dstptr, const char *srcptr, int dstsize);
|
||||
const char *cfg_str_basename(const char *str);
|
||||
char *cfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize);
|
||||
void cfg_file_readdir(const char *pathptr);
|
||||
char *cfg_shorten_filename(const char *in_ptr, int maxlen);
|
||||
void cfg_fix_topent(Cfg_listhdr *listhdrptr);
|
||||
void cfg_file_draw(void);
|
||||
void cfg_partition_select_all(void);
|
||||
void cfg_partition_selected(void);
|
||||
void cfg_file_selected(void);
|
||||
void cfg_file_handle_key(int key);
|
||||
void cfg_draw_menu(void);
|
||||
void cfg_newdisk_pick_menu(word32 slotdrive);
|
||||
int cfg_control_panel_update(void);
|
||||
void cfg_edit_mode_key(int key);
|
||||
int cfg_control_panel_update1(void);
|
||||
|
||||
|
||||
|
||||
/* debugger.c */
|
||||
void debugger_init(void);
|
||||
void check_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type);
|
||||
void debug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type, int pos);
|
||||
int debugger_run_16ms(void);
|
||||
void dbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type);
|
||||
void debugger_update_list_kpc(void);
|
||||
void debugger_key_event(Kimage *kimage_ptr, int a2code, int is_up);
|
||||
void debugger_page_updown(int isup);
|
||||
void debugger_redraw_screen(Kimage *kimage_ptr);
|
||||
void debug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line);
|
||||
void debugger_help(void);
|
||||
void dbg_help_show_strs(int help_depth, const char *str, const char *help_str);
|
||||
const char *debug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr, int help_depth);
|
||||
void do_debug_cmd(const char *in_str);
|
||||
word32 dis_get_memory_ptr(word32 addr);
|
||||
void show_one_toolset(FILE *toolfile, int toolnum, word32 addr);
|
||||
void show_toolset_tables(word32 a2bank, word32 addr);
|
||||
word32 debug_getnum(const char **str_ptr);
|
||||
char *debug_get_filename(const char **str_ptr);
|
||||
void debug_help(const char *str);
|
||||
void debug_bp(const char *str);
|
||||
void debug_bp_set(const char *str);
|
||||
void debug_bp_clear(const char *str);
|
||||
void debug_bp_clear_all(const char *str);
|
||||
void debug_bp_setclr(const char *str, int is_set_clear);
|
||||
void debug_soundfile(const char *cmd_str);
|
||||
void debug_logpc(const char *str);
|
||||
void debug_logpc_on(const char *str);
|
||||
void debug_logpc_off(const char *str);
|
||||
void debug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc);
|
||||
Data_log *debug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc, dword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr, int *count_ptr);
|
||||
void debug_logpc_save(const char *cmd_str);
|
||||
void set_bp(word32 addr, word32 end_addr, word32 acc_type);
|
||||
void show_bp(void);
|
||||
void delete_bp(word32 addr, word32 end_addr);
|
||||
void debug_iwm(const char *str);
|
||||
void debug_iwm_check(const char *str);
|
||||
int do_blank(int mode, int old_mode);
|
||||
void do_go(void);
|
||||
void do_step(void);
|
||||
void xam_mem(int count);
|
||||
void show_hex_mem(word32 startbank, word32 start, word32 end, int count);
|
||||
void do_debug_list(void);
|
||||
void dis_do_memmove(void);
|
||||
void dis_do_pattern_search(void);
|
||||
void dis_do_compare(void);
|
||||
const char *do_debug_unix(const char *str, int old_mode);
|
||||
void do_debug_load(void);
|
||||
char *do_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr, int *size_ptr);
|
||||
int debug_get_view_line(int back);
|
||||
int debug_add_output_line(char *in_str);
|
||||
void debug_add_output_string(char *in_str, int len);
|
||||
void debug_add_output_chars(char *str);
|
||||
int dbg_printf(const char *fmt, ...);
|
||||
int dbg_vprintf(const char *fmt, va_list args);
|
||||
void halt_printf(const char *fmt, ...);
|
||||
void halt2_printf(const char *fmt, ...);
|
||||
|
||||
|
||||
|
||||
/* scc.c */
|
||||
void scc_init(void);
|
||||
void scc_reset(void);
|
||||
void scc_hard_reset_port(int port);
|
||||
void scc_reset_port(int port);
|
||||
void scc_regen_clocks(int port);
|
||||
void scc_port_close(int port);
|
||||
void scc_port_open(dword64 dfcyc, int port);
|
||||
int scc_is_port_closed(dword64 dfcyc, int port);
|
||||
char *scc_get_serial_status(int get_status, int port);
|
||||
void scc_config_changed(int port, int cfg_changed, int remote_changed, int serial_dev_changed);
|
||||
void scc_update(dword64 dfcyc);
|
||||
void scc_try_to_empty_writebuf(dword64 dfcyc, int port);
|
||||
void scc_try_fill_readbuf(dword64 dfcyc, int port);
|
||||
void scc_do_event(dword64 dfcyc, int type);
|
||||
void show_scc_state(void);
|
||||
word32 scc_read_reg(dword64 dfcyc, int port);
|
||||
void scc_write_reg(dword64 dfcyc, int port, word32 val);
|
||||
word32 scc_read_data(dword64 dfcyc, int port);
|
||||
void scc_write_data(dword64 dfcyc, int port, word32 val);
|
||||
word32 scc_do_read_rr2b(void);
|
||||
void scc_maybe_br_event(dword64 dfcyc, int port);
|
||||
void scc_evaluate_ints(int port);
|
||||
void scc_maybe_rx_event(dword64 dfcyc, int port);
|
||||
void scc_maybe_rx_int(int port);
|
||||
void scc_clr_rx_int(int port);
|
||||
void scc_handle_tx_event(int port);
|
||||
void scc_maybe_tx_event(dword64 dfcyc, int port);
|
||||
void scc_clr_tx_int(int port);
|
||||
void scc_set_zerocnt_int(int port);
|
||||
void scc_clr_zerocnt_int(int port);
|
||||
void scc_add_to_readbuf(dword64 dfcyc, int port, word32 val);
|
||||
void scc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...);
|
||||
void scc_transmit(dword64 dfcyc, int port, word32 val);
|
||||
void scc_add_to_writebuf(dword64 dfcyc, int port, word32 val);
|
||||
|
||||
|
||||
|
||||
/* scc_socket_driver.c */
|
||||
void scc_socket_open(dword64 dfcyc, int port, int cfg);
|
||||
void scc_socket_close(int port);
|
||||
void scc_socket_close_extended(dword64 dfcyc, int port, int allow_retry);
|
||||
void scc_socket_maybe_open(dword64 dfcyc, int port, int must);
|
||||
void scc_socket_open_incoming(dword64 dfcyc, int port);
|
||||
void scc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str, int remote_port);
|
||||
void scc_socket_make_nonblock(dword64 dfcyc, int port);
|
||||
void scc_accept_socket(dword64 dfcyc, int port);
|
||||
void scc_socket_telnet_reqs(dword64 dfcyc, int port);
|
||||
void scc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left);
|
||||
void scc_socket_recvd_char(dword64 dfcyc, int port, int c);
|
||||
void scc_socket_empty_writebuf(dword64 dfcyc, int port);
|
||||
void scc_socket_modem_write(dword64 dfcyc, int port, int c);
|
||||
void scc_socket_do_cmd_str(dword64 dfcyc, int port);
|
||||
void scc_socket_send_modem_code(dword64 dfcyc, int port, int code);
|
||||
void scc_socket_modem_connect(dword64 dfcyc, int port);
|
||||
void scc_socket_modem_do_ring(dword64 dfcyc, int port);
|
||||
void scc_socket_do_answer(dword64 dfcyc, int port);
|
||||
|
||||
|
||||
|
||||
/* scc_windriver.c */
|
||||
|
||||
|
||||
|
||||
/* scc_unixdriver.c */
|
||||
|
||||
|
||||
|
||||
/* iwm.c */
|
||||
void iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525);
|
||||
void iwm_init(void);
|
||||
void iwm_reset(void);
|
||||
void disk_set_num_tracks(Disk *dsk, int num_tracks);
|
||||
word32 iwm_get_default_track_bits(Disk *dsk, word32 qtr_trk);
|
||||
void draw_iwm_status(int line, char *buf);
|
||||
void iwm_flush_cur_disk(void);
|
||||
void iwm_flush_disk_to_unix(Disk *dsk);
|
||||
void iwm_vbl_update(void);
|
||||
void iwm_update_fast_disk_emul(int fast_disk_emul_en);
|
||||
void iwm_show_stats(int slot_drive);
|
||||
Disk *iwm_get_dsk(word32 state);
|
||||
Disk *iwm_touch_switches(int loc, dword64 dfcyc);
|
||||
void iwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc);
|
||||
void iwm_move_to_qtr_track(Disk *dsk, word32 qtr_track);
|
||||
void iwm525_update_phases(Disk *dsk, dword64 dfcyc);
|
||||
void iwm525_update_head(Disk *dsk, dword64 dfcyc);
|
||||
int iwm_read_status35(dword64 dfcyc);
|
||||
void iwm_do_action35(dword64 dfcyc);
|
||||
int read_iwm(int loc, dword64 dfcyc);
|
||||
void write_iwm(int loc, int val, dword64 dfcyc);
|
||||
int iwm_read_enable2(dword64 dfcyc);
|
||||
int iwm_read_enable2_handshake(dword64 dfcyc);
|
||||
void iwm_write_enable2(int val);
|
||||
word32 iwm_fastemul_start_write(Disk *dsk, dword64 dfcyc);
|
||||
word32 iwm_read_data_fast(Disk *dsk, dword64 dfcyc);
|
||||
dword64 iwm_return_rand_data(Disk *dsk, dword64 dfcyc);
|
||||
dword64 iwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr);
|
||||
word32 iwm_calc_bit_diff(word32 first, word32 last, word32 track_bits);
|
||||
word32 iwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits);
|
||||
dword64 iwm_calc_forced_sync(dword64 dval, int forced_bit);
|
||||
int iwm_calc_forced_sync_0s(dword64 sync_dval, int bits);
|
||||
word32 iwm_read_data(Disk *dsk, dword64 dfcyc);
|
||||
void iwm_write_data(Disk *dsk, word32 val, dword64 dfcyc);
|
||||
void iwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior);
|
||||
int iwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta);
|
||||
void iwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc);
|
||||
void iwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc);
|
||||
void iwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc);
|
||||
void iwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc);
|
||||
void sector_to_partial_nib(byte *in, byte *nib_ptr);
|
||||
int disk_unnib_4x4(Disk *dsk);
|
||||
int iwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf);
|
||||
int iwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf);
|
||||
int iwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf);
|
||||
void show_hex_data(byte *buf, int count);
|
||||
void iwm_check_nibblization(dword64 dfcyc);
|
||||
void disk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc);
|
||||
void disk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len, int len_bits, dword64 dfcyc);
|
||||
void iwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len);
|
||||
void iwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc);
|
||||
void iwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len, dword64 dfcyc);
|
||||
void disk_4x4_nib_out(Disk *dsk, word32 val);
|
||||
void disk_nib_out(Disk *dsk, word32 val, int size);
|
||||
void disk_nib_end_track(Disk *dsk, dword64 dfcyc);
|
||||
word32 disk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits, word32 bit_pos, dword64 dfcyc);
|
||||
Disk *iwm_get_dsk_from_slot_drive(int slot, int drive);
|
||||
void iwm_toggle_lock(Disk *dsk);
|
||||
void iwm_eject_named_disk(int slot, int drive, const char *name, const char *partition_name);
|
||||
void iwm_eject_disk_by_num(int slot, int drive);
|
||||
void iwm_eject_disk(Disk *dsk);
|
||||
void iwm_show_track(int slot_drive, int track, dword64 dfcyc);
|
||||
void iwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc);
|
||||
void dummy1(word32 psr);
|
||||
void dummy2(word32 psr);
|
||||
|
||||
|
||||
|
||||
/* joystick_driver.c */
|
||||
void joystick_callback_init(int native_type);
|
||||
void joystick_callback_update(word32 buttons, int paddle_x, int paddle_y);
|
||||
|
||||
|
||||
|
||||
/* moremem.c */
|
||||
void fixup_brks(void);
|
||||
void fixup_hires_on(void);
|
||||
void fixup_bank0_2000_4000(void);
|
||||
void fixup_bank0_0400_0800(void);
|
||||
void fixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd, byte *mem0wr);
|
||||
void fixup_intcx(void);
|
||||
void fixup_st80col(dword64 dfcyc);
|
||||
void fixup_altzp(void);
|
||||
void fixup_page2(dword64 dfcyc);
|
||||
void fixup_ramrd(void);
|
||||
void fixup_ramwrt(void);
|
||||
void fixup_lc(void);
|
||||
void set_statereg(dword64 dfcyc, word32 val);
|
||||
void fixup_shadow_txt1(void);
|
||||
void fixup_shadow_txt2(void);
|
||||
void fixup_shadow_hires1(void);
|
||||
void fixup_shadow_hires2(void);
|
||||
void fixup_shadow_shr(void);
|
||||
void fixup_shadow_iolc(void);
|
||||
void update_shadow_reg(dword64 dfcyc, word32 val);
|
||||
void fixup_shadow_all_banks(void);
|
||||
void setup_pageinfo(void);
|
||||
void show_bankptrs_bank0rdwr(void);
|
||||
void show_bankptrs(int bnk);
|
||||
void show_addr(byte *ptr);
|
||||
word32 moremem_fix_vector_pull(word32 addr);
|
||||
word32 io_read(word32 loc, dword64 *cyc_ptr);
|
||||
void io_write(word32 loc, word32 val, dword64 *cyc_ptr);
|
||||
word32 slinky_devsel_read(dword64 dfcyc, word32 loc);
|
||||
void slinky_devsel_write(dword64 dfcyc, word32 loc, word32 val);
|
||||
word32 c3xx_read(dword64 dfcyc, word32 loc);
|
||||
word32 get_lines_since_vbl(dword64 dfcyc);
|
||||
int in_vblank(dword64 dfcyc);
|
||||
int read_vid_counters(int loc, dword64 dfcyc);
|
||||
|
||||
|
||||
|
||||
/* paddles.c */
|
||||
void paddle_fixup_joystick_type(void);
|
||||
void paddle_trigger(dword64 dfcyc);
|
||||
void paddle_trigger_mouse(dword64 dfcyc);
|
||||
void paddle_trigger_keypad(dword64 dfcyc);
|
||||
void paddle_update_trigger_dcycs(dword64 dfcyc);
|
||||
int read_paddles(dword64 dfcyc, int paddle);
|
||||
void paddle_update_buttons(void);
|
||||
|
||||
|
||||
|
||||
/* mockingboard.c */
|
||||
void mock_ay8913_reset(int pair_num, dword64 dfcyc);
|
||||
void mockingboard_reset(dword64 dfcyc);
|
||||
void mock_show_pair(int pair_num, dword64 dfcyc, const char *str);
|
||||
void mock_update_timers(int doit, dword64 dfcyc);
|
||||
void mockingboard_event(dword64 dfcyc);
|
||||
word32 mockingboard_read(word32 loc, dword64 dfcyc);
|
||||
void mockingboard_write(word32 loc, word32 val, dword64 dfcyc);
|
||||
word32 mock_6522_read(int pair_num, word32 loc, dword64 dfcyc);
|
||||
void mock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc);
|
||||
word32 mock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier);
|
||||
void mock_ay8913_reg_read(int pair_num);
|
||||
void mock_ay8913_reg_write(int pair_num, dword64 dfcyc);
|
||||
void mock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val, dword64 dfcyc);
|
||||
void mockingboard_show(int got_num, word32 disable_mask);
|
||||
|
||||
|
||||
|
||||
/* sim65816.c */
|
||||
int sim_get_force_depth(void);
|
||||
int sim_get_use_shmem(void);
|
||||
void sim_set_use_shmem(int use_shmem);
|
||||
word32 toolbox_debug_4byte(word32 addr);
|
||||
void toolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr);
|
||||
void show_toolbox_log(void);
|
||||
word32 get_memory_io(word32 loc, dword64 *dcyc_ptr);
|
||||
void set_memory_io(word32 loc, int val, dword64 *dcyc_ptr);
|
||||
void show_regs_act(Engine_reg *eptr);
|
||||
void show_regs(void);
|
||||
void my_exit(int ret);
|
||||
void do_reset(void);
|
||||
byte *memalloc_align(int size, int skip_amt, void **alloc_ptr);
|
||||
void memory_ptr_init(void);
|
||||
int parse_argv(int argc, char **argv, int slashes_to_find);
|
||||
int kegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window);
|
||||
void load_roms_init_memory(void);
|
||||
void initialize_events(void);
|
||||
void check_for_one_event_type(int type, word32 mask);
|
||||
void add_event_entry(dword64 dfcyc, int type);
|
||||
dword64 remove_event_entry(int type, word32 mask);
|
||||
void add_event_stop(dword64 dfcyc);
|
||||
void add_event_doc(dword64 dfcyc, int osc);
|
||||
void add_event_scc(dword64 dfcyc, int type);
|
||||
void add_event_vbl(void);
|
||||
void add_event_vid_upd(int line);
|
||||
void add_event_mockingboard(dword64 dfcyc);
|
||||
void add_event_scan_int(dword64 dfcyc, int line);
|
||||
dword64 remove_event_doc(int osc);
|
||||
dword64 remove_event_scc(int type);
|
||||
void remove_event_mockingboard(void);
|
||||
void show_all_events(void);
|
||||
void show_pmhz(void);
|
||||
void setup_zip_speeds(void);
|
||||
int run_16ms(void);
|
||||
int run_a2_one_vbl(void);
|
||||
void add_irq(word32 irq_mask);
|
||||
void remove_irq(word32 irq_mask);
|
||||
void take_irq(void);
|
||||
void show_dtime_array(void);
|
||||
void update_60hz(dword64 dfcyc, double dtime_now);
|
||||
void do_vbl_int(void);
|
||||
void do_scan_int(dword64 dfcyc, int line);
|
||||
void check_scan_line_int(int cur_video_line);
|
||||
void check_for_new_scan_int(dword64 dfcyc);
|
||||
void scb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val);
|
||||
void init_reg(void);
|
||||
void handle_action(word32 ret);
|
||||
void do_break(word32 ret);
|
||||
void do_cop(word32 ret);
|
||||
void do_wdm(word32 arg);
|
||||
void do_wai(void);
|
||||
void do_stp(void);
|
||||
void do_wdm_emulator_id(void);
|
||||
void size_fail(int val, word32 v1, word32 v2);
|
||||
int fatal_printf(const char *fmt, ...);
|
||||
int kegs_vprintf(const char *fmt, va_list ap);
|
||||
dword64 must_write(int fd, byte *bufptr, dword64 dsize);
|
||||
void clear_fatal_logs(void);
|
||||
char *kegs_malloc_str(const char *in_str);
|
||||
dword64 kegs_lseek(int fd, dword64 offs, int whence);
|
||||
|
||||
|
||||
|
||||
/* smartport.c */
|
||||
void smartport_error(void);
|
||||
void smartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list);
|
||||
void do_c70d(word32 arg0);
|
||||
void do_c70a(word32 arg0);
|
||||
int do_read_c7(int unit_num, word32 buf, word32 blk);
|
||||
int do_write_c7(int unit_num, word32 buf, word32 blk);
|
||||
int smartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size);
|
||||
int do_format_c7(int unit_num);
|
||||
void do_c700(word32 ret);
|
||||
|
||||
|
||||
|
||||
/* doc.c */
|
||||
void doc_init(void);
|
||||
void doc_reset(dword64 dfcyc);
|
||||
int doc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps, int snd_buf_init, int *outptr_start);
|
||||
void doc_handle_event(int osc, dword64 dfcyc);
|
||||
void doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps);
|
||||
void doc_add_sound_irq(int osc);
|
||||
void doc_remove_sound_irq(int osc, int must);
|
||||
void doc_start_sound2(int osc, dword64 dfcyc);
|
||||
void doc_start_sound(int osc, double eff_dsamps, double dsamps);
|
||||
void doc_wave_end_estimate2(int osc, dword64 dfcyc);
|
||||
void doc_wave_end_estimate(int osc, double eff_dsamps, double dsamps);
|
||||
void doc_remove_sound_event(int osc);
|
||||
void doc_write_ctl_reg(dword64 dfcyc, int osc, int val);
|
||||
void doc_recalc_sound_parms(dword64 dfcyc, int osc);
|
||||
int doc_read_c03c(void);
|
||||
int doc_read_c03d(dword64 dfcyc);
|
||||
void doc_write_c03c(dword64 dfcyc, word32 val);
|
||||
void doc_write_c03d(dword64 dfcyc, word32 val);
|
||||
void doc_show_ensoniq_state(void);
|
||||
|
||||
|
||||
|
||||
/* sound.c */
|
||||
void sound_init(void);
|
||||
void sound_set_audio_rate(int rate);
|
||||
void sound_reset(dword64 dfcyc);
|
||||
void sound_shutdown(void);
|
||||
void sound_update(dword64 dfcyc);
|
||||
void sound_file_start(char *filename);
|
||||
void sound_file_open(void);
|
||||
void sound_file_close(void);
|
||||
void send_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps);
|
||||
void show_c030_state(dword64 dfcyc);
|
||||
void show_c030_samps(dword64 dfcyc, int *outptr, int num);
|
||||
int sound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps);
|
||||
void sound_play(dword64 dfcyc);
|
||||
void sound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr);
|
||||
void sound_mock_noise(int pair, byte *noise_ptr, int num_samps);
|
||||
void sound_mock_play(int pair, int channel, int *outptr, int *env_ptr, byte *noise_ptr, int *vol_ptr, int num_samps);
|
||||
word32 sound_read_c030(dword64 dfcyc);
|
||||
void sound_write_c030(dword64 dfcyc);
|
||||
|
||||
|
||||
|
||||
/* sound_driver.c */
|
||||
void snddrv_init(void);
|
||||
void sound_child_fork(int size);
|
||||
void parent_sound_get_sample_rate(int read_fd);
|
||||
void snddrv_shutdown(void);
|
||||
void snddrv_send_sound(int real_samps, int size);
|
||||
void child_sound_playit(word32 tmp);
|
||||
void reliable_buf_write(word32 *shm_addr, int pos, int size);
|
||||
void reliable_zero_write(int amt);
|
||||
int child_send_samples(byte *ptr, int size);
|
||||
void child_sound_loop(int read_fd, int write_fd, word32 *shm_addr);
|
||||
|
||||
|
||||
|
||||
/* woz.c */
|
||||
void woz_crc_init(void);
|
||||
word32 woz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip);
|
||||
void woz_rewrite_crc(Disk *dsk, int min_write_size);
|
||||
void woz_rewrite_lock(Disk *dsk);
|
||||
void woz_check_file(Disk *dsk);
|
||||
void woz_parse_meta(Disk *dsk, int offset, int size);
|
||||
void woz_parse_info(Disk *dsk, int offset, int size);
|
||||
void woz_parse_tmap(Disk *dsk, int offset, int size);
|
||||
void woz_parse_trks(Disk *dsk, int offset, int size);
|
||||
int woz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc);
|
||||
int woz_parse_header(Disk *dsk);
|
||||
Woz_info *woz_malloc(byte *wozptr, word32 woz_size);
|
||||
int woz_reopen(Disk *dsk, dword64 dfcyc);
|
||||
int woz_open(Disk *dsk, dword64 dfcyc);
|
||||
byte *woz_append_bytes(byte *wozptr, byte *in_bptr, int len);
|
||||
byte *woz_append_word32(byte *wozptr, word32 val);
|
||||
int woz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length, byte *bptr);
|
||||
byte *woz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr, word32 *num_blocks_ptr, dword64 *tmap_dptr);
|
||||
Woz_info *woz_new_from_woz(Disk *dsk, int disk_525);
|
||||
int woz_new(int fd, const char *str, int size_kb);
|
||||
void woz_maybe_reparse(Disk *dsk);
|
||||
void woz_set_reparse(Disk *dsk);
|
||||
void woz_reparse_woz(Disk *dsk);
|
||||
void woz_remove_a_track(Disk *dsk, word32 qtr_track);
|
||||
word32 woz_add_a_track(Disk *dsk, word32 qtr_track);
|
||||
|
||||
|
||||
|
||||
/* unshk.c */
|
||||
word32 unshk_get_long4(byte *bptr);
|
||||
word32 unshk_get_word2(byte *bptr);
|
||||
word32 unshk_calc_crc(byte *bptr, int size, word32 start_crc);
|
||||
int unshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr);
|
||||
void unshk_lzw_clear(Lzw_state *lzw_ptr);
|
||||
byte *unshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen);
|
||||
void unshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr, word32 uncompr_size, word32 thread_format, byte *base_cptr);
|
||||
void unshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr);
|
||||
void unshk(Disk *dsk, const char *name_str);
|
||||
void unshk_dsk_raw_data(Disk *dsk);
|
||||
|
||||
|
||||
|
||||
/* undeflate.c */
|
||||
void *undeflate_realloc(void *ptr, dword64 dsize);
|
||||
void *undeflate_malloc(dword64 dsize);
|
||||
void show_bits(unsigned *llptr, int nl);
|
||||
void show_huftb(unsigned *tabptr, int bits);
|
||||
void undeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start);
|
||||
void undeflate_init_bit_rev_tab(word32 *tabptr, int num);
|
||||
word32 undeflate_bit_reverse(word32 val, word32 bits);
|
||||
word32 undeflate_calc_crc32(byte *bptr, word32 len);
|
||||
byte *undeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len);
|
||||
void undeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code, word32 entry);
|
||||
word32 *undeflate_init_fixed_tabs(void);
|
||||
word32 *undeflate_init_tables(void);
|
||||
void undeflate_free_tables(void);
|
||||
void undeflate_check_bit_reverse(void);
|
||||
word32 *undeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size, word32 *bl_count_ptr, int max_bits);
|
||||
word32 *undeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base);
|
||||
byte *undeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base, byte *cptr_end);
|
||||
byte *undeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size);
|
||||
void undeflate_gzip(Disk *dsk, const char *name_str);
|
||||
byte *undeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size);
|
||||
int undeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off, dword64 uncompr_dsize, dword64 compr_dsize);
|
||||
int undeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len, int min_size);
|
||||
int undeflate_zipfile_make_list(int fd);
|
||||
|
||||
|
||||
|
||||
/* dynapro.c */
|
||||
word32 dynapro_get_word32(byte *bptr);
|
||||
word32 dynapro_get_word24(byte *bptr);
|
||||
word32 dynapro_get_word16(byte *bptr);
|
||||
void dynapro_set_word24(byte *bptr, word32 val);
|
||||
void dynapro_set_word32(byte *bptr, word32 val);
|
||||
void dynapro_set_word16(byte *bptr, word32 val);
|
||||
void dynapro_error(Disk *dsk, const char *fmt, ...);
|
||||
Dynapro_file *dynapro_alloc_file(void);
|
||||
void dynapro_free_file(Dynapro_file *fileptr, int check_map);
|
||||
void dynapro_free_recursive_file(Dynapro_file *fileptr, int check_map);
|
||||
void dynapro_free_dynapro_info(Disk *dsk);
|
||||
word32 dynapro_find_free_block_internal(Disk *dsk);
|
||||
word32 dynapro_find_free_block(Disk *dsk);
|
||||
byte *dynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size);
|
||||
void dynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str, int path_max);
|
||||
word32 dynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr, char *buf32_ptr, word32 dir_byte);
|
||||
word32 dynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr);
|
||||
word32 dynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte);
|
||||
void dynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_try_fix_damaged_disk(Disk *dsk);
|
||||
void dynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str, const char *name_str);
|
||||
Dynapro_file *dynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file **head_ptr_ptr, word32 dir_byte);
|
||||
void dynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file *head_ptr, word32 dir_byte);
|
||||
word32 dynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr);
|
||||
word32 dynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size);
|
||||
void dynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_unlink_file(Dynapro_file *fileptr);
|
||||
void dynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr);
|
||||
void dynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr);
|
||||
int dynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size);
|
||||
void dynapro_debug_update(Disk *dsk);
|
||||
void dynapro_debug_map(Disk *dsk, const char *str);
|
||||
void dynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start);
|
||||
word32 dynapro_unix_to_prodos_time(const time_t *time_ptr);
|
||||
int dynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr, word32 storage_type);
|
||||
Dynapro_file *dynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr, Dynapro_file *match_ptr, word32 storage_type);
|
||||
int dynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr, word32 dir_byte);
|
||||
word32 dynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr, word32 dir_byte, word32 inc);
|
||||
word32 dynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr, word32 key_block, dword64 dsize);
|
||||
word32 dynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr);
|
||||
word32 dynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks);
|
||||
word32 dynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num, word32 file_offset, word32 eof);
|
||||
word32 dynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num, int level, word32 file_offset, word32 eof);
|
||||
word32 dynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data);
|
||||
word32 dynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr);
|
||||
word32 dynapro_build_map(Disk *dsk, Dynapro_file *fileptr);
|
||||
int dynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks);
|
||||
|
||||
|
||||
|
||||
/* dyna_type.c */
|
||||
word32 dynatype_scan_extensions(const char *str);
|
||||
word32 dynatype_find_prodos_type(const char *str);
|
||||
const char *dynatype_find_file_type(word32 file_type);
|
||||
word32 dynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr, word32 storage_type);
|
||||
int dynatype_get_extension(const char *str, char *out_ptr, int buf_len);
|
||||
int dynatype_comma_arg(const char *str, word32 *type_or_aux_ptr);
|
||||
void dynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max);
|
||||
|
||||
|
||||
|
||||
/* dyna_filt.c */
|
||||
|
||||
|
||||
|
||||
/* dyna_validate.c */
|
||||
word32 dynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte, word32 parent_dir_byte);
|
||||
void dynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks);
|
||||
word32 dynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block);
|
||||
word32 dynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof, int level_first);
|
||||
word32 dynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof);
|
||||
word32 dynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte, word32 parent_dir_byte, word32 exp_blocks_used);
|
||||
int dynapro_validate_disk(Disk *dsk);
|
||||
void dynapro_validate_any_image(Disk *dsk);
|
||||
|
||||
|
||||
|
||||
/* applesingle.c */
|
||||
word32 applesingle_get_be32(const byte *bptr);
|
||||
word32 applesingle_get_be16(const byte *bptr);
|
||||
void applesingle_set_be32(byte *bptr, word32 val);
|
||||
void applesingle_set_be16(byte *bptr, word32 val);
|
||||
word32 applesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data);
|
||||
word32 applesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr, dword64 dsize);
|
||||
word32 applesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length);
|
||||
|
||||
|
||||
|
||||
/* video.c */
|
||||
void video_set_red_mask(word32 red_mask);
|
||||
void video_set_green_mask(word32 green_mask);
|
||||
void video_set_blue_mask(word32 blue_mask);
|
||||
void video_set_alpha_mask(word32 alpha_mask);
|
||||
void video_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, int *shift_right_ptr);
|
||||
void video_set_palette(void);
|
||||
void video_set_redraw_skip_amt(int amt);
|
||||
Kimage *video_get_kimage(int win_id);
|
||||
char *video_get_status_ptr(int line);
|
||||
void video_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh);
|
||||
int video_get_active(Kimage *kimage_ptr);
|
||||
void video_set_active(Kimage *kimage_ptr, int active);
|
||||
void video_init(int mdepth, int screen_width, int screen_height, int no_scale_window);
|
||||
void video_init_kimage(Kimage *kimage_ptr, int width, int height, int screen_width, int screen_height);
|
||||
void show_a2_line_stuff(void);
|
||||
void video_reset(void);
|
||||
void video_update(void);
|
||||
word32 video_all_stat_to_filt_stat(int line, word32 new_all_stat);
|
||||
void change_display_mode(dword64 dfcyc);
|
||||
void video_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl);
|
||||
void change_border_color(dword64 dfcyc, int val);
|
||||
void update_border_info(void);
|
||||
void update_border_line(int st_line_offset, int end_line_offset, int color);
|
||||
void video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, int color, int st_off, int end_off);
|
||||
word32 video_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse);
|
||||
void video_update_edges(int line, int left, int right, const char *str);
|
||||
void redraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);
|
||||
void redraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask, word32 *in_wptr, word32 bg_pixel, word32 fg_pixel, int pixels_per_line, int dbl);
|
||||
void redraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);
|
||||
void video_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte, int end_byte, int pixels_per_line, word32 filt_stat);
|
||||
void redraw_changed_hgr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);
|
||||
int video_rebuild_super_hires_palette(int bank, word32 scan_info, int line, int reparse);
|
||||
word32 redraw_changed_super_hires_oneline(int bank, word32 *in_wptr, int pixels_per_line, int y, int scan, word32 ch_mask);
|
||||
void redraw_changed_super_hires_bank(int bank, int start_line, int reparse, word32 *wptr, int pixels_per_line);
|
||||
void redraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr, int pixels_per_line, word32 filt_stat);
|
||||
void video_copy_changed2(void);
|
||||
void video_update_event_line(int line);
|
||||
void video_force_reparse(void);
|
||||
void video_update_through_line(int line);
|
||||
void video_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat);
|
||||
void video_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat);
|
||||
void prepare_a2_font(void);
|
||||
void prepare_a2_romx_font(byte *font_ptr);
|
||||
void video_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height);
|
||||
void video_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix);
|
||||
void video_form_change_rects(void);
|
||||
int video_get_a2_width(Kimage *kimage_ptr);
|
||||
int video_get_x_width(Kimage *kimage_ptr);
|
||||
int video_get_a2_height(Kimage *kimage_ptr);
|
||||
int video_get_x_height(Kimage *kimage_ptr);
|
||||
int video_get_x_xpos(Kimage *kimage_ptr);
|
||||
int video_get_x_ypos(Kimage *kimage_ptr);
|
||||
void video_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos);
|
||||
int video_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height);
|
||||
void video_update_status_enable(Kimage *kimage_ptr);
|
||||
int video_out_query(Kimage *kimage_ptr);
|
||||
void video_out_done(Kimage *kimage_ptr);
|
||||
int video_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr, int pos);
|
||||
int video_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr);
|
||||
int video_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr);
|
||||
word32 video_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv);
|
||||
void video_update_scale(Kimage *kimage_ptr, int out_width, int out_height, int must_update);
|
||||
int video_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width);
|
||||
int video_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height);
|
||||
int video_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width);
|
||||
int video_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height);
|
||||
void video_update_color_raw(int bank, int col_num, int a2_color);
|
||||
void video_update_status_line(int line, const char *string);
|
||||
void video_draw_a2_string(int line, const byte *bptr);
|
||||
void video_show_debug_info(void);
|
||||
word32 read_video_data(dword64 dfcyc);
|
||||
word32 float_bus(dword64 dfcyc);
|
||||
word32 float_bus_lines(dword64 dfcyc, word32 lines_since_vbl);
|
||||
|
||||
|
||||
|
||||
/* voc.c */
|
||||
word32 voc_devsel_read(word32 loc, dword64 dfcyc);
|
||||
void voc_devsel_write(word32 loc, word32 val, dword64 dfcyc);
|
||||
void voc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc);
|
||||
void voc_reset(void);
|
||||
word32 voc_read_reg0(dword64 dfcyc);
|
||||
void voc_update_interlace(dword64 dfcyc);
|
||||
|
||||
|
||||
43
gsplus/src/protos_macdriver.h
Normal file
43
gsplus/src/protos_macdriver.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2019 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
/* END_HDR */
|
||||
|
||||
/* macdriver.c */
|
||||
pascal OSStatus quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore);
|
||||
void show_simple_alert(char *str1, char *str2, char *str3, int num);
|
||||
void x_dialog_create_kegs_conf(const char *str);
|
||||
int x_show_alert(int is_fatal, const char *str);
|
||||
pascal OSStatus my_cmd_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);
|
||||
void update_window(void);
|
||||
void show_event(UInt32 event_class, UInt32 event_kind, int handled);
|
||||
pascal OSStatus my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);
|
||||
pascal OSStatus dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, void *ignore);
|
||||
void mac_update_modifiers(word32 state);
|
||||
void mac_warp_mouse(void);
|
||||
void check_input_events(void);
|
||||
void temp_run_application_event_loop(void);
|
||||
int main(int argc, char *argv[]);
|
||||
void x_update_color(int col_num, int red, int green, int blue, word32 rgb);
|
||||
void x_update_physical_colormap(void);
|
||||
void show_xcolor_array(void);
|
||||
void xdriver_end(void);
|
||||
void x_get_kimage(Kimage *kimage_ptr);
|
||||
void dev_video_init(void);
|
||||
void x_redraw_status_lines(void);
|
||||
void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height);
|
||||
void x_push_done(void);
|
||||
void x_auto_repeat_on(int must);
|
||||
void x_auto_repeat_off(int must);
|
||||
void x_hide_pointer(int do_hide);
|
||||
void x_full_screen(int do_full);
|
||||
void update_main_window_size(void);
|
||||
|
||||
18
gsplus/src/protos_macsnd_driver.h
Normal file
18
gsplus/src/protos_macsnd_driver.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
/* END_HDR */
|
||||
|
||||
/* macsnd_driver.c */
|
||||
int mac_send_audio(byte *ptr, int in_size);
|
||||
void macsnd_init(void);
|
||||
|
||||
|
||||
22
gsplus/src/protos_pulseaudio_driver.h
Normal file
22
gsplus/src/protos_pulseaudio_driver.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2020 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
/* END_HDR */
|
||||
|
||||
/* pulseaudio_driver.c */
|
||||
int pulse_audio_send_audio(byte *ptr, int in_size);
|
||||
void pulse_audio_main_events(void);
|
||||
void pulse_audio_write_to_stream(int dbg_count, int in_sz);
|
||||
int pulse_audio_start_stream(void);
|
||||
int pulse_audio_do_init(void);
|
||||
int pulse_audio_init(void);
|
||||
void pulse_audio_shutdown(void);
|
||||
|
||||
55
gsplus/src/protos_windriver.h
Normal file
55
gsplus/src/protos_windriver.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2022 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// $KmKId: protos_windriver.h,v 1.15 2023-05-17 22:37:57+00 kentd Exp $
|
||||
|
||||
/* END_HDR */
|
||||
|
||||
/* windriver.c */
|
||||
Window_info *win_find_win_info_ptr(HWND hwnd);
|
||||
void win_hide_pointer(Window_info *win_info_ptr, int do_hide);
|
||||
int win_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid);
|
||||
void win_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam);
|
||||
void win_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down);
|
||||
void win_event_redraw(HWND hwnd);
|
||||
void win_event_destroy(HWND hwnd);
|
||||
void win_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam);
|
||||
void win_event_minmaxinfo(HWND hwnd, LPARAM lParam);
|
||||
void win_event_focus(HWND hwnd, int gain_focus);
|
||||
LRESULT CALLBACK win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam);
|
||||
int main(int argc, char **argv);
|
||||
void check_input_events(void);
|
||||
void win_video_init(int mdepth);
|
||||
void win_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str, int mdepth);
|
||||
void win_create_window(Window_info *win_info_ptr);
|
||||
void xdriver_end(void);
|
||||
void win_resize_window(Window_info *win_info_ptr);
|
||||
void x_update_display(Window_info *win_info_ptr);
|
||||
void x_hide_pointer(int do_hide);
|
||||
int opendir_int(DIR *dirp, const char *in_filename);
|
||||
DIR *opendir(const char *in_filename);
|
||||
struct dirent *readdir(DIR *dirp);
|
||||
int closedir(DIR *dirp);
|
||||
int lstat(const char *path, struct stat *bufptr);
|
||||
int ftruncate(int fd, word32 length);
|
||||
|
||||
|
||||
|
||||
/* win32snd_driver.c */
|
||||
void win32snd_init(word32 *shmaddr);
|
||||
void win32snd_shutdown(void);
|
||||
void CALLBACK handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||
void check_wave_error(int res, char *str);
|
||||
void child_sound_init_win32(void);
|
||||
void win32snd_set_playing(int snd_playing);
|
||||
void win32_send_audio2(byte *ptr, int size);
|
||||
int win32_send_audio(byte *ptr, int in_size);
|
||||
|
||||
|
||||
49
gsplus/src/protos_xdriver.h
Normal file
49
gsplus/src/protos_xdriver.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
/* END_HDR */
|
||||
|
||||
/* xdriver.c */
|
||||
int main(int argc, char **argv);
|
||||
int my_error_handler(Display *display, XErrorEvent *ev);
|
||||
void xdriver_end(void);
|
||||
void x_try_xset_r(void);
|
||||
void x_badpipe(int signum);
|
||||
int kegs_x_io_error_handler(Display *display);
|
||||
int x_video_get_mdepth(void);
|
||||
int x_try_find_visual(int depth, int screen_num);
|
||||
void x_video_init(void);
|
||||
void x_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str);
|
||||
void x_create_window(Window_info *win_info_ptr);
|
||||
int xhandle_shm_error(Display *display, XErrorEvent *event);
|
||||
void x_allocate_window_data(Window_info *win_info_ptr);
|
||||
void get_shm(Window_info *win_info_ptr, int width, int height);
|
||||
void get_ximage(Window_info *win_info_ptr, int width, int height);
|
||||
void x_set_size_hints(Window_info *win_info_ptr);
|
||||
void x_resize_window(Window_info *win_info_ptr);
|
||||
void x_update_display(Window_info *win_info_ptr);
|
||||
Window_info *x_find_xwin(Window in_win);
|
||||
void x_send_copy_data(Window_info *win_info_ptr);
|
||||
void x_handle_copy(XSelectionRequestEvent *req_ev_ptr);
|
||||
void x_handle_targets(XSelectionRequestEvent *req_ev_ptr);
|
||||
void x_request_paste_data(Window_info *win_info_ptr);
|
||||
void x_handle_paste(Window w, Atom property);
|
||||
int x_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid);
|
||||
void x_input_events(void);
|
||||
void x_hide_pointer(Window_info *win_info_ptr, int do_hide);
|
||||
void x_handle_keysym(XEvent *xev_in);
|
||||
int x_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up);
|
||||
void x_update_modifier_state(Window_info *win_info_ptr, int state);
|
||||
void x_auto_repeat_on(int must);
|
||||
void x_auto_repeat_off(int must);
|
||||
void x_full_screen(int do_full);
|
||||
|
||||
|
||||
364
gsplus/src/pulseaudio_driver.c
Normal file
364
gsplus/src/pulseaudio_driver.c
Normal file
@@ -0,0 +1,364 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2020 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#ifdef PULSE_AUDIO
|
||||
// Ignore entire file if PULSE_AUDIO is not defined!
|
||||
|
||||
// Some ideas from Sample code:
|
||||
// https://github.com/gavv/snippets/blob/master/pa/pa_play_async_poll.c
|
||||
|
||||
#include "defc.h"
|
||||
#include "sound.h"
|
||||
#include "protos_pulseaudio_driver.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PULSE_REBUF_SIZE (64*1024)
|
||||
|
||||
word32 g_pulseaudio_rebuf[PULSE_REBUF_SIZE];
|
||||
volatile int g_pulseaudio_rd_pos;
|
||||
volatile int g_pulseaudio_wr_pos;
|
||||
volatile int g_pulseaudio_playing = 0;
|
||||
|
||||
extern int Verbose;
|
||||
|
||||
extern int g_preferred_rate;
|
||||
extern int g_audio_enable;
|
||||
extern word32 *g_sound_shm_addr;
|
||||
extern int g_sound_min_samples;
|
||||
extern int g_sound_max_multiplier;
|
||||
extern int g_sound_size;
|
||||
extern word32 g_vbl_count;
|
||||
|
||||
int g_call_num = 0;
|
||||
|
||||
pa_mainloop *g_pa_mainloop_ptr = 0;
|
||||
pa_context *g_pa_context_ptr = 0;
|
||||
pa_stream *g_pa_stream_ptr = 0;
|
||||
pa_sample_spec g_pa_sample_spec = { 0 };
|
||||
pa_buffer_attr g_pa_buffer_attr = { 0 };
|
||||
|
||||
|
||||
int
|
||||
pulse_audio_send_audio(byte *ptr, int in_size)
|
||||
{
|
||||
word32 *wptr, *pa_wptr;
|
||||
int samps, sample_num;
|
||||
int i;
|
||||
|
||||
samps = in_size / 4;
|
||||
wptr = (word32 *)ptr;
|
||||
sample_num = g_pulseaudio_wr_pos;
|
||||
pa_wptr = &(g_pulseaudio_rebuf[0]);
|
||||
for(i = 0; i < samps; i++) {
|
||||
pa_wptr[sample_num] = *wptr++;
|
||||
sample_num++;
|
||||
if(sample_num >= PULSE_REBUF_SIZE) {
|
||||
sample_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_pulseaudio_wr_pos = sample_num;
|
||||
|
||||
pulse_audio_main_events();
|
||||
|
||||
return in_size;
|
||||
}
|
||||
|
||||
void
|
||||
pulse_audio_main_events()
|
||||
{
|
||||
pa_stream_state_t stream_state;
|
||||
pa_context_state_t context_state;
|
||||
int count, num, sz, do_write;
|
||||
|
||||
count = 0;
|
||||
do_write = 1;
|
||||
while(1) {
|
||||
// Do a few mainloop cycles to see if samples are needed
|
||||
num = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0);
|
||||
//printf("pa_mainloop_iterate ret:%d count:%d\n", num, count);
|
||||
if(num < 0) {
|
||||
return;
|
||||
}
|
||||
context_state = pa_context_get_state(g_pa_context_ptr);
|
||||
if((context_state == PA_CONTEXT_FAILED) ||
|
||||
(context_state == PA_CONTEXT_TERMINATED)) {
|
||||
printf("context_state is bad: %d\n", context_state);
|
||||
g_audio_enable = 0;
|
||||
return;
|
||||
}
|
||||
stream_state = pa_stream_get_state(g_pa_stream_ptr);
|
||||
if((stream_state == PA_STREAM_FAILED) ||
|
||||
(stream_state == PA_STREAM_TERMINATED)) {
|
||||
printf("stream state bad: %d\n", stream_state);
|
||||
g_audio_enable = 0;
|
||||
return;
|
||||
}
|
||||
if(do_write) {
|
||||
sz = pa_stream_writable_size(g_pa_stream_ptr);
|
||||
if(sz > 0) {
|
||||
pulse_audio_write_to_stream(count, sz);
|
||||
do_write = 0;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
if(count > 50) {
|
||||
printf("pulse_audio_main_events() looped %d times\n",
|
||||
count);
|
||||
return;
|
||||
}
|
||||
if(num == 0) { // Nothing else to do
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pulse_audio_write_to_stream(int dbg_count, int in_sz)
|
||||
{
|
||||
word32 *wptr;
|
||||
void *vptr;
|
||||
// const pa_timing_info *pa_timing_info_ptr;
|
||||
// pa_usec_t pa_latency;
|
||||
size_t sz;
|
||||
int num_samps, sample_num, samps_avail, err, samps_needed;
|
||||
int min_samples, max_samples;
|
||||
int i;
|
||||
|
||||
samps_needed = in_sz / 4;
|
||||
sample_num = g_pulseaudio_rd_pos;
|
||||
samps_avail = (g_pulseaudio_wr_pos - sample_num) &
|
||||
(PULSE_REBUF_SIZE - 1);
|
||||
min_samples = g_sound_min_samples;
|
||||
max_samples = min_samples * g_sound_max_multiplier;
|
||||
if(samps_needed > min_samples) {
|
||||
min_samples = samps_needed;
|
||||
}
|
||||
#if 0
|
||||
if(samps_needed > samps_avail) {
|
||||
// We don't have enough samples, must pause
|
||||
g_pulseaudio_playing = 0;
|
||||
sample_num = g_pulseaudio_wr_pos; // Eat remaining samps
|
||||
}
|
||||
#endif
|
||||
if((g_pulseaudio_playing == 0) && (samps_avail > min_samples)) {
|
||||
// We can unpause
|
||||
g_pulseaudio_playing = 1;
|
||||
}
|
||||
if(g_pulseaudio_playing && (samps_avail > max_samples)) {
|
||||
printf("JUMP SAMPLE_NUM by %d samples!\n", max_samples / 2);
|
||||
sample_num += (max_samples / 2);
|
||||
sample_num = sample_num & (PULSE_REBUF_SIZE - 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(g_call_num < 100) {
|
||||
printf("call_num:%d playing:%d g_vbl_count:%d samps_needed:%d, "
|
||||
"samps_avail:%d\n", g_call_num, g_pulseaudio_playing,
|
||||
g_vbl_count, samps_needed, samps_avail);
|
||||
}
|
||||
#endif
|
||||
g_call_num++;
|
||||
num_samps = MIN(samps_avail, samps_needed);
|
||||
if(g_pulseaudio_playing) {
|
||||
vptr = 0; // Let it allocate for us
|
||||
sz = num_samps * 4;
|
||||
err = pa_stream_begin_write(g_pa_stream_ptr, &vptr, &sz);
|
||||
wptr = vptr;
|
||||
if(err) {
|
||||
g_audio_enable = 0;
|
||||
printf("pa_stream_begin_write failed: %s\n",
|
||||
pa_strerror(err));
|
||||
return;
|
||||
}
|
||||
num_samps = sz / 4;
|
||||
for(i = 0; i < num_samps; i++) {
|
||||
wptr[i] = g_pulseaudio_rebuf[sample_num];
|
||||
sample_num++;
|
||||
if(sample_num >= PULSE_REBUF_SIZE) {
|
||||
sample_num = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = pa_stream_cancel_write(g_pa_stream_ptr);
|
||||
// Just get out...don't let us get further behind by sending
|
||||
// silence frames.
|
||||
return;
|
||||
}
|
||||
|
||||
g_pulseaudio_rd_pos = sample_num;
|
||||
|
||||
#if 0
|
||||
pa_timing_info_ptr = pa_stream_get_timing_info(g_pa_stream_ptr);
|
||||
err = pa_stream_get_latency(g_pa_stream_ptr, &pa_latency, 0);
|
||||
printf(" will send %d samples to the stream, write_index:%lld, "
|
||||
"latency:%lld\n", num_samps * 4,
|
||||
(word64)pa_timing_info_ptr->write_index, (word64)pa_latency);
|
||||
#endif
|
||||
err = pa_stream_write(g_pa_stream_ptr, wptr, num_samps * 4, 0, 0,
|
||||
PA_SEEK_RELATIVE);
|
||||
if(err) {
|
||||
printf("pa_stream_write: %s\n", pa_strerror(err));
|
||||
g_audio_enable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pulse_audio_start_stream()
|
||||
{
|
||||
int flags, ret;
|
||||
|
||||
g_pa_sample_spec.format = PA_SAMPLE_S16LE;
|
||||
g_pa_sample_spec.rate = g_preferred_rate;
|
||||
g_pa_sample_spec.channels = 2;
|
||||
printf("Set requested rate=%d\n", g_pa_sample_spec.rate);
|
||||
g_pa_stream_ptr = pa_stream_new(g_pa_context_ptr, "KEGS",
|
||||
&g_pa_sample_spec, 0);
|
||||
if(!g_pa_stream_ptr) {
|
||||
printf("pa_stream_new failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_pa_buffer_attr.maxlength = -1; // Maximum server buffer
|
||||
g_pa_buffer_attr.tlength = 4*g_preferred_rate/10; // 1/10th sec
|
||||
//g_pa_buffer_attr.prebuf = 4*g_preferred_rate/100; // 1/100th sec
|
||||
g_pa_buffer_attr.prebuf = -1;
|
||||
g_pa_buffer_attr.minreq = 4*g_preferred_rate/60; // 1/60th sec
|
||||
|
||||
flags = PA_STREAM_ADJUST_LATENCY;
|
||||
//flags = PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
|
||||
// PA_STREAM_ADJUST_LATENCY;
|
||||
// PA_STREAM_AUTO_TIMING_UPDATE and PA_STREAM_INTERPOLATE_TIMING are
|
||||
// to get latency info from the server. PA_STREAM_ADJUST_LATENCY
|
||||
// means the total latency including the server output sink buffer
|
||||
// tries to be tlength
|
||||
|
||||
ret = pa_stream_connect_playback(g_pa_stream_ptr, 0, &g_pa_buffer_attr,
|
||||
flags, 0, 0);
|
||||
if(ret) {
|
||||
printf("pa_stream_connect_playback failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0; // Success!
|
||||
}
|
||||
|
||||
int
|
||||
pulse_audio_do_init()
|
||||
{
|
||||
pa_stream_state_t stream_state;
|
||||
pa_context_state_t context_state;
|
||||
int ret, count, num;
|
||||
|
||||
g_pa_mainloop_ptr = pa_mainloop_new();
|
||||
g_pa_context_ptr = pa_context_new(
|
||||
pa_mainloop_get_api(g_pa_mainloop_ptr), "KEGS");
|
||||
if(!g_pa_context_ptr) {
|
||||
printf("Pulse Audio pa_context_new() failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = pa_context_connect(g_pa_context_ptr, 0, 0, 0);
|
||||
if(ret != 0) {
|
||||
printf("pa_context_connect failed: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
while(1) {
|
||||
// Do a few mainloop cycles to get stream initialized
|
||||
num = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0);
|
||||
#if 0
|
||||
printf("pa_mainloop_iterate ret: %d, count:%d g_pa_stream_ptr:"
|
||||
"%p\n", num, count, g_pa_stream_ptr);
|
||||
#endif
|
||||
if(num < 0) {
|
||||
return 1;
|
||||
}
|
||||
if(num == 0) {
|
||||
usleep(10*1000);
|
||||
if(count++ > 50) {
|
||||
// Waited more than 500ms, just give up
|
||||
printf("Timed out waiting for Pulse Audio to "
|
||||
"start\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// See if context is ready
|
||||
context_state = pa_context_get_state(g_pa_context_ptr);
|
||||
if((context_state == PA_CONTEXT_FAILED) ||
|
||||
(context_state == PA_CONTEXT_TERMINATED)) {
|
||||
printf("context_state is bad: %d\n", context_state);
|
||||
return 1;
|
||||
}
|
||||
if((context_state == PA_CONTEXT_READY) && !g_pa_stream_ptr) {
|
||||
ret = pulse_audio_start_stream();
|
||||
if(ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if(g_pa_stream_ptr) {
|
||||
stream_state = pa_stream_get_state(g_pa_stream_ptr);
|
||||
if((stream_state == PA_STREAM_FAILED) ||
|
||||
(stream_state == PA_STREAM_TERMINATED)){
|
||||
printf("stream state bad: %d\n", stream_state);
|
||||
return 1;
|
||||
}
|
||||
if(stream_state == PA_STREAM_READY) {
|
||||
printf("Pulse Audio stream is now ready!\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pulse_audio_init()
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_pulseaudio_rd_pos = 0;
|
||||
g_pulseaudio_wr_pos = 0;
|
||||
|
||||
ret = pulse_audio_do_init();
|
||||
// printf("pulse_audio_init ret:%d\n", ret);
|
||||
if(ret != 0) {
|
||||
// Free structures, disable sound
|
||||
if(g_pa_stream_ptr) {
|
||||
pa_stream_disconnect(g_pa_stream_ptr);
|
||||
pa_stream_unref(g_pa_stream_ptr);
|
||||
g_pa_stream_ptr = 0;
|
||||
}
|
||||
if(g_pa_context_ptr) {
|
||||
pa_context_disconnect(g_pa_context_ptr);
|
||||
pa_context_unref(g_pa_context_ptr);
|
||||
g_pa_context_ptr = 0;
|
||||
}
|
||||
if(g_pa_mainloop_ptr) {
|
||||
pa_mainloop_free(g_pa_mainloop_ptr);
|
||||
g_pa_mainloop_ptr = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
sound_set_audio_rate(g_preferred_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pulse_audio_shutdown()
|
||||
{
|
||||
printf("pulse_audio_shutdown\n");
|
||||
}
|
||||
|
||||
#endif /* PULSE_AUDIO */
|
||||
1389
gsplus/src/scc.c
Normal file
1389
gsplus/src/scc.c
Normal file
File diff suppressed because it is too large
Load Diff
111
gsplus/src/scc.h
Normal file
111
gsplus/src/scc.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2025 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
#else
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if defined(HPUX) || defined(__linux__) || defined(SOLARIS)
|
||||
# define SCC_SOCKETS
|
||||
#endif
|
||||
#if defined(MAC) || defined(__MACH__) || defined(_WIN32)
|
||||
# define SCC_SOCKETS
|
||||
#endif
|
||||
|
||||
|
||||
/* my scc port 0 == channel A, port 1 = channel B */
|
||||
|
||||
#define SCC_INBUF_SIZE 512 /* must be a power of 2 */
|
||||
#define SCC_OUTBUF_SIZE 512 /* must be a power of 2 */
|
||||
|
||||
#define SCC_MODEM_MAX_CMD_STR 128
|
||||
|
||||
#ifndef _WIN32
|
||||
# define SOCKET int /* For non-Windows */
|
||||
# define INVALID_SOCKET (-1) /* For non-Windows */
|
||||
#endif
|
||||
|
||||
STRUCT(Scc) {
|
||||
int cur_state;
|
||||
int modem_state;
|
||||
SOCKET sockfd;
|
||||
SOCKET rdwrfd;
|
||||
void *sockaddr_ptr; // Socket: pointer to sockaddr struct
|
||||
int sockaddr_size; // Socket: sizeof(sockaddr_in)
|
||||
int unix_dev_fd; // Unix fd to real serial device
|
||||
void *win_com_handle; // Win32 handle to COMx port
|
||||
void *win_dcb_ptr; // Win32 ptr to COMx DCB
|
||||
int read_called_this_vbl;
|
||||
int write_called_this_vbl;
|
||||
|
||||
byte dcd;
|
||||
byte reg_ptr;
|
||||
byte br_is_zero;
|
||||
byte tx_buf_empty;
|
||||
word32 mode;
|
||||
byte reg[16];
|
||||
|
||||
int rx_queue_depth;
|
||||
byte rx_queue[4];
|
||||
|
||||
int in_rdptr;
|
||||
int in_wrptr;
|
||||
byte in_buf[SCC_INBUF_SIZE];
|
||||
|
||||
int out_rdptr;
|
||||
int out_wrptr;
|
||||
byte out_buf[SCC_OUTBUF_SIZE];
|
||||
|
||||
int wantint_rx;
|
||||
int wantint_tx;
|
||||
int wantint_zerocnt;
|
||||
|
||||
double br_dcycs;
|
||||
double tx_dcycs;
|
||||
double rx_dcycs;
|
||||
|
||||
int br_event_pending;
|
||||
int rx_event_pending;
|
||||
int tx_event_pending;
|
||||
|
||||
int char_size;
|
||||
int baud_rate;
|
||||
dword64 out_char_dfcyc;
|
||||
|
||||
int socket_error;
|
||||
int socket_num_rings;
|
||||
dword64 socket_last_ring_dfcyc;
|
||||
word32 modem_mode;
|
||||
int modem_plus_mode;
|
||||
int modem_s0_val;
|
||||
int modem_s2_val;
|
||||
int telnet_mode;
|
||||
int telnet_iac;
|
||||
word32 telnet_local_mode[2];
|
||||
word32 telnet_remote_mode[2];
|
||||
word32 telnet_reqwill_mode[2];
|
||||
word32 telnet_reqdo_mode[2];
|
||||
word32 modem_out_portnum;
|
||||
int modem_cmd_len;
|
||||
byte modem_cmd_str[SCC_MODEM_MAX_CMD_STR + 5];
|
||||
};
|
||||
|
||||
#define SCCMODEM_NOECHO 0x0001
|
||||
#define SCCMODEM_NOVERBOSE 0x0002
|
||||
|
||||
1235
gsplus/src/scc_socket_driver.c
Normal file
1235
gsplus/src/scc_socket_driver.c
Normal file
File diff suppressed because it is too large
Load Diff
224
gsplus/src/scc_unixdriver.c
Normal file
224
gsplus/src/scc_unixdriver.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2025 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
/* This file contains the Mac/Linux calls to a real serial port */
|
||||
|
||||
#include "defc.h"
|
||||
#include "scc.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
extern Scc g_scc[2];
|
||||
extern word32 g_c025_val;
|
||||
extern char *g_serial_device[2];
|
||||
|
||||
#ifndef _WIN32
|
||||
void
|
||||
scc_serial_unix_open(int port)
|
||||
{
|
||||
Scc *scc_ptr;
|
||||
int fd;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
fd = open(&g_serial_device[port][0], O_RDWR | O_NONBLOCK);
|
||||
scc_ptr->unix_dev_fd = fd;
|
||||
|
||||
printf("scc_serial_unix_init %d called, fd: %d\n", port, fd);
|
||||
|
||||
if(fd < 0) {
|
||||
scc_ptr->unix_dev_fd = -1;
|
||||
scc_ptr->cur_state = -1; // Failed to open
|
||||
return;
|
||||
}
|
||||
|
||||
scc_serial_unix_change_params(port);
|
||||
|
||||
scc_ptr->cur_state = 0; // Actual Serial device
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_unix_close(int port)
|
||||
{
|
||||
Scc *scc_ptr;
|
||||
int fd;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
fd = scc_ptr->unix_dev_fd;
|
||||
if(fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
scc_ptr->unix_dev_fd = -1;
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_unix_change_params(int port)
|
||||
{
|
||||
struct termios termios_buf;
|
||||
Scc *scc_ptr;
|
||||
int fd, csz, ret;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
fd = scc_ptr->unix_dev_fd;
|
||||
printf("scc_serial_unix_change_parms port: %d, fd: %d\n", port, fd);
|
||||
if(fd <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = tcgetattr(fd, &termios_buf);
|
||||
if(ret != 0) {
|
||||
printf("tcgetattr port%d ret: %d\n", port, ret);
|
||||
}
|
||||
|
||||
#if 1
|
||||
printf("baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\n",
|
||||
(int)termios_buf.c_ispeed, (int)termios_buf.c_iflag,
|
||||
(int)termios_buf.c_oflag, (int)termios_buf.c_cflag,
|
||||
(int)termios_buf.c_lflag);
|
||||
#endif
|
||||
|
||||
memset(&termios_buf, 0, sizeof(struct termios));
|
||||
cfmakeraw(&termios_buf);
|
||||
cfsetspeed(&termios_buf, scc_ptr->baud_rate);
|
||||
|
||||
csz = scc_ptr->char_size;
|
||||
termios_buf.c_cflag = CREAD | CLOCAL;
|
||||
termios_buf.c_cflag |= (csz == 5) ? CS5 :
|
||||
(csz == 6) ? CS6 :
|
||||
(csz == 7) ? CS7 :
|
||||
CS8;
|
||||
switch((scc_ptr->reg[4] >> 2) & 0x3) {
|
||||
case 2: // 1.5 stop bits
|
||||
termios_buf.c_cflag |= CSTOPB; /* no 1.5 stop bit setting.*/
|
||||
break;
|
||||
case 3: // 2 stop bits
|
||||
termios_buf.c_cflag |= CSTOPB;
|
||||
break;
|
||||
}
|
||||
|
||||
switch((scc_ptr->reg[4]) & 0x3) {
|
||||
case 1: // Odd parity
|
||||
termios_buf.c_cflag |= (PARENB | PARODD);
|
||||
break;
|
||||
case 3: // Even parity
|
||||
termios_buf.c_cflag |= PARENB;
|
||||
break;
|
||||
}
|
||||
|
||||
/* always enabled DTR and RTS control */
|
||||
#ifdef CRTSCTS
|
||||
termios_buf.c_cflag |= CRTSCTS; // Linux: CTS/RTS
|
||||
#endif
|
||||
#ifdef CRTS_IFLOW
|
||||
termios_buf.c_cflag |= CDTR_IFLOW | CRTS_IFLOW; // Mac: CTS/RTS
|
||||
#endif
|
||||
|
||||
printf("fd: %d, baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\n",
|
||||
fd, (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag,
|
||||
(int)termios_buf.c_oflag, (int)termios_buf.c_cflag,
|
||||
(int)termios_buf.c_lflag);
|
||||
ret = tcsetattr(fd, TCSANOW, &termios_buf);
|
||||
if(ret != 0) {
|
||||
printf("tcsetattr ret: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left)
|
||||
{
|
||||
byte tmp_buf[256];
|
||||
Scc *scc_ptr;
|
||||
int fd, ret, flags, dcd;
|
||||
int i;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
fd = scc_ptr->unix_dev_fd;
|
||||
if(fd <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try reading some bytes */
|
||||
space_left = MY_MIN(space_left, 256);
|
||||
ret = read(fd, tmp_buf, space_left);
|
||||
|
||||
if(ret > 0) {
|
||||
for(i = 0; i < ret; i++) {
|
||||
scc_add_to_readbuf(dfcyc, port, tmp_buf[i]);
|
||||
}
|
||||
}
|
||||
flags = 0;
|
||||
dcd = 0;
|
||||
|
||||
#if defined(TIOCMGET) && defined(TIOCM_CAR)
|
||||
ret = ioctl(fd, TIOCMGET, &flags);
|
||||
if(ret == 0) {
|
||||
dcd = 0;
|
||||
if(flags & TIOCM_CAR) { // DCD
|
||||
dcd = 1;
|
||||
}
|
||||
scc_ptr->dcd = dcd;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_unix_empty_writebuf(int port)
|
||||
{
|
||||
Scc *scc_ptr;
|
||||
int fd, rdptr, wrptr, done, ret, len;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
fd = scc_ptr->unix_dev_fd;
|
||||
if(fd <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try writing some bytes */
|
||||
done = 0;
|
||||
while(!done) {
|
||||
rdptr = scc_ptr->out_rdptr;
|
||||
wrptr = scc_ptr->out_wrptr;
|
||||
if(rdptr == wrptr) {
|
||||
//printf("...rdptr == wrptr\n");
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
len = wrptr - rdptr;
|
||||
if(len < 0) {
|
||||
len = SCC_OUTBUF_SIZE - rdptr;
|
||||
}
|
||||
if(len > 32) {
|
||||
len = 32;
|
||||
}
|
||||
if(len <= 0) {
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
ret = write(fd, &(scc_ptr->out_buf[rdptr]), len);
|
||||
|
||||
if(ret <= 0) {
|
||||
done = 1;
|
||||
break;
|
||||
} else {
|
||||
rdptr = rdptr + ret;
|
||||
if(rdptr >= SCC_OUTBUF_SIZE) {
|
||||
rdptr = rdptr - SCC_OUTBUF_SIZE;
|
||||
}
|
||||
scc_ptr->out_rdptr = rdptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !_WIN32 */
|
||||
257
gsplus/src/scc_windriver.c
Normal file
257
gsplus/src/scc_windriver.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
/* This file contains the Win32 COM1/COM2 calls */
|
||||
|
||||
#include "defc.h"
|
||||
#include "scc.h"
|
||||
|
||||
extern Scc g_scc[2];
|
||||
extern word32 g_c025_val;
|
||||
extern int g_serial_win_device[2];
|
||||
|
||||
#ifdef _WIN32
|
||||
void
|
||||
scc_serial_win_open(int port)
|
||||
{
|
||||
COMMTIMEOUTS commtimeouts;
|
||||
char str_buf[32];
|
||||
Scc *scc_ptr;
|
||||
HANDLE com_handle;
|
||||
int ret;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
snprintf(&str_buf[0], sizeof(str_buf), "COM%d",
|
||||
g_serial_win_device[port]);
|
||||
|
||||
com_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE,
|
||||
0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
scc_ptr->win_com_handle = com_handle;
|
||||
|
||||
printf("scc_serial_win_init %d called, com_handle: %p\n", port,
|
||||
com_handle);
|
||||
|
||||
if(com_handle == INVALID_HANDLE_VALUE) {
|
||||
scc_ptr->cur_state = -1; // Failed to open
|
||||
return;
|
||||
}
|
||||
scc_ptr->win_dcb_ptr = malloc(sizeof(DCB));
|
||||
|
||||
scc_serial_win_change_params(port);
|
||||
|
||||
commtimeouts.ReadIntervalTimeout = MAXDWORD;
|
||||
commtimeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
commtimeouts.ReadTotalTimeoutConstant = 0;
|
||||
commtimeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
commtimeouts.WriteTotalTimeoutConstant = 10;
|
||||
ret = SetCommTimeouts(com_handle, &commtimeouts);
|
||||
if(ret == 0) {
|
||||
printf("setcommtimeout ret: %d\n", ret);
|
||||
}
|
||||
scc_ptr->cur_state = 0; // COM* is open
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_win_close(int port)
|
||||
{
|
||||
Scc *scc_ptr;
|
||||
HANDLE com_handle;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
com_handle = scc_ptr->win_com_handle;
|
||||
scc_ptr->win_com_handle = INVALID_HANDLE_VALUE;
|
||||
if(com_handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
CloseHandle(com_handle);
|
||||
free(scc_ptr->win_dcb_ptr);
|
||||
scc_ptr->win_dcb_ptr = 0;
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_win_change_params(int port)
|
||||
{
|
||||
DCB *dcbptr;
|
||||
HANDLE com_handle;
|
||||
Scc *scc_ptr;
|
||||
int ret;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
com_handle = scc_ptr->win_com_handle;
|
||||
dcbptr = scc_ptr->win_dcb_ptr;
|
||||
if((com_handle == INVALID_HANDLE_VALUE) || (scc_ptr->cur_state != 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = GetCommState(com_handle, dcbptr);
|
||||
if(ret == 0) {
|
||||
printf("getcomm port%d ret: %d\n", port, ret);
|
||||
}
|
||||
|
||||
#if 1
|
||||
printf("dcb.baudrate: %d, bytesize:%d, stops:%d, parity:%d\n",
|
||||
(int)dcbptr->BaudRate, (int)dcbptr->ByteSize,
|
||||
(int)dcbptr->StopBits, (int)dcbptr->Parity);
|
||||
printf("dcb.binary: %d, ctsflow: %d, dsrflow: %d, dtr: %d, dsr: %d\n",
|
||||
(int)dcbptr->fBinary,
|
||||
(int)dcbptr->fOutxCtsFlow,
|
||||
(int)dcbptr->fOutxDsrFlow,
|
||||
(int)dcbptr->fDtrControl,
|
||||
(int)dcbptr->fDsrSensitivity);
|
||||
printf("dcb.txonxoff:%d, outx:%d, inx: %d, null: %d, rts: %d\n",
|
||||
(int)dcbptr->fTXContinueOnXoff,
|
||||
(int)dcbptr->fOutX,
|
||||
(int)dcbptr->fInX,
|
||||
(int)dcbptr->fNull,
|
||||
(int)dcbptr->fRtsControl);
|
||||
printf("dcb.fAbortOnErr:%d, fParity:%d\n", (int)dcbptr->fAbortOnError,
|
||||
(int)dcbptr->fParity);
|
||||
#endif
|
||||
|
||||
dcbptr->fAbortOnError = 0;
|
||||
|
||||
dcbptr->BaudRate = scc_ptr->baud_rate;
|
||||
dcbptr->ByteSize = scc_ptr->char_size;
|
||||
dcbptr->StopBits = ONESTOPBIT;
|
||||
switch((scc_ptr->reg[4] >> 2) & 0x3) {
|
||||
case 2: // 1.5 stop bits
|
||||
dcbptr->StopBits = ONE5STOPBITS;
|
||||
break;
|
||||
case 3: // 2 stop bits
|
||||
dcbptr->StopBits = TWOSTOPBITS;
|
||||
break;
|
||||
}
|
||||
|
||||
dcbptr->Parity = NOPARITY;
|
||||
switch((scc_ptr->reg[4]) & 0x3) {
|
||||
case 1: // Odd parity
|
||||
dcbptr->Parity = ODDPARITY;
|
||||
break;
|
||||
case 3: // Even parity
|
||||
dcbptr->Parity = EVENPARITY;
|
||||
break;
|
||||
}
|
||||
|
||||
dcbptr->fNull = 0;
|
||||
dcbptr->fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcbptr->fDsrSensitivity = 0;
|
||||
dcbptr->fOutxCtsFlow = 0;
|
||||
dcbptr->fOutxDsrFlow = 0;
|
||||
dcbptr->fParity = 0;
|
||||
dcbptr->fInX = 0;
|
||||
dcbptr->fOutX = 0;
|
||||
dcbptr->fRtsControl = RTS_CONTROL_ENABLE;
|
||||
|
||||
ret = SetCommState(com_handle, dcbptr);
|
||||
if(ret == 0) {
|
||||
printf("SetCommState ret: %d, new baud: %d\n", ret,
|
||||
(int)dcbptr->BaudRate);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left)
|
||||
{
|
||||
byte tmp_buf[256];
|
||||
Scc *scc_ptr;
|
||||
HANDLE com_handle;
|
||||
DWORD bytes_read;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
com_handle = scc_ptr->win_com_handle;
|
||||
if(com_handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try reading some bytes */
|
||||
space_left = MY_MIN(256, space_left);
|
||||
ret = ReadFile(com_handle, tmp_buf, space_left, &bytes_read, NULL);
|
||||
|
||||
if(ret == 0) {
|
||||
printf("ReadFile ret 0\n");
|
||||
}
|
||||
|
||||
if(ret && (bytes_read > 0)) {
|
||||
for(i = 0; i < (int)bytes_read; i++) {
|
||||
scc_add_to_readbuf(dfcyc, port, tmp_buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scc_serial_win_empty_writebuf(int port)
|
||||
{
|
||||
Scc *scc_ptr;
|
||||
HANDLE com_handle;
|
||||
DWORD bytes_written;
|
||||
word32 err_code;
|
||||
int rdptr, wrptr, done, ret, len;
|
||||
|
||||
scc_ptr = &(g_scc[port]);
|
||||
|
||||
//printf("win_empty_writebuf, com_h: %d\n", scc_ptr->win_com_handle);
|
||||
com_handle = scc_ptr->win_com_handle;
|
||||
if(com_handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try writing some bytes */
|
||||
done = 0;
|
||||
while(!done) {
|
||||
rdptr = scc_ptr->out_rdptr;
|
||||
wrptr = scc_ptr->out_wrptr;
|
||||
if(rdptr == wrptr) {
|
||||
//printf("...rdptr == wrptr\n");
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
len = wrptr - rdptr;
|
||||
if(len < 0) {
|
||||
len = SCC_OUTBUF_SIZE - rdptr;
|
||||
}
|
||||
if(len > 32) {
|
||||
len = 32;
|
||||
}
|
||||
if(len <= 0) {
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
bytes_written = 1;
|
||||
ret = WriteFile(com_handle, &(scc_ptr->out_buf[rdptr]), len,
|
||||
&bytes_written, NULL);
|
||||
printf("WriteFile ret: %d, bytes_written:%d, len:%d\n", ret,
|
||||
(int)bytes_written, len);
|
||||
|
||||
err_code = (word32)-1;
|
||||
if(ret == 0) {
|
||||
err_code = (word32)GetLastError();
|
||||
printf("WriteFile ret:0, err_code: %08x\n", err_code);
|
||||
}
|
||||
|
||||
if(ret == 0 || (bytes_written == 0)) {
|
||||
done = 1;
|
||||
break;
|
||||
} else {
|
||||
rdptr = rdptr + bytes_written;
|
||||
if(rdptr >= SCC_OUTBUF_SIZE) {
|
||||
rdptr = rdptr - SCC_OUTBUF_SIZE;
|
||||
}
|
||||
scc_ptr->out_rdptr = rdptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
2103
gsplus/src/sim65816.c
Normal file
2103
gsplus/src/sim65816.c
Normal file
File diff suppressed because it is too large
Load Diff
269
gsplus/src/size_c.h
Normal file
269
gsplus/src/size_c.h
Normal file
@@ -0,0 +1,269 @@
|
||||
// "@(#)$KmKId: size_c.h,v 1.2 2023-11-12 15:32:17+00 kentd Exp $"
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2020 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
|
||||
0x1, /* 00 */ /* brk */
|
||||
0x1, /* 01 */ /* ORA (Dloc,X) */
|
||||
0x1, /* 02 */ /* COP */
|
||||
0x1, /* 03 */ /* ORA Disp8,S */
|
||||
0x1, /* 04 */ /* TSB Dloc */
|
||||
0x1, /* 05 */ /* ORA Dloc */
|
||||
0x1, /* 06 */ /* ASL Dloc */
|
||||
0x1, /* 07 */ /* ORA [Dloc] */
|
||||
0x0, /* 08 */ /* PHP */
|
||||
0x4, /* 09 */ /* ORA #imm */
|
||||
0x0, /* 0a */ /* ASL a */
|
||||
0x0, /* 0b */ /* PHD */
|
||||
0x2, /* 0c */ /* TSB abs */
|
||||
0x2, /* 0d */ /* ORA abs */
|
||||
0x2, /* 0e */ /* ASL abs */
|
||||
0x3, /* 0f */ /* ORA long */
|
||||
0x1, /* 10 */ /* BPL disp8 */
|
||||
0x1, /* 11 */ /* ORA (),y */
|
||||
0x1, /* 12 */ /* ORA () */
|
||||
0x1, /* 13 */ /* ORA (disp8,s),y */
|
||||
0x1, /* 14 */ /* TRB Dloc */
|
||||
0x1, /* 15 */ /* ORA Dloc,x */
|
||||
0x1, /* 16 */ /* ASL Dloc,x */
|
||||
0x1, /* 17 */ /* ORA [],y */
|
||||
0x0, /* 18 */ /* clc */
|
||||
0x2, /* 19 */ /* ORA abs,y */
|
||||
0x0, /* 1a */ /* INC a */
|
||||
0x0, /* 1b */ /* TCS */
|
||||
0x2, /* 1c */ /* TRB Abs */
|
||||
0x2, /* 1d */ /* ORA Abs,X */
|
||||
0x2, /* 1e */ /* ASL abs,x */
|
||||
0x3, /* 1f */ /* ORA Long,x */
|
||||
0x2, /* 20 */ /* JSR abs */
|
||||
0x1, /* 21 */ /* AND (Dloc,X) */
|
||||
0x3, /* 22 */ /* JSL Abslong */
|
||||
0x1, /* 23 */ /* AND Disp8,S */
|
||||
0x1, /* 24 */ /* BIT Dloc */
|
||||
0x1, /* 25 */ /* AND Dloc */
|
||||
0x1, /* 26 */ /* ROL Dloc */
|
||||
0x1, /* 27 */ /* AND [Dloc] */
|
||||
0x0, /* 28 */ /* PLP */
|
||||
0x4, /* 29 */ /* AND #imm */
|
||||
0x0, /* 2a */ /* ROL a */
|
||||
0x0, /* 2b */ /* PLD */
|
||||
0x2, /* 2c */ /* BIT abs */
|
||||
0x2, /* 2d */ /* AND abs */
|
||||
0x2, /* 2e */ /* ROL abs */
|
||||
0x3, /* 2f */ /* AND long */
|
||||
0x1, /* 30 */ /* BMI disp8 */
|
||||
0x1, /* 31 */ /* AND (),y */
|
||||
0x1, /* 32 */ /* AND () */
|
||||
0x1, /* 33 */ /* AND (disp8,s),y */
|
||||
0x1, /* 34 */ /* BIT Dloc,X */
|
||||
0x1, /* 35 */ /* AND Dloc,x */
|
||||
0x1, /* 36 */ /* ROL Dloc,x */
|
||||
0x1, /* 37 */ /* AND [],y */
|
||||
0x0, /* 38 */ /* SEC */
|
||||
0x2, /* 39 */ /* AND abs,y */
|
||||
0x0, /* 3a */ /* DEC a */
|
||||
0x0, /* 3b */ /* TSC */
|
||||
0x2, /* 3c */ /* BIT Abs,X */
|
||||
0x2, /* 3d */ /* AND Abs,X */
|
||||
0x2, /* 3e */ /* ROL abs,x */
|
||||
0x3, /* 3f */ /* AND Long,x */
|
||||
0x0, /* 40 */ /* RTI */
|
||||
0x1, /* 41 */ /* EOR (Dloc,X) */
|
||||
0x2, /* 42 */ /* WDM HACK: uses 2 args */
|
||||
0x1, /* 43 */ /* EOR Disp8,S */
|
||||
0x2, /* 44 */ /* MVP I,J */
|
||||
0x1, /* 45 */ /* EOR Dloc */
|
||||
0x1, /* 46 */ /* LSR Dloc */
|
||||
0x1, /* 47 */ /* EOR [Dloc] */
|
||||
0x0, /* 48 */ /* PHA */
|
||||
0x4, /* 49 */ /* EOR #imm */
|
||||
0x0, /* 4a */ /* LSR a */
|
||||
0x0, /* 4b */ /* PHK */
|
||||
0x2, /* 4c */ /* JMP abs */
|
||||
0x2, /* 4d */ /* EOR abs */
|
||||
0x2, /* 4e */ /* LSR abs */
|
||||
0x3, /* 4f */ /* EOR long */
|
||||
0x1, /* 50 */ /* BVC disp8 */
|
||||
0x1, /* 51 */ /* EOR (),y */
|
||||
0x1, /* 52 */ /* EOR () */
|
||||
0x1, /* 53 */ /* EOR (disp8,s),y */
|
||||
0x2, /* 54 */ /* MVN I,J */
|
||||
0x1, /* 55 */ /* EOR Dloc,x */
|
||||
0x1, /* 56 */ /* LSR Dloc,x */
|
||||
0x1, /* 57 */ /* EOR [],y */
|
||||
0x0, /* 58 */ /* CLI */
|
||||
0x2, /* 59 */ /* EOR abs,y */
|
||||
0x0, /* 5a */ /* PHY */
|
||||
0x0, /* 5b */ /* TCD */
|
||||
0x3, /* 5c */ /* JMP Long */
|
||||
0x2, /* 5d */ /* EOR Abs,X */
|
||||
0x2, /* 5e */ /* LSR abs,x */
|
||||
0x3, /* 5f */ /* EOR Long,x */
|
||||
0x0, /* 60 */ /* RTS */
|
||||
0x1, /* 61 */ /* ADC (Dloc,X) */
|
||||
0x2, /* 62 */ /* PER DISP16 */
|
||||
0x1, /* 63 */ /* ADC Disp8,S */
|
||||
0x1, /* 64 */ /* STZ Dloc */
|
||||
0x1, /* 65 */ /* ADC Dloc */
|
||||
0x1, /* 66 */ /* ROR Dloc */
|
||||
0x1, /* 67 */ /* ADC [Dloc] */
|
||||
0x0, /* 68 */ /* PLA */
|
||||
0x4, /* 69 */ /* ADC #imm */
|
||||
0x0, /* 6a */ /* ROR a */
|
||||
0x0, /* 6b */ /* RTL */
|
||||
0x2, /* 6c */ /* JMP (abs) */
|
||||
0x2, /* 6d */ /* ADC abs */
|
||||
0x2, /* 6e */ /* ROR abs */
|
||||
0x3, /* 6f */ /* ADC long */
|
||||
0x1, /* 70 */ /* BVS disp8 */
|
||||
0x1, /* 71 */ /* ADC (),y */
|
||||
0x1, /* 72 */ /* ADC () */
|
||||
0x1, /* 73 */ /* ADC (disp8,s),y */
|
||||
0x1, /* 74 */ /* STZ Dloc,X */
|
||||
0x1, /* 75 */ /* ADC Dloc,x */
|
||||
0x1, /* 76 */ /* ROR Dloc,x */
|
||||
0x1, /* 77 */ /* ADC [],y */
|
||||
0x0, /* 78 */ /* SEI */
|
||||
0x2, /* 79 */ /* ADC abs,y */
|
||||
0x0, /* 7a */ /* PLY */
|
||||
0x0, /* 7b */ /* TDC */
|
||||
0x2, /* 7c */ /* JMP (abs,x) */
|
||||
0x2, /* 7d */ /* ADC Abs,X */
|
||||
0x2, /* 7e */ /* ROR abs,x */
|
||||
0x3, /* 7f */ /* ADC Long,x */
|
||||
0x1, /* 80 */ /* BRA Disp8 */
|
||||
0x1, /* 81 */ /* STA (Dloc,X) */
|
||||
0x2, /* 82 */ /* BRL DISP16 */
|
||||
0x1, /* 83 */ /* STA Disp8,S */
|
||||
0x1, /* 84 */ /* STY Dloc */
|
||||
0x1, /* 85 */ /* STA Dloc */
|
||||
0x1, /* 86 */ /* STX Dloc */
|
||||
0x1, /* 87 */ /* STA [Dloc] */
|
||||
0x0, /* 88 */ /* DEY */
|
||||
0x4, /* 89 */ /* BIT #imm */
|
||||
0x0, /* 8a */ /* TXA */
|
||||
0x0, /* 8b */ /* PHB */
|
||||
0x2, /* 8c */ /* STY abs */
|
||||
0x2, /* 8d */ /* STA abs */
|
||||
0x2, /* 8e */ /* STX abs */
|
||||
0x3, /* 8f */ /* STA long */
|
||||
0x1, /* 90 */ /* BCC disp8 */
|
||||
0x1, /* 91 */ /* STA (),y */
|
||||
0x1, /* 92 */ /* STA () */
|
||||
0x1, /* 93 */ /* STA (disp8,s),y */
|
||||
0x1, /* 94 */ /* STY Dloc,X */
|
||||
0x1, /* 95 */ /* STA Dloc,x */
|
||||
0x1, /* 96 */ /* STX Dloc,y */
|
||||
0x1, /* 97 */ /* STA [],y */
|
||||
0x0, /* 98 */ /* TYA */
|
||||
0x2, /* 99 */ /* STA abs,y */
|
||||
0x0, /* 9a */ /* TXS */
|
||||
0x0, /* 9b */ /* TXY */
|
||||
0x2, /* 9c */ /* STX abs */
|
||||
0x2, /* 9d */ /* STA Abs,X */
|
||||
0x2, /* 9e */ /* STZ abs,x */
|
||||
0x3, /* 9f */ /* STA Long,x */
|
||||
0x5, /* a0 */ /* LDY #imm */
|
||||
0x1, /* a1 */ /* LDA (Dloc,X) */
|
||||
0x5, /* a2 */ /* LDX #imm */
|
||||
0x1, /* a3 */ /* LDA Disp8,S */
|
||||
0x1, /* a4 */ /* LDY Dloc */
|
||||
0x1, /* a5 */ /* LDA Dloc */
|
||||
0x1, /* a6 */ /* LDX Dloc */
|
||||
0x1, /* a7 */ /* LDA [Dloc] */
|
||||
0x0, /* a8 */ /* TAY */
|
||||
0x4, /* a9 */ /* LDA #imm */
|
||||
0x0, /* aa */ /* TAX */
|
||||
0x0, /* ab */ /* PLB */
|
||||
0x2, /* ac */ /* LDY abs */
|
||||
0x2, /* ad */ /* LDA abs */
|
||||
0x2, /* ae */ /* LDX abs */
|
||||
0x3, /* af */ /* LDA long */
|
||||
0x1, /* b0 */ /* BCS disp8 */
|
||||
0x1, /* b1 */ /* LDA (),y */
|
||||
0x1, /* b2 */ /* LDA () */
|
||||
0x1, /* b3 */ /* LDA (disp8,s),y */
|
||||
0x1, /* b4 */ /* LDY Dloc,X */
|
||||
0x1, /* b5 */ /* LDA Dloc,x */
|
||||
0x1, /* b6 */ /* LDX Dloc,y */
|
||||
0x1, /* b7 */ /* LDA [],y */
|
||||
0x0, /* b8 */ /* CLV */
|
||||
0x2, /* b9 */ /* LDA abs,y */
|
||||
0x0, /* ba */ /* TSX */
|
||||
0x0, /* bb */ /* TYX */
|
||||
0x2, /* bc */ /* LDY abs,x */
|
||||
0x2, /* bd */ /* LDA Abs,X */
|
||||
0x2, /* be */ /* LDX abs,y */
|
||||
0x3, /* bf */ /* LDA Long,x */
|
||||
0x5, /* c0 */ /* CPY #Imm */
|
||||
0x1, /* c1 */ /* CMP (Dloc,X) */
|
||||
0x1, /* c2 */ /* REP #8bit */
|
||||
0x1, /* c3 */ /* CMP Disp8,S */
|
||||
0x1, /* c4 */ /* CPY Dloc */
|
||||
0x1, /* c5 */ /* CMP Dloc */
|
||||
0x1, /* c6 */ /* DEC Dloc */
|
||||
0x1, /* c7 */ /* CMP [Dloc] */
|
||||
0x0, /* c8 */ /* INY */
|
||||
0x4, /* c9 */ /* CMP #imm */
|
||||
0x0, /* ca */ /* DEX */
|
||||
0x0, /* cb */ /* WAI */
|
||||
0x2, /* cc */ /* CPY abs */
|
||||
0x2, /* cd */ /* CMP abs */
|
||||
0x2, /* ce */ /* DEC abs */
|
||||
0x3, /* cf */ /* CMP long */
|
||||
0x1, /* d0 */ /* BNE disp8 */
|
||||
0x1, /* d1 */ /* CMP (),y */
|
||||
0x1, /* d2 */ /* CMP () */
|
||||
0x1, /* d3 */ /* CMP (disp8,s),y */
|
||||
0x1, /* d4 */ /* PEI Dloc */
|
||||
0x1, /* d5 */ /* CMP Dloc,x */
|
||||
0x1, /* d6 */ /* DEC Dloc,x */
|
||||
0x1, /* d7 */ /* CMP [],y */
|
||||
0x0, /* d8 */ /* CLD */
|
||||
0x2, /* d9 */ /* CMP abs,y */
|
||||
0x0, /* da */ /* PHX */
|
||||
0x0, /* db */ /* STP */
|
||||
0x2, /* dc */ /* JML (Abs) */
|
||||
0x2, /* dd */ /* CMP Abs,X */
|
||||
0x2, /* de */ /* DEC abs,x */
|
||||
0x3, /* df */ /* CMP Long,x */
|
||||
0x5, /* e0 */ /* CPX #Imm */
|
||||
0x1, /* e1 */ /* SBC (Dloc,X) */
|
||||
0x1, /* e2 */ /* SEP #8bit */
|
||||
0x1, /* e3 */ /* SBC Disp8,S */
|
||||
0x1, /* e4 */ /* CPX Dloc */
|
||||
0x1, /* e5 */ /* SBC Dloc */
|
||||
0x1, /* e6 */ /* INC Dloc */
|
||||
0x1, /* e7 */ /* SBC [Dloc] */
|
||||
0x0, /* e8 */ /* INX */
|
||||
0x4, /* e9 */ /* SBC #imm */
|
||||
0x0, /* ea */ /* NOP */
|
||||
0x0, /* eb */ /* XBA */
|
||||
0x2, /* ec */ /* CPX abs */
|
||||
0x2, /* ed */ /* SBC abs */
|
||||
0x2, /* ee */ /* INC abs */
|
||||
0x3, /* ef */ /* SBC long */
|
||||
0x1, /* f0 */ /* BEQ disp8 */
|
||||
0x1, /* f1 */ /* SBC (),y */
|
||||
0x1, /* f2 */ /* SBC () */
|
||||
0x1, /* f3 */ /* SBC (disp8,s),y */
|
||||
0x2, /* f4 */ /* PEA Imm */
|
||||
0x1, /* f5 */ /* SBC Dloc,x */
|
||||
0x1, /* f6 */ /* INC Dloc,x */
|
||||
0x1, /* f7 */ /* SBC [],y */
|
||||
0x0, /* f8 */ /* SED */
|
||||
0x2, /* f9 */ /* SBC abs,y */
|
||||
0x0, /* fa */ /* PLX */
|
||||
0x0, /* fb */ /* XCE */
|
||||
0x2, /* fc */ /* JSR (Abs,x) */
|
||||
0x2, /* fd */ /* SBC Abs,X */
|
||||
0x2, /* fe */ /* INC abs,x */
|
||||
0x3, /* ff */ /* SBC Long,x */
|
||||
|
||||
808
gsplus/src/smartport.c
Normal file
808
gsplus/src/smartport.c
Normal file
@@ -0,0 +1,808 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2024 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
extern int Verbose;
|
||||
extern int Halt_on;
|
||||
extern int g_rom_version;
|
||||
extern int g_io_amt;
|
||||
extern int g_highest_smartport_unit;
|
||||
extern dword64 g_cur_dfcyc;
|
||||
|
||||
extern Engine_reg engine;
|
||||
|
||||
extern Iwm g_iwm;
|
||||
|
||||
#define LEN_SMPT_LOG 16
|
||||
STRUCT(Smpt_log) {
|
||||
word32 start_addr;
|
||||
int cmd;
|
||||
int rts_addr;
|
||||
int cmd_list;
|
||||
int extras;
|
||||
int unit;
|
||||
int buf;
|
||||
int blk;
|
||||
};
|
||||
|
||||
Smpt_log g_smpt_log[LEN_SMPT_LOG];
|
||||
int g_smpt_log_pos = 0;
|
||||
|
||||
void
|
||||
smartport_error(void)
|
||||
{
|
||||
int pos;
|
||||
int i;
|
||||
|
||||
pos = g_smpt_log_pos;
|
||||
printf("Smartport log pos: %d\n", pos);
|
||||
for(i = 0; i < LEN_SMPT_LOG; i++) {
|
||||
pos--;
|
||||
if(pos < 0) {
|
||||
pos = LEN_SMPT_LOG - 1;
|
||||
}
|
||||
printf("%d:%d: t:%04x, cmd:%02x, rts:%04x, "
|
||||
"cmd_l:%04x, x:%d, unit:%d, buf:%04x, blk:%04x\n",
|
||||
i, pos,
|
||||
g_smpt_log[pos].start_addr,
|
||||
g_smpt_log[pos].cmd,
|
||||
g_smpt_log[pos].rts_addr,
|
||||
g_smpt_log[pos].cmd_list,
|
||||
g_smpt_log[pos].extras,
|
||||
g_smpt_log[pos].unit,
|
||||
g_smpt_log[pos].buf,
|
||||
g_smpt_log[pos].blk);
|
||||
}
|
||||
}
|
||||
void
|
||||
smartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list)
|
||||
{
|
||||
int pos;
|
||||
|
||||
pos = g_smpt_log_pos;
|
||||
if(start_addr != 0) {
|
||||
g_smpt_log[pos].start_addr = start_addr;
|
||||
g_smpt_log[pos].cmd = cmd;
|
||||
g_smpt_log[pos].rts_addr = rts_addr;
|
||||
g_smpt_log[pos].cmd_list = cmd_list;
|
||||
g_smpt_log[pos].extras = 0;
|
||||
g_smpt_log[pos].unit = 0;
|
||||
g_smpt_log[pos].buf = 0;
|
||||
g_smpt_log[pos].blk = 0;
|
||||
} else {
|
||||
pos--;
|
||||
if(pos < 0) {
|
||||
pos = LEN_SMPT_LOG - 1;
|
||||
}
|
||||
g_smpt_log[pos].extras = 1;
|
||||
g_smpt_log[pos].unit = cmd;
|
||||
g_smpt_log[pos].buf = rts_addr;
|
||||
g_smpt_log[pos].blk = cmd_list;
|
||||
}
|
||||
pos++;
|
||||
if(pos >= LEN_SMPT_LOG) {
|
||||
pos = 0;
|
||||
}
|
||||
g_smpt_log_pos = pos;
|
||||
}
|
||||
|
||||
void
|
||||
do_c70d(word32 arg0)
|
||||
{
|
||||
dword64 dsize;
|
||||
word32 status_ptr, rts_addr, cmd_list, cmd_list_lo, cmd_list_mid;
|
||||
word32 cmd_list_hi, status_ptr_lo, status_ptr_mid, status_ptr_hi;
|
||||
word32 rts_lo, rts_hi, buf_ptr_lo, buf_ptr_hi, buf_ptr, mask, cmd;
|
||||
word32 block_lo, block_mid, block_hi, block_hi2, unit, ctl_code;
|
||||
word32 ctl_ptr_lo, ctl_ptr_hi, ctl_ptr, block, stat_val;
|
||||
int param_cnt, ret, ext, slot;
|
||||
int i;
|
||||
|
||||
slot = (engine.kpc >> 8) & 7;
|
||||
set_memory_c(0x7f8, 0xc0 | slot, 1);
|
||||
|
||||
if((engine.psr & 0x100) == 0) {
|
||||
disk_printf("c70d %02x called in native mode!\n", arg0);
|
||||
if((engine.psr & 0x30) != 0x30) {
|
||||
halt_printf("c70d called native, psr: %03x!\n",
|
||||
engine.psr);
|
||||
}
|
||||
}
|
||||
|
||||
engine.stack = ((engine.stack + 1) & 0xff) + 0x100;
|
||||
rts_lo = get_memory_c(engine.stack);
|
||||
engine.stack = ((engine.stack + 1) & 0xff) + 0x100;
|
||||
rts_hi = get_memory_c(engine.stack);
|
||||
rts_addr = (rts_lo + (256*rts_hi) + 1) & 0xffff;
|
||||
disk_printf("rts_addr: %04x\n", rts_addr);
|
||||
|
||||
cmd = get_memory_c(rts_addr);
|
||||
cmd_list_lo = get_memory_c((rts_addr + 1) & 0xffff);
|
||||
cmd_list_mid = get_memory_c((rts_addr + 2) & 0xffff);
|
||||
cmd_list_hi = 0;
|
||||
mask = 0xffff;
|
||||
ext = 0;
|
||||
if(cmd & 0x40) {
|
||||
ext = 2;
|
||||
mask = 0xffffff;
|
||||
cmd_list_hi = get_memory_c((rts_addr + 3) & 0xffff);
|
||||
}
|
||||
|
||||
cmd_list = cmd_list_lo + (256*cmd_list_mid) + (65536*cmd_list_hi);
|
||||
|
||||
disk_printf("cmd: %02x, cmd_list: %06x\n", cmd, cmd_list);
|
||||
param_cnt = get_memory_c(cmd_list);
|
||||
unit = get_memory_c((cmd_list + 1) & mask);
|
||||
ctl_code = get_memory_c((cmd_list + 4 + ext) & mask);
|
||||
|
||||
smartport_log(0xc70d, cmd, rts_addr, cmd_list);
|
||||
dbg_log_info(g_cur_dfcyc, (rts_addr << 16) | (unit << 8) | cmd,
|
||||
cmd_list, 0xc70d);
|
||||
#if 0
|
||||
if(cmd != 0x41) {
|
||||
printf("SMTPT: c70d %08x, %08x at %016llx\n",
|
||||
(rts_addr << 16) | (unit << 8) | cmd, cmd_list,
|
||||
g_cur_dfcyc);
|
||||
}
|
||||
#endif
|
||||
ret = 0;
|
||||
if((unit >= 1) && (unit <= MAX_C7_DISKS) && ext) {
|
||||
if(g_iwm.smartport[unit-1].just_ejected) {
|
||||
ret = 0x2e; // DISKSW error
|
||||
}
|
||||
g_iwm.smartport[unit-1].just_ejected = 0;
|
||||
}
|
||||
|
||||
switch(cmd & 0x3f) {
|
||||
case 0x00: /* Status == 0x00 and 0x40 */
|
||||
if(param_cnt != 3) {
|
||||
disk_printf("param_cnt %d is != 3!\n", param_cnt);
|
||||
ret = 0x04; // BADPCNT
|
||||
break;
|
||||
}
|
||||
status_ptr_lo = get_memory_c((cmd_list+2) & mask);
|
||||
status_ptr_mid = get_memory_c((cmd_list+3) & mask);
|
||||
status_ptr_hi = 0;
|
||||
if(cmd & 0x40) {
|
||||
status_ptr_hi = get_memory_c((cmd_list+4) & mask);
|
||||
}
|
||||
|
||||
status_ptr = status_ptr_lo + (256*status_ptr_mid) +
|
||||
(65536*status_ptr_hi);
|
||||
|
||||
smartport_log(0, unit, status_ptr, ctl_code);
|
||||
dbg_log_info(g_cur_dfcyc, (ctl_code << 16) | unit,
|
||||
cmd_list, 0xc700);
|
||||
|
||||
disk_printf("unit: %02x, status_ptr: %06x, code: %02x\n",
|
||||
unit, status_ptr, ctl_code);
|
||||
if((unit == 0) && (ctl_code == 0)) {
|
||||
/* Smartport driver status */
|
||||
/* see technotes/smpt/tn-smpt-002 */
|
||||
set_memory_c(status_ptr, MAX_C7_DISKS, 1);
|
||||
set_memory_c(status_ptr+1, 0xff, 1); // intrpt stat
|
||||
set_memory16_c(status_ptr+2, 0x004b, 1); // vendor id
|
||||
set_memory16_c(status_ptr+4, 0x1000, 1); // version
|
||||
set_memory16_c(status_ptr+6, 0x0000, 1);
|
||||
//printf(" driver status, highest_unit:%02x\n",
|
||||
// g_highest_smartport_unit+1);
|
||||
|
||||
engine.xreg = 8;
|
||||
engine.yreg = 0;
|
||||
} else if((unit > 0) && (ctl_code == 0)) {
|
||||
/* status for unit x */
|
||||
if((unit > MAX_C7_DISKS) ||
|
||||
(g_iwm.smartport[unit-1].fd < 0)) {
|
||||
stat_val = 0x80;
|
||||
dsize = 0;
|
||||
ret = 0; // Not DISK_SWITCHed error
|
||||
} else {
|
||||
stat_val = 0xf8;
|
||||
dsize = g_iwm.smartport[unit-1].dimage_size;
|
||||
dsize = (dsize+511) / 512;
|
||||
if(g_iwm.smartport[unit-1].write_prot) {
|
||||
stat_val |= 4; // Write prot
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
printf(" status unit:%02x just_ejected:%d, "
|
||||
"stat_val:%02x\n", unit,
|
||||
g_iwm.smartport[unit-1].just_ejected,
|
||||
stat_val);
|
||||
#endif
|
||||
set_memory_c(status_ptr, stat_val, 1);
|
||||
set_memory24_c(status_ptr + 1, (word32)dsize);
|
||||
engine.xreg = 4;
|
||||
if(cmd & 0x40) {
|
||||
set_memory_c(status_ptr + 4,
|
||||
(dsize >> 24) & 0xff, 1);
|
||||
engine.xreg = 5;
|
||||
}
|
||||
engine.yreg = 0;
|
||||
disk_printf("just finished unit %d, stat 0\n", unit);
|
||||
} else if(ctl_code == 3) {
|
||||
if((unit > MAX_C7_DISKS) ||
|
||||
(g_iwm.smartport[unit-1].fd < 0)) {
|
||||
stat_val = 0x80;
|
||||
dsize = 0;
|
||||
ret = 0; // Not a disk-switched error
|
||||
} else {
|
||||
stat_val = 0xf8;
|
||||
dsize = g_iwm.smartport[unit-1].dimage_size;
|
||||
dsize = (dsize + 511) / 512;
|
||||
}
|
||||
if(cmd & 0x40) {
|
||||
disk_printf("extended for stat_code 3!\n");
|
||||
}
|
||||
/* DIB for unit 1 */
|
||||
set_memory_c(status_ptr, stat_val, 1);
|
||||
set_memory24_c(status_ptr + 1, (word32)dsize);
|
||||
if(cmd & 0x40) {
|
||||
set_memory_c(status_ptr + 4,
|
||||
(dsize >> 24) & 0xff, 1);
|
||||
status_ptr++;
|
||||
}
|
||||
set_memory_c(status_ptr + 4, 4, 1);
|
||||
for(i = 5; i < 21; i++) {
|
||||
set_memory_c(status_ptr + i, 0x20, 1);
|
||||
}
|
||||
set_memory_c(status_ptr + 5, 'K', 1);
|
||||
set_memory_c(status_ptr + 6, 'E', 1);
|
||||
set_memory_c(status_ptr + 7, 'G', 1);
|
||||
set_memory_c(status_ptr + 8, 'S', 1);
|
||||
|
||||
// Profile hard disk supporting extended calls+disk_sw
|
||||
set_memory16_c(status_ptr + 21, 0xc002, 1);
|
||||
set_memory16_c(status_ptr + 23, 0x0000, 1);
|
||||
|
||||
if(cmd & 0x40) {
|
||||
engine.xreg = 26;
|
||||
} else {
|
||||
engine.xreg = 25;
|
||||
}
|
||||
#if 0
|
||||
printf(" DIB unit:%02x just_ejected:%d, "
|
||||
"stat_val:%02x\n", unit,
|
||||
g_iwm.smartport[unit-1].just_ejected,
|
||||
stat_val);
|
||||
#endif
|
||||
engine.yreg = 0;
|
||||
|
||||
disk_printf("Just finished unit %d, stat 3\n", unit);
|
||||
if(unit == 0 || unit > MAX_C7_DISKS) {
|
||||
ret = 0x28; // NODRIVE error
|
||||
}
|
||||
} else {
|
||||
printf("cmd: 00, unknown unit/status code %02x!\n",
|
||||
ctl_code);
|
||||
ret = 0x21; // BADCTL
|
||||
}
|
||||
break;
|
||||
case 0x01: /* Read Block == 0x01 and 0x41 */
|
||||
if(param_cnt != 3) {
|
||||
halt_printf("param_cnt %d is != 3!\n", param_cnt);
|
||||
ret = 0x04; // BADPCNT
|
||||
break;
|
||||
}
|
||||
buf_ptr_lo = get_memory_c((cmd_list+2) & mask);
|
||||
buf_ptr_hi = get_memory_c((cmd_list+3) & mask);
|
||||
|
||||
buf_ptr = buf_ptr_lo + (256*buf_ptr_hi);
|
||||
if(cmd & 0x40) {
|
||||
buf_ptr_lo = get_memory_c((cmd_list+4) & mask);
|
||||
buf_ptr_hi = get_memory_c((cmd_list+5) & mask);
|
||||
buf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536;
|
||||
cmd_list += 2;
|
||||
}
|
||||
block_lo = get_memory_c((cmd_list+4) & mask);
|
||||
block_mid = get_memory_c((cmd_list+5) & mask);
|
||||
block_hi = get_memory_c((cmd_list+6) & mask);
|
||||
block_hi2 = 0;
|
||||
if(cmd & 0x40) {
|
||||
block_hi2 = get_memory_c((cmd_list+7) & mask);
|
||||
}
|
||||
block = (block_hi2 << 24) | (block_hi << 16) |
|
||||
(block_mid << 8) | block_lo;
|
||||
disk_printf("smartport read unit %d of block %06x to %06x\n",
|
||||
unit, block, buf_ptr);
|
||||
if(unit < 1 || unit > MAX_C7_DISKS) {
|
||||
halt_printf("Unknown unit #: %d\n", unit);
|
||||
}
|
||||
|
||||
smartport_log(0, unit - 1, buf_ptr, block);
|
||||
|
||||
if(ret == 0) {
|
||||
ret = do_read_c7(unit - 1, buf_ptr, block);
|
||||
}
|
||||
engine.xreg = 0;
|
||||
engine.yreg = 2;
|
||||
break;
|
||||
case 0x02: /* Write Block == 0x02 and 0x42 */
|
||||
if(param_cnt != 3) {
|
||||
halt_printf("param_cnt %d is != 3!\n", param_cnt);
|
||||
ret = 0x04; // BADPCNT
|
||||
break;
|
||||
}
|
||||
buf_ptr_lo = get_memory_c((cmd_list+2) & mask);
|
||||
buf_ptr_hi = get_memory_c((cmd_list+3) & mask);
|
||||
|
||||
buf_ptr = buf_ptr_lo + (256*buf_ptr_hi);
|
||||
if(cmd & 0x40) {
|
||||
buf_ptr_lo = get_memory_c((cmd_list+4) & mask);
|
||||
buf_ptr_hi = get_memory_c((cmd_list+5) & mask);
|
||||
buf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536;
|
||||
cmd_list += 2;
|
||||
}
|
||||
block_lo = get_memory_c((cmd_list+4) & mask);
|
||||
block_mid = get_memory_c((cmd_list+5) & mask);
|
||||
block_hi = get_memory_c((cmd_list+6) & mask);
|
||||
block_hi2 = 0;
|
||||
if(cmd & 0x40) {
|
||||
block_hi2 = get_memory_c((cmd_list+7) & mask);
|
||||
}
|
||||
block = (block_hi2 << 24) | (block_hi << 16) |
|
||||
(block_mid << 8) | block_lo;
|
||||
disk_printf("smartport write unit %d of block %04x from %04x\n",
|
||||
unit, block, buf_ptr);
|
||||
if(unit < 1 || unit > MAX_C7_DISKS) {
|
||||
halt_printf("Unknown unit #: %d\n", unit);
|
||||
}
|
||||
|
||||
smartport_log(0, unit - 1, buf_ptr, block);
|
||||
|
||||
if(ret == 0) {
|
||||
ret = do_write_c7(unit - 1, buf_ptr, block);
|
||||
}
|
||||
engine.xreg = 0;
|
||||
engine.yreg = 2;
|
||||
|
||||
HALT_ON(HALT_ON_C70D_WRITES, "c70d Write done\n");
|
||||
break;
|
||||
case 0x03: /* Format == 0x03 and 0x43 */
|
||||
if(param_cnt != 1) {
|
||||
halt_printf("param_cnt %d is != 1!\n", param_cnt);
|
||||
ret = 0x04; // BADPCNT
|
||||
break;
|
||||
}
|
||||
if((unit < 1) || (unit > MAX_C7_DISKS)) {
|
||||
halt_printf("Unknown unit #: %d\n", unit);
|
||||
ret = 0x11; // BADUNIT
|
||||
}
|
||||
|
||||
smartport_log(0, unit - 1, 0, 0);
|
||||
|
||||
if(ret == 0) {
|
||||
ret = do_format_c7(unit - 1);
|
||||
}
|
||||
engine.xreg = 0;
|
||||
engine.yreg = 2;
|
||||
|
||||
HALT_ON(HALT_ON_C70D_WRITES, "c70d Format done\n");
|
||||
break;
|
||||
case 0x04: /* Control == 0x04 and 0x44 */
|
||||
if(cmd == 0x44) {
|
||||
halt_printf("smartport code 0x44 not supported\n");
|
||||
}
|
||||
if(param_cnt != 3) {
|
||||
halt_printf("param_cnt %d is != 3!\n", param_cnt);
|
||||
break;
|
||||
}
|
||||
ctl_ptr_lo = get_memory_c((cmd_list+2) & mask);
|
||||
ctl_ptr_hi = get_memory_c((cmd_list+3) & mask);
|
||||
ctl_ptr = (ctl_ptr_hi << 8) + ctl_ptr_lo;
|
||||
if(cmd & 0x40) {
|
||||
ctl_ptr_lo = get_memory_c((cmd_list+4) & mask);
|
||||
ctl_ptr_hi = get_memory_c((cmd_list+5) & mask);
|
||||
ctl_ptr += ((ctl_ptr_hi << 8) + ctl_ptr_lo) << 16;
|
||||
cmd_list += 2;
|
||||
}
|
||||
|
||||
switch(ctl_code) {
|
||||
case 0x00:
|
||||
printf("Performing a reset on unit %d\n", unit);
|
||||
break;
|
||||
default:
|
||||
halt_printf("control code: %02x ptr:%06x unknown!\n",
|
||||
ctl_code, ctl_ptr);
|
||||
}
|
||||
// printf("CONTROL, ctl_code:%02x\n", ctl_code);
|
||||
|
||||
engine.xreg = 0;
|
||||
engine.yreg = 2;
|
||||
break;
|
||||
default: /* Unknown command! */
|
||||
/* set acc = 1, and set carry, and set kpc */
|
||||
engine.xreg = (rts_addr) & 0xff;
|
||||
engine.yreg = (rts_addr >> 8) & 0xff;
|
||||
ret = 0x01; // BADCMD error
|
||||
if((cmd != 0x4b) && (cmd != 0x48) && (cmd != 0x4a)) {
|
||||
// Finder does 0x4a before dialog for formatting disk
|
||||
// Finder does 0x4b call before formatting disk
|
||||
// Many things do 0x48 call to see online drives
|
||||
// So: ignore those, just return BADCMD
|
||||
halt_printf("Just did smtport cmd:%02x rts_addr:%04x, "
|
||||
"cmdlst:%06x\n", cmd, rts_addr, cmd_list);
|
||||
}
|
||||
}
|
||||
|
||||
engine.acc = (engine.acc & 0xff00) | (ret & 0xff);
|
||||
engine.psr &= ~1;
|
||||
if(ret) {
|
||||
engine.psr |= 1;
|
||||
printf("Smtport cmd:%02x unit:%02x ctl_code:%02x ret:%02x\n",
|
||||
cmd, unit, ctl_code, ret);
|
||||
}
|
||||
engine.kpc = (rts_addr + 3 + ext) & 0xffff;
|
||||
// printf(" ret:%02x psr_c:%d\n", ret & 0xff, engine.psr & 1);
|
||||
}
|
||||
|
||||
// $C70A is the ProDOS entry point, documented in ProDOS 8 Technical Ref
|
||||
// Manual, section 6.3.
|
||||
void
|
||||
do_c70a(word32 arg0)
|
||||
{
|
||||
dword64 dsize;
|
||||
word32 cmd, unit, buf_lo, buf_hi, blk_lo, blk_hi, blk, buf;
|
||||
word32 prodos_unit;
|
||||
int ret, slot;
|
||||
|
||||
slot = (engine.kpc >> 8) & 7;
|
||||
set_memory_c(0x7f8, 0xc0 | slot, 1);
|
||||
|
||||
cmd = get_memory_c((engine.direct + 0x42) & 0xffff);
|
||||
prodos_unit = get_memory_c((engine.direct + 0x43) & 0xffff);
|
||||
buf_lo = get_memory_c((engine.direct + 0x44) & 0xffff);
|
||||
buf_hi = get_memory_c((engine.direct + 0x45) & 0xffff);
|
||||
blk_lo = get_memory_c((engine.direct + 0x46) & 0xffff);
|
||||
blk_hi = get_memory_c((engine.direct + 0x47) & 0xffff);
|
||||
|
||||
blk = (blk_hi << 8) + blk_lo;
|
||||
buf = (buf_hi << 8) + buf_lo;
|
||||
disk_printf("c70a %02x cmd:%02x, pro_unit:%02x, buf:%04x, blk:%04x\n",
|
||||
arg0, cmd, prodos_unit, buf, blk);
|
||||
|
||||
unit = 0 + (prodos_unit >> 7); // units 0,1
|
||||
if((prodos_unit & 0x7f) != (slot << 4)) {
|
||||
unit += 2; // units 2,3
|
||||
}
|
||||
|
||||
smartport_log(0xc70a, cmd, blk, buf);
|
||||
dbg_log_info(g_cur_dfcyc,
|
||||
(buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk, 0xc70a);
|
||||
|
||||
#if 0
|
||||
if(cmd != 0x1ff) {
|
||||
printf("SMTPT: c70a %08x %08x\n",
|
||||
(buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk);
|
||||
}
|
||||
#endif
|
||||
engine.psr &= ~1; /* clear carry */
|
||||
|
||||
ret = 0x27; /* I/O error */
|
||||
if(cmd == 0x00) {
|
||||
dsize = g_iwm.smartport[unit].dimage_size;
|
||||
dsize = (dsize + 511) / 512;
|
||||
|
||||
smartport_log(0, unit, (word32)dsize, 0);
|
||||
dbg_log_info(g_cur_dfcyc, ((unit & 0xff) << 8) | (cmd & 0xff),
|
||||
(word32)dsize, 0x1c700);
|
||||
|
||||
ret = 0;
|
||||
engine.xreg = dsize & 0xff;
|
||||
engine.yreg = (word32)(dsize >> 8);
|
||||
} else if(cmd == 0x01) {
|
||||
smartport_log(0, unit, buf, blk);
|
||||
ret = do_read_c7(unit, buf, blk);
|
||||
} else if(cmd == 0x02) {
|
||||
smartport_log(0, unit, buf, blk);
|
||||
ret = do_write_c7(unit, buf, blk);
|
||||
} else if(cmd == 0x03) { /* format */
|
||||
smartport_log(0, unit, buf, blk);
|
||||
ret = do_format_c7(unit);
|
||||
}
|
||||
|
||||
engine.acc = (engine.acc & 0xff00) | (ret & 0xff);
|
||||
if(ret != 0) {
|
||||
engine.psr |= 1; // Set carry
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
do_read_c7(int unit_num, word32 buf, word32 blk)
|
||||
{
|
||||
byte local_buf[0x200];
|
||||
Disk *dsk;
|
||||
byte *bptr;
|
||||
dword64 dimage_start, dimage_size, dret;
|
||||
word32 val;
|
||||
int len, fd;
|
||||
int i;
|
||||
|
||||
dbg_log_info(g_cur_dfcyc, (buf << 8) | (unit_num & 0xff), blk, 0xc701);
|
||||
if((unit_num < 0) || (unit_num > MAX_C7_DISKS)) {
|
||||
halt_printf("do_read_c7: unit_num: %d\n", unit_num);
|
||||
smartport_error();
|
||||
return 0x28;
|
||||
}
|
||||
|
||||
dsk = &(g_iwm.smartport[unit_num]);
|
||||
fd = dsk->fd;
|
||||
dimage_start = dsk->dimage_start;
|
||||
dimage_size = dsk->dimage_size;
|
||||
if(fd < 0) {
|
||||
printf("c7_fd == %d!\n", fd);
|
||||
#if 0
|
||||
if(blk != 2 && blk != 0) {
|
||||
/* don't print error if only reading directory */
|
||||
smartport_error();
|
||||
halt_printf("Read unit:%02x blk:%04x\n", unit_num, blk);
|
||||
}
|
||||
#endif
|
||||
return 0x2f;
|
||||
}
|
||||
if(((blk + 1) * 0x200ULL) > (dimage_start + dimage_size)) {
|
||||
halt_printf("Tried to read past %08llx on disk (blk:%04x)\n",
|
||||
dimage_start + dimage_size, blk);
|
||||
smartport_error();
|
||||
return 0x27;
|
||||
}
|
||||
|
||||
if(dsk->raw_data) {
|
||||
// image was compressed and is in dsk->raw_data
|
||||
bptr = dsk->raw_data + dimage_start + (blk*0x200ULL);
|
||||
for(i = 0; i < 0x200; i++) {
|
||||
local_buf[i] = bptr[i];
|
||||
}
|
||||
} else {
|
||||
dret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET);
|
||||
if(dret != (dimage_start + blk*0x200ULL)) {
|
||||
halt_printf("lseek ret %08llx, errno:%d\n", dret,
|
||||
errno);
|
||||
smartport_error();
|
||||
return 0x27;
|
||||
}
|
||||
|
||||
len = (int)read(fd, &local_buf[0], 0x200);
|
||||
if(len != 0x200) {
|
||||
printf("read returned %08x, errno:%d, blk:%04x, unit:"
|
||||
"%02x\n", len, errno, blk, unit_num);
|
||||
halt_printf("name: %s\n", dsk->name_ptr);
|
||||
smartport_error();
|
||||
return 0x27;
|
||||
}
|
||||
}
|
||||
|
||||
g_io_amt += 0x200;
|
||||
|
||||
if(buf >= 0xfc0000) {
|
||||
disk_printf("reading into ROM, just returning\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x200; i += 2) {
|
||||
val = (local_buf[i+1] << 8) + local_buf[i];
|
||||
set_memory16_c(buf + i, val, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
do_write_c7(int unit_num, word32 buf, word32 blk)
|
||||
{
|
||||
byte local_buf[0x200];
|
||||
Disk *dsk;
|
||||
dword64 dret, dimage_start, dimage_size;
|
||||
int len, fd, ret;
|
||||
int i;
|
||||
|
||||
dbg_log_info(g_cur_dfcyc, (buf << 16) | (unit_num & 0xff), blk, 0xc702);
|
||||
|
||||
if(unit_num < 0 || unit_num > MAX_C7_DISKS) {
|
||||
halt_printf("do_write_c7: unit_num: %d\n", unit_num);
|
||||
smartport_error();
|
||||
return 0x28;
|
||||
}
|
||||
|
||||
dsk = &(g_iwm.smartport[unit_num]);
|
||||
fd = dsk->fd;
|
||||
dimage_start = dsk->dimage_start;
|
||||
dimage_size = dsk->dimage_size;
|
||||
if(fd < 0) {
|
||||
halt_printf("c7_fd == %d!\n", fd);
|
||||
smartport_error();
|
||||
return 0x28;
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x200; i++) {
|
||||
local_buf[i] = get_memory_c(buf + i);
|
||||
}
|
||||
|
||||
if(dsk->write_prot) {
|
||||
printf("Write, but s7d%d %s is write protected!\n",
|
||||
unit_num + 1, dsk->name_ptr);
|
||||
return 0x2b;
|
||||
}
|
||||
|
||||
if(dsk->write_through_to_unix == 0) {
|
||||
//halt_printf("Write to %s, but not wr_thru!\n", dsk->name_ptr);
|
||||
if(dsk->raw_data) {
|
||||
// Update the memory copy
|
||||
ret = smartport_memory_write(dsk, &local_buf[0],
|
||||
blk * 0x200ULL, 0x200);
|
||||
if(ret) {
|
||||
return 0x27; // I/O Error
|
||||
}
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
if(dsk->dynapro_info_ptr) {
|
||||
dynapro_write(dsk, &local_buf[0], blk*0x200UL, 0x200);
|
||||
} else {
|
||||
dret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET);
|
||||
if(dret != (dimage_start + blk*0x200ULL)) {
|
||||
halt_printf("lseek returned %08llx, errno: %d\n", dret,
|
||||
errno);
|
||||
smartport_error();
|
||||
return 0x27;
|
||||
}
|
||||
|
||||
if(dret >= (dimage_start + dimage_size)) {
|
||||
halt_printf("Tried to write to %08llx\n", dret);
|
||||
smartport_error();
|
||||
return 0x27;
|
||||
}
|
||||
|
||||
len = (int)write(fd, &local_buf[0], 0x200);
|
||||
if(len != 0x200) {
|
||||
halt_printf("write ret %08x bytes, errno: %d\n", len,
|
||||
errno);
|
||||
smartport_error();
|
||||
dsk->write_prot = 1;
|
||||
return 0x2b; // Write protected
|
||||
}
|
||||
}
|
||||
|
||||
g_io_amt += 0x200;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
smartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size)
|
||||
{
|
||||
byte *bptr;
|
||||
word32 ui;
|
||||
|
||||
bptr = dsk->raw_data;
|
||||
if((bptr == 0) || ((doffset + size) > dsk->dimage_size)) {
|
||||
printf("Write to %s failed, %08llx past end %08llx\n",
|
||||
dsk->name_ptr, doffset, dsk->dimage_size);
|
||||
return -1;
|
||||
}
|
||||
for(ui = 0; ui < size; ui++) {
|
||||
bptr[doffset + ui] = bufptr[ui];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
do_format_c7(int unit_num)
|
||||
{
|
||||
byte local_buf[0x1000];
|
||||
Disk *dsk;
|
||||
dword64 dimage_start, dimage_size, dret, dtotal, dsum;
|
||||
int len, max, fd, ret;
|
||||
int i;
|
||||
|
||||
dbg_log_info(g_cur_dfcyc, (unit_num & 0xff), 0, 0xc703);
|
||||
|
||||
if(unit_num < 0 || unit_num > MAX_C7_DISKS) {
|
||||
halt_printf("do_format_c7: unit_num: %d\n", unit_num);
|
||||
smartport_error();
|
||||
return 0x28;
|
||||
}
|
||||
|
||||
dsk = &(g_iwm.smartport[unit_num]);
|
||||
fd = dsk->fd;
|
||||
dimage_start = dsk->dimage_start;
|
||||
dimage_size = dsk->dimage_size;
|
||||
if(fd < 0) {
|
||||
halt_printf("c7_fd == %d!\n", fd);
|
||||
smartport_error();
|
||||
return 0x28;
|
||||
}
|
||||
|
||||
if(dsk->write_prot || (dsk->raw_data && !dsk->dynapro_info_ptr)) {
|
||||
printf("Format, but %s is write protected!\n", dsk->name_ptr);
|
||||
return 0x2b;
|
||||
}
|
||||
|
||||
if(dsk->write_through_to_unix == 0) {
|
||||
if(!dsk->raw_data) {
|
||||
printf("Format of %s ignored\n", dsk->name_ptr);
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x1000; i++) {
|
||||
local_buf[i] = 0;
|
||||
}
|
||||
|
||||
if(!dsk->dynapro_info_ptr) {
|
||||
dret = kegs_lseek(fd, dimage_start, SEEK_SET);
|
||||
if(dret != dimage_start) {
|
||||
halt_printf("lseek returned %08llx, errno: %d\n", dret,
|
||||
errno);
|
||||
smartport_error();
|
||||
return 0x27;
|
||||
}
|
||||
}
|
||||
|
||||
dsum = 0;
|
||||
dtotal = dimage_size;
|
||||
|
||||
while(dsum < dtotal) {
|
||||
max = (int)MY_MIN(0x1000, dtotal - dsum);
|
||||
len = max;
|
||||
if(dsk->dynapro_info_ptr) {
|
||||
dynapro_write(dsk, &local_buf[0], dsum, max);
|
||||
} else if(dsk->raw_data) {
|
||||
ret = smartport_memory_write(dsk, &local_buf[0],
|
||||
dsum, max);
|
||||
if(ret) {
|
||||
return 0x27; // I/O Error
|
||||
}
|
||||
} else {
|
||||
len = (int)write(fd, &local_buf[0], max);
|
||||
}
|
||||
if(len != max) {
|
||||
halt_printf("write ret %08x, errno:%d\n", len, errno);
|
||||
smartport_error();
|
||||
dsk->write_prot = 1;
|
||||
return 0x2b; // Write-protected
|
||||
}
|
||||
dsum += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
do_c700(word32 ret)
|
||||
{
|
||||
int slot;
|
||||
|
||||
disk_printf("do_c700 called, ret: %08x\n", ret);
|
||||
dbg_log_info(g_cur_dfcyc, 0, 0, 0xc700);
|
||||
|
||||
slot = (engine.kpc >> 8) & 7;
|
||||
ret = do_read_c7(0, 0x800, 0); // Always read unit 0, block 0
|
||||
|
||||
set_memory_c(0x7f8, slot, 1);
|
||||
set_memory16_c(0x42, (slot << 12) | 1, 1);
|
||||
set_memory16_c(0x44, 0x0800, 1);
|
||||
set_memory16_c(0x46, 0x0000, 1);
|
||||
engine.xreg = slot << 4; // 0x70 for slot 7
|
||||
engine.kpc = 0x801;
|
||||
|
||||
if(ret != 0) {
|
||||
printf("Failure reading boot disk in s7d1, trying slot 5!\n");
|
||||
engine.kpc = 0xc500; // Try to boot slot 5
|
||||
if((slot == 5) || (g_rom_version == 0)) {
|
||||
engine.kpc = 0xc600; // Try to boot slot 6
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1003
gsplus/src/sound.c
Normal file
1003
gsplus/src/sound.c
Normal file
File diff suppressed because it is too large
Load Diff
101
gsplus/src/sound.h
Normal file
101
gsplus/src/sound.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
#if !defined(_WIN32) && !defined(__CYGWIN__)
|
||||
# include <sys/ipc.h>
|
||||
# include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
#define SOUND_SHM_SAMP_SIZE (32*1024)
|
||||
|
||||
#define SAMPLE_SIZE 2
|
||||
#define NUM_CHANNELS 2
|
||||
#define SAMPLE_CHAN_SIZE (SAMPLE_SIZE * NUM_CHANNELS)
|
||||
|
||||
STRUCT(Doc_reg) {
|
||||
double dsamp_ev;
|
||||
double dsamp_ev2;
|
||||
double complete_dsamp;
|
||||
int samps_left;
|
||||
word32 cur_acc;
|
||||
word32 cur_inc;
|
||||
word32 cur_start;
|
||||
word32 cur_end;
|
||||
word32 cur_mask;
|
||||
int size_bytes;
|
||||
int event;
|
||||
int running;
|
||||
int has_irq_pending;
|
||||
word32 freq;
|
||||
word32 vol;
|
||||
word32 waveptr;
|
||||
word32 ctl;
|
||||
word32 wavesize;
|
||||
word32 last_samp_val;
|
||||
};
|
||||
|
||||
// Mockingboard contains two pairs. Each pair is a 6522 interfacing
|
||||
// to an AY-8913 to generate sounds. Eacho AY-8913 contains 3 channels of
|
||||
// sound. Model each pair separately.
|
||||
|
||||
STRUCT(Mos6522) {
|
||||
byte orb;
|
||||
byte ora;
|
||||
byte ddrb;
|
||||
byte ddra;
|
||||
word32 timer1_latch;
|
||||
word32 timer1_counter;
|
||||
word32 timer2_latch;
|
||||
word32 timer2_counter;
|
||||
byte sr;
|
||||
byte acr;
|
||||
byte pcr;
|
||||
byte ifr;
|
||||
byte ier;
|
||||
};
|
||||
|
||||
STRUCT(Ay8913) {
|
||||
byte regs[16];
|
||||
byte reg_addr_latch;
|
||||
byte toggle_tone[3]; // Channel A,B,C: 0 = low, 1 = high
|
||||
word32 tone_samp[3];
|
||||
word32 noise_val;
|
||||
word32 noise_samp;
|
||||
dword64 env_dsamp;
|
||||
};
|
||||
|
||||
STRUCT(Mock_pair) {
|
||||
Mos6522 mos6522;
|
||||
Ay8913 ay8913;
|
||||
};
|
||||
|
||||
STRUCT(Mockingboard) {
|
||||
Mock_pair pair[2];
|
||||
word32 disable_mask;
|
||||
};
|
||||
|
||||
/* prototypes for win32snd_driver.c functions */
|
||||
void win32snd_init(word32 *);
|
||||
void win32snd_shutdown(void);
|
||||
void child_sound_init_win32(void);
|
||||
int win32_send_audio(byte *ptr, int size);
|
||||
|
||||
/* Prototypes for macsnd_driver.c functions */
|
||||
int mac_send_audio(byte *ptr, int in_size);
|
||||
void macsnd_init();
|
||||
|
||||
/* Prototypes for pulseaudio_driver.c functions */
|
||||
int pulse_audio_init();
|
||||
int pulse_audio_send_audio(byte *ptr, int in_size);
|
||||
void pulse_audio_shutdown(void);
|
||||
|
||||
546
gsplus/src/sound_driver.c
Normal file
546
gsplus/src/sound_driver.c
Normal file
@@ -0,0 +1,546 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// Routines for managing sending sound samples to the hardware. The
|
||||
// primary routines are snddrv_init() for initializing the sound hardware,
|
||||
// and snddrv_send_sound() which calls the driver for the sound hardware
|
||||
// in use to play samples.
|
||||
// Linux forks a child process to manage /dev/dsp (so KEGS will not block)
|
||||
// so the lowerlevel routines for all sound hardware start with child_().
|
||||
|
||||
#include "defc.h"
|
||||
#include "sound.h"
|
||||
|
||||
#if defined(__linux__) || defined(OSS)
|
||||
# include <sys/soundcard.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__) || defined(MAC)
|
||||
# define KEGS_CAN_FORK 0
|
||||
#else
|
||||
// Linux, or other Unix, we may fork and run sound in the child
|
||||
# define KEGS_CAN_FORK 1
|
||||
#endif
|
||||
|
||||
extern int Verbose;
|
||||
|
||||
extern int g_audio_rate;
|
||||
extern int g_audio_enable;
|
||||
extern double g_last_sound_play_dsamp;
|
||||
extern word32 *g_sound_shm_addr;
|
||||
|
||||
int g_preferred_rate = 48000;
|
||||
int g_audio_socket = -1;
|
||||
int g_bytes_written = 0;
|
||||
int g_pulse_audio = 0;
|
||||
int g_pipe_fd[2] = { -1, -1 };
|
||||
int g_pipe2_fd[2] = { -1, -1 };
|
||||
|
||||
#define ZERO_BUF_SIZE 2048
|
||||
|
||||
word32 g_snd_zero_buf[ZERO_BUF_SIZE];
|
||||
|
||||
#define ZERO_PAUSE_SAFETY_SAMPS (g_audio_rate >> 5)
|
||||
#define ZERO_PAUSE_NUM_SAMPS (4*g_audio_rate)
|
||||
|
||||
int g_zeroes_buffered = 0;
|
||||
int g_zeroes_seen = 0;
|
||||
int g_sound_paused = 0;
|
||||
int g_childsnd_vbl = 0;
|
||||
int g_childsnd_pos = 0;
|
||||
|
||||
int child_sound_init_linux(void);
|
||||
|
||||
void
|
||||
snddrv_init()
|
||||
{
|
||||
word32 *shmaddr;
|
||||
int size, ret, use_shm;
|
||||
|
||||
ret = 0;
|
||||
if(ret) { // Avoid unused var warning
|
||||
}
|
||||
|
||||
g_zeroes_buffered = 0;
|
||||
g_zeroes_seen = 0;
|
||||
g_sound_paused = 0;
|
||||
|
||||
g_childsnd_pos = 0;
|
||||
g_childsnd_vbl = 0;
|
||||
|
||||
if(g_audio_enable == 0) {
|
||||
sound_set_audio_rate(g_preferred_rate);
|
||||
return;
|
||||
}
|
||||
printf("snddrv_init, g_audio_enable:%d\n", g_audio_enable);
|
||||
|
||||
size = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE;
|
||||
|
||||
use_shm = KEGS_CAN_FORK;
|
||||
#ifdef PULSE_AUDIO
|
||||
use_shm = 0;
|
||||
#endif
|
||||
if(!use_shm) {
|
||||
/* windows and mac, and Linux Pulse Audio */
|
||||
shmaddr = malloc(size);
|
||||
memset(shmaddr, 0, size);
|
||||
g_sound_shm_addr = shmaddr;
|
||||
#ifdef MAC
|
||||
macsnd_init();
|
||||
return;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
win32snd_init(shmaddr);
|
||||
return;
|
||||
#endif
|
||||
#ifdef PULSE_AUDIO
|
||||
ret = pulse_audio_init(shmaddr);
|
||||
if(ret == 0) {
|
||||
g_pulse_audio = 1;
|
||||
return; // Success!
|
||||
}
|
||||
free(shmaddr);
|
||||
g_sound_shm_addr = 0;
|
||||
use_shm = 1;
|
||||
// Otherwise, fall back on /dev/dsp
|
||||
#endif
|
||||
}
|
||||
|
||||
if(use_shm) {
|
||||
sound_child_fork(size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sound_child_fork(int size)
|
||||
{
|
||||
#if KEGS_CAN_FORK
|
||||
word32 *shmaddr;
|
||||
int shmid, pid, tmp, ret;
|
||||
int i;
|
||||
|
||||
doc_printf("In sound_child_fork, size:%d\n", size);
|
||||
shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
|
||||
if(shmid < 0) {
|
||||
printf("sound_init: shmget ret: %d, errno: %d\n", shmid,
|
||||
errno);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
shmaddr = shmat(shmid, 0, 0);
|
||||
tmp = (int)PTR2WORD(shmaddr);
|
||||
if(tmp == -1) {
|
||||
printf("sound_init: shmat ret: %p, errno: %d\n", shmaddr,
|
||||
errno);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
ret = shmctl(shmid, IPC_RMID, 0);
|
||||
if(ret < 0) {
|
||||
printf("sound_init: shmctl ret: %d, errno: %d\n", ret, errno);
|
||||
exit(4);
|
||||
}
|
||||
|
||||
g_sound_shm_addr = shmaddr;
|
||||
printf("shmaddr: %p\n", shmaddr);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
/* prepare pipe so parent can signal child each other */
|
||||
/* pipe[0] = read side, pipe[1] = write end */
|
||||
ret = pipe(&g_pipe_fd[0]);
|
||||
if(ret < 0) {
|
||||
printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno);
|
||||
exit(5);
|
||||
}
|
||||
ret = pipe(&g_pipe2_fd[0]);
|
||||
if(ret < 0) {
|
||||
printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno);
|
||||
exit(5);
|
||||
}
|
||||
|
||||
|
||||
doc_printf("pipes: pipe_fd = %d, %d pipe2_fd: %d,%d\n",
|
||||
g_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]);
|
||||
fflush(stdout);
|
||||
|
||||
pid = fork();
|
||||
switch(pid) {
|
||||
case 0:
|
||||
/* child */
|
||||
/* close stdin and write-side of pipe */
|
||||
close(0);
|
||||
/* Close other fds to make sure X window fd is closed */
|
||||
for(i = 3; i < 100; i++) {
|
||||
if((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
close(g_pipe_fd[1]); /*make sure write pipe closed*/
|
||||
close(g_pipe2_fd[0]); /*make sure read pipe closed*/
|
||||
child_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr);
|
||||
printf("Child sound loop returned\n");
|
||||
exit(0);
|
||||
case -1:
|
||||
/* error */
|
||||
printf("sound_init: fork ret: -1, errno: %d\n", errno);
|
||||
exit(6);
|
||||
default:
|
||||
/* parent */
|
||||
/* close read-side of pipe1, and the write side of pipe2 */
|
||||
close(g_pipe_fd[0]);
|
||||
close(g_pipe2_fd[1]);
|
||||
doc_printf("Child is pid: %d\n", pid);
|
||||
}
|
||||
|
||||
parent_sound_get_sample_rate(g_pipe2_fd[0]);
|
||||
#endif
|
||||
if(size) {
|
||||
// Avoid unused param warning
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parent_sound_get_sample_rate(int read_fd)
|
||||
{
|
||||
word32 audio_rate, tmp;
|
||||
int ret;
|
||||
|
||||
ret = (int)read(read_fd, &audio_rate, 4);
|
||||
if(ret != 4) {
|
||||
printf("parent dying, could not get sample rate from child\n");
|
||||
printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno);
|
||||
exit(1);
|
||||
}
|
||||
ret = (int)read(read_fd, &tmp, 4);
|
||||
if(ret != 4) {
|
||||
printf("parent dying, could not get audio status from child\n");
|
||||
printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno);
|
||||
exit(1);
|
||||
}
|
||||
if(tmp == 0) {
|
||||
g_audio_enable = 0;
|
||||
printf("Failed to init Sound, turning off audio\n");
|
||||
}
|
||||
close(read_fd);
|
||||
|
||||
sound_set_audio_rate(audio_rate);
|
||||
}
|
||||
|
||||
void
|
||||
snddrv_shutdown()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
win32snd_shutdown();
|
||||
#else
|
||||
if((g_audio_enable != 0) && (g_pipe_fd[1] >= 0)) {
|
||||
close(g_pipe_fd[1]);
|
||||
}
|
||||
#endif
|
||||
#ifdef PULSE_AUDIO
|
||||
if(g_pulse_audio) {
|
||||
pulse_audio_shutdown();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
snddrv_send_sound(int real_samps, int size)
|
||||
{
|
||||
word32 tmp;
|
||||
int ret, call_playit;
|
||||
|
||||
if(g_audio_enable == 0) {
|
||||
printf("Entered send_sound but audio off!\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
if(real_samps) {
|
||||
tmp = size + 0xa2000000;
|
||||
} else {
|
||||
tmp = size + 0xa1000000;
|
||||
}
|
||||
//doc_log_rout("send_sound", -1, g_last_sound_play_dsamp,
|
||||
// (real_samps << 30) + size);
|
||||
|
||||
call_playit = 0;
|
||||
#if defined(MAC) || defined(_WIN32)
|
||||
call_playit = 1; // Never fork child mac/windows
|
||||
#endif
|
||||
if(call_playit || g_pulse_audio) {
|
||||
child_sound_playit(tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Although this looks like a big/little-endian issue, since the */
|
||||
/* child is also reading an int, it just works with no byte swap */
|
||||
ret = (int)write(g_pipe_fd[1], &tmp, 4);
|
||||
if(ret != 4) {
|
||||
halt_printf("send_sound, wr ret: %d, errno: %d\n", ret, errno);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
child_sound_playit(word32 tmp)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = tmp & 0xffffff;
|
||||
|
||||
//printf("child_sound_playit: %08x\n", tmp);
|
||||
|
||||
if((tmp >> 24) == 0xa2) { // play sound
|
||||
if(g_zeroes_buffered) {
|
||||
reliable_zero_write(g_zeroes_buffered);
|
||||
}
|
||||
|
||||
g_zeroes_buffered = 0;
|
||||
g_zeroes_seen = 0;
|
||||
|
||||
if((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) {
|
||||
reliable_buf_write(g_sound_shm_addr, g_childsnd_pos,
|
||||
SOUND_SHM_SAMP_SIZE - g_childsnd_pos);
|
||||
size = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE;
|
||||
g_childsnd_pos = 0;
|
||||
}
|
||||
|
||||
reliable_buf_write(g_sound_shm_addr, g_childsnd_pos, size);
|
||||
|
||||
if(g_sound_paused) {
|
||||
printf("Unpausing sound, zb: %d\n", g_zeroes_buffered);
|
||||
g_sound_paused = 0;
|
||||
}
|
||||
|
||||
} else if((tmp >> 24) == 0xa1) { // play zeroes
|
||||
if(g_sound_paused) {
|
||||
if(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) {
|
||||
g_zeroes_buffered += size;
|
||||
}
|
||||
} else {
|
||||
/* not paused, send it through */
|
||||
g_zeroes_seen += size;
|
||||
|
||||
reliable_zero_write(size);
|
||||
|
||||
if(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) {
|
||||
printf("Pausing sound\n");
|
||||
g_sound_paused = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("tmp received bad: %08x\n", tmp);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
g_childsnd_pos += size;
|
||||
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
|
||||
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
|
||||
}
|
||||
|
||||
g_childsnd_vbl++;
|
||||
if(g_childsnd_vbl >= 60) {
|
||||
g_childsnd_vbl = 0;
|
||||
#if 0
|
||||
printf("sound bytes written: %06x\n", g_bytes_written);
|
||||
#endif
|
||||
g_bytes_written = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reliable_buf_write(word32 *shm_addr, int pos, int size)
|
||||
{
|
||||
byte *ptr;
|
||||
int ret;
|
||||
|
||||
if(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE ||
|
||||
size > SOUND_SHM_SAMP_SIZE ||
|
||||
(pos + size) > SOUND_SHM_SAMP_SIZE) {
|
||||
printf("reliable_buf_write: pos: %04x, size: %04x\n",
|
||||
pos, size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ptr = (byte *)&(shm_addr[pos]);
|
||||
size = size * 4;
|
||||
|
||||
while(size > 0) {
|
||||
ret = child_send_samples(ptr, size);
|
||||
|
||||
if(ret < 0) {
|
||||
printf("audio write, errno: %d %s\n", errno,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
size = size - ret;
|
||||
ptr += ret;
|
||||
g_bytes_written += ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
reliable_zero_write(int amt)
|
||||
{
|
||||
int len;
|
||||
|
||||
while(amt > 0) {
|
||||
len = MY_MIN(amt, ZERO_BUF_SIZE);
|
||||
reliable_buf_write(g_snd_zero_buf, 0, len);
|
||||
amt -= len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
child_send_samples(byte *ptr, int size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return win32_send_audio(ptr, size);
|
||||
#else
|
||||
# ifdef MAC
|
||||
return mac_send_audio(ptr, size);
|
||||
# else
|
||||
# ifdef PULSE_AUDIO
|
||||
if(g_pulse_audio) {
|
||||
return pulse_audio_send_audio(ptr, size);
|
||||
}
|
||||
# endif
|
||||
return (int)write(g_audio_socket, ptr, size);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// child_sound_loop(): used by Linux child process as the main loop, to read
|
||||
// from pipe to get sample info every VBL, and use shm_addr to get samples
|
||||
void
|
||||
child_sound_loop(int read_fd, int write_fd, word32 *shm_addr)
|
||||
{
|
||||
word32 tmp, did_init;
|
||||
int ret, ret1, ret2;
|
||||
|
||||
doc_printf("Child pipe fd: %d, shm_addr:%p\n", read_fd, shm_addr);
|
||||
|
||||
g_audio_rate = g_preferred_rate;
|
||||
|
||||
did_init = 0;
|
||||
#if defined(__linux__) || defined(OSS)
|
||||
did_init = child_sound_init_linux();
|
||||
#endif
|
||||
|
||||
tmp = g_audio_rate;
|
||||
ret1 = (int)write(write_fd, &tmp, 4);
|
||||
tmp = did_init;
|
||||
ret2 = (int)write(write_fd, &tmp, 4);
|
||||
if((ret1) != 4 || (ret2 != 4)) {
|
||||
printf("Unable to send back audio rate to parent\n");
|
||||
printf("ret1: %d,%d fd: %d, errno:%d\n", ret1, ret2, write_fd,
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
doc_printf("Wrote to fd %d the audio rate\n", write_fd);
|
||||
|
||||
close(write_fd);
|
||||
|
||||
while(1) {
|
||||
errno = 0;
|
||||
ret = (int)read(read_fd, &tmp, 4);
|
||||
if(ret <= 0) {
|
||||
printf("child dying from ret: %d, errno: %d\n",
|
||||
ret, errno);
|
||||
break;
|
||||
}
|
||||
|
||||
child_sound_playit(tmp);
|
||||
}
|
||||
|
||||
close(g_audio_socket);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
#if defined(__linux__) || defined(OSS)
|
||||
int
|
||||
child_sound_init_linux()
|
||||
{
|
||||
int stereo, sample_size, rate, fmt, ret;
|
||||
|
||||
g_audio_socket = open("/dev/dsp", O_WRONLY, 0);
|
||||
if(g_audio_socket < 0) {
|
||||
printf("open /dev/dsp failed, ret: %d, errno:%d\n",
|
||||
g_audio_socket, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
fragment = 0x00200009;
|
||||
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment);
|
||||
if(ret < 0) {
|
||||
printf("ioctl SETFRAGEMNT failed, ret:%d, errno:%d\n",
|
||||
ret, errno);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
sample_size = 16;
|
||||
ret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size);
|
||||
if(ret < 0) {
|
||||
printf("ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\n",
|
||||
ret, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef KEGS_BIG_ENDIAN
|
||||
fmt = AFMT_S16_BE;
|
||||
#else
|
||||
fmt = AFMT_S16_LE;
|
||||
#endif
|
||||
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt);
|
||||
if(ret < 0) {
|
||||
printf("ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\n",
|
||||
ret, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stereo = 1;
|
||||
ret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo);
|
||||
if(ret < 0) {
|
||||
printf("ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\n",
|
||||
ret, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rate = g_audio_rate;
|
||||
ret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate);
|
||||
if(ret < 0) {
|
||||
printf("ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\n",
|
||||
ret, errno);
|
||||
return 0;
|
||||
}
|
||||
if(ret > 0) {
|
||||
rate = ret; /* rate is returned value */
|
||||
}
|
||||
if(rate < 8000) {
|
||||
printf("Audio rate of %d which is < 8000!\n", rate);
|
||||
return 0;
|
||||
}
|
||||
g_audio_rate = rate;
|
||||
|
||||
printf("Sound initialized\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
147
gsplus/src/style_check
Normal file
147
gsplus/src/style_check
Normal file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/perl -w
|
||||
# $KmKId: style_check,v 1.1 2020-06-14 02:52:13+00 kentd Exp $
|
||||
|
||||
# Perl script to check for coding conventions
|
||||
use English;
|
||||
|
||||
$some_bad = 0;
|
||||
|
||||
while($#ARGV >= 0) {
|
||||
$file = shift;
|
||||
$check_spaces = 0;
|
||||
if($file =~ /\.c$/) {
|
||||
$check_spaces = 1;
|
||||
} elsif($file =~ /\.s$/) {
|
||||
$check_spaces = 1;
|
||||
} elsif($file =~ /\.k$/) {
|
||||
$check_spaces = 1;
|
||||
} elsif($file =~ /\.h$/) {
|
||||
$check_spaces = 1;
|
||||
if($file =~ /^protos.*.h$/) {
|
||||
next; # skip global_names.h
|
||||
}
|
||||
if($file =~ /^global_names.h$/) {
|
||||
next; # skip global_names.h
|
||||
}
|
||||
if($file =~ /^knobs.h$/) {
|
||||
next; # skip global_names.h
|
||||
}
|
||||
} else {
|
||||
next; # skip this file
|
||||
}
|
||||
print "Style check: $file\n";
|
||||
if(-x $file) {
|
||||
print "File mode is executable\n";
|
||||
$some_bad++;
|
||||
}
|
||||
open(FILE, "<$file") || die "Open: $file: $1\n";
|
||||
$line_num = 1;
|
||||
foreach $line (<FILE>) {
|
||||
chomp($line);
|
||||
$ign_tab_space = 0;
|
||||
if($line =~ m:^[/\t]+{.*}:) {
|
||||
$ign_tab_space = 1;
|
||||
}
|
||||
$len = 0;
|
||||
$prev = 0;
|
||||
$pprev = 0;
|
||||
$bad = 0;
|
||||
$last_tab = -10;
|
||||
if($check_spaces) {
|
||||
if($line =~ / $/ || $line =~ / $/) {
|
||||
print "Line ends in tab or space\n";
|
||||
$bad++;
|
||||
}
|
||||
}
|
||||
if($line =~ /\r$/) { # Line ends with Ctrl-M
|
||||
print "Windows linebreak detected\n";
|
||||
$bad = 101;
|
||||
}
|
||||
while($line =~ m/^([^"]*)("[^"]*")(.*)$/) {
|
||||
# Convert text in strings to '-' to avoid space checks
|
||||
$prev = $1;
|
||||
$post = $3;
|
||||
$quot = &dotify($2);
|
||||
$line = $prev . $quot . $post;
|
||||
}
|
||||
while($line =~ m:^(.*)//(.*)$:) {
|
||||
# Convert text in comments to '-' to avoid space checks
|
||||
$prev = $1;
|
||||
$quot = &dotify($2);
|
||||
$line = $prev . "::" . $quot;
|
||||
}
|
||||
@chars = split(//, $line);
|
||||
foreach $char (@chars) {
|
||||
$len++;
|
||||
if(!$check_spaces) {
|
||||
# do nothing
|
||||
} elsif($char eq "\t") {
|
||||
$len = (($len + 7) >> 3) * 8;
|
||||
if($prev eq ' ') {
|
||||
print "Space followed by tab\n";
|
||||
$bad++;
|
||||
}
|
||||
$last_tab = $len;
|
||||
} elsif($char eq " ") {
|
||||
if($prev eq "\t" && !$ign_tab_space) {
|
||||
print "Tab followed by space\n";
|
||||
$bad++;
|
||||
}
|
||||
if($prev eq " " && $pprev eq " " &&
|
||||
(($len - $last_tab) > 4)) {
|
||||
print "Too many spaces\n";
|
||||
$bad++;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$pprev = $prev;
|
||||
$prev = $char
|
||||
}
|
||||
if($check_spaces) {
|
||||
if(($len > 80) && ($line_num > 2)) {
|
||||
print "Line more than 80 columns\n";
|
||||
$bad++;
|
||||
}
|
||||
#print "line $line has len $len\n";
|
||||
}
|
||||
if($bad) {
|
||||
$some_bad++;
|
||||
print "...at line $line_num in file $file\n";
|
||||
if($some_bad > 20) {
|
||||
die "Too many style errors\n";
|
||||
}
|
||||
if($bad >= 100) {
|
||||
next; # Skip to next file
|
||||
}
|
||||
}
|
||||
$line_num++;
|
||||
}
|
||||
}
|
||||
|
||||
if($some_bad) {
|
||||
die "Style errors\n";
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
sub
|
||||
dotify
|
||||
{
|
||||
my ($str) = @_;
|
||||
my @chars = ();
|
||||
my @outchars = ();
|
||||
my ($char, $result);
|
||||
|
||||
@chars = split(//, $str);
|
||||
@outchars = ();
|
||||
foreach $char (@chars) {
|
||||
if($char ne "\t") {
|
||||
$char = "-";
|
||||
}
|
||||
push(@outchars, $char);
|
||||
}
|
||||
$result = join('', @outchars);
|
||||
#print "Old quote :$str:\n";
|
||||
#print "New quote :$result:\n";
|
||||
return $result;
|
||||
}
|
||||
1450
gsplus/src/undeflate.c
Normal file
1450
gsplus/src/undeflate.c
Normal file
File diff suppressed because it is too large
Load Diff
548
gsplus/src/unshk.c
Normal file
548
gsplus/src/unshk.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2021 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// This code is based on the official NuFX documentation in Apple II FTN.e08002
|
||||
// available at: http://nulib.com/library/FTN.e08002.htm. Andy McFadden has
|
||||
// reverse-engineered GSHK (and its quirks) in Nulib, at: http://nulib.com/,
|
||||
// and that code was very helpful in getting the basic algorithms correct.
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
word32
|
||||
unshk_get_long4(byte *bptr)
|
||||
{
|
||||
word32 val;
|
||||
int i;
|
||||
|
||||
// Get 4 bytes in little-endian form
|
||||
val = 0;
|
||||
for(i = 3; i >=0 ; i--) {
|
||||
val = (val << 8) | bptr[i];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
word32
|
||||
unshk_get_word2(byte *bptr)
|
||||
{
|
||||
// Get 2 bytes in little-endian form
|
||||
return (bptr[1] << 8) | bptr[0];
|
||||
}
|
||||
|
||||
word32
|
||||
unshk_calc_crc(byte *bptr, int size, word32 start_crc)
|
||||
{
|
||||
word32 crc;
|
||||
int i, j;
|
||||
|
||||
// No table used: do basic CRC operation on size bytes. For CCITT-16
|
||||
// (the one used in ShrinkIt), xor the byte into the upper 8 bits of
|
||||
// the current crc, then xor in 0x1021 for each '1' bit shifted out
|
||||
// the top. Use a 32-bit crc variable to get the bit shifted out.
|
||||
crc = start_crc & 0xffff;
|
||||
for(i = 0; i < size; i++) {
|
||||
crc = crc ^ (bptr[i] << 8);
|
||||
for(j = 0; j < 8; j++) {
|
||||
crc = crc << 1;
|
||||
if(crc & 0x10000) {
|
||||
crc = crc ^ 0x11021;
|
||||
// XOR in 0x1021, and clear bit 16 as well.
|
||||
}
|
||||
}
|
||||
// printf("CRC after [%04x]=%02x is %04x\n", i, bptr[i], crc);
|
||||
}
|
||||
return crc & 0xffff;
|
||||
}
|
||||
|
||||
int
|
||||
unshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr)
|
||||
{
|
||||
byte *start_ucptr;
|
||||
word32 c;
|
||||
int outlen, count;
|
||||
int i;
|
||||
|
||||
// RLE is 3 bytes: { 0xdb, char, count}, where count==0 means output
|
||||
// one char.
|
||||
start_ucptr = ucptr;
|
||||
while(len > 0) {
|
||||
c = *cptr++;
|
||||
len--;
|
||||
if(c == rle_delim) {
|
||||
c = *cptr++;
|
||||
count = *cptr++;
|
||||
len -= 2;
|
||||
for(i = 0; i <= count; i++) {
|
||||
*ucptr++ = c;
|
||||
}
|
||||
} else {
|
||||
*ucptr++ = c;
|
||||
}
|
||||
}
|
||||
outlen = (int)(ucptr - start_ucptr);
|
||||
if(outlen != 0x1000) {
|
||||
printf("RLE failed, output %d bytes\n", outlen);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
unshk_lzw_clear(Lzw_state *lzw_ptr)
|
||||
{
|
||||
int i;
|
||||
|
||||
lzw_ptr->entry = 0x100; // First expected table pos
|
||||
lzw_ptr->bits = 9;
|
||||
for(i = 0; i < 256; i++) {
|
||||
lzw_ptr->table[i] = i << 12; // Encodes depth==0 as well
|
||||
}
|
||||
lzw_ptr->table[0x100] = 0;
|
||||
}
|
||||
|
||||
// LZW Table format in 32-bit word: { depth[11:0], finalc[7:0], code[11:0] }
|
||||
|
||||
byte *
|
||||
unshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen)
|
||||
{
|
||||
byte *end_ucptr, *bptr;
|
||||
word32 mask, val, entry, newcode, finalc_code;
|
||||
int bit_pos, depth, bits;
|
||||
|
||||
// This routine handles ShrinkIt LZW/1 and LZW/2 streams. It expects
|
||||
// the caller has set entry=0x100 and bits=9 at the start of each
|
||||
// LZW/1 chunk
|
||||
|
||||
entry = lzw_ptr->entry;
|
||||
bits = lzw_ptr->bits;
|
||||
end_ucptr = ucptr + uclen;
|
||||
//printf("unlzw block: format:%d, uclen:%04x\n", thread_format, uclen);
|
||||
mask = (1 << bits) - 1;
|
||||
bit_pos = 0;
|
||||
|
||||
while(ucptr < end_ucptr) {
|
||||
newcode = (cptr[2] << 16) | (cptr[1] << 8) | cptr[0];
|
||||
newcode = (newcode >> bit_pos) & mask;
|
||||
bit_pos += bits;
|
||||
cptr += (bit_pos >> 3);
|
||||
bit_pos = bit_pos & 7;
|
||||
// printf("At entry:%04x, bits:%d newcode:%04x\n", entry, bits,
|
||||
// newcode);
|
||||
if((entry + 1) >= mask) {
|
||||
bits++;
|
||||
mask = (mask << 1) | 1;
|
||||
// Note this is one too early, but this is needed to
|
||||
// match ShrinkIt
|
||||
}
|
||||
|
||||
// Newcode is up to 12-bits, where <= 0xff means just this
|
||||
// char, and >= 0x101 means chase down that code, output the
|
||||
// character in that table entry, and repeat
|
||||
if(newcode == 0x100) {
|
||||
// printf("Got clear code\n");
|
||||
entry = 0x100;
|
||||
bits = 9;
|
||||
mask = 0x1ff;
|
||||
continue;
|
||||
}
|
||||
if(newcode > entry) {
|
||||
printf("Bad code: %04x, entry:%04x\n", newcode, entry);
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
if(newcode == entry) {
|
||||
// KwKwK case: operate on oldcode
|
||||
printf("KwKwK case!\n");
|
||||
}
|
||||
#endif
|
||||
finalc_code = newcode;
|
||||
depth = lzw_ptr->table[newcode & 0xfff] >> 20;
|
||||
// depth will be 0 for 1 character, 1 for 2 characters, etc.
|
||||
bptr = ucptr + depth;
|
||||
while(bptr >= ucptr) {
|
||||
finalc_code = lzw_ptr->table[finalc_code & 0xfff];
|
||||
*bptr-- = (finalc_code >> 12) & 0xff;
|
||||
}
|
||||
val = lzw_ptr->table[entry];
|
||||
lzw_ptr->table[entry] = (val & (~0xff000)) |
|
||||
(finalc_code & 0xff000);
|
||||
// [entry] has code from last iteration (which stuck in
|
||||
// the last finalc char, which we need to toss now),
|
||||
// and update it with the correct finalc character.
|
||||
// printf("Table[%04x]=%08x\n", entry, lzw_ptr->table[entry]);
|
||||
|
||||
depth++;
|
||||
ucptr += depth;
|
||||
#if 0
|
||||
bptr = ucptr - depth;
|
||||
printf("src:%04x, out+%06x: ", (int)(cptr - cptr_start),
|
||||
(int)(ucptr - depth - (end_ucptr - uclen)));
|
||||
for(i = 0; i < depth; i++) {
|
||||
printf(" %02x", *bptr++);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
lzw_ptr->table[entry + 1] = (depth << 20) |
|
||||
(finalc_code & 0xff000) | newcode;
|
||||
// Set tab[entry+1] for KwKwK case, with this newcode,
|
||||
// and this finalc character. This also saves this
|
||||
// newcode when the next code is received.
|
||||
entry++;
|
||||
}
|
||||
lzw_ptr->entry = entry;
|
||||
lzw_ptr->bits = bits;
|
||||
if(bit_pos) { // We used part of this byte, use it
|
||||
cptr++;
|
||||
}
|
||||
return cptr;
|
||||
}
|
||||
|
||||
void
|
||||
unshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr,
|
||||
word32 uncompr_size, word32 thread_format, byte *base_cptr)
|
||||
{
|
||||
Lzw_state lzw_state;
|
||||
byte *end_cptr, *end_ucptr, *rle_inptr;
|
||||
word32 rle_delim, len, use_lzw, lzw_len, crc, chunk_crc;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
printf("Uncompress %d compress bytes into %d bytes, source offset:"
|
||||
"%08x\n", compr_size, uncompr_size, (word32)(cptr - base_cptr));
|
||||
|
||||
// LZW/1 format: crc_lo, crc_hi, vol, rle_delim then start the chunk
|
||||
// each chunk: rle_len_lo, rle_len_hi, lzw_used
|
||||
// LZW/2 format: vol, rle_delim then start the chunk
|
||||
// each chunk: rle_len_lo, rle_len_hi, lzw_len_lo, lzw_len_hi
|
||||
// where rle_len_hi[7]==1 means LZW was used
|
||||
end_cptr = cptr + compr_size;
|
||||
end_ucptr = ucptr + uncompr_size;
|
||||
|
||||
chunk_crc = 0;
|
||||
if(thread_format != 3) { // LZW/1
|
||||
chunk_crc = (cptr[1] << 8) | cptr[0];
|
||||
cptr += 2; // Skip over CRC bytes
|
||||
}
|
||||
dsk->vol_num = cptr[0]; // LZW/1
|
||||
rle_delim = cptr[1];
|
||||
cptr += 2;
|
||||
unshk_lzw_clear(&lzw_state);
|
||||
|
||||
// printf("vol_num:%02x, rle_delim:%02x\n", dsk->vol_num, rle_delim);
|
||||
|
||||
// LZW/1 format for each chunk: len_lo, len_hi, use_lzw
|
||||
// LZW/2 format for each chunk: len_lo, len_hi. If len_hi[7]=1, then
|
||||
// two more bytes: lzw_len_lo, lzw_len_hi
|
||||
while(cptr < (end_cptr - 4)) {
|
||||
if(ucptr >= end_ucptr) {
|
||||
break;
|
||||
}
|
||||
len = (cptr[1] << 8) | cptr[0];
|
||||
#if 0
|
||||
printf("chunk at +%08x, len:%04x, dest offset:%08x\n",
|
||||
(word32)(cptr - base_cptr), len,
|
||||
(word32)(ucptr - (end_ucptr - uncompr_size)));
|
||||
#endif
|
||||
cptr += 2;
|
||||
use_lzw = (len >> 15) & 1;
|
||||
if(len & 0x6000) {
|
||||
printf("Illegal length: %04x\n", len);
|
||||
return; // Ilegal length
|
||||
}
|
||||
len = len & 0x1fff;
|
||||
lzw_len = 0;
|
||||
if(thread_format == 3) { // LZW/2
|
||||
if(use_lzw) {
|
||||
lzw_len = (cptr[1] << 8) | cptr[0];
|
||||
if(lzw_len > 0x1004) {
|
||||
printf("Bad lzw_len: %04x\n", lzw_len);
|
||||
return; // Illegal
|
||||
}
|
||||
cptr += 2;
|
||||
lzw_len -= 4; // Counts from [-4]
|
||||
}
|
||||
} else { // LZW/1
|
||||
use_lzw = *cptr++;
|
||||
if(use_lzw >= 2) {
|
||||
printf("Bad use_lzw:%02x\n", use_lzw);
|
||||
return; // Bad format
|
||||
}
|
||||
}
|
||||
rle_inptr = cptr;
|
||||
if(use_lzw) {
|
||||
//printf("lzw on %02x.%02x.%02x.., %d bytes (rle:%d)\n",
|
||||
// cptr[0], cptr[1], cptr[2], lzw_len, len);
|
||||
rle_inptr = ucptr;
|
||||
if(len != 0x1000) {
|
||||
// RLE pass is needed: Write to ucptr+0x1000,
|
||||
// and then UnRLE down to ucptr;
|
||||
rle_inptr = ucptr + 0x1000;
|
||||
}
|
||||
cptr = unshk_unlzw(cptr, &lzw_state, rle_inptr, len);
|
||||
if(cptr == 0) {
|
||||
printf("Bad LZW stream\n");
|
||||
return;
|
||||
}
|
||||
if(thread_format != 3) { // LZW/1
|
||||
lzw_state.entry = 0x100; // Reset table
|
||||
lzw_state.bits = 9;
|
||||
}
|
||||
} else {
|
||||
lzw_state.entry = 0x100; // Reset table
|
||||
lzw_state.bits = 9;
|
||||
}
|
||||
if(len != 0x1000) {
|
||||
// printf("RLE on %02x.%02x.%02x... %d bytes\n",
|
||||
// cptr[0], cptr[1], cptr[2], len);
|
||||
ret = unshk_unrle(rle_inptr, len, rle_delim, ucptr);
|
||||
if(ret) {
|
||||
printf("unRLE failed\n");
|
||||
return;
|
||||
}
|
||||
if(!use_lzw) {
|
||||
cptr += len;
|
||||
}
|
||||
} else if(!use_lzw) {
|
||||
// Uncompressed
|
||||
// printf("Uncompressed %02x.%02x.%02x....%d bytes\n",
|
||||
// cptr[0], cptr[1], cptr[2], len);
|
||||
for(i = 0; i < 0x1000; i++) {
|
||||
ucptr[i] = *cptr++;
|
||||
}
|
||||
}
|
||||
// write(g_out_fd, ucptr, 0x1000);
|
||||
ucptr += 0x1000;
|
||||
}
|
||||
|
||||
printf("cptr:%p, end_cptr:%p, uncompr_size:%08x\n", cptr, end_cptr,
|
||||
uncompr_size);
|
||||
if(thread_format != 3) { // LZW/1
|
||||
crc = unshk_calc_crc(ucptr - uncompr_size, uncompr_size, 0);
|
||||
//printf("LZW/1 calc CRC %04x vs CRC %04x\n", crc, chunk_crc);
|
||||
if(crc != chunk_crc) {
|
||||
printf("Bad LZW/1 CRC: %04x != %04x\n", crc, chunk_crc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dsk->fd = 0;
|
||||
}
|
||||
|
||||
void
|
||||
unshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr)
|
||||
{
|
||||
byte *cptr_end, *dptr, *ucptr;
|
||||
word32 total_records, attrib_count, total_threads, thread_class;
|
||||
word32 thread_format, thread_kind, thread_eof, comp_thread_eof;
|
||||
word32 thread_crc, crc, version, disk_size, block_size, num_blocks;
|
||||
word32 filename_length;
|
||||
int i;
|
||||
|
||||
cptr_end = cptr + compr_size;
|
||||
if(compr_size < 0xa0) {
|
||||
printf("Didn't read everything\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse NuFX format: "NuFile" with alternating high bits
|
||||
if((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) ||
|
||||
(cptr[3] != 0xe9) || (cptr[4] != 0x6c) || (cptr[5] != 0xe5)) {
|
||||
printf("Not NuFile, exiting\n");
|
||||
return;
|
||||
}
|
||||
total_records = unshk_get_long4(&cptr[8]);
|
||||
if(total_records < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Master Header to NuFile is apparently 48 bytes. Look for "NuFX"
|
||||
// Header to describe threads
|
||||
cptr += 0x30;
|
||||
if((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) ||
|
||||
(cptr[3] != 0xd8)) {
|
||||
return;
|
||||
}
|
||||
attrib_count = unshk_get_word2(&cptr[6]);
|
||||
version = unshk_get_word2(&cptr[8]); // >= 3 means File CRC
|
||||
total_threads = unshk_get_long4(&cptr[10]);
|
||||
num_blocks = unshk_get_long4(&cptr[26]); // extra_type
|
||||
block_size = unshk_get_word2(&cptr[30]); // storage_type
|
||||
// P8 ShrinkIt is riddled with bugs. Disk archives have incorrect
|
||||
// thread_eof for the uncompressed total size. So we need to do
|
||||
// num_blocks * block_size to get the real size. But this can be
|
||||
// buggy too! These fixes are from NuFxLib::Thread.c actualThreadEOF
|
||||
// comments
|
||||
// First, fix block_size. SHK v3.0.1 stored it as a small value
|
||||
if(block_size < 256) {
|
||||
block_size = 512;
|
||||
}
|
||||
disk_size = block_size * num_blocks;
|
||||
if(disk_size == (70*1024)) {
|
||||
// Old GSHK apparently set block_size==256 but blocks=280 for
|
||||
// 5.25" DOS 3.3 disks...block size must be 512 to equal 140K.
|
||||
disk_size = 140*1024;
|
||||
}
|
||||
if(disk_size < 140*1024) {
|
||||
printf("disk_size %dK is invalid\n", disk_size >> 10);
|
||||
return;
|
||||
}
|
||||
cptr += attrib_count;
|
||||
filename_length = unshk_get_word2(&cptr[-2]); // filename_length
|
||||
cptr += filename_length;
|
||||
dptr = cptr + 16*total_threads;
|
||||
// Each thread is 16 bytes, so the data is at +16*total_threads
|
||||
// The data is in the same order as the header for the threads
|
||||
// We ignore anything other than a data thread for SDK
|
||||
for(i = 0; i < (int)total_threads; i++) {
|
||||
if((dptr >= cptr_end) || (cptr >= cptr_end)) {
|
||||
return;
|
||||
}
|
||||
thread_class = unshk_get_word2(&cptr[0]);
|
||||
thread_format = unshk_get_word2(&cptr[2]);
|
||||
thread_kind = unshk_get_word2(&cptr[4]);
|
||||
thread_crc = unshk_get_word2(&cptr[6]);
|
||||
//thread_eof = unshk_get_long4(&cptr[8]);
|
||||
// thread_eof is wrong in P8 ShrinkIt, so just use disk_size
|
||||
thread_eof = disk_size;
|
||||
comp_thread_eof = unshk_get_long4(&cptr[12]);
|
||||
if((dptr + comp_thread_eof) > cptr_end) {
|
||||
return; // Corrupt
|
||||
}
|
||||
if((thread_class == 2) && (thread_kind == 1)) {
|
||||
// Disk image!
|
||||
ucptr = malloc(thread_eof + 0x1000);
|
||||
unshk_data(dsk, dptr, comp_thread_eof, ucptr,
|
||||
thread_eof, thread_format, base_cptr);
|
||||
if(dsk->fd == 0) {
|
||||
// Success, so far. Check CRC
|
||||
printf("Version:%d, thread_crc:%04x\n",
|
||||
version, thread_crc);
|
||||
if(version >= 3) { // CRC is valid
|
||||
crc = unshk_calc_crc(ucptr, thread_eof,
|
||||
0xffff);
|
||||
#if 0
|
||||
printf("Thread CRC:%04x, exp:%04x\n",
|
||||
crc, thread_crc);
|
||||
#endif
|
||||
if(crc != thread_crc) {
|
||||
printf("Bad CRC: %04x != exp "
|
||||
"%04x\n", crc,
|
||||
thread_crc);
|
||||
dsk->fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(dsk->fd < 0) {
|
||||
free(ucptr);
|
||||
} else {
|
||||
// Real success, set raw_size
|
||||
dsk->raw_dsize = thread_eof;
|
||||
dsk->raw_data = ucptr;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dptr += comp_thread_eof;
|
||||
cptr += 16;
|
||||
}
|
||||
}
|
||||
|
||||
// Disk image thread not found, get out
|
||||
}
|
||||
|
||||
void
|
||||
unshk(Disk *dsk, const char *name_str)
|
||||
{
|
||||
byte *cptr;
|
||||
int compr_size, fd, pos, ret;
|
||||
int i;
|
||||
|
||||
printf("unshk %s\n", name_str);
|
||||
// Handle .sdk inside a .zip
|
||||
if(dsk->raw_data) {
|
||||
unshk_dsk_raw_data(dsk);
|
||||
return;
|
||||
}
|
||||
|
||||
// File is not opened yet, try to open it
|
||||
fd = open(name_str, O_RDONLY | O_BINARY, 0x1b6);
|
||||
if(fd < 0) {
|
||||
return;
|
||||
}
|
||||
compr_size = (int)cfg_get_fd_size(fd);
|
||||
printf("size: %d\n", compr_size);
|
||||
|
||||
cptr = malloc(compr_size + 0x1000);
|
||||
pos = 0;
|
||||
for(i = 0; i < 0x1000; i++) {
|
||||
cptr[compr_size + i] = 0;
|
||||
}
|
||||
while(1) {
|
||||
if(pos >= compr_size) {
|
||||
break;
|
||||
}
|
||||
ret = read(fd, cptr + pos, compr_size - pos);
|
||||
if(ret <= 0) {
|
||||
break;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if(pos != compr_size) {
|
||||
compr_size = 0; // Make header searching fail
|
||||
}
|
||||
unshk_parse_header(dsk, cptr, compr_size, cptr);
|
||||
|
||||
free(cptr);
|
||||
}
|
||||
|
||||
void
|
||||
unshk_dsk_raw_data(Disk *dsk)
|
||||
{
|
||||
byte *save_raw_data, *cptr;
|
||||
dword64 save_raw_dsize;
|
||||
int save_fd, compr_size;
|
||||
int i;
|
||||
// This code handles the case of .sdk inside a .zip (for example).
|
||||
// Since unshk() code uses dsk->fd, dsk->raw_data, and dsk->raw_dsize
|
||||
// to communicate success in unshk'ing the disk, we need to copy
|
||||
// those, and restore them, if the unshk fails
|
||||
save_fd = dsk->fd;
|
||||
save_raw_data = dsk->raw_data;
|
||||
save_raw_dsize = dsk->raw_dsize;
|
||||
if(save_raw_dsize >= (1ULL << 30)) {
|
||||
return; // Too large
|
||||
}
|
||||
|
||||
dsk->fd = -1;
|
||||
dsk->raw_data = 0;
|
||||
dsk->raw_dsize = 0;
|
||||
|
||||
compr_size = (int)save_raw_dsize;
|
||||
cptr = malloc(compr_size + 0x1000);
|
||||
for(i = 0; i < 0x1000; i++) {
|
||||
cptr[compr_size + i] = 0;
|
||||
}
|
||||
for(i = 0; i < compr_size; i++) {
|
||||
cptr[i] = save_raw_data[i];
|
||||
}
|
||||
|
||||
unshk_parse_header(dsk, cptr, compr_size, cptr);
|
||||
free(cptr);
|
||||
|
||||
if(dsk->raw_data) {
|
||||
// Success, free the old raw data
|
||||
free(save_raw_data);
|
||||
return;
|
||||
}
|
||||
dsk->fd = save_fd;
|
||||
dsk->raw_data = save_raw_data;
|
||||
dsk->raw_dsize = save_raw_dsize;
|
||||
}
|
||||
|
||||
8
gsplus/src/vars
Normal file
8
gsplus/src/vars
Normal file
@@ -0,0 +1,8 @@
|
||||
TARGET = gsplus
|
||||
OBJECTS1 = macsnd_driver.o
|
||||
CCOPTS = -Wall -O2 -DMAC
|
||||
SUFFIX =
|
||||
NAME = gsplus
|
||||
|
||||
XOPTS =
|
||||
|
||||
8
gsplus/src/vars_mac
Normal file
8
gsplus/src/vars_mac
Normal file
@@ -0,0 +1,8 @@
|
||||
TARGET = gsplus
|
||||
OBJECTS1 = macsnd_driver.o
|
||||
CCOPTS = -Wall -O2 -DMAC
|
||||
SUFFIX =
|
||||
NAME = gsplus
|
||||
|
||||
XOPTS =
|
||||
|
||||
9
gsplus/src/vars_mac_x
Normal file
9
gsplus/src/vars_mac_x
Normal file
@@ -0,0 +1,9 @@
|
||||
TARGET = gsplus
|
||||
OBJECTS1 = macsnd_driver.o xdriver.o
|
||||
CCOPTS = -O2 -DMAC -Wall -I/usr/X11/include
|
||||
SUFFIX =
|
||||
NAME = gsplus
|
||||
|
||||
XOPTS =
|
||||
LDOPTS = -Wl,-framework,CoreAudio -Wl,-framework,CoreFoundation -Wl,-framework,AudioToolbox
|
||||
|
||||
10
gsplus/src/vars_x86linux
Normal file
10
gsplus/src/vars_x86linux
Normal file
@@ -0,0 +1,10 @@
|
||||
TARGET = gsplus
|
||||
OBJECTS1 = pulseaudio_driver.o xdriver.o
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -DPULSE_AUDIO
|
||||
NAME = gsplus
|
||||
LD = $(CC)
|
||||
EXTRA_LIBS = -lXext -lpulse
|
||||
EXTRA_SPECIALS =
|
||||
|
||||
XOPTS = -I/usr/X11R6/include
|
||||
|
||||
2926
gsplus/src/video.c
Normal file
2926
gsplus/src/video.c
Normal file
File diff suppressed because it is too large
Load Diff
220
gsplus/src/voc.c
Normal file
220
gsplus/src/voc.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// This file provides emulation of the Apple Video Overlay Card, which
|
||||
// will appear to be in slot 3 if g_voc_enable=1 (there's a config.c
|
||||
// setting to control enabling VOC). The only currently supported VOC
|
||||
// feature is the SHR interlaced display using both Main and Aux memory
|
||||
// to provide a 640x400 (or 320x400) pixel display.
|
||||
|
||||
#include "defc.h"
|
||||
|
||||
extern word32 g_c02b_val;
|
||||
extern int g_cur_a2_stat;
|
||||
extern word32 g_vbl_count;
|
||||
|
||||
int g_voc_enable = 0; // Default to disabled for now
|
||||
word32 g_voc_reg1 = 0x09;
|
||||
word32 g_voc_reg3 = 0;
|
||||
word32 g_voc_reg4 = 0;
|
||||
word32 g_voc_reg5 = 0;
|
||||
word32 g_voc_reg6 = 0;
|
||||
|
||||
word32
|
||||
voc_devsel_read(word32 loc, dword64 dfcyc)
|
||||
{
|
||||
// Reads to $c0b0-$c0bf.
|
||||
loc = loc & 0xf;
|
||||
switch(loc) {
|
||||
case 0: // 0xc0b0
|
||||
return voc_read_reg0(dfcyc);
|
||||
break;
|
||||
case 1: // 0xc0b1
|
||||
return g_voc_reg1;
|
||||
break;
|
||||
case 3: // 0xc0b3
|
||||
return g_voc_reg3;
|
||||
break;
|
||||
case 4: // 0xc0b4
|
||||
return g_voc_reg4;
|
||||
break;
|
||||
case 5: // 0xc0b5
|
||||
return g_voc_reg5;
|
||||
break;
|
||||
case 6: // 0xc0b6
|
||||
return g_voc_reg6;
|
||||
break;
|
||||
case 7: // 0xc0b7, possible Uthernet 2 detection
|
||||
return 0x00;
|
||||
break;
|
||||
case 8: // 0xc0b8, Second Sight detection by jpeGS program
|
||||
return 0x00; // Second Sight returns 0x01
|
||||
break;
|
||||
case 0xd: // 0xc0bd, A2OSX Uthernet 1 detection code
|
||||
return 0x00;
|
||||
break;
|
||||
}
|
||||
|
||||
halt_printf("Tried to read: %04x\n", 0xc0b0 + loc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
voc_devsel_write(word32 loc, word32 val, dword64 dfcyc)
|
||||
{
|
||||
// Writes to $c0b0-$c0bf.
|
||||
loc = loc & 0xf;
|
||||
switch(loc) {
|
||||
case 0: // 0xc0b0
|
||||
// Write 0 to clear VBL interrupts
|
||||
if(val != 0) {
|
||||
halt_printf("VOC write %04x = %02x\n", loc, val);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case 1: // 0xc0b1
|
||||
// bit 0: R/W: 1=GG Bus Enable
|
||||
// When 0, I think VOC ignores writes to $c023,etc.
|
||||
// bit 2: R/W: 0=OutChromaFilter enabled, 1=ChromaFilter disab
|
||||
// bit 2 is also TextMonoOver somehow using bit[5]==1
|
||||
// bit 3: R/W: 1=MainPageLin
|
||||
// bits 5:4: R/W: 00=Aux mem; 01=Main Memory; 11=Interlaced
|
||||
// bit 6: R/W: 1=Enable VBL Interrupt
|
||||
// bit 7: R/W: 1=Enable Line interrupts
|
||||
if(!g_voc_enable) {
|
||||
val = 0;
|
||||
}
|
||||
if(val & 0xc0) {
|
||||
halt_printf("VOC write %04x = %02x\n", loc, val);
|
||||
}
|
||||
#if 0
|
||||
if(val != g_voc_reg1) {
|
||||
printf("$c0b1:%02x (was %02x)\n", val, g_voc_reg1);
|
||||
}
|
||||
#endif
|
||||
g_voc_reg1 = val;
|
||||
voc_update_interlace(dfcyc);
|
||||
return;
|
||||
break;
|
||||
case 3: // 0xc0b3
|
||||
// bits 2:0: R/W: Key Dissolve, 0=100% graphics, 7=100% video
|
||||
// bit 3: R/W: 1=Enhanced Dissolve enabled
|
||||
// bits 6:4: R/W: Non-Key Dissolve, 0=100% graphics, 7=100% vid
|
||||
// bit 7: R/W: 0=Output Setup Enabled, 1=Output Setup Disabled
|
||||
g_voc_reg3 = val;
|
||||
return;
|
||||
break;
|
||||
case 4: // 0xc0b4
|
||||
// bits 3:0: R/W: KeyColor Blue
|
||||
// bits 7:4: R/W: KeyColor Green
|
||||
g_voc_reg4 = val;
|
||||
return;
|
||||
break;
|
||||
case 5: // 0xc0b5
|
||||
// bits 3:0: R/W: KeyColor Red
|
||||
// bit 4: R/W: OutExtBlank: 0=Graphics, 1=External
|
||||
// bit 5: R/W: 0=GenLock enabled, 1=GenLock disabled
|
||||
// bit 6: R/W: 0=KeyColor enabled, 1=KeyColor disabled
|
||||
// bit 7: R/W: 1=Interlace mode enabled
|
||||
g_voc_reg5 = val;
|
||||
voc_update_interlace(dfcyc);
|
||||
return;
|
||||
break;
|
||||
case 6: // 0xc0b6
|
||||
// Write 0 to cause AdjSave to occur
|
||||
// Write 8, then 9, then 8 again to cause AdjInc for Hue
|
||||
// Write a, then b, then a again to cause AdjDec for Hue
|
||||
// Write 4, then 5, then 4 again to cause AdjInc for Saturation
|
||||
// Write 6, then 7, then 6 again to cause AdjDec for Saturation
|
||||
// bit 3: hue, bit 2: saturation
|
||||
g_voc_reg6 = val;
|
||||
return;
|
||||
break;
|
||||
case 7: // 0xc0b7
|
||||
// Written by System Disk 1.1 Desktop.sys to 0xfd, ignore
|
||||
if(val == 0xfd) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0xa:
|
||||
case 0xb: // 0xc0ba,0xc0bb written to 0 by A2OSX Uthernet1 detect
|
||||
if(val == 0) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
halt_printf("Unknown Write %04x = %02x %016llx\n", 0xc0b0 + loc, val,
|
||||
dfcyc);
|
||||
}
|
||||
|
||||
void
|
||||
voc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc)
|
||||
{
|
||||
// Writes to $c300-$c3ff
|
||||
halt_printf("Wrote VOC %04x = %02x %016llx\n", 0xc300 + (loc & 0xff),
|
||||
val, dfcyc);
|
||||
}
|
||||
|
||||
void
|
||||
voc_reset()
|
||||
{
|
||||
g_voc_reg1 = 0x0d; // [0]: GG Bus enable, [3]:MainPageLin
|
||||
g_voc_reg3 = 0x07;
|
||||
g_voc_reg4 = 0;
|
||||
g_voc_reg5 = 0x40;
|
||||
g_voc_reg6 = 0;
|
||||
}
|
||||
|
||||
double g_voc_last_pal_vbl = 0;
|
||||
|
||||
word32
|
||||
voc_read_reg0(dword64 dfcyc)
|
||||
{
|
||||
word32 frame, in_vbl;
|
||||
|
||||
if(!g_voc_enable) {
|
||||
return 0;
|
||||
}
|
||||
// Reading $c0b0.
|
||||
// c0b0: bit 2: R/O: 1=In VBL
|
||||
// c0b0: bit 3: R/O: 0=No Video Detected, 1=Video Detected
|
||||
// c0b0: bit 4: R/O: 1=Video Genlocked
|
||||
// c0b0: bit 5: R/O: 0=showing Field 0, 1=showing Field 1
|
||||
// c0b0: bit 6: R/O: 1=VBL Int Request pending
|
||||
// c0b0: bit 7: R/O: 1=Line Int Request pending
|
||||
in_vbl = in_vblank(dfcyc);
|
||||
dbg_log_info(dfcyc, 0, in_vbl, 0x1c0b0);
|
||||
frame = g_vbl_count & 1;
|
||||
return (frame << 5) | (in_vbl << 2);
|
||||
}
|
||||
|
||||
void
|
||||
voc_update_interlace(dword64 dfcyc)
|
||||
{
|
||||
word32 new_stat, mask;
|
||||
|
||||
new_stat = 0;
|
||||
if(((g_voc_reg1 & 0x30) == 0x30) && (g_voc_reg5 & 0x80)) {
|
||||
new_stat = ALL_STAT_VOC_INTERLACE;
|
||||
}
|
||||
if((g_voc_reg1 & 0x30) == 0x10) { // Draw SHR from mainmem
|
||||
new_stat = ALL_STAT_VOC_MAIN;
|
||||
}
|
||||
mask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN;
|
||||
if((g_cur_a2_stat ^ new_stat) & mask) {
|
||||
// Interlace mode has changed
|
||||
g_cur_a2_stat &= (~mask);
|
||||
g_cur_a2_stat |= new_stat;
|
||||
printf("Change VOC interlace mode: %08x\n", new_stat);
|
||||
change_display_mode(dfcyc);
|
||||
}
|
||||
}
|
||||
|
||||
306
gsplus/src/win32snd_driver.c
Normal file
306
gsplus/src/win32snd_driver.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2002-2023 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// Audio is sent every 1/60th of a second to win32_send_audio(). Play it
|
||||
// using waveOutWrite() as long as we've got at least 2/60th of a second
|
||||
// buffered--otherwise waveOutPause(). If we get more than 10 buffers
|
||||
// queued, drop buffers until we get down to 6 buffers queued again.
|
||||
|
||||
// Track headers completed in g_wavehdr_rd_pos using callback function.
|
||||
|
||||
#include "defc.h"
|
||||
#include "sound.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
extern int Verbose;
|
||||
|
||||
extern int g_audio_rate;
|
||||
|
||||
unsigned int __stdcall child_sound_loop_win32(void *param);
|
||||
void check_wave_error(int res, char *str);
|
||||
|
||||
#define NUM_WAVE_HEADERS 32
|
||||
|
||||
HWAVEOUT g_wave_handle;
|
||||
WAVEHDR g_wavehdr[NUM_WAVE_HEADERS];
|
||||
// Each header is for 1/60th of a second of sound (generally). Pause
|
||||
// until 2 headers are available, then unpause. Experimentally it appears
|
||||
// we keep about 5 headers (5/60th of a second = 80msec) ahead, which is
|
||||
// excellent latency
|
||||
|
||||
extern int g_audio_enable;
|
||||
extern word32 *g_sound_shm_addr;
|
||||
extern int g_preferred_rate;
|
||||
|
||||
int g_win32snd_buflen = 0x1000;
|
||||
int g_win32_snd_playing = 0;
|
||||
int g_win32_snd_to_drop = 0;
|
||||
word32 g_win32_snd_dropped = 0;
|
||||
volatile int g_wavehdr_rd_pos = 0;
|
||||
volatile int g_wavehdr_wr_pos = 0;
|
||||
|
||||
void
|
||||
win32snd_init(word32 *shmaddr)
|
||||
{
|
||||
printf("win32snd_init\n");
|
||||
child_sound_init_win32();
|
||||
}
|
||||
|
||||
void
|
||||
win32snd_shutdown()
|
||||
{
|
||||
/* hmm */
|
||||
|
||||
}
|
||||
|
||||
void CALLBACK
|
||||
handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
LPWAVEHDR lpwavehdr;
|
||||
int pos;
|
||||
|
||||
/* Only service "buffer done playing messages */
|
||||
if(uMsg == WOM_DONE) {
|
||||
lpwavehdr = (LPWAVEHDR)dwParam1;
|
||||
if(lpwavehdr->dwFlags == (WHDR_DONE | WHDR_PREPARED)) {
|
||||
lpwavehdr->dwUser = FALSE;
|
||||
}
|
||||
pos = (int)(lpwavehdr - &g_wavehdr[0]);
|
||||
// printf("At %.3f, pos %d is done\n", get_dtime(), pos);
|
||||
if(pos == g_wavehdr_rd_pos) {
|
||||
pos = (pos + 1) % NUM_WAVE_HEADERS;
|
||||
g_wavehdr_rd_pos = pos;
|
||||
} else {
|
||||
printf("wavehdr %d finished, exp %d\n", pos,
|
||||
g_wavehdr_rd_pos);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
check_wave_error(int res, char *str)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
if(res == MMSYSERR_NOERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
waveOutGetErrorText(res, &buf[0], sizeof(buf));
|
||||
printf("%s: %s\n", str, buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
child_sound_init_win32()
|
||||
{
|
||||
WAVEFORMATEX wavefmt;
|
||||
WAVEOUTCAPS caps;
|
||||
byte *bptr;
|
||||
UINT wave_id;
|
||||
int bits_per_sample, channels, block_align, blen, res;
|
||||
int i;
|
||||
|
||||
memset(&wavefmt, 0, sizeof(WAVEFORMATEX));
|
||||
|
||||
wavefmt.wFormatTag = WAVE_FORMAT_PCM;
|
||||
bits_per_sample = 16;
|
||||
channels = 2;
|
||||
wavefmt.wBitsPerSample = bits_per_sample;
|
||||
wavefmt.nChannels = channels;
|
||||
wavefmt.nSamplesPerSec = g_preferred_rate;
|
||||
block_align = channels * (bits_per_sample / 8);
|
||||
wavefmt.nBlockAlign = block_align;
|
||||
wavefmt.nAvgBytesPerSec = block_align * g_audio_rate;
|
||||
|
||||
res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, 0, 0,
|
||||
WAVE_FORMAT_QUERY);
|
||||
|
||||
if(res != MMSYSERR_NOERROR) {
|
||||
printf("Cannot open audio device, res:%d, g_audio_rate:%d\n",
|
||||
res, g_preferred_rate);
|
||||
g_audio_enable = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt,
|
||||
(DWORD_PTR)handle_wav_snd, 0,
|
||||
CALLBACK_FUNCTION | WAVE_ALLOWSYNC);
|
||||
|
||||
if(res != MMSYSERR_NOERROR) {
|
||||
printf("Cannot register audio\n");
|
||||
g_audio_enable = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
g_audio_rate = wavefmt.nSamplesPerSec;
|
||||
blen = (((g_audio_rate * block_align) / 60) * 5) / 4;
|
||||
// Size buffer 25% larger than expected, to add some margin
|
||||
blen = (blen + 15) & -16L;
|
||||
|
||||
g_win32snd_buflen = blen;
|
||||
bptr = malloc(blen * NUM_WAVE_HEADERS);
|
||||
if(bptr == NULL) {
|
||||
printf("Unabled to allocate sound buffer\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for(i = 0; i < NUM_WAVE_HEADERS; i++) {
|
||||
memset(&g_wavehdr[i], 0, sizeof(WAVEHDR));
|
||||
g_wavehdr[i].dwUser = FALSE;
|
||||
g_wavehdr[i].lpData = (char *)&(bptr[i * blen]);
|
||||
g_wavehdr[i].dwBufferLength = blen;
|
||||
g_wavehdr[i].dwFlags = 0;
|
||||
g_wavehdr[i].dwLoops = 0;
|
||||
res = waveOutPrepareHeader(g_wave_handle, &g_wavehdr[i],
|
||||
sizeof(WAVEHDR));
|
||||
check_wave_error(res, "waveOutPrepareHeader");
|
||||
}
|
||||
|
||||
res = waveOutGetID(g_wave_handle, &wave_id);
|
||||
res = waveOutGetDevCaps(wave_id, &caps, sizeof(caps));
|
||||
check_wave_error(res, "waveOutGetDevCaps");
|
||||
printf("Using %s, buflen:%d\n", caps.szPname, g_win32snd_buflen);
|
||||
printf(" Bits per Sample = %d. Channels = %d\n",
|
||||
wavefmt.wBitsPerSample, wavefmt.nChannels);
|
||||
printf(" Sampling rate = %d, avg_bytes_per_sec = %d\n",
|
||||
(int)wavefmt.nSamplesPerSec, (int)wavefmt.nAvgBytesPerSec);
|
||||
|
||||
sound_set_audio_rate(g_audio_rate);
|
||||
}
|
||||
|
||||
void
|
||||
win32snd_set_playing(int snd_playing)
|
||||
{
|
||||
g_win32_snd_playing = snd_playing;
|
||||
if(snd_playing) {
|
||||
waveOutRestart(g_wave_handle);
|
||||
#if 0
|
||||
printf("win32 restarted sound wr:%d rd:%d\n", g_wavehdr_wr_pos,
|
||||
g_wavehdr_rd_pos);
|
||||
#endif
|
||||
} else {
|
||||
waveOutPause(g_wave_handle);
|
||||
#if 0
|
||||
printf("win32 paused sound wr:%d rd:%d\n", g_wavehdr_wr_pos,
|
||||
g_wavehdr_rd_pos);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win32_send_audio2(byte *ptr, int size)
|
||||
{
|
||||
int res, wr_pos, rd_pos, new_pos, bufs_in_use;
|
||||
|
||||
wr_pos = g_wavehdr_wr_pos;
|
||||
rd_pos = g_wavehdr_rd_pos;
|
||||
#if 0
|
||||
if(wr_pos == 0) {
|
||||
printf("send_audio2 wr:%d rd:%d sz:%d at %.3f\n", wr_pos,
|
||||
rd_pos, size, get_dtime());
|
||||
}
|
||||
#endif
|
||||
if(g_wavehdr[wr_pos].dwUser != FALSE) {
|
||||
// Audio buffer busy...should not happen!
|
||||
printf("Audio buffer %d is busy!\n", wr_pos);
|
||||
return;
|
||||
}
|
||||
|
||||
bufs_in_use = (NUM_WAVE_HEADERS + wr_pos - rd_pos) % NUM_WAVE_HEADERS;
|
||||
if(g_win32_snd_to_drop) {
|
||||
g_win32_snd_to_drop--;
|
||||
g_win32_snd_dropped += size;
|
||||
if((bufs_in_use < 4) && (g_win32_snd_to_drop != 0)) {
|
||||
#if 0
|
||||
printf("bufs_in_use:%d, snd_to_drop:%d\n", bufs_in_use,
|
||||
g_win32_snd_to_drop);
|
||||
#endif
|
||||
g_win32_snd_to_drop = 0;
|
||||
}
|
||||
if(g_win32_snd_to_drop == 0) {
|
||||
printf("Dropped %d bytes of sound\n",
|
||||
g_win32_snd_dropped);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
if(g_win32_snd_playing && (bufs_in_use <= 2)) {
|
||||
printf("bufs_in_use:%d, wr:%d, rd:%d\n", bufs_in_use, wr_pos,
|
||||
rd_pos);
|
||||
}
|
||||
#endif
|
||||
if(bufs_in_use == 0) {
|
||||
#if 0
|
||||
printf("bufs_in_use:%d, wr_pos:%d rd_pos:%d\n", bufs_in_use,
|
||||
wr_pos, rd_pos);
|
||||
#endif
|
||||
// We've underflowed, so pause sound until we get some buffered
|
||||
win32snd_set_playing(0);
|
||||
} else if(g_win32_snd_playing == 0) {
|
||||
if(bufs_in_use >= 2) {
|
||||
//printf("bufs_in_use:%d, will start\n", bufs_in_use);
|
||||
win32snd_set_playing(1);
|
||||
}
|
||||
} else {
|
||||
if(bufs_in_use >= 14) { // About 230msec
|
||||
// Drop 6 buffers to get us back down to 100msec delay
|
||||
printf("bufs_in_use:%d, wr:%d will drop 6\n",
|
||||
bufs_in_use, wr_pos);
|
||||
g_win32_snd_to_drop = 6;
|
||||
g_win32_snd_dropped = 0;
|
||||
}
|
||||
}
|
||||
memcpy(g_wavehdr[wr_pos].lpData, ptr, size);
|
||||
g_wavehdr[wr_pos].dwBufferLength = size;
|
||||
g_wavehdr[wr_pos].dwUser = TRUE;
|
||||
|
||||
new_pos = (wr_pos + 1) % NUM_WAVE_HEADERS;
|
||||
g_wavehdr_wr_pos = new_pos;
|
||||
res = waveOutWrite(g_wave_handle, &g_wavehdr[wr_pos],
|
||||
sizeof(g_wavehdr));
|
||||
check_wave_error(res, "waveOutWrite");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
win32_send_audio(byte *ptr, int in_size)
|
||||
{
|
||||
int size;
|
||||
int tmpsize;
|
||||
|
||||
// printf("send_audio %d bytes at %.3f\n", in_size, get_dtime());
|
||||
size = in_size;
|
||||
while(size > 0) {
|
||||
tmpsize = size;
|
||||
if(size > g_win32snd_buflen) {
|
||||
tmpsize = g_win32snd_buflen;
|
||||
}
|
||||
win32_send_audio2(ptr, tmpsize);
|
||||
ptr += tmpsize;
|
||||
if(size != tmpsize) {
|
||||
#if 0
|
||||
printf("Orig size:%d, reduced to %d\n", in_size,
|
||||
tmpsize);
|
||||
#endif
|
||||
}
|
||||
size = size - tmpsize;
|
||||
}
|
||||
|
||||
return in_size;
|
||||
}
|
||||
|
||||
32
gsplus/src/win_dirent.h
Normal file
32
gsplus/src/win_dirent.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifdef INCLUDE_RCSID_C
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* GSplus - Apple //gs Emulator */
|
||||
/* Based on KEGS by Kent Dickey */
|
||||
/* Copyright 2022 Kent Dickey */
|
||||
/* Copyright 2025-2026 GSplus Contributors */
|
||||
/* */
|
||||
/* This code is covered by the GNU GPL v3 */
|
||||
/* See the file COPYING.txt or https://www.gnu.org/licenses/ */
|
||||
/**********************************************************************/
|
||||
|
||||
// Hacky defines to get something to compile for now
|
||||
typedef unsigned short mode_t;
|
||||
|
||||
struct dirent {
|
||||
char d_name[1024];
|
||||
};
|
||||
|
||||
struct DIR_t {
|
||||
int find_data_valid;
|
||||
void *win_handle;
|
||||
void *find_data_ptr;
|
||||
struct dirent dirent;
|
||||
};
|
||||
typedef struct DIR_t DIR;
|
||||
|
||||
DIR *opendir(const char *filename);
|
||||
struct dirent *readdir(DIR *dirp);
|
||||
int closedir(DIR *dirp);
|
||||
|
||||
1072
gsplus/src/windriver.c
Normal file
1072
gsplus/src/windriver.c
Normal file
File diff suppressed because it is too large
Load Diff
1138
gsplus/src/woz.c
Normal file
1138
gsplus/src/woz.c
Normal file
File diff suppressed because it is too large
Load Diff
1511
gsplus/src/xdriver.c
Normal file
1511
gsplus/src/xdriver.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user