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:
Dagen Brock
2026-02-28 00:20:59 -06:00
committed by GitHub
80 changed files with 46765 additions and 0 deletions

7
TODO.md Normal file
View 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

Binary file not shown.

BIN
gsplus/lib/525.icns Normal file

Binary file not shown.

BIN
gsplus/lib/MainMenu.nib generated Normal file

Binary file not shown.

BIN
gsplus/lib/kegsicon.icns Normal file

Binary file not shown.

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
View 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`;

View 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
View 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>

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

362
gsplus/src/applesingle.c Normal file
View 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
View 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
View 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 \
"$@"

View File

@@ -0,0 +1,2 @@
char g_compile_time[] = "Compiled: " __DATE__ " " __TIME__ ;

4613
gsplus/src/config.c Normal file

File diff suppressed because it is too large Load Diff

36
gsplus/src/config.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

364
gsplus/src/defc.h Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

16
gsplus/src/dyna_filt.c Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

84
gsplus/src/engine.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

3752
gsplus/src/iwm.c Normal file

File diff suppressed because it is too large Load Diff

206
gsplus/src/iwm.h Normal file
View 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];
};

View 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

Binary file not shown.

514
gsplus/src/kegsfont.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

143
gsplus/src/op_routs.h Normal file
View 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
View 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
View 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
View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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

File diff suppressed because it is too large Load Diff

111
gsplus/src/scc.h Normal file
View 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

File diff suppressed because it is too large Load Diff

224
gsplus/src/scc_unixdriver.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

269
gsplus/src/size_c.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

101
gsplus/src/sound.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

548
gsplus/src/unshk.c Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

220
gsplus/src/voc.c Normal file
View 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);
}
}

View 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
View 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

File diff suppressed because it is too large Load Diff

1138
gsplus/src/woz.c Normal file

File diff suppressed because it is too large Load Diff

1511
gsplus/src/xdriver.c Normal file

File diff suppressed because it is too large Load Diff